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 16 : static int qsort_string(char * const *s1, char * const *s2)
86 : {
87 16 : return strcmp(*s1, *s2);
88 : }
89 :
90 16 : static int qsort_stream(const struct stream_struct * s1, const struct stream_struct *s2)
91 : {
92 16 : return strcmp(s1->stream_name.s, s2->stream_name.s);
93 : }
94 :
95 11 : 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 : struct smb2_handle handle;
104 : struct smb2_create create;
105 : struct smb2_read r;
106 : NTSTATUS status;
107 : const char *full_name;
108 :
109 11 : full_name = talloc_asprintf(mem_ctx, "%s:%s", fname, sname);
110 :
111 11 : ZERO_STRUCT(create);
112 11 : create.in.desired_access = SEC_RIGHTS_FILE_ALL;
113 11 : create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
114 11 : create.in.create_disposition = NTCREATEX_DISP_OPEN;
115 11 : create.in.fname = full_name;
116 :
117 11 : status = smb2_create(tree, mem_ctx, &create);
118 11 : if (!NT_STATUS_IS_OK(status)) {
119 5 : if (value == NULL) {
120 5 : 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 6 : handle = create.out.file.handle;
129 6 : if (value == NULL) {
130 0 : return true;
131 : }
132 :
133 :
134 6 : ZERO_STRUCT(r);
135 6 : r.in.file.handle = handle;
136 6 : r.in.length = strlen(value)+11;
137 6 : r.in.offset = 0;
138 :
139 6 : status = smb2_read(tree, tree, &r);
140 :
141 6 : 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 6 : 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 6 : smb2_util_close(tree, handle);
153 6 : return true;
154 : }
155 :
156 9 : 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 : union smb_fileinfo finfo;
164 : NTSTATUS status;
165 : unsigned int i;
166 9 : TALLOC_CTX *tmp_ctx = talloc_new(tctx);
167 : char **exp_sort;
168 : struct stream_struct *stream_sort;
169 9 : bool ret = false;
170 :
171 9 : finfo.generic.level = RAW_FILEINFO_STREAM_INFORMATION;
172 9 : finfo.generic.in.file.handle = h;
173 :
174 9 : status = smb2_getinfo_file(tree, tctx, &finfo);
175 9 : if (!NT_STATUS_IS_OK(status)) {
176 0 : torture_comment(tctx, "(%s) smb_raw_pathinfo failed: %s\n",
177 : __location__, nt_errstr(status));
178 0 : goto fail;
179 : }
180 :
181 9 : 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 8 : if (num_exp == 0) {
188 1 : ret = true;
189 1 : goto fail;
190 : }
191 :
192 7 : exp_sort = talloc_memdup(tmp_ctx, exp, num_exp * sizeof(*exp));
193 :
194 7 : if (exp_sort == NULL) {
195 0 : goto fail;
196 : }
197 :
198 7 : TYPESAFE_QSORT(exp_sort, num_exp, qsort_string);
199 :
200 7 : stream_sort = talloc_memdup(tmp_ctx, finfo.stream_info.out.streams,
201 : finfo.stream_info.out.num_streams *
202 : sizeof(*stream_sort));
203 :
204 7 : if (stream_sort == NULL) {
205 0 : goto fail;
206 : }
207 :
208 7 : TYPESAFE_QSORT(stream_sort, finfo.stream_info.out.num_streams, qsort_stream);
209 :
210 23 : for (i=0; i<num_exp; i++) {
211 16 : 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 7 : ret = true;
221 9 : fail:
222 9 : talloc_free(tmp_ctx);
223 9 : return ret;
224 : }
225 :
226 :
227 1 : static bool test_stream_dir(struct torture_context *tctx,
228 : struct smb2_tree *tree)
229 : {
230 1 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
231 : NTSTATUS status;
232 : union smb_open io;
233 1 : const char *fname = DNAME "\\stream.txt";
234 : const char *sname1;
235 1 : bool ret = true;
236 : const char *basedir_data;
237 : struct smb2_handle h;
238 :
239 1 : smb2_util_unlink(tree, fname);
240 1 : smb2_deltree(tree, DNAME);
241 :
242 1 : status = torture_smb2_testdir(tree, DNAME, &h);
243 1 : CHECK_STATUS(status, NT_STATUS_OK);
244 :
245 1 : basedir_data = talloc_asprintf(mem_ctx, "%s::$DATA", DNAME);
246 1 : sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname, "Stream One");
247 1 : torture_comment(tctx, "%s\n", sname1);
248 :
249 1 : torture_comment(tctx, "(%s) opening non-existent directory stream\n",
250 : __location__);
251 1 : ZERO_STRUCT(io.smb2);
252 1 : io.smb2.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
253 1 : io.smb2.in.desired_access = SEC_FILE_WRITE_DATA;
254 1 : io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
255 1 : io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
256 1 : io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
257 1 : io.smb2.in.share_access = 0;
258 1 : io.smb2.in.alloc_size = 0;
259 1 : io.smb2.in.security_flags = 0;
260 1 : io.smb2.in.fname = sname1;
261 1 : io.smb2.in.create_flags = 0;
262 1 : status = smb2_create(tree, mem_ctx, &(io.smb2));
263 1 : CHECK_STATUS(status, NT_STATUS_NOT_A_DIRECTORY);
264 :
265 1 : torture_comment(tctx, "(%s) opening basedir stream\n", __location__);
266 1 : ZERO_STRUCT(io.smb2);
267 1 : io.smb2.in.create_flags = 0;
268 1 : io.smb2.in.desired_access = SEC_FILE_WRITE_DATA;
269 1 : io.smb2.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
270 1 : io.smb2.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
271 1 : io.smb2.in.share_access = 0;
272 1 : io.smb2.in.alloc_size = 0;
273 1 : io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
274 1 : io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
275 1 : io.smb2.in.security_flags = 0;
276 1 : io.smb2.in.fname = basedir_data;
277 1 : status = smb2_create(tree, mem_ctx, &(io.smb2));
278 1 : CHECK_STATUS(status, NT_STATUS_NOT_A_DIRECTORY);
279 :
280 1 : torture_comment(tctx, "(%s) opening basedir ::$DATA stream\n",
281 : __location__);
282 1 : ZERO_STRUCT(io.smb2);
283 1 : io.smb2.in.create_flags = 0x10;
284 1 : io.smb2.in.desired_access = SEC_FILE_WRITE_DATA;
285 1 : io.smb2.in.create_options = 0;
286 1 : io.smb2.in.file_attributes = 0;
287 1 : io.smb2.in.share_access = 0;
288 1 : io.smb2.in.alloc_size = 0;
289 1 : io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
290 1 : io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
291 1 : io.smb2.in.security_flags = 0;
292 1 : io.smb2.in.fname = basedir_data;
293 1 : status = smb2_create(tree, mem_ctx, &(io.smb2));
294 1 : CHECK_STATUS(status, NT_STATUS_FILE_IS_A_DIRECTORY);
295 :
296 1 : torture_comment(tctx, "(%s) list the streams on the basedir\n",
297 : __location__);
298 1 : ret &= check_stream_list(tree, mem_ctx, DNAME, 0, NULL, h);
299 1 : done:
300 1 : smb2_util_unlink(tree, fname);
301 1 : smb2_deltree(tree, DNAME);
302 1 : talloc_free(mem_ctx);
303 :
304 1 : return ret;
305 : }
306 :
307 1 : static bool test_stream_io(struct torture_context *tctx,
308 : struct smb2_tree *tree)
309 : {
310 1 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
311 : NTSTATUS status;
312 : union smb_open io;
313 1 : const char *fname = DNAME "\\stream.txt";
314 : const char *sname1, *sname2;
315 1 : bool ret = true;
316 : struct smb2_handle h, h2;
317 :
318 1 : const char *one[] = { "::$DATA" };
319 1 : const char *two[] = { "::$DATA", ":Second Stream:$DATA" };
320 1 : const char *three[] = { "::$DATA", ":Stream One:$DATA",
321 : ":Second Stream:$DATA" };
322 :
323 1 : ZERO_STRUCT(h);
324 1 : ZERO_STRUCT(h2);
325 :
326 1 : sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname, "Stream One");
327 1 : sname2 = talloc_asprintf(mem_ctx, "%s:%s:$DaTa", fname,
328 : "Second Stream");
329 :
330 1 : smb2_util_unlink(tree, fname);
331 1 : smb2_deltree(tree, DNAME);
332 :
333 1 : status = torture_smb2_testdir(tree, DNAME, &h);
334 1 : CHECK_STATUS(status, NT_STATUS_OK);
335 :
336 1 : torture_comment(tctx, "(%s) creating a stream on a non-existent file\n",
337 : __location__);
338 :
339 1 : ZERO_STRUCT(io.smb2);
340 1 : io.smb2.in.create_flags = 0;
341 1 : io.smb2.in.desired_access = SEC_FILE_WRITE_DATA;
342 1 : io.smb2.in.create_options = 0;
343 1 : io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
344 1 : io.smb2.in.share_access = 0;
345 1 : io.smb2.in.alloc_size = 0;
346 1 : io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
347 1 : io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
348 1 : io.smb2.in.security_flags = 0;
349 1 : io.smb2.in.fname = sname1;
350 1 : status = smb2_create(tree, mem_ctx, &(io.smb2));
351 1 : CHECK_STATUS(status, NT_STATUS_OK);
352 1 : h2 = io.smb2.out.file.handle;
353 :
354 1 : ret &= check_stream(tctx, tree, __location__, mem_ctx, fname,
355 : "Stream One", NULL);
356 :
357 1 : torture_comment(tctx, "(%s) check that open of base file is allowed\n", __location__);
358 1 : io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
359 1 : io.smb2.in.fname = fname;
360 1 : status = smb2_create(tree, mem_ctx, &(io.smb2));
361 1 : CHECK_STATUS(status, NT_STATUS_OK);
362 1 : smb2_util_close(tree, io.smb2.out.file.handle);
363 :
364 1 : torture_comment(tctx, "(%s) writing to stream\n", __location__);
365 1 : status = smb2_util_write(tree, h2, "test data", 0, 9);
366 1 : CHECK_STATUS(status, NT_STATUS_OK);
367 :
368 1 : smb2_util_close(tree, h2);
369 :
370 1 : ret &= check_stream(tctx, tree, __location__, mem_ctx, fname,
371 : "Stream One", "test data");
372 :
373 1 : io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
374 1 : io.smb2.in.fname = sname1;
375 1 : status = smb2_create(tree, mem_ctx, &(io.smb2));
376 1 : CHECK_STATUS(status, NT_STATUS_OK);
377 1 : h2 = io.smb2.out.file.handle;
378 :
379 1 : torture_comment(tctx, "(%s) modifying stream\n", __location__);
380 1 : status = smb2_util_write(tree, h2, "MORE DATA ", 5, 10);
381 1 : CHECK_STATUS(status, NT_STATUS_OK);
382 :
383 1 : smb2_util_close(tree, h2);
384 :
385 1 : ret &= check_stream(tctx, tree, __location__, mem_ctx, fname,
386 : "Stream One:$FOO", NULL);
387 :
388 1 : torture_comment(tctx, "(%s) creating a stream2 on a existing file\n",
389 : __location__);
390 1 : io.smb2.in.fname = sname2;
391 1 : io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
392 1 : status = smb2_create(tree, mem_ctx, &(io.smb2));
393 1 : CHECK_STATUS(status, NT_STATUS_OK);
394 1 : h2 = io.smb2.out.file.handle;
395 :
396 1 : torture_comment(tctx, "(%s) modifying stream\n", __location__);
397 1 : status= smb2_util_write(tree, h2, "SECOND STREAM", 0, 13);
398 1 : CHECK_STATUS(status, NT_STATUS_OK);
399 1 : smb2_util_close(tree, h2);
400 :
401 1 : ret &= check_stream(tctx, tree, __location__, mem_ctx, fname,
402 : "Stream One", "test MORE DATA ");
403 :
404 1 : ret &= check_stream(tctx, tree, __location__, mem_ctx, fname,
405 : "Stream One:$DATA", "test MORE DATA ");
406 :
407 1 : ret &= check_stream(tctx, tree, __location__, mem_ctx, fname,
408 : "Stream One:", NULL);
409 :
410 1 : if (!ret) {
411 0 : torture_result(tctx, TORTURE_FAIL,
412 : "check_stream(\"Stream One:*\") failed\n");
413 0 : goto done;
414 : }
415 :
416 1 : ret &= check_stream(tctx, tree, __location__, mem_ctx, fname,
417 : "Second Stream", "SECOND STREAM");
418 :
419 1 : ret &= check_stream(tctx, tree, __location__, mem_ctx, fname,
420 : "SECOND STREAM:$DATA", "SECOND STREAM");
421 1 : ret &= check_stream(tctx, tree, __location__, mem_ctx, fname,
422 : "Second Stream:$DATA", "SECOND STREAM");
423 :
424 1 : ret &= check_stream(tctx, tree, __location__, mem_ctx, fname,
425 : "Second Stream:", NULL);
426 :
427 1 : ret &= check_stream(tctx, tree, __location__, mem_ctx, fname,
428 : "Second Stream:$FOO", NULL);
429 :
430 1 : if (!ret) {
431 0 : torture_result(tctx, TORTURE_FAIL,
432 : "check_stream(\"Second Stream:*\") failed\n");
433 0 : goto done;
434 : }
435 :
436 1 : io.smb2.in.fname = sname2;
437 1 : io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
438 1 : status = smb2_create(tree, mem_ctx, &(io.smb2));
439 1 : CHECK_STATUS(status, NT_STATUS_OK);
440 1 : h2 = io.smb2.out.file.handle;
441 1 : check_stream_list(tree, tctx, fname, 3, three, h2);
442 :
443 1 : smb2_util_close(tree, h2);
444 :
445 1 : torture_comment(tctx, "(%s) deleting stream\n", __location__);
446 1 : status = smb2_util_unlink(tree, sname1);
447 1 : CHECK_STATUS(status, NT_STATUS_OK);
448 :
449 1 : io.smb2.in.fname = sname2;
450 1 : io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
451 1 : status = smb2_create(tree, mem_ctx, &(io.smb2));
452 1 : CHECK_STATUS(status, NT_STATUS_OK);
453 1 : h2 = io.smb2.out.file.handle;
454 1 : check_stream_list(tree, tctx, fname, 2, two, h2);
455 1 : smb2_util_close(tree, h2);
456 :
457 1 : torture_comment(tctx, "(%s) delete a stream via delete-on-close\n",
458 : __location__);
459 1 : io.smb2.in.fname = sname2;
460 1 : io.smb2.in.create_options = NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
461 1 : io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE;
462 1 : io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL;
463 1 : io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
464 :
465 1 : status = smb2_create(tree, mem_ctx, &(io.smb2));
466 1 : CHECK_STATUS(status, NT_STATUS_OK);
467 1 : h2 = io.smb2.out.file.handle;
468 :
469 1 : smb2_util_close(tree, h2);
470 1 : status = smb2_util_unlink(tree, sname2);
471 1 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
472 :
473 1 : io.smb2.in.fname = fname;
474 1 : io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
475 1 : status = smb2_create(tree, mem_ctx, &(io.smb2));
476 1 : h2 = io.smb2.out.file.handle;
477 1 : check_stream_list(tree,tctx, fname, 1, one, h2);
478 1 : smb2_util_close(tree, h2);
479 :
480 1 : if (!torture_setting_bool(tctx, "samba4", false)) {
481 0 : io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
482 0 : io.smb2.in.fname = sname1;
483 0 : status = smb2_create(tree, mem_ctx, &(io.smb2));
484 0 : CHECK_STATUS(status, NT_STATUS_OK);
485 0 : smb2_util_close(tree, io.ntcreatex.out.file.handle);
486 0 : io.smb2.in.fname = sname2;
487 0 : status = smb2_create(tree, mem_ctx, &(io.smb2));
488 0 : CHECK_STATUS(status, NT_STATUS_OK);
489 0 : smb2_util_close(tree, io.ntcreatex.out.file.handle);
490 0 : torture_comment(tctx, "(%s) deleting file\n", __location__);
491 0 : status = smb2_util_unlink(tree, fname);
492 0 : CHECK_STATUS(status, NT_STATUS_OK);
493 : }
494 :
495 :
496 2 : done:
497 1 : smb2_util_close(tree, h2);
498 1 : smb2_deltree(tree, DNAME);
499 1 : talloc_free(mem_ctx);
500 :
501 1 : return ret;
502 : }
503 :
504 1 : static bool test_zero_byte_stream(struct torture_context *tctx,
505 : struct smb2_tree *tree)
506 : {
507 1 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
508 : NTSTATUS status;
509 : union smb_open io;
510 1 : const char *fname = DNAME "\\stream.txt";
511 : const char *sname;
512 1 : bool ret = true;
513 : struct smb2_handle h, bh;
514 1 : const char *streams[] = { "::$DATA", ":foo:$DATA" };
515 :
516 1 : sname = talloc_asprintf(mem_ctx, "%s:%s", fname, "foo");
517 :
518 1 : smb2_util_unlink(tree, fname);
519 1 : smb2_deltree(tree, DNAME);
520 :
521 1 : status = torture_smb2_testdir(tree, DNAME, &h);
522 1 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "testdir");
523 1 : smb2_util_close(tree, h);
524 :
525 1 : torture_comment(tctx, "(%s) Check 0 byte named stream\n",
526 : __location__);
527 :
528 : /* Create basefile */
529 1 : ZERO_STRUCT(io);
530 1 : io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
531 1 : io.smb2.in.fname = fname;
532 1 : 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 1 : status = smb2_create(tree, mem_ctx, &(io.smb2));
537 1 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "create");
538 1 : smb2_util_close(tree, io.smb2.out.file.handle);
539 :
540 : /* Create named stream and close it */
541 1 : ZERO_STRUCT(io);
542 1 : io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
543 1 : io.smb2.in.fname = sname;
544 1 : 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 1 : status = smb2_create(tree, mem_ctx, &(io.smb2));
549 1 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "create");
550 1 : 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 1 : ZERO_STRUCT(io);
557 1 : io.smb2.in.fname = fname;
558 1 : io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
559 1 : 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 1 : status = smb2_create(tree, mem_ctx, &(io.smb2));
564 1 : bh = io.smb2.out.file.handle;
565 :
566 1 : ret = check_stream_list(tree,tctx, fname, 2, streams, bh);
567 1 : torture_assert_goto(tctx, ret == true, ret, done, "smb2_create");
568 1 : smb2_util_close(tree, bh);
569 :
570 1 : done:
571 1 : smb2_deltree(tree, DNAME);
572 1 : talloc_free(mem_ctx);
573 :
574 1 : return ret;
575 : }
576 :
577 : /*
578 : test stream sharemodes
579 : */
580 1 : static bool test_stream_sharemodes(struct torture_context *tctx,
581 : struct smb2_tree *tree)
582 : {
583 1 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
584 : NTSTATUS status;
585 : union smb_open io;
586 1 : const char *fname = DNAME "\\stream_share.txt";
587 : const char *sname1, *sname2;
588 1 : bool ret = true;
589 : struct smb2_handle h, h1, h2;
590 :
591 1 : ZERO_STRUCT(h);
592 1 : ZERO_STRUCT(h1);
593 1 : ZERO_STRUCT(h2);
594 :
595 1 : sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname, "Stream One");
596 1 : sname2 = talloc_asprintf(mem_ctx, "%s:%s:$DaTa", fname,
597 : "Second Stream");
598 :
599 1 : smb2_util_unlink(tree, fname);
600 1 : smb2_deltree(tree, DNAME);
601 :
602 1 : status = torture_smb2_testdir(tree, DNAME, &h);
603 1 : CHECK_STATUS(status, NT_STATUS_OK);
604 :
605 1 : torture_comment(tctx, "(%s) Testing stream share mode conflicts\n",
606 : __location__);
607 1 : ZERO_STRUCT(io.smb2);
608 1 : io.generic.level = RAW_OPEN_SMB2;
609 1 : io.smb2.in.create_flags = 0;
610 1 : io.smb2.in.desired_access = SEC_FILE_WRITE_DATA;
611 1 : io.smb2.in.create_options = 0;
612 1 : io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
613 1 : io.smb2.in.share_access = 0;
614 1 : io.smb2.in.alloc_size = 0;
615 1 : io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
616 1 : io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
617 1 : io.smb2.in.security_flags = 0;
618 1 : io.smb2.in.fname = sname1;
619 :
620 1 : status = smb2_create(tree, mem_ctx, &(io.smb2));
621 1 : CHECK_STATUS(status, NT_STATUS_OK);
622 1 : h1 = io.smb2.out.file.handle;
623 :
624 : /*
625 : * A different stream does not give a sharing violation
626 : */
627 :
628 1 : io.smb2.in.fname = sname2;
629 1 : status = smb2_create(tree, mem_ctx, &(io.smb2));
630 1 : CHECK_STATUS(status, NT_STATUS_OK);
631 1 : h2 = io.smb2.out.file.handle;
632 :
633 : /*
634 : * ... whereas the same stream does with unchanged access/share_access
635 : * flags
636 : */
637 :
638 1 : io.smb2.in.fname = sname1;
639 1 : io.smb2.in.create_disposition = 0;
640 1 : status = smb2_create(tree, mem_ctx, &(io.smb2));
641 1 : CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
642 :
643 1 : io.smb2.in.fname = sname2;
644 1 : status = smb2_create(tree, mem_ctx, &(io.smb2));
645 1 : CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
646 :
647 2 : done:
648 1 : smb2_util_close(tree, h1);
649 1 : smb2_util_close(tree, h2);
650 1 : status = smb2_util_unlink(tree, fname);
651 1 : smb2_deltree(tree, DNAME);
652 1 : talloc_free(mem_ctx);
653 :
654 1 : 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 1 : static bool test_stream_delete(struct torture_context *tctx,
684 : struct smb2_tree *tree)
685 : {
686 1 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
687 : NTSTATUS status;
688 : union smb_open io;
689 1 : const char *fname = DNAME "\\stream_delete.txt";
690 : const char *sname1;
691 1 : bool ret = true;
692 1 : struct smb2_handle h = {{0}};
693 1 : struct smb2_handle h1 = {{0}};
694 : struct smb2_read r;
695 :
696 1 : 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 0 : ZERO_STRUCT(h);
702 0 : ZERO_STRUCT(h1);
703 :
704 0 : sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname, "Stream One");
705 :
706 : /* clean slate .. */
707 0 : smb2_util_unlink(tree, fname);
708 0 : smb2_deltree(tree, fname);
709 0 : smb2_deltree(tree, DNAME);
710 :
711 0 : status = torture_smb2_testdir(tree, DNAME, &h);
712 0 : CHECK_STATUS(status, NT_STATUS_OK);
713 :
714 0 : torture_comment(tctx, "(%s) opening non-existent file stream\n",
715 : __location__);
716 0 : ZERO_STRUCT(io.smb2);
717 0 : io.smb2.in.create_flags = 0;
718 0 : io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL;
719 0 : io.smb2.in.create_options = 0;
720 0 : io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
721 0 : io.smb2.in.share_access = 0;
722 0 : io.smb2.in.alloc_size = 0;
723 0 : io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
724 0 : io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
725 0 : io.smb2.in.security_flags = 0;
726 0 : io.smb2.in.fname = sname1;
727 :
728 0 : status = smb2_create(tree, mem_ctx, &(io.smb2));
729 0 : CHECK_STATUS(status, NT_STATUS_OK);
730 0 : h1 = io.smb2.out.file.handle;
731 :
732 0 : status = smb2_util_write(tree, h1, "test data", 0, 9);
733 0 : 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 0 : status = smb2_util_unlink(tree, fname);
741 0 : CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
742 :
743 0 : io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
744 0 : io.smb2.in.fname = fname;
745 0 : io.smb2.in.desired_access = SEC_STD_DELETE;
746 0 : status = smb2_create(tree, mem_ctx, &(io.smb2));
747 0 : CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
748 :
749 0 : smb2_util_close(tree, h1);
750 :
751 : /*
752 : * ... but unlink works if a stream is opened with FILE_SHARE_DELETE
753 : */
754 :
755 0 : io.smb2.in.fname = sname1;
756 0 : io.smb2.in.desired_access = SEC_FILE_READ_DATA|SEC_FILE_WRITE_DATA;
757 0 : io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE |
758 : NTCREATEX_SHARE_ACCESS_READ |
759 : NTCREATEX_SHARE_ACCESS_WRITE;
760 0 : status = smb2_create(tree, mem_ctx, &(io.smb2));
761 0 : CHECK_STATUS(status, NT_STATUS_OK);
762 0 : h1 = io.smb2.out.file.handle;
763 :
764 0 : status = smb2_util_unlink(tree, fname);
765 0 : CHECK_STATUS(status, NT_STATUS_OK);
766 :
767 : /*
768 : * file access still works on the stream while the main file is closed
769 : */
770 0 : ZERO_STRUCT(r);
771 0 : r.in.file.handle = h1;
772 0 : r.in.length = 9;
773 0 : r.in.offset = 0;
774 :
775 0 : status = smb2_read(tree, tree, &r);
776 0 : 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 0 : io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
784 0 : io.smb2.in.fname = fname;
785 0 : status = smb2_create(tree, mem_ctx, &(io.smb2));
786 0 : CHECK_STATUS(status, NT_STATUS_DELETE_PENDING);
787 :
788 : /*
789 : * older S3 doesn't do this
790 : */
791 :
792 0 : io.smb2.in.fname = sname1;
793 0 : status = smb2_create(tree, mem_ctx, &(io.smb2));
794 0 : CHECK_STATUS(status, NT_STATUS_DELETE_PENDING);
795 :
796 0 : smb2_util_close(tree, h1);
797 0 : ZERO_STRUCT(h1);
798 :
799 : /*
800 : * After closing the stream the file is really gone.
801 : */
802 :
803 0 : io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
804 0 : io.smb2.in.fname = fname;
805 0 : status = smb2_create(tree, mem_ctx, &(io.smb2));
806 0 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
807 :
808 1 : done:
809 1 : if (!smb2_util_handle_empty(h1)) {
810 0 : smb2_util_close(tree, h1);
811 : }
812 1 : smb2_util_unlink(tree, fname);
813 1 : smb2_deltree(tree, DNAME);
814 1 : talloc_free(mem_ctx);
815 :
816 1 : return ret;
817 : }
818 :
819 : /*
820 : test stream names
821 : */
822 1 : static bool test_stream_names(struct torture_context *tctx,
823 : struct smb2_tree *tree)
824 : {
825 1 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
826 : NTSTATUS status;
827 : union smb_open io;
828 : union smb_fileinfo finfo;
829 : union smb_fileinfo stinfo;
830 : union smb_setfileinfo sinfo;
831 1 : const char *fname = DNAME "\\stream_names.txt";
832 : const char *sname1, *sname1b, *sname1c, *sname1d;
833 : const char *sname2, *snamew, *snamew2;
834 : const char *snamer1;
835 1 : bool ret = true;
836 : struct smb2_handle h, h1, h2, h3;
837 : int i;
838 1 : const char *four[4] = {
839 : "::$DATA",
840 : ":\x05Stream\n One:$DATA",
841 : ":MStream Two:$DATA",
842 : ":?Stream*:$DATA"
843 : };
844 1 : const char *five1[5] = {
845 : "::$DATA",
846 : ":\x05Stream\n One:$DATA",
847 : ":BeforeRename:$DATA",
848 : ":MStream Two:$DATA",
849 : ":?Stream*:$DATA"
850 : };
851 1 : const char *five2[5] = {
852 : "::$DATA",
853 : ":\x05Stream\n One:$DATA",
854 : ":AfterRename:$DATA",
855 : ":MStream Two:$DATA",
856 : ":?Stream*:$DATA"
857 : };
858 :
859 1 : ZERO_STRUCT(h);
860 1 : ZERO_STRUCT(h1);
861 1 : ZERO_STRUCT(h2);
862 1 : ZERO_STRUCT(h3);
863 :
864 1 : sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname, "\x05Stream\n One");
865 1 : sname1b = talloc_asprintf(mem_ctx, "%s:", sname1);
866 1 : sname1c = talloc_asprintf(mem_ctx, "%s:$FOO", sname1);
867 1 : sname1d = talloc_asprintf(mem_ctx, "%s:?D*a", sname1);
868 1 : sname2 = talloc_asprintf(mem_ctx, "%s:%s:$DaTa", fname, "MStream Two");
869 1 : snamew = talloc_asprintf(mem_ctx, "%s:%s:$DATA", fname, "?Stream*");
870 1 : snamew2 = talloc_asprintf(mem_ctx, "%s\\stream*:%s:$DATA", DNAME,
871 : "?Stream*");
872 1 : snamer1 = talloc_asprintf(mem_ctx, "%s:%s:$DATA", fname,
873 : "BeforeRename");
874 :
875 : /* clean slate ...*/
876 1 : smb2_util_unlink(tree, fname);
877 1 : smb2_deltree(tree, fname);
878 1 : smb2_deltree(tree, DNAME);
879 :
880 1 : status = torture_smb2_testdir(tree, DNAME, &h);
881 1 : CHECK_STATUS(status, NT_STATUS_OK);
882 :
883 1 : torture_comment(tctx, "(%s) testing stream names\n", __location__);
884 1 : ZERO_STRUCT(io.smb2);
885 1 : io.smb2.in.create_flags = 0;
886 1 : io.smb2.in.desired_access = SEC_FILE_WRITE_DATA;
887 1 : io.smb2.in.create_options = 0;
888 1 : io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
889 1 : io.smb2.in.share_access = 0;
890 1 : io.smb2.in.alloc_size = 0;
891 1 : io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
892 1 : io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
893 1 : io.smb2.in.security_flags = 0;
894 1 : io.smb2.in.fname = sname1;
895 :
896 1 : status = smb2_create(tree, mem_ctx, &(io.smb2));
897 1 : CHECK_STATUS(status, NT_STATUS_OK);
898 1 : h1 = io.smb2.out.file.handle;
899 :
900 : /*
901 : * A different stream does not give a sharing violation
902 : */
903 :
904 1 : io.smb2.in.fname = sname2;
905 1 : io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
906 1 : status = smb2_create(tree, mem_ctx, &(io.smb2));
907 1 : CHECK_STATUS(status, NT_STATUS_OK);
908 1 : h2 = io.smb2.out.file.handle;
909 :
910 : /*
911 : * ... whereas the same stream does with unchanged access/share_access
912 : * flags
913 : */
914 :
915 1 : io.smb2.in.fname = sname1;
916 1 : io.smb2.in.create_disposition = NTCREATEX_DISP_SUPERSEDE;
917 1 : status = smb2_create(tree, mem_ctx, &(io.smb2));
918 1 : CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
919 :
920 1 : io.smb2.in.fname = sname1b;
921 1 : status = smb2_create(tree, mem_ctx, &(io.smb2));
922 1 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
923 :
924 1 : io.smb2.in.fname = sname1c;
925 1 : status = smb2_create(tree, mem_ctx, &(io.smb2));
926 1 : if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
927 : /* w2k returns INVALID_PARAMETER */
928 1 : CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
929 : } else {
930 0 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
931 : }
932 :
933 1 : io.smb2.in.fname = sname1d;
934 1 : status = smb2_create(tree, mem_ctx, &(io.smb2));
935 1 : if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
936 : /* w2k returns INVALID_PARAMETER */
937 1 : CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
938 : } else {
939 0 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
940 : }
941 :
942 1 : io.smb2.in.fname = sname2;
943 1 : status = smb2_create(tree, mem_ctx, &(io.smb2));
944 1 : CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
945 :
946 1 : io.smb2.in.fname = snamew;
947 1 : status = smb2_create(tree, mem_ctx, &(io.smb2));
948 1 : CHECK_STATUS(status, NT_STATUS_OK);
949 1 : h3 = io.smb2.out.file.handle;
950 :
951 1 : io.smb2.in.fname = snamew2;
952 1 : status = smb2_create(tree, mem_ctx, &(io.smb2));
953 1 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
954 :
955 1 : io.smb2.in.fname = fname;
956 1 : io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
957 1 : status = smb2_create(tree, mem_ctx, &(io.smb2));
958 1 : CHECK_STATUS(status, NT_STATUS_OK);
959 1 : ret &= check_stream_list(tree, tctx, fname, 4, four,
960 : io.smb2.out.file.handle);
961 1 : CHECK_VALUE(ret, true);
962 1 : smb2_util_close(tree, h1);
963 1 : smb2_util_close(tree, h2);
964 1 : smb2_util_close(tree, h3);
965 :
966 1 : if (torture_setting_bool(tctx, "samba4", true)) {
967 1 : 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 : NTTIME write_time;
980 : 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 1 : done:
1148 1 : smb2_util_close(tree, h1);
1149 1 : status = smb2_util_unlink(tree, fname);
1150 1 : smb2_deltree(tree, DNAME);
1151 1 : talloc_free(mem_ctx);
1152 :
1153 1 : return ret;
1154 : }
1155 :
1156 : /*
1157 : test stream names
1158 : */
1159 1 : static bool test_stream_names2(struct torture_context *tctx,
1160 : struct smb2_tree *tree)
1161 : {
1162 1 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
1163 : NTSTATUS status;
1164 : union smb_open io;
1165 1 : const char *fname = DNAME "\\stream_names2.txt";
1166 1 : bool ret = true;
1167 1 : struct smb2_handle h = {{0}};
1168 1 : struct smb2_handle h1 = {{0}};
1169 : uint8_t i;
1170 :
1171 1 : smb2_util_unlink(tree, fname);
1172 1 : smb2_deltree(tree, DNAME);
1173 :
1174 1 : status = torture_smb2_testdir(tree, DNAME, &h);
1175 1 : CHECK_STATUS(status, NT_STATUS_OK);
1176 :
1177 1 : torture_comment(tctx, "(%s) testing stream names\n", __location__);
1178 1 : ZERO_STRUCT(io.smb2);
1179 1 : io.smb2.in.create_flags = 0;
1180 1 : io.smb2.in.desired_access = SEC_FILE_WRITE_DATA;
1181 1 : io.smb2.in.create_options = 0;
1182 1 : io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1183 1 : io.smb2.in.share_access = 0;
1184 1 : io.smb2.in.alloc_size = 0;
1185 1 : io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
1186 1 : io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
1187 1 : io.smb2.in.security_flags = 0;
1188 1 : io.smb2.in.fname = fname;
1189 1 : status = smb2_create(tree, mem_ctx, &(io.smb2));
1190 1 : CHECK_STATUS(status, NT_STATUS_OK);
1191 1 : h1 = io.smb2.out.file.handle;
1192 :
1193 127 : for (i=0x01; i < 0x7F; i++) {
1194 126 : char *path = talloc_asprintf(mem_ctx, "%s:Stream%c0x%02X:$DATA",
1195 : fname, i, i);
1196 : NTSTATUS expected;
1197 :
1198 126 : switch (i) {
1199 3 : case '/':/*0x2F*/
1200 : case ':':/*0x3A*/
1201 : case '\\':/*0x5C*/
1202 3 : expected = NT_STATUS_OBJECT_NAME_INVALID;
1203 3 : break;
1204 123 : default:
1205 123 : expected = NT_STATUS_OBJECT_NAME_NOT_FOUND;
1206 123 : break;
1207 : }
1208 :
1209 :
1210 126 : io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
1211 126 : io.smb2.in.fname = path;
1212 126 : status = smb2_create(tree, mem_ctx, &(io.smb2));
1213 126 : 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 126 : CHECK_STATUS(status, expected);
1221 :
1222 126 : talloc_free(path);
1223 : }
1224 :
1225 1 : done:
1226 1 : smb2_util_close(tree, h1);
1227 1 : status = smb2_util_unlink(tree, fname);
1228 1 : smb2_deltree(tree, DNAME);
1229 1 : talloc_free(mem_ctx);
1230 :
1231 1 : return ret;
1232 : }
1233 :
1234 : /*
1235 : test case insensitive stream names
1236 : */
1237 1 : static bool test_stream_names3(struct torture_context *tctx,
1238 : struct smb2_tree *tree)
1239 : {
1240 1 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
1241 : NTSTATUS status;
1242 : union smb_fsinfo info;
1243 1 : const char *fname = DNAME "\\stream_names3.txt";
1244 1 : const char *sname = NULL;
1245 1 : const char *snamel = NULL;
1246 1 : const char *snameu = NULL;
1247 1 : const char *sdname = NULL;
1248 1 : const char *sdnamel = NULL;
1249 1 : const char *sdnameu = NULL;
1250 1 : bool ret = true;
1251 1 : struct smb2_handle h = {{0}};
1252 1 : struct smb2_handle hf = {{0}};
1253 1 : struct smb2_handle hs = {{0}};
1254 1 : struct smb2_handle hsl = {{0}};
1255 1 : struct smb2_handle hsu = {{0}};
1256 1 : struct smb2_handle hsd = {{0}};
1257 1 : struct smb2_handle hsdl = {{0}};
1258 1 : struct smb2_handle hsdu = {{0}};
1259 1 : const char *streams[] = { "::$DATA", ":StreamName:$DATA", };
1260 :
1261 1 : smb2_deltree(tree, DNAME);
1262 1 : status = torture_smb2_testdir(tree, DNAME, &h);
1263 1 : CHECK_STATUS(status, NT_STATUS_OK);
1264 :
1265 1 : ZERO_STRUCT(info);
1266 1 : info.generic.level = RAW_QFS_ATTRIBUTE_INFORMATION;
1267 1 : info.generic.handle = h;
1268 1 : status = smb2_getinfo_fs(tree, tree, &info);
1269 1 : CHECK_STATUS(status, NT_STATUS_OK);
1270 1 : 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 1 : sname = talloc_asprintf(tctx, "%s:StreamName", fname);
1291 1 : torture_assert_not_null(tctx, sname, __location__);
1292 1 : snamel = strlower_talloc(tctx, sname);
1293 1 : torture_assert_not_null(tctx, snamel, __location__);
1294 1 : snameu = strupper_talloc(tctx, sname);
1295 1 : torture_assert_not_null(tctx, snameu, __location__);
1296 :
1297 1 : sdname = talloc_asprintf(tctx, "%s:$dAtA", sname);
1298 1 : torture_assert_not_null(tctx, sdname, __location__);
1299 1 : sdnamel = strlower_talloc(tctx, sdname);
1300 1 : torture_assert_not_null(tctx, sdnamel, __location__);
1301 1 : sdnameu = strupper_talloc(tctx, sdname);
1302 1 : torture_assert_not_null(tctx, sdnameu, __location__);
1303 :
1304 1 : torture_comment(tctx, "(%s) testing case insensitive stream names\n",
1305 : __location__);
1306 1 : status = torture_smb2_testfile(tree, fname, &hf);
1307 1 : CHECK_STATUS(status, NT_STATUS_OK);
1308 1 : status = torture_smb2_testfile(tree, sname, &hs);
1309 1 : CHECK_STATUS(status, NT_STATUS_OK);
1310 1 : smb2_util_close(tree, hs);
1311 :
1312 1 : torture_assert(tctx,
1313 : check_stream_list(tree, tctx, fname,
1314 : ARRAY_SIZE(streams),
1315 : streams,
1316 : hf),
1317 : "streams");
1318 :
1319 1 : status = torture_smb2_open(tree, sname, SEC_RIGHTS_FILE_ALL, &hs);
1320 1 : CHECK_STATUS(status, NT_STATUS_OK);
1321 1 : status = torture_smb2_open(tree, snamel, SEC_RIGHTS_FILE_ALL, &hsl);
1322 1 : CHECK_STATUS(status, NT_STATUS_OK);
1323 1 : status = torture_smb2_open(tree, snameu, SEC_RIGHTS_FILE_ALL, &hsu);
1324 1 : CHECK_STATUS(status, NT_STATUS_OK);
1325 1 : status = torture_smb2_open(tree, sdname, SEC_RIGHTS_FILE_ALL, &hsd);
1326 1 : CHECK_STATUS(status, NT_STATUS_OK);
1327 1 : status = torture_smb2_open(tree, sdnamel, SEC_RIGHTS_FILE_ALL, &hsdl);
1328 1 : CHECK_STATUS(status, NT_STATUS_OK);
1329 1 : status = torture_smb2_open(tree, sdnameu, SEC_RIGHTS_FILE_ALL, &hsdu);
1330 1 : CHECK_STATUS(status, NT_STATUS_OK);
1331 :
1332 2 : done:
1333 1 : smb2_util_close(tree, hsdu);
1334 1 : smb2_util_close(tree, hsdl);
1335 1 : smb2_util_close(tree, hsd);
1336 1 : smb2_util_close(tree, hsu);
1337 1 : smb2_util_close(tree, hsl);
1338 1 : smb2_util_close(tree, hs);
1339 1 : smb2_util_close(tree, hf);
1340 1 : smb2_util_close(tree, h);
1341 1 : status = smb2_util_unlink(tree, fname);
1342 1 : smb2_deltree(tree, DNAME);
1343 1 : talloc_free(mem_ctx);
1344 :
1345 1 : 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 1 : static bool test_stream_rename(struct torture_context *tctx,
1373 : struct smb2_tree *tree)
1374 : {
1375 1 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
1376 : NTSTATUS status, status2;
1377 : union smb_open io;
1378 1 : const char *fname = DNAME "\\stream_rename.txt";
1379 : const char *sname1, *sname2;
1380 : union smb_fileinfo finfo1;
1381 : union smb_setfileinfo sfinfo;
1382 1 : bool ret = true;
1383 1 : struct smb2_handle h = {{0}};
1384 1 : struct smb2_handle h1 = {{0}};
1385 :
1386 1 : sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname, "Stream One");
1387 1 : sname2 = talloc_asprintf(mem_ctx, "%s:%s:$DaTa", fname,
1388 : "Second Stream");
1389 :
1390 1 : smb2_util_unlink(tree, fname);
1391 1 : smb2_deltree(tree, DNAME);
1392 :
1393 1 : status = torture_smb2_testdir(tree, DNAME, &h);
1394 1 : CHECK_STATUS(status, NT_STATUS_OK);
1395 :
1396 1 : torture_comment(tctx, "(%s) testing stream renames\n", __location__);
1397 1 : ZERO_STRUCT(io.smb2);
1398 1 : io.smb2.in.create_flags = 0;
1399 1 : io.smb2.in.desired_access = SEC_FILE_READ_ATTRIBUTE |
1400 : SEC_FILE_WRITE_ATTRIBUTE |
1401 : SEC_RIGHTS_FILE_ALL;
1402 1 : io.smb2.in.create_options = 0;
1403 1 : io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1404 1 : io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
1405 : NTCREATEX_SHARE_ACCESS_WRITE |
1406 : NTCREATEX_SHARE_ACCESS_DELETE;
1407 1 : io.smb2.in.alloc_size = 0;
1408 1 : io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
1409 1 : io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
1410 1 : io.smb2.in.security_flags = 0;
1411 1 : io.smb2.in.fname = sname1;
1412 :
1413 : /* Create two streams. */
1414 1 : status = smb2_create(tree, mem_ctx, &(io.smb2));
1415 1 : CHECK_STATUS(status, NT_STATUS_OK);
1416 1 : h1 = io.smb2.out.file.handle;
1417 1 : smb2_util_close(tree, h1);
1418 :
1419 1 : io.smb2.in.fname = sname2;
1420 1 : status = smb2_create(tree, mem_ctx, &(io.smb2));
1421 1 : CHECK_STATUS(status, NT_STATUS_OK);
1422 1 : h1 = io.smb2.out.file.handle;
1423 :
1424 1 : smb2_util_close(tree, h1);
1425 :
1426 : /*
1427 : * Open the second stream.
1428 : */
1429 :
1430 1 : io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
1431 1 : status = smb2_create(tree, mem_ctx, &(io.smb2));
1432 1 : CHECK_STATUS(status, NT_STATUS_OK);
1433 1 : h1 = io.smb2.out.file.handle;
1434 :
1435 : /*
1436 : * Now rename the second stream onto the first.
1437 : */
1438 :
1439 1 : ZERO_STRUCT(sfinfo);
1440 :
1441 1 : sfinfo.rename_information.in.overwrite = 1;
1442 1 : sfinfo.rename_information.in.root_fid = 0;
1443 1 : sfinfo.rename_information.in.new_name = ":Stream One";
1444 1 : CHECK_CALL_HANDLE(RENAME_INFORMATION, NT_STATUS_OK);
1445 2 : done:
1446 1 : smb2_util_close(tree, h1);
1447 1 : status = smb2_util_unlink(tree, fname);
1448 1 : smb2_deltree(tree, DNAME);
1449 1 : talloc_free(mem_ctx);
1450 :
1451 1 : return ret;
1452 : }
1453 :
1454 1 : static bool test_stream_rename2(struct torture_context *tctx,
1455 : struct smb2_tree *tree)
1456 : {
1457 1 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
1458 : NTSTATUS status;
1459 : union smb_open io;
1460 1 : const char *fname1 = DNAME "\\stream_rename2.txt";
1461 1 : const char *fname2 = DNAME "\\stream2_rename2.txt";
1462 1 : const char *stream_name1 = ":Stream One:$DATA";
1463 1 : const char *stream_name2 = ":Stream Two:$DATA";
1464 1 : const char *stream_name_default = "::$DATA";
1465 : const char *sname1;
1466 : const char *sname2;
1467 1 : bool ret = true;
1468 : struct smb2_handle h, h1;
1469 : union smb_setfileinfo sinfo;
1470 :
1471 1 : ZERO_STRUCT(h);
1472 1 : ZERO_STRUCT(h1);
1473 :
1474 1 : sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname1, "Stream One");
1475 1 : sname2 = talloc_asprintf(mem_ctx, "%s:%s", fname1, "Stream Two");
1476 :
1477 1 : smb2_util_unlink(tree, fname1);
1478 1 : smb2_util_unlink(tree, fname2);
1479 1 : smb2_deltree(tree, DNAME);
1480 :
1481 1 : status = torture_smb2_testdir(tree, DNAME, &h);
1482 1 : CHECK_STATUS(status, NT_STATUS_OK);
1483 :
1484 1 : ZERO_STRUCT(io.smb2);
1485 1 : io.smb2.in.create_flags = 0;
1486 1 : 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 1 : io.smb2.in.create_options = 0;
1492 1 : io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1493 1 : io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
1494 : NTCREATEX_SHARE_ACCESS_WRITE |
1495 : NTCREATEX_SHARE_ACCESS_DELETE;
1496 1 : io.smb2.in.alloc_size = 0;
1497 1 : io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
1498 1 : io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
1499 1 : io.smb2.in.security_flags = 0;
1500 1 : io.smb2.in.fname = sname1;
1501 :
1502 : /* Open/create new stream. */
1503 1 : status = smb2_create(tree, mem_ctx, &(io.smb2));
1504 1 : CHECK_STATUS(status, NT_STATUS_OK);
1505 :
1506 1 : smb2_util_close(tree, io.smb2.out.file.handle);
1507 :
1508 : /*
1509 : * Reopen the stream for SMB2 renames.
1510 : */
1511 1 : io.smb2.in.fname = sname1;
1512 1 : io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
1513 1 : status = smb2_create(tree, mem_ctx, &(io.smb2));
1514 1 : CHECK_STATUS(status, NT_STATUS_OK);
1515 1 : h1 = io.smb2.out.file.handle;
1516 :
1517 : /*
1518 : * Check SMB2 rename of a stream using :<stream>.
1519 : */
1520 1 : torture_comment(tctx, "(%s) Checking SMB2 rename of a stream using "
1521 : ":<stream>\n", __location__);
1522 1 : ZERO_STRUCT(sinfo);
1523 1 : sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION_SMB2;
1524 1 : sinfo.rename_information.in.file.handle = h1;
1525 1 : sinfo.rename_information.in.overwrite = 1;
1526 1 : sinfo.rename_information.in.root_fid = 0;
1527 1 : sinfo.rename_information.in.new_name = stream_name1;
1528 1 : status = smb2_setinfo_file(tree, &sinfo);
1529 1 : 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 1 : done:
1586 1 : smb2_util_close(tree, h1);
1587 1 : status = smb2_util_unlink(tree, fname1);
1588 1 : status = smb2_util_unlink(tree, fname2);
1589 1 : smb2_deltree(tree, DNAME);
1590 1 : talloc_free(mem_ctx);
1591 :
1592 1 : return ret;
1593 : }
1594 :
1595 2 : 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 : NTSTATUS status;
1602 2 : bool ret = true;
1603 : union smb_open io;
1604 :
1605 : /* Create a file with a stream */
1606 2 : ZERO_STRUCT(io.smb2);
1607 2 : io.smb2.in.create_flags = 0;
1608 2 : 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 2 : io.smb2.in.create_options = 0;
1613 2 : io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1614 2 : io.smb2.in.share_access = 0;
1615 2 : io.smb2.in.alloc_size = 0;
1616 2 : io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
1617 2 : io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
1618 2 : io.smb2.in.security_flags = 0;
1619 2 : io.smb2.in.fname = stream;
1620 :
1621 2 : status = smb2_create(tree, mem_ctx, &(io.smb2));
1622 2 : CHECK_STATUS(status, NT_STATUS_OK);
1623 :
1624 4 : done:
1625 2 : smb2_util_close(tree, io.smb2.out.file.handle);
1626 2 : return ret;
1627 : }
1628 :
1629 :
1630 : /* Test how streams interact with create dispositions */
1631 1 : static bool test_stream_create_disposition(struct torture_context *tctx,
1632 : struct smb2_tree *tree)
1633 : {
1634 1 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
1635 : NTSTATUS status;
1636 : union smb_open io;
1637 1 : const char *fname = DNAME "\\stream_create_disp.txt";
1638 1 : const char *stream = "Stream One:$DATA";
1639 : const char *fname_stream;
1640 1 : const char *default_stream_name = "::$DATA";
1641 : const char *stream_list[2];
1642 1 : bool ret = true;
1643 1 : struct smb2_handle h = {{0}};
1644 1 : struct smb2_handle h1 = {{0}};
1645 :
1646 : /* clean slate .. */
1647 1 : smb2_util_unlink(tree, fname);
1648 1 : smb2_deltree(tree, fname);
1649 1 : smb2_deltree(tree, DNAME);
1650 :
1651 1 : status = torture_smb2_testdir(tree, DNAME, &h);
1652 1 : CHECK_STATUS(status, NT_STATUS_OK);
1653 :
1654 1 : fname_stream = talloc_asprintf(mem_ctx, "%s:%s", fname, stream);
1655 :
1656 1 : stream_list[0] = talloc_asprintf(mem_ctx, ":%s", stream);
1657 1 : stream_list[1] = default_stream_name;
1658 :
1659 1 : 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 1 : ZERO_STRUCT(io.smb2);
1666 1 : io.smb2.in.create_flags = 0;
1667 1 : 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 1 : io.smb2.in.create_options = 0;
1672 1 : io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1673 1 : io.smb2.in.share_access = 0;
1674 1 : io.smb2.in.alloc_size = 0;
1675 1 : io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
1676 1 : io.smb2.in.security_flags = 0;
1677 1 : io.smb2.in.fname = fname;
1678 :
1679 : /*
1680 : * check create open: sanity check
1681 : */
1682 1 : torture_comment(tctx, "(%s) Checking create disp: open\n",
1683 : __location__);
1684 1 : io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
1685 1 : status = smb2_create(tree, mem_ctx, &(io.smb2));
1686 1 : CHECK_STATUS(status, NT_STATUS_OK);
1687 1 : if (!check_stream_list(tree, tctx, fname, 2, stream_list,
1688 : io.smb2.out.file.handle)) {
1689 0 : goto done;
1690 : }
1691 1 : smb2_util_close(tree, io.smb2.out.file.handle);
1692 :
1693 : /*
1694 : * check create overwrite
1695 : */
1696 1 : torture_comment(tctx, "(%s) Checking create disp: overwrite\n",
1697 : __location__);
1698 1 : io.smb2.in.create_disposition = NTCREATEX_DISP_OVERWRITE;
1699 1 : status = smb2_create(tree, mem_ctx, &(io.smb2));
1700 1 : CHECK_STATUS(status, NT_STATUS_OK);
1701 1 : if (!check_stream_list(tree, tctx, fname, 1, &default_stream_name,
1702 : io.smb2.out.file.handle)) {
1703 1 : goto done;
1704 : }
1705 0 : smb2_util_close(tree, io.smb2.out.file.handle);
1706 :
1707 : /*
1708 : * check create overwrite_if
1709 : */
1710 0 : torture_comment(tctx, "(%s) Checking create disp: overwrite_if\n",
1711 : __location__);
1712 0 : smb2_util_unlink(tree, fname);
1713 0 : if (!create_file_with_stream(tctx, tree, mem_ctx, fname, fname_stream))
1714 0 : goto done;
1715 :
1716 0 : io.smb2.in.create_disposition = NTCREATEX_DISP_OVERWRITE_IF;
1717 0 : status = smb2_create(tree, mem_ctx, &(io.smb2));
1718 0 : CHECK_STATUS(status, NT_STATUS_OK);
1719 0 : if (!check_stream_list(tree, tctx, fname, 1, &default_stream_name,
1720 : io.smb2.out.file.handle)) {
1721 0 : goto done;
1722 : }
1723 0 : smb2_util_close(tree, io.smb2.out.file.handle);
1724 :
1725 : /*
1726 : * check create supersede
1727 : */
1728 0 : torture_comment(tctx, "(%s) Checking create disp: supersede\n",
1729 : __location__);
1730 0 : smb2_util_unlink(tree, fname);
1731 0 : if (!create_file_with_stream(tctx, tree, mem_ctx, fname,
1732 : fname_stream)) {
1733 0 : goto done;
1734 : }
1735 :
1736 0 : io.smb2.in.create_disposition = NTCREATEX_DISP_SUPERSEDE;
1737 0 : status = smb2_create(tree, mem_ctx, &(io.smb2));
1738 0 : CHECK_STATUS(status, NT_STATUS_OK);
1739 0 : if (!check_stream_list(tree, tctx, fname, 1, &default_stream_name,
1740 : io.smb2.out.file.handle)) {
1741 0 : goto done;
1742 : }
1743 0 : smb2_util_close(tree, io.smb2.out.file.handle);
1744 :
1745 : /*
1746 : * check create overwrite_if on a stream.
1747 : */
1748 0 : torture_comment(tctx, "(%s) Checking create disp: overwrite_if on "
1749 : "stream\n", __location__);
1750 0 : smb2_util_unlink(tree, fname);
1751 0 : if (!create_file_with_stream(tctx, tree, mem_ctx, fname,
1752 : fname_stream)) {
1753 0 : goto done;
1754 : }
1755 :
1756 0 : io.smb2.in.create_disposition = NTCREATEX_DISP_OVERWRITE_IF;
1757 0 : io.smb2.in.fname = fname_stream;
1758 0 : status = smb2_create(tree, mem_ctx, &(io.smb2));
1759 0 : CHECK_STATUS(status, NT_STATUS_OK);
1760 0 : if (!check_stream_list(tree, tctx, fname, 2, stream_list,
1761 : io.smb2.out.file.handle)) {
1762 0 : goto done;
1763 : }
1764 0 : smb2_util_close(tree, io.smb2.out.file.handle);
1765 1 : done:
1766 1 : smb2_util_close(tree, h1);
1767 1 : smb2_util_unlink(tree, fname);
1768 1 : smb2_deltree(tree, DNAME);
1769 1 : talloc_free(mem_ctx);
1770 :
1771 1 : return ret;
1772 : }
1773 :
1774 2 : 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 : NTSTATUS status;
1780 : union smb_open io;
1781 :
1782 2 : ZERO_STRUCT(io.smb2);
1783 2 : io.smb2.in.create_flags = 0;
1784 2 : 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 2 : io.smb2.in.create_options = 0;
1790 2 : io.smb2.in.file_attributes = 0;
1791 2 : io.smb2.in.share_access = 0;
1792 2 : io.smb2.in.alloc_size = 0;
1793 2 : io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
1794 2 : io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
1795 2 : io.smb2.in.security_flags = 0;
1796 2 : io.smb2.in.fname = fname;
1797 :
1798 2 : status = smb2_create(tree, mem_ctx, &(io.smb2));
1799 2 : if (!NT_STATUS_IS_OK(status)) {
1800 0 : return false;
1801 : }
1802 2 : *h_out = io.smb2.out.file.handle;
1803 2 : return true;
1804 : }
1805 :
1806 :
1807 : /* Test the effect of setting attributes on a stream. */
1808 1 : static bool test_stream_attributes1(struct torture_context *tctx,
1809 : struct smb2_tree *tree)
1810 : {
1811 1 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
1812 1 : bool ret = true;
1813 : NTSTATUS status;
1814 : union smb_open io;
1815 1 : const char *fname = DNAME "\\stream_attr.txt";
1816 1 : const char *stream = "Stream One:$DATA";
1817 : const char *fname_stream;
1818 : struct smb2_handle h, h1;
1819 : union smb_fileinfo finfo;
1820 : union smb_setfileinfo sfinfo;
1821 1 : time_t basetime = (time(NULL) - 86400) & ~1;
1822 :
1823 1 : ZERO_STRUCT(h);
1824 1 : ZERO_STRUCT(h1);
1825 :
1826 1 : torture_comment(tctx, "(%s) testing attribute setting on stream\n",
1827 : __location__);
1828 :
1829 : /* clean slate .. */
1830 1 : smb2_util_unlink(tree, fname);
1831 1 : smb2_deltree(tree, fname);
1832 1 : smb2_deltree(tree, DNAME);
1833 :
1834 1 : status = torture_smb2_testdir(tree, DNAME, &h);
1835 1 : CHECK_STATUS(status, NT_STATUS_OK);
1836 :
1837 1 : fname_stream = talloc_asprintf(mem_ctx, "%s:%s", fname, stream);
1838 :
1839 : /* Create a file with a stream with attribute FILE_ATTRIBUTE_ARCHIVE. */
1840 1 : ret = create_file_with_stream(tctx, tree, mem_ctx, fname,
1841 : fname_stream);
1842 1 : if (!ret) {
1843 0 : goto done;
1844 : }
1845 :
1846 1 : ZERO_STRUCT(io.smb2);
1847 1 : io.smb2.in.fname = fname;
1848 1 : io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
1849 1 : io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL;
1850 1 : io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
1851 : NTCREATEX_SHARE_ACCESS_WRITE |
1852 : NTCREATEX_SHARE_ACCESS_DELETE;
1853 1 : status = smb2_create(tree, mem_ctx, &(io.smb2));
1854 1 : CHECK_STATUS(status, NT_STATUS_OK);
1855 :
1856 1 : ZERO_STRUCT(finfo);
1857 1 : finfo.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
1858 1 : finfo.generic.in.file.handle = io.smb2.out.file.handle;
1859 1 : status = smb2_getinfo_file(tree, mem_ctx, &finfo);
1860 1 : CHECK_STATUS(status, NT_STATUS_OK);
1861 :
1862 1 : 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 1 : smb2_util_close(tree, io.smb2.out.file.handle);
1872 : /* Now open the stream name. */
1873 :
1874 1 : if (!open_stream(tree, tctx, fname_stream, &h1)) {
1875 0 : goto done;
1876 : }
1877 :
1878 : /* Change the time on the stream. */
1879 1 : ZERO_STRUCT(sfinfo);
1880 1 : unix_to_nt_time(&sfinfo.basic_info.in.write_time, basetime);
1881 1 : sfinfo.generic.level = RAW_SFILEINFO_BASIC_INFORMATION;
1882 1 : sfinfo.generic.in.file.handle = h1;
1883 1 : status = smb2_setinfo_file(tree, &sfinfo);
1884 1 : 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 1 : smb2_util_close(tree, h1);
1893 :
1894 1 : ZERO_STRUCT(io.smb2);
1895 1 : io.smb2.in.fname = fname;
1896 1 : io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
1897 1 : io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL;
1898 1 : status = smb2_create(tree, mem_ctx, &(io.smb2));
1899 1 : CHECK_STATUS(status, NT_STATUS_OK);
1900 1 : h1 = io.smb2.out.file.handle;
1901 :
1902 1 : ZERO_STRUCT(finfo);
1903 1 : finfo.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
1904 1 : finfo.generic.in.file.handle = h1;
1905 1 : status = smb2_getinfo_file(tree, mem_ctx, &finfo);
1906 1 : 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 1 : 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 1 : smb2_util_close(tree, h1);
1919 :
1920 1 : if (!open_stream(tree, tctx, fname_stream, &h1)) {
1921 0 : goto done;
1922 : }
1923 :
1924 : /* Changing attributes on stream */
1925 1 : ZERO_STRUCT(sfinfo);
1926 1 : sfinfo.basic_info.in.attrib = FILE_ATTRIBUTE_READONLY;
1927 :
1928 1 : sfinfo.generic.level = RAW_SFILEINFO_BASIC_INFORMATION;
1929 1 : sfinfo.generic.in.file.handle = h1;
1930 1 : status = smb2_setinfo_file(tree, &sfinfo);
1931 1 : 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 1 : smb2_util_close(tree, h1);
1940 :
1941 1 : ZERO_STRUCT(io.smb2);
1942 1 : io.smb2.in.fname = fname;
1943 1 : io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
1944 1 : io.smb2.in.desired_access = SEC_FILE_READ_DATA;
1945 1 : status = smb2_create(tree, mem_ctx, &(io.smb2));
1946 1 : CHECK_STATUS(status, NT_STATUS_OK);
1947 1 : h1 = io.smb2.out.file.handle;
1948 :
1949 1 : ZERO_STRUCT(finfo);
1950 1 : finfo.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
1951 1 : finfo.generic.in.file.handle = h1;
1952 1 : status = smb2_getinfo_file(tree, mem_ctx, &finfo);
1953 1 : CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED);
1954 :
1955 2 : done:
1956 1 : smb2_util_close(tree, h1);
1957 1 : smb2_util_unlink(tree, fname);
1958 1 : smb2_deltree(tree, DNAME);
1959 1 : talloc_free(mem_ctx);
1960 :
1961 1 : return ret;
1962 : }
1963 :
1964 13 : 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 13 : struct smb2_handle h = _h;
1972 : union smb_fileinfo getinfo;
1973 : NTSTATUS status;
1974 13 : bool ret = true;
1975 :
1976 13 : if (smb2_util_handle_empty(h)) {
1977 : struct smb2_create c;
1978 :
1979 10 : 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 10 : status = smb2_create(tree, tctx, &c);
1988 10 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1989 : "smb2_create failed\n");
1990 :
1991 10 : h = c.out.file.handle;
1992 : }
1993 :
1994 13 : getinfo = (union smb_fileinfo) {
1995 : .generic.level = SMB_QFILEINFO_BASIC_INFORMATION,
1996 : .generic.in.file.handle = h,
1997 : };
1998 :
1999 13 : status = smb2_getinfo_file(tree, tctx, &getinfo);
2000 13 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2001 : "smb2_getinfo_file failed\n");
2002 :
2003 13 : 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 13 : 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 26 : done:
2016 13 : if (smb2_util_handle_empty(_h)) {
2017 10 : smb2_util_close(tree, h);
2018 : }
2019 :
2020 13 : return ret;
2021 : }
2022 :
2023 1 : static bool test_stream_attributes2(struct torture_context *tctx,
2024 : struct smb2_tree *tree)
2025 : {
2026 : NTSTATUS status;
2027 : struct smb2_create c1;
2028 1 : struct smb2_handle h1 = {{0}};
2029 1 : const char *fname = DNAME "\\test_stream_btime";
2030 1 : const char *sname = DNAME "\\test_stream_btime:stream";
2031 : union smb_fileinfo getinfo;
2032 : union smb_setfileinfo setinfo;
2033 1 : const char *data = "test data";
2034 : struct timespec ts;
2035 : NTTIME btime;
2036 1 : uint32_t attrib = FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_ARCHIVE;
2037 : bool ret;
2038 :
2039 1 : smb2_deltree(tree, DNAME);
2040 :
2041 1 : status = torture_smb2_testdir(tree, DNAME, &h1);
2042 1 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2043 : "torture_smb2_testdir failed\n");
2044 1 : smb2_util_close(tree, h1);
2045 :
2046 1 : torture_comment(tctx, "Let's dance!\n");
2047 :
2048 : /*
2049 : * Step 1: create file and get creation date
2050 : */
2051 :
2052 1 : 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 1 : status = smb2_create(tree, tctx, &c1);
2061 1 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2062 : "smb2_create failed\n");
2063 1 : h1 = c1.out.file.handle;
2064 :
2065 1 : getinfo = (union smb_fileinfo) {
2066 : .generic.level = SMB_QFILEINFO_BASIC_INFORMATION,
2067 : .generic.in.file.handle = h1,
2068 : };
2069 1 : status = smb2_getinfo_file(tree, tctx, &getinfo);
2070 1 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2071 : "smb2_getinfo_file failed\n");
2072 :
2073 1 : btime = getinfo.basic_info.out.create_time;
2074 :
2075 1 : status = smb2_util_close(tree, h1);
2076 1 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2077 : "smb2_util_close failed\n");
2078 1 : ZERO_STRUCT(h1);
2079 :
2080 : /*
2081 : * Step X: write to file, assert btime was not updated
2082 : */
2083 :
2084 1 : 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 1 : status = smb2_create(tree, tctx, &c1);
2093 1 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2094 : "smb2_create failed\n");
2095 1 : h1 = c1.out.file.handle;
2096 :
2097 1 : status = smb2_util_write(tree, h1, data, 0, strlen(data));
2098 1 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2099 : "smb2_util_write failed\n");
2100 :
2101 1 : ret = check_metadata(tctx, tree, NULL, h1, btime, attrib);
2102 1 : torture_assert_goto(tctx, ret, ret, done, "Bad metadata\n");
2103 :
2104 1 : status = smb2_util_close(tree, h1);
2105 1 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2106 : "smb2_util_close failed\n");
2107 1 : ZERO_STRUCT(h1);
2108 :
2109 1 : ret = check_metadata(tctx, tree, fname, (struct smb2_handle){{0}},
2110 : btime, attrib);
2111 1 : 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 1 : 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 1 : status = smb2_create(tree, tctx, &c1);
2127 1 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2128 : "smb2_create failed\n");
2129 1 : h1 = c1.out.file.handle;
2130 :
2131 1 : status = smb2_util_close(tree, h1);
2132 1 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2133 : "smb2_util_close failed\n");
2134 1 : ZERO_STRUCT(h1);
2135 :
2136 1 : ret = check_metadata(tctx, tree, sname, (struct smb2_handle){{0}},
2137 : btime, attrib);
2138 1 : 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 1 : 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 1 : status = smb2_create(tree, tctx, &c1);
2153 1 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2154 : "smb2_create failed\n");
2155 1 : h1 = c1.out.file.handle;
2156 :
2157 1 : setinfo = (union smb_setfileinfo) {
2158 : .basic_info.level = RAW_SFILEINFO_BASIC_INFORMATION,
2159 : .basic_info.in.file.handle = h1,
2160 : };
2161 1 : clock_gettime_mono(&ts);
2162 1 : btime = setinfo.basic_info.in.create_time = full_timespec_to_nt_time(&ts);
2163 :
2164 1 : status = smb2_setinfo_file(tree, &setinfo);
2165 1 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2166 : "smb2_setinfo_file failed\n");
2167 :
2168 1 : ret = check_metadata(tctx, tree, NULL, h1, btime, attrib);
2169 1 : torture_assert_goto(tctx, ret, ret, done, "Bad time on stream\n");
2170 :
2171 1 : status = smb2_util_close(tree, h1);
2172 1 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2173 : "smb2_util_close failed\n");
2174 1 : ZERO_STRUCT(h1);
2175 :
2176 1 : ret = check_metadata(tctx, tree, fname, (struct smb2_handle){{0}},
2177 : btime, attrib);
2178 1 : torture_assert_goto(tctx, ret, ret, done, "Bad time on basefile\n");
2179 :
2180 1 : ret = check_metadata(tctx, tree, sname, (struct smb2_handle){{0}},
2181 : btime, attrib);
2182 1 : 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 1 : 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 1 : status = smb2_create(tree, tctx, &c1);
2197 1 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2198 : "smb2_create failed\n");
2199 1 : h1 = c1.out.file.handle;
2200 :
2201 1 : status = smb2_util_write(tree, h1, data, 0, strlen(data));
2202 1 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2203 : "smb2_util_write failed\n");
2204 :
2205 1 : ret = check_metadata(tctx, tree, NULL, h1, btime, attrib);
2206 1 : torture_assert_goto(tctx, ret, ret, done, "Bad metadata\n");
2207 :
2208 1 : status = smb2_util_close(tree, h1);
2209 1 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2210 : "smb2_util_close failed\n");
2211 1 : ZERO_STRUCT(h1);
2212 :
2213 1 : ret = check_metadata(tctx, tree, fname, (struct smb2_handle){{0}},
2214 : btime, attrib);
2215 1 : torture_assert_goto(tctx, ret, ret, done, "Bad metadata\n");
2216 :
2217 1 : ret = check_metadata(tctx, tree, sname, (struct smb2_handle){{0}},
2218 : btime, attrib);
2219 1 : 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 1 : 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 1 : status = smb2_create(tree, tctx, &c1);
2235 1 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2236 : "smb2_create failed\n");
2237 1 : h1 = c1.out.file.handle;
2238 :
2239 1 : attrib = FILE_ATTRIBUTE_NORMAL;
2240 :
2241 1 : 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 1 : status = smb2_setinfo_file(tree, &setinfo);
2248 1 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2249 : "smb2_setinfo_file failed\n");
2250 :
2251 1 : status = smb2_util_close(tree, h1);
2252 1 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2253 : "smb2_util_close failed\n");
2254 1 : ZERO_STRUCT(h1);
2255 :
2256 1 : ret = check_metadata(tctx, tree, fname, (struct smb2_handle){{0}},
2257 : btime, attrib);
2258 1 : torture_assert_goto(tctx, ret, ret, done, "Bad metadata\n");
2259 :
2260 1 : ret = check_metadata(tctx, tree, sname, (struct smb2_handle){{0}},
2261 : btime, attrib);
2262 1 : 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 1 : 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 1 : status = smb2_create(tree, tctx, &c1);
2278 1 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2279 : "smb2_create failed\n");
2280 1 : h1 = c1.out.file.handle;
2281 :
2282 1 : attrib = FILE_ATTRIBUTE_HIDDEN;
2283 :
2284 1 : 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 1 : status = smb2_setinfo_file(tree, &setinfo);
2291 1 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2292 : "smb2_setinfo_file failed\n");
2293 :
2294 1 : status = smb2_util_close(tree, h1);
2295 1 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2296 : "smb2_util_close failed\n");
2297 1 : ZERO_STRUCT(h1);
2298 :
2299 1 : ret = check_metadata(tctx, tree, fname, (struct smb2_handle){{0}},
2300 : btime, attrib);
2301 1 : torture_assert_goto(tctx, ret, ret, done, "Bad metadata\n");
2302 :
2303 1 : ret = check_metadata(tctx, tree, sname, (struct smb2_handle){{0}},
2304 : btime, attrib);
2305 1 : torture_assert_goto(tctx, ret, ret, done, "Bad metadata\n");
2306 :
2307 2 : done:
2308 1 : if (!smb2_util_handle_empty(h1)) {
2309 0 : smb2_util_close(tree, h1);
2310 : }
2311 :
2312 1 : smb2_deltree(tree, DNAME);
2313 :
2314 1 : return ret;
2315 : }
2316 :
2317 1 : static bool test_basefile_rename_with_open_stream(struct torture_context *tctx,
2318 : struct smb2_tree *tree)
2319 : {
2320 1 : bool ret = true;
2321 : NTSTATUS status;
2322 1 : struct smb2_tree *tree2 = NULL;
2323 : struct smb2_create create, create2;
2324 1 : struct smb2_handle h1 = {{0}}, h2 = {{0}};
2325 1 : const char *fname = "test_rename_openfile";
2326 1 : const char *sname = "test_rename_openfile:foo";
2327 1 : const char *fname_renamed = "test_rename_openfile_renamed";
2328 : union smb_setfileinfo sinfo;
2329 1 : const char *data = "test data";
2330 :
2331 1 : ret = torture_smb2_connection(tctx, &tree2);
2332 1 : torture_assert_goto(tctx, ret == true, ret, done,
2333 : "torture_smb2_connection failed\n");
2334 :
2335 1 : torture_comment(tctx, "Creating file with stream\n");
2336 :
2337 1 : ZERO_STRUCT(create);
2338 1 : create.in.desired_access = SEC_FILE_ALL;
2339 1 : create.in.share_access = NTCREATEX_SHARE_ACCESS_MASK;
2340 1 : create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
2341 1 : create.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
2342 1 : create.in.impersonation_level = SMB2_IMPERSONATION_IMPERSONATION;
2343 1 : create.in.fname = sname;
2344 :
2345 1 : status = smb2_create(tree, tctx, &create);
2346 1 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2347 : "smb2_create failed\n");
2348 :
2349 1 : h1 = create.out.file.handle;
2350 :
2351 1 : torture_comment(tctx, "Writing to stream\n");
2352 :
2353 1 : status = smb2_util_write(tree, h1, data, 0, strlen(data));
2354 1 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2355 : "smb2_util_write failed\n");
2356 :
2357 1 : torture_comment(tctx, "Renaming base file\n");
2358 :
2359 1 : ZERO_STRUCT(create2);
2360 1 : create2.in.desired_access = SEC_FILE_ALL;
2361 1 : create2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
2362 1 : create2.in.share_access = NTCREATEX_SHARE_ACCESS_MASK;
2363 1 : create2.in.create_disposition = NTCREATEX_DISP_OPEN;
2364 1 : create2.in.impersonation_level = SMB2_IMPERSONATION_IMPERSONATION;
2365 1 : create2.in.fname = fname;
2366 :
2367 1 : status = smb2_create(tree2, tctx, &create2);
2368 1 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2369 : "smb2_create failed\n");
2370 :
2371 1 : h2 = create2.out.file.handle;
2372 :
2373 1 : ZERO_STRUCT(sinfo);
2374 1 : sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION;
2375 1 : sinfo.rename_information.in.file.handle = h2;
2376 1 : sinfo.rename_information.in.new_name = fname_renamed;
2377 :
2378 1 : status = smb2_setinfo_file(tree2, &sinfo);
2379 1 : 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 1 : smb2_util_close(tree2, h2);
2384 :
2385 1 : done:
2386 1 : if (!smb2_util_handle_empty(h1)) {
2387 1 : smb2_util_close(tree, h1);
2388 : }
2389 1 : if (!smb2_util_handle_empty(h2)) {
2390 1 : smb2_util_close(tree2, h2);
2391 : }
2392 1 : smb2_util_unlink(tree, fname);
2393 1 : smb2_util_unlink(tree, fname_renamed);
2394 :
2395 1 : return ret;
2396 : }
2397 :
2398 : /*
2399 : basic testing of streams calls SMB2
2400 : */
2401 964 : struct torture_suite *torture_smb2_streams_init(TALLOC_CTX *ctx)
2402 : {
2403 738 : struct torture_suite *suite =
2404 226 : torture_suite_create(ctx, "streams");
2405 :
2406 964 : torture_suite_add_1smb2_test(suite, "dir", test_stream_dir);
2407 964 : torture_suite_add_1smb2_test(suite, "io", test_stream_io);
2408 964 : torture_suite_add_1smb2_test(suite, "sharemodes", test_stream_sharemodes);
2409 964 : torture_suite_add_1smb2_test(suite, "names", test_stream_names);
2410 964 : torture_suite_add_1smb2_test(suite, "names2", test_stream_names2);
2411 964 : torture_suite_add_1smb2_test(suite, "names3", test_stream_names3);
2412 964 : torture_suite_add_1smb2_test(suite, "rename", test_stream_rename);
2413 964 : torture_suite_add_1smb2_test(suite, "rename2", test_stream_rename2);
2414 964 : torture_suite_add_1smb2_test(suite, "create-disposition", test_stream_create_disposition);
2415 964 : torture_suite_add_1smb2_test(suite, "attributes1", test_stream_attributes1);
2416 964 : torture_suite_add_1smb2_test(suite, "attributes2", test_stream_attributes2);
2417 964 : torture_suite_add_1smb2_test(suite, "delete", test_stream_delete);
2418 964 : torture_suite_add_1smb2_test(suite, "zero-byte", test_zero_byte_stream);
2419 964 : torture_suite_add_1smb2_test(suite, "basefile-rename-with-open-stream",
2420 : test_basefile_rename_with_open_stream);
2421 :
2422 964 : suite->description = talloc_strdup(suite, "SMB2-STREAM tests");
2423 964 : return suite;
2424 : }
|