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 "torture/smb2/block.h"
39 #define BASEDIR "multichanneltestdir"
41 #define CHECK_STATUS(status, correct) \
42 torture_assert_ntstatus_equal_goto(tctx, status, correct,\
45 #define CHECK_VAL(v, correct) do { \
46 if ((v) != (correct)) { \
47 torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s" \
48 " got 0x%x - should be 0x%x\n", \
49 __location__, #v, (int)v, (int)correct); \
54 #define CHECK_VAL_GREATER_THAN(v, gt_val) do { \
55 if ((v) <= (gt_val)) { \
56 torture_result(tctx, TORTURE_FAIL, \
57 "(%s): wrong value for %s got 0x%x - " \
58 "should be greater than 0x%x\n", \
59 __location__, #v, (int)v, (int)gt_val); \
64 #define CHECK_CREATED(__io, __created, __attribute) \
66 CHECK_VAL((__io)->out.create_action, \
67 NTCREATEX_ACTION_ ## __created); \
68 CHECK_VAL((__io)->out.alloc_size, 0); \
69 CHECK_VAL((__io)->out.size, 0); \
70 CHECK_VAL((__io)->out.file_attr, (__attribute)); \
71 CHECK_VAL((__io)->out.reserved2, 0); \
74 #define CHECK_PTR(ptr, correct) do { \
75 if ((ptr) != (correct)) { \
76 torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s " \
77 "got 0x%p - should be 0x%p\n", \
78 __location__, #ptr, ptr, correct); \
83 #define CHECK_LEASE(__io, __state, __oplevel, __key, __flags) \
85 CHECK_VAL((__io)->out.lease_response.lease_version, 1); \
87 CHECK_VAL((__io)->out.oplock_level, \
88 SMB2_OPLOCK_LEVEL_LEASE); \
89 CHECK_VAL((__io)->out.lease_response.lease_key.data[0],\
91 CHECK_VAL((__io)->out.lease_response.lease_key.data[1],\
93 CHECK_VAL((__io)->out.lease_response.lease_state,\
94 smb2_util_lease_state(__state)); \
96 CHECK_VAL((__io)->out.oplock_level,\
97 SMB2_OPLOCK_LEVEL_NONE); \
98 CHECK_VAL((__io)->out.lease_response.lease_key.data[0],\
100 CHECK_VAL((__io)->out.lease_response.lease_key.data[1],\
102 CHECK_VAL((__io)->out.lease_response.lease_state, 0); \
105 CHECK_VAL((__io)->out.lease_response.lease_flags, (__flags)); \
106 CHECK_VAL((__io)->out.lease_response.lease_duration, 0); \
107 CHECK_VAL((__io)->out.lease_response.lease_epoch, 0); \
110 static bool test_ioctl_network_interface_info(struct torture_context *tctx,
111 struct smb2_tree *tree,
112 struct fsctl_net_iface_info *info)
114 union smb_ioctl ioctl;
115 struct smb2_handle fh;
118 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
119 if (!(caps & SMB2_CAP_MULTI_CHANNEL)) {
121 "server doesn't support SMB2_CAP_MULTI_CHANNEL\n");
126 ioctl.smb2.level = RAW_IOCTL_SMB2;
128 fh.data[0] = UINT64_MAX;
129 fh.data[1] = UINT64_MAX;
131 ioctl.smb2.in.file.handle = fh;
132 ioctl.smb2.in.function = FSCTL_QUERY_NETWORK_INTERFACE_INFO;
133 /* Windows client sets this to 64KiB */
134 ioctl.smb2.in.max_output_response = 0x10000;
135 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
137 torture_assert_ntstatus_ok(tctx,
138 smb2_ioctl(tree, tctx, &ioctl.smb2),
139 "FSCTL_QUERY_NETWORK_INTERFACE_INFO failed");
142 (ioctl.smb2.out.out.length != 0),
143 "no interface info returned???");
145 torture_assert_ndr_success(tctx,
146 ndr_pull_struct_blob(&ioctl.smb2.out.out, tctx, info,
147 (ndr_pull_flags_fn_t)ndr_pull_fsctl_net_iface_info),
148 "failed to ndr pull");
151 NDR_PRINT_DEBUG(fsctl_net_iface_info, info);
157 static bool test_multichannel_interface_info(struct torture_context *tctx,
158 struct smb2_tree *tree)
160 struct fsctl_net_iface_info info;
162 return test_ioctl_network_interface_info(tctx, tree, &info);
165 static struct smb2_tree *test_multichannel_create_channel(
166 struct torture_context *tctx,
169 struct cli_credentials *credentials,
170 struct smbcli_options *transport_options,
171 struct smb2_tree *parent_tree
175 struct smb2_transport *transport;
176 struct smb2_session *session;
178 struct smb2_tree *tree;
180 status = smb2_connect(tctx,
182 lpcfg_smb_ports(tctx->lp_ctx),
184 lpcfg_resolve_context(tctx->lp_ctx),
189 lpcfg_socket_options(tctx->lp_ctx),
190 lpcfg_gensec_settings(tctx, tctx->lp_ctx)
192 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
193 "smb2_connect failed");
194 transport = tree->session->transport;
195 transport->oplock.handler = torture_oplock_ack_handler;
196 transport->oplock.private_data = tree;
197 transport->lease.handler = torture_lease_handler;
198 transport->lease.private_data = tree;
199 torture_comment(tctx, "established transport [%p]\n", transport);
202 * If parent tree is set, bind the session to the parent transport
205 session = smb2_session_channel(transport,
206 lpcfg_gensec_settings(tctx, tctx->lp_ctx),
207 parent_tree, parent_tree->session);
208 torture_assert_goto(tctx, session != NULL, ret, done,
209 "smb2_session_channel failed");
211 tree->smbXcli = parent_tree->smbXcli;
212 tree->session = session;
213 status = smb2_session_setup_spnego(session,
215 0 /* previous_session_id */);
216 CHECK_STATUS(status, NT_STATUS_OK);
217 torture_comment(tctx, "bound new session to parent\n");
220 * We absolutely need to make sure to send something over this
221 * connection to register the oplock break handler with the smb client
222 * connection. If we do not send something (at least a keepalive), we
223 * will *NEVER* receive anything over this transport.
225 smb2_keepalive(transport);
235 bool test_multichannel_create_channels(
236 struct torture_context *tctx,
239 struct cli_credentials *credentials,
240 struct smbcli_options *transport_options,
241 struct smb2_tree **tree2A,
242 struct smb2_tree **tree2B,
243 struct smb2_tree **tree2C
246 struct smb2_tree *tree;
247 struct smb2_transport *transport2A;
248 struct smb2_transport *transport2B;
249 struct smb2_transport *transport2C;
250 uint16_t local_port = 0;
252 transport_options->client_guid = GUID_random();
255 torture_comment(tctx, "Setting up connection 2A\n");
256 tree = test_multichannel_create_channel(tctx, host, share,
257 credentials, transport_options, NULL);
262 transport2A = tree->session->transport;
263 local_port = torture_get_local_port_from_transport(transport2A);
264 torture_comment(tctx, "transport2A uses tcp port: %d\n", local_port);
268 torture_comment(tctx, "Setting up connection 2B\n");
269 tree = test_multichannel_create_channel(tctx, host, share,
270 credentials, transport_options, *tree2A);
275 transport2B = tree->session->transport;
276 local_port = torture_get_local_port_from_transport(transport2B);
277 torture_comment(tctx, "transport2B uses tcp port: %d\n",
283 torture_comment(tctx, "Setting up connection 2C\n");
284 tree = test_multichannel_create_channel(tctx, host, share,
285 credentials, transport_options, *tree2A);
290 transport2C = tree->session->transport;
291 local_port = torture_get_local_port_from_transport(transport2C);
292 torture_comment(tctx, "transport2C uses tcp port: %d\n",
301 static void test_multichannel_free_channels(struct smb2_tree *tree2A,
302 struct smb2_tree *tree2B,
303 struct smb2_tree *tree2C)
310 static bool test_multichannel_initial_checks(struct torture_context *tctx,
311 struct smb2_tree *tree1)
313 struct smb2_transport *transport1 = tree1->session->transport;
314 uint32_t server_capabilities;
315 struct fsctl_net_iface_info info;
317 if (smbXcli_conn_protocol(transport1->conn) < PROTOCOL_SMB3_00) {
318 torture_skip_goto(tctx, fail,
319 "SMB 3.X Dialect family required for "
320 "Multichannel tests\n");
323 server_capabilities = smb2cli_conn_server_capabilities(
324 tree1->session->transport->conn);
325 if (!(server_capabilities & SMB2_CAP_MULTI_CHANNEL)) {
326 torture_skip_goto(tctx, fail,
327 "Server does not support multichannel.");
331 test_ioctl_network_interface_info(tctx, tree1, &info),
332 "failed to retrieve network interface info");
339 static void test_multichannel_init_smb_create(struct smb2_create *io)
341 io->in.durable_open = false;
342 io->in.durable_open_v2 = true;
343 io->in.persistent_open = false;
344 io->in.create_guid = GUID_random();
345 io->in.timeout = 0x493E0; /* 300000 */
346 /* windows 2016 returns 300000 0x493E0 */
350 * We simulate blocking incoming oplock break requests by simply ignoring
351 * the incoming break requests.
353 static bool test_set_ignore_break_handler(struct torture_context *tctx,
354 struct smb2_transport *transport)
356 transport->oplock.handler = torture_oplock_ignore_handler;
357 transport->lease.handler = torture_lease_ignore_handler;
362 static bool test_reset_break_handler(struct torture_context *tctx,
363 struct smb2_transport *transport)
365 transport->oplock.handler = torture_oplock_ack_handler;
366 transport->lease.handler = torture_lease_handler;
372 * Use iptables to block channels
374 static bool test_iptables_block_channel(struct torture_context *tctx,
375 struct smb2_transport *transport,
381 local_port = torture_get_local_port_from_transport(transport);
382 torture_comment(tctx, "transport uses tcp port: %d\n", local_port);
383 ret = torture_block_tcp_transport_name(tctx, transport, name);
384 torture_assert(tctx, ret, "we could not block tcp transport");
389 static bool test_iptables_unblock_channel(struct torture_context *tctx,
390 struct smb2_transport *transport,
396 local_port = torture_get_local_port_from_transport(transport);
397 torture_comment(tctx, "transport uses tcp port: %d\n", local_port);
398 ret = torture_unblock_tcp_transport_name(tctx, transport, name);
399 torture_assert(tctx, ret, "we could not block tcp transport");
404 #define test_block_channel(_tctx, _t) _test_block_channel(_tctx, _t, #_t)
405 static bool _test_block_channel(struct torture_context *tctx,
406 struct smb2_transport *transport,
409 bool use_iptables = torture_setting_bool(tctx,
410 "use_iptables", false);
413 return test_iptables_block_channel(tctx, transport, name);
415 return test_set_ignore_break_handler(tctx, transport);
419 #define test_unblock_channel(_tctx, _t) _test_unblock_channel(_tctx, _t, #_t)
420 static bool _test_unblock_channel(struct torture_context *tctx,
421 struct smb2_transport *transport,
424 bool use_iptables = torture_setting_bool(tctx,
425 "use_iptables", false);
428 return test_iptables_unblock_channel(tctx, transport, name);
430 return test_reset_break_handler(tctx, transport);
434 static void test_cleanup_blocked_channels(struct torture_context *tctx)
436 bool use_iptables = torture_setting_bool(tctx,
437 "use_iptables", false);
440 torture_unblock_cleanup(tctx);
445 * Oplock break - Test 1
446 * Test to confirm that server sends oplock breaks as expected.
447 * open file1 in session 2A
448 * open file2 in session 2B
449 * open file1 in session 1
450 * oplock break received
451 * open file1 in session 1
452 * oplock break received
455 static bool test_multichannel_oplock_break_test1(struct torture_context *tctx,
456 struct smb2_tree *tree1)
458 const char *host = torture_setting_string(tctx, "host", NULL);
459 const char *share = torture_setting_string(tctx, "share", NULL);
460 struct cli_credentials *credentials = popt_get_cmdline_credentials();
462 TALLOC_CTX *mem_ctx = talloc_new(tctx);
463 struct smb2_handle _h;
464 struct smb2_handle h_client1_file1 = {{0}};
465 struct smb2_handle h_client1_file2 = {{0}};
466 struct smb2_handle h_client1_file3 = {{0}};
467 struct smb2_handle h_client2_file1 = {{0}};
468 struct smb2_handle h_client2_file2 = {{0}};
469 struct smb2_handle h_client2_file3 = {{0}};
470 struct smb2_create io1, io2, io3;
472 const char *fname1 = BASEDIR "\\oplock_break_test1.dat";
473 const char *fname2 = BASEDIR "\\oplock_break_test2.dat";
474 const char *fname3 = BASEDIR "\\oplock_break_test3.dat";
475 struct smb2_tree *tree2A = NULL;
476 struct smb2_tree *tree2B = NULL;
477 struct smb2_tree *tree2C = NULL;
478 struct smb2_transport *transport1 = tree1->session->transport;
479 struct smbcli_options transport2_options;
480 struct smb2_session *session1 = tree1->session;
481 uint16_t local_port = 0;
483 if (!test_multichannel_initial_checks(tctx, tree1)) {
487 torture_comment(tctx, "Oplock break retry: Test1\n");
489 torture_reset_break_info(tctx, &break_info);
491 transport1->oplock.handler = torture_oplock_ack_handler;
492 transport1->oplock.private_data = tree1;
493 torture_comment(tctx, "transport1 [%p]\n", transport1);
494 local_port = torture_get_local_port_from_transport(transport1);
495 torture_comment(tctx, "transport1 uses tcp port: %d\n", local_port);
497 status = torture_smb2_testdir(tree1, BASEDIR, &_h);
498 CHECK_STATUS(status, NT_STATUS_OK);
499 smb2_util_close(tree1, _h);
500 smb2_util_unlink(tree1, fname1);
501 smb2_util_unlink(tree1, fname2);
502 smb2_util_unlink(tree1, fname3);
503 CHECK_VAL(break_info.count, 0);
505 smb2_oplock_create_share(&io1, fname1,
506 smb2_util_share_access("RWD"),
507 smb2_util_oplock_level("b"));
508 test_multichannel_init_smb_create(&io1);
510 smb2_oplock_create_share(&io2, fname2,
511 smb2_util_share_access("RWD"),
512 smb2_util_oplock_level("b"));
513 test_multichannel_init_smb_create(&io2);
515 smb2_oplock_create_share(&io3, fname3,
516 smb2_util_share_access("RWD"),
517 smb2_util_oplock_level("b"));
518 test_multichannel_init_smb_create(&io3);
520 transport2_options = transport1->options;
522 ret = test_multichannel_create_channels(tctx, host, share,
525 &tree2A, &tree2B, NULL);
526 torture_assert(tctx, ret, "Could not create channels.\n");
529 torture_comment(tctx, "client2 opens fname1 via session 2A\n");
530 status = smb2_create(tree2A, mem_ctx, &io1);
531 CHECK_STATUS(status, NT_STATUS_OK);
532 h_client2_file1 = io1.out.file.handle;
533 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
534 CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
535 torture_wait_for_oplock_break(tctx);
536 CHECK_VAL(break_info.count, 0);
539 torture_comment(tctx, "client2 opens fname2 via session 2B\n");
540 status = smb2_create(tree2B, mem_ctx, &io2);
541 CHECK_STATUS(status, NT_STATUS_OK);
542 h_client2_file2 = io2.out.file.handle;
543 CHECK_CREATED(&io2, CREATED, FILE_ATTRIBUTE_ARCHIVE);
544 CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
545 torture_wait_for_oplock_break(tctx);
546 CHECK_VAL(break_info.count, 0);
549 /* 1 opens file1 - batchoplock break? */
550 torture_comment(tctx, "client1 opens fname1 via session 1\n");
551 io1.in.oplock_level = smb2_util_oplock_level("b");
552 status = smb2_create(tree1, mem_ctx, &io1);
553 CHECK_STATUS(status, NT_STATUS_OK);
554 h_client1_file1 = io1.out.file.handle;
555 CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
556 CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("s"));
557 torture_wait_for_oplock_break(tctx);
558 CHECK_VAL(break_info.count, 1);
560 torture_reset_break_info(tctx, &break_info);
562 /* 1 opens file2 - batchoplock break? */
563 torture_comment(tctx, "client1 opens fname2 via session 1\n");
564 io2.in.oplock_level = smb2_util_oplock_level("b");
565 status = smb2_create(tree1, mem_ctx, &io2);
566 CHECK_STATUS(status, NT_STATUS_OK);
567 h_client1_file2 = io2.out.file.handle;
568 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
569 CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("s"));
570 torture_wait_for_oplock_break(tctx);
571 CHECK_VAL(break_info.count, 1);
573 /* cleanup everything */
574 torture_reset_break_info(tctx, &break_info);
576 smb2_util_close(tree1, h_client1_file1);
577 smb2_util_close(tree1, h_client1_file2);
578 smb2_util_close(tree1, h_client1_file3);
579 smb2_util_close(tree2A, h_client2_file1);
580 smb2_util_close(tree2A, h_client2_file2);
581 smb2_util_close(tree2A, h_client2_file3);
583 smb2_util_unlink(tree1, fname1);
584 smb2_util_unlink(tree1, fname2);
585 smb2_util_unlink(tree1, fname3);
586 CHECK_VAL(break_info.count, 0);
587 test_multichannel_free_channels(tree2A, tree2B, tree2C);
588 tree2A = tree2B = tree2C = NULL;
590 tree1->session = session1;
592 smb2_util_close(tree1, h_client1_file1);
593 smb2_util_close(tree1, h_client1_file2);
594 smb2_util_close(tree1, h_client1_file3);
595 if (tree2A != NULL) {
596 smb2_util_close(tree2A, h_client2_file1);
597 smb2_util_close(tree2A, h_client2_file2);
598 smb2_util_close(tree2A, h_client2_file3);
601 smb2_util_unlink(tree1, fname1);
602 smb2_util_unlink(tree1, fname2);
603 smb2_util_unlink(tree1, fname3);
604 smb2_deltree(tree1, BASEDIR);
606 test_multichannel_free_channels(tree2A, tree2B, tree2C);
608 talloc_free(mem_ctx);
613 struct torture_suite *torture_smb2_multichannel_init(TALLOC_CTX *ctx)
615 struct torture_suite *suite = torture_suite_create(ctx, "multichannel");
616 struct torture_suite *suite_generic = torture_suite_create(ctx,
618 struct torture_suite *suite_oplocks = torture_suite_create(ctx,
621 torture_suite_add_suite(suite, suite_generic);
622 torture_suite_add_suite(suite, suite_oplocks);
624 torture_suite_add_1smb2_test(suite, "interface_info",
625 test_multichannel_interface_info);
626 torture_suite_add_1smb2_test(suite_oplocks, "test1",
627 test_multichannel_oplock_break_test1);
629 suite->description = talloc_strdup(suite, "SMB2 Multichannel tests");