2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A/*
2N/A * Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A/*
2N/A * Server side RPC handler.
2N/A */
2N/A
2N/A#include <sys/byteorder.h>
2N/A#include <sys/errno.h>
2N/A#include <sys/uio.h>
2N/A#include <thread.h>
2N/A#include <synch.h>
2N/A#include <stdlib.h>
2N/A#include <strings.h>
2N/A#include <string.h>
2N/A#include <time.h>
2N/A
2N/A#include <smbsrv/libsmb.h>
2N/A#include <smbsrv/libndr.h>
2N/A#include <smb/smb.h>
2N/A
2N/A#define NDR_GROW_SIZE (8 * 1024)
2N/A#define NDR_GROW_MASK (NDR_GROW_SIZE - 1)
2N/A#define NDR_ALIGN_BUF(S) (((S) + NDR_GROW_SIZE) & ~NDR_GROW_MASK)
2N/A
2N/A#define NDR_PIPE_BUFSZ (64 * 1024)
2N/A#define NDR_PIPE_BUFMAX (64 * 1024 * 1024)
2N/A#define NDR_PIPE_MAX 128
2N/A
2N/Astatic ndr_pipe_t ndr_pipe_table[NDR_PIPE_MAX];
2N/Astatic mutex_t ndr_pipe_lock;
2N/A
2N/Astatic int ndr_pipe_process(ndr_pipe_t *);
2N/Astatic ndr_pipe_t *ndr_pipe_lookup(int);
2N/Astatic void ndr_pipe_release(ndr_pipe_t *);
2N/Astatic ndr_pipe_t *ndr_pipe_allocate(int);
2N/Astatic int ndr_pipe_grow(ndr_pipe_t *, size_t);
2N/Astatic void ndr_pipe_deallocate(ndr_pipe_t *);
2N/Astatic void ndr_pipe_rewind(ndr_pipe_t *);
2N/Astatic void ndr_pipe_flush(ndr_pipe_t *);
2N/A
2N/Astatic int ndr_svc_process(ndr_xa_t *);
2N/Astatic int ndr_svc_defrag(ndr_xa_t *);
2N/Astatic int ndr_svc_bind(ndr_xa_t *);
2N/Astatic int ndr_svc_request(ndr_xa_t *);
2N/Astatic void ndr_reply_prepare_hdr(ndr_xa_t *);
2N/Astatic int ndr_svc_alter_context(ndr_xa_t *);
2N/Astatic void ndr_reply_fault(ndr_xa_t *, unsigned long);
2N/Astatic int ndr_build_reply(ndr_xa_t *);
2N/Astatic void ndr_build_frag(ndr_stream_t *, uint8_t *, uint32_t);
2N/A
2N/A/*
2N/A * Allocate and associate a service context with a fid.
2N/A */
2N/Aint
2N/Andr_pipe_open(int fid, uint8_t *data, uint32_t datalen)
2N/A{
2N/A ndr_pipe_t *np;
2N/A
2N/A (void) mutex_lock(&ndr_pipe_lock);
2N/A
2N/A if ((np = ndr_pipe_lookup(fid)) != NULL) {
2N/A ndr_pipe_release(np);
2N/A (void) mutex_unlock(&ndr_pipe_lock);
2N/A return (EEXIST);
2N/A }
2N/A
2N/A if ((np = ndr_pipe_allocate(fid)) == NULL) {
2N/A (void) mutex_unlock(&ndr_pipe_lock);
2N/A return (ENOMEM);
2N/A }
2N/A
2N/A if (smb_netuserinfo_decode(&np->np_user, data, datalen, NULL) == -1) {
2N/A ndr_pipe_release(np);
2N/A (void) mutex_unlock(&ndr_pipe_lock);
2N/A return (EINVAL);
2N/A }
2N/A
2N/A ndr_svc_binding_pool_init(&np->np_binding, np->np_binding_pool,
2N/A NDR_N_BINDING_POOL);
2N/A
2N/A (void) mutex_unlock(&ndr_pipe_lock);
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Release the context associated with a fid when an opipe is closed.
2N/A */
2N/Aint
2N/Andr_pipe_close(int fid)
2N/A{
2N/A ndr_pipe_t *np;
2N/A
2N/A (void) mutex_lock(&ndr_pipe_lock);
2N/A
2N/A if ((np = ndr_pipe_lookup(fid)) == NULL) {
2N/A (void) mutex_unlock(&ndr_pipe_lock);
2N/A return (ENOENT);
2N/A }
2N/A
2N/A /*
2N/A * Release twice: once for the lookup above
2N/A * and again to close the fid.
2N/A */
2N/A ndr_pipe_release(np);
2N/A ndr_pipe_release(np);
2N/A (void) mutex_unlock(&ndr_pipe_lock);
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Write RPC request data to the input stream. Input data is buffered
2N/A * until the response is requested.
2N/A */
2N/Aint
2N/Andr_pipe_write(int fid, uint8_t *buf, uint32_t len)
2N/A{
2N/A ndr_pipe_t *np;
2N/A ssize_t nbytes;
2N/A int rc;
2N/A
2N/A if (len == 0)
2N/A return (0);
2N/A
2N/A (void) mutex_lock(&ndr_pipe_lock);
2N/A
2N/A if ((np = ndr_pipe_lookup(fid)) == NULL) {
2N/A (void) mutex_unlock(&ndr_pipe_lock);
2N/A return (ENOENT);
2N/A }
2N/A
2N/A if ((rc = ndr_pipe_grow(np, len)) != 0) {
2N/A (void) mutex_unlock(&ndr_pipe_lock);
2N/A return (rc);
2N/A }
2N/A
2N/A nbytes = ndr_uiomove((caddr_t)buf, len, UIO_READ, &np->np_uio);
2N/A
2N/A ndr_pipe_release(np);
2N/A (void) mutex_unlock(&ndr_pipe_lock);
2N/A return ((nbytes == len) ? 0 : EIO);
2N/A}
2N/A
2N/A/*
2N/A * Read RPC response data.
2N/A */
2N/Aint
2N/Andr_pipe_read(int fid, uint8_t *buf, uint32_t *len, uint32_t *resid)
2N/A{
2N/A ndr_pipe_t *np;
2N/A ssize_t nbytes = *len;
2N/A
2N/A if (nbytes == 0) {
2N/A *resid = 0;
2N/A return (0);
2N/A }
2N/A
2N/A (void) mutex_lock(&ndr_pipe_lock);
2N/A if ((np = ndr_pipe_lookup(fid)) == NULL) {
2N/A (void) mutex_unlock(&ndr_pipe_lock);
2N/A return (ENOENT);
2N/A }
2N/A (void) mutex_unlock(&ndr_pipe_lock);
2N/A
2N/A *len = ndr_uiomove((caddr_t)buf, nbytes, UIO_WRITE, &np->np_frags.uio);
2N/A *resid = np->np_frags.uio.uio_resid;
2N/A
2N/A if (*resid == 0) {
2N/A /*
2N/A * Nothing left, cleanup the output stream.
2N/A */
2N/A ndr_pipe_flush(np);
2N/A }
2N/A
2N/A (void) mutex_lock(&ndr_pipe_lock);
2N/A ndr_pipe_release(np);
2N/A (void) mutex_unlock(&ndr_pipe_lock);
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * If the input stream contains an RPC request, process the RPC transaction,
2N/A * which will place the RPC response in the output (frags) stream.
2N/A *
2N/A * arg is freed here; it must have been allocated by malloc().
2N/A */
2N/Avoid *
2N/Andr_pipe_transact(void *arg)
2N/A{
2N/A uint32_t *tmp = (uint32_t *)arg;
2N/A uint32_t fid;
2N/A ndr_pipe_t *np;
2N/A
2N/A if (arg == NULL)
2N/A return (NULL);
2N/A
2N/A fid = *tmp;
2N/A
2N/A (void) mutex_lock(&ndr_pipe_lock);
2N/A if ((np = ndr_pipe_lookup(fid)) == NULL) {
2N/A (void) mutex_unlock(&ndr_pipe_lock);
2N/A (void) smb_kmod_event_notify(fid);
2N/A free(arg);
2N/A return (NULL);
2N/A }
2N/A (void) mutex_unlock(&ndr_pipe_lock);
2N/A
2N/A if (ndr_pipe_process(np) != 0)
2N/A ndr_pipe_flush(np);
2N/A
2N/A (void) mutex_lock(&ndr_pipe_lock);
2N/A ndr_pipe_release(np);
2N/A (void) mutex_unlock(&ndr_pipe_lock);
2N/A (void) smb_kmod_event_notify(fid);
2N/A free(arg);
2N/A return (NULL);
2N/A}
2N/A
2N/A/*
2N/A * Process a server-side RPC request.
2N/A */
2N/Astatic int
2N/Andr_pipe_process(ndr_pipe_t *np)
2N/A{
2N/A ndr_xa_t *mxa;
2N/A ndr_stream_t *recv_nds;
2N/A ndr_stream_t *send_nds;
2N/A char *data;
2N/A int datalen;
2N/A int rc;
2N/A
2N/A data = np->np_buf;
2N/A datalen = np->np_uio.uio_offset;
2N/A
2N/A if (datalen == 0)
2N/A return (0);
2N/A
2N/A if ((mxa = (ndr_xa_t *)malloc(sizeof (ndr_xa_t))) == NULL)
2N/A return (ENOMEM);
2N/A
2N/A bzero(mxa, sizeof (ndr_xa_t));
2N/A mxa->fid = np->np_fid;
2N/A mxa->pipe = np;
2N/A mxa->binding_list = np->np_binding;
2N/A
2N/A if ((mxa->heap = ndr_heap_create()) == NULL) {
2N/A free(mxa);
2N/A return (ENOMEM);
2N/A }
2N/A
2N/A recv_nds = &mxa->recv_nds;
2N/A rc = nds_initialize(recv_nds, datalen, NDR_MODE_CALL_RECV, mxa->heap);
2N/A if (rc != 0) {
2N/A ndr_heap_destroy(mxa->heap);
2N/A free(mxa);
2N/A return (ENOMEM);
2N/A }
2N/A
2N/A /*
2N/A * Copy the input data and reset the input stream.
2N/A */
2N/A bcopy(data, recv_nds->pdu_base_addr, datalen);
2N/A ndr_pipe_rewind(np);
2N/A
2N/A send_nds = &mxa->send_nds;
2N/A rc = nds_initialize(send_nds, 0, NDR_MODE_RETURN_SEND, mxa->heap);
2N/A if (rc != 0) {
2N/A nds_destruct(&mxa->recv_nds);
2N/A ndr_heap_destroy(mxa->heap);
2N/A free(mxa);
2N/A return (ENOMEM);
2N/A }
2N/A
2N/A (void) ndr_svc_process(mxa);
2N/A
2N/A nds_finalize(send_nds, &np->np_frags);
2N/A nds_destruct(&mxa->recv_nds);
2N/A nds_destruct(&mxa->send_nds);
2N/A ndr_heap_destroy(mxa->heap);
2N/A free(mxa);
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Must be called with ndr_pipe_lock held.
2N/A */
2N/Astatic ndr_pipe_t *
2N/Andr_pipe_lookup(int fid)
2N/A{
2N/A ndr_pipe_t *np;
2N/A int i;
2N/A
2N/A for (i = 0; i < NDR_PIPE_MAX; ++i) {
2N/A np = &ndr_pipe_table[i];
2N/A
2N/A if (np->np_fid == fid) {
2N/A if (np->np_refcnt == 0)
2N/A return (NULL);
2N/A
2N/A np->np_refcnt++;
2N/A return (np);
2N/A }
2N/A }
2N/A
2N/A return (NULL);
2N/A}
2N/A
2N/A/*
2N/A * Must be called with ndr_pipe_lock held.
2N/A */
2N/Astatic void
2N/Andr_pipe_release(ndr_pipe_t *np)
2N/A{
2N/A np->np_refcnt--;
2N/A ndr_pipe_deallocate(np);
2N/A}
2N/A
2N/A/*
2N/A * Must be called with ndr_pipe_lock held.
2N/A */
2N/Astatic ndr_pipe_t *
2N/Andr_pipe_allocate(int fid)
2N/A{
2N/A ndr_pipe_t *np = NULL;
2N/A int i;
2N/A
2N/A for (i = 0; i < NDR_PIPE_MAX; ++i) {
2N/A np = &ndr_pipe_table[i];
2N/A
2N/A if (np->np_fid == 0) {
2N/A bzero(np, sizeof (ndr_pipe_t));
2N/A
2N/A if ((np->np_buf = malloc(NDR_PIPE_BUFSZ)) == NULL)
2N/A return (NULL);
2N/A
2N/A ndr_pipe_rewind(np);
2N/A np->np_fid = fid;
2N/A np->np_refcnt = 1;
2N/A return (np);
2N/A }
2N/A }
2N/A
2N/A return (NULL);
2N/A}
2N/A
2N/A/*
2N/A * If the desired space exceeds the current pipe size, try to expand
2N/A * the pipe. Leave the current pipe intact if the realloc fails.
2N/A *
2N/A * Must be called with ndr_pipe_lock held.
2N/A */
2N/Astatic int
2N/Andr_pipe_grow(ndr_pipe_t *np, size_t desired)
2N/A{
2N/A char *newbuf;
2N/A size_t current;
2N/A size_t required;
2N/A
2N/A required = np->np_uio.uio_offset + desired;
2N/A current = np->np_uio.uio_offset + np->np_uio.uio_resid;
2N/A
2N/A if (required <= current)
2N/A return (0);
2N/A
2N/A if (required > NDR_PIPE_BUFMAX) {
2N/A smb_tracef("ndr_pipe_grow: required=%d, max=%d (ENOSPC)",
2N/A required, NDR_PIPE_BUFMAX);
2N/A return (ENOSPC);
2N/A }
2N/A
2N/A required = NDR_ALIGN_BUF(required);
2N/A if (required > NDR_PIPE_BUFMAX)
2N/A required = NDR_PIPE_BUFMAX;
2N/A
2N/A if ((newbuf = realloc(np->np_buf, required)) == NULL) {
2N/A smb_tracef("ndr_pipe_grow: realloc failed (ENOMEM)");
2N/A return (ENOMEM);
2N/A }
2N/A
2N/A np->np_buf = newbuf;
2N/A np->np_iov.iov_base = np->np_buf + np->np_uio.uio_offset;
2N/A np->np_uio.uio_resid += desired;
2N/A np->np_iov.iov_len += desired;
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Must be called with ndr_pipe_lock held.
2N/A */
2N/Astatic void
2N/Andr_pipe_deallocate(ndr_pipe_t *np)
2N/A{
2N/A if (np->np_refcnt == 0) {
2N/A /*
2N/A * Ensure that there are no RPC service policy handles
2N/A * (associated with this fid) left around.
2N/A */
2N/A ndr_hdclose(np->np_fid);
2N/A
2N/A ndr_pipe_rewind(np);
2N/A ndr_pipe_flush(np);
2N/A free(np->np_buf);
2N/A free(np->np_user.ui_domain);
2N/A free(np->np_user.ui_account);
2N/A free(np->np_user.ui_workstation);
2N/A free(np->np_user.ui_posix_name);
2N/A bzero(np, sizeof (ndr_pipe_t));
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * Rewind the input data stream, ready for the next write.
2N/A */
2N/Astatic void
2N/Andr_pipe_rewind(ndr_pipe_t *np)
2N/A{
2N/A np->np_uio.uio_iov = &np->np_iov;
2N/A np->np_uio.uio_iovcnt = 1;
2N/A np->np_uio.uio_offset = 0;
2N/A np->np_uio.uio_segflg = UIO_USERSPACE;
2N/A np->np_uio.uio_resid = NDR_PIPE_BUFSZ;
2N/A np->np_iov.iov_base = np->np_buf;
2N/A np->np_iov.iov_len = NDR_PIPE_BUFSZ;
2N/A}
2N/A
2N/A/*
2N/A * Flush the output data stream.
2N/A */
2N/Astatic void
2N/Andr_pipe_flush(ndr_pipe_t *np)
2N/A{
2N/A ndr_frag_t *frag;
2N/A
2N/A while ((frag = np->np_frags.head) != NULL) {
2N/A np->np_frags.head = frag->next;
2N/A free(frag);
2N/A }
2N/A
2N/A free(np->np_frags.iov);
2N/A bzero(&np->np_frags, sizeof (ndr_fraglist_t));
2N/A}
2N/A
2N/A/*
2N/A * Check whether or not the specified user has administrator privileges,
2N/A * i.e. is a member of Domain Admins or Administrators.
2N/A * Returns true if the user is an administrator, otherwise returns false.
2N/A */
2N/Aboolean_t
2N/Andr_is_admin(ndr_xa_t *xa)
2N/A{
2N/A smb_netuserinfo_t *ctx = &xa->pipe->np_user;
2N/A
2N/A return (ctx->ui_flags & SMB_ATF_ADMIN);
2N/A}
2N/A
2N/A/*
2N/A * Check whether or not the specified user has power-user privileges,
2N/A * i.e. is a member of Domain Admins, Administrators or Power Users.
2N/A * This is typically required for operations such as managing shares.
2N/A * Returns true if the user is a power user, otherwise returns false.
2N/A */
2N/Aboolean_t
2N/Andr_is_poweruser(ndr_xa_t *xa)
2N/A{
2N/A smb_netuserinfo_t *ctx = &xa->pipe->np_user;
2N/A
2N/A return ((ctx->ui_flags & SMB_ATF_ADMIN) ||
2N/A (ctx->ui_flags & SMB_ATF_POWERUSER));
2N/A}
2N/A
2N/Aint32_t
2N/Andr_native_os(ndr_xa_t *xa)
2N/A{
2N/A smb_netuserinfo_t *ctx = &xa->pipe->np_user;
2N/A
2N/A return (ctx->ui_native_os);
2N/A}
2N/A
2N/A/*
2N/A * This is the entry point for all server-side RPC processing.
2N/A * It is assumed that the PDU has already been received.
2N/A */
2N/Astatic int
2N/Andr_svc_process(ndr_xa_t *mxa)
2N/A{
2N/A ndr_common_header_t *hdr = &mxa->recv_hdr.common_hdr;
2N/A ndr_stream_t *nds = &mxa->recv_nds;
2N/A unsigned long saved_offset;
2N/A unsigned long saved_size;
2N/A int rc;
2N/A
2N/A rc = ndr_decode_pdu_hdr(mxa);
2N/A if (!NDR_DRC_IS_OK(rc))
2N/A return (-1);
2N/A
2N/A (void) ndr_reply_prepare_hdr(mxa);
2N/A
2N/A switch (mxa->ptype) {
2N/A case NDR_PTYPE_BIND:
2N/A rc = ndr_svc_bind(mxa);
2N/A break;
2N/A
2N/A case NDR_PTYPE_REQUEST:
2N/A if (!NDR_IS_FIRST_FRAG(hdr->pfc_flags)) {
2N/A ndr_show_hdr(hdr);
2N/A rc = NDR_DRC_FAULT_DECODE_FAILED;
2N/A goto ndr_svc_process_fault;
2N/A }
2N/A
2N/A if (!NDR_IS_LAST_FRAG(hdr->pfc_flags)) {
2N/A /*
2N/A * Multi-fragment request. Preserve the PDU scan
2N/A * offset and size during defrag so that we can
2N/A * continue as if we had received contiguous data.
2N/A */
2N/A saved_offset = nds->pdu_scan_offset;
2N/A saved_size = nds->pdu_size;
2N/A
2N/A nds->pdu_scan_offset = hdr->frag_length;
2N/A nds->pdu_size = nds->pdu_max_size;
2N/A
2N/A rc = ndr_svc_defrag(mxa);
2N/A if (NDR_DRC_IS_FAULT(rc)) {
2N/A ndr_show_hdr(hdr);
2N/A nds_show_state(nds);
2N/A goto ndr_svc_process_fault;
2N/A }
2N/A
2N/A nds->pdu_scan_offset = saved_offset;
2N/A nds->pdu_size = saved_size;
2N/A }
2N/A
2N/A rc = ndr_svc_request(mxa);
2N/A break;
2N/A
2N/A case NDR_PTYPE_ALTER_CONTEXT:
2N/A rc = ndr_svc_alter_context(mxa);
2N/A break;
2N/A
2N/A default:
2N/A rc = NDR_DRC_FAULT_RPCHDR_PTYPE_INVALID;
2N/A break;
2N/A }
2N/A
2N/Andr_svc_process_fault:
2N/A if (NDR_DRC_IS_FAULT(rc))
2N/A ndr_reply_fault(mxa, rc);
2N/A
2N/A (void) ndr_build_reply(mxa);
2N/A return (rc);
2N/A}
2N/A
2N/A/*
2N/A * Remove RPC fragment headers from the received data stream.
2N/A * The first fragment has already been accounted for before this call.
2N/A *
2N/A * NDR stream on entry:
2N/A *
2N/A * |<-- frag 2 -->|<-- frag 3 -->| ... |<- last frag ->|
2N/A *
2N/A * +-----+--------+-----+--------+-----+-----+---------+
2N/A * | hdr | data | hdr | data | ... | hdr | data |
2N/A * +-----+--------+-----+--------+-----+-----+---------+
2N/A *
2N/A * NDR stream on return:
2N/A *
2N/A * +----------------------------------+
2N/A * | data |
2N/A * +----------------------------------+
2N/A */
2N/Astatic int
2N/Andr_svc_defrag(ndr_xa_t *mxa)
2N/A{
2N/A ndr_stream_t *nds = &mxa->recv_nds;
2N/A ndr_common_header_t frag_hdr;
2N/A int frag_size;
2N/A int last_frag;
2N/A
2N/A do {
2N/A ndr_decode_frag_hdr(nds, &frag_hdr);
2N/A ndr_show_hdr(&frag_hdr);
2N/A
2N/A if (NDR_IS_FIRST_FRAG(frag_hdr.pfc_flags))
2N/A return (NDR_DRC_FAULT_DECODE_FAILED);
2N/A
2N/A last_frag = NDR_IS_LAST_FRAG(frag_hdr.pfc_flags);
2N/A frag_size = frag_hdr.frag_length;
2N/A
2N/A if (frag_size > (nds->pdu_size - nds->pdu_scan_offset))
2N/A return (NDR_DRC_FAULT_DECODE_FAILED);
2N/A
2N/A ndr_remove_frag_hdr(nds);
2N/A nds->pdu_scan_offset += frag_size - NDR_RSP_HDR_SIZE;
2N/A } while (!last_frag);
2N/A
2N/A return (NDR_DRC_OK);
2N/A}
2N/A
2N/A/*
2N/A * Multiple p_cont_elem[]s, multiple transfer_syntaxes[] and multiple
2N/A * p_results[] not supported.
2N/A */
2N/Astatic int
2N/Andr_svc_bind(ndr_xa_t *mxa)
2N/A{
2N/A ndr_p_cont_list_t *cont_list;
2N/A ndr_p_result_list_t *result_list;
2N/A ndr_p_result_t *result;
2N/A unsigned p_cont_id;
2N/A ndr_binding_t *mbind;
2N/A ndr_uuid_t *as_uuid;
2N/A ndr_uuid_t *ts_uuid;
2N/A int as_vers;
2N/A int ts_vers;
2N/A ndr_service_t *msvc;
2N/A int rc;
2N/A ndr_port_any_t *sec_addr;
2N/A
2N/A /* acquire targets */
2N/A cont_list = &mxa->recv_hdr.bind_hdr.p_context_elem;
2N/A result_list = &mxa->send_hdr.bind_ack_hdr.p_result_list;
2N/A result = &result_list->p_results[0];
2N/A
2N/A /*
2N/A * Set up temporary secondary address port.
2N/A * We will correct this later (below).
2N/A */
2N/A sec_addr = &mxa->send_hdr.bind_ack_hdr.sec_addr;
2N/A sec_addr->length = 13;
2N/A (void) strcpy((char *)sec_addr->port_spec, "\\PIPE\\ntsvcs");
2N/A
2N/A result_list->n_results = 1;
2N/A result_list->reserved = 0;
2N/A result_list->reserved2 = 0;
2N/A result->result = NDR_PCDR_ACCEPTANCE;
2N/A result->reason = 0;
2N/A bzero(&result->transfer_syntax, sizeof (result->transfer_syntax));
2N/A
2N/A /* sanity check */
2N/A if (cont_list->n_context_elem != 1 ||
2N/A cont_list->p_cont_elem[0].n_transfer_syn != 1) {
2N/A ndo_trace("ndr_svc_bind: warning: multiple p_cont_elem");
2N/A }
2N/A
2N/A p_cont_id = cont_list->p_cont_elem[0].p_cont_id;
2N/A
2N/A if ((mbind = ndr_svc_find_binding(mxa, p_cont_id)) != NULL) {
2N/A /*
2N/A * Duplicate presentation context id.
2N/A */
2N/A ndo_trace("ndr_svc_bind: duplicate binding");
2N/A return (NDR_DRC_FAULT_BIND_PCONT_BUSY);
2N/A }
2N/A
2N/A if ((mbind = ndr_svc_new_binding(mxa)) == NULL) {
2N/A /*
2N/A * No free binding slot
2N/A */
2N/A result->result = NDR_PCDR_PROVIDER_REJECTION;
2N/A result->reason = NDR_PPR_LOCAL_LIMIT_EXCEEDED;
2N/A ndo_trace("ndr_svc_bind: no resources");
2N/A return (NDR_DRC_OK);
2N/A }
2N/A
2N/A as_uuid = &cont_list->p_cont_elem[0].abstract_syntax.if_uuid;
2N/A as_vers = cont_list->p_cont_elem[0].abstract_syntax.if_version;
2N/A
2N/A ts_uuid = &cont_list->p_cont_elem[0].transfer_syntaxes[0].if_uuid;
2N/A ts_vers = cont_list->p_cont_elem[0].transfer_syntaxes[0].if_version;
2N/A
2N/A msvc = ndr_svc_lookup_uuid(as_uuid, as_vers, ts_uuid, ts_vers);
2N/A if (msvc == NULL) {
2N/A result->result = NDR_PCDR_PROVIDER_REJECTION;
2N/A result->reason = NDR_PPR_ABSTRACT_SYNTAX_NOT_SUPPORTED;
2N/A return (NDR_DRC_OK);
2N/A }
2N/A
2N/A /*
2N/A * We can now use the correct secondary address port.
2N/A */
2N/A sec_addr = &mxa->send_hdr.bind_ack_hdr.sec_addr;
2N/A sec_addr->length = strlen(msvc->sec_addr_port) + 1;
2N/A (void) strlcpy((char *)sec_addr->port_spec, msvc->sec_addr_port,
2N/A NDR_PORT_ANY_MAX_PORT_SPEC);
2N/A
2N/A mbind->p_cont_id = p_cont_id;
2N/A mbind->which_side = NDR_BIND_SIDE_SERVER;
2N/A /* mbind->context set by app */
2N/A mbind->service = msvc;
2N/A mbind->instance_specific = 0;
2N/A
2N/A mxa->binding = mbind;
2N/A
2N/A if (msvc->bind_req) {
2N/A /*
2N/A * Call the service-specific bind() handler. If
2N/A * this fails, we shouild send a specific error
2N/A * on the bind ack.
2N/A */
2N/A rc = (msvc->bind_req)(mxa);
2N/A if (NDR_DRC_IS_FAULT(rc)) {
2N/A mbind->service = 0; /* free binding slot */
2N/A mbind->which_side = 0;
2N/A mbind->p_cont_id = 0;
2N/A mbind->instance_specific = 0;
2N/A return (rc);
2N/A }
2N/A }
2N/A
2N/A result->transfer_syntax =
2N/A cont_list->p_cont_elem[0].transfer_syntaxes[0];
2N/A
2N/A return (NDR_DRC_BINDING_MADE);
2N/A}
2N/A
2N/A/*
2N/A * ndr_svc_alter_context
2N/A *
2N/A * The alter context request is used to request additional presentation
2N/A * context for another interface and/or version. It is very similar to
2N/A * a bind request.
2N/A */
2N/Astatic int
2N/Andr_svc_alter_context(ndr_xa_t *mxa)
2N/A{
2N/A ndr_p_result_list_t *result_list;
2N/A ndr_p_result_t *result;
2N/A ndr_p_cont_list_t *cont_list;
2N/A ndr_binding_t *mbind;
2N/A ndr_service_t *msvc;
2N/A unsigned p_cont_id;
2N/A ndr_uuid_t *as_uuid;
2N/A ndr_uuid_t *ts_uuid;
2N/A int as_vers;
2N/A int ts_vers;
2N/A ndr_port_any_t *sec_addr;
2N/A
2N/A result_list = &mxa->send_hdr.alter_context_rsp_hdr.p_result_list;
2N/A result_list->n_results = 1;
2N/A result_list->reserved = 0;
2N/A result_list->reserved2 = 0;
2N/A
2N/A result = &result_list->p_results[0];
2N/A result->result = NDR_PCDR_ACCEPTANCE;
2N/A result->reason = 0;
2N/A bzero(&result->transfer_syntax, sizeof (result->transfer_syntax));
2N/A
2N/A cont_list = &mxa->recv_hdr.alter_context_hdr.p_context_elem;
2N/A p_cont_id = cont_list->p_cont_elem[0].p_cont_id;
2N/A
2N/A if (ndr_svc_find_binding(mxa, p_cont_id) != NULL)
2N/A return (NDR_DRC_FAULT_BIND_PCONT_BUSY);
2N/A
2N/A if ((mbind = ndr_svc_new_binding(mxa)) == NULL) {
2N/A result->result = NDR_PCDR_PROVIDER_REJECTION;
2N/A result->reason = NDR_PPR_LOCAL_LIMIT_EXCEEDED;
2N/A return (NDR_DRC_OK);
2N/A }
2N/A
2N/A as_uuid = &cont_list->p_cont_elem[0].abstract_syntax.if_uuid;
2N/A as_vers = cont_list->p_cont_elem[0].abstract_syntax.if_version;
2N/A
2N/A ts_uuid = &cont_list->p_cont_elem[0].transfer_syntaxes[0].if_uuid;
2N/A ts_vers = cont_list->p_cont_elem[0].transfer_syntaxes[0].if_version;
2N/A
2N/A msvc = ndr_svc_lookup_uuid(as_uuid, as_vers, ts_uuid, ts_vers);
2N/A if (msvc == NULL) {
2N/A result->result = NDR_PCDR_PROVIDER_REJECTION;
2N/A result->reason = NDR_PPR_ABSTRACT_SYNTAX_NOT_SUPPORTED;
2N/A return (NDR_DRC_OK);
2N/A }
2N/A
2N/A mbind->p_cont_id = p_cont_id;
2N/A mbind->which_side = NDR_BIND_SIDE_SERVER;
2N/A /* mbind->context set by app */
2N/A mbind->service = msvc;
2N/A mbind->instance_specific = 0;
2N/A mxa->binding = mbind;
2N/A
2N/A sec_addr = &mxa->send_hdr.alter_context_rsp_hdr.sec_addr;
2N/A sec_addr->length = 0;
2N/A bzero(sec_addr->port_spec, NDR_PORT_ANY_MAX_PORT_SPEC);
2N/A
2N/A result->transfer_syntax =
2N/A cont_list->p_cont_elem[0].transfer_syntaxes[0];
2N/A
2N/A return (NDR_DRC_BINDING_MADE);
2N/A}
2N/A
2N/Astatic int
2N/Andr_svc_request(ndr_xa_t *mxa)
2N/A{
2N/A ndr_binding_t *mbind;
2N/A ndr_service_t *msvc;
2N/A unsigned p_cont_id;
2N/A int rc;
2N/A
2N/A mxa->opnum = mxa->recv_hdr.request_hdr.opnum;
2N/A p_cont_id = mxa->recv_hdr.request_hdr.p_cont_id;
2N/A
2N/A if ((mbind = ndr_svc_find_binding(mxa, p_cont_id)) == NULL)
2N/A return (NDR_DRC_FAULT_REQUEST_PCONT_INVALID);
2N/A
2N/A mxa->binding = mbind;
2N/A msvc = mbind->service;
2N/A
2N/A /*
2N/A * Make room for the response hdr.
2N/A */
2N/A mxa->send_nds.pdu_scan_offset = NDR_RSP_HDR_SIZE;
2N/A
2N/A if (msvc->call_stub)
2N/A rc = (*msvc->call_stub)(mxa);
2N/A else
2N/A rc = ndr_generic_call_stub(mxa);
2N/A
2N/A if (NDR_DRC_IS_FAULT(rc)) {
2N/A ndo_printf(0, 0, "%s[0x%02x]: 0x%04x",
2N/A msvc->name, mxa->opnum, rc);
2N/A }
2N/A
2N/A return (rc);
2N/A}
2N/A
2N/A/*
2N/A * The transaction and the two nds streams use the same heap, which
2N/A * should already exist at this point. The heap will also be available
2N/A * to the stub.
2N/A */
2N/Aint
2N/Andr_generic_call_stub(ndr_xa_t *mxa)
2N/A{
2N/A ndr_binding_t *mbind = mxa->binding;
2N/A ndr_service_t *msvc = mbind->service;
2N/A ndr_typeinfo_t *intf_ti = msvc->interface_ti;
2N/A ndr_stub_table_t *ste;
2N/A int opnum = mxa->opnum;
2N/A unsigned p_len = intf_ti->c_size_fixed_part;
2N/A char *param;
2N/A int rc;
2N/A
2N/A if (mxa->heap == NULL) {
2N/A ndo_printf(0, 0, "%s[0x%02x]: no heap", msvc->name, opnum);
2N/A return (NDR_DRC_FAULT_OUT_OF_MEMORY);
2N/A }
2N/A
2N/A if ((ste = ndr_svc_find_stub(msvc, opnum)) == NULL) {
2N/A ndo_printf(0, 0, "%s[0x%02x]: invalid opnum",
2N/A msvc->name, opnum);
2N/A return (NDR_DRC_FAULT_REQUEST_OPNUM_INVALID);
2N/A }
2N/A
2N/A if ((param = ndr_heap_malloc(mxa->heap, p_len)) == NULL)
2N/A return (NDR_DRC_FAULT_OUT_OF_MEMORY);
2N/A
2N/A bzero(param, p_len);
2N/A
2N/A rc = ndr_decode_call(mxa, param);
2N/A if (!NDR_DRC_IS_OK(rc))
2N/A return (rc);
2N/A
2N/A rc = (*ste->func)(param, mxa);
2N/A if (rc == NDR_DRC_OK)
2N/A rc = ndr_encode_return(mxa, param);
2N/A
2N/A return (rc);
2N/A}
2N/A
2N/A/*
2N/A * We can perform some initial setup of the response header here.
2N/A * We also need to cache some of the information from the bind
2N/A * negotiation for use during subsequent RPC calls.
2N/A */
2N/Astatic void
2N/Andr_reply_prepare_hdr(ndr_xa_t *mxa)
2N/A{
2N/A ndr_common_header_t *rhdr = &mxa->recv_hdr.common_hdr;
2N/A ndr_common_header_t *hdr = &mxa->send_hdr.common_hdr;
2N/A
2N/A hdr->rpc_vers = 5;
2N/A hdr->rpc_vers_minor = 0;
2N/A hdr->pfc_flags = NDR_PFC_FIRST_FRAG + NDR_PFC_LAST_FRAG;
2N/A hdr->packed_drep = rhdr->packed_drep;
2N/A hdr->frag_length = 0;
2N/A hdr->auth_length = 0;
2N/A hdr->call_id = rhdr->call_id;
2N/A#ifdef _BIG_ENDIAN
2N/A hdr->packed_drep.intg_char_rep = NDR_REPLAB_CHAR_ASCII
2N/A | NDR_REPLAB_INTG_BIG_ENDIAN;
2N/A#else
2N/A hdr->packed_drep.intg_char_rep = NDR_REPLAB_CHAR_ASCII
2N/A | NDR_REPLAB_INTG_LITTLE_ENDIAN;
2N/A#endif
2N/A
2N/A switch (mxa->ptype) {
2N/A case NDR_PTYPE_BIND:
2N/A hdr->ptype = NDR_PTYPE_BIND_ACK;
2N/A mxa->send_hdr.bind_ack_hdr.max_xmit_frag =
2N/A mxa->recv_hdr.bind_hdr.max_xmit_frag;
2N/A mxa->send_hdr.bind_ack_hdr.max_recv_frag =
2N/A mxa->recv_hdr.bind_hdr.max_recv_frag;
2N/A mxa->send_hdr.bind_ack_hdr.assoc_group_id =
2N/A mxa->recv_hdr.bind_hdr.assoc_group_id;
2N/A
2N/A if (mxa->send_hdr.bind_ack_hdr.assoc_group_id == 0)
2N/A mxa->send_hdr.bind_ack_hdr.assoc_group_id = time(0);
2N/A
2N/A /*
2N/A * Save the maximum fragment sizes
2N/A * for use with subsequent requests.
2N/A */
2N/A mxa->pipe->np_max_xmit_frag =
2N/A mxa->recv_hdr.bind_hdr.max_xmit_frag;
2N/A mxa->pipe->np_max_recv_frag =
2N/A mxa->recv_hdr.bind_hdr.max_recv_frag;
2N/A break;
2N/A
2N/A case NDR_PTYPE_REQUEST:
2N/A hdr->ptype = NDR_PTYPE_RESPONSE;
2N/A /* mxa->send_hdr.response_hdr.alloc_hint */
2N/A mxa->send_hdr.response_hdr.p_cont_id =
2N/A mxa->recv_hdr.request_hdr.p_cont_id;
2N/A mxa->send_hdr.response_hdr.cancel_count = 0;
2N/A mxa->send_hdr.response_hdr.reserved = 0;
2N/A break;
2N/A
2N/A case NDR_PTYPE_ALTER_CONTEXT:
2N/A hdr->ptype = NDR_PTYPE_ALTER_CONTEXT_RESP;
2N/A /*
2N/A * The max_xmit_frag, max_recv_frag and assoc_group_id are
2N/A * ignored by the client but it's useful to fill them in.
2N/A */
2N/A mxa->send_hdr.alter_context_rsp_hdr.max_xmit_frag =
2N/A mxa->recv_hdr.alter_context_hdr.max_xmit_frag;
2N/A mxa->send_hdr.alter_context_rsp_hdr.max_recv_frag =
2N/A mxa->recv_hdr.alter_context_hdr.max_recv_frag;
2N/A mxa->send_hdr.alter_context_rsp_hdr.assoc_group_id =
2N/A mxa->recv_hdr.alter_context_hdr.assoc_group_id;
2N/A break;
2N/A
2N/A default:
2N/A hdr->ptype = 0xFF;
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * Signal an RPC fault. The stream is reset and we overwrite whatever
2N/A * was in the response header with the fault information.
2N/A */
2N/Astatic void
2N/Andr_reply_fault(ndr_xa_t *mxa, unsigned long drc)
2N/A{
2N/A ndr_common_header_t *rhdr = &mxa->recv_hdr.common_hdr;
2N/A ndr_common_header_t *hdr = &mxa->send_hdr.common_hdr;
2N/A ndr_stream_t *nds = &mxa->send_nds;
2N/A unsigned long fault_status;
2N/A
2N/A NDS_RESET(nds);
2N/A
2N/A hdr->rpc_vers = 5;
2N/A hdr->rpc_vers_minor = 0;
2N/A hdr->pfc_flags = NDR_PFC_FIRST_FRAG + NDR_PFC_LAST_FRAG;
2N/A hdr->packed_drep = rhdr->packed_drep;
2N/A hdr->frag_length = sizeof (mxa->send_hdr.fault_hdr);
2N/A hdr->auth_length = 0;
2N/A hdr->call_id = rhdr->call_id;
2N/A#ifdef _BIG_ENDIAN
2N/A hdr->packed_drep.intg_char_rep = NDR_REPLAB_CHAR_ASCII
2N/A | NDR_REPLAB_INTG_BIG_ENDIAN;
2N/A#else
2N/A hdr->packed_drep.intg_char_rep = NDR_REPLAB_CHAR_ASCII
2N/A | NDR_REPLAB_INTG_LITTLE_ENDIAN;
2N/A#endif
2N/A
2N/A switch (drc & NDR_DRC_MASK_SPECIFIER) {
2N/A case NDR_DRC_FAULT_OUT_OF_MEMORY:
2N/A case NDR_DRC_FAULT_ENCODE_TOO_BIG:
2N/A fault_status = NDR_FAULT_NCA_OUT_ARGS_TOO_BIG;
2N/A break;
2N/A
2N/A case NDR_DRC_FAULT_REQUEST_PCONT_INVALID:
2N/A fault_status = NDR_FAULT_NCA_INVALID_PRES_CONTEXT_ID;
2N/A break;
2N/A
2N/A case NDR_DRC_FAULT_REQUEST_OPNUM_INVALID:
2N/A fault_status = NDR_FAULT_NCA_OP_RNG_ERROR;
2N/A break;
2N/A
2N/A case NDR_DRC_FAULT_DECODE_FAILED:
2N/A case NDR_DRC_FAULT_ENCODE_FAILED:
2N/A fault_status = NDR_FAULT_NCA_PROTO_ERROR;
2N/A break;
2N/A
2N/A default:
2N/A fault_status = NDR_FAULT_NCA_UNSPEC_REJECT;
2N/A break;
2N/A }
2N/A
2N/A mxa->send_hdr.fault_hdr.common_hdr.ptype = NDR_PTYPE_FAULT;
2N/A mxa->send_hdr.fault_hdr.status = fault_status;
2N/A mxa->send_hdr.response_hdr.alloc_hint = hdr->frag_length;
2N/A}
2N/A
2N/A/*
2N/A * Note that the frag_length for bind ack and alter context is
2N/A * non-standard.
2N/A */
2N/Astatic int
2N/Andr_build_reply(ndr_xa_t *mxa)
2N/A{
2N/A ndr_common_header_t *hdr = &mxa->send_hdr.common_hdr;
2N/A ndr_stream_t *nds = &mxa->send_nds;
2N/A uint8_t *pdu_buf;
2N/A unsigned long pdu_size;
2N/A unsigned long frag_size;
2N/A unsigned long pdu_data_size;
2N/A unsigned long frag_data_size;
2N/A
2N/A frag_size = NDR_SERVER_FRAGSZ;
2N/A pdu_size = nds->pdu_size;
2N/A pdu_buf = nds->pdu_base_addr;
2N/A
2N/A if (pdu_size <= frag_size) {
2N/A /*
2N/A * Single fragment response. The PDU size may be zero
2N/A * here (i.e. bind or fault response). So don't make
2N/A * any assumptions about it until after the header is
2N/A * encoded.
2N/A */
2N/A switch (hdr->ptype) {
2N/A case NDR_PTYPE_BIND_ACK:
2N/A hdr->frag_length = ndr_bind_ack_hdr_size(mxa);
2N/A break;
2N/A
2N/A case NDR_PTYPE_FAULT:
2N/A /* already setup */
2N/A break;
2N/A
2N/A case NDR_PTYPE_RESPONSE:
2N/A hdr->frag_length = pdu_size;
2N/A mxa->send_hdr.response_hdr.alloc_hint =
2N/A hdr->frag_length;
2N/A break;
2N/A
2N/A case NDR_PTYPE_ALTER_CONTEXT_RESP:
2N/A hdr->frag_length = ndr_alter_context_rsp_hdr_size();
2N/A break;
2N/A
2N/A default:
2N/A hdr->frag_length = pdu_size;
2N/A break;
2N/A }
2N/A
2N/A nds->pdu_scan_offset = 0;
2N/A (void) ndr_encode_pdu_hdr(mxa);
2N/A pdu_size = nds->pdu_size;
2N/A ndr_build_frag(nds, pdu_buf, pdu_size);
2N/A return (0);
2N/A }
2N/A
2N/A /*
2N/A * Multiple fragment response.
2N/A */
2N/A hdr->pfc_flags = NDR_PFC_FIRST_FRAG;
2N/A hdr->frag_length = frag_size;
2N/A mxa->send_hdr.response_hdr.alloc_hint = pdu_size - NDR_RSP_HDR_SIZE;
2N/A nds->pdu_scan_offset = 0;
2N/A (void) ndr_encode_pdu_hdr(mxa);
2N/A ndr_build_frag(nds, pdu_buf, frag_size);
2N/A
2N/A /*
2N/A * We need to update the 24-byte header in subsequent fragments.
2N/A *
2N/A * pdu_data_size: total data remaining to be handled
2N/A * frag_size: total fragment size including header
2N/A * frag_data_size: data in fragment
2N/A * (i.e. frag_size - NDR_RSP_HDR_SIZE)
2N/A */
2N/A pdu_data_size = pdu_size - NDR_RSP_HDR_SIZE;
2N/A frag_data_size = frag_size - NDR_RSP_HDR_SIZE;
2N/A
2N/A while (pdu_data_size) {
2N/A mxa->send_hdr.response_hdr.alloc_hint -= frag_data_size;
2N/A pdu_data_size -= frag_data_size;
2N/A pdu_buf += frag_data_size;
2N/A
2N/A if (pdu_data_size <= frag_data_size) {
2N/A frag_data_size = pdu_data_size;
2N/A frag_size = frag_data_size + NDR_RSP_HDR_SIZE;
2N/A hdr->pfc_flags = NDR_PFC_LAST_FRAG;
2N/A } else {
2N/A hdr->pfc_flags = 0;
2N/A }
2N/A
2N/A hdr->frag_length = frag_size;
2N/A nds->pdu_scan_offset = 0;
2N/A (void) ndr_encode_pdu_hdr(mxa);
2N/A bcopy(nds->pdu_base_addr, pdu_buf, NDR_RSP_HDR_SIZE);
2N/A
2N/A ndr_build_frag(nds, pdu_buf, frag_size);
2N/A
2N/A if (hdr->pfc_flags & NDR_PFC_LAST_FRAG)
2N/A break;
2N/A }
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * ndr_build_frag
2N/A *
2N/A * Build an RPC PDU fragment from the specified buffer.
2N/A * If malloc fails, the client will see a header/pdu inconsistency
2N/A * and report an error.
2N/A */
2N/Astatic void
2N/Andr_build_frag(ndr_stream_t *nds, uint8_t *buf, uint32_t len)
2N/A{
2N/A ndr_frag_t *frag;
2N/A int size = sizeof (ndr_frag_t) + len;
2N/A
2N/A if ((frag = (ndr_frag_t *)malloc(size)) == NULL)
2N/A return;
2N/A
2N/A frag->next = NULL;
2N/A frag->buf = (uint8_t *)frag + sizeof (ndr_frag_t);
2N/A frag->len = len;
2N/A bcopy(buf, frag->buf, len);
2N/A
2N/A if (nds->frags.head == NULL) {
2N/A nds->frags.head = frag;
2N/A nds->frags.tail = frag;
2N/A nds->frags.nfrag = 1;
2N/A } else {
2N/A nds->frags.tail->next = frag;
2N/A nds->frags.tail = frag;
2N/A ++nds->frags.nfrag;
2N/A }
2N/A}