static int ndr_svc_request(ndr_xa_t *mxa) { ndr_binding_t *mbind; ndr_service_t *msvc; unsigned p_cont_id; int rc; mxa->opnum = mxa->recv_hdr.request_hdr.opnum; p_cont_id = mxa->recv_hdr.request_hdr.p_cont_id; if ((mbind = ndr_svc_find_binding(mxa, p_cont_id)) == NULL) return (NDR_DRC_FAULT_REQUEST_PCONT_INVALID); mxa->binding = mbind; msvc = mbind->service; /* * Make room for the response hdr. */ mxa->send_nds.pdu_scan_offset = NDR_RSP_HDR_SIZE; if (msvc->call_stub) rc = (*msvc->call_stub)(mxa); else rc = ndr_generic_call_stub(mxa); if (NDR_DRC_IS_FAULT(rc)) { ndo_printf(0, 0, "%s[0x%02x]: 0x%04x", msvc->name, mxa->opnum, rc); } return (rc); }
/* * This is the entry point for all server-side RPC processing. * It is assumed that the PDU has already been received. */ static int ndr_svc_process(ndr_xa_t *mxa) { int rc; (void) ndr_reply_prepare_hdr(mxa); switch (mxa->ptype) { case NDR_PTYPE_BIND: rc = ndr_svc_bind(mxa); break; case NDR_PTYPE_REQUEST: rc = ndr_svc_request(mxa); break; case NDR_PTYPE_ALTER_CONTEXT: rc = ndr_svc_alter_context(mxa); break; default: rc = NDR_DRC_FAULT_RPCHDR_PTYPE_INVALID; break; } if (NDR_DRC_IS_FAULT(rc)) ndr_reply_fault(mxa, rc); return (rc); }
/* * Call the RPC function identified by opnum. The remote service is * identified by the handle, which should have been initialized by * ndr_rpc_bind. * * If the RPC call is successful (returns 0), the caller must call * ndr_rpc_release to release the heap. Otherwise, we release the * heap here. */ int ndr_rpc_call(mlsvc_handle_t *handle, int opnum, void *params) { ndr_client_t *clnt = handle->clnt; int rc; if (ndr_rpc_get_heap(handle) == NULL) return (-1); rc = ndr_clnt_call(clnt->binding, opnum, params); /* * Always clear the nonull flag to ensure * it is not applied to subsequent calls. */ clnt->nonull = B_FALSE; if (NDR_DRC_IS_FAULT(rc)) { ndr_rpc_release(handle); return (-1); } 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); }
/* * Multiple p_cont_elem[]s, multiple transfer_syntaxes[] and multiple * p_results[] not supported. */ static int ndr_svc_bind(ndr_xa_t *mxa) { ndr_p_cont_list_t *cont_list; ndr_p_result_list_t *result_list; ndr_p_result_t *result; unsigned p_cont_id; ndr_binding_t *mbind; ndr_uuid_t *as_uuid; ndr_uuid_t *ts_uuid; int as_vers; int ts_vers; ndr_service_t *msvc; int rc; ndr_port_any_t *sec_addr; /* acquire targets */ cont_list = &mxa->recv_hdr.bind_hdr.p_context_elem; result_list = &mxa->send_hdr.bind_ack_hdr.p_result_list; result = &result_list->p_results[0]; /* * Set up temporary secondary address port. * We will correct this later (below). */ sec_addr = &mxa->send_hdr.bind_ack_hdr.sec_addr; sec_addr->length = 13; (void) strcpy((char *)sec_addr->port_spec, "\\PIPE\\ntsvcs"); result_list->n_results = 1; result_list->reserved = 0; result_list->reserved2 = 0; result->result = NDR_PCDR_ACCEPTANCE; result->reason = 0; bzero(&result->transfer_syntax, sizeof (result->transfer_syntax)); /* sanity check */ if (cont_list->n_context_elem != 1 || cont_list->p_cont_elem[0].n_transfer_syn != 1) { ndo_trace("ndr_svc_bind: warning: multiple p_cont_elem"); } p_cont_id = cont_list->p_cont_elem[0].p_cont_id; if ((mbind = ndr_svc_find_binding(mxa, p_cont_id)) != NULL) { /* * Duplicate presentation context id. */ ndo_trace("ndr_svc_bind: duplicate binding"); return (NDR_DRC_FAULT_BIND_PCONT_BUSY); } if ((mbind = ndr_svc_new_binding(mxa)) == NULL) { /* * No free binding slot */ result->result = NDR_PCDR_PROVIDER_REJECTION; result->reason = NDR_PPR_LOCAL_LIMIT_EXCEEDED; ndo_trace("ndr_svc_bind: no resources"); return (NDR_DRC_OK); } as_uuid = &cont_list->p_cont_elem[0].abstract_syntax.if_uuid; as_vers = cont_list->p_cont_elem[0].abstract_syntax.if_version; ts_uuid = &cont_list->p_cont_elem[0].transfer_syntaxes[0].if_uuid; ts_vers = cont_list->p_cont_elem[0].transfer_syntaxes[0].if_version; msvc = ndr_svc_lookup_uuid(as_uuid, as_vers, ts_uuid, ts_vers); if (msvc == NULL) { result->result = NDR_PCDR_PROVIDER_REJECTION; result->reason = NDR_PPR_ABSTRACT_SYNTAX_NOT_SUPPORTED; return (NDR_DRC_OK); } /* * We can now use the correct secondary address port. */ sec_addr = &mxa->send_hdr.bind_ack_hdr.sec_addr; sec_addr->length = strlen(msvc->sec_addr_port) + 1; (void) strlcpy((char *)sec_addr->port_spec, msvc->sec_addr_port, NDR_PORT_ANY_MAX_PORT_SPEC); mbind->p_cont_id = p_cont_id; mbind->which_side = NDR_BIND_SIDE_SERVER; /* mbind->context set by app */ mbind->service = msvc; mbind->instance_specific = 0; mxa->binding = mbind; if (msvc->bind_req) { /* * Call the service-specific bind() handler. If * this fails, we shouild send a specific error * on the bind ack. */ rc = (msvc->bind_req)(mxa); if (NDR_DRC_IS_FAULT(rc)) { mbind->service = 0; /* free binding slot */ mbind->which_side = 0; mbind->p_cont_id = 0; mbind->instance_specific = 0; return (rc); } } result->transfer_syntax = cont_list->p_cont_elem[0].transfer_syntaxes[0]; return (NDR_DRC_BINDING_MADE); }