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