From 6bb92a6d38db41a11e80c4369623d137763f0f52 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Thu, 9 Mar 2000 21:45:16 +0000 Subject: Big update moving the multi-pdu support from 2.0.x into HEAD for JF and the printer functions. Also tidied up some header includes and got the order right so you can now do a : make proto make clean make Jeremy. (This used to be commit 833cd9fba92e4ad5297b235d108dd2be8c17079b) --- source3/rpc_server/srv_pipe_hnd.c | 539 +++++++++++++++++++++++++++++++++----- 1 file changed, 477 insertions(+), 62 deletions(-) (limited to 'source3/rpc_server/srv_pipe_hnd.c') diff --git a/source3/rpc_server/srv_pipe_hnd.c b/source3/rpc_server/srv_pipe_hnd.c index 32a804d5d5..c63390c35f 100644 --- a/source3/rpc_server/srv_pipe_hnd.c +++ b/source3/rpc_server/srv_pipe_hnd.c @@ -1,4 +1,3 @@ - /* * Unix SMB/Netbios implementation. * Version 1.9. @@ -78,67 +77,61 @@ void init_rpc_pipe_hnd(void) Initialise an outgoing packet. ****************************************************************************/ -BOOL pipe_init_outgoing_data(output_data *out_data) +BOOL pipe_init_outgoing_data(output_data *o_data) { - memset(out_data->current_pdu, '\0', sizeof(out_data->current_pdu)); + memset(o_data->current_pdu, '\0', sizeof(o_data->current_pdu)); /* Free any memory in the current return data buffer. */ - prs_mem_free(&out_data->rdata); + prs_mem_free(&o_data->rdata); /* * Initialize the outgoing RPC data buffer. * we will use this as the raw data area for replying to rpc requests. */ - if(!prs_init(&out_data->rdata, 1024, 4, MARSHALL)) { + if(!prs_init(&o_data->rdata, MAX_PDU_FRAG_LEN, 4, MARSHALL)) { DEBUG(0,("pipe_init_outgoing_data: malloc fail.\n")); return False; } /* Reset the offset counters. */ - out_data->data_sent_length = 0; - out_data->current_pdu_len = 0; - out_data->current_pdu_sent = 0; + o_data->data_sent_length = 0; + o_data->current_pdu_len = 0; + o_data->current_pdu_sent = 0; return True; } /**************************************************************************** - Find first available pipe slot. + HACK !!! Attempt to find a remote process to communicate RPC's with. ****************************************************************************/ -pipes_struct *open_rpc_pipe_p(char *pipe_name, - connection_struct *conn, uint16 vuid) +static void attempt_remote_rpc_connect(pipes_struct *p) { - int i; - pipes_struct *p; - static int next_pipe; - struct msrpc_state *m = NULL; - user_struct *vuser = get_valid_user_struct(vuid); struct user_creds usr; + struct msrpc_state *m; + user_struct *vuser = get_valid_user_struct(p->vuid); - ZERO_STRUCT(usr); + p->m = NULL; - DEBUG(4,("Open pipe requested %s (pipes_open=%d)\n", - pipe_name, pipes_open)); - - if (vuser == NULL) - { - DEBUG(4,("invalid vuid %d\n", vuid)); - return NULL; + if (vuser == NULL) { + DEBUG(4,("attempt_remote_rpc_connect: invalid vuid %d\n", (int)p->vuid)); + return; } + ZERO_STRUCT(usr); + /* set up unix credentials from the smb side, to feed over the pipe */ make_creds_unix(&usr.uxc, vuser->name, vuser->requested_name, - vuser->real_name, vuser->guest); + vuser->real_name, vuser->guest); usr.ptr_uxc = 1; make_creds_unix_sec(&usr.uxs, vuser->uid, vuser->gid, - vuser->n_groups, vuser->groups); + vuser->n_groups, vuser->groups); usr.ptr_uxs = 1; usr.ptr_ssk = 1; - DEBUG(0,("user session key not available (yet).\n")); - DEBUG(0,("password-change operations may fail.\n")); + DEBUG(10,("user session key not available (yet).\n")); + DEBUG(10,("password-change operations may fail.\n")); #if USER_SESSION_KEY_DEFINED_IN_VUSER_STRUCT memcpy(usr.usr_sess_key, vuser->usr_sess_key, sizeof(usr.usr_sess_key)); @@ -153,14 +146,28 @@ pipes_struct *open_rpc_pipe_p(char *pipe_name, */ become_root(False); /* to connect to pipe */ - m = msrpc_use_add(pipe_name, getpid(), &usr, False); + p->m = msrpc_use_add(p->name, getpid(), &usr, False); unbecome_root(False); - if (m == NULL) - { - DEBUG(10,("open pipes: msrpc redirect failed - go local.\n")); - } + if (p->m == NULL) + DEBUG(10,("attempt_remote_rpc_connect: msrpc redirect failed - using local implementation.\n")); +} + +/**************************************************************************** + Find first available pipe slot. +****************************************************************************/ + +pipes_struct *open_rpc_pipe_p(char *pipe_name, + connection_struct *conn, uint16 vuid) +{ + int i; + pipes_struct *p; + static int next_pipe; + + DEBUG(4,("Open pipe requested %s (pipes_open=%d)\n", + pipe_name, pipes_open)); + /* not repeating pipe numbers makes it easier to track things in log files and prevents client bugs where pipe numbers are reused over connection restarts */ @@ -180,18 +187,26 @@ pipes_struct *open_rpc_pipe_p(char *pipe_name, DEBUG(5,("open pipes: name %s pnum=%x\n", p->name, p->pnum)); p = (pipes_struct *)malloc(sizeof(*p)); + if (!p) return NULL; ZERO_STRUCTP(p); - /* - * Initialize the RPC and PDU data buffers with no memory. - */ - prs_init(&p->out_data.rdata, 0, 4, MARSHALL); - DLIST_ADD(Pipes, p); + /* + * Initialize the incoming RPC data buffer with one PDU worth of memory. + * We cheat here and say we're marshalling, as we intend to add incoming + * data directly into the prs_struct and we want it to auto grow. We will + * change the type to UNMARSALLING before processing the stream. + */ + + if(!prs_init(&p->in_data.data, MAX_PDU_FRAG_LEN, 4, MARSHALL)) { + DEBUG(0,("open_rpc_pipe_p: malloc fail for in_data struct.\n")); + return NULL; + } + bitmap_set(bmap, i); i += pipe_handle_offset; @@ -204,8 +219,6 @@ pipes_struct *open_rpc_pipe_p(char *pipe_name, p->priority = 0; p->conn = conn; p->vuid = vuid; - - p->m = m; p->max_trans_reply = 0; @@ -213,15 +226,40 @@ pipes_struct *open_rpc_pipe_p(char *pipe_name, p->ntlmssp_auth_validated = False; p->ntlmssp_auth_requested = False; + p->pipe_bound = False; + p->fault_state = False; + + /* + * Initialize the incoming RPC struct. + */ + + p->in_data.pdu_needed_len = 0; + p->in_data.pdu_received_len = 0; + + /* + * Initialize the outgoing RPC struct. + */ + p->out_data.current_pdu_len = 0; p->out_data.current_pdu_sent = 0; p->out_data.data_sent_length = 0; + /* + * Initialize the outgoing RPC data buffer with no memory. + */ + prs_init(&p->out_data.rdata, 0, 4, MARSHALL); + p->uid = (uid_t)-1; p->gid = (gid_t)-1; fstrcpy(p->name, pipe_name); + /* + * HACK !!! For Luke - attempt to connect to RPC redirect process. + */ + + attempt_remote_rpc_connect(p); + DEBUG(4,("Opened pipe %s with handle %x (pipes_open=%d)\n", pipe_name, i, pipes_open)); @@ -234,27 +272,408 @@ pipes_struct *open_rpc_pipe_p(char *pipe_name, return chain_p; } +/**************************************************************************** + Sets the fault state on incoming packets. +****************************************************************************/ + +static void set_incoming_fault(pipes_struct *p) +{ + prs_mem_free(&p->in_data.data); + p->in_data.pdu_needed_len = 0; + p->in_data.pdu_received_len = 0; + p->fault_state = True; + DEBUG(10,("set_incoming_fault: Setting fault state on pipe %s : pnum = 0x%x\n", + p->name, p->pnum )); +} /**************************************************************************** - Accepts incoming data on an rpc pipe. + Ensures we have at least RPC_HEADER_LEN amount of data in the incoming buffer. +****************************************************************************/ - This code is probably incorrect at the moment. The problem is - that the rpc request shouldn't really be executed until all the - data needed for it is received. This currently assumes that each - SMBwrite or SMBwriteX contains all the data needed for an rpc - request. JRA. - ****************************************************************************/ +static ssize_t fill_rpc_header(pipes_struct *p, 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_received_len); + + 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_received_len )); + + memcpy((char *)&p->in_data.current_in_pdu[p->in_data.pdu_received_len], data, len_needed_to_complete_hdr); + p->in_data.pdu_received_len += len_needed_to_complete_hdr; + + return (ssize_t)len_needed_to_complete_hdr; +} + +/**************************************************************************** + Unmarshalls a new PDU header. Assumes the raw header data is in current_in_pdu. +****************************************************************************/ + +static ssize_t unmarshall_rpc_header(pipes_struct *p) +{ + /* + * Unmarshall the header to determine the needed length. + */ + + prs_struct rpc_in; + + if(p->in_data.pdu_received_len != RPC_HEADER_LEN) { + DEBUG(0,("unmarshall_rpc_header: assert on rpc header length failed.\n")); + set_incoming_fault(p); + return -1; + } + + prs_init( &rpc_in, 0, 4, UNMARSHALL); + prs_give_memory( &rpc_in, (char *)&p->in_data.current_in_pdu[0], + p->in_data.pdu_received_len, False); + + /* + * Unmarshall the header as this will tell us how much + * data we need to read to get the complete pdu. + */ + + if(!smb_io_rpc_hdr("", &p->hdr, &rpc_in, 0)) { + DEBUG(0,("unmarshall_rpc_header: failed to unmarshall RPC_HDR.\n")); + set_incoming_fault(p); + return -1; + } + + /* + * Validate the RPC header. + */ + + if(p->hdr.major != 5 && p->hdr.minor != 0) { + DEBUG(0,("unmarshall_rpc_header: invalid major/minor numbers in RPC_HDR.\n")); + set_incoming_fault(p); + return -1; + } + + /* + * If there is no data in the incoming buffer and it's a requst pdu then + * ensure that the FIRST flag is set. If not then we have + * a stream missmatch. + */ + + if((p->hdr.pkt_type == RPC_REQUEST) && (prs_offset(&p->in_data.data) == 0) && !(p->hdr.flags & RPC_FLG_FIRST)) { + DEBUG(0,("unmarshall_rpc_header: FIRST flag not set in first PDU !\n")); + set_incoming_fault(p); + return -1; + } + + /* + * Ensure that the pdu length is sane. + */ + + if((p->hdr.frag_len < RPC_HEADER_LEN) || (p->hdr.frag_len > MAX_PDU_FRAG_LEN)) { + DEBUG(0,("unmarshall_rpc_header: assert on frag length failed.\n")); + set_incoming_fault(p); + return -1; + } + + DEBUG(10,("unmarshall_rpc_header: type = %u, flags = %u\n", (unsigned int)p->hdr.pkt_type, + (unsigned int)p->hdr.flags )); + + /* + * Adjust for the header we just ate. + */ + p->in_data.pdu_received_len = 0; + p->in_data.pdu_needed_len = (uint32)p->hdr.frag_len - RPC_HEADER_LEN; + + /* + * Null the data we just ate. + */ + + memset((char *)&p->in_data.current_in_pdu[0], '\0', RPC_HEADER_LEN); + + return 0; /* No extra data processed. */ +} + +/**************************************************************************** + Processes a request pdu. This will do auth processing if needed, and + appends the data into the complete stream if the LAST flag is not set. +****************************************************************************/ + +static BOOL process_request_pdu(pipes_struct *p, prs_struct *rpc_in_p) +{ + BOOL auth_verify = IS_BITS_SET_ALL(p->ntlmssp_chal_flags, NTLMSSP_NEGOTIATE_SIGN); + size_t data_len = p->hdr.frag_len - RPC_HEADER_LEN - RPC_HDR_REQ_LEN - + (auth_verify ? RPC_HDR_AUTH_LEN : 0) - p->hdr.auth_len; + + if(!p->pipe_bound) { + DEBUG(0,("process_request_pdu: rpc request with no bind.\n")); + set_incoming_fault(p); + return False; + } + + /* + * Check if we need to do authentication processing. + * This is only done on requests, not binds. + */ + + /* + * Read the RPC request header. + */ + + if(!smb_io_rpc_hdr_req("req", &p->hdr_req, rpc_in_p, 0)) { + DEBUG(0,("process_request_pdu: failed to unmarshall RPC_HDR_REQ.\n")); + set_incoming_fault(p); + return False; + } + + if(p->ntlmssp_auth_validated && !api_pipe_auth_process(p, rpc_in_p)) { + DEBUG(0,("process_request_pdu: failed to do auth processing.\n")); + set_incoming_fault(p); + return False; + } + + if (p->ntlmssp_auth_requested && !p->ntlmssp_auth_validated) { + + /* + * Authentication _was_ requested and it already failed. + */ + + DEBUG(0,("process_request_pdu: RPC request received on pipe %s where \ +authentication failed. Denying the request.\n", p->name)); + set_incoming_fault(p); + return False; + } + + /* + * Check the data length doesn't go over the 1Mb limit. + */ + + if(prs_data_size(&p->in_data.data) + data_len > 1024*1024) { + DEBUG(0,("process_request_pdu: rpc data buffer too large (%u) + (%u)\n", + (unsigned int)prs_data_size(&p->in_data.data), (unsigned int)data_len )); + set_incoming_fault(p); + return False; + } + + /* + * Append the data portion into the buffer and return. + */ + + { + char *data_from = prs_data_p(rpc_in_p) + prs_offset(rpc_in_p); + + if(!prs_append_data(&p->in_data.data, data_from, data_len)) { + DEBUG(0,("process_request_pdu: Unable to append data size %u to parse buffer of size %u.\n", + (unsigned int)data_len, (unsigned int)prs_data_size(&p->in_data.data) )); + set_incoming_fault(p); + return False; + } + + } + + if(p->hdr.flags & RPC_FLG_LAST) { + BOOL ret; + /* + * Ok - we finally have a complete RPC stream. + * Call the rpc command to process it. + */ + + /* + * Set the parse offset to the start of the data and set the + * prs_struct to UNMARSHALL. + */ + + prs_set_offset(&p->in_data.data, 0); + prs_switch_type(&p->in_data.data, UNMARSHALL); + + /* + * Process the complete data stream here. + */ + + ret = api_pipe_request(p); + + /* + * We have consumed the whole data stream. Set back to + * marshalling and set the offset back to the start of + * the buffer to re-use it (we could also do a prs_mem_free() + * and then re_init on the next start of PDU. Not sure which + * is best here.... JRA. + */ + + prs_switch_type(&p->in_data.data, MARSHALL); + prs_set_offset(&p->in_data.data, 0); + return ret; + } + + return True; +} + +/**************************************************************************** + Processes a finished PDU stored in current_in_pdu. The RPC_HEADER has + already been parsed and stored in p->hdr. +****************************************************************************/ + +static ssize_t process_complete_pdu(pipes_struct *p) +{ + prs_struct rpc_in; + size_t data_len = p->in_data.pdu_received_len; + char *data_p = (char *)&p->in_data.current_in_pdu[0]; + BOOL reply = False; + + if(p->fault_state) { + DEBUG(10,("process_complete_pdu: pipe %s in fault state.\n", + p->name )); + set_incoming_fault(p); + setup_fault_pdu(p); + return (ssize_t)data_len; + } + + prs_init( &rpc_in, 0, 4, UNMARSHALL); + prs_give_memory( &rpc_in, data_p, (uint32)data_len, False); + + DEBUG(10,("process_complete_pdu: processing packet type %u\n", + (unsigned int)p->hdr.pkt_type )); + + switch (p->hdr.pkt_type) { + case RPC_BIND: + case RPC_ALTCONT: + /* + * We assume that a pipe bind is only in one pdu. + */ + reply = api_pipe_bind_req(p, &rpc_in); + break; + case RPC_BINDRESP: + /* + * We assume that a pipe bind_resp is only in one pdu. + */ + reply = api_pipe_bind_auth_resp(p, &rpc_in); + break; + case RPC_REQUEST: + reply = process_request_pdu(p, &rpc_in); + break; + default: + DEBUG(0,("process_complete_pdu: Unknown rpc type = %u received.\n", (unsigned int)p->hdr.pkt_type )); + break; + } + + if (!reply) { + DEBUG(3,("process_complete_pdu: DCE/RPC fault sent on pipe %s\n", p->pipe_srv_name)); + set_incoming_fault(p); + setup_fault_pdu(p); + } else { + /* + * Reset the lengths. We're ready for a new pdu. + */ + p->in_data.pdu_needed_len = 0; + p->in_data.pdu_received_len = 0; + } + + return (ssize_t)data_len; +} + +/**************************************************************************** + Accepts incoming data on an rpc pipe. Processes the data in pdu sized units. +****************************************************************************/ + +static ssize_t process_incoming_data(pipes_struct *p, char *data, size_t n) +{ + size_t data_to_copy = MIN(n, MAX_PDU_FRAG_LEN - p->in_data.pdu_received_len); + + DEBUG(10,("process_incoming_data: Start: pdu_received_len = %u, pdu_needed_len = %u, incoming data = %u\n", + (unsigned int)p->in_data.pdu_received_len, (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_received_len, (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_received_len < 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 current_in_pdu. + */ + + /* + * If pdu_needed_len is zero this is a new pdu. + * Unmarshall the header so we know how much more + * data we need, then loop again. + */ + + if(p->in_data.pdu_needed_len == 0) + return unmarshall_rpc_header(p); + + /* + * Ok - at this point we have a valid RPC_HEADER in p->hdr. + * 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 current_in_pdu buffer. + */ + + memcpy( (char *)&p->in_data.current_in_pdu[p->in_data.pdu_received_len], data, data_to_copy); + p->in_data.pdu_received_len += data_to_copy; + + /* + * Do we have a complete PDU ? + */ + + if(p->in_data.pdu_received_len == p->in_data.pdu_needed_len) + return process_complete_pdu(p); + + DEBUG(10,("process_incoming_data: not a complete PDU yet. pdu_received_len = %u, pdu_needed_len = %u\n", + (unsigned int)p->in_data.pdu_received_len, (unsigned int)p->in_data.pdu_needed_len )); + + return (ssize_t)data_to_copy; + +} + +/**************************************************************************** + Accepts incoming data on an rpc pipe. +****************************************************************************/ ssize_t write_to_pipe(pipes_struct *p, char *data, size_t n) { + size_t data_left = n; + DEBUG(6,("write_to_pipe: %x", p->pnum)); - DEBUG(6,("name: %s open: %s len: %d", + DEBUG(6,(" name: %s open: %s len: %d\n", p->name, BOOLSTR(p->open), (int)n)); dump_data(50, data, n); - return rpc_command(p, data, (int)n) ? ((ssize_t)n) : -1; + 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; } @@ -282,7 +701,7 @@ int read_from_pipe(pipes_struct *p, char *data, int n) DEBUG(6,("read_from_pipe: %x", p->pnum)); - DEBUG(6,("name: %s len: %d\n", p->name, n)); + DEBUG(6,(" name: %s len: %d\n", p->name, n)); /* * We cannot return more than one PDU length per @@ -320,8 +739,9 @@ returning %d bytes.\n", p->name, (unsigned int)p->out_data.current_pdu_len, * may of course be zero if this is the first return fragment. */ - DEBUG(10,("read_from_pipe: %s: data_sent_length = %u, prs_offset(&p->out_data.rdata) = %u.\n", - p->name, (unsigned int)p->out_data.data_sent_length, (unsigned int)prs_offset(&p->out_data.rdata) )); + DEBUG(10,("read_from_pipe: %s: fault_state = %d : data_sent_length \ += %u, prs_offset(&p->out_data.rdata) = %u.\n", + p->name, (int)p->fault_state, (unsigned int)p->out_data.data_sent_length, (unsigned int)prs_offset(&p->out_data.rdata) )); if(p->out_data.data_sent_length >= prs_offset(&p->out_data.rdata)) { /* @@ -415,22 +835,17 @@ BOOL close_rpc_pipe_hnd(pipes_struct *p, connection_struct *conn) pipes_open--; - DEBUG(4,("closed pipe name %s pnum=%x (pipes_open=%d)\n", - p->name, p->pnum, pipes_open)); - - if (p->m != NULL) - { - DEBUG(4,("closed msrpc redirect: ")); + if (p->m != NULL) { + DEBUG(4,("close_rpc_pipe_hnd: closing msrpc redirect: ")); if (msrpc_use_del(p->m->pipe_name, &p->m->usr, False, NULL)) - { DEBUG(4,("OK\n")); - } else - { DEBUG(4,("FAILED\n")); - } } + DEBUG(4,("closed pipe name %s pnum=%x (pipes_open=%d)\n", + p->name, p->pnum, pipes_open)); + DLIST_REMOVE(Pipes, p); ZERO_STRUCTP(p); -- cgit