diff options
-rw-r--r-- | source3/include/async_smb.h | 1 | ||||
-rw-r--r-- | source3/include/proto.h | 23 | ||||
-rw-r--r-- | source3/lib/util.c | 26 | ||||
-rw-r--r-- | source3/libsmb/async_smb.c | 17 | ||||
-rw-r--r-- | source3/libsmb/clifile.c | 2 | ||||
-rw-r--r-- | source3/libsmb/clirap.c | 40 | ||||
-rw-r--r-- | source3/libsmb/clisecdesc.c | 37 | ||||
-rw-r--r-- | source3/libsmb/clitrans.c | 701 | ||||
-rw-r--r-- | source3/smbd/nttrans.c | 11 |
9 files changed, 814 insertions, 44 deletions
diff --git a/source3/include/async_smb.h b/source3/include/async_smb.h index e9e10023e3..4e2061813f 100644 --- a/source3/include/async_smb.h +++ b/source3/include/async_smb.h @@ -124,6 +124,7 @@ struct async_req *cli_request_send(TALLOC_CTX *mem_ctx, bool cli_chain_cork(struct cli_state *cli, struct event_context *ev, size_t size_hint); void cli_chain_uncork(struct cli_state *cli); +bool cli_in_chain(struct cli_state *cli); NTSTATUS cli_pull_reply(struct async_req *req, uint8_t *pwct, uint16_t **pvwv, diff --git a/source3/include/proto.h b/source3/include/proto.h index e94a2178c0..2901911c70 100644 --- a/source3/include/proto.h +++ b/source3/include/proto.h @@ -1354,6 +1354,7 @@ bool mask_match_list(const char *string, char **list, int listLen, bool is_case_ bool unix_wild_match(const char *pattern, const char *string); bool name_to_fqdn(fstring fqdn, const char *name); void *talloc_check_name_abort(const void *ptr, const char *name); +void *talloc_append_blob(TALLOC_CTX *mem_ctx, void *buf, DATA_BLOB blob); uint32 map_share_mode_to_deny_mode(uint32 share_access, uint32 private_options); pid_t procid_to_pid(const struct server_id *proc); void set_my_vnn(uint32 vnn); @@ -4381,6 +4382,7 @@ int cli_nt_create_full(struct cli_state *cli, const char *fname, uint32 CreateDisposition, uint32 CreateOptions, uint8 SecuityFlags); int cli_nt_create(struct cli_state *cli, const char *fname, uint32 DesiredAccess); +uint8_t *smb_bytes_push_str(uint8_t *buf, bool ucs2, const char *str); struct async_req *cli_open_send(TALLOC_CTX *mem_ctx, struct event_context *ev, struct cli_state *cli, const char *fname, int flags, int share_mode); @@ -4730,6 +4732,27 @@ bool cli_send_nt_trans(struct cli_state *cli, bool cli_receive_nt_trans(struct cli_state *cli, char **param, unsigned int *param_len, char **data, unsigned int *data_len); +struct async_req *cli_trans_send( + TALLOC_CTX *mem_ctx, struct event_context *ev, + struct cli_state *cli, uint8_t trans_cmd, + const char *pipe_name, uint16_t fid, uint16_t function, int flags, + uint16_t *setup, uint8_t num_setup, uint8_t max_setup, + uint8_t *param, uint32_t num_param, uint32_t max_param, + uint8_t *data, uint32_t num_data, uint32_t max_data); +NTSTATUS cli_trans_recv(struct async_req *req, TALLOC_CTX *mem_ctx, + uint16_t **setup, uint8_t *num_setup, + uint8_t **param, uint32_t *num_param, + uint8_t **data, uint32_t *num_data); +NTSTATUS cli_trans(TALLOC_CTX *mem_ctx, struct cli_state *cli, + uint8_t trans_cmd, + const char *pipe_name, uint16_t fid, uint16_t function, + int flags, + uint16_t *setup, uint8_t num_setup, uint8_t max_setup, + uint8_t *param, uint32_t num_param, uint32_t max_param, + uint8_t *data, uint32_t num_data, uint32_t max_data, + uint16_t **rsetup, uint8_t *num_rsetup, + uint8_t **rparam, uint32_t *num_rparam, + uint8_t **rdata, uint32_t *num_rdata); /* The following definitions come from libsmb/conncache.c */ diff --git a/source3/lib/util.c b/source3/lib/util.c index 201d87aeb5..ec43ea7037 100644 --- a/source3/lib/util.c +++ b/source3/lib/util.c @@ -2990,6 +2990,32 @@ void *talloc_check_name_abort(const void *ptr, const char *name) return NULL; } +/********************************************************************** + Append a DATA_BLOB to a talloc'ed object +***********************************************************************/ + +void *talloc_append_blob(TALLOC_CTX *mem_ctx, void *buf, DATA_BLOB blob) +{ + size_t old_size = 0; + char *result; + + if (blob.length == 0) { + return buf; + } + + if (buf != NULL) { + old_size = talloc_get_size(buf); + } + + result = (char *)TALLOC_REALLOC(mem_ctx, buf, old_size + blob.length); + if (result == NULL) { + return NULL; + } + + memcpy(result + old_size, blob.data, blob.length); + return result; +} + uint32 map_share_mode_to_deny_mode(uint32 share_access, uint32 private_options) { switch (share_access & ~FILE_SHARE_DELETE) { diff --git a/source3/libsmb/async_smb.c b/source3/libsmb/async_smb.c index 435c8c1cb9..eedc7d4481 100644 --- a/source3/libsmb/async_smb.c +++ b/source3/libsmb/async_smb.c @@ -137,6 +137,21 @@ static int cli_request_destructor(struct cli_request *req) } /** + * Are there already requests waiting in the chain_accumulator? + * @param[in] cli The cli_state we want to check + * @retval reply :-) + */ + +bool cli_in_chain(struct cli_state *cli) +{ + if (cli->chain_accumulator == NULL) { + return false; + } + + return (cli->chain_accumulator->num_async != 0); +} + +/** * Is the SMB command able to hold an AND_X successor * @param[in] cmd The SMB command in question * @retval Can we add a chained request after "cmd"? @@ -422,7 +437,6 @@ bool cli_chain_cork(struct cli_state *cli, struct event_context *ev, cli_setup_packet_buf(cli, req->outbuf); req->mid = cli_new_mid(cli); - SSVAL(req->outbuf, smb_mid, req->mid); cli->chain_accumulator = req; @@ -453,6 +467,7 @@ void cli_chain_uncork(struct cli_state *cli) cli->chain_accumulator = NULL; + SSVAL(req->outbuf, smb_mid, req->mid); smb_setlen(req->outbuf, talloc_get_size(req->outbuf) - 4); cli_calculate_sign_mac(cli, req->outbuf); diff --git a/source3/libsmb/clifile.c b/source3/libsmb/clifile.c index a8b3440513..d3819af444 100644 --- a/source3/libsmb/clifile.c +++ b/source3/libsmb/clifile.c @@ -781,7 +781,7 @@ int cli_nt_create(struct cli_state *cli, const char *fname, uint32 DesiredAccess FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OPEN, 0x0, 0x0); } -static uint8_t *smb_bytes_push_str(uint8_t *buf, bool ucs2, const char *str) +uint8_t *smb_bytes_push_str(uint8_t *buf, bool ucs2, const char *str) { size_t buflen = talloc_get_size(buf); char *converted; diff --git a/source3/libsmb/clirap.c b/source3/libsmb/clirap.c index 3b4db2e26d..631bc3f36b 100644 --- a/source3/libsmb/clirap.c +++ b/source3/libsmb/clirap.c @@ -998,8 +998,9 @@ bool cli_qfileinfo(struct cli_state *cli, int fnum, unsigned int data_len = 0; unsigned int param_len = 0; uint16 setup = TRANSACT2_QFILEINFO; - char param[4]; - char *rparam=NULL, *rdata=NULL; + uint8_t param[4]; + uint8_t *rparam=NULL, *rdata=NULL; + NTSTATUS status; /* if its a win95 server then fail this - win95 totally screws it up */ @@ -1010,20 +1011,17 @@ bool cli_qfileinfo(struct cli_state *cli, int fnum, SSVAL(param, 0, fnum); SSVAL(param, 2, SMB_QUERY_FILE_ALL_INFO); - if (!cli_send_trans(cli, SMBtrans2, - NULL, /* name */ - -1, 0, /* fid, flags */ - &setup, 1, 0, /* setup, length, max */ - param, param_len, 2, /* param, length, max */ - NULL, data_len, cli->max_xmit /* data, length, max */ - )) { - return False; - } + status = cli_trans(talloc_tos(), cli, SMBtrans2, + NULL, -1, 0, 0, /* name, fid, function, flags */ + &setup, 1, 0, /* setup, length, max */ + param, param_len, 2, /* param, length, max */ + NULL, 0, MIN(cli->max_xmit, 0xffff), /* data, length, max */ + NULL, NULL, /* rsetup, length */ + &rparam, ¶m_len, /* rparam, length */ + &rdata, &data_len); - if (!cli_receive_trans(cli, SMBtrans2, - &rparam, ¶m_len, - &rdata, &data_len)) { - return False; + if (!NT_STATUS_IS_OK(status)) { + return false; } if (!rdata || data_len < 68) { @@ -1031,16 +1029,16 @@ bool cli_qfileinfo(struct cli_state *cli, int fnum, } if (create_time) { - *create_time = interpret_long_date(rdata+0); + *create_time = interpret_long_date((char *)rdata+0); } if (access_time) { - *access_time = interpret_long_date(rdata+8); + *access_time = interpret_long_date((char *)rdata+8); } if (write_time) { - *write_time = interpret_long_date(rdata+16); + *write_time = interpret_long_date((char *)rdata+16); } if (change_time) { - *change_time = interpret_long_date(rdata+24); + *change_time = interpret_long_date((char *)rdata+24); } if (mode) { *mode = SVAL(rdata, 32); @@ -1052,8 +1050,8 @@ bool cli_qfileinfo(struct cli_state *cli, int fnum, *ino = IVAL(rdata, 64); } - SAFE_FREE(rdata); - SAFE_FREE(rparam); + TALLOC_FREE(rdata); + TALLOC_FREE(rparam); return True; } diff --git a/source3/libsmb/clisecdesc.c b/source3/libsmb/clisecdesc.c index adc6fba9af..f0b786c899 100644 --- a/source3/libsmb/clisecdesc.c +++ b/source3/libsmb/clisecdesc.c @@ -25,8 +25,8 @@ SEC_DESC *cli_query_secdesc(struct cli_state *cli, int fnum, TALLOC_CTX *mem_ctx) { - char param[8]; - char *rparam=NULL, *rdata=NULL; + uint8_t param[8]; + uint8_t *rparam=NULL, *rdata=NULL; unsigned int rparam_count=0, rdata_count=0; SEC_DESC *psd = NULL; NTSTATUS status; @@ -34,27 +34,22 @@ SEC_DESC *cli_query_secdesc(struct cli_state *cli, int fnum, SIVAL(param, 0, fnum); SIVAL(param, 4, 0x7); - if (!cli_send_nt_trans(cli, - NT_TRANSACT_QUERY_SECURITY_DESC, - 0, - NULL, 0, 0, - param, 8, 4, - NULL, 0, 0x10000)) { - DEBUG(1,("Failed to send NT_TRANSACT_QUERY_SECURITY_DESC\n")); - goto cleanup; - } - + status = cli_trans(talloc_tos(), cli, SMBnttrans, + NULL, -1, /* name, fid */ + NT_TRANSACT_QUERY_SECURITY_DESC, 0, /* function, flags */ + NULL, 0, 0, /* setup, length, max */ + param, 8, 4, /* param, length, max */ + NULL, 0, 0x10000, /* data, length, max */ + NULL, NULL, /* rsetup, length */ + &rparam, &rparam_count, + &rdata, &rdata_count); - if (!cli_receive_nt_trans(cli, - &rparam, &rparam_count, - &rdata, &rdata_count)) { - DEBUG(1,("Failed to recv NT_TRANSACT_QUERY_SECURITY_DESC\n")); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("NT_TRANSACT_QUERY_SECURITY_DESC failed: %s\n", + nt_errstr(status))); goto cleanup; } - if (cli_is_error(cli)) - goto cleanup; - status = unmarshall_sec_desc(mem_ctx, (uint8 *)rdata, rdata_count, &psd); @@ -66,8 +61,8 @@ SEC_DESC *cli_query_secdesc(struct cli_state *cli, int fnum, cleanup: - SAFE_FREE(rparam); - SAFE_FREE(rdata); + TALLOC_FREE(rparam); + TALLOC_FREE(rdata); return psd; } diff --git a/source3/libsmb/clitrans.c b/source3/libsmb/clitrans.c index 4bb70f1a08..5b84e68fc4 100644 --- a/source3/libsmb/clitrans.c +++ b/source3/libsmb/clitrans.c @@ -701,3 +701,704 @@ bool cli_receive_nt_trans(struct cli_state *cli, client_set_trans_sign_state_off(cli, SVAL(cli->inbuf,smb_mid)); return ret; } + +struct trans_recvblob { + uint8_t *data; + uint32_t max, total, received; +}; + +struct cli_trans_state { + struct cli_state *cli; + struct event_context *ev; + uint8_t cmd; + uint16_t mid; + const char *pipe_name; + uint16_t fid; + uint16_t function; + int flags; + uint16_t *setup; + uint8_t num_setup, max_setup; + uint8_t *param; + uint32_t num_param, param_sent; + uint8_t *data; + uint32_t num_data, data_sent; + + uint8_t num_rsetup; + uint16_t *rsetup; + struct trans_recvblob rparam; + struct trans_recvblob rdata; + + TALLOC_CTX *secondary_request_ctx; +}; + +static void cli_trans_recv_helper(struct async_req *req); + +static struct async_req *cli_ship_trans(TALLOC_CTX *mem_ctx, + struct cli_trans_state *state) +{ + TALLOC_CTX *frame; + struct async_req *result = NULL; + struct cli_request *cli_req; + uint8_t wct; + uint16_t *vwv; + uint8_t *bytes = NULL; + uint16_t param_offset; + uint16_t this_param = 0; + uint16_t this_data = 0; + uint32_t useable_space; + uint8_t cmd; + + frame = talloc_stackframe(); + + cmd = state->cmd; + + if ((state->param_sent != 0) || (state->data_sent != 0)) { + /* The secondary commands are one after the primary ones */ + cmd += 1; + } + + param_offset = smb_size - 4; + + switch (cmd) { + case SMBtrans: + bytes = TALLOC_ZERO_P(talloc_tos(), uint8_t); /* padding */ + if (bytes == NULL) { + goto fail; + } + bytes = smb_bytes_push_str( + bytes, (state->cli->capabilities & CAP_UNICODE) != 0, + state->pipe_name); + if (bytes == NULL) { + goto fail; + } + wct = 14 + state->num_setup; + param_offset += talloc_get_size(bytes); + break; + case SMBtrans2: + bytes = TALLOC_ARRAY(talloc_tos(), uint8_t, 3); /* padding */ + if (bytes == NULL) { + goto fail; + } + bytes[0] = 0; + bytes[1] = 'D'; /* Copy this from "old" 3.0 behaviour */ + bytes[2] = ' '; + wct = 14 + state->num_setup; + param_offset += talloc_get_size(bytes); + break; + case SMBtranss: + wct = 8; + break; + case SMBtranss2: + wct = 9; + break; + case SMBnttrans: + wct = 19 + state->num_setup; + break; + case SMBnttranss: + wct = 18; + break; + default: + goto fail; + } + + useable_space = state->cli->max_xmit - smb_size - sizeof(uint16_t)*wct; + + if (state->param_sent < state->num_param) { + this_param = MIN(state->num_param - state->param_sent, + useable_space); + } + + if (state->data_sent < state->num_data) { + this_data = MIN(state->num_data - state->data_sent, + useable_space - this_param); + } + + vwv = TALLOC_ARRAY(talloc_tos(), uint16_t, wct); + if (vwv == NULL) { + goto fail; + } + param_offset += wct * sizeof(uint16_t); + + DEBUG(10, ("num_setup=%u, max_setup=%u, " + "param_total=%u, this_param=%u, max_param=%u, " + "data_total=%u, this_data=%u, max_data=%u, " + "param_offset=%u, param_disp=%u, data_disp=%u\n", + (unsigned)state->num_setup, (unsigned)state->max_setup, + (unsigned)state->num_param, (unsigned)this_param, + (unsigned)state->rparam.max, + (unsigned)state->num_data, (unsigned)this_data, + (unsigned)state->rdata.max, + (unsigned)param_offset, + (unsigned)state->param_sent, (unsigned)state->data_sent)); + + switch (cmd) { + case SMBtrans: + case SMBtrans2: + SSVAL(vwv + 0, 0, state->num_param); + SSVAL(vwv + 1, 0, state->num_data); + SSVAL(vwv + 2, 0, state->rparam.max); + SSVAL(vwv + 3, 0, state->rdata.max); + SCVAL(vwv + 4, 0, state->max_setup); + SCVAL(vwv + 4, 1, 0); /* reserved */ + SSVAL(vwv + 5, 0, state->flags); + SIVAL(vwv + 6, 0, 0); /* timeout */ + SSVAL(vwv + 8, 0, 0); /* reserved */ + SSVAL(vwv + 9, 0, this_param); + SSVAL(vwv +10, 0, param_offset); + SSVAL(vwv +11, 0, this_data); + SSVAL(vwv +12, 0, param_offset + this_param); + SCVAL(vwv +13, 0, state->num_setup); + SCVAL(vwv +13, 1, 0); /* reserved */ + memcpy(vwv + 14, state->setup, + sizeof(uint16_t) * state->num_setup); + break; + case SMBtranss: + case SMBtranss2: + SSVAL(vwv + 0, 0, state->num_param); + SSVAL(vwv + 1, 0, state->num_data); + SSVAL(vwv + 2, 0, this_param); + SSVAL(vwv + 3, 0, param_offset); + SSVAL(vwv + 4, 0, state->param_sent); + SSVAL(vwv + 5, 0, this_data); + SSVAL(vwv + 6, 0, param_offset + this_param); + SSVAL(vwv + 7, 0, state->data_sent); + if (cmd == SMBtranss2) { + SSVAL(vwv + 8, 0, state->fid); + } + break; + case SMBnttrans: + SCVAL(vwv, 0, state->max_setup); + SSVAL(vwv, 1, 0); /* reserved */ + SIVAL(vwv, 3, state->num_param); + SIVAL(vwv, 7, state->num_data); + SIVAL(vwv, 11, state->rparam.max); + SIVAL(vwv, 15, state->rdata.max); + SIVAL(vwv, 19, this_param); + SIVAL(vwv, 23, param_offset); + SIVAL(vwv, 27, this_data); + SIVAL(vwv, 31, param_offset + this_param); + SCVAL(vwv, 35, state->num_setup); + SSVAL(vwv, 36, state->function); + memcpy(vwv + 19, state->setup, + sizeof(uint16_t) * state->num_setup); + break; + case SMBnttranss: + SSVAL(vwv, 0, 0); /* reserved */ + SCVAL(vwv, 2, 0); /* reserved */ + SIVAL(vwv, 3, state->num_param); + SIVAL(vwv, 7, state->num_data); + SIVAL(vwv, 11, this_param); + SIVAL(vwv, 15, param_offset); + SIVAL(vwv, 19, state->param_sent); + SIVAL(vwv, 23, this_data); + SIVAL(vwv, 27, param_offset + this_param); + SIVAL(vwv, 31, state->data_sent); + SCVAL(vwv, 35, 0); /* reserved */ + break; + } + + bytes = (uint8_t *)talloc_append_blob( + talloc_tos(), bytes, + data_blob_const(state->param + state->param_sent, this_param)); + if (bytes == NULL) { + goto fail; + } + state->param_sent += this_param; + + bytes = (uint8_t *)talloc_append_blob( + talloc_tos(), bytes, + data_blob_const(state->data + state->data_sent, this_data)); + if (bytes == NULL) { + goto fail; + } + state->data_sent += this_data; + + if ((cmd == SMBtrans) || (cmd == SMBtrans2) || (cmd == SMBnttrans)) { + /* + * Primary request, retrieve our mid + */ + result = cli_request_send(mem_ctx, state->ev, state->cli, + cmd, 0, wct, vwv, + talloc_get_size(bytes), bytes); + if (result == NULL) { + goto fail; + } + cli_req = talloc_get_type_abort(result->private_data, + struct cli_request); + state->mid = cli_req->mid; + } else { + uint16_t num_bytes = talloc_get_size(bytes); + /* + * Secondary request, we have to fix up the mid. Thus we do + * the chain_cork/chain/uncork ourselves. + */ + if (!cli_chain_cork(state->cli, state->ev, + wct * sizeof(uint16_t) + num_bytes + 3)) { + goto fail; + } + result = cli_request_send(mem_ctx, state->ev, state->cli, + cmd, 0, wct, vwv, num_bytes, bytes); + if (result == NULL) { + goto fail; + } + cli_req = talloc_get_type_abort(result->private_data, + struct cli_request); + cli_req->recv_helper.fn = cli_trans_recv_helper; + cli_req->recv_helper.priv = state; + cli_req->mid = state->mid; + client_set_trans_sign_state_off(state->cli, state->mid); + cli_chain_uncork(state->cli); + } + + client_set_trans_sign_state_on(state->cli, state->mid); + + fail: + TALLOC_FREE(frame); + return result; +} + +static void cli_trans_ship_rest(struct async_req *req, + struct cli_trans_state *state) +{ + state->secondary_request_ctx = talloc_new(state); + if (state->secondary_request_ctx == NULL) { + async_req_error(req, NT_STATUS_NO_MEMORY); + return; + } + + while ((state->param_sent < state->num_param) + || (state->data_sent < state->num_data)) { + struct async_req *cli_req; + + cli_req = cli_ship_trans(state->secondary_request_ctx, state); + if (cli_req == NULL) { + async_req_error(req, NT_STATUS_NO_MEMORY); + return; + } + } +} + +static bool cli_trans_oob(uint32_t bufsize, uint32_t offset, uint32_t length) +{ + if ((offset + length < offset) || (offset + length < length)) { + /* wrap */ + return true; + } + if ((offset > bufsize) || (offset + length > bufsize)) { + /* overflow */ + return true; + } + return false; +} + +static NTSTATUS cli_pull_trans(struct async_req *req, + struct cli_request *cli_req, + uint8_t smb_cmd, bool expect_first_reply, + uint8_t *pnum_setup, uint16_t **psetup, + uint32_t *ptotal_param, uint32_t *pnum_param, + uint32_t *pparam_disp, uint8_t **pparam, + uint32_t *ptotal_data, uint32_t *pnum_data, + uint32_t *pdata_disp, uint8_t **pdata) +{ + uint32_t param_ofs, data_ofs; + uint8_t wct; + uint16_t *vwv; + uint16_t num_bytes; + uint8_t *bytes; + NTSTATUS status; + + status = cli_pull_reply(req, &wct, &vwv, &num_bytes, &bytes); + + /* + * We can receive something like STATUS_MORE_ENTRIES, so don't use + * !NT_STATUS_IS_OK(status) here. + */ + + if (NT_STATUS_IS_ERR(status)) { + return status; + } + + if (expect_first_reply) { + if ((wct != 0) || (num_bytes != 0)) { + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + return NT_STATUS_OK; + } + + switch (smb_cmd) { + case SMBtrans: + case SMBtrans2: + if (wct < 10) { + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + *ptotal_param = SVAL(vwv + 0, 0); + *ptotal_data = SVAL(vwv + 1, 0); + *pnum_param = SVAL(vwv + 3, 0); + param_ofs = SVAL(vwv + 4, 0); + *pparam_disp = SVAL(vwv + 5, 0); + *pnum_data = SVAL(vwv + 6, 0); + data_ofs = SVAL(vwv + 7, 0); + *pdata_disp = SVAL(vwv + 8, 0); + *pnum_setup = CVAL(vwv + 9, 0); + if (wct < 10 + (*pnum_setup)) { + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + *psetup = vwv + 10; + + break; + case SMBnttrans: + if (wct < 18) { + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + *ptotal_param = IVAL(vwv, 3); + *ptotal_data = IVAL(vwv, 7); + *pnum_param = IVAL(vwv, 11); + param_ofs = IVAL(vwv, 15); + *pparam_disp = IVAL(vwv, 19); + *pnum_data = IVAL(vwv, 23); + data_ofs = IVAL(vwv, 27); + *pdata_disp = IVAL(vwv, 31); + *pnum_setup = CVAL(vwv, 35); + *psetup = vwv + 18; + break; + + default: + return NT_STATUS_INTERNAL_ERROR; + } + + /* + * Check for buffer overflows. data_ofs needs to be checked against + * the incoming buffer length, data_disp against the total + * length. Likewise for param_ofs/param_disp. + */ + + if (cli_trans_oob(smb_len(cli_req->inbuf), param_ofs, *pnum_param) + || cli_trans_oob(*ptotal_param, *pparam_disp, *pnum_param) + || cli_trans_oob(smb_len(cli_req->inbuf), data_ofs, *pnum_data) + || cli_trans_oob(*ptotal_data, *pdata_disp, *pnum_data)) { + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + + *pparam = (uint8_t *)cli_req->inbuf + 4 + param_ofs; + *pdata = (uint8_t *)cli_req->inbuf + 4 + data_ofs; + + return NT_STATUS_OK; +} + +static NTSTATUS cli_trans_pull_blob(TALLOC_CTX *mem_ctx, + struct trans_recvblob *blob, + uint32_t total, uint32_t thistime, + uint8_t *buf, uint32_t displacement) +{ + if (blob->data == NULL) { + if (total > blob->max) { + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + blob->total = total; + blob->data = TALLOC_ARRAY(mem_ctx, uint8_t, total); + if (blob->data == NULL) { + return NT_STATUS_NO_MEMORY; + } + } + + if (total > blob->total) { + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + + if (thistime) { + memcpy(blob->data + displacement, buf, thistime); + blob->received += thistime; + } + + return NT_STATUS_OK; +} + +static void cli_trans_recv_helper(struct async_req *req) +{ + struct cli_request *cli_req = talloc_get_type_abort( + req->private_data, struct cli_request); + struct cli_trans_state *state = talloc_get_type_abort( + cli_req->recv_helper.priv, struct cli_trans_state); + uint8_t num_setup; + uint16_t *setup; + uint32_t total_param, num_param, param_disp; + uint32_t total_data, num_data, data_disp; + uint8_t *param, *data; + bool sent_all; + NTSTATUS status; + + sent_all = (state->param_sent == state->num_param) + && (state->data_sent == state->num_data); + + status = cli_pull_trans( + req, cli_req, state->cmd, !sent_all, &num_setup, &setup, + &total_param, &num_param, ¶m_disp, ¶m, + &total_data, &num_data, &data_disp, &data); + + /* + * We can receive something like STATUS_MORE_ENTRIES, so don't use + * !NT_STATUS_IS_OK(status) here. + */ + + if (NT_STATUS_IS_ERR(status)) { + async_req_error(req, status); + return; + } + + if (!sent_all) { + cli_trans_ship_rest(req, state); + return; + } + + /* + * We've just received a real response. This means that we don't need + * the secondary cli_request structures anymore, they have all been + * shipped to the server. + */ + TALLOC_FREE(state->secondary_request_ctx); + + if (num_setup != 0) { + TALLOC_FREE(state->rsetup); + state->rsetup = (uint16_t *)TALLOC_MEMDUP( + state, setup, sizeof(uint16_t) * num_setup); + if (state->rsetup == NULL) { + async_req_error(req, NT_STATUS_NO_MEMORY); + return; + } + } + + status = cli_trans_pull_blob( + state, &state->rparam, total_param, num_param, param, + param_disp); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10, ("Pulling params failed: %s\n", nt_errstr(status))); + async_req_error(req, status); + return; + } + + status = cli_trans_pull_blob( + state, &state->rdata, total_data, num_data, data, + data_disp); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10, ("Pulling data failed: %s\n", nt_errstr(status))); + async_req_error(req, status); + return; + } + + if ((state->rparam.total == state->rparam.received) + && (state->rdata.total == state->rdata.received)) { + client_set_trans_sign_state_off(state->cli, state->mid); + async_req_done(req); + } +} + +struct async_req *cli_trans_send( + TALLOC_CTX *mem_ctx, struct event_context *ev, + struct cli_state *cli, uint8_t trans_cmd, + const char *pipe_name, uint16_t fid, uint16_t function, int flags, + uint16_t *setup, uint8_t num_setup, uint8_t max_setup, + uint8_t *param, uint32_t num_param, uint32_t max_param, + uint8_t *data, uint32_t num_data, uint32_t max_data) +{ + struct async_req *req; + struct cli_request *cli_req; + struct cli_trans_state *state; + + /* + * We can't use it in a chained request chain, we'd get the offset + * calculations wrong. + */ + + if (cli_in_chain(cli)) { + return NULL; + } + + if ((trans_cmd == SMBtrans) || (trans_cmd == SMBtrans2)) { + if ((num_param > 0xffff) || (max_param > 0xffff) + || (num_data > 0xffff) || (max_data > 0xffff)) { + DEBUG(3, ("Attempt to send invalid trans2 request " + "(setup %u, params %u/%u, data %u/%u)\n", + (unsigned)num_setup, + (unsigned)num_param, (unsigned)max_param, + (unsigned)num_data, (unsigned)max_data)); + return NULL; + } + } + + state = talloc(mem_ctx, struct cli_trans_state); + if (state == NULL) { + goto nomem; + } + + state->cli = cli; + state->ev = ev; + state->cmd = trans_cmd; + state->num_rsetup = 0; + state->rsetup = NULL; + ZERO_STRUCT(state->rparam); + ZERO_STRUCT(state->rdata); + state->secondary_request_ctx = NULL; + + if (trans_cmd == SMBtrans) { + state->pipe_name = talloc_strdup(state, pipe_name); + if (state->pipe_name == NULL) { + goto nomem; + } + } + if (trans_cmd == SMBtrans2) { + state->fid = fid; + } + if (trans_cmd == SMBnttrans) { + state->function = function; + } + + state->flags = flags; + + if (setup != NULL) { + state->setup = (uint16_t *)TALLOC_MEMDUP( + state, setup, sizeof(*setup) * num_setup); + if (state->setup == NULL) { + goto nomem; + } + state->num_setup = num_setup; + } else { + state->setup = NULL; + state->num_setup = 0; + } + + state->max_setup = max_setup; + + if (param != NULL) { + state->param = (uint8_t *)TALLOC_MEMDUP(state, param, + num_param); + if (state->param == NULL) { + goto nomem; + } + state->num_param = num_param; + } else { + state->param = NULL; + state->num_param = 0; + } + + state->param_sent = 0; + state->rparam.max = max_param; + + if (data != NULL) { + state->data = (uint8_t *)TALLOC_MEMDUP(state, data, num_data); + if (state->data == NULL) { + goto nomem; + } + state->num_data = num_data; + } else { + state->data = NULL; + state->num_data = 0; + } + + state->data_sent = 0; + state->rdata.max = max_data; + + req = cli_ship_trans(state, state); + if (req == NULL) { + goto nomem; + } + + cli_req = talloc_get_type_abort(req->private_data, struct cli_request); + cli_req->recv_helper.fn = cli_trans_recv_helper; + cli_req->recv_helper.priv = state; + + return req; + + nomem: + TALLOC_FREE(state); + return NULL; +} + +NTSTATUS cli_trans_recv(struct async_req *req, TALLOC_CTX *mem_ctx, + uint16_t **setup, uint8_t *num_setup, + uint8_t **param, uint32_t *num_param, + uint8_t **data, uint32_t *num_data) +{ + struct cli_request *cli_req = talloc_get_type_abort( + req->private_data, struct cli_request); + struct cli_trans_state *state = talloc_get_type_abort( + cli_req->recv_helper.priv, struct cli_trans_state); + + SMB_ASSERT(req->state >= ASYNC_REQ_DONE); + if (req->state == ASYNC_REQ_ERROR) { + return req->status; + } + + if (setup != NULL) { + *setup = talloc_move(mem_ctx, &state->rsetup); + *num_setup = state->num_rsetup; + } else { + TALLOC_FREE(state->rsetup); + } + + if (param != NULL) { + *param = talloc_move(mem_ctx, &state->rparam.data); + *num_param = state->rparam.total; + } else { + TALLOC_FREE(state->rparam.data); + } + + if (data != NULL) { + *data = talloc_move(mem_ctx, &state->rdata.data); + *num_data = state->rdata.total; + } else { + TALLOC_FREE(state->rdata.data); + } + + return NT_STATUS_OK; +} + +NTSTATUS cli_trans(TALLOC_CTX *mem_ctx, struct cli_state *cli, + uint8_t trans_cmd, + const char *pipe_name, uint16_t fid, uint16_t function, + int flags, + uint16_t *setup, uint8_t num_setup, uint8_t max_setup, + uint8_t *param, uint32_t num_param, uint32_t max_param, + uint8_t *data, uint32_t num_data, uint32_t max_data, + uint16_t **rsetup, uint8_t *num_rsetup, + uint8_t **rparam, uint32_t *num_rparam, + uint8_t **rdata, uint32_t *num_rdata) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct event_context *ev; + struct async_req *req; + NTSTATUS status = NT_STATUS_NO_MEMORY; + + if (cli->fd_event != NULL) { + /* + * Can't use sync call while an async call is in flight + */ + cli_set_error(cli, NT_STATUS_INVALID_PARAMETER); + goto fail; + } + + ev = event_context_init(frame); + if (ev == NULL) { + goto fail; + } + + req = cli_trans_send(frame, ev, cli, trans_cmd, + pipe_name, fid, function, flags, + setup, num_setup, max_setup, + param, num_param, max_param, + data, num_data, max_data); + if (req == NULL) { + goto fail; + } + + while (req->state < ASYNC_REQ_DONE) { + event_loop_once(ev); + } + + status = cli_trans_recv(req, mem_ctx, rsetup, num_rsetup, + rparam, num_rparam, rdata, num_rdata); + fail: + TALLOC_FREE(frame); + return status; +} diff --git a/source3/smbd/nttrans.c b/source3/smbd/nttrans.c index 567c428bb8..584399c86c 100644 --- a/source3/smbd/nttrans.c +++ b/source3/smbd/nttrans.c @@ -2582,6 +2582,17 @@ void reply_nttrans(struct smb_request *req) state->setup = NULL; state->call = function_code; + DEBUG(10, ("num_setup=%u, " + "param_total=%u, this_param=%u, max_param=%u, " + "data_total=%u, this_data=%u, max_data=%u, " + "param_offset=%u, data_offset=%u\n", + (unsigned)state->setup_count, + (unsigned)state->total_param, (unsigned)pscnt, + (unsigned)state->max_param_return, + (unsigned)state->total_data, (unsigned)dscnt, + (unsigned)state->max_data_return, + (unsigned)psoff, (unsigned)dsoff)); + /* * All nttrans messages we handle have smb_wct == 19 + * state->setup_count. Ensure this is so as a sanity check. |