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