/* useful function to store a structure in rpc wire format */ int tdb_prs_store(TDB_CONTEXT *tdb, char *keystr, prs_struct *ps) { TDB_DATA kbuf, dbuf; kbuf.dptr = keystr; kbuf.dsize = strlen(keystr)+1; dbuf.dptr = prs_data_p(ps); dbuf.dsize = prs_offset(ps); return tdb_store(tdb, kbuf, dbuf, TDB_REPLACE); }
BOOL prs_append_prs_data(prs_struct *dst, prs_struct *src) { if(!prs_grow(dst, prs_offset(src))) return False; memcpy(&dst->data_p[dst->data_offset], prs_data_p(src), (size_t)prs_offset(src)); dst->data_offset += prs_offset(src); return True; }
BOOL prs_append_some_prs_data(prs_struct *dst, prs_struct *src, int32 start, uint32 len) { if (len == 0) return True; if(!prs_grow(dst, len)) return False; memcpy(&dst->data_p[dst->data_offset], prs_data_p(src)+start, (size_t)len); dst->data_offset += len; return True; }
/******************************************************************* hash a stream. ********************************************************************/ BOOL prs_hash1(prs_struct *ps, uint32 offset, uint8 sess_key[16]) { char *q; q = prs_data_p(ps); q = &q[offset]; #ifdef DEBUG_PASSWORD DEBUG(100, ("prs_hash1\n")); dump_data(100, sess_key, 16); dump_data(100, q, 68); #endif SamOEMhash((uchar *) q, sess_key, 68); #ifdef DEBUG_PASSWORD dump_data(100, q, 68); #endif return True; }
static BOOL rpc_send_auth_reply(struct cli_state *cli, prs_struct *rdata, uint32 rpc_call_id) { prs_struct rpc_out; ssize_t ret; prs_init(&rpc_out, RPC_HEADER_LEN + RPC_HDR_AUTHA_LEN, /* need at least this much */ cli->mem_ctx, MARSHALL); if (!NT_STATUS_IS_OK(create_rpc_bind_resp(cli, rpc_call_id, &rpc_out))) { return False; } if ((ret = cli_write(cli, cli->nt_pipe_fnum, 0x8, prs_data_p(&rpc_out), 0, (size_t)prs_offset(&rpc_out))) != (ssize_t)prs_offset(&rpc_out)) { DEBUG(0,("rpc_send_auth_reply: cli_write failed. Return was %d\n", (int)ret)); prs_mem_free(&rpc_out); return False; } prs_mem_free(&rpc_out); return True; }
/**************************************************************************** set the security descriptor for a open file ****************************************************************************/ BOOL cli_set_secdesc(struct cli_state *cli, int fnum, SEC_DESC *sd) { char param[8]; char *rparam=NULL, *rdata=NULL; unsigned int rparam_count=0, rdata_count=0; uint32 sec_info = 0; TALLOC_CTX *mem_ctx; prs_struct pd; BOOL ret = False; if ((mem_ctx = talloc_init("cli_set_secdesc")) == NULL) { DEBUG(0,("talloc_init failed.\n")); goto cleanup; } prs_init(&pd, 0, mem_ctx, MARSHALL); prs_give_memory(&pd, NULL, 0, True); if (!sec_io_desc("sd data", &sd, &pd, 1)) { DEBUG(1,("Failed to marshall secdesc\n")); goto cleanup; } SIVAL(param, 0, fnum); if (sd->off_dacl) sec_info |= DACL_SECURITY_INFORMATION; if (sd->off_owner_sid) sec_info |= OWNER_SECURITY_INFORMATION; if (sd->off_grp_sid) sec_info |= GROUP_SECURITY_INFORMATION; SSVAL(param, 4, sec_info); if (!cli_send_nt_trans(cli, NT_TRANSACT_SET_SECURITY_DESC, 0, NULL, 0, 0, param, 8, 0, prs_data_p(&pd), prs_offset(&pd), 0)) { DEBUG(1,("Failed to send NT_TRANSACT_SET_SECURITY_DESC\n")); goto cleanup; } if (!cli_receive_nt_trans(cli, &rparam, &rparam_count, &rdata, &rdata_count)) { DEBUG(1,("NT_TRANSACT_SET_SECURITY_DESC failed\n")); goto cleanup; } ret = True; cleanup: SAFE_FREE(rparam); SAFE_FREE(rdata); talloc_destroy(mem_ctx); prs_mem_free(&pd); return ret; }
BOOL prs_unistr(const char *name, prs_struct *ps, int depth, UNISTR *str) { int len = 0; unsigned char *p = (unsigned char *)str->buffer; uint8 *start; char *q; uint32 max_len; uint16* ptr; if (MARSHALLING(ps)) { for(len = 0; str->buffer[len] != 0; len++) ; q = prs_mem_get(ps, (len+1)*2); if (q == NULL) return False; start = (uint8*)q; for(len = 0; str->buffer[len] != 0; len++) { if(ps->bigendian_data) { /* swap bytes - p is little endian, q is big endian. */ q[0] = (char)p[1]; q[1] = (char)p[0]; p += 2; q += 2; } else { q[0] = (char)p[0]; q[1] = (char)p[1]; p += 2; q += 2; } } /* * even if the string is 'empty' (only an \0 char) * at this point the leading \0 hasn't been parsed. * so parse it now */ q[0] = 0; q[1] = 0; q += 2; len++; dump_data(5+depth, (char *)start, len * 2); } else { /* unmarshalling */ uint32 alloc_len = 0; q = prs_data_p(ps) + prs_offset(ps); /* * Work out how much space we need and talloc it. */ max_len = (ps->buffer_size - ps->data_offset)/sizeof(uint16); /* the test of the value of *ptr helps to catch the circumstance where we have an emtpty (non-existent) string in the buffer */ for ( ptr = (uint16 *)q; *ptr && (alloc_len <= max_len); alloc_len++) /* do nothing */ ; /* should we allocate anything at all? */ str->buffer = (uint16 *)prs_alloc_mem(ps,alloc_len * sizeof(uint16)); if ((str->buffer == NULL) && (alloc_len > 0)) return False; p = (unsigned char *)str->buffer; len = 0; /* the (len < alloc_len) test is to prevent us from overwriting memory that is not ours...if we get that far, we have a non-null terminated string in the buffer and have messed up somewhere */ while ((len < alloc_len) && (*(uint16 *)q != 0)) { if(ps->bigendian_data) { /* swap bytes - q is big endian, p is little endian. */ p[0] = (unsigned char)q[1]; p[1] = (unsigned char)q[0]; p += 2; q += 2; } else { p[0] = (unsigned char)q[0]; p[1] = (unsigned char)q[1]; p += 2; q += 2; } len++; } if (len < alloc_len) { /* NULL terminate the UNISTR */ str->buffer[len++] = '\0'; } } /* set the offset in the prs_struct; 'len' points to the terminiating NULL in the UNISTR so we need to go one more uint16 */ ps->data_offset += (len)*2; return True; }
static void fill_in_printer_values( NT_PRINTER_INFO_LEVEL_2 *info2, REGVAL_CTR *values ) { DEVICEMODE *devmode; prs_struct prs; uint32 offset; UNISTR2 data; char *p; uint32 printer_status = PRINTER_STATUS_OK; regval_ctr_addvalue( values, "Attributes", REG_DWORD, (char*)&info2->attributes, sizeof(info2->attributes) ); regval_ctr_addvalue( values, "Priority", REG_DWORD, (char*)&info2->priority, sizeof(info2->attributes) ); regval_ctr_addvalue( values, "ChangeID", REG_DWORD, (char*)&info2->changeid, sizeof(info2->changeid) ); regval_ctr_addvalue( values, "Default Priority", REG_DWORD, (char*)&info2->default_priority, sizeof(info2->default_priority) ); /* lie and say everything is ok since we don't want to call print_queue_length() to get the real status */ regval_ctr_addvalue( values, "Status", REG_DWORD, (char*)&printer_status, sizeof(info2->status) ); regval_ctr_addvalue( values, "StartTime", REG_DWORD, (char*)&info2->starttime, sizeof(info2->starttime) ); regval_ctr_addvalue( values, "UntilTime", REG_DWORD, (char*)&info2->untiltime, sizeof(info2->untiltime) ); /* strip the \\server\ from this string */ if ( !(p = strrchr( info2->printername, '\\' ) ) ) p = info2->printername; else p++; init_unistr2( &data, p, UNI_STR_TERMINATE); regval_ctr_addvalue( values, "Name", REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) ); init_unistr2( &data, info2->location, UNI_STR_TERMINATE); regval_ctr_addvalue( values, "Location", REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) ); init_unistr2( &data, info2->comment, UNI_STR_TERMINATE); regval_ctr_addvalue( values, "Description", REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) ); init_unistr2( &data, info2->parameters, UNI_STR_TERMINATE); regval_ctr_addvalue( values, "Parameters", REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) ); init_unistr2( &data, info2->portname, UNI_STR_TERMINATE); regval_ctr_addvalue( values, "Port", REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) ); init_unistr2( &data, info2->sharename, UNI_STR_TERMINATE); regval_ctr_addvalue( values, "Share Name", REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) ); init_unistr2( &data, info2->drivername, UNI_STR_TERMINATE); regval_ctr_addvalue( values, "Printer Driver", REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) ); init_unistr2( &data, info2->sepfile, UNI_STR_TERMINATE); regval_ctr_addvalue( values, "Separator File", REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) ); init_unistr2( &data, "WinPrint", UNI_STR_TERMINATE); regval_ctr_addvalue( values, "Print Processor", REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) ); init_unistr2( &data, "RAW", UNI_STR_TERMINATE); regval_ctr_addvalue( values, "Datatype", REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) ); /* use a prs_struct for converting the devmode and security descriptor to REG_BINARY */ prs_init( &prs, RPC_MAX_PDU_FRAG_LEN, values, MARSHALL); /* stream the device mode */ if ( (devmode = construct_dev_mode( info2->sharename )) != NULL ) { if ( spoolss_io_devmode( "devmode", &prs, 0, devmode ) ) { offset = prs_offset( &prs ); regval_ctr_addvalue( values, "Default Devmode", REG_BINARY, prs_data_p(&prs), offset ); } } prs_mem_clear( &prs ); prs_set_offset( &prs, 0 ); /* stream the printer security descriptor */ if ( info2->secdesc_buf && info2->secdesc_buf->len ) { if ( sec_io_desc("sec_desc", &info2->secdesc_buf->sec, &prs, 0 ) ) { offset = prs_offset( &prs ); regval_ctr_addvalue( values, "Security", REG_BINARY, prs_data_p(&prs), offset ); } } prs_mem_free( &prs ); return; }
BOOL create_next_pdu(pipes_struct *p) { RPC_HDR_RESP hdr_resp; BOOL auth_verify = ((p->ntlmssp_chal_flags & NTLMSSP_NEGOTIATE_SIGN) != 0); BOOL auth_seal = ((p->ntlmssp_chal_flags & NTLMSSP_NEGOTIATE_SEAL) != 0); uint32 ss_padding_len = 0; uint32 data_len; uint32 data_space_available; uint32 data_len_left; prs_struct outgoing_pdu; uint32 data_pos; /* * If we're in the fault state, keep returning fault PDU's until * the pipe gets closed. JRA. */ if(p->fault_state) { setup_fault_pdu(p, NT_STATUS(0x1c010002)); return True; } memset((char *)&hdr_resp, '\0', sizeof(hdr_resp)); /* Change the incoming request header to a response. */ p->hdr.pkt_type = RPC_RESPONSE; /* Set up rpc header flags. */ if (p->out_data.data_sent_length == 0) { p->hdr.flags = RPC_FLG_FIRST; } else { p->hdr.flags = 0; } /* * Work out how much we can fit in a single PDU. */ data_space_available = sizeof(p->out_data.current_pdu) - RPC_HEADER_LEN - RPC_HDR_RESP_LEN; if(p->ntlmssp_auth_validated) { data_space_available -= (RPC_HDR_AUTH_LEN + RPC_AUTH_NTLMSSP_CHK_LEN); } else if(p->netsec_auth_validated) { data_space_available -= (RPC_HDR_AUTH_LEN + RPC_AUTH_NETSEC_SIGN_OR_SEAL_CHK_LEN); } /* * The amount we send is the minimum of the available * space and the amount left to send. */ data_len_left = prs_offset(&p->out_data.rdata) - p->out_data.data_sent_length; /* * Ensure there really is data left to send. */ if(!data_len_left) { DEBUG(0,("create_next_pdu: no data left to send !\n")); return False; } data_len = MIN(data_len_left, data_space_available); /* * Set up the alloc hint. This should be the data left to * send. */ hdr_resp.alloc_hint = data_len_left; /* * Work out if this PDU will be the last. */ if(p->out_data.data_sent_length + data_len >= prs_offset(&p->out_data.rdata)) { p->hdr.flags |= RPC_FLG_LAST; if ((auth_seal || auth_verify) && (data_len_left % 8)) { ss_padding_len = 8 - (data_len_left % 8); DEBUG(10,("create_next_pdu: adding sign/seal padding of %u\n", ss_padding_len )); } } /* * Set up the header lengths. */ if (p->ntlmssp_auth_validated) { p->hdr.frag_len = RPC_HEADER_LEN + RPC_HDR_RESP_LEN + data_len + ss_padding_len + RPC_HDR_AUTH_LEN + RPC_AUTH_NTLMSSP_CHK_LEN; p->hdr.auth_len = RPC_AUTH_NTLMSSP_CHK_LEN; } else if (p->netsec_auth_validated) { p->hdr.frag_len = RPC_HEADER_LEN + RPC_HDR_RESP_LEN + data_len + ss_padding_len + RPC_HDR_AUTH_LEN + RPC_AUTH_NETSEC_SIGN_OR_SEAL_CHK_LEN; p->hdr.auth_len = RPC_AUTH_NETSEC_SIGN_OR_SEAL_CHK_LEN; } else { p->hdr.frag_len = RPC_HEADER_LEN + RPC_HDR_RESP_LEN + data_len; p->hdr.auth_len = 0; } /* * Init the parse struct to point at the outgoing * data. */ prs_init( &outgoing_pdu, 0, p->mem_ctx, MARSHALL); prs_give_memory( &outgoing_pdu, (char *)p->out_data.current_pdu, sizeof(p->out_data.current_pdu), False); /* Store the header in the data stream. */ if(!smb_io_rpc_hdr("hdr", &p->hdr, &outgoing_pdu, 0)) { DEBUG(0,("create_next_pdu: failed to marshall RPC_HDR.\n")); prs_mem_free(&outgoing_pdu); return False; } if(!smb_io_rpc_hdr_resp("resp", &hdr_resp, &outgoing_pdu, 0)) { DEBUG(0,("create_next_pdu: failed to marshall RPC_HDR_RESP.\n")); prs_mem_free(&outgoing_pdu); return False; } /* Store the current offset. */ data_pos = prs_offset(&outgoing_pdu); /* Copy the data into the PDU. */ if(!prs_append_some_prs_data(&outgoing_pdu, &p->out_data.rdata, p->out_data.data_sent_length, data_len)) { DEBUG(0,("create_next_pdu: failed to copy %u bytes of data.\n", (unsigned int)data_len)); prs_mem_free(&outgoing_pdu); return False; } /* Copy the sign/seal padding data. */ if (ss_padding_len) { char pad[8]; memset(pad, '\0', 8); if (!prs_copy_data_in(&outgoing_pdu, pad, ss_padding_len)) { DEBUG(0,("create_next_pdu: failed to add %u bytes of pad data.\n", (unsigned int)ss_padding_len)); prs_mem_free(&outgoing_pdu); return False; } } if (p->ntlmssp_auth_validated) { /* * NTLMSSP processing. Mutually exclusive with Schannel. */ uint32 crc32 = 0; char *data; DEBUG(5,("create_next_pdu: sign: %s seal: %s data %d auth %d\n", BOOLSTR(auth_verify), BOOLSTR(auth_seal), data_len + ss_padding_len, p->hdr.auth_len)); /* * Set data to point to where we copied the data into. */ data = prs_data_p(&outgoing_pdu) + data_pos; if (auth_seal) { crc32 = crc32_calc_buffer(data, data_len + ss_padding_len); NTLMSSPcalc_p(p, (uchar*)data, data_len + ss_padding_len); } if (auth_seal || auth_verify) { RPC_HDR_AUTH auth_info; init_rpc_hdr_auth(&auth_info, NTLMSSP_AUTH_TYPE, auth_seal ? RPC_PIPE_AUTH_SEAL_LEVEL : RPC_PIPE_AUTH_SIGN_LEVEL, (auth_verify ? ss_padding_len : 0), (auth_verify ? 1 : 0)); if(!smb_io_rpc_hdr_auth("hdr_auth", &auth_info, &outgoing_pdu, 0)) { DEBUG(0,("create_next_pdu: failed to marshall RPC_HDR_AUTH.\n")); prs_mem_free(&outgoing_pdu); return False; } } if (auth_verify) { RPC_AUTH_NTLMSSP_CHK ntlmssp_chk; char *auth_data = prs_data_p(&outgoing_pdu); p->ntlmssp_seq_num++; init_rpc_auth_ntlmssp_chk(&ntlmssp_chk, NTLMSSP_SIGN_VERSION, crc32, p->ntlmssp_seq_num++); auth_data = prs_data_p(&outgoing_pdu) + prs_offset(&outgoing_pdu) + 4; if(!smb_io_rpc_auth_ntlmssp_chk("auth_sign", &ntlmssp_chk, &outgoing_pdu, 0)) { DEBUG(0,("create_next_pdu: failed to marshall RPC_AUTH_NTLMSSP_CHK.\n")); prs_mem_free(&outgoing_pdu); return False; } NTLMSSPcalc_p(p, (uchar*)auth_data, RPC_AUTH_NTLMSSP_CHK_LEN - 4); } } else if (p->netsec_auth_validated) { /* * Schannel processing. Mutually exclusive with NTLMSSP. */ int auth_type, auth_level; char *data; RPC_HDR_AUTH auth_info; RPC_AUTH_NETSEC_CHK verf; prs_struct rverf; prs_struct rauth; data = prs_data_p(&outgoing_pdu) + data_pos; /* Check it's the type of reply we were expecting to decode */ get_auth_type_level(p->netsec_auth.auth_flags, &auth_type, &auth_level); init_rpc_hdr_auth(&auth_info, auth_type, auth_level, ss_padding_len, 1); if(!smb_io_rpc_hdr_auth("hdr_auth", &auth_info, &outgoing_pdu, 0)) { DEBUG(0,("create_next_pdu: failed to marshall RPC_HDR_AUTH.\n")); prs_mem_free(&outgoing_pdu); return False; } prs_init(&rverf, 0, p->mem_ctx, MARSHALL); prs_init(&rauth, 0, p->mem_ctx, MARSHALL); netsec_encode(&p->netsec_auth, p->netsec_auth.auth_flags, SENDER_IS_ACCEPTOR, &verf, data, data_len + ss_padding_len); smb_io_rpc_auth_netsec_chk("", RPC_AUTH_NETSEC_SIGN_OR_SEAL_CHK_LEN, &verf, &outgoing_pdu, 0); p->netsec_auth.seq_num++; } /* * Setup the counts for this PDU. */ p->out_data.data_sent_length += data_len; p->out_data.current_pdu_len = p->hdr.frag_len; p->out_data.current_pdu_sent = 0; prs_mem_free(&outgoing_pdu); return True; }
BOOL rpc_api_pipe_req(struct cli_state *cli, uint8 op_num, prs_struct *data, prs_struct *rdata) { uint32 auth_len, real_auth_len, auth_hdr_len, max_data, data_left, data_sent; NTSTATUS nt_status; BOOL ret = False; uint32 callid = 0; fstring dump_name; auth_len = 0; real_auth_len = 0; auth_hdr_len = 0; if (cli->pipe_auth_flags & AUTH_PIPE_SIGN) { if (cli->pipe_auth_flags & AUTH_PIPE_NTLMSSP) { auth_len = RPC_AUTH_NTLMSSP_CHK_LEN; } if (cli->pipe_auth_flags & AUTH_PIPE_NETSEC) { auth_len = RPC_AUTH_NETSEC_CHK_LEN; } auth_hdr_len = RPC_HDR_AUTH_LEN; } /* * calc how much actual data we can send in a PDU fragment */ max_data = cli->max_xmit_frag - RPC_HEADER_LEN - RPC_HDR_REQ_LEN - auth_hdr_len - auth_len - 8; for (data_left = prs_offset(data), data_sent = 0; data_left > 0;) { prs_struct outgoing_packet; prs_struct sec_blob; uint32 data_len, send_size; uint8 flags = 0; uint32 auth_padding = 0; DATA_BLOB sign_blob; /* * how much will we send this time */ send_size = MIN(data_left, max_data); if (!prs_init(&sec_blob, send_size, /* will need at least this much */ cli->mem_ctx, MARSHALL)) { DEBUG(0,("Could not malloc %u bytes", send_size+auth_padding)); return False; } if(!prs_append_some_prs_data(&sec_blob, data, data_sent, send_size)) { DEBUG(0,("Failed to append data to netsec blob\n")); prs_mem_free(&sec_blob); return False; } /* * NT expects the data that is sealed to be 8-byte * aligned. The padding must be encrypted as well and * taken into account when generating the * authentication verifier. The amount of padding must * be stored in the auth header. */ if (cli->pipe_auth_flags) { size_t data_and_padding_size; int auth_type; int auth_level; prs_align_uint64(&sec_blob); get_auth_type_level(cli->pipe_auth_flags, &auth_type, &auth_level); data_and_padding_size = prs_offset(&sec_blob); auth_padding = data_and_padding_size - send_size; /* insert the auth header */ if(!create_auth_hdr(&sec_blob, auth_type, auth_level, auth_padding)) { prs_mem_free(&sec_blob); return False; } /* create an NTLMSSP signature */ if (cli->pipe_auth_flags & AUTH_PIPE_NTLMSSP) { /* * Seal the outgoing data if requested. */ if (cli->pipe_auth_flags & AUTH_PIPE_SEAL) { nt_status = ntlmssp_seal_packet(cli->ntlmssp_pipe_state, (unsigned char*)prs_data_p(&sec_blob), data_and_padding_size, &sign_blob); if (!NT_STATUS_IS_OK(nt_status)) { prs_mem_free(&sec_blob); return False; } } else if (cli->pipe_auth_flags & AUTH_PIPE_SIGN) { nt_status = ntlmssp_sign_packet(cli->ntlmssp_pipe_state, (unsigned char*)prs_data_p(&sec_blob), data_and_padding_size, &sign_blob); if (!NT_STATUS_IS_OK(nt_status)) { prs_mem_free(&sec_blob); return False; } } /* write auth footer onto the packet */ real_auth_len = sign_blob.length; prs_copy_data_in(&sec_blob, (char *)sign_blob.data, sign_blob.length); data_blob_free(&sign_blob); } else if (cli->pipe_auth_flags & AUTH_PIPE_NETSEC) { size_t parse_offset_marker; RPC_AUTH_NETSEC_CHK verf; DEBUG(10,("SCHANNEL seq_num=%d\n", cli->auth_info.seq_num)); netsec_encode(&cli->auth_info, cli->pipe_auth_flags, SENDER_IS_INITIATOR, &verf, prs_data_p(&sec_blob), data_and_padding_size); cli->auth_info.seq_num++; /* write auth footer onto the packet */ parse_offset_marker = prs_offset(&sec_blob); if (!smb_io_rpc_auth_netsec_chk("", &verf, &sec_blob, 0)) { prs_mem_free(&sec_blob); return False; } real_auth_len = prs_offset(&sec_blob) - parse_offset_marker; } } data_len = RPC_HEADER_LEN + RPC_HDR_REQ_LEN + prs_offset(&sec_blob); /* * Malloc parse struct to hold it (and enough for alignments). */ if(!prs_init(&outgoing_packet, data_len + 8, cli->mem_ctx, MARSHALL)) { DEBUG(0,("rpc_api_pipe_req: Failed to malloc %u bytes.\n", (unsigned int)data_len )); return False; } if (data_left == prs_offset(data)) flags |= RPC_FLG_FIRST; if (data_left <= max_data) flags |= RPC_FLG_LAST; /* * Write out the RPC header and the request header. */ if(!(callid = create_rpc_request(&outgoing_packet, op_num, data_len, real_auth_len, flags, callid, data_left))) { DEBUG(0,("rpc_api_pipe_req: Failed to create RPC request.\n")); prs_mem_free(&outgoing_packet); prs_mem_free(&sec_blob); return False; } prs_append_prs_data(&outgoing_packet, &sec_blob); prs_mem_free(&sec_blob); DEBUG(100,("data_len: %x data_calc_len: %x\n", data_len, prs_offset(&outgoing_packet))); if (flags & RPC_FLG_LAST) ret = rpc_api_pipe(cli, &outgoing_packet, rdata, RPC_RESPONSE); else { cli_write(cli, cli->nt_pipe_fnum, 0x0008, prs_data_p(&outgoing_packet), data_sent, data_len); } prs_mem_free(&outgoing_packet); data_sent += send_size; data_left -= send_size; } /* Also capture received data */ slprintf(dump_name, sizeof(dump_name) - 1, "reply_%s", cli_pipe_get_name(cli)); prs_dump(dump_name, op_num, rdata); return ret; }
static BOOL rpc_read(struct cli_state *cli, prs_struct *rdata, uint32 data_to_read, uint32 *rdata_offset) { size_t size = (size_t)cli->max_recv_frag; int stream_offset = 0; int num_read; char *pdata; int extra_data_size = ((int)*rdata_offset) + ((int)data_to_read) - (int)prs_data_size(rdata); DEBUG(5,("rpc_read: data_to_read: %u rdata offset: %u extra_data_size: %d\n", (int)data_to_read, (unsigned int)*rdata_offset, extra_data_size)); /* * Grow the buffer if needed to accommodate the data to be read. */ if (extra_data_size > 0) { if(!prs_force_grow(rdata, (uint32)extra_data_size)) { DEBUG(0,("rpc_read: Failed to grow parse struct by %d bytes.\n", extra_data_size )); return False; } DEBUG(5,("rpc_read: grew buffer by %d bytes to %u\n", extra_data_size, prs_data_size(rdata) )); } pdata = prs_data_p(rdata) + *rdata_offset; do /* read data using SMBreadX */ { uint32 ecode; uint8 eclass; if (size > (size_t)data_to_read) size = (size_t)data_to_read; num_read = (int)cli_read(cli, cli->nt_pipe_fnum, pdata, (off_t)stream_offset, size); DEBUG(5,("rpc_read: num_read = %d, read offset: %d, to read: %d\n", num_read, stream_offset, data_to_read)); if (cli_is_dos_error(cli)) { cli_dos_error(cli, &eclass, &ecode); if (eclass != ERRDOS && ecode != ERRmoredata) { DEBUG(0,("rpc_read: Error %d/%u in cli_read\n", eclass, (unsigned int)ecode)); return False; } } data_to_read -= num_read; stream_offset += num_read; pdata += num_read; } while (num_read > 0 && data_to_read > 0); /* && err == (0x80000000 | STATUS_BUFFER_OVERFLOW)); */ /* * Update the current offset into rdata by the amount read. */ *rdata_offset += stream_offset; return True; }
static BOOL rpc_api_pipe(struct cli_state *cli, prs_struct *data, prs_struct *rdata, uint8 expected_pkt_type) { uint32 len; char *rparam = NULL; uint32 rparam_len = 0; uint16 setup[2]; BOOL first = True; BOOL last = True; RPC_HDR rhdr; char *pdata = data ? prs_data_p(data) : NULL; uint32 data_len = data ? prs_offset(data) : 0; char *prdata = NULL; uint32 rdata_len = 0; uint32 current_offset = 0; uint32 fragment_start = 0; uint32 max_data = cli->max_xmit_frag ? cli->max_xmit_frag : 1024; int auth_padding_len = 0; /* Create setup parameters - must be in native byte order. */ setup[0] = TRANSACT_DCERPCCMD; setup[1] = cli->nt_pipe_fnum; /* Pipe file handle. */ DEBUG(5,("rpc_api_pipe: fnum:%x\n", (int)cli->nt_pipe_fnum)); /* Send the RPC request and receive a response. For short RPC calls (about 1024 bytes or so) the RPC request and response appears in a SMBtrans request and response. Larger RPC responses are received further on. */ if (!cli_api_pipe(cli, "\\PIPE\\", setup, 2, 0, /* Setup, length, max */ NULL, 0, 0, /* Params, length, max */ pdata, data_len, max_data, /* data, length, max */ &rparam, &rparam_len, /* return params, len */ &prdata, &rdata_len)) /* return data, len */ { DEBUG(0, ("cli_pipe: return critical error. Error was %s\n", cli_errstr(cli))); return False; } /* Throw away returned params - we know we won't use them. */ SAFE_FREE(rparam); if (prdata == NULL) { DEBUG(0,("rpc_api_pipe: pipe %x failed to return data.\n", (int)cli->nt_pipe_fnum)); return False; } /* * Give this memory as dynamically allocated to the return parse * struct. */ prs_give_memory(rdata, prdata, rdata_len, True); current_offset = rdata_len; /* This next call sets the endian bit correctly in rdata. */ if (!rpc_check_hdr(rdata, &rhdr, &first, &last, &len)) { prs_mem_free(rdata); return False; } if (rhdr.pkt_type == RPC_BINDACK) { if (!last && !first) { DEBUG(5,("rpc_api_pipe: bug in server (AS/U?), setting fragment first/last ON.\n")); first = True; last = True; } } if (rhdr.pkt_type == RPC_BINDNACK) { DEBUG(3, ("Bind NACK received on pipe %x!\n", (int)cli->nt_pipe_fnum)); prs_mem_free(rdata); return False; } if (rhdr.pkt_type == RPC_RESPONSE) { RPC_HDR_RESP rhdr_resp; if(!smb_io_rpc_hdr_resp("rpc_hdr_resp", &rhdr_resp, rdata, 0)) { DEBUG(5,("rpc_api_pipe: failed to unmarshal RPC_HDR_RESP.\n")); prs_mem_free(rdata); return False; } } if (rhdr.pkt_type != expected_pkt_type) { DEBUG(3, ("Connection to pipe %x got an unexpected RPC packet type - %d, not %d\n", (int)cli->nt_pipe_fnum, rhdr.pkt_type, expected_pkt_type)); prs_mem_free(rdata); return False; } DEBUG(5,("rpc_api_pipe: len left: %u smbtrans read: %u\n", (unsigned int)len, (unsigned int)rdata_len )); /* check if data to be sent back was too large for one SMBtrans */ /* err status is only informational: the _real_ check is on the length */ if (len > 0) { /* || err == (0x80000000 | STATUS_BUFFER_OVERFLOW)) */ /* Read the remaining part of the first response fragment */ if (!rpc_read(cli, rdata, len, ¤t_offset)) { prs_mem_free(rdata); return False; } } /* * Now we have a complete PDU, check the auth struct if any was sent. */ if(!rpc_auth_pipe(cli, rdata, fragment_start, rhdr.frag_len, rhdr.auth_len, rhdr.pkt_type, &auth_padding_len)) { prs_mem_free(rdata); return False; } if (rhdr.auth_len != 0) { /* * Drop the auth footers from the current offset. * We need this if there are more fragments. * The auth footers consist of the auth_data and the * preceeding 8 byte auth_header. */ current_offset -= (auth_padding_len + RPC_HDR_AUTH_LEN + rhdr.auth_len); } /* * Only one rpc fragment, and it has been read. */ if (first && last) { DEBUG(6,("rpc_api_pipe: fragment first and last both set\n")); return True; } /* * Read more fragments using SMBreadX until we get one with the * last bit set. */ while (!last) { RPC_HDR_RESP rhdr_resp; int num_read; char hdr_data[RPC_HEADER_LEN+RPC_HDR_RESP_LEN]; prs_struct hps; uint8 eclass; uint32 ecode; /* * First read the header of the next PDU. */ prs_init(&hps, 0, cli->mem_ctx, UNMARSHALL); prs_give_memory(&hps, hdr_data, sizeof(hdr_data), False); num_read = cli_read(cli, cli->nt_pipe_fnum, hdr_data, 0, RPC_HEADER_LEN+RPC_HDR_RESP_LEN); if (cli_is_dos_error(cli)) { cli_dos_error(cli, &eclass, &ecode); if (eclass != ERRDOS && ecode != ERRmoredata) { DEBUG(0,("rpc_api_pipe: cli_read error : %d/%d\n", eclass, ecode)); return False; } } DEBUG(5,("rpc_api_pipe: read header (size:%d)\n", num_read)); if (num_read != RPC_HEADER_LEN+RPC_HDR_RESP_LEN) { DEBUG(0,("rpc_api_pipe: Error : requested %d bytes, got %d.\n", RPC_HEADER_LEN+RPC_HDR_RESP_LEN, num_read )); return False; } /* This call sets the endianness in hps. */ if (!rpc_check_hdr(&hps, &rhdr, &first, &last, &len)) return False; /* Ensure the endianness in rdata is set correctly - must be same as hps. */ if (hps.bigendian_data != rdata->bigendian_data) { DEBUG(0,("rpc_api_pipe: Error : Endianness changed from %s to %s\n", rdata->bigendian_data ? "big" : "little", hps.bigendian_data ? "big" : "little" )); return False; } if(!smb_io_rpc_hdr_resp("rpc_hdr_resp", &rhdr_resp, &hps, 0)) { DEBUG(0,("rpc_api_pipe: Error in unmarshalling RPC_HDR_RESP.\n")); return False; } if (first) { DEBUG(0,("rpc_api_pipe: secondary PDU rpc header has 'first' set !\n")); return False; } /* * Now read the rest of the PDU. */ if (!rpc_read(cli, rdata, len, ¤t_offset)) { prs_mem_free(rdata); return False; } fragment_start = current_offset - len - RPC_HEADER_LEN - RPC_HDR_RESP_LEN; /* * Verify any authentication footer. */ if(!rpc_auth_pipe(cli, rdata, fragment_start, rhdr.frag_len, rhdr.auth_len, rhdr.pkt_type, &auth_padding_len)) { prs_mem_free(rdata); return False; } if (rhdr.auth_len != 0 ) { /* * Drop the auth footers from the current offset. * The auth footers consist of the auth_data and the * preceeding 8 byte auth_header. * We need this if there are more fragments. */ current_offset -= (auth_padding_len + RPC_HDR_AUTH_LEN + rhdr.auth_len); } } return True; }
static BOOL rpc_auth_pipe(struct cli_state *cli, prs_struct *rdata, uint32 fragment_start, int len, int auth_len, uint8 pkt_type, int *pauth_padding_len) { /* * The following is that length of the data we must sign or seal. * This doesn't include the RPC headers or the auth_len or the RPC_HDR_AUTH_LEN * preceeding the auth_data. */ int data_len = len - RPC_HEADER_LEN - RPC_HDR_RESP_LEN - RPC_HDR_AUTH_LEN - auth_len; /* * The start of the data to sign/seal is just after the RPC headers. */ char *reply_data = prs_data_p(rdata) + fragment_start + RPC_HEADER_LEN + RPC_HDR_REQ_LEN; RPC_HDR_AUTH rhdr_auth; char *dp = prs_data_p(rdata) + fragment_start + len - RPC_HDR_AUTH_LEN - auth_len; prs_struct auth_verf; *pauth_padding_len = 0; if (auth_len == 0) { if (cli->pipe_auth_flags == 0) { /* move along, nothing to see here */ return True; } DEBUG(2, ("No authenticaton header recienved on reply, but this pipe is authenticated\n")); return False; } DEBUG(5,("rpc_auth_pipe: pkt_type: %d len: %d auth_len: %d NTLMSSP %s schannel %s sign %s seal %s \n", pkt_type, len, auth_len, BOOLSTR(cli->pipe_auth_flags & AUTH_PIPE_NTLMSSP), BOOLSTR(cli->pipe_auth_flags & AUTH_PIPE_NETSEC), BOOLSTR(cli->pipe_auth_flags & AUTH_PIPE_SIGN), BOOLSTR(cli->pipe_auth_flags & AUTH_PIPE_SEAL))); if (dp - prs_data_p(rdata) > prs_data_size(rdata)) { DEBUG(0,("rpc_auth_pipe: schannel auth data > data size !\n")); return False; } DEBUG(10,("rpc_auth_pipe: packet:\n")); dump_data(100, dp, auth_len); prs_init(&auth_verf, 0, cli->mem_ctx, UNMARSHALL); /* The endinness must be preserved. JRA. */ prs_set_endian_data( &auth_verf, rdata->bigendian_data); /* Point this new parse struct at the auth section of the main parse struct - rather than copying it. Avoids needing to free it on every error */ prs_give_memory(&auth_verf, dp, RPC_HDR_AUTH_LEN + auth_len, False /* not dynamic */); prs_set_offset(&auth_verf, 0); { int auth_type; int auth_level; if (!smb_io_rpc_hdr_auth("auth_hdr", &rhdr_auth, &auth_verf, 0)) { DEBUG(0, ("rpc_auth_pipe: Could not parse auth header\n")); return False; } /* Let the caller know how much padding at the end of the data */ *pauth_padding_len = rhdr_auth.padding; /* Check it's the type of reply we were expecting to decode */ get_auth_type_level(cli->pipe_auth_flags, &auth_type, &auth_level); if (rhdr_auth.auth_type != auth_type) { DEBUG(0, ("BAD auth type %d (should be %d)\n", rhdr_auth.auth_type, auth_type)); return False; } if (rhdr_auth.auth_level != auth_level) { DEBUG(0, ("BAD auth level %d (should be %d)\n", rhdr_auth.auth_level, auth_level)); return False; } } if (pkt_type == RPC_BINDACK) { if (cli->pipe_auth_flags & AUTH_PIPE_NTLMSSP) { /* copy the next auth_len bytes into a buffer for later use */ DATA_BLOB ntlmssp_verf = data_blob(NULL, auth_len); BOOL store_ok; /* save the reply away, for use a little later */ prs_copy_data_out((char *)ntlmssp_verf.data, &auth_verf, auth_len); store_ok = (NT_STATUS_IS_OK(ntlmssp_store_response(cli->ntlmssp_pipe_state, ntlmssp_verf))); data_blob_free(&ntlmssp_verf); return store_ok; } else if (cli->pipe_auth_flags & AUTH_PIPE_NETSEC) { /* nothing to do here - we don't seem to be able to validate the bindack based on VL's comments */ return True; } } if (cli->pipe_auth_flags & AUTH_PIPE_NTLMSSP) { NTSTATUS nt_status; DATA_BLOB sig; if ((cli->pipe_auth_flags & AUTH_PIPE_SIGN) || (cli->pipe_auth_flags & AUTH_PIPE_SEAL)) { if (auth_len != RPC_AUTH_NTLMSSP_CHK_LEN) { DEBUG(0,("rpc_auth_pipe: wrong ntlmssp auth len %d\n", auth_len)); return False; } sig = data_blob(NULL, auth_len); prs_copy_data_out((char *)sig.data, &auth_verf, auth_len); } /* * Unseal any sealed data in the PDU, not including the * 8 byte auth_header or the auth_data. */ /* * Now unseal and check the auth verifier in the auth_data at * the end of the packet. */ if (cli->pipe_auth_flags & AUTH_PIPE_SEAL) { if (data_len < 0) { DEBUG(1, ("Can't unseal - data_len < 0!!\n")); return False; } nt_status = ntlmssp_unseal_packet(cli->ntlmssp_pipe_state, (unsigned char *)reply_data, data_len, &sig); } else if (cli->pipe_auth_flags & AUTH_PIPE_SIGN) { nt_status = ntlmssp_check_packet(cli->ntlmssp_pipe_state, (const unsigned char *)reply_data, data_len, &sig); } data_blob_free(&sig); if (!NT_STATUS_IS_OK(nt_status)) { DEBUG(0, ("rpc_auth_pipe: could not validate " "incoming NTLMSSP packet!\n")); return False; } } if (cli->pipe_auth_flags & AUTH_PIPE_NETSEC) { RPC_AUTH_NETSEC_CHK chk; if (auth_len != RPC_AUTH_NETSEC_CHK_LEN) { DEBUG(0,("rpc_auth_pipe: wrong schannel auth len %d\n", auth_len)); return False; } if (!smb_io_rpc_auth_netsec_chk("schannel_auth_sign", &chk, &auth_verf, 0)) { DEBUG(0, ("rpc_auth_pipe: schannel unmarshalling " "RPC_AUTH_NETSECK_CHK failed\n")); return False; } if (!netsec_decode(&cli->auth_info, cli->pipe_auth_flags, SENDER_IS_ACCEPTOR, &chk, reply_data, data_len)) { DEBUG(0, ("rpc_auth_pipe: Could not decode schannel\n")); return False; } cli->auth_info.seq_num++; } return True; }