s4:torture: Add Replay tests to examine server behaviour when Multiple Channels are...
[obnox/samba/samba-obnox.git] / source4 / torture / smb2 / replay.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    test suite for SMB2 replay
5
6    Copyright (C) Anubhav Rakshit 2014
7    Copyright (C) Stefan Metzmacher 2014
8
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.
13
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.
18
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/>.
21 */
22
23 #include "includes.h"
24 #include "libcli/smb2/smb2.h"
25 #include "libcli/smb2/smb2_calls.h"
26 #include "torture/torture.h"
27 #include "torture/smb2/proto.h"
28 #include "../libcli/smb/smbXcli_base.h"
29 #include "lib/cmdline/popt_common.h"
30 #include "auth/credentials/credentials.h"
31 #include "libcli/security/security.h"
32 #include "libcli/resolve/resolve.h"
33 #include "lib/param/param.h"
34 #include "lib/events/events.h"
35
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)
43
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)
51
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)
60
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)
66
67 #define __IO_OUT_VAL(__io1, __io2, __m) \
68         CHECK_VAL((__io1)->out.__m, (__io2)->out.__m)
69
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)
89
90 #define BASEDIR "replaytestdir"
91
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;
101
102 static void torture_oplock_ack_callback(struct smb2_request *req)
103 {
104         NTSTATUS status;
105
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;
110         }
111
112         return;
113 }
114
115 /**
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.
118  */
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)
123 {
124         struct smb2_tree *tree = private_data;
125         const char *name;
126         struct smb2_request *req;
127
128         ZERO_STRUCT(break_info.br);
129
130         break_info.handle       = *handle;
131         break_info.level        = level;
132         break_info.count++;
133
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++;
144         }
145         torture_comment(break_info.tctx,
146                         "Acking to %s [0x%02X] in oplock handler\n",
147                         name, level);
148
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;
153
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;
158 }
159
160 /**
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.
163  */
164 static bool test_replay1(struct torture_context *tctx, struct smb2_tree *tree)
165 {
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;
180
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");
184         }
185
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;
190
191         status = torture_smb2_testdir(tree, BASEDIR, &h);
192         CHECK_STATUS(status, NT_STATUS_OK);
193         smb2_util_close(tree, h);
194
195         smb2cli_session_start_replay(tree->session->smbXcli);
196
197         torture_comment(tctx, "Try Commands with Replay Flags Enabled\n");
198
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);
203         /*
204          * Wireshark shows that the response has SMB2_FLAGS_REPLAY_OPERATION
205          * flags set. The server should ignore this flag.
206          */
207
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);
211
212         f = (struct smb2_flush) {
213                 .in.file.handle = h
214         };
215         torture_comment(tctx, "Trying flush\n");
216         status = smb2_flush(tree, &f);
217         CHECK_STATUS(status, NT_STATUS_OK);
218
219         rd = (struct smb2_read) {
220                 .in.file.handle = h,
221                 .in.length = 10,
222                 .in.offset = 0,
223                 .in.min_count = 1
224         };
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);
229
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);
236
237         qfinfo = (union smb_fileinfo) {
238                 .generic.level = RAW_SFILEINFO_POSITION_INFORMATION,
239                 .generic.in.file.handle = h
240         };
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);
245
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
252         };
253         torture_comment(tctx, "Trying ioctl\n");
254         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
255         CHECK_STATUS(status, NT_STATUS_OK);
256
257         lck = (struct smb2_lock) {
258                 .in.locks = el,
259                 .in.lock_count = 0x0001,
260                 .in.lock_sequence = 0x00000000,
261                 .in.file.handle = h
262         };
263         el[0].reserved          = 0x00000000;
264         el[0].flags             = SMB2_LOCK_FLAG_EXCLUSIVE |
265                 SMB2_LOCK_FLAG_FAIL_IMMEDIATELY;
266
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);
272
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);
277
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);
283
284         talloc_free(tmp_ctx);
285
286         return ret;
287 }
288
289 /**
290  * Test Durablity V2 Create Replay Detection on Single Channel. Also verify that
291  * regular creates can not be replayed.
292  */
293 static bool test_replay2(struct torture_context *tctx, struct smb2_tree *tree)
294 {
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
306         if (smbXcli_conn_protocol(transport->conn) < PROTOCOL_SMB3_00) {
307                 torture_skip(tctx, "SMB 3.X Dialect family required for "
308                                    "replay tests\n");
309         }
310
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;
315
316         torture_comment(tctx, "Replay of DurableHandleReqV2 on Single "
317                               "Channel\n");
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);
323
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;
332
333         status = smb2_create(tree, mem_ctx, &io);
334         CHECK_STATUS(status, NT_STATUS_OK);
335         ref1 = io;
336         _h = io.out.file.handle;
337         h = &_h;
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);
343
344         /*
345          * Replay Durable V2 Create on single channel
346          */
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);
353
354         /*
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
359          */
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;
368
369         /*
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
373          */
374         ref2 = ref1;
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;
379
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);
386
387         /*
388          * This is a normal open, which triggers an oplock
389          * break and still gets NT_STATUS_SHARING_VIOLATION
390          */
391         io = ref1;
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);
399
400         smb2_util_close(tree, *h);
401         h = NULL;
402         status = smb2_util_unlink(tree, fname);
403         CHECK_STATUS(status, NT_STATUS_OK);
404         CHECK_VAL(break_info.count, 0);
405
406         /*
407          * No Replay detection for regular Creates
408          */
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 |
412                 SEC_FILE_WRITE_DATA;
413
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,
420                 .in.fname   = fname
421         };
422
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;
427         h = &_h;
428         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
429
430         torture_comment(tctx, "No Replay Detection for regular Create\n");
431         /*
432          * Now replay the same create
433          */
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);
438
439 done:
440         smb2cli_session_stop_replay(tree->session->smbXcli);
441
442         if (h != NULL) {
443                 smb2_util_close(tree, *h);
444         }
445         smb2_deltree(tree, BASEDIR);
446
447         talloc_free(tree);
448         talloc_free(mem_ctx);
449
450         return ret;
451 }
452
453 /**
454  * Test Durablity V2 Create Replay Detection on Multi Channel
455  */
456 static bool test_replay3(struct torture_context *tctx, struct smb2_tree *tree1)
457 {
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;
461         NTSTATUS status;
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();
467         bool ret = true;
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;
474
475         if (smbXcli_conn_protocol(transport1->conn) < PROTOCOL_SMB3_00) {
476                 torture_skip(tctx, "SMB 3.X Dialect family required for "
477                                    "Replay tests\n");
478         }
479
480         ZERO_STRUCT(break_info);
481         break_info.tctx = tctx;
482         transport1->oplock.handler = torture_oplock_ack_handler;
483         transport1->oplock.private_data = tree1;
484
485         torture_comment(tctx, "Replay of DurableHandleReqV2 on Multi "
486                               "Channel\n");
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);
492
493         /*
494          * use the 1st channel, 1st session
495          */
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;
504
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;
509         h = &_h;
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);
516
517         status = smb2_connect(tctx,
518                         host,
519                         lpcfg_smb_ports(tctx->lp_ctx),
520                         share,
521                         lpcfg_resolve_context(tctx->lp_ctx),
522                         credentials,
523                         &tree2,
524                         tctx->ev,
525                         &transport1->options,
526                         lpcfg_socket_options(tctx->lp_ctx),
527                         lpcfg_gensec_settings(tctx, tctx->lp_ctx)
528                         );
529         torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
530                         "smb2_connect failed");
531         transport2 = tree2->session->transport;
532
533         transport2->oplock.handler = torture_oplock_ack_handler;
534         transport2->oplock.private_data = tree2;
535
536         /*
537          * Now bind the 1st session to 2nd transport channel
538          */
539         session1_2 = smb2_session_channel(transport2,
540                         lpcfg_gensec_settings(tctx, tctx->lp_ctx),
541                         tree2, session1_1);
542         torture_assert(tctx, session1_2 != NULL, "smb2_session_channel failed");
543
544         status = smb2_session_setup_spnego(session1_2,
545                         cmdline_credentials,
546                         0 /* previous_session_id */);
547         CHECK_STATUS(status, NT_STATUS_OK);
548
549         /*
550          * use the 2nd channel, 1st session
551          */
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;
558         h = &_h;
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);
565
566         tree1->session = session1_1;
567         smb2_util_close(tree1, *h);
568         h = NULL;
569
570 done:
571         talloc_free(tree2);
572         tree1->session = session1_1;
573
574         if (h != NULL) {
575                 smb2_util_close(tree1, *h);
576         }
577
578         smb2_util_unlink(tree1, fname);
579         smb2_deltree(tree1, BASEDIR);
580
581         talloc_free(tree1);
582         talloc_free(mem_ctx);
583
584         return ret;
585 }
586
587 /**
588  * Test Multichannel IO Ordering using ChannelSequence/Channel Epoch number
589  */
590 static bool test_replay4(struct torture_context *tctx, struct smb2_tree *tree1)
591 {
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;
595         NTSTATUS status;
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();
601         uint8_t buf[64];
602         struct smb2_read rd;
603         union smb_setfileinfo sfinfo;
604         bool ret = true;
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;
611         uint16_t curr_cs;
612
613         if (smbXcli_conn_protocol(transport1->conn) < PROTOCOL_SMB3_00) {
614                 torture_skip(tctx, "SMB 3.X Dialect family required for "
615                                    "Replay tests\n");
616         }
617
618         ZERO_STRUCT(break_info);
619         break_info.tctx = tctx;
620         transport1->oplock.handler = torture_oplock_ack_handler;
621         transport1->oplock.private_data = tree1;
622
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);
629
630         /*
631          * use the 1st channel, 1st session
632          */
633
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;
642
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;
647         h1 = &_h1;
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);
654
655         status = smb2_util_write(tree1, *h1, buf, 0, ARRAY_SIZE(buf));
656         CHECK_STATUS(status, NT_STATUS_OK);
657
658         /*
659          * Increment ChannelSequence so that server thinks that there's a
660          * Channel Failure
661          */
662         smb2cli_session_increment_channel_sequence(tree1->session->smbXcli);
663
664         /*
665          * Perform a Read with incremented ChannelSequence
666          */
667         rd = (struct smb2_read) {
668                 .in.file.handle = *h1,
669                 .in.length = sizeof(buf),
670                 .in.offset = 0
671         };
672         status = smb2_read(tree1, tree1, &rd);
673         CHECK_STATUS(status, NT_STATUS_OK);
674
675         /*
676          * Performing a Write with Stale ChannelSequence is not allowed by
677          * server
678          */
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);
683
684         /*
685          * Performing a Write Replay with Stale ChannelSequence is not allowed
686          * by server
687          */
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);
693
694         /*
695          * Performing a SetInfo with stale ChannelSequence is not allowed by
696          * server
697          */
698         ZERO_STRUCT(sfinfo);
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);
704
705         /*
706          * Performing a Read with stale ChannelSequence is allowed
707          */
708         rd = (struct smb2_read) {
709                 .in.file.handle = *h1,
710                 .in.length = ARRAY_SIZE(buf),
711                 .in.offset = 0
712         };
713         status = smb2_read(tree1, tree1, &rd);
714         CHECK_STATUS(status, NT_STATUS_OK);
715
716         status = smb2_connect(tctx,
717                         host,
718                         lpcfg_smb_ports(tctx->lp_ctx),
719                         share,
720                         lpcfg_resolve_context(tctx->lp_ctx),
721                         credentials,
722                         &tree2,
723                         tctx->ev,
724                         &transport1->options,
725                         lpcfg_socket_options(tctx->lp_ctx),
726                         lpcfg_gensec_settings(tctx, tctx->lp_ctx)
727                         );
728         torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
729                         "smb2_connect failed");
730         transport2 = tree2->session->transport;
731
732         transport2->oplock.handler = torture_oplock_ack_handler;
733         transport2->oplock.private_data = tree2;
734
735         /*
736          * Now bind the 1st session to 2nd transport channel
737          */
738         session1_2 = smb2_session_channel(transport2,
739                         lpcfg_gensec_settings(tctx, tctx->lp_ctx),
740                         tree2, session1_1);
741         torture_assert(tctx, session1_2 != NULL, "smb2_session_channel failed");
742
743         status = smb2_session_setup_spnego(session1_2,
744                         cmdline_credentials,
745                         0 /* previous_session_id */);
746         CHECK_STATUS(status, NT_STATUS_OK);
747
748         /*
749          * use the 2nd channel, 1st session
750          */
751         tree1->session = session1_2;
752
753         /*
754          * Write Replay with Correct ChannelSequence is allowed by the server
755          */
756         smb2cli_session_start_replay(tree1->session->smbXcli);
757         smb2cli_session_reset_channel_sequence(tree1->session->smbXcli,
758                                                curr_cs);
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);
762
763         /*
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
766          */
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);
772
773         /*
774          * Read back from File to verify what was written
775          */
776         rd = (struct smb2_read) {
777                 .in.file.handle = *h1,
778                 .in.length = ARRAY_SIZE(buf),
779                 .in.offset = 0
780         };
781         status = smb2_read(tree1, tree1, &rd);
782         CHECK_STATUS(status, NT_STATUS_OK);
783
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");
787         }
788
789         tree1->session = session1_1;
790         smb2_util_close(tree1, *h1);
791         h1 = NULL;
792
793         CHECK_VAL(break_info.count, 0);
794 done:
795         talloc_free(tree2);
796         tree1->session = session1_1;
797
798         if (h1 != NULL) {
799                 smb2_util_close(tree1, *h1);
800         }
801
802         smb2_util_unlink(tree1, fname);
803         smb2_deltree(tree1, BASEDIR);
804
805         talloc_free(tree1);
806         talloc_free(mem_ctx);
807
808         return ret;
809 }
810
811 struct torture_suite *torture_smb2_replay_init(void)
812 {
813         struct torture_suite *suite =
814                 torture_suite_create(talloc_autofree_context(), "replay");
815
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);
820
821         suite->description = talloc_strdup(suite, "SMB2 REPLAY tests");
822
823         return suite;
824 }