Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 :
4 : test suite for SMB2 compounded requests
5 :
6 : Copyright (C) Stefan Metzmacher 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 "libcli/security/security.h"
29 : #include "librpc/gen_ndr/ndr_security.h"
30 : #include "../libcli/smb/smbXcli_base.h"
31 :
32 : #define CHECK_STATUS(status, correct) do { \
33 : if (!NT_STATUS_EQUAL(status, correct)) { \
34 : torture_result(tctx, TORTURE_FAIL, __location__": Incorrect status %s - should be %s", \
35 : nt_errstr(status), nt_errstr(correct)); \
36 : ret = false; \
37 : goto done; \
38 : }} while (0)
39 :
40 : #define CHECK_VALUE(v, correct) do { \
41 : if ((v) != (correct)) { \
42 : torture_result(tctx, TORTURE_FAIL, \
43 : "(%s) Incorrect value %s=%d - should be %d\n", \
44 : __location__, #v, (int)v, (int)correct); \
45 : ret = false; \
46 : }} while (0)
47 :
48 : #define WAIT_FOR_ASYNC_RESPONSE(req) \
49 : while (!req->cancel.can_cancel && req->state <= SMB2_REQUEST_RECV) { \
50 : if (tevent_loop_once(tctx->ev) != 0) { \
51 : break; \
52 : } \
53 : }
54 :
55 : static struct {
56 : struct smb2_handle handle;
57 : uint8_t level;
58 : struct smb2_break br;
59 : int count;
60 : int failures;
61 : NTSTATUS failure_status;
62 : } break_info;
63 :
64 6 : static void torture_oplock_break_callback(struct smb2_request *req)
65 : {
66 0 : NTSTATUS status;
67 0 : struct smb2_break br;
68 :
69 6 : ZERO_STRUCT(br);
70 6 : status = smb2_break_recv(req, &break_info.br);
71 6 : if (!NT_STATUS_IS_OK(status)) {
72 0 : break_info.failures++;
73 0 : break_info.failure_status = status;
74 : }
75 :
76 6 : return;
77 : }
78 :
79 : /* A general oplock break notification handler. This should be used when a
80 : * test expects to break from batch or exclusive to a lower level. */
81 6 : static bool torture_oplock_handler(struct smb2_transport *transport,
82 : const struct smb2_handle *handle,
83 : uint8_t level,
84 : void *private_data)
85 : {
86 6 : struct smb2_tree *tree = private_data;
87 0 : const char *name;
88 0 : struct smb2_request *req;
89 6 : ZERO_STRUCT(break_info.br);
90 :
91 6 : break_info.handle = *handle;
92 6 : break_info.level = level;
93 6 : break_info.count++;
94 :
95 6 : switch (level) {
96 6 : case SMB2_OPLOCK_LEVEL_II:
97 6 : name = "level II";
98 6 : break;
99 0 : case SMB2_OPLOCK_LEVEL_NONE:
100 0 : name = "none";
101 0 : break;
102 0 : default:
103 0 : name = "unknown";
104 0 : break_info.failures++;
105 : }
106 6 : printf("Acking to %s [0x%02X] in oplock handler\n", name, level);
107 :
108 6 : break_info.br.in.file.handle = *handle;
109 6 : break_info.br.in.oplock_level = level;
110 6 : break_info.br.in.reserved = 0;
111 6 : break_info.br.in.reserved2 = 0;
112 :
113 6 : req = smb2_break_send(tree, &break_info.br);
114 6 : req->async.fn = torture_oplock_break_callback;
115 6 : req->async.private_data = NULL;
116 6 : return true;
117 : }
118 :
119 6 : static bool test_compound_break(struct torture_context *tctx,
120 : struct smb2_tree *tree)
121 : {
122 6 : const char *fname1 = "some-file.pptx";
123 0 : NTSTATUS status;
124 6 : bool ret = true;
125 0 : union smb_open io1;
126 0 : struct smb2_create io2;
127 0 : struct smb2_getinfo gf;
128 0 : struct smb2_request *req[2];
129 0 : struct smb2_handle h1;
130 0 : struct smb2_handle h;
131 :
132 6 : tree->session->transport->oplock.handler = torture_oplock_handler;
133 6 : tree->session->transport->oplock.private_data = tree;
134 :
135 6 : ZERO_STRUCT(break_info);
136 :
137 : /*
138 : base ntcreatex parms
139 : */
140 6 : ZERO_STRUCT(io1.smb2);
141 6 : io1.generic.level = RAW_OPEN_SMB2;
142 6 : io1.smb2.in.desired_access = (SEC_STD_SYNCHRONIZE|
143 : SEC_STD_READ_CONTROL|
144 : SEC_FILE_READ_ATTRIBUTE|
145 : SEC_FILE_READ_EA|
146 : SEC_FILE_READ_DATA);
147 6 : io1.smb2.in.alloc_size = 0;
148 6 : io1.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
149 6 : io1.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ|
150 : NTCREATEX_SHARE_ACCESS_WRITE|
151 : NTCREATEX_SHARE_ACCESS_DELETE;
152 6 : io1.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
153 6 : io1.smb2.in.create_options = 0;
154 6 : io1.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
155 6 : io1.smb2.in.security_flags = 0;
156 6 : io1.smb2.in.fname = fname1;
157 :
158 6 : torture_comment(tctx, "TEST2: open a file with an batch "
159 : "oplock (share mode: all)\n");
160 6 : io1.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH;
161 :
162 6 : status = smb2_create(tree, tctx, &(io1.smb2));
163 6 : torture_assert_ntstatus_ok(tctx, status, "Error opening the file");
164 :
165 6 : h1 = io1.smb2.out.file.handle;
166 :
167 6 : torture_comment(tctx, "TEST2: Opening second time with compound\n");
168 :
169 6 : ZERO_STRUCT(io2);
170 :
171 6 : io2.in.desired_access = (SEC_STD_SYNCHRONIZE|
172 : SEC_FILE_READ_ATTRIBUTE|
173 : SEC_FILE_READ_EA);
174 6 : io2.in.alloc_size = 0;
175 6 : io2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
176 6 : io2.in.share_access = NTCREATEX_SHARE_ACCESS_READ|
177 : NTCREATEX_SHARE_ACCESS_WRITE|
178 : NTCREATEX_SHARE_ACCESS_DELETE;
179 6 : io2.in.create_disposition = NTCREATEX_DISP_OPEN;
180 6 : io2.in.create_options = 0;
181 6 : io2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
182 6 : io2.in.security_flags = 0;
183 6 : io2.in.fname = fname1;
184 6 : io2.in.oplock_level = 0;
185 :
186 6 : smb2_transport_compound_start(tree->session->transport, 2);
187 :
188 6 : req[0] = smb2_create_send(tree, &io2);
189 :
190 6 : smb2_transport_compound_set_related(tree->session->transport, true);
191 :
192 6 : h.data[0] = UINT64_MAX;
193 6 : h.data[1] = UINT64_MAX;
194 :
195 6 : ZERO_STRUCT(gf);
196 6 : gf.in.file.handle = h;
197 6 : gf.in.info_type = SMB2_0_INFO_FILE;
198 6 : gf.in.info_class = 0x16;
199 6 : gf.in.output_buffer_length = 0x1000;
200 6 : gf.in.input_buffer = data_blob_null;
201 :
202 6 : req[1] = smb2_getinfo_send(tree, &gf);
203 :
204 6 : status = smb2_create_recv(req[0], tree, &io2);
205 6 : CHECK_STATUS(status, NT_STATUS_OK);
206 :
207 6 : status = smb2_getinfo_recv(req[1], tree, &gf);
208 6 : CHECK_STATUS(status, NT_STATUS_OK);
209 :
210 6 : done:
211 :
212 6 : smb2_util_close(tree, h1);
213 6 : smb2_util_unlink(tree, fname1);
214 6 : return ret;
215 : }
216 :
217 6 : static bool test_compound_related1(struct torture_context *tctx,
218 : struct smb2_tree *tree)
219 : {
220 0 : struct smb2_handle hd;
221 0 : struct smb2_create cr;
222 0 : NTSTATUS status;
223 6 : const char *fname = "compound_related1.dat";
224 0 : struct smb2_close cl;
225 6 : bool ret = true;
226 0 : struct smb2_request *req[2];
227 6 : struct smbXcli_tcon *saved_tcon = tree->smbXcli;
228 6 : struct smbXcli_session *saved_session = tree->session->smbXcli;
229 :
230 6 : smb2_transport_credits_ask_num(tree->session->transport, 2);
231 :
232 6 : smb2_util_unlink(tree, fname);
233 :
234 6 : smb2_transport_credits_ask_num(tree->session->transport, 1);
235 :
236 6 : ZERO_STRUCT(cr);
237 6 : cr.in.security_flags = 0x00;
238 6 : cr.in.oplock_level = 0;
239 6 : cr.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION;
240 6 : cr.in.create_flags = 0x00000000;
241 6 : cr.in.reserved = 0x00000000;
242 6 : cr.in.desired_access = SEC_RIGHTS_FILE_ALL;
243 6 : cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
244 6 : cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
245 : NTCREATEX_SHARE_ACCESS_WRITE |
246 : NTCREATEX_SHARE_ACCESS_DELETE;
247 6 : cr.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
248 6 : cr.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
249 : NTCREATEX_OPTIONS_ASYNC_ALERT |
250 : NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
251 : 0x00200000;
252 6 : cr.in.fname = fname;
253 :
254 6 : smb2_transport_compound_start(tree->session->transport, 2);
255 :
256 6 : req[0] = smb2_create_send(tree, &cr);
257 :
258 6 : smb2_transport_compound_set_related(tree->session->transport, true);
259 :
260 6 : hd.data[0] = UINT64_MAX;
261 6 : hd.data[1] = UINT64_MAX;
262 :
263 6 : ZERO_STRUCT(cl);
264 6 : cl.in.file.handle = hd;
265 :
266 6 : tree->smbXcli = smbXcli_tcon_create(tree);
267 6 : smb2cli_tcon_set_values(tree->smbXcli,
268 : NULL, /* session */
269 : 0xFFFFFFFF, /* tcon_id */
270 : 0, /* type */
271 : 0, /* flags */
272 : 0, /* capabilities */
273 : 0 /* maximal_access */);
274 :
275 12 : tree->session->smbXcli = smbXcli_session_shallow_copy(tree->session,
276 6 : tree->session->smbXcli);
277 6 : smb2cli_session_set_id_and_flags(tree->session->smbXcli, UINT64_MAX, 0);
278 :
279 6 : req[1] = smb2_close_send(tree, &cl);
280 :
281 6 : status = smb2_create_recv(req[0], tree, &cr);
282 6 : CHECK_STATUS(status, NT_STATUS_OK);
283 6 : status = smb2_close_recv(req[1], &cl);
284 6 : CHECK_STATUS(status, NT_STATUS_OK);
285 :
286 6 : TALLOC_FREE(tree->smbXcli);
287 6 : tree->smbXcli = saved_tcon;
288 6 : TALLOC_FREE(tree->session->smbXcli);
289 6 : tree->session->smbXcli = saved_session;
290 :
291 6 : smb2_util_unlink(tree, fname);
292 6 : done:
293 6 : return ret;
294 : }
295 :
296 6 : static bool test_compound_related2(struct torture_context *tctx,
297 : struct smb2_tree *tree)
298 : {
299 0 : struct smb2_handle hd;
300 0 : struct smb2_create cr;
301 0 : NTSTATUS status;
302 6 : const char *fname = "compound_related2.dat";
303 0 : struct smb2_close cl;
304 6 : bool ret = true;
305 0 : struct smb2_request *req[5];
306 6 : struct smbXcli_tcon *saved_tcon = tree->smbXcli;
307 6 : struct smbXcli_session *saved_session = tree->session->smbXcli;
308 :
309 6 : smb2_transport_credits_ask_num(tree->session->transport, 5);
310 :
311 6 : smb2_util_unlink(tree, fname);
312 :
313 6 : smb2_transport_credits_ask_num(tree->session->transport, 1);
314 :
315 6 : ZERO_STRUCT(cr);
316 6 : cr.in.security_flags = 0x00;
317 6 : cr.in.oplock_level = 0;
318 6 : cr.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION;
319 6 : cr.in.create_flags = 0x00000000;
320 6 : cr.in.reserved = 0x00000000;
321 6 : cr.in.desired_access = SEC_RIGHTS_FILE_ALL;
322 6 : cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
323 6 : cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
324 : NTCREATEX_SHARE_ACCESS_WRITE |
325 : NTCREATEX_SHARE_ACCESS_DELETE;
326 6 : cr.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
327 6 : cr.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
328 : NTCREATEX_OPTIONS_ASYNC_ALERT |
329 : NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
330 : 0x00200000;
331 6 : cr.in.fname = fname;
332 :
333 6 : smb2_transport_compound_start(tree->session->transport, 5);
334 :
335 6 : req[0] = smb2_create_send(tree, &cr);
336 :
337 6 : hd.data[0] = UINT64_MAX;
338 6 : hd.data[1] = UINT64_MAX;
339 :
340 6 : smb2_transport_compound_set_related(tree->session->transport, true);
341 :
342 6 : ZERO_STRUCT(cl);
343 6 : cl.in.file.handle = hd;
344 :
345 6 : tree->smbXcli = smbXcli_tcon_create(tree);
346 6 : smb2cli_tcon_set_values(tree->smbXcli,
347 : NULL, /* session */
348 : 0xFFFFFFFF, /* tcon_id */
349 : 0, /* type */
350 : 0, /* flags */
351 : 0, /* capabilities */
352 : 0 /* maximal_access */);
353 :
354 12 : tree->session->smbXcli = smbXcli_session_shallow_copy(tree->session,
355 6 : tree->session->smbXcli);
356 6 : smb2cli_session_set_id_and_flags(tree->session->smbXcli, UINT64_MAX, 0);
357 :
358 6 : req[1] = smb2_close_send(tree, &cl);
359 6 : req[2] = smb2_close_send(tree, &cl);
360 6 : req[3] = smb2_close_send(tree, &cl);
361 6 : req[4] = smb2_close_send(tree, &cl);
362 :
363 6 : status = smb2_create_recv(req[0], tree, &cr);
364 6 : CHECK_STATUS(status, NT_STATUS_OK);
365 6 : status = smb2_close_recv(req[1], &cl);
366 6 : CHECK_STATUS(status, NT_STATUS_OK);
367 6 : status = smb2_close_recv(req[2], &cl);
368 6 : CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
369 6 : status = smb2_close_recv(req[3], &cl);
370 6 : CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
371 6 : status = smb2_close_recv(req[4], &cl);
372 6 : CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
373 :
374 6 : TALLOC_FREE(tree->smbXcli);
375 6 : tree->smbXcli = saved_tcon;
376 6 : TALLOC_FREE(tree->session->smbXcli);
377 6 : tree->session->smbXcli = saved_session;
378 :
379 6 : smb2_util_unlink(tree, fname);
380 6 : done:
381 6 : return ret;
382 : }
383 :
384 6 : static bool test_compound_related3(struct torture_context *tctx,
385 : struct smb2_tree *tree)
386 : {
387 0 : struct smb2_handle hd;
388 0 : struct smb2_ioctl io;
389 0 : struct smb2_create cr;
390 0 : struct smb2_close cl;
391 6 : const char *fname = "compound_related3.dat";
392 0 : struct smb2_request *req[3];
393 0 : NTSTATUS status;
394 6 : bool ret = false;
395 :
396 6 : smb2_util_unlink(tree, fname);
397 :
398 6 : ZERO_STRUCT(cr);
399 6 : cr.in.security_flags = 0x00;
400 6 : cr.in.oplock_level = 0;
401 6 : cr.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION;
402 6 : cr.in.create_flags = 0x00000000;
403 6 : cr.in.reserved = 0x00000000;
404 6 : cr.in.desired_access = SEC_RIGHTS_FILE_ALL;
405 6 : cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
406 6 : cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
407 : NTCREATEX_SHARE_ACCESS_WRITE |
408 : NTCREATEX_SHARE_ACCESS_DELETE;
409 6 : cr.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
410 6 : cr.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
411 : NTCREATEX_OPTIONS_ASYNC_ALERT |
412 : NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
413 : 0x00200000;
414 6 : cr.in.fname = fname;
415 :
416 6 : smb2_transport_compound_start(tree->session->transport, 3);
417 :
418 6 : req[0] = smb2_create_send(tree, &cr);
419 :
420 6 : hd.data[0] = UINT64_MAX;
421 6 : hd.data[1] = UINT64_MAX;
422 :
423 6 : smb2_transport_compound_set_related(tree->session->transport, true);
424 :
425 6 : ZERO_STRUCT(io);
426 6 : io.in.function = FSCTL_CREATE_OR_GET_OBJECT_ID;
427 6 : io.in.file.handle = hd;
428 6 : io.in.reserved2 = 0;
429 6 : io.in.max_output_response = 64;
430 6 : io.in.flags = 1;
431 :
432 6 : req[1] = smb2_ioctl_send(tree, &io);
433 :
434 6 : ZERO_STRUCT(cl);
435 6 : cl.in.file.handle = hd;
436 :
437 6 : req[2] = smb2_close_send(tree, &cl);
438 :
439 6 : status = smb2_create_recv(req[0], tree, &cr);
440 6 : CHECK_STATUS(status, NT_STATUS_OK);
441 6 : status = smb2_ioctl_recv(req[1], tree, &io);
442 6 : CHECK_STATUS(status, NT_STATUS_OK);
443 6 : status = smb2_close_recv(req[2], &cl);
444 6 : CHECK_STATUS(status, NT_STATUS_OK);
445 :
446 6 : status = smb2_util_unlink(tree, fname);
447 6 : CHECK_STATUS(status, NT_STATUS_OK);
448 :
449 6 : ret = true;
450 6 : done:
451 6 : return ret;
452 : }
453 :
454 6 : static bool test_compound_related4(struct torture_context *tctx,
455 : struct smb2_tree *tree)
456 : {
457 6 : const char *fname = "compound_related4.dat";
458 6 : struct security_descriptor *sd = NULL;
459 0 : struct smb2_handle hd;
460 0 : struct smb2_create cr;
461 0 : union smb_setfileinfo set;
462 0 : struct smb2_ioctl io;
463 0 : struct smb2_close cl;
464 0 : struct smb2_request *req[4];
465 0 : NTSTATUS status;
466 6 : bool ret = true;
467 :
468 6 : smb2_util_unlink(tree, fname);
469 :
470 6 : ZERO_STRUCT(cr);
471 6 : cr.level = RAW_OPEN_SMB2;
472 6 : cr.in.create_flags = 0;
473 6 : cr.in.desired_access = SEC_STD_READ_CONTROL |
474 : SEC_STD_WRITE_DAC |
475 : SEC_STD_WRITE_OWNER;
476 6 : cr.in.create_options = 0;
477 6 : cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
478 6 : cr.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE |
479 : NTCREATEX_SHARE_ACCESS_READ |
480 : NTCREATEX_SHARE_ACCESS_WRITE;
481 6 : cr.in.alloc_size = 0;
482 6 : cr.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
483 6 : cr.in.impersonation_level = NTCREATEX_IMPERSONATION_ANONYMOUS;
484 6 : cr.in.security_flags = 0;
485 6 : cr.in.fname = fname;
486 :
487 6 : status = smb2_create(tree, tctx, &cr);
488 6 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create failed\n");
489 :
490 6 : hd = cr.out.file.handle;
491 6 : torture_comment(tctx, "set a sec desc allowing no write by CREATOR_OWNER\n");
492 :
493 6 : sd = security_descriptor_dacl_create(tctx,
494 : 0, NULL, NULL,
495 : SID_CREATOR_OWNER,
496 : SEC_ACE_TYPE_ACCESS_ALLOWED,
497 : SEC_RIGHTS_FILE_READ | SEC_STD_ALL,
498 : 0,
499 : NULL);
500 6 : torture_assert_not_null_goto(tctx, sd, ret, done,
501 : "security_descriptor_dacl_create failed\n");
502 :
503 6 : set.set_secdesc.level = RAW_SFILEINFO_SEC_DESC;
504 6 : set.set_secdesc.in.file.handle = hd;
505 6 : set.set_secdesc.in.secinfo_flags = SECINFO_DACL;
506 6 : set.set_secdesc.in.sd = sd;
507 :
508 6 : status = smb2_setinfo_file(tree, &set);
509 6 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
510 : "smb2_setinfo_file failed\n");
511 :
512 6 : torture_comment(tctx, "try open for write\n");
513 6 : cr.in.desired_access = SEC_FILE_WRITE_DATA;
514 6 : smb2_transport_compound_start(tree->session->transport, 4);
515 :
516 6 : req[0] = smb2_create_send(tree, &cr);
517 6 : torture_assert_not_null_goto(tctx, req[0], ret, done,
518 : "smb2_create_send failed\n");
519 :
520 6 : hd.data[0] = UINT64_MAX;
521 6 : hd.data[1] = UINT64_MAX;
522 :
523 6 : smb2_transport_compound_set_related(tree->session->transport, true);
524 6 : ZERO_STRUCT(io);
525 6 : io.in.function = FSCTL_CREATE_OR_GET_OBJECT_ID;
526 6 : io.in.file.handle = hd;
527 6 : io.in.flags = 1;
528 :
529 6 : req[1] = smb2_ioctl_send(tree, &io);
530 6 : torture_assert_not_null_goto(tctx, req[1], ret, done,
531 : "smb2_ioctl_send failed\n");
532 :
533 6 : ZERO_STRUCT(cl);
534 6 : cl.in.file.handle = hd;
535 :
536 6 : req[2] = smb2_close_send(tree, &cl);
537 6 : torture_assert_not_null_goto(tctx, req[2], ret, done,
538 : "smb2_create_send failed\n");
539 :
540 6 : set.set_secdesc.in.file.handle = hd;
541 :
542 6 : req[3] = smb2_setinfo_file_send(tree, &set);
543 6 : torture_assert_not_null_goto(tctx, req[3], ret, done,
544 : "smb2_create_send failed\n");
545 :
546 6 : status = smb2_create_recv(req[0], tree, &cr);
547 6 : torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_ACCESS_DENIED,
548 : ret, done,
549 : "smb2_create_recv failed\n");
550 :
551 6 : status = smb2_ioctl_recv(req[1], tree, &io);
552 6 : torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_ACCESS_DENIED,
553 : ret, done,
554 : "smb2_ioctl_recv failed\n");
555 :
556 6 : status = smb2_close_recv(req[2], &cl);
557 6 : torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_ACCESS_DENIED,
558 : ret, done,
559 : "smb2_close_recv failed\n");
560 :
561 6 : status = smb2_setinfo_recv(req[3]);
562 6 : torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_ACCESS_DENIED,
563 : ret, done,
564 : "smb2_setinfo_recv failed\n");
565 :
566 6 : done:
567 6 : smb2_util_unlink(tree, fname);
568 6 : smb2_tdis(tree);
569 6 : smb2_logoff(tree->session);
570 6 : return ret;
571 : }
572 :
573 6 : static bool test_compound_related5(struct torture_context *tctx,
574 : struct smb2_tree *tree)
575 : {
576 0 : struct smb2_handle hd;
577 0 : struct smb2_ioctl io;
578 0 : struct smb2_close cl;
579 0 : struct smb2_request *req[2];
580 0 : NTSTATUS status;
581 6 : bool ret = false;
582 :
583 6 : smb2_transport_compound_start(tree->session->transport, 2);
584 :
585 6 : hd.data[0] = UINT64_MAX;
586 6 : hd.data[1] = UINT64_MAX;
587 :
588 6 : ZERO_STRUCT(io);
589 6 : io.in.function = FSCTL_CREATE_OR_GET_OBJECT_ID;
590 6 : io.in.file.handle = hd;
591 6 : io.in.flags = 1;
592 :
593 6 : req[0] = smb2_ioctl_send(tree, &io);
594 6 : torture_assert_not_null_goto(tctx, req[0], ret, done,
595 : "smb2_ioctl_send failed\n");
596 :
597 6 : smb2_transport_compound_set_related(tree->session->transport, true);
598 :
599 6 : ZERO_STRUCT(cl);
600 6 : cl.in.file.handle = hd;
601 :
602 6 : req[1] = smb2_close_send(tree, &cl);
603 6 : torture_assert_not_null_goto(tctx, req[1], ret, done,
604 : "smb2_create_send failed\n");
605 :
606 6 : status = smb2_ioctl_recv(req[0], tree, &io);
607 6 : torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_FILE_CLOSED,
608 : ret, done,
609 : "smb2_ioctl_recv failed\n");
610 :
611 6 : status = smb2_close_recv(req[1], &cl);
612 6 : torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_FILE_CLOSED,
613 : ret, done,
614 : "smb2_close_recv failed\n");
615 :
616 6 : ret = true;
617 :
618 6 : done:
619 6 : smb2_tdis(tree);
620 6 : smb2_logoff(tree->session);
621 6 : return ret;
622 : }
623 :
624 6 : static bool test_compound_related6(struct torture_context *tctx,
625 : struct smb2_tree *tree)
626 : {
627 0 : struct smb2_handle hd;
628 0 : struct smb2_create cr;
629 0 : struct smb2_read rd;
630 0 : struct smb2_write wr;
631 0 : struct smb2_close cl;
632 0 : NTSTATUS status;
633 6 : const char *fname = "compound_related6.dat";
634 0 : struct smb2_request *req[5];
635 0 : uint8_t buf[64];
636 6 : bool ret = true;
637 :
638 6 : smb2_util_unlink(tree, fname);
639 :
640 6 : ZERO_STRUCT(cr);
641 6 : cr.level = RAW_OPEN_SMB2;
642 6 : cr.in.create_flags = 0;
643 6 : cr.in.desired_access = SEC_RIGHTS_FILE_ALL;
644 6 : cr.in.create_options = 0;
645 6 : cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
646 6 : cr.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE |
647 : NTCREATEX_SHARE_ACCESS_READ |
648 : NTCREATEX_SHARE_ACCESS_WRITE;
649 6 : cr.in.alloc_size = 0;
650 6 : cr.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
651 6 : cr.in.impersonation_level = NTCREATEX_IMPERSONATION_ANONYMOUS;
652 6 : cr.in.security_flags = 0;
653 6 : cr.in.fname = fname;
654 :
655 6 : status = smb2_create(tree, tctx, &cr);
656 6 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
657 : "smb2_create failed\n");
658 :
659 6 : hd = cr.out.file.handle;
660 :
661 6 : ZERO_STRUCT(buf);
662 6 : status = smb2_util_write(tree, hd, buf, 0, ARRAY_SIZE(buf));
663 6 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
664 : "smb2_util_write failed\n");
665 :
666 6 : torture_comment(tctx, "try open for read\n");
667 6 : cr.in.desired_access = SEC_FILE_READ_DATA;
668 6 : smb2_transport_compound_start(tree->session->transport, 5);
669 :
670 6 : req[0] = smb2_create_send(tree, &cr);
671 6 : torture_assert_not_null_goto(tctx, req[0], ret, done,
672 : "smb2_create_send failed\n");
673 :
674 6 : hd.data[0] = UINT64_MAX;
675 6 : hd.data[1] = UINT64_MAX;
676 :
677 6 : smb2_transport_compound_set_related(tree->session->transport, true);
678 :
679 6 : ZERO_STRUCT(rd);
680 6 : rd.in.file.handle = hd;
681 6 : rd.in.length = 1;
682 6 : rd.in.offset = 0;
683 :
684 6 : req[1] = smb2_read_send(tree, &rd);
685 6 : torture_assert_not_null_goto(tctx, req[1], ret, done,
686 : "smb2_read_send failed\n");
687 :
688 6 : ZERO_STRUCT(wr);
689 6 : wr.in.file.handle = hd;
690 6 : wr.in.offset = 0;
691 6 : wr.in.data = data_blob_talloc(tctx, NULL, 64);
692 :
693 6 : req[2] = smb2_write_send(tree, &wr);
694 6 : torture_assert_not_null_goto(tctx, req[2], ret, done,
695 : "smb2_write_send failed\n");
696 :
697 6 : ZERO_STRUCT(rd);
698 6 : rd.in.file.handle = hd;
699 6 : rd.in.length = 1;
700 6 : rd.in.offset = 0;
701 :
702 6 : req[3] = smb2_read_send(tree, &rd);
703 6 : torture_assert_not_null_goto(tctx, req[3], ret, done,
704 : "smb2_read_send failed\n");
705 :
706 6 : ZERO_STRUCT(cl);
707 6 : cl.in.file.handle = hd;
708 :
709 6 : req[4] = smb2_close_send(tree, &cl);
710 6 : torture_assert_not_null_goto(tctx, req[4], ret, done,
711 : "smb2_close_send failed\n");
712 :
713 6 : status = smb2_create_recv(req[0], tree, &cr);
714 6 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
715 : "smb2_create_recv failed\n");
716 :
717 6 : status = smb2_read_recv(req[1], tree, &rd);
718 6 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
719 : "smb2_read_recv failed\n");
720 :
721 6 : status = smb2_write_recv(req[2], &wr);
722 6 : torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_ACCESS_DENIED,
723 : ret, done,
724 : "smb2_write_recv failed\n");
725 :
726 6 : status = smb2_read_recv(req[3], tree, &rd);
727 6 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
728 : "smb2_read_recv failed\n");
729 :
730 6 : status = smb2_close_recv(req[4], &cl);
731 6 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
732 : "smb2_close_recv failed\n");
733 :
734 6 : done:
735 6 : smb2_util_unlink(tree, fname);
736 6 : smb2_tdis(tree);
737 6 : smb2_logoff(tree->session);
738 6 : return ret;
739 : }
740 :
741 6 : static bool test_compound_related7(struct torture_context *tctx,
742 : struct smb2_tree *tree)
743 : {
744 6 : const char *fname = "compound_related4.dat";
745 6 : struct security_descriptor *sd = NULL;
746 0 : struct smb2_handle hd;
747 0 : struct smb2_create cr;
748 0 : union smb_setfileinfo set;
749 0 : struct smb2_notify nt;
750 0 : struct smb2_close cl;
751 0 : NTSTATUS status;
752 0 : struct smb2_request *req[4];
753 6 : bool ret = true;
754 :
755 6 : smb2_util_unlink(tree, fname);
756 :
757 6 : ZERO_STRUCT(cr);
758 6 : cr.level = RAW_OPEN_SMB2;
759 6 : cr.in.create_flags = 0;
760 6 : cr.in.desired_access = SEC_STD_READ_CONTROL |
761 : SEC_STD_WRITE_DAC |
762 : SEC_STD_WRITE_OWNER;
763 6 : cr.in.create_options = 0;
764 6 : cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
765 6 : cr.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE |
766 : NTCREATEX_SHARE_ACCESS_READ |
767 : NTCREATEX_SHARE_ACCESS_WRITE;
768 6 : cr.in.alloc_size = 0;
769 6 : cr.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
770 6 : cr.in.impersonation_level = NTCREATEX_IMPERSONATION_ANONYMOUS;
771 6 : cr.in.security_flags = 0;
772 6 : cr.in.fname = fname;
773 :
774 6 : status = smb2_create(tree, tctx, &cr);
775 6 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
776 : "smb2_create failed\n");
777 :
778 6 : hd = cr.out.file.handle;
779 6 : torture_comment(tctx, "set a sec desc allowing no write by CREATOR_OWNER\n");
780 6 : sd = security_descriptor_dacl_create(tctx,
781 : 0, NULL, NULL,
782 : SID_CREATOR_OWNER,
783 : SEC_ACE_TYPE_ACCESS_ALLOWED,
784 : SEC_RIGHTS_FILE_READ | SEC_STD_ALL,
785 : 0,
786 : NULL);
787 6 : torture_assert_not_null_goto(tctx, sd, ret, done,
788 : "security_descriptor_dacl_create failed\n");
789 :
790 6 : set.set_secdesc.level = RAW_SFILEINFO_SEC_DESC;
791 6 : set.set_secdesc.in.file.handle = hd;
792 6 : set.set_secdesc.in.secinfo_flags = SECINFO_DACL;
793 6 : set.set_secdesc.in.sd = sd;
794 :
795 6 : status = smb2_setinfo_file(tree, &set);
796 6 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
797 : "smb2_setinfo_file failed\n");
798 :
799 6 : torture_comment(tctx, "try open for write\n");
800 6 : cr.in.desired_access = SEC_FILE_WRITE_DATA;
801 6 : smb2_transport_compound_start(tree->session->transport, 4);
802 :
803 6 : req[0] = smb2_create_send(tree, &cr);
804 6 : torture_assert_not_null_goto(tctx, req[0], ret, done,
805 : "smb2_create_send failed\n");
806 :
807 6 : hd.data[0] = UINT64_MAX;
808 6 : hd.data[1] = UINT64_MAX;
809 :
810 6 : smb2_transport_compound_set_related(tree->session->transport, true);
811 :
812 6 : ZERO_STRUCT(nt);
813 6 : nt.in.recursive = true;
814 6 : nt.in.buffer_size = 0x1000;
815 6 : nt.in.file.handle = hd;
816 6 : nt.in.completion_filter = FILE_NOTIFY_CHANGE_NAME;
817 6 : nt.in.unknown = 0x00000000;
818 :
819 6 : req[1] = smb2_notify_send(tree, &nt);
820 6 : torture_assert_not_null_goto(tctx, req[1], ret, done,
821 : "smb2_notify_send failed\n");
822 :
823 6 : ZERO_STRUCT(cl);
824 6 : cl.in.file.handle = hd;
825 :
826 6 : req[2] = smb2_close_send(tree, &cl);
827 6 : torture_assert_not_null_goto(tctx, req[2], ret, done,
828 : "smb2_close_send failed\n");
829 :
830 6 : set.set_secdesc.in.file.handle = hd;
831 :
832 6 : req[3] = smb2_setinfo_file_send(tree, &set);
833 6 : torture_assert_not_null_goto(tctx, req[3], ret, done,
834 : "smb2_setinfo_file_send failed\n");
835 :
836 6 : status = smb2_create_recv(req[0], tree, &cr);
837 6 : torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_ACCESS_DENIED,
838 : ret, done,
839 : "smb2_create_recv failed\n");
840 :
841 6 : status = smb2_notify_recv(req[1], tree, &nt);
842 6 : torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_ACCESS_DENIED,
843 : ret, done,
844 : "smb2_notify_recv failed\n");
845 :
846 6 : status = smb2_close_recv(req[2], &cl);
847 6 : torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_ACCESS_DENIED,
848 : ret, done,
849 : "smb2_close_recv failed\n");
850 :
851 6 : status = smb2_setinfo_recv(req[3]);
852 6 : torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_ACCESS_DENIED,
853 : ret, done,
854 : "smb2_setinfo_recv failed\n");
855 :
856 6 : done:
857 6 : smb2_util_unlink(tree, fname);
858 6 : smb2_tdis(tree);
859 6 : smb2_logoff(tree->session);
860 6 : return ret;
861 : }
862 :
863 6 : static bool test_compound_related8(struct torture_context *tctx,
864 : struct smb2_tree *tree)
865 : {
866 6 : const char *fname = "compound_related8.dat";
867 6 : const char *fname_nonexisting = "compound_related8.dat.void";
868 6 : struct security_descriptor *sd = NULL;
869 0 : struct smb2_handle hd;
870 0 : struct smb2_create cr;
871 0 : union smb_setfileinfo set;
872 0 : struct smb2_notify nt;
873 0 : struct smb2_close cl;
874 0 : NTSTATUS status;
875 0 : struct smb2_request *req[4];
876 6 : bool ret = true;
877 :
878 6 : smb2_util_unlink(tree, fname);
879 :
880 6 : ZERO_STRUCT(cr);
881 6 : cr.level = RAW_OPEN_SMB2;
882 6 : cr.in.create_flags = 0;
883 6 : cr.in.desired_access = SEC_STD_READ_CONTROL |
884 : SEC_STD_WRITE_DAC |
885 : SEC_STD_WRITE_OWNER;
886 6 : cr.in.create_options = 0;
887 6 : cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
888 6 : cr.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE |
889 : NTCREATEX_SHARE_ACCESS_READ |
890 : NTCREATEX_SHARE_ACCESS_WRITE;
891 6 : cr.in.alloc_size = 0;
892 6 : cr.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
893 6 : cr.in.impersonation_level = NTCREATEX_IMPERSONATION_ANONYMOUS;
894 6 : cr.in.security_flags = 0;
895 6 : cr.in.fname = fname;
896 :
897 6 : status = smb2_create(tree, tctx, &cr);
898 6 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
899 : "smb2_create failed\n");
900 :
901 6 : hd = cr.out.file.handle;
902 :
903 6 : smb2_transport_compound_start(tree->session->transport, 4);
904 :
905 6 : torture_comment(tctx, "try open for write\n");
906 6 : cr.in.fname = fname_nonexisting;
907 6 : cr.in.create_disposition = NTCREATEX_DISP_OPEN;
908 :
909 6 : req[0] = smb2_create_send(tree, &cr);
910 6 : torture_assert_not_null_goto(tctx, req[0], ret, done,
911 : "smb2_create_send failed\n");
912 :
913 6 : hd.data[0] = UINT64_MAX;
914 6 : hd.data[1] = UINT64_MAX;
915 :
916 6 : smb2_transport_compound_set_related(tree->session->transport, true);
917 :
918 6 : ZERO_STRUCT(nt);
919 6 : nt.in.recursive = true;
920 6 : nt.in.buffer_size = 0x1000;
921 6 : nt.in.file.handle = hd;
922 6 : nt.in.completion_filter = FILE_NOTIFY_CHANGE_NAME;
923 6 : nt.in.unknown = 0x00000000;
924 :
925 6 : req[1] = smb2_notify_send(tree, &nt);
926 6 : torture_assert_not_null_goto(tctx, req[1], ret, done,
927 : "smb2_notify_send failed\n");
928 :
929 6 : ZERO_STRUCT(cl);
930 6 : cl.in.file.handle = hd;
931 :
932 6 : req[2] = smb2_close_send(tree, &cl);
933 6 : torture_assert_not_null_goto(tctx, req[2], ret, done,
934 : "smb2_close_send failed\n");
935 :
936 6 : sd = security_descriptor_dacl_create(tctx,
937 : 0, NULL, NULL,
938 : SID_CREATOR_OWNER,
939 : SEC_ACE_TYPE_ACCESS_ALLOWED,
940 : SEC_RIGHTS_FILE_READ | SEC_STD_ALL,
941 : 0,
942 : NULL);
943 6 : torture_assert_not_null_goto(tctx, sd, ret, done,
944 : "security_descriptor_dacl_create failed\n");
945 :
946 6 : set.set_secdesc.level = RAW_SFILEINFO_SEC_DESC;
947 6 : set.set_secdesc.in.file.handle = hd;
948 6 : set.set_secdesc.in.secinfo_flags = SECINFO_DACL;
949 6 : set.set_secdesc.in.sd = sd;
950 :
951 6 : req[3] = smb2_setinfo_file_send(tree, &set);
952 6 : torture_assert_not_null_goto(tctx, req[3], ret, done,
953 : "smb2_setinfo_file_send failed\n");
954 :
955 6 : status = smb2_create_recv(req[0], tree, &cr);
956 6 : torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND,
957 : ret, done,
958 : "smb2_create_recv failed\n");
959 :
960 6 : status = smb2_notify_recv(req[1], tree, &nt);
961 6 : torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND,
962 : ret, done,
963 : "smb2_notify_recv failed\n");
964 :
965 6 : status = smb2_close_recv(req[2], &cl);
966 6 : torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND,
967 : ret, done,
968 : "smb2_close_recv failed\n");
969 :
970 6 : status = smb2_setinfo_recv(req[3]);
971 6 : torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND,
972 : ret, done,
973 : "smb2_setinfo_recv failed\n");
974 :
975 6 : done:
976 6 : smb2_util_unlink(tree, fname);
977 6 : smb2_tdis(tree);
978 6 : smb2_logoff(tree->session);
979 6 : return ret;
980 : }
981 :
982 6 : static bool test_compound_related9(struct torture_context *tctx,
983 : struct smb2_tree *tree)
984 : {
985 6 : const char *fname = "compound_related9.dat";
986 6 : struct security_descriptor *sd = NULL;
987 0 : struct smb2_handle hd;
988 0 : struct smb2_create cr;
989 0 : union smb_setfileinfo set;
990 0 : struct smb2_notify nt;
991 0 : struct smb2_close cl;
992 0 : NTSTATUS status;
993 0 : struct smb2_request *req[3];
994 6 : bool ret = true;
995 :
996 6 : smb2_util_unlink(tree, fname);
997 :
998 6 : ZERO_STRUCT(cr);
999 6 : cr.level = RAW_OPEN_SMB2;
1000 6 : cr.in.create_flags = 0;
1001 6 : cr.in.desired_access = SEC_STD_READ_CONTROL |
1002 : SEC_STD_WRITE_DAC |
1003 : SEC_STD_WRITE_OWNER;
1004 6 : cr.in.create_options = 0;
1005 6 : cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1006 6 : cr.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE |
1007 : NTCREATEX_SHARE_ACCESS_READ |
1008 : NTCREATEX_SHARE_ACCESS_WRITE;
1009 6 : cr.in.alloc_size = 0;
1010 6 : cr.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
1011 6 : cr.in.impersonation_level = NTCREATEX_IMPERSONATION_ANONYMOUS;
1012 6 : cr.in.security_flags = 0;
1013 6 : cr.in.fname = fname;
1014 :
1015 6 : status = smb2_create(tree, tctx, &cr);
1016 6 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1017 : "smb2_create failed\n");
1018 :
1019 6 : hd = cr.out.file.handle;
1020 :
1021 6 : smb2_transport_compound_start(tree->session->transport, 3);
1022 6 : smb2_transport_compound_set_related(tree->session->transport, true);
1023 :
1024 6 : ZERO_STRUCT(nt);
1025 6 : nt.in.recursive = true;
1026 6 : nt.in.buffer_size = 0x1000;
1027 6 : nt.in.completion_filter = FILE_NOTIFY_CHANGE_NAME;
1028 :
1029 6 : req[0] = smb2_notify_send(tree, &nt);
1030 6 : torture_assert_not_null_goto(tctx, req[0], ret, done,
1031 : "smb2_notify_send failed\n");
1032 :
1033 6 : ZERO_STRUCT(cl);
1034 6 : cl.in.file.handle = hd;
1035 :
1036 6 : req[1] = smb2_close_send(tree, &cl);
1037 6 : torture_assert_not_null_goto(tctx, req[1], ret, done,
1038 : "smb2_close_send failed\n");
1039 :
1040 6 : sd = security_descriptor_dacl_create(tctx,
1041 : 0, NULL, NULL,
1042 : SID_CREATOR_OWNER,
1043 : SEC_ACE_TYPE_ACCESS_ALLOWED,
1044 : SEC_RIGHTS_FILE_READ | SEC_STD_ALL,
1045 : 0,
1046 : NULL);
1047 6 : torture_assert_not_null_goto(tctx, sd, ret, done,
1048 : "security_descriptor_dacl_create failed\n");
1049 :
1050 6 : set.set_secdesc.level = RAW_SFILEINFO_SEC_DESC;
1051 6 : set.set_secdesc.in.file.handle = hd;
1052 6 : set.set_secdesc.in.secinfo_flags = SECINFO_DACL;
1053 6 : set.set_secdesc.in.sd = sd;
1054 :
1055 6 : req[2] = smb2_setinfo_file_send(tree, &set);
1056 6 : torture_assert_not_null_goto(tctx, req[2], ret, done,
1057 : "smb2_setinfo_file_send failed\n");
1058 :
1059 6 : status = smb2_notify_recv(req[0], tree, &nt);
1060 6 : torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_INVALID_PARAMETER,
1061 : ret, done,
1062 : "smb2_notify_recv failed\n");
1063 :
1064 6 : status = smb2_close_recv(req[1], &cl);
1065 6 : torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_INVALID_PARAMETER,
1066 : ret, done,
1067 : "smb2_close_recv failed\n");
1068 :
1069 6 : status = smb2_setinfo_recv(req[2]);
1070 6 : torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_INVALID_PARAMETER,
1071 : ret, done,
1072 : "smb2_setinfo_recv failed\n");
1073 :
1074 6 : done:
1075 6 : smb2_util_unlink(tree, fname);
1076 6 : smb2_tdis(tree);
1077 6 : smb2_logoff(tree->session);
1078 6 : return ret;
1079 : }
1080 :
1081 6 : static bool test_compound_padding(struct torture_context *tctx,
1082 : struct smb2_tree *tree)
1083 : {
1084 0 : struct smb2_handle h;
1085 0 : struct smb2_create cr;
1086 0 : struct smb2_read r;
1087 0 : struct smb2_read r2;
1088 6 : const char *fname = "compound_read.dat";
1089 6 : const char *sname = "compound_read.dat:foo";
1090 0 : struct smb2_request *req[3];
1091 0 : NTSTATUS status;
1092 6 : bool ret = false;
1093 :
1094 6 : smb2_util_unlink(tree, fname);
1095 :
1096 : /* Write file */
1097 6 : ZERO_STRUCT(cr);
1098 6 : cr.in.desired_access = SEC_FILE_WRITE_DATA;
1099 6 : cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1100 6 : cr.in.create_disposition = NTCREATEX_DISP_CREATE;
1101 6 : cr.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
1102 6 : cr.in.fname = fname;
1103 6 : cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ|
1104 : NTCREATEX_SHARE_ACCESS_WRITE|
1105 : NTCREATEX_SHARE_ACCESS_DELETE;
1106 6 : status = smb2_create(tree, tctx, &cr);
1107 6 : CHECK_STATUS(status, NT_STATUS_OK);
1108 6 : h = cr.out.file.handle;
1109 :
1110 6 : status = smb2_util_write(tree, h, "123", 0, 3);
1111 6 : CHECK_STATUS(status, NT_STATUS_OK);
1112 :
1113 6 : smb2_util_close(tree, h);
1114 :
1115 : /* Write stream */
1116 6 : ZERO_STRUCT(cr);
1117 6 : cr.in.desired_access = SEC_FILE_WRITE_DATA;
1118 6 : cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1119 6 : cr.in.create_disposition = NTCREATEX_DISP_CREATE;
1120 6 : cr.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
1121 6 : cr.in.fname = sname;
1122 6 : cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ|
1123 : NTCREATEX_SHARE_ACCESS_WRITE|
1124 : NTCREATEX_SHARE_ACCESS_DELETE;
1125 6 : status = smb2_create(tree, tctx, &cr);
1126 6 : CHECK_STATUS(status, NT_STATUS_OK);
1127 6 : h = cr.out.file.handle;
1128 :
1129 6 : status = smb2_util_write(tree, h, "456", 0, 3);
1130 6 : CHECK_STATUS(status, NT_STATUS_OK);
1131 :
1132 6 : smb2_util_close(tree, h);
1133 :
1134 : /* Check compound read from basefile */
1135 6 : smb2_transport_compound_start(tree->session->transport, 3);
1136 :
1137 6 : ZERO_STRUCT(cr);
1138 6 : cr.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
1139 6 : cr.in.desired_access = SEC_FILE_READ_DATA;
1140 6 : cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1141 6 : cr.in.create_disposition = NTCREATEX_DISP_OPEN;
1142 6 : cr.in.fname = fname;
1143 6 : cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ|
1144 : NTCREATEX_SHARE_ACCESS_WRITE|
1145 : NTCREATEX_SHARE_ACCESS_DELETE;
1146 6 : req[0] = smb2_create_send(tree, &cr);
1147 :
1148 6 : smb2_transport_compound_set_related(tree->session->transport, true);
1149 :
1150 : /*
1151 : * We send 2 reads in the compound here as the protocol
1152 : * allows the last read to be split off and possibly
1153 : * go async. Check the padding on the first read returned,
1154 : * not the second as the second may not be part of the
1155 : * returned compound.
1156 : */
1157 :
1158 6 : ZERO_STRUCT(r);
1159 6 : h.data[0] = UINT64_MAX;
1160 6 : h.data[1] = UINT64_MAX;
1161 6 : r.in.file.handle = h;
1162 6 : r.in.length = 3;
1163 6 : r.in.offset = 0;
1164 6 : r.in.min_count = 1;
1165 6 : req[1] = smb2_read_send(tree, &r);
1166 :
1167 6 : ZERO_STRUCT(r2);
1168 6 : h.data[0] = UINT64_MAX;
1169 6 : h.data[1] = UINT64_MAX;
1170 6 : r2.in.file.handle = h;
1171 6 : r2.in.length = 3;
1172 6 : r2.in.offset = 0;
1173 6 : r2.in.min_count = 1;
1174 6 : req[2] = smb2_read_send(tree, &r2);
1175 :
1176 6 : status = smb2_create_recv(req[0], tree, &cr);
1177 6 : CHECK_STATUS(status, NT_STATUS_OK);
1178 :
1179 : /*
1180 : * We must do a manual smb2_request_receive() in order to be
1181 : * able to check the transport layer info, as smb2_read_recv()
1182 : * will destroy the req. smb2_read_recv() will call
1183 : * smb2_request_receive() again, but that's ok.
1184 : */
1185 6 : if (!smb2_request_receive(req[1]) ||
1186 6 : !smb2_request_is_ok(req[1])) {
1187 0 : torture_fail(tctx, "failed to receive read request");
1188 : }
1189 :
1190 : /*
1191 : * size must be 24: 16 byte read response header plus 3
1192 : * requested bytes padded to an 8 byte boundary.
1193 : */
1194 6 : CHECK_VALUE(req[1]->in.body_size, 24);
1195 :
1196 6 : status = smb2_read_recv(req[1], tree, &r);
1197 6 : CHECK_STATUS(status, NT_STATUS_OK);
1198 :
1199 : /* Pick up the second, possibly async, read. */
1200 6 : status = smb2_read_recv(req[2], tree, &r2);
1201 6 : CHECK_STATUS(status, NT_STATUS_OK);
1202 :
1203 6 : smb2_util_close(tree, cr.out.file.handle);
1204 :
1205 : /* Check compound read from stream */
1206 6 : smb2_transport_compound_start(tree->session->transport, 3);
1207 :
1208 6 : ZERO_STRUCT(cr);
1209 6 : cr.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
1210 6 : cr.in.desired_access = SEC_FILE_READ_DATA;
1211 6 : cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1212 6 : cr.in.create_disposition = NTCREATEX_DISP_OPEN;
1213 6 : cr.in.fname = sname;
1214 6 : cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ|
1215 : NTCREATEX_SHARE_ACCESS_WRITE|
1216 : NTCREATEX_SHARE_ACCESS_DELETE;
1217 6 : req[0] = smb2_create_send(tree, &cr);
1218 :
1219 6 : smb2_transport_compound_set_related(tree->session->transport, true);
1220 :
1221 : /*
1222 : * We send 2 reads in the compound here as the protocol
1223 : * allows the last read to be split off and possibly
1224 : * go async. Check the padding on the first read returned,
1225 : * not the second as the second may not be part of the
1226 : * returned compound.
1227 : */
1228 :
1229 6 : ZERO_STRUCT(r);
1230 6 : h.data[0] = UINT64_MAX;
1231 6 : h.data[1] = UINT64_MAX;
1232 6 : r.in.file.handle = h;
1233 6 : r.in.length = 3;
1234 6 : r.in.offset = 0;
1235 6 : r.in.min_count = 1;
1236 6 : req[1] = smb2_read_send(tree, &r);
1237 :
1238 6 : ZERO_STRUCT(r2);
1239 6 : h.data[0] = UINT64_MAX;
1240 6 : h.data[1] = UINT64_MAX;
1241 6 : r2.in.file.handle = h;
1242 6 : r2.in.length = 3;
1243 6 : r2.in.offset = 0;
1244 6 : r2.in.min_count = 1;
1245 6 : req[2] = smb2_read_send(tree, &r2);
1246 :
1247 6 : status = smb2_create_recv(req[0], tree, &cr);
1248 6 : CHECK_STATUS(status, NT_STATUS_OK);
1249 :
1250 : /*
1251 : * We must do a manual smb2_request_receive() in order to be
1252 : * able to check the transport layer info, as smb2_read_recv()
1253 : * will destroy the req. smb2_read_recv() will call
1254 : * smb2_request_receive() again, but that's ok.
1255 : */
1256 6 : if (!smb2_request_receive(req[1]) ||
1257 6 : !smb2_request_is_ok(req[1])) {
1258 0 : torture_fail(tctx, "failed to receive read request");
1259 : }
1260 :
1261 : /*
1262 : * size must be 24: 16 byte read response header plus 3
1263 : * requested bytes padded to an 8 byte boundary.
1264 : */
1265 6 : CHECK_VALUE(req[1]->in.body_size, 24);
1266 :
1267 6 : status = smb2_read_recv(req[1], tree, &r);
1268 6 : CHECK_STATUS(status, NT_STATUS_OK);
1269 :
1270 : /* Pick up the second, possibly async, read. */
1271 6 : status = smb2_read_recv(req[2], tree, &r2);
1272 6 : CHECK_STATUS(status, NT_STATUS_OK);
1273 :
1274 6 : h = cr.out.file.handle;
1275 :
1276 : /* Check 2 compound (unrelateated) reads from existing stream handle */
1277 6 : smb2_transport_compound_start(tree->session->transport, 2);
1278 :
1279 6 : ZERO_STRUCT(r);
1280 6 : r.in.file.handle = h;
1281 6 : r.in.length = 3;
1282 6 : r.in.offset = 0;
1283 6 : r.in.min_count = 1;
1284 6 : req[0] = smb2_read_send(tree, &r);
1285 6 : req[1] = smb2_read_send(tree, &r);
1286 :
1287 : /*
1288 : * We must do a manual smb2_request_receive() in order to be
1289 : * able to check the transport layer info, as smb2_read_recv()
1290 : * will destroy the req. smb2_read_recv() will call
1291 : * smb2_request_receive() again, but that's ok.
1292 : */
1293 6 : if (!smb2_request_receive(req[0]) ||
1294 6 : !smb2_request_is_ok(req[0])) {
1295 0 : torture_fail(tctx, "failed to receive read request");
1296 : }
1297 6 : if (!smb2_request_receive(req[1]) ||
1298 6 : !smb2_request_is_ok(req[1])) {
1299 0 : torture_fail(tctx, "failed to receive read request");
1300 : }
1301 :
1302 : /*
1303 : * size must be 24: 16 byte read response header plus 3
1304 : * requested bytes padded to an 8 byte boundary.
1305 : */
1306 6 : CHECK_VALUE(req[0]->in.body_size, 24);
1307 6 : CHECK_VALUE(req[1]->in.body_size, 24);
1308 :
1309 6 : status = smb2_read_recv(req[0], tree, &r);
1310 6 : CHECK_STATUS(status, NT_STATUS_OK);
1311 6 : status = smb2_read_recv(req[1], tree, &r);
1312 6 : CHECK_STATUS(status, NT_STATUS_OK);
1313 :
1314 : /*
1315 : * now try a single read from the stream and verify there's no padding
1316 : */
1317 6 : ZERO_STRUCT(r);
1318 6 : r.in.file.handle = h;
1319 6 : r.in.length = 3;
1320 6 : r.in.offset = 0;
1321 6 : r.in.min_count = 1;
1322 6 : req[0] = smb2_read_send(tree, &r);
1323 :
1324 : /*
1325 : * We must do a manual smb2_request_receive() in order to be
1326 : * able to check the transport layer info, as smb2_read_recv()
1327 : * will destroy the req. smb2_read_recv() will call
1328 : * smb2_request_receive() again, but that's ok.
1329 : */
1330 6 : if (!smb2_request_receive(req[0]) ||
1331 6 : !smb2_request_is_ok(req[0])) {
1332 0 : torture_fail(tctx, "failed to receive read request");
1333 : }
1334 :
1335 : /*
1336 : * size must be 19: 16 byte read response header plus 3
1337 : * requested bytes without padding.
1338 : */
1339 6 : CHECK_VALUE(req[0]->in.body_size, 19);
1340 :
1341 6 : status = smb2_read_recv(req[0], tree, &r);
1342 6 : CHECK_STATUS(status, NT_STATUS_OK);
1343 :
1344 6 : smb2_util_close(tree, h);
1345 :
1346 6 : status = smb2_util_unlink(tree, fname);
1347 6 : CHECK_STATUS(status, NT_STATUS_OK);
1348 :
1349 6 : ret = true;
1350 6 : done:
1351 6 : return ret;
1352 : }
1353 :
1354 6 : static bool test_compound_create_write_close(struct torture_context *tctx,
1355 : struct smb2_tree *tree)
1356 : {
1357 6 : struct smb2_handle handle = { .data = { UINT64_MAX, UINT64_MAX } };
1358 0 : struct smb2_create create;
1359 0 : struct smb2_write write;
1360 0 : struct smb2_close close;
1361 6 : const char *fname = "compound_create_write_close.dat";
1362 0 : struct smb2_request *req[3];
1363 0 : NTSTATUS status;
1364 6 : bool ret = false;
1365 :
1366 6 : smb2_util_unlink(tree, fname);
1367 :
1368 6 : ZERO_STRUCT(create);
1369 6 : create.in.security_flags = 0x00;
1370 6 : create.in.oplock_level = 0;
1371 6 : create.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION;
1372 6 : create.in.create_flags = 0x00000000;
1373 6 : create.in.reserved = 0x00000000;
1374 6 : create.in.desired_access = SEC_RIGHTS_FILE_ALL;
1375 6 : create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1376 6 : create.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
1377 : NTCREATEX_SHARE_ACCESS_WRITE |
1378 : NTCREATEX_SHARE_ACCESS_DELETE;
1379 6 : create.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
1380 6 : create.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
1381 : NTCREATEX_OPTIONS_ASYNC_ALERT |
1382 : NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
1383 : 0x00200000;
1384 6 : create.in.fname = fname;
1385 :
1386 6 : smb2_transport_compound_start(tree->session->transport, 3);
1387 :
1388 6 : req[0] = smb2_create_send(tree, &create);
1389 :
1390 6 : smb2_transport_compound_set_related(tree->session->transport, true);
1391 :
1392 6 : ZERO_STRUCT(write);
1393 6 : write.in.file.handle = handle;
1394 6 : write.in.offset = 0;
1395 6 : write.in.data = data_blob_talloc(tctx, NULL, 1024);
1396 :
1397 6 : req[1] = smb2_write_send(tree, &write);
1398 :
1399 6 : ZERO_STRUCT(close);
1400 6 : close.in.file.handle = handle;
1401 :
1402 6 : req[2] = smb2_close_send(tree, &close);
1403 :
1404 6 : status = smb2_create_recv(req[0], tree, &create);
1405 6 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1406 : "CREATE failed.");
1407 :
1408 6 : status = smb2_write_recv(req[1], &write);
1409 6 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1410 : "WRITE failed.");
1411 :
1412 6 : status = smb2_close_recv(req[2], &close);
1413 6 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1414 : "CLOSE failed.");
1415 :
1416 6 : status = smb2_util_unlink(tree, fname);
1417 6 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1418 : "File deletion failed.");
1419 :
1420 6 : ret = true;
1421 6 : done:
1422 6 : return ret;
1423 : }
1424 :
1425 6 : static bool test_compound_unrelated1(struct torture_context *tctx,
1426 : struct smb2_tree *tree)
1427 : {
1428 0 : struct smb2_handle hd;
1429 0 : struct smb2_create cr;
1430 0 : NTSTATUS status;
1431 6 : const char *fname = "compound_unrelated1.dat";
1432 0 : struct smb2_close cl;
1433 6 : bool ret = true;
1434 0 : struct smb2_request *req[5];
1435 :
1436 6 : smb2_transport_credits_ask_num(tree->session->transport, 5);
1437 :
1438 6 : smb2_util_unlink(tree, fname);
1439 :
1440 6 : smb2_transport_credits_ask_num(tree->session->transport, 1);
1441 :
1442 6 : ZERO_STRUCT(cr);
1443 6 : cr.in.security_flags = 0x00;
1444 6 : cr.in.oplock_level = 0;
1445 6 : cr.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION;
1446 6 : cr.in.create_flags = 0x00000000;
1447 6 : cr.in.reserved = 0x00000000;
1448 6 : cr.in.desired_access = SEC_RIGHTS_FILE_ALL;
1449 6 : cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1450 6 : cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
1451 : NTCREATEX_SHARE_ACCESS_WRITE |
1452 : NTCREATEX_SHARE_ACCESS_DELETE;
1453 6 : cr.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
1454 6 : cr.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
1455 : NTCREATEX_OPTIONS_ASYNC_ALERT |
1456 : NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
1457 : 0x00200000;
1458 6 : cr.in.fname = fname;
1459 :
1460 6 : smb2_transport_compound_start(tree->session->transport, 5);
1461 :
1462 6 : req[0] = smb2_create_send(tree, &cr);
1463 :
1464 6 : hd.data[0] = UINT64_MAX;
1465 6 : hd.data[1] = UINT64_MAX;
1466 :
1467 6 : ZERO_STRUCT(cl);
1468 6 : cl.in.file.handle = hd;
1469 6 : req[1] = smb2_close_send(tree, &cl);
1470 6 : req[2] = smb2_close_send(tree, &cl);
1471 6 : req[3] = smb2_close_send(tree, &cl);
1472 6 : req[4] = smb2_close_send(tree, &cl);
1473 :
1474 6 : status = smb2_create_recv(req[0], tree, &cr);
1475 6 : CHECK_STATUS(status, NT_STATUS_OK);
1476 6 : status = smb2_close_recv(req[1], &cl);
1477 6 : CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
1478 6 : status = smb2_close_recv(req[2], &cl);
1479 6 : CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
1480 6 : status = smb2_close_recv(req[3], &cl);
1481 6 : CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
1482 6 : status = smb2_close_recv(req[4], &cl);
1483 6 : CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
1484 :
1485 6 : smb2_util_unlink(tree, fname);
1486 6 : done:
1487 6 : return ret;
1488 : }
1489 :
1490 6 : static bool test_compound_invalid1(struct torture_context *tctx,
1491 : struct smb2_tree *tree)
1492 : {
1493 0 : struct smb2_handle hd;
1494 0 : struct smb2_create cr;
1495 0 : NTSTATUS status;
1496 6 : const char *fname = "compound_invalid1.dat";
1497 0 : struct smb2_close cl;
1498 6 : bool ret = true;
1499 0 : struct smb2_request *req[3];
1500 :
1501 6 : smb2_transport_credits_ask_num(tree->session->transport, 3);
1502 :
1503 6 : smb2_util_unlink(tree, fname);
1504 :
1505 6 : smb2_transport_credits_ask_num(tree->session->transport, 1);
1506 :
1507 6 : ZERO_STRUCT(cr);
1508 6 : cr.in.security_flags = 0x00;
1509 6 : cr.in.oplock_level = 0;
1510 6 : cr.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION;
1511 6 : cr.in.create_flags = 0x00000000;
1512 6 : cr.in.reserved = 0x00000000;
1513 6 : cr.in.desired_access = SEC_RIGHTS_FILE_ALL;
1514 6 : cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1515 6 : cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
1516 : NTCREATEX_SHARE_ACCESS_WRITE |
1517 : NTCREATEX_SHARE_ACCESS_DELETE;
1518 6 : cr.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
1519 6 : cr.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
1520 : NTCREATEX_OPTIONS_ASYNC_ALERT |
1521 : NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
1522 : 0x00200000;
1523 6 : cr.in.fname = fname;
1524 :
1525 6 : smb2_transport_compound_start(tree->session->transport, 3);
1526 :
1527 : /* passing the first request with the related flag is invalid */
1528 6 : smb2_transport_compound_set_related(tree->session->transport, true);
1529 :
1530 6 : req[0] = smb2_create_send(tree, &cr);
1531 :
1532 6 : hd.data[0] = UINT64_MAX;
1533 6 : hd.data[1] = UINT64_MAX;
1534 :
1535 6 : ZERO_STRUCT(cl);
1536 6 : cl.in.file.handle = hd;
1537 6 : req[1] = smb2_close_send(tree, &cl);
1538 :
1539 6 : smb2_transport_compound_set_related(tree->session->transport, false);
1540 6 : req[2] = smb2_close_send(tree, &cl);
1541 :
1542 6 : status = smb2_create_recv(req[0], tree, &cr);
1543 : /* TODO: check why this fails with --signing=required */
1544 6 : CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
1545 6 : status = smb2_close_recv(req[1], &cl);
1546 6 : CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
1547 6 : status = smb2_close_recv(req[2], &cl);
1548 6 : CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
1549 :
1550 6 : smb2_util_unlink(tree, fname);
1551 6 : done:
1552 6 : return ret;
1553 : }
1554 :
1555 6 : static bool test_compound_invalid2(struct torture_context *tctx,
1556 : struct smb2_tree *tree)
1557 : {
1558 0 : struct smb2_handle hd;
1559 0 : struct smb2_create cr;
1560 0 : NTSTATUS status;
1561 6 : const char *fname = "compound_invalid2.dat";
1562 0 : struct smb2_close cl;
1563 6 : bool ret = true;
1564 0 : struct smb2_request *req[5];
1565 6 : struct smbXcli_tcon *saved_tcon = tree->smbXcli;
1566 6 : struct smbXcli_session *saved_session = tree->session->smbXcli;
1567 :
1568 6 : smb2_transport_credits_ask_num(tree->session->transport, 5);
1569 :
1570 6 : smb2_util_unlink(tree, fname);
1571 :
1572 6 : smb2_transport_credits_ask_num(tree->session->transport, 1);
1573 :
1574 6 : ZERO_STRUCT(cr);
1575 6 : cr.in.security_flags = 0x00;
1576 6 : cr.in.oplock_level = 0;
1577 6 : cr.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION;
1578 6 : cr.in.create_flags = 0x00000000;
1579 6 : cr.in.reserved = 0x00000000;
1580 6 : cr.in.desired_access = SEC_RIGHTS_FILE_ALL;
1581 6 : cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1582 6 : cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
1583 : NTCREATEX_SHARE_ACCESS_WRITE |
1584 : NTCREATEX_SHARE_ACCESS_DELETE;
1585 6 : cr.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
1586 6 : cr.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
1587 : NTCREATEX_OPTIONS_ASYNC_ALERT |
1588 : NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
1589 : 0x00200000;
1590 6 : cr.in.fname = fname;
1591 :
1592 6 : smb2_transport_compound_start(tree->session->transport, 5);
1593 :
1594 6 : req[0] = smb2_create_send(tree, &cr);
1595 :
1596 6 : hd.data[0] = UINT64_MAX;
1597 6 : hd.data[1] = UINT64_MAX;
1598 :
1599 6 : smb2_transport_compound_set_related(tree->session->transport, true);
1600 :
1601 6 : ZERO_STRUCT(cl);
1602 6 : cl.in.file.handle = hd;
1603 :
1604 6 : tree->smbXcli = smbXcli_tcon_create(tree);
1605 6 : smb2cli_tcon_set_values(tree->smbXcli,
1606 : NULL, /* session */
1607 : 0xFFFFFFFF, /* tcon_id */
1608 : 0, /* type */
1609 : 0, /* flags */
1610 : 0, /* capabilities */
1611 : 0 /* maximal_access */);
1612 :
1613 12 : tree->session->smbXcli = smbXcli_session_shallow_copy(tree->session,
1614 6 : tree->session->smbXcli);
1615 6 : smb2cli_session_set_id_and_flags(tree->session->smbXcli, UINT64_MAX, 0);
1616 :
1617 6 : req[1] = smb2_close_send(tree, &cl);
1618 : /* strange that this is not generating invalid parameter */
1619 6 : smb2_transport_compound_set_related(tree->session->transport, false);
1620 6 : req[2] = smb2_close_send(tree, &cl);
1621 6 : req[3] = smb2_close_send(tree, &cl);
1622 6 : smb2_transport_compound_set_related(tree->session->transport, true);
1623 6 : req[4] = smb2_close_send(tree, &cl);
1624 :
1625 6 : status = smb2_create_recv(req[0], tree, &cr);
1626 6 : CHECK_STATUS(status, NT_STATUS_OK);
1627 6 : status = smb2_close_recv(req[1], &cl);
1628 6 : CHECK_STATUS(status, NT_STATUS_OK);
1629 6 : status = smb2_close_recv(req[2], &cl);
1630 6 : CHECK_STATUS(status, NT_STATUS_USER_SESSION_DELETED);
1631 6 : status = smb2_close_recv(req[3], &cl);
1632 6 : CHECK_STATUS(status, NT_STATUS_USER_SESSION_DELETED);
1633 6 : status = smb2_close_recv(req[4], &cl);
1634 6 : CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
1635 :
1636 6 : TALLOC_FREE(tree->smbXcli);
1637 6 : tree->smbXcli = saved_tcon;
1638 6 : TALLOC_FREE(tree->session->smbXcli);
1639 6 : tree->session->smbXcli = saved_session;
1640 :
1641 6 : smb2_util_unlink(tree, fname);
1642 6 : done:
1643 6 : return ret;
1644 : }
1645 :
1646 6 : static bool test_compound_invalid3(struct torture_context *tctx,
1647 : struct smb2_tree *tree)
1648 : {
1649 0 : struct smb2_handle hd;
1650 0 : struct smb2_create cr;
1651 0 : NTSTATUS status;
1652 6 : const char *fname = "compound_invalid3.dat";
1653 0 : struct smb2_close cl;
1654 6 : bool ret = true;
1655 0 : struct smb2_request *req[5];
1656 :
1657 6 : smb2_transport_credits_ask_num(tree->session->transport, 5);
1658 :
1659 6 : smb2_util_unlink(tree, fname);
1660 :
1661 6 : smb2_transport_credits_ask_num(tree->session->transport, 1);
1662 :
1663 6 : ZERO_STRUCT(cr);
1664 6 : cr.in.security_flags = 0x00;
1665 6 : cr.in.oplock_level = 0;
1666 6 : cr.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION;
1667 6 : cr.in.create_flags = 0x00000000;
1668 6 : cr.in.reserved = 0x00000000;
1669 6 : cr.in.desired_access = SEC_RIGHTS_FILE_ALL;
1670 6 : cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1671 6 : cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
1672 : NTCREATEX_SHARE_ACCESS_WRITE |
1673 : NTCREATEX_SHARE_ACCESS_DELETE;
1674 6 : cr.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
1675 6 : cr.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
1676 : NTCREATEX_OPTIONS_ASYNC_ALERT |
1677 : NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
1678 : 0x00200000;
1679 6 : cr.in.fname = fname;
1680 :
1681 6 : smb2_transport_compound_start(tree->session->transport, 5);
1682 :
1683 6 : req[0] = smb2_create_send(tree, &cr);
1684 :
1685 6 : hd.data[0] = UINT64_MAX;
1686 6 : hd.data[1] = UINT64_MAX;
1687 :
1688 6 : ZERO_STRUCT(cl);
1689 6 : cl.in.file.handle = hd;
1690 6 : req[1] = smb2_close_send(tree, &cl);
1691 6 : req[2] = smb2_close_send(tree, &cl);
1692 : /* flipping the related flag is invalid */
1693 6 : smb2_transport_compound_set_related(tree->session->transport, true);
1694 6 : req[3] = smb2_close_send(tree, &cl);
1695 6 : req[4] = smb2_close_send(tree, &cl);
1696 :
1697 6 : status = smb2_create_recv(req[0], tree, &cr);
1698 6 : CHECK_STATUS(status, NT_STATUS_OK);
1699 6 : status = smb2_close_recv(req[1], &cl);
1700 6 : CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
1701 6 : status = smb2_close_recv(req[2], &cl);
1702 6 : CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
1703 6 : status = smb2_close_recv(req[3], &cl);
1704 6 : CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
1705 6 : status = smb2_close_recv(req[4], &cl);
1706 6 : CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
1707 :
1708 6 : smb2_util_unlink(tree, fname);
1709 6 : done:
1710 6 : return ret;
1711 : }
1712 :
1713 6 : static bool test_compound_invalid4(struct torture_context *tctx,
1714 : struct smb2_tree *tree)
1715 : {
1716 0 : struct smb2_create cr;
1717 0 : struct smb2_read rd;
1718 0 : NTSTATUS status;
1719 6 : const char *fname = "compound_invalid4.dat";
1720 0 : struct smb2_close cl;
1721 6 : bool ret = true;
1722 0 : bool ok;
1723 0 : struct smb2_request *req[2];
1724 :
1725 6 : smb2_transport_credits_ask_num(tree->session->transport, 2);
1726 :
1727 6 : smb2_util_unlink(tree, fname);
1728 :
1729 6 : ZERO_STRUCT(cr);
1730 6 : cr.in.security_flags = 0x00;
1731 6 : cr.in.oplock_level = 0;
1732 6 : cr.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION;
1733 6 : cr.in.create_flags = 0x00000000;
1734 6 : cr.in.reserved = 0x00000000;
1735 6 : cr.in.desired_access = SEC_RIGHTS_FILE_ALL;
1736 6 : cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1737 6 : cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
1738 : NTCREATEX_SHARE_ACCESS_WRITE |
1739 : NTCREATEX_SHARE_ACCESS_DELETE;
1740 6 : cr.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
1741 6 : cr.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
1742 : NTCREATEX_OPTIONS_ASYNC_ALERT |
1743 : NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
1744 : 0x00200000;
1745 6 : cr.in.fname = fname;
1746 :
1747 6 : status = smb2_create(tree, tctx, &cr);
1748 6 : CHECK_STATUS(status, NT_STATUS_OK);
1749 :
1750 6 : smb2_transport_compound_start(tree->session->transport, 2);
1751 :
1752 6 : ZERO_STRUCT(rd);
1753 6 : rd.in.file.handle = cr.out.file.handle;
1754 6 : rd.in.length = 1;
1755 6 : rd.in.offset = 0;
1756 6 : req[0] = smb2_read_send(tree, &rd);
1757 :
1758 6 : smb2_transport_compound_set_related(tree->session->transport, true);
1759 :
1760 : /*
1761 : * Send a completely bogus request as second compound
1762 : * element. This triggers smbd_smb2_request_error() in in
1763 : * smbd_smb2_request_dispatch() before calling
1764 : * smbd_smb2_request_dispatch_update_counts().
1765 : */
1766 :
1767 6 : req[1] = smb2_request_init_tree(tree, 0xff, 0x04, false, 0);
1768 6 : smb2_transport_send(req[1]);
1769 :
1770 6 : status = smb2_read_recv(req[0], tctx, &rd);
1771 6 : CHECK_STATUS(status, NT_STATUS_END_OF_FILE);
1772 :
1773 6 : ok = smb2_request_receive(req[1]);
1774 6 : torture_assert(tctx, ok, "Invalid request failed\n");
1775 6 : CHECK_STATUS(req[1]->status, NT_STATUS_INVALID_PARAMETER);
1776 :
1777 6 : ZERO_STRUCT(cl);
1778 6 : cl.in.file.handle = cr.out.file.handle;
1779 :
1780 6 : status = smb2_close(tree, &cl);
1781 6 : CHECK_STATUS(status, NT_STATUS_OK);
1782 :
1783 6 : smb2_util_unlink(tree, fname);
1784 6 : done:
1785 6 : return ret;
1786 : }
1787 :
1788 : /* Send a compound request where we expect the last request (Create, Notify)
1789 : * to go asynchronous. This works against a Win7 server and the reply is
1790 : * sent in two different packets. */
1791 6 : static bool test_compound_interim1(struct torture_context *tctx,
1792 : struct smb2_tree *tree)
1793 : {
1794 0 : struct smb2_handle hd;
1795 0 : struct smb2_create cr;
1796 6 : NTSTATUS status = NT_STATUS_OK;
1797 6 : const char *dname = "compound_interim_dir";
1798 0 : struct smb2_notify nt;
1799 6 : bool ret = true;
1800 0 : struct smb2_request *req[2];
1801 :
1802 : /* Win7 compound request implementation deviates substantially from the
1803 : * SMB2 spec as noted in MS-SMB2 <159>, <162>. This, test currently
1804 : * verifies the Windows behavior, not the general spec behavior. */
1805 :
1806 6 : smb2_transport_credits_ask_num(tree->session->transport, 5);
1807 :
1808 6 : smb2_deltree(tree, dname);
1809 :
1810 6 : smb2_transport_credits_ask_num(tree->session->transport, 1);
1811 :
1812 6 : ZERO_STRUCT(cr);
1813 6 : cr.in.desired_access = SEC_RIGHTS_FILE_ALL;
1814 6 : cr.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
1815 6 : cr.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
1816 6 : cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
1817 : NTCREATEX_SHARE_ACCESS_WRITE |
1818 : NTCREATEX_SHARE_ACCESS_DELETE;
1819 6 : cr.in.create_disposition = NTCREATEX_DISP_CREATE;
1820 6 : cr.in.fname = dname;
1821 :
1822 6 : smb2_transport_compound_start(tree->session->transport, 2);
1823 :
1824 6 : req[0] = smb2_create_send(tree, &cr);
1825 :
1826 6 : smb2_transport_compound_set_related(tree->session->transport, true);
1827 :
1828 6 : hd.data[0] = UINT64_MAX;
1829 6 : hd.data[1] = UINT64_MAX;
1830 :
1831 6 : ZERO_STRUCT(nt);
1832 6 : nt.in.recursive = true;
1833 6 : nt.in.buffer_size = 0x1000;
1834 6 : nt.in.file.handle = hd;
1835 6 : nt.in.completion_filter = FILE_NOTIFY_CHANGE_NAME;
1836 6 : nt.in.unknown = 0x00000000;
1837 :
1838 6 : req[1] = smb2_notify_send(tree, &nt);
1839 :
1840 6 : status = smb2_create_recv(req[0], tree, &cr);
1841 6 : CHECK_STATUS(status, NT_STATUS_OK);
1842 :
1843 6 : smb2_cancel(req[1]);
1844 6 : status = smb2_notify_recv(req[1], tree, &nt);
1845 6 : CHECK_STATUS(status, NT_STATUS_CANCELLED);
1846 :
1847 6 : smb2_util_close(tree, cr.out.file.handle);
1848 :
1849 6 : smb2_deltree(tree, dname);
1850 6 : done:
1851 6 : return ret;
1852 : }
1853 :
1854 : /* Send a compound request where we expect the middle request (Create, Notify,
1855 : * GetInfo) to go asynchronous. Against Win7 the sync request succeed while
1856 : * the async fails. All are returned in the same compound response. */
1857 6 : static bool test_compound_interim2(struct torture_context *tctx,
1858 : struct smb2_tree *tree)
1859 : {
1860 0 : struct smb2_handle hd;
1861 0 : struct smb2_create cr;
1862 6 : NTSTATUS status = NT_STATUS_OK;
1863 6 : const char *dname = "compound_interim_dir";
1864 0 : struct smb2_getinfo gf;
1865 0 : struct smb2_notify nt;
1866 6 : bool ret = true;
1867 0 : struct smb2_request *req[3];
1868 :
1869 : /* Win7 compound request implementation deviates substantially from the
1870 : * SMB2 spec as noted in MS-SMB2 <159>, <162>. This, test currently
1871 : * verifies the Windows behavior, not the general spec behavior. */
1872 :
1873 6 : smb2_transport_credits_ask_num(tree->session->transport, 5);
1874 :
1875 6 : smb2_deltree(tree, dname);
1876 :
1877 6 : smb2_transport_credits_ask_num(tree->session->transport, 1);
1878 :
1879 6 : ZERO_STRUCT(cr);
1880 6 : cr.in.desired_access = SEC_RIGHTS_FILE_ALL;
1881 6 : cr.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
1882 6 : cr.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
1883 6 : cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
1884 : NTCREATEX_SHARE_ACCESS_WRITE |
1885 : NTCREATEX_SHARE_ACCESS_DELETE;
1886 6 : cr.in.create_disposition = NTCREATEX_DISP_CREATE;
1887 6 : cr.in.fname = dname;
1888 :
1889 6 : smb2_transport_compound_start(tree->session->transport, 3);
1890 :
1891 6 : req[0] = smb2_create_send(tree, &cr);
1892 :
1893 6 : smb2_transport_compound_set_related(tree->session->transport, true);
1894 :
1895 6 : hd.data[0] = UINT64_MAX;
1896 6 : hd.data[1] = UINT64_MAX;
1897 :
1898 6 : ZERO_STRUCT(nt);
1899 6 : nt.in.recursive = true;
1900 6 : nt.in.buffer_size = 0x1000;
1901 6 : nt.in.file.handle = hd;
1902 6 : nt.in.completion_filter = FILE_NOTIFY_CHANGE_NAME;
1903 6 : nt.in.unknown = 0x00000000;
1904 :
1905 6 : req[1] = smb2_notify_send(tree, &nt);
1906 :
1907 6 : ZERO_STRUCT(gf);
1908 6 : gf.in.file.handle = hd;
1909 6 : gf.in.info_type = SMB2_0_INFO_FILE;
1910 6 : gf.in.info_class = 0x04; /* FILE_BASIC_INFORMATION */
1911 6 : gf.in.output_buffer_length = 0x1000;
1912 6 : gf.in.input_buffer = data_blob_null;
1913 :
1914 6 : req[2] = smb2_getinfo_send(tree, &gf);
1915 :
1916 6 : status = smb2_create_recv(req[0], tree, &cr);
1917 6 : CHECK_STATUS(status, NT_STATUS_OK);
1918 :
1919 6 : status = smb2_notify_recv(req[1], tree, &nt);
1920 6 : CHECK_STATUS(status, NT_STATUS_INTERNAL_ERROR);
1921 :
1922 0 : status = smb2_getinfo_recv(req[2], tree, &gf);
1923 0 : CHECK_STATUS(status, NT_STATUS_OK);
1924 :
1925 0 : smb2_util_close(tree, cr.out.file.handle);
1926 :
1927 0 : smb2_deltree(tree, dname);
1928 6 : done:
1929 6 : return ret;
1930 : }
1931 :
1932 : /* Test compound related finds */
1933 8 : static bool test_compound_find_related(struct torture_context *tctx,
1934 : struct smb2_tree *tree)
1935 : {
1936 8 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
1937 8 : const char *dname = "compound_find_dir";
1938 0 : struct smb2_create create;
1939 0 : struct smb2_find f;
1940 0 : struct smb2_handle h;
1941 0 : struct smb2_request *req[2];
1942 0 : NTSTATUS status;
1943 8 : bool ret = true;
1944 :
1945 8 : smb2_deltree(tree, dname);
1946 :
1947 8 : ZERO_STRUCT(create);
1948 8 : create.in.desired_access = SEC_RIGHTS_DIR_ALL;
1949 8 : create.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
1950 8 : create.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
1951 8 : create.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
1952 : NTCREATEX_SHARE_ACCESS_WRITE |
1953 : NTCREATEX_SHARE_ACCESS_DELETE;
1954 8 : create.in.create_disposition = NTCREATEX_DISP_CREATE;
1955 8 : create.in.fname = dname;
1956 :
1957 8 : status = smb2_create(tree, mem_ctx, &create);
1958 8 : h = create.out.file.handle;
1959 :
1960 8 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create failed\n");
1961 :
1962 8 : smb2_transport_compound_start(tree->session->transport, 2);
1963 :
1964 8 : ZERO_STRUCT(f);
1965 8 : f.in.file.handle = h;
1966 8 : f.in.pattern = "*";
1967 8 : f.in.max_response_size = 0x100;
1968 8 : f.in.level = SMB2_FIND_BOTH_DIRECTORY_INFO;
1969 :
1970 8 : req[0] = smb2_find_send(tree, &f);
1971 :
1972 8 : smb2_transport_compound_set_related(tree->session->transport, true);
1973 :
1974 8 : req[1] = smb2_find_send(tree, &f);
1975 :
1976 8 : status = smb2_find_recv(req[0], mem_ctx, &f);
1977 8 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_find_recv failed\n");
1978 :
1979 8 : status = smb2_find_recv(req[1], mem_ctx, &f);
1980 8 : torture_assert_ntstatus_equal_goto(tctx, status, STATUS_NO_MORE_FILES, ret, done, "smb2_find_recv failed\n");
1981 :
1982 8 : done:
1983 8 : smb2_util_close(tree, h);
1984 8 : smb2_deltree(tree, dname);
1985 8 : TALLOC_FREE(mem_ctx);
1986 8 : return ret;
1987 : }
1988 :
1989 : /* Test compound related finds */
1990 8 : static bool test_compound_find_close(struct torture_context *tctx,
1991 : struct smb2_tree *tree)
1992 : {
1993 8 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
1994 8 : const char *dname = "compound_find_dir";
1995 0 : struct smb2_create create;
1996 0 : struct smb2_find f;
1997 0 : struct smb2_handle h;
1998 8 : struct smb2_request *req = NULL;
1999 8 : const int num_files = 5000;
2000 0 : int i;
2001 0 : NTSTATUS status;
2002 8 : bool ret = true;
2003 :
2004 8 : smb2_deltree(tree, dname);
2005 :
2006 8 : ZERO_STRUCT(create);
2007 8 : create.in.desired_access = SEC_RIGHTS_DIR_ALL;
2008 8 : create.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
2009 8 : create.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
2010 8 : create.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
2011 : NTCREATEX_SHARE_ACCESS_WRITE |
2012 : NTCREATEX_SHARE_ACCESS_DELETE;
2013 8 : create.in.create_disposition = NTCREATEX_DISP_CREATE;
2014 8 : create.in.fname = dname;
2015 :
2016 8 : smb2cli_conn_set_max_credits(tree->session->transport->conn, 256);
2017 :
2018 8 : status = smb2_create(tree, mem_ctx, &create);
2019 8 : h = create.out.file.handle;
2020 :
2021 8 : ZERO_STRUCT(create);
2022 8 : create.in.desired_access = SEC_RIGHTS_FILE_ALL;
2023 8 : create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
2024 8 : create.in.create_disposition = NTCREATEX_DISP_CREATE;
2025 :
2026 40008 : for (i = 0; i < num_files; i++) {
2027 40000 : create.in.fname = talloc_asprintf(mem_ctx, "%s\\file%d",
2028 : dname, i);
2029 40000 : status = smb2_create(tree, mem_ctx, &create);
2030 40000 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "");
2031 40000 : smb2_util_close(tree, create.out.file.handle);
2032 : }
2033 :
2034 8 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create failed\n");
2035 :
2036 8 : ZERO_STRUCT(f);
2037 8 : f.in.file.handle = h;
2038 8 : f.in.pattern = "*";
2039 8 : f.in.max_response_size = 8*1024*1024;
2040 8 : f.in.level = SMB2_FIND_BOTH_DIRECTORY_INFO;
2041 :
2042 8 : req = smb2_find_send(tree, &f);
2043 :
2044 8 : status = smb2_util_close(tree, h);
2045 8 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_util_close failed\n");
2046 :
2047 8 : status = smb2_find_recv(req, mem_ctx, &f);
2048 8 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_find_recv failed\n");
2049 :
2050 8 : done:
2051 8 : smb2_util_close(tree, h);
2052 8 : smb2_deltree(tree, dname);
2053 8 : TALLOC_FREE(mem_ctx);
2054 8 : return ret;
2055 : }
2056 :
2057 : /* Test compound unrelated finds */
2058 8 : static bool test_compound_find_unrelated(struct torture_context *tctx,
2059 : struct smb2_tree *tree)
2060 : {
2061 8 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
2062 8 : const char *dname = "compound_find_dir";
2063 0 : struct smb2_create create;
2064 0 : struct smb2_find f;
2065 0 : struct smb2_handle h;
2066 0 : struct smb2_request *req[2];
2067 0 : NTSTATUS status;
2068 8 : bool ret = true;
2069 :
2070 8 : smb2_deltree(tree, dname);
2071 :
2072 8 : ZERO_STRUCT(create);
2073 8 : create.in.desired_access = SEC_RIGHTS_DIR_ALL;
2074 8 : create.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
2075 8 : create.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
2076 8 : create.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
2077 : NTCREATEX_SHARE_ACCESS_WRITE |
2078 : NTCREATEX_SHARE_ACCESS_DELETE;
2079 8 : create.in.create_disposition = NTCREATEX_DISP_CREATE;
2080 8 : create.in.fname = dname;
2081 :
2082 8 : status = smb2_create(tree, mem_ctx, &create);
2083 8 : h = create.out.file.handle;
2084 :
2085 8 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create failed\n");
2086 :
2087 8 : smb2_transport_compound_start(tree->session->transport, 2);
2088 :
2089 8 : ZERO_STRUCT(f);
2090 8 : f.in.file.handle = h;
2091 8 : f.in.pattern = "*";
2092 8 : f.in.max_response_size = 0x100;
2093 8 : f.in.level = SMB2_FIND_BOTH_DIRECTORY_INFO;
2094 :
2095 8 : req[0] = smb2_find_send(tree, &f);
2096 8 : req[1] = smb2_find_send(tree, &f);
2097 :
2098 8 : status = smb2_find_recv(req[0], mem_ctx, &f);
2099 8 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_find_recv failed\n");
2100 :
2101 8 : status = smb2_find_recv(req[1], mem_ctx, &f);
2102 8 : torture_assert_ntstatus_equal_goto(tctx, status, STATUS_NO_MORE_FILES, ret, done, "smb2_find_recv failed\n");
2103 :
2104 8 : done:
2105 8 : smb2_util_close(tree, h);
2106 8 : smb2_deltree(tree, dname);
2107 8 : TALLOC_FREE(mem_ctx);
2108 8 : return ret;
2109 : }
2110 :
2111 2 : static bool test_compound_async_flush_close(struct torture_context *tctx,
2112 : struct smb2_tree *tree)
2113 : {
2114 2 : struct smb2_handle fhandle = { .data = { 0, 0 } };
2115 2 : struct smb2_handle relhandle = { .data = { UINT64_MAX, UINT64_MAX } };
2116 0 : struct smb2_close cl;
2117 0 : struct smb2_flush fl;
2118 2 : const char *fname = "compound_async_flush_close";
2119 0 : struct smb2_request *req[2];
2120 0 : NTSTATUS status;
2121 2 : bool ret = false;
2122 :
2123 : /* Start clean. */
2124 2 : smb2_util_unlink(tree, fname);
2125 :
2126 : /* Create a file. */
2127 2 : status = torture_smb2_testfile_access(tree,
2128 : fname,
2129 : &fhandle,
2130 : SEC_RIGHTS_FILE_ALL);
2131 2 : CHECK_STATUS(status, NT_STATUS_OK);
2132 :
2133 : /* Now do a compound flush + close handle. */
2134 2 : smb2_transport_compound_start(tree->session->transport, 2);
2135 :
2136 2 : ZERO_STRUCT(fl);
2137 2 : fl.in.file.handle = fhandle;
2138 :
2139 2 : req[0] = smb2_flush_send(tree, &fl);
2140 2 : torture_assert_not_null_goto(tctx, req[0], ret, done,
2141 : "smb2_flush_send failed\n");
2142 :
2143 2 : smb2_transport_compound_set_related(tree->session->transport, true);
2144 :
2145 2 : ZERO_STRUCT(cl);
2146 2 : cl.in.file.handle = relhandle;
2147 2 : req[1] = smb2_close_send(tree, &cl);
2148 2 : torture_assert_not_null_goto(tctx, req[1], ret, done,
2149 : "smb2_close_send failed\n");
2150 :
2151 2 : status = smb2_flush_recv(req[0], &fl);
2152 : /*
2153 : * On Windows, this flush will usually
2154 : * succeed as we have nothing to flush,
2155 : * so allow NT_STATUS_OK. Once bug #15172
2156 : * is fixed Samba will do the flush synchronously
2157 : * so allow NT_STATUS_OK.
2158 : */
2159 2 : if (!NT_STATUS_IS_OK(status)) {
2160 : /*
2161 : * If we didn't get NT_STATUS_OK, we *must*
2162 : * get NT_STATUS_INTERNAL_ERROR if the flush
2163 : * goes async.
2164 : *
2165 : * For pre-bugfix #15172 Samba, the flush goes async and
2166 : * we should get NT_STATUS_INTERNAL_ERROR.
2167 : */
2168 0 : torture_assert_ntstatus_equal_goto(tctx,
2169 : status,
2170 : NT_STATUS_INTERNAL_ERROR,
2171 : ret,
2172 : done,
2173 : "smb2_flush_recv didn't return "
2174 : "NT_STATUS_INTERNAL_ERROR.\n");
2175 : }
2176 2 : status = smb2_close_recv(req[1], &cl);
2177 2 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2178 : "smb2_close_recv failed.");
2179 :
2180 2 : ZERO_STRUCT(fhandle);
2181 :
2182 : /*
2183 : * Do several more operations on the tree, spaced
2184 : * out by 1 sec sleeps to make sure the server didn't
2185 : * crash on the close. The sleeps are required to
2186 : * make test test for a crash reliable, as we ensure
2187 : * the pthread fsync internally finishes and accesses
2188 : * freed memory. Without them the test occasionally
2189 : * passes as we disconnect before the pthread fsync
2190 : * finishes.
2191 : */
2192 2 : status = smb2_util_unlink(tree, fname);
2193 2 : CHECK_STATUS(status, NT_STATUS_OK);
2194 :
2195 2 : sleep(1);
2196 2 : status = smb2_util_unlink(tree, fname);
2197 2 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
2198 :
2199 2 : sleep(1);
2200 2 : status = smb2_util_unlink(tree, fname);
2201 2 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
2202 :
2203 2 : ret = true;
2204 :
2205 2 : done:
2206 :
2207 2 : if (fhandle.data[0] != 0) {
2208 0 : smb2_util_close(tree, fhandle);
2209 : }
2210 :
2211 2 : smb2_util_unlink(tree, fname);
2212 2 : return ret;
2213 : }
2214 :
2215 2 : static bool test_compound_async_flush_flush(struct torture_context *tctx,
2216 : struct smb2_tree *tree)
2217 : {
2218 2 : struct smb2_handle fhandle = { .data = { 0, 0 } };
2219 2 : struct smb2_handle relhandle = { .data = { UINT64_MAX, UINT64_MAX } };
2220 0 : struct smb2_flush fl1;
2221 0 : struct smb2_flush fl2;
2222 2 : const char *fname = "compound_async_flush_flush";
2223 0 : struct smb2_request *req[2];
2224 0 : NTSTATUS status;
2225 2 : bool ret = false;
2226 :
2227 : /* Start clean. */
2228 2 : smb2_util_unlink(tree, fname);
2229 :
2230 : /* Create a file. */
2231 2 : status = torture_smb2_testfile_access(tree,
2232 : fname,
2233 : &fhandle,
2234 : SEC_RIGHTS_FILE_ALL);
2235 2 : CHECK_STATUS(status, NT_STATUS_OK);
2236 :
2237 : /* Now do a compound flush + flush handle. */
2238 2 : smb2_transport_compound_start(tree->session->transport, 2);
2239 :
2240 2 : ZERO_STRUCT(fl1);
2241 2 : fl1.in.file.handle = fhandle;
2242 :
2243 2 : req[0] = smb2_flush_send(tree, &fl1);
2244 2 : torture_assert_not_null_goto(tctx, req[0], ret, done,
2245 : "smb2_flush_send (1) failed\n");
2246 :
2247 2 : smb2_transport_compound_set_related(tree->session->transport, true);
2248 :
2249 2 : ZERO_STRUCT(fl2);
2250 2 : fl2.in.file.handle = relhandle;
2251 :
2252 2 : req[1] = smb2_flush_send(tree, &fl2);
2253 2 : torture_assert_not_null_goto(tctx, req[1], ret, done,
2254 : "smb2_flush_send (2) failed\n");
2255 :
2256 2 : status = smb2_flush_recv(req[0], &fl1);
2257 : /*
2258 : * On Windows, this flush will usually
2259 : * succeed as we have nothing to flush,
2260 : * so allow NT_STATUS_OK. Once bug #15172
2261 : * is fixed Samba will do the flush synchronously
2262 : * so allow NT_STATUS_OK.
2263 : */
2264 2 : if (!NT_STATUS_IS_OK(status)) {
2265 : /*
2266 : * If we didn't get NT_STATUS_OK, we *must*
2267 : * get NT_STATUS_INTERNAL_ERROR if the flush
2268 : * goes async.
2269 : *
2270 : * For pre-bugfix #15172 Samba, the flush goes async and
2271 : * we should get NT_STATUS_INTERNAL_ERROR.
2272 : */
2273 0 : torture_assert_ntstatus_equal_goto(tctx,
2274 : status,
2275 : NT_STATUS_INTERNAL_ERROR,
2276 : ret,
2277 : done,
2278 : "smb2_flush_recv (1) didn't return "
2279 : "NT_STATUS_INTERNAL_ERROR.\n");
2280 : }
2281 :
2282 : /*
2283 : * If the flush is the last entry in a compound,
2284 : * it should always succeed even if it goes async.
2285 : */
2286 2 : status = smb2_flush_recv(req[1], &fl2);
2287 2 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2288 : "smb2_flush_recv (2) failed.");
2289 :
2290 2 : status = smb2_util_close(tree, fhandle);
2291 2 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2292 : "smb2_util_close failed.");
2293 2 : ZERO_STRUCT(fhandle);
2294 :
2295 : /*
2296 : * Do several more operations on the tree, spaced
2297 : * out by 1 sec sleeps to make sure the server didn't
2298 : * crash on the close. The sleeps are required to
2299 : * make test test for a crash reliable, as we ensure
2300 : * the pthread fsync internally finishes and accesses
2301 : * freed memory. Without them the test occasionally
2302 : * passes as we disconnect before the pthread fsync
2303 : * finishes.
2304 : */
2305 2 : status = smb2_util_unlink(tree, fname);
2306 2 : CHECK_STATUS(status, NT_STATUS_OK);
2307 :
2308 2 : sleep(1);
2309 2 : status = smb2_util_unlink(tree, fname);
2310 2 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
2311 :
2312 2 : sleep(1);
2313 2 : status = smb2_util_unlink(tree, fname);
2314 2 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
2315 :
2316 2 : ret = true;
2317 :
2318 2 : done:
2319 :
2320 2 : if (fhandle.data[0] != 0) {
2321 0 : smb2_util_close(tree, fhandle);
2322 : }
2323 :
2324 2 : smb2_util_unlink(tree, fname);
2325 2 : return ret;
2326 : }
2327 :
2328 : /*
2329 : * For Samba/smbd this test must be run against the aio_delay_inject share
2330 : * as we need to ensure the last write in the compound takes longer than
2331 : * 500 us, which is the threshold for going async in smbd SMB2 writes.
2332 : */
2333 :
2334 2 : static bool test_compound_async_write_write(struct torture_context *tctx,
2335 : struct smb2_tree *tree)
2336 : {
2337 2 : struct smb2_handle fhandle = { .data = { 0, 0 } };
2338 2 : struct smb2_handle relhandle = { .data = { UINT64_MAX, UINT64_MAX } };
2339 0 : struct smb2_write w1;
2340 0 : struct smb2_write w2;
2341 2 : const char *fname = "compound_async_write_write";
2342 0 : struct smb2_request *req[2];
2343 0 : NTSTATUS status;
2344 2 : bool is_smbd = torture_setting_bool(tctx, "smbd", true);
2345 2 : bool ret = false;
2346 :
2347 : /* Start clean. */
2348 2 : smb2_util_unlink(tree, fname);
2349 :
2350 : /* Create a file. */
2351 2 : status = torture_smb2_testfile_access(tree,
2352 : fname,
2353 : &fhandle,
2354 : SEC_RIGHTS_FILE_ALL);
2355 2 : CHECK_STATUS(status, NT_STATUS_OK);
2356 :
2357 : /* Now do a compound write + write handle. */
2358 2 : smb2_transport_compound_start(tree->session->transport, 2);
2359 :
2360 2 : ZERO_STRUCT(w1);
2361 2 : w1.in.file.handle = fhandle;
2362 2 : w1.in.offset = 0;
2363 2 : w1.in.data = data_blob_talloc_zero(tctx, 64);
2364 2 : req[0] = smb2_write_send(tree, &w1);
2365 :
2366 2 : torture_assert_not_null_goto(tctx, req[0], ret, done,
2367 : "smb2_write_send (1) failed\n");
2368 :
2369 2 : smb2_transport_compound_set_related(tree->session->transport, true);
2370 :
2371 2 : ZERO_STRUCT(w2);
2372 2 : w2.in.file.handle = relhandle;
2373 2 : w2.in.offset = 64;
2374 2 : w2.in.data = data_blob_talloc_zero(tctx, 64);
2375 2 : req[1] = smb2_write_send(tree, &w2);
2376 :
2377 2 : torture_assert_not_null_goto(tctx, req[0], ret, done,
2378 : "smb2_write_send (2) failed\n");
2379 :
2380 2 : status = smb2_write_recv(req[0], &w1);
2381 2 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2382 : "smb2_write_recv (1) failed.");
2383 :
2384 2 : if (!is_smbd) {
2385 : /*
2386 : * Windows and other servers don't go async.
2387 : */
2388 0 : status = smb2_write_recv(req[1], &w2);
2389 : } else {
2390 : /*
2391 : * For smbd, the second write should go async
2392 : * as it's the last element of a compound.
2393 : */
2394 8 : WAIT_FOR_ASYNC_RESPONSE(req[1]);
2395 2 : CHECK_VALUE(req[1]->cancel.can_cancel, true);
2396 : /*
2397 : * Now pick up the real return.
2398 : */
2399 2 : status = smb2_write_recv(req[1], &w2);
2400 : }
2401 :
2402 2 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2403 : "smb2_write_recv (2) failed.");
2404 :
2405 2 : status = smb2_util_close(tree, fhandle);
2406 2 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2407 : "smb2_util_close failed.");
2408 2 : ZERO_STRUCT(fhandle);
2409 :
2410 2 : ret = true;
2411 :
2412 2 : done:
2413 :
2414 2 : if (fhandle.data[0] != 0) {
2415 0 : smb2_util_close(tree, fhandle);
2416 : }
2417 :
2418 2 : smb2_util_unlink(tree, fname);
2419 2 : return ret;
2420 : }
2421 :
2422 : /*
2423 : * For Samba/smbd this test must be run against the aio_delay_inject share
2424 : * as we need to ensure the last read in the compound takes longer than
2425 : * 500 us, which is the threshold for going async in smbd SMB2 reads.
2426 : */
2427 :
2428 2 : static bool test_compound_async_read_read(struct torture_context *tctx,
2429 : struct smb2_tree *tree)
2430 : {
2431 2 : struct smb2_handle fhandle = { .data = { 0, 0 } };
2432 2 : struct smb2_handle relhandle = { .data = { UINT64_MAX, UINT64_MAX } };
2433 0 : struct smb2_write w;
2434 0 : struct smb2_read r1;
2435 0 : struct smb2_read r2;
2436 2 : const char *fname = "compound_async_read_read";
2437 0 : struct smb2_request *req[2];
2438 0 : NTSTATUS status;
2439 2 : bool is_smbd = torture_setting_bool(tctx, "smbd", true);
2440 2 : bool ret = false;
2441 :
2442 : /* Start clean. */
2443 2 : smb2_util_unlink(tree, fname);
2444 :
2445 : /* Create a file. */
2446 2 : status = torture_smb2_testfile_access(tree,
2447 : fname,
2448 : &fhandle,
2449 : SEC_RIGHTS_FILE_ALL);
2450 2 : CHECK_STATUS(status, NT_STATUS_OK);
2451 :
2452 : /* Write 128 bytes. */
2453 2 : ZERO_STRUCT(w);
2454 2 : w.in.file.handle = fhandle;
2455 2 : w.in.offset = 0;
2456 2 : w.in.data = data_blob_talloc_zero(tctx, 128);
2457 2 : status = smb2_write(tree, &w);
2458 2 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2459 : "smb2_write_recv (1) failed.");
2460 :
2461 : /* Now do a compound read + read handle. */
2462 2 : smb2_transport_compound_start(tree->session->transport, 2);
2463 :
2464 2 : ZERO_STRUCT(r1);
2465 2 : r1.in.file.handle = fhandle;
2466 2 : r1.in.length = 64;
2467 2 : r1.in.offset = 0;
2468 2 : req[0] = smb2_read_send(tree, &r1);
2469 :
2470 2 : torture_assert_not_null_goto(tctx, req[0], ret, done,
2471 : "smb2_read_send (1) failed\n");
2472 :
2473 2 : smb2_transport_compound_set_related(tree->session->transport, true);
2474 :
2475 2 : ZERO_STRUCT(r2);
2476 2 : r2.in.file.handle = relhandle;
2477 2 : r2.in.length = 64;
2478 2 : r2.in.offset = 64;
2479 2 : req[1] = smb2_read_send(tree, &r2);
2480 :
2481 2 : torture_assert_not_null_goto(tctx, req[0], ret, done,
2482 : "smb2_read_send (2) failed\n");
2483 :
2484 2 : status = smb2_read_recv(req[0], tree, &r1);
2485 2 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2486 : "smb2_read_recv (1) failed.");
2487 :
2488 2 : if (!is_smbd) {
2489 : /*
2490 : * Windows and other servers don't go async.
2491 : */
2492 0 : status = smb2_read_recv(req[1], tree, &r2);
2493 : } else {
2494 : /*
2495 : * For smbd, the second write should go async
2496 : * as it's the last element of a compound.
2497 : */
2498 8 : WAIT_FOR_ASYNC_RESPONSE(req[1]);
2499 2 : CHECK_VALUE(req[1]->cancel.can_cancel, true);
2500 : /*
2501 : * Now pick up the real return.
2502 : */
2503 2 : status = smb2_read_recv(req[1], tree, &r2);
2504 : }
2505 :
2506 2 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2507 : "smb2_read_recv (2) failed.");
2508 :
2509 2 : status = smb2_util_close(tree, fhandle);
2510 2 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2511 : "smb2_util_close failed.");
2512 2 : ZERO_STRUCT(fhandle);
2513 :
2514 2 : ret = true;
2515 :
2516 2 : done:
2517 :
2518 2 : if (fhandle.data[0] != 0) {
2519 0 : smb2_util_close(tree, fhandle);
2520 : }
2521 :
2522 2 : smb2_util_unlink(tree, fname);
2523 2 : return ret;
2524 : }
2525 :
2526 :
2527 2354 : struct torture_suite *torture_smb2_compound_init(TALLOC_CTX *ctx)
2528 : {
2529 2354 : struct torture_suite *suite = torture_suite_create(ctx, "compound");
2530 :
2531 2354 : torture_suite_add_1smb2_test(suite, "related1", test_compound_related1);
2532 2354 : torture_suite_add_1smb2_test(suite, "related2", test_compound_related2);
2533 2354 : torture_suite_add_1smb2_test(suite, "related3",
2534 : test_compound_related3);
2535 2354 : torture_suite_add_1smb2_test(suite, "related4",
2536 : test_compound_related4);
2537 2354 : torture_suite_add_1smb2_test(suite, "related5",
2538 : test_compound_related5);
2539 2354 : torture_suite_add_1smb2_test(suite, "related6",
2540 : test_compound_related6);
2541 2354 : torture_suite_add_1smb2_test(suite, "related7",
2542 : test_compound_related7);
2543 2354 : torture_suite_add_1smb2_test(suite, "related8",
2544 : test_compound_related8);
2545 2354 : torture_suite_add_1smb2_test(suite, "related9",
2546 : test_compound_related9);
2547 2354 : torture_suite_add_1smb2_test(suite, "unrelated1", test_compound_unrelated1);
2548 2354 : torture_suite_add_1smb2_test(suite, "invalid1", test_compound_invalid1);
2549 2354 : torture_suite_add_1smb2_test(suite, "invalid2", test_compound_invalid2);
2550 2354 : torture_suite_add_1smb2_test(suite, "invalid3", test_compound_invalid3);
2551 2354 : torture_suite_add_1smb2_test(
2552 : suite, "invalid4", test_compound_invalid4);
2553 2354 : torture_suite_add_1smb2_test(suite, "interim1", test_compound_interim1);
2554 2354 : torture_suite_add_1smb2_test(suite, "interim2", test_compound_interim2);
2555 2354 : torture_suite_add_1smb2_test(suite, "compound-break", test_compound_break);
2556 2354 : torture_suite_add_1smb2_test(suite, "compound-padding", test_compound_padding);
2557 2354 : torture_suite_add_1smb2_test(suite, "create-write-close",
2558 : test_compound_create_write_close);
2559 :
2560 2354 : suite->description = talloc_strdup(suite, "SMB2-COMPOUND tests");
2561 :
2562 2354 : return suite;
2563 : }
2564 :
2565 2354 : struct torture_suite *torture_smb2_compound_find_init(TALLOC_CTX *ctx)
2566 : {
2567 2354 : struct torture_suite *suite = torture_suite_create(ctx, "compound_find");
2568 :
2569 2354 : torture_suite_add_1smb2_test(suite, "compound_find_related", test_compound_find_related);
2570 2354 : torture_suite_add_1smb2_test(suite, "compound_find_unrelated", test_compound_find_unrelated);
2571 2354 : torture_suite_add_1smb2_test(suite, "compound_find_close", test_compound_find_close);
2572 :
2573 2354 : suite->description = talloc_strdup(suite, "SMB2-COMPOUND-FIND tests");
2574 :
2575 2354 : return suite;
2576 : }
2577 :
2578 2354 : struct torture_suite *torture_smb2_compound_async_init(TALLOC_CTX *ctx)
2579 : {
2580 2354 : struct torture_suite *suite = torture_suite_create(ctx,
2581 : "compound_async");
2582 :
2583 2354 : torture_suite_add_1smb2_test(suite, "flush_close",
2584 : test_compound_async_flush_close);
2585 2354 : torture_suite_add_1smb2_test(suite, "flush_flush",
2586 : test_compound_async_flush_flush);
2587 2354 : torture_suite_add_1smb2_test(suite, "write_write",
2588 : test_compound_async_write_write);
2589 2354 : torture_suite_add_1smb2_test(suite, "read_read",
2590 : test_compound_async_read_read);
2591 :
2592 2354 : suite->description = talloc_strdup(suite, "SMB2-COMPOUND-ASYNC tests");
2593 :
2594 2354 : return suite;
2595 : }
|