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 * 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
)
105 struct smb2_handle h
;
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];
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 "
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
) {
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
) {
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
) {
196 .in
.lock_count
= 0x0001,
197 .in
.lock_sequence
= 0x00000000,
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);
217 smb2cli_session_stop_replay(tree
->session
->smbXcli
);
218 smb2_util_close(tree
, h
);
219 smb2_deltree(tree
, BASEDIR
);
221 talloc_free(tmp_ctx
);
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
)
235 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
236 struct smb2_handle _h
;
237 struct smb2_handle
*h
= NULL
;
238 struct smb2_create io
;
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 "
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
|
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,
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
;
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
);
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
;
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
);
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
;
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);
333 smb2_util_close(tree
, *h
);
335 smb2_deltree(tree
, BASEDIR
);
338 talloc_free(mem_ctx
);
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
)
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();
356 const char *fname
= BASEDIR
"\\replay_dhv2_oplock1.dat";
357 struct smb2_transport
*transport
= tree
->session
->transport
;
358 uint32_t share_capabilities
;
361 if (smbXcli_conn_protocol(transport
->conn
) < PROTOCOL_SMB3_00
) {
362 torture_skip(tctx
, "SMB 3.X Dialect family required for "
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 "
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
);
394 _h
= io
.out
.file
.handle
;
396 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
397 CHECK_VAL(io
.out
.durable_open
, false);
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);
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);
420 smb2_util_close(tree
, *h
);
422 smb2_deltree(tree
, BASEDIR
);
425 talloc_free(mem_ctx
);
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
)
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();
447 const char *fname
= BASEDIR
"\\replay_dhv2_oplock2.dat";
448 struct smb2_transport
*transport
= tree
->session
->transport
;
449 uint32_t share_capabilities
;
452 if (smbXcli_conn_protocol(transport
->conn
) < PROTOCOL_SMB3_00
) {
453 torture_skip(tctx
, "SMB 3.X Dialect family required for "
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 "
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
);
485 _h
= io
.out
.file
.handle
;
487 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
488 CHECK_VAL(io
.out
.durable_open
, false);
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);
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
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
);
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
);
557 smb2_util_close(tree
, *h
);
559 smb2_deltree(tree
, BASEDIR
);
562 talloc_free(mem_ctx
);
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
)
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();
582 const char *fname
= BASEDIR
"\\replay_dhv2_oplock3.dat";
583 struct smb2_transport
*transport
= tree
->session
->transport
;
584 uint32_t share_capabilities
;
587 if (smbXcli_conn_protocol(transport
->conn
) < PROTOCOL_SMB3_00
) {
588 torture_skip(tctx
, "SMB 3.X Dialect family required for "
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 "
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
);
620 _h
= io
.out
.file
.handle
;
622 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
623 CHECK_VAL(io
.out
.durable_open
, false);
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);
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
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
);
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
);
683 smb2_util_close(tree
, *h
);
685 smb2_deltree(tree
, BASEDIR
);
688 talloc_free(mem_ctx
);
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
)
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();
707 const char *fname
= BASEDIR
"\\replay_dhv2_oplock1.dat";
708 struct smb2_transport
*transport
= tree
->session
->transport
;
709 uint32_t share_capabilities
;
711 uint32_t server_capabilities
;
712 struct smb2_lease ls
;
715 if (smbXcli_conn_protocol(transport
->conn
) < PROTOCOL_SMB3_00
) {
716 torture_skip(tctx
, "SMB 3.X Dialect family required for "
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 "
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
;
754 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
755 CHECK_VAL(io
.out
.durable_open
, false);
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);
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
);
786 smb2_util_close(tree
, *h
);
788 smb2_deltree(tree
, BASEDIR
);
791 talloc_free(mem_ctx
);
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
)
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();
817 const char *fname
= BASEDIR
"\\replay2_lease1.dat";
818 struct smb2_transport
*transport
= tree
->session
->transport
;
819 uint32_t share_capabilities
;
821 uint32_t server_capabilities
;
822 struct smb2_lease ls1
, ls2
;
825 if (smbXcli_conn_protocol(transport
->conn
) < PROTOCOL_SMB3_00
) {
826 torture_skip(tctx
, "SMB 3.X Dialect family required for "
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
);
864 _h1
= io1
.out
.file
.handle
;
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
);
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);
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
;
900 * Replay Durable V2 Create on single channel.
901 * We get the io from open #1 but with the
905 /* adapt expected lease in response */
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);
919 smb2cli_session_stop_replay(tree
->session
->smbXcli
);
922 smb2_util_close(tree
, *h1
);
925 smb2_util_close(tree
, *h2
);
927 smb2_deltree(tree
, BASEDIR
);
930 talloc_free(mem_ctx
);
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
)
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();
958 const char *fname
= BASEDIR
"\\replay2_lease2.dat";
959 struct smb2_transport
*transport
= tree
->session
->transport
;
960 uint32_t share_capabilities
;
962 uint32_t server_capabilities
;
963 struct smb2_lease ls1
, ls2
;
966 if (smbXcli_conn_protocol(transport
->conn
) < PROTOCOL_SMB3_00
) {
967 torture_skip(tctx
, "SMB 3.X Dialect family required for "
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
);
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);
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);
1021 _h1
= io1
.out
.file
.handle
;
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
;
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
1048 /* adapt the expected response */
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);
1070 smb2cli_session_stop_replay(tree
->session
->smbXcli
);
1073 smb2_util_close(tree
, *h1
);
1076 smb2_util_close(tree
, *h2
);
1078 smb2_deltree(tree
, BASEDIR
);
1081 talloc_free(mem_ctx
);
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
)
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();
1102 const char *fname
= BASEDIR
"\\replay2_lease2.dat";
1103 struct smb2_transport
*transport
= tree
->session
->transport
;
1104 uint32_t share_capabilities
;
1106 uint32_t server_capabilities
;
1107 struct smb2_lease ls1
, ls2
;
1110 if (smbXcli_conn_protocol(transport
->conn
) < PROTOCOL_SMB3_00
) {
1111 torture_skip(tctx
, "SMB 3.X Dialect family required for "
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
);
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);
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
;
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
;
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
);
1203 smb2cli_session_stop_replay(tree
->session
->smbXcli
);
1206 smb2_util_close(tree
, *h1
);
1209 smb2_util_close(tree
, *h2
);
1211 smb2_deltree(tree
, BASEDIR
);
1214 talloc_free(mem_ctx
);
1220 * Test durability v2 create replay detection on single channel.
1221 * Do the original create with a lease, and do the replay
1224 static bool test_replay_dhv2_lease_oplock(struct torture_context
*tctx
,
1225 struct smb2_tree
*tree
)
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();
1236 const char *fname
= BASEDIR
"\\replay2_lease1.dat";
1237 struct smb2_transport
*transport
= tree
->session
->transport
;
1238 uint32_t share_capabilities
;
1240 uint32_t server_capabilities
;
1241 struct smb2_lease ls1
, ls2
;
1244 if (smbXcli_conn_protocol(transport
->conn
) < PROTOCOL_SMB3_00
) {
1245 torture_skip(tctx
, "SMB 3.X Dialect family required for "
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
);
1283 _h1
= io1
.out
.file
.handle
;
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
);
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);
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
;
1319 * Replay Durable V2 Create on single channel.
1320 * We get the io from open #1 but with the
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 */
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);
1347 smb2cli_session_stop_replay(tree
->session
->smbXcli
);
1350 smb2_util_close(tree
, *h1
);
1353 smb2_util_close(tree
, *h2
);
1355 smb2_deltree(tree
, BASEDIR
);
1358 talloc_free(mem_ctx
);
1363 static bool test_channel_sequence_table(struct torture_context
*tctx
,
1364 struct smb2_tree
*tree
,
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();
1375 const char *fname
= BASEDIR
"\\channel_sequence.dat";
1377 uint16_t limit
= UINT16_MAX
- 0x7fff;
1383 NTSTATUS expected_status
;
1387 .expected_status
= NT_STATUS_OK
,
1390 .expected_status
= NT_STATUS_FILE_NOT_AVAILABLE
,
1393 .expected_status
= NT_STATUS_FILE_NOT_AVAILABLE
,
1396 .csn_rand_high
= true,
1397 .expected_status
= NT_STATUS_FILE_NOT_AVAILABLE
,
1400 .expected_status
= NT_STATUS_FILE_NOT_AVAILABLE
,
1403 .expected_status
= NT_STATUS_OK
,
1406 .expected_status
= NT_STATUS_FILE_NOT_AVAILABLE
,
1409 .expected_status
= NT_STATUS_FILE_NOT_AVAILABLE
,
1412 .csn_rand_low
= true,
1413 .expected_status
= NT_STATUS_FILE_NOT_AVAILABLE
,
1416 .expected_status
= NT_STATUS_OK
,
1419 .expected_status
= NT_STATUS_OK
,
1422 .expected_status
= NT_STATUS_OK
,
1425 .expected_status
= NT_STATUS_OK
,
1428 .expected_status
= NT_STATUS_FILE_NOT_AVAILABLE
,
1431 .expected_status
= NT_STATUS_OK
,
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
;
1458 for (i
=0; i
<ARRAY_SIZE(tests
); i
++) {
1460 const char *opstr
= "";
1461 union smb_fileinfo qfinfo
;
1465 if (tests
[i
].csn_rand_low
) {
1466 csn
= rand() % limit
;
1467 } else if (tests
[i
].csn_rand_high
) {
1468 csn
= rand() % limit
+ 0x7fff;
1478 case SMB2_OP_SETINFO
:
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
));
1493 smb2cli_session_start_replay(tree
->session
->smbXcli
);
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
,
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
,
1518 "read/write mismatch");
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
);
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
);
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");
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");
1566 if (phandle
!= NULL
) {
1567 smb2_util_close(tree
, *phandle
);
1570 smb2_util_unlink(tree
, fname
);
1575 static bool test_channel_sequence(struct torture_context
*tctx
,
1576 struct smb2_tree
*tree
)
1578 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
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
};
1586 if (smbXcli_conn_protocol(transport1
->conn
) < PROTOCOL_SMB3_00
) {
1587 torture_skip(tctx
, "SMB 3.X Dialect family required for "
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");
1613 smb2_util_unlink(tree
, fname
);
1614 smb2_deltree(tree
, BASEDIR
);
1617 talloc_free(mem_ctx
);
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
);
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();
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
;
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 "
1651 server_capabilities
= smb2cli_conn_server_capabilities(
1652 tree1
->session
->transport
->conn
);
1653 if (!(server_capabilities
& SMB2_CAP_MULTI_CHANNEL
)) {
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 "
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
;
1691 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
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);
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
,
1706 lpcfg_smb_ports(tctx
->lp_ctx
),
1708 lpcfg_resolve_context(tctx
->lp_ctx
),
1709 popt_get_cmdline_credentials(),
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
),
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
;
1746 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
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);
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
);
1765 tree1
->session
= session1_1
;
1768 smb2_util_close(tree1
, *h
);
1771 smb2_util_unlink(tree1
, fname
);
1772 smb2_deltree(tree1
, BASEDIR
);
1775 talloc_free(mem_ctx
);
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
);
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();
1794 struct smb2_read rd
;
1795 union smb_setfileinfo sfinfo
;
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
;
1804 uint32_t share_capabilities
;
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 "
1813 server_capabilities
= smb2cli_conn_server_capabilities(
1814 tree1
->session
->transport
->conn
);
1815 if (!(server_capabilities
& SMB2_CAP_MULTI_CHANNEL
)) {
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
;
1853 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
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);
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
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
),
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
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
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
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
),
1924 status
= smb2_read(tree1
, tree1
, &rd
);
1925 CHECK_STATUS(status
, NT_STATUS_OK
);
1927 status
= smb2_connect(tctx
,
1929 lpcfg_smb_ports(tctx
->lp_ctx
),
1931 lpcfg_resolve_context(tctx
->lp_ctx
),
1932 popt_get_cmdline_credentials(),
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
),
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
,
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
),
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
);
2005 CHECK_VAL(break_info
.count
, 1);
2007 CHECK_VAL(break_info
.count
, 0);
2011 tree1
->session
= session1_1
;
2014 smb2_util_close(tree1
, *h1
);
2017 smb2_util_unlink(tree1
, fname
);
2018 smb2_deltree(tree1
, BASEDIR
);
2021 talloc_free(mem_ctx
);
2027 * Test Durability V2 Persistent Create Replay on a Single Channel
2029 static bool test_replay5(struct torture_context
*tctx
, struct smb2_tree
*tree
)
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();
2038 uint32_t share_capabilities
;
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 "
2053 server_capabilities
= smb2cli_conn_server_capabilities(
2054 tree
->session
->transport
->conn
);
2055 if (!(server_capabilities
& SMB2_CAP_PERSISTENT_HANDLES
)) {
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
;
2064 torture_skip(tctx
, "Share is not continuously available.");
2067 share_is_so
= share_capabilities
& SMB2_SHARE_CAP_SCALEOUT
;
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 "
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
;
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 */
2110 if (!torture_smb2_connection_ext(tctx
, 0, &options
, &tree
)) {
2111 torture_warning(tctx
, "couldn't reconnect, bailing\n");
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
;
2132 smb2_util_close(tree
, *h
);
2136 smb2_util_close(tree
, *h
);
2139 smb2_util_unlink(tree
, fname
);
2140 smb2_deltree(tree
, BASEDIR
);
2143 talloc_free(mem_ctx
);
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
)
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();
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 "
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
);
2196 _h
= io
.out
.file
.handle
;
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);
2278 smb2_util_close(tree
, *h
);
2281 smb2_util_unlink(tree
, fname
);
2282 smb2_deltree(tree
, BASEDIR
);
2285 talloc_free(mem_ctx
);
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
;
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
;
2302 if (smbXcli_conn_protocol(transport
->conn
) < PROTOCOL_SMB3_00
) {
2303 torture_skip(tctx
, "SMB 3.X Dialect family required for "
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
);
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
, ¬ify
);
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
, ¬ify
);
2371 CHECK_STATUS(status
, NT_STATUS_CANCELLED
);
2377 smb2_util_close(tree
, _dh
);
2379 smb2_deltree(tree
, BASEDIR
);
2381 talloc_free(mem_ctx
);
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");