torture:smb2: skip replay5 test if server does not support persistent handles
[Samba.git] / source4 / torture / smb2 / replay.c
blob2b60cbf8551b8797886565a8f016440f85f35b23
1 /*
2 Unix SMB/CIFS implementation.
4 test suite for SMB2 replay
6 Copyright (C) Anubhav Rakshit 2014
7 Copyright (C) Stefan Metzmacher 2014
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 3 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include "includes.h"
24 #include "libcli/smb2/smb2.h"
25 #include "libcli/smb2/smb2_calls.h"
26 #include "torture/torture.h"
27 #include "torture/smb2/proto.h"
28 #include "../libcli/smb/smbXcli_base.h"
29 #include "lib/cmdline/popt_common.h"
30 #include "auth/credentials/credentials.h"
31 #include "libcli/security/security.h"
32 #include "libcli/resolve/resolve.h"
33 #include "lib/param/param.h"
34 #include "lib/events/events.h"
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); \
40 ret = false; \
41 goto done; \
42 }} while (0)
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)); \
48 ret = false; \
49 goto done; \
50 }} while (0)
52 #define CHECK_CREATED(__io, __created, __attribute) \
53 do { \
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); \
59 } while(0)
61 #define CHECK_HANDLE(__h1, __h2) \
62 do { \
63 CHECK_VAL((__h1)->data[0], (__h2)->data[0]); \
64 CHECK_VAL((__h1)->data[1], (__h2)->data[1]); \
65 } while(0)
67 #define __IO_OUT_VAL(__io1, __io2, __m) \
68 CHECK_VAL((__io1)->out.__m, (__io2)->out.__m)
70 #define CHECK_CREATE_OUT(__io1, __io2) \
71 do { \
72 CHECK_HANDLE(&(__io1)->out.file.handle, \
73 &(__io2)->out.file.handle); \
74 __IO_OUT_VAL(__io1, __io2, oplock_level); \
75 __IO_OUT_VAL(__io1, __io2, create_action); \
76 __IO_OUT_VAL(__io1, __io2, create_time); \
77 __IO_OUT_VAL(__io1, __io2, access_time); \
78 __IO_OUT_VAL(__io1, __io2, write_time); \
79 __IO_OUT_VAL(__io1, __io2, change_time); \
80 __IO_OUT_VAL(__io1, __io2, alloc_size); \
81 __IO_OUT_VAL(__io1, __io2, size); \
82 __IO_OUT_VAL(__io1, __io2, file_attr); \
83 __IO_OUT_VAL(__io1, __io2, durable_open); \
84 __IO_OUT_VAL(__io1, __io2, durable_open_v2); \
85 __IO_OUT_VAL(__io1, __io2, persistent_open); \
86 __IO_OUT_VAL(__io1, __io2, timeout); \
87 __IO_OUT_VAL(__io1, __io2, blobs.num_blobs); \
88 } while(0)
90 #define BASEDIR "replaytestdir"
92 static struct {
93 struct torture_context *tctx;
94 struct smb2_handle handle;
95 uint8_t level;
96 struct smb2_break br;
97 int count;
98 int failures;
99 NTSTATUS failure_status;
100 } break_info;
102 static void torture_oplock_ack_callback(struct smb2_request *req)
104 NTSTATUS status;
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;
112 return;
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,
121 uint8_t level,
122 void *private_data)
124 struct smb2_tree *tree = private_data;
125 const char *name;
126 struct smb2_request *req;
128 ZERO_STRUCT(break_info.br);
130 break_info.handle = *handle;
131 break_info.level = level;
132 break_info.count++;
134 switch (level) {
135 case SMB2_OPLOCK_LEVEL_II:
136 name = "level II";
137 break;
138 case SMB2_OPLOCK_LEVEL_NONE:
139 name = "none";
140 break;
141 default:
142 name = "unknown";
143 break_info.failures++;
145 torture_comment(break_info.tctx,
146 "Acking to %s [0x%02X] in oplock handler\n",
147 name, level);
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;
157 return true;
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)
166 bool ret = true;
167 NTSTATUS status;
168 struct smb2_handle h;
169 uint8_t buf[200];
170 struct smb2_read rd;
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];
176 struct smb2_flush f;
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 "
183 "Replay tests\n");
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) {
213 .in.file.handle = h
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) {
220 .in.file.handle = h,
221 .in.length = 10,
222 .in.offset = 0,
223 .in.min_count = 1
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) {
258 .in.locks = el,
259 .in.lock_count = 0x0001,
260 .in.lock_sequence = 0x00000000,
261 .in.file.handle = h
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);
279 done:
280 smb2cli_session_stop_replay(tree->session->smbXcli);
281 smb2_util_close(tree, h);
282 smb2_deltree(tree, BASEDIR);
284 talloc_free(tmp_ctx);
286 return ret;
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)
295 NTSTATUS status;
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();
301 uint32_t perms = 0;
302 bool ret = true;
303 const char *fname = BASEDIR "\\replay2.dat";
304 struct smb2_transport *transport = tree->session->transport;
305 uint32_t share_capabilities;
306 bool share_is_so;
308 if (smbXcli_conn_protocol(transport->conn) < PROTOCOL_SMB3_00) {
309 torture_skip(tctx, "SMB 3.X Dialect family required for "
310 "replay tests\n");
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 "
322 "Channel\n");
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);
340 ref1 = io;
341 _h = io.out.file.handle;
342 h = &_h;
343 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
344 CHECK_VAL(io.out.durable_open, false);
345 if (share_is_so) {
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);
349 } else {
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
385 ref2 = ref1;
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
402 io = ref1;
403 io.in.durable_open_v2 = false;
404 status = smb2_create(tree, mem_ctx, &io);
405 CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
407 if (!share_is_so) {
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);
415 h = NULL;
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 |
426 SEC_FILE_WRITE_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,
434 .in.fname = fname
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;
441 h = &_h;
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);
453 done:
454 smb2cli_session_stop_replay(tree->session->smbXcli);
456 if (h != NULL) {
457 smb2_util_close(tree, *h);
459 smb2_deltree(tree, BASEDIR);
461 talloc_free(tree);
462 talloc_free(mem_ctx);
464 return ret;
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;
475 NTSTATUS status;
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();
481 bool ret = true;
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;
489 bool share_is_so;
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 "
494 "Replay tests\n");
497 server_capabilities = smb2cli_conn_server_capabilities(
498 tree1->session->transport->conn);
499 if (!(server_capabilities & SMB2_CAP_MULTI_CHANNEL)) {
500 torture_skip(tctx,
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 "
513 "Channel\n");
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;
536 h = &_h;
537 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
538 if (share_is_so) {
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);
542 } else {
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,
551 host,
552 lpcfg_smb_ports(tctx->lp_ctx),
553 share,
554 lpcfg_resolve_context(tctx->lp_ctx),
555 credentials,
556 &tree2,
557 tctx->ev,
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),
574 tree2, session1_1);
575 torture_assert(tctx, session1_2 != NULL, "smb2_session_channel failed");
577 status = smb2_session_setup_spnego(session1_2,
578 cmdline_credentials,
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;
591 h = &_h;
592 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
593 if (share_is_so) {
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);
597 } else {
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);
607 h = NULL;
609 done:
610 talloc_free(tree2);
611 tree1->session = session1_1;
613 if (h != NULL) {
614 smb2_util_close(tree1, *h);
617 smb2_util_unlink(tree1, fname);
618 smb2_deltree(tree1, BASEDIR);
620 talloc_free(tree1);
621 talloc_free(mem_ctx);
623 return ret;
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;
634 NTSTATUS status;
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();
640 uint8_t buf[64];
641 struct smb2_read rd;
642 union smb_setfileinfo sfinfo;
643 bool ret = true;
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;
650 uint16_t curr_cs;
651 uint32_t share_capabilities;
652 bool share_is_so;
654 if (smbXcli_conn_protocol(transport1->conn) < PROTOCOL_SMB3_00) {
655 torture_skip(tctx, "SMB 3.X Dialect family required for "
656 "Replay tests\n");
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;
691 h1 = &_h1;
692 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
693 if (share_is_so) {
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);
697 } else {
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
710 * Channel Failure
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),
720 .in.offset = 0
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
727 * server
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
736 * by server
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
746 * server
748 ZERO_STRUCT(sfinfo);
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),
761 .in.offset = 0
763 status = smb2_read(tree1, tree1, &rd);
764 CHECK_STATUS(status, NT_STATUS_OK);
766 status = smb2_connect(tctx,
767 host,
768 lpcfg_smb_ports(tctx->lp_ctx),
769 share,
770 lpcfg_resolve_context(tctx->lp_ctx),
771 credentials,
772 &tree2,
773 tctx->ev,
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),
790 tree2, session1_1);
791 torture_assert(tctx, session1_2 != NULL, "smb2_session_channel failed");
793 status = smb2_session_setup_spnego(session1_2,
794 cmdline_credentials,
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,
808 curr_cs);
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),
829 .in.offset = 0
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);
841 h1 = NULL;
843 if (share_is_so) {
844 CHECK_VAL(break_info.count, 1);
845 } else {
846 CHECK_VAL(break_info.count, 0);
848 done:
849 talloc_free(tree2);
850 tree1->session = session1_1;
852 if (h1 != NULL) {
853 smb2_util_close(tree1, *h1);
856 smb2_util_unlink(tree1, fname);
857 smb2_deltree(tree1, BASEDIR);
859 talloc_free(tree1);
860 talloc_free(mem_ctx);
862 return ret;
866 * Test Durablity V2 Persistent Create Replay on a Single Channel
868 static bool test_replay5(struct torture_context *tctx, struct smb2_tree *tree)
870 NTSTATUS status;
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();
876 bool ret = true;
877 uint32_t share_capabilities;
878 bool share_is_ca;
879 bool share_is_so;
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 "
889 "Replay tests\n");
892 server_capabilities = smb2cli_conn_server_capabilities(
893 tree->session->transport->conn);
894 if (!(server_capabilities & SMB2_CAP_PERSISTENT_HANDLES)) {
895 torture_skip(tctx,
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;
902 if (!share_is_ca) {
903 torture_skip(tctx, "Persistent File Handles not supported");
906 share_is_so = share_capabilities & SMB2_SHARE_CAP_SCALEOUT;
907 if (share_is_so) {
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 "
918 "Channel\n");
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;
937 h = &_h;
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 */
947 TALLOC_FREE(tree);
949 if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
950 torture_warning(tctx, "couldn't reconnect, bailing\n");
951 ret = false;
952 goto done;
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;
969 h = &_h;
971 smb2_util_close(tree, *h);
972 h = NULL;
973 done:
974 if (h != NULL) {
975 smb2_util_close(tree, *h);
978 smb2_util_unlink(tree, fname);
979 smb2_deltree(tree, BASEDIR);
981 talloc_free(tree);
982 talloc_free(mem_ctx);
984 return ret;
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");
1000 return suite;