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 4 : static void torture_oplock_close_callback(struct smb2_request *req)
59 : {
60 4 : smb2_close_recv(req, &break_info.cl);
61 4 : }
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 4 : 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 4 : struct smb2_tree *tree = private_data;
71 0 : struct smb2_request *req;
72 :
73 4 : break_info.count++;
74 :
75 4 : ZERO_STRUCT(break_info.cl);
76 4 : break_info.cl.in.file.handle = *handle;
77 :
78 4 : req = smb2_close_send(tree, &break_info.cl);
79 4 : req->async.fn = torture_oplock_close_callback;
80 4 : req->async.private_data = NULL;
81 4 : return true;
82 : }
83 :
84 : /**
85 : * testing various create blob combinations.
86 : */
87 4 : bool test_durable_v2_open_create_blob(struct torture_context *tctx,
88 : struct smb2_tree *tree)
89 : {
90 0 : NTSTATUS status;
91 4 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
92 0 : char fname[256];
93 0 : struct smb2_handle _h;
94 4 : struct smb2_handle *h = NULL;
95 0 : struct smb2_create io;
96 4 : struct GUID create_guid = GUID_random();
97 4 : bool ret = true;
98 0 : struct smbcli_options options;
99 :
100 4 : options = tree->session->transport->options;
101 :
102 : /* Choose a random name in case the state is left a little funky. */
103 4 : snprintf(fname, 256, "durable_v2_open_create_blob_%s.dat",
104 : generate_random_str(tctx, 8));
105 :
106 4 : smb2_util_unlink(tree, fname);
107 :
108 4 : smb2_oplock_create_share(&io, fname,
109 : smb2_util_share_access(""),
110 4 : smb2_util_oplock_level("b"));
111 4 : io.in.durable_open = false;
112 4 : io.in.durable_open_v2 = true;
113 4 : io.in.persistent_open = false;
114 4 : io.in.create_guid = create_guid;
115 4 : io.in.timeout = UINT32_MAX;
116 :
117 4 : status = smb2_create(tree, mem_ctx, &io);
118 4 : CHECK_STATUS(status, NT_STATUS_OK);
119 4 : _h = io.out.file.handle;
120 4 : h = &_h;
121 4 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
122 4 : CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
123 4 : CHECK_VAL(io.out.durable_open, false);
124 4 : CHECK_VAL(io.out.durable_open_v2, true);
125 4 : CHECK_VAL(io.out.persistent_open, false);
126 4 : CHECK_VAL(io.out.timeout, 300*1000);
127 :
128 : /* disconnect */
129 4 : TALLOC_FREE(tree);
130 :
131 : /* create a new session (same client_guid) */
132 4 : 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 4 : ZERO_STRUCT(io);
145 4 : io.in.fname = fname;
146 4 : io.in.durable_handle_v2 = h; /* durable v2 reconnect request */
147 4 : io.in.durable_open = true; /* durable v1 handle request */
148 4 : io.in.create_guid = create_guid;
149 4 : status = smb2_create(tree, mem_ctx, &io);
150 4 : CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
151 :
152 4 : ZERO_STRUCT(io);
153 4 : io.in.fname = fname;
154 4 : io.in.durable_handle = h; /* durable v1 reconnect request */
155 4 : io.in.durable_open_v2 = true; /* durable v2 handle request */
156 4 : io.in.create_guid = create_guid;
157 4 : status = smb2_create(tree, mem_ctx, &io);
158 4 : CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
159 :
160 4 : ZERO_STRUCT(io);
161 4 : io.in.fname = fname;
162 4 : io.in.durable_handle = h; /* durable v1 reconnect request */
163 4 : io.in.durable_handle_v2 = h; /* durable v2 reconnect request */
164 4 : io.in.create_guid = create_guid;
165 4 : status = smb2_create(tree, mem_ctx, &io);
166 4 : CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
167 :
168 4 : ZERO_STRUCT(io);
169 4 : io.in.fname = fname;
170 4 : io.in.durable_handle_v2 = h; /* durable v2 reconnect request */
171 4 : io.in.durable_open_v2 = true; /* durable v2 handle request */
172 4 : io.in.create_guid = create_guid;
173 4 : status = smb2_create(tree, mem_ctx, &io);
174 4 : CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
175 :
176 4 : done:
177 4 : if (h != NULL) {
178 4 : smb2_util_close(tree, *h);
179 : }
180 :
181 4 : smb2_util_unlink(tree, fname);
182 :
183 4 : talloc_free(tree);
184 :
185 4 : talloc_free(mem_ctx);
186 :
187 4 : 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 256 : 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 0 : NTSTATUS status;
255 256 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
256 0 : struct smb2_handle _h;
257 256 : struct smb2_handle *h = NULL;
258 256 : bool ret = true;
259 0 : struct smb2_create io;
260 :
261 256 : smb2_util_unlink(tree, fname);
262 :
263 256 : smb2_oplock_create_share(&io, fname,
264 : smb2_util_share_access(test.share_mode),
265 256 : smb2_util_oplock_level(test.level));
266 256 : io.in.durable_open = false;
267 256 : io.in.durable_open_v2 = true;
268 256 : io.in.persistent_open = request_persistent;
269 256 : io.in.create_guid = GUID_random();
270 :
271 256 : status = smb2_create(tree, mem_ctx, &io);
272 256 : CHECK_STATUS(status, NT_STATUS_OK);
273 256 : _h = io.out.file.handle;
274 256 : h = &_h;
275 256 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
276 256 : CHECK_VAL(io.out.durable_open, false);
277 256 : CHECK_VAL(io.out.durable_open_v2, test.durable);
278 256 : CHECK_VAL(io.out.persistent_open, test.persistent);
279 256 : CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level(test.level));
280 :
281 256 : done:
282 256 : if (h != NULL) {
283 256 : smb2_util_close(tree, *h);
284 : }
285 256 : smb2_util_unlink(tree, fname);
286 256 : talloc_free(mem_ctx);
287 :
288 256 : return ret;
289 : }
290 :
291 8 : 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 8 : bool ret = true;
299 0 : uint8_t i;
300 :
301 8 : smb2_util_unlink(tree, fname);
302 :
303 264 : for (i = 0; i < num_tests; i++) {
304 256 : ret = test_one_durable_v2_open_oplock(tctx,
305 : tree,
306 : fname,
307 : request_persistent,
308 256 : table[i]);
309 256 : if (ret == false) {
310 0 : goto done;
311 : }
312 : }
313 :
314 8 : done:
315 8 : smb2_util_unlink(tree, fname);
316 :
317 8 : return ret;
318 : }
319 :
320 4 : bool test_durable_v2_open_oplock(struct torture_context *tctx,
321 : struct smb2_tree *tree)
322 : {
323 0 : bool ret;
324 0 : char fname[256];
325 :
326 : /* Choose a random name in case the state is left a little funky. */
327 4 : snprintf(fname, 256, "durable_open_oplock_%s.dat",
328 : generate_random_str(tctx, 8));
329 :
330 4 : 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 4 : talloc_free(tree);
336 :
337 4 : 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 160 : 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 0 : NTSTATUS status;
412 160 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
413 0 : struct smb2_handle _h;
414 160 : struct smb2_handle *h = NULL;
415 160 : bool ret = true;
416 0 : struct smb2_create io;
417 0 : struct smb2_lease ls;
418 0 : uint64_t lease;
419 :
420 160 : smb2_util_unlink(tree, fname);
421 :
422 160 : lease = random();
423 :
424 160 : 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 160 : io.in.durable_open = false;
429 160 : io.in.durable_open_v2 = true;
430 160 : io.in.persistent_open = request_persistent;
431 160 : io.in.create_guid = GUID_random();
432 :
433 160 : status = smb2_create(tree, mem_ctx, &io);
434 160 : CHECK_STATUS(status, NT_STATUS_OK);
435 160 : _h = io.out.file.handle;
436 160 : h = &_h;
437 160 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
438 160 : CHECK_VAL(io.out.durable_open, false);
439 160 : CHECK_VAL(io.out.durable_open_v2, test.durable);
440 160 : CHECK_VAL(io.out.persistent_open, test.persistent);
441 160 : CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
442 160 : CHECK_VAL(io.out.lease_response.lease_key.data[0], lease);
443 160 : CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease);
444 160 : CHECK_VAL(io.out.lease_response.lease_state,
445 : smb2_util_lease_state(test.type));
446 160 : done:
447 160 : if (h != NULL) {
448 160 : smb2_util_close(tree, *h);
449 : }
450 160 : smb2_util_unlink(tree, fname);
451 160 : talloc_free(mem_ctx);
452 :
453 160 : return ret;
454 : }
455 :
456 4 : 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 4 : bool ret = true;
464 0 : uint8_t i;
465 :
466 4 : smb2_util_unlink(tree, fname);
467 :
468 164 : for (i = 0; i < num_tests; i++) {
469 160 : ret = test_one_durable_v2_open_lease(tctx,
470 : tree,
471 : fname,
472 : request_persistent,
473 160 : table[i]);
474 160 : if (ret == false) {
475 0 : goto done;
476 : }
477 : }
478 :
479 4 : done:
480 4 : smb2_util_unlink(tree, fname);
481 :
482 4 : return ret;
483 : }
484 :
485 4 : bool test_durable_v2_open_lease(struct torture_context *tctx,
486 : struct smb2_tree *tree)
487 : {
488 0 : char fname[256];
489 4 : bool ret = true;
490 0 : uint32_t caps;
491 :
492 4 : caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
493 4 : if (!(caps & SMB2_CAP_LEASING)) {
494 2 : torture_skip(tctx, "leases are not supported");
495 : }
496 :
497 : /* Choose a random name in case the state is left a little funky. */
498 2 : snprintf(fname, 256, "durable_open_lease_%s.dat", generate_random_str(tctx, 8));
499 :
500 2 : 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 2 : talloc_free(tree);
506 2 : 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 4 : bool test_durable_v2_open_reopen1(struct torture_context *tctx,
515 : struct smb2_tree *tree)
516 : {
517 0 : NTSTATUS status;
518 4 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
519 0 : char fname[256];
520 0 : struct smb2_handle _h;
521 4 : struct smb2_handle *h = NULL;
522 0 : struct smb2_create io;
523 4 : struct GUID create_guid = GUID_random();
524 4 : bool ret = true;
525 :
526 : /* Choose a random name in case the state is left a little funky. */
527 4 : snprintf(fname, 256, "durable_v2_open_reopen1_%s.dat",
528 : generate_random_str(tctx, 8));
529 :
530 4 : smb2_util_unlink(tree, fname);
531 :
532 4 : smb2_oplock_create_share(&io, fname,
533 : smb2_util_share_access(""),
534 4 : smb2_util_oplock_level("b"));
535 4 : io.in.durable_open = false;
536 4 : io.in.durable_open_v2 = true;
537 4 : io.in.persistent_open = false;
538 4 : io.in.create_guid = create_guid;
539 4 : io.in.timeout = UINT32_MAX;
540 :
541 4 : status = smb2_create(tree, mem_ctx, &io);
542 4 : CHECK_STATUS(status, NT_STATUS_OK);
543 4 : _h = io.out.file.handle;
544 4 : h = &_h;
545 4 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
546 4 : CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
547 4 : CHECK_VAL(io.out.durable_open, false);
548 4 : CHECK_VAL(io.out.durable_open_v2, true);
549 4 : CHECK_VAL(io.out.persistent_open, false);
550 4 : CHECK_VAL(io.out.timeout, 300*1000);
551 :
552 : /* try a durable reconnect while the file is still open */
553 4 : ZERO_STRUCT(io);
554 4 : io.in.fname = "";
555 4 : io.in.durable_handle_v2 = h;
556 4 : io.in.create_guid = create_guid;
557 4 : status = smb2_create(tree, mem_ctx, &io);
558 4 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
559 :
560 4 : done:
561 4 : if (h != NULL) {
562 4 : smb2_util_close(tree, *h);
563 : }
564 :
565 4 : smb2_util_unlink(tree, fname);
566 :
567 4 : talloc_free(tree);
568 :
569 4 : talloc_free(mem_ctx);
570 :
571 4 : 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 4 : bool test_durable_v2_open_reopen1a(struct torture_context *tctx,
583 : struct smb2_tree *tree)
584 : {
585 0 : NTSTATUS status;
586 4 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
587 0 : char fname[256];
588 0 : struct smb2_handle _h;
589 4 : struct smb2_handle *h = NULL;
590 0 : struct smb2_create io;
591 4 : struct GUID create_guid = GUID_random();
592 4 : bool ret = true;
593 4 : struct smb2_tree *tree2 = NULL;
594 4 : struct smb2_tree *tree3 = NULL;
595 0 : uint64_t previous_session_id;
596 0 : struct smbcli_options options;
597 0 : struct GUID orig_client_guid;
598 :
599 4 : options = tree->session->transport->options;
600 4 : orig_client_guid = options.client_guid;
601 :
602 : /* Choose a random name in case the state is left a little funky. */
603 4 : snprintf(fname, 256, "durable_v2_open_reopen1a_%s.dat",
604 : generate_random_str(tctx, 8));
605 :
606 4 : smb2_util_unlink(tree, fname);
607 :
608 4 : smb2_oplock_create_share(&io, fname,
609 : smb2_util_share_access(""),
610 4 : smb2_util_oplock_level("b"));
611 4 : io.in.durable_open = false;
612 4 : io.in.durable_open_v2 = true;
613 4 : io.in.persistent_open = false;
614 4 : io.in.create_guid = create_guid;
615 4 : io.in.timeout = UINT32_MAX;
616 :
617 4 : status = smb2_create(tree, mem_ctx, &io);
618 4 : CHECK_STATUS(status, NT_STATUS_OK);
619 4 : _h = io.out.file.handle;
620 4 : h = &_h;
621 4 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
622 4 : CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
623 4 : CHECK_VAL(io.out.durable_open, false);
624 4 : CHECK_VAL(io.out.durable_open_v2, true);
625 4 : CHECK_VAL(io.out.persistent_open, false);
626 4 : CHECK_VAL(io.out.timeout, 300*1000);
627 :
628 : /*
629 : * a session reconnect on a second tcp connection
630 : */
631 :
632 4 : previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
633 :
634 : /* for oplocks, the client guid can be different: */
635 4 : options.client_guid = GUID_random();
636 :
637 4 : ret = torture_smb2_connection_ext(tctx, previous_session_id,
638 : &options, &tree2);
639 4 : torture_assert_goto(tctx, ret, ret, done, "couldn't reconnect");
640 :
641 : /*
642 : * check that this has deleted the old session
643 : */
644 :
645 4 : ZERO_STRUCT(io);
646 4 : io.in.fname = "";
647 4 : io.in.durable_handle_v2 = h;
648 4 : io.in.create_guid = create_guid;
649 4 : status = smb2_create(tree, mem_ctx, &io);
650 4 : CHECK_STATUS(status, NT_STATUS_USER_SESSION_DELETED);
651 :
652 4 : TALLOC_FREE(tree);
653 :
654 : /*
655 : * but a durable reconnect on the new session succeeds:
656 : */
657 :
658 4 : ZERO_STRUCT(io);
659 4 : io.in.fname = "";
660 4 : io.in.durable_handle_v2 = h;
661 4 : io.in.create_guid = create_guid;
662 4 : status = smb2_create(tree2, mem_ctx, &io);
663 4 : CHECK_STATUS(status, NT_STATUS_OK);
664 4 : CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
665 4 : CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
666 4 : CHECK_VAL(io.out.durable_open, false);
667 4 : CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
668 4 : CHECK_VAL(io.out.persistent_open, false);
669 4 : CHECK_VAL(io.out.timeout, 0);
670 4 : _h = io.out.file.handle;
671 4 : h = &_h;
672 :
673 : /*
674 : * a session reconnect on a second tcp connection
675 : */
676 :
677 4 : previous_session_id = smb2cli_session_current_id(tree2->session->smbXcli);
678 :
679 : /* it works the same with the original guid */
680 4 : options.client_guid = orig_client_guid;
681 :
682 4 : ret = torture_smb2_connection_ext(tctx, previous_session_id,
683 : &options, &tree3);
684 4 : torture_assert_goto(tctx, ret, ret, done, "couldn't reconnect");
685 :
686 : /*
687 : * check that this has deleted the old session
688 : */
689 :
690 4 : ZERO_STRUCT(io);
691 4 : io.in.fname = "";
692 4 : io.in.durable_handle_v2 = h;
693 4 : io.in.create_guid = create_guid;
694 4 : status = smb2_create(tree2, mem_ctx, &io);
695 4 : CHECK_STATUS(status, NT_STATUS_USER_SESSION_DELETED);
696 4 : TALLOC_FREE(tree2);
697 :
698 : /*
699 : * but a durable reconnect on the new session succeeds:
700 : */
701 :
702 4 : ZERO_STRUCT(io);
703 4 : io.in.fname = "";
704 4 : io.in.durable_handle_v2 = h;
705 4 : io.in.create_guid = create_guid;
706 4 : status = smb2_create(tree3, mem_ctx, &io);
707 4 : CHECK_STATUS(status, NT_STATUS_OK);
708 4 : CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
709 4 : CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
710 4 : CHECK_VAL(io.out.durable_open, false);
711 4 : CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
712 4 : CHECK_VAL(io.out.persistent_open, false);
713 4 : CHECK_VAL(io.out.timeout, 0);
714 4 : _h = io.out.file.handle;
715 4 : h = &_h;
716 :
717 4 : done:
718 4 : if (tree == NULL) {
719 4 : tree = tree2;
720 : }
721 :
722 4 : if (tree == NULL) {
723 4 : tree = tree3;
724 : }
725 :
726 4 : if (tree != NULL) {
727 4 : if (h != NULL) {
728 4 : smb2_util_close(tree, *h);
729 : }
730 :
731 4 : smb2_util_unlink(tree, fname);
732 :
733 4 : talloc_free(tree);
734 : }
735 :
736 4 : talloc_free(mem_ctx);
737 :
738 4 : 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 4 : bool test_durable_v2_open_reopen1a_lease(struct torture_context *tctx,
754 : struct smb2_tree *tree)
755 : {
756 0 : NTSTATUS status;
757 4 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
758 0 : char fname[256];
759 0 : struct smb2_handle _h;
760 4 : struct smb2_handle *h = NULL;
761 0 : struct smb2_create io;
762 4 : struct GUID create_guid = GUID_random();
763 0 : struct smb2_lease ls;
764 0 : uint64_t lease_key;
765 4 : bool ret = true;
766 4 : struct smb2_tree *tree2 = NULL;
767 4 : struct smb2_tree *tree3 = NULL;
768 0 : uint64_t previous_session_id;
769 0 : struct smbcli_options options;
770 0 : struct GUID orig_client_guid;
771 :
772 4 : options = tree->session->transport->options;
773 4 : orig_client_guid = options.client_guid;
774 :
775 : /* Choose a random name in case the state is left a little funky. */
776 4 : snprintf(fname, 256, "durable_v2_open_reopen1a_lease_%s.dat",
777 : generate_random_str(tctx, 8));
778 :
779 4 : smb2_util_unlink(tree, fname);
780 :
781 4 : lease_key = random();
782 4 : smb2_lease_create(&io, &ls, false /* dir */, fname,
783 : lease_key, smb2_util_lease_state("RWH"));
784 4 : io.in.durable_open = false;
785 4 : io.in.durable_open_v2 = true;
786 4 : io.in.persistent_open = false;
787 4 : io.in.create_guid = create_guid;
788 4 : io.in.timeout = UINT32_MAX;
789 :
790 4 : status = smb2_create(tree, mem_ctx, &io);
791 4 : CHECK_STATUS(status, NT_STATUS_OK);
792 4 : _h = io.out.file.handle;
793 4 : h = &_h;
794 4 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
795 4 : CHECK_VAL(io.out.durable_open, false);
796 4 : CHECK_VAL(io.out.durable_open_v2, true);
797 4 : CHECK_VAL(io.out.persistent_open, false);
798 4 : CHECK_VAL(io.out.timeout, 300*1000);
799 4 : CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
800 4 : CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
801 4 : CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
802 4 : CHECK_VAL(io.out.lease_response.lease_state,
803 : smb2_util_lease_state("RWH"));
804 4 : CHECK_VAL(io.out.lease_response.lease_flags, 0);
805 4 : CHECK_VAL(io.out.lease_response.lease_duration, 0);
806 :
807 4 : 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 4 : options.client_guid = GUID_random();
816 :
817 4 : ret = torture_smb2_connection_ext(tctx, previous_session_id,
818 : &options, &tree2);
819 4 : torture_assert_goto(tctx, ret, ret, done, "couldn't reconnect");
820 :
821 : /*
822 : * check that this has deleted the old session
823 : */
824 :
825 4 : ZERO_STRUCT(io);
826 4 : io.in.fname = fname;
827 4 : io.in.durable_handle_v2 = h;
828 4 : io.in.create_guid = create_guid;
829 4 : io.in.lease_request = &ls;
830 4 : status = smb2_create(tree, mem_ctx, &io);
831 4 : CHECK_STATUS(status, NT_STATUS_USER_SESSION_DELETED);
832 4 : TALLOC_FREE(tree);
833 :
834 : /*
835 : * but a durable reconnect on the new session with the wrong
836 : * client guid fails
837 : */
838 :
839 4 : ZERO_STRUCT(io);
840 4 : io.in.fname = fname;
841 4 : io.in.durable_handle_v2 = h;
842 4 : io.in.create_guid = create_guid;
843 4 : io.in.lease_request = &ls;
844 4 : status = smb2_create(tree2, mem_ctx, &io);
845 4 : 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 4 : options.client_guid = orig_client_guid;
854 : //options.client_guid = GUID_random();
855 :
856 4 : ret = torture_smb2_connection_ext(tctx, previous_session_id,
857 : &options, &tree3);
858 4 : torture_assert_goto(tctx, ret, ret, done, "couldn't reconnect");
859 :
860 : /*
861 : * check that this has deleted the old session
862 : */
863 :
864 4 : ZERO_STRUCT(io);
865 4 : io.in.fname = fname;
866 4 : io.in.durable_handle_v2 = h;
867 4 : io.in.create_guid = create_guid;
868 4 : io.in.lease_request = &ls;
869 4 : status = smb2_create(tree2, mem_ctx, &io);
870 4 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
871 4 : TALLOC_FREE(tree2);
872 :
873 : /*
874 : * but a durable reconnect on the new session succeeds:
875 : */
876 :
877 4 : ZERO_STRUCT(io);
878 4 : io.in.fname = fname;
879 4 : io.in.durable_handle_v2 = h;
880 4 : io.in.create_guid = create_guid;
881 4 : io.in.lease_request = &ls;
882 4 : status = smb2_create(tree3, mem_ctx, &io);
883 4 : CHECK_STATUS(status, NT_STATUS_OK);
884 2 : CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
885 2 : CHECK_VAL(io.out.durable_open, false);
886 2 : CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
887 2 : CHECK_VAL(io.out.persistent_open, false);
888 2 : CHECK_VAL(io.out.timeout, 0);
889 2 : CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
890 2 : CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
891 2 : CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
892 2 : CHECK_VAL(io.out.lease_response.lease_state,
893 : smb2_util_lease_state("RWH"));
894 2 : CHECK_VAL(io.out.lease_response.lease_flags, 0);
895 2 : CHECK_VAL(io.out.lease_response.lease_duration, 0);
896 2 : _h = io.out.file.handle;
897 2 : h = &_h;
898 :
899 4 : done:
900 4 : if (tree == NULL) {
901 4 : tree = tree2;
902 : }
903 :
904 4 : if (tree == NULL) {
905 4 : tree = tree3;
906 : }
907 :
908 4 : if (tree != NULL) {
909 4 : if (h != NULL) {
910 4 : smb2_util_close(tree, *h);
911 : }
912 :
913 4 : smb2_util_unlink(tree, fname);
914 :
915 4 : talloc_free(tree);
916 : }
917 :
918 4 : talloc_free(mem_ctx);
919 :
920 4 : return ret;
921 : }
922 :
923 : /**
924 : * basic test for doing a durable open
925 : * tcp disconnect, reconnect, do a durable reopen (succeeds)
926 : */
927 4 : bool test_durable_v2_open_reopen2(struct torture_context *tctx,
928 : struct smb2_tree *tree)
929 : {
930 0 : NTSTATUS status;
931 4 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
932 0 : char fname[256];
933 0 : struct smb2_handle _h;
934 4 : struct smb2_handle *h = NULL;
935 0 : struct smb2_create io;
936 4 : struct GUID create_guid = GUID_random();
937 4 : struct GUID create_guid_invalid = GUID_random();
938 4 : bool ret = true;
939 :
940 : /* Choose a random name in case the state is left a little funky. */
941 4 : snprintf(fname, 256, "durable_v2_open_reopen2_%s.dat",
942 : generate_random_str(tctx, 8));
943 :
944 4 : smb2_util_unlink(tree, fname);
945 :
946 4 : smb2_oplock_create_share(&io, fname,
947 : smb2_util_share_access(""),
948 4 : smb2_util_oplock_level("b"));
949 4 : io.in.durable_open = false;
950 4 : io.in.durable_open_v2 = true;
951 4 : io.in.persistent_open = false;
952 4 : io.in.create_guid = create_guid;
953 4 : io.in.timeout = UINT32_MAX;
954 :
955 4 : status = smb2_create(tree, mem_ctx, &io);
956 4 : CHECK_STATUS(status, NT_STATUS_OK);
957 4 : _h = io.out.file.handle;
958 4 : h = &_h;
959 4 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
960 4 : CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
961 4 : CHECK_VAL(io.out.durable_open, false);
962 4 : CHECK_VAL(io.out.durable_open_v2, true);
963 4 : CHECK_VAL(io.out.persistent_open, false);
964 4 : CHECK_VAL(io.out.timeout, 300*1000);
965 :
966 : /* disconnect, leaving the durable open */
967 4 : TALLOC_FREE(tree);
968 :
969 4 : 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 4 : ZERO_STRUCT(io);
980 4 : io.in.fname = "";
981 4 : io.in.durable_handle_v2 = h;
982 4 : status = smb2_create(tree, mem_ctx, &io);
983 4 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
984 :
985 4 : ZERO_STRUCT(io);
986 4 : io.in.fname = "__non_existing_fname__";
987 4 : io.in.durable_handle_v2 = h;
988 4 : status = smb2_create(tree, mem_ctx, &io);
989 4 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
990 :
991 4 : ZERO_STRUCT(io);
992 4 : io.in.fname = fname;
993 4 : io.in.durable_handle_v2 = h;
994 4 : status = smb2_create(tree, mem_ctx, &io);
995 4 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
996 :
997 : /* a non-zero but non-matching create_guid does not change it: */
998 4 : ZERO_STRUCT(io);
999 4 : io.in.fname = fname;
1000 4 : io.in.durable_handle_v2 = h;
1001 4 : io.in.create_guid = create_guid_invalid;
1002 4 : status = smb2_create(tree, mem_ctx, &io);
1003 4 : 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 4 : ZERO_STRUCT(io);
1010 4 : io.in.fname = fname;
1011 4 : io.in.durable_open_v2 = false;
1012 4 : io.in.durable_handle_v2 = h;
1013 4 : io.in.create_guid = create_guid;
1014 4 : h = NULL;
1015 :
1016 4 : status = smb2_create(tree, mem_ctx, &io);
1017 4 : CHECK_STATUS(status, NT_STATUS_OK);
1018 4 : CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1019 4 : CHECK_VAL(io.out.durable_open, false);
1020 4 : CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
1021 4 : CHECK_VAL(io.out.persistent_open, false);
1022 4 : CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
1023 4 : _h = io.out.file.handle;
1024 4 : h = &_h;
1025 :
1026 : /* disconnect one more time */
1027 4 : TALLOC_FREE(tree);
1028 :
1029 4 : 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 4 : ZERO_STRUCT(io);
1036 : /* These are completely ignored by the server */
1037 4 : io.in.security_flags = 0x78;
1038 4 : io.in.oplock_level = 0x78;
1039 4 : io.in.impersonation_level = 0x12345678;
1040 4 : io.in.create_flags = 0x12345678;
1041 4 : io.in.reserved = 0x12345678;
1042 4 : io.in.desired_access = 0x12345678;
1043 4 : io.in.file_attributes = 0x12345678;
1044 4 : io.in.share_access = 0x12345678;
1045 4 : io.in.create_disposition = 0x12345678;
1046 4 : io.in.create_options = 0x12345678;
1047 4 : 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 4 : io.in.durable_open_v2 = false;
1054 4 : io.in.durable_handle_v2 = h;
1055 4 : io.in.create_guid = create_guid;
1056 4 : h = NULL;
1057 :
1058 4 : status = smb2_create(tree, mem_ctx, &io);
1059 4 : CHECK_STATUS(status, NT_STATUS_OK);
1060 4 : CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1061 4 : CHECK_VAL(io.out.durable_open, false);
1062 4 : CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
1063 4 : CHECK_VAL(io.out.persistent_open, false);
1064 4 : CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
1065 4 : _h = io.out.file.handle;
1066 4 : h = &_h;
1067 :
1068 4 : done:
1069 4 : if (h != NULL) {
1070 4 : smb2_util_close(tree, *h);
1071 : }
1072 :
1073 4 : smb2_util_unlink(tree, fname);
1074 :
1075 4 : talloc_free(tree);
1076 :
1077 4 : talloc_free(mem_ctx);
1078 :
1079 4 : return ret;
1080 : }
1081 :
1082 : /**
1083 : * durable reconnect test:
1084 : * connect with v2, reconnect with v1
1085 : */
1086 4 : bool test_durable_v2_open_reopen2b(struct torture_context *tctx,
1087 : struct smb2_tree *tree)
1088 : {
1089 0 : NTSTATUS status;
1090 4 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
1091 0 : char fname[256];
1092 0 : struct smb2_handle _h;
1093 4 : struct smb2_handle *h = NULL;
1094 0 : struct smb2_create io;
1095 4 : struct GUID create_guid = GUID_random();
1096 4 : bool ret = true;
1097 0 : struct smbcli_options options;
1098 :
1099 4 : options = tree->session->transport->options;
1100 :
1101 : /* Choose a random name in case the state is left a little funky. */
1102 4 : snprintf(fname, 256, "durable_v2_open_reopen2b_%s.dat",
1103 : generate_random_str(tctx, 8));
1104 :
1105 4 : smb2_util_unlink(tree, fname);
1106 :
1107 4 : smb2_oplock_create_share(&io, fname,
1108 : smb2_util_share_access(""),
1109 4 : smb2_util_oplock_level("b"));
1110 4 : io.in.durable_open = false;
1111 4 : io.in.durable_open_v2 = true;
1112 4 : io.in.persistent_open = false;
1113 4 : io.in.create_guid = create_guid;
1114 4 : io.in.timeout = UINT32_MAX;
1115 :
1116 4 : status = smb2_create(tree, mem_ctx, &io);
1117 4 : CHECK_STATUS(status, NT_STATUS_OK);
1118 4 : _h = io.out.file.handle;
1119 4 : h = &_h;
1120 4 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1121 4 : CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
1122 4 : CHECK_VAL(io.out.durable_open, false);
1123 4 : CHECK_VAL(io.out.durable_open_v2, true);
1124 4 : CHECK_VAL(io.out.persistent_open, false);
1125 4 : CHECK_VAL(io.out.timeout, 300*1000);
1126 :
1127 : /* disconnect, leaving the durable open */
1128 4 : TALLOC_FREE(tree);
1129 :
1130 4 : 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 4 : ZERO_STRUCT(io);
1137 4 : io.in.fname = fname;
1138 4 : io.in.durable_handle_v2 = h; /* durable v2 reconnect */
1139 4 : io.in.create_guid = GUID_zero(); /* but zero create GUID */
1140 4 : status = smb2_create(tree, mem_ctx, &io);
1141 4 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1142 :
1143 4 : ZERO_STRUCT(io);
1144 4 : io.in.fname = fname;
1145 4 : io.in.durable_handle = h; /* durable v1 (!) reconnect */
1146 4 : h = NULL;
1147 :
1148 4 : status = smb2_create(tree, mem_ctx, &io);
1149 4 : CHECK_STATUS(status, NT_STATUS_OK);
1150 4 : CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1151 4 : CHECK_VAL(io.out.durable_open, false);
1152 4 : CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
1153 4 : CHECK_VAL(io.out.persistent_open, false);
1154 4 : CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
1155 4 : _h = io.out.file.handle;
1156 4 : h = &_h;
1157 :
1158 4 : done:
1159 4 : if (h != NULL) {
1160 4 : smb2_util_close(tree, *h);
1161 : }
1162 :
1163 4 : smb2_util_unlink(tree, fname);
1164 :
1165 4 : talloc_free(tree);
1166 :
1167 4 : talloc_free(mem_ctx);
1168 :
1169 4 : return ret;
1170 : }
1171 : /**
1172 : * durable reconnect test:
1173 : * connect with v1, reconnect with v2 : fails (no create_guid...)
1174 : */
1175 4 : bool test_durable_v2_open_reopen2c(struct torture_context *tctx,
1176 : struct smb2_tree *tree)
1177 : {
1178 0 : NTSTATUS status;
1179 4 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
1180 0 : char fname[256];
1181 0 : struct smb2_handle _h;
1182 4 : struct smb2_handle *h = NULL;
1183 0 : struct smb2_create io;
1184 4 : struct GUID create_guid = GUID_random();
1185 4 : bool ret = true;
1186 0 : struct smbcli_options options;
1187 :
1188 4 : options = tree->session->transport->options;
1189 :
1190 : /* Choose a random name in case the state is left a little funky. */
1191 4 : snprintf(fname, 256, "durable_v2_open_reopen2c_%s.dat",
1192 : generate_random_str(tctx, 8));
1193 :
1194 4 : smb2_util_unlink(tree, fname);
1195 :
1196 4 : smb2_oplock_create_share(&io, fname,
1197 : smb2_util_share_access(""),
1198 4 : smb2_util_oplock_level("b"));
1199 4 : io.in.durable_open = true;
1200 4 : io.in.durable_open_v2 = false;
1201 4 : io.in.persistent_open = false;
1202 4 : io.in.create_guid = create_guid;
1203 4 : io.in.timeout = UINT32_MAX;
1204 :
1205 4 : status = smb2_create(tree, mem_ctx, &io);
1206 4 : CHECK_STATUS(status, NT_STATUS_OK);
1207 4 : _h = io.out.file.handle;
1208 4 : h = &_h;
1209 4 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1210 4 : CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
1211 4 : CHECK_VAL(io.out.durable_open, true);
1212 4 : CHECK_VAL(io.out.durable_open_v2, false);
1213 4 : CHECK_VAL(io.out.persistent_open, false);
1214 4 : CHECK_VAL(io.out.timeout, 0);
1215 :
1216 : /* disconnect, leaving the durable open */
1217 4 : TALLOC_FREE(tree);
1218 :
1219 4 : 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 4 : ZERO_STRUCT(io);
1226 4 : io.in.fname = fname;
1227 4 : io.in.durable_handle_v2 = h; /* durable v2 reconnect */
1228 4 : io.in.create_guid = create_guid;
1229 4 : status = smb2_create(tree, mem_ctx, &io);
1230 4 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1231 :
1232 4 : done:
1233 4 : if (h != NULL) {
1234 4 : smb2_util_close(tree, *h);
1235 : }
1236 :
1237 4 : smb2_util_unlink(tree, fname);
1238 :
1239 4 : talloc_free(tree);
1240 :
1241 4 : talloc_free(mem_ctx);
1242 :
1243 4 : 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 4 : bool test_durable_v2_open_reopen2_lease(struct torture_context *tctx,
1252 : struct smb2_tree *tree)
1253 : {
1254 0 : NTSTATUS status;
1255 4 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
1256 0 : char fname[256];
1257 0 : struct smb2_handle _h;
1258 4 : struct smb2_handle *h = NULL;
1259 0 : struct smb2_create io;
1260 4 : struct GUID create_guid = GUID_random();
1261 0 : struct smb2_lease ls;
1262 0 : uint64_t lease_key;
1263 4 : bool ret = true;
1264 0 : struct smbcli_options options;
1265 0 : uint32_t caps;
1266 :
1267 4 : caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
1268 4 : if (!(caps & SMB2_CAP_LEASING)) {
1269 2 : torture_skip(tctx, "leases are not supported");
1270 : }
1271 :
1272 2 : options = tree->session->transport->options;
1273 :
1274 : /* Choose a random name in case the state is left a little funky. */
1275 2 : snprintf(fname, 256, "durable_v2_open_reopen2_%s.dat",
1276 : generate_random_str(tctx, 8));
1277 :
1278 2 : smb2_util_unlink(tree, fname);
1279 :
1280 2 : lease_key = generate_random_u64();
1281 2 : smb2_lease_create(&io, &ls, false /* dir */, fname,
1282 : lease_key, smb2_util_lease_state("RWH"));
1283 2 : io.in.durable_open = false;
1284 2 : io.in.durable_open_v2 = true;
1285 2 : io.in.persistent_open = false;
1286 2 : io.in.create_guid = create_guid;
1287 2 : io.in.timeout = UINT32_MAX;
1288 :
1289 2 : status = smb2_create(tree, mem_ctx, &io);
1290 2 : CHECK_STATUS(status, NT_STATUS_OK);
1291 2 : _h = io.out.file.handle;
1292 2 : h = &_h;
1293 2 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1294 2 : CHECK_VAL(io.out.durable_open, false);
1295 2 : CHECK_VAL(io.out.durable_open_v2, true);
1296 2 : CHECK_VAL(io.out.persistent_open, false);
1297 2 : CHECK_VAL(io.out.timeout, 300*1000);
1298 2 : CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1299 2 : CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
1300 2 : CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
1301 2 : CHECK_VAL(io.out.lease_response.lease_state,
1302 : smb2_util_lease_state("RWH"));
1303 2 : CHECK_VAL(io.out.lease_response.lease_flags, 0);
1304 2 : CHECK_VAL(io.out.lease_response.lease_duration, 0);
1305 :
1306 : /* disconnect, reconnect and then do durable reopen */
1307 2 : TALLOC_FREE(tree);
1308 :
1309 2 : 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 2 : ZERO_STRUCT(io);
1324 2 : io.in.fname = "";
1325 2 : io.in.durable_handle_v2 = h;
1326 2 : status = smb2_create(tree, mem_ctx, &io);
1327 2 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1328 :
1329 2 : ZERO_STRUCT(io);
1330 2 : io.in.fname = "__non_existing_fname__";
1331 2 : io.in.durable_handle_v2 = h;
1332 2 : status = smb2_create(tree, mem_ctx, &io);
1333 2 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1334 :
1335 2 : ZERO_STRUCT(io);
1336 2 : io.in.fname = fname;
1337 2 : io.in.durable_handle_v2 = h;
1338 2 : status = smb2_create(tree, mem_ctx, &io);
1339 2 : 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 2 : ZERO_STRUCT(io);
1346 2 : io.in.fname = fname;
1347 2 : io.in.durable_open_v2 = false;
1348 2 : io.in.durable_handle_v2 = h;
1349 2 : io.in.create_guid = create_guid;
1350 2 : io.in.lease_request = &ls;
1351 2 : io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
1352 : /* a wrong lease key lets the request fail */
1353 2 : ls.lease_key.data[0]++;
1354 :
1355 2 : status = smb2_create(tree, mem_ctx, &io);
1356 2 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1357 :
1358 : /* restore the correct lease key */
1359 2 : 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 2 : ZERO_STRUCT(io);
1368 2 : io.in.fname = "__non_existing_fname__";
1369 2 : io.in.durable_open_v2 = false;
1370 2 : io.in.durable_handle_v2 = h;
1371 2 : io.in.create_guid = create_guid;
1372 2 : io.in.lease_request = &ls;
1373 2 : io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
1374 :
1375 2 : status = smb2_create(tree, mem_ctx, &io);
1376 2 : CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
1377 :
1378 : /*
1379 : * Now for a succeeding reconnect:
1380 : */
1381 :
1382 2 : ZERO_STRUCT(io);
1383 2 : io.in.fname = fname;
1384 2 : io.in.durable_open_v2 = false;
1385 2 : io.in.durable_handle_v2 = h;
1386 2 : io.in.create_guid = create_guid;
1387 2 : io.in.lease_request = &ls;
1388 2 : io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
1389 :
1390 : /* the requested lease state is irrelevant */
1391 2 : ls.lease_state = smb2_util_lease_state("");
1392 :
1393 2 : h = NULL;
1394 :
1395 2 : status = smb2_create(tree, mem_ctx, &io);
1396 2 : CHECK_STATUS(status, NT_STATUS_OK);
1397 :
1398 2 : CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1399 2 : CHECK_VAL(io.out.durable_open, false);
1400 2 : CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
1401 2 : CHECK_VAL(io.out.persistent_open, false);
1402 2 : CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1403 2 : CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
1404 2 : CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
1405 2 : CHECK_VAL(io.out.lease_response.lease_state,
1406 : smb2_util_lease_state("RWH"));
1407 2 : CHECK_VAL(io.out.lease_response.lease_flags, 0);
1408 2 : CHECK_VAL(io.out.lease_response.lease_duration, 0);
1409 2 : _h = io.out.file.handle;
1410 2 : h = &_h;
1411 :
1412 : /* disconnect one more time */
1413 2 : TALLOC_FREE(tree);
1414 :
1415 2 : 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 2 : ZERO_STRUCT(io);
1427 : /*
1428 : * These are completely ignored by the server
1429 : */
1430 2 : io.in.security_flags = 0x78;
1431 2 : io.in.oplock_level = 0x78;
1432 2 : io.in.impersonation_level = 0x12345678;
1433 2 : io.in.create_flags = 0x12345678;
1434 2 : io.in.reserved = 0x12345678;
1435 2 : io.in.desired_access = 0x12345678;
1436 2 : io.in.file_attributes = 0x12345678;
1437 2 : io.in.share_access = 0x12345678;
1438 2 : io.in.create_disposition = 0x12345678;
1439 2 : 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 2 : io.in.fname = fname;
1450 2 : io.in.durable_open_v2 = false;
1451 2 : io.in.durable_handle_v2 = h;
1452 2 : io.in.create_guid = create_guid;
1453 2 : io.in.lease_request = &ls;
1454 :
1455 : /* the requested lease state is irrelevant */
1456 2 : ls.lease_state = smb2_util_lease_state("");
1457 :
1458 2 : h = NULL;
1459 :
1460 2 : status = smb2_create(tree, mem_ctx, &io);
1461 2 : CHECK_STATUS(status, NT_STATUS_OK);
1462 :
1463 2 : CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1464 2 : CHECK_VAL(io.out.durable_open, false);
1465 2 : CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
1466 2 : CHECK_VAL(io.out.persistent_open, false);
1467 2 : CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1468 2 : CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
1469 2 : CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
1470 2 : CHECK_VAL(io.out.lease_response.lease_state,
1471 : smb2_util_lease_state("RWH"));
1472 2 : CHECK_VAL(io.out.lease_response.lease_flags, 0);
1473 2 : CHECK_VAL(io.out.lease_response.lease_duration, 0);
1474 :
1475 2 : _h = io.out.file.handle;
1476 2 : h = &_h;
1477 :
1478 2 : done:
1479 2 : if (h != NULL) {
1480 2 : smb2_util_close(tree, *h);
1481 : }
1482 :
1483 2 : smb2_util_unlink(tree, fname);
1484 :
1485 2 : talloc_free(tree);
1486 :
1487 2 : talloc_free(mem_ctx);
1488 :
1489 2 : 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 4 : bool test_durable_v2_open_reopen2_lease_v2(struct torture_context *tctx,
1498 : struct smb2_tree *tree)
1499 : {
1500 0 : NTSTATUS status;
1501 4 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
1502 0 : char fname[256];
1503 0 : struct smb2_handle _h;
1504 4 : struct smb2_handle *h = NULL;
1505 0 : struct smb2_create io;
1506 4 : struct GUID create_guid = GUID_random();
1507 0 : struct smb2_lease ls;
1508 0 : uint64_t lease_key;
1509 4 : bool ret = true;
1510 0 : struct smbcli_options options;
1511 0 : uint32_t caps;
1512 :
1513 4 : caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
1514 4 : if (!(caps & SMB2_CAP_LEASING)) {
1515 2 : torture_skip(tctx, "leases are not supported");
1516 : }
1517 :
1518 2 : options = tree->session->transport->options;
1519 :
1520 2 : smb2_deltree(tree, __func__);
1521 2 : status = torture_smb2_testdir(tree, __func__, &_h);
1522 2 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1523 : "torture_smb2_testdir failed\n");
1524 2 : smb2_util_close(tree, _h);
1525 :
1526 : /* Choose a random name in case the state is left a little funky. */
1527 2 : snprintf(fname, 256, "%s\\durable_v2_open_reopen2_%s.dat",
1528 : __func__, generate_random_str(tctx, 8));
1529 :
1530 2 : smb2_util_unlink(tree, fname);
1531 :
1532 2 : lease_key = random();
1533 2 : 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 2 : io.in.durable_open = false;
1537 2 : io.in.durable_open_v2 = true;
1538 2 : io.in.persistent_open = false;
1539 2 : io.in.create_guid = create_guid;
1540 2 : io.in.timeout = UINT32_MAX;
1541 :
1542 2 : status = smb2_create(tree, mem_ctx, &io);
1543 2 : CHECK_STATUS(status, NT_STATUS_OK);
1544 2 : _h = io.out.file.handle;
1545 2 : h = &_h;
1546 2 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1547 2 : CHECK_VAL(io.out.durable_open, false);
1548 2 : CHECK_VAL(io.out.durable_open_v2, true);
1549 2 : CHECK_VAL(io.out.persistent_open, false);
1550 2 : CHECK_VAL(io.out.timeout, 300*1000);
1551 2 : CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1552 2 : CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key);
1553 2 : CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key);
1554 :
1555 : /* disconnect, reconnect and then do durable reopen */
1556 2 : TALLOC_FREE(tree);
1557 :
1558 2 : 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 2 : ZERO_STRUCT(io);
1573 2 : io.in.fname = "";
1574 2 : io.in.durable_handle_v2 = h;
1575 2 : status = smb2_create(tree, mem_ctx, &io);
1576 2 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1577 :
1578 2 : ZERO_STRUCT(io);
1579 2 : io.in.fname = "__non_existing_fname__";
1580 2 : io.in.durable_handle_v2 = h;
1581 2 : status = smb2_create(tree, mem_ctx, &io);
1582 2 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1583 :
1584 2 : ZERO_STRUCT(io);
1585 2 : io.in.fname = fname;
1586 2 : io.in.durable_handle_v2 = h;
1587 2 : status = smb2_create(tree, mem_ctx, &io);
1588 2 : 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 2 : ZERO_STRUCT(io);
1595 2 : io.in.fname = fname;
1596 2 : io.in.durable_open_v2 = false;
1597 2 : io.in.durable_handle_v2 = h;
1598 2 : io.in.create_guid = create_guid;
1599 2 : io.in.lease_request_v2 = &ls;
1600 2 : io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
1601 : /* a wrong lease key lets the request fail */
1602 2 : ls.lease_key.data[0]++;
1603 :
1604 2 : status = smb2_create(tree, mem_ctx, &io);
1605 2 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1606 :
1607 : /* restore the correct lease key */
1608 2 : 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 2 : ZERO_STRUCT(io);
1618 2 : io.in.fname = "__non_existing_fname__";
1619 2 : io.in.durable_open_v2 = false;
1620 2 : io.in.durable_handle_v2 = h;
1621 2 : io.in.create_guid = create_guid;
1622 2 : io.in.lease_request_v2 = &ls;
1623 2 : io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
1624 :
1625 2 : status = smb2_create(tree, mem_ctx, &io);
1626 2 : CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
1627 :
1628 : /*
1629 : * Now for a succeeding reconnect:
1630 : */
1631 :
1632 2 : ZERO_STRUCT(io);
1633 2 : io.in.fname = fname;
1634 2 : io.in.durable_open_v2 = false;
1635 2 : io.in.durable_handle_v2 = h;
1636 2 : io.in.create_guid = create_guid;
1637 2 : io.in.lease_request_v2 = &ls;
1638 2 : io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
1639 :
1640 : /* the requested lease state is irrelevant */
1641 2 : ls.lease_state = smb2_util_lease_state("");
1642 :
1643 2 : h = NULL;
1644 :
1645 2 : status = smb2_create(tree, mem_ctx, &io);
1646 2 : CHECK_STATUS(status, NT_STATUS_OK);
1647 :
1648 2 : CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1649 2 : CHECK_VAL(io.out.durable_open, false);
1650 2 : CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
1651 2 : CHECK_VAL(io.out.persistent_open, false);
1652 2 : CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1653 2 : CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key);
1654 2 : CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key);
1655 2 : CHECK_VAL(io.out.lease_response_v2.lease_state,
1656 : smb2_util_lease_state("RWH"));
1657 2 : CHECK_VAL(io.out.lease_response_v2.lease_flags, 0);
1658 2 : CHECK_VAL(io.out.lease_response_v2.lease_duration, 0);
1659 2 : _h = io.out.file.handle;
1660 2 : h = &_h;
1661 :
1662 : /* disconnect one more time */
1663 2 : TALLOC_FREE(tree);
1664 :
1665 2 : 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 2 : ZERO_STRUCT(io);
1677 : /*
1678 : * These are completely ignored by the server
1679 : */
1680 2 : io.in.security_flags = 0x78;
1681 2 : io.in.oplock_level = 0x78;
1682 2 : io.in.impersonation_level = 0x12345678;
1683 2 : io.in.create_flags = 0x12345678;
1684 2 : io.in.reserved = 0x12345678;
1685 2 : io.in.desired_access = 0x12345678;
1686 2 : io.in.file_attributes = 0x12345678;
1687 2 : io.in.share_access = 0x12345678;
1688 2 : io.in.create_disposition = 0x12345678;
1689 2 : io.in.create_options = 0x12345678;
1690 2 : 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 2 : io.in.fname = fname;
1701 2 : io.in.durable_open_v2 = false;
1702 2 : io.in.durable_handle_v2 = h;
1703 2 : io.in.create_guid = create_guid;
1704 2 : io.in.lease_request_v2 = &ls;
1705 :
1706 : /* the requested lease state is irrelevant */
1707 2 : ls.lease_state = smb2_util_lease_state("");
1708 :
1709 2 : h = NULL;
1710 :
1711 2 : status = smb2_create(tree, mem_ctx, &io);
1712 2 : CHECK_STATUS(status, NT_STATUS_OK);
1713 2 : CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1714 2 : CHECK_VAL(io.out.durable_open, false);
1715 2 : CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
1716 2 : CHECK_VAL(io.out.persistent_open, false);
1717 2 : CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1718 2 : CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key);
1719 2 : CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key);
1720 2 : CHECK_VAL(io.out.lease_response_v2.lease_state,
1721 : smb2_util_lease_state("RWH"));
1722 2 : CHECK_VAL(io.out.lease_response_v2.lease_flags, 0);
1723 2 : CHECK_VAL(io.out.lease_response_v2.lease_duration, 0);
1724 :
1725 2 : _h = io.out.file.handle;
1726 2 : h = &_h;
1727 :
1728 2 : done:
1729 2 : if (h != NULL) {
1730 2 : smb2_util_close(tree, *h);
1731 : }
1732 :
1733 2 : smb2_util_unlink(tree, fname);
1734 2 : smb2_deltree(tree, __func__);
1735 :
1736 2 : talloc_free(tree);
1737 :
1738 2 : talloc_free(mem_ctx);
1739 :
1740 2 : return ret;
1741 : }
1742 :
1743 : /**
1744 : * Test durable request / reconnect with AppInstanceId
1745 : */
1746 4 : bool test_durable_v2_open_app_instance(struct torture_context *tctx,
1747 : struct smb2_tree *tree1,
1748 : struct smb2_tree *tree2)
1749 : {
1750 0 : NTSTATUS status;
1751 4 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
1752 0 : char fname[256];
1753 0 : struct smb2_handle _h1, _h2;
1754 4 : struct smb2_handle *h1 = NULL, *h2 = NULL;
1755 0 : struct smb2_create io1, io2;
1756 4 : bool ret = true;
1757 4 : struct GUID create_guid_1 = GUID_random();
1758 4 : struct GUID create_guid_2 = GUID_random();
1759 4 : struct GUID app_instance_id = GUID_random();
1760 :
1761 : /* Choose a random name in case the state is left a little funky. */
1762 4 : snprintf(fname, 256, "durable_v2_open_app_instance_%s.dat",
1763 : generate_random_str(tctx, 8));
1764 :
1765 4 : smb2_util_unlink(tree1, fname);
1766 :
1767 4 : ZERO_STRUCT(break_info);
1768 4 : tree1->session->transport->oplock.handler = torture_oplock_handler;
1769 4 : tree1->session->transport->oplock.private_data = tree1;
1770 :
1771 4 : smb2_oplock_create_share(&io1, fname,
1772 : smb2_util_share_access(""),
1773 4 : smb2_util_oplock_level("b"));
1774 4 : io1.in.durable_open = false;
1775 4 : io1.in.durable_open_v2 = true;
1776 4 : io1.in.persistent_open = false;
1777 4 : io1.in.create_guid = create_guid_1;
1778 4 : io1.in.app_instance_id = &app_instance_id;
1779 4 : io1.in.timeout = UINT32_MAX;
1780 :
1781 4 : status = smb2_create(tree1, mem_ctx, &io1);
1782 4 : CHECK_STATUS(status, NT_STATUS_OK);
1783 4 : _h1 = io1.out.file.handle;
1784 4 : h1 = &_h1;
1785 4 : CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1786 4 : CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
1787 4 : CHECK_VAL(io1.out.durable_open, false);
1788 4 : CHECK_VAL(io1.out.durable_open_v2, true);
1789 4 : CHECK_VAL(io1.out.persistent_open, false);
1790 4 : 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 4 : smb2_oplock_create_share(&io2, fname,
1799 : smb2_util_share_access(""),
1800 4 : smb2_util_oplock_level("b"));
1801 4 : io2.in.durable_open = false;
1802 4 : io2.in.durable_open_v2 = true;
1803 4 : io2.in.persistent_open = false;
1804 4 : io2.in.create_guid = create_guid_2;
1805 4 : io2.in.app_instance_id = &app_instance_id;
1806 4 : io2.in.timeout = UINT32_MAX;
1807 :
1808 4 : status = smb2_create(tree2, mem_ctx, &io2);
1809 4 : CHECK_STATUS(status, NT_STATUS_OK);
1810 4 : _h2 = io2.out.file.handle;
1811 4 : h2 = &_h2;
1812 4 : CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1813 4 : CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
1814 4 : CHECK_VAL(io2.out.durable_open, false);
1815 4 : CHECK_VAL(io2.out.durable_open_v2, true);
1816 4 : CHECK_VAL(io2.out.persistent_open, false);
1817 4 : CHECK_VAL(io2.out.timeout, 300*1000);
1818 :
1819 4 : CHECK_VAL(break_info.count, 0);
1820 :
1821 4 : status = smb2_util_close(tree1, *h1);
1822 4 : CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
1823 4 : h1 = NULL;
1824 :
1825 4 : done:
1826 4 : if (h1 != NULL) {
1827 0 : smb2_util_close(tree1, *h1);
1828 : }
1829 4 : if (h2 != NULL) {
1830 4 : smb2_util_close(tree2, *h2);
1831 : }
1832 :
1833 4 : smb2_util_unlink(tree2, fname);
1834 :
1835 4 : talloc_free(tree1);
1836 4 : talloc_free(tree2);
1837 :
1838 4 : talloc_free(mem_ctx);
1839 :
1840 4 : 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 4 : bool test_persistent_open_oplock(struct torture_context *tctx,
1890 : struct smb2_tree *tree)
1891 : {
1892 0 : char fname[256];
1893 4 : bool ret = true;
1894 0 : uint32_t share_capabilities;
1895 4 : bool share_is_ca = false;
1896 0 : struct durable_open_vs_oplock *table;
1897 :
1898 : /* Choose a random name in case the state is left a little funky. */
1899 4 : snprintf(fname, 256, "persistent_open_oplock_%s.dat", generate_random_str(tctx, 8));
1900 :
1901 4 : share_capabilities = smb2cli_tcon_capabilities(tree->smbXcli);
1902 4 : share_is_ca = share_capabilities & SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY;
1903 :
1904 4 : if (share_is_ca) {
1905 0 : table = persistent_open_oplock_ca_table;
1906 : } else {
1907 4 : table = durable_open_vs_oplock_table;
1908 : }
1909 :
1910 4 : ret = test_durable_v2_open_oplock_table(tctx, tree, fname,
1911 : true, /* request_persistent */
1912 : table,
1913 : NUM_OPLOCK_OPEN_TESTS);
1914 :
1915 4 : talloc_free(tree);
1916 :
1917 4 : 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 4 : bool test_persistent_open_lease(struct torture_context *tctx,
1977 : struct smb2_tree *tree)
1978 : {
1979 0 : char fname[256];
1980 4 : bool ret = true;
1981 0 : uint32_t caps;
1982 0 : uint32_t share_capabilities;
1983 0 : bool share_is_ca;
1984 0 : struct durable_open_vs_lease *table;
1985 :
1986 4 : caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
1987 4 : if (!(caps & SMB2_CAP_LEASING)) {
1988 2 : torture_skip(tctx, "leases are not supported");
1989 : }
1990 :
1991 : /* Choose a random name in case the state is left a little funky. */
1992 2 : snprintf(fname, 256, "persistent_open_lease_%s.dat", generate_random_str(tctx, 8));
1993 :
1994 2 : share_capabilities = smb2cli_tcon_capabilities(tree->smbXcli);
1995 2 : share_is_ca = share_capabilities & SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY;
1996 :
1997 2 : if (share_is_ca) {
1998 0 : table = persistent_open_lease_ca_table;
1999 : } else {
2000 2 : table = durable_open_vs_lease_table;
2001 : }
2002 :
2003 2 : ret = test_durable_v2_open_lease_table(tctx, tree, fname,
2004 : true, /* request_persistent */
2005 : table,
2006 : NUM_LEASE_OPEN_TESTS);
2007 :
2008 2 : talloc_free(tree);
2009 :
2010 2 : 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 4 : bool test_durable_v2_setinfo(struct torture_context *tctx,
2022 : struct smb2_tree *tree)
2023 : {
2024 0 : NTSTATUS status;
2025 4 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
2026 0 : char fname[256];
2027 0 : struct smb2_handle _h;
2028 4 : struct smb2_handle *h = NULL;
2029 0 : struct smb2_create io;
2030 0 : union smb_setfileinfo si;
2031 4 : struct GUID create_guid = GUID_random();
2032 0 : struct smb2_lease ls;
2033 0 : uint64_t lease_key;
2034 4 : bool ret = true;
2035 0 : struct smbcli_options options;
2036 0 : uint32_t caps;
2037 :
2038 4 : caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
2039 4 : if (!(caps & SMB2_CAP_LEASING)) {
2040 2 : torture_skip(tctx, "leases are not supported");
2041 : }
2042 :
2043 2 : options = tree->session->transport->options;
2044 :
2045 2 : smb2_deltree(tree, __func__);
2046 2 : status = torture_smb2_testdir(tree, __func__, &_h);
2047 2 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2048 : "torture_smb2_testdir failed\n");
2049 2 : smb2_util_close(tree, _h);
2050 :
2051 : /* Choose a random name in case the state is left a little funky. */
2052 2 : snprintf(fname, 256, "%s\\durable_v2_setinfo%s.dat",
2053 : __func__, generate_random_str(tctx, 8));
2054 :
2055 2 : smb2_util_unlink(tree, fname);
2056 :
2057 2 : lease_key = random();
2058 2 : 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 2 : io.in.durable_open = false;
2062 2 : io.in.durable_open_v2 = true;
2063 2 : io.in.persistent_open = false;
2064 2 : io.in.create_guid = create_guid;
2065 2 : io.in.timeout = UINT32_MAX;
2066 :
2067 2 : status = smb2_create(tree, mem_ctx, &io);
2068 2 : CHECK_STATUS(status, NT_STATUS_OK);
2069 2 : _h = io.out.file.handle;
2070 2 : h = &_h;
2071 2 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2072 2 : CHECK_VAL(io.out.durable_open, false);
2073 2 : CHECK_VAL(io.out.durable_open_v2, true);
2074 2 : CHECK_VAL(io.out.persistent_open, false);
2075 2 : CHECK_VAL(io.out.timeout, 300*1000);
2076 2 : CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
2077 2 : CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key);
2078 2 : 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 2 : ZERO_STRUCT(si);
2086 2 : si.generic.level = SMB_SFILEINFO_END_OF_FILE_INFORMATION;
2087 2 : si.generic.in.file.handle = io.out.file.handle;
2088 2 : si.end_of_file_info.in.size = 0x100000;
2089 2 : status = smb2_setinfo_file(tree, &si);
2090 2 : CHECK_STATUS(status, NT_STATUS_OK);
2091 :
2092 : /* disconnect, reconnect and then do durable reopen */
2093 2 : TALLOC_FREE(tree);
2094 :
2095 2 : 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 2 : ZERO_STRUCT(io);
2106 2 : io.in.fname = fname;
2107 2 : io.in.durable_open_v2 = false;
2108 2 : io.in.durable_handle_v2 = h;
2109 2 : io.in.create_guid = create_guid;
2110 2 : io.in.lease_request_v2 = &ls;
2111 2 : io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
2112 :
2113 : /* the requested lease state is irrelevant */
2114 2 : ls.lease_state = smb2_util_lease_state("");
2115 :
2116 2 : h = NULL;
2117 :
2118 2 : status = smb2_create(tree, mem_ctx, &io);
2119 2 : CHECK_STATUS(status, NT_STATUS_OK);
2120 :
2121 2 : CHECK_VAL(io.out.create_action, NTCREATEX_ACTION_EXISTED);
2122 2 : CHECK_VAL(io.out.size, 0x100000); \
2123 2 : CHECK_VAL(io.out.durable_open, false);
2124 2 : CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
2125 2 : CHECK_VAL(io.out.persistent_open, false);
2126 2 : CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
2127 2 : CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key);
2128 2 : CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key);
2129 2 : CHECK_VAL(io.out.lease_response_v2.lease_state,
2130 : smb2_util_lease_state("RWH"));
2131 2 : CHECK_VAL(io.out.lease_response_v2.lease_flags, 0);
2132 2 : CHECK_VAL(io.out.lease_response_v2.lease_duration, 0);
2133 2 : _h = io.out.file.handle;
2134 2 : h = &_h;
2135 :
2136 2 : done:
2137 :
2138 2 : if (h != NULL) {
2139 2 : smb2_util_close(tree, *h);
2140 : }
2141 :
2142 2 : smb2_util_unlink(tree, fname);
2143 2 : smb2_deltree(tree, __func__);
2144 :
2145 2 : talloc_free(tree);
2146 :
2147 2 : talloc_free(mem_ctx);
2148 :
2149 2 : return ret;
2150 : }
2151 :
2152 2354 : struct torture_suite *torture_smb2_durable_v2_open_init(TALLOC_CTX *ctx)
2153 : {
2154 125 : struct torture_suite *suite =
2155 2354 : torture_suite_create(ctx, "durable-v2-open");
2156 :
2157 2354 : torture_suite_add_1smb2_test(suite, "create-blob", test_durable_v2_open_create_blob);
2158 2354 : torture_suite_add_1smb2_test(suite, "open-oplock", test_durable_v2_open_oplock);
2159 2354 : torture_suite_add_1smb2_test(suite, "open-lease", test_durable_v2_open_lease);
2160 2354 : torture_suite_add_1smb2_test(suite, "reopen1", test_durable_v2_open_reopen1);
2161 2354 : torture_suite_add_1smb2_test(suite, "reopen1a", test_durable_v2_open_reopen1a);
2162 2354 : torture_suite_add_1smb2_test(suite, "reopen1a-lease", test_durable_v2_open_reopen1a_lease);
2163 2354 : torture_suite_add_1smb2_test(suite, "reopen2", test_durable_v2_open_reopen2);
2164 2354 : torture_suite_add_1smb2_test(suite, "reopen2b", test_durable_v2_open_reopen2b);
2165 2354 : torture_suite_add_1smb2_test(suite, "reopen2c", test_durable_v2_open_reopen2c);
2166 2354 : torture_suite_add_1smb2_test(suite, "reopen2-lease", test_durable_v2_open_reopen2_lease);
2167 2354 : torture_suite_add_1smb2_test(suite, "reopen2-lease-v2", test_durable_v2_open_reopen2_lease_v2);
2168 2354 : torture_suite_add_1smb2_test(suite, "durable-v2-setinfo", test_durable_v2_setinfo);
2169 2354 : torture_suite_add_2smb2_test(suite, "app-instance", test_durable_v2_open_app_instance);
2170 2354 : torture_suite_add_1smb2_test(suite, "persistent-open-oplock", test_persistent_open_oplock);
2171 2354 : torture_suite_add_1smb2_test(suite, "persistent-open-lease", test_persistent_open_lease);
2172 :
2173 2354 : suite->description = talloc_strdup(suite, "SMB2-DURABLE-V2-OPEN tests");
2174 :
2175 2354 : return suite;
2176 : }
2177 :
2178 : /**
2179 : * basic test for doing a durable open
2180 : * tcp disconnect, reconnect, do a durable reopen (succeeds)
2181 : */
2182 6 : static bool test_durable_v2_reconnect_delay(struct torture_context *tctx,
2183 : struct smb2_tree *tree,
2184 : struct smb2_tree *tree2)
2185 : {
2186 0 : NTSTATUS status;
2187 6 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
2188 0 : char fname[256];
2189 0 : struct smb2_handle _h;
2190 6 : struct smb2_handle *h = NULL;
2191 0 : struct smb2_create io;
2192 6 : struct GUID create_guid = GUID_random();
2193 0 : struct smbcli_options options;
2194 0 : uint64_t previous_session_id;
2195 6 : uint8_t b = 0;
2196 6 : bool ret = true;
2197 0 : bool ok;
2198 :
2199 6 : options = tree->session->transport->options;
2200 6 : 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 6 : snprintf(fname,
2204 : sizeof(fname),
2205 : "durable_v2_reconnect_delay_%s.dat",
2206 : generate_random_str(tctx, 8));
2207 :
2208 6 : smb2_util_unlink(tree, fname);
2209 :
2210 6 : smb2_oplock_create_share(&io, fname,
2211 : smb2_util_share_access(""),
2212 6 : smb2_util_oplock_level("b"));
2213 6 : io.in.durable_open = false;
2214 6 : io.in.durable_open_v2 = true;
2215 6 : io.in.persistent_open = false;
2216 6 : io.in.create_guid = create_guid;
2217 6 : io.in.timeout = 0;
2218 :
2219 6 : status = smb2_create(tree, mem_ctx, &io);
2220 6 : CHECK_STATUS(status, NT_STATUS_OK);
2221 :
2222 6 : _h = io.out.file.handle;
2223 6 : h = &_h;
2224 6 : CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
2225 6 : CHECK_VAL(io.out.durable_open_v2, true);
2226 :
2227 6 : status = smb2_util_write(tree, *h, &b, 0, 1);
2228 6 : CHECK_STATUS(status, NT_STATUS_OK);
2229 :
2230 : /* disconnect, leaving the durable open */
2231 6 : TALLOC_FREE(tree);
2232 6 : h = NULL;
2233 :
2234 6 : ok = torture_smb2_connection_ext(tctx, previous_session_id,
2235 : &options, &tree);
2236 6 : torture_assert_goto(tctx, ok, ret, done, "couldn't reconnect, bailing\n");
2237 :
2238 6 : ZERO_STRUCT(io);
2239 6 : io.in.fname = fname;
2240 6 : io.in.durable_open_v2 = false;
2241 6 : io.in.durable_handle_v2 = &_h;
2242 6 : io.in.create_guid = create_guid;
2243 :
2244 6 : status = smb2_create(tree, mem_ctx, &io);
2245 6 : CHECK_STATUS(status, NT_STATUS_OK);
2246 6 : CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
2247 6 : _h = io.out.file.handle;
2248 6 : h = &_h;
2249 :
2250 6 : done:
2251 6 : if (h != NULL) {
2252 6 : smb2_util_close(tree, *h);
2253 : }
2254 6 : TALLOC_FREE(tree);
2255 :
2256 6 : smb2_util_unlink(tree2, fname);
2257 :
2258 6 : TALLOC_FREE(tree2);
2259 :
2260 6 : talloc_free(mem_ctx);
2261 :
2262 6 : 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 6 : static bool test_durable_v2_reconnect_delay_msec(struct torture_context *tctx,
2270 : struct smb2_tree *tree,
2271 : struct smb2_tree *tree2)
2272 : {
2273 0 : NTSTATUS status;
2274 6 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
2275 0 : char fname[256];
2276 0 : struct smb2_handle _h;
2277 6 : struct smb2_handle *h = NULL;
2278 0 : struct smb2_create io;
2279 0 : struct smb2_lease ls;
2280 6 : struct GUID create_guid = GUID_random();
2281 0 : struct smbcli_options options;
2282 0 : uint64_t previous_session_id;
2283 6 : uint8_t b = 0;
2284 6 : bool ret = true;
2285 0 : bool ok;
2286 :
2287 6 : options = tree->session->transport->options;
2288 6 : 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 6 : snprintf(fname,
2292 : sizeof(fname),
2293 : "durable_v2_reconnect_delay_%s.dat",
2294 : generate_random_str(tctx, 8));
2295 :
2296 6 : smb2_util_unlink(tree, fname);
2297 :
2298 6 : smb2_lease_create(
2299 : &io,
2300 : &ls,
2301 : false /* dir */,
2302 : fname,
2303 : generate_random_u64(),
2304 : smb2_util_lease_state("RWH"));
2305 6 : io.in.durable_open = false;
2306 6 : io.in.durable_open_v2 = true;
2307 6 : io.in.persistent_open = false;
2308 6 : io.in.create_guid = create_guid;
2309 6 : io.in.timeout = 1;
2310 :
2311 6 : status = smb2_create(tree, mem_ctx, &io);
2312 6 : CHECK_STATUS(status, NT_STATUS_OK);
2313 :
2314 6 : _h = io.out.file.handle;
2315 6 : h = &_h;
2316 6 : CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
2317 6 : CHECK_VAL(io.out.durable_open_v2, true);
2318 :
2319 6 : status = smb2_util_write(tree, *h, &b, 0, 1);
2320 6 : CHECK_STATUS(status, NT_STATUS_OK);
2321 :
2322 : /* disconnect, leaving the durable open */
2323 6 : TALLOC_FREE(tree);
2324 6 : h = NULL;
2325 :
2326 6 : ok = torture_smb2_connection_ext(tctx, previous_session_id,
2327 : &options, &tree);
2328 6 : torture_assert_goto(tctx, ok, ret, done, "couldn't reconnect, bailing\n");
2329 :
2330 6 : sleep(10);
2331 :
2332 6 : ZERO_STRUCT(io);
2333 6 : io.in.fname = fname;
2334 6 : io.in.durable_open_v2 = false;
2335 6 : io.in.durable_handle_v2 = &_h;
2336 6 : io.in.create_guid = create_guid;
2337 :
2338 6 : status = smb2_create(tree, mem_ctx, &io);
2339 6 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
2340 6 : _h = io.out.file.handle;
2341 6 : h = &_h;
2342 :
2343 6 : done:
2344 6 : if (h != NULL) {
2345 6 : smb2_util_close(tree, *h);
2346 : }
2347 6 : TALLOC_FREE(tree);
2348 :
2349 6 : smb2_util_unlink(tree2, fname);
2350 :
2351 6 : TALLOC_FREE(tree2);
2352 :
2353 6 : talloc_free(mem_ctx);
2354 :
2355 6 : return ret;
2356 : }
2357 :
2358 2354 : struct torture_suite *torture_smb2_durable_v2_delay_init(TALLOC_CTX *ctx)
2359 : {
2360 125 : struct torture_suite *suite =
2361 2354 : torture_suite_create(ctx, "durable-v2-delay");
2362 :
2363 2354 : torture_suite_add_2smb2_test(suite,
2364 : "durable_v2_reconnect_delay",
2365 : test_durable_v2_reconnect_delay);
2366 2354 : torture_suite_add_2smb2_test(suite,
2367 : "durable_v2_reconnect_delay_msec",
2368 : test_durable_v2_reconnect_delay_msec);
2369 :
2370 2354 : return suite;
2371 : }
|