talloc: setup the new 'tc' before TC_UNDEFINE_GROW_CHUNK() _talloc_realloc()
[Samba.git] / source3 / smbd / smb2_ioctl.c
blob6dc0ceca1ce964e741c3051682ecba5ec2e95440
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"
28 static struct tevent_req *smbd_smb2_ioctl_send(TALLOC_CTX *mem_ctx,
29 struct tevent_context *ev,
30 struct smbd_smb2_request *smb2req,
31 uint32_t in_ctl_code,
32 uint64_t in_file_id_volatile,
33 DATA_BLOB in_input,
34 uint32_t in_max_output,
35 uint32_t in_flags);
36 static NTSTATUS smbd_smb2_ioctl_recv(struct tevent_req *req,
37 TALLOC_CTX *mem_ctx,
38 DATA_BLOB *out_output);
40 static void smbd_smb2_request_ioctl_done(struct tevent_req *subreq);
41 NTSTATUS smbd_smb2_request_process_ioctl(struct smbd_smb2_request *req)
43 const uint8_t *inhdr;
44 const uint8_t *inbody;
45 int i = req->current_idx;
46 size_t expected_body_size = 0x39;
47 size_t body_size;
48 uint32_t in_ctl_code;
49 uint64_t in_file_id_persistent;
50 uint64_t in_file_id_volatile;
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 inhdr = (const uint8_t *)req->in.vector[i+0].iov_base;
59 if (req->in.vector[i+1].iov_len != (expected_body_size & 0xFFFFFFFE)) {
60 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
63 inbody = (const uint8_t *)req->in.vector[i+1].iov_base;
65 body_size = SVAL(inbody, 0x00);
66 if (body_size != expected_body_size) {
67 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
70 in_ctl_code = IVAL(inbody, 0x04);
71 in_file_id_persistent = BVAL(inbody, 0x08);
72 in_file_id_volatile = BVAL(inbody, 0x10);
73 in_input_offset = IVAL(inbody, 0x18);
74 in_input_length = IVAL(inbody, 0x1C);
75 in_max_output_length = IVAL(inbody, 0x2C);
76 in_flags = IVAL(inbody, 0x30);
78 if (in_input_offset != (SMB2_HDR_BODY + (body_size & 0xFFFFFFFE))) {
79 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
82 if (in_input_length > req->in.vector[i+2].iov_len) {
83 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
86 in_input_buffer.data = (uint8_t *)req->in.vector[i+2].iov_base;
87 in_input_buffer.length = in_input_length;
89 if (req->compat_chain_fsp) {
90 /* skip check */
91 } else if (in_file_id_persistent == UINT64_MAX &&
92 in_file_id_volatile == UINT64_MAX) {
93 /* without a handle */
94 } else if (in_file_id_persistent != in_file_id_volatile) {
95 return smbd_smb2_request_error(req, NT_STATUS_FILE_CLOSED);
98 subreq = smbd_smb2_ioctl_send(req,
99 req->sconn->smb2.event_ctx,
100 req,
101 in_ctl_code,
102 in_file_id_volatile,
103 in_input_buffer,
104 in_max_output_length,
105 in_flags);
106 if (subreq == NULL) {
107 return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
109 tevent_req_set_callback(subreq, smbd_smb2_request_ioctl_done, req);
111 return smbd_smb2_request_pending_queue(req, subreq);
114 static void smbd_smb2_request_ioctl_done(struct tevent_req *subreq)
116 struct smbd_smb2_request *req = tevent_req_callback_data(subreq,
117 struct smbd_smb2_request);
118 const uint8_t *inbody;
119 int i = req->current_idx;
120 uint8_t *outhdr;
121 DATA_BLOB outbody;
122 DATA_BLOB outdyn;
123 uint32_t in_ctl_code;
124 uint64_t in_file_id_persistent;
125 uint64_t in_file_id_volatile;
126 uint32_t out_input_offset;
127 uint32_t out_output_offset;
128 DATA_BLOB out_output_buffer = data_blob_null;
129 NTSTATUS status;
130 NTSTATUS error; /* transport error */
132 status = smbd_smb2_ioctl_recv(subreq, req, &out_output_buffer);
134 DEBUG(10,("smbd_smb2_request_ioctl_done: smbd_smb2_ioctl_recv returned "
135 "%u status %s\n",
136 (unsigned int)out_output_buffer.length,
137 nt_errstr(status) ));
139 TALLOC_FREE(subreq);
140 if (NT_STATUS_EQUAL(status, STATUS_BUFFER_OVERFLOW)) {
141 /* also ok */
142 } else if (!NT_STATUS_IS_OK(status)) {
143 error = smbd_smb2_request_error(req, status);
144 if (!NT_STATUS_IS_OK(error)) {
145 smbd_server_connection_terminate(req->sconn,
146 nt_errstr(error));
147 return;
149 return;
152 out_input_offset = SMB2_HDR_BODY + 0x30;
153 out_output_offset = SMB2_HDR_BODY + 0x30;
155 inbody = (const uint8_t *)req->in.vector[i+1].iov_base;
157 in_ctl_code = IVAL(inbody, 0x04);
158 in_file_id_persistent = BVAL(inbody, 0x08);
159 in_file_id_volatile = BVAL(inbody, 0x10);
161 outhdr = (uint8_t *)req->out.vector[i].iov_base;
163 outbody = data_blob_talloc(req->out.vector, NULL, 0x30);
164 if (outbody.data == NULL) {
165 error = smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
166 if (!NT_STATUS_IS_OK(error)) {
167 smbd_server_connection_terminate(req->sconn,
168 nt_errstr(error));
169 return;
171 return;
174 SSVAL(outbody.data, 0x00, 0x30 + 1); /* struct size */
175 SSVAL(outbody.data, 0x02, 0); /* reserved */
176 SIVAL(outbody.data, 0x04,
177 in_ctl_code); /* ctl code */
178 SBVAL(outbody.data, 0x08,
179 in_file_id_persistent); /* file id (persistent) */
180 SBVAL(outbody.data, 0x10,
181 in_file_id_volatile); /* file id (volatile) */
182 SIVAL(outbody.data, 0x18,
183 out_input_offset); /* input offset */
184 SIVAL(outbody.data, 0x1C, 0); /* input count */
185 SIVAL(outbody.data, 0x20,
186 out_output_offset); /* output offset */
187 SIVAL(outbody.data, 0x24,
188 out_output_buffer.length); /* output count */
189 SIVAL(outbody.data, 0x28, 0); /* flags */
190 SIVAL(outbody.data, 0x2C, 0); /* reserved */
193 * Note: Windows Vista and 2008 send back also the
194 * input from the request. But it was fixed in
195 * Windows 7.
197 outdyn = out_output_buffer;
199 error = smbd_smb2_request_done_ex(req, status, outbody, &outdyn,
200 __location__);
201 if (!NT_STATUS_IS_OK(error)) {
202 smbd_server_connection_terminate(req->sconn,
203 nt_errstr(error));
204 return;
208 struct smbd_smb2_ioctl_state {
209 struct smbd_smb2_request *smb2req;
210 struct smb_request *smbreq;
211 files_struct *fsp;
212 DATA_BLOB in_input;
213 uint32_t in_max_output;
214 DATA_BLOB out_output;
217 static void smbd_smb2_ioctl_pipe_write_done(struct tevent_req *subreq);
218 static void smbd_smb2_ioctl_pipe_read_done(struct tevent_req *subreq);
220 static struct tevent_req *smbd_smb2_ioctl_send(TALLOC_CTX *mem_ctx,
221 struct tevent_context *ev,
222 struct smbd_smb2_request *smb2req,
223 uint32_t in_ctl_code,
224 uint64_t in_file_id_volatile,
225 DATA_BLOB in_input,
226 uint32_t in_max_output,
227 uint32_t in_flags)
229 struct tevent_req *req;
230 struct smbd_smb2_ioctl_state *state;
231 struct smb_request *smbreq;
232 files_struct *fsp = NULL;
233 struct tevent_req *subreq;
235 req = tevent_req_create(mem_ctx, &state,
236 struct smbd_smb2_ioctl_state);
237 if (req == NULL) {
238 return NULL;
240 state->smb2req = smb2req;
241 state->smbreq = NULL;
242 state->fsp = NULL;
243 state->in_input = in_input;
244 state->in_max_output = in_max_output;
245 state->out_output = data_blob_null;
247 DEBUG(10,("smbd_smb2_ioctl: file_id[0x%016llX]\n",
248 (unsigned long long)in_file_id_volatile));
250 smbreq = smbd_smb2_fake_smb_request(smb2req);
251 if (tevent_req_nomem(smbreq, req)) {
252 return tevent_req_post(req, ev);
254 state->smbreq = smbreq;
256 if (in_file_id_volatile != UINT64_MAX) {
257 fsp = file_fsp(smbreq, (uint16_t)in_file_id_volatile);
258 if (fsp == NULL) {
259 tevent_req_nterror(req, NT_STATUS_FILE_CLOSED);
260 return tevent_req_post(req, ev);
262 if (smbreq->conn != fsp->conn) {
263 tevent_req_nterror(req, NT_STATUS_FILE_CLOSED);
264 return tevent_req_post(req, ev);
266 if (smb2req->session->vuid != fsp->vuid) {
267 tevent_req_nterror(req, NT_STATUS_FILE_CLOSED);
268 return tevent_req_post(req, ev);
270 state->fsp = fsp;
273 switch (in_ctl_code) {
274 case 0x00060194: /* FSCTL_DFS_GET_REFERRALS */
276 uint16_t in_max_referral_level;
277 DATA_BLOB in_file_name_buffer;
278 char *in_file_name_string;
279 size_t in_file_name_string_size;
280 bool ok;
281 bool overflow = false;
282 NTSTATUS status;
283 int dfs_size;
284 char *dfs_data = NULL;
286 if (!IS_IPC(smbreq->conn)) {
287 tevent_req_nterror(req, NT_STATUS_INVALID_DEVICE_REQUEST);
288 return tevent_req_post(req, ev);
291 if (!lp_host_msdfs()) {
292 tevent_req_nterror(req, NT_STATUS_FS_DRIVER_REQUIRED);
293 return tevent_req_post(req, ev);
296 if (in_input.length < (2 + 2)) {
297 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
298 return tevent_req_post(req, ev);
301 in_max_referral_level = SVAL(in_input.data, 0);
302 in_file_name_buffer.data = in_input.data + 2;
303 in_file_name_buffer.length = in_input.length - 2;
305 ok = convert_string_talloc(state, CH_UTF16, CH_UNIX,
306 in_file_name_buffer.data,
307 in_file_name_buffer.length,
308 &in_file_name_string,
309 &in_file_name_string_size, false);
310 if (!ok) {
311 tevent_req_nterror(req, NT_STATUS_ILLEGAL_CHARACTER);
312 return tevent_req_post(req, ev);
315 dfs_size = setup_dfs_referral(smbreq->conn,
316 in_file_name_string,
317 in_max_referral_level,
318 &dfs_data, &status);
319 if (dfs_size < 0) {
320 tevent_req_nterror(req, status);
321 return tevent_req_post(req, ev);
324 if (dfs_size > in_max_output) {
326 * TODO: we need a testsuite for this
328 overflow = true;
329 dfs_size = in_max_output;
332 state->out_output = data_blob_talloc(state,
333 (uint8_t *)dfs_data,
334 dfs_size);
335 SAFE_FREE(dfs_data);
336 if (dfs_size > 0 &&
337 tevent_req_nomem(state->out_output.data, req)) {
338 return tevent_req_post(req, ev);
341 if (overflow) {
342 tevent_req_nterror(req, STATUS_BUFFER_OVERFLOW);
343 } else {
344 tevent_req_done(req);
346 return tevent_req_post(req, ev);
348 case 0x0011C017: /* FSCTL_PIPE_TRANSCEIVE */
350 if (!IS_IPC(smbreq->conn)) {
351 tevent_req_nterror(req, NT_STATUS_NOT_SUPPORTED);
352 return tevent_req_post(req, ev);
355 if (fsp == NULL) {
356 tevent_req_nterror(req, NT_STATUS_FILE_CLOSED);
357 return tevent_req_post(req, ev);
360 if (!fsp_is_np(fsp)) {
361 tevent_req_nterror(req, NT_STATUS_FILE_CLOSED);
362 return tevent_req_post(req, ev);
365 DEBUG(10,("smbd_smb2_ioctl_send: np_write_send of size %u\n",
366 (unsigned int)in_input.length ));
368 subreq = np_write_send(state, ev,
369 fsp->fake_file_handle,
370 in_input.data,
371 in_input.length);
372 if (tevent_req_nomem(subreq, req)) {
373 return tevent_req_post(req, ev);
375 tevent_req_set_callback(subreq,
376 smbd_smb2_ioctl_pipe_write_done,
377 req);
378 return req;
380 default:
381 if (IS_IPC(smbreq->conn)) {
382 tevent_req_nterror(req, NT_STATUS_FS_DRIVER_REQUIRED);
383 return tevent_req_post(req, ev);
385 tevent_req_nterror(req, NT_STATUS_INVALID_DEVICE_REQUEST);
386 return tevent_req_post(req, ev);
389 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
390 return tevent_req_post(req, ev);
393 static void smbd_smb2_ioctl_pipe_write_done(struct tevent_req *subreq)
395 struct tevent_req *req = tevent_req_callback_data(subreq,
396 struct tevent_req);
397 struct smbd_smb2_ioctl_state *state = tevent_req_data(req,
398 struct smbd_smb2_ioctl_state);
399 NTSTATUS status;
400 ssize_t nwritten = -1;
402 status = np_write_recv(subreq, &nwritten);
404 DEBUG(10,("smbd_smb2_ioctl_pipe_write_done: received %ld\n",
405 (long int)nwritten ));
407 TALLOC_FREE(subreq);
408 if (!NT_STATUS_IS_OK(status)) {
409 tevent_req_nterror(req, status);
410 return;
413 if (nwritten != state->in_input.length) {
414 tevent_req_nterror(req, NT_STATUS_PIPE_NOT_AVAILABLE);
415 return;
418 state->out_output = data_blob_talloc(state, NULL, state->in_max_output);
419 if (state->in_max_output > 0 &&
420 tevent_req_nomem(state->out_output.data, req)) {
421 return;
424 DEBUG(10,("smbd_smb2_ioctl_pipe_write_done: issuing np_read_send "
425 "of size %u\n",
426 (unsigned int)state->out_output.length ));
428 TALLOC_FREE(subreq);
429 subreq = np_read_send(state->smbreq->conn,
430 state->smb2req->sconn->smb2.event_ctx,
431 state->fsp->fake_file_handle,
432 state->out_output.data,
433 state->out_output.length);
434 if (tevent_req_nomem(subreq, req)) {
435 return;
437 tevent_req_set_callback(subreq, smbd_smb2_ioctl_pipe_read_done, req);
440 static void smbd_smb2_ioctl_pipe_read_done(struct tevent_req *subreq)
442 struct tevent_req *req = tevent_req_callback_data(subreq,
443 struct tevent_req);
444 struct smbd_smb2_ioctl_state *state = tevent_req_data(req,
445 struct smbd_smb2_ioctl_state);
446 NTSTATUS status;
447 ssize_t nread = -1;
448 bool is_data_outstanding = false;
450 status = np_read_recv(subreq, &nread, &is_data_outstanding);
452 DEBUG(10,("smbd_smb2_ioctl_pipe_read_done: np_read_recv nread = %d "
453 "is_data_outstanding = %d, status = %s\n",
454 (int)nread,
455 (int)is_data_outstanding,
456 nt_errstr(status) ));
458 TALLOC_FREE(subreq);
459 if (!NT_STATUS_IS_OK(status)) {
460 tevent_req_nterror(req, status);
461 return;
464 state->out_output.length = nread;
466 tevent_req_done(req);
469 static NTSTATUS smbd_smb2_ioctl_recv(struct tevent_req *req,
470 TALLOC_CTX *mem_ctx,
471 DATA_BLOB *out_output)
473 NTSTATUS status = NT_STATUS_OK;
474 struct smbd_smb2_ioctl_state *state = tevent_req_data(req,
475 struct smbd_smb2_ioctl_state);
477 if (tevent_req_is_nterror(req, &status)) {
478 if (!NT_STATUS_EQUAL(status, STATUS_BUFFER_OVERFLOW)) {
479 tevent_req_received(req);
480 return status;
484 *out_output = state->out_output;
485 talloc_steal(mem_ctx, out_output->data);
487 tevent_req_received(req);
488 return status;