e48fb9b5f354b9d881a060eef7d6440ce30e8efb
[sfrench/samba-autobuild/.git] / source4 / torture / smb2 / durable_v2_open.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    test suite for SMB2 version two of durable opens
5
6    Copyright (C) Michael Adam 2012
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 3 of the License, or
11    (at your option) any later version.
12
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include "includes.h"
23 #include "libcli/smb2/smb2.h"
24 #include "libcli/smb2/smb2_calls.h"
25 #include "../libcli/smb/smbXcli_base.h"
26 #include "torture/torture.h"
27 #include "torture/smb2/proto.h"
28 #include "librpc/ndr/libndr.h"
29
30 #define CHECK_VAL(v, correct) do { \
31         if ((v) != (correct)) { \
32                 torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s got 0x%x - should be 0x%x\n", \
33                                 __location__, #v, (int)v, (int)correct); \
34                 ret = false; \
35         }} while (0)
36
37 #define CHECK_STATUS(status, correct) do { \
38         if (!NT_STATUS_EQUAL(status, correct)) { \
39                 torture_result(tctx, TORTURE_FAIL, __location__": Incorrect status %s - should be %s", \
40                        nt_errstr(status), nt_errstr(correct)); \
41                 ret = false; \
42                 goto done; \
43         }} while (0)
44
45 #define CHECK_CREATED(__io, __created, __attribute)                     \
46         do {                                                            \
47                 CHECK_VAL((__io)->out.create_action, NTCREATEX_ACTION_ ## __created); \
48                 CHECK_VAL((__io)->out.alloc_size, 0);                   \
49                 CHECK_VAL((__io)->out.size, 0);                         \
50                 CHECK_VAL((__io)->out.file_attr, (__attribute));        \
51                 CHECK_VAL((__io)->out.reserved2, 0);                    \
52         } while(0)
53
54 static struct {
55         int count;
56         struct smb2_close cl;
57 } break_info;
58
59 static void torture_oplock_close_callback(struct smb2_request *req)
60 {
61         smb2_close_recv(req, &break_info.cl);
62 }
63
64 /* A general oplock break notification handler.  This should be used when a
65  * test expects to break from batch or exclusive to a lower level. */
66 static bool torture_oplock_handler(struct smb2_transport *transport,
67                                    const struct smb2_handle *handle,
68                                    uint8_t level,
69                                    void *private_data)
70 {
71         struct smb2_tree *tree = private_data;
72         struct smb2_request *req;
73
74         break_info.count++;
75
76         ZERO_STRUCT(break_info.cl);
77         break_info.cl.in.file.handle = *handle;
78
79         req = smb2_close_send(tree, &break_info.cl);
80         req->async.fn = torture_oplock_close_callback;
81         req->async.private_data = NULL;
82         return true;
83 }
84
85 /**
86  * testing various create blob combinations.
87  */
88 bool test_durable_v2_open_create_blob(struct torture_context *tctx,
89                                       struct smb2_tree *tree)
90 {
91         NTSTATUS status;
92         TALLOC_CTX *mem_ctx = talloc_new(tctx);
93         char fname[256];
94         struct smb2_handle _h;
95         struct smb2_handle *h = NULL;
96         struct smb2_create io;
97         struct GUID create_guid = GUID_random();
98         bool ret = true;
99         struct smbcli_options options;
100
101         options = tree->session->transport->options;
102
103         /* Choose a random name in case the state is left a little funky. */
104         snprintf(fname, 256, "durable_v2_open_create_blob_%s.dat",
105                  generate_random_str(tctx, 8));
106
107         smb2_util_unlink(tree, fname);
108
109         smb2_oplock_create_share(&io, fname,
110                                  smb2_util_share_access(""),
111                                  smb2_util_oplock_level("b"));
112         io.in.durable_open = false;
113         io.in.durable_open_v2 = true;
114         io.in.persistent_open = false;
115         io.in.create_guid = create_guid;
116         io.in.timeout = UINT32_MAX;
117
118         status = smb2_create(tree, mem_ctx, &io);
119         CHECK_STATUS(status, NT_STATUS_OK);
120         _h = io.out.file.handle;
121         h = &_h;
122         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
123         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
124         CHECK_VAL(io.out.durable_open, false);
125         CHECK_VAL(io.out.durable_open_v2, true);
126         CHECK_VAL(io.out.persistent_open, false);
127         CHECK_VAL(io.out.timeout, io.in.timeout);
128
129         /* disconnect */
130         TALLOC_FREE(tree);
131
132         /* create a new session (same client_guid) */
133         if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
134                 torture_warning(tctx, "couldn't reconnect, bailing\n");
135                 ret = false;
136                 goto done;
137         }
138
139         /*
140          * check invalid combinations of durable handle
141          * request and reconnect blobs
142          * See MS-SMB2: 3.3.5.9.12
143          * Handling the SMB2_CREATE_DURABLE_HANDLE_RECONNECT_V2 Create Context
144          */
145         ZERO_STRUCT(io);
146         io.in.fname = fname;
147         io.in.durable_handle_v2 = h; /* durable v2 reconnect request */
148         io.in.durable_open = true;   /* durable v1 handle request */
149         io.in.create_guid = create_guid;
150         status = smb2_create(tree, mem_ctx, &io);
151         CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
152
153         ZERO_STRUCT(io);
154         io.in.fname = fname;
155         io.in.durable_handle = h;     /* durable v1 reconnect request */
156         io.in.durable_open_v2 = true; /* durable v2 handle request */
157         io.in.create_guid = create_guid;
158         status = smb2_create(tree, mem_ctx, &io);
159         CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
160
161         ZERO_STRUCT(io);
162         io.in.fname = fname;
163         io.in.durable_handle = h;    /* durable v1 reconnect request */
164         io.in.durable_handle_v2 = h; /* durable v2 reconnect request */
165         io.in.create_guid = create_guid;
166         status = smb2_create(tree, mem_ctx, &io);
167         CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
168
169         ZERO_STRUCT(io);
170         io.in.fname = fname;
171         io.in.durable_handle_v2 = h;  /* durable v2 reconnect request */
172         io.in.durable_open_v2 = true; /* durable v2 handle request */
173         io.in.create_guid = create_guid;
174         status = smb2_create(tree, mem_ctx, &io);
175         CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
176
177 done:
178         if (h != NULL) {
179                 smb2_util_close(tree, *h);
180         }
181
182         smb2_util_unlink(tree, fname);
183
184         talloc_free(tree);
185
186         talloc_free(mem_ctx);
187
188         return ret;
189 }
190
191
192 /**
193  * basic durable_open test.
194  * durable state should only be granted when requested
195  * along with a batch oplock or a handle lease.
196  *
197  * This test tests durable open with all possible oplock types.
198  */
199
200 struct durable_open_vs_oplock {
201         const char *level;
202         const char *share_mode;
203         bool durable;
204         bool persistent;
205 };
206
207 #define NUM_OPLOCK_TYPES 4
208 #define NUM_SHARE_MODES 8
209 #define NUM_OPLOCK_OPEN_TESTS ( NUM_OPLOCK_TYPES * NUM_SHARE_MODES )
210 static struct durable_open_vs_oplock durable_open_vs_oplock_table[NUM_OPLOCK_OPEN_TESTS] =
211 {
212         { "", "", false, false },
213         { "", "R", false, false },
214         { "", "W", false, false },
215         { "", "D", false, false },
216         { "", "RD", false, false },
217         { "", "RW", false, false },
218         { "", "WD", false, false },
219         { "", "RWD", false, false },
220
221         { "s", "", false, false },
222         { "s", "R", false, false },
223         { "s", "W", false, false },
224         { "s", "D", false, false },
225         { "s", "RD", false, false },
226         { "s", "RW", false, false },
227         { "s", "WD", false, false },
228         { "s", "RWD", false, false },
229
230         { "x", "", false, false },
231         { "x", "R", false, false },
232         { "x", "W", false, false },
233         { "x", "D", false, false },
234         { "x", "RD", false, false },
235         { "x", "RW", false, false },
236         { "x", "WD", false, false },
237         { "x", "RWD", false, false },
238
239         { "b", "", true, false },
240         { "b", "R", true, false },
241         { "b", "W", true, false },
242         { "b", "D", true, false },
243         { "b", "RD", true, false },
244         { "b", "RW", true, false },
245         { "b", "WD", true, false },
246         { "b", "RWD", true, false },
247 };
248
249 static bool test_one_durable_v2_open_oplock(struct torture_context *tctx,
250                                             struct smb2_tree *tree,
251                                             const char *fname,
252                                             bool request_persistent,
253                                             struct durable_open_vs_oplock test)
254 {
255         NTSTATUS status;
256         TALLOC_CTX *mem_ctx = talloc_new(tctx);
257         struct smb2_handle _h;
258         struct smb2_handle *h = NULL;
259         bool ret = true;
260         struct smb2_create io;
261
262         smb2_util_unlink(tree, fname);
263
264         smb2_oplock_create_share(&io, fname,
265                                  smb2_util_share_access(test.share_mode),
266                                  smb2_util_oplock_level(test.level));
267         io.in.durable_open = false;
268         io.in.durable_open_v2 = true;
269         io.in.persistent_open = request_persistent;
270         io.in.create_guid = GUID_random();
271
272         status = smb2_create(tree, mem_ctx, &io);
273         CHECK_STATUS(status, NT_STATUS_OK);
274         _h = io.out.file.handle;
275         h = &_h;
276         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
277         CHECK_VAL(io.out.durable_open, false);
278         CHECK_VAL(io.out.durable_open_v2, test.durable);
279         CHECK_VAL(io.out.persistent_open, test.persistent);
280         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level(test.level));
281
282 done:
283         if (h != NULL) {
284                 smb2_util_close(tree, *h);
285         }
286         smb2_util_unlink(tree, fname);
287         talloc_free(mem_ctx);
288
289         return ret;
290 }
291
292 static bool test_durable_v2_open_oplock_table(struct torture_context *tctx,
293                                               struct smb2_tree *tree,
294                                               const char *fname,
295                                               bool request_persistent,
296                                               struct durable_open_vs_oplock *table,
297                                               uint8_t num_tests)
298 {
299         bool ret = true;
300         uint8_t i;
301
302         smb2_util_unlink(tree, fname);
303
304         for (i = 0; i < num_tests; i++) {
305                 ret = test_one_durable_v2_open_oplock(tctx,
306                                                       tree,
307                                                       fname,
308                                                       request_persistent,
309                                                       table[i]);
310                 if (ret == false) {
311                         goto done;
312                 }
313         }
314
315 done:
316         smb2_util_unlink(tree, fname);
317
318         return ret;
319 }
320
321 bool test_durable_v2_open_oplock(struct torture_context *tctx,
322                                  struct smb2_tree *tree)
323 {
324         bool ret;
325         char fname[256];
326
327         /* Choose a random name in case the state is left a little funky. */
328         snprintf(fname, 256, "durable_open_oplock_%s.dat",
329                  generate_random_str(tctx, 8));
330
331         ret = test_durable_v2_open_oplock_table(tctx, tree, fname,
332                                                 false, /* request_persistent */
333                                                 durable_open_vs_oplock_table,
334                                                 NUM_OPLOCK_OPEN_TESTS);
335
336         talloc_free(tree);
337
338         return ret;
339 }
340
341 /**
342  * basic durable handle open test.
343  * persistent state should only be granted when requested
344  * along with a batch oplock or a handle lease.
345  *
346  * This test tests persistent open with all valid lease types.
347  */
348
349 struct durable_open_vs_lease {
350         const char *type;
351         const char *share_mode;
352         bool durable;
353         bool persistent;
354 };
355
356 #define NUM_LEASE_TYPES 5
357 #define NUM_LEASE_OPEN_TESTS ( NUM_LEASE_TYPES * NUM_SHARE_MODES )
358 static struct durable_open_vs_lease durable_open_vs_lease_table[NUM_LEASE_OPEN_TESTS] =
359 {
360         { "", "", false, false },
361         { "", "R", false, false },
362         { "", "W", false, false },
363         { "", "D", false, false },
364         { "", "RW", false, false },
365         { "", "RD", false, false },
366         { "", "WD", false, false },
367         { "", "RWD", false, false },
368
369         { "R", "", false, false },
370         { "R", "R", false, false },
371         { "R", "W", false, false },
372         { "R", "D", false, false },
373         { "R", "RW", false, false },
374         { "R", "RD", false, false },
375         { "R", "DW", false, false },
376         { "R", "RWD", false, false },
377
378         { "RW", "", false, false },
379         { "RW", "R", false, false },
380         { "RW", "W", false, false },
381         { "RW", "D", false, false },
382         { "RW", "RW", false, false },
383         { "RW", "RD", false, false },
384         { "RW", "WD", false, false },
385         { "RW", "RWD", false, false },
386
387         { "RH", "", true, false },
388         { "RH", "R", true, false },
389         { "RH", "W", true, false },
390         { "RH", "D", true, false },
391         { "RH", "RW", true, false },
392         { "RH", "RD", true, false },
393         { "RH", "WD", true, false },
394         { "RH", "RWD", true, false },
395
396         { "RHW", "", true, false },
397         { "RHW", "R", true, false },
398         { "RHW", "W", true, false },
399         { "RHW", "D", true, false },
400         { "RHW", "RW", true, false },
401         { "RHW", "RD", true, false },
402         { "RHW", "WD", true, false },
403         { "RHW", "RWD", true, false },
404 };
405
406 static bool test_one_durable_v2_open_lease(struct torture_context *tctx,
407                                            struct smb2_tree *tree,
408                                            const char *fname,
409                                            bool request_persistent,
410                                            struct durable_open_vs_lease test)
411 {
412         NTSTATUS status;
413         TALLOC_CTX *mem_ctx = talloc_new(tctx);
414         struct smb2_handle _h;
415         struct smb2_handle *h = NULL;
416         bool ret = true;
417         struct smb2_create io;
418         struct smb2_lease ls;
419         uint64_t lease;
420
421         smb2_util_unlink(tree, fname);
422
423         lease = random();
424
425         smb2_lease_create_share(&io, &ls, false /* dir */, fname,
426                                 smb2_util_share_access(test.share_mode),
427                                 lease,
428                                 smb2_util_lease_state(test.type));
429         io.in.durable_open = false;
430         io.in.durable_open_v2 = true;
431         io.in.persistent_open = request_persistent;
432         io.in.create_guid = GUID_random();
433
434         status = smb2_create(tree, mem_ctx, &io);
435         CHECK_STATUS(status, NT_STATUS_OK);
436         _h = io.out.file.handle;
437         h = &_h;
438         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
439         CHECK_VAL(io.out.durable_open, false);
440         CHECK_VAL(io.out.durable_open_v2, test.durable);
441         CHECK_VAL(io.out.persistent_open, test.persistent);
442         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
443         CHECK_VAL(io.out.lease_response.lease_key.data[0], lease);
444         CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease);
445         CHECK_VAL(io.out.lease_response.lease_state,
446                   smb2_util_lease_state(test.type));
447 done:
448         if (h != NULL) {
449                 smb2_util_close(tree, *h);
450         }
451         smb2_util_unlink(tree, fname);
452         talloc_free(mem_ctx);
453
454         return ret;
455 }
456
457 static bool test_durable_v2_open_lease_table(struct torture_context *tctx,
458                                              struct smb2_tree *tree,
459                                              const char *fname,
460                                              bool request_persistent,
461                                              struct durable_open_vs_lease *table,
462                                              uint8_t num_tests)
463 {
464         bool ret = true;
465         uint8_t i;
466
467         smb2_util_unlink(tree, fname);
468
469         for (i = 0; i < num_tests; i++) {
470                 ret = test_one_durable_v2_open_lease(tctx,
471                                                      tree,
472                                                      fname,
473                                                      request_persistent,
474                                                      table[i]);
475                 if (ret == false) {
476                         goto done;
477                 }
478         }
479
480 done:
481         smb2_util_unlink(tree, fname);
482
483         return ret;
484 }
485
486 bool test_durable_v2_open_lease(struct torture_context *tctx,
487                                 struct smb2_tree *tree)
488 {
489         char fname[256];
490         bool ret = true;
491         uint32_t caps;
492
493         caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
494         if (!(caps & SMB2_CAP_LEASING)) {
495                 torture_skip(tctx, "leases are not supported");
496         }
497
498         /* Choose a random name in case the state is left a little funky. */
499         snprintf(fname, 256, "durable_open_lease_%s.dat", generate_random_str(tctx, 8));
500
501         ret = test_durable_v2_open_lease_table(tctx, tree, fname,
502                                                false, /* request_persistent */
503                                                durable_open_vs_lease_table,
504                                                NUM_LEASE_OPEN_TESTS);
505
506         talloc_free(tree);
507         return ret;
508 }
509
510 /**
511  * basic test for doing a durable open
512  * and do a durable reopen on the same connection
513  * while the first open is still active (fails)
514  */
515 bool test_durable_v2_open_reopen1(struct torture_context *tctx,
516                                   struct smb2_tree *tree)
517 {
518         NTSTATUS status;
519         TALLOC_CTX *mem_ctx = talloc_new(tctx);
520         char fname[256];
521         struct smb2_handle _h;
522         struct smb2_handle *h = NULL;
523         struct smb2_create io;
524         struct GUID create_guid = GUID_random();
525         bool ret = true;
526
527         /* Choose a random name in case the state is left a little funky. */
528         snprintf(fname, 256, "durable_v2_open_reopen1_%s.dat",
529                  generate_random_str(tctx, 8));
530
531         smb2_util_unlink(tree, fname);
532
533         smb2_oplock_create_share(&io, fname,
534                                  smb2_util_share_access(""),
535                                  smb2_util_oplock_level("b"));
536         io.in.durable_open = false;
537         io.in.durable_open_v2 = true;
538         io.in.persistent_open = false;
539         io.in.create_guid = create_guid;
540         io.in.timeout = UINT32_MAX;
541
542         status = smb2_create(tree, mem_ctx, &io);
543         CHECK_STATUS(status, NT_STATUS_OK);
544         _h = io.out.file.handle;
545         h = &_h;
546         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
547         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
548         CHECK_VAL(io.out.durable_open, false);
549         CHECK_VAL(io.out.durable_open_v2, true);
550         CHECK_VAL(io.out.persistent_open, false);
551         CHECK_VAL(io.out.timeout, io.in.timeout);
552
553         /* try a durable reconnect while the file is still open */
554         ZERO_STRUCT(io);
555         io.in.fname = "";
556         io.in.durable_handle_v2 = h;
557         io.in.create_guid = create_guid;
558         status = smb2_create(tree, mem_ctx, &io);
559         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
560
561 done:
562         if (h != NULL) {
563                 smb2_util_close(tree, *h);
564         }
565
566         smb2_util_unlink(tree, fname);
567
568         talloc_free(tree);
569
570         talloc_free(mem_ctx);
571
572         return ret;
573 }
574
575 /**
576  * Basic test for doing a durable open
577  * and do a session reconnect while the first
578  * session is still active and the handle is
579  * still open in the client.
580  * This closes the original session and  a
581  * durable reconnect on the new session succeeds.
582  */
583 bool test_durable_v2_open_reopen1a(struct torture_context *tctx,
584                                    struct smb2_tree *tree)
585 {
586         NTSTATUS status;
587         TALLOC_CTX *mem_ctx = talloc_new(tctx);
588         char fname[256];
589         struct smb2_handle _h;
590         struct smb2_handle *h = NULL;
591         struct smb2_create io;
592         struct GUID create_guid = GUID_random();
593         bool ret = true;
594         struct smb2_tree *tree2 = NULL;
595         struct smb2_tree *tree3 = NULL;
596         uint64_t previous_session_id;
597         struct smbcli_options options;
598         struct GUID orig_client_guid;
599
600         options = tree->session->transport->options;
601         orig_client_guid = options.client_guid;
602
603         /* Choose a random name in case the state is left a little funky. */
604         snprintf(fname, 256, "durable_v2_open_reopen1a_%s.dat",
605                  generate_random_str(tctx, 8));
606
607         smb2_util_unlink(tree, fname);
608
609         smb2_oplock_create_share(&io, fname,
610                                  smb2_util_share_access(""),
611                                  smb2_util_oplock_level("b"));
612         io.in.durable_open = false;
613         io.in.durable_open_v2 = true;
614         io.in.persistent_open = false;
615         io.in.create_guid = create_guid;
616         io.in.timeout = UINT32_MAX;
617
618         status = smb2_create(tree, mem_ctx, &io);
619         CHECK_STATUS(status, NT_STATUS_OK);
620         _h = io.out.file.handle;
621         h = &_h;
622         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
623         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
624         CHECK_VAL(io.out.durable_open, false);
625         CHECK_VAL(io.out.durable_open_v2, true);
626         CHECK_VAL(io.out.persistent_open, false);
627         CHECK_VAL(io.out.timeout, io.in.timeout);
628
629         /*
630          * a session reconnect on a second tcp connection
631          */
632
633         previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
634
635         /* for oplocks, the client guid can be different: */
636         options.client_guid = GUID_random();
637
638         ret = torture_smb2_connection_ext(tctx, previous_session_id,
639                                           &options, &tree2);
640         torture_assert_goto(tctx, ret, ret, done, "couldn't reconnect");
641
642         /*
643          * check that this has deleted the old session
644          */
645
646         ZERO_STRUCT(io);
647         io.in.fname = "";
648         io.in.durable_handle_v2 = h;
649         io.in.create_guid = create_guid;
650         status = smb2_create(tree, mem_ctx, &io);
651         CHECK_STATUS(status, NT_STATUS_USER_SESSION_DELETED);
652
653         TALLOC_FREE(tree);
654
655         /*
656          * but a durable reconnect on the new session succeeds:
657          */
658
659         ZERO_STRUCT(io);
660         io.in.fname = "";
661         io.in.durable_handle_v2 = h;
662         io.in.create_guid = create_guid;
663         status = smb2_create(tree2, mem_ctx, &io);
664         CHECK_STATUS(status, NT_STATUS_OK);
665         CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
666         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
667         CHECK_VAL(io.out.durable_open, false);
668         CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
669         CHECK_VAL(io.out.persistent_open, false);
670         CHECK_VAL(io.out.timeout, io.in.timeout);
671         _h = io.out.file.handle;
672         h = &_h;
673
674         /*
675          * a session reconnect on a second tcp connection
676          */
677
678         previous_session_id = smb2cli_session_current_id(tree2->session->smbXcli);
679
680         /* it works the same with the original guid */
681         options.client_guid = orig_client_guid;
682
683         ret = torture_smb2_connection_ext(tctx, previous_session_id,
684                                           &options, &tree3);
685         torture_assert_goto(tctx, ret, ret, done, "couldn't reconnect");
686
687         /*
688          * check that this has deleted the old session
689          */
690
691         ZERO_STRUCT(io);
692         io.in.fname = "";
693         io.in.durable_handle_v2 = h;
694         io.in.create_guid = create_guid;
695         status = smb2_create(tree2, mem_ctx, &io);
696         CHECK_STATUS(status, NT_STATUS_USER_SESSION_DELETED);
697         TALLOC_FREE(tree2);
698
699         /*
700          * but a durable reconnect on the new session succeeds:
701          */
702
703         ZERO_STRUCT(io);
704         io.in.fname = "";
705         io.in.durable_handle_v2 = h;
706         io.in.create_guid = create_guid;
707         status = smb2_create(tree3, mem_ctx, &io);
708         CHECK_STATUS(status, NT_STATUS_OK);
709         CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
710         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
711         CHECK_VAL(io.out.durable_open, false);
712         CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
713         CHECK_VAL(io.out.persistent_open, false);
714         CHECK_VAL(io.out.timeout, io.in.timeout);
715         _h = io.out.file.handle;
716         h = &_h;
717
718 done:
719         if (tree == NULL) {
720                 tree = tree2;
721         }
722
723         if (tree == NULL) {
724                 tree = tree3;
725         }
726
727         if (tree != NULL) {
728                 if (h != NULL) {
729                         smb2_util_close(tree, *h);
730                 }
731
732                 smb2_util_unlink(tree, fname);
733
734                 talloc_free(tree);
735         }
736
737         talloc_free(mem_ctx);
738
739         return ret;
740 }
741
742 /**
743  * basic test for doing a durable open
744  * tcp disconnect, reconnect, do a durable reopen (succeeds)
745  */
746 bool test_durable_v2_open_reopen2(struct torture_context *tctx,
747                                   struct smb2_tree *tree)
748 {
749         NTSTATUS status;
750         TALLOC_CTX *mem_ctx = talloc_new(tctx);
751         char fname[256];
752         struct smb2_handle _h;
753         struct smb2_handle *h = NULL;
754         struct smb2_create io;
755         struct GUID create_guid = GUID_random();
756         struct GUID create_guid_invalid = GUID_random();
757         bool ret = true;
758
759         /* Choose a random name in case the state is left a little funky. */
760         snprintf(fname, 256, "durable_v2_open_reopen2_%s.dat",
761                  generate_random_str(tctx, 8));
762
763         smb2_util_unlink(tree, fname);
764
765         smb2_oplock_create_share(&io, fname,
766                                  smb2_util_share_access(""),
767                                  smb2_util_oplock_level("b"));
768         io.in.durable_open = false;
769         io.in.durable_open_v2 = true;
770         io.in.persistent_open = false;
771         io.in.create_guid = create_guid;
772         io.in.timeout = UINT32_MAX;
773
774         status = smb2_create(tree, mem_ctx, &io);
775         CHECK_STATUS(status, NT_STATUS_OK);
776         _h = io.out.file.handle;
777         h = &_h;
778         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
779         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
780         CHECK_VAL(io.out.durable_open, false);
781         CHECK_VAL(io.out.durable_open_v2, true);
782         CHECK_VAL(io.out.persistent_open, false);
783         CHECK_VAL(io.out.timeout, io.in.timeout);
784
785         /* disconnect, leaving the durable open */
786         TALLOC_FREE(tree);
787
788         if (!torture_smb2_connection(tctx, &tree)) {
789                 torture_warning(tctx, "couldn't reconnect, bailing\n");
790                 ret = false;
791                 goto done;
792         }
793
794         /*
795          * first a few failure cases
796          */
797
798         ZERO_STRUCT(io);
799         io.in.fname = "";
800         io.in.durable_handle_v2 = h;
801         status = smb2_create(tree, mem_ctx, &io);
802         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
803
804         ZERO_STRUCT(io);
805         io.in.fname = "__non_existing_fname__";
806         io.in.durable_handle_v2 = h;
807         status = smb2_create(tree, mem_ctx, &io);
808         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
809
810         ZERO_STRUCT(io);
811         io.in.fname = fname;
812         io.in.durable_handle_v2 = h;
813         status = smb2_create(tree, mem_ctx, &io);
814         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
815
816         /* a non-zero but non-matching create_guid does not change it: */
817         ZERO_STRUCT(io);
818         io.in.fname = fname;
819         io.in.durable_handle_v2 = h;
820         io.in.create_guid = create_guid_invalid;
821         status = smb2_create(tree, mem_ctx, &io);
822         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
823
824         /*
825          * now success:
826          * The important difference is that the create_guid is provided.
827          */
828         ZERO_STRUCT(io);
829         io.in.fname = fname;
830         io.in.durable_open_v2 = false;
831         io.in.durable_handle_v2 = h;
832         io.in.create_guid = create_guid;
833         h = NULL;
834
835         status = smb2_create(tree, mem_ctx, &io);
836         CHECK_STATUS(status, NT_STATUS_OK);
837         CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
838         CHECK_VAL(io.out.durable_open, false);
839         CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
840         CHECK_VAL(io.out.persistent_open, false);
841         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
842         _h = io.out.file.handle;
843         h = &_h;
844
845         /* disconnect one more time */
846         TALLOC_FREE(tree);
847
848         if (!torture_smb2_connection(tctx, &tree)) {
849                 torture_warning(tctx, "couldn't reconnect, bailing\n");
850                 ret = false;
851                 goto done;
852         }
853
854         ZERO_STRUCT(io);
855         /* These are completely ignored by the server */
856         io.in.security_flags = 0x78;
857         io.in.oplock_level = 0x78;
858         io.in.impersonation_level = 0x12345678;
859         io.in.create_flags = 0x12345678;
860         io.in.reserved = 0x12345678;
861         io.in.desired_access = 0x12345678;
862         io.in.file_attributes = 0x12345678;
863         io.in.share_access = 0x12345678;
864         io.in.create_disposition = 0x12345678;
865         io.in.create_options = 0x12345678;
866         io.in.fname = "__non_existing_fname__";
867
868         /*
869          * only io.in.durable_handle_v2 and
870          * io.in.create_guid are checked
871          */
872         io.in.durable_open_v2 = false;
873         io.in.durable_handle_v2 = h;
874         io.in.create_guid = create_guid;
875         h = NULL;
876
877         status = smb2_create(tree, mem_ctx, &io);
878         CHECK_STATUS(status, NT_STATUS_OK);
879         CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
880         CHECK_VAL(io.out.durable_open, false);
881         CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
882         CHECK_VAL(io.out.persistent_open, false);
883         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
884         _h = io.out.file.handle;
885         h = &_h;
886
887 done:
888         if (h != NULL) {
889                 smb2_util_close(tree, *h);
890         }
891
892         smb2_util_unlink(tree, fname);
893
894         talloc_free(tree);
895
896         talloc_free(mem_ctx);
897
898         return ret;
899 }
900
901 /**
902  * durable reconnect test:
903  * connect with v2, reconnect with v1
904  */
905 bool test_durable_v2_open_reopen2b(struct torture_context *tctx,
906                                    struct smb2_tree *tree)
907 {
908         NTSTATUS status;
909         TALLOC_CTX *mem_ctx = talloc_new(tctx);
910         char fname[256];
911         struct smb2_handle _h;
912         struct smb2_handle *h = NULL;
913         struct smb2_create io;
914         struct GUID create_guid = GUID_random();
915         bool ret = true;
916         struct smbcli_options options;
917
918         options = tree->session->transport->options;
919
920         /* Choose a random name in case the state is left a little funky. */
921         snprintf(fname, 256, "durable_v2_open_reopen2b_%s.dat",
922                  generate_random_str(tctx, 8));
923
924         smb2_util_unlink(tree, fname);
925
926         smb2_oplock_create_share(&io, fname,
927                                  smb2_util_share_access(""),
928                                  smb2_util_oplock_level("b"));
929         io.in.durable_open = false;
930         io.in.durable_open_v2 = true;
931         io.in.persistent_open = false;
932         io.in.create_guid = create_guid;
933         io.in.timeout = UINT32_MAX;
934
935         status = smb2_create(tree, mem_ctx, &io);
936         CHECK_STATUS(status, NT_STATUS_OK);
937         _h = io.out.file.handle;
938         h = &_h;
939         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
940         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
941         CHECK_VAL(io.out.durable_open, false);
942         CHECK_VAL(io.out.durable_open_v2, true);
943         CHECK_VAL(io.out.persistent_open, false);
944         CHECK_VAL(io.out.timeout, io.in.timeout);
945
946         /* disconnect, leaving the durable open */
947         TALLOC_FREE(tree);
948
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;
953         }
954
955         ZERO_STRUCT(io);
956         io.in.fname = fname;
957         io.in.durable_handle_v2 = h;     /* durable v2 reconnect */
958         io.in.create_guid = GUID_zero(); /* but zero create GUID */
959         status = smb2_create(tree, mem_ctx, &io);
960         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
961
962         ZERO_STRUCT(io);
963         io.in.fname = fname;
964         io.in.durable_handle = h; /* durable v1 (!) reconnect */
965         h = NULL;
966
967         status = smb2_create(tree, mem_ctx, &io);
968         CHECK_STATUS(status, NT_STATUS_OK);
969         CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
970         CHECK_VAL(io.out.durable_open, false);
971         CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
972         CHECK_VAL(io.out.persistent_open, false);
973         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
974         _h = io.out.file.handle;
975         h = &_h;
976
977 done:
978         if (h != NULL) {
979                 smb2_util_close(tree, *h);
980         }
981
982         smb2_util_unlink(tree, fname);
983
984         talloc_free(tree);
985
986         talloc_free(mem_ctx);
987
988         return ret;
989 }
990 /**
991  * durable reconnect test:
992  * connect with v1, reconnect with v2 : fails (no create_guid...)
993  */
994 bool test_durable_v2_open_reopen2c(struct torture_context *tctx,
995                                    struct smb2_tree *tree)
996 {
997         NTSTATUS status;
998         TALLOC_CTX *mem_ctx = talloc_new(tctx);
999         char fname[256];
1000         struct smb2_handle _h;
1001         struct smb2_handle *h = NULL;
1002         struct smb2_create io;
1003         struct GUID create_guid = GUID_random();
1004         bool ret = true;
1005         struct smbcli_options options;
1006
1007         options = tree->session->transport->options;
1008
1009         /* Choose a random name in case the state is left a little funky. */
1010         snprintf(fname, 256, "durable_v2_open_reopen2c_%s.dat",
1011                  generate_random_str(tctx, 8));
1012
1013         smb2_util_unlink(tree, fname);
1014
1015         smb2_oplock_create_share(&io, fname,
1016                                  smb2_util_share_access(""),
1017                                  smb2_util_oplock_level("b"));
1018         io.in.durable_open = true;
1019         io.in.durable_open_v2 = false;
1020         io.in.persistent_open = false;
1021         io.in.create_guid = create_guid;
1022         io.in.timeout = UINT32_MAX;
1023
1024         status = smb2_create(tree, mem_ctx, &io);
1025         CHECK_STATUS(status, NT_STATUS_OK);
1026         _h = io.out.file.handle;
1027         h = &_h;
1028         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1029         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
1030         CHECK_VAL(io.out.durable_open, true);
1031         CHECK_VAL(io.out.durable_open_v2, false);
1032         CHECK_VAL(io.out.persistent_open, false);
1033         CHECK_VAL(io.out.timeout, 0);
1034
1035         /* disconnect, leaving the durable open */
1036         TALLOC_FREE(tree);
1037
1038         if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
1039                 torture_warning(tctx, "couldn't reconnect, bailing\n");
1040                 ret = false;
1041                 goto done;
1042         }
1043
1044         ZERO_STRUCT(io);
1045         io.in.fname = fname;
1046         io.in.durable_handle_v2 = h;     /* durable v2 reconnect */
1047         io.in.create_guid = create_guid;
1048         status = smb2_create(tree, mem_ctx, &io);
1049         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1050
1051 done:
1052         if (h != NULL) {
1053                 smb2_util_close(tree, *h);
1054         }
1055
1056         smb2_util_unlink(tree, fname);
1057
1058         talloc_free(tree);
1059
1060         talloc_free(mem_ctx);
1061
1062         return ret;
1063 }
1064
1065 /**
1066  * lease variant of reopen2
1067  * basic test for doing a durable open
1068  * tcp disconnect, reconnect, do a durable reopen (succeeds)
1069  */
1070 bool test_durable_v2_open_reopen2_lease(struct torture_context *tctx,
1071                                         struct smb2_tree *tree)
1072 {
1073         NTSTATUS status;
1074         TALLOC_CTX *mem_ctx = talloc_new(tctx);
1075         char fname[256];
1076         struct smb2_handle _h;
1077         struct smb2_handle *h = NULL;
1078         struct smb2_create io;
1079         struct GUID create_guid = GUID_random();
1080         struct smb2_lease ls;
1081         uint64_t lease_key;
1082         bool ret = true;
1083         struct smbcli_options options;
1084         uint32_t caps;
1085
1086         caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
1087         if (!(caps & SMB2_CAP_LEASING)) {
1088                 torture_skip(tctx, "leases are not supported");
1089         }
1090
1091         options = tree->session->transport->options;
1092
1093         /* Choose a random name in case the state is left a little funky. */
1094         snprintf(fname, 256, "durable_v2_open_reopen2_%s.dat",
1095                  generate_random_str(tctx, 8));
1096
1097         smb2_util_unlink(tree, fname);
1098
1099         lease_key = random();
1100         smb2_lease_create(&io, &ls, false /* dir */, fname,
1101                           lease_key, smb2_util_lease_state("RWH"));
1102         io.in.durable_open = false;
1103         io.in.durable_open_v2 = true;
1104         io.in.persistent_open = false;
1105         io.in.create_guid = create_guid;
1106         io.in.timeout = UINT32_MAX;
1107
1108         status = smb2_create(tree, mem_ctx, &io);
1109         CHECK_STATUS(status, NT_STATUS_OK);
1110         _h = io.out.file.handle;
1111         h = &_h;
1112         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1113         CHECK_VAL(io.out.durable_open, false);
1114         CHECK_VAL(io.out.durable_open_v2, true);
1115         CHECK_VAL(io.out.persistent_open, false);
1116         CHECK_VAL(io.out.timeout, io.in.timeout);
1117         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1118         CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
1119         CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
1120         CHECK_VAL(io.out.lease_response.lease_state,
1121                   smb2_util_lease_state("RWH"));
1122         CHECK_VAL(io.out.lease_response.lease_flags, 0);
1123         CHECK_VAL(io.out.lease_response.lease_duration, 0);
1124
1125         /* disconnect, reconnect and then do durable reopen */
1126         TALLOC_FREE(tree);
1127
1128         if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
1129                 torture_warning(tctx, "couldn't reconnect, bailing\n");
1130                 ret = false;
1131                 goto done;
1132         }
1133
1134         /* a few failure tests: */
1135
1136         /*
1137          * several attempts without lease attached:
1138          * all fail with NT_STATUS_OBJECT_NAME_NOT_FOUND
1139          * irrespective of file name provided
1140          */
1141
1142         ZERO_STRUCT(io);
1143         io.in.fname = "";
1144         io.in.durable_handle_v2 = h;
1145         status = smb2_create(tree, mem_ctx, &io);
1146         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1147
1148         ZERO_STRUCT(io);
1149         io.in.fname = "__non_existing_fname__";
1150         io.in.durable_handle_v2 = h;
1151         status = smb2_create(tree, mem_ctx, &io);
1152         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1153
1154         ZERO_STRUCT(io);
1155         io.in.fname = fname;
1156         io.in.durable_handle_v2 = h;
1157         status = smb2_create(tree, mem_ctx, &io);
1158         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1159
1160         /*
1161          * attempt with lease provided, but
1162          * with a changed lease key. => fails
1163          */
1164         ZERO_STRUCT(io);
1165         io.in.fname = fname;
1166         io.in.durable_open_v2 = false;
1167         io.in.durable_handle_v2 = h;
1168         io.in.create_guid = create_guid;
1169         io.in.lease_request = &ls;
1170         io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
1171         /* a wrong lease key lets the request fail */
1172         ls.lease_key.data[0]++;
1173
1174         status = smb2_create(tree, mem_ctx, &io);
1175         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1176
1177         /* restore the correct lease key */
1178         ls.lease_key.data[0]--;
1179
1180         /*
1181          * this last failing attempt is almost correct:
1182          * only problem is: we use the wrong filename...
1183          * Note that this gives INVALID_PARAMETER.
1184          * This is different from oplocks!
1185          */
1186         ZERO_STRUCT(io);
1187         io.in.fname = "__non_existing_fname__";
1188         io.in.durable_open_v2 = false;
1189         io.in.durable_handle_v2 = h;
1190         io.in.create_guid = create_guid;
1191         io.in.lease_request = &ls;
1192         io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
1193
1194         status = smb2_create(tree, mem_ctx, &io);
1195         CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
1196
1197         /*
1198          * Now for a succeeding reconnect:
1199          */
1200
1201         ZERO_STRUCT(io);
1202         io.in.fname = fname;
1203         io.in.durable_open_v2 = false;
1204         io.in.durable_handle_v2 = h;
1205         io.in.create_guid = create_guid;
1206         io.in.lease_request = &ls;
1207         io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
1208
1209         /* the requested lease state is irrelevant */
1210         ls.lease_state = smb2_util_lease_state("");
1211
1212         h = NULL;
1213
1214         status = smb2_create(tree, mem_ctx, &io);
1215         CHECK_STATUS(status, NT_STATUS_OK);
1216
1217         CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1218         CHECK_VAL(io.out.durable_open, false);
1219         CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
1220         CHECK_VAL(io.out.persistent_open, false);
1221         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1222         CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
1223         CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
1224         CHECK_VAL(io.out.lease_response.lease_state,
1225                   smb2_util_lease_state("RWH"));
1226         CHECK_VAL(io.out.lease_response.lease_flags, 0);
1227         CHECK_VAL(io.out.lease_response.lease_duration, 0);
1228         _h = io.out.file.handle;
1229         h = &_h;
1230
1231         /* disconnect one more time */
1232         TALLOC_FREE(tree);
1233
1234         if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
1235                 torture_warning(tctx, "couldn't reconnect, bailing\n");
1236                 ret = false;
1237                 goto done;
1238         }
1239
1240         /*
1241          * demonstrate that various parameters are ignored
1242          * in the reconnect
1243          */
1244
1245         ZERO_STRUCT(io);
1246         /*
1247          * These are completely ignored by the server
1248          */
1249         io.in.security_flags = 0x78;
1250         io.in.oplock_level = 0x78;
1251         io.in.impersonation_level = 0x12345678;
1252         io.in.create_flags = 0x12345678;
1253         io.in.reserved = 0x12345678;
1254         io.in.desired_access = 0x12345678;
1255         io.in.file_attributes = 0x12345678;
1256         io.in.share_access = 0x12345678;
1257         io.in.create_disposition = 0x12345678;
1258         io.in.create_options = 0x12345678;
1259
1260         /*
1261          * only these are checked:
1262          * - io.in.fname
1263          * - io.in.durable_handle_v2,
1264          * - io.in.create_guid
1265          * - io.in.lease_request->lease_key
1266          */
1267
1268         io.in.fname = fname;
1269         io.in.durable_open_v2 = false;
1270         io.in.durable_handle_v2 = h;
1271         io.in.create_guid = create_guid;
1272         io.in.lease_request = &ls;
1273
1274         /* the requested lease state is irrelevant */
1275         ls.lease_state = smb2_util_lease_state("");
1276
1277         h = NULL;
1278
1279         status = smb2_create(tree, mem_ctx, &io);
1280         CHECK_STATUS(status, NT_STATUS_OK);
1281
1282         CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1283         CHECK_VAL(io.out.durable_open, false);
1284         CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
1285         CHECK_VAL(io.out.persistent_open, false);
1286         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1287         CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
1288         CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
1289         CHECK_VAL(io.out.lease_response.lease_state,
1290                   smb2_util_lease_state("RWH"));
1291         CHECK_VAL(io.out.lease_response.lease_flags, 0);
1292         CHECK_VAL(io.out.lease_response.lease_duration, 0);
1293
1294         _h = io.out.file.handle;
1295         h = &_h;
1296
1297 done:
1298         if (h != NULL) {
1299                 smb2_util_close(tree, *h);
1300         }
1301
1302         smb2_util_unlink(tree, fname);
1303
1304         talloc_free(tree);
1305
1306         talloc_free(mem_ctx);
1307
1308         return ret;
1309 }
1310
1311 /**
1312  * lease_v2 variant of reopen2
1313  * basic test for doing a durable open
1314  * tcp disconnect, reconnect, do a durable reopen (succeeds)
1315  */
1316 bool test_durable_v2_open_reopen2_lease_v2(struct torture_context *tctx,
1317                                            struct smb2_tree *tree)
1318 {
1319         NTSTATUS status;
1320         TALLOC_CTX *mem_ctx = talloc_new(tctx);
1321         char fname[256];
1322         struct smb2_handle _h;
1323         struct smb2_handle *h = NULL;
1324         struct smb2_create io;
1325         struct GUID create_guid = GUID_random();
1326         struct smb2_lease ls;
1327         uint64_t lease_key;
1328         bool ret = true;
1329         struct smbcli_options options;
1330         uint32_t caps;
1331
1332         caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
1333         if (!(caps & SMB2_CAP_LEASING)) {
1334                 torture_skip(tctx, "leases are not supported");
1335         }
1336
1337         options = tree->session->transport->options;
1338
1339         /* Choose a random name in case the state is left a little funky. */
1340         snprintf(fname, 256, "durable_v2_open_reopen2_%s.dat",
1341                  generate_random_str(tctx, 8));
1342
1343         smb2_util_unlink(tree, fname);
1344
1345         lease_key = random();
1346         smb2_lease_v2_create(&io, &ls, false /* dir */, fname,
1347                              lease_key, 0, /* parent lease key */
1348                              smb2_util_lease_state("RWH"), 0 /* lease epoch */);
1349         io.in.durable_open = false;
1350         io.in.durable_open_v2 = true;
1351         io.in.persistent_open = false;
1352         io.in.create_guid = create_guid;
1353         io.in.timeout = UINT32_MAX;
1354
1355         status = smb2_create(tree, mem_ctx, &io);
1356         CHECK_STATUS(status, NT_STATUS_OK);
1357         _h = io.out.file.handle;
1358         h = &_h;
1359         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1360         CHECK_VAL(io.out.durable_open, false);
1361         CHECK_VAL(io.out.durable_open_v2, true);
1362         CHECK_VAL(io.out.persistent_open, false);
1363         CHECK_VAL(io.out.timeout, io.in.timeout);
1364         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1365         CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key);
1366         CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key);
1367
1368         /* disconnect, reconnect and then do durable reopen */
1369         TALLOC_FREE(tree);
1370
1371         if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
1372                 torture_warning(tctx, "couldn't reconnect, bailing\n");
1373                 ret = false;
1374                 goto done;
1375         }
1376
1377         /* a few failure tests: */
1378
1379         /*
1380          * several attempts without lease attached:
1381          * all fail with NT_STATUS_OBJECT_NAME_NOT_FOUND
1382          * irrespective of file name provided
1383          */
1384
1385         ZERO_STRUCT(io);
1386         io.in.fname = "";
1387         io.in.durable_handle_v2 = h;
1388         status = smb2_create(tree, mem_ctx, &io);
1389         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1390
1391         ZERO_STRUCT(io);
1392         io.in.fname = "__non_existing_fname__";
1393         io.in.durable_handle_v2 = h;
1394         status = smb2_create(tree, mem_ctx, &io);
1395         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1396
1397         ZERO_STRUCT(io);
1398         io.in.fname = fname;
1399         io.in.durable_handle_v2 = h;
1400         status = smb2_create(tree, mem_ctx, &io);
1401         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1402
1403         /*
1404          * attempt with lease provided, but
1405          * with a changed lease key. => fails
1406          */
1407         ZERO_STRUCT(io);
1408         io.in.fname = fname;
1409         io.in.durable_open_v2 = false;
1410         io.in.durable_handle_v2 = h;
1411         io.in.create_guid = create_guid;
1412         io.in.lease_request_v2 = &ls;
1413         io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
1414         /* a wrong lease key lets the request fail */
1415         ls.lease_key.data[0]++;
1416
1417         status = smb2_create(tree, mem_ctx, &io);
1418         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1419
1420         /* restore the correct lease key */
1421         ls.lease_key.data[0]--;
1422
1423
1424         /*
1425          * this last failing attempt is almost correct:
1426          * only problem is: we use the wrong filename...
1427          * Note that this gives INVALID_PARAMETER.
1428          * This is different from oplocks!
1429          */
1430         ZERO_STRUCT(io);
1431         io.in.fname = "__non_existing_fname__";
1432         io.in.durable_open_v2 = false;
1433         io.in.durable_handle_v2 = h;
1434         io.in.create_guid = create_guid;
1435         io.in.lease_request_v2 = &ls;
1436         io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
1437
1438         status = smb2_create(tree, mem_ctx, &io);
1439         CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
1440
1441         /*
1442          * Now for a succeeding reconnect:
1443          */
1444
1445         ZERO_STRUCT(io);
1446         io.in.fname = fname;
1447         io.in.durable_open_v2 = false;
1448         io.in.durable_handle_v2 = h;
1449         io.in.create_guid = create_guid;
1450         io.in.lease_request_v2 = &ls;
1451         io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
1452
1453         /* the requested lease state is irrelevant */
1454         ls.lease_state = smb2_util_lease_state("");
1455
1456         h = NULL;
1457
1458         status = smb2_create(tree, mem_ctx, &io);
1459         CHECK_STATUS(status, NT_STATUS_OK);
1460
1461         CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1462         CHECK_VAL(io.out.durable_open, false);
1463         CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
1464         CHECK_VAL(io.out.persistent_open, false);
1465         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1466         CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key);
1467         CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key);
1468         CHECK_VAL(io.out.lease_response_v2.lease_state,
1469                   smb2_util_lease_state("RWH"));
1470         CHECK_VAL(io.out.lease_response_v2.lease_flags, 0);
1471         CHECK_VAL(io.out.lease_response_v2.lease_duration, 0);
1472         _h = io.out.file.handle;
1473         h = &_h;
1474
1475         /* disconnect one more time */
1476         TALLOC_FREE(tree);
1477
1478         if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
1479                 torture_warning(tctx, "couldn't reconnect, bailing\n");
1480                 ret = false;
1481                 goto done;
1482         }
1483
1484         /*
1485          * demonstrate that various parameters are ignored
1486          * in the reconnect
1487          */
1488
1489         ZERO_STRUCT(io);
1490         /*
1491          * These are completely ignored by the server
1492          */
1493         io.in.security_flags = 0x78;
1494         io.in.oplock_level = 0x78;
1495         io.in.impersonation_level = 0x12345678;
1496         io.in.create_flags = 0x12345678;
1497         io.in.reserved = 0x12345678;
1498         io.in.desired_access = 0x12345678;
1499         io.in.file_attributes = 0x12345678;
1500         io.in.share_access = 0x12345678;
1501         io.in.create_disposition = 0x12345678;
1502         io.in.create_options = 0x12345678;
1503         io.in.fname = "__non_existing_fname__";
1504
1505         /*
1506          * only these are checked:
1507          * - io.in.fname
1508          * - io.in.durable_handle_v2,
1509          * - io.in.create_guid
1510          * - io.in.lease_request_v2->lease_key
1511          */
1512
1513         io.in.fname = fname;
1514         io.in.durable_open_v2 = false;
1515         io.in.durable_handle_v2 = h;
1516         io.in.create_guid = create_guid;
1517         io.in.lease_request_v2 = &ls;
1518
1519         /* the requested lease state is irrelevant */
1520         ls.lease_state = smb2_util_lease_state("");
1521
1522         h = NULL;
1523
1524         status = smb2_create(tree, mem_ctx, &io);
1525         CHECK_STATUS(status, NT_STATUS_OK);
1526         CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1527         CHECK_VAL(io.out.durable_open, false);
1528         CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
1529         CHECK_VAL(io.out.persistent_open, false);
1530         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1531         CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key);
1532         CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key);
1533         CHECK_VAL(io.out.lease_response_v2.lease_state,
1534                   smb2_util_lease_state("RWH"));
1535         CHECK_VAL(io.out.lease_response_v2.lease_flags, 0);
1536         CHECK_VAL(io.out.lease_response_v2.lease_duration, 0);
1537
1538         _h = io.out.file.handle;
1539         h = &_h;
1540
1541 done:
1542         if (h != NULL) {
1543                 smb2_util_close(tree, *h);
1544         }
1545
1546         smb2_util_unlink(tree, fname);
1547
1548         talloc_free(tree);
1549
1550         talloc_free(mem_ctx);
1551
1552         return ret;
1553 }
1554
1555 /**
1556  * Test durable request / reconnect with AppInstanceId
1557  */
1558 bool test_durable_v2_open_app_instance(struct torture_context *tctx,
1559                                        struct smb2_tree *tree1,
1560                                        struct smb2_tree *tree2)
1561 {
1562         NTSTATUS status;
1563         TALLOC_CTX *mem_ctx = talloc_new(tctx);
1564         char fname[256];
1565         struct smb2_handle _h1, _h2;
1566         struct smb2_handle *h1 = NULL, *h2 = NULL;
1567         struct smb2_create io1, io2;
1568         bool ret = true;
1569         struct GUID create_guid_1 = GUID_random();
1570         struct GUID create_guid_2 = GUID_random();
1571         struct GUID app_instance_id = GUID_random();
1572
1573         /* Choose a random name in case the state is left a little funky. */
1574         snprintf(fname, 256, "durable_v2_open_app_instance_%s.dat",
1575                  generate_random_str(tctx, 8));
1576
1577         smb2_util_unlink(tree1, fname);
1578
1579         ZERO_STRUCT(break_info);
1580         tree1->session->transport->oplock.handler = torture_oplock_handler;
1581         tree1->session->transport->oplock.private_data = tree1;
1582
1583         smb2_oplock_create_share(&io1, fname,
1584                                  smb2_util_share_access(""),
1585                                  smb2_util_oplock_level("b"));
1586         io1.in.durable_open = false;
1587         io1.in.durable_open_v2 = true;
1588         io1.in.persistent_open = false;
1589         io1.in.create_guid = create_guid_1;
1590         io1.in.app_instance_id = &app_instance_id;
1591         io1.in.timeout = UINT32_MAX;
1592
1593         status = smb2_create(tree1, mem_ctx, &io1);
1594         CHECK_STATUS(status, NT_STATUS_OK);
1595         _h1 = io1.out.file.handle;
1596         h1 = &_h1;
1597         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1598         CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
1599         CHECK_VAL(io1.out.durable_open, false);
1600         CHECK_VAL(io1.out.durable_open_v2, true);
1601         CHECK_VAL(io1.out.persistent_open, false);
1602         CHECK_VAL(io1.out.timeout, io1.in.timeout);
1603
1604         /*
1605          * try to open the file as durable from a second tree with
1606          * a different create guid but the same app_instance_id
1607          * while the first handle is still open.
1608          */
1609
1610         smb2_oplock_create_share(&io2, fname,
1611                                  smb2_util_share_access(""),
1612                                  smb2_util_oplock_level("b"));
1613         io2.in.durable_open = false;
1614         io2.in.durable_open_v2 = true;
1615         io2.in.persistent_open = false;
1616         io2.in.create_guid = create_guid_2;
1617         io2.in.app_instance_id = &app_instance_id;
1618         io2.in.timeout = UINT32_MAX;
1619
1620         status = smb2_create(tree2, mem_ctx, &io2);
1621         CHECK_STATUS(status, NT_STATUS_OK);
1622         _h2 = io2.out.file.handle;
1623         h2 = &_h2;
1624         CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1625         CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
1626         CHECK_VAL(io2.out.durable_open, false);
1627         CHECK_VAL(io2.out.durable_open_v2, true);
1628         CHECK_VAL(io2.out.persistent_open, false);
1629         CHECK_VAL(io2.out.timeout, io2.in.timeout);
1630
1631         CHECK_VAL(break_info.count, 0);
1632
1633         status = smb2_util_close(tree1, *h1);
1634         CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
1635         h1 = NULL;
1636
1637 done:
1638         if (h1 != NULL) {
1639                 smb2_util_close(tree1, *h1);
1640         }
1641         if (h2 != NULL) {
1642                 smb2_util_close(tree2, *h2);
1643         }
1644
1645         smb2_util_unlink(tree2, fname);
1646
1647         talloc_free(tree1);
1648         talloc_free(tree2);
1649
1650         talloc_free(mem_ctx);
1651
1652         return ret;
1653 }
1654
1655
1656 /**
1657  * basic persistent open test.
1658  *
1659  * This test tests durable open with all possible oplock types.
1660  */
1661
1662 struct durable_open_vs_oplock persistent_open_oplock_ca_table[NUM_OPLOCK_OPEN_TESTS] =
1663 {
1664         { "", "", true, true },
1665         { "", "R", true, true },
1666         { "", "W", true, true },
1667         { "", "D", true, true },
1668         { "", "RD", true, true },
1669         { "", "RW", true, true },
1670         { "", "WD", true, true },
1671         { "", "RWD", true, true },
1672
1673         { "s", "", true, true },
1674         { "s", "R", true, true },
1675         { "s", "W", true, true },
1676         { "s", "D", true, true },
1677         { "s", "RD", true, true },
1678         { "s", "RW", true, true },
1679         { "s", "WD", true, true },
1680         { "s", "RWD", true, true },
1681
1682         { "x", "", true, true },
1683         { "x", "R", true, true },
1684         { "x", "W", true, true },
1685         { "x", "D", true, true },
1686         { "x", "RD", true, true },
1687         { "x", "RW", true, true },
1688         { "x", "WD", true, true },
1689         { "x", "RWD", true, true },
1690
1691         { "b", "", true, true },
1692         { "b", "R", true, true },
1693         { "b", "W", true, true },
1694         { "b", "D", true, true },
1695         { "b", "RD", true, true },
1696         { "b", "RW", true, true },
1697         { "b", "WD", true, true },
1698         { "b", "RWD", true, true },
1699 };
1700
1701 bool test_persistent_open_oplock(struct torture_context *tctx,
1702                                  struct smb2_tree *tree)
1703 {
1704         char fname[256];
1705         bool ret = true;
1706         uint32_t share_capabilities;
1707         bool share_is_ca = false;
1708         struct durable_open_vs_oplock *table;
1709
1710         /* Choose a random name in case the state is left a little funky. */
1711         snprintf(fname, 256, "persistent_open_oplock_%s.dat", generate_random_str(tctx, 8));
1712
1713         share_capabilities = smb2cli_tcon_capabilities(tree->smbXcli);
1714         share_is_ca = share_capabilities & SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY;
1715
1716         if (share_is_ca) {
1717                 table = persistent_open_oplock_ca_table;
1718         } else {
1719                 table = durable_open_vs_oplock_table;
1720         }
1721
1722         ret = test_durable_v2_open_oplock_table(tctx, tree, fname,
1723                                                 true, /* request_persistent */
1724                                                 table,
1725                                                 NUM_OPLOCK_OPEN_TESTS);
1726
1727         talloc_free(tree);
1728
1729         return ret;
1730 }
1731
1732 /**
1733  * basic persistent handle open test.
1734  * persistent state should only be granted when requested
1735  * along with a batch oplock or a handle lease.
1736  *
1737  * This test tests persistent open with all valid lease types.
1738  */
1739
1740 struct durable_open_vs_lease persistent_open_lease_ca_table[NUM_LEASE_OPEN_TESTS] =
1741 {
1742         { "", "", true, true },
1743         { "", "R", true, true },
1744         { "", "W", true, true },
1745         { "", "D", true, true },
1746         { "", "RW", true, true },
1747         { "", "RD", true, true },
1748         { "", "WD", true, true },
1749         { "", "RWD", true, true },
1750
1751         { "R", "", true, true },
1752         { "R", "R", true, true },
1753         { "R", "W", true, true },
1754         { "R", "D", true, true },
1755         { "R", "RW", true, true },
1756         { "R", "RD", true, true },
1757         { "R", "DW", true, true },
1758         { "R", "RWD", true, true },
1759
1760         { "RW", "", true, true },
1761         { "RW", "R", true, true },
1762         { "RW", "W", true, true },
1763         { "RW", "D", true, true },
1764         { "RW", "RW", true, true },
1765         { "RW", "RD", true, true },
1766         { "RW", "WD", true, true },
1767         { "RW", "RWD", true, true },
1768
1769         { "RH", "", true, true },
1770         { "RH", "R", true, true },
1771         { "RH", "W", true, true },
1772         { "RH", "D", true, true },
1773         { "RH", "RW", true, true },
1774         { "RH", "RD", true, true },
1775         { "RH", "WD", true, true },
1776         { "RH", "RWD", true, true },
1777
1778         { "RHW", "", true, true },
1779         { "RHW", "R", true, true },
1780         { "RHW", "W", true, true },
1781         { "RHW", "D", true, true },
1782         { "RHW", "RW", true, true },
1783         { "RHW", "RD", true, true },
1784         { "RHW", "WD", true, true },
1785         { "RHW", "RWD", true, true },
1786 };
1787
1788 bool test_persistent_open_lease(struct torture_context *tctx,
1789                                 struct smb2_tree *tree)
1790 {
1791         char fname[256];
1792         bool ret = true;
1793         uint32_t caps;
1794         uint32_t share_capabilities;
1795         bool share_is_ca;
1796         struct durable_open_vs_lease *table;
1797
1798         caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
1799         if (!(caps & SMB2_CAP_LEASING)) {
1800                 torture_skip(tctx, "leases are not supported");
1801         }
1802
1803         /* Choose a random name in case the state is left a little funky. */
1804         snprintf(fname, 256, "persistent_open_lease_%s.dat", generate_random_str(tctx, 8));
1805
1806         share_capabilities = smb2cli_tcon_capabilities(tree->smbXcli);
1807         share_is_ca = share_capabilities & SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY;
1808
1809         if (share_is_ca) {
1810                 table = persistent_open_lease_ca_table;
1811         } else {
1812                 table = durable_open_vs_lease_table;
1813         }
1814
1815         ret = test_durable_v2_open_lease_table(tctx, tree, fname,
1816                                                true, /* request_persistent */
1817                                                table,
1818                                                NUM_LEASE_OPEN_TESTS);
1819
1820         talloc_free(tree);
1821
1822         return ret;
1823 }
1824
1825 struct torture_suite *torture_smb2_durable_v2_open_init(void)
1826 {
1827         struct torture_suite *suite =
1828             torture_suite_create(talloc_autofree_context(), "durable-v2-open");
1829
1830         torture_suite_add_1smb2_test(suite, "create-blob", test_durable_v2_open_create_blob);
1831         torture_suite_add_1smb2_test(suite, "open-oplock", test_durable_v2_open_oplock);
1832         torture_suite_add_1smb2_test(suite, "open-lease", test_durable_v2_open_lease);
1833         torture_suite_add_1smb2_test(suite, "reopen1", test_durable_v2_open_reopen1);
1834         torture_suite_add_1smb2_test(suite, "reopen1a", test_durable_v2_open_reopen1a);
1835         torture_suite_add_1smb2_test(suite, "reopen2", test_durable_v2_open_reopen2);
1836         torture_suite_add_1smb2_test(suite, "reopen2b", test_durable_v2_open_reopen2b);
1837         torture_suite_add_1smb2_test(suite, "reopen2c", test_durable_v2_open_reopen2c);
1838         torture_suite_add_1smb2_test(suite, "reopen2-lease", test_durable_v2_open_reopen2_lease);
1839         torture_suite_add_1smb2_test(suite, "reopen2-lease-v2", test_durable_v2_open_reopen2_lease_v2);
1840         torture_suite_add_2smb2_test(suite, "app-instance", test_durable_v2_open_app_instance);
1841         torture_suite_add_1smb2_test(suite, "persistent-open-oplock", test_persistent_open_oplock);
1842         torture_suite_add_1smb2_test(suite, "persistent-open-lease", test_persistent_open_lease);
1843
1844         suite->description = talloc_strdup(suite, "SMB2-DURABLE-V2-OPEN tests");
1845
1846         return suite;
1847 }