Line data Source code
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.size, 0); \
49 : CHECK_VAL((__io)->out.file_attr, (__attribute)); \
50 : CHECK_VAL((__io)->out.reserved2, 0); \
51 : } while(0)
52 :
53 : static struct {
54 : int count;
55 : struct smb2_close cl;
56 : } break_info;
57 :
58 0 : static void torture_oplock_close_callback(struct smb2_request *req)
59 : {
60 0 : smb2_close_recv(req, &break_info.cl);
61 0 : }
62 :
63 : /* A general oplock break notification handler. This should be used when a
64 : * test expects to break from batch or exclusive to a lower level. */
65 0 : static bool torture_oplock_handler(struct smb2_transport *transport,
66 : const struct smb2_handle *handle,
67 : uint8_t level,
68 : void *private_data)
69 : {
70 0 : struct smb2_tree *tree = private_data;
71 : struct smb2_request *req;
72 :
73 0 : break_info.count++;
74 :
75 0 : ZERO_STRUCT(break_info.cl);
76 0 : break_info.cl.in.file.handle = *handle;
77 :
78 0 : req = smb2_close_send(tree, &break_info.cl);
79 0 : req->async.fn = torture_oplock_close_callback;
80 0 : req->async.private_data = NULL;
81 0 : return true;
82 : }
83 :
84 : /**
85 : * testing various create blob combinations.
86 : */
87 0 : bool test_durable_v2_open_create_blob(struct torture_context *tctx,
88 : struct smb2_tree *tree)
89 : {
90 : NTSTATUS status;
91 0 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
92 : char fname[256];
93 : struct smb2_handle _h;
94 0 : struct smb2_handle *h = NULL;
95 : struct smb2_create io;
96 0 : struct GUID create_guid = GUID_random();
97 0 : bool ret = true;
98 : struct smbcli_options options;
99 :
100 0 : options = tree->session->transport->options;
101 :
102 : /* Choose a random name in case the state is left a little funky. */
103 0 : snprintf(fname, 256, "durable_v2_open_create_blob_%s.dat",
104 : generate_random_str(tctx, 8));
105 :
106 0 : smb2_util_unlink(tree, fname);
107 :
108 0 : smb2_oplock_create_share(&io, fname,
109 : smb2_util_share_access(""),
110 0 : smb2_util_oplock_level("b"));
111 0 : io.in.durable_open = false;
112 0 : io.in.durable_open_v2 = true;
113 0 : io.in.persistent_open = false;
114 0 : io.in.create_guid = create_guid;
115 0 : io.in.timeout = UINT32_MAX;
116 :
117 0 : status = smb2_create(tree, mem_ctx, &io);
118 0 : CHECK_STATUS(status, NT_STATUS_OK);
119 0 : _h = io.out.file.handle;
120 0 : h = &_h;
121 0 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
122 0 : CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
123 0 : CHECK_VAL(io.out.durable_open, false);
124 0 : CHECK_VAL(io.out.durable_open_v2, true);
125 0 : CHECK_VAL(io.out.persistent_open, false);
126 0 : CHECK_VAL(io.out.timeout, 300*1000);
127 :
128 : /* disconnect */
129 0 : TALLOC_FREE(tree);
130 :
131 : /* create a new session (same client_guid) */
132 0 : if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
133 0 : torture_warning(tctx, "couldn't reconnect, bailing\n");
134 0 : ret = false;
135 0 : goto done;
136 : }
137 :
138 : /*
139 : * check invalid combinations of durable handle
140 : * request and reconnect blobs
141 : * See MS-SMB2: 3.3.5.9.12
142 : * Handling the SMB2_CREATE_DURABLE_HANDLE_RECONNECT_V2 Create Context
143 : */
144 0 : ZERO_STRUCT(io);
145 0 : io.in.fname = fname;
146 0 : io.in.durable_handle_v2 = h; /* durable v2 reconnect request */
147 0 : io.in.durable_open = true; /* durable v1 handle request */
148 0 : io.in.create_guid = create_guid;
149 0 : status = smb2_create(tree, mem_ctx, &io);
150 0 : CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
151 :
152 0 : ZERO_STRUCT(io);
153 0 : io.in.fname = fname;
154 0 : io.in.durable_handle = h; /* durable v1 reconnect request */
155 0 : io.in.durable_open_v2 = true; /* durable v2 handle request */
156 0 : io.in.create_guid = create_guid;
157 0 : status = smb2_create(tree, mem_ctx, &io);
158 0 : CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
159 :
160 0 : ZERO_STRUCT(io);
161 0 : io.in.fname = fname;
162 0 : io.in.durable_handle = h; /* durable v1 reconnect request */
163 0 : io.in.durable_handle_v2 = h; /* durable v2 reconnect request */
164 0 : io.in.create_guid = create_guid;
165 0 : status = smb2_create(tree, mem_ctx, &io);
166 0 : CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
167 :
168 0 : ZERO_STRUCT(io);
169 0 : io.in.fname = fname;
170 0 : io.in.durable_handle_v2 = h; /* durable v2 reconnect request */
171 0 : io.in.durable_open_v2 = true; /* durable v2 handle request */
172 0 : io.in.create_guid = create_guid;
173 0 : status = smb2_create(tree, mem_ctx, &io);
174 0 : CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
175 :
176 0 : done:
177 0 : if (h != NULL) {
178 0 : smb2_util_close(tree, *h);
179 : }
180 :
181 0 : smb2_util_unlink(tree, fname);
182 :
183 0 : talloc_free(tree);
184 :
185 0 : talloc_free(mem_ctx);
186 :
187 0 : return ret;
188 : }
189 :
190 :
191 : /**
192 : * basic durable_open test.
193 : * durable state should only be granted when requested
194 : * along with a batch oplock or a handle lease.
195 : *
196 : * This test tests durable open with all possible oplock types.
197 : */
198 :
199 : struct durable_open_vs_oplock {
200 : const char *level;
201 : const char *share_mode;
202 : bool durable;
203 : bool persistent;
204 : };
205 :
206 : #define NUM_OPLOCK_TYPES 4
207 : #define NUM_SHARE_MODES 8
208 : #define NUM_OPLOCK_OPEN_TESTS ( NUM_OPLOCK_TYPES * NUM_SHARE_MODES )
209 : static struct durable_open_vs_oplock durable_open_vs_oplock_table[NUM_OPLOCK_OPEN_TESTS] =
210 : {
211 : { "", "", false, false },
212 : { "", "R", false, false },
213 : { "", "W", false, false },
214 : { "", "D", false, false },
215 : { "", "RD", false, false },
216 : { "", "RW", false, false },
217 : { "", "WD", false, false },
218 : { "", "RWD", false, false },
219 :
220 : { "s", "", false, false },
221 : { "s", "R", false, false },
222 : { "s", "W", false, false },
223 : { "s", "D", false, false },
224 : { "s", "RD", false, false },
225 : { "s", "RW", false, false },
226 : { "s", "WD", false, false },
227 : { "s", "RWD", false, false },
228 :
229 : { "x", "", false, false },
230 : { "x", "R", false, false },
231 : { "x", "W", false, false },
232 : { "x", "D", false, false },
233 : { "x", "RD", false, false },
234 : { "x", "RW", false, false },
235 : { "x", "WD", false, false },
236 : { "x", "RWD", false, false },
237 :
238 : { "b", "", true, false },
239 : { "b", "R", true, false },
240 : { "b", "W", true, false },
241 : { "b", "D", true, false },
242 : { "b", "RD", true, false },
243 : { "b", "RW", true, false },
244 : { "b", "WD", true, false },
245 : { "b", "RWD", true, false },
246 : };
247 :
248 0 : static bool test_one_durable_v2_open_oplock(struct torture_context *tctx,
249 : struct smb2_tree *tree,
250 : const char *fname,
251 : bool request_persistent,
252 : struct durable_open_vs_oplock test)
253 : {
254 : NTSTATUS status;
255 0 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
256 : struct smb2_handle _h;
257 0 : struct smb2_handle *h = NULL;
258 0 : bool ret = true;
259 : struct smb2_create io;
260 :
261 0 : smb2_util_unlink(tree, fname);
262 :
263 0 : smb2_oplock_create_share(&io, fname,
264 : smb2_util_share_access(test.share_mode),
265 0 : smb2_util_oplock_level(test.level));
266 0 : io.in.durable_open = false;
267 0 : io.in.durable_open_v2 = true;
268 0 : io.in.persistent_open = request_persistent;
269 0 : io.in.create_guid = GUID_random();
270 :
271 0 : status = smb2_create(tree, mem_ctx, &io);
272 0 : CHECK_STATUS(status, NT_STATUS_OK);
273 0 : _h = io.out.file.handle;
274 0 : h = &_h;
275 0 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
276 0 : CHECK_VAL(io.out.durable_open, false);
277 0 : CHECK_VAL(io.out.durable_open_v2, test.durable);
278 0 : CHECK_VAL(io.out.persistent_open, test.persistent);
279 0 : CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level(test.level));
280 :
281 0 : done:
282 0 : if (h != NULL) {
283 0 : smb2_util_close(tree, *h);
284 : }
285 0 : smb2_util_unlink(tree, fname);
286 0 : talloc_free(mem_ctx);
287 :
288 0 : return ret;
289 : }
290 :
291 0 : static bool test_durable_v2_open_oplock_table(struct torture_context *tctx,
292 : struct smb2_tree *tree,
293 : const char *fname,
294 : bool request_persistent,
295 : struct durable_open_vs_oplock *table,
296 : uint8_t num_tests)
297 : {
298 0 : bool ret = true;
299 : uint8_t i;
300 :
301 0 : smb2_util_unlink(tree, fname);
302 :
303 0 : for (i = 0; i < num_tests; i++) {
304 0 : ret = test_one_durable_v2_open_oplock(tctx,
305 : tree,
306 : fname,
307 : request_persistent,
308 0 : table[i]);
309 0 : if (ret == false) {
310 0 : goto done;
311 : }
312 : }
313 :
314 0 : done:
315 0 : smb2_util_unlink(tree, fname);
316 :
317 0 : return ret;
318 : }
319 :
320 0 : bool test_durable_v2_open_oplock(struct torture_context *tctx,
321 : struct smb2_tree *tree)
322 : {
323 : bool ret;
324 : char fname[256];
325 :
326 : /* Choose a random name in case the state is left a little funky. */
327 0 : snprintf(fname, 256, "durable_open_oplock_%s.dat",
328 : generate_random_str(tctx, 8));
329 :
330 0 : ret = test_durable_v2_open_oplock_table(tctx, tree, fname,
331 : false, /* request_persistent */
332 : durable_open_vs_oplock_table,
333 : NUM_OPLOCK_OPEN_TESTS);
334 :
335 0 : talloc_free(tree);
336 :
337 0 : return ret;
338 : }
339 :
340 : /**
341 : * basic durable handle open test.
342 : * persistent state should only be granted when requested
343 : * along with a batch oplock or a handle lease.
344 : *
345 : * This test tests persistent open with all valid lease types.
346 : */
347 :
348 : struct durable_open_vs_lease {
349 : const char *type;
350 : const char *share_mode;
351 : bool durable;
352 : bool persistent;
353 : };
354 :
355 : #define NUM_LEASE_TYPES 5
356 : #define NUM_LEASE_OPEN_TESTS ( NUM_LEASE_TYPES * NUM_SHARE_MODES )
357 : static struct durable_open_vs_lease durable_open_vs_lease_table[NUM_LEASE_OPEN_TESTS] =
358 : {
359 : { "", "", false, false },
360 : { "", "R", false, false },
361 : { "", "W", false, false },
362 : { "", "D", false, false },
363 : { "", "RW", false, false },
364 : { "", "RD", false, false },
365 : { "", "WD", false, false },
366 : { "", "RWD", false, false },
367 :
368 : { "R", "", false, false },
369 : { "R", "R", false, false },
370 : { "R", "W", false, false },
371 : { "R", "D", false, false },
372 : { "R", "RW", false, false },
373 : { "R", "RD", false, false },
374 : { "R", "DW", false, false },
375 : { "R", "RWD", false, false },
376 :
377 : { "RW", "", false, false },
378 : { "RW", "R", false, false },
379 : { "RW", "W", false, false },
380 : { "RW", "D", false, false },
381 : { "RW", "RW", false, false },
382 : { "RW", "RD", false, false },
383 : { "RW", "WD", false, false },
384 : { "RW", "RWD", false, false },
385 :
386 : { "RH", "", true, false },
387 : { "RH", "R", true, false },
388 : { "RH", "W", true, false },
389 : { "RH", "D", true, false },
390 : { "RH", "RW", true, false },
391 : { "RH", "RD", true, false },
392 : { "RH", "WD", true, false },
393 : { "RH", "RWD", true, false },
394 :
395 : { "RHW", "", true, false },
396 : { "RHW", "R", true, false },
397 : { "RHW", "W", true, false },
398 : { "RHW", "D", true, false },
399 : { "RHW", "RW", true, false },
400 : { "RHW", "RD", true, false },
401 : { "RHW", "WD", true, false },
402 : { "RHW", "RWD", true, false },
403 : };
404 :
405 0 : static bool test_one_durable_v2_open_lease(struct torture_context *tctx,
406 : struct smb2_tree *tree,
407 : const char *fname,
408 : bool request_persistent,
409 : struct durable_open_vs_lease test)
410 : {
411 : NTSTATUS status;
412 0 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
413 : struct smb2_handle _h;
414 0 : struct smb2_handle *h = NULL;
415 0 : bool ret = true;
416 : struct smb2_create io;
417 : struct smb2_lease ls;
418 : uint64_t lease;
419 :
420 0 : smb2_util_unlink(tree, fname);
421 :
422 0 : lease = random();
423 :
424 0 : smb2_lease_create_share(&io, &ls, false /* dir */, fname,
425 : smb2_util_share_access(test.share_mode),
426 : lease,
427 : smb2_util_lease_state(test.type));
428 0 : io.in.durable_open = false;
429 0 : io.in.durable_open_v2 = true;
430 0 : io.in.persistent_open = request_persistent;
431 0 : io.in.create_guid = GUID_random();
432 :
433 0 : status = smb2_create(tree, mem_ctx, &io);
434 0 : CHECK_STATUS(status, NT_STATUS_OK);
435 0 : _h = io.out.file.handle;
436 0 : h = &_h;
437 0 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
438 0 : CHECK_VAL(io.out.durable_open, false);
439 0 : CHECK_VAL(io.out.durable_open_v2, test.durable);
440 0 : CHECK_VAL(io.out.persistent_open, test.persistent);
441 0 : CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
442 0 : CHECK_VAL(io.out.lease_response.lease_key.data[0], lease);
443 0 : CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease);
444 0 : CHECK_VAL(io.out.lease_response.lease_state,
445 : smb2_util_lease_state(test.type));
446 0 : done:
447 0 : if (h != NULL) {
448 0 : smb2_util_close(tree, *h);
449 : }
450 0 : smb2_util_unlink(tree, fname);
451 0 : talloc_free(mem_ctx);
452 :
453 0 : return ret;
454 : }
455 :
456 0 : static bool test_durable_v2_open_lease_table(struct torture_context *tctx,
457 : struct smb2_tree *tree,
458 : const char *fname,
459 : bool request_persistent,
460 : struct durable_open_vs_lease *table,
461 : uint8_t num_tests)
462 : {
463 0 : bool ret = true;
464 : uint8_t i;
465 :
466 0 : smb2_util_unlink(tree, fname);
467 :
468 0 : for (i = 0; i < num_tests; i++) {
469 0 : ret = test_one_durable_v2_open_lease(tctx,
470 : tree,
471 : fname,
472 : request_persistent,
473 0 : table[i]);
474 0 : if (ret == false) {
475 0 : goto done;
476 : }
477 : }
478 :
479 0 : done:
480 0 : smb2_util_unlink(tree, fname);
481 :
482 0 : return ret;
483 : }
484 :
485 0 : bool test_durable_v2_open_lease(struct torture_context *tctx,
486 : struct smb2_tree *tree)
487 : {
488 : char fname[256];
489 0 : bool ret = true;
490 : uint32_t caps;
491 :
492 0 : caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
493 0 : if (!(caps & SMB2_CAP_LEASING)) {
494 0 : torture_skip(tctx, "leases are not supported");
495 : }
496 :
497 : /* Choose a random name in case the state is left a little funky. */
498 0 : snprintf(fname, 256, "durable_open_lease_%s.dat", generate_random_str(tctx, 8));
499 :
500 0 : ret = test_durable_v2_open_lease_table(tctx, tree, fname,
501 : false, /* request_persistent */
502 : durable_open_vs_lease_table,
503 : NUM_LEASE_OPEN_TESTS);
504 :
505 0 : talloc_free(tree);
506 0 : return ret;
507 : }
508 :
509 : /**
510 : * basic test for doing a durable open
511 : * and do a durable reopen on the same connection
512 : * while the first open is still active (fails)
513 : */
514 0 : bool test_durable_v2_open_reopen1(struct torture_context *tctx,
515 : struct smb2_tree *tree)
516 : {
517 : NTSTATUS status;
518 0 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
519 : char fname[256];
520 : struct smb2_handle _h;
521 0 : struct smb2_handle *h = NULL;
522 : struct smb2_create io;
523 0 : struct GUID create_guid = GUID_random();
524 0 : bool ret = true;
525 :
526 : /* Choose a random name in case the state is left a little funky. */
527 0 : snprintf(fname, 256, "durable_v2_open_reopen1_%s.dat",
528 : generate_random_str(tctx, 8));
529 :
530 0 : smb2_util_unlink(tree, fname);
531 :
532 0 : smb2_oplock_create_share(&io, fname,
533 : smb2_util_share_access(""),
534 0 : smb2_util_oplock_level("b"));
535 0 : io.in.durable_open = false;
536 0 : io.in.durable_open_v2 = true;
537 0 : io.in.persistent_open = false;
538 0 : io.in.create_guid = create_guid;
539 0 : io.in.timeout = UINT32_MAX;
540 :
541 0 : status = smb2_create(tree, mem_ctx, &io);
542 0 : CHECK_STATUS(status, NT_STATUS_OK);
543 0 : _h = io.out.file.handle;
544 0 : h = &_h;
545 0 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
546 0 : CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
547 0 : CHECK_VAL(io.out.durable_open, false);
548 0 : CHECK_VAL(io.out.durable_open_v2, true);
549 0 : CHECK_VAL(io.out.persistent_open, false);
550 0 : CHECK_VAL(io.out.timeout, 300*1000);
551 :
552 : /* try a durable reconnect while the file is still open */
553 0 : ZERO_STRUCT(io);
554 0 : io.in.fname = "";
555 0 : io.in.durable_handle_v2 = h;
556 0 : io.in.create_guid = create_guid;
557 0 : status = smb2_create(tree, mem_ctx, &io);
558 0 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
559 :
560 0 : done:
561 0 : if (h != NULL) {
562 0 : smb2_util_close(tree, *h);
563 : }
564 :
565 0 : smb2_util_unlink(tree, fname);
566 :
567 0 : talloc_free(tree);
568 :
569 0 : talloc_free(mem_ctx);
570 :
571 0 : return ret;
572 : }
573 :
574 : /**
575 : * Basic test for doing a durable open
576 : * and do a session reconnect while the first
577 : * session is still active and the handle is
578 : * still open in the client.
579 : * This closes the original session and a
580 : * durable reconnect on the new session succeeds.
581 : */
582 0 : bool test_durable_v2_open_reopen1a(struct torture_context *tctx,
583 : struct smb2_tree *tree)
584 : {
585 : NTSTATUS status;
586 0 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
587 : char fname[256];
588 : struct smb2_handle _h;
589 0 : struct smb2_handle *h = NULL;
590 : struct smb2_create io;
591 0 : struct GUID create_guid = GUID_random();
592 0 : bool ret = true;
593 0 : struct smb2_tree *tree2 = NULL;
594 0 : struct smb2_tree *tree3 = NULL;
595 : uint64_t previous_session_id;
596 : struct smbcli_options options;
597 : struct GUID orig_client_guid;
598 :
599 0 : options = tree->session->transport->options;
600 0 : orig_client_guid = options.client_guid;
601 :
602 : /* Choose a random name in case the state is left a little funky. */
603 0 : snprintf(fname, 256, "durable_v2_open_reopen1a_%s.dat",
604 : generate_random_str(tctx, 8));
605 :
606 0 : smb2_util_unlink(tree, fname);
607 :
608 0 : smb2_oplock_create_share(&io, fname,
609 : smb2_util_share_access(""),
610 0 : smb2_util_oplock_level("b"));
611 0 : io.in.durable_open = false;
612 0 : io.in.durable_open_v2 = true;
613 0 : io.in.persistent_open = false;
614 0 : io.in.create_guid = create_guid;
615 0 : io.in.timeout = UINT32_MAX;
616 :
617 0 : status = smb2_create(tree, mem_ctx, &io);
618 0 : CHECK_STATUS(status, NT_STATUS_OK);
619 0 : _h = io.out.file.handle;
620 0 : h = &_h;
621 0 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
622 0 : CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
623 0 : CHECK_VAL(io.out.durable_open, false);
624 0 : CHECK_VAL(io.out.durable_open_v2, true);
625 0 : CHECK_VAL(io.out.persistent_open, false);
626 0 : CHECK_VAL(io.out.timeout, 300*1000);
627 :
628 : /*
629 : * a session reconnect on a second tcp connection
630 : */
631 :
632 0 : previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
633 :
634 : /* for oplocks, the client guid can be different: */
635 0 : options.client_guid = GUID_random();
636 :
637 0 : ret = torture_smb2_connection_ext(tctx, previous_session_id,
638 : &options, &tree2);
639 0 : torture_assert_goto(tctx, ret, ret, done, "couldn't reconnect");
640 :
641 : /*
642 : * check that this has deleted the old session
643 : */
644 :
645 0 : ZERO_STRUCT(io);
646 0 : io.in.fname = "";
647 0 : io.in.durable_handle_v2 = h;
648 0 : io.in.create_guid = create_guid;
649 0 : status = smb2_create(tree, mem_ctx, &io);
650 0 : CHECK_STATUS(status, NT_STATUS_USER_SESSION_DELETED);
651 :
652 0 : TALLOC_FREE(tree);
653 :
654 : /*
655 : * but a durable reconnect on the new session succeeds:
656 : */
657 :
658 0 : ZERO_STRUCT(io);
659 0 : io.in.fname = "";
660 0 : io.in.durable_handle_v2 = h;
661 0 : io.in.create_guid = create_guid;
662 0 : status = smb2_create(tree2, mem_ctx, &io);
663 0 : CHECK_STATUS(status, NT_STATUS_OK);
664 0 : CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
665 0 : CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
666 0 : CHECK_VAL(io.out.durable_open, false);
667 0 : CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
668 0 : CHECK_VAL(io.out.persistent_open, false);
669 0 : CHECK_VAL(io.out.timeout, 0);
670 0 : _h = io.out.file.handle;
671 0 : h = &_h;
672 :
673 : /*
674 : * a session reconnect on a second tcp connection
675 : */
676 :
677 0 : previous_session_id = smb2cli_session_current_id(tree2->session->smbXcli);
678 :
679 : /* it works the same with the original guid */
680 0 : options.client_guid = orig_client_guid;
681 :
682 0 : ret = torture_smb2_connection_ext(tctx, previous_session_id,
683 : &options, &tree3);
684 0 : torture_assert_goto(tctx, ret, ret, done, "couldn't reconnect");
685 :
686 : /*
687 : * check that this has deleted the old session
688 : */
689 :
690 0 : ZERO_STRUCT(io);
691 0 : io.in.fname = "";
692 0 : io.in.durable_handle_v2 = h;
693 0 : io.in.create_guid = create_guid;
694 0 : status = smb2_create(tree2, mem_ctx, &io);
695 0 : CHECK_STATUS(status, NT_STATUS_USER_SESSION_DELETED);
696 0 : TALLOC_FREE(tree2);
697 :
698 : /*
699 : * but a durable reconnect on the new session succeeds:
700 : */
701 :
702 0 : ZERO_STRUCT(io);
703 0 : io.in.fname = "";
704 0 : io.in.durable_handle_v2 = h;
705 0 : io.in.create_guid = create_guid;
706 0 : status = smb2_create(tree3, mem_ctx, &io);
707 0 : CHECK_STATUS(status, NT_STATUS_OK);
708 0 : CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
709 0 : CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
710 0 : CHECK_VAL(io.out.durable_open, false);
711 0 : CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
712 0 : CHECK_VAL(io.out.persistent_open, false);
713 0 : CHECK_VAL(io.out.timeout, 0);
714 0 : _h = io.out.file.handle;
715 0 : h = &_h;
716 :
717 0 : done:
718 0 : if (tree == NULL) {
719 0 : tree = tree2;
720 : }
721 :
722 0 : if (tree == NULL) {
723 0 : tree = tree3;
724 : }
725 :
726 0 : if (tree != NULL) {
727 0 : if (h != NULL) {
728 0 : smb2_util_close(tree, *h);
729 : }
730 :
731 0 : smb2_util_unlink(tree, fname);
732 :
733 0 : talloc_free(tree);
734 : }
735 :
736 0 : talloc_free(mem_ctx);
737 :
738 0 : return ret;
739 : }
740 :
741 : /**
742 : * lease variant of reopen1a
743 : *
744 : * Basic test for doing a durable open and doing a session
745 : * reconnect while the first session is still active and the
746 : * handle is still open in the client.
747 : * This closes the original session and a durable reconnect on
748 : * the new session succeeds depending on the client guid:
749 : *
750 : * Durable reconnect on a session with a different client guid fails.
751 : * Durable reconnect on a session with the original client guid succeeds.
752 : */
753 0 : bool test_durable_v2_open_reopen1a_lease(struct torture_context *tctx,
754 : struct smb2_tree *tree)
755 : {
756 : NTSTATUS status;
757 0 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
758 : char fname[256];
759 : struct smb2_handle _h;
760 0 : struct smb2_handle *h = NULL;
761 : struct smb2_create io;
762 0 : struct GUID create_guid = GUID_random();
763 : struct smb2_lease ls;
764 : uint64_t lease_key;
765 0 : bool ret = true;
766 0 : struct smb2_tree *tree2 = NULL;
767 0 : struct smb2_tree *tree3 = NULL;
768 : uint64_t previous_session_id;
769 : struct smbcli_options options;
770 : struct GUID orig_client_guid;
771 :
772 0 : options = tree->session->transport->options;
773 0 : orig_client_guid = options.client_guid;
774 :
775 : /* Choose a random name in case the state is left a little funky. */
776 0 : snprintf(fname, 256, "durable_v2_open_reopen1a_lease_%s.dat",
777 : generate_random_str(tctx, 8));
778 :
779 0 : smb2_util_unlink(tree, fname);
780 :
781 0 : lease_key = random();
782 0 : smb2_lease_create(&io, &ls, false /* dir */, fname,
783 : lease_key, smb2_util_lease_state("RWH"));
784 0 : io.in.durable_open = false;
785 0 : io.in.durable_open_v2 = true;
786 0 : io.in.persistent_open = false;
787 0 : io.in.create_guid = create_guid;
788 0 : io.in.timeout = UINT32_MAX;
789 :
790 0 : status = smb2_create(tree, mem_ctx, &io);
791 0 : CHECK_STATUS(status, NT_STATUS_OK);
792 0 : _h = io.out.file.handle;
793 0 : h = &_h;
794 0 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
795 0 : CHECK_VAL(io.out.durable_open, false);
796 0 : CHECK_VAL(io.out.durable_open_v2, true);
797 0 : CHECK_VAL(io.out.persistent_open, false);
798 0 : CHECK_VAL(io.out.timeout, 300*1000);
799 0 : CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
800 0 : CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
801 0 : CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
802 0 : CHECK_VAL(io.out.lease_response.lease_state,
803 : smb2_util_lease_state("RWH"));
804 0 : CHECK_VAL(io.out.lease_response.lease_flags, 0);
805 0 : CHECK_VAL(io.out.lease_response.lease_duration, 0);
806 :
807 0 : previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
808 :
809 : /*
810 : * a session reconnect on a second tcp connection
811 : * with a different client_guid does not allow
812 : * the durable reconnect.
813 : */
814 :
815 0 : options.client_guid = GUID_random();
816 :
817 0 : ret = torture_smb2_connection_ext(tctx, previous_session_id,
818 : &options, &tree2);
819 0 : torture_assert_goto(tctx, ret, ret, done, "couldn't reconnect");
820 :
821 : /*
822 : * check that this has deleted the old session
823 : */
824 :
825 0 : ZERO_STRUCT(io);
826 0 : io.in.fname = fname;
827 0 : io.in.durable_handle_v2 = h;
828 0 : io.in.create_guid = create_guid;
829 0 : io.in.lease_request = &ls;
830 0 : status = smb2_create(tree, mem_ctx, &io);
831 0 : CHECK_STATUS(status, NT_STATUS_USER_SESSION_DELETED);
832 0 : TALLOC_FREE(tree);
833 :
834 : /*
835 : * but a durable reconnect on the new session with the wrong
836 : * client guid fails
837 : */
838 :
839 0 : ZERO_STRUCT(io);
840 0 : io.in.fname = fname;
841 0 : io.in.durable_handle_v2 = h;
842 0 : io.in.create_guid = create_guid;
843 0 : io.in.lease_request = &ls;
844 0 : status = smb2_create(tree2, mem_ctx, &io);
845 0 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
846 :
847 :
848 : /*
849 : * now a session reconnect on a second tcp connection
850 : * with original client_guid allows the durable reconnect.
851 : */
852 :
853 0 : options.client_guid = orig_client_guid;
854 : //options.client_guid = GUID_random();
855 :
856 0 : ret = torture_smb2_connection_ext(tctx, previous_session_id,
857 : &options, &tree3);
858 0 : torture_assert_goto(tctx, ret, ret, done, "couldn't reconnect");
859 :
860 : /*
861 : * check that this has deleted the old session
862 : */
863 :
864 0 : ZERO_STRUCT(io);
865 0 : io.in.fname = fname;
866 0 : io.in.durable_handle_v2 = h;
867 0 : io.in.create_guid = create_guid;
868 0 : io.in.lease_request = &ls;
869 0 : status = smb2_create(tree2, mem_ctx, &io);
870 0 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
871 0 : TALLOC_FREE(tree2);
872 :
873 : /*
874 : * but a durable reconnect on the new session succeeds:
875 : */
876 :
877 0 : ZERO_STRUCT(io);
878 0 : io.in.fname = fname;
879 0 : io.in.durable_handle_v2 = h;
880 0 : io.in.create_guid = create_guid;
881 0 : io.in.lease_request = &ls;
882 0 : status = smb2_create(tree3, mem_ctx, &io);
883 0 : CHECK_STATUS(status, NT_STATUS_OK);
884 0 : CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
885 0 : CHECK_VAL(io.out.durable_open, false);
886 0 : CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
887 0 : CHECK_VAL(io.out.persistent_open, false);
888 0 : CHECK_VAL(io.out.timeout, 0);
889 0 : CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
890 0 : CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
891 0 : CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
892 0 : CHECK_VAL(io.out.lease_response.lease_state,
893 : smb2_util_lease_state("RWH"));
894 0 : CHECK_VAL(io.out.lease_response.lease_flags, 0);
895 0 : CHECK_VAL(io.out.lease_response.lease_duration, 0);
896 0 : _h = io.out.file.handle;
897 0 : h = &_h;
898 :
899 0 : done:
900 0 : if (tree == NULL) {
901 0 : tree = tree2;
902 : }
903 :
904 0 : if (tree == NULL) {
905 0 : tree = tree3;
906 : }
907 :
908 0 : if (tree != NULL) {
909 0 : if (h != NULL) {
910 0 : smb2_util_close(tree, *h);
911 : }
912 :
913 0 : smb2_util_unlink(tree, fname);
914 :
915 0 : talloc_free(tree);
916 : }
917 :
918 0 : talloc_free(mem_ctx);
919 :
920 0 : return ret;
921 : }
922 :
923 : /**
924 : * basic test for doing a durable open
925 : * tcp disconnect, reconnect, do a durable reopen (succeeds)
926 : */
927 0 : bool test_durable_v2_open_reopen2(struct torture_context *tctx,
928 : struct smb2_tree *tree)
929 : {
930 : NTSTATUS status;
931 0 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
932 : char fname[256];
933 : struct smb2_handle _h;
934 0 : struct smb2_handle *h = NULL;
935 : struct smb2_create io;
936 0 : struct GUID create_guid = GUID_random();
937 0 : struct GUID create_guid_invalid = GUID_random();
938 0 : bool ret = true;
939 :
940 : /* Choose a random name in case the state is left a little funky. */
941 0 : snprintf(fname, 256, "durable_v2_open_reopen2_%s.dat",
942 : generate_random_str(tctx, 8));
943 :
944 0 : smb2_util_unlink(tree, fname);
945 :
946 0 : smb2_oplock_create_share(&io, fname,
947 : smb2_util_share_access(""),
948 0 : smb2_util_oplock_level("b"));
949 0 : io.in.durable_open = false;
950 0 : io.in.durable_open_v2 = true;
951 0 : io.in.persistent_open = false;
952 0 : io.in.create_guid = create_guid;
953 0 : io.in.timeout = UINT32_MAX;
954 :
955 0 : status = smb2_create(tree, mem_ctx, &io);
956 0 : CHECK_STATUS(status, NT_STATUS_OK);
957 0 : _h = io.out.file.handle;
958 0 : h = &_h;
959 0 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
960 0 : CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
961 0 : CHECK_VAL(io.out.durable_open, false);
962 0 : CHECK_VAL(io.out.durable_open_v2, true);
963 0 : CHECK_VAL(io.out.persistent_open, false);
964 0 : CHECK_VAL(io.out.timeout, 300*1000);
965 :
966 : /* disconnect, leaving the durable open */
967 0 : TALLOC_FREE(tree);
968 :
969 0 : if (!torture_smb2_connection(tctx, &tree)) {
970 0 : torture_warning(tctx, "couldn't reconnect, bailing\n");
971 0 : ret = false;
972 0 : goto done;
973 : }
974 :
975 : /*
976 : * first a few failure cases
977 : */
978 :
979 0 : ZERO_STRUCT(io);
980 0 : io.in.fname = "";
981 0 : io.in.durable_handle_v2 = h;
982 0 : status = smb2_create(tree, mem_ctx, &io);
983 0 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
984 :
985 0 : ZERO_STRUCT(io);
986 0 : io.in.fname = "__non_existing_fname__";
987 0 : io.in.durable_handle_v2 = h;
988 0 : status = smb2_create(tree, mem_ctx, &io);
989 0 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
990 :
991 0 : ZERO_STRUCT(io);
992 0 : io.in.fname = fname;
993 0 : io.in.durable_handle_v2 = h;
994 0 : status = smb2_create(tree, mem_ctx, &io);
995 0 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
996 :
997 : /* a non-zero but non-matching create_guid does not change it: */
998 0 : ZERO_STRUCT(io);
999 0 : io.in.fname = fname;
1000 0 : io.in.durable_handle_v2 = h;
1001 0 : io.in.create_guid = create_guid_invalid;
1002 0 : status = smb2_create(tree, mem_ctx, &io);
1003 0 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1004 :
1005 : /*
1006 : * now success:
1007 : * The important difference is that the create_guid is provided.
1008 : */
1009 0 : ZERO_STRUCT(io);
1010 0 : io.in.fname = fname;
1011 0 : io.in.durable_open_v2 = false;
1012 0 : io.in.durable_handle_v2 = h;
1013 0 : io.in.create_guid = create_guid;
1014 0 : h = NULL;
1015 :
1016 0 : status = smb2_create(tree, mem_ctx, &io);
1017 0 : CHECK_STATUS(status, NT_STATUS_OK);
1018 0 : CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1019 0 : CHECK_VAL(io.out.durable_open, false);
1020 0 : CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
1021 0 : CHECK_VAL(io.out.persistent_open, false);
1022 0 : CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
1023 0 : _h = io.out.file.handle;
1024 0 : h = &_h;
1025 :
1026 : /* disconnect one more time */
1027 0 : TALLOC_FREE(tree);
1028 :
1029 0 : if (!torture_smb2_connection(tctx, &tree)) {
1030 0 : torture_warning(tctx, "couldn't reconnect, bailing\n");
1031 0 : ret = false;
1032 0 : goto done;
1033 : }
1034 :
1035 0 : ZERO_STRUCT(io);
1036 : /* These are completely ignored by the server */
1037 0 : io.in.security_flags = 0x78;
1038 0 : io.in.oplock_level = 0x78;
1039 0 : io.in.impersonation_level = 0x12345678;
1040 0 : io.in.create_flags = 0x12345678;
1041 0 : io.in.reserved = 0x12345678;
1042 0 : io.in.desired_access = 0x12345678;
1043 0 : io.in.file_attributes = 0x12345678;
1044 0 : io.in.share_access = 0x12345678;
1045 0 : io.in.create_disposition = 0x12345678;
1046 0 : io.in.create_options = 0x12345678;
1047 0 : io.in.fname = "__non_existing_fname__";
1048 :
1049 : /*
1050 : * only io.in.durable_handle_v2 and
1051 : * io.in.create_guid are checked
1052 : */
1053 0 : io.in.durable_open_v2 = false;
1054 0 : io.in.durable_handle_v2 = h;
1055 0 : io.in.create_guid = create_guid;
1056 0 : h = NULL;
1057 :
1058 0 : status = smb2_create(tree, mem_ctx, &io);
1059 0 : CHECK_STATUS(status, NT_STATUS_OK);
1060 0 : CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1061 0 : CHECK_VAL(io.out.durable_open, false);
1062 0 : CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
1063 0 : CHECK_VAL(io.out.persistent_open, false);
1064 0 : CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
1065 0 : _h = io.out.file.handle;
1066 0 : h = &_h;
1067 :
1068 0 : done:
1069 0 : if (h != NULL) {
1070 0 : smb2_util_close(tree, *h);
1071 : }
1072 :
1073 0 : smb2_util_unlink(tree, fname);
1074 :
1075 0 : talloc_free(tree);
1076 :
1077 0 : talloc_free(mem_ctx);
1078 :
1079 0 : return ret;
1080 : }
1081 :
1082 : /**
1083 : * durable reconnect test:
1084 : * connect with v2, reconnect with v1
1085 : */
1086 0 : bool test_durable_v2_open_reopen2b(struct torture_context *tctx,
1087 : struct smb2_tree *tree)
1088 : {
1089 : NTSTATUS status;
1090 0 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
1091 : char fname[256];
1092 : struct smb2_handle _h;
1093 0 : struct smb2_handle *h = NULL;
1094 : struct smb2_create io;
1095 0 : struct GUID create_guid = GUID_random();
1096 0 : bool ret = true;
1097 : struct smbcli_options options;
1098 :
1099 0 : options = tree->session->transport->options;
1100 :
1101 : /* Choose a random name in case the state is left a little funky. */
1102 0 : snprintf(fname, 256, "durable_v2_open_reopen2b_%s.dat",
1103 : generate_random_str(tctx, 8));
1104 :
1105 0 : smb2_util_unlink(tree, fname);
1106 :
1107 0 : smb2_oplock_create_share(&io, fname,
1108 : smb2_util_share_access(""),
1109 0 : smb2_util_oplock_level("b"));
1110 0 : io.in.durable_open = false;
1111 0 : io.in.durable_open_v2 = true;
1112 0 : io.in.persistent_open = false;
1113 0 : io.in.create_guid = create_guid;
1114 0 : io.in.timeout = UINT32_MAX;
1115 :
1116 0 : status = smb2_create(tree, mem_ctx, &io);
1117 0 : CHECK_STATUS(status, NT_STATUS_OK);
1118 0 : _h = io.out.file.handle;
1119 0 : h = &_h;
1120 0 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1121 0 : CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
1122 0 : CHECK_VAL(io.out.durable_open, false);
1123 0 : CHECK_VAL(io.out.durable_open_v2, true);
1124 0 : CHECK_VAL(io.out.persistent_open, false);
1125 0 : CHECK_VAL(io.out.timeout, 300*1000);
1126 :
1127 : /* disconnect, leaving the durable open */
1128 0 : TALLOC_FREE(tree);
1129 :
1130 0 : if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
1131 0 : torture_warning(tctx, "couldn't reconnect, bailing\n");
1132 0 : ret = false;
1133 0 : goto done;
1134 : }
1135 :
1136 0 : ZERO_STRUCT(io);
1137 0 : io.in.fname = fname;
1138 0 : io.in.durable_handle_v2 = h; /* durable v2 reconnect */
1139 0 : io.in.create_guid = GUID_zero(); /* but zero create GUID */
1140 0 : status = smb2_create(tree, mem_ctx, &io);
1141 0 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1142 :
1143 0 : ZERO_STRUCT(io);
1144 0 : io.in.fname = fname;
1145 0 : io.in.durable_handle = h; /* durable v1 (!) reconnect */
1146 0 : h = NULL;
1147 :
1148 0 : status = smb2_create(tree, mem_ctx, &io);
1149 0 : CHECK_STATUS(status, NT_STATUS_OK);
1150 0 : CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1151 0 : CHECK_VAL(io.out.durable_open, false);
1152 0 : CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
1153 0 : CHECK_VAL(io.out.persistent_open, false);
1154 0 : CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
1155 0 : _h = io.out.file.handle;
1156 0 : h = &_h;
1157 :
1158 0 : done:
1159 0 : if (h != NULL) {
1160 0 : smb2_util_close(tree, *h);
1161 : }
1162 :
1163 0 : smb2_util_unlink(tree, fname);
1164 :
1165 0 : talloc_free(tree);
1166 :
1167 0 : talloc_free(mem_ctx);
1168 :
1169 0 : return ret;
1170 : }
1171 : /**
1172 : * durable reconnect test:
1173 : * connect with v1, reconnect with v2 : fails (no create_guid...)
1174 : */
1175 0 : bool test_durable_v2_open_reopen2c(struct torture_context *tctx,
1176 : struct smb2_tree *tree)
1177 : {
1178 : NTSTATUS status;
1179 0 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
1180 : char fname[256];
1181 : struct smb2_handle _h;
1182 0 : struct smb2_handle *h = NULL;
1183 : struct smb2_create io;
1184 0 : struct GUID create_guid = GUID_random();
1185 0 : bool ret = true;
1186 : struct smbcli_options options;
1187 :
1188 0 : options = tree->session->transport->options;
1189 :
1190 : /* Choose a random name in case the state is left a little funky. */
1191 0 : snprintf(fname, 256, "durable_v2_open_reopen2c_%s.dat",
1192 : generate_random_str(tctx, 8));
1193 :
1194 0 : smb2_util_unlink(tree, fname);
1195 :
1196 0 : smb2_oplock_create_share(&io, fname,
1197 : smb2_util_share_access(""),
1198 0 : smb2_util_oplock_level("b"));
1199 0 : io.in.durable_open = true;
1200 0 : io.in.durable_open_v2 = false;
1201 0 : io.in.persistent_open = false;
1202 0 : io.in.create_guid = create_guid;
1203 0 : io.in.timeout = UINT32_MAX;
1204 :
1205 0 : status = smb2_create(tree, mem_ctx, &io);
1206 0 : CHECK_STATUS(status, NT_STATUS_OK);
1207 0 : _h = io.out.file.handle;
1208 0 : h = &_h;
1209 0 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1210 0 : CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
1211 0 : CHECK_VAL(io.out.durable_open, true);
1212 0 : CHECK_VAL(io.out.durable_open_v2, false);
1213 0 : CHECK_VAL(io.out.persistent_open, false);
1214 0 : CHECK_VAL(io.out.timeout, 0);
1215 :
1216 : /* disconnect, leaving the durable open */
1217 0 : TALLOC_FREE(tree);
1218 :
1219 0 : if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
1220 0 : torture_warning(tctx, "couldn't reconnect, bailing\n");
1221 0 : ret = false;
1222 0 : goto done;
1223 : }
1224 :
1225 0 : ZERO_STRUCT(io);
1226 0 : io.in.fname = fname;
1227 0 : io.in.durable_handle_v2 = h; /* durable v2 reconnect */
1228 0 : io.in.create_guid = create_guid;
1229 0 : status = smb2_create(tree, mem_ctx, &io);
1230 0 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1231 :
1232 0 : done:
1233 0 : if (h != NULL) {
1234 0 : smb2_util_close(tree, *h);
1235 : }
1236 :
1237 0 : smb2_util_unlink(tree, fname);
1238 :
1239 0 : talloc_free(tree);
1240 :
1241 0 : talloc_free(mem_ctx);
1242 :
1243 0 : return ret;
1244 : }
1245 :
1246 : /**
1247 : * lease variant of reopen2
1248 : * basic test for doing a durable open
1249 : * tcp disconnect, reconnect, do a durable reopen (succeeds)
1250 : */
1251 0 : bool test_durable_v2_open_reopen2_lease(struct torture_context *tctx,
1252 : struct smb2_tree *tree)
1253 : {
1254 : NTSTATUS status;
1255 0 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
1256 : char fname[256];
1257 : struct smb2_handle _h;
1258 0 : struct smb2_handle *h = NULL;
1259 : struct smb2_create io;
1260 0 : struct GUID create_guid = GUID_random();
1261 : struct smb2_lease ls;
1262 : uint64_t lease_key;
1263 0 : bool ret = true;
1264 : struct smbcli_options options;
1265 : uint32_t caps;
1266 :
1267 0 : caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
1268 0 : if (!(caps & SMB2_CAP_LEASING)) {
1269 0 : torture_skip(tctx, "leases are not supported");
1270 : }
1271 :
1272 0 : options = tree->session->transport->options;
1273 :
1274 : /* Choose a random name in case the state is left a little funky. */
1275 0 : snprintf(fname, 256, "durable_v2_open_reopen2_%s.dat",
1276 : generate_random_str(tctx, 8));
1277 :
1278 0 : smb2_util_unlink(tree, fname);
1279 :
1280 0 : lease_key = generate_random_u64();
1281 0 : smb2_lease_create(&io, &ls, false /* dir */, fname,
1282 : lease_key, smb2_util_lease_state("RWH"));
1283 0 : io.in.durable_open = false;
1284 0 : io.in.durable_open_v2 = true;
1285 0 : io.in.persistent_open = false;
1286 0 : io.in.create_guid = create_guid;
1287 0 : io.in.timeout = UINT32_MAX;
1288 :
1289 0 : status = smb2_create(tree, mem_ctx, &io);
1290 0 : CHECK_STATUS(status, NT_STATUS_OK);
1291 0 : _h = io.out.file.handle;
1292 0 : h = &_h;
1293 0 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1294 0 : CHECK_VAL(io.out.durable_open, false);
1295 0 : CHECK_VAL(io.out.durable_open_v2, true);
1296 0 : CHECK_VAL(io.out.persistent_open, false);
1297 0 : CHECK_VAL(io.out.timeout, 300*1000);
1298 0 : CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1299 0 : CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
1300 0 : CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
1301 0 : CHECK_VAL(io.out.lease_response.lease_state,
1302 : smb2_util_lease_state("RWH"));
1303 0 : CHECK_VAL(io.out.lease_response.lease_flags, 0);
1304 0 : CHECK_VAL(io.out.lease_response.lease_duration, 0);
1305 :
1306 : /* disconnect, reconnect and then do durable reopen */
1307 0 : TALLOC_FREE(tree);
1308 :
1309 0 : if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
1310 0 : torture_warning(tctx, "couldn't reconnect, bailing\n");
1311 0 : ret = false;
1312 0 : goto done;
1313 : }
1314 :
1315 : /* a few failure tests: */
1316 :
1317 : /*
1318 : * several attempts without lease attached:
1319 : * all fail with NT_STATUS_OBJECT_NAME_NOT_FOUND
1320 : * irrespective of file name provided
1321 : */
1322 :
1323 0 : ZERO_STRUCT(io);
1324 0 : io.in.fname = "";
1325 0 : io.in.durable_handle_v2 = h;
1326 0 : status = smb2_create(tree, mem_ctx, &io);
1327 0 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1328 :
1329 0 : ZERO_STRUCT(io);
1330 0 : io.in.fname = "__non_existing_fname__";
1331 0 : io.in.durable_handle_v2 = h;
1332 0 : status = smb2_create(tree, mem_ctx, &io);
1333 0 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1334 :
1335 0 : ZERO_STRUCT(io);
1336 0 : io.in.fname = fname;
1337 0 : io.in.durable_handle_v2 = h;
1338 0 : status = smb2_create(tree, mem_ctx, &io);
1339 0 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1340 :
1341 : /*
1342 : * attempt with lease provided, but
1343 : * with a changed lease key. => fails
1344 : */
1345 0 : ZERO_STRUCT(io);
1346 0 : io.in.fname = fname;
1347 0 : io.in.durable_open_v2 = false;
1348 0 : io.in.durable_handle_v2 = h;
1349 0 : io.in.create_guid = create_guid;
1350 0 : io.in.lease_request = &ls;
1351 0 : io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
1352 : /* a wrong lease key lets the request fail */
1353 0 : ls.lease_key.data[0]++;
1354 :
1355 0 : status = smb2_create(tree, mem_ctx, &io);
1356 0 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1357 :
1358 : /* restore the correct lease key */
1359 0 : ls.lease_key.data[0]--;
1360 :
1361 : /*
1362 : * this last failing attempt is almost correct:
1363 : * only problem is: we use the wrong filename...
1364 : * Note that this gives INVALID_PARAMETER.
1365 : * This is different from oplocks!
1366 : */
1367 0 : ZERO_STRUCT(io);
1368 0 : io.in.fname = "__non_existing_fname__";
1369 0 : io.in.durable_open_v2 = false;
1370 0 : io.in.durable_handle_v2 = h;
1371 0 : io.in.create_guid = create_guid;
1372 0 : io.in.lease_request = &ls;
1373 0 : io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
1374 :
1375 0 : status = smb2_create(tree, mem_ctx, &io);
1376 0 : CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
1377 :
1378 : /*
1379 : * Now for a succeeding reconnect:
1380 : */
1381 :
1382 0 : ZERO_STRUCT(io);
1383 0 : io.in.fname = fname;
1384 0 : io.in.durable_open_v2 = false;
1385 0 : io.in.durable_handle_v2 = h;
1386 0 : io.in.create_guid = create_guid;
1387 0 : io.in.lease_request = &ls;
1388 0 : io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
1389 :
1390 : /* the requested lease state is irrelevant */
1391 0 : ls.lease_state = smb2_util_lease_state("");
1392 :
1393 0 : h = NULL;
1394 :
1395 0 : status = smb2_create(tree, mem_ctx, &io);
1396 0 : CHECK_STATUS(status, NT_STATUS_OK);
1397 :
1398 0 : CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1399 0 : CHECK_VAL(io.out.durable_open, false);
1400 0 : CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
1401 0 : CHECK_VAL(io.out.persistent_open, false);
1402 0 : CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1403 0 : CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
1404 0 : CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
1405 0 : CHECK_VAL(io.out.lease_response.lease_state,
1406 : smb2_util_lease_state("RWH"));
1407 0 : CHECK_VAL(io.out.lease_response.lease_flags, 0);
1408 0 : CHECK_VAL(io.out.lease_response.lease_duration, 0);
1409 0 : _h = io.out.file.handle;
1410 0 : h = &_h;
1411 :
1412 : /* disconnect one more time */
1413 0 : TALLOC_FREE(tree);
1414 :
1415 0 : if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
1416 0 : torture_warning(tctx, "couldn't reconnect, bailing\n");
1417 0 : ret = false;
1418 0 : goto done;
1419 : }
1420 :
1421 : /*
1422 : * demonstrate that various parameters are ignored
1423 : * in the reconnect
1424 : */
1425 :
1426 0 : ZERO_STRUCT(io);
1427 : /*
1428 : * These are completely ignored by the server
1429 : */
1430 0 : io.in.security_flags = 0x78;
1431 0 : io.in.oplock_level = 0x78;
1432 0 : io.in.impersonation_level = 0x12345678;
1433 0 : io.in.create_flags = 0x12345678;
1434 0 : io.in.reserved = 0x12345678;
1435 0 : io.in.desired_access = 0x12345678;
1436 0 : io.in.file_attributes = 0x12345678;
1437 0 : io.in.share_access = 0x12345678;
1438 0 : io.in.create_disposition = 0x12345678;
1439 0 : io.in.create_options = 0x12345678;
1440 :
1441 : /*
1442 : * only these are checked:
1443 : * - io.in.fname
1444 : * - io.in.durable_handle_v2,
1445 : * - io.in.create_guid
1446 : * - io.in.lease_request->lease_key
1447 : */
1448 :
1449 0 : io.in.fname = fname;
1450 0 : io.in.durable_open_v2 = false;
1451 0 : io.in.durable_handle_v2 = h;
1452 0 : io.in.create_guid = create_guid;
1453 0 : io.in.lease_request = &ls;
1454 :
1455 : /* the requested lease state is irrelevant */
1456 0 : ls.lease_state = smb2_util_lease_state("");
1457 :
1458 0 : h = NULL;
1459 :
1460 0 : status = smb2_create(tree, mem_ctx, &io);
1461 0 : CHECK_STATUS(status, NT_STATUS_OK);
1462 :
1463 0 : CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1464 0 : CHECK_VAL(io.out.durable_open, false);
1465 0 : CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
1466 0 : CHECK_VAL(io.out.persistent_open, false);
1467 0 : CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1468 0 : CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
1469 0 : CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
1470 0 : CHECK_VAL(io.out.lease_response.lease_state,
1471 : smb2_util_lease_state("RWH"));
1472 0 : CHECK_VAL(io.out.lease_response.lease_flags, 0);
1473 0 : CHECK_VAL(io.out.lease_response.lease_duration, 0);
1474 :
1475 0 : _h = io.out.file.handle;
1476 0 : h = &_h;
1477 :
1478 0 : done:
1479 0 : if (h != NULL) {
1480 0 : smb2_util_close(tree, *h);
1481 : }
1482 :
1483 0 : smb2_util_unlink(tree, fname);
1484 :
1485 0 : talloc_free(tree);
1486 :
1487 0 : talloc_free(mem_ctx);
1488 :
1489 0 : return ret;
1490 : }
1491 :
1492 : /**
1493 : * lease_v2 variant of reopen2
1494 : * basic test for doing a durable open
1495 : * tcp disconnect, reconnect, do a durable reopen (succeeds)
1496 : */
1497 0 : bool test_durable_v2_open_reopen2_lease_v2(struct torture_context *tctx,
1498 : struct smb2_tree *tree)
1499 : {
1500 : NTSTATUS status;
1501 0 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
1502 : char fname[256];
1503 : struct smb2_handle _h;
1504 0 : struct smb2_handle *h = NULL;
1505 : struct smb2_create io;
1506 0 : struct GUID create_guid = GUID_random();
1507 : struct smb2_lease ls;
1508 : uint64_t lease_key;
1509 0 : bool ret = true;
1510 : struct smbcli_options options;
1511 : uint32_t caps;
1512 :
1513 0 : caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
1514 0 : if (!(caps & SMB2_CAP_LEASING)) {
1515 0 : torture_skip(tctx, "leases are not supported");
1516 : }
1517 :
1518 0 : options = tree->session->transport->options;
1519 :
1520 0 : smb2_deltree(tree, __func__);
1521 0 : status = torture_smb2_testdir(tree, __func__, &_h);
1522 0 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1523 : "torture_smb2_testdir failed\n");
1524 0 : smb2_util_close(tree, _h);
1525 :
1526 : /* Choose a random name in case the state is left a little funky. */
1527 0 : snprintf(fname, 256, "%s\\durable_v2_open_reopen2_%s.dat",
1528 : __func__, generate_random_str(tctx, 8));
1529 :
1530 0 : smb2_util_unlink(tree, fname);
1531 :
1532 0 : lease_key = random();
1533 0 : smb2_lease_v2_create(&io, &ls, false /* dir */, fname,
1534 : lease_key, 0, /* parent lease key */
1535 : smb2_util_lease_state("RWH"), 0 /* lease epoch */);
1536 0 : io.in.durable_open = false;
1537 0 : io.in.durable_open_v2 = true;
1538 0 : io.in.persistent_open = false;
1539 0 : io.in.create_guid = create_guid;
1540 0 : io.in.timeout = UINT32_MAX;
1541 :
1542 0 : status = smb2_create(tree, mem_ctx, &io);
1543 0 : CHECK_STATUS(status, NT_STATUS_OK);
1544 0 : _h = io.out.file.handle;
1545 0 : h = &_h;
1546 0 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1547 0 : CHECK_VAL(io.out.durable_open, false);
1548 0 : CHECK_VAL(io.out.durable_open_v2, true);
1549 0 : CHECK_VAL(io.out.persistent_open, false);
1550 0 : CHECK_VAL(io.out.timeout, 300*1000);
1551 0 : CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1552 0 : CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key);
1553 0 : CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key);
1554 :
1555 : /* disconnect, reconnect and then do durable reopen */
1556 0 : TALLOC_FREE(tree);
1557 :
1558 0 : if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
1559 0 : torture_warning(tctx, "couldn't reconnect, bailing\n");
1560 0 : ret = false;
1561 0 : goto done;
1562 : }
1563 :
1564 : /* a few failure tests: */
1565 :
1566 : /*
1567 : * several attempts without lease attached:
1568 : * all fail with NT_STATUS_OBJECT_NAME_NOT_FOUND
1569 : * irrespective of file name provided
1570 : */
1571 :
1572 0 : ZERO_STRUCT(io);
1573 0 : io.in.fname = "";
1574 0 : io.in.durable_handle_v2 = h;
1575 0 : status = smb2_create(tree, mem_ctx, &io);
1576 0 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1577 :
1578 0 : ZERO_STRUCT(io);
1579 0 : io.in.fname = "__non_existing_fname__";
1580 0 : io.in.durable_handle_v2 = h;
1581 0 : status = smb2_create(tree, mem_ctx, &io);
1582 0 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1583 :
1584 0 : ZERO_STRUCT(io);
1585 0 : io.in.fname = fname;
1586 0 : io.in.durable_handle_v2 = h;
1587 0 : status = smb2_create(tree, mem_ctx, &io);
1588 0 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1589 :
1590 : /*
1591 : * attempt with lease provided, but
1592 : * with a changed lease key. => fails
1593 : */
1594 0 : ZERO_STRUCT(io);
1595 0 : io.in.fname = fname;
1596 0 : io.in.durable_open_v2 = false;
1597 0 : io.in.durable_handle_v2 = h;
1598 0 : io.in.create_guid = create_guid;
1599 0 : io.in.lease_request_v2 = &ls;
1600 0 : io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
1601 : /* a wrong lease key lets the request fail */
1602 0 : ls.lease_key.data[0]++;
1603 :
1604 0 : status = smb2_create(tree, mem_ctx, &io);
1605 0 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1606 :
1607 : /* restore the correct lease key */
1608 0 : ls.lease_key.data[0]--;
1609 :
1610 :
1611 : /*
1612 : * this last failing attempt is almost correct:
1613 : * only problem is: we use the wrong filename...
1614 : * Note that this gives INVALID_PARAMETER.
1615 : * This is different from oplocks!
1616 : */
1617 0 : ZERO_STRUCT(io);
1618 0 : io.in.fname = "__non_existing_fname__";
1619 0 : io.in.durable_open_v2 = false;
1620 0 : io.in.durable_handle_v2 = h;
1621 0 : io.in.create_guid = create_guid;
1622 0 : io.in.lease_request_v2 = &ls;
1623 0 : io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
1624 :
1625 0 : status = smb2_create(tree, mem_ctx, &io);
1626 0 : CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
1627 :
1628 : /*
1629 : * Now for a succeeding reconnect:
1630 : */
1631 :
1632 0 : ZERO_STRUCT(io);
1633 0 : io.in.fname = fname;
1634 0 : io.in.durable_open_v2 = false;
1635 0 : io.in.durable_handle_v2 = h;
1636 0 : io.in.create_guid = create_guid;
1637 0 : io.in.lease_request_v2 = &ls;
1638 0 : io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
1639 :
1640 : /* the requested lease state is irrelevant */
1641 0 : ls.lease_state = smb2_util_lease_state("");
1642 :
1643 0 : h = NULL;
1644 :
1645 0 : status = smb2_create(tree, mem_ctx, &io);
1646 0 : CHECK_STATUS(status, NT_STATUS_OK);
1647 :
1648 0 : CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1649 0 : CHECK_VAL(io.out.durable_open, false);
1650 0 : CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
1651 0 : CHECK_VAL(io.out.persistent_open, false);
1652 0 : CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1653 0 : CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key);
1654 0 : CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key);
1655 0 : CHECK_VAL(io.out.lease_response_v2.lease_state,
1656 : smb2_util_lease_state("RWH"));
1657 0 : CHECK_VAL(io.out.lease_response_v2.lease_flags, 0);
1658 0 : CHECK_VAL(io.out.lease_response_v2.lease_duration, 0);
1659 0 : _h = io.out.file.handle;
1660 0 : h = &_h;
1661 :
1662 : /* disconnect one more time */
1663 0 : TALLOC_FREE(tree);
1664 :
1665 0 : if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
1666 0 : torture_warning(tctx, "couldn't reconnect, bailing\n");
1667 0 : ret = false;
1668 0 : goto done;
1669 : }
1670 :
1671 : /*
1672 : * demonstrate that various parameters are ignored
1673 : * in the reconnect
1674 : */
1675 :
1676 0 : ZERO_STRUCT(io);
1677 : /*
1678 : * These are completely ignored by the server
1679 : */
1680 0 : io.in.security_flags = 0x78;
1681 0 : io.in.oplock_level = 0x78;
1682 0 : io.in.impersonation_level = 0x12345678;
1683 0 : io.in.create_flags = 0x12345678;
1684 0 : io.in.reserved = 0x12345678;
1685 0 : io.in.desired_access = 0x12345678;
1686 0 : io.in.file_attributes = 0x12345678;
1687 0 : io.in.share_access = 0x12345678;
1688 0 : io.in.create_disposition = 0x12345678;
1689 0 : io.in.create_options = 0x12345678;
1690 0 : io.in.fname = "__non_existing_fname__";
1691 :
1692 : /*
1693 : * only these are checked:
1694 : * - io.in.fname
1695 : * - io.in.durable_handle_v2,
1696 : * - io.in.create_guid
1697 : * - io.in.lease_request_v2->lease_key
1698 : */
1699 :
1700 0 : io.in.fname = fname;
1701 0 : io.in.durable_open_v2 = false;
1702 0 : io.in.durable_handle_v2 = h;
1703 0 : io.in.create_guid = create_guid;
1704 0 : io.in.lease_request_v2 = &ls;
1705 :
1706 : /* the requested lease state is irrelevant */
1707 0 : ls.lease_state = smb2_util_lease_state("");
1708 :
1709 0 : h = NULL;
1710 :
1711 0 : status = smb2_create(tree, mem_ctx, &io);
1712 0 : CHECK_STATUS(status, NT_STATUS_OK);
1713 0 : CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1714 0 : CHECK_VAL(io.out.durable_open, false);
1715 0 : CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
1716 0 : CHECK_VAL(io.out.persistent_open, false);
1717 0 : CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1718 0 : CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key);
1719 0 : CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key);
1720 0 : CHECK_VAL(io.out.lease_response_v2.lease_state,
1721 : smb2_util_lease_state("RWH"));
1722 0 : CHECK_VAL(io.out.lease_response_v2.lease_flags, 0);
1723 0 : CHECK_VAL(io.out.lease_response_v2.lease_duration, 0);
1724 :
1725 0 : _h = io.out.file.handle;
1726 0 : h = &_h;
1727 :
1728 0 : done:
1729 0 : if (h != NULL) {
1730 0 : smb2_util_close(tree, *h);
1731 : }
1732 :
1733 0 : smb2_util_unlink(tree, fname);
1734 0 : smb2_deltree(tree, __func__);
1735 :
1736 0 : talloc_free(tree);
1737 :
1738 0 : talloc_free(mem_ctx);
1739 :
1740 0 : return ret;
1741 : }
1742 :
1743 : /**
1744 : * Test durable request / reconnect with AppInstanceId
1745 : */
1746 0 : bool test_durable_v2_open_app_instance(struct torture_context *tctx,
1747 : struct smb2_tree *tree1,
1748 : struct smb2_tree *tree2)
1749 : {
1750 : NTSTATUS status;
1751 0 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
1752 : char fname[256];
1753 : struct smb2_handle _h1, _h2;
1754 0 : struct smb2_handle *h1 = NULL, *h2 = NULL;
1755 : struct smb2_create io1, io2;
1756 0 : bool ret = true;
1757 0 : struct GUID create_guid_1 = GUID_random();
1758 0 : struct GUID create_guid_2 = GUID_random();
1759 0 : struct GUID app_instance_id = GUID_random();
1760 :
1761 : /* Choose a random name in case the state is left a little funky. */
1762 0 : snprintf(fname, 256, "durable_v2_open_app_instance_%s.dat",
1763 : generate_random_str(tctx, 8));
1764 :
1765 0 : smb2_util_unlink(tree1, fname);
1766 :
1767 0 : ZERO_STRUCT(break_info);
1768 0 : tree1->session->transport->oplock.handler = torture_oplock_handler;
1769 0 : tree1->session->transport->oplock.private_data = tree1;
1770 :
1771 0 : smb2_oplock_create_share(&io1, fname,
1772 : smb2_util_share_access(""),
1773 0 : smb2_util_oplock_level("b"));
1774 0 : io1.in.durable_open = false;
1775 0 : io1.in.durable_open_v2 = true;
1776 0 : io1.in.persistent_open = false;
1777 0 : io1.in.create_guid = create_guid_1;
1778 0 : io1.in.app_instance_id = &app_instance_id;
1779 0 : io1.in.timeout = UINT32_MAX;
1780 :
1781 0 : status = smb2_create(tree1, mem_ctx, &io1);
1782 0 : CHECK_STATUS(status, NT_STATUS_OK);
1783 0 : _h1 = io1.out.file.handle;
1784 0 : h1 = &_h1;
1785 0 : CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1786 0 : CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
1787 0 : CHECK_VAL(io1.out.durable_open, false);
1788 0 : CHECK_VAL(io1.out.durable_open_v2, true);
1789 0 : CHECK_VAL(io1.out.persistent_open, false);
1790 0 : CHECK_VAL(io1.out.timeout, 300*1000);
1791 :
1792 : /*
1793 : * try to open the file as durable from a second tree with
1794 : * a different create guid but the same app_instance_id
1795 : * while the first handle is still open.
1796 : */
1797 :
1798 0 : smb2_oplock_create_share(&io2, fname,
1799 : smb2_util_share_access(""),
1800 0 : smb2_util_oplock_level("b"));
1801 0 : io2.in.durable_open = false;
1802 0 : io2.in.durable_open_v2 = true;
1803 0 : io2.in.persistent_open = false;
1804 0 : io2.in.create_guid = create_guid_2;
1805 0 : io2.in.app_instance_id = &app_instance_id;
1806 0 : io2.in.timeout = UINT32_MAX;
1807 :
1808 0 : status = smb2_create(tree2, mem_ctx, &io2);
1809 0 : CHECK_STATUS(status, NT_STATUS_OK);
1810 0 : _h2 = io2.out.file.handle;
1811 0 : h2 = &_h2;
1812 0 : CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1813 0 : CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
1814 0 : CHECK_VAL(io2.out.durable_open, false);
1815 0 : CHECK_VAL(io2.out.durable_open_v2, true);
1816 0 : CHECK_VAL(io2.out.persistent_open, false);
1817 0 : CHECK_VAL(io2.out.timeout, 300*1000);
1818 :
1819 0 : CHECK_VAL(break_info.count, 0);
1820 :
1821 0 : status = smb2_util_close(tree1, *h1);
1822 0 : CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
1823 0 : h1 = NULL;
1824 :
1825 0 : done:
1826 0 : if (h1 != NULL) {
1827 0 : smb2_util_close(tree1, *h1);
1828 : }
1829 0 : if (h2 != NULL) {
1830 0 : smb2_util_close(tree2, *h2);
1831 : }
1832 :
1833 0 : smb2_util_unlink(tree2, fname);
1834 :
1835 0 : talloc_free(tree1);
1836 0 : talloc_free(tree2);
1837 :
1838 0 : talloc_free(mem_ctx);
1839 :
1840 0 : return ret;
1841 : }
1842 :
1843 :
1844 : /**
1845 : * basic persistent open test.
1846 : *
1847 : * This test tests durable open with all possible oplock types.
1848 : */
1849 :
1850 : struct durable_open_vs_oplock persistent_open_oplock_ca_table[NUM_OPLOCK_OPEN_TESTS] =
1851 : {
1852 : { "", "", true, true },
1853 : { "", "R", true, true },
1854 : { "", "W", true, true },
1855 : { "", "D", true, true },
1856 : { "", "RD", true, true },
1857 : { "", "RW", true, true },
1858 : { "", "WD", true, true },
1859 : { "", "RWD", true, true },
1860 :
1861 : { "s", "", true, true },
1862 : { "s", "R", true, true },
1863 : { "s", "W", true, true },
1864 : { "s", "D", true, true },
1865 : { "s", "RD", true, true },
1866 : { "s", "RW", true, true },
1867 : { "s", "WD", true, true },
1868 : { "s", "RWD", true, true },
1869 :
1870 : { "x", "", true, true },
1871 : { "x", "R", true, true },
1872 : { "x", "W", true, true },
1873 : { "x", "D", true, true },
1874 : { "x", "RD", true, true },
1875 : { "x", "RW", true, true },
1876 : { "x", "WD", true, true },
1877 : { "x", "RWD", true, true },
1878 :
1879 : { "b", "", true, true },
1880 : { "b", "R", true, true },
1881 : { "b", "W", true, true },
1882 : { "b", "D", true, true },
1883 : { "b", "RD", true, true },
1884 : { "b", "RW", true, true },
1885 : { "b", "WD", true, true },
1886 : { "b", "RWD", true, true },
1887 : };
1888 :
1889 0 : bool test_persistent_open_oplock(struct torture_context *tctx,
1890 : struct smb2_tree *tree)
1891 : {
1892 : char fname[256];
1893 0 : bool ret = true;
1894 : uint32_t share_capabilities;
1895 0 : bool share_is_ca = false;
1896 : struct durable_open_vs_oplock *table;
1897 :
1898 : /* Choose a random name in case the state is left a little funky. */
1899 0 : snprintf(fname, 256, "persistent_open_oplock_%s.dat", generate_random_str(tctx, 8));
1900 :
1901 0 : share_capabilities = smb2cli_tcon_capabilities(tree->smbXcli);
1902 0 : share_is_ca = share_capabilities & SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY;
1903 :
1904 0 : if (share_is_ca) {
1905 0 : table = persistent_open_oplock_ca_table;
1906 : } else {
1907 0 : table = durable_open_vs_oplock_table;
1908 : }
1909 :
1910 0 : ret = test_durable_v2_open_oplock_table(tctx, tree, fname,
1911 : true, /* request_persistent */
1912 : table,
1913 : NUM_OPLOCK_OPEN_TESTS);
1914 :
1915 0 : talloc_free(tree);
1916 :
1917 0 : return ret;
1918 : }
1919 :
1920 : /**
1921 : * basic persistent handle open test.
1922 : * persistent state should only be granted when requested
1923 : * along with a batch oplock or a handle lease.
1924 : *
1925 : * This test tests persistent open with all valid lease types.
1926 : */
1927 :
1928 : struct durable_open_vs_lease persistent_open_lease_ca_table[NUM_LEASE_OPEN_TESTS] =
1929 : {
1930 : { "", "", true, true },
1931 : { "", "R", true, true },
1932 : { "", "W", true, true },
1933 : { "", "D", true, true },
1934 : { "", "RW", true, true },
1935 : { "", "RD", true, true },
1936 : { "", "WD", true, true },
1937 : { "", "RWD", true, true },
1938 :
1939 : { "R", "", true, true },
1940 : { "R", "R", true, true },
1941 : { "R", "W", true, true },
1942 : { "R", "D", true, true },
1943 : { "R", "RW", true, true },
1944 : { "R", "RD", true, true },
1945 : { "R", "DW", true, true },
1946 : { "R", "RWD", true, true },
1947 :
1948 : { "RW", "", true, true },
1949 : { "RW", "R", true, true },
1950 : { "RW", "W", true, true },
1951 : { "RW", "D", true, true },
1952 : { "RW", "RW", true, true },
1953 : { "RW", "RD", true, true },
1954 : { "RW", "WD", true, true },
1955 : { "RW", "RWD", true, true },
1956 :
1957 : { "RH", "", true, true },
1958 : { "RH", "R", true, true },
1959 : { "RH", "W", true, true },
1960 : { "RH", "D", true, true },
1961 : { "RH", "RW", true, true },
1962 : { "RH", "RD", true, true },
1963 : { "RH", "WD", true, true },
1964 : { "RH", "RWD", true, true },
1965 :
1966 : { "RHW", "", true, true },
1967 : { "RHW", "R", true, true },
1968 : { "RHW", "W", true, true },
1969 : { "RHW", "D", true, true },
1970 : { "RHW", "RW", true, true },
1971 : { "RHW", "RD", true, true },
1972 : { "RHW", "WD", true, true },
1973 : { "RHW", "RWD", true, true },
1974 : };
1975 :
1976 0 : bool test_persistent_open_lease(struct torture_context *tctx,
1977 : struct smb2_tree *tree)
1978 : {
1979 : char fname[256];
1980 0 : bool ret = true;
1981 : uint32_t caps;
1982 : uint32_t share_capabilities;
1983 : bool share_is_ca;
1984 : struct durable_open_vs_lease *table;
1985 :
1986 0 : caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
1987 0 : if (!(caps & SMB2_CAP_LEASING)) {
1988 0 : torture_skip(tctx, "leases are not supported");
1989 : }
1990 :
1991 : /* Choose a random name in case the state is left a little funky. */
1992 0 : snprintf(fname, 256, "persistent_open_lease_%s.dat", generate_random_str(tctx, 8));
1993 :
1994 0 : share_capabilities = smb2cli_tcon_capabilities(tree->smbXcli);
1995 0 : share_is_ca = share_capabilities & SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY;
1996 :
1997 0 : if (share_is_ca) {
1998 0 : table = persistent_open_lease_ca_table;
1999 : } else {
2000 0 : table = durable_open_vs_lease_table;
2001 : }
2002 :
2003 0 : ret = test_durable_v2_open_lease_table(tctx, tree, fname,
2004 : true, /* request_persistent */
2005 : table,
2006 : NUM_LEASE_OPEN_TESTS);
2007 :
2008 0 : talloc_free(tree);
2009 :
2010 0 : return ret;
2011 : }
2012 :
2013 : /**
2014 : * setfileinfo test for doing a durable open
2015 : * create the file with lease and durable handle,
2016 : * write to it (via set end-of-file), tcp disconnect,
2017 : * reconnect, do a durable reopen - should succeed.
2018 : *
2019 : * BUG: https://bugzilla.samba.org/show_bug.cgi?id=15022
2020 : */
2021 0 : bool test_durable_v2_setinfo(struct torture_context *tctx,
2022 : struct smb2_tree *tree)
2023 : {
2024 : NTSTATUS status;
2025 0 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
2026 : char fname[256];
2027 : struct smb2_handle _h;
2028 0 : struct smb2_handle *h = NULL;
2029 : struct smb2_create io;
2030 : union smb_setfileinfo si;
2031 0 : struct GUID create_guid = GUID_random();
2032 : struct smb2_lease ls;
2033 : uint64_t lease_key;
2034 0 : bool ret = true;
2035 : struct smbcli_options options;
2036 : uint32_t caps;
2037 :
2038 0 : caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
2039 0 : if (!(caps & SMB2_CAP_LEASING)) {
2040 0 : torture_skip(tctx, "leases are not supported");
2041 : }
2042 :
2043 0 : options = tree->session->transport->options;
2044 :
2045 0 : smb2_deltree(tree, __func__);
2046 0 : status = torture_smb2_testdir(tree, __func__, &_h);
2047 0 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2048 : "torture_smb2_testdir failed\n");
2049 0 : smb2_util_close(tree, _h);
2050 :
2051 : /* Choose a random name in case the state is left a little funky. */
2052 0 : snprintf(fname, 256, "%s\\durable_v2_setinfo%s.dat",
2053 : __func__, generate_random_str(tctx, 8));
2054 :
2055 0 : smb2_util_unlink(tree, fname);
2056 :
2057 0 : lease_key = random();
2058 0 : smb2_lease_v2_create(&io, &ls, false /* dir */, fname,
2059 : lease_key, 0, /* parent lease key */
2060 : smb2_util_lease_state("RWH"), 0 /* lease epoch */);
2061 0 : io.in.durable_open = false;
2062 0 : io.in.durable_open_v2 = true;
2063 0 : io.in.persistent_open = false;
2064 0 : io.in.create_guid = create_guid;
2065 0 : io.in.timeout = UINT32_MAX;
2066 :
2067 0 : status = smb2_create(tree, mem_ctx, &io);
2068 0 : CHECK_STATUS(status, NT_STATUS_OK);
2069 0 : _h = io.out.file.handle;
2070 0 : h = &_h;
2071 0 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2072 0 : CHECK_VAL(io.out.durable_open, false);
2073 0 : CHECK_VAL(io.out.durable_open_v2, true);
2074 0 : CHECK_VAL(io.out.persistent_open, false);
2075 0 : CHECK_VAL(io.out.timeout, 300*1000);
2076 0 : CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
2077 0 : CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key);
2078 0 : CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key);
2079 :
2080 : /*
2081 : * Set EOF to 0x100000.
2082 : * Mimics an Apple client test, but most importantly
2083 : * causes the mtime timestamp on disk to be updated.
2084 : */
2085 0 : ZERO_STRUCT(si);
2086 0 : si.generic.level = SMB_SFILEINFO_END_OF_FILE_INFORMATION;
2087 0 : si.generic.in.file.handle = io.out.file.handle;
2088 0 : si.end_of_file_info.in.size = 0x100000;
2089 0 : status = smb2_setinfo_file(tree, &si);
2090 0 : CHECK_STATUS(status, NT_STATUS_OK);
2091 :
2092 : /* disconnect, reconnect and then do durable reopen */
2093 0 : TALLOC_FREE(tree);
2094 :
2095 0 : if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
2096 0 : torture_warning(tctx, "couldn't reconnect, bailing\n");
2097 0 : ret = false;
2098 0 : goto done;
2099 : }
2100 :
2101 : /*
2102 : * Now for a succeeding reconnect:
2103 : */
2104 :
2105 0 : ZERO_STRUCT(io);
2106 0 : io.in.fname = fname;
2107 0 : io.in.durable_open_v2 = false;
2108 0 : io.in.durable_handle_v2 = h;
2109 0 : io.in.create_guid = create_guid;
2110 0 : io.in.lease_request_v2 = &ls;
2111 0 : io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
2112 :
2113 : /* the requested lease state is irrelevant */
2114 0 : ls.lease_state = smb2_util_lease_state("");
2115 :
2116 0 : h = NULL;
2117 :
2118 0 : status = smb2_create(tree, mem_ctx, &io);
2119 0 : CHECK_STATUS(status, NT_STATUS_OK);
2120 :
2121 0 : CHECK_VAL(io.out.create_action, NTCREATEX_ACTION_EXISTED);
2122 0 : CHECK_VAL(io.out.size, 0x100000); \
2123 0 : CHECK_VAL(io.out.durable_open, false);
2124 0 : CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
2125 0 : CHECK_VAL(io.out.persistent_open, false);
2126 0 : CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
2127 0 : CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key);
2128 0 : CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key);
2129 0 : CHECK_VAL(io.out.lease_response_v2.lease_state,
2130 : smb2_util_lease_state("RWH"));
2131 0 : CHECK_VAL(io.out.lease_response_v2.lease_flags, 0);
2132 0 : CHECK_VAL(io.out.lease_response_v2.lease_duration, 0);
2133 0 : _h = io.out.file.handle;
2134 0 : h = &_h;
2135 :
2136 0 : done:
2137 :
2138 0 : if (h != NULL) {
2139 0 : smb2_util_close(tree, *h);
2140 : }
2141 :
2142 0 : smb2_util_unlink(tree, fname);
2143 0 : smb2_deltree(tree, __func__);
2144 :
2145 0 : talloc_free(tree);
2146 :
2147 0 : talloc_free(mem_ctx);
2148 :
2149 0 : return ret;
2150 : }
2151 :
2152 964 : struct torture_suite *torture_smb2_durable_v2_open_init(TALLOC_CTX *ctx)
2153 : {
2154 738 : struct torture_suite *suite =
2155 226 : torture_suite_create(ctx, "durable-v2-open");
2156 :
2157 964 : torture_suite_add_1smb2_test(suite, "create-blob", test_durable_v2_open_create_blob);
2158 964 : torture_suite_add_1smb2_test(suite, "open-oplock", test_durable_v2_open_oplock);
2159 964 : torture_suite_add_1smb2_test(suite, "open-lease", test_durable_v2_open_lease);
2160 964 : torture_suite_add_1smb2_test(suite, "reopen1", test_durable_v2_open_reopen1);
2161 964 : torture_suite_add_1smb2_test(suite, "reopen1a", test_durable_v2_open_reopen1a);
2162 964 : torture_suite_add_1smb2_test(suite, "reopen1a-lease", test_durable_v2_open_reopen1a_lease);
2163 964 : torture_suite_add_1smb2_test(suite, "reopen2", test_durable_v2_open_reopen2);
2164 964 : torture_suite_add_1smb2_test(suite, "reopen2b", test_durable_v2_open_reopen2b);
2165 964 : torture_suite_add_1smb2_test(suite, "reopen2c", test_durable_v2_open_reopen2c);
2166 964 : torture_suite_add_1smb2_test(suite, "reopen2-lease", test_durable_v2_open_reopen2_lease);
2167 964 : torture_suite_add_1smb2_test(suite, "reopen2-lease-v2", test_durable_v2_open_reopen2_lease_v2);
2168 964 : torture_suite_add_1smb2_test(suite, "durable-v2-setinfo", test_durable_v2_setinfo);
2169 964 : torture_suite_add_2smb2_test(suite, "app-instance", test_durable_v2_open_app_instance);
2170 964 : torture_suite_add_1smb2_test(suite, "persistent-open-oplock", test_persistent_open_oplock);
2171 964 : torture_suite_add_1smb2_test(suite, "persistent-open-lease", test_persistent_open_lease);
2172 :
2173 964 : suite->description = talloc_strdup(suite, "SMB2-DURABLE-V2-OPEN tests");
2174 :
2175 964 : return suite;
2176 : }
2177 :
2178 : /**
2179 : * basic test for doing a durable open
2180 : * tcp disconnect, reconnect, do a durable reopen (succeeds)
2181 : */
2182 0 : static bool test_durable_v2_reconnect_delay(struct torture_context *tctx,
2183 : struct smb2_tree *tree,
2184 : struct smb2_tree *tree2)
2185 : {
2186 : NTSTATUS status;
2187 0 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
2188 : char fname[256];
2189 : struct smb2_handle _h;
2190 0 : struct smb2_handle *h = NULL;
2191 : struct smb2_create io;
2192 0 : struct GUID create_guid = GUID_random();
2193 : struct smbcli_options options;
2194 : uint64_t previous_session_id;
2195 0 : uint8_t b = 0;
2196 0 : bool ret = true;
2197 : bool ok;
2198 :
2199 0 : options = tree->session->transport->options;
2200 0 : previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
2201 :
2202 : /* Choose a random name in case the state is left a little funky. */
2203 0 : snprintf(fname,
2204 : sizeof(fname),
2205 : "durable_v2_reconnect_delay_%s.dat",
2206 : generate_random_str(tctx, 8));
2207 :
2208 0 : smb2_util_unlink(tree, fname);
2209 :
2210 0 : smb2_oplock_create_share(&io, fname,
2211 : smb2_util_share_access(""),
2212 0 : smb2_util_oplock_level("b"));
2213 0 : io.in.durable_open = false;
2214 0 : io.in.durable_open_v2 = true;
2215 0 : io.in.persistent_open = false;
2216 0 : io.in.create_guid = create_guid;
2217 0 : io.in.timeout = 0;
2218 :
2219 0 : status = smb2_create(tree, mem_ctx, &io);
2220 0 : CHECK_STATUS(status, NT_STATUS_OK);
2221 :
2222 0 : _h = io.out.file.handle;
2223 0 : h = &_h;
2224 0 : CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
2225 0 : CHECK_VAL(io.out.durable_open_v2, true);
2226 :
2227 0 : status = smb2_util_write(tree, *h, &b, 0, 1);
2228 0 : CHECK_STATUS(status, NT_STATUS_OK);
2229 :
2230 : /* disconnect, leaving the durable open */
2231 0 : TALLOC_FREE(tree);
2232 0 : h = NULL;
2233 :
2234 0 : ok = torture_smb2_connection_ext(tctx, previous_session_id,
2235 : &options, &tree);
2236 0 : torture_assert_goto(tctx, ok, ret, done, "couldn't reconnect, bailing\n");
2237 :
2238 0 : ZERO_STRUCT(io);
2239 0 : io.in.fname = fname;
2240 0 : io.in.durable_open_v2 = false;
2241 0 : io.in.durable_handle_v2 = &_h;
2242 0 : io.in.create_guid = create_guid;
2243 :
2244 0 : status = smb2_create(tree, mem_ctx, &io);
2245 0 : CHECK_STATUS(status, NT_STATUS_OK);
2246 0 : CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
2247 0 : _h = io.out.file.handle;
2248 0 : h = &_h;
2249 :
2250 0 : done:
2251 0 : if (h != NULL) {
2252 0 : smb2_util_close(tree, *h);
2253 : }
2254 0 : TALLOC_FREE(tree);
2255 :
2256 0 : smb2_util_unlink(tree2, fname);
2257 :
2258 0 : TALLOC_FREE(tree2);
2259 :
2260 0 : talloc_free(mem_ctx);
2261 :
2262 0 : return ret;
2263 : }
2264 :
2265 : /**
2266 : * basic test for doing a durable open with 1msec cleanup time
2267 : * tcp disconnect, wait a bit, reconnect, do a durable reopen (fails)
2268 : */
2269 0 : static bool test_durable_v2_reconnect_delay_msec(struct torture_context *tctx,
2270 : struct smb2_tree *tree,
2271 : struct smb2_tree *tree2)
2272 : {
2273 : NTSTATUS status;
2274 0 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
2275 : char fname[256];
2276 : struct smb2_handle _h;
2277 0 : struct smb2_handle *h = NULL;
2278 : struct smb2_create io;
2279 : struct smb2_lease ls;
2280 0 : struct GUID create_guid = GUID_random();
2281 : struct smbcli_options options;
2282 : uint64_t previous_session_id;
2283 0 : uint8_t b = 0;
2284 0 : bool ret = true;
2285 : bool ok;
2286 :
2287 0 : options = tree->session->transport->options;
2288 0 : previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
2289 :
2290 : /* Choose a random name in case the state is left a little funky. */
2291 0 : snprintf(fname,
2292 : sizeof(fname),
2293 : "durable_v2_reconnect_delay_%s.dat",
2294 : generate_random_str(tctx, 8));
2295 :
2296 0 : smb2_util_unlink(tree, fname);
2297 :
2298 0 : smb2_lease_create(
2299 : &io,
2300 : &ls,
2301 : false /* dir */,
2302 : fname,
2303 : generate_random_u64(),
2304 : smb2_util_lease_state("RWH"));
2305 0 : io.in.durable_open = false;
2306 0 : io.in.durable_open_v2 = true;
2307 0 : io.in.persistent_open = false;
2308 0 : io.in.create_guid = create_guid;
2309 0 : io.in.timeout = 1;
2310 :
2311 0 : status = smb2_create(tree, mem_ctx, &io);
2312 0 : CHECK_STATUS(status, NT_STATUS_OK);
2313 :
2314 0 : _h = io.out.file.handle;
2315 0 : h = &_h;
2316 0 : CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
2317 0 : CHECK_VAL(io.out.durable_open_v2, true);
2318 :
2319 0 : status = smb2_util_write(tree, *h, &b, 0, 1);
2320 0 : CHECK_STATUS(status, NT_STATUS_OK);
2321 :
2322 : /* disconnect, leaving the durable open */
2323 0 : TALLOC_FREE(tree);
2324 0 : h = NULL;
2325 :
2326 0 : ok = torture_smb2_connection_ext(tctx, previous_session_id,
2327 : &options, &tree);
2328 0 : torture_assert_goto(tctx, ok, ret, done, "couldn't reconnect, bailing\n");
2329 :
2330 0 : sleep(10);
2331 :
2332 0 : ZERO_STRUCT(io);
2333 0 : io.in.fname = fname;
2334 0 : io.in.durable_open_v2 = false;
2335 0 : io.in.durable_handle_v2 = &_h;
2336 0 : io.in.create_guid = create_guid;
2337 :
2338 0 : status = smb2_create(tree, mem_ctx, &io);
2339 0 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
2340 0 : _h = io.out.file.handle;
2341 0 : h = &_h;
2342 :
2343 0 : done:
2344 0 : if (h != NULL) {
2345 0 : smb2_util_close(tree, *h);
2346 : }
2347 0 : TALLOC_FREE(tree);
2348 :
2349 0 : smb2_util_unlink(tree2, fname);
2350 :
2351 0 : TALLOC_FREE(tree2);
2352 :
2353 0 : talloc_free(mem_ctx);
2354 :
2355 0 : return ret;
2356 : }
2357 :
2358 964 : struct torture_suite *torture_smb2_durable_v2_delay_init(TALLOC_CTX *ctx)
2359 : {
2360 738 : struct torture_suite *suite =
2361 226 : torture_suite_create(ctx, "durable-v2-delay");
2362 :
2363 964 : torture_suite_add_2smb2_test(suite,
2364 : "durable_v2_reconnect_delay",
2365 : test_durable_v2_reconnect_delay);
2366 964 : torture_suite_add_2smb2_test(suite,
2367 : "durable_v2_reconnect_delay_msec",
2368 : test_durable_v2_reconnect_delay_msec);
2369 :
2370 964 : return suite;
2371 : }
|