static bool cli_dfs_check_error(struct cli_state *cli, NTSTATUS expected, NTSTATUS status) { /* only deal with DS when we negotiated NT_STATUS codes and UNICODE */ if (!(smbXcli_conn_use_unicode(cli->conn))) { return false; } if (!(smb1cli_conn_capabilities(cli->conn) & CAP_STATUS32)) { return false; } if (NT_STATUS_EQUAL(status, expected)) { return true; } return false; }
struct tevent_req *smb1cli_trans_send( TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct smbXcli_conn *conn, uint8_t cmd, uint8_t additional_flags, uint8_t clear_flags, uint16_t additional_flags2, uint16_t clear_flags2, uint32_t timeout_msec, uint32_t pid, uint16_t tid, uint16_t uid, 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 tevent_req *req, *subreq; struct smb1cli_trans_state *state; int iov_count; uint8_t wct; NTSTATUS status; charset_t charset; req = tevent_req_create(mem_ctx, &state, struct smb1cli_trans_state); if (req == NULL) { return NULL; } if ((cmd == SMBtrans) || (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)); tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX); return tevent_req_post(req, ev); } } /* * The largest wct will be for nttrans (19+num_setup). Make sure we * don't overflow state->vwv in smb1cli_trans_format. */ if ((num_setup + 19) > ARRAY_SIZE(state->vwv)) { tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX); return tevent_req_post(req, ev); } state->conn = conn; state->ev = ev; state->cmd = cmd; state->additional_flags = additional_flags; state->clear_flags = clear_flags; state->additional_flags2 = additional_flags2; state->clear_flags2 = clear_flags2; state->timeout_msec = timeout_msec; state->flags = flags; state->num_rsetup = 0; state->rsetup = NULL; state->pid = pid; state->tid = tid; state->uid = uid; ZERO_STRUCT(state->rparam); ZERO_STRUCT(state->rdata); if (smbXcli_conn_use_unicode(conn)) { charset = CH_UTF16LE; } else { charset = CH_DOS; } if ((pipe_name != NULL) && (!convert_string_talloc(state, CH_UNIX, charset, pipe_name, strlen(pipe_name) + 1, &state->pipe_name_conv, &state->pipe_name_conv_len))) { tevent_req_nterror(req, NT_STATUS_NO_MEMORY); return tevent_req_post(req, ev); } state->fid = fid; /* trans2 */ state->function = function; /* nttrans */ state->setup = setup; state->num_setup = num_setup; state->max_setup = max_setup; state->param = param; state->num_param = num_param; state->param_sent = 0; state->rparam.max = max_param; state->data = data; state->num_data = num_data; state->data_sent = 0; state->rdata.max = max_data; smb1cli_trans_format(state, &wct, &iov_count); subreq = smb1cli_req_create(state, ev, conn, cmd, state->additional_flags, state->clear_flags, state->additional_flags2, state->clear_flags2, state->timeout_msec, state->pid, state->tid, state->uid, wct, state->vwv, iov_count, state->iov); if (tevent_req_nomem(subreq, req)) { return tevent_req_post(req, ev); } status = smb1cli_req_chain_submit(&subreq, 1); if (tevent_req_nterror(req, status)) { return tevent_req_post(req, state->ev); } tevent_req_set_callback(subreq, smb1cli_trans_done, req); /* * Now get the MID of the primary request * and mark it as persistent. This means * we will able to send and receive multiple * SMB pdus using this MID in both directions * (including correct SMB signing). */ state->mid = smb1cli_req_mid(subreq); smb1cli_req_set_mid(subreq, state->mid); state->primary_subreq = subreq; talloc_set_destructor(state, smb1cli_trans_state_destructor); tevent_req_set_cancel_fn(req, smb1cli_trans_cancel); return req; }
static void smb1cli_trans_format(struct smb1cli_trans_state *state, uint8_t *pwct, int *piov_count) { uint8_t wct = 0; struct iovec *iov = state->iov; uint8_t *pad = state->pad; uint16_t *vwv = state->vwv; uint32_t param_offset; uint32_t this_param = 0; uint32_t param_pad; uint32_t data_offset; uint32_t this_data = 0; uint32_t data_pad; uint32_t useable_space; uint8_t cmd; uint32_t max_trans = smb1cli_conn_max_xmit(state->conn); 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 = MIN_SMB_SIZE; switch (cmd) { case SMBtrans: if (smbXcli_conn_use_unicode(state->conn)) { pad[0] = 0; iov[0].iov_base = (void *)pad; iov[0].iov_len = 1; param_offset += 1; iov += 1; } iov[0].iov_base = (void *)state->pipe_name_conv; iov[0].iov_len = state->pipe_name_conv_len; wct = 14 + state->num_setup; param_offset += iov[0].iov_len; iov += 1; break; case SMBtrans2: pad[0] = 0; pad[1] = 'D'; /* Copy this from "old" 3.0 behaviour */ pad[2] = ' '; iov[0].iov_base = (void *)pad; iov[0].iov_len = 3; wct = 14 + state->num_setup; param_offset += 3; iov += 1; break; case SMBtranss: wct = 8; break; case SMBtranss2: wct = 9; break; case SMBnttrans: wct = 19 + state->num_setup; break; case SMBnttranss: wct = 18; break; } param_offset += wct * sizeof(uint16_t); useable_space = max_trans - param_offset; param_pad = param_offset % 4; if (param_pad > 0) { param_pad = MIN(param_pad, useable_space); iov[0].iov_base = (void *)state->zero_pad; iov[0].iov_len = param_pad; iov += 1; param_offset += param_pad; } useable_space = max_trans - param_offset; if (state->param_sent < state->num_param) { this_param = MIN(state->num_param - state->param_sent, useable_space); iov[0].iov_base = (void *)(state->param + state->param_sent); iov[0].iov_len = this_param; iov += 1; } data_offset = param_offset + this_param; useable_space = max_trans - data_offset; data_pad = data_offset % 4; if (data_pad > 0) { data_pad = MIN(data_pad, useable_space); iov[0].iov_base = (void *)state->zero_pad; iov[0].iov_len = data_pad; iov += 1; data_offset += data_pad; } useable_space = max_trans - data_offset; if (state->data_sent < state->num_data) { this_data = MIN(state->num_data - state->data_sent, useable_space); iov[0].iov_base = (void *)(state->data + state->data_sent); iov[0].iov_len = this_data; iov += 1; } 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_pad=%u, param_disp=%u, " "data_offset=%u, data_pad=%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)param_pad, (unsigned)state->param_sent, (unsigned)data_offset, (unsigned)data_pad, (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, data_offset); 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, data_offset); SSVAL(vwv + 7, 0, state->data_sent); if (cmd == SMBtranss2) { SSVAL(vwv + 8, 0, state->fid); } break; case SMBnttrans: SCVAL(vwv + 0, 0, state->max_setup); SSVAL(vwv + 0, 1, 0); /* reserved */ SIVAL(vwv + 1, 1, state->num_param); SIVAL(vwv + 3, 1, state->num_data); SIVAL(vwv + 5, 1, state->rparam.max); SIVAL(vwv + 7, 1, state->rdata.max); SIVAL(vwv + 9, 1, this_param); SIVAL(vwv +11, 1, param_offset); SIVAL(vwv +13, 1, this_data); SIVAL(vwv +15, 1, data_offset); SCVAL(vwv +17, 1, state->num_setup); SSVAL(vwv +18, 0, state->function); memcpy(vwv + 19, state->setup, sizeof(uint16_t) * state->num_setup); break; case SMBnttranss: SSVAL(vwv + 0, 0, 0); /* reserved */ SCVAL(vwv + 1, 0, 0); /* reserved */ SIVAL(vwv + 1, 1, state->num_param); SIVAL(vwv + 3, 1, state->num_data); SIVAL(vwv + 5, 1, this_param); SIVAL(vwv + 7, 1, param_offset); SIVAL(vwv + 9, 1, state->param_sent); SIVAL(vwv +11, 1, this_data); SIVAL(vwv +13, 1, data_offset); SIVAL(vwv +15, 1, state->data_sent); SCVAL(vwv +17, 1, 0); /* reserved */ break; } state->param_sent += this_param; state->data_sent += this_data; *pwct = wct; *piov_count = iov - state->iov; }
NTSTATUS cli_dfs_get_referral(TALLOC_CTX *ctx, struct cli_state *cli, const char *path, struct client_dfs_referral **refs, size_t *num_refs, size_t *consumed) { unsigned int param_len = 0; uint16_t recv_flags2; uint8_t *param = NULL; uint8_t *rdata = NULL; char *p; char *endp; smb_ucs2_t *path_ucs; char *consumed_path = NULL; uint16_t consumed_ucs; uint16_t num_referrals; struct client_dfs_referral *referrals = NULL; NTSTATUS status; TALLOC_CTX *frame = talloc_stackframe(); *num_refs = 0; *refs = NULL; param = talloc_array(talloc_tos(), uint8_t, 2); if (!param) { status = NT_STATUS_NO_MEMORY; goto out; } SSVAL(param, 0, 0x03); /* max referral level */ param = trans2_bytes_push_str(param, smbXcli_conn_use_unicode(cli->conn), path, strlen(path)+1, NULL); if (!param) { status = NT_STATUS_NO_MEMORY; goto out; } param_len = talloc_get_size(param); path_ucs = (smb_ucs2_t *)¶m[2]; if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { DATA_BLOB in_input_buffer; DATA_BLOB in_output_buffer = data_blob_null; DATA_BLOB out_input_buffer = data_blob_null; DATA_BLOB out_output_buffer = data_blob_null; in_input_buffer.data = param; in_input_buffer.length = param_len; status = smb2cli_ioctl(cli->conn, cli->timeout, cli->smb2.session, cli->smb2.tcon, UINT64_MAX, /* in_fid_persistent */ UINT64_MAX, /* in_fid_volatile */ FSCTL_DFS_GET_REFERRALS, 0, /* in_max_input_length */ &in_input_buffer, CLI_BUFFER_SIZE, /* in_max_output_length */ &in_output_buffer, SMB2_IOCTL_FLAG_IS_FSCTL, talloc_tos(), &out_input_buffer, &out_output_buffer); if (!NT_STATUS_IS_OK(status)) { goto out; } if (out_output_buffer.length < 4) { status = NT_STATUS_INVALID_NETWORK_RESPONSE; goto out; } recv_flags2 = FLAGS2_UNICODE_STRINGS; rdata = out_output_buffer.data; endp = (char *)rdata + out_output_buffer.length; } else { unsigned int data_len = 0; uint16_t setup[1]; SSVAL(setup, 0, TRANSACT2_GET_DFS_REFERRAL); status = cli_trans(talloc_tos(), cli, SMBtrans2, NULL, 0xffff, 0, 0, setup, 1, 0, param, param_len, 2, NULL, 0, CLI_BUFFER_SIZE, &recv_flags2, NULL, 0, NULL, /* rsetup */ NULL, 0, NULL, &rdata, 4, &data_len); if (!NT_STATUS_IS_OK(status)) { goto out; } endp = (char *)rdata + data_len; } consumed_ucs = SVAL(rdata, 0); num_referrals = SVAL(rdata, 2); /* consumed_ucs is the number of bytes * of the UCS2 path consumed not counting any * terminating null. We need to convert * back to unix charset and count again * to get the number of bytes consumed from * the incoming path. */ errno = 0; if (pull_string_talloc(talloc_tos(), NULL, 0, &consumed_path, path_ucs, consumed_ucs, STR_UNICODE) == 0) { if (errno != 0) { status = map_nt_error_from_unix(errno); } else { status = NT_STATUS_INVALID_NETWORK_RESPONSE; } goto out; } if (consumed_path == NULL) { status = map_nt_error_from_unix(errno); goto out; } *consumed = strlen(consumed_path); if (num_referrals != 0) { uint16_t ref_version; uint16_t ref_size; int i; uint16_t node_offset; referrals = talloc_array(ctx, struct client_dfs_referral, num_referrals); if (!referrals) { status = NT_STATUS_NO_MEMORY; goto out; } /* start at the referrals array */ p = (char *)rdata+8; for (i=0; i<num_referrals && p < endp; i++) { if (p + 18 > endp) { goto out; } ref_version = SVAL(p, 0); ref_size = SVAL(p, 2); node_offset = SVAL(p, 16); if (ref_version != 3) { p += ref_size; continue; } referrals[i].proximity = SVAL(p, 8); referrals[i].ttl = SVAL(p, 10); if (p + node_offset > endp) { status = NT_STATUS_INVALID_NETWORK_RESPONSE; goto out; } clistr_pull_talloc(referrals, (const char *)rdata, recv_flags2, &referrals[i].dfspath, p+node_offset, PTR_DIFF(endp, p+node_offset), STR_TERMINATE|STR_UNICODE); if (!referrals[i].dfspath) { status = map_nt_error_from_unix(errno); goto out; } p += ref_size; } if (i < num_referrals) { status = NT_STATUS_INVALID_NETWORK_RESPONSE; goto out; } }