Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 :
4 : test alternate data streams
5 :
6 : Copyright (C) Andrew Tridgell 2004
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 :
26 : #include "smb_constants.h"
27 : #include "torture/torture.h"
28 : #include "torture/smb2/proto.h"
29 :
30 : #include "system/filesys.h"
31 : #include "system/locale.h"
32 : #include "lib/util/tsort.h"
33 :
34 : #define DNAME "teststreams"
35 :
36 : #define CHECK_STATUS(status, correct) do { \
37 : if (!NT_STATUS_EQUAL(status, correct)) { \
38 : torture_result(tctx, TORTURE_FAIL, \
39 : "(%s) Incorrect status %s - should be %s\n", \
40 : __location__, nt_errstr(status), nt_errstr(correct)); \
41 : ret = false; \
42 : goto done; \
43 : }} while (0)
44 :
45 : #define CHECK_VALUE(v, correct) do { \
46 : if ((v) != (correct)) { \
47 : torture_result(tctx, TORTURE_FAIL, \
48 : "(%s) Incorrect value %s=%d - should be %d\n", \
49 : __location__, #v, (int)v, (int)correct); \
50 : ret = false; \
51 : }} while (0)
52 :
53 : #define CHECK_NTTIME(v, correct) do { \
54 : if ((v) != (correct)) { \
55 : torture_result(tctx, TORTURE_FAIL, \
56 : "(%s) Incorrect value %s=%llu - should be %llu\n", \
57 : __location__, #v, (unsigned long long)v, \
58 : (unsigned long long)correct); \
59 : ret = false; \
60 : }} while (0)
61 :
62 : #define CHECK_STR(v, correct) do { \
63 : bool ok; \
64 : if ((v) && !(correct)) { \
65 : ok = false; \
66 : } else if (!(v) && (correct)) { \
67 : ok = false; \
68 : } else if (!(v) && !(correct)) { \
69 : ok = true; \
70 : } else if (strcmp((v), (correct)) == 0) { \
71 : ok = true; \
72 : } else { \
73 : ok = false; \
74 : } \
75 : if (!ok) { \
76 : torture_result(tctx, TORTURE_FAIL, \
77 : "(%s) Incorrect value %s='%s' - " \
78 : "should be '%s'\n", \
79 : __location__, #v, (v)?(v):"NULL", \
80 : (correct)?(correct):"NULL"); \
81 : ret = false; \
82 : }} while (0)
83 :
84 :
85 104 : static int qsort_string(char * const *s1, char * const *s2)
86 : {
87 104 : return strcmp(*s1, *s2);
88 : }
89 :
90 112 : static int qsort_stream(const struct stream_struct * s1, const struct stream_struct *s2)
91 : {
92 112 : return strcmp(s1->stream_name.s, s2->stream_name.s);
93 : }
94 :
95 99 : static bool check_stream(struct torture_context *tctx,
96 : struct smb2_tree *tree,
97 : const char *location,
98 : TALLOC_CTX *mem_ctx,
99 : const char *fname,
100 : const char *sname,
101 : const char *value)
102 : {
103 0 : struct smb2_handle handle;
104 0 : struct smb2_create create;
105 0 : struct smb2_read r;
106 0 : NTSTATUS status;
107 0 : const char *full_name;
108 :
109 99 : full_name = talloc_asprintf(mem_ctx, "%s:%s", fname, sname);
110 :
111 99 : ZERO_STRUCT(create);
112 99 : create.in.desired_access = SEC_RIGHTS_FILE_ALL;
113 99 : create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
114 99 : create.in.create_disposition = NTCREATEX_DISP_OPEN;
115 99 : create.in.fname = full_name;
116 :
117 99 : status = smb2_create(tree, mem_ctx, &create);
118 99 : if (!NT_STATUS_IS_OK(status)) {
119 45 : if (value == NULL) {
120 45 : return true;
121 : } else {
122 0 : torture_comment(tctx, "Unable to open stream %s\n",
123 : full_name);
124 0 : return false;
125 : }
126 : }
127 :
128 54 : handle = create.out.file.handle;
129 54 : if (value == NULL) {
130 0 : return true;
131 : }
132 :
133 :
134 54 : ZERO_STRUCT(r);
135 54 : r.in.file.handle = handle;
136 54 : r.in.length = strlen(value)+11;
137 54 : r.in.offset = 0;
138 :
139 54 : status = smb2_read(tree, tree, &r);
140 :
141 54 : if (!NT_STATUS_IS_OK(status)) {
142 0 : torture_comment(tctx, "(%s) Failed to read %lu bytes from "
143 0 : "stream '%s'\n", location, (long)strlen(value), full_name);
144 0 : return false;
145 : }
146 :
147 54 : if (memcmp(r.out.data.data, value, strlen(value)) != 0) {
148 0 : torture_comment(tctx, "(%s) Bad data in stream\n", location);
149 0 : return false;
150 : }
151 :
152 54 : smb2_util_close(tree, handle);
153 54 : return true;
154 : }
155 :
156 105 : static bool check_stream_list(struct smb2_tree *tree,
157 : struct torture_context *tctx,
158 : const char *fname,
159 : unsigned int num_exp,
160 : const char **exp,
161 : struct smb2_handle h)
162 : {
163 0 : union smb_fileinfo finfo;
164 0 : NTSTATUS status;
165 0 : unsigned int i;
166 105 : TALLOC_CTX *tmp_ctx = talloc_new(tctx);
167 0 : char **exp_sort;
168 0 : struct stream_struct *stream_sort;
169 105 : bool ret = false;
170 :
171 105 : finfo.generic.level = RAW_FILEINFO_STREAM_INFORMATION;
172 105 : finfo.generic.in.file.handle = h;
173 :
174 105 : status = smb2_getinfo_file(tree, tctx, &finfo);
175 105 : if (!NT_STATUS_IS_OK(status)) {
176 24 : torture_comment(tctx, "(%s) smb_raw_pathinfo failed: %s\n",
177 : __location__, nt_errstr(status));
178 24 : goto fail;
179 : }
180 :
181 81 : if (finfo.stream_info.out.num_streams != num_exp) {
182 1 : torture_comment(tctx, "(%s) expected %d streams, got %d\n",
183 : __location__, num_exp, finfo.stream_info.out.num_streams);
184 1 : goto fail;
185 : }
186 :
187 80 : if (num_exp == 0) {
188 9 : ret = true;
189 9 : goto fail;
190 : }
191 :
192 71 : exp_sort = talloc_memdup(tmp_ctx, exp, num_exp * sizeof(*exp));
193 :
194 71 : if (exp_sort == NULL) {
195 0 : goto fail;
196 : }
197 :
198 71 : TYPESAFE_QSORT(exp_sort, num_exp, qsort_string);
199 :
200 71 : stream_sort = talloc_memdup(tmp_ctx, finfo.stream_info.out.streams,
201 : finfo.stream_info.out.num_streams *
202 : sizeof(*stream_sort));
203 :
204 71 : if (stream_sort == NULL) {
205 0 : goto fail;
206 : }
207 :
208 71 : TYPESAFE_QSORT(stream_sort, finfo.stream_info.out.num_streams, qsort_stream);
209 :
210 199 : for (i=0; i<num_exp; i++) {
211 128 : if (strcmp(exp_sort[i], stream_sort[i].stream_name.s) != 0) {
212 0 : torture_comment(tctx,
213 : "(%s) expected stream name %s, got %s\n",
214 0 : __location__, exp_sort[i],
215 0 : stream_sort[i].stream_name.s);
216 0 : goto fail;
217 : }
218 : }
219 :
220 71 : ret = true;
221 105 : fail:
222 105 : talloc_free(tmp_ctx);
223 105 : return ret;
224 : }
225 :
226 :
227 9 : static bool test_stream_dir(struct torture_context *tctx,
228 : struct smb2_tree *tree)
229 : {
230 9 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
231 0 : NTSTATUS status;
232 0 : union smb_open io;
233 9 : const char *fname = DNAME "\\stream.txt";
234 0 : const char *sname1;
235 9 : bool ret = true;
236 0 : const char *basedir_data;
237 0 : struct smb2_handle h;
238 :
239 9 : smb2_util_unlink(tree, fname);
240 9 : smb2_deltree(tree, DNAME);
241 :
242 9 : status = torture_smb2_testdir(tree, DNAME, &h);
243 9 : CHECK_STATUS(status, NT_STATUS_OK);
244 :
245 9 : basedir_data = talloc_asprintf(mem_ctx, "%s::$DATA", DNAME);
246 9 : sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname, "Stream One");
247 9 : torture_comment(tctx, "%s\n", sname1);
248 :
249 9 : torture_comment(tctx, "(%s) opening non-existent directory stream\n",
250 : __location__);
251 9 : ZERO_STRUCT(io.smb2);
252 9 : io.smb2.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
253 9 : io.smb2.in.desired_access = SEC_FILE_WRITE_DATA;
254 9 : io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
255 9 : io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
256 9 : io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
257 9 : io.smb2.in.share_access = 0;
258 9 : io.smb2.in.alloc_size = 0;
259 9 : io.smb2.in.security_flags = 0;
260 9 : io.smb2.in.fname = sname1;
261 9 : io.smb2.in.create_flags = 0;
262 9 : status = smb2_create(tree, mem_ctx, &(io.smb2));
263 9 : CHECK_STATUS(status, NT_STATUS_NOT_A_DIRECTORY);
264 :
265 9 : torture_comment(tctx, "(%s) opening basedir stream\n", __location__);
266 9 : ZERO_STRUCT(io.smb2);
267 9 : io.smb2.in.create_flags = 0;
268 9 : io.smb2.in.desired_access = SEC_FILE_WRITE_DATA;
269 9 : io.smb2.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
270 9 : io.smb2.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
271 9 : io.smb2.in.share_access = 0;
272 9 : io.smb2.in.alloc_size = 0;
273 9 : io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
274 9 : io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
275 9 : io.smb2.in.security_flags = 0;
276 9 : io.smb2.in.fname = basedir_data;
277 9 : status = smb2_create(tree, mem_ctx, &(io.smb2));
278 9 : CHECK_STATUS(status, NT_STATUS_NOT_A_DIRECTORY);
279 :
280 9 : torture_comment(tctx, "(%s) opening basedir ::$DATA stream\n",
281 : __location__);
282 9 : ZERO_STRUCT(io.smb2);
283 9 : io.smb2.in.create_flags = 0x10;
284 9 : io.smb2.in.desired_access = SEC_FILE_WRITE_DATA;
285 9 : io.smb2.in.create_options = 0;
286 9 : io.smb2.in.file_attributes = 0;
287 9 : io.smb2.in.share_access = 0;
288 9 : io.smb2.in.alloc_size = 0;
289 9 : io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
290 9 : io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
291 9 : io.smb2.in.security_flags = 0;
292 9 : io.smb2.in.fname = basedir_data;
293 9 : status = smb2_create(tree, mem_ctx, &(io.smb2));
294 9 : CHECK_STATUS(status, NT_STATUS_FILE_IS_A_DIRECTORY);
295 :
296 9 : torture_comment(tctx, "(%s) list the streams on the basedir\n",
297 : __location__);
298 9 : ret &= check_stream_list(tree, mem_ctx, DNAME, 0, NULL, h);
299 9 : done:
300 9 : smb2_util_unlink(tree, fname);
301 9 : smb2_deltree(tree, DNAME);
302 9 : talloc_free(mem_ctx);
303 :
304 9 : return ret;
305 : }
306 :
307 9 : static bool test_stream_io(struct torture_context *tctx,
308 : struct smb2_tree *tree)
309 : {
310 9 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
311 0 : NTSTATUS status;
312 0 : union smb_open io;
313 9 : const char *fname = DNAME "\\stream.txt";
314 0 : const char *sname1, *sname2;
315 9 : bool ret = true;
316 0 : struct smb2_handle h, h2;
317 :
318 9 : const char *one[] = { "::$DATA" };
319 9 : const char *two[] = { "::$DATA", ":Second Stream:$DATA" };
320 9 : const char *three[] = { "::$DATA", ":Stream One:$DATA",
321 : ":Second Stream:$DATA" };
322 :
323 9 : ZERO_STRUCT(h);
324 9 : ZERO_STRUCT(h2);
325 :
326 9 : sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname, "Stream One");
327 9 : sname2 = talloc_asprintf(mem_ctx, "%s:%s:$DaTa", fname,
328 : "Second Stream");
329 :
330 9 : smb2_util_unlink(tree, fname);
331 9 : smb2_deltree(tree, DNAME);
332 :
333 9 : status = torture_smb2_testdir(tree, DNAME, &h);
334 9 : CHECK_STATUS(status, NT_STATUS_OK);
335 :
336 9 : torture_comment(tctx, "(%s) creating a stream on a non-existent file\n",
337 : __location__);
338 :
339 9 : ZERO_STRUCT(io.smb2);
340 9 : io.smb2.in.create_flags = 0;
341 9 : io.smb2.in.desired_access = SEC_FILE_WRITE_DATA;
342 9 : io.smb2.in.create_options = 0;
343 9 : io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
344 9 : io.smb2.in.share_access = 0;
345 9 : io.smb2.in.alloc_size = 0;
346 9 : io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
347 9 : io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
348 9 : io.smb2.in.security_flags = 0;
349 9 : io.smb2.in.fname = sname1;
350 9 : status = smb2_create(tree, mem_ctx, &(io.smb2));
351 9 : CHECK_STATUS(status, NT_STATUS_OK);
352 9 : h2 = io.smb2.out.file.handle;
353 :
354 9 : ret &= check_stream(tctx, tree, __location__, mem_ctx, fname,
355 : "Stream One", NULL);
356 :
357 9 : torture_comment(tctx, "(%s) check that open of base file is allowed\n", __location__);
358 9 : io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
359 9 : io.smb2.in.fname = fname;
360 9 : status = smb2_create(tree, mem_ctx, &(io.smb2));
361 9 : CHECK_STATUS(status, NT_STATUS_OK);
362 9 : smb2_util_close(tree, io.smb2.out.file.handle);
363 :
364 9 : torture_comment(tctx, "(%s) writing to stream\n", __location__);
365 9 : status = smb2_util_write(tree, h2, "test data", 0, 9);
366 9 : CHECK_STATUS(status, NT_STATUS_OK);
367 :
368 9 : smb2_util_close(tree, h2);
369 :
370 9 : ret &= check_stream(tctx, tree, __location__, mem_ctx, fname,
371 : "Stream One", "test data");
372 :
373 9 : io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
374 9 : io.smb2.in.fname = sname1;
375 9 : status = smb2_create(tree, mem_ctx, &(io.smb2));
376 9 : CHECK_STATUS(status, NT_STATUS_OK);
377 9 : h2 = io.smb2.out.file.handle;
378 :
379 9 : torture_comment(tctx, "(%s) modifying stream\n", __location__);
380 9 : status = smb2_util_write(tree, h2, "MORE DATA ", 5, 10);
381 9 : CHECK_STATUS(status, NT_STATUS_OK);
382 :
383 9 : smb2_util_close(tree, h2);
384 :
385 9 : ret &= check_stream(tctx, tree, __location__, mem_ctx, fname,
386 : "Stream One:$FOO", NULL);
387 :
388 9 : torture_comment(tctx, "(%s) creating a stream2 on a existing file\n",
389 : __location__);
390 9 : io.smb2.in.fname = sname2;
391 9 : io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
392 9 : status = smb2_create(tree, mem_ctx, &(io.smb2));
393 9 : CHECK_STATUS(status, NT_STATUS_OK);
394 9 : h2 = io.smb2.out.file.handle;
395 :
396 9 : torture_comment(tctx, "(%s) modifying stream\n", __location__);
397 9 : status= smb2_util_write(tree, h2, "SECOND STREAM", 0, 13);
398 9 : CHECK_STATUS(status, NT_STATUS_OK);
399 9 : smb2_util_close(tree, h2);
400 :
401 9 : ret &= check_stream(tctx, tree, __location__, mem_ctx, fname,
402 : "Stream One", "test MORE DATA ");
403 :
404 9 : ret &= check_stream(tctx, tree, __location__, mem_ctx, fname,
405 : "Stream One:$DATA", "test MORE DATA ");
406 :
407 9 : ret &= check_stream(tctx, tree, __location__, mem_ctx, fname,
408 : "Stream One:", NULL);
409 :
410 9 : if (!ret) {
411 0 : torture_result(tctx, TORTURE_FAIL,
412 : "check_stream(\"Stream One:*\") failed\n");
413 0 : goto done;
414 : }
415 :
416 9 : ret &= check_stream(tctx, tree, __location__, mem_ctx, fname,
417 : "Second Stream", "SECOND STREAM");
418 :
419 9 : ret &= check_stream(tctx, tree, __location__, mem_ctx, fname,
420 : "SECOND STREAM:$DATA", "SECOND STREAM");
421 9 : ret &= check_stream(tctx, tree, __location__, mem_ctx, fname,
422 : "Second Stream:$DATA", "SECOND STREAM");
423 :
424 9 : ret &= check_stream(tctx, tree, __location__, mem_ctx, fname,
425 : "Second Stream:", NULL);
426 :
427 9 : ret &= check_stream(tctx, tree, __location__, mem_ctx, fname,
428 : "Second Stream:$FOO", NULL);
429 :
430 9 : if (!ret) {
431 0 : torture_result(tctx, TORTURE_FAIL,
432 : "check_stream(\"Second Stream:*\") failed\n");
433 0 : goto done;
434 : }
435 :
436 9 : io.smb2.in.fname = sname2;
437 9 : io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
438 9 : status = smb2_create(tree, mem_ctx, &(io.smb2));
439 9 : CHECK_STATUS(status, NT_STATUS_OK);
440 9 : h2 = io.smb2.out.file.handle;
441 9 : check_stream_list(tree, tctx, fname, 3, three, h2);
442 :
443 9 : smb2_util_close(tree, h2);
444 :
445 9 : torture_comment(tctx, "(%s) deleting stream\n", __location__);
446 9 : status = smb2_util_unlink(tree, sname1);
447 9 : CHECK_STATUS(status, NT_STATUS_OK);
448 :
449 9 : io.smb2.in.fname = sname2;
450 9 : io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
451 9 : status = smb2_create(tree, mem_ctx, &(io.smb2));
452 9 : CHECK_STATUS(status, NT_STATUS_OK);
453 9 : h2 = io.smb2.out.file.handle;
454 9 : check_stream_list(tree, tctx, fname, 2, two, h2);
455 9 : smb2_util_close(tree, h2);
456 :
457 9 : torture_comment(tctx, "(%s) delete a stream via delete-on-close\n",
458 : __location__);
459 9 : io.smb2.in.fname = sname2;
460 9 : io.smb2.in.create_options = NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
461 9 : io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE;
462 9 : io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL;
463 9 : io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
464 :
465 9 : status = smb2_create(tree, mem_ctx, &(io.smb2));
466 9 : CHECK_STATUS(status, NT_STATUS_OK);
467 9 : h2 = io.smb2.out.file.handle;
468 :
469 9 : smb2_util_close(tree, h2);
470 9 : status = smb2_util_unlink(tree, sname2);
471 9 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
472 :
473 9 : io.smb2.in.fname = fname;
474 9 : io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
475 9 : status = smb2_create(tree, mem_ctx, &(io.smb2));
476 9 : h2 = io.smb2.out.file.handle;
477 9 : check_stream_list(tree,tctx, fname, 1, one, h2);
478 9 : smb2_util_close(tree, h2);
479 :
480 9 : if (!torture_setting_bool(tctx, "samba4", false)) {
481 8 : io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
482 8 : io.smb2.in.fname = sname1;
483 8 : status = smb2_create(tree, mem_ctx, &(io.smb2));
484 8 : CHECK_STATUS(status, NT_STATUS_OK);
485 8 : smb2_util_close(tree, io.ntcreatex.out.file.handle);
486 8 : io.smb2.in.fname = sname2;
487 8 : status = smb2_create(tree, mem_ctx, &(io.smb2));
488 8 : CHECK_STATUS(status, NT_STATUS_OK);
489 8 : smb2_util_close(tree, io.ntcreatex.out.file.handle);
490 8 : torture_comment(tctx, "(%s) deleting file\n", __location__);
491 8 : status = smb2_util_unlink(tree, fname);
492 8 : CHECK_STATUS(status, NT_STATUS_OK);
493 : }
494 :
495 :
496 1 : done:
497 9 : smb2_util_close(tree, h2);
498 9 : smb2_deltree(tree, DNAME);
499 9 : talloc_free(mem_ctx);
500 :
501 9 : return ret;
502 : }
503 :
504 9 : static bool test_zero_byte_stream(struct torture_context *tctx,
505 : struct smb2_tree *tree)
506 : {
507 9 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
508 0 : NTSTATUS status;
509 0 : union smb_open io;
510 9 : const char *fname = DNAME "\\stream.txt";
511 0 : const char *sname;
512 9 : bool ret = true;
513 0 : struct smb2_handle h, bh;
514 9 : const char *streams[] = { "::$DATA", ":foo:$DATA" };
515 :
516 9 : sname = talloc_asprintf(mem_ctx, "%s:%s", fname, "foo");
517 :
518 9 : smb2_util_unlink(tree, fname);
519 9 : smb2_deltree(tree, DNAME);
520 :
521 9 : status = torture_smb2_testdir(tree, DNAME, &h);
522 9 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "testdir");
523 9 : smb2_util_close(tree, h);
524 :
525 9 : torture_comment(tctx, "(%s) Check 0 byte named stream\n",
526 : __location__);
527 :
528 : /* Create basefile */
529 9 : ZERO_STRUCT(io);
530 9 : io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
531 9 : io.smb2.in.fname = fname;
532 9 : io.smb2.in.desired_access = SEC_FILE_READ_ATTRIBUTE |
533 : SEC_FILE_WRITE_ATTRIBUTE |
534 : SEC_FILE_READ_DATA |
535 : SEC_FILE_WRITE_DATA;
536 9 : status = smb2_create(tree, mem_ctx, &(io.smb2));
537 9 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "create");
538 9 : smb2_util_close(tree, io.smb2.out.file.handle);
539 :
540 : /* Create named stream and close it */
541 9 : ZERO_STRUCT(io);
542 9 : io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
543 9 : io.smb2.in.fname = sname;
544 9 : io.smb2.in.desired_access = SEC_FILE_READ_ATTRIBUTE |
545 : SEC_FILE_WRITE_ATTRIBUTE |
546 : SEC_FILE_READ_DATA |
547 : SEC_FILE_WRITE_DATA;
548 9 : status = smb2_create(tree, mem_ctx, &(io.smb2));
549 9 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "create");
550 9 : smb2_util_close(tree, io.smb2.out.file.handle);
551 :
552 : /*
553 : * Check stream list, the 0 byte stream MUST be returned by
554 : * the server.
555 : */
556 9 : ZERO_STRUCT(io);
557 9 : io.smb2.in.fname = fname;
558 9 : io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
559 9 : io.smb2.in.desired_access = SEC_FILE_READ_ATTRIBUTE |
560 : SEC_FILE_WRITE_ATTRIBUTE |
561 : SEC_FILE_READ_DATA |
562 : SEC_FILE_WRITE_DATA;
563 9 : status = smb2_create(tree, mem_ctx, &(io.smb2));
564 9 : bh = io.smb2.out.file.handle;
565 :
566 9 : ret = check_stream_list(tree,tctx, fname, 2, streams, bh);
567 9 : torture_assert_goto(tctx, ret == true, ret, done, "smb2_create");
568 9 : smb2_util_close(tree, bh);
569 :
570 9 : done:
571 9 : smb2_deltree(tree, DNAME);
572 9 : talloc_free(mem_ctx);
573 :
574 9 : return ret;
575 : }
576 :
577 : /*
578 : test stream sharemodes
579 : */
580 9 : static bool test_stream_sharemodes(struct torture_context *tctx,
581 : struct smb2_tree *tree)
582 : {
583 9 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
584 0 : NTSTATUS status;
585 0 : union smb_open io;
586 9 : const char *fname = DNAME "\\stream_share.txt";
587 0 : const char *sname1, *sname2;
588 9 : bool ret = true;
589 0 : struct smb2_handle h, h1, h2;
590 :
591 9 : ZERO_STRUCT(h);
592 9 : ZERO_STRUCT(h1);
593 9 : ZERO_STRUCT(h2);
594 :
595 9 : sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname, "Stream One");
596 9 : sname2 = talloc_asprintf(mem_ctx, "%s:%s:$DaTa", fname,
597 : "Second Stream");
598 :
599 9 : smb2_util_unlink(tree, fname);
600 9 : smb2_deltree(tree, DNAME);
601 :
602 9 : status = torture_smb2_testdir(tree, DNAME, &h);
603 9 : CHECK_STATUS(status, NT_STATUS_OK);
604 :
605 9 : torture_comment(tctx, "(%s) Testing stream share mode conflicts\n",
606 : __location__);
607 9 : ZERO_STRUCT(io.smb2);
608 9 : io.generic.level = RAW_OPEN_SMB2;
609 9 : io.smb2.in.create_flags = 0;
610 9 : io.smb2.in.desired_access = SEC_FILE_WRITE_DATA;
611 9 : io.smb2.in.create_options = 0;
612 9 : io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
613 9 : io.smb2.in.share_access = 0;
614 9 : io.smb2.in.alloc_size = 0;
615 9 : io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
616 9 : io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
617 9 : io.smb2.in.security_flags = 0;
618 9 : io.smb2.in.fname = sname1;
619 :
620 9 : status = smb2_create(tree, mem_ctx, &(io.smb2));
621 9 : CHECK_STATUS(status, NT_STATUS_OK);
622 9 : h1 = io.smb2.out.file.handle;
623 :
624 : /*
625 : * A different stream does not give a sharing violation
626 : */
627 :
628 9 : io.smb2.in.fname = sname2;
629 9 : status = smb2_create(tree, mem_ctx, &(io.smb2));
630 9 : CHECK_STATUS(status, NT_STATUS_OK);
631 9 : h2 = io.smb2.out.file.handle;
632 :
633 : /*
634 : * ... whereas the same stream does with unchanged access/share_access
635 : * flags
636 : */
637 :
638 9 : io.smb2.in.fname = sname1;
639 9 : io.smb2.in.create_disposition = 0;
640 9 : status = smb2_create(tree, mem_ctx, &(io.smb2));
641 9 : CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
642 :
643 9 : io.smb2.in.fname = sname2;
644 9 : status = smb2_create(tree, mem_ctx, &(io.smb2));
645 9 : CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
646 :
647 9 : done:
648 9 : smb2_util_close(tree, h1);
649 9 : smb2_util_close(tree, h2);
650 9 : status = smb2_util_unlink(tree, fname);
651 9 : smb2_deltree(tree, DNAME);
652 9 : talloc_free(mem_ctx);
653 :
654 9 : return ret;
655 : }
656 :
657 : /*
658 : * Test FILE_SHARE_DELETE on streams
659 : *
660 : * A stream opened with !FILE_SHARE_DELETE prevents the main file to be opened
661 : * with SEC_STD_DELETE.
662 : *
663 : * The main file opened with !FILE_SHARE_DELETE does *not* prevent a stream to
664 : * be opened with SEC_STD_DELETE.
665 : *
666 : * A stream held open with FILE_SHARE_DELETE allows the file to be
667 : * deleted. After the main file is deleted, access to the open file descriptor
668 : * still works, but all name-based access to both the main file as well as the
669 : * stream is denied with DELETE pending.
670 : *
671 : * This means, an open of the main file with SEC_STD_DELETE should walk all
672 : * streams and also open them with SEC_STD_DELETE. If any of these opens gives
673 : * SHARING_VIOLATION, the main open fails.
674 : *
675 : * Closing the main file after delete_on_close has been set does not really
676 : * unlink it but leaves the corresponding share mode entry with
677 : * delete_on_close being set around until all streams are closed.
678 : *
679 : * Opening a stream must also look at the main file's share mode entry, look
680 : * at the delete_on_close bit and potentially return DELETE_PENDING.
681 : */
682 :
683 9 : static bool test_stream_delete(struct torture_context *tctx,
684 : struct smb2_tree *tree)
685 : {
686 9 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
687 0 : NTSTATUS status;
688 0 : union smb_open io;
689 9 : const char *fname = DNAME "\\stream_delete.txt";
690 0 : const char *sname1;
691 9 : bool ret = true;
692 9 : struct smb2_handle h = {{0}};
693 9 : struct smb2_handle h1 = {{0}};
694 0 : struct smb2_read r;
695 :
696 9 : if (torture_setting_bool(tctx, "samba4", false)) {
697 1 : torture_comment(tctx, "Skipping test as samba4 is enabled\n");
698 1 : goto done;
699 : }
700 :
701 8 : ZERO_STRUCT(h);
702 8 : ZERO_STRUCT(h1);
703 :
704 8 : sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname, "Stream One");
705 :
706 : /* clean slate .. */
707 8 : smb2_util_unlink(tree, fname);
708 8 : smb2_deltree(tree, fname);
709 8 : smb2_deltree(tree, DNAME);
710 :
711 8 : status = torture_smb2_testdir(tree, DNAME, &h);
712 8 : CHECK_STATUS(status, NT_STATUS_OK);
713 :
714 8 : torture_comment(tctx, "(%s) opening non-existent file stream\n",
715 : __location__);
716 8 : ZERO_STRUCT(io.smb2);
717 8 : io.smb2.in.create_flags = 0;
718 8 : io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL;
719 8 : io.smb2.in.create_options = 0;
720 8 : io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
721 8 : io.smb2.in.share_access = 0;
722 8 : io.smb2.in.alloc_size = 0;
723 8 : io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
724 8 : io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
725 8 : io.smb2.in.security_flags = 0;
726 8 : io.smb2.in.fname = sname1;
727 :
728 8 : status = smb2_create(tree, mem_ctx, &(io.smb2));
729 8 : CHECK_STATUS(status, NT_STATUS_OK);
730 8 : h1 = io.smb2.out.file.handle;
731 :
732 8 : status = smb2_util_write(tree, h1, "test data", 0, 9);
733 8 : CHECK_STATUS(status, NT_STATUS_OK);
734 :
735 : /*
736 : * One stream opened without FILE_SHARE_DELETE prevents the main file
737 : * to be deleted or even opened with DELETE access
738 : */
739 :
740 8 : status = smb2_util_unlink(tree, fname);
741 8 : CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
742 :
743 8 : io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
744 8 : io.smb2.in.fname = fname;
745 8 : io.smb2.in.desired_access = SEC_STD_DELETE;
746 8 : status = smb2_create(tree, mem_ctx, &(io.smb2));
747 8 : CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
748 :
749 8 : smb2_util_close(tree, h1);
750 :
751 : /*
752 : * ... but unlink works if a stream is opened with FILE_SHARE_DELETE
753 : */
754 :
755 8 : io.smb2.in.fname = sname1;
756 8 : io.smb2.in.desired_access = SEC_FILE_READ_DATA|SEC_FILE_WRITE_DATA;
757 8 : io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE |
758 : NTCREATEX_SHARE_ACCESS_READ |
759 : NTCREATEX_SHARE_ACCESS_WRITE;
760 8 : status = smb2_create(tree, mem_ctx, &(io.smb2));
761 8 : CHECK_STATUS(status, NT_STATUS_OK);
762 8 : h1 = io.smb2.out.file.handle;
763 :
764 8 : status = smb2_util_unlink(tree, fname);
765 8 : CHECK_STATUS(status, NT_STATUS_OK);
766 :
767 : /*
768 : * file access still works on the stream while the main file is closed
769 : */
770 8 : ZERO_STRUCT(r);
771 8 : r.in.file.handle = h1;
772 8 : r.in.length = 9;
773 8 : r.in.offset = 0;
774 :
775 8 : status = smb2_read(tree, tree, &r);
776 8 : CHECK_STATUS(status, NT_STATUS_OK);
777 :
778 : /*
779 : * name-based access to both the main file and the stream does not
780 : * work anymore but gives DELETE_PENDING
781 : */
782 :
783 8 : io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
784 8 : io.smb2.in.fname = fname;
785 8 : status = smb2_create(tree, mem_ctx, &(io.smb2));
786 8 : CHECK_STATUS(status, NT_STATUS_DELETE_PENDING);
787 :
788 : /*
789 : * older S3 doesn't do this
790 : */
791 :
792 8 : io.smb2.in.fname = sname1;
793 8 : status = smb2_create(tree, mem_ctx, &(io.smb2));
794 8 : CHECK_STATUS(status, NT_STATUS_DELETE_PENDING);
795 :
796 8 : smb2_util_close(tree, h1);
797 8 : ZERO_STRUCT(h1);
798 :
799 : /*
800 : * After closing the stream the file is really gone.
801 : */
802 :
803 8 : io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
804 8 : io.smb2.in.fname = fname;
805 8 : status = smb2_create(tree, mem_ctx, &(io.smb2));
806 8 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
807 :
808 9 : done:
809 9 : if (!smb2_util_handle_empty(h1)) {
810 0 : smb2_util_close(tree, h1);
811 : }
812 9 : smb2_util_unlink(tree, fname);
813 9 : smb2_deltree(tree, DNAME);
814 9 : talloc_free(mem_ctx);
815 :
816 9 : return ret;
817 : }
818 :
819 : /*
820 : test stream names
821 : */
822 9 : static bool test_stream_names(struct torture_context *tctx,
823 : struct smb2_tree *tree)
824 : {
825 9 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
826 0 : NTSTATUS status;
827 0 : union smb_open io;
828 0 : union smb_fileinfo finfo;
829 0 : union smb_fileinfo stinfo;
830 0 : union smb_setfileinfo sinfo;
831 9 : const char *fname = DNAME "\\stream_names.txt";
832 0 : const char *sname1, *sname1b, *sname1c, *sname1d;
833 0 : const char *sname2, *snamew, *snamew2;
834 0 : const char *snamer1;
835 9 : bool ret = true;
836 0 : struct smb2_handle h, h1, h2, h3;
837 0 : int i;
838 9 : const char *four[4] = {
839 : "::$DATA",
840 : ":\x05Stream\n One:$DATA",
841 : ":MStream Two:$DATA",
842 : ":?Stream*:$DATA"
843 : };
844 9 : const char *five1[5] = {
845 : "::$DATA",
846 : ":\x05Stream\n One:$DATA",
847 : ":BeforeRename:$DATA",
848 : ":MStream Two:$DATA",
849 : ":?Stream*:$DATA"
850 : };
851 9 : const char *five2[5] = {
852 : "::$DATA",
853 : ":\x05Stream\n One:$DATA",
854 : ":AfterRename:$DATA",
855 : ":MStream Two:$DATA",
856 : ":?Stream*:$DATA"
857 : };
858 :
859 9 : ZERO_STRUCT(h);
860 9 : ZERO_STRUCT(h1);
861 9 : ZERO_STRUCT(h2);
862 9 : ZERO_STRUCT(h3);
863 :
864 9 : sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname, "\x05Stream\n One");
865 9 : sname1b = talloc_asprintf(mem_ctx, "%s:", sname1);
866 9 : sname1c = talloc_asprintf(mem_ctx, "%s:$FOO", sname1);
867 9 : sname1d = talloc_asprintf(mem_ctx, "%s:?D*a", sname1);
868 9 : sname2 = talloc_asprintf(mem_ctx, "%s:%s:$DaTa", fname, "MStream Two");
869 9 : snamew = talloc_asprintf(mem_ctx, "%s:%s:$DATA", fname, "?Stream*");
870 9 : snamew2 = talloc_asprintf(mem_ctx, "%s\\stream*:%s:$DATA", DNAME,
871 : "?Stream*");
872 9 : snamer1 = talloc_asprintf(mem_ctx, "%s:%s:$DATA", fname,
873 : "BeforeRename");
874 :
875 : /* clean slate ...*/
876 9 : smb2_util_unlink(tree, fname);
877 9 : smb2_deltree(tree, fname);
878 9 : smb2_deltree(tree, DNAME);
879 :
880 9 : status = torture_smb2_testdir(tree, DNAME, &h);
881 9 : CHECK_STATUS(status, NT_STATUS_OK);
882 :
883 9 : torture_comment(tctx, "(%s) testing stream names\n", __location__);
884 9 : ZERO_STRUCT(io.smb2);
885 9 : io.smb2.in.create_flags = 0;
886 9 : io.smb2.in.desired_access = SEC_FILE_WRITE_DATA;
887 9 : io.smb2.in.create_options = 0;
888 9 : io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
889 9 : io.smb2.in.share_access = 0;
890 9 : io.smb2.in.alloc_size = 0;
891 9 : io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
892 9 : io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
893 9 : io.smb2.in.security_flags = 0;
894 9 : io.smb2.in.fname = sname1;
895 :
896 9 : status = smb2_create(tree, mem_ctx, &(io.smb2));
897 9 : CHECK_STATUS(status, NT_STATUS_OK);
898 9 : h1 = io.smb2.out.file.handle;
899 :
900 : /*
901 : * A different stream does not give a sharing violation
902 : */
903 :
904 9 : io.smb2.in.fname = sname2;
905 9 : io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
906 9 : status = smb2_create(tree, mem_ctx, &(io.smb2));
907 9 : CHECK_STATUS(status, NT_STATUS_OK);
908 9 : h2 = io.smb2.out.file.handle;
909 :
910 : /*
911 : * ... whereas the same stream does with unchanged access/share_access
912 : * flags
913 : */
914 :
915 9 : io.smb2.in.fname = sname1;
916 9 : io.smb2.in.create_disposition = NTCREATEX_DISP_SUPERSEDE;
917 9 : status = smb2_create(tree, mem_ctx, &(io.smb2));
918 9 : CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
919 :
920 9 : io.smb2.in.fname = sname1b;
921 9 : status = smb2_create(tree, mem_ctx, &(io.smb2));
922 9 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
923 :
924 9 : io.smb2.in.fname = sname1c;
925 9 : status = smb2_create(tree, mem_ctx, &(io.smb2));
926 9 : if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
927 : /* w2k returns INVALID_PARAMETER */
928 9 : CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
929 : } else {
930 0 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
931 : }
932 :
933 9 : io.smb2.in.fname = sname1d;
934 9 : status = smb2_create(tree, mem_ctx, &(io.smb2));
935 9 : if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
936 : /* w2k returns INVALID_PARAMETER */
937 9 : CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
938 : } else {
939 0 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
940 : }
941 :
942 9 : io.smb2.in.fname = sname2;
943 9 : status = smb2_create(tree, mem_ctx, &(io.smb2));
944 9 : CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
945 :
946 9 : io.smb2.in.fname = snamew;
947 9 : status = smb2_create(tree, mem_ctx, &(io.smb2));
948 9 : CHECK_STATUS(status, NT_STATUS_OK);
949 9 : h3 = io.smb2.out.file.handle;
950 :
951 9 : io.smb2.in.fname = snamew2;
952 9 : status = smb2_create(tree, mem_ctx, &(io.smb2));
953 9 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
954 :
955 9 : io.smb2.in.fname = fname;
956 9 : io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
957 9 : status = smb2_create(tree, mem_ctx, &(io.smb2));
958 9 : CHECK_STATUS(status, NT_STATUS_OK);
959 9 : ret &= check_stream_list(tree, tctx, fname, 4, four,
960 : io.smb2.out.file.handle);
961 9 : CHECK_VALUE(ret, true);
962 9 : smb2_util_close(tree, h1);
963 9 : smb2_util_close(tree, h2);
964 9 : smb2_util_close(tree, h3);
965 :
966 9 : if (torture_setting_bool(tctx, "samba4", true)) {
967 9 : goto done;
968 : }
969 :
970 0 : finfo.generic.level = RAW_FILEINFO_ALL_INFORMATION;
971 0 : finfo.generic.in.file.handle = io.smb2.out.file.handle;
972 0 : status = smb2_getinfo_file(tree, mem_ctx, &finfo);
973 0 : CHECK_STATUS(status, NT_STATUS_OK);
974 0 : ret &= check_stream_list(tree, tctx, fname, 4, four,
975 : io.smb2.out.file.handle);
976 :
977 0 : CHECK_VALUE(ret, true);
978 0 : for (i=0; i < 4; i++) {
979 0 : NTTIME write_time;
980 0 : uint64_t stream_size;
981 0 : char *path = talloc_asprintf(tctx, "%s%s",
982 : fname, four[i]);
983 :
984 0 : char *rpath = talloc_strdup(path, path);
985 0 : char *p = strrchr(rpath, ':');
986 : /* eat :$DATA */
987 0 : *p = 0;
988 0 : p--;
989 0 : if (*p == ':') {
990 : /* eat ::$DATA */
991 0 : *p = 0;
992 : }
993 0 : torture_comment(tctx, "(%s): i[%u][%s]\n",
994 : __location__, i,path);
995 0 : io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
996 0 : io.smb2.in.desired_access = SEC_FILE_READ_ATTRIBUTE |
997 : SEC_FILE_WRITE_ATTRIBUTE |
998 : SEC_RIGHTS_FILE_ALL;
999 0 : io.smb2.in.fname = path;
1000 0 : status = smb2_create(tree, mem_ctx, &(io.smb2));
1001 0 : CHECK_STATUS(status, NT_STATUS_OK);
1002 0 : h1 = io.smb2.out.file.handle;
1003 :
1004 0 : finfo.generic.level = RAW_FILEINFO_ALL_INFORMATION;
1005 0 : finfo.generic.in.file.path = fname;
1006 0 : status = smb2_getinfo_file(tree, mem_ctx, &finfo);
1007 0 : CHECK_STATUS(status, NT_STATUS_OK);
1008 :
1009 0 : stinfo.generic.level = RAW_FILEINFO_ALL_INFORMATION;
1010 0 : stinfo.generic.in.file.handle = h1;
1011 0 : status = smb2_getinfo_file(tree, mem_ctx, &stinfo);
1012 0 : CHECK_STATUS(status, NT_STATUS_OK);
1013 0 : if (!torture_setting_bool(tctx, "samba3", false)) {
1014 0 : CHECK_NTTIME(stinfo.all_info.out.create_time,
1015 : finfo.all_info.out.create_time);
1016 0 : CHECK_NTTIME(stinfo.all_info.out.access_time,
1017 : finfo.all_info.out.access_time);
1018 0 : CHECK_NTTIME(stinfo.all_info.out.write_time,
1019 : finfo.all_info.out.write_time);
1020 0 : CHECK_NTTIME(stinfo.all_info.out.change_time,
1021 : finfo.all_info.out.change_time);
1022 : }
1023 0 : CHECK_VALUE(stinfo.all_info.out.attrib,
1024 : finfo.all_info.out.attrib);
1025 0 : CHECK_VALUE(stinfo.all_info.out.size,
1026 : finfo.all_info.out.size);
1027 0 : CHECK_VALUE(stinfo.all_info.out.delete_pending,
1028 : finfo.all_info.out.delete_pending);
1029 0 : CHECK_VALUE(stinfo.all_info.out.directory,
1030 : finfo.all_info.out.directory);
1031 0 : CHECK_VALUE(stinfo.all_info.out.ea_size,
1032 : finfo.all_info.out.ea_size);
1033 :
1034 0 : stinfo.generic.level = RAW_FILEINFO_NAME_INFORMATION;
1035 0 : stinfo.generic.in.file.handle = h1;
1036 0 : status = smb2_getinfo_file(tree, mem_ctx, &stinfo);
1037 0 : CHECK_STATUS(status, NT_STATUS_OK);
1038 0 : if (!torture_setting_bool(tctx, "samba3", false)) {
1039 0 : CHECK_STR(rpath, stinfo.name_info.out.fname.s);
1040 : }
1041 :
1042 0 : write_time = finfo.all_info.out.write_time;
1043 0 : write_time += i*1000000;
1044 0 : write_time /= 1000000;
1045 0 : write_time *= 1000000;
1046 :
1047 0 : ZERO_STRUCT(sinfo);
1048 0 : sinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFORMATION;
1049 0 : sinfo.basic_info.in.file.handle = h1;
1050 0 : sinfo.basic_info.in.write_time = write_time;
1051 0 : sinfo.basic_info.in.attrib = stinfo.all_info.out.attrib;
1052 0 : status = smb2_setinfo_file(tree, &sinfo);
1053 0 : CHECK_STATUS(status, NT_STATUS_OK);
1054 :
1055 0 : stream_size = i*8192;
1056 :
1057 0 : ZERO_STRUCT(sinfo);
1058 0 : sinfo.end_of_file_info.level =
1059 : RAW_SFILEINFO_END_OF_FILE_INFORMATION;
1060 0 : sinfo.end_of_file_info.in.file.handle = h1;
1061 0 : sinfo.end_of_file_info.in.size = stream_size;
1062 0 : status = smb2_setinfo_file(tree, &sinfo);
1063 0 : CHECK_STATUS(status, NT_STATUS_OK);
1064 :
1065 0 : stinfo.generic.level = RAW_FILEINFO_ALL_INFORMATION;
1066 0 : stinfo.generic.in.file.handle = h1;
1067 0 : status = smb2_getinfo_file(tree, mem_ctx, &stinfo);
1068 0 : CHECK_STATUS(status, NT_STATUS_OK);
1069 0 : if (!torture_setting_bool(tctx, "samba3", false)) {
1070 0 : CHECK_NTTIME(stinfo.all_info.out.write_time,
1071 : write_time);
1072 0 : CHECK_VALUE(stinfo.all_info.out.attrib,
1073 : finfo.all_info.out.attrib);
1074 : }
1075 0 : CHECK_VALUE(stinfo.all_info.out.size,
1076 : stream_size);
1077 0 : CHECK_VALUE(stinfo.all_info.out.delete_pending,
1078 : finfo.all_info.out.delete_pending);
1079 0 : CHECK_VALUE(stinfo.all_info.out.directory,
1080 : finfo.all_info.out.directory);
1081 0 : CHECK_VALUE(stinfo.all_info.out.ea_size,
1082 : finfo.all_info.out.ea_size);
1083 :
1084 0 : io.smb2.in.fname = fname;
1085 0 : io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
1086 0 : status = smb2_create(tree, mem_ctx, &(io.smb2));
1087 0 : CHECK_STATUS(status, NT_STATUS_OK);
1088 0 : ret &= check_stream_list(tree, tctx, fname, 4, four,
1089 : io.smb2.out.file.handle);
1090 :
1091 0 : smb2_util_close(tree, h1);
1092 0 : talloc_free(path);
1093 : }
1094 :
1095 0 : torture_comment(tctx, "(%s): testing stream renames\n", __location__);
1096 0 : io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
1097 0 : io.smb2.in.desired_access = SEC_FILE_READ_ATTRIBUTE |
1098 : SEC_FILE_WRITE_ATTRIBUTE |
1099 : SEC_RIGHTS_FILE_ALL;
1100 0 : io.smb2.in.fname = snamer1;
1101 0 : status = smb2_create(tree, mem_ctx, &(io.smb2));
1102 0 : CHECK_STATUS(status, NT_STATUS_OK);
1103 0 : h1 = io.smb2.out.file.handle;
1104 0 : ret &= check_stream_list(tree,tctx, fname, 5, five1,
1105 : io.smb2.out.file.handle);
1106 :
1107 0 : ZERO_STRUCT(sinfo);
1108 0 : sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION;
1109 0 : sinfo.rename_information.in.file.handle = h1;
1110 0 : sinfo.rename_information.in.overwrite = true;
1111 0 : sinfo.rename_information.in.root_fid = 0;
1112 0 : sinfo.rename_information.in.new_name = ":AfterRename:$DATA";
1113 0 : status = smb2_setinfo_file(tree, &sinfo);
1114 0 : CHECK_STATUS(status, NT_STATUS_OK);
1115 :
1116 0 : ret &= check_stream_list(tree,tctx, fname, 5, five2,
1117 : io.smb2.out.file.handle);
1118 :
1119 0 : CHECK_VALUE(ret, true);
1120 0 : ZERO_STRUCT(sinfo);
1121 0 : sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION;
1122 0 : sinfo.rename_information.in.file.handle = h1;
1123 0 : sinfo.rename_information.in.overwrite = false;
1124 0 : sinfo.rename_information.in.root_fid = 0;
1125 0 : sinfo.rename_information.in.new_name = ":MStream Two:$DATA";
1126 0 : status = smb2_setinfo_file(tree, &sinfo);
1127 0 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_COLLISION);
1128 :
1129 0 : ret &= check_stream_list(tree,tctx, fname, 5, five2,
1130 : io.smb2.out.file.handle);
1131 :
1132 0 : ZERO_STRUCT(sinfo);
1133 0 : sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION;
1134 0 : sinfo.rename_information.in.file.handle = h1;
1135 0 : sinfo.rename_information.in.overwrite = true;
1136 0 : sinfo.rename_information.in.root_fid = 0;
1137 0 : sinfo.rename_information.in.new_name = ":MStream Two:$DATA";
1138 0 : status = smb2_setinfo_file(tree, &sinfo);
1139 0 : CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
1140 :
1141 0 : ret &= check_stream_list(tree,tctx, fname, 5, five2,
1142 : io.smb2.out.file.handle);
1143 :
1144 0 : CHECK_VALUE(ret, true);
1145 : /* TODO: we need to test more rename combinations */
1146 :
1147 0 : done:
1148 9 : smb2_util_close(tree, h1);
1149 9 : status = smb2_util_unlink(tree, fname);
1150 9 : smb2_deltree(tree, DNAME);
1151 9 : talloc_free(mem_ctx);
1152 :
1153 9 : return ret;
1154 : }
1155 :
1156 : /*
1157 : test stream names
1158 : */
1159 9 : static bool test_stream_names2(struct torture_context *tctx,
1160 : struct smb2_tree *tree)
1161 : {
1162 9 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
1163 0 : NTSTATUS status;
1164 0 : union smb_open io;
1165 9 : const char *fname = DNAME "\\stream_names2.txt";
1166 9 : bool ret = true;
1167 9 : struct smb2_handle h = {{0}};
1168 9 : struct smb2_handle h1 = {{0}};
1169 0 : uint8_t i;
1170 :
1171 9 : smb2_util_unlink(tree, fname);
1172 9 : smb2_deltree(tree, DNAME);
1173 :
1174 9 : status = torture_smb2_testdir(tree, DNAME, &h);
1175 9 : CHECK_STATUS(status, NT_STATUS_OK);
1176 :
1177 9 : torture_comment(tctx, "(%s) testing stream names\n", __location__);
1178 9 : ZERO_STRUCT(io.smb2);
1179 9 : io.smb2.in.create_flags = 0;
1180 9 : io.smb2.in.desired_access = SEC_FILE_WRITE_DATA;
1181 9 : io.smb2.in.create_options = 0;
1182 9 : io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1183 9 : io.smb2.in.share_access = 0;
1184 9 : io.smb2.in.alloc_size = 0;
1185 9 : io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
1186 9 : io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
1187 9 : io.smb2.in.security_flags = 0;
1188 9 : io.smb2.in.fname = fname;
1189 9 : status = smb2_create(tree, mem_ctx, &(io.smb2));
1190 9 : CHECK_STATUS(status, NT_STATUS_OK);
1191 9 : h1 = io.smb2.out.file.handle;
1192 :
1193 1143 : for (i=0x01; i < 0x7F; i++) {
1194 1134 : char *path = talloc_asprintf(mem_ctx, "%s:Stream%c0x%02X:$DATA",
1195 : fname, i, i);
1196 0 : NTSTATUS expected;
1197 :
1198 1134 : switch (i) {
1199 27 : case '/':/*0x2F*/
1200 : case ':':/*0x3A*/
1201 : case '\\':/*0x5C*/
1202 27 : expected = NT_STATUS_OBJECT_NAME_INVALID;
1203 27 : break;
1204 1107 : default:
1205 1107 : expected = NT_STATUS_OBJECT_NAME_NOT_FOUND;
1206 1107 : break;
1207 : }
1208 :
1209 :
1210 1134 : io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
1211 1134 : io.smb2.in.fname = path;
1212 1134 : status = smb2_create(tree, mem_ctx, &(io.smb2));
1213 1134 : if (!NT_STATUS_EQUAL(status, expected)) {
1214 0 : torture_comment(tctx,
1215 : "(%s) %s:Stream%c0x%02X:$DATA%s => expected[%s]\n",
1216 0 : __location__, fname, isprint(i)?(char)i:' ', i,
1217 0 : isprint(i)?"":" (not printable)",
1218 : nt_errstr(expected));
1219 : }
1220 1134 : CHECK_STATUS(status, expected);
1221 :
1222 1134 : talloc_free(path);
1223 : }
1224 :
1225 9 : done:
1226 9 : smb2_util_close(tree, h1);
1227 9 : status = smb2_util_unlink(tree, fname);
1228 9 : smb2_deltree(tree, DNAME);
1229 9 : talloc_free(mem_ctx);
1230 :
1231 9 : return ret;
1232 : }
1233 :
1234 : /*
1235 : test case insensitive stream names
1236 : */
1237 9 : static bool test_stream_names3(struct torture_context *tctx,
1238 : struct smb2_tree *tree)
1239 : {
1240 9 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
1241 0 : NTSTATUS status;
1242 0 : union smb_fsinfo info;
1243 9 : const char *fname = DNAME "\\stream_names3.txt";
1244 9 : const char *sname = NULL;
1245 9 : const char *snamel = NULL;
1246 9 : const char *snameu = NULL;
1247 9 : const char *sdname = NULL;
1248 9 : const char *sdnamel = NULL;
1249 9 : const char *sdnameu = NULL;
1250 9 : bool ret = true;
1251 9 : struct smb2_handle h = {{0}};
1252 9 : struct smb2_handle hf = {{0}};
1253 9 : struct smb2_handle hs = {{0}};
1254 9 : struct smb2_handle hsl = {{0}};
1255 9 : struct smb2_handle hsu = {{0}};
1256 9 : struct smb2_handle hsd = {{0}};
1257 9 : struct smb2_handle hsdl = {{0}};
1258 9 : struct smb2_handle hsdu = {{0}};
1259 9 : const char *streams[] = { "::$DATA", ":StreamName:$DATA", };
1260 :
1261 9 : smb2_deltree(tree, DNAME);
1262 9 : status = torture_smb2_testdir(tree, DNAME, &h);
1263 9 : CHECK_STATUS(status, NT_STATUS_OK);
1264 :
1265 9 : ZERO_STRUCT(info);
1266 9 : info.generic.level = RAW_QFS_ATTRIBUTE_INFORMATION;
1267 9 : info.generic.handle = h;
1268 9 : status = smb2_getinfo_fs(tree, tree, &info);
1269 9 : CHECK_STATUS(status, NT_STATUS_OK);
1270 9 : if (!(info.attribute_info.out.fs_attr & FILE_CASE_SENSITIVE_SEARCH)) {
1271 0 : torture_skip(tctx, "No FILE_CASE_SENSITIVE_SEARCH supported");
1272 : }
1273 :
1274 : /*
1275 : * We create the following file:
1276 : *
1277 : * teststreams\\stream_names3.txt
1278 : *
1279 : * and add a stream named 'StreamName'
1280 : *
1281 : * Then we try to open the stream using the following names:
1282 : *
1283 : * teststreams\\stream_names3.txt:StreamName
1284 : * teststreams\\stream_names3.txt:streamname
1285 : * teststreams\\stream_names3.txt:STREAMNAME
1286 : * teststreams\\stream_names3.txt:StreamName:$dAtA
1287 : * teststreams\\stream_names3.txt:streamname:$data
1288 : * teststreams\\stream_names3.txt:STREAMNAME:$DATA
1289 : */
1290 9 : sname = talloc_asprintf(tctx, "%s:StreamName", fname);
1291 9 : torture_assert_not_null(tctx, sname, __location__);
1292 9 : snamel = strlower_talloc(tctx, sname);
1293 9 : torture_assert_not_null(tctx, snamel, __location__);
1294 9 : snameu = strupper_talloc(tctx, sname);
1295 9 : torture_assert_not_null(tctx, snameu, __location__);
1296 :
1297 9 : sdname = talloc_asprintf(tctx, "%s:$dAtA", sname);
1298 9 : torture_assert_not_null(tctx, sdname, __location__);
1299 9 : sdnamel = strlower_talloc(tctx, sdname);
1300 9 : torture_assert_not_null(tctx, sdnamel, __location__);
1301 9 : sdnameu = strupper_talloc(tctx, sdname);
1302 9 : torture_assert_not_null(tctx, sdnameu, __location__);
1303 :
1304 9 : torture_comment(tctx, "(%s) testing case insensitive stream names\n",
1305 : __location__);
1306 9 : status = torture_smb2_testfile(tree, fname, &hf);
1307 9 : CHECK_STATUS(status, NT_STATUS_OK);
1308 9 : status = torture_smb2_testfile(tree, sname, &hs);
1309 9 : CHECK_STATUS(status, NT_STATUS_OK);
1310 9 : smb2_util_close(tree, hs);
1311 :
1312 9 : torture_assert(tctx,
1313 : check_stream_list(tree, tctx, fname,
1314 : ARRAY_SIZE(streams),
1315 : streams,
1316 : hf),
1317 : "streams");
1318 :
1319 9 : status = torture_smb2_open(tree, sname, SEC_RIGHTS_FILE_ALL, &hs);
1320 9 : CHECK_STATUS(status, NT_STATUS_OK);
1321 9 : status = torture_smb2_open(tree, snamel, SEC_RIGHTS_FILE_ALL, &hsl);
1322 9 : CHECK_STATUS(status, NT_STATUS_OK);
1323 9 : status = torture_smb2_open(tree, snameu, SEC_RIGHTS_FILE_ALL, &hsu);
1324 9 : CHECK_STATUS(status, NT_STATUS_OK);
1325 9 : status = torture_smb2_open(tree, sdname, SEC_RIGHTS_FILE_ALL, &hsd);
1326 9 : CHECK_STATUS(status, NT_STATUS_OK);
1327 9 : status = torture_smb2_open(tree, sdnamel, SEC_RIGHTS_FILE_ALL, &hsdl);
1328 9 : CHECK_STATUS(status, NT_STATUS_OK);
1329 9 : status = torture_smb2_open(tree, sdnameu, SEC_RIGHTS_FILE_ALL, &hsdu);
1330 9 : CHECK_STATUS(status, NT_STATUS_OK);
1331 :
1332 9 : done:
1333 9 : smb2_util_close(tree, hsdu);
1334 9 : smb2_util_close(tree, hsdl);
1335 9 : smb2_util_close(tree, hsd);
1336 9 : smb2_util_close(tree, hsu);
1337 9 : smb2_util_close(tree, hsl);
1338 9 : smb2_util_close(tree, hs);
1339 9 : smb2_util_close(tree, hf);
1340 9 : smb2_util_close(tree, h);
1341 9 : status = smb2_util_unlink(tree, fname);
1342 9 : smb2_deltree(tree, DNAME);
1343 9 : talloc_free(mem_ctx);
1344 :
1345 9 : return ret;
1346 : }
1347 :
1348 : #define CHECK_CALL_HANDLE(call, rightstatus) do { \
1349 : sfinfo.generic.level = RAW_SFILEINFO_ ## call; \
1350 : sfinfo.generic.in.file.handle = h1; \
1351 : status = smb2_setinfo_file(tree, &sfinfo); \
1352 : if (!NT_STATUS_EQUAL(status, rightstatus)) { \
1353 : torture_result(tctx, TORTURE_FAIL, \
1354 : "(%s) %s - %s (should be %s)\n", \
1355 : __location__, #call, \
1356 : nt_errstr(status), nt_errstr(rightstatus)); \
1357 : ret = false; \
1358 : } \
1359 : finfo1.generic.level = RAW_FILEINFO_ALL_INFORMATION; \
1360 : finfo1.generic.in.file.handle = h1; \
1361 : status2 = smb2_getinfo_file(tree, tctx, &finfo1); \
1362 : if (!NT_STATUS_IS_OK(status2)) { \
1363 : torture_result(tctx, TORTURE_FAIL, \
1364 : "(%s) %s pathinfo - %s\n", \
1365 : __location__, #call, nt_errstr(status)); \
1366 : ret = false; \
1367 : }} while (0)
1368 :
1369 : /*
1370 : test stream renames
1371 : */
1372 9 : static bool test_stream_rename(struct torture_context *tctx,
1373 : struct smb2_tree *tree)
1374 : {
1375 9 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
1376 0 : NTSTATUS status, status2;
1377 0 : union smb_open io;
1378 9 : const char *fname = DNAME "\\stream_rename.txt";
1379 0 : const char *sname1, *sname2;
1380 0 : union smb_fileinfo finfo1;
1381 0 : union smb_setfileinfo sfinfo;
1382 9 : bool ret = true;
1383 9 : struct smb2_handle h = {{0}};
1384 9 : struct smb2_handle h1 = {{0}};
1385 :
1386 9 : sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname, "Stream One");
1387 9 : sname2 = talloc_asprintf(mem_ctx, "%s:%s:$DaTa", fname,
1388 : "Second Stream");
1389 :
1390 9 : smb2_util_unlink(tree, fname);
1391 9 : smb2_deltree(tree, DNAME);
1392 :
1393 9 : status = torture_smb2_testdir(tree, DNAME, &h);
1394 9 : CHECK_STATUS(status, NT_STATUS_OK);
1395 :
1396 9 : torture_comment(tctx, "(%s) testing stream renames\n", __location__);
1397 9 : ZERO_STRUCT(io.smb2);
1398 9 : io.smb2.in.create_flags = 0;
1399 9 : io.smb2.in.desired_access = SEC_FILE_READ_ATTRIBUTE |
1400 : SEC_FILE_WRITE_ATTRIBUTE |
1401 : SEC_RIGHTS_FILE_ALL;
1402 9 : io.smb2.in.create_options = 0;
1403 9 : io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1404 9 : io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
1405 : NTCREATEX_SHARE_ACCESS_WRITE |
1406 : NTCREATEX_SHARE_ACCESS_DELETE;
1407 9 : io.smb2.in.alloc_size = 0;
1408 9 : io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
1409 9 : io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
1410 9 : io.smb2.in.security_flags = 0;
1411 9 : io.smb2.in.fname = sname1;
1412 :
1413 : /* Create two streams. */
1414 9 : status = smb2_create(tree, mem_ctx, &(io.smb2));
1415 9 : CHECK_STATUS(status, NT_STATUS_OK);
1416 9 : h1 = io.smb2.out.file.handle;
1417 9 : smb2_util_close(tree, h1);
1418 :
1419 9 : io.smb2.in.fname = sname2;
1420 9 : status = smb2_create(tree, mem_ctx, &(io.smb2));
1421 9 : CHECK_STATUS(status, NT_STATUS_OK);
1422 9 : h1 = io.smb2.out.file.handle;
1423 :
1424 9 : smb2_util_close(tree, h1);
1425 :
1426 : /*
1427 : * Open the second stream.
1428 : */
1429 :
1430 9 : io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
1431 9 : status = smb2_create(tree, mem_ctx, &(io.smb2));
1432 9 : CHECK_STATUS(status, NT_STATUS_OK);
1433 9 : h1 = io.smb2.out.file.handle;
1434 :
1435 : /*
1436 : * Now rename the second stream onto the first.
1437 : */
1438 :
1439 9 : ZERO_STRUCT(sfinfo);
1440 :
1441 9 : sfinfo.rename_information.in.overwrite = 1;
1442 9 : sfinfo.rename_information.in.root_fid = 0;
1443 9 : sfinfo.rename_information.in.new_name = ":Stream One";
1444 9 : CHECK_CALL_HANDLE(RENAME_INFORMATION, NT_STATUS_OK);
1445 9 : done:
1446 9 : smb2_util_close(tree, h1);
1447 9 : status = smb2_util_unlink(tree, fname);
1448 9 : smb2_deltree(tree, DNAME);
1449 9 : talloc_free(mem_ctx);
1450 :
1451 9 : return ret;
1452 : }
1453 :
1454 9 : static bool test_stream_rename2(struct torture_context *tctx,
1455 : struct smb2_tree *tree)
1456 : {
1457 9 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
1458 0 : NTSTATUS status;
1459 0 : union smb_open io;
1460 9 : const char *fname1 = DNAME "\\stream_rename2.txt";
1461 9 : const char *fname2 = DNAME "\\stream2_rename2.txt";
1462 9 : const char *stream_name1 = ":Stream One:$DATA";
1463 9 : const char *stream_name2 = ":Stream Two:$DATA";
1464 9 : const char *stream_name_default = "::$DATA";
1465 0 : const char *sname1;
1466 0 : const char *sname2;
1467 9 : bool ret = true;
1468 0 : struct smb2_handle h, h1;
1469 0 : union smb_setfileinfo sinfo;
1470 :
1471 9 : ZERO_STRUCT(h);
1472 9 : ZERO_STRUCT(h1);
1473 :
1474 9 : sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname1, "Stream One");
1475 9 : sname2 = talloc_asprintf(mem_ctx, "%s:%s", fname1, "Stream Two");
1476 :
1477 9 : smb2_util_unlink(tree, fname1);
1478 9 : smb2_util_unlink(tree, fname2);
1479 9 : smb2_deltree(tree, DNAME);
1480 :
1481 9 : status = torture_smb2_testdir(tree, DNAME, &h);
1482 9 : CHECK_STATUS(status, NT_STATUS_OK);
1483 :
1484 9 : ZERO_STRUCT(io.smb2);
1485 9 : io.smb2.in.create_flags = 0;
1486 9 : io.smb2.in.desired_access = SEC_FILE_READ_DATA |
1487 : SEC_FILE_WRITE_DATA |
1488 : SEC_STD_DELETE |
1489 : SEC_FILE_APPEND_DATA |
1490 : SEC_STD_READ_CONTROL;
1491 9 : io.smb2.in.create_options = 0;
1492 9 : io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1493 9 : io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
1494 : NTCREATEX_SHARE_ACCESS_WRITE |
1495 : NTCREATEX_SHARE_ACCESS_DELETE;
1496 9 : io.smb2.in.alloc_size = 0;
1497 9 : io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
1498 9 : io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
1499 9 : io.smb2.in.security_flags = 0;
1500 9 : io.smb2.in.fname = sname1;
1501 :
1502 : /* Open/create new stream. */
1503 9 : status = smb2_create(tree, mem_ctx, &(io.smb2));
1504 9 : CHECK_STATUS(status, NT_STATUS_OK);
1505 :
1506 9 : smb2_util_close(tree, io.smb2.out.file.handle);
1507 :
1508 : /*
1509 : * Reopen the stream for SMB2 renames.
1510 : */
1511 9 : io.smb2.in.fname = sname1;
1512 9 : io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
1513 9 : status = smb2_create(tree, mem_ctx, &(io.smb2));
1514 9 : CHECK_STATUS(status, NT_STATUS_OK);
1515 9 : h1 = io.smb2.out.file.handle;
1516 :
1517 : /*
1518 : * Check SMB2 rename of a stream using :<stream>.
1519 : */
1520 9 : torture_comment(tctx, "(%s) Checking SMB2 rename of a stream using "
1521 : ":<stream>\n", __location__);
1522 9 : ZERO_STRUCT(sinfo);
1523 9 : sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION_SMB2;
1524 9 : sinfo.rename_information.in.file.handle = h1;
1525 9 : sinfo.rename_information.in.overwrite = 1;
1526 9 : sinfo.rename_information.in.root_fid = 0;
1527 9 : sinfo.rename_information.in.new_name = stream_name1;
1528 9 : status = smb2_setinfo_file(tree, &sinfo);
1529 9 : CHECK_STATUS(status, NT_STATUS_OK);
1530 :
1531 : /*
1532 : * Check SMB2 rename of an overwriting stream using :<stream>.
1533 : */
1534 1 : torture_comment(tctx, "(%s) Checking SMB2 rename of an overwriting "
1535 : "stream using :<stream>\n", __location__);
1536 :
1537 : /* Create second stream. */
1538 1 : io.smb2.in.fname = sname2;
1539 1 : io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
1540 1 : status = smb2_create(tree, mem_ctx, &(io.smb2));
1541 1 : CHECK_STATUS(status, NT_STATUS_OK);
1542 1 : smb2_util_close(tree, io.smb2.out.file.handle);
1543 :
1544 : /* Rename the first stream onto the second. */
1545 1 : sinfo.rename_information.in.file.handle = h1;
1546 1 : sinfo.rename_information.in.new_name = stream_name2;
1547 1 : status = smb2_setinfo_file(tree, &sinfo);
1548 1 : CHECK_STATUS(status, NT_STATUS_OK);
1549 :
1550 1 : smb2_util_close(tree, h1);
1551 :
1552 : /*
1553 : * Reopen the stream with the new name.
1554 : */
1555 1 : io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
1556 1 : io.smb2.in.fname = sname2;
1557 1 : status = smb2_create(tree, mem_ctx, &(io.smb2));
1558 1 : CHECK_STATUS(status, NT_STATUS_OK);
1559 1 : h1 = io.smb2.out.file.handle;
1560 :
1561 : /*
1562 : * Check SMB2 rename of a stream using <base>:<stream>.
1563 : */
1564 1 : torture_comment(tctx, "(%s) Checking SMB2 rename of a stream using "
1565 : "<base>:<stream>\n", __location__);
1566 1 : sinfo.rename_information.in.file.handle = h1;
1567 1 : sinfo.rename_information.in.new_name = sname1;
1568 1 : status = smb2_setinfo_file(tree, &sinfo);
1569 1 : CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
1570 :
1571 1 : if (!torture_setting_bool(tctx, "samba4", false)) {
1572 : /*
1573 : * Check SMB2 rename to the default stream using :<stream>.
1574 : */
1575 0 : torture_comment(tctx, "(%s) Checking SMB2 rename to default stream "
1576 : "using :<stream>\n", __location__);
1577 0 : sinfo.rename_information.in.file.handle = h1;
1578 0 : sinfo.rename_information.in.new_name = stream_name_default;
1579 0 : status = smb2_setinfo_file(tree, &sinfo);
1580 0 : CHECK_STATUS(status, NT_STATUS_OK);
1581 : }
1582 :
1583 1 : smb2_util_close(tree, h1);
1584 :
1585 9 : done:
1586 9 : smb2_util_close(tree, h1);
1587 9 : status = smb2_util_unlink(tree, fname1);
1588 9 : status = smb2_util_unlink(tree, fname2);
1589 9 : smb2_deltree(tree, DNAME);
1590 9 : talloc_free(mem_ctx);
1591 :
1592 9 : return ret;
1593 : }
1594 :
1595 42 : static bool create_file_with_stream(struct torture_context *tctx,
1596 : struct smb2_tree *tree,
1597 : TALLOC_CTX *mem_ctx,
1598 : const char *base_fname,
1599 : const char *stream)
1600 : {
1601 0 : NTSTATUS status;
1602 42 : bool ret = true;
1603 0 : union smb_open io;
1604 :
1605 : /* Create a file with a stream */
1606 42 : ZERO_STRUCT(io.smb2);
1607 42 : io.smb2.in.create_flags = 0;
1608 42 : io.smb2.in.desired_access = SEC_FILE_READ_DATA |
1609 : SEC_FILE_WRITE_DATA |
1610 : SEC_FILE_APPEND_DATA |
1611 : SEC_STD_READ_CONTROL;
1612 42 : io.smb2.in.create_options = 0;
1613 42 : io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1614 42 : io.smb2.in.share_access = 0;
1615 42 : io.smb2.in.alloc_size = 0;
1616 42 : io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
1617 42 : io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
1618 42 : io.smb2.in.security_flags = 0;
1619 42 : io.smb2.in.fname = stream;
1620 :
1621 42 : status = smb2_create(tree, mem_ctx, &(io.smb2));
1622 42 : CHECK_STATUS(status, NT_STATUS_OK);
1623 :
1624 42 : done:
1625 42 : smb2_util_close(tree, io.smb2.out.file.handle);
1626 42 : return ret;
1627 : }
1628 :
1629 :
1630 : /* Test how streams interact with create dispositions */
1631 9 : static bool test_stream_create_disposition(struct torture_context *tctx,
1632 : struct smb2_tree *tree)
1633 : {
1634 9 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
1635 0 : NTSTATUS status;
1636 0 : union smb_open io;
1637 9 : const char *fname = DNAME "\\stream_create_disp.txt";
1638 9 : const char *stream = "Stream One:$DATA";
1639 0 : const char *fname_stream;
1640 9 : const char *default_stream_name = "::$DATA";
1641 0 : const char *stream_list[2];
1642 9 : bool ret = true;
1643 9 : struct smb2_handle h = {{0}};
1644 9 : struct smb2_handle h1 = {{0}};
1645 :
1646 : /* clean slate .. */
1647 9 : smb2_util_unlink(tree, fname);
1648 9 : smb2_deltree(tree, fname);
1649 9 : smb2_deltree(tree, DNAME);
1650 :
1651 9 : status = torture_smb2_testdir(tree, DNAME, &h);
1652 9 : CHECK_STATUS(status, NT_STATUS_OK);
1653 :
1654 9 : fname_stream = talloc_asprintf(mem_ctx, "%s:%s", fname, stream);
1655 :
1656 9 : stream_list[0] = talloc_asprintf(mem_ctx, ":%s", stream);
1657 9 : stream_list[1] = default_stream_name;
1658 :
1659 9 : if (!create_file_with_stream(tctx, tree, mem_ctx, fname,
1660 : fname_stream)) {
1661 0 : goto done;
1662 : }
1663 :
1664 : /* Open the base file with OPEN */
1665 9 : ZERO_STRUCT(io.smb2);
1666 9 : io.smb2.in.create_flags = 0;
1667 9 : io.smb2.in.desired_access = SEC_FILE_READ_DATA |
1668 : SEC_FILE_WRITE_DATA |
1669 : SEC_FILE_APPEND_DATA |
1670 : SEC_STD_READ_CONTROL;
1671 9 : io.smb2.in.create_options = 0;
1672 9 : io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1673 9 : io.smb2.in.share_access = 0;
1674 9 : io.smb2.in.alloc_size = 0;
1675 9 : io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
1676 9 : io.smb2.in.security_flags = 0;
1677 9 : io.smb2.in.fname = fname;
1678 :
1679 : /*
1680 : * check create open: sanity check
1681 : */
1682 9 : torture_comment(tctx, "(%s) Checking create disp: open\n",
1683 : __location__);
1684 9 : io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
1685 9 : status = smb2_create(tree, mem_ctx, &(io.smb2));
1686 9 : CHECK_STATUS(status, NT_STATUS_OK);
1687 9 : if (!check_stream_list(tree, tctx, fname, 2, stream_list,
1688 : io.smb2.out.file.handle)) {
1689 0 : goto done;
1690 : }
1691 9 : smb2_util_close(tree, io.smb2.out.file.handle);
1692 :
1693 : /*
1694 : * check create overwrite
1695 : */
1696 9 : torture_comment(tctx, "(%s) Checking create disp: overwrite\n",
1697 : __location__);
1698 9 : io.smb2.in.create_disposition = NTCREATEX_DISP_OVERWRITE;
1699 9 : status = smb2_create(tree, mem_ctx, &(io.smb2));
1700 9 : CHECK_STATUS(status, NT_STATUS_OK);
1701 9 : if (!check_stream_list(tree, tctx, fname, 1, &default_stream_name,
1702 : io.smb2.out.file.handle)) {
1703 1 : goto done;
1704 : }
1705 8 : smb2_util_close(tree, io.smb2.out.file.handle);
1706 :
1707 : /*
1708 : * check create overwrite_if
1709 : */
1710 8 : torture_comment(tctx, "(%s) Checking create disp: overwrite_if\n",
1711 : __location__);
1712 8 : smb2_util_unlink(tree, fname);
1713 8 : if (!create_file_with_stream(tctx, tree, mem_ctx, fname, fname_stream))
1714 0 : goto done;
1715 :
1716 8 : io.smb2.in.create_disposition = NTCREATEX_DISP_OVERWRITE_IF;
1717 8 : status = smb2_create(tree, mem_ctx, &(io.smb2));
1718 8 : CHECK_STATUS(status, NT_STATUS_OK);
1719 8 : if (!check_stream_list(tree, tctx, fname, 1, &default_stream_name,
1720 : io.smb2.out.file.handle)) {
1721 0 : goto done;
1722 : }
1723 8 : smb2_util_close(tree, io.smb2.out.file.handle);
1724 :
1725 : /*
1726 : * check create supersede
1727 : */
1728 8 : torture_comment(tctx, "(%s) Checking create disp: supersede\n",
1729 : __location__);
1730 8 : smb2_util_unlink(tree, fname);
1731 8 : if (!create_file_with_stream(tctx, tree, mem_ctx, fname,
1732 : fname_stream)) {
1733 0 : goto done;
1734 : }
1735 :
1736 8 : io.smb2.in.create_disposition = NTCREATEX_DISP_SUPERSEDE;
1737 8 : status = smb2_create(tree, mem_ctx, &(io.smb2));
1738 8 : CHECK_STATUS(status, NT_STATUS_OK);
1739 8 : if (!check_stream_list(tree, tctx, fname, 1, &default_stream_name,
1740 : io.smb2.out.file.handle)) {
1741 0 : goto done;
1742 : }
1743 8 : smb2_util_close(tree, io.smb2.out.file.handle);
1744 :
1745 : /*
1746 : * check create overwrite_if on a stream.
1747 : */
1748 8 : torture_comment(tctx, "(%s) Checking create disp: overwrite_if on "
1749 : "stream\n", __location__);
1750 8 : smb2_util_unlink(tree, fname);
1751 8 : if (!create_file_with_stream(tctx, tree, mem_ctx, fname,
1752 : fname_stream)) {
1753 0 : goto done;
1754 : }
1755 :
1756 8 : io.smb2.in.create_disposition = NTCREATEX_DISP_OVERWRITE_IF;
1757 8 : io.smb2.in.fname = fname_stream;
1758 8 : status = smb2_create(tree, mem_ctx, &(io.smb2));
1759 8 : CHECK_STATUS(status, NT_STATUS_OK);
1760 8 : if (!check_stream_list(tree, tctx, fname, 2, stream_list,
1761 : io.smb2.out.file.handle)) {
1762 8 : goto done;
1763 : }
1764 0 : smb2_util_close(tree, io.smb2.out.file.handle);
1765 9 : done:
1766 9 : smb2_util_close(tree, h1);
1767 9 : smb2_util_unlink(tree, fname);
1768 9 : smb2_deltree(tree, DNAME);
1769 9 : talloc_free(mem_ctx);
1770 :
1771 9 : return ret;
1772 : }
1773 :
1774 18 : static bool open_stream(struct smb2_tree *tree,
1775 : struct torture_context *mem_ctx,
1776 : const char *fname,
1777 : struct smb2_handle *h_out)
1778 : {
1779 0 : NTSTATUS status;
1780 0 : union smb_open io;
1781 :
1782 18 : ZERO_STRUCT(io.smb2);
1783 18 : io.smb2.in.create_flags = 0;
1784 18 : io.smb2.in.desired_access = SEC_FILE_READ_DATA |
1785 : SEC_FILE_WRITE_DATA |
1786 : SEC_FILE_APPEND_DATA |
1787 : SEC_STD_READ_CONTROL |
1788 : SEC_FILE_WRITE_ATTRIBUTE;
1789 18 : io.smb2.in.create_options = 0;
1790 18 : io.smb2.in.file_attributes = 0;
1791 18 : io.smb2.in.share_access = 0;
1792 18 : io.smb2.in.alloc_size = 0;
1793 18 : io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
1794 18 : io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
1795 18 : io.smb2.in.security_flags = 0;
1796 18 : io.smb2.in.fname = fname;
1797 :
1798 18 : status = smb2_create(tree, mem_ctx, &(io.smb2));
1799 18 : if (!NT_STATUS_IS_OK(status)) {
1800 0 : return false;
1801 : }
1802 18 : *h_out = io.smb2.out.file.handle;
1803 18 : return true;
1804 : }
1805 :
1806 :
1807 : /* Test the effect of setting attributes on a stream. */
1808 9 : static bool test_stream_attributes1(struct torture_context *tctx,
1809 : struct smb2_tree *tree)
1810 : {
1811 9 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
1812 9 : bool ret = true;
1813 0 : NTSTATUS status;
1814 0 : union smb_open io;
1815 9 : const char *fname = DNAME "\\stream_attr.txt";
1816 9 : const char *stream = "Stream One:$DATA";
1817 0 : const char *fname_stream;
1818 0 : struct smb2_handle h, h1;
1819 0 : union smb_fileinfo finfo;
1820 0 : union smb_setfileinfo sfinfo;
1821 9 : time_t basetime = (time(NULL) - 86400) & ~1;
1822 :
1823 9 : ZERO_STRUCT(h);
1824 9 : ZERO_STRUCT(h1);
1825 :
1826 9 : torture_comment(tctx, "(%s) testing attribute setting on stream\n",
1827 : __location__);
1828 :
1829 : /* clean slate .. */
1830 9 : smb2_util_unlink(tree, fname);
1831 9 : smb2_deltree(tree, fname);
1832 9 : smb2_deltree(tree, DNAME);
1833 :
1834 9 : status = torture_smb2_testdir(tree, DNAME, &h);
1835 9 : CHECK_STATUS(status, NT_STATUS_OK);
1836 :
1837 9 : fname_stream = talloc_asprintf(mem_ctx, "%s:%s", fname, stream);
1838 :
1839 : /* Create a file with a stream with attribute FILE_ATTRIBUTE_ARCHIVE. */
1840 9 : ret = create_file_with_stream(tctx, tree, mem_ctx, fname,
1841 : fname_stream);
1842 9 : if (!ret) {
1843 0 : goto done;
1844 : }
1845 :
1846 9 : ZERO_STRUCT(io.smb2);
1847 9 : io.smb2.in.fname = fname;
1848 9 : io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
1849 9 : io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL;
1850 9 : io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
1851 : NTCREATEX_SHARE_ACCESS_WRITE |
1852 : NTCREATEX_SHARE_ACCESS_DELETE;
1853 9 : status = smb2_create(tree, mem_ctx, &(io.smb2));
1854 9 : CHECK_STATUS(status, NT_STATUS_OK);
1855 :
1856 9 : ZERO_STRUCT(finfo);
1857 9 : finfo.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
1858 9 : finfo.generic.in.file.handle = io.smb2.out.file.handle;
1859 9 : status = smb2_getinfo_file(tree, mem_ctx, &finfo);
1860 9 : CHECK_STATUS(status, NT_STATUS_OK);
1861 :
1862 9 : if (finfo.basic_info.out.attrib != FILE_ATTRIBUTE_ARCHIVE) {
1863 0 : torture_comment(tctx, "(%s) Incorrect attrib %x - should be "
1864 : "%x\n", __location__,
1865 0 : (unsigned int)finfo.basic_info.out.attrib,
1866 : (unsigned int)FILE_ATTRIBUTE_ARCHIVE);
1867 0 : ret = false;
1868 0 : goto done;
1869 : }
1870 :
1871 9 : smb2_util_close(tree, io.smb2.out.file.handle);
1872 : /* Now open the stream name. */
1873 :
1874 9 : if (!open_stream(tree, tctx, fname_stream, &h1)) {
1875 0 : goto done;
1876 : }
1877 :
1878 : /* Change the time on the stream. */
1879 9 : ZERO_STRUCT(sfinfo);
1880 9 : unix_to_nt_time(&sfinfo.basic_info.in.write_time, basetime);
1881 9 : sfinfo.generic.level = RAW_SFILEINFO_BASIC_INFORMATION;
1882 9 : sfinfo.generic.in.file.handle = h1;
1883 9 : status = smb2_setinfo_file(tree, &sfinfo);
1884 9 : if (!NT_STATUS_EQUAL(status, NT_STATUS_OK)) {
1885 0 : torture_comment(tctx, "(%s) %s - %s (should be %s)\n",
1886 : __location__, "SETATTR",
1887 0 : nt_errstr(status), nt_errstr(NT_STATUS_OK));
1888 0 : ret = false;
1889 0 : goto done;
1890 : }
1891 :
1892 9 : smb2_util_close(tree, h1);
1893 :
1894 9 : ZERO_STRUCT(io.smb2);
1895 9 : io.smb2.in.fname = fname;
1896 9 : io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
1897 9 : io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL;
1898 9 : status = smb2_create(tree, mem_ctx, &(io.smb2));
1899 9 : CHECK_STATUS(status, NT_STATUS_OK);
1900 9 : h1 = io.smb2.out.file.handle;
1901 :
1902 9 : ZERO_STRUCT(finfo);
1903 9 : finfo.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
1904 9 : finfo.generic.in.file.handle = h1;
1905 9 : status = smb2_getinfo_file(tree, mem_ctx, &finfo);
1906 9 : if (!NT_STATUS_IS_OK(status)) {
1907 0 : torture_comment(tctx, "(%s) %s pathinfo - %s\n",
1908 : __location__, "SETATTRE", nt_errstr(status));
1909 0 : ret = false;
1910 0 : goto done;
1911 : }
1912 :
1913 9 : if (nt_time_to_unix(finfo.basic_info.out.write_time) != basetime) {
1914 0 : torture_comment(tctx, "(%s) time incorrect.\n", __location__);
1915 0 : ret = false;
1916 0 : goto done;
1917 : }
1918 9 : smb2_util_close(tree, h1);
1919 :
1920 9 : if (!open_stream(tree, tctx, fname_stream, &h1)) {
1921 0 : goto done;
1922 : }
1923 :
1924 : /* Changing attributes on stream */
1925 9 : ZERO_STRUCT(sfinfo);
1926 9 : sfinfo.basic_info.in.attrib = FILE_ATTRIBUTE_READONLY;
1927 :
1928 9 : sfinfo.generic.level = RAW_SFILEINFO_BASIC_INFORMATION;
1929 9 : sfinfo.generic.in.file.handle = h1;
1930 9 : status = smb2_setinfo_file(tree, &sfinfo);
1931 9 : if (!NT_STATUS_EQUAL(status, NT_STATUS_OK)) {
1932 0 : torture_comment(tctx, "(%s) %s - %s (should be %s)\n",
1933 : __location__, "SETATTR",
1934 0 : nt_errstr(status), nt_errstr(NT_STATUS_OK));
1935 0 : ret = false;
1936 0 : goto done;
1937 : }
1938 :
1939 9 : smb2_util_close(tree, h1);
1940 :
1941 9 : ZERO_STRUCT(io.smb2);
1942 9 : io.smb2.in.fname = fname;
1943 9 : io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
1944 9 : io.smb2.in.desired_access = SEC_FILE_READ_DATA;
1945 9 : status = smb2_create(tree, mem_ctx, &(io.smb2));
1946 9 : CHECK_STATUS(status, NT_STATUS_OK);
1947 9 : h1 = io.smb2.out.file.handle;
1948 :
1949 9 : ZERO_STRUCT(finfo);
1950 9 : finfo.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
1951 9 : finfo.generic.in.file.handle = h1;
1952 9 : status = smb2_getinfo_file(tree, mem_ctx, &finfo);
1953 9 : CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED);
1954 :
1955 9 : done:
1956 9 : smb2_util_close(tree, h1);
1957 9 : smb2_util_unlink(tree, fname);
1958 9 : smb2_deltree(tree, DNAME);
1959 9 : talloc_free(mem_ctx);
1960 :
1961 9 : return ret;
1962 : }
1963 :
1964 117 : static bool check_metadata(struct torture_context *tctx,
1965 : struct smb2_tree *tree,
1966 : const char *path,
1967 : struct smb2_handle _h,
1968 : NTTIME expected_btime,
1969 : uint32_t expected_attribs)
1970 : {
1971 117 : struct smb2_handle h = _h;
1972 0 : union smb_fileinfo getinfo;
1973 0 : NTSTATUS status;
1974 117 : bool ret = true;
1975 :
1976 117 : if (smb2_util_handle_empty(h)) {
1977 0 : struct smb2_create c;
1978 :
1979 90 : c = (struct smb2_create) {
1980 : .in.desired_access = SEC_FILE_ALL,
1981 : .in.share_access = NTCREATEX_SHARE_ACCESS_MASK,
1982 : .in.file_attributes = FILE_ATTRIBUTE_HIDDEN,
1983 : .in.create_disposition = NTCREATEX_DISP_OPEN,
1984 : .in.impersonation_level = SMB2_IMPERSONATION_IMPERSONATION,
1985 : .in.fname = path,
1986 : };
1987 90 : status = smb2_create(tree, tctx, &c);
1988 90 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1989 : "smb2_create failed\n");
1990 :
1991 90 : h = c.out.file.handle;
1992 : }
1993 :
1994 117 : getinfo = (union smb_fileinfo) {
1995 : .generic.level = SMB_QFILEINFO_BASIC_INFORMATION,
1996 : .generic.in.file.handle = h,
1997 : };
1998 :
1999 117 : status = smb2_getinfo_file(tree, tctx, &getinfo);
2000 117 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2001 : "smb2_getinfo_file failed\n");
2002 :
2003 117 : torture_assert_u64_equal_goto(tctx,
2004 : expected_btime,
2005 : getinfo.basic_info.out.create_time,
2006 : ret, done,
2007 : "btime was updated\n");
2008 :
2009 117 : torture_assert_u32_equal_goto(tctx,
2010 : expected_attribs,
2011 : getinfo.basic_info.out.attrib,
2012 : ret, done,
2013 : "btime was updated\n");
2014 :
2015 117 : done:
2016 117 : if (smb2_util_handle_empty(_h)) {
2017 90 : smb2_util_close(tree, h);
2018 : }
2019 :
2020 117 : return ret;
2021 : }
2022 :
2023 9 : static bool test_stream_attributes2(struct torture_context *tctx,
2024 : struct smb2_tree *tree)
2025 : {
2026 0 : NTSTATUS status;
2027 0 : struct smb2_create c1;
2028 9 : struct smb2_handle h1 = {{0}};
2029 9 : const char *fname = DNAME "\\test_stream_btime";
2030 9 : const char *sname = DNAME "\\test_stream_btime:stream";
2031 0 : union smb_fileinfo getinfo;
2032 0 : union smb_setfileinfo setinfo;
2033 9 : const char *data = "test data";
2034 0 : struct timespec ts;
2035 0 : NTTIME btime;
2036 9 : uint32_t attrib = FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_ARCHIVE;
2037 0 : bool ret;
2038 :
2039 9 : smb2_deltree(tree, DNAME);
2040 :
2041 9 : status = torture_smb2_testdir(tree, DNAME, &h1);
2042 9 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2043 : "torture_smb2_testdir failed\n");
2044 9 : smb2_util_close(tree, h1);
2045 :
2046 9 : torture_comment(tctx, "Let's dance!\n");
2047 :
2048 : /*
2049 : * Step 1: create file and get creation date
2050 : */
2051 :
2052 9 : c1 = (struct smb2_create) {
2053 : .in.desired_access = SEC_FILE_ALL,
2054 : .in.share_access = NTCREATEX_SHARE_ACCESS_MASK,
2055 : .in.file_attributes = FILE_ATTRIBUTE_HIDDEN,
2056 : .in.create_disposition = NTCREATEX_DISP_CREATE,
2057 : .in.impersonation_level = SMB2_IMPERSONATION_IMPERSONATION,
2058 : .in.fname = fname,
2059 : };
2060 9 : status = smb2_create(tree, tctx, &c1);
2061 9 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2062 : "smb2_create failed\n");
2063 9 : h1 = c1.out.file.handle;
2064 :
2065 9 : getinfo = (union smb_fileinfo) {
2066 : .generic.level = SMB_QFILEINFO_BASIC_INFORMATION,
2067 : .generic.in.file.handle = h1,
2068 : };
2069 9 : status = smb2_getinfo_file(tree, tctx, &getinfo);
2070 9 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2071 : "smb2_getinfo_file failed\n");
2072 :
2073 9 : btime = getinfo.basic_info.out.create_time;
2074 :
2075 9 : status = smb2_util_close(tree, h1);
2076 9 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2077 : "smb2_util_close failed\n");
2078 9 : ZERO_STRUCT(h1);
2079 :
2080 : /*
2081 : * Step X: write to file, assert btime was not updated
2082 : */
2083 :
2084 9 : c1 = (struct smb2_create) {
2085 : .in.desired_access = SEC_FILE_ALL,
2086 : .in.share_access = NTCREATEX_SHARE_ACCESS_MASK,
2087 : .in.file_attributes = attrib,
2088 : .in.create_disposition = NTCREATEX_DISP_OPEN,
2089 : .in.impersonation_level = SMB2_IMPERSONATION_IMPERSONATION,
2090 : .in.fname = fname,
2091 : };
2092 9 : status = smb2_create(tree, tctx, &c1);
2093 9 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2094 : "smb2_create failed\n");
2095 9 : h1 = c1.out.file.handle;
2096 :
2097 9 : status = smb2_util_write(tree, h1, data, 0, strlen(data));
2098 9 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2099 : "smb2_util_write failed\n");
2100 :
2101 9 : ret = check_metadata(tctx, tree, NULL, h1, btime, attrib);
2102 9 : torture_assert_goto(tctx, ret, ret, done, "Bad metadata\n");
2103 :
2104 9 : status = smb2_util_close(tree, h1);
2105 9 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2106 : "smb2_util_close failed\n");
2107 9 : ZERO_STRUCT(h1);
2108 :
2109 9 : ret = check_metadata(tctx, tree, fname, (struct smb2_handle){{0}},
2110 : btime, attrib);
2111 9 : torture_assert_goto(tctx, ret, ret, done, "Bad metadata\n");
2112 :
2113 : /*
2114 : * Step X: create stream, assert creation date is the same
2115 : * as the one on the basefile
2116 : */
2117 :
2118 9 : c1 = (struct smb2_create) {
2119 : .in.desired_access = SEC_FILE_ALL,
2120 : .in.share_access = NTCREATEX_SHARE_ACCESS_MASK,
2121 : .in.file_attributes = attrib,
2122 : .in.create_disposition = NTCREATEX_DISP_CREATE,
2123 : .in.impersonation_level = SMB2_IMPERSONATION_IMPERSONATION,
2124 : .in.fname = sname,
2125 : };
2126 9 : status = smb2_create(tree, tctx, &c1);
2127 9 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2128 : "smb2_create failed\n");
2129 9 : h1 = c1.out.file.handle;
2130 :
2131 9 : status = smb2_util_close(tree, h1);
2132 9 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2133 : "smb2_util_close failed\n");
2134 9 : ZERO_STRUCT(h1);
2135 :
2136 9 : ret = check_metadata(tctx, tree, sname, (struct smb2_handle){{0}},
2137 : btime, attrib);
2138 9 : torture_assert_goto(tctx, ret, ret, done, "Bad metadata\n");
2139 :
2140 : /*
2141 : * Step X: set btime on stream, verify basefile has the same btime.
2142 : */
2143 :
2144 9 : c1 = (struct smb2_create) {
2145 : .in.desired_access = SEC_FILE_ALL,
2146 : .in.share_access = NTCREATEX_SHARE_ACCESS_MASK,
2147 : .in.file_attributes = attrib,
2148 : .in.create_disposition = NTCREATEX_DISP_OPEN,
2149 : .in.impersonation_level = SMB2_IMPERSONATION_IMPERSONATION,
2150 : .in.fname = sname,
2151 : };
2152 9 : status = smb2_create(tree, tctx, &c1);
2153 9 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2154 : "smb2_create failed\n");
2155 9 : h1 = c1.out.file.handle;
2156 :
2157 9 : setinfo = (union smb_setfileinfo) {
2158 : .basic_info.level = RAW_SFILEINFO_BASIC_INFORMATION,
2159 : .basic_info.in.file.handle = h1,
2160 : };
2161 9 : clock_gettime_mono(&ts);
2162 9 : btime = setinfo.basic_info.in.create_time = full_timespec_to_nt_time(&ts);
2163 :
2164 9 : status = smb2_setinfo_file(tree, &setinfo);
2165 9 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2166 : "smb2_setinfo_file failed\n");
2167 :
2168 9 : ret = check_metadata(tctx, tree, NULL, h1, btime, attrib);
2169 9 : torture_assert_goto(tctx, ret, ret, done, "Bad time on stream\n");
2170 :
2171 9 : status = smb2_util_close(tree, h1);
2172 9 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2173 : "smb2_util_close failed\n");
2174 9 : ZERO_STRUCT(h1);
2175 :
2176 9 : ret = check_metadata(tctx, tree, fname, (struct smb2_handle){{0}},
2177 : btime, attrib);
2178 9 : torture_assert_goto(tctx, ret, ret, done, "Bad time on basefile\n");
2179 :
2180 9 : ret = check_metadata(tctx, tree, sname, (struct smb2_handle){{0}},
2181 : btime, attrib);
2182 9 : torture_assert_goto(tctx, ret, ret, done, "Bad time on stream\n");
2183 :
2184 : /*
2185 : * Step X: write to stream, assert btime was not updated
2186 : */
2187 :
2188 9 : c1 = (struct smb2_create) {
2189 : .in.desired_access = SEC_FILE_ALL,
2190 : .in.share_access = NTCREATEX_SHARE_ACCESS_MASK,
2191 : .in.file_attributes = attrib,
2192 : .in.create_disposition = NTCREATEX_DISP_OPEN,
2193 : .in.impersonation_level = SMB2_IMPERSONATION_IMPERSONATION,
2194 : .in.fname = sname,
2195 : };
2196 9 : status = smb2_create(tree, tctx, &c1);
2197 9 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2198 : "smb2_create failed\n");
2199 9 : h1 = c1.out.file.handle;
2200 :
2201 9 : status = smb2_util_write(tree, h1, data, 0, strlen(data));
2202 9 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2203 : "smb2_util_write failed\n");
2204 :
2205 9 : ret = check_metadata(tctx, tree, NULL, h1, btime, attrib);
2206 9 : torture_assert_goto(tctx, ret, ret, done, "Bad metadata\n");
2207 :
2208 9 : status = smb2_util_close(tree, h1);
2209 9 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2210 : "smb2_util_close failed\n");
2211 9 : ZERO_STRUCT(h1);
2212 :
2213 9 : ret = check_metadata(tctx, tree, fname, (struct smb2_handle){{0}},
2214 : btime, attrib);
2215 9 : torture_assert_goto(tctx, ret, ret, done, "Bad metadata\n");
2216 :
2217 9 : ret = check_metadata(tctx, tree, sname, (struct smb2_handle){{0}},
2218 : btime, attrib);
2219 9 : torture_assert_goto(tctx, ret, ret, done, "Bad metadata\n");
2220 :
2221 : /*
2222 : * Step X: modify attributes via stream, verify it's "also" set on the
2223 : * basefile.
2224 : */
2225 :
2226 9 : c1 = (struct smb2_create) {
2227 : .in.desired_access = SEC_FILE_ALL,
2228 : .in.share_access = NTCREATEX_SHARE_ACCESS_MASK,
2229 : .in.file_attributes = attrib,
2230 : .in.create_disposition = NTCREATEX_DISP_OPEN,
2231 : .in.impersonation_level = SMB2_IMPERSONATION_IMPERSONATION,
2232 : .in.fname = sname,
2233 : };
2234 9 : status = smb2_create(tree, tctx, &c1);
2235 9 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2236 : "smb2_create failed\n");
2237 9 : h1 = c1.out.file.handle;
2238 :
2239 9 : attrib = FILE_ATTRIBUTE_NORMAL;
2240 :
2241 9 : setinfo = (union smb_setfileinfo) {
2242 : .basic_info.level = RAW_SFILEINFO_BASIC_INFORMATION,
2243 : .basic_info.in.file.handle = h1,
2244 : .basic_info.in.attrib = attrib,
2245 : };
2246 :
2247 9 : status = smb2_setinfo_file(tree, &setinfo);
2248 9 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2249 : "smb2_setinfo_file failed\n");
2250 :
2251 9 : status = smb2_util_close(tree, h1);
2252 9 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2253 : "smb2_util_close failed\n");
2254 9 : ZERO_STRUCT(h1);
2255 :
2256 9 : ret = check_metadata(tctx, tree, fname, (struct smb2_handle){{0}},
2257 : btime, attrib);
2258 9 : torture_assert_goto(tctx, ret, ret, done, "Bad metadata\n");
2259 :
2260 9 : ret = check_metadata(tctx, tree, sname, (struct smb2_handle){{0}},
2261 : btime, attrib);
2262 9 : torture_assert_goto(tctx, ret, ret, done, "Bad metadata\n");
2263 :
2264 : /*
2265 : * Step X: modify attributes via basefile, verify it's "also" set on the
2266 : * stream.
2267 : */
2268 :
2269 9 : c1 = (struct smb2_create) {
2270 : .in.desired_access = SEC_FILE_ALL,
2271 : .in.share_access = NTCREATEX_SHARE_ACCESS_MASK,
2272 : .in.file_attributes = attrib,
2273 : .in.create_disposition = NTCREATEX_DISP_OPEN,
2274 : .in.impersonation_level = SMB2_IMPERSONATION_IMPERSONATION,
2275 : .in.fname = fname,
2276 : };
2277 9 : status = smb2_create(tree, tctx, &c1);
2278 9 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2279 : "smb2_create failed\n");
2280 9 : h1 = c1.out.file.handle;
2281 :
2282 9 : attrib = FILE_ATTRIBUTE_HIDDEN;
2283 :
2284 9 : setinfo = (union smb_setfileinfo) {
2285 : .basic_info.level = RAW_SFILEINFO_BASIC_INFORMATION,
2286 : .basic_info.in.file.handle = h1,
2287 : .basic_info.in.attrib = attrib,
2288 : };
2289 :
2290 9 : status = smb2_setinfo_file(tree, &setinfo);
2291 9 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2292 : "smb2_setinfo_file failed\n");
2293 :
2294 9 : status = smb2_util_close(tree, h1);
2295 9 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2296 : "smb2_util_close failed\n");
2297 9 : ZERO_STRUCT(h1);
2298 :
2299 9 : ret = check_metadata(tctx, tree, fname, (struct smb2_handle){{0}},
2300 : btime, attrib);
2301 9 : torture_assert_goto(tctx, ret, ret, done, "Bad metadata\n");
2302 :
2303 9 : ret = check_metadata(tctx, tree, sname, (struct smb2_handle){{0}},
2304 : btime, attrib);
2305 9 : torture_assert_goto(tctx, ret, ret, done, "Bad metadata\n");
2306 :
2307 9 : done:
2308 9 : if (!smb2_util_handle_empty(h1)) {
2309 0 : smb2_util_close(tree, h1);
2310 : }
2311 :
2312 9 : smb2_deltree(tree, DNAME);
2313 :
2314 9 : return ret;
2315 : }
2316 :
2317 9 : static bool test_basefile_rename_with_open_stream(struct torture_context *tctx,
2318 : struct smb2_tree *tree)
2319 : {
2320 9 : bool ret = true;
2321 0 : NTSTATUS status;
2322 9 : struct smb2_tree *tree2 = NULL;
2323 0 : struct smb2_create create, create2;
2324 9 : struct smb2_handle h1 = {{0}}, h2 = {{0}};
2325 9 : const char *fname = "test_rename_openfile";
2326 9 : const char *sname = "test_rename_openfile:foo";
2327 9 : const char *fname_renamed = "test_rename_openfile_renamed";
2328 0 : union smb_setfileinfo sinfo;
2329 9 : const char *data = "test data";
2330 :
2331 9 : ret = torture_smb2_connection(tctx, &tree2);
2332 9 : torture_assert_goto(tctx, ret == true, ret, done,
2333 : "torture_smb2_connection failed\n");
2334 :
2335 9 : torture_comment(tctx, "Creating file with stream\n");
2336 :
2337 9 : ZERO_STRUCT(create);
2338 9 : create.in.desired_access = SEC_FILE_ALL;
2339 9 : create.in.share_access = NTCREATEX_SHARE_ACCESS_MASK;
2340 9 : create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
2341 9 : create.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
2342 9 : create.in.impersonation_level = SMB2_IMPERSONATION_IMPERSONATION;
2343 9 : create.in.fname = sname;
2344 :
2345 9 : status = smb2_create(tree, tctx, &create);
2346 9 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2347 : "smb2_create failed\n");
2348 :
2349 9 : h1 = create.out.file.handle;
2350 :
2351 9 : torture_comment(tctx, "Writing to stream\n");
2352 :
2353 9 : status = smb2_util_write(tree, h1, data, 0, strlen(data));
2354 9 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2355 : "smb2_util_write failed\n");
2356 :
2357 9 : torture_comment(tctx, "Renaming base file\n");
2358 :
2359 9 : ZERO_STRUCT(create2);
2360 9 : create2.in.desired_access = SEC_FILE_ALL;
2361 9 : create2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
2362 9 : create2.in.share_access = NTCREATEX_SHARE_ACCESS_MASK;
2363 9 : create2.in.create_disposition = NTCREATEX_DISP_OPEN;
2364 9 : create2.in.impersonation_level = SMB2_IMPERSONATION_IMPERSONATION;
2365 9 : create2.in.fname = fname;
2366 :
2367 9 : status = smb2_create(tree2, tctx, &create2);
2368 9 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2369 : "smb2_create failed\n");
2370 :
2371 9 : h2 = create2.out.file.handle;
2372 :
2373 9 : ZERO_STRUCT(sinfo);
2374 9 : sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION;
2375 9 : sinfo.rename_information.in.file.handle = h2;
2376 9 : sinfo.rename_information.in.new_name = fname_renamed;
2377 :
2378 9 : status = smb2_setinfo_file(tree2, &sinfo);
2379 9 : torture_assert_ntstatus_equal_goto(
2380 : tctx, status, NT_STATUS_ACCESS_DENIED, ret, done,
2381 : "smb2_setinfo_file didn't return NT_STATUS_ACCESS_DENIED\n");
2382 :
2383 9 : smb2_util_close(tree2, h2);
2384 :
2385 9 : done:
2386 9 : if (!smb2_util_handle_empty(h1)) {
2387 9 : smb2_util_close(tree, h1);
2388 : }
2389 9 : if (!smb2_util_handle_empty(h2)) {
2390 9 : smb2_util_close(tree2, h2);
2391 : }
2392 9 : smb2_util_unlink(tree, fname);
2393 9 : smb2_util_unlink(tree, fname_renamed);
2394 :
2395 9 : return ret;
2396 : }
2397 :
2398 : /*
2399 : basic testing of streams calls SMB2
2400 : */
2401 2354 : struct torture_suite *torture_smb2_streams_init(TALLOC_CTX *ctx)
2402 : {
2403 125 : struct torture_suite *suite =
2404 2354 : torture_suite_create(ctx, "streams");
2405 :
2406 2354 : torture_suite_add_1smb2_test(suite, "dir", test_stream_dir);
2407 2354 : torture_suite_add_1smb2_test(suite, "io", test_stream_io);
2408 2354 : torture_suite_add_1smb2_test(suite, "sharemodes", test_stream_sharemodes);
2409 2354 : torture_suite_add_1smb2_test(suite, "names", test_stream_names);
2410 2354 : torture_suite_add_1smb2_test(suite, "names2", test_stream_names2);
2411 2354 : torture_suite_add_1smb2_test(suite, "names3", test_stream_names3);
2412 2354 : torture_suite_add_1smb2_test(suite, "rename", test_stream_rename);
2413 2354 : torture_suite_add_1smb2_test(suite, "rename2", test_stream_rename2);
2414 2354 : torture_suite_add_1smb2_test(suite, "create-disposition", test_stream_create_disposition);
2415 2354 : torture_suite_add_1smb2_test(suite, "attributes1", test_stream_attributes1);
2416 2354 : torture_suite_add_1smb2_test(suite, "attributes2", test_stream_attributes2);
2417 2354 : torture_suite_add_1smb2_test(suite, "delete", test_stream_delete);
2418 2354 : torture_suite_add_1smb2_test(suite, "zero-byte", test_zero_byte_stream);
2419 2354 : torture_suite_add_1smb2_test(suite, "basefile-rename-with-open-stream",
2420 : test_basefile_rename_with_open_stream);
2421 :
2422 2354 : suite->description = talloc_strdup(suite, "SMB2-STREAM tests");
2423 2354 : return suite;
2424 : }
|