smb2_ioctl: remove ioctl error response assumptions
[gd/samba-autobuild/.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 "include/ntioctl.h"
27 #include "smb2_ioctl_private.h"
28 #include "librpc/gen_ndr/ioctl.h"
29
30 static struct tevent_req *smbd_smb2_ioctl_send(TALLOC_CTX *mem_ctx,
31                                                struct tevent_context *ev,
32                                                struct smbd_smb2_request *smb2req,
33                                                struct files_struct *in_fsp,
34                                                uint32_t in_ctl_code,
35                                                DATA_BLOB in_input,
36                                                uint32_t in_max_output,
37                                                uint32_t in_flags);
38 static NTSTATUS smbd_smb2_ioctl_recv(struct tevent_req *req,
39                                      TALLOC_CTX *mem_ctx,
40                                      DATA_BLOB *out_output,
41                                      bool *disconnect);
42
43 static void smbd_smb2_request_ioctl_done(struct tevent_req *subreq);
44 NTSTATUS smbd_smb2_request_process_ioctl(struct smbd_smb2_request *req)
45 {
46         NTSTATUS status;
47         const uint8_t *inbody;
48         uint32_t min_buffer_offset;
49         uint32_t max_buffer_offset;
50         uint32_t min_output_offset;
51         uint32_t allowed_length_in;
52         uint32_t allowed_length_out;
53         uint32_t in_ctl_code;
54         uint64_t in_file_id_persistent;
55         uint64_t in_file_id_volatile;
56         struct files_struct *in_fsp = NULL;
57         uint32_t in_input_offset;
58         uint32_t in_input_length;
59         DATA_BLOB in_input_buffer = data_blob_null;
60         uint32_t in_max_input_length;
61         uint32_t in_output_offset;
62         uint32_t in_output_length;
63         DATA_BLOB in_output_buffer = data_blob_null;
64         uint32_t in_max_output_length;
65         uint32_t in_flags;
66         uint32_t data_length_in;
67         uint32_t data_length_out;
68         uint32_t data_length_tmp;
69         uint32_t data_length_max;
70         struct tevent_req *subreq;
71
72         status = smbd_smb2_request_verify_sizes(req, 0x39);
73         if (!NT_STATUS_IS_OK(status)) {
74                 return smbd_smb2_request_error(req, status);
75         }
76         inbody = SMBD_SMB2_IN_BODY_PTR(req);
77
78         in_ctl_code             = IVAL(inbody, 0x04);
79         in_file_id_persistent   = BVAL(inbody, 0x08);
80         in_file_id_volatile     = BVAL(inbody, 0x10);
81         in_input_offset         = IVAL(inbody, 0x18);
82         in_input_length         = IVAL(inbody, 0x1C);
83         in_max_input_length     = IVAL(inbody, 0x20);
84         in_output_offset        = IVAL(inbody, 0x24);
85         in_output_length        = IVAL(inbody, 0x28);
86         in_max_output_length    = IVAL(inbody, 0x2C);
87         in_flags                = IVAL(inbody, 0x30);
88
89         min_buffer_offset = SMB2_HDR_BODY + SMBD_SMB2_IN_BODY_LEN(req);
90         max_buffer_offset = min_buffer_offset + SMBD_SMB2_IN_DYN_LEN(req);
91         min_output_offset = min_buffer_offset;
92
93         /*
94          * InputOffset (4 bytes): The offset, in bytes, from the beginning of
95          * the SMB2 header to the input data buffer. If no input data is
96          * required for the FSCTL/IOCTL command being issued, the client SHOULD
97          * set this value to 0.<49>
98          * <49> If no input data is required for the FSCTL/IOCTL command being
99          * issued, Windows-based clients set this field to any value.
100          */
101         allowed_length_in = 0;
102         if ((in_input_offset > 0) && (in_input_length > 0)) {
103                 uint32_t tmp_ofs;
104
105                 if (in_input_offset < min_buffer_offset) {
106                         return smbd_smb2_request_error(req,
107                                         NT_STATUS_INVALID_PARAMETER);
108                 }
109                 if (in_input_offset > max_buffer_offset) {
110                         return smbd_smb2_request_error(req,
111                                         NT_STATUS_INVALID_PARAMETER);
112                 }
113                 allowed_length_in = max_buffer_offset - in_input_offset;
114
115                 tmp_ofs = in_input_offset - min_buffer_offset;
116                 in_input_buffer.data = SMBD_SMB2_IN_DYN_PTR(req);
117                 in_input_buffer.data += tmp_ofs;
118                 in_input_buffer.length = in_input_length;
119                 min_output_offset += tmp_ofs;
120                 min_output_offset += in_input_length;
121         }
122
123         if (in_input_length > allowed_length_in) {
124                 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
125         }
126
127         allowed_length_out = 0;
128         if (in_output_offset > 0) {
129                 if (in_output_offset < min_buffer_offset) {
130                         return smbd_smb2_request_error(req,
131                                         NT_STATUS_INVALID_PARAMETER);
132                 }
133                 if (in_output_offset > max_buffer_offset) {
134                         return smbd_smb2_request_error(req,
135                                         NT_STATUS_INVALID_PARAMETER);
136                 }
137                 allowed_length_out = max_buffer_offset - in_output_offset;
138         }
139
140         if (in_output_length > allowed_length_out) {
141                 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
142         }
143
144         if (in_output_length > 0) {
145                 uint32_t tmp_ofs;
146
147                 if (in_output_offset < min_output_offset) {
148                         return smbd_smb2_request_error(req,
149                                         NT_STATUS_INVALID_PARAMETER);
150                 }
151
152                 tmp_ofs = in_output_offset - min_buffer_offset;
153                 in_output_buffer.data = SMBD_SMB2_IN_DYN_PTR(req);
154                 in_output_buffer.data += tmp_ofs;
155                 in_output_buffer.length = in_output_length;
156         }
157
158         /*
159          * verify the credits and avoid overflows
160          * in_input_buffer.length and in_output_buffer.length
161          * are already verified.
162          */
163         data_length_in = in_input_buffer.length + in_output_buffer.length;
164
165         data_length_out = in_max_input_length;
166         data_length_tmp = UINT32_MAX - data_length_out;
167         if (data_length_tmp < in_max_output_length) {
168                 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
169         }
170         data_length_out += in_max_output_length;
171
172         data_length_max = MAX(data_length_in, data_length_out);
173
174         status = smbd_smb2_request_verify_creditcharge(req, data_length_max);
175         if (!NT_STATUS_IS_OK(status)) {
176                 return smbd_smb2_request_error(req, status);
177         }
178
179         /*
180          * If the Flags field of the request is not SMB2_0_IOCTL_IS_FSCTL the
181          * server MUST fail the request with STATUS_NOT_SUPPORTED.
182          */
183         if (in_flags != SMB2_IOCTL_FLAG_IS_FSCTL) {
184                 return smbd_smb2_request_error(req, NT_STATUS_NOT_SUPPORTED);
185         }
186
187         switch (in_ctl_code) {
188         case FSCTL_DFS_GET_REFERRALS:
189         case FSCTL_DFS_GET_REFERRALS_EX:
190         case FSCTL_PIPE_WAIT:
191         case FSCTL_VALIDATE_NEGOTIATE_INFO_224:
192         case FSCTL_VALIDATE_NEGOTIATE_INFO:
193         case FSCTL_QUERY_NETWORK_INTERFACE_INFO:
194                 /*
195                  * Some SMB2 specific CtlCodes like FSCTL_DFS_GET_REFERRALS or
196                  * FSCTL_PIPE_WAIT does not take a file handle.
197                  *
198                  * If FileId in the SMB2 Header of the request is not
199                  * 0xFFFFFFFFFFFFFFFF, then the server MUST fail the request
200                  * with STATUS_INVALID_PARAMETER.
201                  */
202                 if (in_file_id_persistent != UINT64_MAX ||
203                     in_file_id_volatile != UINT64_MAX) {
204                         return smbd_smb2_request_error(req,
205                                 NT_STATUS_INVALID_PARAMETER);
206                 }
207                 break;
208         default:
209                 in_fsp = file_fsp_smb2(req, in_file_id_persistent,
210                                        in_file_id_volatile);
211                 if (in_fsp == NULL) {
212                         return smbd_smb2_request_error(req, NT_STATUS_FILE_CLOSED);
213                 }
214                 break;
215         }
216
217         subreq = smbd_smb2_ioctl_send(req, req->sconn->ev_ctx,
218                                       req, in_fsp,
219                                       in_ctl_code,
220                                       in_input_buffer,
221                                       in_max_output_length,
222                                       in_flags);
223         if (subreq == NULL) {
224                 return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
225         }
226         tevent_req_set_callback(subreq, smbd_smb2_request_ioctl_done, req);
227
228         return smbd_smb2_request_pending_queue(req, subreq, 1000);
229 }
230
231 /*
232  * 3.3.4.4 Sending an Error Response
233  * An error code other than one of the following indicates a failure:
234  */
235 static bool smbd_smb2_ioctl_is_failure(uint32_t ctl_code, NTSTATUS status,
236                                        size_t data_size)
237 {
238         if (NT_STATUS_IS_OK(status)) {
239                 return false;
240         }
241
242         /*
243          * STATUS_BUFFER_OVERFLOW in a FSCTL_PIPE_TRANSCEIVE, FSCTL_PIPE_PEEK or
244          * FSCTL_DFS_GET_REFERRALS Response specified in section 2.2.32.<153>
245          */
246         if (NT_STATUS_EQUAL(status, STATUS_BUFFER_OVERFLOW)
247          && ((ctl_code == FSCTL_PIPE_TRANSCEIVE)
248           || (ctl_code == FSCTL_PIPE_PEEK)
249           || (ctl_code == FSCTL_DFS_GET_REFERRALS))) {
250                 return false;
251         }
252
253         /*
254          * Any status other than STATUS_SUCCESS in a FSCTL_SRV_COPYCHUNK or
255          * FSCTL_SRV_COPYCHUNK_WRITE Response, when returning an
256          * SRV_COPYCHUNK_RESPONSE as described in section 2.2.32.1.
257          */
258         if (((ctl_code == FSCTL_SRV_COPYCHUNK)
259                                 || (ctl_code == FSCTL_SRV_COPYCHUNK_WRITE))
260          && (data_size == sizeof(struct srv_copychunk_rsp))) {
261                 return false;
262         }
263
264         return true;
265 }
266
267 static void smbd_smb2_request_ioctl_done(struct tevent_req *subreq)
268 {
269         struct smbd_smb2_request *req = tevent_req_callback_data(subreq,
270                                         struct smbd_smb2_request);
271         const uint8_t *inbody;
272         DATA_BLOB outbody;
273         DATA_BLOB outdyn;
274         uint32_t in_ctl_code;
275         uint64_t in_file_id_persistent;
276         uint64_t in_file_id_volatile;
277         uint32_t out_input_offset;
278         uint32_t out_output_offset;
279         DATA_BLOB out_output_buffer = data_blob_null;
280         NTSTATUS status;
281         NTSTATUS error; /* transport error */
282         bool disconnect = false;
283
284         status = smbd_smb2_ioctl_recv(subreq, req,
285                                       &out_output_buffer,
286                                       &disconnect);
287
288         DEBUG(10,("smbd_smb2_request_ioctl_done: smbd_smb2_ioctl_recv returned "
289                 "%u status %s\n",
290                 (unsigned int)out_output_buffer.length,
291                 nt_errstr(status) ));
292
293         TALLOC_FREE(subreq);
294         if (disconnect) {
295                 error = status;
296                 smbd_server_connection_terminate(req->sconn,
297                                                  nt_errstr(error));
298                 return;
299         }
300
301         inbody = SMBD_SMB2_IN_BODY_PTR(req);
302
303         in_ctl_code             = IVAL(inbody, 0x04);
304         in_file_id_persistent   = BVAL(inbody, 0x08);
305         in_file_id_volatile     = BVAL(inbody, 0x10);
306
307         if (smbd_smb2_ioctl_is_failure(in_ctl_code, status,
308                                        out_output_buffer.length)) {
309                 error = smbd_smb2_request_error(req, status);
310                 if (!NT_STATUS_IS_OK(error)) {
311                         smbd_server_connection_terminate(req->sconn,
312                                                          nt_errstr(error));
313                         return;
314                 }
315                 return;
316         }
317
318         out_input_offset = SMB2_HDR_BODY + 0x30;
319         out_output_offset = SMB2_HDR_BODY + 0x30;
320
321         outbody = data_blob_talloc(req->out.vector, NULL, 0x30);
322         if (outbody.data == NULL) {
323                 error = smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
324                 if (!NT_STATUS_IS_OK(error)) {
325                         smbd_server_connection_terminate(req->sconn,
326                                                          nt_errstr(error));
327                         return;
328                 }
329                 return;
330         }
331
332         SSVAL(outbody.data, 0x00, 0x30 + 1);    /* struct size */
333         SSVAL(outbody.data, 0x02, 0);           /* reserved */
334         SIVAL(outbody.data, 0x04,
335               in_ctl_code);                     /* ctl code */
336         SBVAL(outbody.data, 0x08,
337               in_file_id_persistent);           /* file id (persistent) */
338         SBVAL(outbody.data, 0x10,
339               in_file_id_volatile);             /* file id (volatile) */
340         SIVAL(outbody.data, 0x18,
341               out_input_offset);                /* input offset */
342         SIVAL(outbody.data, 0x1C, 0);           /* input count */
343         SIVAL(outbody.data, 0x20,
344               out_output_offset);               /* output offset */
345         SIVAL(outbody.data, 0x24,
346               out_output_buffer.length);        /* output count */
347         SIVAL(outbody.data, 0x28, 0);           /* flags */
348         SIVAL(outbody.data, 0x2C, 0);           /* reserved */
349
350         /*
351          * Note: Windows Vista and 2008 send back also the
352          *       input from the request. But it was fixed in
353          *       Windows 7.
354          */
355         outdyn = out_output_buffer;
356
357         error = smbd_smb2_request_done_ex(req, status, outbody, &outdyn,
358                                           __location__);
359         if (!NT_STATUS_IS_OK(error)) {
360                 smbd_server_connection_terminate(req->sconn,
361                                                  nt_errstr(error));
362                 return;
363         }
364 }
365
366 static struct tevent_req *smbd_smb2_ioctl_send(TALLOC_CTX *mem_ctx,
367                                                struct tevent_context *ev,
368                                                struct smbd_smb2_request *smb2req,
369                                                struct files_struct *fsp,
370                                                uint32_t in_ctl_code,
371                                                DATA_BLOB in_input,
372                                                uint32_t in_max_output,
373                                                uint32_t in_flags)
374 {
375         struct tevent_req *req;
376         struct smbd_smb2_ioctl_state *state;
377         struct smb_request *smbreq;
378
379         req = tevent_req_create(mem_ctx, &state,
380                                 struct smbd_smb2_ioctl_state);
381         if (req == NULL) {
382                 return NULL;
383         }
384         state->smb2req = smb2req;
385         state->smbreq = NULL;
386         state->fsp = fsp;
387         state->in_input = in_input;
388         state->in_max_output = in_max_output;
389         state->out_output = data_blob_null;
390
391         DEBUG(10, ("smbd_smb2_ioctl: ctl_code[0x%08x] %s, %s\n",
392                    (unsigned)in_ctl_code,
393                    fsp ? fsp_str_dbg(fsp) : "<no handle>",
394                    fsp_fnum_dbg(fsp)));
395
396         smbreq = smbd_smb2_fake_smb_request(smb2req);
397         if (tevent_req_nomem(smbreq, req)) {
398                 return tevent_req_post(req, ev);
399         }
400         state->smbreq = smbreq;
401
402         switch (in_ctl_code & IOCTL_DEV_TYPE_MASK) {
403         case FSCTL_DFS:
404                 return smb2_ioctl_dfs(in_ctl_code, ev, req, state);
405                 break;
406         case FSCTL_FILESYSTEM:
407                 return smb2_ioctl_filesys(in_ctl_code, ev, req, state);
408                 break;
409         case FSCTL_NAMED_PIPE:
410                 return smb2_ioctl_named_pipe(in_ctl_code, ev, req, state);
411                 break;
412         case FSCTL_NETWORK_FILESYSTEM:
413                 return smb2_ioctl_network_fs(in_ctl_code, ev, req, state);
414                 break;
415         default:
416                 if (IS_IPC(smbreq->conn)) {
417                         tevent_req_nterror(req, NT_STATUS_FS_DRIVER_REQUIRED);
418                 } else {
419                         tevent_req_nterror(req, NT_STATUS_INVALID_DEVICE_REQUEST);
420                 }
421
422                 return tevent_req_post(req, ev);
423                 break;
424         }
425
426         tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
427         return tevent_req_post(req, ev);
428 }
429
430 static NTSTATUS smbd_smb2_ioctl_recv(struct tevent_req *req,
431                                      TALLOC_CTX *mem_ctx,
432                                      DATA_BLOB *out_output,
433                                      bool *disconnect)
434 {
435         NTSTATUS status = NT_STATUS_OK;
436         struct smbd_smb2_ioctl_state *state = tevent_req_data(req,
437                                               struct smbd_smb2_ioctl_state);
438         enum tevent_req_state req_state;
439         uint64_t err;
440
441         *disconnect = state->disconnect;
442
443         if ((tevent_req_is_error(req, &req_state, &err) == false)
444          || (req_state == TEVENT_REQ_USER_ERROR)) {
445                 /*
446                  * Return output buffer to caller if the ioctl was successfully
447                  * processed, even if a user error occurred. Some ioctls return
448                  * data on failure.
449                  */
450                 *out_output = state->out_output;
451                 talloc_steal(mem_ctx, out_output->data);
452         }
453
454         tevent_req_is_nterror(req, &status);
455         tevent_req_received(req);
456         return status;
457 }