s3/rpc_server: don't unmarshall PDUs twice
authorDavid Disseldorp <ddiss@samba.org>
Tue, 10 Dec 2013 12:59:06 +0000 (13:59 +0100)
committerStefan Metzmacher <metze@samba.org>
Wed, 11 Dec 2013 21:24:31 +0000 (22:24 +0100)
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

source3/rpc_server/rpc_server.c
source3/rpc_server/rpc_server.h
source3/rpc_server/srv_pipe.c
source3/rpc_server/srv_pipe_hnd.c
source3/rpc_server/srv_pipe_hnd.h

index f283559dddd4e6d6966453abba1cba01938f64d4..d10d3ac3b1218d3f3eacf95717a6ed184dbbea59 100644 (file)
@@ -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);
 
index d204a8b3adc6bf66823a054eaca653c3ec21f5b6..0d7bdb527450f87b5f9311978244f26872e53ffe 100644 (file)
@@ -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,
index f92de3b594e70f86b9efeee61180b13ab73ade00..8f30386a6eaf6fe0c5582587271e084ee860d5df 100644 (file)
@@ -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 */
 }
 
index a95aa06525a780f0187699b75c9bdeec5e9bb929..a05fafd4b070c34c2d206dfc7ec51921fb7d68b5 100644 (file)
 #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;
index b80f4a983d58de10b620c95e60ccdf5c3889bfde..f6837dc87ec3f61cf91235d3e69b138bdc859f0e 100644 (file)
@@ -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_ */