eb2c545d3f5f9b4ae97b13de9d2c8766acb92f20
[metze/samba/wip.git] / source4 / torture / smb2 / compound.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    test suite for SMB2 compounded requests
5
6    Copyright (C) Stefan Metzmacher 2009
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 "libcli/smb2/smb2.h"
24 #include "libcli/smb2/smb2_calls.h"
25 #include "torture/torture.h"
26 #include "torture/smb2/proto.h"
27 #include "../libcli/smb/smbXcli_base.h"
28
29 #define CHECK_STATUS(status, correct) do { \
30         if (!NT_STATUS_EQUAL(status, correct)) { \
31                 torture_result(tctx, TORTURE_FAIL, __location__": Incorrect status %s - should be %s", \
32                        nt_errstr(status), nt_errstr(correct)); \
33                 ret = false; \
34                 goto done; \
35         }} while (0)
36
37 #define TARGET_IS_W2K8(_tctx) (torture_setting_bool(_tctx, "w2k8", false))
38 #define TARGET_IS_WIN7(_tctx) (torture_setting_bool(_tctx, "win7", false))
39
40 static bool test_compound_related1(struct torture_context *tctx,
41                                    struct smb2_tree *tree)
42 {
43         struct smb2_handle hd;
44         struct smb2_create cr;
45         NTSTATUS status;
46         const char *fname = "compound_related1.dat";
47         struct smb2_close cl;
48         bool ret = true;
49         struct smb2_request *req[2];
50         struct smbXcli_tcon *saved_tcon = tree->smbXcli;
51         struct smbXcli_session *saved_session = tree->session->smbXcli;
52
53         struct smb2_session *session2;
54         struct smb2_tree *tree2;
55
56         if (!torture_smb2_session_setup(tctx, tree->session->transport,
57                                         0, /* previous_session_id */
58                                         tctx, &session2))
59         {
60                 torture_warning(tctx, "session setup failed.\n");
61                 ret = false;
62                 goto done;
63         }
64
65         if (!torture_smb2_tree_connect(tctx, session2, tctx, &tree2)) {
66                 torture_warning(tctx, "tree connect failed.\n");
67                 ret = false;
68                 goto done;
69         }
70
71         smb2_transport_credits_ask_num(tree->session->transport, 2);
72
73         smb2_util_unlink(tree, fname);
74
75         smb2_transport_credits_ask_num(tree->session->transport, 1);
76
77         ZERO_STRUCT(cr);
78         cr.in.security_flags            = 0x00;
79         cr.in.oplock_level              = 0;
80         cr.in.impersonation_level       = NTCREATEX_IMPERSONATION_IMPERSONATION;
81         cr.in.create_flags              = 0x00000000;
82         cr.in.reserved                  = 0x00000000;
83         cr.in.desired_access            = SEC_RIGHTS_FILE_ALL;
84         cr.in.file_attributes           = FILE_ATTRIBUTE_NORMAL;
85         cr.in.share_access              = NTCREATEX_SHARE_ACCESS_READ |
86                                           NTCREATEX_SHARE_ACCESS_WRITE |
87                                           NTCREATEX_SHARE_ACCESS_DELETE;
88         cr.in.create_disposition        = NTCREATEX_DISP_OPEN_IF;
89         cr.in.create_options            = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
90                                           NTCREATEX_OPTIONS_ASYNC_ALERT |
91                                           NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
92                                           0x00200000;
93         cr.in.fname                     = fname;
94
95         smb2_transport_compound_start(tree->session->transport, 2);
96
97         req[0] = smb2_create_send(tree, &cr);
98
99         smb2_transport_compound_set_related(tree->session->transport, true);
100
101         hd.data[0] = UINT64_MAX;
102         hd.data[1] = UINT64_MAX;
103
104         ZERO_STRUCT(cl);
105         cl.in.file.handle = hd;
106
107         tree->smbXcli = smbXcli_tcon_create(tree);
108         smb2cli_tcon_set_values(tree->smbXcli,
109                                 NULL, /* session */
110                                 0xFFFFFFFF, /* tcon_id */
111                                 0, /* type */
112                                 0, /* flags */
113                                 0, /* capabilities */
114                                 0 /* maximal_access */);
115
116         tree->session->smbXcli = smbXcli_session_create(tree->session,
117                                                         tree->session->transport->conn);
118         smb2cli_session_set_id_and_flags(tree->session->smbXcli, UINT64_MAX, 0);
119
120         req[1] = smb2_close_send(tree, &cl);
121
122         status = smb2_create_recv(req[0], tree, &cr);
123         CHECK_STATUS(status, NT_STATUS_OK);
124         status = smb2_close_recv(req[1], &cl);
125         CHECK_STATUS(status, NT_STATUS_OK);
126
127         TALLOC_FREE(tree->smbXcli);
128         tree->smbXcli = saved_tcon;
129         TALLOC_FREE(tree->session->smbXcli);
130         tree->session->smbXcli = saved_session;
131
132         smb2_util_unlink(tree, fname);
133 done:
134         return ret;
135 }
136
137 static bool test_compound_related2(struct torture_context *tctx,
138                                    struct smb2_tree *tree)
139 {
140         struct smb2_handle hd;
141         struct smb2_create cr;
142         NTSTATUS status;
143         const char *fname = "compound_related2.dat";
144         struct smb2_close cl;
145         bool ret = true;
146         struct smb2_request *req[5];
147         struct smbXcli_tcon *saved_tcon = tree->smbXcli;
148         struct smbXcli_session *saved_session = tree->session->smbXcli;
149         struct smbXcli_session *saved_session2;
150         struct smbXcli_session *saved_session3;
151
152         struct smb2_session *session2;
153         struct smb2_tree *tree2;
154
155         if (!torture_smb2_session_setup(tctx, tree->session->transport,
156                                         0, /* previous_session_id */
157                                         tctx, &session2))
158         {
159                 torture_warning(tctx, "session setup failed.\n");
160                 ret = false;
161                 goto done;
162         }
163         saved_session2 = session2->smbXcli;
164
165         if (!torture_smb2_tree_connect(tctx, session2, tctx, &tree2)) {
166                 torture_warning(tctx, "tree connect failed.\n");
167                 ret = false;
168                 goto done;
169         }
170         smb2_transport_credits_ask_num(tree->session->transport, 5);
171
172         smb2_util_unlink(tree, fname);
173
174         smb2_transport_credits_ask_num(tree->session->transport, 1);
175
176         ZERO_STRUCT(cr);
177         cr.in.security_flags            = 0x00;
178         cr.in.oplock_level              = 0;
179         cr.in.impersonation_level       = NTCREATEX_IMPERSONATION_IMPERSONATION;
180         cr.in.create_flags              = 0x00000000;
181         cr.in.reserved                  = 0x00000000;
182         cr.in.desired_access            = SEC_RIGHTS_FILE_ALL;
183         cr.in.file_attributes           = FILE_ATTRIBUTE_NORMAL;
184         cr.in.share_access              = NTCREATEX_SHARE_ACCESS_READ |
185                                           NTCREATEX_SHARE_ACCESS_WRITE |
186                                           NTCREATEX_SHARE_ACCESS_DELETE;
187         cr.in.create_disposition        = NTCREATEX_DISP_OPEN_IF;
188         cr.in.create_options            = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
189                                           NTCREATEX_OPTIONS_ASYNC_ALERT |
190                                           NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
191                                           0x00200000;
192         cr.in.fname                     = fname;
193
194         smb2_transport_compound_start(tree->session->transport, 5);
195
196         req[0] = smb2_create_send(tree, &cr);
197
198         hd.data[0] = UINT64_MAX;
199         hd.data[1] = UINT64_MAX;
200
201         smb2_transport_compound_set_related(tree->session->transport, true);
202
203         ZERO_STRUCT(cl);
204         cl.in.file.handle = hd;
205
206         tree->smbXcli = smbXcli_tcon_create(tree);
207         smb2cli_tcon_set_values(tree->smbXcli,
208                                 NULL, /* session */
209                                 0xFFFFFFFF, /* tcon_id */
210                                 0, /* type */
211                                 0, /* flags */
212                                 0, /* capabilities */
213                                 0 /* maximal_access */);
214
215         tree->session->smbXcli = smbXcli_session_create(tree->session,
216                                                         tree->session->transport->conn);
217         smb2cli_session_set_id_and_flags(tree->session->smbXcli, UINT64_MAX, 0);
218         saved_session3 = tree->session->smbXcli;
219
220         tree->session->smbXcli = saved_session2;
221         req[1] = smb2_close_send(tree, &cl);
222         tree->session->smbXcli = saved_session3;
223         req[2] = smb2_close_send(tree, &cl);
224         req[3] = smb2_close_send(tree, &cl);
225         req[4] = smb2_close_send(tree, &cl);
226
227         tree->session->smbXcli = saved_session3;
228
229         status = smb2_create_recv(req[0], tree, &cr);
230         CHECK_STATUS(status, NT_STATUS_OK);
231         status = smb2_close_recv(req[1], &cl);
232         CHECK_STATUS(status, NT_STATUS_OK);
233         status = smb2_close_recv(req[2], &cl);
234         CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
235         status = smb2_close_recv(req[3], &cl);
236         CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
237         status = smb2_close_recv(req[4], &cl);
238         CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
239
240         TALLOC_FREE(tree->smbXcli);
241         tree->smbXcli = saved_tcon;
242         TALLOC_FREE(tree->session->smbXcli);
243         tree->session->smbXcli = saved_session;
244
245         smb2_util_unlink(tree, fname);
246 done:
247         return ret;
248 }
249
250 static bool test_compound_related3(struct torture_context *tctx,
251                                    struct smb2_tree *tree)
252 {
253         struct smb2_handle hd;
254         struct smb2_ioctl io;
255         struct smb2_create cr;
256         struct smb2_close cl;
257         const char *fname = "compound_related3.dat";
258         struct smb2_request *req[3];
259         NTSTATUS status;
260         bool ret = false;
261
262         smb2_util_unlink(tree, fname);
263
264         ZERO_STRUCT(cr);
265         cr.in.security_flags    = 0x00;
266         cr.in.oplock_level      = 0;
267         cr.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION;
268         cr.in.create_flags      = 0x00000000;
269         cr.in.reserved          = 0x00000000;
270         cr.in.desired_access    = SEC_RIGHTS_FILE_ALL;
271         cr.in.file_attributes   = FILE_ATTRIBUTE_NORMAL;
272         cr.in.share_access      = NTCREATEX_SHARE_ACCESS_READ |
273                                   NTCREATEX_SHARE_ACCESS_WRITE |
274                                   NTCREATEX_SHARE_ACCESS_DELETE;
275         cr.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
276         cr.in.create_options    = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
277                                   NTCREATEX_OPTIONS_ASYNC_ALERT |
278                                   NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
279                                   0x00200000;
280         cr.in.fname             = fname;
281
282         smb2_transport_compound_start(tree->session->transport, 3);
283
284         req[0] = smb2_create_send(tree, &cr);
285
286         hd.data[0] = UINT64_MAX;
287         hd.data[1] = UINT64_MAX;
288
289         smb2_transport_compound_set_related(tree->session->transport, true);
290
291         ZERO_STRUCT(io);
292         io.in.function = FSCTL_CREATE_OR_GET_OBJECT_ID;
293         io.in.file.handle = hd;
294         io.in.unknown2 = 0;
295         io.in.max_response_size = 64;
296         io.in.flags = 1;
297
298         req[1] = smb2_ioctl_send(tree, &io);
299
300         ZERO_STRUCT(cl);
301         cl.in.file.handle = hd;
302
303         req[2] = smb2_close_send(tree, &cl);
304
305         status = smb2_create_recv(req[0], tree, &cr);
306         CHECK_STATUS(status, NT_STATUS_OK);
307         status = smb2_ioctl_recv(req[1], tree, &io);
308         CHECK_STATUS(status, NT_STATUS_OK);
309         status = smb2_close_recv(req[2], &cl);
310         CHECK_STATUS(status, NT_STATUS_OK);
311
312         status = smb2_util_unlink(tree, fname);
313         CHECK_STATUS(status, NT_STATUS_OK);
314
315         ret = true;
316 done:
317         return ret;
318 }
319
320 static bool test_compound_unrelated1(struct torture_context *tctx,
321                                      struct smb2_tree *tree)
322 {
323         struct smb2_handle hd;
324         struct smb2_create cr;
325         NTSTATUS status;
326         const char *fname = "compound_unrelated1.dat";
327         struct smb2_close cl;
328         bool ret = true;
329         struct smb2_request *req[5];
330         struct smbXcli_session *saved_session = tree->session->smbXcli;
331         struct smbXcli_session *saved_session2;
332         struct smbXcli_session *saved_session3;
333
334         struct smb2_session *session2;
335         struct smb2_tree *tree2;
336
337         if (!torture_smb2_session_setup(tctx, tree->session->transport,
338                                         0, /* previous_session_id */
339                                         tctx, &session2))
340         {
341                 torture_warning(tctx, "session setup failed.\n");
342                 ret = false;
343                 goto done;
344         }
345         saved_session2 = session2->smbXcli;
346
347         if (!torture_smb2_tree_connect(tctx, session2, tctx, &tree2)) {
348                 torture_warning(tctx, "tree connect failed.\n");
349                 ret = false;
350                 goto done;
351         }
352
353         smb2_transport_credits_ask_num(tree->session->transport, 5);
354
355         smb2_util_unlink(tree, fname);
356
357         smb2_transport_credits_ask_num(tree->session->transport, 1);
358
359         ZERO_STRUCT(cr);
360         cr.in.security_flags            = 0x00;
361         cr.in.oplock_level              = 0;
362         cr.in.impersonation_level       = NTCREATEX_IMPERSONATION_IMPERSONATION;
363         cr.in.create_flags              = 0x00000000;
364         cr.in.reserved                  = 0x00000000;
365         cr.in.desired_access            = SEC_RIGHTS_FILE_ALL;
366         cr.in.file_attributes           = FILE_ATTRIBUTE_NORMAL;
367         cr.in.share_access              = NTCREATEX_SHARE_ACCESS_READ |
368                                           NTCREATEX_SHARE_ACCESS_WRITE |
369                                           NTCREATEX_SHARE_ACCESS_DELETE;
370         cr.in.create_disposition        = NTCREATEX_DISP_OPEN_IF;
371         cr.in.create_options            = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
372                                           NTCREATEX_OPTIONS_ASYNC_ALERT |
373                                           NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
374                                           0x00200000;
375         cr.in.fname                     = fname;
376
377         smb2_transport_compound_start(tree->session->transport, 5);
378         tree->session->transport->compound.session = session2->smbXcli;
379
380         req[0] = smb2_create_send(tree, &cr);
381
382         hd.data[0] = UINT64_MAX;
383         hd.data[1] = UINT64_MAX;
384
385         ZERO_STRUCT(cl);
386         cl.in.file.handle = hd;
387
388         //tree->session->smbXcli = smbXcli_session_create(tree->session,
389                                                         //tree->session->transport->conn);
390         //smb2cli_session_set_id_and_flags(tree->session->smbXcli, 12345678, 0);
391         //saved_session3 = tree->session->smbXcli;
392
393         req[1] = smb2_close_send(tree, &cl);
394         req[2] = smb2_close_send(tree, &cl);
395         req[3] = smb2_close_send(tree, &cl);
396         req[4] = smb2_close_send(tree, &cl);
397
398         status = smb2_create_recv(req[0], tree, &cr);
399         CHECK_STATUS(status, NT_STATUS_OK);
400         status = smb2_close_recv(req[1], &cl);
401         CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
402         status = smb2_close_recv(req[2], &cl);
403         CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
404         status = smb2_close_recv(req[3], &cl);
405         CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
406         status = smb2_close_recv(req[4], &cl);
407         CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
408
409         smb2_util_unlink(tree, fname);
410 done:
411         return ret;
412 }
413
414 static bool test_compound_invalid1(struct torture_context *tctx,
415                                    struct smb2_tree *tree)
416 {
417         struct smb2_handle hd;
418         struct smb2_create cr;
419         NTSTATUS status;
420         const char *fname = "compound_invalid1.dat";
421         struct smb2_close cl;
422         bool ret = true;
423         struct smb2_request *req[2];
424
425         smb2_transport_credits_ask_num(tree->session->transport, 2);
426
427         smb2_util_unlink(tree, fname);
428
429         smb2_transport_credits_ask_num(tree->session->transport, 1);
430
431         ZERO_STRUCT(cr);
432         cr.in.security_flags            = 0x00;
433         cr.in.oplock_level              = 0;
434         cr.in.impersonation_level       = NTCREATEX_IMPERSONATION_IMPERSONATION;
435         cr.in.create_flags              = 0x00000000;
436         cr.in.reserved                  = 0x00000000;
437         cr.in.desired_access            = SEC_RIGHTS_FILE_ALL;
438         cr.in.file_attributes           = FILE_ATTRIBUTE_NORMAL;
439         cr.in.share_access              = NTCREATEX_SHARE_ACCESS_READ |
440                                           NTCREATEX_SHARE_ACCESS_WRITE |
441                                           NTCREATEX_SHARE_ACCESS_DELETE;
442         cr.in.create_disposition        = NTCREATEX_DISP_OPEN_IF;
443         cr.in.create_options            = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
444                                           NTCREATEX_OPTIONS_ASYNC_ALERT |
445                                           NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
446                                           0x00200000;
447         cr.in.fname                     = fname;
448
449         smb2_transport_compound_start(tree->session->transport, 2);
450
451         /* passing the first request with the related flag is invalid */
452         smb2_transport_compound_set_related(tree->session->transport, true);
453
454         req[0] = smb2_create_send(tree, &cr);
455
456         hd.data[0] = UINT64_MAX;
457         hd.data[1] = UINT64_MAX;
458
459         ZERO_STRUCT(cl);
460         cl.in.file.handle = hd;
461         req[1] = smb2_close_send(tree, &cl);
462
463         status = smb2_create_recv(req[0], tree, &cr);
464         /* TODO: check why this fails with --signing=required */
465         CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
466         status = smb2_close_recv(req[1], &cl);
467         CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
468
469         smb2_util_unlink(tree, fname);
470 done:
471         return ret;
472 }
473
474 static bool test_compound_invalid2(struct torture_context *tctx,
475                                    struct smb2_tree *tree)
476 {
477         struct smb2_handle hd;
478         struct smb2_create cr;
479         NTSTATUS status;
480         const char *fname = "compound_invalid2.dat";
481         struct smb2_close cl;
482         bool ret = true;
483         struct smb2_request *req[5];
484         struct smbXcli_tcon *saved_tcon = tree->smbXcli;
485         struct smbXcli_session *saved_session = tree->session->smbXcli;
486
487         smb2_transport_credits_ask_num(tree->session->transport, 5);
488
489         smb2_util_unlink(tree, fname);
490
491         smb2_transport_credits_ask_num(tree->session->transport, 1);
492
493         ZERO_STRUCT(cr);
494         cr.in.security_flags            = 0x00;
495         cr.in.oplock_level              = 0;
496         cr.in.impersonation_level       = NTCREATEX_IMPERSONATION_IMPERSONATION;
497         cr.in.create_flags              = 0x00000000;
498         cr.in.reserved                  = 0x00000000;
499         cr.in.desired_access            = SEC_RIGHTS_FILE_ALL;
500         cr.in.file_attributes           = FILE_ATTRIBUTE_NORMAL;
501         cr.in.share_access              = NTCREATEX_SHARE_ACCESS_READ |
502                                           NTCREATEX_SHARE_ACCESS_WRITE |
503                                           NTCREATEX_SHARE_ACCESS_DELETE;
504         cr.in.create_disposition        = NTCREATEX_DISP_OPEN_IF;
505         cr.in.create_options            = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
506                                           NTCREATEX_OPTIONS_ASYNC_ALERT |
507                                           NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
508                                           0x00200000;
509         cr.in.fname                     = fname;
510
511         smb2_transport_compound_start(tree->session->transport, 5);
512
513         req[0] = smb2_create_send(tree, &cr);
514
515         hd.data[0] = UINT64_MAX;
516         hd.data[1] = UINT64_MAX;
517
518         smb2_transport_compound_set_related(tree->session->transport, true);
519
520         ZERO_STRUCT(cl);
521         cl.in.file.handle = hd;
522
523         tree->smbXcli = smbXcli_tcon_create(tree);
524         smb2cli_tcon_set_values(tree->smbXcli,
525                                 NULL, /* session */
526                                 0xFFFFFFFF, /* tcon_id */
527                                 0, /* type */
528                                 0, /* flags */
529                                 0, /* capabilities */
530                                 0 /* maximal_access */);
531
532         tree->session->smbXcli = smbXcli_session_create(tree->session,
533                                                         tree->session->transport->conn);
534         smb2cli_session_set_id_and_flags(tree->session->smbXcli, UINT64_MAX, 0);
535
536         req[1] = smb2_close_send(tree, &cl);
537         /* strange that this is not generating invalid parameter */
538         smb2_transport_compound_set_related(tree->session->transport, false);
539         req[2] = smb2_close_send(tree, &cl);
540         req[3] = smb2_close_send(tree, &cl);
541         smb2_transport_compound_set_related(tree->session->transport, true);
542         req[4] = smb2_close_send(tree, &cl);
543
544         status = smb2_create_recv(req[0], tree, &cr);
545         CHECK_STATUS(status, NT_STATUS_OK);
546         status = smb2_close_recv(req[1], &cl);
547         CHECK_STATUS(status, NT_STATUS_OK);
548         status = smb2_close_recv(req[2], &cl);
549         CHECK_STATUS(status, NT_STATUS_USER_SESSION_DELETED);
550         status = smb2_close_recv(req[3], &cl);
551         CHECK_STATUS(status, NT_STATUS_USER_SESSION_DELETED);
552         status = smb2_close_recv(req[4], &cl);
553         CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
554
555         TALLOC_FREE(tree->smbXcli);
556         tree->smbXcli = saved_tcon;
557         TALLOC_FREE(tree->session->smbXcli);
558         tree->session->smbXcli = saved_session;
559
560         smb2_util_unlink(tree, fname);
561 done:
562         return ret;
563 }
564
565 static bool test_compound_invalid3(struct torture_context *tctx,
566                                    struct smb2_tree *tree)
567 {
568         struct smb2_handle hd;
569         struct smb2_create cr;
570         NTSTATUS status;
571         const char *fname = "compound_invalid3.dat";
572         struct smb2_close cl;
573         bool ret = true;
574         struct smb2_request *req[5];
575
576         smb2_transport_credits_ask_num(tree->session->transport, 5);
577
578         smb2_util_unlink(tree, fname);
579
580         smb2_transport_credits_ask_num(tree->session->transport, 1);
581
582         ZERO_STRUCT(cr);
583         cr.in.security_flags            = 0x00;
584         cr.in.oplock_level              = 0;
585         cr.in.impersonation_level       = NTCREATEX_IMPERSONATION_IMPERSONATION;
586         cr.in.create_flags              = 0x00000000;
587         cr.in.reserved                  = 0x00000000;
588         cr.in.desired_access            = SEC_RIGHTS_FILE_ALL;
589         cr.in.file_attributes           = FILE_ATTRIBUTE_NORMAL;
590         cr.in.share_access              = NTCREATEX_SHARE_ACCESS_READ |
591                                           NTCREATEX_SHARE_ACCESS_WRITE |
592                                           NTCREATEX_SHARE_ACCESS_DELETE;
593         cr.in.create_disposition        = NTCREATEX_DISP_OPEN_IF;
594         cr.in.create_options            = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
595                                           NTCREATEX_OPTIONS_ASYNC_ALERT |
596                                           NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
597                                           0x00200000;
598         cr.in.fname                     = fname;
599
600         smb2_transport_compound_start(tree->session->transport, 5);
601
602         req[0] = smb2_create_send(tree, &cr);
603
604         hd.data[0] = UINT64_MAX;
605         hd.data[1] = UINT64_MAX;
606
607         ZERO_STRUCT(cl);
608         cl.in.file.handle = hd;
609         req[1] = smb2_close_send(tree, &cl);
610         req[2] = smb2_close_send(tree, &cl);
611         /* flipping the related flag is invalid */
612         smb2_transport_compound_set_related(tree->session->transport, true);
613         req[3] = smb2_close_send(tree, &cl);
614         req[4] = smb2_close_send(tree, &cl);
615
616         status = smb2_create_recv(req[0], tree, &cr);
617         CHECK_STATUS(status, NT_STATUS_OK);
618         status = smb2_close_recv(req[1], &cl);
619         CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
620         status = smb2_close_recv(req[2], &cl);
621         CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
622         status = smb2_close_recv(req[3], &cl);
623         CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
624         status = smb2_close_recv(req[4], &cl);
625         CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
626
627         smb2_util_unlink(tree, fname);
628 done:
629         return ret;
630 }
631
632 /* Send a compound request where we expect the last request (Create, Notify)
633  * to go asynchronous. This works against a Win7 server and the reply is
634  * sent in two different packets. */
635 static bool test_compound_interim1(struct torture_context *tctx,
636                                    struct smb2_tree *tree)
637 {
638     struct smb2_handle hd;
639     struct smb2_create cr;
640     NTSTATUS status = NT_STATUS_OK;
641     const char *dname = "compound_interim_dir";
642     struct smb2_notify nt;
643     bool ret = true;
644     struct smb2_request *req[2];
645
646     /* Win7 compound request implementation deviates substantially from the
647      * SMB2 spec as noted in MS-SMB2 <159>, <162>.  This, test currently
648      * verifies the Windows behavior, not the general spec behavior. */
649     if (!TARGET_IS_WIN7(tctx) && !TARGET_IS_W2K8(tctx)) {
650             torture_skip(tctx, "Interim test is specific to Windows server "
651                                "behavior.\n");
652     }
653
654     smb2_transport_credits_ask_num(tree->session->transport, 5);
655
656     smb2_deltree(tree, dname);
657
658     smb2_transport_credits_ask_num(tree->session->transport, 1);
659
660     ZERO_STRUCT(cr);
661     cr.in.desired_access        = SEC_RIGHTS_FILE_ALL;
662     cr.in.create_options        = NTCREATEX_OPTIONS_DIRECTORY;
663     cr.in.file_attributes       = FILE_ATTRIBUTE_DIRECTORY;
664     cr.in.share_access          = NTCREATEX_SHARE_ACCESS_READ |
665                                   NTCREATEX_SHARE_ACCESS_WRITE |
666                                   NTCREATEX_SHARE_ACCESS_DELETE;
667     cr.in.create_disposition    = NTCREATEX_DISP_CREATE;
668     cr.in.fname                 = dname;
669
670     smb2_transport_compound_start(tree->session->transport, 2);
671
672     req[0] = smb2_create_send(tree, &cr);
673
674     smb2_transport_compound_set_related(tree->session->transport, true);
675
676     hd.data[0] = UINT64_MAX;
677     hd.data[1] = UINT64_MAX;
678
679     ZERO_STRUCT(nt);
680     nt.in.recursive          = true;
681     nt.in.buffer_size        = 0x1000;
682     nt.in.file.handle        = hd;
683     nt.in.completion_filter  = FILE_NOTIFY_CHANGE_NAME;
684     nt.in.unknown            = 0x00000000;
685
686     req[1] = smb2_notify_send(tree, &nt);
687
688     status = smb2_create_recv(req[0], tree, &cr);
689     CHECK_STATUS(status, NT_STATUS_OK);
690
691     smb2_cancel(req[1]);
692     status = smb2_notify_recv(req[1], tree, &nt);
693     CHECK_STATUS(status, NT_STATUS_CANCELLED);
694
695     smb2_util_close(tree, cr.out.file.handle);
696
697     smb2_deltree(tree, dname);
698 done:
699     return ret;
700 }
701
702 /* Send a compound request where we expect the middle request (Create, Notify,
703  * GetInfo) to go asynchronous. Against Win7 the sync request succeed while
704  * the async fails. All are returned in the same compound response. */
705 static bool test_compound_interim2(struct torture_context *tctx,
706                                    struct smb2_tree *tree)
707 {
708     struct smb2_handle hd;
709     struct smb2_create cr;
710     NTSTATUS status = NT_STATUS_OK;
711     const char *dname = "compound_interim_dir";
712     struct smb2_getinfo gf;
713     struct smb2_notify  nt;
714     bool ret = true;
715     struct smb2_request *req[3];
716
717     /* Win7 compound request implementation deviates substantially from the
718      * SMB2 spec as noted in MS-SMB2 <159>, <162>.  This, test currently
719      * verifies the Windows behavior, not the general spec behavior. */
720     if (!TARGET_IS_WIN7(tctx) && !TARGET_IS_W2K8(tctx)) {
721             torture_skip(tctx, "Interim test is specific to Windows server "
722                                "behavior.\n");
723     }
724
725     smb2_transport_credits_ask_num(tree->session->transport, 5);
726
727     smb2_deltree(tree, dname);
728
729     smb2_transport_credits_ask_num(tree->session->transport, 1);
730
731     ZERO_STRUCT(cr);
732     cr.in.desired_access        = SEC_RIGHTS_FILE_ALL;
733     cr.in.create_options        = NTCREATEX_OPTIONS_DIRECTORY;
734     cr.in.file_attributes       = FILE_ATTRIBUTE_DIRECTORY;
735     cr.in.share_access      = NTCREATEX_SHARE_ACCESS_READ |
736                       NTCREATEX_SHARE_ACCESS_WRITE |
737                       NTCREATEX_SHARE_ACCESS_DELETE;
738     cr.in.create_disposition    = NTCREATEX_DISP_CREATE;
739     cr.in.fname         = dname;
740
741     smb2_transport_compound_start(tree->session->transport, 3);
742
743     req[0] = smb2_create_send(tree, &cr);
744
745     smb2_transport_compound_set_related(tree->session->transport, true);
746
747     hd.data[0] = UINT64_MAX;
748     hd.data[1] = UINT64_MAX;
749
750     ZERO_STRUCT(nt);
751     nt.in.recursive          = true;
752     nt.in.buffer_size        = 0x1000;
753     nt.in.file.handle        = hd;
754     nt.in.completion_filter  = FILE_NOTIFY_CHANGE_NAME;
755     nt.in.unknown            = 0x00000000;
756
757     req[1] = smb2_notify_send(tree, &nt);
758
759     ZERO_STRUCT(gf);
760     gf.in.file.handle = hd;
761     gf.in.info_type   = SMB2_GETINFO_FILE;
762     gf.in.info_class  = 0x04; /* FILE_BASIC_INFORMATION */
763     gf.in.output_buffer_length = 0x1000;
764     gf.in.input_buffer_length = 0;
765
766     req[2] = smb2_getinfo_send(tree, &gf);
767
768     status = smb2_create_recv(req[0], tree, &cr);
769     CHECK_STATUS(status, NT_STATUS_OK);
770
771     status = smb2_notify_recv(req[1], tree, &nt);
772     CHECK_STATUS(status, NT_STATUS_INTERNAL_ERROR);
773
774     status = smb2_getinfo_recv(req[2], tree, &gf);
775     CHECK_STATUS(status, NT_STATUS_OK);
776
777     smb2_util_close(tree, cr.out.file.handle);
778
779     smb2_deltree(tree, dname);
780 done:
781     return ret;
782 }
783
784 struct torture_suite *torture_smb2_compound_init(void)
785 {
786         struct torture_suite *suite = torture_suite_create(talloc_autofree_context(), "compound");
787
788         torture_suite_add_1smb2_test(suite, "related1", test_compound_related1);
789         torture_suite_add_1smb2_test(suite, "related2", test_compound_related2);
790         torture_suite_add_1smb2_test(suite, "related3",
791                                      test_compound_related3);
792         torture_suite_add_1smb2_test(suite, "unrelated1", test_compound_unrelated1);
793         torture_suite_add_1smb2_test(suite, "invalid1", test_compound_invalid1);
794         torture_suite_add_1smb2_test(suite, "invalid2", test_compound_invalid2);
795         torture_suite_add_1smb2_test(suite, "invalid3", test_compound_invalid3);
796         torture_suite_add_1smb2_test(suite, "interim1",  test_compound_interim1);
797         torture_suite_add_1smb2_test(suite, "interim2",  test_compound_interim2);
798
799         suite->description = talloc_strdup(suite, "SMB2-COMPOUND tests");
800
801         return suite;
802 }