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
;
306 if (smbXcli_conn_protocol(transport
->conn
) < PROTOCOL_SMB3_00
) {
307 torture_skip(tctx
, "SMB 3.X Dialect family required for "
311 ZERO_STRUCT(break_info
);
312 break_info
.tctx
= tctx
;
313 tree
->session
->transport
->oplock
.handler
= torture_oplock_ack_handler
;
314 tree
->session
->transport
->oplock
.private_data
= tree
;
316 torture_comment(tctx
, "Replay of DurableHandleReqV2 on Single "
318 smb2_util_unlink(tree
, fname
);
319 status
= torture_smb2_testdir(tree
, BASEDIR
, &_h
);
320 CHECK_STATUS(status
, NT_STATUS_OK
);
321 smb2_util_close(tree
, _h
);
322 CHECK_VAL(break_info
.count
, 0);
324 smb2_oplock_create_share(&io
, fname
,
325 smb2_util_share_access(""),
326 smb2_util_oplock_level("b"));
327 io
.in
.durable_open
= false;
328 io
.in
.durable_open_v2
= true;
329 io
.in
.persistent_open
= false;
330 io
.in
.create_guid
= create_guid
;
331 io
.in
.timeout
= UINT32_MAX
;
333 status
= smb2_create(tree
, mem_ctx
, &io
);
334 CHECK_STATUS(status
, NT_STATUS_OK
);
336 _h
= io
.out
.file
.handle
;
338 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
339 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
340 CHECK_VAL(io
.out
.durable_open
, false);
341 CHECK_VAL(io
.out
.durable_open_v2
, true);
342 CHECK_VAL(io
.out
.timeout
, io
.in
.timeout
);
345 * Replay Durable V2 Create on single channel
347 smb2cli_session_start_replay(tree
->session
->smbXcli
);
348 status
= smb2_create(tree
, mem_ctx
, &io
);
349 smb2cli_session_stop_replay(tree
->session
->smbXcli
);
350 CHECK_STATUS(status
, NT_STATUS_OK
);
351 CHECK_CREATE_OUT(&io
, &ref1
);
352 CHECK_VAL(break_info
.count
, 0);
355 * See how server behaves if we change some of the Create params while
356 * Replaying. Change Share Access and Oplock Level. It seems the server
357 * does not care for change in these parameters. The server seems to
358 * only care for the File Name and GUID
360 smb2_oplock_create_share(&io
, fname
,
361 smb2_util_share_access("RWD"),
362 smb2_util_oplock_level(""));
363 io
.in
.durable_open
= false;
364 io
.in
.durable_open_v2
= true;
365 io
.in
.persistent_open
= false;
366 io
.in
.create_guid
= create_guid
;
367 io
.in
.timeout
= UINT32_MAX
;
370 * The output will just react on the
371 * input, but it doesn't change the oplock
372 * or share access values on the existing open
375 ref2
.out
.oplock_level
= smb2_util_oplock_level("");
376 ref2
.out
.durable_open_v2
= false;
377 ref2
.out
.timeout
= 0;
378 ref2
.out
.blobs
.num_blobs
= 0;
380 smb2cli_session_start_replay(tree
->session
->smbXcli
);
381 status
= smb2_create(tree
, mem_ctx
, &io
);
382 smb2cli_session_stop_replay(tree
->session
->smbXcli
);
383 CHECK_STATUS(status
, NT_STATUS_OK
);
384 CHECK_CREATE_OUT(&io
, &ref2
);
385 CHECK_VAL(break_info
.count
, 0);
388 * This is a normal open, which triggers an oplock
389 * break and still gets NT_STATUS_SHARING_VIOLATION
392 io
.in
.durable_open_v2
= false;
393 status
= smb2_create(tree
, mem_ctx
, &io
);
394 CHECK_STATUS(status
, NT_STATUS_SHARING_VIOLATION
);
395 CHECK_VAL(break_info
.count
, 1);
396 CHECK_HANDLE(&break_info
.handle
, &ref1
.out
.file
.handle
);
397 CHECK_VAL(break_info
.level
, smb2_util_oplock_level("s"));
398 ZERO_STRUCT(break_info
);
400 smb2_util_close(tree
, *h
);
402 status
= smb2_util_unlink(tree
, fname
);
403 CHECK_STATUS(status
, NT_STATUS_OK
);
404 CHECK_VAL(break_info
.count
, 0);
407 * No Replay detection for regular Creates
409 perms
= SEC_STD_SYNCHRONIZE
| SEC_STD_READ_CONTROL
| SEC_STD_DELETE
|
410 SEC_DIR_WRITE_ATTRIBUTE
| SEC_DIR_READ_ATTRIBUTE
|
411 SEC_DIR_WRITE_EA
| SEC_FILE_APPEND_DATA
|
414 io
= (struct smb2_create
) {
415 .in
.desired_access
= perms
,
416 .in
.file_attributes
= 0,
417 .in
.create_disposition
= NTCREATEX_DISP_CREATE
,
418 .in
.share_access
= NTCREATEX_SHARE_ACCESS_DELETE
,
419 .in
.create_options
= 0x0,
423 status
= smb2_create(tree
, tctx
, &io
);
424 CHECK_STATUS(status
, NT_STATUS_OK
);
425 CHECK_VAL(break_info
.count
, 0);
426 _h
= io
.out
.file
.handle
;
428 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
430 torture_comment(tctx
, "No Replay Detection for regular Create\n");
432 * Now replay the same create
434 smb2cli_session_start_replay(tree
->session
->smbXcli
);
435 status
= smb2_create(tree
, tctx
, &io
);
436 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_COLLISION
);
437 CHECK_VAL(break_info
.count
, 0);
440 smb2cli_session_stop_replay(tree
->session
->smbXcli
);
443 smb2_util_close(tree
, *h
);
445 smb2_deltree(tree
, BASEDIR
);
448 talloc_free(mem_ctx
);
454 * Test Durablity V2 Create Replay Detection on Multi Channel
456 static bool test_replay3(struct torture_context
*tctx
, struct smb2_tree
*tree1
)
458 const char *host
= torture_setting_string(tctx
, "host", NULL
);
459 const char *share
= torture_setting_string(tctx
, "share", NULL
);
460 struct cli_credentials
*credentials
= cmdline_credentials
;
462 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
463 struct smb2_handle _h
;
464 struct smb2_handle
*h
= NULL
;
465 struct smb2_create io
;
466 struct GUID create_guid
= GUID_random();
468 const char *fname
= BASEDIR
"\\replay3.dat";
469 struct smb2_tree
*tree2
= NULL
;
470 struct smb2_transport
*transport1
= tree1
->session
->transport
;
471 struct smb2_transport
*transport2
= NULL
;
472 struct smb2_session
*session1_1
= tree1
->session
;
473 struct smb2_session
*session1_2
= NULL
;
475 if (smbXcli_conn_protocol(transport1
->conn
) < PROTOCOL_SMB3_00
) {
476 torture_skip(tctx
, "SMB 3.X Dialect family required for "
480 ZERO_STRUCT(break_info
);
481 break_info
.tctx
= tctx
;
482 transport1
->oplock
.handler
= torture_oplock_ack_handler
;
483 transport1
->oplock
.private_data
= tree1
;
485 torture_comment(tctx
, "Replay of DurableHandleReqV2 on Multi "
487 status
= torture_smb2_testdir(tree1
, BASEDIR
, &_h
);
488 CHECK_STATUS(status
, NT_STATUS_OK
);
489 smb2_util_close(tree1
, _h
);
490 smb2_util_unlink(tree1
, fname
);
491 CHECK_VAL(break_info
.count
, 0);
494 * use the 1st channel, 1st session
496 smb2_oplock_create_share(&io
, fname
,
497 smb2_util_share_access(""),
498 smb2_util_oplock_level("b"));
499 io
.in
.durable_open
= false;
500 io
.in
.durable_open_v2
= true;
501 io
.in
.persistent_open
= false;
502 io
.in
.create_guid
= create_guid
;
503 io
.in
.timeout
= UINT32_MAX
;
505 tree1
->session
= session1_1
;
506 status
= smb2_create(tree1
, mem_ctx
, &io
);
507 CHECK_STATUS(status
, NT_STATUS_OK
);
508 _h
= io
.out
.file
.handle
;
510 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
511 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
512 CHECK_VAL(io
.out
.durable_open
, false);
513 CHECK_VAL(io
.out
.durable_open_v2
, true);
514 CHECK_VAL(io
.out
.timeout
, io
.in
.timeout
);
515 CHECK_VAL(break_info
.count
, 0);
517 status
= smb2_connect(tctx
,
519 lpcfg_smb_ports(tctx
->lp_ctx
),
521 lpcfg_resolve_context(tctx
->lp_ctx
),
525 &transport1
->options
,
526 lpcfg_socket_options(tctx
->lp_ctx
),
527 lpcfg_gensec_settings(tctx
, tctx
->lp_ctx
)
529 torture_assert_ntstatus_ok_goto(tctx
, status
, ret
, done
,
530 "smb2_connect failed");
531 transport2
= tree2
->session
->transport
;
533 transport2
->oplock
.handler
= torture_oplock_ack_handler
;
534 transport2
->oplock
.private_data
= tree2
;
537 * Now bind the 1st session to 2nd transport channel
539 session1_2
= smb2_session_channel(transport2
,
540 lpcfg_gensec_settings(tctx
, tctx
->lp_ctx
),
542 torture_assert(tctx
, session1_2
!= NULL
, "smb2_session_channel failed");
544 status
= smb2_session_setup_spnego(session1_2
,
546 0 /* previous_session_id */);
547 CHECK_STATUS(status
, NT_STATUS_OK
);
550 * use the 2nd channel, 1st session
552 tree1
->session
= session1_2
;
553 smb2cli_session_start_replay(tree1
->session
->smbXcli
);
554 status
= smb2_create(tree1
, mem_ctx
, &io
);
555 smb2cli_session_stop_replay(tree1
->session
->smbXcli
);
556 CHECK_STATUS(status
, NT_STATUS_OK
);
557 _h
= io
.out
.file
.handle
;
559 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
560 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
561 CHECK_VAL(io
.out
.durable_open
, false);
562 CHECK_VAL(io
.out
.durable_open_v2
, true);
563 CHECK_VAL(io
.out
.timeout
, io
.in
.timeout
);
564 CHECK_VAL(break_info
.count
, 0);
566 tree1
->session
= session1_1
;
567 smb2_util_close(tree1
, *h
);
572 tree1
->session
= session1_1
;
575 smb2_util_close(tree1
, *h
);
578 smb2_util_unlink(tree1
, fname
);
579 smb2_deltree(tree1
, BASEDIR
);
582 talloc_free(mem_ctx
);
588 * Test Multichannel IO Ordering using ChannelSequence/Channel Epoch number
590 static bool test_replay4(struct torture_context
*tctx
, struct smb2_tree
*tree1
)
592 const char *host
= torture_setting_string(tctx
, "host", NULL
);
593 const char *share
= torture_setting_string(tctx
, "share", NULL
);
594 struct cli_credentials
*credentials
= cmdline_credentials
;
596 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
597 struct smb2_handle _h1
;
598 struct smb2_handle
*h1
= NULL
;
599 struct smb2_create io
;
600 struct GUID create_guid
= GUID_random();
603 union smb_setfileinfo sfinfo
;
605 const char *fname
= BASEDIR
"\\replay4.dat";
606 struct smb2_tree
*tree2
= NULL
;
607 struct smb2_transport
*transport1
= tree1
->session
->transport
;
608 struct smb2_transport
*transport2
= NULL
;
609 struct smb2_session
*session1_1
= tree1
->session
;
610 struct smb2_session
*session1_2
= NULL
;
613 if (smbXcli_conn_protocol(transport1
->conn
) < PROTOCOL_SMB3_00
) {
614 torture_skip(tctx
, "SMB 3.X Dialect family required for "
618 ZERO_STRUCT(break_info
);
619 break_info
.tctx
= tctx
;
620 transport1
->oplock
.handler
= torture_oplock_ack_handler
;
621 transport1
->oplock
.private_data
= tree1
;
623 torture_comment(tctx
, "IO Ordering for Multi Channel\n");
624 status
= torture_smb2_testdir(tree1
, BASEDIR
, &_h1
);
625 CHECK_STATUS(status
, NT_STATUS_OK
);
626 smb2_util_close(tree1
, _h1
);
627 smb2_util_unlink(tree1
, fname
);
628 CHECK_VAL(break_info
.count
, 0);
631 * use the 1st channel, 1st session
634 smb2_oplock_create_share(&io
, fname
,
635 smb2_util_share_access(""),
636 smb2_util_oplock_level("b"));
637 io
.in
.durable_open
= false;
638 io
.in
.durable_open_v2
= true;
639 io
.in
.persistent_open
= false;
640 io
.in
.create_guid
= create_guid
;
641 io
.in
.timeout
= UINT32_MAX
;
643 tree1
->session
= session1_1
;
644 status
= smb2_create(tree1
, mem_ctx
, &io
);
645 CHECK_STATUS(status
, NT_STATUS_OK
);
646 _h1
= io
.out
.file
.handle
;
648 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
649 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
650 CHECK_VAL(io
.out
.durable_open
, false);
651 CHECK_VAL(io
.out
.durable_open_v2
, true);
652 CHECK_VAL(io
.out
.timeout
, io
.in
.timeout
);
653 CHECK_VAL(break_info
.count
, 0);
655 status
= smb2_util_write(tree1
, *h1
, buf
, 0, ARRAY_SIZE(buf
));
656 CHECK_STATUS(status
, NT_STATUS_OK
);
659 * Increment ChannelSequence so that server thinks that there's a
662 smb2cli_session_increment_channel_sequence(tree1
->session
->smbXcli
);
665 * Perform a Read with incremented ChannelSequence
667 rd
= (struct smb2_read
) {
668 .in
.file
.handle
= *h1
,
669 .in
.length
= sizeof(buf
),
672 status
= smb2_read(tree1
, tree1
, &rd
);
673 CHECK_STATUS(status
, NT_STATUS_OK
);
676 * Performing a Write with Stale ChannelSequence is not allowed by
679 curr_cs
= smb2cli_session_reset_channel_sequence(
680 tree1
->session
->smbXcli
, 0);
681 status
= smb2_util_write(tree1
, *h1
, buf
, 0, ARRAY_SIZE(buf
));
682 CHECK_STATUS(status
, NT_STATUS_FILE_NOT_AVAILABLE
);
685 * Performing a Write Replay with Stale ChannelSequence is not allowed
688 smb2cli_session_start_replay(tree1
->session
->smbXcli
);
689 smb2cli_session_reset_channel_sequence(tree1
->session
->smbXcli
, 0);
690 status
= smb2_util_write(tree1
, *h1
, buf
, 0, ARRAY_SIZE(buf
));
691 smb2cli_session_stop_replay(tree1
->session
->smbXcli
);
692 CHECK_STATUS(status
, NT_STATUS_FILE_NOT_AVAILABLE
);
695 * Performing a SetInfo with stale ChannelSequence is not allowed by
699 sfinfo
.generic
.level
= RAW_SFILEINFO_POSITION_INFORMATION
;
700 sfinfo
.generic
.in
.file
.handle
= *h1
;
701 sfinfo
.position_information
.in
.position
= 0x1000;
702 status
= smb2_setinfo_file(tree1
, &sfinfo
);
703 CHECK_STATUS(status
, NT_STATUS_FILE_NOT_AVAILABLE
);
706 * Performing a Read with stale ChannelSequence is allowed
708 rd
= (struct smb2_read
) {
709 .in
.file
.handle
= *h1
,
710 .in
.length
= ARRAY_SIZE(buf
),
713 status
= smb2_read(tree1
, tree1
, &rd
);
714 CHECK_STATUS(status
, NT_STATUS_OK
);
716 status
= smb2_connect(tctx
,
718 lpcfg_smb_ports(tctx
->lp_ctx
),
720 lpcfg_resolve_context(tctx
->lp_ctx
),
724 &transport1
->options
,
725 lpcfg_socket_options(tctx
->lp_ctx
),
726 lpcfg_gensec_settings(tctx
, tctx
->lp_ctx
)
728 torture_assert_ntstatus_ok_goto(tctx
, status
, ret
, done
,
729 "smb2_connect failed");
730 transport2
= tree2
->session
->transport
;
732 transport2
->oplock
.handler
= torture_oplock_ack_handler
;
733 transport2
->oplock
.private_data
= tree2
;
736 * Now bind the 1st session to 2nd transport channel
738 session1_2
= smb2_session_channel(transport2
,
739 lpcfg_gensec_settings(tctx
, tctx
->lp_ctx
),
741 torture_assert(tctx
, session1_2
!= NULL
, "smb2_session_channel failed");
743 status
= smb2_session_setup_spnego(session1_2
,
745 0 /* previous_session_id */);
746 CHECK_STATUS(status
, NT_STATUS_OK
);
749 * use the 2nd channel, 1st session
751 tree1
->session
= session1_2
;
754 * Write Replay with Correct ChannelSequence is allowed by the server
756 smb2cli_session_start_replay(tree1
->session
->smbXcli
);
757 smb2cli_session_reset_channel_sequence(tree1
->session
->smbXcli
,
759 status
= smb2_util_write(tree1
, *h1
, buf
, 0, ARRAY_SIZE(buf
));
760 CHECK_STATUS(status
, NT_STATUS_OK
);
761 smb2cli_session_stop_replay(tree1
->session
->smbXcli
);
764 * See what happens if we change the Buffer and perform a Write Replay.
765 * This is to show that Write Replay does not really care about the data
767 memset(buf
, 'r', ARRAY_SIZE(buf
));
768 smb2cli_session_start_replay(tree1
->session
->smbXcli
);
769 status
= smb2_util_write(tree1
, *h1
, buf
, 0, ARRAY_SIZE(buf
));
770 CHECK_STATUS(status
, NT_STATUS_OK
);
771 smb2cli_session_stop_replay(tree1
->session
->smbXcli
);
774 * Read back from File to verify what was written
776 rd
= (struct smb2_read
) {
777 .in
.file
.handle
= *h1
,
778 .in
.length
= ARRAY_SIZE(buf
),
781 status
= smb2_read(tree1
, tree1
, &rd
);
782 CHECK_STATUS(status
, NT_STATUS_OK
);
784 if ((rd
.out
.data
.length
!= ARRAY_SIZE(buf
)) ||
785 memcmp(rd
.out
.data
.data
, buf
, ARRAY_SIZE(buf
))) {
786 torture_comment(tctx
, "Write Replay Data Mismatch\n");
789 tree1
->session
= session1_1
;
790 smb2_util_close(tree1
, *h1
);
793 CHECK_VAL(break_info
.count
, 0);
796 tree1
->session
= session1_1
;
799 smb2_util_close(tree1
, *h1
);
802 smb2_util_unlink(tree1
, fname
);
803 smb2_deltree(tree1
, BASEDIR
);
806 talloc_free(mem_ctx
);
811 struct torture_suite
*torture_smb2_replay_init(void)
813 struct torture_suite
*suite
=
814 torture_suite_create(talloc_autofree_context(), "replay");
816 torture_suite_add_1smb2_test(suite
, "replay1", test_replay1
);
817 torture_suite_add_1smb2_test(suite
, "replay2", test_replay2
);
818 torture_suite_add_1smb2_test(suite
, "replay3", test_replay3
);
819 torture_suite_add_1smb2_test(suite
, "replay4", test_replay4
);
821 suite
->description
= talloc_strdup(suite
, "SMB2 REPLAY tests");