2 * Unix SMB/CIFS implementation.
4 * test SMB2 multichannel operations
6 * Copyright (C) Guenther Deschner, 2016
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/>.
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/security/security.h"
28 #include "librpc/gen_ndr/ndr_security.h"
29 #include "librpc/gen_ndr/ndr_ioctl.h"
30 #include "../libcli/smb/smbXcli_base.h"
31 #include "lib/cmdline/popt_common.h"
32 #include "libcli/security/security.h"
33 #include "libcli/resolve/resolve.h"
34 #include "lib/param/param.h"
35 #include "lib/events/events.h"
36 #include "oplock_break_handler.h"
37 #include "lease_break_handler.h"
38 #include "torture/smb2/block.h"
40 #define BASEDIR "multichanneltestdir"
42 #define CHECK_STATUS(status, correct) \
43 torture_assert_ntstatus_equal_goto(tctx, status, correct,\
46 #define CHECK_VAL(v, correct) do { \
47 if ((v) != (correct)) { \
48 torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s" \
49 " got 0x%x - should be 0x%x\n", \
50 __location__, #v, (int)v, (int)correct); \
55 #define CHECK_VAL_GREATER_THAN(v, gt_val) do { \
56 if ((v) <= (gt_val)) { \
57 torture_result(tctx, TORTURE_FAIL, \
58 "(%s): wrong value for %s got 0x%x - " \
59 "should be greater than 0x%x\n", \
60 __location__, #v, (int)v, (int)gt_val); \
65 #define CHECK_CREATED(__io, __created, __attribute) \
67 CHECK_VAL((__io)->out.create_action, \
68 NTCREATEX_ACTION_ ## __created); \
69 CHECK_VAL((__io)->out.alloc_size, 0); \
70 CHECK_VAL((__io)->out.size, 0); \
71 CHECK_VAL((__io)->out.file_attr, (__attribute)); \
72 CHECK_VAL((__io)->out.reserved2, 0); \
75 #define CHECK_PTR(ptr, correct) do { \
76 if ((ptr) != (correct)) { \
77 torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s " \
78 "got 0x%p - should be 0x%p\n", \
79 __location__, #ptr, ptr, correct); \
84 #define CHECK_LEASE(__io, __state, __oplevel, __key, __flags) \
86 CHECK_VAL((__io)->out.lease_response.lease_version, 1); \
88 CHECK_VAL((__io)->out.oplock_level, \
89 SMB2_OPLOCK_LEVEL_LEASE); \
90 CHECK_VAL((__io)->out.lease_response.lease_key.data[0],\
92 CHECK_VAL((__io)->out.lease_response.lease_key.data[1],\
94 CHECK_VAL((__io)->out.lease_response.lease_state,\
95 smb2_util_lease_state(__state)); \
97 CHECK_VAL((__io)->out.oplock_level,\
98 SMB2_OPLOCK_LEVEL_NONE); \
99 CHECK_VAL((__io)->out.lease_response.lease_key.data[0],\
101 CHECK_VAL((__io)->out.lease_response.lease_key.data[1],\
103 CHECK_VAL((__io)->out.lease_response.lease_state, 0); \
106 CHECK_VAL((__io)->out.lease_response.lease_flags, (__flags)); \
107 CHECK_VAL((__io)->out.lease_response.lease_duration, 0); \
108 CHECK_VAL((__io)->out.lease_response.lease_epoch, 0); \
111 static bool test_ioctl_network_interface_info(struct torture_context *tctx,
112 struct smb2_tree *tree,
113 struct fsctl_net_iface_info *info)
115 union smb_ioctl ioctl;
116 struct smb2_handle fh;
119 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
120 if (!(caps & SMB2_CAP_MULTI_CHANNEL)) {
122 "server doesn't support SMB2_CAP_MULTI_CHANNEL\n");
127 ioctl.smb2.level = RAW_IOCTL_SMB2;
129 fh.data[0] = UINT64_MAX;
130 fh.data[1] = UINT64_MAX;
132 ioctl.smb2.in.file.handle = fh;
133 ioctl.smb2.in.function = FSCTL_QUERY_NETWORK_INTERFACE_INFO;
134 /* Windows client sets this to 64KiB */
135 ioctl.smb2.in.max_output_response = 0x10000;
136 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
138 torture_assert_ntstatus_ok(tctx,
139 smb2_ioctl(tree, tctx, &ioctl.smb2),
140 "FSCTL_QUERY_NETWORK_INTERFACE_INFO failed");
143 (ioctl.smb2.out.out.length != 0),
144 "no interface info returned???");
146 torture_assert_ndr_success(tctx,
147 ndr_pull_struct_blob(&ioctl.smb2.out.out, tctx, info,
148 (ndr_pull_flags_fn_t)ndr_pull_fsctl_net_iface_info),
149 "failed to ndr pull");
152 NDR_PRINT_DEBUG(fsctl_net_iface_info, info);
158 static bool test_multichannel_interface_info(struct torture_context *tctx,
159 struct smb2_tree *tree)
161 struct fsctl_net_iface_info info;
163 return test_ioctl_network_interface_info(tctx, tree, &info);
166 static struct smb2_tree *test_multichannel_create_channel(
167 struct torture_context *tctx,
170 struct cli_credentials *credentials,
171 struct smbcli_options *transport_options,
172 struct smb2_tree *parent_tree
176 struct smb2_transport *transport;
177 struct smb2_session *session;
179 struct smb2_tree *tree;
181 status = smb2_connect(tctx,
183 lpcfg_smb_ports(tctx->lp_ctx),
185 lpcfg_resolve_context(tctx->lp_ctx),
190 lpcfg_socket_options(tctx->lp_ctx),
191 lpcfg_gensec_settings(tctx, tctx->lp_ctx)
193 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
194 "smb2_connect failed");
195 transport = tree->session->transport;
196 transport->oplock.handler = torture_oplock_ack_handler;
197 transport->oplock.private_data = tree;
198 transport->lease.handler = torture_lease_handler;
199 transport->lease.private_data = tree;
200 torture_comment(tctx, "established transport [%p]\n", transport);
203 * If parent tree is set, bind the session to the parent transport
206 session = smb2_session_channel(transport,
207 lpcfg_gensec_settings(tctx, tctx->lp_ctx),
208 parent_tree, parent_tree->session);
209 torture_assert_goto(tctx, session != NULL, ret, done,
210 "smb2_session_channel failed");
212 tree->smbXcli = parent_tree->smbXcli;
213 tree->session = session;
214 status = smb2_session_setup_spnego(session,
216 0 /* previous_session_id */);
217 CHECK_STATUS(status, NT_STATUS_OK);
218 torture_comment(tctx, "bound new session to parent\n");
221 * We absolutely need to make sure to send something over this
222 * connection to register the oplock break handler with the smb client
223 * connection. If we do not send something (at least a keepalive), we
224 * will *NEVER* receive anything over this transport.
226 smb2_keepalive(transport);
236 bool test_multichannel_create_channels(
237 struct torture_context *tctx,
240 struct cli_credentials *credentials,
241 struct smbcli_options *transport_options,
242 struct smb2_tree **tree2A,
243 struct smb2_tree **tree2B,
244 struct smb2_tree **tree2C
247 struct smb2_tree *tree;
248 struct smb2_transport *transport2A;
249 struct smb2_transport *transport2B;
250 struct smb2_transport *transport2C;
251 uint16_t local_port = 0;
253 transport_options->client_guid = GUID_random();
256 torture_comment(tctx, "Setting up connection 2A\n");
257 tree = test_multichannel_create_channel(tctx, host, share,
258 credentials, transport_options, NULL);
263 transport2A = tree->session->transport;
264 local_port = torture_get_local_port_from_transport(transport2A);
265 torture_comment(tctx, "transport2A uses tcp port: %d\n", local_port);
269 torture_comment(tctx, "Setting up connection 2B\n");
270 tree = test_multichannel_create_channel(tctx, host, share,
271 credentials, transport_options, *tree2A);
276 transport2B = tree->session->transport;
277 local_port = torture_get_local_port_from_transport(transport2B);
278 torture_comment(tctx, "transport2B uses tcp port: %d\n",
284 torture_comment(tctx, "Setting up connection 2C\n");
285 tree = test_multichannel_create_channel(tctx, host, share,
286 credentials, transport_options, *tree2A);
291 transport2C = tree->session->transport;
292 local_port = torture_get_local_port_from_transport(transport2C);
293 torture_comment(tctx, "transport2C uses tcp port: %d\n",
302 static void test_multichannel_free_channels(struct smb2_tree *tree2A,
303 struct smb2_tree *tree2B,
304 struct smb2_tree *tree2C)
311 static bool test_multichannel_initial_checks(struct torture_context *tctx,
312 struct smb2_tree *tree1)
314 struct smb2_transport *transport1 = tree1->session->transport;
315 uint32_t server_capabilities;
316 struct fsctl_net_iface_info info;
318 if (smbXcli_conn_protocol(transport1->conn) < PROTOCOL_SMB3_00) {
319 torture_skip_goto(tctx, fail,
320 "SMB 3.X Dialect family required for "
321 "Multichannel tests\n");
324 server_capabilities = smb2cli_conn_server_capabilities(
325 tree1->session->transport->conn);
326 if (!(server_capabilities & SMB2_CAP_MULTI_CHANNEL)) {
327 torture_skip_goto(tctx, fail,
328 "Server does not support multichannel.");
332 test_ioctl_network_interface_info(tctx, tree1, &info),
333 "failed to retrieve network interface info");
340 static void test_multichannel_init_smb_create(struct smb2_create *io)
342 io->in.durable_open = false;
343 io->in.durable_open_v2 = true;
344 io->in.persistent_open = false;
345 io->in.create_guid = GUID_random();
346 io->in.timeout = 0x493E0; /* 300000 */
347 /* windows 2016 returns 300000 0x493E0 */
351 * We simulate blocking incoming oplock break requests by simply ignoring
352 * the incoming break requests.
354 static bool test_set_ignore_break_handler(struct torture_context *tctx,
355 struct smb2_transport *transport)
357 transport->oplock.handler = torture_oplock_ignore_handler;
358 transport->lease.handler = torture_lease_ignore_handler;
363 static bool test_reset_break_handler(struct torture_context *tctx,
364 struct smb2_transport *transport)
366 transport->oplock.handler = torture_oplock_ack_handler;
367 transport->lease.handler = torture_lease_handler;
373 * Use iptables to block channels
375 static bool test_iptables_block_channel(struct torture_context *tctx,
376 struct smb2_transport *transport,
382 local_port = torture_get_local_port_from_transport(transport);
383 torture_comment(tctx, "transport uses tcp port: %d\n", local_port);
384 ret = torture_block_tcp_transport_name(tctx, transport, name);
385 torture_assert(tctx, ret, "we could not block tcp transport");
390 static bool test_iptables_unblock_channel(struct torture_context *tctx,
391 struct smb2_transport *transport,
397 local_port = torture_get_local_port_from_transport(transport);
398 torture_comment(tctx, "transport uses tcp port: %d\n", local_port);
399 ret = torture_unblock_tcp_transport_name(tctx, transport, name);
400 torture_assert(tctx, ret, "we could not block tcp transport");
405 #define test_block_channel(_tctx, _t) _test_block_channel(_tctx, _t, #_t)
406 static bool _test_block_channel(struct torture_context *tctx,
407 struct smb2_transport *transport,
410 bool use_iptables = torture_setting_bool(tctx,
411 "use_iptables", false);
414 return test_iptables_block_channel(tctx, transport, name);
416 return test_set_ignore_break_handler(tctx, transport);
420 #define test_unblock_channel(_tctx, _t) _test_unblock_channel(_tctx, _t, #_t)
421 static bool _test_unblock_channel(struct torture_context *tctx,
422 struct smb2_transport *transport,
425 bool use_iptables = torture_setting_bool(tctx,
426 "use_iptables", false);
429 return test_iptables_unblock_channel(tctx, transport, name);
431 return test_reset_break_handler(tctx, transport);
435 static void test_cleanup_blocked_channels(struct torture_context *tctx)
437 bool use_iptables = torture_setting_bool(tctx,
438 "use_iptables", false);
441 torture_unblock_cleanup(tctx);
446 * Oplock break - Test 1
447 * Test to confirm that server sends oplock breaks as expected.
448 * open file1 in session 2A
449 * open file2 in session 2B
450 * open file1 in session 1
451 * oplock break received
452 * open file1 in session 1
453 * oplock break received
456 static bool test_multichannel_oplock_break_test1(struct torture_context *tctx,
457 struct smb2_tree *tree1)
459 const char *host = torture_setting_string(tctx, "host", NULL);
460 const char *share = torture_setting_string(tctx, "share", NULL);
461 struct cli_credentials *credentials = popt_get_cmdline_credentials();
463 TALLOC_CTX *mem_ctx = talloc_new(tctx);
464 struct smb2_handle _h;
465 struct smb2_handle h_client1_file1 = {{0}};
466 struct smb2_handle h_client1_file2 = {{0}};
467 struct smb2_handle h_client1_file3 = {{0}};
468 struct smb2_handle h_client2_file1 = {{0}};
469 struct smb2_handle h_client2_file2 = {{0}};
470 struct smb2_handle h_client2_file3 = {{0}};
471 struct smb2_create io1, io2, io3;
473 const char *fname1 = BASEDIR "\\oplock_break_test1.dat";
474 const char *fname2 = BASEDIR "\\oplock_break_test2.dat";
475 const char *fname3 = BASEDIR "\\oplock_break_test3.dat";
476 struct smb2_tree *tree2A = NULL;
477 struct smb2_tree *tree2B = NULL;
478 struct smb2_tree *tree2C = NULL;
479 struct smb2_transport *transport1 = tree1->session->transport;
480 struct smbcli_options transport2_options;
481 struct smb2_session *session1 = tree1->session;
482 uint16_t local_port = 0;
484 if (!test_multichannel_initial_checks(tctx, tree1)) {
488 torture_comment(tctx, "Oplock break retry: Test1\n");
490 torture_reset_break_info(tctx, &break_info);
492 transport1->oplock.handler = torture_oplock_ack_handler;
493 transport1->oplock.private_data = tree1;
494 torture_comment(tctx, "transport1 [%p]\n", transport1);
495 local_port = torture_get_local_port_from_transport(transport1);
496 torture_comment(tctx, "transport1 uses tcp port: %d\n", local_port);
498 status = torture_smb2_testdir(tree1, BASEDIR, &_h);
499 CHECK_STATUS(status, NT_STATUS_OK);
500 smb2_util_close(tree1, _h);
501 smb2_util_unlink(tree1, fname1);
502 smb2_util_unlink(tree1, fname2);
503 smb2_util_unlink(tree1, fname3);
504 CHECK_VAL(break_info.count, 0);
506 smb2_oplock_create_share(&io1, fname1,
507 smb2_util_share_access("RWD"),
508 smb2_util_oplock_level("b"));
509 test_multichannel_init_smb_create(&io1);
511 smb2_oplock_create_share(&io2, fname2,
512 smb2_util_share_access("RWD"),
513 smb2_util_oplock_level("b"));
514 test_multichannel_init_smb_create(&io2);
516 smb2_oplock_create_share(&io3, fname3,
517 smb2_util_share_access("RWD"),
518 smb2_util_oplock_level("b"));
519 test_multichannel_init_smb_create(&io3);
521 transport2_options = transport1->options;
523 ret = test_multichannel_create_channels(tctx, host, share,
526 &tree2A, &tree2B, NULL);
527 torture_assert(tctx, ret, "Could not create channels.\n");
530 torture_comment(tctx, "client2 opens fname1 via session 2A\n");
531 status = smb2_create(tree2A, mem_ctx, &io1);
532 CHECK_STATUS(status, NT_STATUS_OK);
533 h_client2_file1 = io1.out.file.handle;
534 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
535 CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
536 torture_wait_for_oplock_break(tctx);
537 CHECK_VAL(break_info.count, 0);
540 torture_comment(tctx, "client2 opens fname2 via session 2B\n");
541 status = smb2_create(tree2B, mem_ctx, &io2);
542 CHECK_STATUS(status, NT_STATUS_OK);
543 h_client2_file2 = io2.out.file.handle;
544 CHECK_CREATED(&io2, CREATED, FILE_ATTRIBUTE_ARCHIVE);
545 CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
546 torture_wait_for_oplock_break(tctx);
547 CHECK_VAL(break_info.count, 0);
550 /* 1 opens file1 - batchoplock break? */
551 torture_comment(tctx, "client1 opens fname1 via session 1\n");
552 io1.in.oplock_level = smb2_util_oplock_level("b");
553 status = smb2_create(tree1, mem_ctx, &io1);
554 CHECK_STATUS(status, NT_STATUS_OK);
555 h_client1_file1 = io1.out.file.handle;
556 CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
557 CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("s"));
558 torture_wait_for_oplock_break(tctx);
559 CHECK_VAL(break_info.count, 1);
561 torture_reset_break_info(tctx, &break_info);
563 /* 1 opens file2 - batchoplock break? */
564 torture_comment(tctx, "client1 opens fname2 via session 1\n");
565 io2.in.oplock_level = smb2_util_oplock_level("b");
566 status = smb2_create(tree1, mem_ctx, &io2);
567 CHECK_STATUS(status, NT_STATUS_OK);
568 h_client1_file2 = io2.out.file.handle;
569 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
570 CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("s"));
571 torture_wait_for_oplock_break(tctx);
572 CHECK_VAL(break_info.count, 1);
574 /* cleanup everything */
575 torture_reset_break_info(tctx, &break_info);
577 smb2_util_close(tree1, h_client1_file1);
578 smb2_util_close(tree1, h_client1_file2);
579 smb2_util_close(tree1, h_client1_file3);
580 smb2_util_close(tree2A, h_client2_file1);
581 smb2_util_close(tree2A, h_client2_file2);
582 smb2_util_close(tree2A, h_client2_file3);
584 smb2_util_unlink(tree1, fname1);
585 smb2_util_unlink(tree1, fname2);
586 smb2_util_unlink(tree1, fname3);
587 CHECK_VAL(break_info.count, 0);
588 test_multichannel_free_channels(tree2A, tree2B, tree2C);
589 tree2A = tree2B = tree2C = NULL;
591 tree1->session = session1;
593 smb2_util_close(tree1, h_client1_file1);
594 smb2_util_close(tree1, h_client1_file2);
595 smb2_util_close(tree1, h_client1_file3);
596 if (tree2A != NULL) {
597 smb2_util_close(tree2A, h_client2_file1);
598 smb2_util_close(tree2A, h_client2_file2);
599 smb2_util_close(tree2A, h_client2_file3);
602 smb2_util_unlink(tree1, fname1);
603 smb2_util_unlink(tree1, fname2);
604 smb2_util_unlink(tree1, fname3);
605 smb2_deltree(tree1, BASEDIR);
607 test_multichannel_free_channels(tree2A, tree2B, tree2C);
609 talloc_free(mem_ctx);
615 * Oplock Break Test 2
616 * Test to see if oplock break retries are sent by the server.
617 * Also checks to see if new channels can be created and used
618 * after an oplock break retry.
621 * open file1 in session 1
622 * oplock break received
623 * block channel on which oplock break received
624 * open file2 in session 1
625 * oplock break not received. Retry received.
627 * write to file2 on 2B
628 * Break sent to session 1(which has file2 open)
629 * Break sent to session 2A(which has read oplock)
630 * close file1 in session 1
631 * open file1 with session 1
632 * unblock blocked channel
633 * disconnect blocked channel
636 * open file3 in session 1
639 static bool test_multichannel_oplock_break_test2(struct torture_context *tctx,
640 struct smb2_tree *tree1)
642 const char *host = torture_setting_string(tctx, "host", NULL);
643 const char *share = torture_setting_string(tctx, "share", NULL);
644 struct cli_credentials *credentials = popt_get_cmdline_credentials();
646 TALLOC_CTX *mem_ctx = talloc_new(tctx);
647 struct smb2_handle _h;
648 struct smb2_handle h_client1_file1 = {{0}};
649 struct smb2_handle h_client1_file2 = {{0}};
650 struct smb2_handle h_client1_file3 = {{0}};
651 struct smb2_handle h_client2_file1 = {{0}};
652 struct smb2_handle h_client2_file2 = {{0}};
653 struct smb2_handle h_client2_file3 = {{0}};
654 struct smb2_create io1, io2, io3;
656 const char *fname1 = BASEDIR "\\oplock_break_test1.dat";
657 const char *fname2 = BASEDIR "\\oplock_break_test2.dat";
658 const char *fname3 = BASEDIR "\\oplock_break_test3.dat";
659 struct smb2_tree *tree2A = NULL;
660 struct smb2_tree *tree2B = NULL;
661 struct smb2_tree *tree2C = NULL;
662 struct smb2_tree *tree2D = NULL;
663 struct smb2_transport *transport1 = tree1->session->transport;
664 struct smb2_transport *transport2 = NULL;
665 struct smbcli_options transport2_options;
666 struct smb2_session *session1 = tree1->session;
667 uint16_t local_port = 0;
669 bool block_ok = false;
670 bool unblock_ok = false;
672 if (!test_multichannel_initial_checks(tctx, tree1)) {
676 torture_comment(tctx, "Oplock break retry: Test2\n");
678 torture_reset_break_info(tctx, &break_info);
680 transport1->oplock.handler = torture_oplock_ack_handler;
681 transport1->oplock.private_data = tree1;
682 torture_comment(tctx, "transport1 [%p]\n", transport1);
683 local_port = torture_get_local_port_from_transport(transport1);
684 torture_comment(tctx, "transport1 uses tcp port: %d\n", local_port);
686 status = torture_smb2_testdir(tree1, BASEDIR, &_h);
687 CHECK_STATUS(status, NT_STATUS_OK);
688 smb2_util_close(tree1, _h);
689 smb2_util_unlink(tree1, fname1);
690 smb2_util_unlink(tree1, fname2);
691 smb2_util_unlink(tree1, fname3);
692 CHECK_VAL(break_info.count, 0);
694 smb2_oplock_create_share(&io1, fname1,
695 smb2_util_share_access("RWD"),
696 smb2_util_oplock_level("b"));
697 test_multichannel_init_smb_create(&io1);
699 smb2_oplock_create_share(&io2, fname2,
700 smb2_util_share_access("RWD"),
701 smb2_util_oplock_level("b"));
702 test_multichannel_init_smb_create(&io2);
704 smb2_oplock_create_share(&io3, fname3,
705 smb2_util_share_access("RWD"),
706 smb2_util_oplock_level("b"));
707 test_multichannel_init_smb_create(&io3);
709 transport2_options = transport1->options;
711 ret = test_multichannel_create_channels(tctx, host, share,
714 &tree2A, &tree2B, &tree2C);
715 torture_assert(tctx, ret, "Could not create channels.\n")
717 torture_comment(tctx, "client2 opens fname1 via session 2A\n");
718 io1.in.oplock_level = smb2_util_oplock_level("b");
719 status = smb2_create(tree2A, mem_ctx, &io1);
720 CHECK_STATUS(status, NT_STATUS_OK);
721 h_client2_file1 = io1.out.file.handle;
722 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
723 CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
724 torture_wait_for_oplock_break(tctx);
725 CHECK_VAL(break_info.count, 0);
728 torture_comment(tctx, "client2 opens fname2 via session 2B\n");
729 io2.in.oplock_level = smb2_util_oplock_level("b");
730 status = smb2_create(tree2B, mem_ctx, &io2);
731 CHECK_STATUS(status, NT_STATUS_OK);
732 h_client2_file2 = io2.out.file.handle;
733 CHECK_CREATED(&io2, CREATED, FILE_ATTRIBUTE_ARCHIVE);
734 CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
735 torture_wait_for_oplock_break(tctx);
736 CHECK_VAL(break_info.count, 0);
739 torture_comment(tctx, "client1 opens fname1 via session 1\n");
740 io1.in.oplock_level = smb2_util_oplock_level("b");
741 status = smb2_create(tree1, mem_ctx, &io1);
742 CHECK_STATUS(status, NT_STATUS_OK);
743 h_client1_file1 = io1.out.file.handle;
744 CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
745 CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("s"));
746 torture_wait_for_oplock_break(tctx);
747 CHECK_VAL(break_info.count, 1);
749 /* We use the transport over which this oplock break was received */
750 transport2 = break_info.received_transport;
751 torture_reset_break_info(tctx, &break_info);
754 block_ok = test_block_channel(tctx, transport2);
756 torture_comment(tctx, "client1 opens fname2 via session 1\n");
757 io2.in.oplock_level = smb2_util_oplock_level("b");
758 status = smb2_create(tree1, mem_ctx, &io2);
759 CHECK_STATUS(status, NT_STATUS_OK);
760 h_client1_file2 = io2.out.file.handle;
761 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
762 CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("s"));
765 * Samba downgrades oplock to a level 2 oplock.
766 * Windows 2016 revokes oplock
768 torture_wait_for_oplock_break(tctx);
769 CHECK_VAL(break_info.count, 1);
770 torture_reset_break_info(tctx, &break_info);
772 torture_comment(tctx, "Trying write to file2 on tree2B\n");
774 blob = data_blob_string_const("Here I am");
775 status = smb2_util_write(tree2B,
780 torture_assert_ntstatus_ok(tctx, status,
781 "failed to write file2 via channel 2B");
784 * Samba: Write triggers 2 oplock breaks
785 * for session 1 which has file2 open
786 * for session 2 which has type 2 oplock
787 * Windows 2016: Only one oplock break for session 1
789 torture_wait_for_oplock_break(tctx);
790 CHECK_VAL_GREATER_THAN(break_info.count, 0);
791 torture_reset_break_info(tctx, &break_info);
793 torture_comment(tctx, "client1 closes fname2 via session 1\n");
794 smb2_util_close(tree1, h_client1_file2);
796 torture_comment(tctx, "client1 opens fname2 via session 1 again\n");
797 io2.in.oplock_level = smb2_util_oplock_level("b");
798 status = smb2_create(tree1, mem_ctx, &io2);
799 CHECK_STATUS(status, NT_STATUS_OK);
800 h_client1_file2 = io2.out.file.handle;
801 io2.out.alloc_size = 0;
803 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
804 CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("s"));
807 * now add a fourth channel and repeat the test, we need to reestablish
808 * transport2 because the remote end has invalidated our connection
810 torture_comment(tctx, "Connecting session 2D\n");
811 tree2D = test_multichannel_create_channel(tctx, host, share,
812 credentials, &transport2_options, tree2B);
817 torture_reset_break_info(tctx, &break_info);
818 torture_comment(tctx, "client 2 opening fname3 over transport2D\n");
819 status = smb2_create(tree2D, mem_ctx, &io3);
820 CHECK_STATUS(status, NT_STATUS_OK);
821 h_client2_file3 = io3.out.file.handle;
822 CHECK_CREATED(&io3, CREATED, FILE_ATTRIBUTE_ARCHIVE);
823 CHECK_VAL(io3.out.oplock_level, smb2_util_oplock_level("b"));
824 torture_wait_for_oplock_break(tctx);
825 CHECK_VAL(break_info.count, 0);
827 torture_comment(tctx, "client1 opens fname3 via session 1\n");
828 status = smb2_create(tree1, mem_ctx, &io3);
829 CHECK_STATUS(status, NT_STATUS_OK);
830 h_client1_file3 = io3.out.file.handle;
831 CHECK_CREATED(&io3, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
832 CHECK_VAL(io3.out.oplock_level, smb2_util_oplock_level("s"));
833 torture_wait_for_oplock_break(tctx);
834 CHECK_VAL(break_info.count, 1);
837 if (block_ok && !unblock_ok) {
838 test_unblock_channel(tctx, transport2);
840 test_cleanup_blocked_channels(tctx);
842 tree1->session = session1;
844 smb2_util_close(tree1, h_client1_file1);
845 smb2_util_close(tree1, h_client1_file2);
846 smb2_util_close(tree1, h_client1_file3);
847 if (tree2B != NULL) {
848 smb2_util_close(tree2B, h_client2_file1);
849 smb2_util_close(tree2B, h_client2_file2);
850 smb2_util_close(tree2B, h_client2_file3);
853 smb2_util_unlink(tree1, fname1);
854 smb2_util_unlink(tree1, fname2);
855 smb2_util_unlink(tree1, fname3);
856 smb2_deltree(tree1, BASEDIR);
858 test_multichannel_free_channels(tree2A, tree2B, tree2C);
859 if (tree2D != NULL) {
863 talloc_free(mem_ctx);
868 static const uint64_t LEASE1F1 = 0xBADC0FFEE0DDF00Dull;
869 static const uint64_t LEASE1F2 = 0xBADC0FFEE0DDD00Dull;
870 static const uint64_t LEASE1F3 = 0xDADC0FFEE0DDD00Dull;
871 static const uint64_t LEASE2F1 = 0xDEADBEEFFEEDBEADull;
872 static const uint64_t LEASE2F2 = 0xDAD0FFEDD00DF00Dull;
873 static const uint64_t LEASE2F3 = 0xBAD0FFEDD00DF00Dull;
876 * Lease Break Test 1:
877 * Test to check if lease breaks are sent by the server as expected.
878 * open file1 in session 2A
879 * open file2 in session 2B
880 * open file3 in session 2C
881 * open file1 in session 1
883 * open file2 in session 1
885 * open file3 in session 1
888 static bool test_multichannel_lease_break_test1(struct torture_context *tctx,
889 struct smb2_tree *tree1)
891 const char *host = torture_setting_string(tctx, "host", NULL);
892 const char *share = torture_setting_string(tctx, "share", NULL);
893 struct cli_credentials *credentials = popt_get_cmdline_credentials();
895 TALLOC_CTX *mem_ctx = talloc_new(tctx);
896 struct smb2_handle _h;
897 struct smb2_handle *h = NULL;
898 struct smb2_handle h_client1_file1 = {{0}};
899 struct smb2_handle h_client1_file2 = {{0}};
900 struct smb2_handle h_client1_file3 = {{0}};
901 struct smb2_handle h_client2_file1 = {{0}};
902 struct smb2_handle h_client2_file2 = {{0}};
903 struct smb2_handle h_client2_file3 = {{0}};
904 struct smb2_create io1, io2, io3;
906 const char *fname1 = BASEDIR "\\lease_break_test1.dat";
907 const char *fname2 = BASEDIR "\\lease_break_test2.dat";
908 const char *fname3 = BASEDIR "\\lease_break_test3.dat";
909 struct smb2_tree *tree2A = NULL;
910 struct smb2_tree *tree2B = NULL;
911 struct smb2_tree *tree2C = NULL;
912 struct smb2_transport *transport1 = tree1->session->transport;
913 struct smbcli_options transport2_options;
914 struct smb2_session *session1 = tree1->session;
915 uint16_t local_port = 0;
916 struct smb2_lease ls1;
917 struct smb2_lease ls2;
918 struct smb2_lease ls3;
920 if (!test_multichannel_initial_checks(tctx, tree1)) {
924 torture_comment(tctx, "Lease break retry: Test1\n");
926 torture_reset_lease_break_info(tctx, &lease_break_info);
928 transport1->lease.handler = torture_lease_handler;
929 transport1->lease.private_data = tree1;
930 torture_comment(tctx, "transport1 [%p]\n", transport1);
931 local_port = torture_get_local_port_from_transport(transport1);
932 torture_comment(tctx, "transport1 uses tcp port: %d\n", local_port);
934 status = torture_smb2_testdir(tree1, BASEDIR, &_h);
935 CHECK_STATUS(status, NT_STATUS_OK);
936 smb2_util_close(tree1, _h);
937 smb2_util_unlink(tree1, fname1);
938 smb2_util_unlink(tree1, fname2);
939 smb2_util_unlink(tree1, fname3);
940 CHECK_VAL(lease_break_info.count, 0);
942 smb2_lease_create(&io1, &ls1, false, fname1, LEASE2F1,
943 smb2_util_lease_state("RHW"));
944 test_multichannel_init_smb_create(&io1);
946 smb2_lease_create(&io2, &ls2, false, fname2, LEASE2F2,
947 smb2_util_lease_state("RHW"));
948 test_multichannel_init_smb_create(&io2);
950 smb2_lease_create(&io3, &ls3, false, fname3, LEASE2F3,
951 smb2_util_lease_state("RHW"));
952 test_multichannel_init_smb_create(&io3);
954 transport2_options = transport1->options;
956 ret = test_multichannel_create_channels(tctx, host, share,
959 &tree2A, &tree2B, &tree2C);
960 torture_assert(tctx, ret, "Could not create channels.\n");
963 torture_comment(tctx, "client2 opens fname1 via session 2A\n");
964 status = smb2_create(tree2A, mem_ctx, &io1);
965 CHECK_STATUS(status, NT_STATUS_OK);
966 h_client2_file1 = io1.out.file.handle;
967 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
968 CHECK_LEASE(&io1, "RHW", true, LEASE2F1, 0);
969 CHECK_VAL(lease_break_info.count, 0);
972 torture_comment(tctx, "client2 opens fname2 via session 2B\n");
973 status = smb2_create(tree2B, mem_ctx, &io2);
974 CHECK_STATUS(status, NT_STATUS_OK);
975 h_client2_file2 = io2.out.file.handle;
976 CHECK_CREATED(&io2, CREATED, FILE_ATTRIBUTE_ARCHIVE);
977 CHECK_LEASE(&io2, "RHW", true, LEASE2F2, 0);
978 CHECK_VAL(lease_break_info.count, 0);
981 torture_comment(tctx, "client2 opens fname3 via session 2C\n");
982 smb2_lease_create(&io3, &ls3, false, fname3, LEASE2F3,
983 smb2_util_lease_state("RHW"));
984 status = smb2_create(tree2C, mem_ctx, &io3);
985 CHECK_STATUS(status, NT_STATUS_OK);
986 h_client2_file3 = io3.out.file.handle;
987 CHECK_CREATED(&io3, CREATED, FILE_ATTRIBUTE_ARCHIVE);
988 CHECK_LEASE(&io3, "RHW", true, LEASE2F3, 0);
989 CHECK_VAL(lease_break_info.count, 0);
991 /* 1 opens file1 - lease break? */
992 torture_comment(tctx, "client1 opens fname1 via session 1\n");
993 smb2_lease_create(&io1, &ls1, false, fname1, LEASE1F1,
994 smb2_util_lease_state("RHW"));
995 status = smb2_create(tree1, mem_ctx, &io1);
996 CHECK_STATUS(status, NT_STATUS_OK);
997 h_client1_file1 = io1.out.file.handle;
998 CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
999 CHECK_LEASE(&io1, "RH", true, LEASE1F1, 0);
1000 CHECK_BREAK_INFO("RHW", "RH", LEASE2F1);
1001 CHECK_VAL(lease_break_info.count, 1);
1003 torture_reset_lease_break_info(tctx, &lease_break_info);
1005 /* 1 opens file2 - lease break? */
1006 torture_comment(tctx, "client1 opens fname2 via session 1\n");
1007 smb2_lease_create(&io2, &ls2, false, fname2, LEASE1F2,
1008 smb2_util_lease_state("RHW"));
1009 status = smb2_create(tree1, mem_ctx, &io2);
1010 CHECK_STATUS(status, NT_STATUS_OK);
1011 h_client1_file2 = io2.out.file.handle;
1012 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1013 CHECK_LEASE(&io2, "RH", true, LEASE1F2, 0);
1014 CHECK_BREAK_INFO("RHW", "RH", LEASE2F2);
1015 CHECK_VAL(lease_break_info.count, 1);
1017 torture_reset_lease_break_info(tctx, &lease_break_info);
1019 /* 1 opens file3 - lease break? */
1020 torture_comment(tctx, "client1 opens fname3 via session 1\n");
1021 smb2_lease_create(&io3, &ls3, false, fname3, LEASE1F3,
1022 smb2_util_lease_state("RHW"));
1023 status = smb2_create(tree1, mem_ctx, &io3);
1024 CHECK_STATUS(status, NT_STATUS_OK);
1025 h_client1_file3 = io3.out.file.handle;
1026 CHECK_CREATED(&io3, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1027 CHECK_LEASE(&io3, "RH", true, LEASE1F3, 0);
1028 CHECK_BREAK_INFO("RHW", "RH", LEASE2F3);
1029 CHECK_VAL(lease_break_info.count, 1);
1031 /* cleanup everything */
1032 torture_reset_lease_break_info(tctx, &lease_break_info);
1034 smb2_util_close(tree1, h_client1_file1);
1035 smb2_util_close(tree1, h_client1_file2);
1036 smb2_util_close(tree1, h_client1_file3);
1037 smb2_util_close(tree2A, h_client2_file1);
1038 smb2_util_close(tree2A, h_client2_file2);
1039 smb2_util_close(tree2A, h_client2_file3);
1041 smb2_util_unlink(tree1, fname1);
1042 smb2_util_unlink(tree1, fname2);
1043 smb2_util_unlink(tree1, fname3);
1044 CHECK_VAL(lease_break_info.count, 0);
1045 test_multichannel_free_channels(tree2A, tree2B, tree2C);
1046 tree2A = tree2B = tree2C = NULL;
1048 tree1->session = session1;
1050 smb2_util_close(tree1, h_client1_file1);
1051 smb2_util_close(tree1, h_client1_file2);
1052 smb2_util_close(tree1, h_client1_file3);
1053 if (tree2A != NULL) {
1054 smb2_util_close(tree2A, h_client2_file1);
1055 smb2_util_close(tree2A, h_client2_file2);
1056 smb2_util_close(tree2A, h_client2_file3);
1060 smb2_util_close(tree1, *h);
1063 smb2_util_unlink(tree1, fname1);
1064 smb2_util_unlink(tree1, fname2);
1065 smb2_util_unlink(tree1, fname3);
1066 smb2_deltree(tree1, BASEDIR);
1068 test_multichannel_free_channels(tree2A, tree2B, tree2C);
1070 talloc_free(mem_ctx);
1075 struct torture_suite *torture_smb2_multichannel_init(TALLOC_CTX *ctx)
1077 struct torture_suite *suite = torture_suite_create(ctx, "multichannel");
1078 struct torture_suite *suite_generic = torture_suite_create(ctx,
1080 struct torture_suite *suite_oplocks = torture_suite_create(ctx,
1082 struct torture_suite *suite_leases = torture_suite_create(ctx,
1085 torture_suite_add_suite(suite, suite_generic);
1086 torture_suite_add_suite(suite, suite_oplocks);
1087 torture_suite_add_suite(suite, suite_leases);
1089 torture_suite_add_1smb2_test(suite, "interface_info",
1090 test_multichannel_interface_info);
1091 torture_suite_add_1smb2_test(suite_oplocks, "test1",
1092 test_multichannel_oplock_break_test1);
1093 torture_suite_add_1smb2_test(suite_oplocks, "test2",
1094 test_multichannel_oplock_break_test2);
1095 torture_suite_add_1smb2_test(suite_leases, "test1",
1096 test_multichannel_lease_break_test1);
1098 suite->description = talloc_strdup(suite, "SMB2 Multichannel tests");