📄 dcerpc.c
字号:
if (conn->dead) return; conn->dead = true; if (conn->transport.shutdown_pipe) { conn->transport.shutdown_pipe(conn, status); } /* all pending requests get the error */ while (conn->pending) { struct rpc_request *req = conn->pending; dcerpc_req_dequeue(req); req->state = RPC_REQUEST_DONE; req->status = status; if (req->async.callback) { req->async.callback(req); } } talloc_set_destructor(conn, NULL); if (conn->free_skipped) { talloc_free(conn); }}/* forward declarations of the recv_data handlers for the types of packets we need to handle*/static void dcerpc_request_recv_data(struct dcerpc_connection *c, DATA_BLOB *raw_packet, struct ncacn_packet *pkt);/* receive a dcerpc reply from the transport. Here we work out what type of reply it is (normal request, bind or alter context) and dispatch to the appropriate handler*/static void dcerpc_recv_data(struct dcerpc_connection *conn, DATA_BLOB *blob, NTSTATUS status){ struct ncacn_packet pkt; if (NT_STATUS_IS_OK(status) && blob->length == 0) { status = NT_STATUS_UNEXPECTED_NETWORK_ERROR; } /* the transport may be telling us of a severe error, such as a dropped socket */ if (!NT_STATUS_IS_OK(status)) { data_blob_free(blob); dcerpc_connection_dead(conn, status); return; } /* parse the basic packet to work out what type of response this is */ status = ncacn_pull(conn, blob, blob->data, &pkt); if (!NT_STATUS_IS_OK(status)) { data_blob_free(blob); dcerpc_connection_dead(conn, status); } dcerpc_request_recv_data(conn, blob, &pkt);}/* Receive a bind reply from the transport*/static void dcerpc_bind_recv_handler(struct rpc_request *req, DATA_BLOB *raw_packet, struct ncacn_packet *pkt){ struct composite_context *c; struct dcerpc_connection *conn; c = talloc_get_type(req->async.private_data, struct composite_context); if (pkt->ptype == DCERPC_PKT_BIND_NAK) { DEBUG(2,("dcerpc: bind_nak reason %d\n", pkt->u.bind_nak.reject_reason)); composite_error(c, dcerpc_map_reason(pkt->u.bind_nak. reject_reason)); return; } if ((pkt->ptype != DCERPC_PKT_BIND_ACK) || (pkt->u.bind_ack.num_results == 0) || (pkt->u.bind_ack.ctx_list[0].result != 0)) { composite_error(c, NT_STATUS_NET_WRITE_FAULT); return; } conn = req->p->conn; conn->srv_max_xmit_frag = pkt->u.bind_ack.max_xmit_frag; conn->srv_max_recv_frag = pkt->u.bind_ack.max_recv_frag; /* the bind_ack might contain a reply set of credentials */ if (conn->security_state.auth_info && pkt->u.bind_ack.auth_info.length) { enum ndr_err_code ndr_err; ndr_err = ndr_pull_struct_blob( &pkt->u.bind_ack.auth_info, conn, NULL, conn->security_state.auth_info, (ndr_pull_flags_fn_t)ndr_pull_dcerpc_auth); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { c->status = ndr_map_error2ntstatus(ndr_err); if (!composite_is_ok(c)) return; } } req->p->assoc_group_id = pkt->u.bind_ack.assoc_group_id; composite_done(c);}/* handle timeouts of individual dcerpc requests*/static void dcerpc_timeout_handler(struct event_context *ev, struct timed_event *te, struct timeval t, void *private){ struct rpc_request *req = talloc_get_type(private, struct rpc_request); if (req->ignore_timeout) { dcerpc_req_dequeue(req); req->state = RPC_REQUEST_DONE; req->status = NT_STATUS_IO_TIMEOUT; if (req->async.callback) { req->async.callback(req); } return; } dcerpc_connection_dead(req->p->conn, NT_STATUS_IO_TIMEOUT);}/* send a async dcerpc bind request*/struct composite_context *dcerpc_bind_send(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, const struct ndr_syntax_id *syntax, const struct ndr_syntax_id *transfer_syntax){ struct composite_context *c; struct ncacn_packet pkt; DATA_BLOB blob; struct rpc_request *req; c = composite_create(mem_ctx,p->conn->event_ctx); if (c == NULL) return NULL; c->private_data = p; p->syntax = *syntax; p->transfer_syntax = *transfer_syntax; init_ncacn_hdr(p->conn, &pkt); pkt.ptype = DCERPC_PKT_BIND; pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST; pkt.call_id = p->conn->call_id; pkt.auth_length = 0; if (p->binding->flags & DCERPC_CONCURRENT_MULTIPLEX) { pkt.pfc_flags |= DCERPC_PFC_FLAG_CONC_MPX; } pkt.u.bind.max_xmit_frag = 5840; pkt.u.bind.max_recv_frag = 5840; pkt.u.bind.assoc_group_id = p->binding->assoc_group_id; pkt.u.bind.num_contexts = 1; pkt.u.bind.ctx_list = talloc_array(mem_ctx, struct dcerpc_ctx_list, 1); if (composite_nomem(pkt.u.bind.ctx_list, c)) return c; pkt.u.bind.ctx_list[0].context_id = p->context_id; pkt.u.bind.ctx_list[0].num_transfer_syntaxes = 1; pkt.u.bind.ctx_list[0].abstract_syntax = p->syntax; pkt.u.bind.ctx_list[0].transfer_syntaxes = &p->transfer_syntax; pkt.u.bind.auth_info = data_blob(NULL, 0); /* construct the NDR form of the packet */ c->status = ncacn_push_auth(&blob, c, p->conn->iconv_convenience, &pkt, p->conn->security_state.auth_info); if (!composite_is_ok(c)) return c; p->conn->transport.recv_data = dcerpc_recv_data; /* * we allocate a dcerpc_request so we can be in the same * request queue as normal requests */ req = talloc_zero(c, struct rpc_request); if (composite_nomem(req, c)) return c; req->state = RPC_REQUEST_PENDING; req->call_id = pkt.call_id; req->async.private_data = c; req->async.callback = dcerpc_composite_fail; req->p = p; req->recv_handler = dcerpc_bind_recv_handler; DLIST_ADD_END(p->conn->pending, req, struct rpc_request *); talloc_set_destructor(req, dcerpc_req_dequeue); c->status = p->conn->transport.send_request(p->conn, &blob, true); if (!composite_is_ok(c)) return c; event_add_timed(c->event_ctx, req, timeval_current_ofs(DCERPC_REQUEST_TIMEOUT, 0), dcerpc_timeout_handler, req); return c;}/* recv side of async dcerpc bind request*/NTSTATUS dcerpc_bind_recv(struct composite_context *ctx){ NTSTATUS result = composite_wait(ctx); talloc_free(ctx); return result;}/* perform a continued bind (and auth3)*/NTSTATUS dcerpc_auth3(struct dcerpc_connection *c, TALLOC_CTX *mem_ctx){ struct ncacn_packet pkt; NTSTATUS status; DATA_BLOB blob; init_ncacn_hdr(c, &pkt); pkt.ptype = DCERPC_PKT_AUTH3; pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST; pkt.call_id = next_call_id(c); pkt.auth_length = 0; pkt.u.auth3._pad = 0; pkt.u.auth3.auth_info = data_blob(NULL, 0); /* construct the NDR form of the packet */ status = ncacn_push_auth(&blob, mem_ctx, c->iconv_convenience, &pkt, c->security_state.auth_info); if (!NT_STATUS_IS_OK(status)) { return status; } /* send it on its way */ status = c->transport.send_request(c, &blob, false); if (!NT_STATUS_IS_OK(status)) { return status; } return NT_STATUS_OK; }/* process a fragment received from the transport layer during a request This function frees the data */static void dcerpc_request_recv_data(struct dcerpc_connection *c, DATA_BLOB *raw_packet, struct ncacn_packet *pkt){ struct rpc_request *req; uint_t length; NTSTATUS status = NT_STATUS_OK; /* if this is an authenticated connection then parse and check the auth info. We have to do this before finding the matching packet, as the request structure might have been removed due to a timeout, but if it has been we still need to run the auth routines so that we don't get the sign/seal info out of step with the server */ if (c->security_state.auth_info && c->security_state.generic_state && pkt->ptype == DCERPC_PKT_RESPONSE) { status = ncacn_pull_request_auth(c, raw_packet->data, raw_packet, pkt); } /* find the matching request */ for (req=c->pending;req;req=req->next) { if (pkt->call_id == req->call_id) break; }#if 0 /* useful for testing certain vendors RPC servers */ if (req == NULL && c->pending && pkt->call_id == 0) { DEBUG(0,("HACK FOR INCORRECT CALL ID\n")); req = c->pending; }#endif if (req == NULL) { DEBUG(2,("dcerpc_request: unmatched call_id %u in response packet\n", pkt->call_id)); data_blob_free(raw_packet); return; } talloc_steal(req, raw_packet->data); if (req->recv_handler != NULL) { dcerpc_req_dequeue(req); req->state = RPC_REQUEST_DONE; req->recv_handler(req, raw_packet, pkt); return; } if (pkt->ptype == DCERPC_PKT_FAULT) { DEBUG(5,("rpc fault: %s\n", dcerpc_errstr(c, pkt->u.fault.status))); req->fault_code = pkt->u.fault.status; req->status = NT_STATUS_NET_WRITE_FAULT; goto req_done; } if (pkt->ptype != DCERPC_PKT_RESPONSE) { DEBUG(2,("Unexpected packet type %d in dcerpc response\n", (int)pkt->ptype)); req->fault_code = DCERPC_FAULT_OTHER; req->status = NT_STATUS_NET_WRITE_FAULT; goto req_done; } /* now check the status from the auth routines, and if it failed then fail this request accordingly */ if (!NT_STATUS_IS_OK(status)) { req->status = status; goto req_done; } length = pkt->u.response.stub_and_verifier.length; if (length > 0) { req->payload.data = talloc_realloc(req, req->payload.data, uint8_t, req->payload.length + length); if (!req->payload.data) { req->status = NT_STATUS_NO_MEMORY; goto req_done; } memcpy(req->payload.data+req->payload.length, pkt->u.response.stub_and_verifier.data, length); req->payload.length += length; } if (!(pkt->pfc_flags & DCERPC_PFC_FLAG_LAST)) { c->transport.send_read(c); return; } if (!(pkt->drep[0] & DCERPC_DREP_LE)) { req->flags |= DCERPC_PULL_BIGENDIAN; } else { req->flags &= ~DCERPC_PULL_BIGENDIAN; }req_done: /* we've got the full payload */ req->state = RPC_REQUEST_DONE; DLIST_REMOVE(c->pending, req); if (c->request_queue != NULL) { /* We have to look at shipping further requests before calling * the async function, that one might close the pipe */ dcerpc_ship_next_request(c); } if (req->async.callback) { req->async.callback(req); }}/* perform the send side of a async dcerpc request*/static struct rpc_request *dcerpc_request_send(struct dcerpc_pipe *p, const struct GUID *object, uint16_t opnum, bool async, DATA_BLOB *stub_data){ struct rpc_request *req; p->conn->transport.recv_data = dcerpc_recv_data; req = talloc(p, struct rpc_request); if (req == NULL) { return NULL; } req->p = p; req->call_id = next_call_id(p->conn); req->status = NT_STATUS_OK; req->state = RPC_REQUEST_QUEUED; req->payload = data_blob(NULL, 0); req->flags = 0; req->fault_code = 0; req->async_call = async; req->ignore_timeout = false; req->async.callback = NULL; req->async.private_data = NULL; req->recv_handler = NULL; if (object != NULL) { req->object = (struct GUID *)talloc_memdup(req, (const void *)object, sizeof(*object)); if (req->object == NULL) { talloc_free(req); return NULL; } } else { req->object = NULL; } req->opnum = opnum; req->request_data.length = stub_data->length; req->request_data.data = talloc_reference(req, stub_data->data); if (req->request_data.length && req->request_data.data == NULL) { return NULL; } DLIST_ADD_END(p->conn->request_queue, req, struct rpc_request *); talloc_set_destructor(req, dcerpc_req_dequeue); dcerpc_ship_next_request(p->conn); if (p->request_timeout) { event_add_timed(dcerpc_event_context(p), req, timeval_current_ofs(p->request_timeout, 0), dcerpc_timeout_handler, req); } return req;}/* Send a request using the transport*/static void dcerpc_ship_next_request(struct dcerpc_connection *c){ struct rpc_request *req; struct dcerpc_pipe *p; DATA_BLOB *stub_data; struct ncacn_packet pkt; DATA_BLOB blob; uint32_t remaining, chunk_size; bool first_packet = true; req = c->request_queue; if (req == NULL) { return; } p = req->p; stub_data = &req->request_data; if (!req->async_call && (c->pending != NULL)) { return; } DLIST_REMOVE(c->request_queue, req); DLIST_ADD(c->pending, req); req->state = RPC_REQUEST_PENDING; init_ncacn_hdr(p->conn, &pkt); remaining = stub_data->length; /* we can write a full max_recv_frag size, minus the dcerpc request header size */ chunk_size = p->conn->srv_max_recv_frag - (DCERPC_MAX_SIGN_SIZE+DCERPC_REQUEST_LENGTH); pkt.ptype = DCERPC_PKT_REQUEST; pkt.call_id = req->call_id; pkt.auth_length = 0; pkt.pfc_flags = 0; pkt.u.request.alloc_hint = remaining; pkt.u.request.context_id = p->context_id; pkt.u.request.opnum = req->opnum; if (req->object) { pkt.u.request.object.object = *req->object; pkt.pfc_flags |= DCERPC_PFC_FLAG_OBJECT_UUID; chunk_size -= ndr_size_GUID(req->object,0); } /* we send a series of pdus without waiting for a reply */ while (remaining > 0 || first_packet) { uint32_t chunk = MIN(chunk_size, remaining); bool last_frag = false; first_packet = false; pkt.pfc_flags &= ~(DCERPC_PFC_FLAG_FIRST |DCERPC_PFC_FLAG_LAST); if (remaining == stub_data->length) { pkt.pfc_flags |= DCERPC_PFC_FLAG_FIRST; } if (chunk == remaining) { pkt.pfc_flags |= DCERPC_PFC_FLAG_LAST; last_frag = true; } pkt.u.request.stub_and_verifier.data = stub_data->data + (stub_data->length - remaining); pkt.u.request.stub_and_verifier.length = chunk; req->status = ncacn_push_request_sign(p->conn, &blob, req, &pkt); if (!NT_STATUS_IS_OK(req->status)) { req->state = RPC_REQUEST_DONE; DLIST_REMOVE(p->conn->pending, req); return; } req->status = p->conn->transport.send_request(p->conn, &blob, last_frag); if (!NT_STATUS_IS_OK(req->status)) { req->state = RPC_REQUEST_DONE; DLIST_REMOVE(p->conn->pending, req); return; } remaining -= chunk; }}/* return the event context for a dcerpc pipe used by callers who wish to operate asynchronously*/_PUBLIC_ struct event_context *dcerpc_event_context(struct dcerpc_pipe *p){ return p->conn->event_ctx;}/* perform the receive side of a async dcerpc request*/NTSTATUS dcerpc_request_recv(struct rpc_request *req, TALLOC_CTX *mem_ctx, DATA_BLOB *stub_data){ NTSTATUS status; while (req->state != RPC_REQUEST_DONE) { struct event_context *ctx = dcerpc_event_context(req->p); if (event_loop_once(ctx) != 0) { return NT_STATUS_CONNECTION_DISCONNECTED; } } *stub_data = req->payload; status = req->status; if (stub_data->data) { stub_data->data = talloc_steal(mem_ctx, stub_data->data); } if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) { req->p->last_fault_code = req->fault_code; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -