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"
36 #define CHECK_VAL(v, correct) do { \
37 if ((v) != (correct)) { \
38 torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s got 0x%x - should be 0x%x\n", \
39 __location__, #v, (int)v, (int)correct); \
44 #define CHECK_STATUS(status, correct) do { \
45 if (!NT_STATUS_EQUAL(status, correct)) { \
46 torture_result(tctx, TORTURE_FAIL, __location__": Incorrect status %s - should be %s", \
47 nt_errstr(status), nt_errstr(correct)); \
52 #define CHECK_CREATED(__io, __created, __attribute) \
54 CHECK_VAL((__io)->out.create_action, NTCREATEX_ACTION_ ## __created); \
55 CHECK_VAL((__io)->out.alloc_size, 0); \
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); \
90 #define BASEDIR "replaytestdir"
93 struct torture_context
*tctx
;
94 struct smb2_handle handle
;
99 NTSTATUS failure_status
;
102 static void torture_oplock_ack_callback(struct smb2_request
*req
)
106 status
= smb2_break_recv(req
, &break_info
.br
);
107 if (!NT_STATUS_IS_OK(status
)) {
108 break_info
.failures
++;
109 break_info
.failure_status
= status
;
116 * A general oplock break notification handler. This should be used when a
117 * test expects to break from batch or exclusive to a lower level.
119 static bool torture_oplock_ack_handler(struct smb2_transport
*transport
,
120 const struct smb2_handle
*handle
,
124 struct smb2_tree
*tree
= private_data
;
126 struct smb2_request
*req
;
128 ZERO_STRUCT(break_info
.br
);
130 break_info
.handle
= *handle
;
131 break_info
.level
= level
;
135 case SMB2_OPLOCK_LEVEL_II
:
138 case SMB2_OPLOCK_LEVEL_NONE
:
143 break_info
.failures
++;
145 torture_comment(break_info
.tctx
,
146 "Acking to %s [0x%02X] in oplock handler\n",
149 break_info
.br
.in
.file
.handle
= *handle
;
150 break_info
.br
.in
.oplock_level
= level
;
151 break_info
.br
.in
.reserved
= 0;
152 break_info
.br
.in
.reserved2
= 0;
154 req
= smb2_break_send(tree
, &break_info
.br
);
155 req
->async
.fn
= torture_oplock_ack_callback
;
156 req
->async
.private_data
= NULL
;
161 * Test what happens when SMB2_FLAGS_REPLAY_OPERATION is enabled for various
162 * commands. We want to verify if the server returns an error code or not.
164 static bool test_replay1(struct torture_context
*tctx
, struct smb2_tree
*tree
)
168 struct smb2_handle h
;
171 union smb_setfileinfo sfinfo
;
172 union smb_fileinfo qfinfo
;
173 union smb_ioctl ioctl
;
174 struct smb2_lock lck
;
175 struct smb2_lock_element el
[2];
177 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
178 const char *fname
= BASEDIR
"\\replay1.dat";
179 struct smb2_transport
*transport
= tree
->session
->transport
;
181 if (smbXcli_conn_protocol(transport
->conn
) < PROTOCOL_SMB3_00
) {
182 torture_skip(tctx
, "SMB 3.X Dialect family required for "
186 ZERO_STRUCT(break_info
);
187 break_info
.tctx
= tctx
;
188 tree
->session
->transport
->oplock
.handler
= torture_oplock_ack_handler
;
189 tree
->session
->transport
->oplock
.private_data
= tree
;
191 status
= torture_smb2_testdir(tree
, BASEDIR
, &h
);
192 CHECK_STATUS(status
, NT_STATUS_OK
);
193 smb2_util_close(tree
, h
);
195 smb2cli_session_start_replay(tree
->session
->smbXcli
);
197 torture_comment(tctx
, "Try Commands with Replay Flags Enabled\n");
199 torture_comment(tctx
, "Trying create\n");
200 status
= torture_smb2_testfile(tree
, fname
, &h
);
201 CHECK_STATUS(status
, NT_STATUS_OK
);
202 CHECK_VAL(break_info
.count
, 0);
204 * Wireshark shows that the response has SMB2_FLAGS_REPLAY_OPERATION
205 * flags set. The server should ignore this flag.
208 torture_comment(tctx
, "Trying write\n");
209 status
= smb2_util_write(tree
, h
, buf
, 0, ARRAY_SIZE(buf
));
210 CHECK_STATUS(status
, NT_STATUS_OK
);
212 f
= (struct smb2_flush
) {
215 torture_comment(tctx
, "Trying flush\n");
216 status
= smb2_flush(tree
, &f
);
217 CHECK_STATUS(status
, NT_STATUS_OK
);
219 rd
= (struct smb2_read
) {
225 torture_comment(tctx
, "Trying read\n");
226 status
= smb2_read(tree
, tmp_ctx
, &rd
);
227 CHECK_STATUS(status
, NT_STATUS_OK
);
228 CHECK_VAL(rd
.out
.data
.length
, 10);
230 sfinfo
.generic
.level
= RAW_SFILEINFO_POSITION_INFORMATION
;
231 sfinfo
.position_information
.in
.file
.handle
= h
;
232 sfinfo
.position_information
.in
.position
= 0x1000;
233 torture_comment(tctx
, "Trying setinfo\n");
234 status
= smb2_setinfo_file(tree
, &sfinfo
);
235 CHECK_STATUS(status
, NT_STATUS_OK
);
237 qfinfo
= (union smb_fileinfo
) {
238 .generic
.level
= RAW_SFILEINFO_POSITION_INFORMATION
,
239 .generic
.in
.file
.handle
= h
241 torture_comment(tctx
, "Trying getinfo\n");
242 status
= smb2_getinfo_file(tree
, tmp_ctx
, &qfinfo
);
243 CHECK_STATUS(status
, NT_STATUS_OK
);
244 CHECK_VAL(qfinfo
.position_information
.out
.position
, 0x1000);
246 ioctl
= (union smb_ioctl
) {
247 .smb2
.level
= RAW_IOCTL_SMB2
,
248 .smb2
.in
.file
.handle
= h
,
249 .smb2
.in
.function
= FSCTL_CREATE_OR_GET_OBJECT_ID
,
250 .smb2
.in
.max_response_size
= 64,
251 .smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
253 torture_comment(tctx
, "Trying ioctl\n");
254 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
255 CHECK_STATUS(status
, NT_STATUS_OK
);
257 lck
= (struct smb2_lock
) {
259 .in
.lock_count
= 0x0001,
260 .in
.lock_sequence
= 0x00000000,
263 el
[0].reserved
= 0x00000000;
264 el
[0].flags
= SMB2_LOCK_FLAG_EXCLUSIVE
|
265 SMB2_LOCK_FLAG_FAIL_IMMEDIATELY
;
267 torture_comment(tctx
, "Trying lock\n");
268 el
[0].offset
= 0x0000000000000000;
269 el
[0].length
= 0x0000000000000100;
270 status
= smb2_lock(tree
, &lck
);
271 CHECK_STATUS(status
, NT_STATUS_OK
);
273 lck
.in
.file
.handle
= h
;
274 el
[0].flags
= SMB2_LOCK_FLAG_UNLOCK
;
275 status
= smb2_lock(tree
, &lck
);
276 CHECK_STATUS(status
, NT_STATUS_OK
);
278 CHECK_VAL(break_info
.count
, 0);
280 smb2cli_session_stop_replay(tree
->session
->smbXcli
);
281 smb2_util_close(tree
, h
);
282 smb2_deltree(tree
, BASEDIR
);
284 talloc_free(tmp_ctx
);
290 * Test Durablity V2 Create Replay Detection on Single Channel. Also verify that
291 * regular creates can not be replayed.
293 static bool test_replay2(struct torture_context
*tctx
, struct smb2_tree
*tree
)
296 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
297 struct smb2_handle _h
;
298 struct smb2_handle
*h
= NULL
;
299 struct smb2_create io
, ref1
, ref2
;
300 struct GUID create_guid
= GUID_random();
303 const char *fname
= BASEDIR
"\\replay2.dat";
304 struct smb2_transport
*transport
= tree
->session
->transport
;
305 uint32_t share_capabilities
;
308 if (smbXcli_conn_protocol(transport
->conn
) < PROTOCOL_SMB3_00
) {
309 torture_skip(tctx
, "SMB 3.X Dialect family required for "
313 share_capabilities
= smb2cli_tcon_capabilities(tree
->smbXcli
);
314 share_is_so
= share_capabilities
& SMB2_SHARE_CAP_SCALEOUT
;
316 ZERO_STRUCT(break_info
);
317 break_info
.tctx
= tctx
;
318 tree
->session
->transport
->oplock
.handler
= torture_oplock_ack_handler
;
319 tree
->session
->transport
->oplock
.private_data
= tree
;
321 torture_comment(tctx
, "Replay of DurableHandleReqV2 on Single "
323 smb2_util_unlink(tree
, fname
);
324 status
= torture_smb2_testdir(tree
, BASEDIR
, &_h
);
325 CHECK_STATUS(status
, NT_STATUS_OK
);
326 smb2_util_close(tree
, _h
);
327 CHECK_VAL(break_info
.count
, 0);
329 smb2_oplock_create_share(&io
, fname
,
330 smb2_util_share_access(""),
331 smb2_util_oplock_level("b"));
332 io
.in
.durable_open
= false;
333 io
.in
.durable_open_v2
= true;
334 io
.in
.persistent_open
= false;
335 io
.in
.create_guid
= create_guid
;
336 io
.in
.timeout
= UINT32_MAX
;
338 status
= smb2_create(tree
, mem_ctx
, &io
);
339 CHECK_STATUS(status
, NT_STATUS_OK
);
341 _h
= io
.out
.file
.handle
;
343 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
344 CHECK_VAL(io
.out
.durable_open
, false);
346 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("s"));
347 CHECK_VAL(io
.out
.durable_open_v2
, false);
348 CHECK_VAL(io
.out
.timeout
, 0);
350 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
351 CHECK_VAL(io
.out
.durable_open_v2
, true);
352 CHECK_VAL(io
.out
.timeout
, io
.in
.timeout
);
356 * Replay Durable V2 Create on single channel
358 smb2cli_session_start_replay(tree
->session
->smbXcli
);
359 status
= smb2_create(tree
, mem_ctx
, &io
);
360 smb2cli_session_stop_replay(tree
->session
->smbXcli
);
361 CHECK_STATUS(status
, NT_STATUS_OK
);
362 CHECK_CREATE_OUT(&io
, &ref1
);
363 CHECK_VAL(break_info
.count
, 0);
366 * See how server behaves if we change some of the Create params while
367 * Replaying. Change Share Access and Oplock Level. It seems the server
368 * does not care for change in these parameters. The server seems to
369 * only care for the File Name and GUID
371 smb2_oplock_create_share(&io
, fname
,
372 smb2_util_share_access("RWD"),
373 smb2_util_oplock_level(""));
374 io
.in
.durable_open
= false;
375 io
.in
.durable_open_v2
= true;
376 io
.in
.persistent_open
= false;
377 io
.in
.create_guid
= create_guid
;
378 io
.in
.timeout
= UINT32_MAX
;
381 * The output will just react on the
382 * input, but it doesn't change the oplock
383 * or share access values on the existing open
386 ref2
.out
.oplock_level
= smb2_util_oplock_level("");
387 ref2
.out
.durable_open_v2
= false;
388 ref2
.out
.timeout
= 0;
389 ref2
.out
.blobs
.num_blobs
= 0;
391 smb2cli_session_start_replay(tree
->session
->smbXcli
);
392 status
= smb2_create(tree
, mem_ctx
, &io
);
393 smb2cli_session_stop_replay(tree
->session
->smbXcli
);
394 CHECK_STATUS(status
, NT_STATUS_OK
);
395 CHECK_CREATE_OUT(&io
, &ref2
);
396 CHECK_VAL(break_info
.count
, 0);
399 * This is a normal open, which triggers an oplock
400 * break and still gets NT_STATUS_SHARING_VIOLATION
403 io
.in
.durable_open_v2
= false;
404 status
= smb2_create(tree
, mem_ctx
, &io
);
405 CHECK_STATUS(status
, NT_STATUS_SHARING_VIOLATION
);
408 CHECK_VAL(break_info
.count
, 1);
409 CHECK_HANDLE(&break_info
.handle
, &ref1
.out
.file
.handle
);
410 CHECK_VAL(break_info
.level
, smb2_util_oplock_level("s"));
411 ZERO_STRUCT(break_info
);
414 smb2_util_close(tree
, *h
);
416 status
= smb2_util_unlink(tree
, fname
);
417 CHECK_STATUS(status
, NT_STATUS_OK
);
418 CHECK_VAL(break_info
.count
, 0);
421 * No Replay detection for regular Creates
423 perms
= SEC_STD_SYNCHRONIZE
| SEC_STD_READ_CONTROL
| SEC_STD_DELETE
|
424 SEC_DIR_WRITE_ATTRIBUTE
| SEC_DIR_READ_ATTRIBUTE
|
425 SEC_DIR_WRITE_EA
| SEC_FILE_APPEND_DATA
|
428 io
= (struct smb2_create
) {
429 .in
.desired_access
= perms
,
430 .in
.file_attributes
= 0,
431 .in
.create_disposition
= NTCREATEX_DISP_CREATE
,
432 .in
.share_access
= NTCREATEX_SHARE_ACCESS_DELETE
,
433 .in
.create_options
= 0x0,
437 status
= smb2_create(tree
, tctx
, &io
);
438 CHECK_STATUS(status
, NT_STATUS_OK
);
439 CHECK_VAL(break_info
.count
, 0);
440 _h
= io
.out
.file
.handle
;
442 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
444 torture_comment(tctx
, "No Replay Detection for regular Create\n");
446 * Now replay the same create
448 smb2cli_session_start_replay(tree
->session
->smbXcli
);
449 status
= smb2_create(tree
, tctx
, &io
);
450 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_COLLISION
);
451 CHECK_VAL(break_info
.count
, 0);
454 smb2cli_session_stop_replay(tree
->session
->smbXcli
);
457 smb2_util_close(tree
, *h
);
459 smb2_deltree(tree
, BASEDIR
);
462 talloc_free(mem_ctx
);
468 * Test Durablity V2 Create Replay Detection on Multi Channel
470 static bool test_replay3(struct torture_context
*tctx
, struct smb2_tree
*tree1
)
472 const char *host
= torture_setting_string(tctx
, "host", NULL
);
473 const char *share
= torture_setting_string(tctx
, "share", NULL
);
474 struct cli_credentials
*credentials
= cmdline_credentials
;
476 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
477 struct smb2_handle _h
;
478 struct smb2_handle
*h
= NULL
;
479 struct smb2_create io
;
480 struct GUID create_guid
= GUID_random();
482 const char *fname
= BASEDIR
"\\replay3.dat";
483 struct smb2_tree
*tree2
= NULL
;
484 struct smb2_transport
*transport1
= tree1
->session
->transport
;
485 struct smb2_transport
*transport2
= NULL
;
486 struct smb2_session
*session1_1
= tree1
->session
;
487 struct smb2_session
*session1_2
= NULL
;
488 uint32_t share_capabilities
;
490 uint32_t server_capabilities
;
492 if (smbXcli_conn_protocol(transport1
->conn
) < PROTOCOL_SMB3_00
) {
493 torture_skip(tctx
, "SMB 3.X Dialect family required for "
497 server_capabilities
= smb2cli_conn_server_capabilities(
498 tree1
->session
->transport
->conn
);
499 if (!(server_capabilities
& SMB2_CAP_MULTI_CHANNEL
)) {
501 "Server does not support multi-channel.");
504 share_capabilities
= smb2cli_tcon_capabilities(tree1
->smbXcli
);
505 share_is_so
= share_capabilities
& SMB2_SHARE_CAP_SCALEOUT
;
507 ZERO_STRUCT(break_info
);
508 break_info
.tctx
= tctx
;
509 transport1
->oplock
.handler
= torture_oplock_ack_handler
;
510 transport1
->oplock
.private_data
= tree1
;
512 torture_comment(tctx
, "Replay of DurableHandleReqV2 on Multi "
514 status
= torture_smb2_testdir(tree1
, BASEDIR
, &_h
);
515 CHECK_STATUS(status
, NT_STATUS_OK
);
516 smb2_util_close(tree1
, _h
);
517 smb2_util_unlink(tree1
, fname
);
518 CHECK_VAL(break_info
.count
, 0);
521 * use the 1st channel, 1st session
523 smb2_oplock_create_share(&io
, fname
,
524 smb2_util_share_access(""),
525 smb2_util_oplock_level("b"));
526 io
.in
.durable_open
= false;
527 io
.in
.durable_open_v2
= true;
528 io
.in
.persistent_open
= false;
529 io
.in
.create_guid
= create_guid
;
530 io
.in
.timeout
= UINT32_MAX
;
532 tree1
->session
= session1_1
;
533 status
= smb2_create(tree1
, mem_ctx
, &io
);
534 CHECK_STATUS(status
, NT_STATUS_OK
);
535 _h
= io
.out
.file
.handle
;
537 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
539 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("s"));
540 CHECK_VAL(io
.out
.durable_open_v2
, false);
541 CHECK_VAL(io
.out
.timeout
, 0);
543 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
544 CHECK_VAL(io
.out
.durable_open_v2
, true);
545 CHECK_VAL(io
.out
.timeout
, io
.in
.timeout
);
547 CHECK_VAL(io
.out
.durable_open
, false);
548 CHECK_VAL(break_info
.count
, 0);
550 status
= smb2_connect(tctx
,
552 lpcfg_smb_ports(tctx
->lp_ctx
),
554 lpcfg_resolve_context(tctx
->lp_ctx
),
558 &transport1
->options
,
559 lpcfg_socket_options(tctx
->lp_ctx
),
560 lpcfg_gensec_settings(tctx
, tctx
->lp_ctx
)
562 torture_assert_ntstatus_ok_goto(tctx
, status
, ret
, done
,
563 "smb2_connect failed");
564 transport2
= tree2
->session
->transport
;
566 transport2
->oplock
.handler
= torture_oplock_ack_handler
;
567 transport2
->oplock
.private_data
= tree2
;
570 * Now bind the 1st session to 2nd transport channel
572 session1_2
= smb2_session_channel(transport2
,
573 lpcfg_gensec_settings(tctx
, tctx
->lp_ctx
),
575 torture_assert(tctx
, session1_2
!= NULL
, "smb2_session_channel failed");
577 status
= smb2_session_setup_spnego(session1_2
,
579 0 /* previous_session_id */);
580 CHECK_STATUS(status
, NT_STATUS_OK
);
583 * use the 2nd channel, 1st session
585 tree1
->session
= session1_2
;
586 smb2cli_session_start_replay(tree1
->session
->smbXcli
);
587 status
= smb2_create(tree1
, mem_ctx
, &io
);
588 smb2cli_session_stop_replay(tree1
->session
->smbXcli
);
589 CHECK_STATUS(status
, NT_STATUS_OK
);
590 _h
= io
.out
.file
.handle
;
592 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
594 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("s"));
595 CHECK_VAL(io
.out
.durable_open_v2
, false);
596 CHECK_VAL(io
.out
.timeout
, 0);
598 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
599 CHECK_VAL(io
.out
.durable_open_v2
, true);
600 CHECK_VAL(io
.out
.timeout
, io
.in
.timeout
);
602 CHECK_VAL(io
.out
.durable_open
, false);
603 CHECK_VAL(break_info
.count
, 0);
605 tree1
->session
= session1_1
;
606 smb2_util_close(tree1
, *h
);
611 tree1
->session
= session1_1
;
614 smb2_util_close(tree1
, *h
);
617 smb2_util_unlink(tree1
, fname
);
618 smb2_deltree(tree1
, BASEDIR
);
621 talloc_free(mem_ctx
);
627 * Test Multichannel IO Ordering using ChannelSequence/Channel Epoch number
629 static bool test_replay4(struct torture_context
*tctx
, struct smb2_tree
*tree1
)
631 const char *host
= torture_setting_string(tctx
, "host", NULL
);
632 const char *share
= torture_setting_string(tctx
, "share", NULL
);
633 struct cli_credentials
*credentials
= cmdline_credentials
;
635 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
636 struct smb2_handle _h1
;
637 struct smb2_handle
*h1
= NULL
;
638 struct smb2_create io
;
639 struct GUID create_guid
= GUID_random();
642 union smb_setfileinfo sfinfo
;
644 const char *fname
= BASEDIR
"\\replay4.dat";
645 struct smb2_tree
*tree2
= NULL
;
646 struct smb2_transport
*transport1
= tree1
->session
->transport
;
647 struct smb2_transport
*transport2
= NULL
;
648 struct smb2_session
*session1_1
= tree1
->session
;
649 struct smb2_session
*session1_2
= NULL
;
651 uint32_t share_capabilities
;
654 if (smbXcli_conn_protocol(transport1
->conn
) < PROTOCOL_SMB3_00
) {
655 torture_skip(tctx
, "SMB 3.X Dialect family required for "
659 share_capabilities
= smb2cli_tcon_capabilities(tree1
->smbXcli
);
660 share_is_so
= share_capabilities
& SMB2_SHARE_CAP_SCALEOUT
;
662 ZERO_STRUCT(break_info
);
663 break_info
.tctx
= tctx
;
664 transport1
->oplock
.handler
= torture_oplock_ack_handler
;
665 transport1
->oplock
.private_data
= tree1
;
667 torture_comment(tctx
, "IO Ordering for Multi Channel\n");
668 status
= torture_smb2_testdir(tree1
, BASEDIR
, &_h1
);
669 CHECK_STATUS(status
, NT_STATUS_OK
);
670 smb2_util_close(tree1
, _h1
);
671 smb2_util_unlink(tree1
, fname
);
672 CHECK_VAL(break_info
.count
, 0);
675 * use the 1st channel, 1st session
678 smb2_oplock_create_share(&io
, fname
,
679 smb2_util_share_access(""),
680 smb2_util_oplock_level("b"));
681 io
.in
.durable_open
= false;
682 io
.in
.durable_open_v2
= true;
683 io
.in
.persistent_open
= false;
684 io
.in
.create_guid
= create_guid
;
685 io
.in
.timeout
= UINT32_MAX
;
687 tree1
->session
= session1_1
;
688 status
= smb2_create(tree1
, mem_ctx
, &io
);
689 CHECK_STATUS(status
, NT_STATUS_OK
);
690 _h1
= io
.out
.file
.handle
;
692 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
694 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("s"));
695 CHECK_VAL(io
.out
.durable_open_v2
, false);
696 CHECK_VAL(io
.out
.timeout
, 0);
698 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
699 CHECK_VAL(io
.out
.durable_open_v2
, true);
700 CHECK_VAL(io
.out
.timeout
, io
.in
.timeout
);
702 CHECK_VAL(io
.out
.durable_open
, false);
703 CHECK_VAL(break_info
.count
, 0);
705 status
= smb2_util_write(tree1
, *h1
, buf
, 0, ARRAY_SIZE(buf
));
706 CHECK_STATUS(status
, NT_STATUS_OK
);
709 * Increment ChannelSequence so that server thinks that there's a
712 smb2cli_session_increment_channel_sequence(tree1
->session
->smbXcli
);
715 * Perform a Read with incremented ChannelSequence
717 rd
= (struct smb2_read
) {
718 .in
.file
.handle
= *h1
,
719 .in
.length
= sizeof(buf
),
722 status
= smb2_read(tree1
, tree1
, &rd
);
723 CHECK_STATUS(status
, NT_STATUS_OK
);
726 * Performing a Write with Stale ChannelSequence is not allowed by
729 curr_cs
= smb2cli_session_reset_channel_sequence(
730 tree1
->session
->smbXcli
, 0);
731 status
= smb2_util_write(tree1
, *h1
, buf
, 0, ARRAY_SIZE(buf
));
732 CHECK_STATUS(status
, NT_STATUS_FILE_NOT_AVAILABLE
);
735 * Performing a Write Replay with Stale ChannelSequence is not allowed
738 smb2cli_session_start_replay(tree1
->session
->smbXcli
);
739 smb2cli_session_reset_channel_sequence(tree1
->session
->smbXcli
, 0);
740 status
= smb2_util_write(tree1
, *h1
, buf
, 0, ARRAY_SIZE(buf
));
741 smb2cli_session_stop_replay(tree1
->session
->smbXcli
);
742 CHECK_STATUS(status
, NT_STATUS_FILE_NOT_AVAILABLE
);
745 * Performing a SetInfo with stale ChannelSequence is not allowed by
749 sfinfo
.generic
.level
= RAW_SFILEINFO_POSITION_INFORMATION
;
750 sfinfo
.generic
.in
.file
.handle
= *h1
;
751 sfinfo
.position_information
.in
.position
= 0x1000;
752 status
= smb2_setinfo_file(tree1
, &sfinfo
);
753 CHECK_STATUS(status
, NT_STATUS_FILE_NOT_AVAILABLE
);
756 * Performing a Read with stale ChannelSequence is allowed
758 rd
= (struct smb2_read
) {
759 .in
.file
.handle
= *h1
,
760 .in
.length
= ARRAY_SIZE(buf
),
763 status
= smb2_read(tree1
, tree1
, &rd
);
764 CHECK_STATUS(status
, NT_STATUS_OK
);
766 status
= smb2_connect(tctx
,
768 lpcfg_smb_ports(tctx
->lp_ctx
),
770 lpcfg_resolve_context(tctx
->lp_ctx
),
774 &transport1
->options
,
775 lpcfg_socket_options(tctx
->lp_ctx
),
776 lpcfg_gensec_settings(tctx
, tctx
->lp_ctx
)
778 torture_assert_ntstatus_ok_goto(tctx
, status
, ret
, done
,
779 "smb2_connect failed");
780 transport2
= tree2
->session
->transport
;
782 transport2
->oplock
.handler
= torture_oplock_ack_handler
;
783 transport2
->oplock
.private_data
= tree2
;
786 * Now bind the 1st session to 2nd transport channel
788 session1_2
= smb2_session_channel(transport2
,
789 lpcfg_gensec_settings(tctx
, tctx
->lp_ctx
),
791 torture_assert(tctx
, session1_2
!= NULL
, "smb2_session_channel failed");
793 status
= smb2_session_setup_spnego(session1_2
,
795 0 /* previous_session_id */);
796 CHECK_STATUS(status
, NT_STATUS_OK
);
799 * use the 2nd channel, 1st session
801 tree1
->session
= session1_2
;
804 * Write Replay with Correct ChannelSequence is allowed by the server
806 smb2cli_session_start_replay(tree1
->session
->smbXcli
);
807 smb2cli_session_reset_channel_sequence(tree1
->session
->smbXcli
,
809 status
= smb2_util_write(tree1
, *h1
, buf
, 0, ARRAY_SIZE(buf
));
810 CHECK_STATUS(status
, NT_STATUS_OK
);
811 smb2cli_session_stop_replay(tree1
->session
->smbXcli
);
814 * See what happens if we change the Buffer and perform a Write Replay.
815 * This is to show that Write Replay does not really care about the data
817 memset(buf
, 'r', ARRAY_SIZE(buf
));
818 smb2cli_session_start_replay(tree1
->session
->smbXcli
);
819 status
= smb2_util_write(tree1
, *h1
, buf
, 0, ARRAY_SIZE(buf
));
820 CHECK_STATUS(status
, NT_STATUS_OK
);
821 smb2cli_session_stop_replay(tree1
->session
->smbXcli
);
824 * Read back from File to verify what was written
826 rd
= (struct smb2_read
) {
827 .in
.file
.handle
= *h1
,
828 .in
.length
= ARRAY_SIZE(buf
),
831 status
= smb2_read(tree1
, tree1
, &rd
);
832 CHECK_STATUS(status
, NT_STATUS_OK
);
834 if ((rd
.out
.data
.length
!= ARRAY_SIZE(buf
)) ||
835 memcmp(rd
.out
.data
.data
, buf
, ARRAY_SIZE(buf
))) {
836 torture_comment(tctx
, "Write Replay Data Mismatch\n");
839 tree1
->session
= session1_1
;
840 smb2_util_close(tree1
, *h1
);
844 CHECK_VAL(break_info
.count
, 1);
846 CHECK_VAL(break_info
.count
, 0);
850 tree1
->session
= session1_1
;
853 smb2_util_close(tree1
, *h1
);
856 smb2_util_unlink(tree1
, fname
);
857 smb2_deltree(tree1
, BASEDIR
);
860 talloc_free(mem_ctx
);
866 * Test Durablity V2 Persistent Create Replay on a Single Channel
868 static bool test_replay5(struct torture_context
*tctx
, struct smb2_tree
*tree
)
871 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
872 struct smb2_handle _h
;
873 struct smb2_handle
*h
= NULL
;
874 struct smb2_create io
;
875 struct GUID create_guid
= GUID_random();
877 uint32_t share_capabilities
;
880 uint32_t server_capabilities
;
881 const char *fname
= BASEDIR
"\\replay5.dat";
882 struct smb2_transport
*transport
= tree
->session
->transport
;
883 struct smbcli_options options
= tree
->session
->transport
->options
;
884 uint8_t expect_oplock
= smb2_util_oplock_level("b");
885 NTSTATUS expect_status
= NT_STATUS_DUPLICATE_OBJECTID
;
887 if (smbXcli_conn_protocol(transport
->conn
) < PROTOCOL_SMB3_00
) {
888 torture_skip(tctx
, "SMB 3.X Dialect family required for "
892 server_capabilities
= smb2cli_conn_server_capabilities(
893 tree
->session
->transport
->conn
);
894 if (!(server_capabilities
& SMB2_CAP_PERSISTENT_HANDLES
)) {
896 "Server does not support persistent handles.");
899 share_capabilities
= smb2cli_tcon_capabilities(tree
->smbXcli
);
901 share_is_ca
= share_capabilities
& SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY
;
903 torture_skip(tctx
, "Persistent File Handles not supported");
906 share_is_so
= share_capabilities
& SMB2_SHARE_CAP_SCALEOUT
;
908 expect_oplock
= smb2_util_oplock_level("s");
909 expect_status
= NT_STATUS_FILE_NOT_AVAILABLE
;
912 ZERO_STRUCT(break_info
);
913 break_info
.tctx
= tctx
;
914 transport
->oplock
.handler
= torture_oplock_ack_handler
;
915 transport
->oplock
.private_data
= tree
;
917 torture_comment(tctx
, "Replay of Persistent DurableHandleReqV2 on Single "
919 status
= torture_smb2_testdir(tree
, BASEDIR
, &_h
);
920 CHECK_STATUS(status
, NT_STATUS_OK
);
921 smb2_util_close(tree
, _h
);
922 smb2_util_unlink(tree
, fname
);
923 CHECK_VAL(break_info
.count
, 0);
925 smb2_oplock_create_share(&io
, fname
,
926 smb2_util_share_access("RWD"),
927 smb2_util_oplock_level("b"));
928 io
.in
.durable_open
= false;
929 io
.in
.durable_open_v2
= true;
930 io
.in
.persistent_open
= true;
931 io
.in
.create_guid
= create_guid
;
932 io
.in
.timeout
= UINT32_MAX
;
934 status
= smb2_create(tree
, mem_ctx
, &io
);
935 CHECK_STATUS(status
, NT_STATUS_OK
);
936 _h
= io
.out
.file
.handle
;
938 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
939 CHECK_VAL(io
.out
.oplock_level
, expect_oplock
);
940 CHECK_VAL(io
.out
.durable_open
, false);
941 CHECK_VAL(io
.out
.durable_open_v2
, true);
942 CHECK_VAL(io
.out
.persistent_open
, true);
943 CHECK_VAL(io
.out
.timeout
, io
.in
.timeout
);
944 CHECK_VAL(break_info
.count
, 0);
946 /* disconnect, leaving the durable open */
949 if (!torture_smb2_connection_ext(tctx
, 0, &options
, &tree
)) {
950 torture_warning(tctx
, "couldn't reconnect, bailing\n");
955 /* a re-open of a persistent handle causes an error */
956 status
= smb2_create(tree
, mem_ctx
, &io
);
957 CHECK_STATUS(status
, expect_status
);
959 /* SMB2_FLAGS_REPLAY_OPERATION must be set to open the Persistent Handle */
960 smb2cli_session_start_replay(tree
->session
->smbXcli
);
961 smb2cli_session_increment_channel_sequence(tree
->session
->smbXcli
);
962 status
= smb2_create(tree
, mem_ctx
, &io
);
963 CHECK_STATUS(status
, NT_STATUS_OK
);
964 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
965 CHECK_VAL(io
.out
.durable_open
, false);
966 CHECK_VAL(io
.out
.persistent_open
, true);
967 CHECK_VAL(io
.out
.oplock_level
, expect_oplock
);
968 _h
= io
.out
.file
.handle
;
971 smb2_util_close(tree
, *h
);
975 smb2_util_close(tree
, *h
);
978 smb2_util_unlink(tree
, fname
);
979 smb2_deltree(tree
, BASEDIR
);
982 talloc_free(mem_ctx
);
987 struct torture_suite
*torture_smb2_replay_init(void)
989 struct torture_suite
*suite
=
990 torture_suite_create(talloc_autofree_context(), "replay");
992 torture_suite_add_1smb2_test(suite
, "replay1", test_replay1
);
993 torture_suite_add_1smb2_test(suite
, "replay2", test_replay2
);
994 torture_suite_add_1smb2_test(suite
, "replay3", test_replay3
);
995 torture_suite_add_1smb2_test(suite
, "replay4", test_replay4
);
996 torture_suite_add_1smb2_test(suite
, "replay5", test_replay5
);
998 suite
->description
= talloc_strdup(suite
, "SMB2 REPLAY tests");