s4:torture:smb2:compound: compound read and padding
[Samba.git] / source4 / torture / smb2 / compound.c
bloba502103be68317fc057c5de242d9ea7315822e46
1 /*
2 Unix SMB/CIFS implementation.
4 test suite for SMB2 compounded requests
6 Copyright (C) Stefan Metzmacher 2009
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.
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.
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/>.
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"
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)
37 #define CHECK_VALUE(v, correct) do { \
38 if ((v) != (correct)) { \
39 torture_result(tctx, TORTURE_FAIL, \
40 "(%s) Incorrect value %s=%d - should be %d\n", \
41 __location__, #v, (int)v, (int)correct); \
42 ret = false; \
43 }} while (0)
45 static struct {
46 struct smb2_handle handle;
47 uint8_t level;
48 struct smb2_break br;
49 int count;
50 int failures;
51 NTSTATUS failure_status;
52 } break_info;
54 static void torture_oplock_break_callback(struct smb2_request *req)
56 NTSTATUS status;
57 struct smb2_break br;
59 ZERO_STRUCT(br);
60 status = smb2_break_recv(req, &break_info.br);
61 if (!NT_STATUS_IS_OK(status)) {
62 break_info.failures++;
63 break_info.failure_status = status;
66 return;
69 /* A general oplock break notification handler. This should be used when a
70 * test expects to break from batch or exclusive to a lower level. */
71 static bool torture_oplock_handler(struct smb2_transport *transport,
72 const struct smb2_handle *handle,
73 uint8_t level,
74 void *private_data)
76 struct smb2_tree *tree = private_data;
77 const char *name;
78 struct smb2_request *req;
79 ZERO_STRUCT(break_info.br);
81 break_info.handle = *handle;
82 break_info.level = level;
83 break_info.count++;
85 switch (level) {
86 case SMB2_OPLOCK_LEVEL_II:
87 name = "level II";
88 break;
89 case SMB2_OPLOCK_LEVEL_NONE:
90 name = "none";
91 break;
92 default:
93 name = "unknown";
94 break_info.failures++;
96 printf("Acking to %s [0x%02X] in oplock handler\n", name, level);
98 break_info.br.in.file.handle = *handle;
99 break_info.br.in.oplock_level = level;
100 break_info.br.in.reserved = 0;
101 break_info.br.in.reserved2 = 0;
103 req = smb2_break_send(tree, &break_info.br);
104 req->async.fn = torture_oplock_break_callback;
105 req->async.private_data = NULL;
106 return true;
109 static bool test_compound_break(struct torture_context *tctx,
110 struct smb2_tree *tree)
112 const char *fname1 = "some-file.pptx";
113 NTSTATUS status;
114 bool ret = true;
115 union smb_open io1;
116 struct smb2_create io2;
117 struct smb2_getinfo gf;
118 struct smb2_request *req[2];
119 struct smb2_handle h1;
120 struct smb2_handle h;
122 tree->session->transport->oplock.handler = torture_oplock_handler;
123 tree->session->transport->oplock.private_data = tree;
125 ZERO_STRUCT(break_info);
128 base ntcreatex parms
130 ZERO_STRUCT(io1.smb2);
131 io1.generic.level = RAW_OPEN_SMB2;
132 io1.smb2.in.desired_access = (SEC_STD_SYNCHRONIZE|
133 SEC_STD_READ_CONTROL|
134 SEC_FILE_READ_ATTRIBUTE|
135 SEC_FILE_READ_EA|
136 SEC_FILE_READ_DATA);
137 io1.smb2.in.alloc_size = 0;
138 io1.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
139 io1.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ|
140 NTCREATEX_SHARE_ACCESS_WRITE|
141 NTCREATEX_SHARE_ACCESS_DELETE;
142 io1.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
143 io1.smb2.in.create_options = 0;
144 io1.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
145 io1.smb2.in.security_flags = 0;
146 io1.smb2.in.fname = fname1;
148 torture_comment(tctx, "TEST2: open a file with an batch "
149 "oplock (share mode: all)\n");
150 io1.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH;
152 status = smb2_create(tree, tctx, &(io1.smb2));
153 torture_assert_ntstatus_ok(tctx, status, "Error opening the file");
155 h1 = io1.smb2.out.file.handle;
157 torture_comment(tctx, "TEST2: Opening second time with compound\n");
159 ZERO_STRUCT(io2);
161 io2.in.desired_access = (SEC_STD_SYNCHRONIZE|
162 SEC_FILE_READ_ATTRIBUTE|
163 SEC_FILE_READ_EA);
164 io2.in.alloc_size = 0;
165 io2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
166 io2.in.share_access = NTCREATEX_SHARE_ACCESS_READ|
167 NTCREATEX_SHARE_ACCESS_WRITE|
168 NTCREATEX_SHARE_ACCESS_DELETE;
169 io2.in.create_disposition = NTCREATEX_DISP_OPEN;
170 io2.in.create_options = 0;
171 io2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
172 io2.in.security_flags = 0;
173 io2.in.fname = fname1;
174 io2.in.oplock_level = 0;
176 smb2_transport_compound_start(tree->session->transport, 2);
178 req[0] = smb2_create_send(tree, &io2);
180 smb2_transport_compound_set_related(tree->session->transport, true);
182 h.data[0] = UINT64_MAX;
183 h.data[1] = UINT64_MAX;
185 ZERO_STRUCT(gf);
186 gf.in.file.handle = h;
187 gf.in.info_type = SMB2_GETINFO_FILE;
188 gf.in.info_class = 0x16;
189 gf.in.output_buffer_length = 0x1000;
190 gf.in.input_buffer_length = 0;
192 req[1] = smb2_getinfo_send(tree, &gf);
194 status = smb2_create_recv(req[0], tree, &io2);
195 CHECK_STATUS(status, NT_STATUS_OK);
197 status = smb2_getinfo_recv(req[1], tree, &gf);
198 CHECK_STATUS(status, NT_STATUS_OK);
200 done:
202 smb2_util_close(tree, h1);
203 smb2_util_unlink(tree, fname1);
204 return ret;
207 static bool test_compound_related1(struct torture_context *tctx,
208 struct smb2_tree *tree)
210 struct smb2_handle hd;
211 struct smb2_create cr;
212 NTSTATUS status;
213 const char *fname = "compound_related1.dat";
214 struct smb2_close cl;
215 bool ret = true;
216 struct smb2_request *req[2];
217 struct smbXcli_tcon *saved_tcon = tree->smbXcli;
218 struct smbXcli_session *saved_session = tree->session->smbXcli;
220 smb2_transport_credits_ask_num(tree->session->transport, 2);
222 smb2_util_unlink(tree, fname);
224 smb2_transport_credits_ask_num(tree->session->transport, 1);
226 ZERO_STRUCT(cr);
227 cr.in.security_flags = 0x00;
228 cr.in.oplock_level = 0;
229 cr.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION;
230 cr.in.create_flags = 0x00000000;
231 cr.in.reserved = 0x00000000;
232 cr.in.desired_access = SEC_RIGHTS_FILE_ALL;
233 cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
234 cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
235 NTCREATEX_SHARE_ACCESS_WRITE |
236 NTCREATEX_SHARE_ACCESS_DELETE;
237 cr.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
238 cr.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
239 NTCREATEX_OPTIONS_ASYNC_ALERT |
240 NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
241 0x00200000;
242 cr.in.fname = fname;
244 smb2_transport_compound_start(tree->session->transport, 2);
246 req[0] = smb2_create_send(tree, &cr);
248 smb2_transport_compound_set_related(tree->session->transport, true);
250 hd.data[0] = UINT64_MAX;
251 hd.data[1] = UINT64_MAX;
253 ZERO_STRUCT(cl);
254 cl.in.file.handle = hd;
256 tree->smbXcli = smbXcli_tcon_create(tree);
257 smb2cli_tcon_set_values(tree->smbXcli,
258 NULL, /* session */
259 0xFFFFFFFF, /* tcon_id */
260 0, /* type */
261 0, /* flags */
262 0, /* capabilities */
263 0 /* maximal_access */);
265 tree->session->smbXcli = smbXcli_session_copy(tree->session,
266 tree->session->smbXcli);
267 smb2cli_session_set_id_and_flags(tree->session->smbXcli, UINT64_MAX, 0);
269 req[1] = smb2_close_send(tree, &cl);
271 status = smb2_create_recv(req[0], tree, &cr);
272 CHECK_STATUS(status, NT_STATUS_OK);
273 status = smb2_close_recv(req[1], &cl);
274 CHECK_STATUS(status, NT_STATUS_OK);
276 TALLOC_FREE(tree->smbXcli);
277 tree->smbXcli = saved_tcon;
278 TALLOC_FREE(tree->session->smbXcli);
279 tree->session->smbXcli = saved_session;
281 smb2_util_unlink(tree, fname);
282 done:
283 return ret;
286 static bool test_compound_related2(struct torture_context *tctx,
287 struct smb2_tree *tree)
289 struct smb2_handle hd;
290 struct smb2_create cr;
291 NTSTATUS status;
292 const char *fname = "compound_related2.dat";
293 struct smb2_close cl;
294 bool ret = true;
295 struct smb2_request *req[5];
296 struct smbXcli_tcon *saved_tcon = tree->smbXcli;
297 struct smbXcli_session *saved_session = tree->session->smbXcli;
299 smb2_transport_credits_ask_num(tree->session->transport, 5);
301 smb2_util_unlink(tree, fname);
303 smb2_transport_credits_ask_num(tree->session->transport, 1);
305 ZERO_STRUCT(cr);
306 cr.in.security_flags = 0x00;
307 cr.in.oplock_level = 0;
308 cr.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION;
309 cr.in.create_flags = 0x00000000;
310 cr.in.reserved = 0x00000000;
311 cr.in.desired_access = SEC_RIGHTS_FILE_ALL;
312 cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
313 cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
314 NTCREATEX_SHARE_ACCESS_WRITE |
315 NTCREATEX_SHARE_ACCESS_DELETE;
316 cr.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
317 cr.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
318 NTCREATEX_OPTIONS_ASYNC_ALERT |
319 NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
320 0x00200000;
321 cr.in.fname = fname;
323 smb2_transport_compound_start(tree->session->transport, 5);
325 req[0] = smb2_create_send(tree, &cr);
327 hd.data[0] = UINT64_MAX;
328 hd.data[1] = UINT64_MAX;
330 smb2_transport_compound_set_related(tree->session->transport, true);
332 ZERO_STRUCT(cl);
333 cl.in.file.handle = hd;
335 tree->smbXcli = smbXcli_tcon_create(tree);
336 smb2cli_tcon_set_values(tree->smbXcli,
337 NULL, /* session */
338 0xFFFFFFFF, /* tcon_id */
339 0, /* type */
340 0, /* flags */
341 0, /* capabilities */
342 0 /* maximal_access */);
344 tree->session->smbXcli = smbXcli_session_copy(tree->session,
345 tree->session->smbXcli);
346 smb2cli_session_set_id_and_flags(tree->session->smbXcli, UINT64_MAX, 0);
348 req[1] = smb2_close_send(tree, &cl);
349 req[2] = smb2_close_send(tree, &cl);
350 req[3] = smb2_close_send(tree, &cl);
351 req[4] = smb2_close_send(tree, &cl);
353 status = smb2_create_recv(req[0], tree, &cr);
354 CHECK_STATUS(status, NT_STATUS_OK);
355 status = smb2_close_recv(req[1], &cl);
356 CHECK_STATUS(status, NT_STATUS_OK);
357 status = smb2_close_recv(req[2], &cl);
358 CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
359 status = smb2_close_recv(req[3], &cl);
360 CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
361 status = smb2_close_recv(req[4], &cl);
362 CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
364 TALLOC_FREE(tree->smbXcli);
365 tree->smbXcli = saved_tcon;
366 TALLOC_FREE(tree->session->smbXcli);
367 tree->session->smbXcli = saved_session;
369 smb2_util_unlink(tree, fname);
370 done:
371 return ret;
374 static bool test_compound_related3(struct torture_context *tctx,
375 struct smb2_tree *tree)
377 struct smb2_handle hd;
378 struct smb2_ioctl io;
379 struct smb2_create cr;
380 struct smb2_close cl;
381 const char *fname = "compound_related3.dat";
382 struct smb2_request *req[3];
383 NTSTATUS status;
384 bool ret = false;
386 smb2_util_unlink(tree, fname);
388 ZERO_STRUCT(cr);
389 cr.in.security_flags = 0x00;
390 cr.in.oplock_level = 0;
391 cr.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION;
392 cr.in.create_flags = 0x00000000;
393 cr.in.reserved = 0x00000000;
394 cr.in.desired_access = SEC_RIGHTS_FILE_ALL;
395 cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
396 cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
397 NTCREATEX_SHARE_ACCESS_WRITE |
398 NTCREATEX_SHARE_ACCESS_DELETE;
399 cr.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
400 cr.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
401 NTCREATEX_OPTIONS_ASYNC_ALERT |
402 NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
403 0x00200000;
404 cr.in.fname = fname;
406 smb2_transport_compound_start(tree->session->transport, 3);
408 req[0] = smb2_create_send(tree, &cr);
410 hd.data[0] = UINT64_MAX;
411 hd.data[1] = UINT64_MAX;
413 smb2_transport_compound_set_related(tree->session->transport, true);
415 ZERO_STRUCT(io);
416 io.in.function = FSCTL_CREATE_OR_GET_OBJECT_ID;
417 io.in.file.handle = hd;
418 io.in.unknown2 = 0;
419 io.in.max_response_size = 64;
420 io.in.flags = 1;
422 req[1] = smb2_ioctl_send(tree, &io);
424 ZERO_STRUCT(cl);
425 cl.in.file.handle = hd;
427 req[2] = smb2_close_send(tree, &cl);
429 status = smb2_create_recv(req[0], tree, &cr);
430 CHECK_STATUS(status, NT_STATUS_OK);
431 status = smb2_ioctl_recv(req[1], tree, &io);
432 CHECK_STATUS(status, NT_STATUS_OK);
433 status = smb2_close_recv(req[2], &cl);
434 CHECK_STATUS(status, NT_STATUS_OK);
436 status = smb2_util_unlink(tree, fname);
437 CHECK_STATUS(status, NT_STATUS_OK);
439 ret = true;
440 done:
441 return ret;
444 static bool test_compound_padding(struct torture_context *tctx,
445 struct smb2_tree *tree)
447 struct smb2_handle h;
448 struct smb2_create cr;
449 struct smb2_read r;
450 const char *fname = "compound_read.dat";
451 const char *sname = "compound_read.dat:foo";
452 struct smb2_request *req[3];
453 NTSTATUS status;
454 bool ret = false;
456 smb2_util_unlink(tree, fname);
458 /* Write file */
459 ZERO_STRUCT(cr);
460 cr.in.desired_access = SEC_FILE_WRITE_DATA;
461 cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
462 cr.in.create_disposition = NTCREATEX_DISP_CREATE;
463 cr.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
464 cr.in.fname = fname;
465 cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ|
466 NTCREATEX_SHARE_ACCESS_WRITE|
467 NTCREATEX_SHARE_ACCESS_DELETE;
468 status = smb2_create(tree, tctx, &cr);
469 CHECK_STATUS(status, NT_STATUS_OK);
470 h = cr.out.file.handle;
472 status = smb2_util_write(tree, h, "123", 0, 3);
473 CHECK_STATUS(status, NT_STATUS_OK);
475 smb2_util_close(tree, h);
477 /* Write stream */
478 ZERO_STRUCT(cr);
479 cr.in.desired_access = SEC_FILE_WRITE_DATA;
480 cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
481 cr.in.create_disposition = NTCREATEX_DISP_CREATE;
482 cr.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
483 cr.in.fname = sname;
484 cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ|
485 NTCREATEX_SHARE_ACCESS_WRITE|
486 NTCREATEX_SHARE_ACCESS_DELETE;
487 status = smb2_create(tree, tctx, &cr);
488 CHECK_STATUS(status, NT_STATUS_OK);
489 h = cr.out.file.handle;
491 status = smb2_util_write(tree, h, "456", 0, 3);
492 CHECK_STATUS(status, NT_STATUS_OK);
494 smb2_util_close(tree, h);
496 /* Check compound read from basefile */
497 smb2_transport_compound_start(tree->session->transport, 2);
499 ZERO_STRUCT(cr);
500 cr.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
501 cr.in.desired_access = SEC_FILE_READ_DATA;
502 cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
503 cr.in.create_disposition = NTCREATEX_DISP_OPEN;
504 cr.in.fname = fname;
505 cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ|
506 NTCREATEX_SHARE_ACCESS_WRITE|
507 NTCREATEX_SHARE_ACCESS_DELETE;
508 req[0] = smb2_create_send(tree, &cr);
510 smb2_transport_compound_set_related(tree->session->transport, true);
512 ZERO_STRUCT(r);
513 h.data[0] = UINT64_MAX;
514 h.data[1] = UINT64_MAX;
515 r.in.file.handle = h;
516 r.in.length = 3;
517 r.in.offset = 0;
518 r.in.min_count = 1;
519 req[1] = smb2_read_send(tree, &r);
521 status = smb2_create_recv(req[0], tree, &cr);
522 CHECK_STATUS(status, NT_STATUS_OK);
525 * We must do a manual smb2_request_receive() in order to be
526 * able to check the transport layer info, as smb2_read_recv()
527 * will destroy the req. smb2_read_recv() will call
528 * smb2_request_receive() again, but that's ok.
530 if (!smb2_request_receive(req[1]) ||
531 !smb2_request_is_ok(req[1])) {
532 torture_fail(tctx, "failed to receive read request");
536 * size must be 24: 16 byte read response header plus 3
537 * requested bytes padded to an 8 byte boundary.
539 CHECK_VALUE(req[1]->in.body_size, 24);
541 status = smb2_read_recv(req[1], tree, &r);
542 CHECK_STATUS(status, NT_STATUS_OK);
544 smb2_util_close(tree, cr.out.file.handle);
546 /* Check compound read from stream */
547 smb2_transport_compound_start(tree->session->transport, 2);
549 ZERO_STRUCT(cr);
550 cr.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
551 cr.in.desired_access = SEC_FILE_READ_DATA;
552 cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
553 cr.in.create_disposition = NTCREATEX_DISP_OPEN;
554 cr.in.fname = sname;
555 cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ|
556 NTCREATEX_SHARE_ACCESS_WRITE|
557 NTCREATEX_SHARE_ACCESS_DELETE;
558 req[0] = smb2_create_send(tree, &cr);
560 smb2_transport_compound_set_related(tree->session->transport, true);
562 ZERO_STRUCT(r);
563 h.data[0] = UINT64_MAX;
564 h.data[1] = UINT64_MAX;
565 r.in.file.handle = h;
566 r.in.length = 3;
567 r.in.offset = 0;
568 r.in.min_count = 1;
569 req[1] = smb2_read_send(tree, &r);
571 status = smb2_create_recv(req[0], tree, &cr);
572 CHECK_STATUS(status, NT_STATUS_OK);
575 * We must do a manual smb2_request_receive() in order to be
576 * able to check the transport layer info, as smb2_read_recv()
577 * will destroy the req. smb2_read_recv() will call
578 * smb2_request_receive() again, but that's ok.
580 if (!smb2_request_receive(req[1]) ||
581 !smb2_request_is_ok(req[1])) {
582 torture_fail(tctx, "failed to receive read request");
586 * size must be 24: 16 byte read response header plus 3
587 * requested bytes padded to an 8 byte boundary.
589 CHECK_VALUE(req[1]->in.body_size, 24);
591 status = smb2_read_recv(req[1], tree, &r);
592 CHECK_STATUS(status, NT_STATUS_OK);
594 h = cr.out.file.handle;
596 /* Check 2 compound (unrelateated) reads from existing stream handle */
597 smb2_transport_compound_start(tree->session->transport, 2);
599 ZERO_STRUCT(r);
600 r.in.file.handle = h;
601 r.in.length = 3;
602 r.in.offset = 0;
603 r.in.min_count = 1;
604 req[0] = smb2_read_send(tree, &r);
605 req[1] = smb2_read_send(tree, &r);
608 * We must do a manual smb2_request_receive() in order to be
609 * able to check the transport layer info, as smb2_read_recv()
610 * will destroy the req. smb2_read_recv() will call
611 * smb2_request_receive() again, but that's ok.
613 if (!smb2_request_receive(req[0]) ||
614 !smb2_request_is_ok(req[0])) {
615 torture_fail(tctx, "failed to receive read request");
617 if (!smb2_request_receive(req[1]) ||
618 !smb2_request_is_ok(req[1])) {
619 torture_fail(tctx, "failed to receive read request");
623 * size must be 24: 16 byte read response header plus 3
624 * requested bytes padded to an 8 byte boundary.
626 CHECK_VALUE(req[0]->in.body_size, 24);
627 CHECK_VALUE(req[1]->in.body_size, 24);
629 status = smb2_read_recv(req[0], tree, &r);
630 CHECK_STATUS(status, NT_STATUS_OK);
631 status = smb2_read_recv(req[1], tree, &r);
632 CHECK_STATUS(status, NT_STATUS_OK);
635 * now try a single read from the stream and verify there's no padding
637 ZERO_STRUCT(r);
638 r.in.file.handle = h;
639 r.in.length = 3;
640 r.in.offset = 0;
641 r.in.min_count = 1;
642 req[0] = smb2_read_send(tree, &r);
645 * We must do a manual smb2_request_receive() in order to be
646 * able to check the transport layer info, as smb2_read_recv()
647 * will destroy the req. smb2_read_recv() will call
648 * smb2_request_receive() again, but that's ok.
650 if (!smb2_request_receive(req[0]) ||
651 !smb2_request_is_ok(req[0])) {
652 torture_fail(tctx, "failed to receive read request");
656 * size must be 19: 16 byte read response header plus 3
657 * requested bytes without padding.
659 CHECK_VALUE(req[0]->in.body_size, 19);
661 status = smb2_read_recv(req[0], tree, &r);
662 CHECK_STATUS(status, NT_STATUS_OK);
664 smb2_util_close(tree, h);
666 status = smb2_util_unlink(tree, fname);
667 CHECK_STATUS(status, NT_STATUS_OK);
669 ret = true;
670 done:
671 return ret;
674 static bool test_compound_unrelated1(struct torture_context *tctx,
675 struct smb2_tree *tree)
677 struct smb2_handle hd;
678 struct smb2_create cr;
679 NTSTATUS status;
680 const char *fname = "compound_unrelated1.dat";
681 struct smb2_close cl;
682 bool ret = true;
683 struct smb2_request *req[5];
685 smb2_transport_credits_ask_num(tree->session->transport, 5);
687 smb2_util_unlink(tree, fname);
689 smb2_transport_credits_ask_num(tree->session->transport, 1);
691 ZERO_STRUCT(cr);
692 cr.in.security_flags = 0x00;
693 cr.in.oplock_level = 0;
694 cr.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION;
695 cr.in.create_flags = 0x00000000;
696 cr.in.reserved = 0x00000000;
697 cr.in.desired_access = SEC_RIGHTS_FILE_ALL;
698 cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
699 cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
700 NTCREATEX_SHARE_ACCESS_WRITE |
701 NTCREATEX_SHARE_ACCESS_DELETE;
702 cr.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
703 cr.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
704 NTCREATEX_OPTIONS_ASYNC_ALERT |
705 NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
706 0x00200000;
707 cr.in.fname = fname;
709 smb2_transport_compound_start(tree->session->transport, 5);
711 req[0] = smb2_create_send(tree, &cr);
713 hd.data[0] = UINT64_MAX;
714 hd.data[1] = UINT64_MAX;
716 ZERO_STRUCT(cl);
717 cl.in.file.handle = hd;
718 req[1] = smb2_close_send(tree, &cl);
719 req[2] = smb2_close_send(tree, &cl);
720 req[3] = smb2_close_send(tree, &cl);
721 req[4] = smb2_close_send(tree, &cl);
723 status = smb2_create_recv(req[0], tree, &cr);
724 CHECK_STATUS(status, NT_STATUS_OK);
725 status = smb2_close_recv(req[1], &cl);
726 CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
727 status = smb2_close_recv(req[2], &cl);
728 CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
729 status = smb2_close_recv(req[3], &cl);
730 CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
731 status = smb2_close_recv(req[4], &cl);
732 CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
734 smb2_util_unlink(tree, fname);
735 done:
736 return ret;
739 static bool test_compound_invalid1(struct torture_context *tctx,
740 struct smb2_tree *tree)
742 struct smb2_handle hd;
743 struct smb2_create cr;
744 NTSTATUS status;
745 const char *fname = "compound_invalid1.dat";
746 struct smb2_close cl;
747 bool ret = true;
748 struct smb2_request *req[3];
750 smb2_transport_credits_ask_num(tree->session->transport, 3);
752 smb2_util_unlink(tree, fname);
754 smb2_transport_credits_ask_num(tree->session->transport, 1);
756 ZERO_STRUCT(cr);
757 cr.in.security_flags = 0x00;
758 cr.in.oplock_level = 0;
759 cr.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION;
760 cr.in.create_flags = 0x00000000;
761 cr.in.reserved = 0x00000000;
762 cr.in.desired_access = SEC_RIGHTS_FILE_ALL;
763 cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
764 cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
765 NTCREATEX_SHARE_ACCESS_WRITE |
766 NTCREATEX_SHARE_ACCESS_DELETE;
767 cr.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
768 cr.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
769 NTCREATEX_OPTIONS_ASYNC_ALERT |
770 NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
771 0x00200000;
772 cr.in.fname = fname;
774 smb2_transport_compound_start(tree->session->transport, 3);
776 /* passing the first request with the related flag is invalid */
777 smb2_transport_compound_set_related(tree->session->transport, true);
779 req[0] = smb2_create_send(tree, &cr);
781 hd.data[0] = UINT64_MAX;
782 hd.data[1] = UINT64_MAX;
784 ZERO_STRUCT(cl);
785 cl.in.file.handle = hd;
786 req[1] = smb2_close_send(tree, &cl);
788 smb2_transport_compound_set_related(tree->session->transport, false);
789 req[2] = smb2_close_send(tree, &cl);
791 status = smb2_create_recv(req[0], tree, &cr);
792 /* TODO: check why this fails with --signing=required */
793 CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
794 status = smb2_close_recv(req[1], &cl);
795 CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
796 status = smb2_close_recv(req[2], &cl);
797 CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
799 smb2_util_unlink(tree, fname);
800 done:
801 return ret;
804 static bool test_compound_invalid2(struct torture_context *tctx,
805 struct smb2_tree *tree)
807 struct smb2_handle hd;
808 struct smb2_create cr;
809 NTSTATUS status;
810 const char *fname = "compound_invalid2.dat";
811 struct smb2_close cl;
812 bool ret = true;
813 struct smb2_request *req[5];
814 struct smbXcli_tcon *saved_tcon = tree->smbXcli;
815 struct smbXcli_session *saved_session = tree->session->smbXcli;
817 smb2_transport_credits_ask_num(tree->session->transport, 5);
819 smb2_util_unlink(tree, fname);
821 smb2_transport_credits_ask_num(tree->session->transport, 1);
823 ZERO_STRUCT(cr);
824 cr.in.security_flags = 0x00;
825 cr.in.oplock_level = 0;
826 cr.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION;
827 cr.in.create_flags = 0x00000000;
828 cr.in.reserved = 0x00000000;
829 cr.in.desired_access = SEC_RIGHTS_FILE_ALL;
830 cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
831 cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
832 NTCREATEX_SHARE_ACCESS_WRITE |
833 NTCREATEX_SHARE_ACCESS_DELETE;
834 cr.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
835 cr.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
836 NTCREATEX_OPTIONS_ASYNC_ALERT |
837 NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
838 0x00200000;
839 cr.in.fname = fname;
841 smb2_transport_compound_start(tree->session->transport, 5);
843 req[0] = smb2_create_send(tree, &cr);
845 hd.data[0] = UINT64_MAX;
846 hd.data[1] = UINT64_MAX;
848 smb2_transport_compound_set_related(tree->session->transport, true);
850 ZERO_STRUCT(cl);
851 cl.in.file.handle = hd;
853 tree->smbXcli = smbXcli_tcon_create(tree);
854 smb2cli_tcon_set_values(tree->smbXcli,
855 NULL, /* session */
856 0xFFFFFFFF, /* tcon_id */
857 0, /* type */
858 0, /* flags */
859 0, /* capabilities */
860 0 /* maximal_access */);
862 tree->session->smbXcli = smbXcli_session_copy(tree->session,
863 tree->session->smbXcli);
864 smb2cli_session_set_id_and_flags(tree->session->smbXcli, UINT64_MAX, 0);
866 req[1] = smb2_close_send(tree, &cl);
867 /* strange that this is not generating invalid parameter */
868 smb2_transport_compound_set_related(tree->session->transport, false);
869 req[2] = smb2_close_send(tree, &cl);
870 req[3] = smb2_close_send(tree, &cl);
871 smb2_transport_compound_set_related(tree->session->transport, true);
872 req[4] = smb2_close_send(tree, &cl);
874 status = smb2_create_recv(req[0], tree, &cr);
875 CHECK_STATUS(status, NT_STATUS_OK);
876 status = smb2_close_recv(req[1], &cl);
877 CHECK_STATUS(status, NT_STATUS_OK);
878 status = smb2_close_recv(req[2], &cl);
879 CHECK_STATUS(status, NT_STATUS_USER_SESSION_DELETED);
880 status = smb2_close_recv(req[3], &cl);
881 CHECK_STATUS(status, NT_STATUS_USER_SESSION_DELETED);
882 status = smb2_close_recv(req[4], &cl);
883 CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
885 TALLOC_FREE(tree->smbXcli);
886 tree->smbXcli = saved_tcon;
887 TALLOC_FREE(tree->session->smbXcli);
888 tree->session->smbXcli = saved_session;
890 smb2_util_unlink(tree, fname);
891 done:
892 return ret;
895 static bool test_compound_invalid3(struct torture_context *tctx,
896 struct smb2_tree *tree)
898 struct smb2_handle hd;
899 struct smb2_create cr;
900 NTSTATUS status;
901 const char *fname = "compound_invalid3.dat";
902 struct smb2_close cl;
903 bool ret = true;
904 struct smb2_request *req[5];
906 smb2_transport_credits_ask_num(tree->session->transport, 5);
908 smb2_util_unlink(tree, fname);
910 smb2_transport_credits_ask_num(tree->session->transport, 1);
912 ZERO_STRUCT(cr);
913 cr.in.security_flags = 0x00;
914 cr.in.oplock_level = 0;
915 cr.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION;
916 cr.in.create_flags = 0x00000000;
917 cr.in.reserved = 0x00000000;
918 cr.in.desired_access = SEC_RIGHTS_FILE_ALL;
919 cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
920 cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
921 NTCREATEX_SHARE_ACCESS_WRITE |
922 NTCREATEX_SHARE_ACCESS_DELETE;
923 cr.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
924 cr.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
925 NTCREATEX_OPTIONS_ASYNC_ALERT |
926 NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
927 0x00200000;
928 cr.in.fname = fname;
930 smb2_transport_compound_start(tree->session->transport, 5);
932 req[0] = smb2_create_send(tree, &cr);
934 hd.data[0] = UINT64_MAX;
935 hd.data[1] = UINT64_MAX;
937 ZERO_STRUCT(cl);
938 cl.in.file.handle = hd;
939 req[1] = smb2_close_send(tree, &cl);
940 req[2] = smb2_close_send(tree, &cl);
941 /* flipping the related flag is invalid */
942 smb2_transport_compound_set_related(tree->session->transport, true);
943 req[3] = smb2_close_send(tree, &cl);
944 req[4] = smb2_close_send(tree, &cl);
946 status = smb2_create_recv(req[0], tree, &cr);
947 CHECK_STATUS(status, NT_STATUS_OK);
948 status = smb2_close_recv(req[1], &cl);
949 CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
950 status = smb2_close_recv(req[2], &cl);
951 CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
952 status = smb2_close_recv(req[3], &cl);
953 CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
954 status = smb2_close_recv(req[4], &cl);
955 CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
957 smb2_util_unlink(tree, fname);
958 done:
959 return ret;
962 /* Send a compound request where we expect the last request (Create, Notify)
963 * to go asynchronous. This works against a Win7 server and the reply is
964 * sent in two different packets. */
965 static bool test_compound_interim1(struct torture_context *tctx,
966 struct smb2_tree *tree)
968 struct smb2_handle hd;
969 struct smb2_create cr;
970 NTSTATUS status = NT_STATUS_OK;
971 const char *dname = "compound_interim_dir";
972 struct smb2_notify nt;
973 bool ret = true;
974 struct smb2_request *req[2];
976 /* Win7 compound request implementation deviates substantially from the
977 * SMB2 spec as noted in MS-SMB2 <159>, <162>. This, test currently
978 * verifies the Windows behavior, not the general spec behavior. */
980 smb2_transport_credits_ask_num(tree->session->transport, 5);
982 smb2_deltree(tree, dname);
984 smb2_transport_credits_ask_num(tree->session->transport, 1);
986 ZERO_STRUCT(cr);
987 cr.in.desired_access = SEC_RIGHTS_FILE_ALL;
988 cr.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
989 cr.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
990 cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
991 NTCREATEX_SHARE_ACCESS_WRITE |
992 NTCREATEX_SHARE_ACCESS_DELETE;
993 cr.in.create_disposition = NTCREATEX_DISP_CREATE;
994 cr.in.fname = dname;
996 smb2_transport_compound_start(tree->session->transport, 2);
998 req[0] = smb2_create_send(tree, &cr);
1000 smb2_transport_compound_set_related(tree->session->transport, true);
1002 hd.data[0] = UINT64_MAX;
1003 hd.data[1] = UINT64_MAX;
1005 ZERO_STRUCT(nt);
1006 nt.in.recursive = true;
1007 nt.in.buffer_size = 0x1000;
1008 nt.in.file.handle = hd;
1009 nt.in.completion_filter = FILE_NOTIFY_CHANGE_NAME;
1010 nt.in.unknown = 0x00000000;
1012 req[1] = smb2_notify_send(tree, &nt);
1014 status = smb2_create_recv(req[0], tree, &cr);
1015 CHECK_STATUS(status, NT_STATUS_OK);
1017 smb2_cancel(req[1]);
1018 status = smb2_notify_recv(req[1], tree, &nt);
1019 CHECK_STATUS(status, NT_STATUS_CANCELLED);
1021 smb2_util_close(tree, cr.out.file.handle);
1023 smb2_deltree(tree, dname);
1024 done:
1025 return ret;
1028 /* Send a compound request where we expect the middle request (Create, Notify,
1029 * GetInfo) to go asynchronous. Against Win7 the sync request succeed while
1030 * the async fails. All are returned in the same compound response. */
1031 static bool test_compound_interim2(struct torture_context *tctx,
1032 struct smb2_tree *tree)
1034 struct smb2_handle hd;
1035 struct smb2_create cr;
1036 NTSTATUS status = NT_STATUS_OK;
1037 const char *dname = "compound_interim_dir";
1038 struct smb2_getinfo gf;
1039 struct smb2_notify nt;
1040 bool ret = true;
1041 struct smb2_request *req[3];
1043 /* Win7 compound request implementation deviates substantially from the
1044 * SMB2 spec as noted in MS-SMB2 <159>, <162>. This, test currently
1045 * verifies the Windows behavior, not the general spec behavior. */
1047 smb2_transport_credits_ask_num(tree->session->transport, 5);
1049 smb2_deltree(tree, dname);
1051 smb2_transport_credits_ask_num(tree->session->transport, 1);
1053 ZERO_STRUCT(cr);
1054 cr.in.desired_access = SEC_RIGHTS_FILE_ALL;
1055 cr.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
1056 cr.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
1057 cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
1058 NTCREATEX_SHARE_ACCESS_WRITE |
1059 NTCREATEX_SHARE_ACCESS_DELETE;
1060 cr.in.create_disposition = NTCREATEX_DISP_CREATE;
1061 cr.in.fname = dname;
1063 smb2_transport_compound_start(tree->session->transport, 3);
1065 req[0] = smb2_create_send(tree, &cr);
1067 smb2_transport_compound_set_related(tree->session->transport, true);
1069 hd.data[0] = UINT64_MAX;
1070 hd.data[1] = UINT64_MAX;
1072 ZERO_STRUCT(nt);
1073 nt.in.recursive = true;
1074 nt.in.buffer_size = 0x1000;
1075 nt.in.file.handle = hd;
1076 nt.in.completion_filter = FILE_NOTIFY_CHANGE_NAME;
1077 nt.in.unknown = 0x00000000;
1079 req[1] = smb2_notify_send(tree, &nt);
1081 ZERO_STRUCT(gf);
1082 gf.in.file.handle = hd;
1083 gf.in.info_type = SMB2_GETINFO_FILE;
1084 gf.in.info_class = 0x04; /* FILE_BASIC_INFORMATION */
1085 gf.in.output_buffer_length = 0x1000;
1086 gf.in.input_buffer_length = 0;
1088 req[2] = smb2_getinfo_send(tree, &gf);
1090 status = smb2_create_recv(req[0], tree, &cr);
1091 CHECK_STATUS(status, NT_STATUS_OK);
1093 status = smb2_notify_recv(req[1], tree, &nt);
1094 CHECK_STATUS(status, NT_STATUS_INTERNAL_ERROR);
1096 status = smb2_getinfo_recv(req[2], tree, &gf);
1097 CHECK_STATUS(status, NT_STATUS_OK);
1099 smb2_util_close(tree, cr.out.file.handle);
1101 smb2_deltree(tree, dname);
1102 done:
1103 return ret;
1106 struct torture_suite *torture_smb2_compound_init(void)
1108 struct torture_suite *suite = torture_suite_create(talloc_autofree_context(), "compound");
1110 torture_suite_add_1smb2_test(suite, "related1", test_compound_related1);
1111 torture_suite_add_1smb2_test(suite, "related2", test_compound_related2);
1112 torture_suite_add_1smb2_test(suite, "related3",
1113 test_compound_related3);
1114 torture_suite_add_1smb2_test(suite, "unrelated1", test_compound_unrelated1);
1115 torture_suite_add_1smb2_test(suite, "invalid1", test_compound_invalid1);
1116 torture_suite_add_1smb2_test(suite, "invalid2", test_compound_invalid2);
1117 torture_suite_add_1smb2_test(suite, "invalid3", test_compound_invalid3);
1118 torture_suite_add_1smb2_test(suite, "interim1", test_compound_interim1);
1119 torture_suite_add_1smb2_test(suite, "interim2", test_compound_interim2);
1120 torture_suite_add_1smb2_test(suite, "compound-break", test_compound_break);
1121 torture_suite_add_1smb2_test(suite, "compound-padding", test_compound_padding);
1123 suite->description = talloc_strdup(suite, "SMB2-COMPOUND tests");
1125 return suite;