smbd: Tune "dir" a bit.
[Samba.git] / source3 / smbd / smb2_ioctl.c
blobe869839f2ed496bc5cd2d906c68915a798776a4d
1 /*
2 Unix SMB/CIFS implementation.
3 Core SMB2 server
5 Copyright (C) Stefan Metzmacher 2009
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
21 #include "includes.h"
22 #include "smbd/smbd.h"
23 #include "smbd/globals.h"
24 #include "../libcli/smb/smb_common.h"
25 #include "../lib/util/tevent_ntstatus.h"
26 #include "rpc_server/srv_pipe_hnd.h"
27 #include "include/ntioctl.h"
29 static struct tevent_req *smbd_smb2_ioctl_send(TALLOC_CTX *mem_ctx,
30 struct tevent_context *ev,
31 struct smbd_smb2_request *smb2req,
32 struct files_struct *in_fsp,
33 uint32_t in_ctl_code,
34 DATA_BLOB in_input,
35 uint32_t in_max_output,
36 uint32_t in_flags);
37 static NTSTATUS smbd_smb2_ioctl_recv(struct tevent_req *req,
38 TALLOC_CTX *mem_ctx,
39 DATA_BLOB *out_output);
41 static void smbd_smb2_request_ioctl_done(struct tevent_req *subreq);
42 NTSTATUS smbd_smb2_request_process_ioctl(struct smbd_smb2_request *req)
44 NTSTATUS status;
45 const uint8_t *inbody;
46 int i = req->current_idx;
47 uint32_t in_ctl_code;
48 uint64_t in_file_id_persistent;
49 uint64_t in_file_id_volatile;
50 struct files_struct *in_fsp = NULL;
51 uint32_t in_input_offset;
52 uint32_t in_input_length;
53 DATA_BLOB in_input_buffer;
54 uint32_t in_max_output_length;
55 uint32_t in_flags;
56 struct tevent_req *subreq;
58 status = smbd_smb2_request_verify_sizes(req, 0x39);
59 if (!NT_STATUS_IS_OK(status)) {
60 return smbd_smb2_request_error(req, status);
62 inbody = (const uint8_t *)req->in.vector[i+1].iov_base;
64 in_ctl_code = IVAL(inbody, 0x04);
65 in_file_id_persistent = BVAL(inbody, 0x08);
66 in_file_id_volatile = BVAL(inbody, 0x10);
67 in_input_offset = IVAL(inbody, 0x18);
68 in_input_length = IVAL(inbody, 0x1C);
69 in_max_output_length = IVAL(inbody, 0x2C);
70 in_flags = IVAL(inbody, 0x30);
73 * InputOffset (4 bytes): The offset, in bytes, from the beginning of
74 * the SMB2 header to the input data buffer. If no input data is
75 * required for the FSCTL/IOCTL command being issued, the client SHOULD
76 * set this value to 0.<49>
77 * <49> If no input data is required for the FSCTL/IOCTL command being
78 * issued, Windows-based clients set this field to any value.
80 if ((in_input_length > 0)
81 && (in_input_offset != (SMB2_HDR_BODY + req->in.vector[i+1].iov_len))) {
82 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
85 if (in_input_length > req->in.vector[i+2].iov_len) {
86 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
89 in_input_buffer.data = (uint8_t *)req->in.vector[i+2].iov_base;
90 in_input_buffer.length = in_input_length;
92 switch (in_ctl_code) {
93 case FSCTL_DFS_GET_REFERRALS:
94 case FSCTL_DFS_GET_REFERRALS_EX:
95 case FSCTL_PIPE_WAIT:
96 case FSCTL_VALIDATE_NEGOTIATE_INFO_224:
97 case FSCTL_VALIDATE_NEGOTIATE_INFO:
98 case FSCTL_QUERY_NETWORK_INTERFACE_INFO:
100 * Some SMB2 specific CtlCodes like FSCTL_DFS_GET_REFERRALS or
101 * FSCTL_PIPE_WAIT does not take a file handle.
103 * If FileId in the SMB2 Header of the request is not
104 * 0xFFFFFFFFFFFFFFFF, then the server MUST fail the request
105 * with STATUS_INVALID_PARAMETER.
107 if (in_file_id_persistent != UINT64_MAX ||
108 in_file_id_volatile != UINT64_MAX) {
109 return smbd_smb2_request_error(req,
110 NT_STATUS_INVALID_PARAMETER);
112 break;
113 default:
114 in_fsp = file_fsp_smb2(req, in_file_id_persistent,
115 in_file_id_volatile);
116 if (in_fsp == NULL) {
117 return smbd_smb2_request_error(req, NT_STATUS_FILE_CLOSED);
119 break;
122 subreq = smbd_smb2_ioctl_send(req,
123 req->sconn->smb2.event_ctx,
124 req, in_fsp,
125 in_ctl_code,
126 in_input_buffer,
127 in_max_output_length,
128 in_flags);
129 if (subreq == NULL) {
130 return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
132 tevent_req_set_callback(subreq, smbd_smb2_request_ioctl_done, req);
134 return smbd_smb2_request_pending_queue(req, subreq);
137 static void smbd_smb2_request_ioctl_done(struct tevent_req *subreq)
139 struct smbd_smb2_request *req = tevent_req_callback_data(subreq,
140 struct smbd_smb2_request);
141 const uint8_t *inbody;
142 int i = req->current_idx;
143 uint8_t *outhdr;
144 DATA_BLOB outbody;
145 DATA_BLOB outdyn;
146 uint32_t in_ctl_code;
147 uint64_t in_file_id_persistent;
148 uint64_t in_file_id_volatile;
149 uint32_t out_input_offset;
150 uint32_t out_output_offset;
151 DATA_BLOB out_output_buffer = data_blob_null;
152 NTSTATUS status;
153 NTSTATUS error; /* transport error */
155 status = smbd_smb2_ioctl_recv(subreq, req, &out_output_buffer);
157 DEBUG(10,("smbd_smb2_request_ioctl_done: smbd_smb2_ioctl_recv returned "
158 "%u status %s\n",
159 (unsigned int)out_output_buffer.length,
160 nt_errstr(status) ));
162 TALLOC_FREE(subreq);
163 if (NT_STATUS_EQUAL(status, STATUS_BUFFER_OVERFLOW)) {
164 /* also ok */
165 } else if (!NT_STATUS_IS_OK(status)) {
166 error = smbd_smb2_request_error(req, status);
167 if (!NT_STATUS_IS_OK(error)) {
168 smbd_server_connection_terminate(req->sconn,
169 nt_errstr(error));
170 return;
172 return;
175 out_input_offset = SMB2_HDR_BODY + 0x30;
176 out_output_offset = SMB2_HDR_BODY + 0x30;
178 inbody = (const uint8_t *)req->in.vector[i+1].iov_base;
180 in_ctl_code = IVAL(inbody, 0x04);
181 in_file_id_persistent = BVAL(inbody, 0x08);
182 in_file_id_volatile = BVAL(inbody, 0x10);
184 outhdr = (uint8_t *)req->out.vector[i].iov_base;
186 outbody = data_blob_talloc(req->out.vector, NULL, 0x30);
187 if (outbody.data == NULL) {
188 error = smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
189 if (!NT_STATUS_IS_OK(error)) {
190 smbd_server_connection_terminate(req->sconn,
191 nt_errstr(error));
192 return;
194 return;
197 SSVAL(outbody.data, 0x00, 0x30 + 1); /* struct size */
198 SSVAL(outbody.data, 0x02, 0); /* reserved */
199 SIVAL(outbody.data, 0x04,
200 in_ctl_code); /* ctl code */
201 SBVAL(outbody.data, 0x08,
202 in_file_id_persistent); /* file id (persistent) */
203 SBVAL(outbody.data, 0x10,
204 in_file_id_volatile); /* file id (volatile) */
205 SIVAL(outbody.data, 0x18,
206 out_input_offset); /* input offset */
207 SIVAL(outbody.data, 0x1C, 0); /* input count */
208 SIVAL(outbody.data, 0x20,
209 out_output_offset); /* output offset */
210 SIVAL(outbody.data, 0x24,
211 out_output_buffer.length); /* output count */
212 SIVAL(outbody.data, 0x28, 0); /* flags */
213 SIVAL(outbody.data, 0x2C, 0); /* reserved */
216 * Note: Windows Vista and 2008 send back also the
217 * input from the request. But it was fixed in
218 * Windows 7.
220 outdyn = out_output_buffer;
222 error = smbd_smb2_request_done_ex(req, status, outbody, &outdyn,
223 __location__);
224 if (!NT_STATUS_IS_OK(error)) {
225 smbd_server_connection_terminate(req->sconn,
226 nt_errstr(error));
227 return;
231 struct smbd_smb2_ioctl_state {
232 struct smbd_smb2_request *smb2req;
233 struct smb_request *smbreq;
234 files_struct *fsp;
235 DATA_BLOB in_input;
236 uint32_t in_max_output;
237 DATA_BLOB out_output;
240 static void smbd_smb2_ioctl_pipe_write_done(struct tevent_req *subreq);
241 static void smbd_smb2_ioctl_pipe_read_done(struct tevent_req *subreq);
243 static struct tevent_req *smbd_smb2_ioctl_send(TALLOC_CTX *mem_ctx,
244 struct tevent_context *ev,
245 struct smbd_smb2_request *smb2req,
246 struct files_struct *fsp,
247 uint32_t in_ctl_code,
248 DATA_BLOB in_input,
249 uint32_t in_max_output,
250 uint32_t in_flags)
252 struct tevent_req *req;
253 struct smbd_smb2_ioctl_state *state;
254 struct smb_request *smbreq;
255 struct tevent_req *subreq;
257 req = tevent_req_create(mem_ctx, &state,
258 struct smbd_smb2_ioctl_state);
259 if (req == NULL) {
260 return NULL;
262 state->smb2req = smb2req;
263 state->smbreq = NULL;
264 state->fsp = fsp;
265 state->in_input = in_input;
266 state->in_max_output = in_max_output;
267 state->out_output = data_blob_null;
269 DEBUG(10, ("smbd_smb2_ioctl: ctl_code[0x%08x] %s fnum[%d]\n",
270 (unsigned)in_ctl_code,
271 fsp ? fsp_str_dbg(fsp) : "<no handle>",
272 fsp ? fsp->fnum : -1));
274 smbreq = smbd_smb2_fake_smb_request(smb2req);
275 if (tevent_req_nomem(smbreq, req)) {
276 return tevent_req_post(req, ev);
278 state->smbreq = smbreq;
280 switch (in_ctl_code) {
281 case 0x00060194: /* FSCTL_DFS_GET_REFERRALS */
283 uint16_t in_max_referral_level;
284 DATA_BLOB in_file_name_buffer;
285 char *in_file_name_string;
286 size_t in_file_name_string_size;
287 bool ok;
288 bool overflow = false;
289 NTSTATUS status;
290 int dfs_size;
291 char *dfs_data = NULL;
293 if (!IS_IPC(smbreq->conn)) {
294 tevent_req_nterror(req, NT_STATUS_INVALID_DEVICE_REQUEST);
295 return tevent_req_post(req, ev);
298 if (!lp_host_msdfs()) {
299 tevent_req_nterror(req, NT_STATUS_FS_DRIVER_REQUIRED);
300 return tevent_req_post(req, ev);
303 if (in_input.length < (2 + 2)) {
304 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
305 return tevent_req_post(req, ev);
308 in_max_referral_level = SVAL(in_input.data, 0);
309 in_file_name_buffer.data = in_input.data + 2;
310 in_file_name_buffer.length = in_input.length - 2;
312 ok = convert_string_talloc(state, CH_UTF16, CH_UNIX,
313 in_file_name_buffer.data,
314 in_file_name_buffer.length,
315 &in_file_name_string,
316 &in_file_name_string_size, false);
317 if (!ok) {
318 tevent_req_nterror(req, NT_STATUS_ILLEGAL_CHARACTER);
319 return tevent_req_post(req, ev);
322 dfs_size = setup_dfs_referral(smbreq->conn,
323 in_file_name_string,
324 in_max_referral_level,
325 &dfs_data, &status);
326 if (dfs_size < 0) {
327 tevent_req_nterror(req, status);
328 return tevent_req_post(req, ev);
331 if (dfs_size > in_max_output) {
333 * TODO: we need a testsuite for this
335 overflow = true;
336 dfs_size = in_max_output;
339 state->out_output = data_blob_talloc(state,
340 (uint8_t *)dfs_data,
341 dfs_size);
342 SAFE_FREE(dfs_data);
343 if (dfs_size > 0 &&
344 tevent_req_nomem(state->out_output.data, req)) {
345 return tevent_req_post(req, ev);
348 if (overflow) {
349 tevent_req_nterror(req, STATUS_BUFFER_OVERFLOW);
350 } else {
351 tevent_req_done(req);
353 return tevent_req_post(req, ev);
355 case 0x0011C017: /* FSCTL_PIPE_TRANSCEIVE */
357 if (!IS_IPC(smbreq->conn)) {
358 tevent_req_nterror(req, NT_STATUS_NOT_SUPPORTED);
359 return tevent_req_post(req, ev);
362 if (fsp == NULL) {
363 tevent_req_nterror(req, NT_STATUS_FILE_CLOSED);
364 return tevent_req_post(req, ev);
367 if (!fsp_is_np(fsp)) {
368 tevent_req_nterror(req, NT_STATUS_FILE_CLOSED);
369 return tevent_req_post(req, ev);
372 DEBUG(10,("smbd_smb2_ioctl_send: np_write_send of size %u\n",
373 (unsigned int)in_input.length ));
375 subreq = np_write_send(state, ev,
376 fsp->fake_file_handle,
377 in_input.data,
378 in_input.length);
379 if (tevent_req_nomem(subreq, req)) {
380 return tevent_req_post(req, ev);
382 tevent_req_set_callback(subreq,
383 smbd_smb2_ioctl_pipe_write_done,
384 req);
385 return req;
387 default: {
388 uint8_t *out_data = NULL;
389 uint32_t out_data_len = 0;
390 NTSTATUS status;
392 if (fsp == NULL) {
393 tevent_req_nterror(req, NT_STATUS_FILE_CLOSED);
394 return tevent_req_post(req, ev);
397 status = smb_fsctl(fsp,
398 state,
399 in_ctl_code,
400 smbreq->flags2,
401 in_input.data,
402 in_input.length,
403 &out_data,
404 in_max_output,
405 &out_data_len);
406 state->out_output = data_blob_const(out_data, out_data_len);
407 if (NT_STATUS_IS_OK(status)) {
408 tevent_req_done(req);
409 return tevent_req_post(req, ev);
412 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) {
413 if (IS_IPC(smbreq->conn)) {
414 status = NT_STATUS_FS_DRIVER_REQUIRED;
415 } else {
416 status = NT_STATUS_INVALID_DEVICE_REQUEST;
420 tevent_req_nterror(req, status);
421 return tevent_req_post(req, ev);
425 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
426 return tevent_req_post(req, ev);
429 static void smbd_smb2_ioctl_pipe_write_done(struct tevent_req *subreq)
431 struct tevent_req *req = tevent_req_callback_data(subreq,
432 struct tevent_req);
433 struct smbd_smb2_ioctl_state *state = tevent_req_data(req,
434 struct smbd_smb2_ioctl_state);
435 NTSTATUS status;
436 ssize_t nwritten = -1;
438 status = np_write_recv(subreq, &nwritten);
440 DEBUG(10,("smbd_smb2_ioctl_pipe_write_done: received %ld\n",
441 (long int)nwritten ));
443 TALLOC_FREE(subreq);
444 if (!NT_STATUS_IS_OK(status)) {
445 tevent_req_nterror(req, status);
446 return;
449 if (nwritten != state->in_input.length) {
450 tevent_req_nterror(req, NT_STATUS_PIPE_NOT_AVAILABLE);
451 return;
454 state->out_output = data_blob_talloc(state, NULL, state->in_max_output);
455 if (state->in_max_output > 0 &&
456 tevent_req_nomem(state->out_output.data, req)) {
457 return;
460 DEBUG(10,("smbd_smb2_ioctl_pipe_write_done: issuing np_read_send "
461 "of size %u\n",
462 (unsigned int)state->out_output.length ));
464 TALLOC_FREE(subreq);
465 subreq = np_read_send(state->smbreq->conn,
466 state->smb2req->sconn->smb2.event_ctx,
467 state->fsp->fake_file_handle,
468 state->out_output.data,
469 state->out_output.length);
470 if (tevent_req_nomem(subreq, req)) {
471 return;
473 tevent_req_set_callback(subreq, smbd_smb2_ioctl_pipe_read_done, req);
476 static void smbd_smb2_ioctl_pipe_read_done(struct tevent_req *subreq)
478 struct tevent_req *req = tevent_req_callback_data(subreq,
479 struct tevent_req);
480 struct smbd_smb2_ioctl_state *state = tevent_req_data(req,
481 struct smbd_smb2_ioctl_state);
482 NTSTATUS status;
483 ssize_t nread = -1;
484 bool is_data_outstanding = false;
486 status = np_read_recv(subreq, &nread, &is_data_outstanding);
488 DEBUG(10,("smbd_smb2_ioctl_pipe_read_done: np_read_recv nread = %d "
489 "is_data_outstanding = %d, status = %s\n",
490 (int)nread,
491 (int)is_data_outstanding,
492 nt_errstr(status) ));
494 TALLOC_FREE(subreq);
495 if (!NT_STATUS_IS_OK(status)) {
496 tevent_req_nterror(req, status);
497 return;
500 state->out_output.length = nread;
502 if (is_data_outstanding) {
503 tevent_req_nterror(req, STATUS_BUFFER_OVERFLOW);
504 return;
507 tevent_req_done(req);
510 static NTSTATUS smbd_smb2_ioctl_recv(struct tevent_req *req,
511 TALLOC_CTX *mem_ctx,
512 DATA_BLOB *out_output)
514 NTSTATUS status = NT_STATUS_OK;
515 struct smbd_smb2_ioctl_state *state = tevent_req_data(req,
516 struct smbd_smb2_ioctl_state);
518 if (tevent_req_is_nterror(req, &status)) {
519 if (!NT_STATUS_EQUAL(status, STATUS_BUFFER_OVERFLOW)) {
520 tevent_req_received(req);
521 return status;
525 *out_output = state->out_output;
526 talloc_steal(mem_ctx, out_output->data);
528 tevent_req_received(req);
529 return status;