s4:torture:smb2: fix illegal write/uninitialized data bug in the lease.multibreak...
[tridge/samba.git] / source4 / torture / smb2 / lease.c
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 "libcli/smb2/smb2.h"
24 #include "libcli/smb2/smb2_calls.h"
25 #include "torture/torture.h"
26 #include "torture/smb2/proto.h"
27
28 static inline uint32_t lease(const char *ls) {
29         uint32_t val = 0;
30         int i;
31
32         for (i = 0; i < strlen(ls); i++) {
33                 switch (ls[i]) {
34                 case 'R':
35                         val |= SMB2_LEASE_READ;
36                         break;
37                 case 'H':
38                         val |= SMB2_LEASE_HANDLE;
39                         break;
40                 case 'W':
41                         val |= SMB2_LEASE_WRITE;
42                         break;
43                 }
44         }
45
46         return val;
47 }
48
49 #define CHECK_VAL(v, correct) do { \
50         if ((v) != (correct)) { \
51                 torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s got 0x%x - should be 0x%x\n", \
52                                 __location__, #v, (int)(v), (int)(correct)); \
53                 ret = false; \
54         }} while (0)
55
56 #define CHECK_STATUS(status, correct) do { \
57         if (!NT_STATUS_EQUAL(status, correct)) { \
58                 torture_result(tctx, TORTURE_FAIL, __location__": Incorrect status %s - should be %s", \
59                        nt_errstr(status), nt_errstr(correct)); \
60                 ret = false; \
61                 goto done; \
62         }} while (0)
63
64 static void smb2_generic_create(struct smb2_create *io, struct smb2_lease *ls,
65                                 bool dir, const char *name, uint32_t disposition,
66                                 uint32_t oplock, uint64_t leasekey,
67                                 uint32_t leasestate)
68 {
69         ZERO_STRUCT(*io);
70         io->in.security_flags           = 0x00;
71         io->in.oplock_level             = oplock;
72         io->in.impersonation_level      = NTCREATEX_IMPERSONATION_IMPERSONATION;
73         io->in.create_flags             = 0x00000000;
74         io->in.reserved                 = 0x00000000;
75         io->in.desired_access           = SEC_RIGHTS_FILE_ALL;
76         io->in.file_attributes          = FILE_ATTRIBUTE_NORMAL;
77         io->in.share_access             = NTCREATEX_SHARE_ACCESS_READ |
78                                           NTCREATEX_SHARE_ACCESS_WRITE |
79                                           NTCREATEX_SHARE_ACCESS_DELETE;
80         io->in.create_disposition       = disposition;
81         io->in.create_options           = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
82                                           NTCREATEX_OPTIONS_ASYNC_ALERT |
83                                           NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
84                                           0x00200000;
85         io->in.fname                    = name;
86
87         if (dir) {
88                 io->in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
89                 io->in.share_access &= ~NTCREATEX_SHARE_ACCESS_DELETE;
90                 io->in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
91                 io->in.create_disposition = NTCREATEX_DISP_CREATE;
92         }
93
94         if (ls) {
95                 ZERO_STRUCT(*ls);
96                 ls->lease_key.data[0] = leasekey;
97                 ls->lease_key.data[1] = ~leasekey;
98                 ls->lease_state = leasestate;
99                 io->in.lease_request = ls;
100         }
101 }
102
103 static void smb2_lease_create(struct smb2_create *io, struct smb2_lease *ls,
104                               bool dir, const char *name, uint64_t leasekey,
105                               uint32_t leasestate)
106 {
107         smb2_generic_create(io, ls, dir, name, NTCREATEX_DISP_OPEN_IF,
108             SMB2_OPLOCK_LEVEL_LEASE, leasekey, leasestate);
109 }
110
111 static void smb2_oplock_create(struct smb2_create *io, const char *name,
112                                uint32_t oplock)
113 {
114         smb2_generic_create(io, NULL, false, name, NTCREATEX_DISP_OPEN_IF,
115             oplock, 0, 0);
116 }
117
118 #define CHECK_CREATED(__io, __created, __attribute)                     \
119         do {                                                            \
120                 CHECK_VAL((__io)->out.create_action, NTCREATEX_ACTION_ ## __created); \
121                 CHECK_VAL((__io)->out.alloc_size, 0);                   \
122                 CHECK_VAL((__io)->out.size, 0);                         \
123                 CHECK_VAL((__io)->out.file_attr, (__attribute));        \
124                 CHECK_VAL((__io)->out.reserved2, 0);                    \
125         } while(0)
126
127 #define CHECK_LEASE(__io, __state, __oplevel, __key)                    \
128         do {                                                            \
129                 if (__oplevel) {                                        \
130                         CHECK_VAL((__io)->out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE); \
131                         CHECK_VAL((__io)->out.lease_response.lease_key.data[0], (__key)); \
132                         CHECK_VAL((__io)->out.lease_response.lease_key.data[1], ~(__key)); \
133                         CHECK_VAL((__io)->out.lease_response.lease_state, lease(__state)); \
134                 } else {                                                \
135                         CHECK_VAL((__io)->out.oplock_level, SMB2_OPLOCK_LEVEL_NONE); \
136                         CHECK_VAL((__io)->out.lease_response.lease_key.data[0], 0); \
137                         CHECK_VAL((__io)->out.lease_response.lease_key.data[1], 0); \
138                         CHECK_VAL((__io)->out.lease_response.lease_state, 0); \
139                 }                                                       \
140                                                                         \
141                 CHECK_VAL((__io)->out.lease_response.lease_flags, 0);   \
142                 CHECK_VAL((__io)->out.lease_response.lease_duration, 0); \
143         } while(0)                                                      \
144
145 static const uint64_t LEASE1 = 0xBADC0FFEE0DDF00Dull;
146 static const uint64_t LEASE2 = 0xDEADBEEFFEEDBEADull;
147 static const uint64_t LEASE3 = 0xDAD0FFEDD00DF00Dull;
148
149 #define NREQUEST_RESULTS 8
150 static const char *request_results[NREQUEST_RESULTS][2] = {
151         { "", "" },
152         { "R", "R" },
153         { "H", "" },
154         { "W", "" },
155         { "RH", "RH" },
156         { "RW", "RW" },
157         { "HW", "" },
158         { "RHW", "RHW" },
159 };
160
161 static bool test_lease_request(struct torture_context *tctx,
162                                struct smb2_tree *tree)
163 {
164         TALLOC_CTX *mem_ctx = talloc_new(tctx);
165         struct smb2_create io;
166         struct smb2_lease ls;
167         struct smb2_handle h1, h2;
168         NTSTATUS status;
169         const char *fname = "lease.dat";
170         const char *fname2 = "lease2.dat";
171         const char *sname = "lease.dat:stream";
172         const char *dname = "lease.dir";
173         bool ret = true;
174         int i;
175
176         smb2_util_unlink(tree, fname);
177         smb2_util_unlink(tree, fname2);
178         smb2_util_rmdir(tree, dname);
179
180         /* Win7 is happy to grant RHW leases on files. */
181         smb2_lease_create(&io, &ls, false, fname, LEASE1, lease("RHW"));
182         status = smb2_create(tree, mem_ctx, &io);
183         CHECK_STATUS(status, NT_STATUS_OK);
184         h1 = io.out.file.handle;
185         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
186         CHECK_LEASE(&io, "RHW", true, LEASE1);
187
188         /* But will reject leases on directories. */
189         smb2_lease_create(&io, &ls, true, dname, LEASE2, lease("RHW"));
190         status = smb2_create(tree, mem_ctx, &io);
191         CHECK_STATUS(status, NT_STATUS_OK);
192         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_DIRECTORY);
193         CHECK_LEASE(&io, "", false, 0);
194         smb2_util_close(tree, io.out.file.handle);
195
196         /* Also rejects multiple files leased under the same key. */
197         smb2_lease_create(&io, &ls, true, fname2, LEASE1, lease("RHW"));
198         status = smb2_create(tree, mem_ctx, &io);
199         CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
200
201         /* And grants leases on streams (with separate leasekey). */
202         smb2_lease_create(&io, &ls, false, sname, LEASE2, lease("RHW"));
203         status = smb2_create(tree, mem_ctx, &io);
204         h2 = io.out.file.handle;
205         CHECK_STATUS(status, NT_STATUS_OK);
206         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
207         CHECK_LEASE(&io, "RHW", true, LEASE2);
208         smb2_util_close(tree, h2);
209
210         smb2_util_close(tree, h1);
211
212         /* Now see what combos are actually granted. */
213         for (i = 0; i < NREQUEST_RESULTS; i++) {
214                 torture_comment(tctx, "Requesting lease type %s(%x),"
215                     " expecting %s(%x)\n",
216                     request_results[i][0], lease(request_results[i][0]),
217                     request_results[i][1], lease(request_results[i][1]));
218                 smb2_lease_create(&io, &ls, false, fname, LEASE1,
219                     lease(request_results[i][0]));
220                 status = smb2_create(tree, mem_ctx, &io);
221                 h2 = io.out.file.handle;
222                 CHECK_STATUS(status, NT_STATUS_OK);
223                 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
224                 CHECK_LEASE(&io, request_results[i][1], true, LEASE1);
225                 smb2_util_close(tree, io.out.file.handle);
226         }
227
228  done:
229         smb2_util_close(tree, h1);
230         smb2_util_close(tree, h2);
231
232         smb2_util_unlink(tree, fname);
233         smb2_util_unlink(tree, fname2);
234         smb2_util_rmdir(tree, dname);
235
236         talloc_free(mem_ctx);
237
238         return ret;
239 }
240
241 static bool test_lease_upgrade(struct torture_context *tctx,
242                                struct smb2_tree *tree)
243 {
244         TALLOC_CTX *mem_ctx = talloc_new(tctx);
245         struct smb2_create io;
246         struct smb2_lease ls;
247         struct smb2_handle h, hnew;
248         NTSTATUS status;
249         const char *fname = "lease.dat";
250         bool ret = true;
251
252         smb2_util_unlink(tree, fname);
253
254         /* Grab a RH lease. */
255         smb2_lease_create(&io, &ls, false, fname, LEASE1, lease("RH"));
256         status = smb2_create(tree, mem_ctx, &io);
257         CHECK_STATUS(status, NT_STATUS_OK);
258         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
259         CHECK_LEASE(&io, "RH", true, LEASE1);
260         h = io.out.file.handle;
261
262         /* Upgrades (sidegrades?) to RW leave us with an RH. */
263         smb2_lease_create(&io, &ls, false, fname, LEASE1, lease("RW"));
264         status = smb2_create(tree, mem_ctx, &io);
265         CHECK_STATUS(status, NT_STATUS_OK);
266         CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
267         CHECK_LEASE(&io, "RH", true, LEASE1);
268         hnew = io.out.file.handle;
269
270         smb2_util_close(tree, hnew);
271
272         /* Upgrade to RHW lease. */
273         smb2_lease_create(&io, &ls, false, fname, LEASE1, lease("RHW"));
274         status = smb2_create(tree, mem_ctx, &io);
275         CHECK_STATUS(status, NT_STATUS_OK);
276         CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
277         CHECK_LEASE(&io, "RHW", true, LEASE1);
278         hnew = io.out.file.handle;
279
280         smb2_util_close(tree, h);
281         h = hnew;
282
283         /* Attempt to downgrade - original lease state is maintained. */
284         smb2_lease_create(&io, &ls, false, fname, LEASE1, lease("RH"));
285         status = smb2_create(tree, mem_ctx, &io);
286         CHECK_STATUS(status, NT_STATUS_OK);
287         CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
288         CHECK_LEASE(&io, "RHW", true, LEASE1);
289         hnew = io.out.file.handle;
290
291         smb2_util_close(tree, hnew);
292
293  done:
294         smb2_util_close(tree, h);
295         smb2_util_close(tree, hnew);
296
297         smb2_util_unlink(tree, fname);
298
299         talloc_free(mem_ctx);
300
301         return ret;
302 }
303
304 /**
305  * upgrade2 test.
306  * full matrix of lease upgrade combinations
307  */
308 struct lease_upgrade2_test {
309         const char *initial;
310         const char *upgrade_to;
311         const char *expected;
312 };
313
314 #define NUM_LEASE_TYPES 5
315 #define NUM_UPGRADE_TESTS ( NUM_LEASE_TYPES * NUM_LEASE_TYPES )
316 struct lease_upgrade2_test lease_upgrade2_tests[NUM_UPGRADE_TESTS] = {
317         { "", "", "" },
318         { "", "R", "R" },
319         { "", "RH", "RH" },
320         { "", "RW", "RW" },
321         { "", "RWH", "RWH" },
322
323         { "R", "", "R" },
324         { "R", "R", "R" },
325         { "R", "RH", "RH" },
326         { "R", "RW", "RW" },
327         { "R", "RWH", "RWH" },
328
329         { "RH", "", "RH" },
330         { "RH", "R", "RH" },
331         { "RH", "RH", "RH" },
332         { "RH", "RW", "RH" },
333         { "RH", "RWH", "RWH" },
334
335         { "RW", "", "RW" },
336         { "RW", "R", "RW" },
337         { "RW", "RH", "RW" },
338         { "RW", "RW", "RW" },
339         { "RW", "RWH", "RWH" },
340
341         { "RWH", "", "RWH" },
342         { "RWH", "R", "RWH" },
343         { "RWH", "RH", "RWH" },
344         { "RWH", "RW", "RWH" },
345         { "RWH", "RWH", "RWH" },
346 };
347
348 static bool test_lease_upgrade2(struct torture_context *tctx,
349                                 struct smb2_tree *tree)
350 {
351         TALLOC_CTX *mem_ctx = talloc_new(tctx);
352         struct smb2_handle h, hnew;
353         NTSTATUS status;
354         struct smb2_create io;
355         struct smb2_lease ls;
356         const char *fname = "lease.dat";
357         bool ret = true;
358         int i;
359
360         for (i = 0; i < NUM_UPGRADE_TESTS; i++) {
361                 struct lease_upgrade2_test t = lease_upgrade2_tests[i];
362
363                 smb2_util_unlink(tree, fname);
364
365                 /* Grab a lease. */
366                 smb2_lease_create(&io, &ls, false, fname, LEASE1, lease(t.initial));
367                 status = smb2_create(tree, mem_ctx, &io);
368                 CHECK_STATUS(status, NT_STATUS_OK);
369                 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
370                 CHECK_LEASE(&io, t.initial, true, LEASE1);
371                 h = io.out.file.handle;
372
373                 /* Upgrade. */
374                 smb2_lease_create(&io, &ls, false, fname, LEASE1, lease(t.upgrade_to));
375                 status = smb2_create(tree, mem_ctx, &io);
376                 CHECK_STATUS(status, NT_STATUS_OK);
377                 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
378                 CHECK_LEASE(&io, t.expected, true, LEASE1);
379                 hnew = io.out.file.handle;
380
381                 smb2_util_close(tree, hnew);
382                 smb2_util_close(tree, h);
383         }
384
385  done:
386         smb2_util_close(tree, h);
387         smb2_util_close(tree, hnew);
388
389         smb2_util_unlink(tree, fname);
390
391         talloc_free(mem_ctx);
392
393         return ret;
394 }
395
396
397 #define CHECK_LEASE_BREAK(__lb, __oldstate, __state, __key)             \
398         do {                                                            \
399                 CHECK_VAL((__lb)->new_lease_state, lease(__state));     \
400                 CHECK_VAL((__lb)->current_lease.lease_state, lease(__oldstate)); \
401                 CHECK_VAL((__lb)->current_lease.lease_key.data[0], (__key)); \
402                 CHECK_VAL((__lb)->current_lease.lease_key.data[1], ~(__key)); \
403         } while(0)
404
405 #define CHECK_LEASE_BREAK_ACK(__lba, __state, __key)                    \
406         do {                                                            \
407                 CHECK_VAL((__lba)->out.reserved, 0);                    \
408                 CHECK_VAL((__lba)->out.lease.lease_key.data[0], (__key)); \
409                 CHECK_VAL((__lba)->out.lease.lease_key.data[1], ~(__key)); \
410                 CHECK_VAL((__lba)->out.lease.lease_state, lease(__state)); \
411                 CHECK_VAL((__lba)->out.lease.lease_flags, 0);           \
412                 CHECK_VAL((__lba)->out.lease.lease_duration, 0);        \
413         } while(0)
414
415 static struct {
416         struct smb2_lease_break lease_break;
417         struct smb2_lease_break_ack lease_break_ack;
418         int count;
419         int failures;
420
421         struct smb2_handle oplock_handle;
422         int held_oplock_level;
423         int oplock_level;
424         int oplock_count;
425         int oplock_failures;
426 } break_info;
427
428 #define CHECK_BREAK_INFO(__oldstate, __state, __key)                    \
429         do {                                                            \
430                 CHECK_VAL(break_info.failures, 0);                      \
431                 CHECK_VAL(break_info.count, 1);                         \
432                 CHECK_LEASE_BREAK(&break_info.lease_break, (__oldstate), \
433                     (__state), (__key));                                \
434                 if (break_info.lease_break.break_flags &                \
435                     SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED) {        \
436                         CHECK_LEASE_BREAK_ACK(&break_info.lease_break_ack, \
437                                               (__state), (__key));      \
438                 }                                                       \
439         } while(0)
440
441 static void torture_lease_break_callback(struct smb2_request *req)
442 {
443         NTSTATUS status;
444
445         status = smb2_lease_break_ack_recv(req, &break_info.lease_break_ack);
446         if (!NT_STATUS_IS_OK(status))
447                 break_info.failures++;
448
449         return;
450 }
451
452 /* a lease break request handler */
453 static bool torture_lease_handler(struct smb2_transport *transport,
454                                   const struct smb2_lease_break *lb,
455                                   void *private_data)
456 {
457         struct smb2_tree *tree = private_data;
458         struct smb2_lease_break_ack io;
459         struct smb2_request *req;
460
461         break_info.lease_break = *lb;
462         break_info.count++;
463
464         if (lb->break_flags & SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED) {
465                 ZERO_STRUCT(io);
466                 io.in.lease.lease_key = lb->current_lease.lease_key;
467                 io.in.lease.lease_state = lb->new_lease_state;
468
469                 req = smb2_lease_break_ack_send(tree, &io);
470                 req->async.fn = torture_lease_break_callback;
471                 req->async.private_data = NULL;
472         }
473
474         return true;
475 }
476
477 /*
478   break_results should be read as "held lease, new lease, hold broken to, new
479   grant", i.e. { "RH", "RW", "RH", "R" } means that if key1 holds RH and key2
480   tries for RW, key1 will be broken to RH (in this case, not broken at all)
481   and key2 will be granted R.
482
483   Note: break_results only includes things that Win7 will actually grant (see
484   request_results above).
485  */
486 #define NBREAK_RESULTS 16
487 static const char *break_results[NBREAK_RESULTS][4] = {
488         {"R",   "R",    "R",    "R"},
489         {"R",   "RH",   "R",    "RH"},
490         {"R",   "RW",   "R",    "R"},
491         {"R",   "RHW",  "R",    "RH"},
492
493         {"RH",  "R",    "RH",   "R"},
494         {"RH",  "RH",   "RH",   "RH"},
495         {"RH",  "RW",   "RH",   "R"},
496         {"RH",  "RHW",  "RH",   "RH"},
497
498         {"RW",  "R",    "R",    "R"},
499         {"RW",  "RH",   "R",    "RH"},
500         {"RW",  "RW",   "R",    "R"},
501         {"RW",  "RHW",  "R",    "RH"},
502
503         {"RHW", "R",    "RH",   "R"},
504         {"RHW", "RH",   "RH",   "RH"},
505         {"RHW", "RW",   "RH",   "R"},
506         {"RHW", "RHW",  "RH",   "RH"},
507 };
508
509 static bool test_lease_break(struct torture_context *tctx,
510                                struct smb2_tree *tree)
511 {
512         TALLOC_CTX *mem_ctx = talloc_new(tctx);
513         struct smb2_create io;
514         struct smb2_lease ls;
515         struct smb2_handle h, h2, h3;
516         NTSTATUS status;
517         const char *fname = "lease.dat";
518         bool ret = true;
519         int i;
520
521         tree->session->transport->lease.handler = torture_lease_handler;
522         tree->session->transport->lease.private_data = tree;
523
524         smb2_util_unlink(tree, fname);
525
526         for (i = 0; i < NBREAK_RESULTS; i++) {
527                 const char *held = break_results[i][0];
528                 const char *contend = break_results[i][1];
529                 const char *brokento = break_results[i][2];
530                 const char *granted = break_results[i][3];
531                 torture_comment(tctx, "Hold %s(%x), requesting %s(%x), "
532                     "expecting break to %s(%x) and grant of %s(%x)\n",
533                     held, lease(held), contend, lease(contend),
534                     brokento, lease(brokento), granted, lease(granted));
535
536                 ZERO_STRUCT(break_info);
537
538                 /* Grab lease. */
539                 smb2_lease_create(&io, &ls, false, fname, LEASE1, lease(held));
540                 status = smb2_create(tree, mem_ctx, &io);
541                 CHECK_STATUS(status, NT_STATUS_OK);
542                 h = io.out.file.handle;
543                 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
544                 CHECK_LEASE(&io, held, true, LEASE1);
545
546                 /* Possibly contend lease. */
547                 smb2_lease_create(&io, &ls, false, fname, LEASE2, lease(contend));
548                 status = smb2_create(tree, mem_ctx, &io);
549                 CHECK_STATUS(status, NT_STATUS_OK);
550                 h2 = io.out.file.handle;
551                 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
552                 CHECK_LEASE(&io, granted, true, LEASE2);
553
554                 if (lease(held) != lease(brokento)) {
555                         CHECK_BREAK_INFO(held, brokento, LEASE1);
556                 } else {
557                         CHECK_VAL(break_info.count, 0);
558                         CHECK_VAL(break_info.failures, 0);
559                 }
560
561                 ZERO_STRUCT(break_info);
562
563                 /*
564                   Now verify that an attempt to upgrade LEASE1 results in no
565                   break and no change in LEASE1.
566                  */
567                 smb2_lease_create(&io, &ls, false, fname, LEASE1, lease("RHW"));
568                 status = smb2_create(tree, mem_ctx, &io);
569                 CHECK_STATUS(status, NT_STATUS_OK);
570                 h3 = io.out.file.handle;
571                 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
572                 CHECK_LEASE(&io, brokento, true, LEASE1);
573                 CHECK_VAL(break_info.count, 0);
574                 CHECK_VAL(break_info.failures, 0);
575
576                 smb2_util_close(tree, h);
577                 smb2_util_close(tree, h2);
578                 smb2_util_close(tree, h3);
579
580                 status = smb2_util_unlink(tree, fname);
581                 CHECK_STATUS(status, NT_STATUS_OK);
582         }
583
584  done:
585         smb2_util_close(tree, h);
586         smb2_util_close(tree, h2);
587
588         smb2_util_unlink(tree, fname);
589
590         talloc_free(mem_ctx);
591
592         return ret;
593 }
594
595 static void torture_oplock_break_callback(struct smb2_request *req)
596 {
597         NTSTATUS status;
598         struct smb2_break br;
599
600         ZERO_STRUCT(br);
601         status = smb2_break_recv(req, &br);
602         if (!NT_STATUS_IS_OK(status))
603                 break_info.oplock_failures++;
604
605         return;
606 }
607
608 /* a oplock break request handler */
609 static bool torture_oplock_handler(struct smb2_transport *transport,
610                                    const struct smb2_handle *handle,
611                                    uint8_t level, void *private_data)
612 {
613         struct smb2_tree *tree = private_data;
614         struct smb2_request *req;
615         struct smb2_break br;
616
617         break_info.oplock_handle = *handle;
618         break_info.oplock_level = level;
619         break_info.oplock_count++;
620
621         ZERO_STRUCT(br);
622         br.in.file.handle = *handle;
623         br.in.oplock_level = level;
624
625         if (break_info.held_oplock_level > SMB2_OPLOCK_LEVEL_II) {
626                 req = smb2_break_send(tree, &br);
627                 req->async.fn = torture_oplock_break_callback;
628                 req->async.private_data = NULL;
629         }
630         break_info.held_oplock_level = level;
631
632         return true;
633 }
634
635 static inline uint32_t oplock(const char *op) {
636         uint32_t val = SMB2_OPLOCK_LEVEL_NONE;
637         int i;
638
639         for (i = 0; i < strlen(op); i++) {
640                 switch (op[i]) {
641                 case 's':
642                         return SMB2_OPLOCK_LEVEL_II;
643                 case 'x':
644                         return SMB2_OPLOCK_LEVEL_EXCLUSIVE;
645                 case 'b':
646                         return SMB2_OPLOCK_LEVEL_BATCH;
647                 default:
648                         continue;
649                 }
650         }
651
652         return val;
653 }
654
655 #define NOPLOCK_RESULTS 12
656 static const char *oplock_results[NOPLOCK_RESULTS][4] = {
657         {"R",   "s",    "R",    "s"},
658         {"R",   "x",    "R",    "s"},
659         {"R",   "b",    "R",    "s"},
660
661         {"RH",  "s",    "RH",   ""},
662         {"RH",  "x",    "RH",   ""},
663         {"RH",  "b",    "RH",   ""},
664
665         {"RW",  "s",    "R",    "s"},
666         {"RW",  "x",    "R",    "s"},
667         {"RW",  "b",    "R",    "s"},
668
669         {"RHW", "s",    "RH",   ""},
670         {"RHW", "x",    "RH",   ""},
671         {"RHW", "b",    "RH",   ""},
672 };
673
674 static const char *oplock_results_2[NOPLOCK_RESULTS][4] = {
675         {"s",   "R",    "s",    "R"},
676         {"s",   "RH",   "s",    "R"},
677         {"s",   "RW",   "s",    "R"},
678         {"s",   "RHW",  "s",    "R"},
679
680         {"x",   "R",    "s",    "R"},
681         {"x",   "RH",   "s",    "R"},
682         {"x",   "RW",   "s",    "R"},
683         {"x",   "RHW",  "s",    "R"},
684
685         {"b",   "R",    "s",    "R"},
686         {"b",   "RH",   "s",    "R"},
687         {"b",   "RW",   "s",    "R"},
688         {"b",   "RHW",  "s",    "R"},
689 };
690
691 static bool test_lease_oplock(struct torture_context *tctx,
692                               struct smb2_tree *tree)
693 {
694         TALLOC_CTX *mem_ctx = talloc_new(tctx);
695         struct smb2_create io;
696         struct smb2_lease ls;
697         struct smb2_handle h, h2;
698         NTSTATUS status;
699         const char *fname = "lease.dat";
700         bool ret = true;
701         int i;
702
703         tree->session->transport->lease.handler = torture_lease_handler;
704         tree->session->transport->lease.private_data = tree;
705         tree->session->transport->oplock.handler = torture_oplock_handler;
706         tree->session->transport->oplock.private_data = tree;
707
708         smb2_util_unlink(tree, fname);
709
710         for (i = 0; i < NOPLOCK_RESULTS; i++) {
711                 const char *held = oplock_results[i][0];
712                 const char *contend = oplock_results[i][1];
713                 const char *brokento = oplock_results[i][2];
714                 const char *granted = oplock_results[i][3];
715                 torture_comment(tctx, "Hold %s(%x), requesting %s(%x), "
716                     "expecting break to %s(%x) and grant of %s(%x)\n",
717                     held, lease(held), contend, oplock(contend),
718                     brokento, lease(brokento), granted, oplock(granted));
719
720                 ZERO_STRUCT(break_info);
721
722                 /* Grab lease. */
723                 smb2_lease_create(&io, &ls, false, fname, LEASE1, lease(held));
724                 status = smb2_create(tree, mem_ctx, &io);
725                 CHECK_STATUS(status, NT_STATUS_OK);
726                 h = io.out.file.handle;
727                 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
728                 CHECK_LEASE(&io, held, true, LEASE1);
729
730                 /* Does an oplock contend the lease? */
731                 smb2_oplock_create(&io, fname, oplock(contend));
732                 status = smb2_create(tree, mem_ctx, &io);
733                 CHECK_STATUS(status, NT_STATUS_OK);
734                 h2 = io.out.file.handle;
735                 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
736                 CHECK_VAL(io.out.oplock_level, oplock(granted));
737                 break_info.held_oplock_level = io.out.oplock_level;
738
739                 if (lease(held) != lease(brokento)) {
740                         CHECK_BREAK_INFO(held, brokento, LEASE1);
741                 } else {
742                         CHECK_VAL(break_info.count, 0);
743                         CHECK_VAL(break_info.failures, 0);
744                 }
745
746                 smb2_util_close(tree, h);
747                 smb2_util_close(tree, h2);
748
749                 status = smb2_util_unlink(tree, fname);
750                 CHECK_STATUS(status, NT_STATUS_OK);
751         }
752
753         for (i = 0; i < NOPLOCK_RESULTS; i++) {
754                 const char *held = oplock_results_2[i][0];
755                 const char *contend = oplock_results_2[i][1];
756                 const char *brokento = oplock_results_2[i][2];
757                 const char *granted = oplock_results_2[i][3];
758                 torture_comment(tctx, "Hold %s(%x), requesting %s(%x), "
759                     "expecting break to %s(%x) and grant of %s(%x)\n",
760                     held, oplock(held), contend, lease(contend),
761                     brokento, oplock(brokento), granted, lease(granted));
762
763                 ZERO_STRUCT(break_info);
764
765                 /* Grab an oplock. */
766                 smb2_oplock_create(&io, fname, oplock(held));
767                 status = smb2_create(tree, mem_ctx, &io);
768                 CHECK_STATUS(status, NT_STATUS_OK);
769                 h = io.out.file.handle;
770                 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
771                 CHECK_VAL(io.out.oplock_level, oplock(held));
772                 break_info.held_oplock_level = io.out.oplock_level;
773
774                 /* Grab lease. */
775                 smb2_lease_create(&io, &ls, false, fname, LEASE1, lease(contend));
776                 status = smb2_create(tree, mem_ctx, &io);
777                 CHECK_STATUS(status, NT_STATUS_OK);
778                 h2 = io.out.file.handle;
779                 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
780                 CHECK_LEASE(&io, granted, true, LEASE1);
781
782                 if (oplock(held) != oplock(brokento)) {
783                         CHECK_VAL(break_info.oplock_count, 1);
784                         CHECK_VAL(break_info.oplock_failures, 0);
785                         CHECK_VAL(break_info.oplock_level, oplock(brokento));
786                         break_info.held_oplock_level = break_info.oplock_level;
787                 } else {
788                         CHECK_VAL(break_info.oplock_count, 0);
789                         CHECK_VAL(break_info.oplock_failures, 0);
790                 }
791
792                 smb2_util_close(tree, h);
793                 smb2_util_close(tree, h2);
794
795                 status = smb2_util_unlink(tree, fname);
796                 CHECK_STATUS(status, NT_STATUS_OK);
797         }
798
799  done:
800         smb2_util_close(tree, h);
801         smb2_util_close(tree, h2);
802
803         smb2_util_unlink(tree, fname);
804
805         talloc_free(mem_ctx);
806
807         return ret;
808 }
809
810 static bool test_lease_multibreak(struct torture_context *tctx,
811                                   struct smb2_tree *tree)
812 {
813         TALLOC_CTX *mem_ctx = talloc_new(tctx);
814         struct smb2_create io;
815         struct smb2_lease ls;
816         struct smb2_handle h, h2, h3;
817         struct smb2_write w;
818         NTSTATUS status;
819         const char *fname = "lease.dat";
820         bool ret = true;
821
822         tree->session->transport->lease.handler = torture_lease_handler;
823         tree->session->transport->lease.private_data = tree;
824         tree->session->transport->oplock.handler = torture_oplock_handler;
825         tree->session->transport->oplock.private_data = tree;
826
827         smb2_util_unlink(tree, fname);
828
829         ZERO_STRUCT(break_info);
830
831         /* Grab lease, upgrade to RHW .. */
832         smb2_lease_create(&io, &ls, false, fname, LEASE1, lease("RH"));
833         status = smb2_create(tree, mem_ctx, &io);
834         CHECK_STATUS(status, NT_STATUS_OK);
835         h = io.out.file.handle;
836         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
837         CHECK_LEASE(&io, "RH", true, LEASE1);
838
839         smb2_lease_create(&io, &ls, false, fname, LEASE1, lease("RHW"));
840         status = smb2_create(tree, mem_ctx, &io);
841         CHECK_STATUS(status, NT_STATUS_OK);
842         h2 = io.out.file.handle;
843         CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
844         CHECK_LEASE(&io, "RHW", true, LEASE1);
845
846         /* Contend with LEASE2. */
847         smb2_lease_create(&io, &ls, false, fname, LEASE2, lease("RHW"));
848         status = smb2_create(tree, mem_ctx, &io);
849         CHECK_STATUS(status, NT_STATUS_OK);
850         h3 = io.out.file.handle;
851         CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
852         CHECK_LEASE(&io, "RH", true, LEASE2);
853
854         /* Verify that we were only sent one break. */
855         CHECK_BREAK_INFO("RHW", "RH", LEASE1);
856
857         /* Drop LEASE1 / LEASE2 */
858         status = smb2_util_close(tree, h);
859         CHECK_STATUS(status, NT_STATUS_OK);
860         status = smb2_util_close(tree, h2);
861         CHECK_STATUS(status, NT_STATUS_OK);
862         status = smb2_util_close(tree, h3);
863         CHECK_STATUS(status, NT_STATUS_OK);
864
865         ZERO_STRUCT(break_info);
866
867         /* Grab an R lease. */
868         smb2_lease_create(&io, &ls, false, fname, LEASE1, lease("R"));
869         status = smb2_create(tree, mem_ctx, &io);
870         CHECK_STATUS(status, NT_STATUS_OK);
871         h = io.out.file.handle;
872         CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
873         CHECK_LEASE(&io, "R", true, LEASE1);
874
875         /* Grab a level-II oplock. */
876         smb2_oplock_create(&io, fname, oplock("s"));
877         status = smb2_create(tree, mem_ctx, &io);
878         CHECK_STATUS(status, NT_STATUS_OK);
879         h2 = io.out.file.handle;
880         CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
881         CHECK_VAL(io.out.oplock_level, oplock("s"));
882         break_info.held_oplock_level = io.out.oplock_level;
883
884         /* Verify no breaks. */
885         CHECK_VAL(break_info.count, 0);
886         CHECK_VAL(break_info.failures, 0);
887
888         /* Open for truncate, force a break. */
889         smb2_generic_create(&io, NULL, false, fname,
890             NTCREATEX_DISP_OVERWRITE_IF, oplock(""), 0, 0);
891         status = smb2_create(tree, mem_ctx, &io);
892         CHECK_STATUS(status, NT_STATUS_OK);
893         h3 = io.out.file.handle;
894         CHECK_CREATED(&io, TRUNCATED, FILE_ATTRIBUTE_ARCHIVE);
895         CHECK_VAL(io.out.oplock_level, oplock(""));
896         break_info.held_oplock_level = io.out.oplock_level;
897
898         /* Sleep, use a write to clear the recv queue. */
899         smb_msleep(250);
900         ZERO_STRUCT(w);
901         w.in.file.handle = h3;
902         w.in.offset      = 0;
903         w.in.data        = data_blob_talloc(mem_ctx, NULL, 4096);
904         memset(w.in.data.data, 'o', w.in.data.length);
905         status = smb2_write(tree, &w);
906         CHECK_STATUS(status, NT_STATUS_OK);
907
908         /* Verify one oplock break, one lease break. */
909         CHECK_VAL(break_info.oplock_count, 1);
910         CHECK_VAL(break_info.oplock_failures, 0);
911         CHECK_VAL(break_info.oplock_level, oplock(""));
912         CHECK_BREAK_INFO("R", "", LEASE1);
913
914  done:
915         smb2_util_close(tree, h);
916         smb2_util_close(tree, h2);
917         smb2_util_close(tree, h3);
918
919         smb2_util_unlink(tree, fname);
920
921         talloc_free(mem_ctx);
922
923         return ret;
924 }
925
926 struct torture_suite *torture_smb2_lease_init(void)
927 {
928         struct torture_suite *suite =
929             torture_suite_create(talloc_autofree_context(), "lease");
930
931         torture_suite_add_1smb2_test(suite, "request", test_lease_request);
932         torture_suite_add_1smb2_test(suite, "upgrade", test_lease_upgrade);
933         torture_suite_add_1smb2_test(suite, "upgrade2", test_lease_upgrade2);
934         torture_suite_add_1smb2_test(suite, "break", test_lease_break);
935         torture_suite_add_1smb2_test(suite, "oplock", test_lease_oplock);
936         torture_suite_add_1smb2_test(suite, "multibreak", test_lease_multibreak);
937
938         suite->description = talloc_strdup(suite, "SMB2-LEASE tests");
939
940         return suite;
941 }