/* * Dispose of a preserved heap. */ static void ndr_xa_release(ndr_client_t *clnt) { if (clnt->heap_preserved) { ndr_heap_destroy(clnt->heap); clnt->heap = NULL; clnt->heap_preserved = B_FALSE; } }
/* * Dispose of the transaction streams. If the heap has not been * preserved, we can destroy it here. */ static void ndr_xa_destruct(ndr_client_t *clnt, ndr_xa_t *mxa) { nds_destruct(&mxa->recv_nds); nds_destruct(&mxa->send_nds); if (!clnt->heap_preserved) { ndr_heap_destroy(mxa->heap); mxa->heap = NULL; clnt->heap = NULL; } }
/* * Must be called by RPC clients to free the heap after a successful RPC * call, i.e. ndr_rpc_call returned 0. The caller should take a copy * of any data returned by the RPC prior to calling this function because * returned data is in the heap. */ void ndr_rpc_release(mlsvc_handle_t *handle) { ndr_client_t *clnt = handle->clnt; if (clnt->heap_preserved) ndr_clnt_free_heap(clnt); else ndr_heap_destroy(clnt->heap); clnt->heap = NULL; }
/* * Unbind and close the pipe to an RPC service. * * If the heap has been preserved we need to go through an xa release. * The heap is preserved during an RPC call because that's where data * returned from the server is stored. * * Otherwise we destroy the heap directly. */ void ndr_rpc_unbind(mlsvc_handle_t *handle) { ndr_client_t *clnt = handle->clnt; struct smb_ctx *ctx = clnt->xa_private; if (clnt->heap_preserved) ndr_clnt_free_heap(clnt); else ndr_heap_destroy(clnt->heap); (void) smb_fh_close(clnt->xa_fd); smbrdr_ctx_free(ctx); free(clnt); bzero(handle, sizeof (mlsvc_handle_t)); }
/* * Process one server-side RPC request. */ static int ndr_pipe_process(ndr_pipe_t *np, ndr_xa_t *mxa) { ndr_stream_t *recv_nds; ndr_stream_t *send_nds; int rc = ENOMEM; mxa->pipe = np; mxa->binding_list = np->np_binding; if ((mxa->heap = ndr_heap_create()) == NULL) goto out1; recv_nds = &mxa->recv_nds; rc = nds_initialize(recv_nds, 0, NDR_MODE_CALL_RECV, mxa->heap); if (rc != 0) goto out2; send_nds = &mxa->send_nds; rc = nds_initialize(send_nds, 0, NDR_MODE_RETURN_SEND, mxa->heap); if (rc != 0) goto out3; rc = ndr_recv_request(mxa); if (rc != 0) goto out4; (void) ndr_svc_process(mxa); (void) ndr_send_reply(mxa); rc = 0; out4: nds_destruct(&mxa->send_nds); out3: nds_destruct(&mxa->recv_nds); out2: ndr_heap_destroy(mxa->heap); out1: return (rc); }
/* * The following functions provide the client callback interface. * If the caller hasn't provided a heap, create one here. */ static int ndr_xa_init(ndr_client_t *clnt, ndr_xa_t *mxa) { ndr_stream_t *recv_nds = &mxa->recv_nds; ndr_stream_t *send_nds = &mxa->send_nds; ndr_heap_t *heap = clnt->heap; int rc; if (heap == NULL) { if ((heap = ndr_heap_create()) == NULL) return (-1); clnt->heap = heap; } mxa->heap = heap; rc = nds_initialize(send_nds, 0, NDR_MODE_CALL_SEND, heap); if (rc == 0) rc = nds_initialize(recv_nds, NDR_PDU_SIZE_HINT_DEFAULT, NDR_MODE_RETURN_RECV, heap); if (rc != 0) { nds_destruct(&mxa->recv_nds); nds_destruct(&mxa->send_nds); ndr_heap_destroy(mxa->heap); mxa->heap = NULL; clnt->heap = NULL; return (-1); } if (clnt->nonull) NDS_SETF(send_nds, NDS_F_NONULL); return (0); }
/* * This call must be made to initialize an RPC client structure and bind * to the remote service before any RPCs can be exchanged with that service. * * The mlsvc_handle_t is a wrapper that is used to associate an RPC handle * with the client context for an instance of the interface. The handle * is zeroed to ensure that it doesn't look like a valid handle - * handle content is provided by the remove service. * * The client points to this top-level handle so that we know when to * unbind and teardown the connection. As each handle is initialized it * will inherit a reference to the client context. * * Returns 0 or an NT_STATUS: * NT_STATUS_BAD_NETWORK_PATH (get server addr) * NT_STATUS_NETWORK_ACCESS_DENIED (connect, auth) * NT_STATUS_BAD_NETWORK_NAME (tcon, open) * NT_STATUS_ACCESS_DENIED (open pipe) * NT_STATUS_INVALID_PARAMETER (rpc bind) * * NT_STATUS_INTERNAL_ERROR (bad args etc) * NT_STATUS_NO_MEMORY */ DWORD ndr_rpc_bind(mlsvc_handle_t *handle, char *server, char *domain, char *username, const char *service) { struct smb_ctx *ctx = NULL; ndr_client_t *clnt = NULL; ndr_service_t *svc; srvsvc_server_info_t svinfo; DWORD status; int fd = -1; int rc; if (handle == NULL || server == NULL || server[0] == '\0' || domain == NULL || username == NULL) return (NT_STATUS_INTERNAL_ERROR); /* In case the service was not registered... */ if ((svc = ndr_svc_lookup_name(service)) == NULL) return (NT_STATUS_INTERNAL_ERROR); /* * Set the default based on the assumption that most * servers will be Windows 2000 or later. This used to * try to get the actual server version, but that RPC * is not necessarily allowed anymore, so don't bother. */ bzero(&svinfo, sizeof (srvsvc_server_info_t)); svinfo.sv_platform_id = SV_PLATFORM_ID_NT; svinfo.sv_version_major = 5; svinfo.sv_version_minor = 0; svinfo.sv_type = SV_TYPE_DEFAULT; svinfo.sv_os = NATIVE_OS_WIN2000; /* * Some callers pass this when they want a NULL session. * Todo: have callers pass an empty string for that. */ if (strcmp(username, MLSVC_ANON_USER) == 0) username = ""; /* * Setup smbfs library handle, authenticate, connect to * the IPC$ share. This will reuse an existing connection * if the driver already has one for this combination of * server, user, domain. It may return any of: * NT_STATUS_BAD_NETWORK_PATH (get server addr) * NT_STATUS_NETWORK_ACCESS_DENIED (connect, auth) * NT_STATUS_BAD_NETWORK_NAME (tcon) */ status = smbrdr_ctx_new(&ctx, server, domain, username); if (status != NT_STATUS_SUCCESS) { syslog(LOG_ERR, "ndr_rpc_bind: smbrdr_ctx_new" "(Srv=%s Dom=%s User=%s), %s (0x%x)", server, domain, username, xlate_nt_status(status), status); /* Tell the DC Locator this DC failed. */ smb_ddiscover_bad_dc(server); goto errout; } /* * Open the named pipe. */ fd = smb_fh_open(ctx, svc->endpoint, O_RDWR); if (fd < 0) { rc = errno; syslog(LOG_DEBUG, "ndr_rpc_bind: " "smb_fh_open (%s) err=%d", svc->endpoint, rc); switch (rc) { case EACCES: status = NT_STATUS_ACCESS_DENIED; break; default: status = NT_STATUS_BAD_NETWORK_NAME; break; } goto errout; } /* * Setup the RPC client handle. */ if ((clnt = malloc(sizeof (ndr_client_t))) == NULL) { status = NT_STATUS_NO_MEMORY; goto errout; } bzero(clnt, sizeof (ndr_client_t)); clnt->handle = &handle->handle; clnt->xa_init = ndr_xa_init; clnt->xa_exchange = ndr_xa_exchange; clnt->xa_read = ndr_xa_read; clnt->xa_preserve = ndr_xa_preserve; clnt->xa_destruct = ndr_xa_destruct; clnt->xa_release = ndr_xa_release; clnt->xa_private = ctx; clnt->xa_fd = fd; ndr_svc_binding_pool_init(&clnt->binding_list, clnt->binding_pool, NDR_N_BINDING_POOL); if ((clnt->heap = ndr_heap_create()) == NULL) { status = NT_STATUS_NO_MEMORY; goto errout; } /* * Fill in the caller's handle. */ bzero(&handle->handle, sizeof (ndr_hdid_t)); handle->clnt = clnt; bcopy(&svinfo, &handle->svinfo, sizeof (srvsvc_server_info_t)); /* * Do the OtW RPC bind. */ rc = ndr_clnt_bind(clnt, service, &clnt->binding); switch (rc) { case NDR_DRC_FAULT_OUT_OF_MEMORY: status = NT_STATUS_NO_MEMORY; break; case NDR_DRC_FAULT_API_SERVICE_INVALID: /* not registered */ status = NT_STATUS_INTERNAL_ERROR; break; default: if (NDR_DRC_IS_FAULT(rc)) { status = NT_STATUS_INVALID_PARAMETER; break; } /* FALLTHROUGH */ case NDR_DRC_OK: return (NT_STATUS_SUCCESS); } syslog(LOG_DEBUG, "ndr_rpc_bind: " "ndr_clnt_bind, %s (0x%x)", xlate_nt_status(status), status); errout: handle->clnt = NULL; if (clnt != NULL) { ndr_heap_destroy(clnt->heap); free(clnt); } if (ctx != NULL) { if (fd != -1) (void) smb_fh_close(fd); smbrdr_ctx_free(ctx); } return (status); }