Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 :
4 : test suite for SMB2 leases
5 :
6 : Copyright (C) Zachary Loafman 2009
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 <tevent.h>
24 : #include "libcli/smb2/smb2.h"
25 : #include "libcli/smb2/smb2_calls.h"
26 : #include "torture/torture.h"
27 : #include "torture/smb2/proto.h"
28 : #include "torture/util.h"
29 : #include "libcli/smb/smbXcli_base.h"
30 : #include "libcli/security/security.h"
31 : #include "lib/param/param.h"
32 : #include "lease_break_handler.h"
33 :
34 : #define CHECK_VAL(v, correct) do { \
35 : if ((v) != (correct)) { \
36 : torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s got 0x%x - should be 0x%x\n", \
37 : __location__, #v, (int)(v), (int)(correct)); \
38 : ret = false; \
39 : }} while (0)
40 :
41 : #define CHECK_STATUS(status, correct) do { \
42 : if (!NT_STATUS_EQUAL(status, correct)) { \
43 : torture_result(tctx, TORTURE_FAIL, __location__": Incorrect status %s - should be %s", \
44 : nt_errstr(status), nt_errstr(correct)); \
45 : ret = false; \
46 : goto done; \
47 : }} while (0)
48 :
49 : #define CHECK_CREATED(__io, __created, __attribute) \
50 : do { \
51 : CHECK_VAL((__io)->out.create_action, NTCREATEX_ACTION_ ## __created); \
52 : CHECK_VAL((__io)->out.size, 0); \
53 : CHECK_VAL((__io)->out.file_attr, (__attribute)); \
54 : CHECK_VAL((__io)->out.reserved2, 0); \
55 : } while(0)
56 :
57 : #define CHECK_LEASE(__io, __state, __oplevel, __key, __flags) \
58 : do { \
59 : CHECK_VAL((__io)->out.lease_response.lease_version, 1); \
60 : if (__oplevel) { \
61 : CHECK_VAL((__io)->out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE); \
62 : CHECK_VAL((__io)->out.lease_response.lease_key.data[0], (__key)); \
63 : CHECK_VAL((__io)->out.lease_response.lease_key.data[1], ~(__key)); \
64 : CHECK_VAL((__io)->out.lease_response.lease_state, smb2_util_lease_state(__state)); \
65 : } else { \
66 : CHECK_VAL((__io)->out.oplock_level, SMB2_OPLOCK_LEVEL_NONE); \
67 : CHECK_VAL((__io)->out.lease_response.lease_key.data[0], 0); \
68 : CHECK_VAL((__io)->out.lease_response.lease_key.data[1], 0); \
69 : CHECK_VAL((__io)->out.lease_response.lease_state, 0); \
70 : } \
71 : \
72 : CHECK_VAL((__io)->out.lease_response.lease_flags, (__flags)); \
73 : CHECK_VAL((__io)->out.lease_response.lease_duration, 0); \
74 : CHECK_VAL((__io)->out.lease_response.lease_epoch, 0); \
75 : } while(0)
76 :
77 : #define CHECK_LEASE_V2(__io, __state, __oplevel, __key, __flags, __parent, __epoch) \
78 : do { \
79 : CHECK_VAL((__io)->out.lease_response_v2.lease_version, 2); \
80 : if (__oplevel) { \
81 : CHECK_VAL((__io)->out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE); \
82 : CHECK_VAL((__io)->out.lease_response_v2.lease_key.data[0], (__key)); \
83 : CHECK_VAL((__io)->out.lease_response_v2.lease_key.data[1], ~(__key)); \
84 : CHECK_VAL((__io)->out.lease_response_v2.lease_state, smb2_util_lease_state(__state)); \
85 : } else { \
86 : CHECK_VAL((__io)->out.oplock_level, SMB2_OPLOCK_LEVEL_NONE); \
87 : CHECK_VAL((__io)->out.lease_response_v2.lease_key.data[0], 0); \
88 : CHECK_VAL((__io)->out.lease_response_v2.lease_key.data[1], 0); \
89 : CHECK_VAL((__io)->out.lease_response_v2.lease_state, 0); \
90 : } \
91 : \
92 : CHECK_VAL((__io)->out.lease_response_v2.lease_flags, __flags); \
93 : if (__flags & SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET) { \
94 : CHECK_VAL((__io)->out.lease_response_v2.parent_lease_key.data[0], (__parent)); \
95 : CHECK_VAL((__io)->out.lease_response_v2.parent_lease_key.data[1], ~(__parent)); \
96 : } \
97 : CHECK_VAL((__io)->out.lease_response_v2.lease_duration, 0); \
98 : CHECK_VAL((__io)->out.lease_response_v2.lease_epoch, (__epoch)); \
99 : } while(0)
100 :
101 : static const uint64_t LEASE1 = 0xBADC0FFEE0DDF00Dull;
102 : static const uint64_t LEASE2 = 0xDEADBEEFFEEDBEADull;
103 : static const uint64_t LEASE3 = 0xDAD0FFEDD00DF00Dull;
104 : static const uint64_t LEASE4 = 0xBAD0FFEDD00DF00Dull;
105 :
106 : #define NREQUEST_RESULTS 8
107 : static const char *request_results[NREQUEST_RESULTS][2] = {
108 : { "", "" },
109 : { "R", "R" },
110 : { "H", "" },
111 : { "W", "" },
112 : { "RH", "RH" },
113 : { "RW", "RW" },
114 : { "HW", "" },
115 : { "RHW", "RHW" },
116 : };
117 :
118 4 : static bool test_lease_request(struct torture_context *tctx,
119 : struct smb2_tree *tree)
120 : {
121 4 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
122 0 : struct smb2_create io;
123 0 : struct smb2_lease ls;
124 4 : struct smb2_handle h1 = {{0}};
125 4 : struct smb2_handle h2 = {{0}};
126 0 : NTSTATUS status;
127 4 : const char *fname = "lease_request.dat";
128 4 : const char *fname2 = "lease_request.2.dat";
129 4 : const char *sname = "lease_request.dat:stream";
130 4 : const char *dname = "lease_request.dir";
131 4 : bool ret = true;
132 0 : int i;
133 0 : uint32_t caps;
134 :
135 4 : caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
136 4 : if (!(caps & SMB2_CAP_LEASING)) {
137 2 : torture_skip(tctx, "leases are not supported");
138 : }
139 :
140 2 : smb2_util_unlink(tree, fname);
141 2 : smb2_util_unlink(tree, fname2);
142 2 : smb2_util_rmdir(tree, dname);
143 :
144 : /* Win7 is happy to grant RHW leases on files. */
145 2 : smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state("RHW"));
146 2 : status = smb2_create(tree, mem_ctx, &io);
147 2 : CHECK_STATUS(status, NT_STATUS_OK);
148 2 : h1 = io.out.file.handle;
149 2 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
150 2 : CHECK_LEASE(&io, "RHW", true, LEASE1, 0);
151 :
152 : /* But will reject leases on directories. */
153 2 : if (!(caps & SMB2_CAP_DIRECTORY_LEASING)) {
154 2 : smb2_lease_create(&io, &ls, true, dname, LEASE2, smb2_util_lease_state("RHW"));
155 2 : status = smb2_create(tree, mem_ctx, &io);
156 2 : CHECK_STATUS(status, NT_STATUS_OK);
157 2 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_DIRECTORY);
158 2 : CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_NONE);
159 2 : smb2_util_close(tree, io.out.file.handle);
160 : }
161 :
162 : /* Also rejects multiple files leased under the same key. */
163 2 : smb2_lease_create(&io, &ls, true, fname2, LEASE1, smb2_util_lease_state("RHW"));
164 2 : status = smb2_create(tree, mem_ctx, &io);
165 2 : CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
166 :
167 : /* And grants leases on streams (with separate leasekey). */
168 2 : smb2_lease_create(&io, &ls, false, sname, LEASE2, smb2_util_lease_state("RHW"));
169 2 : status = smb2_create(tree, mem_ctx, &io);
170 2 : h2 = io.out.file.handle;
171 2 : CHECK_STATUS(status, NT_STATUS_OK);
172 2 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
173 2 : CHECK_LEASE(&io, "RHW", true, LEASE2, 0);
174 2 : smb2_util_close(tree, h2);
175 :
176 2 : smb2_util_close(tree, h1);
177 :
178 : /* Now see what combos are actually granted. */
179 18 : for (i = 0; i < NREQUEST_RESULTS; i++) {
180 16 : torture_comment(tctx, "Requesting lease type %s(%x),"
181 : " expecting %s(%x)\n",
182 : request_results[i][0], smb2_util_lease_state(request_results[i][0]),
183 : request_results[i][1], smb2_util_lease_state(request_results[i][1]));
184 16 : smb2_lease_create(&io, &ls, false, fname, LEASE1,
185 : smb2_util_lease_state(request_results[i][0]));
186 16 : status = smb2_create(tree, mem_ctx, &io);
187 16 : h2 = io.out.file.handle;
188 16 : CHECK_STATUS(status, NT_STATUS_OK);
189 16 : CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
190 16 : CHECK_LEASE(&io, request_results[i][1], true, LEASE1, 0);
191 16 : smb2_util_close(tree, io.out.file.handle);
192 : }
193 :
194 2 : done:
195 2 : smb2_util_close(tree, h1);
196 2 : smb2_util_close(tree, h2);
197 :
198 2 : smb2_util_unlink(tree, fname);
199 2 : smb2_util_unlink(tree, fname2);
200 2 : smb2_util_rmdir(tree, dname);
201 :
202 2 : talloc_free(mem_ctx);
203 :
204 2 : return ret;
205 : }
206 :
207 4 : static bool test_lease_upgrade(struct torture_context *tctx,
208 : struct smb2_tree *tree)
209 : {
210 4 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
211 0 : struct smb2_create io;
212 0 : struct smb2_lease ls;
213 4 : struct smb2_handle h = {{0}};
214 4 : struct smb2_handle hnew = {{0}};
215 0 : NTSTATUS status;
216 4 : const char *fname = "lease_upgrade.dat";
217 4 : bool ret = true;
218 0 : uint32_t caps;
219 :
220 4 : caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
221 4 : if (!(caps & SMB2_CAP_LEASING)) {
222 2 : torture_skip(tctx, "leases are not supported");
223 : }
224 :
225 2 : smb2_util_unlink(tree, fname);
226 :
227 : /* Grab a RH lease. */
228 2 : smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state("RH"));
229 2 : status = smb2_create(tree, mem_ctx, &io);
230 2 : CHECK_STATUS(status, NT_STATUS_OK);
231 2 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
232 2 : CHECK_LEASE(&io, "RH", true, LEASE1, 0);
233 2 : h = io.out.file.handle;
234 :
235 : /* Upgrades (sidegrades?) to RW leave us with an RH. */
236 2 : smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state("RW"));
237 2 : status = smb2_create(tree, mem_ctx, &io);
238 2 : CHECK_STATUS(status, NT_STATUS_OK);
239 2 : CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
240 2 : CHECK_LEASE(&io, "RH", true, LEASE1, 0);
241 2 : hnew = io.out.file.handle;
242 :
243 2 : smb2_util_close(tree, hnew);
244 :
245 : /* Upgrade to RHW lease. */
246 2 : smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state("RHW"));
247 2 : status = smb2_create(tree, mem_ctx, &io);
248 2 : CHECK_STATUS(status, NT_STATUS_OK);
249 2 : CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
250 2 : CHECK_LEASE(&io, "RHW", true, LEASE1, 0);
251 2 : hnew = io.out.file.handle;
252 :
253 2 : smb2_util_close(tree, h);
254 2 : h = hnew;
255 :
256 : /* Attempt to downgrade - original lease state is maintained. */
257 2 : smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state("RH"));
258 2 : status = smb2_create(tree, mem_ctx, &io);
259 2 : CHECK_STATUS(status, NT_STATUS_OK);
260 2 : CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
261 2 : CHECK_LEASE(&io, "RHW", true, LEASE1, 0);
262 2 : hnew = io.out.file.handle;
263 :
264 2 : smb2_util_close(tree, hnew);
265 :
266 2 : done:
267 2 : smb2_util_close(tree, h);
268 2 : smb2_util_close(tree, hnew);
269 :
270 2 : smb2_util_unlink(tree, fname);
271 :
272 2 : talloc_free(mem_ctx);
273 :
274 2 : return ret;
275 : }
276 :
277 : /**
278 : * upgrade2 test.
279 : * full matrix of lease upgrade combinations
280 : * (non-contended case)
281 : *
282 : * The summary of the behaviour is this:
283 : * -------------------------------------
284 : * An uncontended lease upgrade results in a change
285 : * if and only if the requested lease state is
286 : * - valid, and
287 : * - strictly a superset of the lease state already held.
288 : *
289 : * In that case the resulting lease state is the one
290 : * requested in the upgrade.
291 : */
292 : struct lease_upgrade2_test {
293 : const char *initial;
294 : const char *upgrade_to;
295 : const char *expected;
296 : };
297 :
298 : #define NUM_LEASE_TYPES 5
299 : #define NUM_UPGRADE_TESTS ( NUM_LEASE_TYPES * NUM_LEASE_TYPES )
300 : struct lease_upgrade2_test lease_upgrade2_tests[NUM_UPGRADE_TESTS] = {
301 : { "", "", "" },
302 : { "", "R", "R" },
303 : { "", "RH", "RH" },
304 : { "", "RW", "RW" },
305 : { "", "RWH", "RWH" },
306 :
307 : { "R", "", "R" },
308 : { "R", "R", "R" },
309 : { "R", "RH", "RH" },
310 : { "R", "RW", "RW" },
311 : { "R", "RWH", "RWH" },
312 :
313 : { "RH", "", "RH" },
314 : { "RH", "R", "RH" },
315 : { "RH", "RH", "RH" },
316 : { "RH", "RW", "RH" },
317 : { "RH", "RWH", "RWH" },
318 :
319 : { "RW", "", "RW" },
320 : { "RW", "R", "RW" },
321 : { "RW", "RH", "RW" },
322 : { "RW", "RW", "RW" },
323 : { "RW", "RWH", "RWH" },
324 :
325 : { "RWH", "", "RWH" },
326 : { "RWH", "R", "RWH" },
327 : { "RWH", "RH", "RWH" },
328 : { "RWH", "RW", "RWH" },
329 : { "RWH", "RWH", "RWH" },
330 : };
331 :
332 4 : static bool test_lease_upgrade2(struct torture_context *tctx,
333 : struct smb2_tree *tree)
334 : {
335 4 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
336 0 : struct smb2_handle h, hnew;
337 0 : NTSTATUS status;
338 0 : struct smb2_create io;
339 0 : struct smb2_lease ls;
340 4 : const char *fname = "lease_upgrade2.dat";
341 4 : bool ret = true;
342 0 : int i;
343 0 : uint32_t caps;
344 :
345 4 : caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
346 4 : if (!(caps & SMB2_CAP_LEASING)) {
347 2 : torture_skip(tctx, "leases are not supported");
348 : }
349 :
350 52 : for (i = 0; i < NUM_UPGRADE_TESTS; i++) {
351 50 : struct lease_upgrade2_test t = lease_upgrade2_tests[i];
352 :
353 50 : smb2_util_unlink(tree, fname);
354 :
355 : /* Grab a lease. */
356 50 : smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state(t.initial));
357 50 : status = smb2_create(tree, mem_ctx, &io);
358 50 : CHECK_STATUS(status, NT_STATUS_OK);
359 50 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
360 50 : CHECK_LEASE(&io, t.initial, true, LEASE1, 0);
361 50 : h = io.out.file.handle;
362 :
363 : /* Upgrade. */
364 50 : smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state(t.upgrade_to));
365 50 : status = smb2_create(tree, mem_ctx, &io);
366 50 : CHECK_STATUS(status, NT_STATUS_OK);
367 50 : CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
368 50 : CHECK_LEASE(&io, t.expected, true, LEASE1, 0);
369 50 : hnew = io.out.file.handle;
370 :
371 50 : smb2_util_close(tree, hnew);
372 50 : smb2_util_close(tree, h);
373 : }
374 :
375 2 : done:
376 2 : smb2_util_close(tree, h);
377 2 : smb2_util_close(tree, hnew);
378 :
379 2 : smb2_util_unlink(tree, fname);
380 :
381 2 : talloc_free(mem_ctx);
382 :
383 2 : return ret;
384 : }
385 :
386 :
387 : /**
388 : * upgrade3:
389 : * full matrix of lease upgrade combinations
390 : * (contended case)
391 : *
392 : * We start with 2 leases, and check how one can
393 : * be upgraded
394 : *
395 : * The summary of the behaviour is this:
396 : * -------------------------------------
397 : *
398 : * If we have two leases (lease1 and lease2) on the same file,
399 : * then attempt to upgrade lease1 results in a change if and only
400 : * if the requested lease state:
401 : * - is valid,
402 : * - is strictly a superset of lease1, and
403 : * - can held together with lease2.
404 : *
405 : * In that case, the resulting lease state of the upgraded lease1
406 : * is the state requested in the upgrade. lease2 is not broken
407 : * and remains unchanged.
408 : *
409 : * Note that this contrasts the case of directly opening with
410 : * an initial requested lease state, in which case you get that
411 : * portion of the requested state that can be shared with the
412 : * already existing leases (or the states that they get broken to).
413 : */
414 : struct lease_upgrade3_test {
415 : const char *held1;
416 : const char *held2;
417 : const char *upgrade_to;
418 : const char *upgraded_to;
419 : };
420 :
421 : #define NUM_UPGRADE3_TESTS ( 20 )
422 : struct lease_upgrade3_test lease_upgrade3_tests[NUM_UPGRADE3_TESTS] = {
423 : {"R", "R", "", "R" },
424 : {"R", "R", "R", "R" },
425 : {"R", "R", "RW", "R" },
426 : {"R", "R", "RH", "RH" },
427 : {"R", "R", "RHW", "R" },
428 :
429 : {"R", "RH", "", "R" },
430 : {"R", "RH", "R", "R" },
431 : {"R", "RH", "RW", "R" },
432 : {"R", "RH", "RH", "RH" },
433 : {"R", "RH", "RHW", "R" },
434 :
435 : {"RH", "R", "", "RH" },
436 : {"RH", "R", "R", "RH" },
437 : {"RH", "R", "RW", "RH" },
438 : {"RH", "R", "RH", "RH" },
439 : {"RH", "R", "RHW", "RH" },
440 :
441 : {"RH", "RH", "", "RH" },
442 : {"RH", "RH", "R", "RH" },
443 : {"RH", "RH", "RW", "RH" },
444 : {"RH", "RH", "RH", "RH" },
445 : {"RH", "RH", "RHW", "RH" },
446 : };
447 :
448 4 : static bool test_lease_upgrade3(struct torture_context *tctx,
449 : struct smb2_tree *tree)
450 : {
451 4 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
452 0 : struct smb2_handle h, h2, hnew;
453 0 : NTSTATUS status;
454 0 : struct smb2_create io;
455 0 : struct smb2_lease ls;
456 4 : const char *fname = "lease_upgrade3.dat";
457 4 : bool ret = true;
458 0 : int i;
459 0 : uint32_t caps;
460 :
461 4 : caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
462 4 : if (!(caps & SMB2_CAP_LEASING)) {
463 2 : torture_skip(tctx, "leases are not supported");
464 : }
465 :
466 2 : tree->session->transport->lease.handler = torture_lease_handler;
467 2 : tree->session->transport->lease.private_data = tree;
468 :
469 2 : smb2_util_unlink(tree, fname);
470 :
471 42 : for (i = 0; i < NUM_UPGRADE3_TESTS; i++) {
472 40 : struct lease_upgrade3_test t = lease_upgrade3_tests[i];
473 :
474 40 : smb2_util_unlink(tree, fname);
475 :
476 40 : torture_reset_lease_break_info(tctx, &lease_break_info);
477 :
478 : /* grab first lease */
479 40 : smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state(t.held1));
480 40 : status = smb2_create(tree, mem_ctx, &io);
481 40 : CHECK_STATUS(status, NT_STATUS_OK);
482 40 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
483 40 : CHECK_LEASE(&io, t.held1, true, LEASE1, 0);
484 40 : h = io.out.file.handle;
485 :
486 : /* grab second lease */
487 40 : smb2_lease_create(&io, &ls, false, fname, LEASE2, smb2_util_lease_state(t.held2));
488 40 : status = smb2_create(tree, mem_ctx, &io);
489 40 : CHECK_STATUS(status, NT_STATUS_OK);
490 40 : CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
491 40 : CHECK_LEASE(&io, t.held2, true, LEASE2, 0);
492 40 : h2 = io.out.file.handle;
493 :
494 : /* no break has happened */
495 40 : CHECK_VAL(lease_break_info.count, 0);
496 40 : CHECK_VAL(lease_break_info.failures, 0);
497 :
498 : /* try to upgrade lease1 */
499 40 : smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state(t.upgrade_to));
500 40 : status = smb2_create(tree, mem_ctx, &io);
501 40 : CHECK_STATUS(status, NT_STATUS_OK);
502 40 : CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
503 40 : CHECK_LEASE(&io, t.upgraded_to, true, LEASE1, 0);
504 40 : hnew = io.out.file.handle;
505 :
506 : /* no break has happened */
507 40 : CHECK_VAL(lease_break_info.count, 0);
508 40 : CHECK_VAL(lease_break_info.failures, 0);
509 :
510 40 : smb2_util_close(tree, hnew);
511 40 : smb2_util_close(tree, h);
512 40 : smb2_util_close(tree, h2);
513 : }
514 :
515 2 : done:
516 2 : smb2_util_close(tree, h);
517 2 : smb2_util_close(tree, hnew);
518 2 : smb2_util_close(tree, h2);
519 :
520 2 : smb2_util_unlink(tree, fname);
521 :
522 2 : talloc_free(mem_ctx);
523 :
524 2 : return ret;
525 : }
526 :
527 :
528 :
529 : /*
530 : break_results should be read as "held lease, new lease, hold broken to, new
531 : grant", i.e. { "RH", "RW", "RH", "R" } means that if key1 holds RH and key2
532 : tries for RW, key1 will be broken to RH (in this case, not broken at all)
533 : and key2 will be granted R.
534 :
535 : Note: break_results only includes things that Win7 will actually grant (see
536 : request_results above).
537 : */
538 : #define NBREAK_RESULTS 16
539 : static const char *break_results[NBREAK_RESULTS][4] = {
540 : {"R", "R", "R", "R"},
541 : {"R", "RH", "R", "RH"},
542 : {"R", "RW", "R", "R"},
543 : {"R", "RHW", "R", "RH"},
544 :
545 : {"RH", "R", "RH", "R"},
546 : {"RH", "RH", "RH", "RH"},
547 : {"RH", "RW", "RH", "R"},
548 : {"RH", "RHW", "RH", "RH"},
549 :
550 : {"RW", "R", "R", "R"},
551 : {"RW", "RH", "R", "RH"},
552 : {"RW", "RW", "R", "R"},
553 : {"RW", "RHW", "R", "RH"},
554 :
555 : {"RHW", "R", "RH", "R"},
556 : {"RHW", "RH", "RH", "RH"},
557 : {"RHW", "RW", "RH", "R"},
558 : {"RHW", "RHW", "RH", "RH"},
559 : };
560 :
561 4 : static bool test_lease_break(struct torture_context *tctx,
562 : struct smb2_tree *tree)
563 : {
564 4 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
565 0 : struct smb2_create io;
566 0 : struct smb2_lease ls;
567 0 : struct smb2_handle h, h2, h3;
568 0 : NTSTATUS status;
569 4 : const char *fname = "lease_break.dat";
570 4 : bool ret = true;
571 0 : int i;
572 0 : uint32_t caps;
573 :
574 4 : caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
575 4 : if (!(caps & SMB2_CAP_LEASING)) {
576 2 : torture_skip(tctx, "leases are not supported");
577 : }
578 :
579 2 : tree->session->transport->lease.handler = torture_lease_handler;
580 2 : tree->session->transport->lease.private_data = tree;
581 :
582 2 : smb2_util_unlink(tree, fname);
583 :
584 34 : for (i = 0; i < NBREAK_RESULTS; i++) {
585 32 : const char *held = break_results[i][0];
586 32 : const char *contend = break_results[i][1];
587 32 : const char *brokento = break_results[i][2];
588 32 : const char *granted = break_results[i][3];
589 32 : torture_comment(tctx, "Hold %s(%x), requesting %s(%x), "
590 : "expecting break to %s(%x) and grant of %s(%x)\n",
591 : held, smb2_util_lease_state(held), contend, smb2_util_lease_state(contend),
592 : brokento, smb2_util_lease_state(brokento), granted, smb2_util_lease_state(granted));
593 :
594 32 : torture_reset_lease_break_info(tctx, &lease_break_info);
595 :
596 : /* Grab lease. */
597 32 : smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state(held));
598 32 : status = smb2_create(tree, mem_ctx, &io);
599 32 : CHECK_STATUS(status, NT_STATUS_OK);
600 32 : h = io.out.file.handle;
601 32 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
602 32 : CHECK_LEASE(&io, held, true, LEASE1, 0);
603 :
604 : /* Possibly contend lease. */
605 32 : smb2_lease_create(&io, &ls, false, fname, LEASE2, smb2_util_lease_state(contend));
606 32 : status = smb2_create(tree, mem_ctx, &io);
607 32 : CHECK_STATUS(status, NT_STATUS_OK);
608 32 : h2 = io.out.file.handle;
609 32 : CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
610 32 : CHECK_LEASE(&io, granted, true, LEASE2, 0);
611 :
612 32 : if (smb2_util_lease_state(held) != smb2_util_lease_state(brokento)) {
613 16 : CHECK_BREAK_INFO(held, brokento, LEASE1);
614 : } else {
615 16 : CHECK_NO_BREAK(tctx);
616 : }
617 :
618 32 : torture_reset_lease_break_info(tctx, &lease_break_info);
619 :
620 : /*
621 : Now verify that an attempt to upgrade LEASE1 results in no
622 : break and no change in LEASE1.
623 : */
624 32 : smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state("RHW"));
625 32 : status = smb2_create(tree, mem_ctx, &io);
626 32 : CHECK_STATUS(status, NT_STATUS_OK);
627 32 : h3 = io.out.file.handle;
628 32 : CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
629 32 : CHECK_LEASE(&io, brokento, true, LEASE1, 0);
630 32 : CHECK_VAL(lease_break_info.count, 0);
631 32 : CHECK_VAL(lease_break_info.failures, 0);
632 :
633 32 : smb2_util_close(tree, h);
634 32 : smb2_util_close(tree, h2);
635 32 : smb2_util_close(tree, h3);
636 :
637 32 : status = smb2_util_unlink(tree, fname);
638 32 : CHECK_STATUS(status, NT_STATUS_OK);
639 : }
640 :
641 2 : done:
642 2 : smb2_util_close(tree, h);
643 2 : smb2_util_close(tree, h2);
644 :
645 2 : smb2_util_unlink(tree, fname);
646 :
647 2 : talloc_free(mem_ctx);
648 :
649 2 : return ret;
650 : }
651 :
652 4 : static bool test_lease_nobreakself(struct torture_context *tctx,
653 : struct smb2_tree *tree)
654 : {
655 4 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
656 0 : struct smb2_create io;
657 0 : struct smb2_lease ls;
658 4 : struct smb2_handle h1 = {{0}};
659 4 : struct smb2_handle h2 = {{0}};
660 0 : NTSTATUS status;
661 4 : const char *fname = "lease_nobreakself.dat";
662 4 : bool ret = true;
663 0 : uint32_t caps;
664 4 : char c = 0;
665 :
666 4 : caps = smb2cli_conn_server_capabilities(
667 4 : tree->session->transport->conn);
668 4 : if (!(caps & SMB2_CAP_LEASING)) {
669 2 : torture_skip(tctx, "leases are not supported");
670 : }
671 :
672 2 : smb2_util_unlink(tree, fname);
673 :
674 : /* Win7 is happy to grant RHW leases on files. */
675 2 : smb2_lease_create(&io, &ls, false, fname, LEASE1,
676 : smb2_util_lease_state("R"));
677 2 : status = smb2_create(tree, mem_ctx, &io);
678 2 : CHECK_STATUS(status, NT_STATUS_OK);
679 2 : h1 = io.out.file.handle;
680 2 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
681 2 : CHECK_LEASE(&io, "R", true, LEASE1, 0);
682 :
683 2 : smb2_lease_create(&io, &ls, false, fname, LEASE2,
684 : smb2_util_lease_state("R"));
685 2 : status = smb2_create(tree, mem_ctx, &io);
686 2 : CHECK_STATUS(status, NT_STATUS_OK);
687 2 : h2 = io.out.file.handle;
688 2 : CHECK_LEASE(&io, "R", true, LEASE2, 0);
689 :
690 2 : torture_reset_lease_break_info(tctx, &lease_break_info);
691 :
692 2 : tree->session->transport->lease.handler = torture_lease_handler;
693 2 : tree->session->transport->lease.private_data = tree;
694 :
695 : /* Make sure we don't break ourselves on write */
696 :
697 2 : status = smb2_util_write(tree, h1, &c, 0, 1);
698 2 : CHECK_STATUS(status, NT_STATUS_OK);
699 2 : CHECK_BREAK_INFO("R", "", LEASE2);
700 :
701 : /* Try the other way round. First, upgrade LEASE2 to R again */
702 :
703 2 : smb2_lease_create(&io, &ls, false, fname, LEASE2,
704 : smb2_util_lease_state("R"));
705 2 : status = smb2_create(tree, mem_ctx, &io);
706 2 : CHECK_STATUS(status, NT_STATUS_OK);
707 2 : CHECK_LEASE(&io, "R", true, LEASE2, 0);
708 2 : smb2_util_close(tree, io.out.file.handle);
709 :
710 : /* Now break LEASE1 via h2 */
711 :
712 2 : torture_reset_lease_break_info(tctx, &lease_break_info);
713 2 : status = smb2_util_write(tree, h2, &c, 0, 1);
714 2 : CHECK_STATUS(status, NT_STATUS_OK);
715 2 : CHECK_BREAK_INFO("R", "", LEASE1);
716 :
717 : /* .. and break LEASE2 via h1 */
718 :
719 2 : torture_reset_lease_break_info(tctx, &lease_break_info);
720 2 : status = smb2_util_write(tree, h1, &c, 0, 1);
721 2 : CHECK_STATUS(status, NT_STATUS_OK);
722 2 : CHECK_BREAK_INFO("R", "", LEASE2);
723 :
724 2 : done:
725 2 : smb2_util_close(tree, h2);
726 2 : smb2_util_close(tree, h1);
727 2 : smb2_util_unlink(tree, fname);
728 2 : talloc_free(mem_ctx);
729 2 : return ret;
730 : }
731 :
732 4 : static bool test_lease_statopen(struct torture_context *tctx,
733 : struct smb2_tree *tree)
734 : {
735 4 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
736 0 : struct smb2_create io;
737 0 : struct smb2_lease ls;
738 4 : struct smb2_handle h1 = {{0}};
739 4 : struct smb2_handle h2 = {{0}};
740 0 : NTSTATUS status;
741 4 : const char *fname = "lease_statopen.dat";
742 4 : bool ret = true;
743 0 : uint32_t caps;
744 :
745 4 : caps = smb2cli_conn_server_capabilities(
746 4 : tree->session->transport->conn);
747 4 : if (!(caps & SMB2_CAP_LEASING)) {
748 2 : torture_skip(tctx, "leases are not supported");
749 : }
750 :
751 2 : smb2_util_unlink(tree, fname);
752 :
753 : /* Create file. */
754 2 : smb2_lease_create(&io, &ls, false, fname, LEASE1,
755 : smb2_util_lease_state("RWH"));
756 2 : status = smb2_create(tree, mem_ctx, &io);
757 2 : CHECK_STATUS(status, NT_STATUS_OK);
758 2 : h1 = io.out.file.handle;
759 2 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
760 2 : CHECK_LEASE(&io, "RWH", true, LEASE1, 0);
761 2 : smb2_util_close(tree, h1);
762 :
763 : /* Stat open file with RWH lease. */
764 2 : smb2_lease_create_share(&io, &ls, false, fname, 0, LEASE1,
765 : smb2_util_lease_state("RWH"));
766 2 : io.in.desired_access = FILE_READ_ATTRIBUTES;
767 2 : status = smb2_create(tree, mem_ctx, &io);
768 2 : CHECK_STATUS(status, NT_STATUS_OK);
769 2 : h2 = io.out.file.handle;
770 2 : CHECK_LEASE(&io, "RWH", true, LEASE1, 0);
771 :
772 2 : torture_reset_lease_break_info(tctx, &lease_break_info);
773 :
774 2 : tree->session->transport->lease.handler = torture_lease_handler;
775 2 : tree->session->transport->lease.private_data = tree;
776 :
777 : /* Ensure non-stat open doesn't break and gets same lease
778 : state as existing stat open. */
779 2 : smb2_lease_create(&io, &ls, false, fname, LEASE1,
780 : smb2_util_lease_state(""));
781 2 : status = smb2_create(tree, mem_ctx, &io);
782 2 : CHECK_STATUS(status, NT_STATUS_OK);
783 2 : h1 = io.out.file.handle;
784 2 : CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
785 2 : CHECK_LEASE(&io, "RWH", true, LEASE1, 0);
786 :
787 2 : CHECK_NO_BREAK(tctx);
788 2 : smb2_util_close(tree, h1);
789 :
790 : /* Open with conflicting lease. stat open should break down to RH */
791 2 : smb2_lease_create(&io, &ls, false, fname, LEASE2,
792 : smb2_util_lease_state("RWH"));
793 2 : status = smb2_create(tree, mem_ctx, &io);
794 2 : CHECK_STATUS(status, NT_STATUS_OK);
795 2 : h1 = io.out.file.handle;
796 2 : CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
797 2 : CHECK_LEASE(&io, "RH", true, LEASE2, 0);
798 :
799 2 : CHECK_BREAK_INFO("RWH", "RH", LEASE1);
800 :
801 2 : done:
802 2 : smb2_util_close(tree, h2);
803 2 : smb2_util_close(tree, h1);
804 2 : smb2_util_unlink(tree, fname);
805 2 : talloc_free(mem_ctx);
806 2 : return ret;
807 : }
808 :
809 4 : static bool test_lease_statopen2(struct torture_context *tctx,
810 : struct smb2_tree *tree)
811 : {
812 4 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
813 0 : struct smb2_create io;
814 0 : struct smb2_lease ls;
815 4 : struct smb2_handle h1 = {{0}};
816 4 : struct smb2_handle h2 = {{0}};
817 4 : struct smb2_handle h3 = {{0}};
818 0 : NTSTATUS status;
819 4 : const char *fname = "lease_statopen2.dat";
820 4 : bool ret = true;
821 0 : uint32_t caps;
822 :
823 4 : caps = smb2cli_conn_server_capabilities(
824 4 : tree->session->transport->conn);
825 4 : if (!(caps & SMB2_CAP_LEASING)) {
826 2 : torture_skip(tctx, "leases are not supported");
827 : }
828 :
829 2 : smb2_util_unlink(tree, fname);
830 2 : torture_reset_lease_break_info(tctx, &lease_break_info);
831 2 : tree->session->transport->lease.handler = torture_lease_handler;
832 2 : tree->session->transport->lease.private_data = tree;
833 :
834 2 : status = torture_smb2_testfile(tree, fname, &h1);
835 2 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
836 : "smb2_create failed\n");
837 2 : smb2_util_close(tree, h1);
838 2 : ZERO_STRUCT(h1);
839 :
840 : /* Open file with RWH lease. */
841 2 : smb2_lease_create_share(&io, &ls, false, fname,
842 : smb2_util_share_access("RWD"),
843 : LEASE1,
844 : smb2_util_lease_state("RWH"));
845 2 : io.in.desired_access = SEC_FILE_WRITE_DATA;
846 2 : status = smb2_create(tree, mem_ctx, &io);
847 2 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
848 : "smb2_create failed\n");
849 2 : h1 = io.out.file.handle;
850 2 : CHECK_LEASE(&io, "RWH", true, LEASE1, 0);
851 :
852 : /* Stat open */
853 2 : ZERO_STRUCT(io);
854 2 : io.in.desired_access = FILE_READ_ATTRIBUTES;
855 2 : io.in.share_access = NTCREATEX_SHARE_ACCESS_MASK;
856 2 : io.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
857 2 : io.in.create_disposition = NTCREATEX_DISP_OPEN;
858 2 : io.in.fname = fname;
859 2 : status = smb2_create(tree, mem_ctx, &io);
860 2 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
861 : "smb2_create failed\n");
862 2 : h2 = io.out.file.handle;
863 :
864 : /* Open file with RWH lease. */
865 2 : smb2_lease_create_share(&io, &ls, false, fname,
866 : smb2_util_share_access("RWD"),
867 : LEASE1,
868 : smb2_util_lease_state("RWH"));
869 2 : io.in.desired_access = SEC_FILE_WRITE_DATA;
870 2 : status = smb2_create(tree, mem_ctx, &io);
871 2 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
872 : "smb2_create failed\n");
873 2 : h3 = io.out.file.handle;
874 2 : CHECK_LEASE(&io, "RWH", true, LEASE1, 0);
875 :
876 2 : done:
877 2 : if (!smb2_util_handle_empty(h3)) {
878 2 : smb2_util_close(tree, h3);
879 : }
880 2 : if (!smb2_util_handle_empty(h2)) {
881 2 : smb2_util_close(tree, h2);
882 : }
883 2 : if (!smb2_util_handle_empty(h1)) {
884 2 : smb2_util_close(tree, h1);
885 : }
886 2 : smb2_util_unlink(tree, fname);
887 2 : talloc_free(mem_ctx);
888 2 : return ret;
889 : }
890 :
891 4 : static bool test_lease_statopen3(struct torture_context *tctx,
892 : struct smb2_tree *tree)
893 : {
894 4 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
895 0 : struct smb2_create io;
896 0 : struct smb2_lease ls;
897 4 : struct smb2_handle h1 = {{0}};
898 4 : struct smb2_handle h2 = {{0}};
899 0 : NTSTATUS status;
900 4 : const char *fname = "lease_statopen3.dat";
901 4 : bool ret = true;
902 0 : uint32_t caps;
903 :
904 4 : caps = smb2cli_conn_server_capabilities(
905 4 : tree->session->transport->conn);
906 4 : if (!(caps & SMB2_CAP_LEASING)) {
907 2 : torture_skip(tctx, "leases are not supported");
908 : }
909 :
910 2 : smb2_util_unlink(tree, fname);
911 2 : torture_reset_lease_break_info(tctx, &lease_break_info);
912 2 : tree->session->transport->lease.handler = torture_lease_handler;
913 2 : tree->session->transport->lease.private_data = tree;
914 :
915 2 : status = torture_smb2_testfile(tree, fname, &h1);
916 2 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
917 : "smb2_create failed\n");
918 2 : smb2_util_close(tree, h1);
919 2 : ZERO_STRUCT(h1);
920 :
921 : /* Stat open */
922 2 : ZERO_STRUCT(io);
923 2 : io.in.desired_access = FILE_READ_ATTRIBUTES;
924 2 : io.in.share_access = NTCREATEX_SHARE_ACCESS_MASK;
925 2 : io.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
926 2 : io.in.create_disposition = NTCREATEX_DISP_OPEN;
927 2 : io.in.fname = fname;
928 2 : status = smb2_create(tree, mem_ctx, &io);
929 2 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
930 : "smb2_create failed\n");
931 2 : h1 = io.out.file.handle;
932 :
933 : /* Open file with RWH lease. */
934 2 : smb2_lease_create_share(&io, &ls, false, fname,
935 : smb2_util_share_access("RWD"),
936 : LEASE1,
937 : smb2_util_lease_state("RWH"));
938 2 : io.in.desired_access = SEC_FILE_WRITE_DATA;
939 2 : status = smb2_create(tree, mem_ctx, &io);
940 2 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
941 : "smb2_create failed\n");
942 2 : h2 = io.out.file.handle;
943 2 : CHECK_LEASE(&io, "RWH", true, LEASE1, 0);
944 :
945 2 : done:
946 2 : if (!smb2_util_handle_empty(h1)) {
947 2 : smb2_util_close(tree, h1);
948 : }
949 2 : if (!smb2_util_handle_empty(h2)) {
950 2 : smb2_util_close(tree, h2);
951 : }
952 2 : smb2_util_unlink(tree, fname);
953 2 : talloc_free(mem_ctx);
954 2 : return ret;
955 : }
956 :
957 24 : static bool test_lease_statopen4_do(struct torture_context *tctx,
958 : struct smb2_tree *tree,
959 : uint32_t access_mask,
960 : bool expect_stat_open)
961 : {
962 24 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
963 0 : struct smb2_create io;
964 0 : struct smb2_lease ls;
965 24 : struct smb2_handle h1 = {{0}};
966 24 : struct smb2_handle h2 = {{0}};
967 24 : struct smb2_handle h3 = {{0}};
968 0 : NTSTATUS status;
969 24 : const char *fname = "lease_statopen2.dat";
970 24 : bool ret = true;
971 :
972 : /* Open file with RWH lease. */
973 24 : smb2_lease_create_share(&io, &ls, false, fname,
974 : smb2_util_share_access("RWD"),
975 : LEASE1,
976 : smb2_util_lease_state("RWH"));
977 24 : io.in.desired_access = SEC_FILE_ALL;
978 24 : status = smb2_create(tree, mem_ctx, &io);
979 24 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
980 : "smb2_create failed\n");
981 24 : h1 = io.out.file.handle;
982 24 : CHECK_LEASE(&io, "RWH", true, LEASE1, 0);
983 :
984 : /* Stat open */
985 24 : ZERO_STRUCT(io);
986 24 : io.in.desired_access = access_mask;
987 24 : io.in.share_access = NTCREATEX_SHARE_ACCESS_MASK;
988 24 : io.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
989 24 : io.in.create_disposition = NTCREATEX_DISP_OPEN;
990 24 : io.in.fname = fname;
991 24 : status = smb2_create(tree, mem_ctx, &io);
992 24 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
993 : "smb2_create failed\n");
994 24 : h2 = io.out.file.handle;
995 :
996 24 : if (expect_stat_open) {
997 8 : CHECK_NO_BREAK(tctx);
998 8 : if (!ret) {
999 0 : goto done;
1000 : }
1001 : } else {
1002 16 : CHECK_VAL(lease_break_info.count, 1);
1003 16 : if (!ret) {
1004 0 : goto done;
1005 : }
1006 : /*
1007 : * Don't bother checking the lease state of an additional open
1008 : * below...
1009 : */
1010 16 : goto done;
1011 : }
1012 :
1013 : /* Open file with RWH lease. */
1014 8 : smb2_lease_create_share(&io, &ls, false, fname,
1015 : smb2_util_share_access("RWD"),
1016 : LEASE1,
1017 : smb2_util_lease_state("RWH"));
1018 8 : io.in.desired_access = SEC_FILE_WRITE_DATA;
1019 8 : status = smb2_create(tree, mem_ctx, &io);
1020 8 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1021 : "smb2_create failed\n");
1022 8 : h3 = io.out.file.handle;
1023 8 : CHECK_LEASE(&io, "RWH", true, LEASE1, 0);
1024 :
1025 8 : done:
1026 24 : if (!smb2_util_handle_empty(h3)) {
1027 8 : smb2_util_close(tree, h3);
1028 : }
1029 24 : if (!smb2_util_handle_empty(h2)) {
1030 24 : smb2_util_close(tree, h2);
1031 : }
1032 24 : if (!smb2_util_handle_empty(h1)) {
1033 24 : smb2_util_close(tree, h1);
1034 : }
1035 24 : talloc_free(mem_ctx);
1036 24 : return ret;
1037 : }
1038 :
1039 4 : static bool test_lease_statopen4(struct torture_context *tctx,
1040 : struct smb2_tree *tree)
1041 : {
1042 4 : const char *fname = "lease_statopen4.dat";
1043 4 : struct smb2_handle h1 = {{0}};
1044 0 : uint32_t caps;
1045 0 : size_t i;
1046 0 : NTSTATUS status;
1047 4 : bool ret = true;
1048 0 : struct {
1049 : uint32_t access_mask;
1050 : bool expect_stat_open;
1051 4 : } tests[] = {
1052 : {
1053 : .access_mask = FILE_READ_DATA,
1054 : .expect_stat_open = false,
1055 : },
1056 : {
1057 : .access_mask = FILE_WRITE_DATA,
1058 : .expect_stat_open = false,
1059 : },
1060 : {
1061 : .access_mask = FILE_READ_EA,
1062 : .expect_stat_open = false,
1063 : },
1064 : {
1065 : .access_mask = FILE_WRITE_EA,
1066 : .expect_stat_open = false,
1067 : },
1068 : {
1069 : .access_mask = FILE_EXECUTE,
1070 : .expect_stat_open = false,
1071 : },
1072 : {
1073 : .access_mask = FILE_READ_ATTRIBUTES,
1074 : .expect_stat_open = true,
1075 : },
1076 : {
1077 : .access_mask = FILE_WRITE_ATTRIBUTES,
1078 : .expect_stat_open = true,
1079 : },
1080 : {
1081 : .access_mask = DELETE_ACCESS,
1082 : .expect_stat_open = false,
1083 : },
1084 : {
1085 : .access_mask = READ_CONTROL_ACCESS,
1086 : .expect_stat_open = true,
1087 : },
1088 : {
1089 : .access_mask = WRITE_DAC_ACCESS,
1090 : .expect_stat_open = false,
1091 : },
1092 : {
1093 : .access_mask = WRITE_OWNER_ACCESS,
1094 : .expect_stat_open = false,
1095 : },
1096 : {
1097 : .access_mask = SYNCHRONIZE_ACCESS,
1098 : .expect_stat_open = true,
1099 : },
1100 : };
1101 :
1102 4 : caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
1103 4 : if (!(caps & SMB2_CAP_LEASING)) {
1104 2 : torture_skip(tctx, "leases are not supported");
1105 : }
1106 :
1107 2 : smb2_util_unlink(tree, fname);
1108 2 : tree->session->transport->lease.handler = torture_lease_handler;
1109 2 : tree->session->transport->lease.private_data = tree;
1110 :
1111 2 : status = torture_smb2_testfile(tree, fname, &h1);
1112 2 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1113 : "smb2_create failed\n");
1114 2 : smb2_util_close(tree, h1);
1115 2 : ZERO_STRUCT(h1);
1116 :
1117 26 : for (i = 0; i < ARRAY_SIZE(tests); i++) {
1118 24 : torture_reset_lease_break_info(tctx, &lease_break_info);
1119 :
1120 24 : ret = test_lease_statopen4_do(tctx,
1121 : tree,
1122 : tests[i].access_mask,
1123 24 : tests[i].expect_stat_open);
1124 24 : if (ret == true) {
1125 24 : continue;
1126 : }
1127 0 : torture_result(tctx, TORTURE_FAIL,
1128 : "test %zu: access_mask: %s, "
1129 : "expect_stat_open: %s\n",
1130 : i,
1131 : get_sec_mask_str(tree, tests[i].access_mask),
1132 0 : tests[i].expect_stat_open ? "yes" : "no");
1133 0 : goto done;
1134 : }
1135 :
1136 2 : done:
1137 2 : smb2_util_unlink(tree, fname);
1138 2 : return ret;
1139 : }
1140 :
1141 16 : static void torture_oplock_break_callback(struct smb2_request *req)
1142 : {
1143 0 : NTSTATUS status;
1144 0 : struct smb2_break br;
1145 :
1146 16 : ZERO_STRUCT(br);
1147 16 : status = smb2_break_recv(req, &br);
1148 16 : if (!NT_STATUS_IS_OK(status))
1149 0 : lease_break_info.oplock_failures++;
1150 :
1151 16 : return;
1152 : }
1153 :
1154 : /* a oplock break request handler */
1155 18 : static bool torture_oplock_handler(struct smb2_transport *transport,
1156 : const struct smb2_handle *handle,
1157 : uint8_t level, void *private_data)
1158 : {
1159 18 : struct smb2_tree *tree = private_data;
1160 0 : struct smb2_request *req;
1161 0 : struct smb2_break br;
1162 :
1163 18 : lease_break_info.oplock_handle = *handle;
1164 18 : lease_break_info.oplock_level = level;
1165 18 : lease_break_info.oplock_count++;
1166 :
1167 18 : ZERO_STRUCT(br);
1168 18 : br.in.file.handle = *handle;
1169 18 : br.in.oplock_level = level;
1170 :
1171 18 : if (lease_break_info.held_oplock_level > SMB2_OPLOCK_LEVEL_II) {
1172 16 : req = smb2_break_send(tree, &br);
1173 16 : req->async.fn = torture_oplock_break_callback;
1174 16 : req->async.private_data = NULL;
1175 : }
1176 18 : lease_break_info.held_oplock_level = level;
1177 :
1178 18 : return true;
1179 : }
1180 :
1181 : #define NOPLOCK_RESULTS 12
1182 : static const char *oplock_results[NOPLOCK_RESULTS][4] = {
1183 : {"R", "s", "R", "s"},
1184 : {"R", "x", "R", "s"},
1185 : {"R", "b", "R", "s"},
1186 :
1187 : {"RH", "s", "RH", ""},
1188 : {"RH", "x", "RH", ""},
1189 : {"RH", "b", "RH", ""},
1190 :
1191 : {"RW", "s", "R", "s"},
1192 : {"RW", "x", "R", "s"},
1193 : {"RW", "b", "R", "s"},
1194 :
1195 : {"RHW", "s", "RH", ""},
1196 : {"RHW", "x", "RH", ""},
1197 : {"RHW", "b", "RH", ""},
1198 : };
1199 :
1200 : static const char *oplock_results_2[NOPLOCK_RESULTS][4] = {
1201 : {"s", "R", "s", "R"},
1202 : {"s", "RH", "s", "R"},
1203 : {"s", "RW", "s", "R"},
1204 : {"s", "RHW", "s", "R"},
1205 :
1206 : {"x", "R", "s", "R"},
1207 : {"x", "RH", "s", "R"},
1208 : {"x", "RW", "s", "R"},
1209 : {"x", "RHW", "s", "R"},
1210 :
1211 : {"b", "R", "s", "R"},
1212 : {"b", "RH", "s", "R"},
1213 : {"b", "RW", "s", "R"},
1214 : {"b", "RHW", "s", "R"},
1215 : };
1216 :
1217 4 : static bool test_lease_oplock(struct torture_context *tctx,
1218 : struct smb2_tree *tree)
1219 : {
1220 4 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
1221 0 : struct smb2_create io;
1222 0 : struct smb2_lease ls;
1223 0 : struct smb2_handle h, h2;
1224 0 : NTSTATUS status;
1225 4 : const char *fname = "lease_oplock.dat";
1226 4 : bool ret = true;
1227 0 : int i;
1228 0 : uint32_t caps;
1229 :
1230 4 : caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
1231 4 : if (!(caps & SMB2_CAP_LEASING)) {
1232 2 : torture_skip(tctx, "leases are not supported");
1233 : }
1234 :
1235 2 : tree->session->transport->lease.handler = torture_lease_handler;
1236 2 : tree->session->transport->lease.private_data = tree;
1237 2 : tree->session->transport->oplock.handler = torture_oplock_handler;
1238 2 : tree->session->transport->oplock.private_data = tree;
1239 :
1240 2 : smb2_util_unlink(tree, fname);
1241 :
1242 26 : for (i = 0; i < NOPLOCK_RESULTS; i++) {
1243 24 : const char *held = oplock_results[i][0];
1244 24 : const char *contend = oplock_results[i][1];
1245 24 : const char *brokento = oplock_results[i][2];
1246 24 : const char *granted = oplock_results[i][3];
1247 48 : torture_comment(tctx, "Hold %s(%x), requesting %s(%x), "
1248 : "expecting break to %s(%x) and grant of %s(%x)\n",
1249 24 : held, smb2_util_lease_state(held), contend, smb2_util_oplock_level(contend),
1250 24 : brokento, smb2_util_lease_state(brokento), granted, smb2_util_oplock_level(granted));
1251 :
1252 24 : torture_reset_lease_break_info(tctx, &lease_break_info);
1253 :
1254 : /* Grab lease. */
1255 24 : smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state(held));
1256 24 : status = smb2_create(tree, mem_ctx, &io);
1257 24 : CHECK_STATUS(status, NT_STATUS_OK);
1258 24 : h = io.out.file.handle;
1259 24 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1260 24 : CHECK_LEASE(&io, held, true, LEASE1, 0);
1261 :
1262 : /* Does an oplock contend the lease? */
1263 24 : smb2_oplock_create(&io, fname, smb2_util_oplock_level(contend));
1264 24 : status = smb2_create(tree, mem_ctx, &io);
1265 24 : CHECK_STATUS(status, NT_STATUS_OK);
1266 24 : h2 = io.out.file.handle;
1267 24 : CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1268 24 : CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level(granted));
1269 24 : lease_break_info.held_oplock_level = io.out.oplock_level;
1270 :
1271 24 : if (smb2_util_lease_state(held) != smb2_util_lease_state(brokento)) {
1272 12 : CHECK_BREAK_INFO(held, brokento, LEASE1);
1273 : } else {
1274 12 : CHECK_NO_BREAK(tctx);
1275 : }
1276 :
1277 24 : smb2_util_close(tree, h);
1278 24 : smb2_util_close(tree, h2);
1279 :
1280 24 : status = smb2_util_unlink(tree, fname);
1281 24 : CHECK_STATUS(status, NT_STATUS_OK);
1282 : }
1283 :
1284 26 : for (i = 0; i < NOPLOCK_RESULTS; i++) {
1285 24 : const char *held = oplock_results_2[i][0];
1286 24 : const char *contend = oplock_results_2[i][1];
1287 24 : const char *brokento = oplock_results_2[i][2];
1288 24 : const char *granted = oplock_results_2[i][3];
1289 72 : torture_comment(tctx, "Hold %s(%x), requesting %s(%x), "
1290 : "expecting break to %s(%x) and grant of %s(%x)\n",
1291 24 : held, smb2_util_oplock_level(held), contend, smb2_util_lease_state(contend),
1292 24 : brokento, smb2_util_oplock_level(brokento), granted, smb2_util_lease_state(granted));
1293 :
1294 24 : torture_reset_lease_break_info(tctx, &lease_break_info);
1295 :
1296 : /* Grab an oplock. */
1297 24 : smb2_oplock_create(&io, fname, smb2_util_oplock_level(held));
1298 24 : status = smb2_create(tree, mem_ctx, &io);
1299 24 : CHECK_STATUS(status, NT_STATUS_OK);
1300 24 : h = io.out.file.handle;
1301 24 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1302 24 : CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level(held));
1303 24 : lease_break_info.held_oplock_level = io.out.oplock_level;
1304 :
1305 : /* Grab lease. */
1306 24 : smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state(contend));
1307 24 : status = smb2_create(tree, mem_ctx, &io);
1308 24 : CHECK_STATUS(status, NT_STATUS_OK);
1309 24 : h2 = io.out.file.handle;
1310 24 : CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1311 24 : CHECK_LEASE(&io, granted, true, LEASE1, 0);
1312 :
1313 24 : if (smb2_util_oplock_level(held) != smb2_util_oplock_level(brokento)) {
1314 16 : CHECK_OPLOCK_BREAK(brokento);
1315 : } else {
1316 8 : CHECK_NO_BREAK(tctx);
1317 : }
1318 :
1319 24 : smb2_util_close(tree, h);
1320 24 : smb2_util_close(tree, h2);
1321 :
1322 24 : status = smb2_util_unlink(tree, fname);
1323 24 : CHECK_STATUS(status, NT_STATUS_OK);
1324 : }
1325 :
1326 2 : done:
1327 2 : smb2_util_close(tree, h);
1328 2 : smb2_util_close(tree, h2);
1329 :
1330 2 : smb2_util_unlink(tree, fname);
1331 :
1332 2 : talloc_free(mem_ctx);
1333 :
1334 2 : return ret;
1335 : }
1336 :
1337 4 : static bool test_lease_multibreak(struct torture_context *tctx,
1338 : struct smb2_tree *tree)
1339 : {
1340 4 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
1341 0 : struct smb2_create io;
1342 0 : struct smb2_lease ls;
1343 4 : struct smb2_handle h = {{0}};
1344 4 : struct smb2_handle h2 = {{0}};
1345 4 : struct smb2_handle h3 = {{0}};
1346 0 : struct smb2_write w;
1347 0 : NTSTATUS status;
1348 4 : const char *fname = "lease_multibreak.dat";
1349 4 : bool ret = true;
1350 0 : uint32_t caps;
1351 :
1352 4 : caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
1353 4 : if (!(caps & SMB2_CAP_LEASING)) {
1354 2 : torture_skip(tctx, "leases are not supported");
1355 : }
1356 :
1357 2 : tree->session->transport->lease.handler = torture_lease_handler;
1358 2 : tree->session->transport->lease.private_data = tree;
1359 2 : tree->session->transport->oplock.handler = torture_oplock_handler;
1360 2 : tree->session->transport->oplock.private_data = tree;
1361 :
1362 2 : smb2_util_unlink(tree, fname);
1363 :
1364 2 : torture_reset_lease_break_info(tctx, &lease_break_info);
1365 :
1366 : /* Grab lease, upgrade to RHW .. */
1367 2 : smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state("RH"));
1368 2 : status = smb2_create(tree, mem_ctx, &io);
1369 2 : CHECK_STATUS(status, NT_STATUS_OK);
1370 2 : h = io.out.file.handle;
1371 2 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1372 2 : CHECK_LEASE(&io, "RH", true, LEASE1, 0);
1373 :
1374 2 : smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state("RHW"));
1375 2 : status = smb2_create(tree, mem_ctx, &io);
1376 2 : CHECK_STATUS(status, NT_STATUS_OK);
1377 2 : h2 = io.out.file.handle;
1378 2 : CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1379 2 : CHECK_LEASE(&io, "RHW", true, LEASE1, 0);
1380 :
1381 : /* Contend with LEASE2. */
1382 2 : smb2_lease_create(&io, &ls, false, fname, LEASE2, smb2_util_lease_state("RHW"));
1383 2 : status = smb2_create(tree, mem_ctx, &io);
1384 2 : CHECK_STATUS(status, NT_STATUS_OK);
1385 2 : h3 = io.out.file.handle;
1386 2 : CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1387 2 : CHECK_LEASE(&io, "RH", true, LEASE2, 0);
1388 :
1389 : /* Verify that we were only sent one break. */
1390 2 : CHECK_BREAK_INFO("RHW", "RH", LEASE1);
1391 :
1392 : /* Drop LEASE1 / LEASE2 */
1393 2 : status = smb2_util_close(tree, h);
1394 2 : CHECK_STATUS(status, NT_STATUS_OK);
1395 2 : status = smb2_util_close(tree, h2);
1396 2 : CHECK_STATUS(status, NT_STATUS_OK);
1397 2 : status = smb2_util_close(tree, h3);
1398 2 : CHECK_STATUS(status, NT_STATUS_OK);
1399 :
1400 2 : torture_reset_lease_break_info(tctx, &lease_break_info);
1401 :
1402 : /* Grab an R lease. */
1403 2 : smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state("R"));
1404 2 : status = smb2_create(tree, mem_ctx, &io);
1405 2 : CHECK_STATUS(status, NT_STATUS_OK);
1406 2 : h = io.out.file.handle;
1407 2 : CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1408 2 : CHECK_LEASE(&io, "R", true, LEASE1, 0);
1409 :
1410 : /* Grab a level-II oplock. */
1411 2 : smb2_oplock_create(&io, fname, smb2_util_oplock_level("s"));
1412 2 : status = smb2_create(tree, mem_ctx, &io);
1413 2 : CHECK_STATUS(status, NT_STATUS_OK);
1414 2 : h2 = io.out.file.handle;
1415 2 : CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1416 2 : CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("s"));
1417 2 : lease_break_info.held_oplock_level = io.out.oplock_level;
1418 :
1419 : /* Verify no breaks. */
1420 2 : CHECK_NO_BREAK(tctx);
1421 :
1422 : /* Open for truncate, force a break. */
1423 2 : smb2_generic_create(&io, NULL, false, fname,
1424 2 : NTCREATEX_DISP_OVERWRITE_IF, smb2_util_oplock_level(""), 0, 0);
1425 2 : status = smb2_create(tree, mem_ctx, &io);
1426 2 : CHECK_STATUS(status, NT_STATUS_OK);
1427 2 : h3 = io.out.file.handle;
1428 2 : CHECK_CREATED(&io, TRUNCATED, FILE_ATTRIBUTE_ARCHIVE);
1429 2 : CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level(""));
1430 2 : lease_break_info.held_oplock_level = io.out.oplock_level;
1431 :
1432 : /* Sleep, use a write to clear the recv queue. */
1433 2 : smb_msleep(250);
1434 2 : ZERO_STRUCT(w);
1435 2 : w.in.file.handle = h3;
1436 2 : w.in.offset = 0;
1437 2 : w.in.data = data_blob_talloc(mem_ctx, NULL, 4096);
1438 2 : memset(w.in.data.data, 'o', w.in.data.length);
1439 2 : status = smb2_write(tree, &w);
1440 2 : CHECK_STATUS(status, NT_STATUS_OK);
1441 :
1442 : /* Verify one oplock break, one lease break. */
1443 2 : CHECK_OPLOCK_BREAK("");
1444 2 : CHECK_BREAK_INFO("R", "", LEASE1);
1445 :
1446 2 : done:
1447 2 : smb2_util_close(tree, h);
1448 2 : smb2_util_close(tree, h2);
1449 2 : smb2_util_close(tree, h3);
1450 :
1451 2 : smb2_util_unlink(tree, fname);
1452 :
1453 2 : talloc_free(mem_ctx);
1454 :
1455 2 : return ret;
1456 : }
1457 :
1458 4 : static bool test_lease_v2_request_parent(struct torture_context *tctx,
1459 : struct smb2_tree *tree)
1460 : {
1461 4 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
1462 0 : struct smb2_create io;
1463 0 : struct smb2_lease ls;
1464 4 : struct smb2_handle h1 = {{0}};
1465 4 : uint64_t parent = LEASE2;
1466 0 : NTSTATUS status;
1467 4 : const char *fname = "lease_v2_request_parent.dat";
1468 4 : bool ret = true;
1469 0 : uint32_t caps;
1470 0 : enum protocol_types protocol;
1471 :
1472 4 : caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
1473 4 : if (!(caps & SMB2_CAP_LEASING)) {
1474 2 : torture_skip(tctx, "leases are not supported");
1475 : }
1476 2 : if (!(caps & SMB2_CAP_DIRECTORY_LEASING)) {
1477 2 : torture_skip(tctx, "directory leases are not supported");
1478 : }
1479 :
1480 0 : protocol = smbXcli_conn_protocol(tree->session->transport->conn);
1481 0 : if (protocol < PROTOCOL_SMB3_00) {
1482 0 : torture_skip(tctx, "v2 leases are not supported");
1483 : }
1484 :
1485 0 : smb2_util_unlink(tree, fname);
1486 :
1487 0 : torture_reset_lease_break_info(tctx, &lease_break_info);
1488 :
1489 0 : ZERO_STRUCT(io);
1490 0 : smb2_lease_v2_create_share(&io, &ls, false, fname,
1491 : smb2_util_share_access("RWD"),
1492 : LEASE1, &parent,
1493 : smb2_util_lease_state("RHW"),
1494 : 0x11);
1495 :
1496 0 : status = smb2_create(tree, mem_ctx, &io);
1497 0 : CHECK_STATUS(status, NT_STATUS_OK);
1498 0 : h1 = io.out.file.handle;
1499 0 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1500 0 : CHECK_LEASE_V2(&io, "RHW", true, LEASE1,
1501 : SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET, LEASE2,
1502 : ls.lease_epoch + 1);
1503 :
1504 0 : done:
1505 0 : smb2_util_close(tree, h1);
1506 0 : smb2_util_unlink(tree, fname);
1507 :
1508 0 : talloc_free(mem_ctx);
1509 :
1510 0 : return ret;
1511 : }
1512 :
1513 4 : static bool test_lease_break_twice(struct torture_context *tctx,
1514 : struct smb2_tree *tree)
1515 : {
1516 4 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
1517 0 : struct smb2_create io;
1518 0 : struct smb2_lease ls1;
1519 0 : struct smb2_lease ls2;
1520 4 : struct smb2_handle h1 = {{0}};
1521 0 : NTSTATUS status;
1522 4 : const char *fname = "lease_break_twice.dat";
1523 4 : bool ret = true;
1524 0 : uint32_t caps;
1525 0 : enum protocol_types protocol;
1526 :
1527 4 : caps = smb2cli_conn_server_capabilities(
1528 4 : tree->session->transport->conn);
1529 4 : if (!(caps & SMB2_CAP_LEASING)) {
1530 2 : torture_skip(tctx, "leases are not supported");
1531 : }
1532 :
1533 2 : protocol = smbXcli_conn_protocol(tree->session->transport->conn);
1534 2 : if (protocol < PROTOCOL_SMB3_00) {
1535 0 : torture_skip(tctx, "v2 leases are not supported");
1536 : }
1537 :
1538 2 : smb2_util_unlink(tree, fname);
1539 :
1540 2 : torture_reset_lease_break_info(tctx, &lease_break_info);
1541 2 : ZERO_STRUCT(io);
1542 :
1543 2 : smb2_lease_v2_create_share(
1544 : &io, &ls1, false, fname, smb2_util_share_access("RWD"),
1545 : LEASE1, NULL, smb2_util_lease_state("RWH"), 0x11);
1546 :
1547 2 : status = smb2_create(tree, mem_ctx, &io);
1548 2 : CHECK_STATUS(status, NT_STATUS_OK);
1549 2 : h1 = io.out.file.handle;
1550 2 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1551 2 : CHECK_LEASE_V2(&io, "RHW", true, LEASE1, 0, 0, ls1.lease_epoch + 1);
1552 :
1553 2 : tree->session->transport->lease.handler = torture_lease_handler;
1554 2 : tree->session->transport->lease.private_data = tree;
1555 :
1556 2 : torture_reset_lease_break_info(tctx, &lease_break_info);
1557 :
1558 2 : smb2_lease_v2_create_share(
1559 : &io, &ls2, false, fname, smb2_util_share_access("R"),
1560 : LEASE2, NULL, smb2_util_lease_state("RWH"), 0x22);
1561 :
1562 2 : status = smb2_create(tree, mem_ctx, &io);
1563 2 : CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
1564 2 : CHECK_BREAK_INFO_V2(tree->session->transport,
1565 : "RWH", "RW", LEASE1, ls1.lease_epoch + 2);
1566 :
1567 2 : smb2_lease_v2_create_share(
1568 : &io, &ls2, false, fname, smb2_util_share_access("RWD"),
1569 : LEASE2, NULL, smb2_util_lease_state("RWH"), 0x22);
1570 :
1571 2 : torture_reset_lease_break_info(tctx, &lease_break_info);
1572 :
1573 2 : status = smb2_create(tree, mem_ctx, &io);
1574 2 : CHECK_STATUS(status, NT_STATUS_OK);
1575 2 : CHECK_LEASE_V2(&io, "RH", true, LEASE2, 0, 0, ls2.lease_epoch + 1);
1576 2 : CHECK_BREAK_INFO_V2(tree->session->transport,
1577 : "RW", "R", LEASE1, ls1.lease_epoch + 3);
1578 :
1579 2 : done:
1580 2 : smb2_util_close(tree, h1);
1581 2 : smb2_util_unlink(tree, fname);
1582 2 : talloc_free(mem_ctx);
1583 2 : return ret;
1584 : }
1585 :
1586 4 : static bool test_lease_v2_request(struct torture_context *tctx,
1587 : struct smb2_tree *tree)
1588 : {
1589 4 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
1590 0 : struct smb2_create io;
1591 0 : struct smb2_lease ls1, ls2, ls2t, ls3, ls4;
1592 4 : struct smb2_handle h1 = {{0}};
1593 4 : struct smb2_handle h2 = {{0}};
1594 4 : struct smb2_handle h3 = {{0}};
1595 4 : struct smb2_handle h4 = {{0}};
1596 4 : struct smb2_handle h5 = {{0}};
1597 0 : struct smb2_write w;
1598 0 : NTSTATUS status;
1599 4 : const char *fname = "lease_v2_request.dat";
1600 4 : const char *dname = "lease_v2_request.dir";
1601 4 : const char *dnamefname = "lease_v2_request.dir\\lease.dat";
1602 4 : const char *dnamefname2 = "lease_v2_request.dir\\lease2.dat";
1603 4 : bool ret = true;
1604 0 : uint32_t caps;
1605 0 : enum protocol_types protocol;
1606 :
1607 4 : caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
1608 4 : if (!(caps & SMB2_CAP_LEASING)) {
1609 2 : torture_skip(tctx, "leases are not supported");
1610 : }
1611 2 : if (!(caps & SMB2_CAP_DIRECTORY_LEASING)) {
1612 2 : torture_skip(tctx, "directory leases are not supported");
1613 : }
1614 :
1615 0 : protocol = smbXcli_conn_protocol(tree->session->transport->conn);
1616 0 : if (protocol < PROTOCOL_SMB3_00) {
1617 0 : torture_skip(tctx, "v2 leases are not supported");
1618 : }
1619 :
1620 0 : smb2_util_unlink(tree, fname);
1621 0 : smb2_deltree(tree, dname);
1622 :
1623 0 : tree->session->transport->lease.handler = torture_lease_handler;
1624 0 : tree->session->transport->lease.private_data = tree;
1625 0 : tree->session->transport->oplock.handler = torture_oplock_handler;
1626 0 : tree->session->transport->oplock.private_data = tree;
1627 :
1628 0 : torture_reset_lease_break_info(tctx, &lease_break_info);
1629 :
1630 0 : ZERO_STRUCT(io);
1631 0 : smb2_lease_v2_create_share(&io, &ls1, false, fname,
1632 : smb2_util_share_access("RWD"),
1633 : LEASE1, NULL,
1634 : smb2_util_lease_state("RHW"),
1635 : 0x11);
1636 :
1637 0 : status = smb2_create(tree, mem_ctx, &io);
1638 0 : CHECK_STATUS(status, NT_STATUS_OK);
1639 0 : h1 = io.out.file.handle;
1640 0 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1641 0 : CHECK_LEASE_V2(&io, "RHW", true, LEASE1, 0, 0, ls1.lease_epoch + 1);
1642 :
1643 0 : ZERO_STRUCT(io);
1644 0 : smb2_lease_v2_create_share(&io, &ls2, true, dname,
1645 : smb2_util_share_access("RWD"),
1646 : LEASE2, NULL,
1647 : smb2_util_lease_state("RHW"),
1648 : 0x22);
1649 0 : status = smb2_create(tree, mem_ctx, &io);
1650 0 : CHECK_STATUS(status, NT_STATUS_OK);
1651 0 : h2 = io.out.file.handle;
1652 0 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_DIRECTORY);
1653 0 : CHECK_LEASE_V2(&io, "RH", true, LEASE2, 0, 0, ls2.lease_epoch + 1);
1654 :
1655 0 : ZERO_STRUCT(io);
1656 0 : smb2_lease_v2_create_share(&io, &ls3, false, dnamefname,
1657 : smb2_util_share_access("RWD"),
1658 : LEASE3, &LEASE2,
1659 : smb2_util_lease_state("RHW"),
1660 : 0x33);
1661 0 : status = smb2_create(tree, mem_ctx, &io);
1662 0 : CHECK_STATUS(status, NT_STATUS_OK);
1663 0 : h3 = io.out.file.handle;
1664 0 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1665 0 : CHECK_LEASE_V2(&io, "RHW", true, LEASE3,
1666 : SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET, LEASE2,
1667 : ls3.lease_epoch + 1);
1668 :
1669 0 : CHECK_NO_BREAK(tctx);
1670 :
1671 0 : ZERO_STRUCT(io);
1672 0 : smb2_lease_v2_create_share(&io, &ls4, false, dnamefname2,
1673 : smb2_util_share_access("RWD"),
1674 : LEASE4, NULL,
1675 : smb2_util_lease_state("RHW"),
1676 : 0x44);
1677 0 : status = smb2_create(tree, mem_ctx, &io);
1678 0 : CHECK_STATUS(status, NT_STATUS_OK);
1679 0 : h4 = io.out.file.handle;
1680 0 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1681 0 : CHECK_LEASE_V2(&io, "RHW", true, LEASE4, 0, 0, ls4.lease_epoch + 1);
1682 :
1683 0 : CHECK_BREAK_INFO_V2(tree->session->transport,
1684 : "RH", "", LEASE2, ls2.lease_epoch + 2);
1685 :
1686 0 : torture_reset_lease_break_info(tctx, &lease_break_info);
1687 :
1688 0 : ZERO_STRUCT(io);
1689 0 : smb2_lease_v2_create_share(&io, &ls2t, true, dname,
1690 : smb2_util_share_access("RWD"),
1691 : LEASE2, NULL,
1692 : smb2_util_lease_state("RHW"),
1693 : 0x222);
1694 0 : io.in.create_disposition = NTCREATEX_DISP_OPEN;
1695 0 : status = smb2_create(tree, mem_ctx, &io);
1696 0 : CHECK_STATUS(status, NT_STATUS_OK);
1697 0 : h5 = io.out.file.handle;
1698 0 : CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_DIRECTORY);
1699 0 : CHECK_LEASE_V2(&io, "RH", true, LEASE2, 0, 0, ls2.lease_epoch+3);
1700 0 : smb2_util_close(tree, h5);
1701 :
1702 0 : ZERO_STRUCT(w);
1703 0 : w.in.file.handle = h4;
1704 0 : w.in.offset = 0;
1705 0 : w.in.data = data_blob_talloc(mem_ctx, NULL, 4096);
1706 0 : memset(w.in.data.data, 'o', w.in.data.length);
1707 0 : status = smb2_write(tree, &w);
1708 0 : CHECK_STATUS(status, NT_STATUS_OK);
1709 :
1710 : /*
1711 : * Wait 4 seconds in order to check if the write time
1712 : * was updated (after 2 seconds).
1713 : */
1714 0 : smb_msleep(4000);
1715 0 : CHECK_NO_BREAK(tctx);
1716 :
1717 : /*
1718 : * only the close on the modified file break the
1719 : * directory lease.
1720 : */
1721 0 : smb2_util_close(tree, h4);
1722 :
1723 0 : CHECK_BREAK_INFO_V2(tree->session->transport,
1724 : "RH", "", LEASE2, ls2.lease_epoch+4);
1725 :
1726 0 : done:
1727 0 : smb2_util_close(tree, h1);
1728 0 : smb2_util_close(tree, h2);
1729 0 : smb2_util_close(tree, h3);
1730 0 : smb2_util_close(tree, h4);
1731 0 : smb2_util_close(tree, h5);
1732 :
1733 0 : smb2_util_unlink(tree, fname);
1734 0 : smb2_deltree(tree, dname);
1735 :
1736 0 : talloc_free(mem_ctx);
1737 :
1738 0 : return ret;
1739 : }
1740 :
1741 4 : static bool test_lease_v2_epoch1(struct torture_context *tctx,
1742 : struct smb2_tree *tree)
1743 : {
1744 4 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
1745 0 : struct smb2_create io;
1746 0 : struct smb2_lease ls;
1747 0 : struct smb2_handle h;
1748 4 : const char *fname = "lease_v2_epoch1.dat";
1749 4 : bool ret = true;
1750 0 : NTSTATUS status;
1751 0 : uint32_t caps;
1752 0 : enum protocol_types protocol;
1753 :
1754 4 : caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
1755 4 : if (!(caps & SMB2_CAP_LEASING)) {
1756 2 : torture_skip(tctx, "leases are not supported");
1757 : }
1758 :
1759 2 : protocol = smbXcli_conn_protocol(tree->session->transport->conn);
1760 2 : if (protocol < PROTOCOL_SMB3_00) {
1761 0 : torture_skip(tctx, "v2 leases are not supported");
1762 : }
1763 :
1764 2 : smb2_util_unlink(tree, fname);
1765 :
1766 2 : tree->session->transport->lease.handler = torture_lease_handler;
1767 2 : tree->session->transport->lease.private_data = tree;
1768 2 : tree->session->transport->oplock.handler = torture_oplock_handler;
1769 2 : tree->session->transport->oplock.private_data = tree;
1770 :
1771 2 : torture_reset_lease_break_info(tctx, &lease_break_info);
1772 :
1773 2 : ZERO_STRUCT(io);
1774 2 : smb2_lease_v2_create_share(&io, &ls, false, fname,
1775 : smb2_util_share_access("RWD"),
1776 : LEASE1, NULL,
1777 : smb2_util_lease_state("RHW"),
1778 : 0x4711);
1779 2 : status = smb2_create(tree, mem_ctx, &io);
1780 2 : CHECK_STATUS(status, NT_STATUS_OK);
1781 2 : h = io.out.file.handle;
1782 2 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1783 2 : CHECK_LEASE_V2(&io, "RHW", true, LEASE1, 0, 0, ls.lease_epoch + 1);
1784 2 : smb2_util_close(tree, h);
1785 2 : smb2_util_unlink(tree, fname);
1786 :
1787 2 : smb2_lease_v2_create_share(&io, &ls, false, fname,
1788 : smb2_util_share_access("RWD"),
1789 : LEASE1, NULL,
1790 : smb2_util_lease_state("RHW"),
1791 : 0x11);
1792 :
1793 2 : status = smb2_create(tree, mem_ctx, &io);
1794 2 : CHECK_STATUS(status, NT_STATUS_OK);
1795 2 : h = io.out.file.handle;
1796 2 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1797 2 : CHECK_LEASE_V2(&io, "RWH", true, LEASE1, 0, 0, ls.lease_epoch + 1);
1798 2 : smb2_util_close(tree, h);
1799 :
1800 2 : done:
1801 2 : smb2_util_unlink(tree, fname);
1802 2 : talloc_free(mem_ctx);
1803 2 : return ret;
1804 : }
1805 :
1806 4 : static bool test_lease_v2_epoch2(struct torture_context *tctx,
1807 : struct smb2_tree *tree)
1808 : {
1809 4 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
1810 0 : struct smb2_create io;
1811 0 : struct smb2_lease ls1v2, ls1v2t, ls1v1;
1812 4 : struct smb2_handle hv2 = {}, hv1 = {};
1813 4 : const char *fname = "lease_v2_epoch2.dat";
1814 4 : bool ret = true;
1815 0 : NTSTATUS status;
1816 0 : uint32_t caps;
1817 0 : enum protocol_types protocol;
1818 :
1819 4 : caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
1820 4 : if (!(caps & SMB2_CAP_LEASING)) {
1821 2 : torture_skip(tctx, "leases are not supported");
1822 : }
1823 :
1824 2 : protocol = smbXcli_conn_protocol(tree->session->transport->conn);
1825 2 : if (protocol < PROTOCOL_SMB3_00) {
1826 0 : torture_skip(tctx, "v2 leases are not supported");
1827 : }
1828 :
1829 2 : smb2_util_unlink(tree, fname);
1830 :
1831 2 : tree->session->transport->lease.handler = torture_lease_handler;
1832 2 : tree->session->transport->lease.private_data = tree;
1833 2 : tree->session->transport->oplock.handler = torture_oplock_handler;
1834 2 : tree->session->transport->oplock.private_data = tree;
1835 :
1836 2 : torture_reset_lease_break_info(tctx, &lease_break_info);
1837 :
1838 2 : ZERO_STRUCT(io);
1839 2 : smb2_lease_v2_create_share(&io, &ls1v2, false, fname,
1840 : smb2_util_share_access("RWD"),
1841 : LEASE1, NULL,
1842 : smb2_util_lease_state("R"),
1843 : 0x4711);
1844 2 : status = smb2_create(tree, mem_ctx, &io);
1845 2 : CHECK_STATUS(status, NT_STATUS_OK);
1846 2 : hv2 = io.out.file.handle;
1847 2 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1848 2 : CHECK_LEASE_V2(&io, "R", true, LEASE1, 0, 0, ls1v2.lease_epoch + 1);
1849 :
1850 2 : ZERO_STRUCT(io);
1851 2 : smb2_lease_create_share(&io, &ls1v1, false, fname,
1852 : smb2_util_share_access("RWD"),
1853 : LEASE1,
1854 : smb2_util_lease_state("RH"));
1855 2 : status = smb2_create(tree, mem_ctx, &io);
1856 2 : CHECK_STATUS(status, NT_STATUS_OK);
1857 2 : hv1 = io.out.file.handle;
1858 2 : CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1859 2 : CHECK_LEASE_V2(&io, "RH", true, LEASE1, 0, 0, ls1v2.lease_epoch + 2);
1860 :
1861 2 : smb2_util_close(tree, hv2);
1862 :
1863 2 : ZERO_STRUCT(io);
1864 2 : smb2_lease_v2_create_share(&io, &ls1v2t, false, fname,
1865 : smb2_util_share_access("RWD"),
1866 : LEASE1, NULL,
1867 : smb2_util_lease_state("RHW"),
1868 : 0x11);
1869 2 : status = smb2_create(tree, mem_ctx, &io);
1870 2 : CHECK_STATUS(status, NT_STATUS_OK);
1871 2 : hv2 = io.out.file.handle;
1872 2 : CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1873 2 : CHECK_LEASE_V2(&io, "RHW", true, LEASE1, 0, 0, ls1v2.lease_epoch + 3);
1874 :
1875 2 : smb2_util_close(tree, hv2);
1876 :
1877 2 : smb2_oplock_create(&io, fname, SMB2_OPLOCK_LEVEL_NONE);
1878 2 : status = smb2_create(tree, mem_ctx, &io);
1879 2 : CHECK_STATUS(status, NT_STATUS_OK);
1880 2 : hv2 = io.out.file.handle;
1881 2 : CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1882 2 : CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_NONE);
1883 :
1884 2 : CHECK_BREAK_INFO_V2(tree->session->transport,
1885 : "RWH", "RH", LEASE1, ls1v2.lease_epoch + 4);
1886 :
1887 2 : smb2_util_close(tree, hv2);
1888 2 : smb2_util_close(tree, hv1);
1889 :
1890 2 : ZERO_STRUCT(io);
1891 2 : smb2_lease_create_share(&io, &ls1v1, false, fname,
1892 : smb2_util_share_access("RWD"),
1893 : LEASE1,
1894 : smb2_util_lease_state("RHW"));
1895 2 : status = smb2_create(tree, mem_ctx, &io);
1896 2 : CHECK_STATUS(status, NT_STATUS_OK);
1897 2 : hv1 = io.out.file.handle;
1898 2 : CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1899 2 : CHECK_LEASE(&io, "RHW", true, LEASE1, 0);
1900 :
1901 2 : smb2_util_close(tree, hv1);
1902 :
1903 2 : done:
1904 2 : smb2_util_close(tree, hv2);
1905 2 : smb2_util_close(tree, hv1);
1906 2 : smb2_util_unlink(tree, fname);
1907 2 : talloc_free(mem_ctx);
1908 2 : return ret;
1909 : }
1910 :
1911 4 : static bool test_lease_v2_epoch3(struct torture_context *tctx,
1912 : struct smb2_tree *tree)
1913 : {
1914 4 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
1915 0 : struct smb2_create io;
1916 4 : struct smb2_lease ls1v1 = {}, ls1v1t = {},ls1v2 = {};
1917 4 : struct smb2_handle hv1 = {}, hv2 = {};
1918 4 : const char *fname = "lease_v2_epoch3.dat";
1919 4 : bool ret = true;
1920 0 : NTSTATUS status;
1921 0 : uint32_t caps;
1922 0 : enum protocol_types protocol;
1923 :
1924 4 : caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
1925 4 : if (!(caps & SMB2_CAP_LEASING)) {
1926 2 : torture_skip(tctx, "leases are not supported");
1927 : }
1928 :
1929 2 : protocol = smbXcli_conn_protocol(tree->session->transport->conn);
1930 2 : if (protocol < PROTOCOL_SMB3_00) {
1931 0 : torture_skip(tctx, "v2 leases are not supported");
1932 : }
1933 :
1934 2 : smb2_util_unlink(tree, fname);
1935 :
1936 2 : tree->session->transport->lease.handler = torture_lease_handler;
1937 2 : tree->session->transport->lease.private_data = tree;
1938 2 : tree->session->transport->oplock.handler = torture_oplock_handler;
1939 2 : tree->session->transport->oplock.private_data = tree;
1940 :
1941 2 : torture_reset_lease_break_info(tctx, &lease_break_info);
1942 :
1943 2 : ZERO_STRUCT(io);
1944 2 : smb2_lease_create_share(&io, &ls1v1, false, fname,
1945 : smb2_util_share_access("RWD"),
1946 : LEASE1,
1947 : smb2_util_lease_state("R"));
1948 2 : status = smb2_create(tree, mem_ctx, &io);
1949 2 : CHECK_STATUS(status, NT_STATUS_OK);
1950 2 : hv1 = io.out.file.handle;
1951 2 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1952 2 : CHECK_LEASE(&io, "R", true, LEASE1, 0);
1953 :
1954 2 : ZERO_STRUCT(io);
1955 2 : smb2_lease_v2_create_share(&io, &ls1v2, false, fname,
1956 : smb2_util_share_access("RWD"),
1957 : LEASE1, NULL,
1958 : smb2_util_lease_state("RW"),
1959 : 0x4711);
1960 2 : status = smb2_create(tree, mem_ctx, &io);
1961 2 : CHECK_STATUS(status, NT_STATUS_OK);
1962 2 : hv2 = io.out.file.handle;
1963 2 : CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1964 2 : CHECK_LEASE(&io, "RW", true, LEASE1, 0);
1965 :
1966 2 : smb2_util_close(tree, hv1);
1967 :
1968 2 : ZERO_STRUCT(io);
1969 2 : smb2_lease_create_share(&io, &ls1v1t, false, fname,
1970 : smb2_util_share_access("RWD"),
1971 : LEASE1,
1972 : smb2_util_lease_state("RWH"));
1973 2 : status = smb2_create(tree, mem_ctx, &io);
1974 2 : CHECK_STATUS(status, NT_STATUS_OK);
1975 2 : hv1 = io.out.file.handle;
1976 2 : CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1977 2 : CHECK_LEASE(&io, "RWH", true, LEASE1, 0);
1978 :
1979 2 : smb2_util_close(tree, hv1);
1980 :
1981 2 : smb2_oplock_create(&io, fname, SMB2_OPLOCK_LEVEL_NONE);
1982 2 : status = smb2_create(tree, mem_ctx, &io);
1983 2 : CHECK_STATUS(status, NT_STATUS_OK);
1984 2 : hv1 = io.out.file.handle;
1985 2 : CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1986 2 : CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_NONE);
1987 :
1988 2 : CHECK_BREAK_INFO("RWH", "RH", LEASE1);
1989 :
1990 2 : smb2_util_close(tree, hv1);
1991 2 : smb2_util_close(tree, hv2);
1992 :
1993 2 : ZERO_STRUCT(io);
1994 2 : smb2_lease_v2_create_share(&io, &ls1v2, false, fname,
1995 : smb2_util_share_access("RWD"),
1996 : LEASE1, NULL,
1997 : smb2_util_lease_state("RWH"),
1998 : 0x4711);
1999 2 : status = smb2_create(tree, mem_ctx, &io);
2000 2 : CHECK_STATUS(status, NT_STATUS_OK);
2001 2 : hv2 = io.out.file.handle;
2002 2 : CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
2003 2 : CHECK_LEASE_V2(&io, "RHW", true, LEASE1, 0, 0, ls1v2.lease_epoch + 1);
2004 2 : smb2_util_close(tree, hv2);
2005 :
2006 2 : done:
2007 2 : smb2_util_close(tree, hv2);
2008 2 : smb2_util_close(tree, hv1);
2009 2 : smb2_util_unlink(tree, fname);
2010 2 : talloc_free(mem_ctx);
2011 2 : return ret;
2012 : }
2013 :
2014 4 : static bool test_lease_breaking1(struct torture_context *tctx,
2015 : struct smb2_tree *tree)
2016 : {
2017 4 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
2018 4 : struct smb2_create io1 = {};
2019 4 : struct smb2_create io2 = {};
2020 4 : struct smb2_lease ls1 = {};
2021 4 : struct smb2_handle h1a = {};
2022 4 : struct smb2_handle h1b = {};
2023 4 : struct smb2_handle h2 = {};
2024 4 : struct smb2_request *req2 = NULL;
2025 4 : struct smb2_lease_break_ack ack = {};
2026 4 : const char *fname = "lease_breaking1.dat";
2027 4 : bool ret = true;
2028 0 : NTSTATUS status;
2029 0 : uint32_t caps;
2030 :
2031 4 : caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
2032 4 : if (!(caps & SMB2_CAP_LEASING)) {
2033 2 : torture_skip(tctx, "leases are not supported");
2034 : }
2035 :
2036 2 : smb2_util_unlink(tree, fname);
2037 :
2038 2 : tree->session->transport->lease.handler = torture_lease_handler;
2039 2 : tree->session->transport->lease.private_data = tree;
2040 2 : tree->session->transport->oplock.handler = torture_oplock_handler;
2041 2 : tree->session->transport->oplock.private_data = tree;
2042 :
2043 : /*
2044 : * we defer acking the lease break.
2045 : */
2046 2 : torture_reset_lease_break_info(tctx, &lease_break_info);
2047 2 : lease_break_info.lease_skip_ack = true;
2048 :
2049 2 : smb2_lease_create_share(&io1, &ls1, false, fname,
2050 : smb2_util_share_access("RWD"),
2051 : LEASE1,
2052 : smb2_util_lease_state("RWH"));
2053 2 : status = smb2_create(tree, mem_ctx, &io1);
2054 2 : CHECK_STATUS(status, NT_STATUS_OK);
2055 2 : h1a = io1.out.file.handle;
2056 2 : CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2057 2 : CHECK_LEASE(&io1, "RWH", true, LEASE1, 0);
2058 :
2059 : /*
2060 : * a conflicting open is blocked until we ack the
2061 : * lease break
2062 : */
2063 2 : smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE);
2064 2 : req2 = smb2_create_send(tree, &io2);
2065 2 : torture_assert(tctx, req2 != NULL, "smb2_create_send");
2066 :
2067 : /*
2068 : * we got the lease break, but defer the ack.
2069 : */
2070 2 : CHECK_BREAK_INFO("RWH", "RH", LEASE1);
2071 :
2072 2 : torture_assert(tctx, req2->state == SMB2_REQUEST_RECV, "req2 pending");
2073 :
2074 2 : ack.in.lease.lease_key =
2075 : lease_break_info.lease_break.current_lease.lease_key;
2076 2 : ack.in.lease.lease_state =
2077 2 : lease_break_info.lease_break.new_lease_state;
2078 2 : torture_reset_lease_break_info(tctx, &lease_break_info);
2079 :
2080 : /*
2081 : * a open using the same lease key is still works,
2082 : * but reports SMB2_LEASE_FLAG_BREAK_IN_PROGRESS
2083 : */
2084 2 : status = smb2_create(tree, mem_ctx, &io1);
2085 2 : CHECK_STATUS(status, NT_STATUS_OK);
2086 2 : h1b = io1.out.file.handle;
2087 2 : CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
2088 2 : CHECK_LEASE(&io1, "RWH", true, LEASE1, SMB2_LEASE_FLAG_BREAK_IN_PROGRESS);
2089 2 : smb2_util_close(tree, h1b);
2090 :
2091 2 : CHECK_NO_BREAK(tctx);
2092 :
2093 2 : torture_assert(tctx, req2->state == SMB2_REQUEST_RECV, "req2 pending");
2094 :
2095 : /*
2096 : * We ack the lease break.
2097 : */
2098 2 : status = smb2_lease_break_ack(tree, &ack);
2099 2 : CHECK_STATUS(status, NT_STATUS_OK);
2100 2 : CHECK_LEASE_BREAK_ACK(&ack, "RH", LEASE1);
2101 :
2102 2 : torture_assert(tctx, req2->cancel.can_cancel,
2103 : "req2 can_cancel");
2104 :
2105 2 : status = smb2_create_recv(req2, tctx, &io2);
2106 2 : CHECK_STATUS(status, NT_STATUS_OK);
2107 2 : h2 = io2.out.file.handle;
2108 2 : CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
2109 2 : CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_NONE);
2110 :
2111 2 : CHECK_NO_BREAK(tctx);
2112 2 : done:
2113 2 : smb2_util_close(tree, h1a);
2114 2 : smb2_util_close(tree, h1b);
2115 2 : smb2_util_close(tree, h2);
2116 2 : smb2_util_unlink(tree, fname);
2117 2 : talloc_free(mem_ctx);
2118 2 : return ret;
2119 : }
2120 :
2121 4 : static bool test_lease_breaking2(struct torture_context *tctx,
2122 : struct smb2_tree *tree)
2123 : {
2124 4 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
2125 4 : struct smb2_create io1 = {};
2126 4 : struct smb2_create io2 = {};
2127 4 : struct smb2_lease ls1 = {};
2128 4 : struct smb2_handle h1a = {};
2129 4 : struct smb2_handle h1b = {};
2130 4 : struct smb2_handle h2 = {};
2131 4 : struct smb2_request *req2 = NULL;
2132 4 : struct smb2_lease_break_ack ack = {};
2133 4 : const char *fname = "lease_breaking2.dat";
2134 4 : bool ret = true;
2135 0 : NTSTATUS status;
2136 0 : uint32_t caps;
2137 :
2138 4 : caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
2139 4 : if (!(caps & SMB2_CAP_LEASING)) {
2140 2 : torture_skip(tctx, "leases are not supported");
2141 : }
2142 :
2143 2 : smb2_util_unlink(tree, fname);
2144 :
2145 2 : tree->session->transport->lease.handler = torture_lease_handler;
2146 2 : tree->session->transport->lease.private_data = tree;
2147 2 : tree->session->transport->oplock.handler = torture_oplock_handler;
2148 2 : tree->session->transport->oplock.private_data = tree;
2149 :
2150 : /*
2151 : * we defer acking the lease break.
2152 : */
2153 2 : torture_reset_lease_break_info(tctx, &lease_break_info);
2154 2 : lease_break_info.lease_skip_ack = true;
2155 :
2156 2 : smb2_lease_create_share(&io1, &ls1, false, fname,
2157 : smb2_util_share_access("RWD"),
2158 : LEASE1,
2159 : smb2_util_lease_state("RWH"));
2160 2 : status = smb2_create(tree, mem_ctx, &io1);
2161 2 : CHECK_STATUS(status, NT_STATUS_OK);
2162 2 : h1a = io1.out.file.handle;
2163 2 : CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2164 2 : CHECK_LEASE(&io1, "RWH", true, LEASE1, 0);
2165 :
2166 : /*
2167 : * a conflicting open is blocked until we ack the
2168 : * lease break
2169 : */
2170 2 : smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE);
2171 2 : io2.in.create_disposition = NTCREATEX_DISP_OVERWRITE;
2172 2 : req2 = smb2_create_send(tree, &io2);
2173 2 : torture_assert(tctx, req2 != NULL, "smb2_create_send");
2174 :
2175 : /*
2176 : * we got the lease break, but defer the ack.
2177 : */
2178 2 : CHECK_BREAK_INFO("RWH", "", LEASE1);
2179 :
2180 2 : torture_assert(tctx, req2->state == SMB2_REQUEST_RECV, "req2 pending");
2181 :
2182 2 : ack.in.lease.lease_key =
2183 : lease_break_info.lease_break.current_lease.lease_key;
2184 2 : torture_reset_lease_break_info(tctx, &lease_break_info);
2185 :
2186 : /*
2187 : * a open using the same lease key is still works,
2188 : * but reports SMB2_LEASE_FLAG_BREAK_IN_PROGRESS
2189 : */
2190 2 : status = smb2_create(tree, mem_ctx, &io1);
2191 2 : CHECK_STATUS(status, NT_STATUS_OK);
2192 2 : h1b = io1.out.file.handle;
2193 2 : CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
2194 2 : CHECK_LEASE(&io1, "RWH", true, LEASE1, SMB2_LEASE_FLAG_BREAK_IN_PROGRESS);
2195 2 : smb2_util_close(tree, h1b);
2196 :
2197 2 : CHECK_NO_BREAK(tctx);
2198 :
2199 2 : torture_assert(tctx, req2->state == SMB2_REQUEST_RECV, "req2 pending");
2200 :
2201 : /*
2202 : * We ack the lease break.
2203 : */
2204 2 : ack.in.lease.lease_state =
2205 : SMB2_LEASE_READ | SMB2_LEASE_WRITE | SMB2_LEASE_HANDLE;
2206 2 : status = smb2_lease_break_ack(tree, &ack);
2207 2 : CHECK_STATUS(status, NT_STATUS_REQUEST_NOT_ACCEPTED);
2208 :
2209 2 : ack.in.lease.lease_state =
2210 : SMB2_LEASE_READ | SMB2_LEASE_WRITE;
2211 2 : status = smb2_lease_break_ack(tree, &ack);
2212 2 : CHECK_STATUS(status, NT_STATUS_REQUEST_NOT_ACCEPTED);
2213 :
2214 2 : ack.in.lease.lease_state =
2215 : SMB2_LEASE_WRITE | SMB2_LEASE_HANDLE;
2216 2 : status = smb2_lease_break_ack(tree, &ack);
2217 2 : CHECK_STATUS(status, NT_STATUS_REQUEST_NOT_ACCEPTED);
2218 :
2219 2 : ack.in.lease.lease_state =
2220 : SMB2_LEASE_READ | SMB2_LEASE_HANDLE;
2221 2 : status = smb2_lease_break_ack(tree, &ack);
2222 2 : CHECK_STATUS(status, NT_STATUS_REQUEST_NOT_ACCEPTED);
2223 :
2224 2 : ack.in.lease.lease_state = SMB2_LEASE_WRITE;
2225 2 : status = smb2_lease_break_ack(tree, &ack);
2226 2 : CHECK_STATUS(status, NT_STATUS_REQUEST_NOT_ACCEPTED);
2227 :
2228 2 : ack.in.lease.lease_state = SMB2_LEASE_HANDLE;
2229 2 : status = smb2_lease_break_ack(tree, &ack);
2230 2 : CHECK_STATUS(status, NT_STATUS_REQUEST_NOT_ACCEPTED);
2231 :
2232 2 : ack.in.lease.lease_state = SMB2_LEASE_READ;
2233 2 : status = smb2_lease_break_ack(tree, &ack);
2234 2 : CHECK_STATUS(status, NT_STATUS_REQUEST_NOT_ACCEPTED);
2235 :
2236 : /* Try again with the correct state this time. */
2237 2 : ack.in.lease.lease_state = SMB2_LEASE_NONE;;
2238 2 : status = smb2_lease_break_ack(tree, &ack);
2239 2 : CHECK_STATUS(status, NT_STATUS_OK);
2240 2 : CHECK_LEASE_BREAK_ACK(&ack, "", LEASE1);
2241 :
2242 2 : status = smb2_lease_break_ack(tree, &ack);
2243 2 : CHECK_STATUS(status, NT_STATUS_UNSUCCESSFUL);
2244 :
2245 2 : torture_assert(tctx, req2->cancel.can_cancel,
2246 : "req2 can_cancel");
2247 :
2248 2 : status = smb2_create_recv(req2, tctx, &io2);
2249 2 : CHECK_STATUS(status, NT_STATUS_OK);
2250 2 : h2 = io2.out.file.handle;
2251 2 : CHECK_CREATED(&io2, TRUNCATED, FILE_ATTRIBUTE_ARCHIVE);
2252 2 : CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_NONE);
2253 :
2254 2 : CHECK_NO_BREAK(tctx);
2255 :
2256 : /* Get state of the original handle. */
2257 2 : smb2_lease_create(&io1, &ls1, false, fname, LEASE1, smb2_util_lease_state(""));
2258 2 : status = smb2_create(tree, mem_ctx, &io1);
2259 2 : CHECK_STATUS(status, NT_STATUS_OK);
2260 2 : CHECK_LEASE(&io1, "", true, LEASE1, 0);
2261 2 : smb2_util_close(tree, io1.out.file.handle);
2262 :
2263 2 : done:
2264 2 : smb2_util_close(tree, h1a);
2265 2 : smb2_util_close(tree, h1b);
2266 2 : smb2_util_close(tree, h2);
2267 2 : smb2_util_unlink(tree, fname);
2268 2 : talloc_free(mem_ctx);
2269 2 : return ret;
2270 : }
2271 :
2272 4 : static bool test_lease_breaking3(struct torture_context *tctx,
2273 : struct smb2_tree *tree)
2274 : {
2275 4 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
2276 4 : struct smb2_create io1 = {};
2277 4 : struct smb2_create io2 = {};
2278 4 : struct smb2_create io3 = {};
2279 4 : struct smb2_lease ls1 = {};
2280 4 : struct smb2_handle h1a = {};
2281 4 : struct smb2_handle h1b = {};
2282 4 : struct smb2_handle h2 = {};
2283 4 : struct smb2_handle h3 = {};
2284 4 : struct smb2_request *req2 = NULL;
2285 4 : struct smb2_request *req3 = NULL;
2286 4 : struct lease_break_info lease_break_info_tmp = {};
2287 4 : struct smb2_lease_break_ack ack = {};
2288 4 : const char *fname = "lease_breaking3.dat";
2289 4 : bool ret = true;
2290 0 : NTSTATUS status;
2291 0 : uint32_t caps;
2292 :
2293 4 : caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
2294 4 : if (!(caps & SMB2_CAP_LEASING)) {
2295 2 : torture_skip(tctx, "leases are not supported");
2296 : }
2297 :
2298 2 : smb2_util_unlink(tree, fname);
2299 :
2300 2 : tree->session->transport->lease.handler = torture_lease_handler;
2301 2 : tree->session->transport->lease.private_data = tree;
2302 2 : tree->session->transport->oplock.handler = torture_oplock_handler;
2303 2 : tree->session->transport->oplock.private_data = tree;
2304 :
2305 : /*
2306 : * we defer acking the lease break.
2307 : */
2308 2 : torture_reset_lease_break_info(tctx, &lease_break_info);
2309 2 : lease_break_info.lease_skip_ack = true;
2310 :
2311 2 : smb2_lease_create_share(&io1, &ls1, false, fname,
2312 : smb2_util_share_access("RWD"),
2313 : LEASE1,
2314 : smb2_util_lease_state("RWH"));
2315 2 : status = smb2_create(tree, mem_ctx, &io1);
2316 2 : CHECK_STATUS(status, NT_STATUS_OK);
2317 2 : h1a = io1.out.file.handle;
2318 2 : CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2319 2 : CHECK_LEASE(&io1, "RWH", true, LEASE1, 0);
2320 :
2321 : /*
2322 : * a conflicting open is blocked until we ack the
2323 : * lease break
2324 : */
2325 2 : smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE);
2326 2 : req2 = smb2_create_send(tree, &io2);
2327 2 : torture_assert(tctx, req2 != NULL, "smb2_create_send");
2328 :
2329 : /*
2330 : * we got the lease break, but defer the ack.
2331 : */
2332 2 : CHECK_BREAK_INFO("RWH", "RH", LEASE1);
2333 :
2334 2 : torture_assert(tctx, req2->state == SMB2_REQUEST_RECV, "req2 pending");
2335 :
2336 : /*
2337 : * a open using the same lease key is still works,
2338 : * but reports SMB2_LEASE_FLAG_BREAK_IN_PROGRESS
2339 : */
2340 2 : status = smb2_create(tree, mem_ctx, &io1);
2341 2 : CHECK_STATUS(status, NT_STATUS_OK);
2342 2 : h1b = io1.out.file.handle;
2343 2 : CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
2344 2 : CHECK_LEASE(&io1, "RWH", true, LEASE1, SMB2_LEASE_FLAG_BREAK_IN_PROGRESS);
2345 2 : smb2_util_close(tree, h1b);
2346 :
2347 : /*
2348 : * a conflicting open with NTCREATEX_DISP_OVERWRITE
2349 : * doesn't trigger an immediate lease break to none.
2350 : */
2351 2 : lease_break_info_tmp = lease_break_info;
2352 2 : torture_reset_lease_break_info(tctx, &lease_break_info);
2353 2 : smb2_oplock_create(&io3, fname, SMB2_OPLOCK_LEVEL_NONE);
2354 2 : io3.in.create_disposition = NTCREATEX_DISP_OVERWRITE;
2355 2 : req3 = smb2_create_send(tree, &io3);
2356 2 : torture_assert(tctx, req3 != NULL, "smb2_create_send");
2357 2 : CHECK_NO_BREAK(tctx);
2358 2 : lease_break_info = lease_break_info_tmp;
2359 :
2360 2 : torture_assert(tctx, req3->state == SMB2_REQUEST_RECV, "req3 pending");
2361 :
2362 2 : ack.in.lease.lease_key =
2363 : lease_break_info.lease_break.current_lease.lease_key;
2364 2 : ack.in.lease.lease_state =
2365 2 : lease_break_info.lease_break.new_lease_state;
2366 2 : torture_reset_lease_break_info(tctx, &lease_break_info);
2367 :
2368 : /*
2369 : * a open using the same lease key is still works,
2370 : * but reports SMB2_LEASE_FLAG_BREAK_IN_PROGRESS
2371 : */
2372 2 : status = smb2_create(tree, mem_ctx, &io1);
2373 2 : CHECK_STATUS(status, NT_STATUS_OK);
2374 2 : h1b = io1.out.file.handle;
2375 2 : CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
2376 2 : CHECK_LEASE(&io1, "RWH", true, LEASE1, SMB2_LEASE_FLAG_BREAK_IN_PROGRESS);
2377 2 : smb2_util_close(tree, h1b);
2378 :
2379 2 : CHECK_NO_BREAK(tctx);
2380 :
2381 : /*
2382 : * We ack the lease break, but defer acking the next break (to "R")
2383 : */
2384 2 : lease_break_info.lease_skip_ack = true;
2385 2 : status = smb2_lease_break_ack(tree, &ack);
2386 2 : CHECK_STATUS(status, NT_STATUS_OK);
2387 2 : CHECK_LEASE_BREAK_ACK(&ack, "RH", LEASE1);
2388 :
2389 : /*
2390 : * We got an additional break downgrading to just "R"
2391 : * while we defer the ack.
2392 : */
2393 2 : CHECK_BREAK_INFO("RH", "R", LEASE1);
2394 :
2395 2 : ack.in.lease.lease_key =
2396 : lease_break_info.lease_break.current_lease.lease_key;
2397 2 : ack.in.lease.lease_state =
2398 2 : lease_break_info.lease_break.new_lease_state;
2399 2 : torture_reset_lease_break_info(tctx, &lease_break_info);
2400 :
2401 : /*
2402 : * a open using the same lease key is still works,
2403 : * but reports SMB2_LEASE_FLAG_BREAK_IN_PROGRESS
2404 : */
2405 2 : status = smb2_create(tree, mem_ctx, &io1);
2406 2 : CHECK_STATUS(status, NT_STATUS_OK);
2407 2 : h1b = io1.out.file.handle;
2408 2 : CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
2409 2 : CHECK_LEASE(&io1, "RH", true, LEASE1, SMB2_LEASE_FLAG_BREAK_IN_PROGRESS);
2410 2 : smb2_util_close(tree, h1b);
2411 :
2412 2 : CHECK_NO_BREAK(tctx);
2413 :
2414 2 : torture_assert(tctx, req2->state == SMB2_REQUEST_RECV, "req2 pending");
2415 2 : torture_assert(tctx, req3->state == SMB2_REQUEST_RECV, "req3 pending");
2416 :
2417 : /*
2418 : * We ack the downgrade to "R" and get an immediate break to none
2419 : */
2420 2 : status = smb2_lease_break_ack(tree, &ack);
2421 2 : CHECK_STATUS(status, NT_STATUS_OK);
2422 2 : CHECK_LEASE_BREAK_ACK(&ack, "R", LEASE1);
2423 :
2424 : /*
2425 : * We get the downgrade to none.
2426 : */
2427 2 : CHECK_BREAK_INFO("R", "", LEASE1);
2428 :
2429 2 : torture_assert(tctx, req2->cancel.can_cancel,
2430 : "req2 can_cancel");
2431 2 : torture_assert(tctx, req3->cancel.can_cancel,
2432 : "req3 can_cancel");
2433 :
2434 2 : torture_reset_lease_break_info(tctx, &lease_break_info);
2435 :
2436 2 : status = smb2_create_recv(req2, tctx, &io2);
2437 2 : CHECK_STATUS(status, NT_STATUS_OK);
2438 2 : h2 = io2.out.file.handle;
2439 2 : CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
2440 2 : CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_NONE);
2441 :
2442 2 : status = smb2_create_recv(req3, tctx, &io3);
2443 2 : CHECK_STATUS(status, NT_STATUS_OK);
2444 2 : h3 = io3.out.file.handle;
2445 2 : CHECK_CREATED(&io3, TRUNCATED, FILE_ATTRIBUTE_ARCHIVE);
2446 2 : CHECK_VAL(io3.out.oplock_level, SMB2_OPLOCK_LEVEL_NONE);
2447 :
2448 2 : CHECK_NO_BREAK(tctx);
2449 2 : done:
2450 2 : smb2_util_close(tree, h1a);
2451 2 : smb2_util_close(tree, h1b);
2452 2 : smb2_util_close(tree, h2);
2453 2 : smb2_util_close(tree, h3);
2454 :
2455 2 : smb2_util_unlink(tree, fname);
2456 2 : talloc_free(mem_ctx);
2457 2 : return ret;
2458 : }
2459 :
2460 4 : static bool test_lease_v2_breaking3(struct torture_context *tctx,
2461 : struct smb2_tree *tree)
2462 : {
2463 4 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
2464 4 : struct smb2_create io1 = {};
2465 4 : struct smb2_create io2 = {};
2466 4 : struct smb2_create io3 = {};
2467 4 : struct smb2_lease ls1 = {};
2468 4 : struct smb2_handle h1a = {};
2469 4 : struct smb2_handle h1b = {};
2470 4 : struct smb2_handle h2 = {};
2471 4 : struct smb2_handle h3 = {};
2472 4 : struct smb2_request *req2 = NULL;
2473 4 : struct smb2_request *req3 = NULL;
2474 4 : struct lease_break_info lease_break_info_tmp = {};
2475 4 : struct smb2_lease_break_ack ack = {};
2476 4 : const char *fname = "v2_lease_breaking3.dat";
2477 4 : bool ret = true;
2478 0 : NTSTATUS status;
2479 0 : uint32_t caps;
2480 0 : enum protocol_types protocol;
2481 :
2482 4 : caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
2483 4 : if (!(caps & SMB2_CAP_LEASING)) {
2484 2 : torture_skip(tctx, "leases are not supported");
2485 : }
2486 :
2487 2 : protocol = smbXcli_conn_protocol(tree->session->transport->conn);
2488 2 : if (protocol < PROTOCOL_SMB3_00) {
2489 0 : torture_skip(tctx, "v2 leases are not supported");
2490 : }
2491 :
2492 2 : smb2_util_unlink(tree, fname);
2493 :
2494 2 : tree->session->transport->lease.handler = torture_lease_handler;
2495 2 : tree->session->transport->lease.private_data = tree;
2496 2 : tree->session->transport->oplock.handler = torture_oplock_handler;
2497 2 : tree->session->transport->oplock.private_data = tree;
2498 :
2499 : /*
2500 : * we defer acking the lease break.
2501 : */
2502 2 : torture_reset_lease_break_info(tctx, &lease_break_info);
2503 2 : lease_break_info.lease_skip_ack = true;
2504 :
2505 2 : smb2_lease_v2_create_share(&io1, &ls1, false, fname,
2506 : smb2_util_share_access("RWD"),
2507 : LEASE1, NULL,
2508 : smb2_util_lease_state("RHW"),
2509 : 0x11);
2510 2 : status = smb2_create(tree, mem_ctx, &io1);
2511 2 : CHECK_STATUS(status, NT_STATUS_OK);
2512 2 : h1a = io1.out.file.handle;
2513 2 : CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2514 : /* Epoch increases on open. */
2515 2 : ls1.lease_epoch += 1;
2516 2 : CHECK_LEASE_V2(&io1, "RHW", true, LEASE1, 0, 0, ls1.lease_epoch);
2517 :
2518 : /*
2519 : * a conflicting open is blocked until we ack the
2520 : * lease break
2521 : */
2522 2 : smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE);
2523 2 : req2 = smb2_create_send(tree, &io2);
2524 2 : torture_assert(tctx, req2 != NULL, "smb2_create_send");
2525 :
2526 : /*
2527 : * we got the lease break, but defer the ack.
2528 : */
2529 2 : CHECK_BREAK_INFO_V2(tree->session->transport,
2530 : "RWH", "RH", LEASE1, ls1.lease_epoch + 1);
2531 :
2532 2 : torture_assert(tctx, req2->state == SMB2_REQUEST_RECV, "req2 pending");
2533 :
2534 : /* On receiving a lease break, we must sync the new epoch. */
2535 2 : ls1.lease_epoch = lease_break_info.lease_break.new_epoch;
2536 :
2537 : /*
2538 : * a open using the same lease key is still works,
2539 : * but reports SMB2_LEASE_FLAG_BREAK_IN_PROGRESS
2540 : */
2541 2 : status = smb2_create(tree, mem_ctx, &io1);
2542 2 : CHECK_STATUS(status, NT_STATUS_OK);
2543 2 : h1b = io1.out.file.handle;
2544 2 : CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
2545 2 : CHECK_LEASE_V2(&io1, "RHW", true, LEASE1, SMB2_LEASE_FLAG_BREAK_IN_PROGRESS, 0, ls1.lease_epoch);
2546 2 : smb2_util_close(tree, h1b);
2547 :
2548 : /*
2549 : * a conflicting open with NTCREATEX_DISP_OVERWRITE
2550 : * doesn't trigger an immediate lease break to none.
2551 : */
2552 2 : lease_break_info_tmp = lease_break_info;
2553 2 : torture_reset_lease_break_info(tctx, &lease_break_info);
2554 2 : smb2_oplock_create(&io3, fname, SMB2_OPLOCK_LEVEL_NONE);
2555 2 : io3.in.create_disposition = NTCREATEX_DISP_OVERWRITE;
2556 2 : req3 = smb2_create_send(tree, &io3);
2557 2 : torture_assert(tctx, req3 != NULL, "smb2_create_send");
2558 2 : CHECK_NO_BREAK(tctx);
2559 2 : lease_break_info = lease_break_info_tmp;
2560 :
2561 2 : torture_assert(tctx, req3->state == SMB2_REQUEST_RECV, "req3 pending");
2562 :
2563 2 : ack.in.lease.lease_key =
2564 : lease_break_info.lease_break.current_lease.lease_key;
2565 2 : ack.in.lease.lease_state =
2566 2 : lease_break_info.lease_break.new_lease_state;
2567 2 : torture_reset_lease_break_info(tctx, &lease_break_info);
2568 :
2569 : /*
2570 : * a open using the same lease key is still works,
2571 : * but reports SMB2_LEASE_FLAG_BREAK_IN_PROGRESS
2572 : */
2573 2 : status = smb2_create(tree, mem_ctx, &io1);
2574 2 : CHECK_STATUS(status, NT_STATUS_OK);
2575 2 : h1b = io1.out.file.handle;
2576 2 : CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
2577 2 : CHECK_LEASE_V2(&io1, "RHW", true, LEASE1, SMB2_LEASE_FLAG_BREAK_IN_PROGRESS, 0, ls1.lease_epoch);
2578 2 : smb2_util_close(tree, h1b);
2579 :
2580 2 : CHECK_NO_BREAK(tctx);
2581 :
2582 : /*
2583 : * We ack the lease break, but defer acking the next break (to "R")
2584 : */
2585 2 : lease_break_info.lease_skip_ack = true;
2586 2 : status = smb2_lease_break_ack(tree, &ack);
2587 2 : CHECK_STATUS(status, NT_STATUS_OK);
2588 2 : CHECK_LEASE_BREAK_ACK(&ack, "RH", LEASE1);
2589 :
2590 : /*
2591 : * We got an additional break downgrading to just "R"
2592 : * while we defer the ack.
2593 : */
2594 2 : CHECK_BREAK_INFO_V2(tree->session->transport,
2595 : "RH", "R", LEASE1, ls1.lease_epoch);
2596 : /* On receiving a lease break, we must sync the new epoch. */
2597 2 : ls1.lease_epoch = lease_break_info.lease_break.new_epoch;
2598 :
2599 2 : ack.in.lease.lease_key =
2600 : lease_break_info.lease_break.current_lease.lease_key;
2601 2 : ack.in.lease.lease_state =
2602 2 : lease_break_info.lease_break.new_lease_state;
2603 2 : torture_reset_lease_break_info(tctx, &lease_break_info);
2604 :
2605 : /*
2606 : * a open using the same lease key is still works,
2607 : * but reports SMB2_LEASE_FLAG_BREAK_IN_PROGRESS
2608 : */
2609 2 : status = smb2_create(tree, mem_ctx, &io1);
2610 2 : CHECK_STATUS(status, NT_STATUS_OK);
2611 2 : h1b = io1.out.file.handle;
2612 2 : CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
2613 2 : CHECK_LEASE_V2(&io1, "RH", true, LEASE1, SMB2_LEASE_FLAG_BREAK_IN_PROGRESS, 0, ls1.lease_epoch);
2614 2 : smb2_util_close(tree, h1b);
2615 :
2616 2 : CHECK_NO_BREAK(tctx);
2617 :
2618 2 : torture_assert(tctx, req2->state == SMB2_REQUEST_RECV, "req2 pending");
2619 2 : torture_assert(tctx, req3->state == SMB2_REQUEST_RECV, "req3 pending");
2620 :
2621 : /*
2622 : * We ack the downgrade to "R" and get an immediate break to none
2623 : */
2624 2 : status = smb2_lease_break_ack(tree, &ack);
2625 2 : CHECK_STATUS(status, NT_STATUS_OK);
2626 2 : CHECK_LEASE_BREAK_ACK(&ack, "R", LEASE1);
2627 :
2628 : /*
2629 : * We get the downgrade to none.
2630 : */
2631 2 : CHECK_BREAK_INFO_V2(tree->session->transport,
2632 : "R", "", LEASE1, ls1.lease_epoch);
2633 :
2634 2 : torture_assert(tctx, req2->cancel.can_cancel,
2635 : "req2 can_cancel");
2636 2 : torture_assert(tctx, req3->cancel.can_cancel,
2637 : "req3 can_cancel");
2638 :
2639 2 : torture_reset_lease_break_info(tctx, &lease_break_info);
2640 :
2641 2 : status = smb2_create_recv(req2, tctx, &io2);
2642 2 : CHECK_STATUS(status, NT_STATUS_OK);
2643 2 : h2 = io2.out.file.handle;
2644 2 : CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
2645 2 : CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_NONE);
2646 :
2647 2 : status = smb2_create_recv(req3, tctx, &io3);
2648 2 : CHECK_STATUS(status, NT_STATUS_OK);
2649 2 : h3 = io3.out.file.handle;
2650 2 : CHECK_CREATED(&io3, TRUNCATED, FILE_ATTRIBUTE_ARCHIVE);
2651 2 : CHECK_VAL(io3.out.oplock_level, SMB2_OPLOCK_LEVEL_NONE);
2652 :
2653 2 : CHECK_NO_BREAK(tctx);
2654 2 : done:
2655 2 : smb2_util_close(tree, h1a);
2656 2 : smb2_util_close(tree, h1b);
2657 2 : smb2_util_close(tree, h2);
2658 2 : smb2_util_close(tree, h3);
2659 :
2660 2 : smb2_util_unlink(tree, fname);
2661 2 : talloc_free(mem_ctx);
2662 2 : return ret;
2663 : }
2664 :
2665 :
2666 4 : static bool test_lease_breaking4(struct torture_context *tctx,
2667 : struct smb2_tree *tree)
2668 : {
2669 4 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
2670 4 : struct smb2_create io1 = {};
2671 4 : struct smb2_create io2 = {};
2672 4 : struct smb2_create io3 = {};
2673 4 : struct smb2_lease ls1 = {};
2674 4 : struct smb2_lease ls1t = {};
2675 4 : struct smb2_handle h1 = {};
2676 4 : struct smb2_handle h2 = {};
2677 4 : struct smb2_handle h3 = {};
2678 4 : struct smb2_request *req2 = NULL;
2679 4 : struct lease_break_info lease_break_info_tmp = {};
2680 4 : struct smb2_lease_break_ack ack = {};
2681 4 : const char *fname = "lease_breaking4.dat";
2682 4 : bool ret = true;
2683 0 : NTSTATUS status;
2684 0 : uint32_t caps;
2685 :
2686 4 : caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
2687 4 : if (!(caps & SMB2_CAP_LEASING)) {
2688 2 : torture_skip(tctx, "leases are not supported");
2689 : }
2690 :
2691 2 : smb2_util_unlink(tree, fname);
2692 :
2693 2 : tree->session->transport->lease.handler = torture_lease_handler;
2694 2 : tree->session->transport->lease.private_data = tree;
2695 2 : tree->session->transport->oplock.handler = torture_oplock_handler;
2696 2 : tree->session->transport->oplock.private_data = tree;
2697 :
2698 : /*
2699 : * we defer acking the lease break.
2700 : */
2701 2 : torture_reset_lease_break_info(tctx, &lease_break_info);
2702 2 : lease_break_info.lease_skip_ack = true;
2703 :
2704 2 : smb2_lease_create_share(&io1, &ls1, false, fname,
2705 : smb2_util_share_access("RWD"),
2706 : LEASE1,
2707 : smb2_util_lease_state("RH"));
2708 2 : status = smb2_create(tree, mem_ctx, &io1);
2709 2 : CHECK_STATUS(status, NT_STATUS_OK);
2710 2 : h1 = io1.out.file.handle;
2711 2 : CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2712 2 : CHECK_LEASE(&io1, "RH", true, LEASE1, 0);
2713 :
2714 2 : CHECK_NO_BREAK(tctx);
2715 :
2716 : /*
2717 : * a conflicting open is *not* blocked until we ack the
2718 : * lease break
2719 : */
2720 2 : smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE);
2721 2 : io2.in.create_disposition = NTCREATEX_DISP_OVERWRITE;
2722 2 : req2 = smb2_create_send(tree, &io2);
2723 2 : torture_assert(tctx, req2 != NULL, "smb2_create_send");
2724 :
2725 : /*
2726 : * We got a break from RH to NONE, we're supported to ack
2727 : * this downgrade
2728 : */
2729 2 : CHECK_BREAK_INFO("RH", "", LEASE1);
2730 :
2731 2 : lease_break_info_tmp = lease_break_info;
2732 2 : torture_reset_lease_break_info(tctx, &lease_break_info);
2733 2 : CHECK_NO_BREAK(tctx);
2734 :
2735 2 : torture_assert(tctx, req2->state == SMB2_REQUEST_DONE, "req2 done");
2736 :
2737 2 : status = smb2_create_recv(req2, tctx, &io2);
2738 2 : CHECK_STATUS(status, NT_STATUS_OK);
2739 2 : h2 = io2.out.file.handle;
2740 2 : CHECK_CREATED(&io2, TRUNCATED, FILE_ATTRIBUTE_ARCHIVE);
2741 2 : CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_NONE);
2742 2 : smb2_util_close(tree, h2);
2743 :
2744 2 : CHECK_NO_BREAK(tctx);
2745 :
2746 : /*
2747 : * a conflicting open is *not* blocked until we ack the
2748 : * lease break, even if the lease is in breaking state.
2749 : */
2750 2 : smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE);
2751 2 : io2.in.create_disposition = NTCREATEX_DISP_OVERWRITE;
2752 2 : req2 = smb2_create_send(tree, &io2);
2753 2 : torture_assert(tctx, req2 != NULL, "smb2_create_send");
2754 :
2755 2 : CHECK_NO_BREAK(tctx);
2756 :
2757 2 : torture_assert(tctx, req2->state == SMB2_REQUEST_DONE, "req2 done");
2758 :
2759 2 : status = smb2_create_recv(req2, tctx, &io2);
2760 2 : CHECK_STATUS(status, NT_STATUS_OK);
2761 2 : h2 = io2.out.file.handle;
2762 2 : CHECK_CREATED(&io2, TRUNCATED, FILE_ATTRIBUTE_ARCHIVE);
2763 2 : CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_NONE);
2764 2 : smb2_util_close(tree, h2);
2765 :
2766 2 : CHECK_NO_BREAK(tctx);
2767 :
2768 : /*
2769 : * We now ask the server about the current lease state
2770 : * which should still be "RH", but with
2771 : * SMB2_LEASE_FLAG_BREAK_IN_PROGRESS.
2772 : */
2773 2 : smb2_lease_create_share(&io3, &ls1t, false, fname,
2774 : smb2_util_share_access("RWD"),
2775 : LEASE1,
2776 : smb2_util_lease_state(""));
2777 2 : status = smb2_create(tree, mem_ctx, &io3);
2778 2 : CHECK_STATUS(status, NT_STATUS_OK);
2779 2 : h3 = io3.out.file.handle;
2780 2 : CHECK_CREATED(&io3, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
2781 2 : CHECK_LEASE(&io3, "RH", true, LEASE1, SMB2_LEASE_FLAG_BREAK_IN_PROGRESS);
2782 :
2783 : /*
2784 : * We finally ack the lease break...
2785 : */
2786 2 : CHECK_NO_BREAK(tctx);
2787 2 : lease_break_info = lease_break_info_tmp;
2788 2 : ack.in.lease.lease_key =
2789 : lease_break_info.lease_break.current_lease.lease_key;
2790 2 : ack.in.lease.lease_state =
2791 2 : lease_break_info.lease_break.new_lease_state;
2792 2 : torture_reset_lease_break_info(tctx, &lease_break_info);
2793 2 : lease_break_info.lease_skip_ack = true;
2794 :
2795 2 : status = smb2_lease_break_ack(tree, &ack);
2796 2 : CHECK_STATUS(status, NT_STATUS_OK);
2797 2 : CHECK_LEASE_BREAK_ACK(&ack, "", LEASE1);
2798 :
2799 2 : CHECK_NO_BREAK(tctx);
2800 :
2801 2 : done:
2802 2 : smb2_util_close(tree, h1);
2803 2 : smb2_util_close(tree, h2);
2804 2 : smb2_util_close(tree, h3);
2805 :
2806 2 : smb2_util_unlink(tree, fname);
2807 2 : talloc_free(mem_ctx);
2808 2 : return ret;
2809 : }
2810 :
2811 4 : static bool test_lease_breaking5(struct torture_context *tctx,
2812 : struct smb2_tree *tree)
2813 : {
2814 4 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
2815 4 : struct smb2_create io1 = {};
2816 4 : struct smb2_create io2 = {};
2817 4 : struct smb2_create io3 = {};
2818 4 : struct smb2_lease ls1 = {};
2819 4 : struct smb2_lease ls1t = {};
2820 4 : struct smb2_handle h1 = {};
2821 4 : struct smb2_handle h2 = {};
2822 4 : struct smb2_handle h3 = {};
2823 4 : struct smb2_request *req2 = NULL;
2824 4 : struct lease_break_info lease_break_info_tmp = {};
2825 4 : struct smb2_lease_break_ack ack = {};
2826 4 : const char *fname = "lease_breaking5.dat";
2827 4 : bool ret = true;
2828 0 : NTSTATUS status;
2829 0 : uint32_t caps;
2830 :
2831 4 : caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
2832 4 : if (!(caps & SMB2_CAP_LEASING)) {
2833 2 : torture_skip(tctx, "leases are not supported");
2834 : }
2835 :
2836 2 : smb2_util_unlink(tree, fname);
2837 :
2838 2 : tree->session->transport->lease.handler = torture_lease_handler;
2839 2 : tree->session->transport->lease.private_data = tree;
2840 2 : tree->session->transport->oplock.handler = torture_oplock_handler;
2841 2 : tree->session->transport->oplock.private_data = tree;
2842 :
2843 : /*
2844 : * we defer acking the lease break.
2845 : */
2846 2 : torture_reset_lease_break_info(tctx, &lease_break_info);
2847 2 : lease_break_info.lease_skip_ack = true;
2848 :
2849 2 : smb2_lease_create_share(&io1, &ls1, false, fname,
2850 : smb2_util_share_access("RWD"),
2851 : LEASE1,
2852 : smb2_util_lease_state("R"));
2853 2 : status = smb2_create(tree, mem_ctx, &io1);
2854 2 : CHECK_STATUS(status, NT_STATUS_OK);
2855 2 : h1 = io1.out.file.handle;
2856 2 : CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2857 2 : CHECK_LEASE(&io1, "R", true, LEASE1, 0);
2858 :
2859 2 : CHECK_NO_BREAK(tctx);
2860 :
2861 : /*
2862 : * a conflicting open is *not* blocked until we ack the
2863 : * lease break
2864 : */
2865 2 : smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE);
2866 2 : io2.in.create_disposition = NTCREATEX_DISP_OVERWRITE;
2867 2 : req2 = smb2_create_send(tree, &io2);
2868 2 : torture_assert(tctx, req2 != NULL, "smb2_create_send");
2869 :
2870 : /*
2871 : * We got a break from RH to NONE, we're supported to ack
2872 : * this downgrade
2873 : */
2874 2 : CHECK_BREAK_INFO("R", "", LEASE1);
2875 :
2876 2 : lease_break_info_tmp = lease_break_info;
2877 2 : torture_reset_lease_break_info(tctx, &lease_break_info);
2878 2 : CHECK_NO_BREAK(tctx);
2879 :
2880 2 : torture_assert(tctx, req2->state == SMB2_REQUEST_DONE, "req2 done");
2881 :
2882 2 : status = smb2_create_recv(req2, tctx, &io2);
2883 2 : CHECK_STATUS(status, NT_STATUS_OK);
2884 2 : h2 = io2.out.file.handle;
2885 2 : CHECK_CREATED(&io2, TRUNCATED, FILE_ATTRIBUTE_ARCHIVE);
2886 2 : CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_NONE);
2887 :
2888 2 : CHECK_NO_BREAK(tctx);
2889 :
2890 : /*
2891 : * We now ask the server about the current lease state
2892 : * which should still be "RH", but with
2893 : * SMB2_LEASE_FLAG_BREAK_IN_PROGRESS.
2894 : */
2895 2 : smb2_lease_create_share(&io3, &ls1t, false, fname,
2896 : smb2_util_share_access("RWD"),
2897 : LEASE1,
2898 : smb2_util_lease_state(""));
2899 2 : status = smb2_create(tree, mem_ctx, &io3);
2900 2 : CHECK_STATUS(status, NT_STATUS_OK);
2901 2 : h3 = io3.out.file.handle;
2902 2 : CHECK_CREATED(&io3, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
2903 2 : CHECK_LEASE(&io3, "", true, LEASE1, 0);
2904 :
2905 : /*
2906 : * We send an ack without without being asked.
2907 : */
2908 2 : CHECK_NO_BREAK(tctx);
2909 2 : lease_break_info = lease_break_info_tmp;
2910 2 : ack.in.lease.lease_key =
2911 : lease_break_info.lease_break.current_lease.lease_key;
2912 2 : ack.in.lease.lease_state =
2913 2 : lease_break_info.lease_break.new_lease_state;
2914 2 : torture_reset_lease_break_info(tctx, &lease_break_info);
2915 2 : status = smb2_lease_break_ack(tree, &ack);
2916 2 : CHECK_STATUS(status, NT_STATUS_UNSUCCESSFUL);
2917 :
2918 2 : CHECK_NO_BREAK(tctx);
2919 :
2920 2 : done:
2921 2 : smb2_util_close(tree, h1);
2922 2 : smb2_util_close(tree, h2);
2923 2 : smb2_util_close(tree, h3);
2924 :
2925 2 : smb2_util_unlink(tree, fname);
2926 2 : talloc_free(mem_ctx);
2927 2 : return ret;
2928 : }
2929 :
2930 4 : static bool test_lease_breaking6(struct torture_context *tctx,
2931 : struct smb2_tree *tree)
2932 : {
2933 4 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
2934 4 : struct smb2_create io1 = {};
2935 4 : struct smb2_create io2 = {};
2936 4 : struct smb2_lease ls1 = {};
2937 4 : struct smb2_handle h1a = {};
2938 4 : struct smb2_handle h1b = {};
2939 4 : struct smb2_handle h2 = {};
2940 4 : struct smb2_request *req2 = NULL;
2941 4 : struct smb2_lease_break_ack ack = {};
2942 4 : const char *fname = "lease_breaking6.dat";
2943 4 : bool ret = true;
2944 0 : NTSTATUS status;
2945 0 : uint32_t caps;
2946 :
2947 4 : caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
2948 4 : if (!(caps & SMB2_CAP_LEASING)) {
2949 2 : torture_skip(tctx, "leases are not supported");
2950 : }
2951 :
2952 2 : smb2_util_unlink(tree, fname);
2953 :
2954 2 : tree->session->transport->lease.handler = torture_lease_handler;
2955 2 : tree->session->transport->lease.private_data = tree;
2956 2 : tree->session->transport->oplock.handler = torture_oplock_handler;
2957 2 : tree->session->transport->oplock.private_data = tree;
2958 :
2959 : /*
2960 : * we defer acking the lease break.
2961 : */
2962 2 : torture_reset_lease_break_info(tctx, &lease_break_info);
2963 2 : lease_break_info.lease_skip_ack = true;
2964 :
2965 2 : smb2_lease_create_share(&io1, &ls1, false, fname,
2966 : smb2_util_share_access("RWD"),
2967 : LEASE1,
2968 : smb2_util_lease_state("RWH"));
2969 2 : status = smb2_create(tree, mem_ctx, &io1);
2970 2 : CHECK_STATUS(status, NT_STATUS_OK);
2971 2 : h1a = io1.out.file.handle;
2972 2 : CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2973 2 : CHECK_LEASE(&io1, "RWH", true, LEASE1, 0);
2974 :
2975 : /*
2976 : * a conflicting open is blocked until we ack the
2977 : * lease break
2978 : */
2979 2 : smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE);
2980 2 : req2 = smb2_create_send(tree, &io2);
2981 2 : torture_assert(tctx, req2 != NULL, "smb2_create_send");
2982 :
2983 : /*
2984 : * we got the lease break, but defer the ack.
2985 : */
2986 2 : CHECK_BREAK_INFO("RWH", "RH", LEASE1);
2987 :
2988 2 : torture_assert(tctx, req2->state == SMB2_REQUEST_RECV, "req2 pending");
2989 :
2990 2 : ack.in.lease.lease_key =
2991 : lease_break_info.lease_break.current_lease.lease_key;
2992 2 : torture_reset_lease_break_info(tctx, &lease_break_info);
2993 :
2994 : /*
2995 : * a open using the same lease key is still works,
2996 : * but reports SMB2_LEASE_FLAG_BREAK_IN_PROGRESS
2997 : */
2998 2 : status = smb2_create(tree, mem_ctx, &io1);
2999 2 : CHECK_STATUS(status, NT_STATUS_OK);
3000 2 : h1b = io1.out.file.handle;
3001 2 : CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
3002 2 : CHECK_LEASE(&io1, "RWH", true, LEASE1, SMB2_LEASE_FLAG_BREAK_IN_PROGRESS);
3003 2 : smb2_util_close(tree, h1b);
3004 :
3005 2 : CHECK_NO_BREAK(tctx);
3006 :
3007 2 : torture_assert(tctx, req2->state == SMB2_REQUEST_RECV, "req2 pending");
3008 :
3009 : /*
3010 : * We are asked to break to "RH", but we are allowed to
3011 : * break to any of "RH", "R" or NONE.
3012 : */
3013 2 : ack.in.lease.lease_state = SMB2_LEASE_NONE;
3014 2 : status = smb2_lease_break_ack(tree, &ack);
3015 2 : CHECK_STATUS(status, NT_STATUS_OK);
3016 2 : CHECK_LEASE_BREAK_ACK(&ack, "", LEASE1);
3017 :
3018 2 : torture_assert(tctx, req2->cancel.can_cancel,
3019 : "req2 can_cancel");
3020 :
3021 2 : status = smb2_create_recv(req2, tctx, &io2);
3022 2 : CHECK_STATUS(status, NT_STATUS_OK);
3023 2 : h2 = io2.out.file.handle;
3024 2 : CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
3025 2 : CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_NONE);
3026 :
3027 2 : CHECK_NO_BREAK(tctx);
3028 2 : done:
3029 2 : smb2_util_close(tree, h1a);
3030 2 : smb2_util_close(tree, h1b);
3031 2 : smb2_util_close(tree, h2);
3032 2 : smb2_util_unlink(tree, fname);
3033 2 : talloc_free(mem_ctx);
3034 2 : return ret;
3035 : }
3036 :
3037 4 : static bool test_lease_lock1(struct torture_context *tctx,
3038 : struct smb2_tree *tree1a,
3039 : struct smb2_tree *tree2)
3040 : {
3041 4 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
3042 4 : struct smb2_create io1 = {};
3043 4 : struct smb2_create io2 = {};
3044 4 : struct smb2_create io3 = {};
3045 4 : struct smb2_lease ls1 = {};
3046 4 : struct smb2_lease ls2 = {};
3047 4 : struct smb2_lease ls3 = {};
3048 4 : struct smb2_handle h1 = {};
3049 4 : struct smb2_handle h2 = {};
3050 4 : struct smb2_handle h3 = {};
3051 0 : struct smb2_lock lck;
3052 0 : struct smb2_lock_element el[1];
3053 4 : const char *fname = "locktest.dat";
3054 4 : bool ret = true;
3055 0 : NTSTATUS status;
3056 0 : uint32_t caps;
3057 0 : struct smbcli_options options1;
3058 4 : struct smb2_tree *tree1b = NULL;
3059 :
3060 4 : options1 = tree1a->session->transport->options;
3061 :
3062 4 : caps = smb2cli_conn_server_capabilities(tree1a->session->transport->conn);
3063 4 : if (!(caps & SMB2_CAP_LEASING)) {
3064 2 : torture_skip(tctx, "leases are not supported");
3065 : }
3066 :
3067 : /* Set up handlers. */
3068 2 : tree2->session->transport->lease.handler = torture_lease_handler;
3069 2 : tree2->session->transport->lease.private_data = tree2;
3070 2 : tree2->session->transport->oplock.handler = torture_oplock_handler;
3071 2 : tree2->session->transport->oplock.private_data = tree2;
3072 :
3073 2 : tree1a->session->transport->lease.handler = torture_lease_handler;
3074 2 : tree1a->session->transport->lease.private_data = tree1a;
3075 2 : tree1a->session->transport->oplock.handler = torture_oplock_handler;
3076 2 : tree1a->session->transport->oplock.private_data = tree1a;
3077 :
3078 : /* create a new connection (same client_guid) */
3079 2 : if (!torture_smb2_connection_ext(tctx, 0, &options1, &tree1b)) {
3080 0 : torture_warning(tctx, "couldn't reconnect, bailing\n");
3081 0 : ret = false;
3082 0 : goto done;
3083 : }
3084 :
3085 2 : tree1b->session->transport->lease.handler = torture_lease_handler;
3086 2 : tree1b->session->transport->lease.private_data = tree1b;
3087 2 : tree1b->session->transport->oplock.handler = torture_oplock_handler;
3088 2 : tree1b->session->transport->oplock.private_data = tree1b;
3089 :
3090 2 : smb2_util_unlink(tree1a, fname);
3091 :
3092 2 : torture_reset_lease_break_info(tctx, &lease_break_info);
3093 2 : ZERO_STRUCT(lck);
3094 :
3095 : /* Open a handle on tree1a. */
3096 2 : smb2_lease_create_share(&io1, &ls1, false, fname,
3097 : smb2_util_share_access("RWD"),
3098 : LEASE1,
3099 : smb2_util_lease_state("RWH"));
3100 2 : status = smb2_create(tree1a, mem_ctx, &io1);
3101 2 : CHECK_STATUS(status, NT_STATUS_OK);
3102 2 : h1 = io1.out.file.handle;
3103 2 : CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
3104 2 : CHECK_LEASE(&io1, "RWH", true, LEASE1, 0);
3105 :
3106 : /* Open a second handle on tree1b. */
3107 2 : smb2_lease_create_share(&io2, &ls2, false, fname,
3108 : smb2_util_share_access("RWD"),
3109 : LEASE2,
3110 : smb2_util_lease_state("RWH"));
3111 2 : status = smb2_create(tree1b, mem_ctx, &io2);
3112 2 : CHECK_STATUS(status, NT_STATUS_OK);
3113 2 : h2 = io2.out.file.handle;
3114 2 : CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
3115 2 : CHECK_LEASE(&io2, "RH", true, LEASE2, 0);
3116 : /* And LEASE1 got broken to RH. */
3117 2 : CHECK_BREAK_INFO("RWH", "RH", LEASE1);
3118 2 : torture_reset_lease_break_info(tctx, &lease_break_info);
3119 :
3120 : /* Now open a lease on a different client guid. */
3121 2 : smb2_lease_create_share(&io3, &ls3, false, fname,
3122 : smb2_util_share_access("RWD"),
3123 : LEASE3,
3124 : smb2_util_lease_state("RWH"));
3125 2 : status = smb2_create(tree2, mem_ctx, &io3);
3126 2 : CHECK_STATUS(status, NT_STATUS_OK);
3127 2 : h3 = io3.out.file.handle;
3128 2 : CHECK_CREATED(&io3, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
3129 2 : CHECK_LEASE(&io3, "RH", true, LEASE3, 0);
3130 : /* Doesn't break. */
3131 2 : CHECK_NO_BREAK(tctx);
3132 :
3133 2 : lck.in.locks = el;
3134 : /*
3135 : * Try and get get an exclusive byte
3136 : * range lock on H1 (LEASE1).
3137 : */
3138 :
3139 2 : lck.in.lock_count = 1;
3140 2 : lck.in.lock_sequence = 1;
3141 2 : lck.in.file.handle = h1;
3142 2 : el[0].offset = 0;
3143 2 : el[0].length = 1;
3144 2 : el[0].reserved = 0;
3145 2 : el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
3146 2 : status = smb2_lock(tree1a, &lck);
3147 2 : CHECK_STATUS(status, NT_STATUS_OK);
3148 :
3149 : /* LEASE2 and LEASE3 should get broken to NONE. */
3150 2 : torture_wait_for_lease_break(tctx);
3151 2 : torture_wait_for_lease_break(tctx);
3152 2 : torture_wait_for_lease_break(tctx);
3153 2 : torture_wait_for_lease_break(tctx);
3154 :
3155 2 : CHECK_VAL(lease_break_info.failures, 0); \
3156 2 : CHECK_VAL(lease_break_info.count, 2); \
3157 :
3158 : /* Get state of the H1 (LEASE1) */
3159 2 : smb2_lease_create(&io1, &ls1, false, fname, LEASE1, smb2_util_lease_state(""));
3160 2 : status = smb2_create(tree1a, mem_ctx, &io1);
3161 2 : CHECK_STATUS(status, NT_STATUS_OK);
3162 : /* Should still be RH. */
3163 2 : CHECK_LEASE(&io1, "RH", true, LEASE1, 0);
3164 2 : smb2_util_close(tree1a, io1.out.file.handle);
3165 :
3166 : /* Get state of the H2 (LEASE2) */
3167 2 : smb2_lease_create(&io2, &ls2, false, fname, LEASE2, smb2_util_lease_state(""));
3168 2 : status = smb2_create(tree1b, mem_ctx, &io2);
3169 2 : CHECK_STATUS(status, NT_STATUS_OK);
3170 2 : CHECK_LEASE(&io2, "", true, LEASE2, 0);
3171 2 : smb2_util_close(tree1b, io2.out.file.handle);
3172 :
3173 : /* Get state of the H3 (LEASE3) */
3174 2 : smb2_lease_create(&io3, &ls3, false, fname, LEASE3, smb2_util_lease_state(""));
3175 2 : status = smb2_create(tree2, mem_ctx, &io3);
3176 2 : CHECK_STATUS(status, NT_STATUS_OK);
3177 2 : CHECK_LEASE(&io3, "", true, LEASE3, 0);
3178 2 : smb2_util_close(tree2, io3.out.file.handle);
3179 :
3180 2 : torture_reset_lease_break_info(tctx, &lease_break_info);
3181 :
3182 : /*
3183 : * Try and get get an exclusive byte
3184 : * range lock on H3 (LEASE3).
3185 : */
3186 2 : lck.in.lock_count = 1;
3187 2 : lck.in.lock_sequence = 2;
3188 2 : lck.in.file.handle = h3;
3189 2 : el[0].offset = 100;
3190 2 : el[0].length = 1;
3191 2 : el[0].reserved = 0;
3192 2 : el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
3193 2 : status = smb2_lock(tree2, &lck);
3194 2 : CHECK_STATUS(status, NT_STATUS_OK);
3195 : /* LEASE1 got broken to NONE. */
3196 2 : CHECK_BREAK_INFO("RH", "", LEASE1);
3197 2 : torture_reset_lease_break_info(tctx, &lease_break_info);
3198 :
3199 2 : done:
3200 2 : smb2_util_close(tree1a, h1);
3201 2 : smb2_util_close(tree1b, h2);
3202 2 : smb2_util_close(tree2, h3);
3203 :
3204 2 : smb2_util_unlink(tree1a, fname);
3205 2 : talloc_free(mem_ctx);
3206 2 : return ret;
3207 : }
3208 :
3209 4 : static bool test_lease_complex1(struct torture_context *tctx,
3210 : struct smb2_tree *tree1a)
3211 : {
3212 4 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
3213 0 : struct smb2_create io1;
3214 0 : struct smb2_create io2;
3215 0 : struct smb2_lease ls1;
3216 0 : struct smb2_lease ls2;
3217 4 : struct smb2_handle h = {{0}};
3218 4 : struct smb2_handle h2 = {{0}};
3219 4 : struct smb2_handle h3 = {{0}};
3220 0 : struct smb2_write w;
3221 0 : NTSTATUS status;
3222 4 : const char *fname = "lease_complex1.dat";
3223 4 : bool ret = true;
3224 0 : uint32_t caps;
3225 4 : struct smb2_tree *tree1b = NULL;
3226 0 : struct smbcli_options options1;
3227 :
3228 4 : options1 = tree1a->session->transport->options;
3229 :
3230 4 : caps = smb2cli_conn_server_capabilities(tree1a->session->transport->conn);
3231 4 : if (!(caps & SMB2_CAP_LEASING)) {
3232 2 : torture_skip(tctx, "leases are not supported");
3233 : }
3234 :
3235 2 : tree1a->session->transport->lease.handler = torture_lease_handler;
3236 2 : tree1a->session->transport->lease.private_data = tree1a;
3237 2 : tree1a->session->transport->oplock.handler = torture_oplock_handler;
3238 2 : tree1a->session->transport->oplock.private_data = tree1a;
3239 :
3240 : /* create a new connection (same client_guid) */
3241 2 : if (!torture_smb2_connection_ext(tctx, 0, &options1, &tree1b)) {
3242 0 : torture_warning(tctx, "couldn't reconnect, bailing\n");
3243 0 : ret = false;
3244 0 : goto done;
3245 : }
3246 :
3247 2 : tree1b->session->transport->lease.handler = torture_lease_handler;
3248 2 : tree1b->session->transport->lease.private_data = tree1b;
3249 2 : tree1b->session->transport->oplock.handler = torture_oplock_handler;
3250 2 : tree1b->session->transport->oplock.private_data = tree1b;
3251 :
3252 2 : smb2_util_unlink(tree1a, fname);
3253 :
3254 2 : torture_reset_lease_break_info(tctx, &lease_break_info);
3255 :
3256 : /* Grab R lease over connection 1a */
3257 2 : smb2_lease_create(&io1, &ls1, false, fname, LEASE1, smb2_util_lease_state("R"));
3258 2 : status = smb2_create(tree1a, mem_ctx, &io1);
3259 2 : CHECK_STATUS(status, NT_STATUS_OK);
3260 2 : h = io1.out.file.handle;
3261 2 : CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
3262 2 : CHECK_LEASE(&io1, "R", true, LEASE1, 0);
3263 :
3264 : /* Upgrade to RWH over connection 1b */
3265 2 : ls1.lease_state = smb2_util_lease_state("RWH");
3266 2 : status = smb2_create(tree1b, mem_ctx, &io1);
3267 2 : CHECK_STATUS(status, NT_STATUS_OK);
3268 2 : h2 = io1.out.file.handle;
3269 2 : CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
3270 2 : CHECK_LEASE(&io1, "RHW", true, LEASE1, 0);
3271 :
3272 : /* close over connection 1b */
3273 2 : status = smb2_util_close(tree1b, h2);
3274 2 : CHECK_STATUS(status, NT_STATUS_OK);
3275 :
3276 : /* Contend with LEASE2. */
3277 2 : smb2_lease_create(&io2, &ls2, false, fname, LEASE2, smb2_util_lease_state("R"));
3278 2 : status = smb2_create(tree1b, mem_ctx, &io2);
3279 2 : CHECK_STATUS(status, NT_STATUS_OK);
3280 2 : h3 = io2.out.file.handle;
3281 2 : CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
3282 2 : CHECK_LEASE(&io2, "R", true, LEASE2, 0);
3283 :
3284 : /* Verify that we were only sent one break. */
3285 2 : CHECK_BREAK_INFO("RHW", "RH", LEASE1);
3286 :
3287 : /* again RH over connection 1b doesn't change the epoch */
3288 2 : ls1.lease_state = smb2_util_lease_state("RH");
3289 2 : status = smb2_create(tree1b, mem_ctx, &io1);
3290 2 : CHECK_STATUS(status, NT_STATUS_OK);
3291 2 : h2 = io1.out.file.handle;
3292 2 : CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
3293 2 : CHECK_LEASE(&io1, "RH", true, LEASE1, 0);
3294 :
3295 : /* close over connection 1b */
3296 2 : status = smb2_util_close(tree1b, h2);
3297 2 : CHECK_STATUS(status, NT_STATUS_OK);
3298 :
3299 2 : torture_reset_lease_break_info(tctx, &lease_break_info);
3300 :
3301 2 : ZERO_STRUCT(w);
3302 2 : w.in.file.handle = h;
3303 2 : w.in.offset = 0;
3304 2 : w.in.data = data_blob_talloc(mem_ctx, NULL, 4096);
3305 2 : memset(w.in.data.data, 'o', w.in.data.length);
3306 2 : status = smb2_write(tree1a, &w);
3307 2 : CHECK_STATUS(status, NT_STATUS_OK);
3308 :
3309 2 : ls2.lease_epoch += 1;
3310 2 : CHECK_BREAK_INFO("R", "", LEASE2);
3311 :
3312 2 : torture_reset_lease_break_info(tctx, &lease_break_info);
3313 :
3314 2 : ZERO_STRUCT(w);
3315 2 : w.in.file.handle = h3;
3316 2 : w.in.offset = 0;
3317 2 : w.in.data = data_blob_talloc(mem_ctx, NULL, 4096);
3318 2 : memset(w.in.data.data, 'o', w.in.data.length);
3319 2 : status = smb2_write(tree1b, &w);
3320 2 : CHECK_STATUS(status, NT_STATUS_OK);
3321 :
3322 2 : ls1.lease_epoch += 1;
3323 2 : CHECK_BREAK_INFO("RH", "", LEASE1);
3324 :
3325 2 : done:
3326 2 : smb2_util_close(tree1a, h);
3327 2 : smb2_util_close(tree1b, h2);
3328 2 : smb2_util_close(tree1b, h3);
3329 :
3330 2 : smb2_util_unlink(tree1a, fname);
3331 :
3332 2 : talloc_free(mem_ctx);
3333 :
3334 2 : return ret;
3335 : }
3336 :
3337 4 : static bool test_lease_v2_complex1(struct torture_context *tctx,
3338 : struct smb2_tree *tree1a)
3339 : {
3340 4 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
3341 0 : struct smb2_create io1;
3342 0 : struct smb2_create io2;
3343 0 : struct smb2_lease ls1;
3344 0 : struct smb2_lease ls2;
3345 4 : struct smb2_handle h = {{0}};
3346 4 : struct smb2_handle h2 = {{0}};
3347 4 : struct smb2_handle h3 = {{0}};
3348 0 : struct smb2_write w;
3349 0 : NTSTATUS status;
3350 4 : const char *fname = "lease_v2_complex1.dat";
3351 4 : bool ret = true;
3352 0 : uint32_t caps;
3353 0 : enum protocol_types protocol;
3354 4 : struct smb2_tree *tree1b = NULL;
3355 0 : struct smbcli_options options1;
3356 :
3357 4 : options1 = tree1a->session->transport->options;
3358 :
3359 4 : caps = smb2cli_conn_server_capabilities(tree1a->session->transport->conn);
3360 4 : if (!(caps & SMB2_CAP_LEASING)) {
3361 2 : torture_skip(tctx, "leases are not supported");
3362 : }
3363 :
3364 2 : protocol = smbXcli_conn_protocol(tree1a->session->transport->conn);
3365 2 : if (protocol < PROTOCOL_SMB3_00) {
3366 0 : torture_skip(tctx, "v2 leases are not supported");
3367 : }
3368 :
3369 2 : tree1a->session->transport->lease.handler = torture_lease_handler;
3370 2 : tree1a->session->transport->lease.private_data = tree1a;
3371 2 : tree1a->session->transport->oplock.handler = torture_oplock_handler;
3372 2 : tree1a->session->transport->oplock.private_data = tree1a;
3373 :
3374 : /* create a new connection (same client_guid) */
3375 2 : if (!torture_smb2_connection_ext(tctx, 0, &options1, &tree1b)) {
3376 0 : torture_warning(tctx, "couldn't reconnect, bailing\n");
3377 0 : ret = false;
3378 0 : goto done;
3379 : }
3380 :
3381 2 : tree1b->session->transport->lease.handler = torture_lease_handler;
3382 2 : tree1b->session->transport->lease.private_data = tree1b;
3383 2 : tree1b->session->transport->oplock.handler = torture_oplock_handler;
3384 2 : tree1b->session->transport->oplock.private_data = tree1b;
3385 :
3386 2 : smb2_util_unlink(tree1a, fname);
3387 :
3388 2 : torture_reset_lease_break_info(tctx, &lease_break_info);
3389 :
3390 : /* Grab R lease over connection 1a */
3391 2 : smb2_lease_v2_create(&io1, &ls1, false, fname, LEASE1, NULL,
3392 : smb2_util_lease_state("R"), 0x4711);
3393 2 : status = smb2_create(tree1a, mem_ctx, &io1);
3394 2 : CHECK_STATUS(status, NT_STATUS_OK);
3395 2 : h = io1.out.file.handle;
3396 2 : CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
3397 2 : ls1.lease_epoch += 1;
3398 2 : CHECK_LEASE_V2(&io1, "R", true, LEASE1,
3399 : 0, 0, ls1.lease_epoch);
3400 :
3401 : /* Upgrade to RWH over connection 1b */
3402 2 : ls1.lease_state = smb2_util_lease_state("RWH");
3403 2 : status = smb2_create(tree1b, mem_ctx, &io1);
3404 2 : CHECK_STATUS(status, NT_STATUS_OK);
3405 2 : h2 = io1.out.file.handle;
3406 2 : CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
3407 2 : ls1.lease_epoch += 1;
3408 2 : CHECK_LEASE_V2(&io1, "RHW", true, LEASE1,
3409 : 0, 0, ls1.lease_epoch);
3410 :
3411 : /* close over connection 1b */
3412 2 : status = smb2_util_close(tree1b, h2);
3413 2 : CHECK_STATUS(status, NT_STATUS_OK);
3414 :
3415 : /* Contend with LEASE2. */
3416 2 : smb2_lease_v2_create(&io2, &ls2, false, fname, LEASE2, NULL,
3417 : smb2_util_lease_state("R"), 0x11);
3418 2 : status = smb2_create(tree1b, mem_ctx, &io2);
3419 2 : CHECK_STATUS(status, NT_STATUS_OK);
3420 2 : h3 = io2.out.file.handle;
3421 2 : CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
3422 2 : ls2.lease_epoch += 1;
3423 2 : CHECK_LEASE_V2(&io2, "R", true, LEASE2,
3424 : 0, 0, ls2.lease_epoch);
3425 :
3426 : /* Verify that we were only sent one break. */
3427 2 : ls1.lease_epoch += 1;
3428 2 : CHECK_BREAK_INFO_V2(tree1a->session->transport,
3429 : "RHW", "RH", LEASE1, ls1.lease_epoch);
3430 :
3431 : /* again RH over connection 1b doesn't change the epoch */
3432 2 : ls1.lease_state = smb2_util_lease_state("RH");
3433 2 : status = smb2_create(tree1b, mem_ctx, &io1);
3434 2 : CHECK_STATUS(status, NT_STATUS_OK);
3435 2 : h2 = io1.out.file.handle;
3436 2 : CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
3437 2 : CHECK_LEASE_V2(&io1, "RH", true, LEASE1,
3438 : 0, 0, ls1.lease_epoch);
3439 :
3440 : /* close over connection 1b */
3441 2 : status = smb2_util_close(tree1b, h2);
3442 2 : CHECK_STATUS(status, NT_STATUS_OK);
3443 :
3444 2 : torture_reset_lease_break_info(tctx, &lease_break_info);
3445 :
3446 2 : ZERO_STRUCT(w);
3447 2 : w.in.file.handle = h;
3448 2 : w.in.offset = 0;
3449 2 : w.in.data = data_blob_talloc(mem_ctx, NULL, 4096);
3450 2 : memset(w.in.data.data, 'o', w.in.data.length);
3451 2 : status = smb2_write(tree1a, &w);
3452 2 : CHECK_STATUS(status, NT_STATUS_OK);
3453 :
3454 2 : ls2.lease_epoch += 1;
3455 2 : CHECK_BREAK_INFO_V2(tree1a->session->transport,
3456 : "R", "", LEASE2, ls2.lease_epoch);
3457 :
3458 2 : torture_reset_lease_break_info(tctx, &lease_break_info);
3459 :
3460 2 : ZERO_STRUCT(w);
3461 2 : w.in.file.handle = h3;
3462 2 : w.in.offset = 0;
3463 2 : w.in.data = data_blob_talloc(mem_ctx, NULL, 4096);
3464 2 : memset(w.in.data.data, 'o', w.in.data.length);
3465 2 : status = smb2_write(tree1b, &w);
3466 2 : CHECK_STATUS(status, NT_STATUS_OK);
3467 :
3468 2 : ls1.lease_epoch += 1;
3469 2 : CHECK_BREAK_INFO_V2(tree1a->session->transport,
3470 : "RH", "", LEASE1, ls1.lease_epoch);
3471 :
3472 2 : done:
3473 2 : smb2_util_close(tree1a, h);
3474 2 : smb2_util_close(tree1b, h2);
3475 2 : smb2_util_close(tree1b, h3);
3476 :
3477 2 : smb2_util_unlink(tree1a, fname);
3478 :
3479 2 : talloc_free(mem_ctx);
3480 :
3481 2 : return ret;
3482 : }
3483 :
3484 4 : static bool test_lease_v2_complex2(struct torture_context *tctx,
3485 : struct smb2_tree *tree1a)
3486 : {
3487 4 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
3488 0 : struct smb2_create io1;
3489 0 : struct smb2_create io2;
3490 0 : struct smb2_lease ls1;
3491 0 : struct smb2_lease ls2;
3492 4 : struct smb2_handle h = {{0}};
3493 4 : struct smb2_handle h2 = {{0}};
3494 4 : struct smb2_request *req2 = NULL;
3495 4 : struct smb2_lease_break_ack ack = {};
3496 0 : NTSTATUS status;
3497 4 : const char *fname = "lease_v2_complex2.dat";
3498 4 : bool ret = true;
3499 0 : uint32_t caps;
3500 0 : enum protocol_types protocol;
3501 4 : struct smb2_tree *tree1b = NULL;
3502 0 : struct smbcli_options options1;
3503 :
3504 4 : options1 = tree1a->session->transport->options;
3505 :
3506 4 : caps = smb2cli_conn_server_capabilities(tree1a->session->transport->conn);
3507 4 : if (!(caps & SMB2_CAP_LEASING)) {
3508 2 : torture_skip(tctx, "leases are not supported");
3509 : }
3510 :
3511 2 : protocol = smbXcli_conn_protocol(tree1a->session->transport->conn);
3512 2 : if (protocol < PROTOCOL_SMB3_00) {
3513 0 : torture_skip(tctx, "v2 leases are not supported");
3514 : }
3515 :
3516 2 : tree1a->session->transport->lease.handler = torture_lease_handler;
3517 2 : tree1a->session->transport->lease.private_data = tree1a;
3518 2 : tree1a->session->transport->oplock.handler = torture_oplock_handler;
3519 2 : tree1a->session->transport->oplock.private_data = tree1a;
3520 :
3521 : /* create a new connection (same client_guid) */
3522 2 : if (!torture_smb2_connection_ext(tctx, 0, &options1, &tree1b)) {
3523 0 : torture_warning(tctx, "couldn't reconnect, bailing\n");
3524 0 : ret = false;
3525 0 : goto done;
3526 : }
3527 :
3528 2 : tree1b->session->transport->lease.handler = torture_lease_handler;
3529 2 : tree1b->session->transport->lease.private_data = tree1b;
3530 2 : tree1b->session->transport->oplock.handler = torture_oplock_handler;
3531 2 : tree1b->session->transport->oplock.private_data = tree1b;
3532 :
3533 2 : smb2_util_unlink(tree1a, fname);
3534 :
3535 2 : torture_reset_lease_break_info(tctx, &lease_break_info);
3536 :
3537 : /* Grab RWH lease over connection 1a */
3538 2 : smb2_lease_v2_create(&io1, &ls1, false, fname, LEASE1, NULL,
3539 : smb2_util_lease_state("RWH"), 0x4711);
3540 2 : status = smb2_create(tree1a, mem_ctx, &io1);
3541 2 : CHECK_STATUS(status, NT_STATUS_OK);
3542 2 : h = io1.out.file.handle;
3543 2 : CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
3544 2 : ls1.lease_epoch += 1;
3545 2 : CHECK_LEASE_V2(&io1, "RWH", true, LEASE1,
3546 : 0, 0, ls1.lease_epoch);
3547 :
3548 : /*
3549 : * we defer acking the lease break.
3550 : */
3551 2 : torture_reset_lease_break_info(tctx, &lease_break_info);
3552 2 : lease_break_info.lease_skip_ack = true;
3553 :
3554 : /* Ask for RWH on connection 1b, different lease. */
3555 2 : smb2_lease_v2_create(&io2, &ls2, false, fname, LEASE2, NULL,
3556 : smb2_util_lease_state("RWH"), 0x11);
3557 2 : req2 = smb2_create_send(tree1b, &io2);
3558 2 : torture_assert(tctx, req2 != NULL, "smb2_create_send");
3559 :
3560 2 : ls1.lease_epoch += 1;
3561 :
3562 2 : CHECK_BREAK_INFO_V2(tree1a->session->transport,
3563 : "RWH", "RH", LEASE1, ls1.lease_epoch);
3564 :
3565 : /* Send the break ACK on tree1b. */
3566 2 : ack.in.lease.lease_key =
3567 : lease_break_info.lease_break.current_lease.lease_key;
3568 2 : ack.in.lease.lease_state = SMB2_LEASE_HANDLE|SMB2_LEASE_READ;
3569 :
3570 2 : status = smb2_lease_break_ack(tree1b, &ack);
3571 2 : CHECK_STATUS(status, NT_STATUS_OK);
3572 2 : CHECK_LEASE_BREAK_ACK(&ack, "RH", LEASE1);
3573 :
3574 2 : torture_reset_lease_break_info(tctx, &lease_break_info);
3575 :
3576 2 : status = smb2_create_recv(req2, tctx, &io2);
3577 2 : CHECK_STATUS(status, NT_STATUS_OK);
3578 2 : CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
3579 2 : CHECK_LEASE_V2(&io2, "RH", true, LEASE2,
3580 : 0, 0, ls2.lease_epoch+1);
3581 2 : h2 = io2.out.file.handle;
3582 :
3583 2 : done:
3584 2 : smb2_util_close(tree1a, h);
3585 2 : smb2_util_close(tree1b, h2);
3586 :
3587 2 : smb2_util_unlink(tree1a, fname);
3588 :
3589 2 : talloc_free(mem_ctx);
3590 :
3591 2 : return ret;
3592 : }
3593 :
3594 :
3595 4 : static bool test_lease_timeout(struct torture_context *tctx,
3596 : struct smb2_tree *tree)
3597 : {
3598 4 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
3599 0 : struct smb2_create io;
3600 0 : struct smb2_lease ls1;
3601 0 : struct smb2_lease ls2;
3602 4 : struct smb2_handle h = {{0}};
3603 4 : struct smb2_handle hnew = {{0}};
3604 4 : struct smb2_handle h1b = {{0}};
3605 0 : NTSTATUS status;
3606 4 : const char *fname = "lease_timeout.dat";
3607 4 : bool ret = true;
3608 4 : struct smb2_lease_break_ack ack = {};
3609 4 : struct smb2_request *req2 = NULL;
3610 0 : struct smb2_write w;
3611 0 : uint32_t caps;
3612 :
3613 4 : caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
3614 4 : if (!(caps & SMB2_CAP_LEASING)) {
3615 2 : torture_skip(tctx, "leases are not supported");
3616 : }
3617 :
3618 2 : smb2_util_unlink(tree, fname);
3619 :
3620 : /* Grab a RWH lease. */
3621 2 : smb2_lease_create(&io, &ls1, false, fname, LEASE1, smb2_util_lease_state("RWH"));
3622 2 : status = smb2_create(tree, mem_ctx, &io);
3623 2 : CHECK_STATUS(status, NT_STATUS_OK);
3624 2 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
3625 2 : CHECK_LEASE(&io, "RWH", true, LEASE1, 0);
3626 2 : h = io.out.file.handle;
3627 :
3628 2 : tree->session->transport->lease.handler = torture_lease_handler;
3629 2 : tree->session->transport->lease.private_data = tree;
3630 2 : tree->session->transport->oplock.handler = torture_oplock_handler;
3631 2 : tree->session->transport->oplock.private_data = tree;
3632 :
3633 : /*
3634 : * Just don't ack the lease break.
3635 : */
3636 2 : torture_reset_lease_break_info(tctx, &lease_break_info);
3637 2 : lease_break_info.lease_skip_ack = true;
3638 :
3639 : /* Break with a RWH request. */
3640 2 : smb2_lease_create(&io, &ls2, false, fname, LEASE2, smb2_util_lease_state("RWH"));
3641 2 : req2 = smb2_create_send(tree, &io);
3642 2 : torture_assert(tctx, req2 != NULL, "smb2_create_send");
3643 2 : torture_assert(tctx, req2->state == SMB2_REQUEST_RECV, "req2 pending");
3644 :
3645 2 : CHECK_BREAK_INFO("RWH", "RH", LEASE1);
3646 :
3647 : /* Copy the break request. */
3648 2 : ack.in.lease.lease_key =
3649 : lease_break_info.lease_break.current_lease.lease_key;
3650 2 : ack.in.lease.lease_state =
3651 2 : lease_break_info.lease_break.new_lease_state;
3652 :
3653 : /* Now wait for the timeout and get the reply. */
3654 2 : status = smb2_create_recv(req2, tctx, &io);
3655 2 : CHECK_STATUS(status, NT_STATUS_OK);
3656 2 : CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
3657 2 : CHECK_LEASE(&io, "RH", true, LEASE2, 0);
3658 2 : hnew = io.out.file.handle;
3659 :
3660 : /* Ack the break after the timeout... */
3661 2 : status = smb2_lease_break_ack(tree, &ack);
3662 2 : CHECK_STATUS(status, NT_STATUS_UNSUCCESSFUL);
3663 :
3664 : /* Get state of the original handle. */
3665 2 : smb2_lease_create(&io, &ls1, false, fname, LEASE1, smb2_util_lease_state(""));
3666 2 : status = smb2_create(tree, mem_ctx, &io);
3667 2 : CHECK_STATUS(status, NT_STATUS_OK);
3668 2 : CHECK_LEASE(&io, "", true, LEASE1, 0);
3669 2 : smb2_util_close(tree, io.out.file.handle);
3670 :
3671 : /* Write on the original handle and make sure it's still valid. */
3672 2 : torture_reset_lease_break_info(tctx, &lease_break_info);
3673 2 : ZERO_STRUCT(w);
3674 2 : w.in.file.handle = h;
3675 2 : w.in.offset = 0;
3676 2 : w.in.data = data_blob_talloc(mem_ctx, NULL, 4096);
3677 2 : memset(w.in.data.data, '1', w.in.data.length);
3678 2 : status = smb2_write(tree, &w);
3679 2 : CHECK_STATUS(status, NT_STATUS_OK);
3680 :
3681 : /* Causes new handle to break to NONE. */
3682 2 : CHECK_BREAK_INFO("RH", "", LEASE2);
3683 :
3684 : /* Write on the new handle. */
3685 2 : torture_reset_lease_break_info(tctx, &lease_break_info);
3686 2 : ZERO_STRUCT(w);
3687 2 : w.in.file.handle = hnew;
3688 2 : w.in.offset = 0;
3689 2 : w.in.data = data_blob_talloc(mem_ctx, NULL, 1024);
3690 2 : memset(w.in.data.data, '2', w.in.data.length);
3691 2 : status = smb2_write(tree, &w);
3692 2 : CHECK_STATUS(status, NT_STATUS_OK);
3693 : /* No break - original handle was already NONE. */
3694 2 : CHECK_NO_BREAK(tctx);
3695 2 : smb2_util_close(tree, hnew);
3696 :
3697 : /* Upgrade to R on LEASE1. */
3698 2 : smb2_lease_create(&io, &ls1, false, fname, LEASE1, smb2_util_lease_state("R"));
3699 2 : status = smb2_create(tree, mem_ctx, &io);
3700 2 : CHECK_STATUS(status, NT_STATUS_OK);
3701 2 : CHECK_LEASE(&io, "R", true, LEASE1, 0);
3702 2 : h1b = io.out.file.handle;
3703 2 : smb2_util_close(tree, h1b);
3704 :
3705 : /* Upgrade to RWH on LEASE1. */
3706 2 : smb2_lease_create(&io, &ls1, false, fname, LEASE1, smb2_util_lease_state("RWH"));
3707 2 : status = smb2_create(tree, mem_ctx, &io);
3708 2 : CHECK_STATUS(status, NT_STATUS_OK);
3709 2 : CHECK_LEASE(&io, "RWH", true, LEASE1, 0);
3710 2 : h1b = io.out.file.handle;
3711 2 : smb2_util_close(tree, h1b);
3712 :
3713 2 : done:
3714 2 : smb2_util_close(tree, h);
3715 2 : smb2_util_close(tree, hnew);
3716 2 : smb2_util_close(tree, h1b);
3717 :
3718 2 : smb2_util_unlink(tree, fname);
3719 :
3720 2 : talloc_free(mem_ctx);
3721 :
3722 2 : return ret;
3723 : }
3724 :
3725 4 : static bool test_lease_rename_wait(struct torture_context *tctx,
3726 : struct smb2_tree *tree)
3727 : {
3728 4 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
3729 0 : struct smb2_create io;
3730 0 : struct smb2_lease ls1;
3731 0 : struct smb2_lease ls2;
3732 0 : struct smb2_lease ls3;
3733 4 : struct smb2_handle h1 = {{0}};
3734 4 : struct smb2_handle h2 = {{0}};
3735 4 : struct smb2_handle h3 = {{0}};
3736 0 : union smb_setfileinfo sinfo;
3737 0 : NTSTATUS status;
3738 4 : const char *fname_src = "lease_rename_src.dat";
3739 4 : const char *fname_dst = "lease_rename_dst.dat";
3740 4 : bool ret = true;
3741 4 : struct smb2_lease_break_ack ack = {};
3742 4 : struct smb2_request *rename_req = NULL;
3743 0 : uint32_t caps;
3744 0 : unsigned int i;
3745 :
3746 4 : caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
3747 4 : if (!(caps & SMB2_CAP_LEASING)) {
3748 2 : torture_skip(tctx, "leases are not supported");
3749 : }
3750 :
3751 2 : smb2_util_unlink(tree, fname_src);
3752 2 : smb2_util_unlink(tree, fname_dst);
3753 :
3754 : /* Short timeout for fails. */
3755 2 : tree->session->transport->options.request_timeout = 15;
3756 :
3757 : /* Grab a RH lease. */
3758 2 : smb2_lease_create(&io,
3759 : &ls1,
3760 : false,
3761 : fname_src,
3762 : LEASE1,
3763 : smb2_util_lease_state("RH"));
3764 2 : status = smb2_create(tree, mem_ctx, &io);
3765 2 : CHECK_STATUS(status, NT_STATUS_OK);
3766 2 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
3767 2 : CHECK_LEASE(&io, "RH", true, LEASE1, 0);
3768 2 : h1 = io.out.file.handle;
3769 :
3770 : /* Second open with a RH lease. */
3771 2 : smb2_lease_create(&io,
3772 : &ls2,
3773 : false,
3774 : fname_src,
3775 : LEASE2,
3776 : smb2_util_lease_state("RH"));
3777 2 : io.in.create_disposition = NTCREATEX_DISP_OPEN;
3778 2 : io.in.desired_access = GENERIC_READ_ACCESS;
3779 2 : status = smb2_create(tree, mem_ctx, &io);
3780 2 : CHECK_STATUS(status, NT_STATUS_OK);
3781 2 : CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
3782 2 : CHECK_LEASE(&io, "RH", true, LEASE2, 0);
3783 2 : h2 = io.out.file.handle;
3784 :
3785 : /*
3786 : * Don't ack a lease break.
3787 : */
3788 2 : tree->session->transport->lease.handler = torture_lease_handler;
3789 2 : tree->session->transport->lease.private_data = tree;
3790 2 : torture_reset_lease_break_info(tctx, &lease_break_info);
3791 2 : lease_break_info.lease_skip_ack = true;
3792 :
3793 : /* Break with a rename. */
3794 2 : ZERO_STRUCT(sinfo);
3795 2 : sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION;
3796 2 : sinfo.rename_information.in.file.handle = h1;
3797 2 : sinfo.rename_information.in.overwrite = true;
3798 2 : sinfo.rename_information.in.new_name = fname_dst;
3799 2 : rename_req = smb2_setinfo_file_send(tree, &sinfo);
3800 :
3801 2 : torture_assert(tctx,
3802 : rename_req != NULL,
3803 : "smb2_setinfo_file_send");
3804 2 : torture_assert(tctx,
3805 : rename_req->state == SMB2_REQUEST_RECV,
3806 : "rename pending");
3807 :
3808 : /* Try and open the destination with a RH lease. */
3809 2 : smb2_lease_create(&io,
3810 : &ls3,
3811 : false,
3812 : fname_dst,
3813 : LEASE3,
3814 : smb2_util_lease_state("RH"));
3815 : /* We want to open, not create. */
3816 2 : io.in.create_disposition = NTCREATEX_DISP_OPEN;
3817 2 : io.in.desired_access = GENERIC_READ_ACCESS;
3818 2 : status = smb2_create(tree, mem_ctx, &io);
3819 2 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
3820 :
3821 : /*
3822 : * The smb2_create() I/O should have picked up the break request
3823 : * caused by the pending rename.
3824 : */
3825 :
3826 : /* Copy the break request. */
3827 2 : ack.in.lease.lease_key =
3828 : lease_break_info.lease_break.current_lease.lease_key;
3829 2 : ack.in.lease.lease_state =
3830 2 : lease_break_info.lease_break.new_lease_state;
3831 :
3832 : /*
3833 : * Give the server 3 more chances to have renamed
3834 : * the file. Better than doing a sleep.
3835 : */
3836 8 : for (i = 0; i < 3; i++) {
3837 6 : status = smb2_create(tree, mem_ctx, &io);
3838 6 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
3839 : }
3840 :
3841 : /* Ack the break. The server is now free to rename. */
3842 2 : status = smb2_lease_break_ack(tree, &ack);
3843 2 : CHECK_STATUS(status, NT_STATUS_OK);
3844 :
3845 : /* Get the rename reply. */
3846 2 : status = smb2_setinfo_recv(rename_req);
3847 2 : CHECK_STATUS(status, NT_STATUS_OK);
3848 :
3849 : /* The target should now exist. */
3850 2 : status = smb2_create(tree, mem_ctx, &io);
3851 2 : CHECK_STATUS(status, NT_STATUS_OK);
3852 2 : h3 = io.out.file.handle;
3853 :
3854 2 : done:
3855 2 : smb2_util_close(tree, h1);
3856 2 : smb2_util_close(tree, h2);
3857 2 : smb2_util_close(tree, h3);
3858 :
3859 2 : smb2_util_unlink(tree, fname_src);
3860 2 : smb2_util_unlink(tree, fname_dst);
3861 :
3862 2 : talloc_free(mem_ctx);
3863 :
3864 2 : return ret;
3865 : }
3866 :
3867 4 : static bool test_lease_v2_rename(struct torture_context *tctx,
3868 : struct smb2_tree *tree)
3869 : {
3870 4 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
3871 0 : struct smb2_create io;
3872 0 : struct smb2_lease ls1;
3873 0 : struct smb2_lease ls2;
3874 4 : struct smb2_handle h = {{0}};
3875 4 : struct smb2_handle h1 = {{0}};
3876 4 : struct smb2_handle h2 = {{0}};
3877 0 : union smb_setfileinfo sinfo;
3878 4 : const char *fname = "lease_v2_rename_src.dat";
3879 4 : const char *fname_dst = "lease_v2_rename_dst.dat";
3880 4 : bool ret = true;
3881 0 : NTSTATUS status;
3882 0 : uint32_t caps;
3883 0 : enum protocol_types protocol;
3884 :
3885 4 : caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
3886 4 : if (!(caps & SMB2_CAP_LEASING)) {
3887 2 : torture_skip(tctx, "leases are not supported");
3888 : }
3889 :
3890 2 : protocol = smbXcli_conn_protocol(tree->session->transport->conn);
3891 2 : if (protocol < PROTOCOL_SMB3_00) {
3892 0 : torture_skip(tctx, "v2 leases are not supported");
3893 : }
3894 :
3895 2 : smb2_util_unlink(tree, fname);
3896 2 : smb2_util_unlink(tree, fname_dst);
3897 :
3898 2 : tree->session->transport->lease.handler = torture_lease_handler;
3899 2 : tree->session->transport->lease.private_data = tree;
3900 2 : tree->session->transport->oplock.handler = torture_oplock_handler;
3901 2 : tree->session->transport->oplock.private_data = tree;
3902 :
3903 2 : torture_reset_lease_break_info(tctx, &lease_break_info);
3904 :
3905 2 : ZERO_STRUCT(io);
3906 2 : smb2_lease_v2_create_share(&io, &ls1, false, fname,
3907 : smb2_util_share_access("RWD"),
3908 : LEASE1, NULL,
3909 : smb2_util_lease_state("RHW"),
3910 : 0x4711);
3911 2 : status = smb2_create(tree, mem_ctx, &io);
3912 2 : CHECK_STATUS(status, NT_STATUS_OK);
3913 2 : h = io.out.file.handle;
3914 2 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
3915 2 : ls1.lease_epoch += 1;
3916 2 : CHECK_LEASE_V2(&io, "RHW", true, LEASE1, 0, 0, ls1.lease_epoch);
3917 :
3918 : /* Now rename - what happens ? */
3919 2 : ZERO_STRUCT(sinfo);
3920 2 : sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION;
3921 2 : sinfo.rename_information.in.file.handle = h;
3922 2 : sinfo.rename_information.in.overwrite = true;
3923 2 : sinfo.rename_information.in.new_name = fname_dst;
3924 2 : status = smb2_setinfo_file(tree, &sinfo);
3925 2 : CHECK_STATUS(status, NT_STATUS_OK);
3926 :
3927 : /* No lease break. */
3928 2 : CHECK_NO_BREAK(tctx);
3929 :
3930 : /* Check we can open another handle on the new name. */
3931 2 : smb2_lease_v2_create_share(&io, &ls1, false, fname_dst,
3932 : smb2_util_share_access("RWD"),
3933 : LEASE1, NULL,
3934 : smb2_util_lease_state(""),
3935 2 : ls1.lease_epoch);
3936 2 : status = smb2_create(tree, mem_ctx, &io);
3937 2 : CHECK_STATUS(status, NT_STATUS_OK);
3938 2 : h1 = io.out.file.handle;
3939 2 : CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
3940 2 : CHECK_LEASE_V2(&io, "RHW", true, LEASE1, 0, 0, ls1.lease_epoch);
3941 2 : smb2_util_close(tree, h1);
3942 :
3943 : /* Try another lease key. */
3944 2 : smb2_lease_v2_create_share(&io, &ls2, false, fname_dst,
3945 : smb2_util_share_access("RWD"),
3946 : LEASE2, NULL,
3947 : smb2_util_lease_state("RWH"),
3948 : 0x44);
3949 2 : status = smb2_create(tree, mem_ctx, &io);
3950 2 : CHECK_STATUS(status, NT_STATUS_OK);
3951 2 : h2 = io.out.file.handle;
3952 2 : CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
3953 2 : ls2.lease_epoch += 1;
3954 2 : CHECK_LEASE_V2(&io, "RH", true, LEASE2, 0, 0, ls2.lease_epoch );
3955 2 : CHECK_BREAK_INFO_V2(tree->session->transport,
3956 : "RWH", "RH", LEASE1, ls1.lease_epoch + 1);
3957 2 : ls1.lease_epoch += 1;
3958 2 : torture_reset_lease_break_info(tctx, &lease_break_info);
3959 :
3960 : /* Now rename back. */
3961 2 : ZERO_STRUCT(sinfo);
3962 2 : sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION;
3963 2 : sinfo.rename_information.in.file.handle = h;
3964 2 : sinfo.rename_information.in.overwrite = true;
3965 2 : sinfo.rename_information.in.new_name = fname;
3966 2 : status = smb2_setinfo_file(tree, &sinfo);
3967 2 : CHECK_STATUS(status, NT_STATUS_OK);
3968 :
3969 : /* Breaks to R on LEASE2. */
3970 2 : CHECK_BREAK_INFO_V2(tree->session->transport,
3971 : "RH", "R", LEASE2, ls2.lease_epoch + 1);
3972 2 : ls2.lease_epoch += 1;
3973 :
3974 : /* Check we can open another handle on the current name. */
3975 2 : smb2_lease_v2_create_share(&io, &ls1, false, fname,
3976 : smb2_util_share_access("RWD"),
3977 : LEASE1, NULL,
3978 : smb2_util_lease_state(""),
3979 2 : ls1.lease_epoch);
3980 2 : status = smb2_create(tree, mem_ctx, &io);
3981 2 : CHECK_STATUS(status, NT_STATUS_OK);
3982 2 : h1 = io.out.file.handle;
3983 2 : CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
3984 2 : CHECK_LEASE_V2(&io, "RH", true, LEASE1, 0, 0, ls1.lease_epoch);
3985 2 : smb2_util_close(tree, h1);
3986 :
3987 2 : done:
3988 :
3989 2 : smb2_util_close(tree, h);
3990 2 : smb2_util_close(tree, h1);
3991 2 : smb2_util_close(tree, h2);
3992 :
3993 2 : smb2_util_unlink(tree, fname);
3994 2 : smb2_util_unlink(tree, fname_dst);
3995 :
3996 2 : smb2_util_unlink(tree, fname);
3997 2 : talloc_free(mem_ctx);
3998 2 : return ret;
3999 : }
4000 :
4001 :
4002 4 : static bool test_lease_dynamic_share(struct torture_context *tctx,
4003 : struct smb2_tree *tree1a)
4004 : {
4005 4 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
4006 0 : struct smb2_create io;
4007 0 : struct smb2_lease ls1;
4008 0 : struct smb2_handle h, h1, h2;
4009 0 : struct smb2_write w;
4010 0 : NTSTATUS status;
4011 4 : const char *fname = "dynamic_path.dat";
4012 4 : bool ret = true;
4013 0 : uint32_t caps;
4014 4 : struct smb2_tree *tree_2 = NULL;
4015 4 : struct smb2_tree *tree_3 = NULL;
4016 0 : struct smbcli_options options;
4017 4 : const char *orig_share = NULL;
4018 :
4019 4 : if (!TARGET_IS_SAMBA3(tctx)) {
4020 0 : torture_skip(tctx, "dynamic shares are not supported");
4021 : return true;
4022 : }
4023 :
4024 4 : options = tree1a->session->transport->options;
4025 4 : options.client_guid = GUID_random();
4026 :
4027 4 : caps = smb2cli_conn_server_capabilities(tree1a->session->transport->conn);
4028 4 : if (!(caps & SMB2_CAP_LEASING)) {
4029 2 : torture_skip(tctx, "leases are not supported");
4030 : }
4031 :
4032 : /*
4033 : * Save off original share name and change it to dynamic_share.
4034 : * This must have been pre-created with a dynamic path containing
4035 : * %t. It means we'll sleep between the connects in order to
4036 : * get a different timestamp for the share path.
4037 : */
4038 :
4039 2 : orig_share = lpcfg_parm_string(tctx->lp_ctx, NULL, "torture", "share");
4040 2 : orig_share = talloc_strdup(tctx->lp_ctx, orig_share);
4041 2 : if (orig_share == NULL) {
4042 0 : torture_result(tctx, TORTURE_FAIL, __location__ "no memory\n");
4043 0 : ret = false;
4044 0 : goto done;
4045 : }
4046 2 : lpcfg_set_cmdline(tctx->lp_ctx, "torture:share", "dynamic_share");
4047 :
4048 : /* create a new connection (same client_guid) */
4049 2 : sleep(2);
4050 2 : if (!torture_smb2_connection_ext(tctx, 0, &options, &tree_2)) {
4051 0 : torture_result(tctx, TORTURE_FAIL,
4052 : __location__ "couldn't reconnect "
4053 : "max protocol 2.1, bailing\n");
4054 0 : ret = false;
4055 0 : goto done;
4056 : }
4057 :
4058 2 : tree_2->session->transport->lease.handler = torture_lease_handler;
4059 2 : tree_2->session->transport->lease.private_data = tree_2;
4060 2 : tree_2->session->transport->oplock.handler = torture_oplock_handler;
4061 2 : tree_2->session->transport->oplock.private_data = tree_2;
4062 :
4063 2 : smb2_util_unlink(tree_2, fname);
4064 :
4065 : /* create a new connection (same client_guid) */
4066 2 : sleep(2);
4067 2 : if (!torture_smb2_connection_ext(tctx, 0, &options, &tree_3)) {
4068 0 : torture_result(tctx, TORTURE_FAIL,
4069 : __location__ "couldn't reconnect "
4070 : "max protocol 3.0, bailing\n");
4071 0 : ret = false;
4072 0 : goto done;
4073 : }
4074 :
4075 2 : tree_3->session->transport->lease.handler = torture_lease_handler;
4076 2 : tree_3->session->transport->lease.private_data = tree_3;
4077 2 : tree_3->session->transport->oplock.handler = torture_oplock_handler;
4078 2 : tree_3->session->transport->oplock.private_data = tree_3;
4079 :
4080 2 : smb2_util_unlink(tree_3, fname);
4081 :
4082 2 : torture_reset_lease_break_info(tctx, &lease_break_info);
4083 :
4084 : /* Get RWH lease over connection 2 */
4085 2 : smb2_lease_create(&io, &ls1, false, fname, LEASE1, smb2_util_lease_state("RWH"));
4086 2 : status = smb2_create(tree_2, mem_ctx, &io);
4087 2 : CHECK_STATUS(status, NT_STATUS_OK);
4088 2 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
4089 2 : CHECK_LEASE(&io, "RWH", true, LEASE1, 0);
4090 2 : h = io.out.file.handle;
4091 :
4092 : /* Write some data into it. */
4093 2 : w.in.file.handle = h;
4094 2 : w.in.offset = 0;
4095 2 : w.in.data = data_blob_talloc(mem_ctx, NULL, 4096);
4096 2 : memset(w.in.data.data, '1', w.in.data.length);
4097 2 : status = smb2_write(tree_2, &w);
4098 2 : CHECK_STATUS(status, NT_STATUS_OK);
4099 :
4100 : /* Open the same name over connection 3. */
4101 2 : smb2_lease_create(&io, &ls1, false, fname, LEASE1, smb2_util_lease_state("RWH"));
4102 2 : status = smb2_create(tree_3, mem_ctx, &io);
4103 2 : CHECK_STATUS(status, NT_STATUS_OK);
4104 2 : h1 = io.out.file.handle;
4105 2 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
4106 :
4107 : /* h1 should have replied with NONE. */
4108 2 : CHECK_LEASE(&io, "", true, LEASE1, 0);
4109 :
4110 : /* We should have broken h to NONE. */
4111 2 : CHECK_BREAK_INFO("RWH", "", LEASE1);
4112 :
4113 : /* Try to upgrade to RWH over connection 2 */
4114 2 : smb2_lease_create(&io, &ls1, false, fname, LEASE1, smb2_util_lease_state("RWH"));
4115 2 : status = smb2_create(tree_2, mem_ctx, &io);
4116 2 : CHECK_STATUS(status, NT_STATUS_OK);
4117 2 : h2 = io.out.file.handle;
4118 2 : CHECK_VAL(io.out.create_action, NTCREATEX_ACTION_EXISTED);
4119 2 : CHECK_VAL(io.out.size, 4096);
4120 2 : CHECK_VAL(io.out.file_attr, FILE_ATTRIBUTE_ARCHIVE);
4121 : /* Should have been denied. */
4122 2 : CHECK_LEASE(&io, "", true, LEASE1, 0);
4123 2 : smb2_util_close(tree_2, h2);
4124 :
4125 : /* Try to upgrade to RWH over connection 3 */
4126 2 : smb2_lease_create(&io, &ls1, false, fname, LEASE1, smb2_util_lease_state("RWH"));
4127 2 : status = smb2_create(tree_3, mem_ctx, &io);
4128 2 : CHECK_STATUS(status, NT_STATUS_OK);
4129 2 : h2 = io.out.file.handle;
4130 2 : CHECK_VAL(io.out.create_action, NTCREATEX_ACTION_EXISTED);
4131 2 : CHECK_VAL(io.out.size, 0);
4132 2 : CHECK_VAL(io.out.file_attr, FILE_ATTRIBUTE_ARCHIVE);
4133 : /* Should have been denied. */
4134 2 : CHECK_LEASE(&io, "", true, LEASE1, 0);
4135 2 : smb2_util_close(tree_3, h2);
4136 :
4137 : /* Write some data into it. */
4138 2 : w.in.file.handle = h1;
4139 2 : w.in.offset = 0;
4140 2 : w.in.data = data_blob_talloc(mem_ctx, NULL, 1024);
4141 2 : memset(w.in.data.data, '2', w.in.data.length);
4142 2 : status = smb2_write(tree_3, &w);
4143 2 : CHECK_STATUS(status, NT_STATUS_OK);
4144 :
4145 : /* Close everything.. */
4146 2 : smb2_util_close(tree_2, h);
4147 2 : smb2_util_close(tree_3, h1);
4148 :
4149 : /* And ensure we can get a lease ! */
4150 2 : smb2_lease_create(&io, &ls1, false, fname, LEASE1, smb2_util_lease_state("RWH"));
4151 2 : status = smb2_create(tree_2, mem_ctx, &io);
4152 2 : CHECK_STATUS(status, NT_STATUS_OK);
4153 2 : CHECK_VAL(io.out.create_action, NTCREATEX_ACTION_EXISTED);
4154 2 : CHECK_VAL(io.out.file_attr, FILE_ATTRIBUTE_ARCHIVE);
4155 2 : CHECK_LEASE(&io, "RWH", true, LEASE1, 0);
4156 2 : h = io.out.file.handle;
4157 : /* And the file is the right size. */
4158 2 : CHECK_VAL(io.out.size, 4096); \
4159 : /* Close it. */
4160 2 : smb2_util_close(tree_2, h);
4161 :
4162 : /* And ensure we can get a lease ! */
4163 2 : smb2_lease_create(&io, &ls1, false, fname, LEASE1, smb2_util_lease_state("RWH"));
4164 2 : status = smb2_create(tree_3, mem_ctx, &io);
4165 2 : CHECK_STATUS(status, NT_STATUS_OK);
4166 2 : CHECK_VAL(io.out.create_action, NTCREATEX_ACTION_EXISTED);
4167 2 : CHECK_VAL(io.out.file_attr, FILE_ATTRIBUTE_ARCHIVE);
4168 2 : CHECK_LEASE(&io, "RWH", true, LEASE1, 0);
4169 2 : h = io.out.file.handle;
4170 : /* And the file is the right size. */
4171 2 : CHECK_VAL(io.out.size, 1024); \
4172 : /* Close it. */
4173 2 : smb2_util_close(tree_3, h);
4174 :
4175 2 : done:
4176 :
4177 2 : if (tree_2 != NULL) {
4178 2 : smb2_util_close(tree_2, h);
4179 2 : smb2_util_unlink(tree_2, fname);
4180 : }
4181 2 : if (tree_3 != NULL) {
4182 2 : smb2_util_close(tree_3, h1);
4183 2 : smb2_util_close(tree_3, h2);
4184 :
4185 2 : smb2_util_unlink(tree_3, fname);
4186 : }
4187 :
4188 : /* Set sharename back. */
4189 2 : lpcfg_set_cmdline(tctx->lp_ctx, "torture:share", orig_share);
4190 :
4191 2 : talloc_free(mem_ctx);
4192 :
4193 2 : return ret;
4194 : }
4195 :
4196 : /*
4197 : * Test identifies a bug where the Samba server will not trigger a lease break
4198 : * for a handle caching lease held by a client when the underlying file is
4199 : * deleted.
4200 : * Test:
4201 : * Connect session2.
4202 : * open file in session1
4203 : * session1 should have RWH lease.
4204 : * open file in session2
4205 : * lease break sent to session1 to downgrade lease to RH
4206 : * close file in session 2
4207 : * unlink file in session 2
4208 : * lease break sent to session1 to downgrade lease to R
4209 : * Cleanup
4210 : */
4211 4 : static bool test_lease_unlink(struct torture_context *tctx,
4212 : struct smb2_tree *tree1)
4213 : {
4214 4 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
4215 0 : NTSTATUS status;
4216 4 : bool ret = true;
4217 0 : struct smbcli_options transport2_options;
4218 4 : struct smb2_tree *tree2 = NULL;
4219 4 : struct smb2_transport *transport1 = tree1->session->transport;
4220 0 : struct smb2_transport *transport2;
4221 4 : struct smb2_handle h1 = {{ 0 }};
4222 4 : struct smb2_handle h2 = {{ 0 }};
4223 4 : const char *fname = "lease_unlink.dat";
4224 0 : uint32_t caps;
4225 0 : struct smb2_create io1;
4226 0 : struct smb2_create io2;
4227 0 : struct smb2_lease ls1;
4228 0 : struct smb2_lease ls2;
4229 :
4230 4 : caps = smb2cli_conn_server_capabilities(
4231 4 : tree1->session->transport->conn);
4232 4 : if (!(caps & SMB2_CAP_LEASING)) {
4233 2 : torture_skip(tctx, "leases are not supported");
4234 : }
4235 :
4236 : /* Connect 2nd connection */
4237 2 : transport2_options = transport1->options;
4238 2 : transport2_options.client_guid = GUID_random();
4239 2 : if (!torture_smb2_connection_ext(tctx, 0, &transport2_options, &tree2)) {
4240 0 : torture_warning(tctx, "couldn't reconnect, bailing\n");
4241 0 : return false;
4242 : }
4243 2 : transport2 = tree2->session->transport;
4244 :
4245 : /* Set lease handlers */
4246 2 : transport1->lease.handler = torture_lease_handler;
4247 2 : transport1->lease.private_data = tree1;
4248 2 : transport2->lease.handler = torture_lease_handler;
4249 2 : transport2->lease.private_data = tree2;
4250 :
4251 :
4252 2 : smb2_lease_create(&io1, &ls1, false, fname, LEASE1,
4253 : smb2_util_lease_state("RHW"));
4254 2 : smb2_lease_create(&io2, &ls2, false, fname, LEASE2,
4255 : smb2_util_lease_state("RHW"));
4256 :
4257 2 : smb2_util_unlink(tree1, fname);
4258 :
4259 2 : torture_comment(tctx, "Client opens fname with session 1\n");
4260 2 : torture_reset_lease_break_info(tctx, &lease_break_info);
4261 2 : status = smb2_create(tree1, mem_ctx, &io1);
4262 2 : CHECK_STATUS(status, NT_STATUS_OK);
4263 2 : h1 = io1.out.file.handle;
4264 2 : CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
4265 2 : CHECK_LEASE(&io1, "RHW", true, LEASE1, 0);
4266 2 : CHECK_VAL(lease_break_info.count, 0);
4267 :
4268 2 : torture_comment(tctx, "Client opens fname with session 2\n");
4269 2 : torture_reset_lease_break_info(tctx, &lease_break_info);
4270 2 : status = smb2_create(tree2, mem_ctx, &io2);
4271 2 : CHECK_STATUS(status, NT_STATUS_OK);
4272 2 : h2 = io2.out.file.handle;
4273 2 : CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
4274 2 : CHECK_LEASE(&io2, "RH", true, LEASE2, 0);
4275 2 : CHECK_VAL(lease_break_info.count, 1);
4276 2 : CHECK_BREAK_INFO("RHW", "RH", LEASE1);
4277 :
4278 2 : torture_comment(tctx,
4279 : "Client closes and then unlinks fname with session 2\n");
4280 2 : torture_reset_lease_break_info(tctx, &lease_break_info);
4281 2 : smb2_util_close(tree2, h2);
4282 2 : smb2_util_unlink(tree2, fname);
4283 2 : CHECK_VAL(lease_break_info.count, 1);
4284 2 : CHECK_BREAK_INFO("RH", "R", LEASE1);
4285 :
4286 2 : done:
4287 2 : smb2_util_close(tree1, h1);
4288 2 : smb2_util_close(tree2, h2);
4289 2 : smb2_util_unlink(tree1, fname);
4290 :
4291 2 : return ret;
4292 : }
4293 :
4294 4 : static bool test_lease_timeout_disconnect(struct torture_context *tctx,
4295 : struct smb2_tree *tree1)
4296 : {
4297 4 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
4298 0 : NTSTATUS status;
4299 4 : bool ret = true;
4300 0 : struct smbcli_options transport2_options;
4301 0 : struct smbcli_options transport3_options;
4302 4 : struct smb2_tree *tree2 = NULL;
4303 4 : struct smb2_tree *tree3 = NULL;
4304 4 : struct smb2_transport *transport1 = tree1->session->transport;
4305 0 : struct smb2_transport *transport2;
4306 0 : struct smb2_transport *transport3;
4307 4 : const char *fname = "lease_timeout_logoff.dat" ;
4308 0 : uint32_t caps;
4309 0 : struct smb2_create io1;
4310 0 : struct smb2_create io2;
4311 4 : struct smb2_request *req2 = NULL;
4312 0 : struct smb2_lease ls1;
4313 :
4314 4 : caps = smb2cli_conn_server_capabilities(
4315 4 : tree1->session->transport->conn);
4316 4 : if (!(caps & SMB2_CAP_LEASING)) {
4317 2 : torture_skip(tctx, "leases are not supported");
4318 : }
4319 :
4320 2 : smb2_util_unlink(tree1, fname);
4321 :
4322 : /* Connect 2nd connection */
4323 2 : torture_comment(tctx, "connect tree2 with the same client_guid\n");
4324 2 : transport2_options = transport1->options;
4325 2 : if (!torture_smb2_connection_ext(tctx, 0, &transport2_options, &tree2)) {
4326 0 : torture_warning(tctx, "couldn't reconnect, bailing\n");
4327 0 : return false;
4328 : }
4329 2 : transport2 = tree2->session->transport;
4330 :
4331 : /* Connect 3rd connection */
4332 2 : torture_comment(tctx, "connect tree3 with the same client_guid\n");
4333 2 : transport3_options = transport1->options;
4334 2 : if (!torture_smb2_connection_ext(tctx, 0, &transport3_options, &tree3)) {
4335 0 : torture_warning(tctx, "couldn't reconnect, bailing\n");
4336 0 : return false;
4337 : }
4338 2 : transport3 = tree3->session->transport;
4339 :
4340 : /* Set lease handlers */
4341 2 : transport1->lease.handler = torture_lease_handler;
4342 2 : transport1->lease.private_data = tree1;
4343 2 : transport2->lease.handler = torture_lease_handler;
4344 2 : transport2->lease.private_data = tree2;
4345 2 : transport3->lease.handler = torture_lease_handler;
4346 2 : transport3->lease.private_data = tree3;
4347 :
4348 2 : smb2_lease_create_share(&io1, &ls1, false, fname,
4349 : smb2_util_share_access(""),
4350 : LEASE1,
4351 : smb2_util_lease_state("RH"));
4352 2 : io1.in.durable_open = true;
4353 2 : smb2_generic_create(&io2, NULL, false, fname,
4354 : NTCREATEX_DISP_OPEN_IF,
4355 : SMB2_OPLOCK_LEVEL_NONE, 0, 0);
4356 :
4357 2 : torture_comment(tctx, "tree1: create file[%s] with durable RH lease (SHARE NONE)\n", fname);
4358 2 : torture_reset_lease_break_info(tctx, &lease_break_info);
4359 2 : lease_break_info.lease_skip_ack = true;
4360 2 : status = smb2_create(tree1, mem_ctx, &io1);
4361 2 : CHECK_STATUS(status, NT_STATUS_OK);
4362 2 : CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
4363 2 : CHECK_LEASE(&io1, "RH", true, LEASE1, 0);
4364 2 : CHECK_VAL(lease_break_info.count, 0);
4365 :
4366 2 : torture_comment(tctx, "tree1: skip lease acks\n");
4367 2 : torture_reset_lease_break_info(tctx, &lease_break_info);
4368 2 : lease_break_info.lease_skip_ack = true;
4369 2 : torture_comment(tctx, "tree2: open file[%s] without lease (SHARE RWD)\n", fname);
4370 2 : req2 = smb2_create_send(tree2, &io2);
4371 2 : torture_assert(tctx, req2 != NULL, "req2 started");
4372 :
4373 2 : torture_comment(tctx, "tree1: wait for lease break\n");
4374 2 : torture_wait_for_lease_break(tctx);
4375 2 : CHECK_VAL(lease_break_info.count, 1);
4376 2 : CHECK_BREAK_INFO("RH", "R", LEASE1);
4377 :
4378 2 : torture_comment(tctx, "tree1: reset lease handler\n");
4379 2 : torture_reset_lease_break_info(tctx, &lease_break_info);
4380 2 : lease_break_info.lease_skip_ack = true;
4381 2 : CHECK_VAL(lease_break_info.count, 0);
4382 :
4383 2 : torture_comment(tctx, "tree2: check for SMB2_REQUEST_RECV\n");
4384 2 : torture_assert_int_equal(tctx, req2->state,
4385 : SMB2_REQUEST_RECV,
4386 : "SMB2_REQUEST_RECV");
4387 :
4388 2 : torture_comment(tctx, "sleep 1\n");
4389 2 : smb_msleep(1000);
4390 :
4391 2 : torture_comment(tctx, "transport1: keepalive\n");
4392 2 : status = smb2_keepalive(transport1);
4393 2 : CHECK_STATUS(status, NT_STATUS_OK);
4394 :
4395 2 : torture_comment(tctx, "transport2: keepalive\n");
4396 2 : status = smb2_keepalive(transport2);
4397 2 : CHECK_STATUS(status, NT_STATUS_OK);
4398 :
4399 2 : torture_comment(tctx, "transport3: keepalive\n");
4400 2 : status = smb2_keepalive(transport3);
4401 2 : CHECK_STATUS(status, NT_STATUS_OK);
4402 :
4403 2 : torture_comment(tctx, "tree2: check for SMB2_REQUEST_RECV\n");
4404 2 : torture_assert_int_equal(tctx, req2->state,
4405 : SMB2_REQUEST_RECV,
4406 : "SMB2_REQUEST_RECV");
4407 2 : torture_comment(tctx, "tree2: check for STATUS_PENDING\n");
4408 2 : torture_assert(tctx, req2->cancel.can_cancel, "STATUS_PENDING");
4409 :
4410 2 : torture_comment(tctx, "sleep 1\n");
4411 2 : smb_msleep(1000);
4412 2 : torture_comment(tctx, "transport1: keepalive\n");
4413 2 : status = smb2_keepalive(transport1);
4414 2 : CHECK_STATUS(status, NT_STATUS_OK);
4415 2 : torture_comment(tctx, "transport2: disconnect\n");
4416 2 : TALLOC_FREE(tree2);
4417 :
4418 2 : torture_comment(tctx, "sleep 1\n");
4419 2 : smb_msleep(1000);
4420 2 : torture_comment(tctx, "transport1: keepalive\n");
4421 2 : status = smb2_keepalive(transport1);
4422 2 : CHECK_STATUS(status, NT_STATUS_OK);
4423 2 : torture_comment(tctx, "transport1: disconnect\n");
4424 2 : TALLOC_FREE(tree1);
4425 :
4426 2 : torture_comment(tctx, "sleep 1\n");
4427 2 : smb_msleep(1000);
4428 2 : torture_comment(tctx, "transport3: keepalive\n");
4429 2 : status = smb2_keepalive(transport3);
4430 2 : CHECK_STATUS(status, NT_STATUS_OK);
4431 2 : torture_comment(tctx, "transport3: disconnect\n");
4432 2 : TALLOC_FREE(tree3);
4433 :
4434 0 : done:
4435 :
4436 2 : return ret;
4437 : }
4438 :
4439 4 : static bool test_lease_duplicate_create(struct torture_context *tctx,
4440 : struct smb2_tree *tree)
4441 : {
4442 4 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
4443 0 : struct smb2_create io;
4444 0 : struct smb2_lease ls;
4445 4 : struct smb2_handle h1 = {{0}};
4446 4 : struct smb2_handle h2 = {{0}};
4447 0 : NTSTATUS status;
4448 4 : const char *fname1 = "duplicate_create1.dat";
4449 4 : const char *fname2 = "duplicate_create2.dat";
4450 4 : bool ret = true;
4451 0 : uint32_t caps;
4452 :
4453 4 : caps = smb2cli_conn_server_capabilities(
4454 4 : tree->session->transport->conn);
4455 4 : if (!(caps & SMB2_CAP_LEASING)) {
4456 2 : torture_skip(tctx, "leases are not supported");
4457 : }
4458 :
4459 : /* Ensure files don't exist. */
4460 2 : smb2_util_unlink(tree, fname1);
4461 2 : smb2_util_unlink(tree, fname2);
4462 :
4463 : /* Create file1 - LEASE1 key. */
4464 2 : smb2_lease_create(&io, &ls, false, fname1, LEASE1,
4465 : smb2_util_lease_state("RWH"));
4466 2 : status = smb2_create(tree, mem_ctx, &io);
4467 2 : CHECK_STATUS(status, NT_STATUS_OK);
4468 2 : h1 = io.out.file.handle;
4469 2 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
4470 2 : CHECK_LEASE(&io, "RWH", true, LEASE1, 0);
4471 :
4472 : /*
4473 : * Create file2 with the same LEASE1 key - this should fail with.
4474 : * INVALID_PARAMETER.
4475 : */
4476 2 : smb2_lease_create(&io, &ls, false, fname2, LEASE1,
4477 : smb2_util_lease_state("RWH"));
4478 2 : status = smb2_create(tree, mem_ctx, &io);
4479 2 : CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
4480 2 : smb2_util_close(tree, h1);
4481 :
4482 2 : done:
4483 2 : smb2_util_close(tree, h2);
4484 2 : smb2_util_close(tree, h1);
4485 2 : smb2_util_unlink(tree, fname1);
4486 2 : smb2_util_unlink(tree, fname2);
4487 2 : talloc_free(mem_ctx);
4488 2 : return ret;
4489 : }
4490 :
4491 4 : static bool test_lease_duplicate_open(struct torture_context *tctx,
4492 : struct smb2_tree *tree)
4493 : {
4494 4 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
4495 0 : struct smb2_create io;
4496 0 : struct smb2_lease ls;
4497 4 : struct smb2_handle h1 = {{0}};
4498 4 : struct smb2_handle h2 = {{0}};
4499 0 : NTSTATUS status;
4500 4 : const char *fname1 = "duplicate_open1.dat";
4501 4 : const char *fname2 = "duplicate_open2.dat";
4502 4 : bool ret = true;
4503 0 : uint32_t caps;
4504 :
4505 4 : caps = smb2cli_conn_server_capabilities(
4506 4 : tree->session->transport->conn);
4507 4 : if (!(caps & SMB2_CAP_LEASING)) {
4508 2 : torture_skip(tctx, "leases are not supported");
4509 : }
4510 :
4511 : /* Ensure files don't exist. */
4512 2 : smb2_util_unlink(tree, fname1);
4513 2 : smb2_util_unlink(tree, fname2);
4514 :
4515 : /* Create file1 - LEASE1 key. */
4516 2 : smb2_lease_create(&io, &ls, false, fname1, LEASE1,
4517 : smb2_util_lease_state("RWH"));
4518 2 : status = smb2_create(tree, mem_ctx, &io);
4519 2 : CHECK_STATUS(status, NT_STATUS_OK);
4520 2 : h1 = io.out.file.handle;
4521 2 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
4522 2 : CHECK_LEASE(&io, "RWH", true, LEASE1, 0);
4523 :
4524 : /* Leave file1 open and leased. */
4525 :
4526 : /* Create file2 - no lease. */
4527 2 : smb2_lease_create(&io, NULL, false, fname2, 0,
4528 : smb2_util_lease_state("RWH"));
4529 2 : status = smb2_create(tree, mem_ctx, &io);
4530 2 : CHECK_STATUS(status, NT_STATUS_OK);
4531 2 : h2 = io.out.file.handle;
4532 2 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
4533 : /* Close it. */
4534 2 : smb2_util_close(tree, h2);
4535 :
4536 : /*
4537 : * Try and open file2 with the same LEASE1 key - this should fail with.
4538 : * INVALID_PARAMETER.
4539 : */
4540 2 : smb2_lease_create(&io, &ls, false, fname2, LEASE1,
4541 : smb2_util_lease_state("RWH"));
4542 2 : status = smb2_create(tree, mem_ctx, &io);
4543 2 : CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
4544 : /*
4545 : * If we did open this is an error, but save off
4546 : * the handle so we close below.
4547 : */
4548 2 : h2 = io.out.file.handle;
4549 :
4550 2 : done:
4551 2 : smb2_util_close(tree, h2);
4552 2 : smb2_util_close(tree, h1);
4553 2 : smb2_util_unlink(tree, fname1);
4554 2 : smb2_util_unlink(tree, fname2);
4555 2 : talloc_free(mem_ctx);
4556 2 : return ret;
4557 : }
4558 :
4559 4 : static bool test_lease_v1_bug_15148(struct torture_context *tctx,
4560 : struct smb2_tree *tree)
4561 : {
4562 4 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
4563 0 : struct smb2_create io1;
4564 0 : struct smb2_create io2;
4565 0 : struct smb2_lease ls1;
4566 0 : struct smb2_lease ls2;
4567 4 : struct smb2_handle h1 = {{0}};
4568 4 : struct smb2_handle h2 = {{0}};
4569 0 : struct smb2_write w;
4570 0 : NTSTATUS status;
4571 4 : const char *fname = "lease_v1_bug_15148.dat";
4572 4 : bool ret = true;
4573 0 : uint32_t caps;
4574 :
4575 4 : caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
4576 4 : if (!(caps & SMB2_CAP_LEASING)) {
4577 2 : torture_skip(tctx, "leases are not supported");
4578 : }
4579 :
4580 2 : tree->session->transport->lease.handler = torture_lease_handler;
4581 2 : tree->session->transport->lease.private_data = tree;
4582 2 : tree->session->transport->oplock.handler = torture_oplock_handler;
4583 2 : tree->session->transport->oplock.private_data = tree;
4584 :
4585 2 : smb2_util_unlink(tree, fname);
4586 :
4587 2 : torture_reset_lease_break_info(tctx, &lease_break_info);
4588 :
4589 : /* Grab R lease over connection 1a */
4590 2 : smb2_lease_create(&io1, &ls1, false, fname, LEASE1, smb2_util_lease_state("R"));
4591 2 : status = smb2_create(tree, mem_ctx, &io1);
4592 2 : CHECK_STATUS(status, NT_STATUS_OK);
4593 2 : h1 = io1.out.file.handle;
4594 2 : CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
4595 2 : CHECK_LEASE(&io1, "R", true, LEASE1, 0);
4596 :
4597 2 : CHECK_NO_BREAK(tctx);
4598 :
4599 : /* Contend with LEASE2. */
4600 2 : smb2_lease_create(&io2, &ls2, false, fname, LEASE2, smb2_util_lease_state("R"));
4601 2 : status = smb2_create(tree, mem_ctx, &io2);
4602 2 : CHECK_STATUS(status, NT_STATUS_OK);
4603 2 : h2 = io2.out.file.handle;
4604 2 : CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
4605 2 : CHECK_LEASE(&io2, "R", true, LEASE2, 0);
4606 :
4607 2 : CHECK_NO_BREAK(tctx);
4608 :
4609 2 : ZERO_STRUCT(w);
4610 2 : w.in.file.handle = h1;
4611 2 : w.in.offset = 0;
4612 2 : w.in.data = data_blob_talloc(mem_ctx, NULL, 4096);
4613 2 : memset(w.in.data.data, 'o', w.in.data.length);
4614 2 : status = smb2_write(tree, &w);
4615 2 : CHECK_STATUS(status, NT_STATUS_OK);
4616 :
4617 2 : ls2.lease_epoch += 1;
4618 2 : CHECK_BREAK_INFO("R", "", LEASE2);
4619 :
4620 2 : torture_reset_lease_break_info(tctx, &lease_break_info);
4621 :
4622 2 : ZERO_STRUCT(w);
4623 2 : w.in.file.handle = h1;
4624 2 : w.in.offset = 0;
4625 2 : w.in.data = data_blob_talloc(mem_ctx, NULL, 4096);
4626 2 : memset(w.in.data.data, 'O', w.in.data.length);
4627 2 : status = smb2_write(tree, &w);
4628 2 : CHECK_STATUS(status, NT_STATUS_OK);
4629 :
4630 2 : CHECK_NO_BREAK(tctx);
4631 :
4632 2 : ZERO_STRUCT(w);
4633 2 : w.in.file.handle = h2;
4634 2 : w.in.offset = 0;
4635 2 : w.in.data = data_blob_talloc(mem_ctx, NULL, 4096);
4636 2 : memset(w.in.data.data, 'o', w.in.data.length);
4637 2 : status = smb2_write(tree, &w);
4638 2 : CHECK_STATUS(status, NT_STATUS_OK);
4639 :
4640 2 : ls1.lease_epoch += 1;
4641 2 : CHECK_BREAK_INFO("R", "", LEASE1);
4642 :
4643 2 : done:
4644 2 : smb2_util_close(tree, h1);
4645 2 : smb2_util_close(tree, h2);
4646 :
4647 2 : smb2_util_unlink(tree, fname);
4648 :
4649 2 : talloc_free(mem_ctx);
4650 :
4651 2 : return ret;
4652 : }
4653 :
4654 4 : static bool test_lease_v2_bug_15148(struct torture_context *tctx,
4655 : struct smb2_tree *tree)
4656 : {
4657 4 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
4658 0 : struct smb2_create io1;
4659 0 : struct smb2_create io2;
4660 0 : struct smb2_lease ls1;
4661 0 : struct smb2_lease ls2;
4662 4 : struct smb2_handle h1 = {{0}};
4663 4 : struct smb2_handle h2 = {{0}};
4664 0 : struct smb2_write w;
4665 0 : NTSTATUS status;
4666 4 : const char *fname = "lease_v2_bug_15148.dat";
4667 4 : bool ret = true;
4668 0 : uint32_t caps;
4669 0 : enum protocol_types protocol;
4670 :
4671 4 : caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
4672 4 : if (!(caps & SMB2_CAP_LEASING)) {
4673 2 : torture_skip(tctx, "leases are not supported");
4674 : }
4675 :
4676 2 : protocol = smbXcli_conn_protocol(tree->session->transport->conn);
4677 2 : if (protocol < PROTOCOL_SMB3_00) {
4678 0 : torture_skip(tctx, "v2 leases are not supported");
4679 : }
4680 :
4681 2 : tree->session->transport->lease.handler = torture_lease_handler;
4682 2 : tree->session->transport->lease.private_data = tree;
4683 2 : tree->session->transport->oplock.handler = torture_oplock_handler;
4684 2 : tree->session->transport->oplock.private_data = tree;
4685 :
4686 2 : smb2_util_unlink(tree, fname);
4687 :
4688 2 : torture_reset_lease_break_info(tctx, &lease_break_info);
4689 :
4690 : /* Grab R lease over connection 1a */
4691 2 : smb2_lease_v2_create(&io1, &ls1, false, fname, LEASE1, NULL,
4692 : smb2_util_lease_state("R"), 0x4711);
4693 2 : status = smb2_create(tree, mem_ctx, &io1);
4694 2 : CHECK_STATUS(status, NT_STATUS_OK);
4695 2 : h1 = io1.out.file.handle;
4696 2 : CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
4697 2 : ls1.lease_epoch += 1;
4698 2 : CHECK_LEASE_V2(&io1, "R", true, LEASE1,
4699 : 0, 0, ls1.lease_epoch);
4700 :
4701 2 : CHECK_NO_BREAK(tctx);
4702 :
4703 : /* Contend with LEASE2. */
4704 2 : smb2_lease_v2_create(&io2, &ls2, false, fname, LEASE2, NULL,
4705 : smb2_util_lease_state("R"), 0x11);
4706 2 : status = smb2_create(tree, mem_ctx, &io2);
4707 2 : CHECK_STATUS(status, NT_STATUS_OK);
4708 2 : h2 = io2.out.file.handle;
4709 2 : CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
4710 2 : ls2.lease_epoch += 1;
4711 2 : CHECK_LEASE_V2(&io2, "R", true, LEASE2,
4712 : 0, 0, ls2.lease_epoch);
4713 :
4714 2 : CHECK_NO_BREAK(tctx);
4715 :
4716 2 : ZERO_STRUCT(w);
4717 2 : w.in.file.handle = h1;
4718 2 : w.in.offset = 0;
4719 2 : w.in.data = data_blob_talloc(mem_ctx, NULL, 4096);
4720 2 : memset(w.in.data.data, 'o', w.in.data.length);
4721 2 : status = smb2_write(tree, &w);
4722 2 : CHECK_STATUS(status, NT_STATUS_OK);
4723 :
4724 2 : ls2.lease_epoch += 1;
4725 2 : CHECK_BREAK_INFO_V2(tree->session->transport,
4726 : "R", "", LEASE2, ls2.lease_epoch);
4727 :
4728 2 : torture_reset_lease_break_info(tctx, &lease_break_info);
4729 :
4730 2 : ZERO_STRUCT(w);
4731 2 : w.in.file.handle = h1;
4732 2 : w.in.offset = 0;
4733 2 : w.in.data = data_blob_talloc(mem_ctx, NULL, 4096);
4734 2 : memset(w.in.data.data, 'O', w.in.data.length);
4735 2 : status = smb2_write(tree, &w);
4736 2 : CHECK_STATUS(status, NT_STATUS_OK);
4737 :
4738 2 : CHECK_NO_BREAK(tctx);
4739 :
4740 2 : ZERO_STRUCT(w);
4741 2 : w.in.file.handle = h2;
4742 2 : w.in.offset = 0;
4743 2 : w.in.data = data_blob_talloc(mem_ctx, NULL, 4096);
4744 2 : memset(w.in.data.data, 'o', w.in.data.length);
4745 2 : status = smb2_write(tree, &w);
4746 2 : CHECK_STATUS(status, NT_STATUS_OK);
4747 :
4748 2 : ls1.lease_epoch += 1;
4749 2 : CHECK_BREAK_INFO_V2(tree->session->transport,
4750 : "R", "", LEASE1, ls1.lease_epoch);
4751 :
4752 2 : done:
4753 2 : smb2_util_close(tree, h1);
4754 2 : smb2_util_close(tree, h2);
4755 :
4756 2 : smb2_util_unlink(tree, fname);
4757 :
4758 2 : talloc_free(mem_ctx);
4759 :
4760 2 : return ret;
4761 : }
4762 :
4763 2354 : struct torture_suite *torture_smb2_lease_init(TALLOC_CTX *ctx)
4764 : {
4765 125 : struct torture_suite *suite =
4766 2354 : torture_suite_create(ctx, "lease");
4767 :
4768 2354 : torture_suite_add_1smb2_test(suite, "request", test_lease_request);
4769 2354 : torture_suite_add_1smb2_test(suite, "break_twice",
4770 : test_lease_break_twice);
4771 2354 : torture_suite_add_1smb2_test(suite, "nobreakself",
4772 : test_lease_nobreakself);
4773 2354 : torture_suite_add_1smb2_test(suite, "statopen", test_lease_statopen);
4774 2354 : torture_suite_add_1smb2_test(suite, "statopen2", test_lease_statopen2);
4775 2354 : torture_suite_add_1smb2_test(suite, "statopen3", test_lease_statopen3);
4776 2354 : torture_suite_add_1smb2_test(suite, "statopen4", test_lease_statopen4);
4777 2354 : torture_suite_add_1smb2_test(suite, "upgrade", test_lease_upgrade);
4778 2354 : torture_suite_add_1smb2_test(suite, "upgrade2", test_lease_upgrade2);
4779 2354 : torture_suite_add_1smb2_test(suite, "upgrade3", test_lease_upgrade3);
4780 2354 : torture_suite_add_1smb2_test(suite, "break", test_lease_break);
4781 2354 : torture_suite_add_1smb2_test(suite, "oplock", test_lease_oplock);
4782 2354 : torture_suite_add_1smb2_test(suite, "multibreak", test_lease_multibreak);
4783 2354 : torture_suite_add_1smb2_test(suite, "breaking1", test_lease_breaking1);
4784 2354 : torture_suite_add_1smb2_test(suite, "breaking2", test_lease_breaking2);
4785 2354 : torture_suite_add_1smb2_test(suite, "breaking3", test_lease_breaking3);
4786 2354 : torture_suite_add_1smb2_test(suite, "v2_breaking3", test_lease_v2_breaking3);
4787 2354 : torture_suite_add_1smb2_test(suite, "breaking4", test_lease_breaking4);
4788 2354 : torture_suite_add_1smb2_test(suite, "breaking5", test_lease_breaking5);
4789 2354 : torture_suite_add_1smb2_test(suite, "breaking6", test_lease_breaking6);
4790 2354 : torture_suite_add_2smb2_test(suite, "lock1", test_lease_lock1);
4791 2354 : torture_suite_add_1smb2_test(suite, "complex1", test_lease_complex1);
4792 2354 : torture_suite_add_1smb2_test(suite, "v2_request_parent",
4793 : test_lease_v2_request_parent);
4794 2354 : torture_suite_add_1smb2_test(suite, "v2_request", test_lease_v2_request);
4795 2354 : torture_suite_add_1smb2_test(suite, "v2_epoch1", test_lease_v2_epoch1);
4796 2354 : torture_suite_add_1smb2_test(suite, "v2_epoch2", test_lease_v2_epoch2);
4797 2354 : torture_suite_add_1smb2_test(suite, "v2_epoch3", test_lease_v2_epoch3);
4798 2354 : torture_suite_add_1smb2_test(suite, "v2_complex1", test_lease_v2_complex1);
4799 2354 : torture_suite_add_1smb2_test(suite, "v2_complex2", test_lease_v2_complex2);
4800 2354 : torture_suite_add_1smb2_test(suite, "v2_rename", test_lease_v2_rename);
4801 2354 : torture_suite_add_1smb2_test(suite, "dynamic_share", test_lease_dynamic_share);
4802 2354 : torture_suite_add_1smb2_test(suite, "timeout", test_lease_timeout);
4803 2354 : torture_suite_add_1smb2_test(suite, "unlink", test_lease_unlink);
4804 2354 : torture_suite_add_1smb2_test(suite, "timeout-disconnect", test_lease_timeout_disconnect);
4805 2354 : torture_suite_add_1smb2_test(suite, "rename_wait",
4806 : test_lease_rename_wait);
4807 2354 : torture_suite_add_1smb2_test(suite, "duplicate_create",
4808 : test_lease_duplicate_create);
4809 2354 : torture_suite_add_1smb2_test(suite, "duplicate_open",
4810 : test_lease_duplicate_open);
4811 2354 : torture_suite_add_1smb2_test(suite, "v1_bug15148",
4812 : test_lease_v1_bug_15148);
4813 2354 : torture_suite_add_1smb2_test(suite, "v2_bug15148",
4814 : test_lease_v2_bug_15148);
4815 :
4816 2354 : suite->description = talloc_strdup(suite, "SMB2-LEASE tests");
4817 :
4818 2354 : return suite;
4819 : }
|