s3-tevent: only include ../lib/util/tevent wrappers where needed.
[amitay/samba.git] / source3 / smbd / smb2_ioctl.c
1 /*
2    Unix SMB/CIFS implementation.
3    Core SMB2 server
4
5    Copyright (C) Stefan Metzmacher 2009
6
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.
11
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.
16
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/>.
19 */
20
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
27 static struct tevent_req *smbd_smb2_ioctl_send(TALLOC_CTX *mem_ctx,
28                                                struct tevent_context *ev,
29                                                struct smbd_smb2_request *smb2req,
30                                                uint32_t in_ctl_code,
31                                                uint64_t in_file_id_volatile,
32                                                DATA_BLOB in_input,
33                                                uint32_t in_max_output,
34                                                uint32_t in_flags);
35 static NTSTATUS smbd_smb2_ioctl_recv(struct tevent_req *req,
36                                      TALLOC_CTX *mem_ctx,
37                                      DATA_BLOB *out_output);
38
39 static void smbd_smb2_request_ioctl_done(struct tevent_req *subreq);
40 NTSTATUS smbd_smb2_request_process_ioctl(struct smbd_smb2_request *req)
41 {
42         const uint8_t *inhdr;
43         const uint8_t *inbody;
44         int i = req->current_idx;
45         size_t expected_body_size = 0x39;
46         size_t body_size;
47         uint32_t in_ctl_code;
48         uint64_t in_file_id_persistent;
49         uint64_t in_file_id_volatile;
50         uint32_t in_input_offset;
51         uint32_t in_input_length;
52         DATA_BLOB in_input_buffer;
53         uint32_t in_max_output_length;
54         uint32_t in_flags;
55         struct tevent_req *subreq;
56
57         inhdr = (const uint8_t *)req->in.vector[i+0].iov_base;
58         if (req->in.vector[i+1].iov_len != (expected_body_size & 0xFFFFFFFE)) {
59                 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
60         }
61
62         inbody = (const uint8_t *)req->in.vector[i+1].iov_base;
63
64         body_size = SVAL(inbody, 0x00);
65         if (body_size != expected_body_size) {
66                 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
67         }
68
69         in_ctl_code             = IVAL(inbody, 0x04);
70         in_file_id_persistent   = BVAL(inbody, 0x08);
71         in_file_id_volatile     = BVAL(inbody, 0x10);
72         in_input_offset         = IVAL(inbody, 0x18);
73         in_input_length         = IVAL(inbody, 0x1C);
74         in_max_output_length    = IVAL(inbody, 0x2C);
75         in_flags                = IVAL(inbody, 0x30);
76
77         if (in_input_offset != (SMB2_HDR_BODY + (body_size & 0xFFFFFFFE))) {
78                 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
79         }
80
81         if (in_input_length > req->in.vector[i+2].iov_len) {
82                 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
83         }
84
85         in_input_buffer.data = (uint8_t *)req->in.vector[i+2].iov_base;
86         in_input_buffer.length = in_input_length;
87
88         if (req->compat_chain_fsp) {
89                 /* skip check */
90         } else if (in_file_id_persistent == UINT64_MAX &&
91                    in_file_id_volatile == UINT64_MAX) {
92                 /* without a handle */
93         } else if (in_file_id_persistent != in_file_id_volatile) {
94                 return smbd_smb2_request_error(req, NT_STATUS_FILE_CLOSED);
95         }
96
97         subreq = smbd_smb2_ioctl_send(req,
98                                       req->sconn->smb2.event_ctx,
99                                       req,
100                                       in_ctl_code,
101                                       in_file_id_volatile,
102                                       in_input_buffer,
103                                       in_max_output_length,
104                                       in_flags);
105         if (subreq == NULL) {
106                 return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
107         }
108         tevent_req_set_callback(subreq, smbd_smb2_request_ioctl_done, req);
109
110         return smbd_smb2_request_pending_queue(req, subreq);
111 }
112
113 static void smbd_smb2_request_ioctl_done(struct tevent_req *subreq)
114 {
115         struct smbd_smb2_request *req = tevent_req_callback_data(subreq,
116                                         struct smbd_smb2_request);
117         const uint8_t *inbody;
118         int i = req->current_idx;
119         uint8_t *outhdr;
120         DATA_BLOB outbody;
121         DATA_BLOB outdyn;
122         uint32_t in_ctl_code;
123         uint64_t in_file_id_persistent;
124         uint64_t in_file_id_volatile;
125         uint32_t out_input_offset;
126         uint32_t out_output_offset;
127         DATA_BLOB out_output_buffer = data_blob_null;
128         NTSTATUS status;
129         NTSTATUS error; /* transport error */
130
131         status = smbd_smb2_ioctl_recv(subreq, req, &out_output_buffer);
132
133         DEBUG(10,("smbd_smb2_request_ioctl_done: smbd_smb2_ioctl_recv returned "
134                 "%u status %s\n",
135                 (unsigned int)out_output_buffer.length,
136                 nt_errstr(status) ));
137
138         TALLOC_FREE(subreq);
139         if (NT_STATUS_EQUAL(status, STATUS_BUFFER_OVERFLOW)) {
140                 /* also ok */
141         } else if (!NT_STATUS_IS_OK(status)) {
142                 error = smbd_smb2_request_error(req, status);
143                 if (!NT_STATUS_IS_OK(error)) {
144                         smbd_server_connection_terminate(req->sconn,
145                                                          nt_errstr(error));
146                         return;
147                 }
148                 return;
149         }
150
151         out_input_offset = SMB2_HDR_BODY + 0x30;
152         out_output_offset = SMB2_HDR_BODY + 0x30;
153
154         inbody = (const uint8_t *)req->in.vector[i+1].iov_base;
155
156         in_ctl_code             = IVAL(inbody, 0x04);
157         in_file_id_persistent   = BVAL(inbody, 0x08);
158         in_file_id_volatile     = BVAL(inbody, 0x10);
159
160         outhdr = (uint8_t *)req->out.vector[i].iov_base;
161
162         outbody = data_blob_talloc(req->out.vector, NULL, 0x30);
163         if (outbody.data == NULL) {
164                 error = smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
165                 if (!NT_STATUS_IS_OK(error)) {
166                         smbd_server_connection_terminate(req->sconn,
167                                                          nt_errstr(error));
168                         return;
169                 }
170                 return;
171         }
172
173         SSVAL(outbody.data, 0x00, 0x30 + 1);    /* struct size */
174         SSVAL(outbody.data, 0x02, 0);           /* reserved */
175         SIVAL(outbody.data, 0x04,
176               in_ctl_code);                     /* ctl code */
177         SBVAL(outbody.data, 0x08,
178               in_file_id_persistent);           /* file id (persistent) */
179         SBVAL(outbody.data, 0x10,
180               in_file_id_volatile);             /* file id (volatile) */
181         SIVAL(outbody.data, 0x18,
182               out_input_offset);                /* input offset */
183         SIVAL(outbody.data, 0x1C, 0);           /* input count */
184         SIVAL(outbody.data, 0x20,
185               out_output_offset);               /* output offset */
186         SIVAL(outbody.data, 0x24,
187               out_output_buffer.length);        /* output count */
188         SIVAL(outbody.data, 0x28, 0);           /* flags */
189         SIVAL(outbody.data, 0x2C, 0);           /* reserved */
190
191         /*
192          * Note: Windows Vista and 2008 send back also the
193          *       input from the request. But it was fixed in
194          *       Windows 7.
195          */
196         outdyn = out_output_buffer;
197
198         error = smbd_smb2_request_done_ex(req, status, outbody, &outdyn,
199                                           __location__);
200         if (!NT_STATUS_IS_OK(error)) {
201                 smbd_server_connection_terminate(req->sconn,
202                                                  nt_errstr(error));
203                 return;
204         }
205 }
206
207 struct smbd_smb2_ioctl_state {
208         struct smbd_smb2_request *smb2req;
209         struct smb_request *smbreq;
210         files_struct *fsp;
211         DATA_BLOB in_input;
212         uint32_t in_max_output;
213         DATA_BLOB out_output;
214 };
215
216 static void smbd_smb2_ioctl_pipe_write_done(struct tevent_req *subreq);
217 static void smbd_smb2_ioctl_pipe_read_done(struct tevent_req *subreq);
218
219 static struct tevent_req *smbd_smb2_ioctl_send(TALLOC_CTX *mem_ctx,
220                                                struct tevent_context *ev,
221                                                struct smbd_smb2_request *smb2req,
222                                                uint32_t in_ctl_code,
223                                                uint64_t in_file_id_volatile,
224                                                DATA_BLOB in_input,
225                                                uint32_t in_max_output,
226                                                uint32_t in_flags)
227 {
228         struct tevent_req *req;
229         struct smbd_smb2_ioctl_state *state;
230         struct smb_request *smbreq;
231         files_struct *fsp = NULL;
232         struct tevent_req *subreq;
233
234         req = tevent_req_create(mem_ctx, &state,
235                                 struct smbd_smb2_ioctl_state);
236         if (req == NULL) {
237                 return NULL;
238         }
239         state->smb2req = smb2req;
240         state->smbreq = NULL;
241         state->fsp = NULL;
242         state->in_input = in_input;
243         state->in_max_output = in_max_output;
244         state->out_output = data_blob_null;
245
246         DEBUG(10,("smbd_smb2_ioctl: file_id[0x%016llX]\n",
247                   (unsigned long long)in_file_id_volatile));
248
249         smbreq = smbd_smb2_fake_smb_request(smb2req);
250         if (tevent_req_nomem(smbreq, req)) {
251                 return tevent_req_post(req, ev);
252         }
253         state->smbreq = smbreq;
254
255         if (in_file_id_volatile != UINT64_MAX) {
256                 fsp = file_fsp(smbreq, (uint16_t)in_file_id_volatile);
257                 if (fsp == NULL) {
258                         tevent_req_nterror(req, NT_STATUS_FILE_CLOSED);
259                         return tevent_req_post(req, ev);
260                 }
261                 if (smbreq->conn != fsp->conn) {
262                         tevent_req_nterror(req, NT_STATUS_FILE_CLOSED);
263                         return tevent_req_post(req, ev);
264                 }
265                 if (smb2req->session->vuid != fsp->vuid) {
266                         tevent_req_nterror(req, NT_STATUS_FILE_CLOSED);
267                         return tevent_req_post(req, ev);
268                 }
269                 state->fsp = fsp;
270         }
271
272         switch (in_ctl_code) {
273         case 0x00060194: /* FSCTL_DFS_GET_REFERRALS */
274         {
275                 uint16_t in_max_referral_level;
276                 DATA_BLOB in_file_name_buffer;
277                 char *in_file_name_string;
278                 size_t in_file_name_string_size;
279                 bool ok;
280                 bool overflow = false;
281                 NTSTATUS status;
282                 int dfs_size;
283                 char *dfs_data = NULL;
284
285                 if (!IS_IPC(smbreq->conn)) {
286                         tevent_req_nterror(req, NT_STATUS_INVALID_DEVICE_REQUEST);
287                         return tevent_req_post(req, ev);
288                 }
289
290                 if (!lp_host_msdfs()) {
291                         tevent_req_nterror(req, NT_STATUS_FS_DRIVER_REQUIRED);
292                         return tevent_req_post(req, ev);
293                 }
294
295                 if (in_input.length < (2 + 2)) {
296                         tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
297                         return tevent_req_post(req, ev);
298                 }
299
300                 in_max_referral_level = SVAL(in_input.data, 0);
301                 in_file_name_buffer.data = in_input.data + 2;
302                 in_file_name_buffer.length = in_input.length - 2;
303
304                 ok = convert_string_talloc(state, CH_UTF16, CH_UNIX,
305                                            in_file_name_buffer.data,
306                                            in_file_name_buffer.length,
307                                            &in_file_name_string,
308                                            &in_file_name_string_size);
309                 if (!ok) {
310                         tevent_req_nterror(req, NT_STATUS_ILLEGAL_CHARACTER);
311                         return tevent_req_post(req, ev);
312                 }
313
314                 dfs_size = setup_dfs_referral(smbreq->conn,
315                                               in_file_name_string,
316                                               in_max_referral_level,
317                                               &dfs_data, &status);
318                 if (dfs_size < 0) {
319                         tevent_req_nterror(req, status);
320                         return tevent_req_post(req, ev);
321                 }
322
323                 if (dfs_size > in_max_output) {
324                         /*
325                          * TODO: we need a testsuite for this
326                          */
327                         overflow = true;
328                         dfs_size = in_max_output;
329                 }
330
331                 state->out_output = data_blob_talloc(state,
332                                                      (uint8_t *)dfs_data,
333                                                      dfs_size);
334                 SAFE_FREE(dfs_data);
335                 if (dfs_size > 0 &&
336                     tevent_req_nomem(state->out_output.data, req)) {
337                         return tevent_req_post(req, ev);
338                 }
339
340                 if (overflow) {
341                         tevent_req_nterror(req, STATUS_BUFFER_OVERFLOW);
342                 } else {
343                         tevent_req_done(req);
344                 }
345                 return tevent_req_post(req, ev);
346         }
347         case 0x0011C017: /* FSCTL_PIPE_TRANSCEIVE */
348
349                 if (!IS_IPC(smbreq->conn)) {
350                         tevent_req_nterror(req, NT_STATUS_NOT_SUPPORTED);
351                         return tevent_req_post(req, ev);
352                 }
353
354                 if (fsp == NULL) {
355                         tevent_req_nterror(req, NT_STATUS_FILE_CLOSED);
356                         return tevent_req_post(req, ev);
357                 }
358
359                 if (!fsp_is_np(fsp)) {
360                         tevent_req_nterror(req, NT_STATUS_FILE_CLOSED);
361                         return tevent_req_post(req, ev);
362                 }
363
364                 DEBUG(10,("smbd_smb2_ioctl_send: np_write_send of size %u\n",
365                         (unsigned int)in_input.length ));
366
367                 subreq = np_write_send(state, ev,
368                                        fsp->fake_file_handle,
369                                        in_input.data,
370                                        in_input.length);
371                 if (tevent_req_nomem(subreq, req)) {
372                         return tevent_req_post(req, ev);
373                 }
374                 tevent_req_set_callback(subreq,
375                                         smbd_smb2_ioctl_pipe_write_done,
376                                         req);
377                 return req;
378
379         default:
380                 if (IS_IPC(smbreq->conn)) {
381                         tevent_req_nterror(req, NT_STATUS_FS_DRIVER_REQUIRED);
382                         return tevent_req_post(req, ev);
383                 }
384                 tevent_req_nterror(req, NT_STATUS_INVALID_DEVICE_REQUEST);
385                 return tevent_req_post(req, ev);
386         }
387
388         tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
389         return tevent_req_post(req, ev);
390 }
391
392 static void smbd_smb2_ioctl_pipe_write_done(struct tevent_req *subreq)
393 {
394         struct tevent_req *req = tevent_req_callback_data(subreq,
395                                  struct tevent_req);
396         struct smbd_smb2_ioctl_state *state = tevent_req_data(req,
397                                               struct smbd_smb2_ioctl_state);
398         NTSTATUS status;
399         ssize_t nwritten = -1;
400
401         status = np_write_recv(subreq, &nwritten);
402
403         DEBUG(10,("smbd_smb2_ioctl_pipe_write_done: received %ld\n",
404                 (long int)nwritten ));
405
406         TALLOC_FREE(subreq);
407         if (!NT_STATUS_IS_OK(status)) {
408                 tevent_req_nterror(req, status);
409                 return;
410         }
411
412         if (nwritten != state->in_input.length) {
413                 tevent_req_nterror(req, NT_STATUS_PIPE_NOT_AVAILABLE);
414                 return;
415         }
416
417         state->out_output = data_blob_talloc(state, NULL, state->in_max_output);
418         if (state->in_max_output > 0 &&
419             tevent_req_nomem(state->out_output.data, req)) {
420                 return;
421         }
422
423         DEBUG(10,("smbd_smb2_ioctl_pipe_write_done: issuing np_read_send "
424                 "of size %u\n",
425                 (unsigned int)state->out_output.length ));
426
427         TALLOC_FREE(subreq);
428         subreq = np_read_send(state->smbreq->conn,
429                               state->smb2req->sconn->smb2.event_ctx,
430                               state->fsp->fake_file_handle,
431                               state->out_output.data,
432                               state->out_output.length);
433         if (tevent_req_nomem(subreq, req)) {
434                 return;
435         }
436         tevent_req_set_callback(subreq, smbd_smb2_ioctl_pipe_read_done, req);
437 }
438
439 static void smbd_smb2_ioctl_pipe_read_done(struct tevent_req *subreq)
440 {
441         struct tevent_req *req = tevent_req_callback_data(subreq,
442                                  struct tevent_req);
443         struct smbd_smb2_ioctl_state *state = tevent_req_data(req,
444                                               struct smbd_smb2_ioctl_state);
445         NTSTATUS status;
446         ssize_t nread = -1;
447         bool is_data_outstanding = false;
448
449         status = np_read_recv(subreq, &nread, &is_data_outstanding);
450
451         DEBUG(10,("smbd_smb2_ioctl_pipe_read_done: np_read_recv nread = %d "
452                  "is_data_outstanding = %d, status = %s\n",
453                 (int)nread,
454                 (int)is_data_outstanding,
455                 nt_errstr(status) ));
456
457         TALLOC_FREE(subreq);
458         if (!NT_STATUS_IS_OK(status)) {
459                 tevent_req_nterror(req, status);
460                 return;
461         }
462
463         state->out_output.length = nread;
464
465         tevent_req_done(req);
466 }
467
468 static NTSTATUS smbd_smb2_ioctl_recv(struct tevent_req *req,
469                                      TALLOC_CTX *mem_ctx,
470                                      DATA_BLOB *out_output)
471 {
472         NTSTATUS status = NT_STATUS_OK;
473         struct smbd_smb2_ioctl_state *state = tevent_req_data(req,
474                                               struct smbd_smb2_ioctl_state);
475
476         if (tevent_req_is_nterror(req, &status)) {
477                 if (!NT_STATUS_EQUAL(status, STATUS_BUFFER_OVERFLOW)) {
478                         tevent_req_received(req);
479                         return status;
480                 }
481         }
482
483         *out_output = state->out_output;
484         talloc_steal(mem_ctx, out_output->data);
485
486         tevent_req_received(req);
487         return status;
488 }