2 Unix SMB/CIFS implementation.
4 test suite for SMB2 replay
6 Copyright (C) Anubhav Rakshit 2014
7 Copyright (C) Stefan Metzmacher 2014
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 3 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include "libcli/smb2/smb2.h"
25 #include "libcli/smb2/smb2_calls.h"
26 #include "torture/torture.h"
27 #include "torture/smb2/proto.h"
28 #include "../libcli/smb/smbXcli_base.h"
29 #include "lib/cmdline/popt_common.h"
30 #include "auth/credentials/credentials.h"
31 #include "libcli/security/security.h"
32 #include "libcli/resolve/resolve.h"
33 #include "lib/param/param.h"
34 #include "lib/events/events.h"
35 #include "oplock_break_handler.h"
37 #define CHECK_VAL(v, correct) do { \
38 if ((v) != (correct)) { \
39 torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s got 0x%x - should be 0x%x\n", \
40 __location__, #v, (int)v, (int)correct); \
45 #define CHECK_STATUS(status, correct) do { \
46 if (!NT_STATUS_EQUAL(status, correct)) { \
47 torture_result(tctx, TORTURE_FAIL, __location__": Incorrect status %s - should be %s", \
48 nt_errstr(status), nt_errstr(correct)); \
53 #define CHECK_CREATED(__io, __created, __attribute) \
55 CHECK_VAL((__io)->out.create_action, NTCREATEX_ACTION_ ## __created); \
56 CHECK_VAL((__io)->out.size, 0); \
57 CHECK_VAL((__io)->out.file_attr, (__attribute)); \
58 CHECK_VAL((__io)->out.reserved2, 0); \
61 #define CHECK_HANDLE(__h1, __h2) \
63 CHECK_VAL((__h1)->data[0], (__h2)->data[0]); \
64 CHECK_VAL((__h1)->data[1], (__h2)->data[1]); \
67 #define __IO_OUT_VAL(__io1, __io2, __m) \
68 CHECK_VAL((__io1)->out.__m, (__io2)->out.__m)
70 #define CHECK_CREATE_OUT(__io1, __io2) \
72 CHECK_HANDLE(&(__io1)->out.file.handle, \
73 &(__io2)->out.file.handle); \
74 __IO_OUT_VAL(__io1, __io2, oplock_level); \
75 __IO_OUT_VAL(__io1, __io2, create_action); \
76 __IO_OUT_VAL(__io1, __io2, create_time); \
77 __IO_OUT_VAL(__io1, __io2, access_time); \
78 __IO_OUT_VAL(__io1, __io2, write_time); \
79 __IO_OUT_VAL(__io1, __io2, change_time); \
80 __IO_OUT_VAL(__io1, __io2, alloc_size); \
81 __IO_OUT_VAL(__io1, __io2, size); \
82 __IO_OUT_VAL(__io1, __io2, file_attr); \
83 __IO_OUT_VAL(__io1, __io2, durable_open); \
84 __IO_OUT_VAL(__io1, __io2, durable_open_v2); \
85 __IO_OUT_VAL(__io1, __io2, persistent_open); \
86 __IO_OUT_VAL(__io1, __io2, timeout); \
87 __IO_OUT_VAL(__io1, __io2, blobs.num_blobs); \
88 if ((__io1)->out.oplock_level == SMB2_OPLOCK_LEVEL_LEASE) { \
89 __IO_OUT_VAL(__io1, __io2, lease_response.lease_state);\
90 __IO_OUT_VAL(__io1, __io2, lease_response.lease_key.data[0]);\
91 __IO_OUT_VAL(__io1, __io2, lease_response.lease_key.data[1]);\
95 #define BASEDIR "replaytestdir"
98 * Timer handler function notifies the registering function that time is up
100 static void timeout_cb(struct tevent_context *ev,
101 struct tevent_timer *te,
102 struct timeval current_time,
105 bool *timesup = (bool *)private_data;
111 * Wait a short period of time to receive a single oplock break request
113 static void torture_wait_for_oplock_break(struct torture_context *tctx)
115 TALLOC_CTX *tmp_ctx = talloc_new(NULL);
116 struct tevent_timer *te = NULL;
118 bool timesup = false;
119 int old_count = break_info.count;
121 /* Wait .1 seconds for an oplock break */
122 ne = tevent_timeval_current_ofs(0, 100000);
124 te = tevent_add_timer(tctx->ev, tmp_ctx, ne, timeout_cb, ×up);
126 torture_comment(tctx, "Failed to wait for an oplock break. "
127 "test results may not be accurate.");
131 while (!timesup && break_info.count < old_count + 1) {
132 if (tevent_loop_once(tctx->ev) != 0) {
133 torture_comment(tctx, "Failed to wait for an oplock "
134 "break. test results may not be "
142 * We don't know if the timed event fired and was freed, we received
143 * our oplock break, or some other event triggered the loop. Thus,
144 * we create a tmp_ctx to be able to safely free/remove the timed
145 * event in all 3 cases.
147 talloc_free(tmp_ctx);
153 * Test what happens when SMB2_FLAGS_REPLAY_OPERATION is enabled for various
154 * commands. We want to verify if the server returns an error code or not.
156 static bool test_replay_commands(struct torture_context *tctx, struct smb2_tree *tree)
160 struct smb2_handle h;
163 union smb_setfileinfo sfinfo;
164 union smb_fileinfo qfinfo;
165 union smb_ioctl ioctl;
166 struct smb2_lock lck;
167 struct smb2_lock_element el[2];
169 TALLOC_CTX *tmp_ctx = talloc_new(tree);
170 const char *fname = BASEDIR "\\replay_commands.dat";
171 struct smb2_transport *transport = tree->session->transport;
173 if (smbXcli_conn_protocol(transport->conn) < PROTOCOL_SMB3_00) {
174 torture_skip(tctx, "SMB 3.X Dialect family required for "
178 ZERO_STRUCT(break_info);
179 break_info.tctx = tctx;
180 tree->session->transport->oplock.handler = torture_oplock_ack_handler;
181 tree->session->transport->oplock.private_data = tree;
183 status = torture_smb2_testdir(tree, BASEDIR, &h);
184 CHECK_STATUS(status, NT_STATUS_OK);
185 smb2_util_close(tree, h);
187 smb2cli_session_start_replay(tree->session->smbXcli);
189 torture_comment(tctx, "Try Commands with Replay Flags Enabled\n");
191 torture_comment(tctx, "Trying create\n");
192 status = torture_smb2_testfile(tree, fname, &h);
193 CHECK_STATUS(status, NT_STATUS_OK);
194 CHECK_VAL(break_info.count, 0);
196 * Wireshark shows that the response has SMB2_FLAGS_REPLAY_OPERATION
197 * flags set. The server should ignore this flag.
200 torture_comment(tctx, "Trying write\n");
201 status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf));
202 CHECK_STATUS(status, NT_STATUS_OK);
204 f = (struct smb2_flush) {
207 torture_comment(tctx, "Trying flush\n");
208 status = smb2_flush(tree, &f);
209 CHECK_STATUS(status, NT_STATUS_OK);
211 rd = (struct smb2_read) {
217 torture_comment(tctx, "Trying read\n");
218 status = smb2_read(tree, tmp_ctx, &rd);
219 CHECK_STATUS(status, NT_STATUS_OK);
220 CHECK_VAL(rd.out.data.length, 10);
222 sfinfo.generic.level = RAW_SFILEINFO_POSITION_INFORMATION;
223 sfinfo.position_information.in.file.handle = h;
224 sfinfo.position_information.in.position = 0x1000;
225 torture_comment(tctx, "Trying setinfo\n");
226 status = smb2_setinfo_file(tree, &sfinfo);
227 CHECK_STATUS(status, NT_STATUS_OK);
229 qfinfo = (union smb_fileinfo) {
230 .generic.level = RAW_FILEINFO_POSITION_INFORMATION,
231 .generic.in.file.handle = h
233 torture_comment(tctx, "Trying getinfo\n");
234 status = smb2_getinfo_file(tree, tmp_ctx, &qfinfo);
235 CHECK_STATUS(status, NT_STATUS_OK);
236 CHECK_VAL(qfinfo.position_information.out.position, 0x1000);
238 ioctl = (union smb_ioctl) {
239 .smb2.level = RAW_IOCTL_SMB2,
240 .smb2.in.file.handle = h,
241 .smb2.in.function = FSCTL_CREATE_OR_GET_OBJECT_ID,
242 .smb2.in.max_output_response = 64,
243 .smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL
245 torture_comment(tctx, "Trying ioctl\n");
246 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
247 CHECK_STATUS(status, NT_STATUS_OK);
249 lck = (struct smb2_lock) {
251 .in.lock_count = 0x0001,
252 .in.lock_sequence = 0x00000000,
255 el[0].reserved = 0x00000000;
256 el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE |
257 SMB2_LOCK_FLAG_FAIL_IMMEDIATELY;
259 torture_comment(tctx, "Trying lock\n");
260 el[0].offset = 0x0000000000000000;
261 el[0].length = 0x0000000000000100;
262 status = smb2_lock(tree, &lck);
263 CHECK_STATUS(status, NT_STATUS_OK);
265 lck.in.file.handle = h;
266 el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
267 status = smb2_lock(tree, &lck);
268 CHECK_STATUS(status, NT_STATUS_OK);
270 CHECK_VAL(break_info.count, 0);
272 smb2cli_session_stop_replay(tree->session->smbXcli);
273 smb2_util_close(tree, h);
274 smb2_deltree(tree, BASEDIR);
276 talloc_free(tmp_ctx);
282 * Test replay detection without create GUID on single channel.
283 * Regular creates can not be replayed.
284 * The return code is unaffected of the REPLAY_OPERATION flag.
286 static bool test_replay_regular(struct torture_context *tctx,
287 struct smb2_tree *tree)
290 TALLOC_CTX *mem_ctx = talloc_new(tctx);
291 struct smb2_handle _h;
292 struct smb2_handle *h = NULL;
293 struct smb2_create io;
296 const char *fname = BASEDIR "\\replay_regular.dat";
297 struct smb2_transport *transport = tree->session->transport;
299 if (smbXcli_conn_protocol(transport->conn) < PROTOCOL_SMB3_00) {
300 torture_skip(tctx, "SMB 3.X Dialect family required for "
304 ZERO_STRUCT(break_info);
305 break_info.tctx = tctx;
306 tree->session->transport->oplock.handler = torture_oplock_ack_handler;
307 tree->session->transport->oplock.private_data = tree;
309 smb2_util_unlink(tree, fname);
310 status = torture_smb2_testdir(tree, BASEDIR, &_h);
311 CHECK_STATUS(status, NT_STATUS_OK);
312 smb2_util_close(tree, _h);
313 CHECK_VAL(break_info.count, 0);
315 torture_comment(tctx, "No replay detection for regular create\n");
317 perms = SEC_STD_SYNCHRONIZE | SEC_STD_READ_CONTROL | SEC_STD_DELETE |
318 SEC_DIR_WRITE_ATTRIBUTE | SEC_DIR_READ_ATTRIBUTE |
319 SEC_DIR_WRITE_EA | SEC_FILE_APPEND_DATA |
322 io = (struct smb2_create) {
323 .in.desired_access = perms,
324 .in.file_attributes = 0,
325 .in.create_disposition = NTCREATEX_DISP_CREATE,
326 .in.share_access = NTCREATEX_SHARE_ACCESS_DELETE,
327 .in.create_options = 0x0,
331 status = smb2_create(tree, tctx, &io);
332 CHECK_STATUS(status, NT_STATUS_OK);
333 CHECK_VAL(break_info.count, 0);
334 _h = io.out.file.handle;
336 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
338 smb2cli_session_start_replay(tree->session->smbXcli);
339 status = smb2_create(tree, tctx, &io);
340 smb2cli_session_stop_replay(tree->session->smbXcli);
341 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_COLLISION);
342 CHECK_VAL(break_info.count, 0);
344 smb2_util_close(tree, *h);
346 smb2_util_unlink(tree, fname);
349 * Same experiment with different create disposition.
351 io.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
352 status = smb2_create(tree, tctx, &io);
353 CHECK_STATUS(status, NT_STATUS_OK);
354 CHECK_VAL(break_info.count, 0);
355 _h = io.out.file.handle;
357 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
359 smb2cli_session_start_replay(tree->session->smbXcli);
360 status = smb2_create(tree, tctx, &io);
361 smb2cli_session_stop_replay(tree->session->smbXcli);
362 CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
363 CHECK_VAL(break_info.count, 0);
365 smb2_util_close(tree, *h);
367 smb2_util_unlink(tree, fname);
370 * Now with more generous share mode.
372 io.in.share_access = smb2_util_share_access("RWD");
373 status = smb2_create(tree, tctx, &io);
374 CHECK_STATUS(status, NT_STATUS_OK);
375 CHECK_VAL(break_info.count, 0);
376 _h = io.out.file.handle;
378 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
380 smb2cli_session_start_replay(tree->session->smbXcli);
381 status = smb2_create(tree, tctx, &io);
382 smb2cli_session_stop_replay(tree->session->smbXcli);
383 CHECK_STATUS(status, NT_STATUS_OK);
384 CHECK_VAL(break_info.count, 0);
388 smb2_util_close(tree, *h);
390 smb2_deltree(tree, BASEDIR);
393 talloc_free(mem_ctx);
399 * Test Durability V2 Create Replay Detection on Single Channel.
401 static bool test_replay_dhv2_oplock1(struct torture_context *tctx,
402 struct smb2_tree *tree)
405 TALLOC_CTX *mem_ctx = talloc_new(tctx);
406 struct smb2_handle _h;
407 struct smb2_handle *h = NULL;
408 struct smb2_create io, ref1;
409 struct GUID create_guid = GUID_random();
411 const char *fname = BASEDIR "\\replay_dhv2_oplock1.dat";
412 struct smb2_transport *transport = tree->session->transport;
413 uint32_t share_capabilities;
416 if (smbXcli_conn_protocol(transport->conn) < PROTOCOL_SMB3_00) {
417 torture_skip(tctx, "SMB 3.X Dialect family required for "
421 share_capabilities = smb2cli_tcon_capabilities(tree->smbXcli);
422 share_is_so = share_capabilities & SMB2_SHARE_CAP_SCALEOUT;
424 ZERO_STRUCT(break_info);
425 break_info.tctx = tctx;
426 tree->session->transport->oplock.handler = torture_oplock_ack_handler;
427 tree->session->transport->oplock.private_data = tree;
429 torture_comment(tctx, "Replay of DurableHandleReqV2 on Single "
431 smb2_util_unlink(tree, fname);
432 status = torture_smb2_testdir(tree, BASEDIR, &_h);
433 CHECK_STATUS(status, NT_STATUS_OK);
434 smb2_util_close(tree, _h);
435 CHECK_VAL(break_info.count, 0);
437 smb2_oplock_create_share(&io, fname,
438 smb2_util_share_access(""),
439 smb2_util_oplock_level("b"));
440 io.in.durable_open = false;
441 io.in.durable_open_v2 = true;
442 io.in.persistent_open = false;
443 io.in.create_guid = create_guid;
444 io.in.timeout = UINT32_MAX;
446 status = smb2_create(tree, mem_ctx, &io);
447 CHECK_STATUS(status, NT_STATUS_OK);
449 _h = io.out.file.handle;
451 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
452 CHECK_VAL(io.out.durable_open, false);
454 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("s"));
455 CHECK_VAL(io.out.durable_open_v2, false);
456 CHECK_VAL(io.out.timeout, 0);
458 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
459 CHECK_VAL(io.out.durable_open_v2, true);
460 CHECK_VAL(io.out.timeout, io.in.timeout);
464 * Replay Durable V2 Create on single channel
466 smb2cli_session_start_replay(tree->session->smbXcli);
467 status = smb2_create(tree, mem_ctx, &io);
468 smb2cli_session_stop_replay(tree->session->smbXcli);
469 CHECK_STATUS(status, NT_STATUS_OK);
470 CHECK_CREATE_OUT(&io, &ref1);
471 CHECK_VAL(break_info.count, 0);
475 smb2_util_close(tree, *h);
477 smb2_deltree(tree, BASEDIR);
480 talloc_free(mem_ctx);
486 * Test Durability V2 Create Replay Detection on Single Channel.
487 * Hand in a different oplock level in the replay.
488 * Server responds with the handed in oplock level and
489 * corresponding durable status, but does not change the
490 * oplock level or durable status of the opened file.
492 static bool test_replay_dhv2_oplock2(struct torture_context *tctx,
493 struct smb2_tree *tree)
496 TALLOC_CTX *mem_ctx = talloc_new(tctx);
497 struct smb2_handle _h;
498 struct smb2_handle *h = NULL;
499 struct smb2_create io, ref1, ref2;
500 struct GUID create_guid = GUID_random();
502 const char *fname = BASEDIR "\\replay_dhv2_oplock2.dat";
503 struct smb2_transport *transport = tree->session->transport;
504 uint32_t share_capabilities;
507 if (smbXcli_conn_protocol(transport->conn) < PROTOCOL_SMB3_00) {
508 torture_skip(tctx, "SMB 3.X Dialect family required for "
512 share_capabilities = smb2cli_tcon_capabilities(tree->smbXcli);
513 share_is_so = share_capabilities & SMB2_SHARE_CAP_SCALEOUT;
515 ZERO_STRUCT(break_info);
516 break_info.tctx = tctx;
517 tree->session->transport->oplock.handler = torture_oplock_ack_handler;
518 tree->session->transport->oplock.private_data = tree;
520 torture_comment(tctx, "Replay of DurableHandleReqV2 on Single "
522 smb2_util_unlink(tree, fname);
523 status = torture_smb2_testdir(tree, BASEDIR, &_h);
524 CHECK_STATUS(status, NT_STATUS_OK);
525 smb2_util_close(tree, _h);
526 CHECK_VAL(break_info.count, 0);
528 smb2_oplock_create_share(&io, fname,
529 smb2_util_share_access(""),
530 smb2_util_oplock_level("b"));
531 io.in.durable_open = false;
532 io.in.durable_open_v2 = true;
533 io.in.persistent_open = false;
534 io.in.create_guid = create_guid;
535 io.in.timeout = UINT32_MAX;
537 status = smb2_create(tree, mem_ctx, &io);
538 CHECK_STATUS(status, NT_STATUS_OK);
540 _h = io.out.file.handle;
542 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
543 CHECK_VAL(io.out.durable_open, false);
545 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("s"));
546 CHECK_VAL(io.out.durable_open_v2, false);
547 CHECK_VAL(io.out.timeout, 0);
549 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
550 CHECK_VAL(io.out.durable_open_v2, true);
551 CHECK_VAL(io.out.timeout, io.in.timeout);
555 * Replay durable v2 create on single channel:
557 * Replay the create with a different oplock (none).
558 * The server replies with the requested oplock level
559 * and also only replies with durable handle based
560 * on whether it could have been granted based on
561 * the requested oplock type.
563 smb2_oplock_create_share(&io, fname,
564 smb2_util_share_access(""),
565 smb2_util_oplock_level(""));
566 io.in.durable_open = false;
567 io.in.durable_open_v2 = true;
568 io.in.persistent_open = false;
569 io.in.create_guid = create_guid;
570 io.in.timeout = UINT32_MAX;
573 * Adapt the response to the exepected values
576 ref2.out.oplock_level = smb2_util_oplock_level("");
577 ref2.out.durable_open_v2 = false;
578 ref2.out.timeout = 0;
579 ref2.out.blobs.num_blobs = 0;
581 smb2cli_session_start_replay(tree->session->smbXcli);
582 status = smb2_create(tree, mem_ctx, &io);
583 smb2cli_session_stop_replay(tree->session->smbXcli);
584 CHECK_STATUS(status, NT_STATUS_OK);
585 CHECK_CREATE_OUT(&io, &ref2);
586 CHECK_VAL(break_info.count, 0);
589 * Prove that the open file still has a batch oplock
590 * by breaking it with another open.
592 smb2_oplock_create_share(&io, fname,
593 smb2_util_share_access(""),
594 smb2_util_oplock_level("b"));
595 io.in.durable_open = false;
596 io.in.durable_open_v2 = true;
597 io.in.persistent_open = false;
598 io.in.create_guid = GUID_random();
599 io.in.timeout = UINT32_MAX;
600 status = smb2_create(tree, mem_ctx, &io);
601 CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
604 CHECK_VAL(break_info.count, 1);
605 CHECK_HANDLE(&break_info.handle, &ref1.out.file.handle);
606 CHECK_VAL(break_info.level, smb2_util_oplock_level("s"));
607 ZERO_STRUCT(break_info);
612 smb2_util_close(tree, *h);
614 smb2_deltree(tree, BASEDIR);
617 talloc_free(mem_ctx);
623 * Test Durability V2 Create Replay Detection on Single Channel.
624 * Replay with a different share mode. The share mode of
625 * the opened file is not changed by this.
627 static bool test_replay_dhv2_oplock3(struct torture_context *tctx,
628 struct smb2_tree *tree)
631 TALLOC_CTX *mem_ctx = talloc_new(tctx);
632 struct smb2_handle _h;
633 struct smb2_handle *h = NULL;
634 struct smb2_create io, ref1;
635 struct GUID create_guid = GUID_random();
637 const char *fname = BASEDIR "\\replay_dhv2_oplock3.dat";
638 struct smb2_transport *transport = tree->session->transport;
639 uint32_t share_capabilities;
642 if (smbXcli_conn_protocol(transport->conn) < PROTOCOL_SMB3_00) {
643 torture_skip(tctx, "SMB 3.X Dialect family required for "
647 share_capabilities = smb2cli_tcon_capabilities(tree->smbXcli);
648 share_is_so = share_capabilities & SMB2_SHARE_CAP_SCALEOUT;
650 ZERO_STRUCT(break_info);
651 break_info.tctx = tctx;
652 tree->session->transport->oplock.handler = torture_oplock_ack_handler;
653 tree->session->transport->oplock.private_data = tree;
655 torture_comment(tctx, "Replay of DurableHandleReqV2 on Single "
657 smb2_util_unlink(tree, fname);
658 status = torture_smb2_testdir(tree, BASEDIR, &_h);
659 CHECK_STATUS(status, NT_STATUS_OK);
660 smb2_util_close(tree, _h);
661 CHECK_VAL(break_info.count, 0);
663 smb2_oplock_create_share(&io, fname,
664 smb2_util_share_access(""),
665 smb2_util_oplock_level("b"));
666 io.in.durable_open = false;
667 io.in.durable_open_v2 = true;
668 io.in.persistent_open = false;
669 io.in.create_guid = create_guid;
670 io.in.timeout = UINT32_MAX;
672 status = smb2_create(tree, mem_ctx, &io);
673 CHECK_STATUS(status, NT_STATUS_OK);
675 _h = io.out.file.handle;
677 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
678 CHECK_VAL(io.out.durable_open, false);
680 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("s"));
681 CHECK_VAL(io.out.durable_open_v2, false);
682 CHECK_VAL(io.out.timeout, 0);
684 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
685 CHECK_VAL(io.out.durable_open_v2, true);
686 CHECK_VAL(io.out.timeout, io.in.timeout);
690 * Replay durable v2 create on single channel:
692 * Replay the create with a different share mode.
693 * The server replies with the requested share
694 * mode instead of that which is associated to
697 smb2_oplock_create_share(&io, fname,
698 smb2_util_share_access("RWD"),
699 smb2_util_oplock_level("b"));
700 io.in.durable_open = false;
701 io.in.durable_open_v2 = true;
702 io.in.persistent_open = false;
703 io.in.create_guid = create_guid;
704 io.in.timeout = UINT32_MAX;
706 smb2cli_session_start_replay(tree->session->smbXcli);
707 status = smb2_create(tree, mem_ctx, &io);
708 smb2cli_session_stop_replay(tree->session->smbXcli);
709 CHECK_STATUS(status, NT_STATUS_OK);
710 CHECK_CREATE_OUT(&io, &ref1);
711 CHECK_VAL(break_info.count, 0);
714 * In order to prove that the different share mode in the
715 * replayed create had no effect on the open file handle,
716 * show that a new create yields NT_STATUS_SHARING_VIOLATION.
718 smb2_oplock_create_share(&io, fname,
719 smb2_util_share_access(""),
720 smb2_util_oplock_level("b"));
721 io.in.durable_open = false;
722 io.in.durable_open_v2 = true;
723 io.in.persistent_open = false;
724 io.in.create_guid = GUID_random();
725 io.in.timeout = UINT32_MAX;
726 status = smb2_create(tree, mem_ctx, &io);
727 CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
730 CHECK_VAL(break_info.count, 1);
731 CHECK_HANDLE(&break_info.handle, &ref1.out.file.handle);
732 CHECK_VAL(break_info.level, smb2_util_oplock_level("s"));
733 ZERO_STRUCT(break_info);
738 smb2_util_close(tree, *h);
740 smb2_deltree(tree, BASEDIR);
743 talloc_free(mem_ctx);
749 * Test Durability V2 Create Replay Detection on Single Channel.
750 * Create with an oplock, and replay with a lease.
752 static bool test_replay_dhv2_oplock_lease(struct torture_context *tctx,
753 struct smb2_tree *tree)
756 TALLOC_CTX *mem_ctx = talloc_new(tctx);
757 struct smb2_handle _h;
758 struct smb2_handle *h = NULL;
759 struct smb2_create io;
760 struct GUID create_guid = GUID_random();
762 const char *fname = BASEDIR "\\replay_dhv2_oplock1.dat";
763 struct smb2_transport *transport = tree->session->transport;
764 uint32_t share_capabilities;
766 uint32_t server_capabilities;
767 struct smb2_lease ls;
770 if (smbXcli_conn_protocol(transport->conn) < PROTOCOL_SMB3_00) {
771 torture_skip(tctx, "SMB 3.X Dialect family required for "
775 server_capabilities = smb2cli_conn_server_capabilities(transport->conn);
776 if (!(server_capabilities & SMB2_CAP_LEASING)) {
777 torture_skip(tctx, "leases are not supported");
780 share_capabilities = smb2cli_tcon_capabilities(tree->smbXcli);
781 share_is_so = share_capabilities & SMB2_SHARE_CAP_SCALEOUT;
783 ZERO_STRUCT(break_info);
784 break_info.tctx = tctx;
785 tree->session->transport->oplock.handler = torture_oplock_ack_handler;
786 tree->session->transport->oplock.private_data = tree;
788 torture_comment(tctx, "Replay of DurableHandleReqV2 on Single "
790 smb2_util_unlink(tree, fname);
791 status = torture_smb2_testdir(tree, BASEDIR, &_h);
792 CHECK_STATUS(status, NT_STATUS_OK);
793 smb2_util_close(tree, _h);
794 CHECK_VAL(break_info.count, 0);
796 smb2_oplock_create_share(&io, fname,
797 smb2_util_share_access(""),
798 smb2_util_oplock_level("b"));
799 io.in.durable_open = false;
800 io.in.durable_open_v2 = true;
801 io.in.persistent_open = false;
802 io.in.create_guid = create_guid;
803 io.in.timeout = UINT32_MAX;
805 status = smb2_create(tree, mem_ctx, &io);
806 CHECK_STATUS(status, NT_STATUS_OK);
807 _h = io.out.file.handle;
809 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
810 CHECK_VAL(io.out.durable_open, false);
812 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("s"));
813 CHECK_VAL(io.out.durable_open_v2, false);
814 CHECK_VAL(io.out.timeout, 0);
816 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
817 CHECK_VAL(io.out.durable_open_v2, true);
818 CHECK_VAL(io.out.timeout, io.in.timeout);
822 * Replay Durable V2 Create on single channel
823 * but replay it with a lease instead of an oplock.
825 lease_key = random();
826 smb2_lease_create(&io, &ls, false /* dir */, fname,
827 lease_key, smb2_util_lease_state("RH"));
828 io.in.durable_open = false;
829 io.in.durable_open_v2 = true;
830 io.in.persistent_open = false;
831 io.in.create_guid = create_guid;
832 io.in.timeout = UINT32_MAX;
834 smb2cli_session_start_replay(tree->session->smbXcli);
835 status = smb2_create(tree, mem_ctx, &io);
836 smb2cli_session_stop_replay(tree->session->smbXcli);
837 CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED);
841 smb2_util_close(tree, *h);
843 smb2_deltree(tree, BASEDIR);
846 talloc_free(mem_ctx);
853 * Test durability v2 create replay detection on single channel.
854 * Variant with leases instead of oplocks:
855 * - open a file with a rh lease
856 * - upgrade to a rwh lease with a second create
857 * - replay the first create.
858 * ==> it gets back the upgraded lease level
860 static bool test_replay_dhv2_lease1(struct torture_context *tctx,
861 struct smb2_tree *tree)
864 TALLOC_CTX *mem_ctx = talloc_new(tctx);
865 struct smb2_handle _h1;
866 struct smb2_handle *h1 = NULL;
867 struct smb2_handle _h2;
868 struct smb2_handle *h2 = NULL;
869 struct smb2_create io1, io2, ref1;
870 struct GUID create_guid = GUID_random();
872 const char *fname = BASEDIR "\\replay2_lease1.dat";
873 struct smb2_transport *transport = tree->session->transport;
874 uint32_t share_capabilities;
876 uint32_t server_capabilities;
877 struct smb2_lease ls1, ls2;
880 if (smbXcli_conn_protocol(transport->conn) < PROTOCOL_SMB3_00) {
881 torture_skip(tctx, "SMB 3.X Dialect family required for "
885 server_capabilities = smb2cli_conn_server_capabilities(transport->conn);
886 if (!(server_capabilities & SMB2_CAP_LEASING)) {
887 torture_skip(tctx, "leases are not supported");
890 share_capabilities = smb2cli_tcon_capabilities(tree->smbXcli);
891 share_is_so = share_capabilities & SMB2_SHARE_CAP_SCALEOUT;
893 ZERO_STRUCT(break_info);
894 break_info.tctx = tctx;
895 tree->session->transport->oplock.handler = torture_oplock_ack_handler;
896 tree->session->transport->oplock.private_data = tree;
898 torture_comment(tctx, "Replay of DurableHandleReqV2 with Lease "
899 "on Single Channel\n");
900 smb2_util_unlink(tree, fname);
901 status = torture_smb2_testdir(tree, BASEDIR, &_h1);
902 CHECK_STATUS(status, NT_STATUS_OK);
903 smb2_util_close(tree, _h1);
904 CHECK_VAL(break_info.count, 0);
906 lease_key = random();
908 smb2_lease_create(&io1, &ls1, false /* dir */, fname,
909 lease_key, smb2_util_lease_state("RH"));
910 io1.in.durable_open = false;
911 io1.in.durable_open_v2 = true;
912 io1.in.persistent_open = false;
913 io1.in.create_guid = create_guid;
914 io1.in.timeout = UINT32_MAX;
916 status = smb2_create(tree, mem_ctx, &io1);
917 CHECK_STATUS(status, NT_STATUS_OK);
919 _h1 = io1.out.file.handle;
921 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
922 CHECK_VAL(io1.out.durable_open, false);
923 CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
924 CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease_key);
925 CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease_key);
927 CHECK_VAL(io1.out.lease_response.lease_state,
928 smb2_util_lease_state("R"));
929 CHECK_VAL(io1.out.durable_open_v2, false);
930 CHECK_VAL(io1.out.timeout, 0);
932 CHECK_VAL(io1.out.lease_response.lease_state,
933 smb2_util_lease_state("RH"));
934 CHECK_VAL(io1.out.durable_open_v2, true);
935 CHECK_VAL(io1.out.timeout, io1.in.timeout);
939 * Upgrade the lease to RWH
941 smb2_lease_create(&io2, &ls2, false /* dir */, fname,
942 lease_key, smb2_util_lease_state("RHW"));
943 io2.in.durable_open = false;
944 io2.in.durable_open_v2 = true;
945 io2.in.persistent_open = false;
946 io2.in.create_guid = GUID_random(); /* new guid... */
947 io2.in.timeout = UINT32_MAX;
949 status = smb2_create(tree, mem_ctx, &io2);
950 CHECK_STATUS(status, NT_STATUS_OK);
951 _h2 = io2.out.file.handle;
955 * Replay Durable V2 Create on single channel.
956 * We get the io from open #1 but with the
960 /* adapt expected lease in response */
962 ref1.out.lease_response.lease_state =
963 smb2_util_lease_state("RHW");
966 smb2cli_session_start_replay(tree->session->smbXcli);
967 status = smb2_create(tree, mem_ctx, &io1);
968 smb2cli_session_stop_replay(tree->session->smbXcli);
969 CHECK_STATUS(status, NT_STATUS_OK);
970 CHECK_CREATE_OUT(&io1, &ref1);
971 CHECK_VAL(break_info.count, 0);
974 smb2cli_session_stop_replay(tree->session->smbXcli);
977 smb2_util_close(tree, *h1);
980 smb2_util_close(tree, *h2);
982 smb2_deltree(tree, BASEDIR);
985 talloc_free(mem_ctx);
991 * Test durability v2 create replay detection on single channel.
992 * Variant with leases instead of oplocks, where the
993 * replay does not specify the original lease level but
994 * just a "R" lease. This still gives the upgraded lease
995 * level in the reply.
996 * - open a file with a rh lease
997 * - upgrade to a rwh lease with a second create
998 * - replay the first create.
999 * ==> it gets back the upgraded lease level
1001 static bool test_replay_dhv2_lease2(struct torture_context *tctx,
1002 struct smb2_tree *tree)
1005 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1006 struct smb2_handle _h1;
1007 struct smb2_handle *h1 = NULL;
1008 struct smb2_handle _h2;
1009 struct smb2_handle *h2 = NULL;
1010 struct smb2_create io1, io2, ref1;
1011 struct GUID create_guid = GUID_random();
1013 const char *fname = BASEDIR "\\replay2_lease2.dat";
1014 struct smb2_transport *transport = tree->session->transport;
1015 uint32_t share_capabilities;
1017 uint32_t server_capabilities;
1018 struct smb2_lease ls1, ls2;
1021 if (smbXcli_conn_protocol(transport->conn) < PROTOCOL_SMB3_00) {
1022 torture_skip(tctx, "SMB 3.X Dialect family required for "
1026 server_capabilities = smb2cli_conn_server_capabilities(transport->conn);
1027 if (!(server_capabilities & SMB2_CAP_LEASING)) {
1028 torture_skip(tctx, "leases are not supported");
1031 share_capabilities = smb2cli_tcon_capabilities(tree->smbXcli);
1032 share_is_so = share_capabilities & SMB2_SHARE_CAP_SCALEOUT;
1034 ZERO_STRUCT(break_info);
1035 break_info.tctx = tctx;
1036 tree->session->transport->oplock.handler = torture_oplock_ack_handler;
1037 tree->session->transport->oplock.private_data = tree;
1039 torture_comment(tctx, "Replay of DurableHandleReqV2 with Lease "
1040 "on Single Channel\n");
1041 smb2_util_unlink(tree, fname);
1042 status = torture_smb2_testdir(tree, BASEDIR, &_h1);
1043 CHECK_STATUS(status, NT_STATUS_OK);
1044 smb2_util_close(tree, _h1);
1045 CHECK_VAL(break_info.count, 0);
1047 lease_key = random();
1049 smb2_lease_create(&io1, &ls1, false /* dir */, fname,
1050 lease_key, smb2_util_lease_state("RH"));
1051 io1.in.durable_open = false;
1052 io1.in.durable_open_v2 = true;
1053 io1.in.persistent_open = false;
1054 io1.in.create_guid = create_guid;
1055 io1.in.timeout = UINT32_MAX;
1057 status = smb2_create(tree, mem_ctx, &io1);
1058 CHECK_STATUS(status, NT_STATUS_OK);
1059 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1060 CHECK_VAL(io1.out.durable_open, false);
1061 CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1062 CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease_key);
1063 CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease_key);
1065 CHECK_VAL(io1.out.lease_response.lease_state,
1066 smb2_util_lease_state("R"));
1067 CHECK_VAL(io1.out.durable_open_v2, false);
1068 CHECK_VAL(io1.out.timeout, 0);
1070 CHECK_VAL(io1.out.lease_response.lease_state,
1071 smb2_util_lease_state("RH"));
1072 CHECK_VAL(io1.out.durable_open_v2, true);
1073 CHECK_VAL(io1.out.timeout, io1.in.timeout);
1076 _h1 = io1.out.file.handle;
1080 * Upgrade the lease to RWH
1082 smb2_lease_create(&io2, &ls2, false /* dir */, fname,
1083 lease_key, smb2_util_lease_state("RHW"));
1084 io2.in.durable_open = false;
1085 io2.in.durable_open_v2 = true;
1086 io2.in.persistent_open = false;
1087 io2.in.create_guid = GUID_random(); /* new guid... */
1088 io2.in.timeout = UINT32_MAX;
1090 status = smb2_create(tree, mem_ctx, &io2);
1091 CHECK_STATUS(status, NT_STATUS_OK);
1092 _h2 = io2.out.file.handle;
1096 * Replay Durable V2 Create on single channel.
1097 * Changing the requested lease level to "R"
1098 * does not change the response:
1099 * We get the reply from open #1 but with the
1103 /* adapt the expected response */
1105 ref1.out.lease_response.lease_state =
1106 smb2_util_lease_state("RHW");
1109 smb2_lease_create(&io1, &ls1, false /* dir */, fname,
1110 lease_key, smb2_util_lease_state("R"));
1111 io1.in.durable_open = false;
1112 io1.in.durable_open_v2 = true;
1113 io1.in.persistent_open = false;
1114 io1.in.create_guid = create_guid;
1115 io1.in.timeout = UINT32_MAX;
1117 smb2cli_session_start_replay(tree->session->smbXcli);
1118 status = smb2_create(tree, mem_ctx, &io1);
1119 smb2cli_session_stop_replay(tree->session->smbXcli);
1120 CHECK_STATUS(status, NT_STATUS_OK);
1121 CHECK_CREATE_OUT(&io1, &ref1);
1122 CHECK_VAL(break_info.count, 0);
1125 smb2cli_session_stop_replay(tree->session->smbXcli);
1128 smb2_util_close(tree, *h1);
1131 smb2_util_close(tree, *h2);
1133 smb2_deltree(tree, BASEDIR);
1136 talloc_free(mem_ctx);
1142 * Test durability v2 create replay detection on single channel.
1143 * create with a lease, and replay with a different lease key
1145 static bool test_replay_dhv2_lease3(struct torture_context *tctx,
1146 struct smb2_tree *tree)
1149 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1150 struct smb2_handle _h1;
1151 struct smb2_handle *h1 = NULL;
1152 struct smb2_handle _h2;
1153 struct smb2_handle *h2 = NULL;
1154 struct smb2_create io1, io2;
1155 struct GUID create_guid = GUID_random();
1157 const char *fname = BASEDIR "\\replay2_lease2.dat";
1158 struct smb2_transport *transport = tree->session->transport;
1159 uint32_t share_capabilities;
1161 uint32_t server_capabilities;
1162 struct smb2_lease ls1, ls2;
1165 if (smbXcli_conn_protocol(transport->conn) < PROTOCOL_SMB3_00) {
1166 torture_skip(tctx, "SMB 3.X Dialect family required for "
1170 server_capabilities = smb2cli_conn_server_capabilities(transport->conn);
1171 if (!(server_capabilities & SMB2_CAP_LEASING)) {
1172 torture_skip(tctx, "leases are not supported");
1175 share_capabilities = smb2cli_tcon_capabilities(tree->smbXcli);
1176 share_is_so = share_capabilities & SMB2_SHARE_CAP_SCALEOUT;
1178 ZERO_STRUCT(break_info);
1179 break_info.tctx = tctx;
1180 tree->session->transport->oplock.handler = torture_oplock_ack_handler;
1181 tree->session->transport->oplock.private_data = tree;
1183 torture_comment(tctx, "Replay of DurableHandleReqV2 with Lease "
1184 "on Single Channel\n");
1185 smb2_util_unlink(tree, fname);
1186 status = torture_smb2_testdir(tree, BASEDIR, &_h1);
1187 CHECK_STATUS(status, NT_STATUS_OK);
1188 smb2_util_close(tree, _h1);
1189 CHECK_VAL(break_info.count, 0);
1191 lease_key = random();
1193 smb2_lease_create(&io1, &ls1, false /* dir */, fname,
1194 lease_key, smb2_util_lease_state("RH"));
1195 io1.in.durable_open = false;
1196 io1.in.durable_open_v2 = true;
1197 io1.in.persistent_open = false;
1198 io1.in.create_guid = create_guid;
1199 io1.in.timeout = UINT32_MAX;
1201 status = smb2_create(tree, mem_ctx, &io1);
1202 CHECK_STATUS(status, NT_STATUS_OK);
1203 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1204 CHECK_VAL(io1.out.durable_open, false);
1205 CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1206 CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease_key);
1207 CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease_key);
1209 CHECK_VAL(io1.out.lease_response.lease_state,
1210 smb2_util_lease_state("R"));
1211 CHECK_VAL(io1.out.durable_open_v2, false);
1212 CHECK_VAL(io1.out.timeout, 0);
1214 CHECK_VAL(io1.out.lease_response.lease_state,
1215 smb2_util_lease_state("RH"));
1216 CHECK_VAL(io1.out.durable_open_v2, true);
1217 CHECK_VAL(io1.out.timeout, io1.in.timeout);
1219 _h1 = io1.out.file.handle;
1223 * Upgrade the lease to RWH
1225 smb2_lease_create(&io2, &ls2, false /* dir */, fname,
1226 lease_key, smb2_util_lease_state("RHW"));
1227 io2.in.durable_open = false;
1228 io2.in.durable_open_v2 = true;
1229 io2.in.persistent_open = false;
1230 io2.in.create_guid = GUID_random(); /* new guid... */
1231 io2.in.timeout = UINT32_MAX;
1233 status = smb2_create(tree, mem_ctx, &io2);
1234 CHECK_STATUS(status, NT_STATUS_OK);
1235 _h2 = io2.out.file.handle;
1239 * Replay Durable V2 Create on single channel.
1240 * use a different lease key.
1243 smb2_lease_create(&io1, &ls1, false /* dir */, fname,
1244 random() /* lease key */,
1245 smb2_util_lease_state("RH"));
1246 io1.in.durable_open = false;
1247 io1.in.durable_open_v2 = true;
1248 io1.in.persistent_open = false;
1249 io1.in.create_guid = create_guid;
1250 io1.in.timeout = UINT32_MAX;
1252 smb2cli_session_start_replay(tree->session->smbXcli);
1253 status = smb2_create(tree, mem_ctx, &io1);
1254 smb2cli_session_stop_replay(tree->session->smbXcli);
1255 CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED);
1258 smb2cli_session_stop_replay(tree->session->smbXcli);
1261 smb2_util_close(tree, *h1);
1264 smb2_util_close(tree, *h2);
1266 smb2_deltree(tree, BASEDIR);
1269 talloc_free(mem_ctx);
1275 * Test durability v2 create replay detection on single channel.
1276 * Do the original create with a lease, and do the replay
1279 static bool test_replay_dhv2_lease_oplock(struct torture_context *tctx,
1280 struct smb2_tree *tree)
1283 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1284 struct smb2_handle _h1;
1285 struct smb2_handle *h1 = NULL;
1286 struct smb2_handle _h2;
1287 struct smb2_handle *h2 = NULL;
1288 struct smb2_create io1, io2, ref1;
1289 struct GUID create_guid = GUID_random();
1291 const char *fname = BASEDIR "\\replay2_lease1.dat";
1292 struct smb2_transport *transport = tree->session->transport;
1293 uint32_t share_capabilities;
1295 uint32_t server_capabilities;
1296 struct smb2_lease ls1, ls2;
1299 if (smbXcli_conn_protocol(transport->conn) < PROTOCOL_SMB3_00) {
1300 torture_skip(tctx, "SMB 3.X Dialect family required for "
1304 server_capabilities = smb2cli_conn_server_capabilities(transport->conn);
1305 if (!(server_capabilities & SMB2_CAP_LEASING)) {
1306 torture_skip(tctx, "leases are not supported");
1309 share_capabilities = smb2cli_tcon_capabilities(tree->smbXcli);
1310 share_is_so = share_capabilities & SMB2_SHARE_CAP_SCALEOUT;
1312 ZERO_STRUCT(break_info);
1313 break_info.tctx = tctx;
1314 tree->session->transport->oplock.handler = torture_oplock_ack_handler;
1315 tree->session->transport->oplock.private_data = tree;
1317 torture_comment(tctx, "Replay of DurableHandleReqV2 with Lease "
1318 "on Single Channel\n");
1319 smb2_util_unlink(tree, fname);
1320 status = torture_smb2_testdir(tree, BASEDIR, &_h1);
1321 CHECK_STATUS(status, NT_STATUS_OK);
1322 smb2_util_close(tree, _h1);
1323 CHECK_VAL(break_info.count, 0);
1325 lease_key = random();
1327 smb2_lease_create(&io1, &ls1, false /* dir */, fname,
1328 lease_key, smb2_util_lease_state("RH"));
1329 io1.in.durable_open = false;
1330 io1.in.durable_open_v2 = true;
1331 io1.in.persistent_open = false;
1332 io1.in.create_guid = create_guid;
1333 io1.in.timeout = UINT32_MAX;
1335 status = smb2_create(tree, mem_ctx, &io1);
1336 CHECK_STATUS(status, NT_STATUS_OK);
1338 _h1 = io1.out.file.handle;
1340 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1341 CHECK_VAL(io1.out.durable_open, false);
1342 CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1343 CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease_key);
1344 CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease_key);
1346 CHECK_VAL(io1.out.lease_response.lease_state,
1347 smb2_util_lease_state("R"));
1348 CHECK_VAL(io1.out.durable_open_v2, false);
1349 CHECK_VAL(io1.out.timeout, 0);
1351 CHECK_VAL(io1.out.lease_response.lease_state,
1352 smb2_util_lease_state("RH"));
1353 CHECK_VAL(io1.out.durable_open_v2, true);
1354 CHECK_VAL(io1.out.timeout, io1.in.timeout);
1358 * Upgrade the lease to RWH
1360 smb2_lease_create(&io2, &ls2, false /* dir */, fname,
1361 lease_key, smb2_util_lease_state("RHW"));
1362 io2.in.durable_open = false;
1363 io2.in.durable_open_v2 = true;
1364 io2.in.persistent_open = false;
1365 io2.in.create_guid = GUID_random(); /* new guid... */
1366 io2.in.timeout = UINT32_MAX;
1368 status = smb2_create(tree, mem_ctx, &io2);
1369 CHECK_STATUS(status, NT_STATUS_OK);
1370 _h2 = io2.out.file.handle;
1374 * Replay Durable V2 Create on single channel.
1375 * We get the io from open #1 but with the
1379 smb2_oplock_create_share(&io2, fname,
1380 smb2_util_share_access(""),
1381 smb2_util_oplock_level("b"));
1382 io2.in.durable_open = false;
1383 io2.in.durable_open_v2 = true;
1384 io2.in.persistent_open = false;
1385 io2.in.create_guid = create_guid;
1386 io2.in.timeout = UINT32_MAX;
1388 /* adapt expected lease in response */
1390 ref1.out.lease_response.lease_state =
1391 smb2_util_lease_state("RHW");
1394 smb2cli_session_start_replay(tree->session->smbXcli);
1395 status = smb2_create(tree, mem_ctx, &io1);
1396 smb2cli_session_stop_replay(tree->session->smbXcli);
1397 CHECK_STATUS(status, NT_STATUS_OK);
1398 CHECK_CREATE_OUT(&io1, &ref1);
1399 CHECK_VAL(break_info.count, 0);
1402 smb2cli_session_stop_replay(tree->session->smbXcli);
1405 smb2_util_close(tree, *h1);
1408 smb2_util_close(tree, *h2);
1410 smb2_deltree(tree, BASEDIR);
1413 talloc_free(mem_ctx);
1418 static bool test_channel_sequence_table(struct torture_context *tctx,
1419 struct smb2_tree *tree,
1423 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
1424 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1425 struct smb2_handle handle;
1426 struct smb2_handle *phandle = NULL;
1427 struct smb2_create io;
1428 struct GUID create_guid = GUID_random();
1430 const char *fname = BASEDIR "\\channel_sequence.dat";
1432 uint16_t limit = UINT16_MAX - 0x7fff;
1438 NTSTATUS expected_status;
1442 .expected_status = NT_STATUS_OK,
1445 .expected_status = NT_STATUS_FILE_NOT_AVAILABLE,
1448 .expected_status = NT_STATUS_FILE_NOT_AVAILABLE,
1451 .csn_rand_high = true,
1452 .expected_status = NT_STATUS_FILE_NOT_AVAILABLE,
1455 .expected_status = NT_STATUS_FILE_NOT_AVAILABLE,
1458 .expected_status = NT_STATUS_OK,
1461 .expected_status = NT_STATUS_FILE_NOT_AVAILABLE,
1464 .expected_status = NT_STATUS_FILE_NOT_AVAILABLE,
1467 .csn_rand_low = true,
1468 .expected_status = NT_STATUS_FILE_NOT_AVAILABLE,
1471 .expected_status = NT_STATUS_OK,
1474 .expected_status = NT_STATUS_OK,
1477 .expected_status = NT_STATUS_OK,
1480 .expected_status = NT_STATUS_OK,
1483 .expected_status = NT_STATUS_FILE_NOT_AVAILABLE,
1486 .expected_status = NT_STATUS_OK,
1489 .expected_status = NT_STATUS_FILE_NOT_AVAILABLE,
1493 smb2cli_session_reset_channel_sequence(tree->session->smbXcli, 0);
1495 csn = smb2cli_session_current_channel_sequence(tree->session->smbXcli);
1496 torture_comment(tctx, "Testing create with channel sequence number: 0x%04x\n", csn);
1498 smb2_oplock_create_share(&io, fname,
1499 smb2_util_share_access("RWD"),
1500 smb2_util_oplock_level("b"));
1501 io.in.durable_open = false;
1502 io.in.durable_open_v2 = true;
1503 io.in.create_guid = create_guid;
1504 io.in.timeout = UINT32_MAX;
1506 torture_assert_ntstatus_ok_goto(tctx,
1507 smb2_create(tree, mem_ctx, &io),
1508 ret, done, "failed to call smb2_create");
1510 handle = io.out.file.handle;
1513 for (i=0; i <ARRAY_SIZE(tests); i++) {
1515 const char *opstr = "";
1516 union smb_fileinfo qfinfo;
1520 if (tests[i].csn_rand_low) {
1521 csn = rand() % limit;
1522 } else if (tests[i].csn_rand_high) {
1523 csn = rand() % limit + 0x7fff;
1533 case SMB2_OP_SETINFO:
1540 smb2cli_session_reset_channel_sequence(tree->session->smbXcli, csn);
1541 csn = smb2cli_session_current_channel_sequence(tree->session->smbXcli);
1543 torture_comment(tctx, "Testing %s (replay: %s) with CSN 0x%04x, expecting: %s\n",
1544 opstr, do_replay ? "true" : "false", csn,
1545 nt_errstr(tests[i].expected_status));
1548 smb2cli_session_start_replay(tree->session->smbXcli);
1552 case SMB2_OP_WRITE: {
1553 DATA_BLOB blob = data_blob_talloc(tctx, NULL, 255);
1555 generate_random_buffer(blob.data, blob.length);
1557 status = smb2_util_write(tree, handle, blob.data, 0, blob.length);
1558 if (NT_STATUS_IS_OK(status)) {
1559 struct smb2_read rd;
1561 rd = (struct smb2_read) {
1562 .in.file.handle = handle,
1563 .in.length = blob.length,
1567 torture_assert_ntstatus_ok_goto(tctx,
1568 smb2_read(tree, tree, &rd),
1569 ret, done, "failed to read after write");
1571 torture_assert_data_blob_equal(tctx,
1573 "read/write mismatch");
1577 case SMB2_OP_IOCTL: {
1578 union smb_ioctl ioctl;
1579 ioctl = (union smb_ioctl) {
1580 .smb2.level = RAW_IOCTL_SMB2,
1581 .smb2.in.file.handle = handle,
1582 .smb2.in.function = FSCTL_CREATE_OR_GET_OBJECT_ID,
1583 .smb2.in.max_output_response = 64,
1584 .smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL
1586 status = smb2_ioctl(tree, mem_ctx, &ioctl.smb2);
1589 case SMB2_OP_SETINFO: {
1590 union smb_setfileinfo sfinfo;
1591 ZERO_STRUCT(sfinfo);
1592 sfinfo.generic.level = RAW_SFILEINFO_POSITION_INFORMATION;
1593 sfinfo.generic.in.file.handle = handle;
1594 sfinfo.position_information.in.position = 0x1000;
1595 status = smb2_setinfo_file(tree, &sfinfo);
1602 qfinfo = (union smb_fileinfo) {
1603 .generic.level = RAW_FILEINFO_POSITION_INFORMATION,
1604 .generic.in.file.handle = handle
1607 torture_assert_ntstatus_ok_goto(tctx,
1608 smb2_getinfo_file(tree, mem_ctx, &qfinfo),
1609 ret, done, "failed to read after write");
1612 smb2cli_session_stop_replay(tree->session->smbXcli);
1615 torture_assert_ntstatus_equal_goto(tctx,
1616 status, tests[i].expected_status,
1617 ret, done, "got unexpected failure code");
1621 if (phandle != NULL) {
1622 smb2_util_close(tree, *phandle);
1625 smb2_util_unlink(tree, fname);
1630 static bool test_channel_sequence(struct torture_context *tctx,
1631 struct smb2_tree *tree)
1633 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1635 const char *fname = BASEDIR "\\channel_sequence.dat";
1636 struct smb2_transport *transport1 = tree->session->transport;
1637 struct smb2_handle handle;
1638 uint32_t server_capabilities;
1639 uint16_t opcodes[] = { SMB2_OP_WRITE, SMB2_OP_IOCTL, SMB2_OP_SETINFO };
1642 if (smbXcli_conn_protocol(transport1->conn) < PROTOCOL_SMB3_00) {
1643 torture_skip(tctx, "SMB 3.X Dialect family required for "
1647 server_capabilities = smb2cli_conn_server_capabilities(
1648 tree->session->transport->conn);
1649 if (!(server_capabilities & SMB2_CAP_MULTI_CHANNEL)) {
1651 "Server does not support multi-channel.");
1654 torture_comment(tctx, "Testing channel sequence numbers\n");
1656 torture_assert_ntstatus_ok_goto(tctx,
1657 torture_smb2_testdir(tree, BASEDIR, &handle),
1658 ret, done, "failed to setup test directory");
1660 smb2_util_close(tree, handle);
1661 smb2_util_unlink(tree, fname);
1663 for (i=0; i <ARRAY_SIZE(opcodes); i++) {
1664 torture_assert(tctx,
1665 test_channel_sequence_table(tctx, tree, false, opcodes[i]),
1666 "failed to test CSN without replay flag");
1667 torture_assert(tctx,
1668 test_channel_sequence_table(tctx, tree, true, opcodes[i]),
1669 "failed to test CSN with replay flag");
1674 smb2_util_unlink(tree, fname);
1675 smb2_deltree(tree, BASEDIR);
1678 talloc_free(mem_ctx);
1684 * Test Durability V2 Create Replay Detection on Multi Channel
1686 static bool test_replay3(struct torture_context *tctx, struct smb2_tree *tree1)
1688 const char *host = torture_setting_string(tctx, "host", NULL);
1689 const char *share = torture_setting_string(tctx, "share", NULL);
1691 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1692 struct smb2_handle _h;
1693 struct smb2_handle *h = NULL;
1694 struct smb2_create io;
1695 struct GUID create_guid = GUID_random();
1697 const char *fname = BASEDIR "\\replay3.dat";
1698 struct smb2_tree *tree2 = NULL;
1699 struct smb2_transport *transport1 = tree1->session->transport;
1700 struct smb2_transport *transport2 = NULL;
1701 struct smb2_session *session1_1 = tree1->session;
1702 struct smb2_session *session1_2 = NULL;
1703 uint32_t share_capabilities;
1705 uint32_t server_capabilities;
1707 if (smbXcli_conn_protocol(transport1->conn) < PROTOCOL_SMB3_00) {
1708 torture_skip(tctx, "SMB 3.X Dialect family required for "
1712 server_capabilities = smb2cli_conn_server_capabilities(
1713 tree1->session->transport->conn);
1714 if (!(server_capabilities & SMB2_CAP_MULTI_CHANNEL)) {
1716 "Server does not support multi-channel.");
1719 share_capabilities = smb2cli_tcon_capabilities(tree1->smbXcli);
1720 share_is_so = share_capabilities & SMB2_SHARE_CAP_SCALEOUT;
1722 ZERO_STRUCT(break_info);
1723 break_info.tctx = tctx;
1724 transport1->oplock.handler = torture_oplock_ack_handler;
1725 transport1->oplock.private_data = tree1;
1727 torture_comment(tctx, "Replay of DurableHandleReqV2 on Multi "
1729 status = torture_smb2_testdir(tree1, BASEDIR, &_h);
1730 CHECK_STATUS(status, NT_STATUS_OK);
1731 smb2_util_close(tree1, _h);
1732 smb2_util_unlink(tree1, fname);
1733 CHECK_VAL(break_info.count, 0);
1736 * use the 1st channel, 1st session
1738 smb2_oplock_create_share(&io, fname,
1739 smb2_util_share_access(""),
1740 smb2_util_oplock_level("b"));
1741 io.in.durable_open = false;
1742 io.in.durable_open_v2 = true;
1743 io.in.persistent_open = false;
1744 io.in.create_guid = create_guid;
1745 io.in.timeout = UINT32_MAX;
1747 tree1->session = session1_1;
1748 status = smb2_create(tree1, mem_ctx, &io);
1749 CHECK_STATUS(status, NT_STATUS_OK);
1750 _h = io.out.file.handle;
1752 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1754 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("s"));
1755 CHECK_VAL(io.out.durable_open_v2, false);
1756 CHECK_VAL(io.out.timeout, 0);
1758 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
1759 CHECK_VAL(io.out.durable_open_v2, true);
1760 CHECK_VAL(io.out.timeout, io.in.timeout);
1762 CHECK_VAL(io.out.durable_open, false);
1763 CHECK_VAL(break_info.count, 0);
1765 status = smb2_connect(tctx,
1767 lpcfg_smb_ports(tctx->lp_ctx),
1769 lpcfg_resolve_context(tctx->lp_ctx),
1770 popt_get_cmdline_credentials(),
1773 &transport1->options,
1774 lpcfg_socket_options(tctx->lp_ctx),
1775 lpcfg_gensec_settings(tctx, tctx->lp_ctx)
1777 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1778 "smb2_connect failed");
1779 transport2 = tree2->session->transport;
1781 transport2->oplock.handler = torture_oplock_ack_handler;
1782 transport2->oplock.private_data = tree2;
1785 * Now bind the 1st session to 2nd transport channel
1787 session1_2 = smb2_session_channel(transport2,
1788 lpcfg_gensec_settings(tctx, tctx->lp_ctx),
1790 torture_assert(tctx, session1_2 != NULL, "smb2_session_channel failed");
1792 status = smb2_session_setup_spnego(session1_2,
1793 popt_get_cmdline_credentials(),
1794 0 /* previous_session_id */);
1795 CHECK_STATUS(status, NT_STATUS_OK);
1798 * use the 2nd channel, 1st session
1800 tree1->session = session1_2;
1801 smb2cli_session_start_replay(tree1->session->smbXcli);
1802 status = smb2_create(tree1, mem_ctx, &io);
1803 smb2cli_session_stop_replay(tree1->session->smbXcli);
1804 CHECK_STATUS(status, NT_STATUS_OK);
1805 _h = io.out.file.handle;
1807 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1809 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("s"));
1810 CHECK_VAL(io.out.durable_open_v2, false);
1811 CHECK_VAL(io.out.timeout, 0);
1813 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
1814 CHECK_VAL(io.out.durable_open_v2, true);
1815 CHECK_VAL(io.out.timeout, io.in.timeout);
1817 CHECK_VAL(io.out.durable_open, false);
1818 CHECK_VAL(break_info.count, 0);
1820 tree1->session = session1_1;
1821 smb2_util_close(tree1, *h);
1826 tree1->session = session1_1;
1829 smb2_util_close(tree1, *h);
1832 smb2_util_unlink(tree1, fname);
1833 smb2_deltree(tree1, BASEDIR);
1836 talloc_free(mem_ctx);
1842 * Test Multichannel IO Ordering using ChannelSequence/Channel Epoch number
1844 static bool test_replay4(struct torture_context *tctx, struct smb2_tree *tree1)
1846 const char *host = torture_setting_string(tctx, "host", NULL);
1847 const char *share = torture_setting_string(tctx, "share", NULL);
1849 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1850 struct smb2_handle _h1;
1851 struct smb2_handle *h1 = NULL;
1852 struct smb2_create io;
1853 struct GUID create_guid = GUID_random();
1855 struct smb2_read rd;
1856 union smb_setfileinfo sfinfo;
1858 const char *fname = BASEDIR "\\replay4.dat";
1859 struct smb2_tree *tree2 = NULL;
1860 struct smb2_transport *transport1 = tree1->session->transport;
1861 struct smb2_transport *transport2 = NULL;
1862 struct smb2_session *session1_1 = tree1->session;
1863 struct smb2_session *session1_2 = NULL;
1865 uint32_t share_capabilities;
1867 uint32_t server_capabilities;
1869 if (smbXcli_conn_protocol(transport1->conn) < PROTOCOL_SMB3_00) {
1870 torture_skip(tctx, "SMB 3.X Dialect family required for "
1874 server_capabilities = smb2cli_conn_server_capabilities(
1875 tree1->session->transport->conn);
1876 if (!(server_capabilities & SMB2_CAP_MULTI_CHANNEL)) {
1878 "Server does not support multi-channel.");
1881 share_capabilities = smb2cli_tcon_capabilities(tree1->smbXcli);
1882 share_is_so = share_capabilities & SMB2_SHARE_CAP_SCALEOUT;
1884 ZERO_STRUCT(break_info);
1885 break_info.tctx = tctx;
1886 transport1->oplock.handler = torture_oplock_ack_handler;
1887 transport1->oplock.private_data = tree1;
1889 torture_comment(tctx, "IO Ordering for Multi Channel\n");
1890 status = torture_smb2_testdir(tree1, BASEDIR, &_h1);
1891 CHECK_STATUS(status, NT_STATUS_OK);
1892 smb2_util_close(tree1, _h1);
1893 smb2_util_unlink(tree1, fname);
1894 CHECK_VAL(break_info.count, 0);
1897 * use the 1st channel, 1st session
1900 smb2_oplock_create_share(&io, fname,
1901 smb2_util_share_access(""),
1902 smb2_util_oplock_level("b"));
1903 io.in.durable_open = false;
1904 io.in.durable_open_v2 = true;
1905 io.in.persistent_open = false;
1906 io.in.create_guid = create_guid;
1907 io.in.timeout = UINT32_MAX;
1909 tree1->session = session1_1;
1910 status = smb2_create(tree1, mem_ctx, &io);
1911 CHECK_STATUS(status, NT_STATUS_OK);
1912 _h1 = io.out.file.handle;
1914 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1916 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("s"));
1917 CHECK_VAL(io.out.durable_open_v2, false);
1918 CHECK_VAL(io.out.timeout, 0);
1920 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
1921 CHECK_VAL(io.out.durable_open_v2, true);
1922 CHECK_VAL(io.out.timeout, io.in.timeout);
1924 CHECK_VAL(io.out.durable_open, false);
1925 CHECK_VAL(break_info.count, 0);
1927 status = smb2_util_write(tree1, *h1, buf, 0, ARRAY_SIZE(buf));
1928 CHECK_STATUS(status, NT_STATUS_OK);
1931 * Increment ChannelSequence so that server thinks that there's a
1934 smb2cli_session_increment_channel_sequence(tree1->session->smbXcli);
1937 * Perform a Read with incremented ChannelSequence
1939 rd = (struct smb2_read) {
1940 .in.file.handle = *h1,
1941 .in.length = sizeof(buf),
1944 status = smb2_read(tree1, tree1, &rd);
1945 CHECK_STATUS(status, NT_STATUS_OK);
1948 * Performing a Write with Stale ChannelSequence is not allowed by
1951 curr_cs = smb2cli_session_reset_channel_sequence(
1952 tree1->session->smbXcli, 0);
1953 status = smb2_util_write(tree1, *h1, buf, 0, ARRAY_SIZE(buf));
1954 CHECK_STATUS(status, NT_STATUS_FILE_NOT_AVAILABLE);
1957 * Performing a Write Replay with Stale ChannelSequence is not allowed
1960 smb2cli_session_start_replay(tree1->session->smbXcli);
1961 smb2cli_session_reset_channel_sequence(tree1->session->smbXcli, 0);
1962 status = smb2_util_write(tree1, *h1, buf, 0, ARRAY_SIZE(buf));
1963 smb2cli_session_stop_replay(tree1->session->smbXcli);
1964 CHECK_STATUS(status, NT_STATUS_FILE_NOT_AVAILABLE);
1967 * Performing a SetInfo with stale ChannelSequence is not allowed by
1970 ZERO_STRUCT(sfinfo);
1971 sfinfo.generic.level = RAW_SFILEINFO_POSITION_INFORMATION;
1972 sfinfo.generic.in.file.handle = *h1;
1973 sfinfo.position_information.in.position = 0x1000;
1974 status = smb2_setinfo_file(tree1, &sfinfo);
1975 CHECK_STATUS(status, NT_STATUS_FILE_NOT_AVAILABLE);
1978 * Performing a Read with stale ChannelSequence is allowed
1980 rd = (struct smb2_read) {
1981 .in.file.handle = *h1,
1982 .in.length = ARRAY_SIZE(buf),
1985 status = smb2_read(tree1, tree1, &rd);
1986 CHECK_STATUS(status, NT_STATUS_OK);
1988 status = smb2_connect(tctx,
1990 lpcfg_smb_ports(tctx->lp_ctx),
1992 lpcfg_resolve_context(tctx->lp_ctx),
1993 popt_get_cmdline_credentials(),
1996 &transport1->options,
1997 lpcfg_socket_options(tctx->lp_ctx),
1998 lpcfg_gensec_settings(tctx, tctx->lp_ctx)
2000 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2001 "smb2_connect failed");
2002 transport2 = tree2->session->transport;
2004 transport2->oplock.handler = torture_oplock_ack_handler;
2005 transport2->oplock.private_data = tree2;
2008 * Now bind the 1st session to 2nd transport channel
2010 session1_2 = smb2_session_channel(transport2,
2011 lpcfg_gensec_settings(tctx, tctx->lp_ctx),
2013 torture_assert(tctx, session1_2 != NULL, "smb2_session_channel failed");
2015 status = smb2_session_setup_spnego(session1_2,
2016 popt_get_cmdline_credentials(),
2017 0 /* previous_session_id */);
2018 CHECK_STATUS(status, NT_STATUS_OK);
2021 * use the 2nd channel, 1st session
2023 tree1->session = session1_2;
2026 * Write Replay with Correct ChannelSequence is allowed by the server
2028 smb2cli_session_start_replay(tree1->session->smbXcli);
2029 smb2cli_session_reset_channel_sequence(tree1->session->smbXcli,
2031 status = smb2_util_write(tree1, *h1, buf, 0, ARRAY_SIZE(buf));
2032 CHECK_STATUS(status, NT_STATUS_OK);
2033 smb2cli_session_stop_replay(tree1->session->smbXcli);
2036 * See what happens if we change the Buffer and perform a Write Replay.
2037 * This is to show that Write Replay does not really care about the data
2039 memset(buf, 'r', ARRAY_SIZE(buf));
2040 smb2cli_session_start_replay(tree1->session->smbXcli);
2041 status = smb2_util_write(tree1, *h1, buf, 0, ARRAY_SIZE(buf));
2042 CHECK_STATUS(status, NT_STATUS_OK);
2043 smb2cli_session_stop_replay(tree1->session->smbXcli);
2046 * Read back from File to verify what was written
2048 rd = (struct smb2_read) {
2049 .in.file.handle = *h1,
2050 .in.length = ARRAY_SIZE(buf),
2053 status = smb2_read(tree1, tree1, &rd);
2054 CHECK_STATUS(status, NT_STATUS_OK);
2056 if ((rd.out.data.length != ARRAY_SIZE(buf)) ||
2057 memcmp(rd.out.data.data, buf, ARRAY_SIZE(buf))) {
2058 torture_comment(tctx, "Write Replay Data Mismatch\n");
2061 tree1->session = session1_1;
2062 smb2_util_close(tree1, *h1);
2066 CHECK_VAL(break_info.count, 1);
2068 CHECK_VAL(break_info.count, 0);
2072 tree1->session = session1_1;
2075 smb2_util_close(tree1, *h1);
2078 smb2_util_unlink(tree1, fname);
2079 smb2_deltree(tree1, BASEDIR);
2082 talloc_free(mem_ctx);
2088 * Test Durability V2 Persistent Create Replay on a Single Channel
2090 static bool test_replay5(struct torture_context *tctx, struct smb2_tree *tree)
2093 TALLOC_CTX *mem_ctx = talloc_new(tctx);
2094 struct smb2_handle _h;
2095 struct smb2_handle *h = NULL;
2096 struct smb2_create io;
2097 struct GUID create_guid = GUID_random();
2099 uint32_t share_capabilities;
2102 uint32_t server_capabilities;
2103 const char *fname = BASEDIR "\\replay5.dat";
2104 struct smb2_transport *transport = tree->session->transport;
2105 struct smbcli_options options = tree->session->transport->options;
2106 uint8_t expect_oplock = smb2_util_oplock_level("b");
2107 NTSTATUS expect_status = NT_STATUS_DUPLICATE_OBJECTID;
2109 if (smbXcli_conn_protocol(transport->conn) < PROTOCOL_SMB3_00) {
2110 torture_skip(tctx, "SMB 3.X Dialect family required for "
2114 server_capabilities = smb2cli_conn_server_capabilities(
2115 tree->session->transport->conn);
2116 if (!(server_capabilities & SMB2_CAP_PERSISTENT_HANDLES)) {
2118 "Server does not support persistent handles.");
2121 share_capabilities = smb2cli_tcon_capabilities(tree->smbXcli);
2123 share_is_ca = share_capabilities & SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY;
2125 torture_skip(tctx, "Share is not continuously available.");
2128 share_is_so = share_capabilities & SMB2_SHARE_CAP_SCALEOUT;
2130 expect_oplock = smb2_util_oplock_level("s");
2131 expect_status = NT_STATUS_FILE_NOT_AVAILABLE;
2134 ZERO_STRUCT(break_info);
2135 break_info.tctx = tctx;
2136 transport->oplock.handler = torture_oplock_ack_handler;
2137 transport->oplock.private_data = tree;
2139 torture_comment(tctx, "Replay of Persistent DurableHandleReqV2 on Single "
2141 status = torture_smb2_testdir(tree, BASEDIR, &_h);
2142 CHECK_STATUS(status, NT_STATUS_OK);
2143 smb2_util_close(tree, _h);
2144 smb2_util_unlink(tree, fname);
2145 CHECK_VAL(break_info.count, 0);
2147 smb2_oplock_create_share(&io, fname,
2148 smb2_util_share_access("RWD"),
2149 smb2_util_oplock_level("b"));
2150 io.in.durable_open = false;
2151 io.in.durable_open_v2 = true;
2152 io.in.persistent_open = true;
2153 io.in.create_guid = create_guid;
2154 io.in.timeout = UINT32_MAX;
2156 status = smb2_create(tree, mem_ctx, &io);
2157 CHECK_STATUS(status, NT_STATUS_OK);
2158 _h = io.out.file.handle;
2160 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2161 CHECK_VAL(io.out.oplock_level, expect_oplock);
2162 CHECK_VAL(io.out.durable_open, false);
2163 CHECK_VAL(io.out.durable_open_v2, true);
2164 CHECK_VAL(io.out.persistent_open, true);
2165 CHECK_VAL(io.out.timeout, io.in.timeout);
2166 CHECK_VAL(break_info.count, 0);
2168 /* disconnect, leaving the durable open */
2171 if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
2172 torture_warning(tctx, "couldn't reconnect, bailing\n");
2177 /* a re-open of a persistent handle causes an error */
2178 status = smb2_create(tree, mem_ctx, &io);
2179 CHECK_STATUS(status, expect_status);
2181 /* SMB2_FLAGS_REPLAY_OPERATION must be set to open the Persistent Handle */
2182 smb2cli_session_start_replay(tree->session->smbXcli);
2183 smb2cli_session_increment_channel_sequence(tree->session->smbXcli);
2184 status = smb2_create(tree, mem_ctx, &io);
2185 CHECK_STATUS(status, NT_STATUS_OK);
2186 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2187 CHECK_VAL(io.out.durable_open, false);
2188 CHECK_VAL(io.out.persistent_open, true);
2189 CHECK_VAL(io.out.oplock_level, expect_oplock);
2190 _h = io.out.file.handle;
2193 smb2_util_close(tree, *h);
2197 smb2_util_close(tree, *h);
2200 smb2_util_unlink(tree, fname);
2201 smb2_deltree(tree, BASEDIR);
2204 talloc_free(mem_ctx);
2211 * Test Error Codes when a DurableHandleReqV2 with matching CreateGuid is
2212 * re-sent with or without SMB2_FLAGS_REPLAY_OPERATION
2214 static bool test_replay6(struct torture_context *tctx, struct smb2_tree *tree)
2217 TALLOC_CTX *mem_ctx = talloc_new(tctx);
2218 struct smb2_handle _h;
2219 struct smb2_handle *h = NULL;
2220 struct smb2_create io, ref1;
2221 union smb_fileinfo qfinfo;
2222 struct GUID create_guid = GUID_random();
2224 const char *fname = BASEDIR "\\replay6.dat";
2225 struct smb2_transport *transport = tree->session->transport;
2227 if (smbXcli_conn_protocol(transport->conn) < PROTOCOL_SMB3_00) {
2228 torture_skip(tctx, "SMB 3.X Dialect family required for "
2232 torture_reset_break_info(tctx, &break_info);
2233 tree->session->transport->oplock.handler = torture_oplock_ack_handler;
2234 tree->session->transport->oplock.private_data = tree;
2236 torture_comment(tctx, "Error Codes for DurableHandleReqV2 Replay\n");
2237 smb2_util_unlink(tree, fname);
2238 status = torture_smb2_testdir(tree, BASEDIR, &_h);
2239 CHECK_STATUS(status, NT_STATUS_OK);
2240 smb2_util_close(tree, _h);
2241 torture_wait_for_oplock_break(tctx);
2242 CHECK_VAL(break_info.count, 0);
2243 torture_reset_break_info(tctx, &break_info);
2245 smb2_oplock_create_share(&io, fname,
2246 smb2_util_share_access("RWD"),
2247 smb2_util_oplock_level("b"));
2248 io.in.durable_open = false;
2249 io.in.durable_open_v2 = true;
2250 io.in.persistent_open = false;
2251 io.in.create_guid = create_guid;
2252 io.in.timeout = UINT32_MAX;
2254 status = smb2_create(tree, mem_ctx, &io);
2255 CHECK_STATUS(status, NT_STATUS_OK);
2257 _h = io.out.file.handle;
2259 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2260 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
2261 CHECK_VAL(io.out.durable_open, false);
2262 CHECK_VAL(io.out.durable_open_v2, true);
2264 io.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
2265 io.in.create_disposition = NTCREATEX_DISP_OPEN;
2266 smb2cli_session_start_replay(tree->session->smbXcli);
2267 status = smb2_create(tree, mem_ctx, &io);
2268 smb2cli_session_stop_replay(tree->session->smbXcli);
2269 CHECK_STATUS(status, NT_STATUS_OK);
2270 CHECK_CREATE_OUT(&io, &ref1);
2271 torture_wait_for_oplock_break(tctx);
2272 CHECK_VAL(break_info.count, 0);
2273 torture_reset_break_info(tctx, &break_info);
2275 qfinfo = (union smb_fileinfo) {
2276 .generic.level = RAW_FILEINFO_POSITION_INFORMATION,
2277 .generic.in.file.handle = *h
2279 torture_comment(tctx, "Trying getinfo\n");
2280 status = smb2_getinfo_file(tree, mem_ctx, &qfinfo);
2281 CHECK_STATUS(status, NT_STATUS_OK);
2282 CHECK_VAL(qfinfo.position_information.out.position, 0);
2284 smb2cli_session_start_replay(tree->session->smbXcli);
2285 status = smb2_create(tree, mem_ctx, &io);
2286 smb2cli_session_stop_replay(tree->session->smbXcli);
2287 CHECK_STATUS(status, NT_STATUS_OK);
2288 torture_assert_u64_not_equal_goto(tctx,
2289 io.out.file.handle.data[0],
2290 ref1.out.file.handle.data[0],
2291 ret, done, "data 0");
2292 torture_assert_u64_not_equal_goto(tctx,
2293 io.out.file.handle.data[1],
2294 ref1.out.file.handle.data[1],
2295 ret, done, "data 1");
2296 torture_wait_for_oplock_break(tctx);
2297 CHECK_VAL(break_info.count, 1);
2298 CHECK_VAL(break_info.level, smb2_util_oplock_level("s"));
2299 torture_reset_break_info(tctx, &break_info);
2302 * Resend the matching Durable V2 Create without
2303 * SMB2_FLAGS_REPLAY_OPERATION. This triggers an oplock break and still
2304 * gets NT_STATUS_DUPLICATE_OBJECTID
2306 status = smb2_create(tree, mem_ctx, &io);
2307 CHECK_STATUS(status, NT_STATUS_DUPLICATE_OBJECTID);
2308 torture_wait_for_oplock_break(tctx);
2309 CHECK_VAL(break_info.count, 0);
2310 torture_reset_break_info(tctx, &break_info);
2313 * According to MS-SMB2 3.3.5.9.10 if Durable V2 Create is replayed and
2314 * FileAttributes or CreateDisposition do not match the earlier Create
2315 * request the Server fails request with
2316 * NT_STATUS_INVALID_PARAMETER. But through this test we see that server
2317 * does not really care about changed FileAttributes or
2318 * CreateDisposition.
2320 io.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
2321 io.in.create_disposition = NTCREATEX_DISP_OPEN;
2322 smb2cli_session_start_replay(tree->session->smbXcli);
2323 status = smb2_create(tree, mem_ctx, &io);
2324 smb2cli_session_stop_replay(tree->session->smbXcli);
2325 CHECK_STATUS(status, NT_STATUS_OK);
2326 torture_assert_u64_not_equal_goto(tctx,
2327 io.out.file.handle.data[0],
2328 ref1.out.file.handle.data[0],
2329 ret, done, "data 0");
2330 torture_assert_u64_not_equal_goto(tctx,
2331 io.out.file.handle.data[1],
2332 ref1.out.file.handle.data[1],
2333 ret, done, "data 1");
2334 torture_wait_for_oplock_break(tctx);
2335 CHECK_VAL(break_info.count, 0);
2339 smb2_util_close(tree, *h);
2342 smb2_util_unlink(tree, fname);
2343 smb2_deltree(tree, BASEDIR);
2346 talloc_free(mem_ctx);
2351 static bool test_replay7(struct torture_context *tctx, struct smb2_tree *tree)
2353 TALLOC_CTX *mem_ctx = talloc_new(tctx);
2354 struct smb2_transport *transport = tree->session->transport;
2356 struct smb2_handle _dh;
2357 struct smb2_handle *dh = NULL;
2358 struct smb2_notify notify;
2359 struct smb2_request *req;
2360 union smb_fileinfo qfinfo;
2363 if (smbXcli_conn_protocol(transport->conn) < PROTOCOL_SMB3_00) {
2364 torture_skip(tctx, "SMB 3.X Dialect family required for "
2368 torture_comment(tctx, "Notify across increment/decrement of csn\n");
2370 smbXcli_conn_set_force_channel_sequence(transport->conn, true);
2372 status = torture_smb2_testdir(tree, BASEDIR, &_dh);
2373 CHECK_STATUS(status, NT_STATUS_OK);
2376 notify.in.recursive = 0x0000;
2377 notify.in.buffer_size = 0xffff;
2378 notify.in.file.handle = _dh;
2379 notify.in.completion_filter = FILE_NOTIFY_CHANGE_FILE_NAME;
2380 notify.in.unknown = 0x00000000;
2383 * This posts a long-running request with csn==0 to "dh". Now
2384 * op->request_count==1 in smb2_server.c.
2386 smb2cli_session_reset_channel_sequence(tree->session->smbXcli, 0);
2387 req = smb2_notify_send(tree, ¬ify);
2389 qfinfo = (union smb_fileinfo) {
2390 .generic.level = RAW_FILEINFO_POSITION_INFORMATION,
2391 .generic.in.file.handle = _dh
2395 * This sequence of 2 dummy requests moves
2396 * op->request_count==1 to op->pre_request_count. The numbers
2397 * used avoid int16 overflow.
2400 smb2cli_session_reset_channel_sequence(tree->session->smbXcli, 30000);
2401 status = smb2_getinfo_file(tree, mem_ctx, &qfinfo);
2402 CHECK_STATUS(status, NT_STATUS_OK);
2404 smb2cli_session_reset_channel_sequence(tree->session->smbXcli, 60000);
2405 status = smb2_getinfo_file(tree, mem_ctx, &qfinfo);
2406 CHECK_STATUS(status, NT_STATUS_OK);
2409 * This final request turns the op->global->channel_sequence
2410 * to the same as we had when sending the notify above. The
2411 * notify's request count has in the meantime moved to
2412 * op->pre_request_count.
2415 smb2cli_session_reset_channel_sequence(tree->session->smbXcli, 0);
2416 status = smb2_getinfo_file(tree, mem_ctx, &qfinfo);
2417 CHECK_STATUS(status, NT_STATUS_OK);
2420 * At this point op->request_count==0.
2422 * The next cancel makes us reply to the notify. Because the
2423 * csn we currently use is the same as we used when sending
2424 * the notify, smbd thinks it must decrement op->request_count
2425 * and not op->pre_request_count.
2428 status = smb2_cancel(req);
2429 CHECK_STATUS(status, NT_STATUS_OK);
2431 status = smb2_notify_recv(req, mem_ctx, ¬ify);
2432 CHECK_STATUS(status, NT_STATUS_CANCELLED);
2438 smb2_util_close(tree, _dh);
2440 smb2_deltree(tree, BASEDIR);
2442 talloc_free(mem_ctx);
2447 struct torture_suite *torture_smb2_replay_init(TALLOC_CTX *ctx)
2449 struct torture_suite *suite =
2450 torture_suite_create(ctx, "replay");
2452 torture_suite_add_1smb2_test(suite, "replay-commands", test_replay_commands);
2453 torture_suite_add_1smb2_test(suite, "replay-regular", test_replay_regular);
2454 torture_suite_add_1smb2_test(suite, "replay-dhv2-oplock1", test_replay_dhv2_oplock1);
2455 torture_suite_add_1smb2_test(suite, "replay-dhv2-oplock2", test_replay_dhv2_oplock2);
2456 torture_suite_add_1smb2_test(suite, "replay-dhv2-oplock3", test_replay_dhv2_oplock3);
2457 torture_suite_add_1smb2_test(suite, "replay-dhv2-oplock-lease", test_replay_dhv2_oplock_lease);
2458 torture_suite_add_1smb2_test(suite, "replay-dhv2-lease1", test_replay_dhv2_lease1);
2459 torture_suite_add_1smb2_test(suite, "replay-dhv2-lease2", test_replay_dhv2_lease2);
2460 torture_suite_add_1smb2_test(suite, "replay-dhv2-lease3", test_replay_dhv2_lease3);
2461 torture_suite_add_1smb2_test(suite, "replay-dhv2-lease-oplock", test_replay_dhv2_lease_oplock);
2462 torture_suite_add_1smb2_test(suite, "channel-sequence", test_channel_sequence);
2463 torture_suite_add_1smb2_test(suite, "replay3", test_replay3);
2464 torture_suite_add_1smb2_test(suite, "replay4", test_replay4);
2465 torture_suite_add_1smb2_test(suite, "replay5", test_replay5);
2466 torture_suite_add_1smb2_test(suite, "replay6", test_replay6);
2467 torture_suite_add_1smb2_test(suite, "replay7", test_replay7);
2469 suite->description = talloc_strdup(suite, "SMB2 REPLAY tests");