s3:pylibsmb: Add ipc=True support for CLI_FULL_CONNECTION_IPC
[Samba.git] / source4 / torture / smb2 / replay.c
blob7399777a36769a252f04a5f66da805e6784297f9
1 /*
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/>.
23 #include "includes.h"
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); \
41 ret = false; \
42 goto done; \
43 }} while (0)
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)); \
49 ret = false; \
50 goto done; \
51 }} while (0)
53 #define CHECK_CREATED(__io, __created, __attribute) \
54 do { \
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); \
59 } while(0)
61 #define CHECK_HANDLE(__h1, __h2) \
62 do { \
63 CHECK_VAL((__h1)->data[0], (__h2)->data[0]); \
64 CHECK_VAL((__h1)->data[1], (__h2)->data[1]); \
65 } while(0)
67 #define __IO_OUT_VAL(__io1, __io2, __m) \
68 CHECK_VAL((__io1)->out.__m, (__io2)->out.__m)
70 #define CHECK_CREATE_OUT(__io1, __io2) \
71 do { \
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]);\
92 } \
93 } while(0)
95 #define BASEDIR "replaytestdir"
97 /**
98 * Test what happens when SMB2_FLAGS_REPLAY_OPERATION is enabled for various
99 * commands. We want to verify if the server returns an error code or not.
101 static bool test_replay_commands(struct torture_context *tctx, struct smb2_tree *tree)
103 bool ret = true;
104 NTSTATUS status;
105 struct smb2_handle h;
106 uint8_t buf[200];
107 struct smb2_read rd;
108 union smb_setfileinfo sfinfo;
109 union smb_fileinfo qfinfo;
110 union smb_ioctl ioctl;
111 struct smb2_lock lck;
112 struct smb2_lock_element el[2];
113 struct smb2_flush f;
114 TALLOC_CTX *tmp_ctx = talloc_new(tree);
115 const char *fname = BASEDIR "\\replay_commands.dat";
116 struct smb2_transport *transport = tree->session->transport;
118 if (smbXcli_conn_protocol(transport->conn) < PROTOCOL_SMB3_00) {
119 torture_skip(tctx, "SMB 3.X Dialect family required for "
120 "Replay tests\n");
123 ZERO_STRUCT(break_info);
124 break_info.tctx = tctx;
125 tree->session->transport->oplock.handler = torture_oplock_ack_handler;
126 tree->session->transport->oplock.private_data = tree;
128 status = torture_smb2_testdir(tree, BASEDIR, &h);
129 CHECK_STATUS(status, NT_STATUS_OK);
130 smb2_util_close(tree, h);
132 smb2cli_session_start_replay(tree->session->smbXcli);
134 torture_comment(tctx, "Try Commands with Replay Flags Enabled\n");
136 torture_comment(tctx, "Trying create\n");
137 status = torture_smb2_testfile(tree, fname, &h);
138 CHECK_STATUS(status, NT_STATUS_OK);
139 CHECK_VAL(break_info.count, 0);
141 * Wireshark shows that the response has SMB2_FLAGS_REPLAY_OPERATION
142 * flags set. The server should ignore this flag.
145 torture_comment(tctx, "Trying write\n");
146 status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf));
147 CHECK_STATUS(status, NT_STATUS_OK);
149 f = (struct smb2_flush) {
150 .in.file.handle = h
152 torture_comment(tctx, "Trying flush\n");
153 status = smb2_flush(tree, &f);
154 CHECK_STATUS(status, NT_STATUS_OK);
156 rd = (struct smb2_read) {
157 .in.file.handle = h,
158 .in.length = 10,
159 .in.offset = 0,
160 .in.min_count = 1
162 torture_comment(tctx, "Trying read\n");
163 status = smb2_read(tree, tmp_ctx, &rd);
164 CHECK_STATUS(status, NT_STATUS_OK);
165 CHECK_VAL(rd.out.data.length, 10);
167 sfinfo.generic.level = RAW_SFILEINFO_POSITION_INFORMATION;
168 sfinfo.position_information.in.file.handle = h;
169 sfinfo.position_information.in.position = 0x1000;
170 torture_comment(tctx, "Trying setinfo\n");
171 status = smb2_setinfo_file(tree, &sfinfo);
172 CHECK_STATUS(status, NT_STATUS_OK);
174 qfinfo = (union smb_fileinfo) {
175 .generic.level = RAW_FILEINFO_POSITION_INFORMATION,
176 .generic.in.file.handle = h
178 torture_comment(tctx, "Trying getinfo\n");
179 status = smb2_getinfo_file(tree, tmp_ctx, &qfinfo);
180 CHECK_STATUS(status, NT_STATUS_OK);
181 CHECK_VAL(qfinfo.position_information.out.position, 0x1000);
183 ioctl = (union smb_ioctl) {
184 .smb2.level = RAW_IOCTL_SMB2,
185 .smb2.in.file.handle = h,
186 .smb2.in.function = FSCTL_CREATE_OR_GET_OBJECT_ID,
187 .smb2.in.max_output_response = 64,
188 .smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL
190 torture_comment(tctx, "Trying ioctl\n");
191 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
192 CHECK_STATUS(status, NT_STATUS_OK);
194 lck = (struct smb2_lock) {
195 .in.locks = el,
196 .in.lock_count = 0x0001,
197 .in.lock_sequence = 0x00000000,
198 .in.file.handle = h
200 el[0].reserved = 0x00000000;
201 el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE |
202 SMB2_LOCK_FLAG_FAIL_IMMEDIATELY;
204 torture_comment(tctx, "Trying lock\n");
205 el[0].offset = 0x0000000000000000;
206 el[0].length = 0x0000000000000100;
207 status = smb2_lock(tree, &lck);
208 CHECK_STATUS(status, NT_STATUS_OK);
210 lck.in.file.handle = h;
211 el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
212 status = smb2_lock(tree, &lck);
213 CHECK_STATUS(status, NT_STATUS_OK);
215 CHECK_VAL(break_info.count, 0);
216 done:
217 smb2cli_session_stop_replay(tree->session->smbXcli);
218 smb2_util_close(tree, h);
219 smb2_deltree(tree, BASEDIR);
221 talloc_free(tmp_ctx);
223 return ret;
227 * Test replay detection without create GUID on single channel.
228 * Regular creates can not be replayed.
229 * The return code is unaffected of the REPLAY_OPERATION flag.
231 static bool test_replay_regular(struct torture_context *tctx,
232 struct smb2_tree *tree)
234 NTSTATUS status;
235 TALLOC_CTX *mem_ctx = talloc_new(tctx);
236 struct smb2_handle _h;
237 struct smb2_handle *h = NULL;
238 struct smb2_create io;
239 uint32_t perms = 0;
240 bool ret = true;
241 const char *fname = BASEDIR "\\replay_regular.dat";
242 struct smb2_transport *transport = tree->session->transport;
244 if (smbXcli_conn_protocol(transport->conn) < PROTOCOL_SMB3_00) {
245 torture_skip(tctx, "SMB 3.X Dialect family required for "
246 "replay tests\n");
249 ZERO_STRUCT(break_info);
250 break_info.tctx = tctx;
251 tree->session->transport->oplock.handler = torture_oplock_ack_handler;
252 tree->session->transport->oplock.private_data = tree;
254 smb2_util_unlink(tree, fname);
255 status = torture_smb2_testdir(tree, BASEDIR, &_h);
256 CHECK_STATUS(status, NT_STATUS_OK);
257 smb2_util_close(tree, _h);
258 CHECK_VAL(break_info.count, 0);
260 torture_comment(tctx, "No replay detection for regular create\n");
262 perms = SEC_STD_SYNCHRONIZE | SEC_STD_READ_CONTROL | SEC_STD_DELETE |
263 SEC_DIR_WRITE_ATTRIBUTE | SEC_DIR_READ_ATTRIBUTE |
264 SEC_DIR_WRITE_EA | SEC_FILE_APPEND_DATA |
265 SEC_FILE_WRITE_DATA;
267 io = (struct smb2_create) {
268 .in.desired_access = perms,
269 .in.file_attributes = 0,
270 .in.create_disposition = NTCREATEX_DISP_CREATE,
271 .in.share_access = NTCREATEX_SHARE_ACCESS_DELETE,
272 .in.create_options = 0x0,
273 .in.fname = fname
276 status = smb2_create(tree, tctx, &io);
277 CHECK_STATUS(status, NT_STATUS_OK);
278 CHECK_VAL(break_info.count, 0);
279 _h = io.out.file.handle;
280 h = &_h;
281 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
283 smb2cli_session_start_replay(tree->session->smbXcli);
284 status = smb2_create(tree, tctx, &io);
285 smb2cli_session_stop_replay(tree->session->smbXcli);
286 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_COLLISION);
287 CHECK_VAL(break_info.count, 0);
289 smb2_util_close(tree, *h);
290 h = NULL;
291 smb2_util_unlink(tree, fname);
294 * Same experiment with different create disposition.
296 io.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
297 status = smb2_create(tree, tctx, &io);
298 CHECK_STATUS(status, NT_STATUS_OK);
299 CHECK_VAL(break_info.count, 0);
300 _h = io.out.file.handle;
301 h = &_h;
302 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
304 smb2cli_session_start_replay(tree->session->smbXcli);
305 status = smb2_create(tree, tctx, &io);
306 smb2cli_session_stop_replay(tree->session->smbXcli);
307 CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
308 CHECK_VAL(break_info.count, 0);
310 smb2_util_close(tree, *h);
311 h = NULL;
312 smb2_util_unlink(tree, fname);
315 * Now with more generous share mode.
317 io.in.share_access = smb2_util_share_access("RWD");
318 status = smb2_create(tree, tctx, &io);
319 CHECK_STATUS(status, NT_STATUS_OK);
320 CHECK_VAL(break_info.count, 0);
321 _h = io.out.file.handle;
322 h = &_h;
323 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
325 smb2cli_session_start_replay(tree->session->smbXcli);
326 status = smb2_create(tree, tctx, &io);
327 smb2cli_session_stop_replay(tree->session->smbXcli);
328 CHECK_STATUS(status, NT_STATUS_OK);
329 CHECK_VAL(break_info.count, 0);
331 done:
332 if (h != NULL) {
333 smb2_util_close(tree, *h);
335 smb2_deltree(tree, BASEDIR);
337 talloc_free(tree);
338 talloc_free(mem_ctx);
340 return ret;
344 * Test Durability V2 Create Replay Detection on Single Channel.
346 static bool test_replay_dhv2_oplock1(struct torture_context *tctx,
347 struct smb2_tree *tree)
349 NTSTATUS status;
350 TALLOC_CTX *mem_ctx = talloc_new(tctx);
351 struct smb2_handle _h;
352 struct smb2_handle *h = NULL;
353 struct smb2_create io, ref1;
354 struct GUID create_guid = GUID_random();
355 bool ret = true;
356 const char *fname = BASEDIR "\\replay_dhv2_oplock1.dat";
357 struct smb2_transport *transport = tree->session->transport;
358 uint32_t share_capabilities;
359 bool share_is_so;
361 if (smbXcli_conn_protocol(transport->conn) < PROTOCOL_SMB3_00) {
362 torture_skip(tctx, "SMB 3.X Dialect family required for "
363 "replay tests\n");
366 share_capabilities = smb2cli_tcon_capabilities(tree->smbXcli);
367 share_is_so = share_capabilities & SMB2_SHARE_CAP_SCALEOUT;
369 ZERO_STRUCT(break_info);
370 break_info.tctx = tctx;
371 tree->session->transport->oplock.handler = torture_oplock_ack_handler;
372 tree->session->transport->oplock.private_data = tree;
374 torture_comment(tctx, "Replay of DurableHandleReqV2 on Single "
375 "Channel\n");
376 smb2_util_unlink(tree, fname);
377 status = torture_smb2_testdir(tree, BASEDIR, &_h);
378 CHECK_STATUS(status, NT_STATUS_OK);
379 smb2_util_close(tree, _h);
380 CHECK_VAL(break_info.count, 0);
382 smb2_oplock_create_share(&io, fname,
383 smb2_util_share_access(""),
384 smb2_util_oplock_level("b"));
385 io.in.durable_open = false;
386 io.in.durable_open_v2 = true;
387 io.in.persistent_open = false;
388 io.in.create_guid = create_guid;
389 io.in.timeout = UINT32_MAX;
391 status = smb2_create(tree, mem_ctx, &io);
392 CHECK_STATUS(status, NT_STATUS_OK);
393 ref1 = io;
394 _h = io.out.file.handle;
395 h = &_h;
396 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
397 CHECK_VAL(io.out.durable_open, false);
398 if (share_is_so) {
399 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("s"));
400 CHECK_VAL(io.out.durable_open_v2, false);
401 CHECK_VAL(io.out.timeout, 0);
402 } else {
403 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
404 CHECK_VAL(io.out.durable_open_v2, true);
405 CHECK_VAL(io.out.timeout, 300*1000);
409 * Replay Durable V2 Create on single channel
411 smb2cli_session_start_replay(tree->session->smbXcli);
412 status = smb2_create(tree, mem_ctx, &io);
413 smb2cli_session_stop_replay(tree->session->smbXcli);
414 CHECK_STATUS(status, NT_STATUS_OK);
415 CHECK_CREATE_OUT(&io, &ref1);
416 CHECK_VAL(break_info.count, 0);
418 done:
419 if (h != NULL) {
420 smb2_util_close(tree, *h);
422 smb2_deltree(tree, BASEDIR);
424 talloc_free(tree);
425 talloc_free(mem_ctx);
427 return ret;
431 * Test Durability V2 Create Replay Detection on Single Channel.
432 * Hand in a different oplock level in the replay.
433 * Server responds with the handed in oplock level and
434 * corresponding durable status, but does not change the
435 * oplock level or durable status of the opened file.
437 static bool test_replay_dhv2_oplock2(struct torture_context *tctx,
438 struct smb2_tree *tree)
440 NTSTATUS status;
441 TALLOC_CTX *mem_ctx = talloc_new(tctx);
442 struct smb2_handle _h;
443 struct smb2_handle *h = NULL;
444 struct smb2_create io, ref1, ref2;
445 struct GUID create_guid = GUID_random();
446 bool ret = true;
447 const char *fname = BASEDIR "\\replay_dhv2_oplock2.dat";
448 struct smb2_transport *transport = tree->session->transport;
449 uint32_t share_capabilities;
450 bool share_is_so;
452 if (smbXcli_conn_protocol(transport->conn) < PROTOCOL_SMB3_00) {
453 torture_skip(tctx, "SMB 3.X Dialect family required for "
454 "replay tests\n");
457 share_capabilities = smb2cli_tcon_capabilities(tree->smbXcli);
458 share_is_so = share_capabilities & SMB2_SHARE_CAP_SCALEOUT;
460 ZERO_STRUCT(break_info);
461 break_info.tctx = tctx;
462 tree->session->transport->oplock.handler = torture_oplock_ack_handler;
463 tree->session->transport->oplock.private_data = tree;
465 torture_comment(tctx, "Replay of DurableHandleReqV2 on Single "
466 "Channel\n");
467 smb2_util_unlink(tree, fname);
468 status = torture_smb2_testdir(tree, BASEDIR, &_h);
469 CHECK_STATUS(status, NT_STATUS_OK);
470 smb2_util_close(tree, _h);
471 CHECK_VAL(break_info.count, 0);
473 smb2_oplock_create_share(&io, fname,
474 smb2_util_share_access(""),
475 smb2_util_oplock_level("b"));
476 io.in.durable_open = false;
477 io.in.durable_open_v2 = true;
478 io.in.persistent_open = false;
479 io.in.create_guid = create_guid;
480 io.in.timeout = UINT32_MAX;
482 status = smb2_create(tree, mem_ctx, &io);
483 CHECK_STATUS(status, NT_STATUS_OK);
484 ref1 = io;
485 _h = io.out.file.handle;
486 h = &_h;
487 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
488 CHECK_VAL(io.out.durable_open, false);
489 if (share_is_so) {
490 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("s"));
491 CHECK_VAL(io.out.durable_open_v2, false);
492 CHECK_VAL(io.out.timeout, 0);
493 } else {
494 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
495 CHECK_VAL(io.out.durable_open_v2, true);
496 CHECK_VAL(io.out.timeout, 300*1000);
500 * Replay durable v2 create on single channel:
502 * Replay the create with a different oplock (none).
503 * The server replies with the requested oplock level
504 * and also only replies with durable handle based
505 * on whether it could have been granted based on
506 * the requested oplock type.
508 smb2_oplock_create_share(&io, fname,
509 smb2_util_share_access(""),
510 smb2_util_oplock_level(""));
511 io.in.durable_open = false;
512 io.in.durable_open_v2 = true;
513 io.in.persistent_open = false;
514 io.in.create_guid = create_guid;
515 io.in.timeout = UINT32_MAX;
518 * Adapt the response to the exepected values
520 ref2 = ref1;
521 ref2.out.oplock_level = smb2_util_oplock_level("");
522 ref2.out.durable_open_v2 = false;
523 ref2.out.timeout = 0;
524 ref2.out.blobs.num_blobs = 0;
526 smb2cli_session_start_replay(tree->session->smbXcli);
527 status = smb2_create(tree, mem_ctx, &io);
528 smb2cli_session_stop_replay(tree->session->smbXcli);
529 CHECK_STATUS(status, NT_STATUS_OK);
530 CHECK_CREATE_OUT(&io, &ref2);
531 CHECK_VAL(break_info.count, 0);
534 * Prove that the open file still has a batch oplock
535 * by breaking it with another open.
537 smb2_oplock_create_share(&io, fname,
538 smb2_util_share_access(""),
539 smb2_util_oplock_level("b"));
540 io.in.durable_open = false;
541 io.in.durable_open_v2 = true;
542 io.in.persistent_open = false;
543 io.in.create_guid = GUID_random();
544 io.in.timeout = UINT32_MAX;
545 status = smb2_create(tree, mem_ctx, &io);
546 CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
548 if (!share_is_so) {
549 CHECK_VAL(break_info.count, 1);
550 CHECK_HANDLE(&break_info.handle, &ref1.out.file.handle);
551 CHECK_VAL(break_info.level, smb2_util_oplock_level("s"));
552 ZERO_STRUCT(break_info);
555 done:
556 if (h != NULL) {
557 smb2_util_close(tree, *h);
559 smb2_deltree(tree, BASEDIR);
561 talloc_free(tree);
562 talloc_free(mem_ctx);
564 return ret;
568 * Test Durability V2 Create Replay Detection on Single Channel.
569 * Replay with a different share mode. The share mode of
570 * the opened file is not changed by this.
572 static bool test_replay_dhv2_oplock3(struct torture_context *tctx,
573 struct smb2_tree *tree)
575 NTSTATUS status;
576 TALLOC_CTX *mem_ctx = talloc_new(tctx);
577 struct smb2_handle _h;
578 struct smb2_handle *h = NULL;
579 struct smb2_create io, ref1;
580 struct GUID create_guid = GUID_random();
581 bool ret = true;
582 const char *fname = BASEDIR "\\replay_dhv2_oplock3.dat";
583 struct smb2_transport *transport = tree->session->transport;
584 uint32_t share_capabilities;
585 bool share_is_so;
587 if (smbXcli_conn_protocol(transport->conn) < PROTOCOL_SMB3_00) {
588 torture_skip(tctx, "SMB 3.X Dialect family required for "
589 "replay tests\n");
592 share_capabilities = smb2cli_tcon_capabilities(tree->smbXcli);
593 share_is_so = share_capabilities & SMB2_SHARE_CAP_SCALEOUT;
595 ZERO_STRUCT(break_info);
596 break_info.tctx = tctx;
597 tree->session->transport->oplock.handler = torture_oplock_ack_handler;
598 tree->session->transport->oplock.private_data = tree;
600 torture_comment(tctx, "Replay of DurableHandleReqV2 on Single "
601 "Channel\n");
602 smb2_util_unlink(tree, fname);
603 status = torture_smb2_testdir(tree, BASEDIR, &_h);
604 CHECK_STATUS(status, NT_STATUS_OK);
605 smb2_util_close(tree, _h);
606 CHECK_VAL(break_info.count, 0);
608 smb2_oplock_create_share(&io, fname,
609 smb2_util_share_access(""),
610 smb2_util_oplock_level("b"));
611 io.in.durable_open = false;
612 io.in.durable_open_v2 = true;
613 io.in.persistent_open = false;
614 io.in.create_guid = create_guid;
615 io.in.timeout = UINT32_MAX;
617 status = smb2_create(tree, mem_ctx, &io);
618 CHECK_STATUS(status, NT_STATUS_OK);
619 ref1 = io;
620 _h = io.out.file.handle;
621 h = &_h;
622 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
623 CHECK_VAL(io.out.durable_open, false);
624 if (share_is_so) {
625 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("s"));
626 CHECK_VAL(io.out.durable_open_v2, false);
627 CHECK_VAL(io.out.timeout, 0);
628 } else {
629 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
630 CHECK_VAL(io.out.durable_open_v2, true);
631 CHECK_VAL(io.out.timeout, 300*1000);
635 * Replay durable v2 create on single channel:
637 * Replay the create with a different share mode.
638 * The server replies with the requested share
639 * mode instead of that which is associated to
640 * the handle.
642 smb2_oplock_create_share(&io, fname,
643 smb2_util_share_access("RWD"),
644 smb2_util_oplock_level("b"));
645 io.in.durable_open = false;
646 io.in.durable_open_v2 = true;
647 io.in.persistent_open = false;
648 io.in.create_guid = create_guid;
649 io.in.timeout = UINT32_MAX;
651 smb2cli_session_start_replay(tree->session->smbXcli);
652 status = smb2_create(tree, mem_ctx, &io);
653 smb2cli_session_stop_replay(tree->session->smbXcli);
654 CHECK_STATUS(status, NT_STATUS_OK);
655 CHECK_CREATE_OUT(&io, &ref1);
656 CHECK_VAL(break_info.count, 0);
659 * In order to prove that the different share mode in the
660 * replayed create had no effect on the open file handle,
661 * show that a new create yields NT_STATUS_SHARING_VIOLATION.
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 = GUID_random();
670 io.in.timeout = UINT32_MAX;
671 status = smb2_create(tree, mem_ctx, &io);
672 CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
674 if (!share_is_so) {
675 CHECK_VAL(break_info.count, 1);
676 CHECK_HANDLE(&break_info.handle, &ref1.out.file.handle);
677 CHECK_VAL(break_info.level, smb2_util_oplock_level("s"));
678 ZERO_STRUCT(break_info);
681 done:
682 if (h != NULL) {
683 smb2_util_close(tree, *h);
685 smb2_deltree(tree, BASEDIR);
687 talloc_free(tree);
688 talloc_free(mem_ctx);
690 return ret;
694 * Test Durability V2 Create Replay Detection on Single Channel.
695 * Create with an oplock, and replay with a lease.
697 static bool test_replay_dhv2_oplock_lease(struct torture_context *tctx,
698 struct smb2_tree *tree)
700 NTSTATUS status;
701 TALLOC_CTX *mem_ctx = talloc_new(tctx);
702 struct smb2_handle _h;
703 struct smb2_handle *h = NULL;
704 struct smb2_create io;
705 struct GUID create_guid = GUID_random();
706 bool ret = true;
707 const char *fname = BASEDIR "\\replay_dhv2_oplock1.dat";
708 struct smb2_transport *transport = tree->session->transport;
709 uint32_t share_capabilities;
710 bool share_is_so;
711 uint32_t server_capabilities;
712 struct smb2_lease ls;
713 uint64_t lease_key;
715 if (smbXcli_conn_protocol(transport->conn) < PROTOCOL_SMB3_00) {
716 torture_skip(tctx, "SMB 3.X Dialect family required for "
717 "replay tests\n");
720 server_capabilities = smb2cli_conn_server_capabilities(transport->conn);
721 if (!(server_capabilities & SMB2_CAP_LEASING)) {
722 torture_skip(tctx, "leases are not supported");
725 share_capabilities = smb2cli_tcon_capabilities(tree->smbXcli);
726 share_is_so = share_capabilities & SMB2_SHARE_CAP_SCALEOUT;
728 ZERO_STRUCT(break_info);
729 break_info.tctx = tctx;
730 tree->session->transport->oplock.handler = torture_oplock_ack_handler;
731 tree->session->transport->oplock.private_data = tree;
733 torture_comment(tctx, "Replay of DurableHandleReqV2 on Single "
734 "Channel\n");
735 smb2_util_unlink(tree, fname);
736 status = torture_smb2_testdir(tree, BASEDIR, &_h);
737 CHECK_STATUS(status, NT_STATUS_OK);
738 smb2_util_close(tree, _h);
739 CHECK_VAL(break_info.count, 0);
741 smb2_oplock_create_share(&io, fname,
742 smb2_util_share_access(""),
743 smb2_util_oplock_level("b"));
744 io.in.durable_open = false;
745 io.in.durable_open_v2 = true;
746 io.in.persistent_open = false;
747 io.in.create_guid = create_guid;
748 io.in.timeout = UINT32_MAX;
750 status = smb2_create(tree, mem_ctx, &io);
751 CHECK_STATUS(status, NT_STATUS_OK);
752 _h = io.out.file.handle;
753 h = &_h;
754 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
755 CHECK_VAL(io.out.durable_open, false);
756 if (share_is_so) {
757 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("s"));
758 CHECK_VAL(io.out.durable_open_v2, false);
759 CHECK_VAL(io.out.timeout, 0);
760 } else {
761 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
762 CHECK_VAL(io.out.durable_open_v2, true);
763 CHECK_VAL(io.out.timeout, 300*1000);
767 * Replay Durable V2 Create on single channel
768 * but replay it with a lease instead of an oplock.
770 lease_key = random();
771 smb2_lease_create(&io, &ls, false /* dir */, fname,
772 lease_key, smb2_util_lease_state("RH"));
773 io.in.durable_open = false;
774 io.in.durable_open_v2 = true;
775 io.in.persistent_open = false;
776 io.in.create_guid = create_guid;
777 io.in.timeout = UINT32_MAX;
779 smb2cli_session_start_replay(tree->session->smbXcli);
780 status = smb2_create(tree, mem_ctx, &io);
781 smb2cli_session_stop_replay(tree->session->smbXcli);
782 CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED);
784 done:
785 if (h != NULL) {
786 smb2_util_close(tree, *h);
788 smb2_deltree(tree, BASEDIR);
790 talloc_free(tree);
791 talloc_free(mem_ctx);
793 return ret;
798 * Test durability v2 create replay detection on single channel.
799 * Variant with leases instead of oplocks:
800 * - open a file with a rh lease
801 * - upgrade to a rwh lease with a second create
802 * - replay the first create.
803 * ==> it gets back the upgraded lease level
805 static bool test_replay_dhv2_lease1(struct torture_context *tctx,
806 struct smb2_tree *tree)
808 NTSTATUS status;
809 TALLOC_CTX *mem_ctx = talloc_new(tctx);
810 struct smb2_handle _h1;
811 struct smb2_handle *h1 = NULL;
812 struct smb2_handle _h2;
813 struct smb2_handle *h2 = NULL;
814 struct smb2_create io1, io2, ref1;
815 struct GUID create_guid = GUID_random();
816 bool ret = true;
817 const char *fname = BASEDIR "\\replay2_lease1.dat";
818 struct smb2_transport *transport = tree->session->transport;
819 uint32_t share_capabilities;
820 bool share_is_so;
821 uint32_t server_capabilities;
822 struct smb2_lease ls1, ls2;
823 uint64_t lease_key;
825 if (smbXcli_conn_protocol(transport->conn) < PROTOCOL_SMB3_00) {
826 torture_skip(tctx, "SMB 3.X Dialect family required for "
827 "replay tests\n");
830 server_capabilities = smb2cli_conn_server_capabilities(transport->conn);
831 if (!(server_capabilities & SMB2_CAP_LEASING)) {
832 torture_skip(tctx, "leases are not supported");
835 share_capabilities = smb2cli_tcon_capabilities(tree->smbXcli);
836 share_is_so = share_capabilities & SMB2_SHARE_CAP_SCALEOUT;
838 ZERO_STRUCT(break_info);
839 break_info.tctx = tctx;
840 tree->session->transport->oplock.handler = torture_oplock_ack_handler;
841 tree->session->transport->oplock.private_data = tree;
843 torture_comment(tctx, "Replay of DurableHandleReqV2 with Lease "
844 "on Single Channel\n");
845 smb2_util_unlink(tree, fname);
846 status = torture_smb2_testdir(tree, BASEDIR, &_h1);
847 CHECK_STATUS(status, NT_STATUS_OK);
848 smb2_util_close(tree, _h1);
849 CHECK_VAL(break_info.count, 0);
851 lease_key = random();
853 smb2_lease_create(&io1, &ls1, false /* dir */, fname,
854 lease_key, smb2_util_lease_state("RH"));
855 io1.in.durable_open = false;
856 io1.in.durable_open_v2 = true;
857 io1.in.persistent_open = false;
858 io1.in.create_guid = create_guid;
859 io1.in.timeout = UINT32_MAX;
861 status = smb2_create(tree, mem_ctx, &io1);
862 CHECK_STATUS(status, NT_STATUS_OK);
863 ref1 = io1;
864 _h1 = io1.out.file.handle;
865 h1 = &_h1;
866 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
867 CHECK_VAL(io1.out.durable_open, false);
868 CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
869 CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease_key);
870 CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease_key);
871 if (share_is_so) {
872 CHECK_VAL(io1.out.lease_response.lease_state,
873 smb2_util_lease_state("R"));
874 CHECK_VAL(io1.out.durable_open_v2, false);
875 CHECK_VAL(io1.out.timeout, 0);
876 } else {
877 CHECK_VAL(io1.out.lease_response.lease_state,
878 smb2_util_lease_state("RH"));
879 CHECK_VAL(io1.out.durable_open_v2, true);
880 CHECK_VAL(io1.out.timeout, 300*1000);
884 * Upgrade the lease to RWH
886 smb2_lease_create(&io2, &ls2, false /* dir */, fname,
887 lease_key, smb2_util_lease_state("RHW"));
888 io2.in.durable_open = false;
889 io2.in.durable_open_v2 = true;
890 io2.in.persistent_open = false;
891 io2.in.create_guid = GUID_random(); /* new guid... */
892 io2.in.timeout = UINT32_MAX;
894 status = smb2_create(tree, mem_ctx, &io2);
895 CHECK_STATUS(status, NT_STATUS_OK);
896 _h2 = io2.out.file.handle;
897 h2 = &_h2;
900 * Replay Durable V2 Create on single channel.
901 * We get the io from open #1 but with the
902 * upgraded lease.
905 /* adapt expected lease in response */
906 if (!share_is_so) {
907 ref1.out.lease_response.lease_state =
908 smb2_util_lease_state("RHW");
911 smb2cli_session_start_replay(tree->session->smbXcli);
912 status = smb2_create(tree, mem_ctx, &io1);
913 smb2cli_session_stop_replay(tree->session->smbXcli);
914 CHECK_STATUS(status, NT_STATUS_OK);
915 CHECK_CREATE_OUT(&io1, &ref1);
916 CHECK_VAL(break_info.count, 0);
918 done:
919 smb2cli_session_stop_replay(tree->session->smbXcli);
921 if (h1 != NULL) {
922 smb2_util_close(tree, *h1);
924 if (h2 != NULL) {
925 smb2_util_close(tree, *h2);
927 smb2_deltree(tree, BASEDIR);
929 talloc_free(tree);
930 talloc_free(mem_ctx);
932 return ret;
936 * Test durability v2 create replay detection on single channel.
937 * Variant with leases instead of oplocks, where the
938 * replay does not specify the original lease level but
939 * just a "R" lease. This still gives the upgraded lease
940 * level in the reply.
941 * - open a file with a rh lease
942 * - upgrade to a rwh lease with a second create
943 * - replay the first create.
944 * ==> it gets back the upgraded lease level
946 static bool test_replay_dhv2_lease2(struct torture_context *tctx,
947 struct smb2_tree *tree)
949 NTSTATUS status;
950 TALLOC_CTX *mem_ctx = talloc_new(tctx);
951 struct smb2_handle _h1;
952 struct smb2_handle *h1 = NULL;
953 struct smb2_handle _h2;
954 struct smb2_handle *h2 = NULL;
955 struct smb2_create io1, io2, ref1;
956 struct GUID create_guid = GUID_random();
957 bool ret = true;
958 const char *fname = BASEDIR "\\replay2_lease2.dat";
959 struct smb2_transport *transport = tree->session->transport;
960 uint32_t share_capabilities;
961 bool share_is_so;
962 uint32_t server_capabilities;
963 struct smb2_lease ls1, ls2;
964 uint64_t lease_key;
966 if (smbXcli_conn_protocol(transport->conn) < PROTOCOL_SMB3_00) {
967 torture_skip(tctx, "SMB 3.X Dialect family required for "
968 "replay tests\n");
971 server_capabilities = smb2cli_conn_server_capabilities(transport->conn);
972 if (!(server_capabilities & SMB2_CAP_LEASING)) {
973 torture_skip(tctx, "leases are not supported");
976 share_capabilities = smb2cli_tcon_capabilities(tree->smbXcli);
977 share_is_so = share_capabilities & SMB2_SHARE_CAP_SCALEOUT;
979 ZERO_STRUCT(break_info);
980 break_info.tctx = tctx;
981 tree->session->transport->oplock.handler = torture_oplock_ack_handler;
982 tree->session->transport->oplock.private_data = tree;
984 torture_comment(tctx, "Replay of DurableHandleReqV2 with Lease "
985 "on Single Channel\n");
986 smb2_util_unlink(tree, fname);
987 status = torture_smb2_testdir(tree, BASEDIR, &_h1);
988 CHECK_STATUS(status, NT_STATUS_OK);
989 smb2_util_close(tree, _h1);
990 CHECK_VAL(break_info.count, 0);
992 lease_key = random();
994 smb2_lease_create(&io1, &ls1, false /* dir */, fname,
995 lease_key, smb2_util_lease_state("RH"));
996 io1.in.durable_open = false;
997 io1.in.durable_open_v2 = true;
998 io1.in.persistent_open = false;
999 io1.in.create_guid = create_guid;
1000 io1.in.timeout = UINT32_MAX;
1002 status = smb2_create(tree, mem_ctx, &io1);
1003 CHECK_STATUS(status, NT_STATUS_OK);
1004 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1005 CHECK_VAL(io1.out.durable_open, false);
1006 CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1007 CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease_key);
1008 CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease_key);
1009 if (share_is_so) {
1010 CHECK_VAL(io1.out.lease_response.lease_state,
1011 smb2_util_lease_state("R"));
1012 CHECK_VAL(io1.out.durable_open_v2, false);
1013 CHECK_VAL(io1.out.timeout, 0);
1014 } else {
1015 CHECK_VAL(io1.out.lease_response.lease_state,
1016 smb2_util_lease_state("RH"));
1017 CHECK_VAL(io1.out.durable_open_v2, true);
1018 CHECK_VAL(io1.out.timeout, 300*1000);
1020 ref1 = io1;
1021 _h1 = io1.out.file.handle;
1022 h1 = &_h1;
1025 * Upgrade the lease to RWH
1027 smb2_lease_create(&io2, &ls2, false /* dir */, fname,
1028 lease_key, smb2_util_lease_state("RHW"));
1029 io2.in.durable_open = false;
1030 io2.in.durable_open_v2 = true;
1031 io2.in.persistent_open = false;
1032 io2.in.create_guid = GUID_random(); /* new guid... */
1033 io2.in.timeout = UINT32_MAX;
1035 status = smb2_create(tree, mem_ctx, &io2);
1036 CHECK_STATUS(status, NT_STATUS_OK);
1037 _h2 = io2.out.file.handle;
1038 h2 = &_h2;
1041 * Replay Durable V2 Create on single channel.
1042 * Changing the requested lease level to "R"
1043 * does not change the response:
1044 * We get the reply from open #1 but with the
1045 * upgraded lease.
1048 /* adapt the expected response */
1049 if (!share_is_so) {
1050 ref1.out.lease_response.lease_state =
1051 smb2_util_lease_state("RHW");
1054 smb2_lease_create(&io1, &ls1, false /* dir */, fname,
1055 lease_key, smb2_util_lease_state("R"));
1056 io1.in.durable_open = false;
1057 io1.in.durable_open_v2 = true;
1058 io1.in.persistent_open = false;
1059 io1.in.create_guid = create_guid;
1060 io1.in.timeout = UINT32_MAX;
1062 smb2cli_session_start_replay(tree->session->smbXcli);
1063 status = smb2_create(tree, mem_ctx, &io1);
1064 smb2cli_session_stop_replay(tree->session->smbXcli);
1065 CHECK_STATUS(status, NT_STATUS_OK);
1066 CHECK_CREATE_OUT(&io1, &ref1);
1067 CHECK_VAL(break_info.count, 0);
1069 done:
1070 smb2cli_session_stop_replay(tree->session->smbXcli);
1072 if (h1 != NULL) {
1073 smb2_util_close(tree, *h1);
1075 if (h2 != NULL) {
1076 smb2_util_close(tree, *h2);
1078 smb2_deltree(tree, BASEDIR);
1080 talloc_free(tree);
1081 talloc_free(mem_ctx);
1083 return ret;
1087 * Test durability v2 create replay detection on single channel.
1088 * create with a lease, and replay with a different lease key
1090 static bool test_replay_dhv2_lease3(struct torture_context *tctx,
1091 struct smb2_tree *tree)
1093 NTSTATUS status;
1094 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1095 struct smb2_handle _h1;
1096 struct smb2_handle *h1 = NULL;
1097 struct smb2_handle _h2;
1098 struct smb2_handle *h2 = NULL;
1099 struct smb2_create io1, io2;
1100 struct GUID create_guid = GUID_random();
1101 bool ret = true;
1102 const char *fname = BASEDIR "\\replay2_lease2.dat";
1103 struct smb2_transport *transport = tree->session->transport;
1104 uint32_t share_capabilities;
1105 bool share_is_so;
1106 uint32_t server_capabilities;
1107 struct smb2_lease ls1, ls2;
1108 uint64_t lease_key;
1110 if (smbXcli_conn_protocol(transport->conn) < PROTOCOL_SMB3_00) {
1111 torture_skip(tctx, "SMB 3.X Dialect family required for "
1112 "replay tests\n");
1115 server_capabilities = smb2cli_conn_server_capabilities(transport->conn);
1116 if (!(server_capabilities & SMB2_CAP_LEASING)) {
1117 torture_skip(tctx, "leases are not supported");
1120 share_capabilities = smb2cli_tcon_capabilities(tree->smbXcli);
1121 share_is_so = share_capabilities & SMB2_SHARE_CAP_SCALEOUT;
1123 ZERO_STRUCT(break_info);
1124 break_info.tctx = tctx;
1125 tree->session->transport->oplock.handler = torture_oplock_ack_handler;
1126 tree->session->transport->oplock.private_data = tree;
1128 torture_comment(tctx, "Replay of DurableHandleReqV2 with Lease "
1129 "on Single Channel\n");
1130 smb2_util_unlink(tree, fname);
1131 status = torture_smb2_testdir(tree, BASEDIR, &_h1);
1132 CHECK_STATUS(status, NT_STATUS_OK);
1133 smb2_util_close(tree, _h1);
1134 CHECK_VAL(break_info.count, 0);
1136 lease_key = random();
1138 smb2_lease_create(&io1, &ls1, false /* dir */, fname,
1139 lease_key, smb2_util_lease_state("RH"));
1140 io1.in.durable_open = false;
1141 io1.in.durable_open_v2 = true;
1142 io1.in.persistent_open = false;
1143 io1.in.create_guid = create_guid;
1144 io1.in.timeout = UINT32_MAX;
1146 status = smb2_create(tree, mem_ctx, &io1);
1147 CHECK_STATUS(status, NT_STATUS_OK);
1148 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1149 CHECK_VAL(io1.out.durable_open, false);
1150 CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1151 CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease_key);
1152 CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease_key);
1153 if (share_is_so) {
1154 CHECK_VAL(io1.out.lease_response.lease_state,
1155 smb2_util_lease_state("R"));
1156 CHECK_VAL(io1.out.durable_open_v2, false);
1157 CHECK_VAL(io1.out.timeout, 0);
1158 } else {
1159 CHECK_VAL(io1.out.lease_response.lease_state,
1160 smb2_util_lease_state("RH"));
1161 CHECK_VAL(io1.out.durable_open_v2, true);
1162 CHECK_VAL(io1.out.timeout, 300*1000);
1164 _h1 = io1.out.file.handle;
1165 h1 = &_h1;
1168 * Upgrade the lease to RWH
1170 smb2_lease_create(&io2, &ls2, false /* dir */, fname,
1171 lease_key, smb2_util_lease_state("RHW"));
1172 io2.in.durable_open = false;
1173 io2.in.durable_open_v2 = true;
1174 io2.in.persistent_open = false;
1175 io2.in.create_guid = GUID_random(); /* new guid... */
1176 io2.in.timeout = UINT32_MAX;
1178 status = smb2_create(tree, mem_ctx, &io2);
1179 CHECK_STATUS(status, NT_STATUS_OK);
1180 _h2 = io2.out.file.handle;
1181 h2 = &_h2;
1184 * Replay Durable V2 Create on single channel.
1185 * use a different lease key.
1188 smb2_lease_create(&io1, &ls1, false /* dir */, fname,
1189 random() /* lease key */,
1190 smb2_util_lease_state("RH"));
1191 io1.in.durable_open = false;
1192 io1.in.durable_open_v2 = true;
1193 io1.in.persistent_open = false;
1194 io1.in.create_guid = create_guid;
1195 io1.in.timeout = UINT32_MAX;
1197 smb2cli_session_start_replay(tree->session->smbXcli);
1198 status = smb2_create(tree, mem_ctx, &io1);
1199 smb2cli_session_stop_replay(tree->session->smbXcli);
1200 CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED);
1202 done:
1203 smb2cli_session_stop_replay(tree->session->smbXcli);
1205 if (h1 != NULL) {
1206 smb2_util_close(tree, *h1);
1208 if (h2 != NULL) {
1209 smb2_util_close(tree, *h2);
1211 smb2_deltree(tree, BASEDIR);
1213 talloc_free(tree);
1214 talloc_free(mem_ctx);
1216 return ret;
1220 * Test durability v2 create replay detection on single channel.
1221 * Do the original create with a lease, and do the replay
1222 * with an oplock.
1224 static bool test_replay_dhv2_lease_oplock(struct torture_context *tctx,
1225 struct smb2_tree *tree)
1227 NTSTATUS status;
1228 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1229 struct smb2_handle _h1;
1230 struct smb2_handle *h1 = NULL;
1231 struct smb2_handle _h2;
1232 struct smb2_handle *h2 = NULL;
1233 struct smb2_create io1, io2, ref1;
1234 struct GUID create_guid = GUID_random();
1235 bool ret = true;
1236 const char *fname = BASEDIR "\\replay2_lease1.dat";
1237 struct smb2_transport *transport = tree->session->transport;
1238 uint32_t share_capabilities;
1239 bool share_is_so;
1240 uint32_t server_capabilities;
1241 struct smb2_lease ls1, ls2;
1242 uint64_t lease_key;
1244 if (smbXcli_conn_protocol(transport->conn) < PROTOCOL_SMB3_00) {
1245 torture_skip(tctx, "SMB 3.X Dialect family required for "
1246 "replay tests\n");
1249 server_capabilities = smb2cli_conn_server_capabilities(transport->conn);
1250 if (!(server_capabilities & SMB2_CAP_LEASING)) {
1251 torture_skip(tctx, "leases are not supported");
1254 share_capabilities = smb2cli_tcon_capabilities(tree->smbXcli);
1255 share_is_so = share_capabilities & SMB2_SHARE_CAP_SCALEOUT;
1257 ZERO_STRUCT(break_info);
1258 break_info.tctx = tctx;
1259 tree->session->transport->oplock.handler = torture_oplock_ack_handler;
1260 tree->session->transport->oplock.private_data = tree;
1262 torture_comment(tctx, "Replay of DurableHandleReqV2 with Lease "
1263 "on Single Channel\n");
1264 smb2_util_unlink(tree, fname);
1265 status = torture_smb2_testdir(tree, BASEDIR, &_h1);
1266 CHECK_STATUS(status, NT_STATUS_OK);
1267 smb2_util_close(tree, _h1);
1268 CHECK_VAL(break_info.count, 0);
1270 lease_key = random();
1272 smb2_lease_create(&io1, &ls1, false /* dir */, fname,
1273 lease_key, smb2_util_lease_state("RH"));
1274 io1.in.durable_open = false;
1275 io1.in.durable_open_v2 = true;
1276 io1.in.persistent_open = false;
1277 io1.in.create_guid = create_guid;
1278 io1.in.timeout = UINT32_MAX;
1280 status = smb2_create(tree, mem_ctx, &io1);
1281 CHECK_STATUS(status, NT_STATUS_OK);
1282 ref1 = io1;
1283 _h1 = io1.out.file.handle;
1284 h1 = &_h1;
1285 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1286 CHECK_VAL(io1.out.durable_open, false);
1287 CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1288 CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease_key);
1289 CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease_key);
1290 if (share_is_so) {
1291 CHECK_VAL(io1.out.lease_response.lease_state,
1292 smb2_util_lease_state("R"));
1293 CHECK_VAL(io1.out.durable_open_v2, false);
1294 CHECK_VAL(io1.out.timeout, 0);
1295 } else {
1296 CHECK_VAL(io1.out.lease_response.lease_state,
1297 smb2_util_lease_state("RH"));
1298 CHECK_VAL(io1.out.durable_open_v2, true);
1299 CHECK_VAL(io1.out.timeout, 300*1000);
1303 * Upgrade the lease to RWH
1305 smb2_lease_create(&io2, &ls2, false /* dir */, fname,
1306 lease_key, smb2_util_lease_state("RHW"));
1307 io2.in.durable_open = false;
1308 io2.in.durable_open_v2 = true;
1309 io2.in.persistent_open = false;
1310 io2.in.create_guid = GUID_random(); /* new guid... */
1311 io2.in.timeout = UINT32_MAX;
1313 status = smb2_create(tree, mem_ctx, &io2);
1314 CHECK_STATUS(status, NT_STATUS_OK);
1315 _h2 = io2.out.file.handle;
1316 h2 = &_h2;
1319 * Replay Durable V2 Create on single channel.
1320 * We get the io from open #1 but with the
1321 * upgraded lease.
1324 smb2_oplock_create_share(&io2, fname,
1325 smb2_util_share_access(""),
1326 smb2_util_oplock_level("b"));
1327 io2.in.durable_open = false;
1328 io2.in.durable_open_v2 = true;
1329 io2.in.persistent_open = false;
1330 io2.in.create_guid = create_guid;
1331 io2.in.timeout = UINT32_MAX;
1333 /* adapt expected lease in response */
1334 if (!share_is_so) {
1335 ref1.out.lease_response.lease_state =
1336 smb2_util_lease_state("RHW");
1339 smb2cli_session_start_replay(tree->session->smbXcli);
1340 status = smb2_create(tree, mem_ctx, &io1);
1341 smb2cli_session_stop_replay(tree->session->smbXcli);
1342 CHECK_STATUS(status, NT_STATUS_OK);
1343 CHECK_CREATE_OUT(&io1, &ref1);
1344 CHECK_VAL(break_info.count, 0);
1346 done:
1347 smb2cli_session_stop_replay(tree->session->smbXcli);
1349 if (h1 != NULL) {
1350 smb2_util_close(tree, *h1);
1352 if (h2 != NULL) {
1353 smb2_util_close(tree, *h2);
1355 smb2_deltree(tree, BASEDIR);
1357 talloc_free(tree);
1358 talloc_free(mem_ctx);
1360 return ret;
1363 static bool test_channel_sequence_table(struct torture_context *tctx,
1364 struct smb2_tree *tree,
1365 bool do_replay,
1366 uint16_t opcode)
1368 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
1369 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1370 struct smb2_handle handle;
1371 struct smb2_handle *phandle = NULL;
1372 struct smb2_create io;
1373 struct GUID create_guid = GUID_random();
1374 bool ret = true;
1375 const char *fname = BASEDIR "\\channel_sequence.dat";
1376 uint16_t csn = 0;
1377 uint16_t limit = UINT16_MAX - 0x7fff;
1378 int i;
1379 struct {
1380 uint16_t csn;
1381 bool csn_rand_low;
1382 bool csn_rand_high;
1383 NTSTATUS expected_status;
1384 } tests[] = {
1386 .csn = 0,
1387 .expected_status = NT_STATUS_OK,
1389 .csn = 0x7fff + 1,
1390 .expected_status = NT_STATUS_FILE_NOT_AVAILABLE,
1392 .csn = 0x7fff + 2,
1393 .expected_status = NT_STATUS_FILE_NOT_AVAILABLE,
1395 .csn = -1,
1396 .csn_rand_high = true,
1397 .expected_status = NT_STATUS_FILE_NOT_AVAILABLE,
1399 .csn = 0xffff,
1400 .expected_status = NT_STATUS_FILE_NOT_AVAILABLE,
1402 .csn = 0x7fff,
1403 .expected_status = NT_STATUS_OK,
1405 .csn = 0x7ffe,
1406 .expected_status = NT_STATUS_FILE_NOT_AVAILABLE,
1408 .csn = 0,
1409 .expected_status = NT_STATUS_FILE_NOT_AVAILABLE,
1411 .csn = -1,
1412 .csn_rand_low = true,
1413 .expected_status = NT_STATUS_FILE_NOT_AVAILABLE,
1415 .csn = 0x7fff + 1,
1416 .expected_status = NT_STATUS_OK,
1418 .csn = 0xffff,
1419 .expected_status = NT_STATUS_OK,
1421 .csn = 0,
1422 .expected_status = NT_STATUS_OK,
1424 .csn = 1,
1425 .expected_status = NT_STATUS_OK,
1427 .csn = 0,
1428 .expected_status = NT_STATUS_FILE_NOT_AVAILABLE,
1430 .csn = 1,
1431 .expected_status = NT_STATUS_OK,
1433 .csn = 0xffff,
1434 .expected_status = NT_STATUS_FILE_NOT_AVAILABLE,
1438 smb2cli_session_reset_channel_sequence(tree->session->smbXcli, 0);
1440 csn = smb2cli_session_current_channel_sequence(tree->session->smbXcli);
1441 torture_comment(tctx, "Testing create with channel sequence number: 0x%04x\n", csn);
1443 smb2_oplock_create_share(&io, fname,
1444 smb2_util_share_access("RWD"),
1445 smb2_util_oplock_level("b"));
1446 io.in.durable_open = false;
1447 io.in.durable_open_v2 = true;
1448 io.in.create_guid = create_guid;
1449 io.in.timeout = UINT32_MAX;
1451 torture_assert_ntstatus_ok_goto(tctx,
1452 smb2_create(tree, mem_ctx, &io),
1453 ret, done, "failed to call smb2_create");
1455 handle = io.out.file.handle;
1456 phandle = &handle;
1458 for (i=0; i <ARRAY_SIZE(tests); i++) {
1460 const char *opstr = "";
1461 union smb_fileinfo qfinfo;
1463 csn = tests[i].csn;
1465 if (tests[i].csn_rand_low) {
1466 csn = rand() % limit;
1467 } else if (tests[i].csn_rand_high) {
1468 csn = rand() % limit + 0x7fff;
1471 switch (opcode) {
1472 case SMB2_OP_WRITE:
1473 opstr = "write";
1474 break;
1475 case SMB2_OP_IOCTL:
1476 opstr = "ioctl";
1477 break;
1478 case SMB2_OP_SETINFO:
1479 opstr = "setinfo";
1480 break;
1481 default:
1482 break;
1485 smb2cli_session_reset_channel_sequence(tree->session->smbXcli, csn);
1486 csn = smb2cli_session_current_channel_sequence(tree->session->smbXcli);
1488 torture_comment(tctx, "Testing %s (replay: %s) with CSN 0x%04x, expecting: %s\n",
1489 opstr, do_replay ? "true" : "false", csn,
1490 nt_errstr(tests[i].expected_status));
1492 if (do_replay) {
1493 smb2cli_session_start_replay(tree->session->smbXcli);
1496 switch (opcode) {
1497 case SMB2_OP_WRITE: {
1498 DATA_BLOB blob = data_blob_talloc(tctx, NULL, 255);
1500 generate_random_buffer(blob.data, blob.length);
1502 status = smb2_util_write(tree, handle, blob.data, 0, blob.length);
1503 if (NT_STATUS_IS_OK(status)) {
1504 struct smb2_read rd;
1506 rd = (struct smb2_read) {
1507 .in.file.handle = handle,
1508 .in.length = blob.length,
1509 .in.offset = 0
1512 torture_assert_ntstatus_ok_goto(tctx,
1513 smb2_read(tree, tree, &rd),
1514 ret, done, "failed to read after write");
1516 torture_assert_data_blob_equal(tctx,
1517 rd.out.data, blob,
1518 "read/write mismatch");
1520 break;
1522 case SMB2_OP_IOCTL: {
1523 union smb_ioctl ioctl;
1524 ioctl = (union smb_ioctl) {
1525 .smb2.level = RAW_IOCTL_SMB2,
1526 .smb2.in.file.handle = handle,
1527 .smb2.in.function = FSCTL_CREATE_OR_GET_OBJECT_ID,
1528 .smb2.in.max_output_response = 64,
1529 .smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL
1531 status = smb2_ioctl(tree, mem_ctx, &ioctl.smb2);
1532 break;
1534 case SMB2_OP_SETINFO: {
1535 union smb_setfileinfo sfinfo;
1536 ZERO_STRUCT(sfinfo);
1537 sfinfo.generic.level = RAW_SFILEINFO_POSITION_INFORMATION;
1538 sfinfo.generic.in.file.handle = handle;
1539 sfinfo.position_information.in.position = 0x1000;
1540 status = smb2_setinfo_file(tree, &sfinfo);
1541 break;
1543 default:
1544 break;
1547 qfinfo = (union smb_fileinfo) {
1548 .generic.level = RAW_FILEINFO_POSITION_INFORMATION,
1549 .generic.in.file.handle = handle
1552 torture_assert_ntstatus_ok_goto(tctx,
1553 smb2_getinfo_file(tree, mem_ctx, &qfinfo),
1554 ret, done, "failed to read after write");
1556 if (do_replay) {
1557 smb2cli_session_stop_replay(tree->session->smbXcli);
1560 torture_assert_ntstatus_equal_goto(tctx,
1561 status, tests[i].expected_status,
1562 ret, done, "got unexpected failure code");
1565 done:
1566 if (phandle != NULL) {
1567 smb2_util_close(tree, *phandle);
1570 smb2_util_unlink(tree, fname);
1572 return ret;
1575 static bool test_channel_sequence(struct torture_context *tctx,
1576 struct smb2_tree *tree)
1578 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1579 bool ret = true;
1580 const char *fname = BASEDIR "\\channel_sequence.dat";
1581 struct smb2_transport *transport1 = tree->session->transport;
1582 struct smb2_handle handle;
1583 uint16_t opcodes[] = { SMB2_OP_WRITE, SMB2_OP_IOCTL, SMB2_OP_SETINFO };
1584 int i;
1586 if (smbXcli_conn_protocol(transport1->conn) < PROTOCOL_SMB3_00) {
1587 torture_skip(tctx, "SMB 3.X Dialect family required for "
1588 "Replay tests\n");
1591 torture_comment(tctx, "Testing channel sequence numbers\n");
1593 smbXcli_conn_set_force_channel_sequence(transport1->conn, true);
1595 torture_assert_ntstatus_ok_goto(tctx,
1596 torture_smb2_testdir(tree, BASEDIR, &handle),
1597 ret, done, "failed to setup test directory");
1599 smb2_util_close(tree, handle);
1600 smb2_util_unlink(tree, fname);
1602 for (i=0; i <ARRAY_SIZE(opcodes); i++) {
1603 torture_assert(tctx,
1604 test_channel_sequence_table(tctx, tree, false, opcodes[i]),
1605 "failed to test CSN without replay flag");
1606 torture_assert(tctx,
1607 test_channel_sequence_table(tctx, tree, true, opcodes[i]),
1608 "failed to test CSN with replay flag");
1611 done:
1613 smb2_util_unlink(tree, fname);
1614 smb2_deltree(tree, BASEDIR);
1616 talloc_free(tree);
1617 talloc_free(mem_ctx);
1619 return ret;
1623 * Test Durability V2 Create Replay Detection on Multi Channel
1625 static bool test_replay3(struct torture_context *tctx, struct smb2_tree *tree1)
1627 const char *host = torture_setting_string(tctx, "host", NULL);
1628 const char *share = torture_setting_string(tctx, "share", NULL);
1629 NTSTATUS status;
1630 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1631 struct smb2_handle _h;
1632 struct smb2_handle *h = NULL;
1633 struct smb2_create io;
1634 struct GUID create_guid = GUID_random();
1635 bool ret = true;
1636 const char *fname = BASEDIR "\\replay3.dat";
1637 struct smb2_tree *tree2 = NULL;
1638 struct smb2_transport *transport1 = tree1->session->transport;
1639 struct smb2_transport *transport2 = NULL;
1640 struct smb2_session *session1_1 = tree1->session;
1641 struct smb2_session *session1_2 = NULL;
1642 uint32_t share_capabilities;
1643 bool share_is_so;
1644 uint32_t server_capabilities;
1646 if (smbXcli_conn_protocol(transport1->conn) < PROTOCOL_SMB3_00) {
1647 torture_skip(tctx, "SMB 3.X Dialect family required for "
1648 "Replay tests\n");
1651 server_capabilities = smb2cli_conn_server_capabilities(
1652 tree1->session->transport->conn);
1653 if (!(server_capabilities & SMB2_CAP_MULTI_CHANNEL)) {
1654 torture_skip(tctx,
1655 "Server does not support multi-channel.");
1658 share_capabilities = smb2cli_tcon_capabilities(tree1->smbXcli);
1659 share_is_so = share_capabilities & SMB2_SHARE_CAP_SCALEOUT;
1661 ZERO_STRUCT(break_info);
1662 break_info.tctx = tctx;
1663 transport1->oplock.handler = torture_oplock_ack_handler;
1664 transport1->oplock.private_data = tree1;
1666 torture_comment(tctx, "Replay of DurableHandleReqV2 on Multi "
1667 "Channel\n");
1668 status = torture_smb2_testdir(tree1, BASEDIR, &_h);
1669 CHECK_STATUS(status, NT_STATUS_OK);
1670 smb2_util_close(tree1, _h);
1671 smb2_util_unlink(tree1, fname);
1672 CHECK_VAL(break_info.count, 0);
1675 * use the 1st channel, 1st session
1677 smb2_oplock_create_share(&io, fname,
1678 smb2_util_share_access(""),
1679 smb2_util_oplock_level("b"));
1680 io.in.durable_open = false;
1681 io.in.durable_open_v2 = true;
1682 io.in.persistent_open = false;
1683 io.in.create_guid = create_guid;
1684 io.in.timeout = UINT32_MAX;
1686 tree1->session = session1_1;
1687 status = smb2_create(tree1, mem_ctx, &io);
1688 CHECK_STATUS(status, NT_STATUS_OK);
1689 _h = io.out.file.handle;
1690 h = &_h;
1691 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1692 if (share_is_so) {
1693 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("s"));
1694 CHECK_VAL(io.out.durable_open_v2, false);
1695 CHECK_VAL(io.out.timeout, 0);
1696 } else {
1697 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
1698 CHECK_VAL(io.out.durable_open_v2, true);
1699 CHECK_VAL(io.out.timeout, 300*1000);
1701 CHECK_VAL(io.out.durable_open, false);
1702 CHECK_VAL(break_info.count, 0);
1704 status = smb2_connect(tctx,
1705 host,
1706 lpcfg_smb_ports(tctx->lp_ctx),
1707 share,
1708 lpcfg_resolve_context(tctx->lp_ctx),
1709 popt_get_cmdline_credentials(),
1710 &tree2,
1711 tctx->ev,
1712 &transport1->options,
1713 lpcfg_socket_options(tctx->lp_ctx),
1714 lpcfg_gensec_settings(tctx, tctx->lp_ctx)
1716 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1717 "smb2_connect failed");
1718 transport2 = tree2->session->transport;
1720 transport2->oplock.handler = torture_oplock_ack_handler;
1721 transport2->oplock.private_data = tree2;
1724 * Now bind the 1st session to 2nd transport channel
1726 session1_2 = smb2_session_channel(transport2,
1727 lpcfg_gensec_settings(tctx, tctx->lp_ctx),
1728 tree2, session1_1);
1729 torture_assert(tctx, session1_2 != NULL, "smb2_session_channel failed");
1731 status = smb2_session_setup_spnego(session1_2,
1732 popt_get_cmdline_credentials(),
1733 0 /* previous_session_id */);
1734 CHECK_STATUS(status, NT_STATUS_OK);
1737 * use the 2nd channel, 1st session
1739 tree1->session = session1_2;
1740 smb2cli_session_start_replay(tree1->session->smbXcli);
1741 status = smb2_create(tree1, mem_ctx, &io);
1742 smb2cli_session_stop_replay(tree1->session->smbXcli);
1743 CHECK_STATUS(status, NT_STATUS_OK);
1744 _h = io.out.file.handle;
1745 h = &_h;
1746 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1747 if (share_is_so) {
1748 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("s"));
1749 CHECK_VAL(io.out.durable_open_v2, false);
1750 CHECK_VAL(io.out.timeout, 0);
1751 } else {
1752 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
1753 CHECK_VAL(io.out.durable_open_v2, true);
1754 CHECK_VAL(io.out.timeout, 300*1000);
1756 CHECK_VAL(io.out.durable_open, false);
1757 CHECK_VAL(break_info.count, 0);
1759 tree1->session = session1_1;
1760 smb2_util_close(tree1, *h);
1761 h = NULL;
1763 done:
1764 talloc_free(tree2);
1765 tree1->session = session1_1;
1767 if (h != NULL) {
1768 smb2_util_close(tree1, *h);
1771 smb2_util_unlink(tree1, fname);
1772 smb2_deltree(tree1, BASEDIR);
1774 talloc_free(tree1);
1775 talloc_free(mem_ctx);
1777 return ret;
1781 * Test Multichannel IO Ordering using ChannelSequence/Channel Epoch number
1783 static bool test_replay4(struct torture_context *tctx, struct smb2_tree *tree1)
1785 const char *host = torture_setting_string(tctx, "host", NULL);
1786 const char *share = torture_setting_string(tctx, "share", NULL);
1787 NTSTATUS status;
1788 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1789 struct smb2_handle _h1;
1790 struct smb2_handle *h1 = NULL;
1791 struct smb2_create io;
1792 struct GUID create_guid = GUID_random();
1793 uint8_t buf[64];
1794 struct smb2_read rd;
1795 union smb_setfileinfo sfinfo;
1796 bool ret = true;
1797 const char *fname = BASEDIR "\\replay4.dat";
1798 struct smb2_tree *tree2 = NULL;
1799 struct smb2_transport *transport1 = tree1->session->transport;
1800 struct smb2_transport *transport2 = NULL;
1801 struct smb2_session *session1_1 = tree1->session;
1802 struct smb2_session *session1_2 = NULL;
1803 uint16_t curr_cs;
1804 uint32_t share_capabilities;
1805 bool share_is_so;
1806 uint32_t server_capabilities;
1808 if (smbXcli_conn_protocol(transport1->conn) < PROTOCOL_SMB3_00) {
1809 torture_skip(tctx, "SMB 3.X Dialect family required for "
1810 "Replay tests\n");
1813 server_capabilities = smb2cli_conn_server_capabilities(
1814 tree1->session->transport->conn);
1815 if (!(server_capabilities & SMB2_CAP_MULTI_CHANNEL)) {
1816 torture_skip(tctx,
1817 "Server does not support multi-channel.");
1820 share_capabilities = smb2cli_tcon_capabilities(tree1->smbXcli);
1821 share_is_so = share_capabilities & SMB2_SHARE_CAP_SCALEOUT;
1823 ZERO_STRUCT(break_info);
1824 break_info.tctx = tctx;
1825 transport1->oplock.handler = torture_oplock_ack_handler;
1826 transport1->oplock.private_data = tree1;
1828 torture_comment(tctx, "IO Ordering for Multi Channel\n");
1829 status = torture_smb2_testdir(tree1, BASEDIR, &_h1);
1830 CHECK_STATUS(status, NT_STATUS_OK);
1831 smb2_util_close(tree1, _h1);
1832 smb2_util_unlink(tree1, fname);
1833 CHECK_VAL(break_info.count, 0);
1836 * use the 1st channel, 1st session
1839 smb2_oplock_create_share(&io, fname,
1840 smb2_util_share_access(""),
1841 smb2_util_oplock_level("b"));
1842 io.in.durable_open = false;
1843 io.in.durable_open_v2 = true;
1844 io.in.persistent_open = false;
1845 io.in.create_guid = create_guid;
1846 io.in.timeout = UINT32_MAX;
1848 tree1->session = session1_1;
1849 status = smb2_create(tree1, mem_ctx, &io);
1850 CHECK_STATUS(status, NT_STATUS_OK);
1851 _h1 = io.out.file.handle;
1852 h1 = &_h1;
1853 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1854 if (share_is_so) {
1855 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("s"));
1856 CHECK_VAL(io.out.durable_open_v2, false);
1857 CHECK_VAL(io.out.timeout, 0);
1858 } else {
1859 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
1860 CHECK_VAL(io.out.durable_open_v2, true);
1861 CHECK_VAL(io.out.timeout, 300*1000);
1863 CHECK_VAL(io.out.durable_open, false);
1864 CHECK_VAL(break_info.count, 0);
1866 status = smb2_util_write(tree1, *h1, buf, 0, ARRAY_SIZE(buf));
1867 CHECK_STATUS(status, NT_STATUS_OK);
1870 * Increment ChannelSequence so that server thinks that there's a
1871 * Channel Failure
1873 smb2cli_session_increment_channel_sequence(tree1->session->smbXcli);
1876 * Perform a Read with incremented ChannelSequence
1878 rd = (struct smb2_read) {
1879 .in.file.handle = *h1,
1880 .in.length = sizeof(buf),
1881 .in.offset = 0
1883 status = smb2_read(tree1, tree1, &rd);
1884 CHECK_STATUS(status, NT_STATUS_OK);
1887 * Performing a Write with Stale ChannelSequence is not allowed by
1888 * server
1890 curr_cs = smb2cli_session_reset_channel_sequence(
1891 tree1->session->smbXcli, 0);
1892 status = smb2_util_write(tree1, *h1, buf, 0, ARRAY_SIZE(buf));
1893 CHECK_STATUS(status, NT_STATUS_FILE_NOT_AVAILABLE);
1896 * Performing a Write Replay with Stale ChannelSequence is not allowed
1897 * by server
1899 smb2cli_session_start_replay(tree1->session->smbXcli);
1900 smb2cli_session_reset_channel_sequence(tree1->session->smbXcli, 0);
1901 status = smb2_util_write(tree1, *h1, buf, 0, ARRAY_SIZE(buf));
1902 smb2cli_session_stop_replay(tree1->session->smbXcli);
1903 CHECK_STATUS(status, NT_STATUS_FILE_NOT_AVAILABLE);
1906 * Performing a SetInfo with stale ChannelSequence is not allowed by
1907 * server
1909 ZERO_STRUCT(sfinfo);
1910 sfinfo.generic.level = RAW_SFILEINFO_POSITION_INFORMATION;
1911 sfinfo.generic.in.file.handle = *h1;
1912 sfinfo.position_information.in.position = 0x1000;
1913 status = smb2_setinfo_file(tree1, &sfinfo);
1914 CHECK_STATUS(status, NT_STATUS_FILE_NOT_AVAILABLE);
1917 * Performing a Read with stale ChannelSequence is allowed
1919 rd = (struct smb2_read) {
1920 .in.file.handle = *h1,
1921 .in.length = ARRAY_SIZE(buf),
1922 .in.offset = 0
1924 status = smb2_read(tree1, tree1, &rd);
1925 CHECK_STATUS(status, NT_STATUS_OK);
1927 status = smb2_connect(tctx,
1928 host,
1929 lpcfg_smb_ports(tctx->lp_ctx),
1930 share,
1931 lpcfg_resolve_context(tctx->lp_ctx),
1932 popt_get_cmdline_credentials(),
1933 &tree2,
1934 tctx->ev,
1935 &transport1->options,
1936 lpcfg_socket_options(tctx->lp_ctx),
1937 lpcfg_gensec_settings(tctx, tctx->lp_ctx)
1939 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1940 "smb2_connect failed");
1941 transport2 = tree2->session->transport;
1943 transport2->oplock.handler = torture_oplock_ack_handler;
1944 transport2->oplock.private_data = tree2;
1947 * Now bind the 1st session to 2nd transport channel
1949 session1_2 = smb2_session_channel(transport2,
1950 lpcfg_gensec_settings(tctx, tctx->lp_ctx),
1951 tree2, session1_1);
1952 torture_assert(tctx, session1_2 != NULL, "smb2_session_channel failed");
1954 status = smb2_session_setup_spnego(session1_2,
1955 popt_get_cmdline_credentials(),
1956 0 /* previous_session_id */);
1957 CHECK_STATUS(status, NT_STATUS_OK);
1960 * use the 2nd channel, 1st session
1962 tree1->session = session1_2;
1965 * Write Replay with Correct ChannelSequence is allowed by the server
1967 smb2cli_session_start_replay(tree1->session->smbXcli);
1968 smb2cli_session_reset_channel_sequence(tree1->session->smbXcli,
1969 curr_cs);
1970 status = smb2_util_write(tree1, *h1, buf, 0, ARRAY_SIZE(buf));
1971 CHECK_STATUS(status, NT_STATUS_OK);
1972 smb2cli_session_stop_replay(tree1->session->smbXcli);
1975 * See what happens if we change the Buffer and perform a Write Replay.
1976 * This is to show that Write Replay does not really care about the data
1978 memset(buf, 'r', ARRAY_SIZE(buf));
1979 smb2cli_session_start_replay(tree1->session->smbXcli);
1980 status = smb2_util_write(tree1, *h1, buf, 0, ARRAY_SIZE(buf));
1981 CHECK_STATUS(status, NT_STATUS_OK);
1982 smb2cli_session_stop_replay(tree1->session->smbXcli);
1985 * Read back from File to verify what was written
1987 rd = (struct smb2_read) {
1988 .in.file.handle = *h1,
1989 .in.length = ARRAY_SIZE(buf),
1990 .in.offset = 0
1992 status = smb2_read(tree1, tree1, &rd);
1993 CHECK_STATUS(status, NT_STATUS_OK);
1995 if ((rd.out.data.length != ARRAY_SIZE(buf)) ||
1996 memcmp(rd.out.data.data, buf, ARRAY_SIZE(buf))) {
1997 torture_comment(tctx, "Write Replay Data Mismatch\n");
2000 tree1->session = session1_1;
2001 smb2_util_close(tree1, *h1);
2002 h1 = NULL;
2004 if (share_is_so) {
2005 CHECK_VAL(break_info.count, 1);
2006 } else {
2007 CHECK_VAL(break_info.count, 0);
2009 done:
2010 talloc_free(tree2);
2011 tree1->session = session1_1;
2013 if (h1 != NULL) {
2014 smb2_util_close(tree1, *h1);
2017 smb2_util_unlink(tree1, fname);
2018 smb2_deltree(tree1, BASEDIR);
2020 talloc_free(tree1);
2021 talloc_free(mem_ctx);
2023 return ret;
2027 * Test Durability V2 Persistent Create Replay on a Single Channel
2029 static bool test_replay5(struct torture_context *tctx, struct smb2_tree *tree)
2031 NTSTATUS status;
2032 TALLOC_CTX *mem_ctx = talloc_new(tctx);
2033 struct smb2_handle _h;
2034 struct smb2_handle *h = NULL;
2035 struct smb2_create io;
2036 struct GUID create_guid = GUID_random();
2037 bool ret = true;
2038 uint32_t share_capabilities;
2039 bool share_is_ca;
2040 bool share_is_so;
2041 uint32_t server_capabilities;
2042 const char *fname = BASEDIR "\\replay5.dat";
2043 struct smb2_transport *transport = tree->session->transport;
2044 struct smbcli_options options = tree->session->transport->options;
2045 uint8_t expect_oplock = smb2_util_oplock_level("b");
2046 NTSTATUS expect_status = NT_STATUS_DUPLICATE_OBJECTID;
2048 if (smbXcli_conn_protocol(transport->conn) < PROTOCOL_SMB3_00) {
2049 torture_skip(tctx, "SMB 3.X Dialect family required for "
2050 "Replay tests\n");
2053 server_capabilities = smb2cli_conn_server_capabilities(
2054 tree->session->transport->conn);
2055 if (!(server_capabilities & SMB2_CAP_PERSISTENT_HANDLES)) {
2056 torture_skip(tctx,
2057 "Server does not support persistent handles.");
2060 share_capabilities = smb2cli_tcon_capabilities(tree->smbXcli);
2062 share_is_ca = share_capabilities & SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY;
2063 if (!share_is_ca) {
2064 torture_skip(tctx, "Share is not continuously available.");
2067 share_is_so = share_capabilities & SMB2_SHARE_CAP_SCALEOUT;
2068 if (share_is_so) {
2069 expect_oplock = smb2_util_oplock_level("s");
2070 expect_status = NT_STATUS_FILE_NOT_AVAILABLE;
2073 ZERO_STRUCT(break_info);
2074 break_info.tctx = tctx;
2075 transport->oplock.handler = torture_oplock_ack_handler;
2076 transport->oplock.private_data = tree;
2078 torture_comment(tctx, "Replay of Persistent DurableHandleReqV2 on Single "
2079 "Channel\n");
2080 status = torture_smb2_testdir(tree, BASEDIR, &_h);
2081 CHECK_STATUS(status, NT_STATUS_OK);
2082 smb2_util_close(tree, _h);
2083 smb2_util_unlink(tree, fname);
2084 CHECK_VAL(break_info.count, 0);
2086 smb2_oplock_create_share(&io, fname,
2087 smb2_util_share_access("RWD"),
2088 smb2_util_oplock_level("b"));
2089 io.in.durable_open = false;
2090 io.in.durable_open_v2 = true;
2091 io.in.persistent_open = true;
2092 io.in.create_guid = create_guid;
2093 io.in.timeout = UINT32_MAX;
2095 status = smb2_create(tree, mem_ctx, &io);
2096 CHECK_STATUS(status, NT_STATUS_OK);
2097 _h = io.out.file.handle;
2098 h = &_h;
2099 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2100 CHECK_VAL(io.out.oplock_level, expect_oplock);
2101 CHECK_VAL(io.out.durable_open, false);
2102 CHECK_VAL(io.out.durable_open_v2, true);
2103 CHECK_VAL(io.out.persistent_open, true);
2104 CHECK_VAL(io.out.timeout, 300*1000);
2105 CHECK_VAL(break_info.count, 0);
2107 /* disconnect, leaving the durable open */
2108 TALLOC_FREE(tree);
2110 if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
2111 torture_warning(tctx, "couldn't reconnect, bailing\n");
2112 ret = false;
2113 goto done;
2116 /* a re-open of a persistent handle causes an error */
2117 status = smb2_create(tree, mem_ctx, &io);
2118 CHECK_STATUS(status, expect_status);
2120 /* SMB2_FLAGS_REPLAY_OPERATION must be set to open the Persistent Handle */
2121 smb2cli_session_start_replay(tree->session->smbXcli);
2122 smb2cli_session_increment_channel_sequence(tree->session->smbXcli);
2123 status = smb2_create(tree, mem_ctx, &io);
2124 CHECK_STATUS(status, NT_STATUS_OK);
2125 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2126 CHECK_VAL(io.out.durable_open, false);
2127 CHECK_VAL(io.out.persistent_open, true);
2128 CHECK_VAL(io.out.oplock_level, expect_oplock);
2129 _h = io.out.file.handle;
2130 h = &_h;
2132 smb2_util_close(tree, *h);
2133 h = NULL;
2134 done:
2135 if (h != NULL) {
2136 smb2_util_close(tree, *h);
2139 smb2_util_unlink(tree, fname);
2140 smb2_deltree(tree, BASEDIR);
2142 talloc_free(tree);
2143 talloc_free(mem_ctx);
2145 return ret;
2150 * Test Error Codes when a DurableHandleReqV2 with matching CreateGuid is
2151 * re-sent with or without SMB2_FLAGS_REPLAY_OPERATION
2153 static bool test_replay6(struct torture_context *tctx, struct smb2_tree *tree)
2155 NTSTATUS status;
2156 TALLOC_CTX *mem_ctx = talloc_new(tctx);
2157 struct smb2_handle _h;
2158 struct smb2_handle *h = NULL;
2159 struct smb2_create io, ref1;
2160 union smb_fileinfo qfinfo;
2161 struct GUID create_guid = GUID_random();
2162 bool ret = true;
2163 const char *fname = BASEDIR "\\replay6.dat";
2164 struct smb2_transport *transport = tree->session->transport;
2166 if (smbXcli_conn_protocol(transport->conn) < PROTOCOL_SMB3_00) {
2167 torture_skip(tctx, "SMB 3.X Dialect family required for "
2168 "replay tests\n");
2171 torture_reset_break_info(tctx, &break_info);
2172 tree->session->transport->oplock.handler = torture_oplock_ack_handler;
2173 tree->session->transport->oplock.private_data = tree;
2175 torture_comment(tctx, "Error Codes for DurableHandleReqV2 Replay\n");
2176 smb2_util_unlink(tree, fname);
2177 status = torture_smb2_testdir(tree, BASEDIR, &_h);
2178 CHECK_STATUS(status, NT_STATUS_OK);
2179 smb2_util_close(tree, _h);
2180 torture_wait_for_oplock_break(tctx);
2181 CHECK_VAL(break_info.count, 0);
2182 torture_reset_break_info(tctx, &break_info);
2184 smb2_oplock_create_share(&io, fname,
2185 smb2_util_share_access("RWD"),
2186 smb2_util_oplock_level("b"));
2187 io.in.durable_open = false;
2188 io.in.durable_open_v2 = true;
2189 io.in.persistent_open = false;
2190 io.in.create_guid = create_guid;
2191 io.in.timeout = UINT32_MAX;
2193 status = smb2_create(tree, mem_ctx, &io);
2194 CHECK_STATUS(status, NT_STATUS_OK);
2195 ref1 = io;
2196 _h = io.out.file.handle;
2197 h = &_h;
2198 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2199 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
2200 CHECK_VAL(io.out.durable_open, false);
2201 CHECK_VAL(io.out.durable_open_v2, true);
2203 io.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
2204 io.in.create_disposition = NTCREATEX_DISP_OPEN;
2205 smb2cli_session_start_replay(tree->session->smbXcli);
2206 status = smb2_create(tree, mem_ctx, &io);
2207 smb2cli_session_stop_replay(tree->session->smbXcli);
2208 CHECK_STATUS(status, NT_STATUS_OK);
2209 CHECK_CREATE_OUT(&io, &ref1);
2210 torture_wait_for_oplock_break(tctx);
2211 CHECK_VAL(break_info.count, 0);
2212 torture_reset_break_info(tctx, &break_info);
2214 qfinfo = (union smb_fileinfo) {
2215 .generic.level = RAW_FILEINFO_POSITION_INFORMATION,
2216 .generic.in.file.handle = *h
2218 torture_comment(tctx, "Trying getinfo\n");
2219 status = smb2_getinfo_file(tree, mem_ctx, &qfinfo);
2220 CHECK_STATUS(status, NT_STATUS_OK);
2221 CHECK_VAL(qfinfo.position_information.out.position, 0);
2223 smb2cli_session_start_replay(tree->session->smbXcli);
2224 status = smb2_create(tree, mem_ctx, &io);
2225 smb2cli_session_stop_replay(tree->session->smbXcli);
2226 CHECK_STATUS(status, NT_STATUS_OK);
2227 torture_assert_u64_not_equal_goto(tctx,
2228 io.out.file.handle.data[0],
2229 ref1.out.file.handle.data[0],
2230 ret, done, "data 0");
2231 torture_assert_u64_not_equal_goto(tctx,
2232 io.out.file.handle.data[1],
2233 ref1.out.file.handle.data[1],
2234 ret, done, "data 1");
2235 torture_wait_for_oplock_break(tctx);
2236 CHECK_VAL(break_info.count, 1);
2237 CHECK_VAL(break_info.level, smb2_util_oplock_level("s"));
2238 torture_reset_break_info(tctx, &break_info);
2241 * Resend the matching Durable V2 Create without
2242 * SMB2_FLAGS_REPLAY_OPERATION. This triggers an oplock break and still
2243 * gets NT_STATUS_DUPLICATE_OBJECTID
2245 status = smb2_create(tree, mem_ctx, &io);
2246 CHECK_STATUS(status, NT_STATUS_DUPLICATE_OBJECTID);
2247 torture_wait_for_oplock_break(tctx);
2248 CHECK_VAL(break_info.count, 0);
2249 torture_reset_break_info(tctx, &break_info);
2252 * According to MS-SMB2 3.3.5.9.10 if Durable V2 Create is replayed and
2253 * FileAttributes or CreateDisposition do not match the earlier Create
2254 * request the Server fails request with
2255 * NT_STATUS_INVALID_PARAMETER. But through this test we see that server
2256 * does not really care about changed FileAttributes or
2257 * CreateDisposition.
2259 io.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
2260 io.in.create_disposition = NTCREATEX_DISP_OPEN;
2261 smb2cli_session_start_replay(tree->session->smbXcli);
2262 status = smb2_create(tree, mem_ctx, &io);
2263 smb2cli_session_stop_replay(tree->session->smbXcli);
2264 CHECK_STATUS(status, NT_STATUS_OK);
2265 torture_assert_u64_not_equal_goto(tctx,
2266 io.out.file.handle.data[0],
2267 ref1.out.file.handle.data[0],
2268 ret, done, "data 0");
2269 torture_assert_u64_not_equal_goto(tctx,
2270 io.out.file.handle.data[1],
2271 ref1.out.file.handle.data[1],
2272 ret, done, "data 1");
2273 torture_wait_for_oplock_break(tctx);
2274 CHECK_VAL(break_info.count, 0);
2276 done:
2277 if (h != NULL) {
2278 smb2_util_close(tree, *h);
2281 smb2_util_unlink(tree, fname);
2282 smb2_deltree(tree, BASEDIR);
2284 talloc_free(tree);
2285 talloc_free(mem_ctx);
2287 return ret;
2290 static bool test_replay7(struct torture_context *tctx, struct smb2_tree *tree)
2292 TALLOC_CTX *mem_ctx = talloc_new(tctx);
2293 struct smb2_transport *transport = tree->session->transport;
2294 NTSTATUS status;
2295 struct smb2_handle _dh;
2296 struct smb2_handle *dh = NULL;
2297 struct smb2_notify notify;
2298 struct smb2_request *req;
2299 union smb_fileinfo qfinfo;
2300 bool ret = false;
2302 if (smbXcli_conn_protocol(transport->conn) < PROTOCOL_SMB3_00) {
2303 torture_skip(tctx, "SMB 3.X Dialect family required for "
2304 "replay tests\n");
2307 torture_comment(tctx, "Notify across increment/decrement of csn\n");
2309 smbXcli_conn_set_force_channel_sequence(transport->conn, true);
2311 status = torture_smb2_testdir(tree, BASEDIR, &_dh);
2312 CHECK_STATUS(status, NT_STATUS_OK);
2313 dh = &_dh;
2315 notify.in.recursive = 0x0000;
2316 notify.in.buffer_size = 0xffff;
2317 notify.in.file.handle = _dh;
2318 notify.in.completion_filter = FILE_NOTIFY_CHANGE_FILE_NAME;
2319 notify.in.unknown = 0x00000000;
2322 * This posts a long-running request with csn==0 to "dh". Now
2323 * op->request_count==1 in smb2_server.c.
2325 smb2cli_session_reset_channel_sequence(tree->session->smbXcli, 0);
2326 req = smb2_notify_send(tree, &notify);
2328 qfinfo = (union smb_fileinfo) {
2329 .generic.level = RAW_FILEINFO_POSITION_INFORMATION,
2330 .generic.in.file.handle = _dh
2334 * This sequence of 2 dummy requests moves
2335 * op->request_count==1 to op->pre_request_count. The numbers
2336 * used avoid int16 overflow.
2339 smb2cli_session_reset_channel_sequence(tree->session->smbXcli, 30000);
2340 status = smb2_getinfo_file(tree, mem_ctx, &qfinfo);
2341 CHECK_STATUS(status, NT_STATUS_OK);
2343 smb2cli_session_reset_channel_sequence(tree->session->smbXcli, 60000);
2344 status = smb2_getinfo_file(tree, mem_ctx, &qfinfo);
2345 CHECK_STATUS(status, NT_STATUS_OK);
2348 * This final request turns the op->global->channel_sequence
2349 * to the same as we had when sending the notify above. The
2350 * notify's request count has in the meantime moved to
2351 * op->pre_request_count.
2354 smb2cli_session_reset_channel_sequence(tree->session->smbXcli, 0);
2355 status = smb2_getinfo_file(tree, mem_ctx, &qfinfo);
2356 CHECK_STATUS(status, NT_STATUS_OK);
2359 * At this point op->request_count==0.
2361 * The next cancel makes us reply to the notify. Because the
2362 * csn we currently use is the same as we used when sending
2363 * the notify, smbd thinks it must decrement op->request_count
2364 * and not op->pre_request_count.
2367 status = smb2_cancel(req);
2368 CHECK_STATUS(status, NT_STATUS_OK);
2370 status = smb2_notify_recv(req, mem_ctx, &notify);
2371 CHECK_STATUS(status, NT_STATUS_CANCELLED);
2373 ret = true;
2375 done:
2376 if (dh != NULL) {
2377 smb2_util_close(tree, _dh);
2379 smb2_deltree(tree, BASEDIR);
2380 talloc_free(tree);
2381 talloc_free(mem_ctx);
2383 return ret;
2386 struct torture_suite *torture_smb2_replay_init(TALLOC_CTX *ctx)
2388 struct torture_suite *suite =
2389 torture_suite_create(ctx, "replay");
2391 torture_suite_add_1smb2_test(suite, "replay-commands", test_replay_commands);
2392 torture_suite_add_1smb2_test(suite, "replay-regular", test_replay_regular);
2393 torture_suite_add_1smb2_test(suite, "replay-dhv2-oplock1", test_replay_dhv2_oplock1);
2394 torture_suite_add_1smb2_test(suite, "replay-dhv2-oplock2", test_replay_dhv2_oplock2);
2395 torture_suite_add_1smb2_test(suite, "replay-dhv2-oplock3", test_replay_dhv2_oplock3);
2396 torture_suite_add_1smb2_test(suite, "replay-dhv2-oplock-lease", test_replay_dhv2_oplock_lease);
2397 torture_suite_add_1smb2_test(suite, "replay-dhv2-lease1", test_replay_dhv2_lease1);
2398 torture_suite_add_1smb2_test(suite, "replay-dhv2-lease2", test_replay_dhv2_lease2);
2399 torture_suite_add_1smb2_test(suite, "replay-dhv2-lease3", test_replay_dhv2_lease3);
2400 torture_suite_add_1smb2_test(suite, "replay-dhv2-lease-oplock", test_replay_dhv2_lease_oplock);
2401 torture_suite_add_1smb2_test(suite, "channel-sequence", test_channel_sequence);
2402 torture_suite_add_1smb2_test(suite, "replay3", test_replay3);
2403 torture_suite_add_1smb2_test(suite, "replay4", test_replay4);
2404 torture_suite_add_1smb2_test(suite, "replay5", test_replay5);
2405 torture_suite_add_1smb2_test(suite, "replay6", test_replay6);
2406 torture_suite_add_1smb2_test(suite, "replay7", test_replay7);
2408 suite->description = talloc_strdup(suite, "SMB2 REPLAY tests");
2410 return suite;