smb2_ioctl: only pass through to VFS on a valid fsp
[samba.git] / source3 / smbd / smb2_ioctl_network_fs.c
1 /*
2    Unix SMB/CIFS implementation.
3    Core SMB2 server
4
5    Copyright (C) Stefan Metzmacher 2009
6    Copyright (C) David Disseldorp 2012
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 3 of the License, or
11    (at your option) any later version.
12
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include "includes.h"
23 #include "smbd/smbd.h"
24 #include "smbd/globals.h"
25 #include "../libcli/smb/smb_common.h"
26 #include "../lib/util/tevent_ntstatus.h"
27 #include "../lib/ccan/build_assert/build_assert.h"
28 #include "include/ntioctl.h"
29 #include "../librpc/ndr/libndr.h"
30 #include "librpc/gen_ndr/ndr_ioctl.h"
31 #include "smb2_ioctl_private.h"
32
33 #define COPYCHUNK_MAX_CHUNKS    256             /* 2k8r2 & win8 = 256 */
34 #define COPYCHUNK_MAX_CHUNK_LEN 1048576         /* 2k8r2 & win8 = 1048576 */
35 #define COPYCHUNK_MAX_TOTAL_LEN 16777216        /* 2k8r2 & win8 = 16777216 */
36 static void copychunk_pack_limits(struct srv_copychunk_rsp *cc_rsp)
37 {
38         cc_rsp->chunks_written = COPYCHUNK_MAX_CHUNKS;
39         cc_rsp->chunk_bytes_written = COPYCHUNK_MAX_CHUNK_LEN;
40         cc_rsp->total_bytes_written = COPYCHUNK_MAX_TOTAL_LEN;
41 }
42
43 static NTSTATUS copychunk_check_limits(struct srv_copychunk_copy *cc_copy)
44 {
45         uint32_t i;
46         uint32_t total_len = 0;
47
48         if (cc_copy->chunk_count > COPYCHUNK_MAX_CHUNKS) {
49                 return NT_STATUS_INVALID_PARAMETER;
50         }
51
52         for (i = 0; i < cc_copy->chunk_count; i++) {
53                 if (cc_copy->chunks[i].length > COPYCHUNK_MAX_CHUNK_LEN) {
54                         return NT_STATUS_INVALID_PARAMETER;
55                 }
56                 total_len += cc_copy->chunks[i].length;
57         }
58         if (total_len > COPYCHUNK_MAX_TOTAL_LEN) {
59                 return NT_STATUS_INVALID_PARAMETER;
60         }
61
62         return NT_STATUS_OK;
63 }
64
65 static void copychunk_unlock_all(struct files_struct *src_fsp,
66                                  struct files_struct *dst_fsp,
67                                  struct lock_struct *rd_locks,
68                                  struct lock_struct *wr_locks,
69                                  uint32_t num_locks)
70 {
71
72         uint32_t i;
73
74         for (i = 0; i < num_locks; i++) {
75                 SMB_VFS_STRICT_UNLOCK(src_fsp->conn, src_fsp, &rd_locks[i]);
76                 SMB_VFS_STRICT_UNLOCK(dst_fsp->conn, dst_fsp, &wr_locks[i]);
77         }
78 }
79
80 /* request read and write locks for each chunk */
81 static NTSTATUS copychunk_lock_all(TALLOC_CTX *mem_ctx,
82                                    struct srv_copychunk_copy *cc_copy,
83                                    struct files_struct *src_fsp,
84                                    struct files_struct *dst_fsp,
85                                    struct lock_struct **rd_locks,
86                                    struct lock_struct **wr_locks,
87                                    uint32_t *num_locks)
88 {
89         NTSTATUS status;
90         uint32_t i;
91         struct lock_struct *rlocks;
92         struct lock_struct *wlocks;
93
94         rlocks = talloc_array(mem_ctx, struct lock_struct,
95                               cc_copy->chunk_count);
96         if (rlocks == NULL) {
97                 status = NT_STATUS_NO_MEMORY;
98                 goto err_out;
99         }
100
101         wlocks = talloc_array(mem_ctx, struct lock_struct,
102                               cc_copy->chunk_count);
103         if (wlocks == NULL) {
104                 status = NT_STATUS_NO_MEMORY;
105                 goto err_rlocks_free;
106         }
107
108         for (i = 0; i < cc_copy->chunk_count; i++) {
109                 init_strict_lock_struct(src_fsp,
110                                         src_fsp->op->global->open_persistent_id,
111                                         cc_copy->chunks[i].source_off,
112                                         cc_copy->chunks[i].length,
113                                         READ_LOCK,
114                                         &rlocks[i]);
115                 init_strict_lock_struct(dst_fsp,
116                                         dst_fsp->op->global->open_persistent_id,
117                                         cc_copy->chunks[i].target_off,
118                                         cc_copy->chunks[i].length,
119                                         WRITE_LOCK,
120                                         &wlocks[i]);
121
122                 if (!SMB_VFS_STRICT_LOCK(src_fsp->conn, src_fsp, &rlocks[i])) {
123                         status = NT_STATUS_FILE_LOCK_CONFLICT;
124                         goto err_unlock;
125                 }
126                 if (!SMB_VFS_STRICT_LOCK(dst_fsp->conn, dst_fsp, &wlocks[i])) {
127                         /* unlock last rlock, otherwise missed by cleanup */
128                         SMB_VFS_STRICT_UNLOCK(src_fsp->conn, src_fsp,
129                                               &rlocks[i]);
130                         status = NT_STATUS_FILE_LOCK_CONFLICT;
131                         goto err_unlock;
132                 }
133         }
134
135         *rd_locks = rlocks;
136         *wr_locks = wlocks;
137         *num_locks = i;
138
139         return NT_STATUS_OK;
140
141 err_unlock:
142         if (i > 0) {
143                 /* cleanup all locks successfully issued so far */
144                 copychunk_unlock_all(src_fsp, dst_fsp, rlocks, wlocks, i);
145         }
146         talloc_free(wlocks);
147 err_rlocks_free:
148         talloc_free(rlocks);
149 err_out:
150         return status;
151 }
152
153 struct fsctl_srv_copychunk_state {
154         struct connection_struct *conn;
155         uint32_t dispatch_count;
156         uint32_t recv_count;
157         uint32_t bad_recv_count;
158         NTSTATUS status;
159         off_t total_written;
160         struct files_struct *src_fsp;
161         struct files_struct *dst_fsp;
162         struct lock_struct *wlocks;
163         struct lock_struct *rlocks;
164         uint32_t num_locks;
165 };
166 static void fsctl_srv_copychunk_vfs_done(struct tevent_req *subreq);
167
168 static struct tevent_req *fsctl_srv_copychunk_send(TALLOC_CTX *mem_ctx,
169                                                    struct tevent_context *ev,
170                                                    struct files_struct *dst_fsp,
171                                                    DATA_BLOB *in_input,
172                                                    struct smbd_smb2_request *smb2req)
173 {
174         struct tevent_req *req;
175         struct srv_copychunk_copy cc_copy;
176         enum ndr_err_code ndr_ret;
177         uint64_t src_persistent_h;
178         uint64_t src_volatile_h;
179         int i;
180         struct srv_copychunk *chunk;
181         struct fsctl_srv_copychunk_state *state;
182
183         req = tevent_req_create(mem_ctx, &state,
184                                 struct fsctl_srv_copychunk_state);
185         if (req == NULL) {
186                 return NULL;
187         }
188         state->conn = dst_fsp->conn;
189         ndr_ret = ndr_pull_struct_blob(in_input, mem_ctx, &cc_copy,
190                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_copy);
191         if (ndr_ret != NDR_ERR_SUCCESS) {
192                 DEBUG(0, ("failed to unmarshall copy chunk req\n"));
193                 state->status = NT_STATUS_INVALID_PARAMETER;
194                 tevent_req_nterror(req, state->status);
195                 return tevent_req_post(req, ev);
196         }
197
198         /* persistent/volatile keys sent as the resume key */
199         src_persistent_h = BVAL(cc_copy.source_key, 0);
200         src_volatile_h = BVAL(cc_copy.source_key, 8);
201         state->src_fsp = file_fsp_get(smb2req, src_persistent_h, src_volatile_h);
202         if (state->src_fsp == NULL) {
203                 DEBUG(3, ("invalid resume key in copy chunk req\n"));
204                 state->status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
205                 tevent_req_nterror(req, state->status);
206                 return tevent_req_post(req, ev);
207         }
208
209         state->dst_fsp = dst_fsp;
210
211         state->status = copychunk_check_limits(&cc_copy);
212         if (tevent_req_nterror(req, state->status)) {
213                 DEBUG(3, ("copy chunk req exceeds limits\n"));
214                 return tevent_req_post(req, ev);
215         }
216
217         state->status = copychunk_lock_all(state,
218                                            &cc_copy,
219                                            state->src_fsp,
220                                            state->dst_fsp,
221                                            &state->rlocks,
222                                            &state->wlocks,
223                                            &state->num_locks);
224         if (tevent_req_nterror(req, state->status)) {
225                 return tevent_req_post(req, ev);
226         }
227
228         for (i = 0; i < cc_copy.chunk_count; i++) {
229                 struct tevent_req *vfs_subreq;
230                 chunk = &cc_copy.chunks[i];
231                 vfs_subreq = SMB_VFS_COPY_CHUNK_SEND(dst_fsp->conn,
232                                                      state, ev,
233                                                      state->src_fsp,
234                                                      chunk->source_off,
235                                                      state->dst_fsp,
236                                                      chunk->target_off,
237                                                      chunk->length);
238                 if (vfs_subreq == NULL) {
239                         DEBUG(0, ("VFS copy chunk send failed\n"));
240                         state->status = NT_STATUS_NO_MEMORY;
241                         if (state->dispatch_count == 0) {
242                                 /* nothing dispatched, return immediately */
243                                 copychunk_unlock_all(state->src_fsp,
244                                                      state->dst_fsp,
245                                                      state->rlocks,
246                                                      state->wlocks,
247                                                      state->num_locks);
248                                 tevent_req_nterror(req, state->status);
249                                 return tevent_req_post(req, ev);
250                         } else {
251                                 /*
252                                  * wait for dispatched to complete before
253                                  * returning error, locks held.
254                                  */
255                                 break;
256                         }
257                 }
258                 tevent_req_set_callback(vfs_subreq,
259                                         fsctl_srv_copychunk_vfs_done, req);
260                 state->dispatch_count++;
261         }
262
263         /* hold locks until all dispatched requests are completed */
264         return req;
265 }
266
267 static void fsctl_srv_copychunk_vfs_done(struct tevent_req *subreq)
268 {
269         struct tevent_req *req = tevent_req_callback_data(
270                 subreq, struct tevent_req);
271         struct fsctl_srv_copychunk_state *state = tevent_req_data(req,
272                                         struct fsctl_srv_copychunk_state);
273         off_t chunk_nwritten;
274         NTSTATUS status;
275
276         state->recv_count++;
277         status = SMB_VFS_COPY_CHUNK_RECV(state->conn, subreq,
278                                          &chunk_nwritten);
279         TALLOC_FREE(subreq);
280         if (NT_STATUS_IS_OK(status)) {
281                 DEBUG(10, ("good copy chunk recv %d of %d\n",
282                            state->recv_count,
283                            state->dispatch_count));
284                 state->total_written += chunk_nwritten;
285         } else {
286                 DEBUG(0, ("bad status in copy chunk recv %d of %d: %s\n",
287                           state->recv_count,
288                           state->dispatch_count,
289                           nt_errstr(status)));
290                 state->bad_recv_count++;
291                 /* may overwrite previous failed status */
292                 state->status = status;
293         }
294
295         if (state->recv_count != state->dispatch_count) {
296                 /*
297                  * Wait for all VFS copy_chunk requests to complete, even
298                  * if an error is received for a specific chunk.
299                  */
300                 return;
301         }
302
303         /* all VFS copy_chunk requests done */
304         copychunk_unlock_all(state->src_fsp,
305                              state->dst_fsp,
306                              state->rlocks,
307                              state->wlocks,
308                              state->num_locks);
309
310         if (!tevent_req_nterror(req, state->status)) {
311                 tevent_req_done(req);
312         }
313 }
314
315 static NTSTATUS fsctl_srv_copychunk_recv(struct tevent_req *req,
316                                          struct srv_copychunk_rsp *cc_rsp)
317 {
318         struct fsctl_srv_copychunk_state *state = tevent_req_data(req,
319                                         struct fsctl_srv_copychunk_state);
320         NTSTATUS status;
321
322         if (NT_STATUS_EQUAL(state->status, NT_STATUS_INVALID_PARAMETER)) {
323                 /* 2.2.32.1 - send back our maximum transfer size limits */
324                 copychunk_pack_limits(cc_rsp);
325                 tevent_req_received(req);
326                 return NT_STATUS_INVALID_PARAMETER;
327         }
328
329         cc_rsp->chunks_written = state->recv_count - state->bad_recv_count;
330         cc_rsp->chunk_bytes_written = 0;
331         cc_rsp->total_bytes_written = state->total_written;
332         status = state->status;
333         tevent_req_received(req);
334
335         return status;
336 }
337
338 static NTSTATUS fsctl_validate_neg_info(TALLOC_CTX *mem_ctx,
339                                         struct tevent_context *ev,
340                                         struct smbXsrv_connection *conn,
341                                         DATA_BLOB *in_input,
342                                         uint32_t in_max_output,
343                                         DATA_BLOB *out_output,
344                                         bool *disconnect)
345 {
346         uint32_t in_capabilities;
347         DATA_BLOB in_guid_blob;
348         struct GUID in_guid;
349         uint16_t in_security_mode;
350         uint16_t in_num_dialects;
351         uint16_t i;
352         DATA_BLOB out_guid_blob;
353         NTSTATUS status;
354
355         if (in_input->length < 0x18) {
356                 return NT_STATUS_INVALID_PARAMETER;
357         }
358
359         in_capabilities = IVAL(in_input->data, 0x00);
360         in_guid_blob = data_blob_const(in_input->data + 0x04, 16);
361         in_security_mode = SVAL(in_input->data, 0x14);
362         in_num_dialects = SVAL(in_input->data, 0x16);
363
364         if (in_input->length < (0x18 + in_num_dialects*2)) {
365                 return NT_STATUS_INVALID_PARAMETER;
366         }
367
368         if (in_max_output < 0x18) {
369                 return NT_STATUS_BUFFER_TOO_SMALL;
370         }
371
372         status = GUID_from_ndr_blob(&in_guid_blob, &in_guid);
373         if (!NT_STATUS_IS_OK(status)) {
374                 return status;
375         }
376
377         if (in_num_dialects != conn->smb2.client.num_dialects) {
378                 *disconnect = true;
379                 return NT_STATUS_ACCESS_DENIED;
380         }
381
382         for (i=0; i < in_num_dialects; i++) {
383                 uint16_t v = SVAL(in_input->data, 0x18 + i*2);
384
385                 if (conn->smb2.client.dialects[i] != v) {
386                         *disconnect = true;
387                         return NT_STATUS_ACCESS_DENIED;
388                 }
389         }
390
391         if (GUID_compare(&in_guid, &conn->smb2.client.guid) != 0) {
392                 *disconnect = true;
393                 return NT_STATUS_ACCESS_DENIED;
394         }
395
396         if (in_security_mode != conn->smb2.client.security_mode) {
397                 *disconnect = true;
398                 return NT_STATUS_ACCESS_DENIED;
399         }
400
401         if (in_capabilities != conn->smb2.client.capabilities) {
402                 *disconnect = true;
403                 return NT_STATUS_ACCESS_DENIED;
404         }
405
406         status = GUID_to_ndr_blob(&conn->smb2.server.guid, mem_ctx,
407                                   &out_guid_blob);
408         if (!NT_STATUS_IS_OK(status)) {
409                 return status;
410         }
411
412         *out_output = data_blob_talloc(mem_ctx, NULL, 0x18);
413         if (out_output->data == NULL) {
414                 return NT_STATUS_NO_MEMORY;
415         }
416
417         SIVAL(out_output->data, 0x00, conn->smb2.server.capabilities);
418         memcpy(out_output->data+0x04, out_guid_blob.data, 16);
419         SIVAL(out_output->data, 0x14, conn->smb2.server.security_mode);
420         SIVAL(out_output->data, 0x16, conn->smb2.server.dialect);
421
422         return NT_STATUS_OK;
423 }
424
425 static NTSTATUS fsctl_srv_req_resume_key(TALLOC_CTX *mem_ctx,
426                                          struct tevent_context *ev,
427                                          struct files_struct *fsp,
428                                          uint32_t in_max_output,
429                                          DATA_BLOB *out_output)
430 {
431         struct req_resume_key_rsp rkey_rsp;
432         enum ndr_err_code ndr_ret;
433         DATA_BLOB output;
434
435         if (fsp == NULL) {
436                 return NT_STATUS_FILE_CLOSED;
437         }
438
439         ZERO_STRUCT(rkey_rsp);
440         /* combine persistent and volatile handles for the resume key */
441         SBVAL(rkey_rsp.resume_key, 0, fsp->op->global->open_persistent_id);
442         SBVAL(rkey_rsp.resume_key, 8, fsp->op->global->open_volatile_id);
443
444         ndr_ret = ndr_push_struct_blob(&output, mem_ctx, &rkey_rsp,
445                         (ndr_push_flags_fn_t)ndr_push_req_resume_key_rsp);
446         if (ndr_ret != NDR_ERR_SUCCESS) {
447                 return NT_STATUS_INTERNAL_ERROR;
448         }
449
450         if (in_max_output < output.length) {
451                 DEBUG(1, ("max output %u too small for resume key rsp %ld\n",
452                           in_max_output, (long int)output.length));
453                 return NT_STATUS_INVALID_PARAMETER;
454         }
455         *out_output = output;
456
457         return NT_STATUS_OK;
458 }
459
460 static void smb2_ioctl_network_fs_copychunk_done(struct tevent_req *subreq);
461
462 struct tevent_req *smb2_ioctl_network_fs(uint32_t ctl_code,
463                                          struct tevent_context *ev,
464                                          struct tevent_req *req,
465                                          struct smbd_smb2_ioctl_state *state)
466 {
467         struct tevent_req *subreq;
468         NTSTATUS status;
469
470         switch (ctl_code) {
471         case FSCTL_SRV_COPYCHUNK:
472                 subreq = fsctl_srv_copychunk_send(state, ev, state->fsp,
473                                                   &state->in_input,
474                                                   state->smb2req);
475                 if (tevent_req_nomem(subreq, req)) {
476                         return tevent_req_post(req, ev);
477                 }
478                 tevent_req_set_callback(subreq,
479                                         smb2_ioctl_network_fs_copychunk_done,
480                                         req);
481                 return req;
482                 break;
483         case FSCTL_VALIDATE_NEGOTIATE_INFO:
484                 status = fsctl_validate_neg_info(state, ev,
485                                                  state->smbreq->sconn->conn,
486                                                  &state->in_input,
487                                                  state->in_max_output,
488                                                  &state->out_output,
489                                                  &state->disconnect);
490                 if (!tevent_req_nterror(req, status)) {
491                         tevent_req_done(req);
492                 }
493                 return tevent_req_post(req, ev);
494                 break;
495         case FSCTL_SRV_REQUEST_RESUME_KEY:
496                 status = fsctl_srv_req_resume_key(state, ev, state->fsp,
497                                                   state->in_max_output,
498                                                   &state->out_output);
499                 if (!tevent_req_nterror(req, status)) {
500                         tevent_req_done(req);
501                 }
502                 return tevent_req_post(req, ev);
503                 break;
504         default: {
505                 uint8_t *out_data = NULL;
506                 uint32_t out_data_len = 0;
507
508                 if (state->fsp == NULL) {
509                         status = NT_STATUS_NOT_SUPPORTED;
510                 } else {
511                         status = SMB_VFS_FSCTL(state->fsp,
512                                                state,
513                                                ctl_code,
514                                                state->smbreq->flags2,
515                                                state->in_input.data,
516                                                state->in_input.length,
517                                                &out_data,
518                                                state->in_max_output,
519                                                &out_data_len);
520                         state->out_output = data_blob_const(out_data, out_data_len);
521                         if (NT_STATUS_IS_OK(status)) {
522                                 tevent_req_done(req);
523                                 return tevent_req_post(req, ev);
524                         }
525                 }
526
527                 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) {
528                         if (IS_IPC(state->smbreq->conn)) {
529                                 status = NT_STATUS_FS_DRIVER_REQUIRED;
530                         } else {
531                                 status = NT_STATUS_INVALID_DEVICE_REQUEST;
532                         }
533                 }
534
535                 tevent_req_nterror(req, status);
536                 return tevent_req_post(req, ev);
537                 break;
538         }
539         }
540
541         tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
542         return tevent_req_post(req, ev);
543 }
544
545 static void smb2_ioctl_network_fs_copychunk_done(struct tevent_req *subreq)
546 {
547         struct tevent_req *req = tevent_req_callback_data(subreq,
548                                                           struct tevent_req);
549         struct smbd_smb2_ioctl_state *ioctl_state = tevent_req_data(req,
550                                                 struct smbd_smb2_ioctl_state);
551         struct srv_copychunk_rsp cc_rsp;
552         NTSTATUS status;
553         enum ndr_err_code ndr_ret;
554
555         ZERO_STRUCT(cc_rsp);
556         status = fsctl_srv_copychunk_recv(subreq, &cc_rsp);
557         TALLOC_FREE(subreq);
558         ndr_ret = ndr_push_struct_blob(&ioctl_state->out_output,
559                                        ioctl_state,
560                                        &cc_rsp,
561                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_rsp);
562         if (ndr_ret != NDR_ERR_SUCCESS) {
563                 status = NT_STATUS_INTERNAL_ERROR;
564         }
565
566         if (!tevent_req_nterror(req, status)) {
567                 tevent_req_done(req);
568         }
569 }