diff options
author | David Disseldorp <ddiss@samba.org> | 2013-12-10 13:59:06 +0100 |
---|---|---|
committer | Stefan Metzmacher <metze@samba.org> | 2013-12-11 22:24:31 +0100 |
commit | 646d8c26f82ce3a70b189f618979f63448658c4c (patch) | |
tree | 7dffd7f0c6b7302291ea61025217dd9388d8053c | |
parent | 27baff0ec96cded0446ecd7739e9d31aaeb90868 (diff) | |
download | samba-646d8c26f82ce3a70b189f618979f63448658c4c.tar.gz |
s3/rpc_server: don't unmarshall PDUs twice
DCE/RPC PDUs are currently unmarshalled firstly by the generic librpc
dcerpc_read_ncacn_packet_[send/recv] functions, and subsequently a
second time by the source3 rpc_server, which ignores the unmarshalled
packet and re-parses the receive buffer.
Signed-off-by: David Disseldorp <ddiss@samba.org>
Reviewed-by: Stefan Metzmacher <metze@samba.org>
Autobuild-User(master): Stefan Metzmacher <metze@samba.org>
Autobuild-Date(master): Wed Dec 11 22:24:31 CET 2013 on sn-devel-104
-rw-r--r-- | source3/rpc_server/rpc_server.c | 59 | ||||
-rw-r--r-- | source3/rpc_server/rpc_server.h | 2 | ||||
-rw-r--r-- | source3/rpc_server/srv_pipe.c | 52 | ||||
-rw-r--r-- | source3/rpc_server/srv_pipe_hnd.c | 366 | ||||
-rw-r--r-- | source3/rpc_server/srv_pipe_hnd.h | 2 |
5 files changed, 28 insertions, 453 deletions
diff --git a/source3/rpc_server/rpc_server.c b/source3/rpc_server/rpc_server.c index f283559dddd..d10d3ac3b12 100644 --- a/source3/rpc_server/rpc_server.c +++ b/source3/rpc_server/rpc_server.c @@ -432,9 +432,6 @@ void named_pipe_packet_process(struct tevent_req *subreq) DATA_BLOB recv_buffer = data_blob_null; struct ncacn_packet *pkt; NTSTATUS status; - ssize_t data_left; - ssize_t data_used; - char *data; uint32_t to_send; size_t i; bool ok; @@ -445,23 +442,20 @@ void named_pipe_packet_process(struct tevent_req *subreq) goto fail; } - data_left = recv_buffer.length; - data = (char *)recv_buffer.data; - - while (data_left) { - - data_used = process_incoming_data(npc->p, data, data_left); - if (data_used < 0) { - DEBUG(3, ("Failed to process dceprc request!\n")); - status = NT_STATUS_UNEXPECTED_IO_ERROR; - goto fail; - } - - data_left -= data_used; - data += data_used; + /* dcerpc_read_ncacn_packet_recv() returns a full PDU */ + npc->p->in_data.pdu_needed_len = 0; + npc->p->in_data.pdu = recv_buffer; + if (dcerpc_get_endian_flag(&recv_buffer) & DCERPC_DREP_LE) { + npc->p->endian = RPC_LITTLE_ENDIAN; + } else { + npc->p->endian = RPC_BIG_ENDIAN; } + DEBUG(10, ("PDU is in %s Endian format!\n", + npc->p->endian ? "Big" : "Little")); + process_complete_pdu(npc->p, pkt); - /* Do not leak this buffer, npc is a long lived context */ + /* reset pipe state and free PDU */ + npc->p->in_data.pdu.length = 0; talloc_free(recv_buffer.data); talloc_free(pkt); @@ -1134,10 +1128,7 @@ static void dcerpc_ncacn_packet_process(struct tevent_req *subreq) struct _output_data *out = &ncacn_conn->p->out_data; DATA_BLOB recv_buffer = data_blob_null; struct ncacn_packet *pkt; - ssize_t data_left; - ssize_t data_used; uint32_t to_send; - char *data; NTSTATUS status; bool ok; @@ -1153,22 +1144,20 @@ static void dcerpc_ncacn_packet_process(struct tevent_req *subreq) goto fail; } - data_left = recv_buffer.length; - data = (char *) recv_buffer.data; - - while (data_left) { - data_used = process_incoming_data(ncacn_conn->p, data, data_left); - if (data_used < 0) { - DEBUG(3, ("Failed to process dcerpc request!\n")); - status = NT_STATUS_UNEXPECTED_IO_ERROR; - goto fail; - } - - data_left -= data_used; - data += data_used; + /* dcerpc_read_ncacn_packet_recv() returns a full PDU */ + ncacn_conn->p->in_data.pdu_needed_len = 0; + ncacn_conn->p->in_data.pdu = recv_buffer; + if (dcerpc_get_endian_flag(&recv_buffer) & DCERPC_DREP_LE) { + ncacn_conn->p->endian = RPC_LITTLE_ENDIAN; + } else { + ncacn_conn->p->endian = RPC_BIG_ENDIAN; } + DEBUG(10, ("PDU is in %s Endian format!\n", + ncacn_conn->p->endian ? "Big" : "Little")); + process_complete_pdu(ncacn_conn->p, pkt); - /* Do not leak this buffer */ + /* reset pipe state and free PDU */ + ncacn_conn->p->in_data.pdu.length = 0; talloc_free(recv_buffer.data); talloc_free(pkt); diff --git a/source3/rpc_server/rpc_server.h b/source3/rpc_server/rpc_server.h index d204a8b3adc..0d7bdb52745 100644 --- a/source3/rpc_server/rpc_server.h +++ b/source3/rpc_server/rpc_server.h @@ -77,7 +77,7 @@ int make_server_pipes_struct(TALLOC_CTX *mem_ctx, int *perrno); void set_incoming_fault(struct pipes_struct *p); -void process_complete_pdu(struct pipes_struct *p); +void process_complete_pdu(struct pipes_struct *p, struct ncacn_packet *pkt); int create_named_pipe_socket(const char *pipe_name); bool setup_named_pipe_socket(const char *pipe_name, struct tevent_context *ev_ctx, diff --git a/source3/rpc_server/srv_pipe.c b/source3/rpc_server/srv_pipe.c index f92de3b594e..8f30386a6ea 100644 --- a/source3/rpc_server/srv_pipe.c +++ b/source3/rpc_server/srv_pipe.c @@ -1486,45 +1486,9 @@ static bool process_request_pdu(struct pipes_struct *p, struct ncacn_packet *pkt return True; } -/**************************************************************************** - Processes a finished PDU stored in p->in_data.pdu. -****************************************************************************/ - -void process_complete_pdu(struct pipes_struct *p) +void process_complete_pdu(struct pipes_struct *p, struct ncacn_packet *pkt) { - struct ncacn_packet *pkt = NULL; - NTSTATUS status; - bool reply = False; - - if(p->fault_state) { - DEBUG(10,("RPC connection in fault state.\n")); - goto done; - } - - pkt = talloc(p->mem_ctx, struct ncacn_packet); - if (!pkt) { - DEBUG(0, ("Out of memory!\n")); - goto done; - } - - /* - * Ensure we're using the corrent endianness for both the - * RPC header flags and the raw data we will be reading from. - */ - if (dcerpc_get_endian_flag(&p->in_data.pdu) & DCERPC_DREP_LE) { - p->endian = RPC_LITTLE_ENDIAN; - } else { - p->endian = RPC_BIG_ENDIAN; - } - DEBUG(10, ("PDU is in %s Endian format!\n", p->endian?"Big":"Little")); - - status = dcerpc_pull_ncacn_packet(pkt, &p->in_data.pdu, - pkt, p->endian); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(0, ("Failed to unmarshal rpc packet: %s!\n", - nt_errstr(status))); - goto done; - } + bool reply = false; /* Store the call_id */ p->call_id = pkt->call_id; @@ -1644,21 +1608,11 @@ void process_complete_pdu(struct pipes_struct *p) break; } -done: if (!reply) { DEBUG(3,("DCE/RPC fault sent!")); set_incoming_fault(p); setup_fault_pdu(p, NT_STATUS(DCERPC_FAULT_OP_RNG_ERROR)); - TALLOC_FREE(pkt); - } else { - /* - * Reset the lengths. We're ready for a new pdu. - */ - TALLOC_FREE(p->in_data.pdu.data); - p->in_data.pdu_needed_len = 0; - p->in_data.pdu.length = 0; } - - TALLOC_FREE(pkt); + /* pkt and p->in_data.pdu.data freed by caller */ } diff --git a/source3/rpc_server/srv_pipe_hnd.c b/source3/rpc_server/srv_pipe_hnd.c index a95aa06525a..a05fafd4b07 100644 --- a/source3/rpc_server/srv_pipe_hnd.c +++ b/source3/rpc_server/srv_pipe_hnd.c @@ -35,372 +35,6 @@ #undef DBGC_CLASS #define DBGC_CLASS DBGC_RPC_SRV -/**************************************************************************** - Ensures we have at least RPC_HEADER_LEN amount of data in the incoming buffer. -****************************************************************************/ - -static ssize_t fill_rpc_header(struct pipes_struct *p, const char *data, size_t data_to_copy) -{ - size_t len_needed_to_complete_hdr = - MIN(data_to_copy, RPC_HEADER_LEN - p->in_data.pdu.length); - - DEBUG(10, ("fill_rpc_header: data_to_copy = %u, " - "len_needed_to_complete_hdr = %u, " - "receive_len = %u\n", - (unsigned int)data_to_copy, - (unsigned int)len_needed_to_complete_hdr, - (unsigned int)p->in_data.pdu.length )); - - if (p->in_data.pdu.data == NULL) { - p->in_data.pdu.data = talloc_array(p, uint8_t, RPC_HEADER_LEN); - } - if (p->in_data.pdu.data == NULL) { - DEBUG(0, ("talloc failed\n")); - return -1; - } - - memcpy((char *)&p->in_data.pdu.data[p->in_data.pdu.length], - data, len_needed_to_complete_hdr); - p->in_data.pdu.length += len_needed_to_complete_hdr; - - return (ssize_t)len_needed_to_complete_hdr; -} - -static bool get_pdu_size(struct pipes_struct *p) -{ - uint16_t frag_len; - /* the fill_rpc_header() call insures we copy only - * RPC_HEADER_LEN bytes. If this doesn't match then - * somethign is very wrong and we can only abort */ - if (p->in_data.pdu.length != RPC_HEADER_LEN) { - DEBUG(0, ("Unexpected RPC Header size! " - "got %d, expected %d)\n", - (int)p->in_data.pdu.length, - RPC_HEADER_LEN)); - set_incoming_fault(p); - return false; - } - - frag_len = dcerpc_get_frag_length(&p->in_data.pdu); - - /* verify it is a reasonable value */ - if ((frag_len < RPC_HEADER_LEN) || - (frag_len > RPC_MAX_PDU_FRAG_LEN)) { - DEBUG(0, ("Unexpected RPC Fragment size! (%d)\n", - frag_len)); - set_incoming_fault(p); - return false; - } - - p->in_data.pdu_needed_len = frag_len - RPC_HEADER_LEN; - - /* allocate the space needed to fill the pdu */ - p->in_data.pdu.data = talloc_realloc(p, p->in_data.pdu.data, - uint8_t, frag_len); - if (p->in_data.pdu.data == NULL) { - DEBUG(0, ("talloc_realloc failed\n")); - set_incoming_fault(p); - return false; - } - - return true; -} - -/**************************************************************************** - Call this to free any talloc'ed memory. Do this after processing - a complete incoming and outgoing request (multiple incoming/outgoing - PDU's). -****************************************************************************/ - -static void free_pipe_context(struct pipes_struct *p) -{ - data_blob_free(&p->out_data.frag); - data_blob_free(&p->out_data.rdata); - data_blob_free(&p->in_data.data); - - DEBUG(3, ("free_pipe_context: " - "destroying talloc pool of size %lu\n", - (unsigned long)talloc_total_size(p->mem_ctx))); - talloc_free_children(p->mem_ctx); -} - -/**************************************************************************** - Accepts incoming data on an rpc pipe. Processes the data in pdu sized units. -****************************************************************************/ - -ssize_t process_incoming_data(struct pipes_struct *p, const char *data, size_t n) -{ - size_t data_to_copy = MIN(n, RPC_MAX_PDU_FRAG_LEN - - p->in_data.pdu.length); - - DEBUG(10, ("process_incoming_data: Start: pdu.length = %u, " - "pdu_needed_len = %u, incoming data = %u\n", - (unsigned int)p->in_data.pdu.length, - (unsigned int)p->in_data.pdu_needed_len, - (unsigned int)n )); - - if(data_to_copy == 0) { - /* - * This is an error - data is being received and there is no - * space in the PDU. Free the received data and go into the - * fault state. - */ - DEBUG(0, ("process_incoming_data: " - "No space in incoming pdu buffer. " - "Current size = %u incoming data size = %u\n", - (unsigned int)p->in_data.pdu.length, - (unsigned int)n)); - set_incoming_fault(p); - return -1; - } - - /* - * If we have no data already, wait until we get at least - * a RPC_HEADER_LEN * number of bytes before we can do anything. - */ - - if ((p->in_data.pdu_needed_len == 0) && - (p->in_data.pdu.length < RPC_HEADER_LEN)) { - /* - * Always return here. If we have more data then the RPC_HEADER - * will be processed the next time around the loop. - */ - return fill_rpc_header(p, data, data_to_copy); - } - - /* - * At this point we know we have at least an RPC_HEADER_LEN amount of - * data stored in p->in_data.pdu. - */ - - /* - * If pdu_needed_len is zero this is a new pdu. - * Check how much more data we need, then loop again. - */ - if (p->in_data.pdu_needed_len == 0) { - - bool ok = get_pdu_size(p); - if (!ok) { - return -1; - } - if (p->in_data.pdu_needed_len > 0) { - return 0; - } - - /* If rret == 0 and pdu_needed_len == 0 here we have a PDU - * that consists of an RPC_HEADER only. This is a - * DCERPC_PKT_SHUTDOWN, DCERPC_PKT_CO_CANCEL or - * DCERPC_PKT_ORPHANED pdu type. - * Deal with this in process_complete_pdu(). */ - } - - /* - * Ok - at this point we have a valid RPC_HEADER. - * Keep reading until we have a full pdu. - */ - - data_to_copy = MIN(data_to_copy, p->in_data.pdu_needed_len); - - /* - * Copy as much of the data as we need into the p->in_data.pdu buffer. - * pdu_needed_len becomes zero when we have a complete pdu. - */ - - memcpy((char *)&p->in_data.pdu.data[p->in_data.pdu.length], - data, data_to_copy); - p->in_data.pdu.length += data_to_copy; - p->in_data.pdu_needed_len -= data_to_copy; - - /* - * Do we have a complete PDU ? - * (return the number of bytes handled in the call) - */ - - if(p->in_data.pdu_needed_len == 0) { - process_complete_pdu(p); - return data_to_copy; - } - - DEBUG(10, ("process_incoming_data: not a complete PDU yet. " - "pdu.length = %u, pdu_needed_len = %u\n", - (unsigned int)p->in_data.pdu.length, - (unsigned int)p->in_data.pdu_needed_len)); - - return (ssize_t)data_to_copy; -} - -/**************************************************************************** - Accepts incoming data on an internal rpc pipe. -****************************************************************************/ - -static ssize_t write_to_internal_pipe(struct pipes_struct *p, const char *data, size_t n) -{ - size_t data_left = n; - - while(data_left) { - ssize_t data_used; - - DEBUG(10, ("write_to_pipe: data_left = %u\n", - (unsigned int)data_left)); - - data_used = process_incoming_data(p, data, data_left); - - DEBUG(10, ("write_to_pipe: data_used = %d\n", - (int)data_used)); - - if(data_used < 0) { - return -1; - } - - data_left -= data_used; - data += data_used; - } - - return n; -} - -/**************************************************************************** - Replies to a request to read data from a pipe. - - Headers are interspersed with the data at PDU intervals. By the time - this function is called, the start of the data could possibly have been - read by an SMBtrans (file_offset != 0). - - Calling create_rpc_reply() here is a hack. The data should already - have been prepared into arrays of headers + data stream sections. -****************************************************************************/ - -static ssize_t read_from_internal_pipe(struct pipes_struct *p, char *data, - size_t n, bool *is_data_outstanding) -{ - uint32 pdu_remaining = 0; - ssize_t data_returned = 0; - - if (!p) { - DEBUG(0,("read_from_pipe: pipe not open\n")); - return -1; - } - - DEBUG(6,(" name: %s len: %u\n", - ndr_interface_name(&p->contexts->syntax.uuid, - p->contexts->syntax.if_version), - (unsigned int)n)); - - /* - * We cannot return more than one PDU length per - * read request. - */ - - /* - * This condition should result in the connection being closed. - * Netapp filers seem to set it to 0xffff which results in domain - * authentications failing. Just ignore it so things work. - */ - - if(n > RPC_MAX_PDU_FRAG_LEN) { - DEBUG(5,("read_from_pipe: too large read (%u) requested on " - "pipe %s. We can only service %d sized reads.\n", - (unsigned int)n, - ndr_interface_name(&p->contexts->syntax.uuid, - p->contexts->syntax.if_version), - RPC_MAX_PDU_FRAG_LEN )); - n = RPC_MAX_PDU_FRAG_LEN; - } - - /* - * Determine if there is still data to send in the - * pipe PDU buffer. Always send this first. Never - * send more than is left in the current PDU. The - * client should send a new read request for a new - * PDU. - */ - - pdu_remaining = p->out_data.frag.length - - p->out_data.current_pdu_sent; - - if (pdu_remaining > 0) { - data_returned = (ssize_t)MIN(n, pdu_remaining); - - DEBUG(10,("read_from_pipe: %s: current_pdu_len = %u, " - "current_pdu_sent = %u returning %d bytes.\n", - ndr_interface_name(&p->contexts->syntax.uuid, - p->contexts->syntax.if_version), - (unsigned int)p->out_data.frag.length, - (unsigned int)p->out_data.current_pdu_sent, - (int)data_returned)); - - memcpy(data, - p->out_data.frag.data - + p->out_data.current_pdu_sent, - data_returned); - - p->out_data.current_pdu_sent += (uint32)data_returned; - goto out; - } - - /* - * At this point p->current_pdu_len == p->current_pdu_sent (which - * may of course be zero if this is the first return fragment. - */ - - DEBUG(10,("read_from_pipe: %s: fault_state = %d : data_sent_length " - "= %u, p->out_data.rdata.length = %u.\n", - ndr_interface_name(&p->contexts->syntax.uuid, - p->contexts->syntax.if_version), - (int)p->fault_state, - (unsigned int)p->out_data.data_sent_length, - (unsigned int)p->out_data.rdata.length)); - - if (p->out_data.data_sent_length >= p->out_data.rdata.length) { - /* - * We have sent all possible data, return 0. - */ - data_returned = 0; - goto out; - } - - /* - * We need to create a new PDU from the data left in p->rdata. - * Create the header/data/footers. This also sets up the fields - * p->current_pdu_len, p->current_pdu_sent, p->data_sent_length - * and stores the outgoing PDU in p->current_pdu. - */ - - if(!create_next_pdu(p)) { - DEBUG(0,("read_from_pipe: %s: create_next_pdu failed.\n", - ndr_interface_name(&p->contexts->syntax.uuid, - p->contexts->syntax.if_version))); - return -1; - } - - data_returned = MIN(n, p->out_data.frag.length); - - memcpy(data, p->out_data.frag.data, (size_t)data_returned); - p->out_data.current_pdu_sent += (uint32)data_returned; - - out: - (*is_data_outstanding) = p->out_data.frag.length > n; - - if (p->out_data.current_pdu_sent == p->out_data.frag.length) { - /* We've returned everything in the out_data.frag - * so we're done with this pdu. Free it and reset - * current_pdu_sent. */ - p->out_data.current_pdu_sent = 0; - data_blob_free(&p->out_data.frag); - - if (p->out_data.data_sent_length >= p->out_data.rdata.length) { - /* - * We're completely finished with both outgoing and - * incoming data streams. It's safe to free all - * temporary data from this request. - */ - free_pipe_context(p); - } - } - - return data_returned; -} - bool fsp_is_np(struct files_struct *fsp) { enum FAKE_FILE_TYPE type; diff --git a/source3/rpc_server/srv_pipe_hnd.h b/source3/rpc_server/srv_pipe_hnd.h index b80f4a983d5..f6837dc87ec 100644 --- a/source3/rpc_server/srv_pipe_hnd.h +++ b/source3/rpc_server/srv_pipe_hnd.h @@ -46,6 +46,4 @@ struct tevent_req *np_read_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, NTSTATUS np_read_recv(struct tevent_req *req, ssize_t *nread, bool *is_data_outstanding); -ssize_t process_incoming_data(struct pipes_struct *p, const char *data, size_t n); - #endif /* _RPC_SERVER_SRV_PIPE_HND_H_ */ |