X-Git-Url: http://git.samba.org/samba.git/?p=ira%2Fwip.git;a=blobdiff_plain;f=source3%2Fsmbd%2Fsmb2_ioctl.c;h=9ff6c8ea979436fe180444f597ffab4beed761a7;hp=3ebce3eba8f36aac4965b05ce85f31903b60d795;hb=0e261d0e9c89ff11dc37b2bfd70c74c3a06486bd;hpb=fabdebdcf4479fe85db41577c7acfa08378bd425 diff --git a/source3/smbd/smb2_ioctl.c b/source3/smbd/smb2_ioctl.c index 3ebce3eba8f..9ff6c8ea979 100644 --- a/source3/smbd/smb2_ioctl.c +++ b/source3/smbd/smb2_ioctl.c @@ -109,10 +109,15 @@ static void smbd_smb2_request_ioctl_done(struct tevent_req *subreq) { struct smbd_smb2_request *req = tevent_req_callback_data(subreq, struct smbd_smb2_request); + const uint8_t *inbody; int i = req->current_idx; uint8_t *outhdr; DATA_BLOB outbody; DATA_BLOB outdyn; + uint32_t in_ctl_code; + uint64_t in_file_id_persistent; + uint64_t in_file_id_volatile; + uint32_t out_input_offset; uint32_t out_output_offset; DATA_BLOB out_output_buffer; NTSTATUS status; @@ -120,7 +125,9 @@ static void smbd_smb2_request_ioctl_done(struct tevent_req *subreq) status = smbd_smb2_ioctl_recv(subreq, req, &out_output_buffer); TALLOC_FREE(subreq); - if (!NT_STATUS_IS_OK(status)) { + if (NT_STATUS_EQUAL(status, STATUS_BUFFER_OVERFLOW)) { + /* also ok */ + } else if (!NT_STATUS_IS_OK(status)) { error = smbd_smb2_request_error(req, status); if (!NT_STATUS_IS_OK(error)) { smbd_server_connection_terminate(req->conn, @@ -130,8 +137,15 @@ static void smbd_smb2_request_ioctl_done(struct tevent_req *subreq) return; } + out_input_offset = SMB2_HDR_BODY + 0x30; out_output_offset = SMB2_HDR_BODY + 0x30; + inbody = (const uint8_t *)req->in.vector[i+1].iov_base; + + in_ctl_code = IVAL(inbody, 0x04); + in_file_id_persistent = BVAL(inbody, 0x08); + in_file_id_volatile = BVAL(inbody, 0x10); + outhdr = (uint8_t *)req->out.vector[i].iov_base; outbody = data_blob_talloc(req->out.vector, NULL, 0x30); @@ -147,10 +161,14 @@ static void smbd_smb2_request_ioctl_done(struct tevent_req *subreq) SSVAL(outbody.data, 0x00, 0x30 + 1); /* struct size */ SSVAL(outbody.data, 0x02, 0); /* reserved */ - SIVAL(outbody.data, 0x04, 0); /* ctl code */ - SBVAL(outbody.data, 0x08, 0); /* file id (persistent) */ - SBVAL(outbody.data, 0x10, 0); /* file id (volatile) */ - SIVAL(outbody.data, 0x18, 0); /* input offset */ + SIVAL(outbody.data, 0x04, + in_ctl_code); /* ctl code */ + SBVAL(outbody.data, 0x08, + in_file_id_persistent); /* file id (persistent) */ + SBVAL(outbody.data, 0x10, + in_file_id_volatile); /* file id (volatile) */ + SIVAL(outbody.data, 0x18, + out_input_offset); /* input offset */ SIVAL(outbody.data, 0x1C, 0); /* input count */ SIVAL(outbody.data, 0x20, out_output_offset); /* output offset */ @@ -160,13 +178,14 @@ static void smbd_smb2_request_ioctl_done(struct tevent_req *subreq) SIVAL(outbody.data, 0x2C, 0); /* reserved */ /* - * Note: Windows sends back also the input from the request. - * I think this is stupid and I hope not required. - * For now we avoid a talloc + memcopy here... + * Note: Windows Vista and 2008 send back also the + * input from the request. But it was fixed in + * Windows 7. */ outdyn = out_output_buffer; - error = smbd_smb2_request_done(req, outbody, &outdyn); + error = smbd_smb2_request_done_ex(req, status, outbody, &outdyn, + __location__); if (!NT_STATUS_IS_OK(error)) { smbd_server_connection_terminate(req->conn, nt_errstr(error)); @@ -183,6 +202,9 @@ struct smbd_smb2_ioctl_state { DATA_BLOB out_output; }; +static void smbd_smb2_ioctl_pipe_write_done(struct tevent_req *subreq); +static void smbd_smb2_ioctl_pipe_read_done(struct tevent_req *subreq); + static struct tevent_req *smbd_smb2_ioctl_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct smbd_smb2_request *smb2req, @@ -237,6 +259,109 @@ static struct tevent_req *smbd_smb2_ioctl_send(TALLOC_CTX *mem_ctx, } switch (in_ctl_code) { + case 0x00060194: /* FSCTL_DFS_GET_REFERRALS */ + { + uint16_t in_max_referral_level; + DATA_BLOB in_file_name_buffer; + char *in_file_name_string; + size_t in_file_name_string_size; + bool ok; + bool overflow = false; + NTSTATUS status; + int dfs_size; + char *dfs_data = NULL; + + if (!IS_IPC(smbreq->conn)) { + tevent_req_nterror(req, NT_STATUS_INVALID_DEVICE_REQUEST); + return tevent_req_post(req, ev); + } + + if (!lp_host_msdfs()) { + tevent_req_nterror(req, NT_STATUS_FS_DRIVER_REQUIRED); + return tevent_req_post(req, ev); + } + + if (in_input.length < (2 + 2)) { + tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); + return tevent_req_post(req, ev); + } + + in_max_referral_level = SVAL(in_input.data, 0); + in_file_name_buffer.data = in_input.data + 2; + in_file_name_buffer.length = in_input.length - 2; + + ok = convert_string_talloc(state, CH_UTF16, CH_UNIX, + in_file_name_buffer.data, + in_file_name_buffer.length, + &in_file_name_string, + &in_file_name_string_size, false); + if (!ok) { + tevent_req_nterror(req, NT_STATUS_ILLEGAL_CHARACTER); + return tevent_req_post(req, ev); + } + + dfs_size = setup_dfs_referral(smbreq->conn, + in_file_name_string, + in_max_referral_level, + &dfs_data, &status); + if (dfs_size < 0) { + tevent_req_nterror(req, status); + return tevent_req_post(req, ev); + } + + if (dfs_size > in_max_output) { + /* + * TODO: we need a testsuite for this + */ + overflow = true; + dfs_size = in_max_output; + } + + state->out_output = data_blob_talloc(state, + (uint8_t *)dfs_data, + dfs_size); + SAFE_FREE(dfs_data); + if (dfs_size > 0 && + tevent_req_nomem(state->out_output.data, req)) { + return tevent_req_post(req, ev); + } + + if (overflow) { + tevent_req_nterror(req, STATUS_BUFFER_OVERFLOW); + } else { + tevent_req_done(req); + } + return tevent_req_post(req, ev); + } + case 0x0011C017: /* FSCTL_PIPE_TRANSCEIVE */ + + if (!IS_IPC(smbreq->conn)) { + tevent_req_nterror(req, NT_STATUS_NOT_SUPPORTED); + return tevent_req_post(req, ev); + } + + if (fsp == NULL) { + tevent_req_nterror(req, NT_STATUS_FILE_CLOSED); + return tevent_req_post(req, ev); + } + + if (!fsp_is_np(fsp)) { + tevent_req_nterror(req, NT_STATUS_FILE_CLOSED); + return tevent_req_post(req, ev); + } + + subreq = np_write_send(state, ev, + fsp->fake_file_handle, + in_input.data, + in_input.length); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, + smbd_smb2_ioctl_pipe_write_done, + req); + return req; + default: if (IS_IPC(smbreq->conn)) { tevent_req_nterror(req, NT_STATUS_FS_DRIVER_REQUIRED); @@ -250,6 +375,66 @@ static struct tevent_req *smbd_smb2_ioctl_send(TALLOC_CTX *mem_ctx, return tevent_req_post(req, ev); } +static void smbd_smb2_ioctl_pipe_write_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct smbd_smb2_ioctl_state *state = tevent_req_data(req, + struct smbd_smb2_ioctl_state); + NTSTATUS status; + ssize_t nwritten = -1; + + status = np_write_recv(subreq, &nwritten); + TALLOC_FREE(subreq); + if (!NT_STATUS_IS_OK(status)) { + tevent_req_nterror(req, status); + return; + } + + if (nwritten != state->in_input.length) { + tevent_req_nterror(req, NT_STATUS_PIPE_NOT_AVAILABLE); + return; + } + + state->out_output = data_blob_talloc(state, NULL, state->in_max_output); + if (state->in_max_output > 0 && + tevent_req_nomem(state->out_output.data, req)) { + return; + } + + subreq = np_read_send(state->smbreq->conn, + state->smb2req->conn->smb2.event_ctx, + state->fsp->fake_file_handle, + state->out_output.data, + state->out_output.length); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, smbd_smb2_ioctl_pipe_read_done, req); +} + +static void smbd_smb2_ioctl_pipe_read_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct smbd_smb2_ioctl_state *state = tevent_req_data(req, + struct smbd_smb2_ioctl_state); + NTSTATUS status; + ssize_t nread; + bool is_data_outstanding; + + status = np_read_recv(subreq, &nread, &is_data_outstanding); + TALLOC_FREE(subreq); + if (!NT_STATUS_IS_OK(status)) { + tevent_req_nterror(req, status); + return; + } + + state->out_output.length = nread; + + tevent_req_done(req); +} + static NTSTATUS smbd_smb2_ioctl_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, DATA_BLOB *out_output) @@ -259,13 +444,15 @@ static NTSTATUS smbd_smb2_ioctl_recv(struct tevent_req *req, struct smbd_smb2_ioctl_state); if (tevent_req_is_nterror(req, &status)) { - tevent_req_received(req); - return status; + if (!NT_STATUS_EQUAL(status, STATUS_BUFFER_OVERFLOW)) { + tevent_req_received(req); + return status; + } } *out_output = state->out_output; talloc_steal(mem_ctx, out_output->data); tevent_req_received(req); - return NT_STATUS_OK; + return status; }