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 "system/locale.h"
24 : #include "torture/torture.h"
25 : #include "libcli/raw/libcliraw.h"
26 : #include "libcli/security/dom_sid.h"
27 : #include "libcli/security/security_descriptor.h"
28 : #include "system/filesys.h"
29 : #include "libcli/libcli.h"
30 : #include "torture/util.h"
31 : #include "lib/util/tsort.h"
32 : #include "torture/raw/proto.h"
33 :
34 : #define BASEDIR "\\teststreams"
35 :
36 : #define CHECK_STATUS(status, correct) \
37 : torture_assert_ntstatus_equal_goto(tctx,status,correct,ret,done,"CHECK_STATUS")
38 :
39 : #define CHECK_VALUE(v, correct) \
40 : torture_assert_int_equal(tctx,v,correct,"CHECK_VALUE")
41 :
42 : #define CHECK_NTTIME(v, correct) \
43 : torture_assert_u64_equal(tctx,v,correct,"CHECK_NTTIME")
44 :
45 : #define CHECK_STR(v, correct) do { \
46 : bool ok; \
47 : if ((v) && !(correct)) { \
48 : ok = false; \
49 : } else if (!(v) && (correct)) { \
50 : ok = false; \
51 : } else if (!(v) && !(correct)) { \
52 : ok = true; \
53 : } else if (strcmp((v), (correct)) == 0) { \
54 : ok = true; \
55 : } else { \
56 : ok = false; \
57 : } \
58 : torture_assert(tctx,ok,\
59 : talloc_asprintf(tctx, "got '%s', expected '%s'",\
60 : (v)?(v):"NULL", (correct)?(correct):"NULL")); \
61 : } while (0)
62 :
63 : /*
64 : check that a stream has the right contents
65 : */
66 55 : static bool check_stream(struct smbcli_state *cli, const char *location,
67 : TALLOC_CTX *mem_ctx,
68 : const char *fname, const char *sname,
69 : const char *value)
70 : {
71 0 : int fnum;
72 0 : const char *full_name;
73 0 : uint8_t *buf;
74 0 : ssize_t ret;
75 :
76 55 : full_name = talloc_asprintf(mem_ctx, "%s:%s", fname, sname);
77 :
78 55 : fnum = smbcli_open(cli->tree, full_name, O_RDONLY, DENY_NONE);
79 :
80 55 : if (value == NULL) {
81 25 : if (fnum != -1) {
82 0 : printf("(%s) should have failed stream open of %s\n",
83 : location, full_name);
84 0 : return false;
85 : }
86 25 : return true;
87 : }
88 :
89 30 : if (fnum == -1) {
90 0 : printf("(%s) Failed to open stream '%s' - %s\n",
91 : location, full_name, smbcli_errstr(cli->tree));
92 0 : return false;
93 : }
94 :
95 30 : buf = talloc_array(mem_ctx, uint8_t, strlen(value)+11);
96 :
97 30 : ret = smbcli_read(cli->tree, fnum, buf, 0, strlen(value)+11);
98 30 : if (ret != strlen(value)) {
99 0 : printf("(%s) Failed to read %lu bytes from stream '%s' - got %d\n",
100 0 : location, (long)strlen(value), full_name, (int)ret);
101 0 : return false;
102 : }
103 :
104 30 : if (memcmp(buf, value, strlen(value)) != 0) {
105 0 : printf("(%s) Bad data in stream\n", location);
106 0 : return false;
107 : }
108 :
109 30 : smbcli_close(cli->tree, fnum);
110 30 : return true;
111 : }
112 :
113 323 : static int qsort_string(char * const *s1, char * const *s2)
114 : {
115 323 : return strcmp(*s1, *s2);
116 : }
117 :
118 367 : static int qsort_stream(const struct stream_struct *s1, const struct stream_struct *s2)
119 : {
120 367 : return strcmp(s1->stream_name.s, s2->stream_name.s);
121 : }
122 :
123 96 : static bool check_stream_list(struct torture_context *tctx,
124 : struct smbcli_state *cli, const char *fname,
125 : int num_exp, const char **exp)
126 : {
127 0 : union smb_fileinfo finfo;
128 0 : NTSTATUS status;
129 0 : int i;
130 96 : TALLOC_CTX *tmp_ctx = talloc_new(cli);
131 0 : char **exp_sort;
132 0 : struct stream_struct *stream_sort;
133 96 : bool ret = false;
134 96 : int fail = -1;
135 :
136 96 : finfo.generic.level = RAW_FILEINFO_STREAM_INFO;
137 96 : finfo.generic.in.file.path = fname;
138 :
139 96 : status = smb_raw_pathinfo(cli->tree, tmp_ctx, &finfo);
140 96 : CHECK_STATUS(status, NT_STATUS_OK);
141 :
142 96 : CHECK_VALUE(finfo.stream_info.out.num_streams, num_exp);
143 :
144 95 : if (num_exp == 0) {
145 5 : ret = true;
146 5 : goto done;
147 : }
148 :
149 90 : exp_sort = (char **)talloc_memdup(tmp_ctx, exp, num_exp * sizeof(*exp));
150 :
151 90 : if (exp_sort == NULL) {
152 0 : goto done;
153 : }
154 :
155 90 : TYPESAFE_QSORT(exp_sort, num_exp, qsort_string);
156 :
157 90 : stream_sort = (struct stream_struct *)talloc_memdup(tmp_ctx,
158 : finfo.stream_info.out.streams,
159 : finfo.stream_info.out.num_streams *
160 : sizeof(*stream_sort));
161 :
162 90 : if (stream_sort == NULL) {
163 0 : goto done;
164 : }
165 :
166 90 : TYPESAFE_QSORT(stream_sort, finfo.stream_info.out.num_streams, qsort_stream);
167 :
168 369 : for (i=0; i<num_exp; i++) {
169 279 : if (strcmp(exp_sort[i], stream_sort[i].stream_name.s) != 0) {
170 0 : fail = i;
171 0 : goto show_streams;
172 : }
173 : }
174 :
175 90 : ret = true;
176 95 : done:
177 95 : talloc_free(tmp_ctx);
178 95 : return ret;
179 :
180 0 : show_streams:
181 0 : for (i=0; i<num_exp; i++) {
182 0 : torture_comment(tctx, "stream names '%s' '%s'\n",
183 0 : exp_sort[i], stream_sort[i].stream_name.s);
184 : }
185 0 : CHECK_STR(stream_sort[fail].stream_name.s, exp_sort[fail]);
186 0 : talloc_free(tmp_ctx);
187 0 : return ret;
188 : }
189 :
190 : /*
191 : test behavior of streams on directories
192 : */
193 5 : static bool test_stream_dir(struct torture_context *tctx,
194 : struct smbcli_state *cli)
195 : {
196 0 : NTSTATUS status;
197 0 : union smb_open io;
198 5 : const char *fname = BASEDIR "\\stream.txt";
199 0 : const char *sname1;
200 5 : bool ret = true;
201 0 : const char *basedir_data;
202 :
203 5 : torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
204 :
205 5 : basedir_data = talloc_asprintf(tctx, "%s::$DATA", BASEDIR);
206 5 : sname1 = talloc_asprintf(tctx, "%s:%s", fname, "Stream One");
207 :
208 5 : printf("(%s) opening non-existent directory stream\n", __location__);
209 5 : io.generic.level = RAW_OPEN_NTCREATEX;
210 5 : io.ntcreatex.in.root_fid.fnum = 0;
211 5 : io.ntcreatex.in.flags = 0;
212 5 : io.ntcreatex.in.access_mask = SEC_FILE_WRITE_DATA;
213 5 : io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
214 5 : io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
215 5 : io.ntcreatex.in.share_access = 0;
216 5 : io.ntcreatex.in.alloc_size = 0;
217 5 : io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
218 5 : io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
219 5 : io.ntcreatex.in.security_flags = 0;
220 5 : io.ntcreatex.in.fname = sname1;
221 5 : status = smb_raw_open(cli->tree, tctx, &io);
222 5 : CHECK_STATUS(status, NT_STATUS_NOT_A_DIRECTORY);
223 :
224 5 : printf("(%s) opening basedir stream\n", __location__);
225 5 : io.generic.level = RAW_OPEN_NTCREATEX;
226 5 : io.ntcreatex.in.root_fid.fnum = 0;
227 5 : io.ntcreatex.in.flags = 0;
228 5 : io.ntcreatex.in.access_mask = SEC_FILE_WRITE_DATA;
229 5 : io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
230 5 : io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_DIRECTORY;
231 5 : io.ntcreatex.in.share_access = 0;
232 5 : io.ntcreatex.in.alloc_size = 0;
233 5 : io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
234 5 : io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
235 5 : io.ntcreatex.in.security_flags = 0;
236 5 : io.ntcreatex.in.fname = basedir_data;
237 5 : status = smb_raw_open(cli->tree, tctx, &io);
238 5 : CHECK_STATUS(status, NT_STATUS_NOT_A_DIRECTORY);
239 :
240 5 : printf("(%s) opening basedir ::$DATA stream\n", __location__);
241 5 : io.generic.level = RAW_OPEN_NTCREATEX;
242 5 : io.ntcreatex.in.root_fid.fnum = 0;
243 5 : io.ntcreatex.in.flags = 0x10;
244 5 : io.ntcreatex.in.access_mask = SEC_FILE_WRITE_DATA;
245 5 : io.ntcreatex.in.create_options = 0;
246 5 : io.ntcreatex.in.file_attr = 0;
247 5 : io.ntcreatex.in.share_access = 0;
248 5 : io.ntcreatex.in.alloc_size = 0;
249 5 : io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
250 5 : io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
251 5 : io.ntcreatex.in.security_flags = 0;
252 5 : io.ntcreatex.in.fname = basedir_data;
253 5 : status = smb_raw_open(cli->tree, tctx, &io);
254 5 : CHECK_STATUS(status, NT_STATUS_FILE_IS_A_DIRECTORY);
255 :
256 5 : printf("(%s) list the streams on the basedir\n", __location__);
257 5 : ret &= check_stream_list(tctx, cli, BASEDIR, 0, NULL);
258 5 : done:
259 5 : smbcli_deltree(cli->tree, BASEDIR);
260 5 : return ret;
261 : }
262 :
263 : /*
264 : test basic behavior of streams on directories
265 : */
266 5 : static bool test_stream_io(struct torture_context *tctx,
267 : struct smbcli_state *cli)
268 : {
269 0 : NTSTATUS status;
270 0 : union smb_open io;
271 5 : const char *fname = BASEDIR "\\stream.txt";
272 0 : const char *sname1, *sname2;
273 5 : bool ret = true;
274 5 : int fnum = -1;
275 0 : ssize_t retsize;
276 :
277 5 : const char *one[] = { "::$DATA" };
278 5 : const char *two[] = { "::$DATA", ":Second Stream:$DATA" };
279 5 : const char *three[] = { "::$DATA", ":Stream One:$DATA",
280 : ":Second Stream:$DATA" };
281 :
282 5 : torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
283 :
284 5 : sname1 = talloc_asprintf(tctx, "%s:%s", fname, "Stream One");
285 5 : sname2 = talloc_asprintf(tctx, "%s:%s:$DaTa", fname, "Second Stream");
286 :
287 5 : printf("(%s) creating a stream on a non-existent file\n", __location__);
288 5 : io.generic.level = RAW_OPEN_NTCREATEX;
289 5 : io.ntcreatex.in.root_fid.fnum = 0;
290 5 : io.ntcreatex.in.flags = 0;
291 5 : io.ntcreatex.in.access_mask = SEC_FILE_WRITE_DATA;
292 5 : io.ntcreatex.in.create_options = 0;
293 5 : io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
294 5 : io.ntcreatex.in.share_access = 0;
295 5 : io.ntcreatex.in.alloc_size = 0;
296 5 : io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
297 5 : io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
298 5 : io.ntcreatex.in.security_flags = 0;
299 5 : io.ntcreatex.in.fname = sname1;
300 5 : status = smb_raw_open(cli->tree, tctx, &io);
301 5 : CHECK_STATUS(status, NT_STATUS_OK);
302 5 : fnum = io.ntcreatex.out.file.fnum;
303 :
304 5 : ret &= check_stream(cli, __location__, tctx, fname, "Stream One", NULL);
305 :
306 5 : printf("(%s) check that open of base file is allowed\n", __location__);
307 5 : io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
308 5 : io.ntcreatex.in.fname = fname;
309 5 : status = smb_raw_open(cli->tree, tctx, &io);
310 5 : CHECK_STATUS(status, NT_STATUS_OK);
311 5 : smbcli_close(cli->tree, io.ntcreatex.out.file.fnum);
312 :
313 5 : printf("(%s) writing to stream\n", __location__);
314 5 : retsize = smbcli_write(cli->tree, fnum, 0, "test data", 0, 9);
315 5 : CHECK_VALUE(retsize, 9);
316 :
317 5 : smbcli_close(cli->tree, fnum);
318 :
319 5 : ret &= check_stream(cli, __location__, tctx, fname, "Stream One", "test data");
320 :
321 5 : io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
322 5 : io.ntcreatex.in.fname = sname1;
323 5 : status = smb_raw_open(cli->tree, tctx, &io);
324 5 : CHECK_STATUS(status, NT_STATUS_OK);
325 5 : fnum = io.ntcreatex.out.file.fnum;
326 :
327 5 : printf("(%s) modifying stream\n", __location__);
328 5 : retsize = smbcli_write(cli->tree, fnum, 0, "MORE DATA ", 5, 10);
329 5 : CHECK_VALUE(retsize, 10);
330 :
331 5 : smbcli_close(cli->tree, fnum);
332 :
333 5 : ret &= check_stream(cli, __location__, tctx, fname, "Stream One:$FOO", NULL);
334 :
335 5 : printf("(%s) creating a stream2 on a existing file\n", __location__);
336 5 : io.ntcreatex.in.fname = sname2;
337 5 : io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
338 5 : status = smb_raw_open(cli->tree, tctx, &io);
339 5 : CHECK_STATUS(status, NT_STATUS_OK);
340 5 : fnum = io.ntcreatex.out.file.fnum;
341 :
342 5 : printf("(%s) modifying stream\n", __location__);
343 5 : retsize = smbcli_write(cli->tree, fnum, 0, "SECOND STREAM", 0, 13);
344 5 : CHECK_VALUE(retsize, 13);
345 :
346 5 : smbcli_close(cli->tree, fnum);
347 :
348 5 : ret &= check_stream(cli, __location__, tctx, fname, "Stream One", "test MORE DATA ");
349 5 : ret &= check_stream(cli, __location__, tctx, fname, "Stream One:$DATA", "test MORE DATA ");
350 5 : ret &= check_stream(cli, __location__, tctx, fname, "Stream One:", NULL);
351 5 : ret &= check_stream(cli, __location__, tctx, fname, "Second Stream", "SECOND STREAM");
352 5 : ret &= check_stream(cli, __location__, tctx, fname,
353 : "SECOND STREAM:$DATA", "SECOND STREAM");
354 5 : ret &= check_stream(cli, __location__, tctx, fname, "Second Stream:$DATA", "SECOND STREAM");
355 5 : ret &= check_stream(cli, __location__, tctx, fname, "Second Stream:", NULL);
356 5 : ret &= check_stream(cli, __location__, tctx, fname, "Second Stream:$FOO", NULL);
357 :
358 5 : check_stream_list(tctx, cli, fname, 3, three);
359 :
360 5 : printf("(%s) deleting stream\n", __location__);
361 5 : status = smbcli_unlink(cli->tree, sname1);
362 5 : CHECK_STATUS(status, NT_STATUS_OK);
363 :
364 5 : check_stream_list(tctx, cli, fname, 2, two);
365 :
366 5 : printf("(%s) delete a stream via delete-on-close\n", __location__);
367 5 : io.ntcreatex.in.fname = sname2;
368 5 : io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
369 5 : io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE;
370 5 : io.ntcreatex.in.access_mask = SEC_STD_DELETE;
371 5 : io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
372 :
373 5 : status = smb_raw_open(cli->tree, tctx, &io);
374 5 : CHECK_STATUS(status, NT_STATUS_OK);
375 5 : fnum = io.ntcreatex.out.file.fnum;
376 :
377 5 : smbcli_close(cli->tree, fnum);
378 5 : status = smbcli_unlink(cli->tree, sname2);
379 5 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
380 :
381 5 : check_stream_list(tctx, cli, fname, 1, one);
382 :
383 5 : io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
384 5 : io.ntcreatex.in.fname = sname1;
385 5 : status = smb_raw_open(cli->tree, tctx, &io);
386 5 : CHECK_STATUS(status, NT_STATUS_OK);
387 5 : smbcli_close(cli->tree, io.ntcreatex.out.file.fnum);
388 5 : io.ntcreatex.in.fname = sname2;
389 5 : status = smb_raw_open(cli->tree, tctx, &io);
390 5 : CHECK_STATUS(status, NT_STATUS_OK);
391 5 : smbcli_close(cli->tree, io.ntcreatex.out.file.fnum);
392 :
393 5 : printf("(%s) deleting file\n", __location__);
394 5 : status = smbcli_unlink(cli->tree, fname);
395 5 : CHECK_STATUS(status, NT_STATUS_OK);
396 :
397 5 : done:
398 5 : smbcli_close(cli->tree, fnum);
399 5 : smbcli_deltree(cli->tree, BASEDIR);
400 5 : return ret;
401 : }
402 :
403 : /*
404 : test stream sharemodes
405 : */
406 5 : static bool test_stream_sharemodes(struct torture_context *tctx,
407 : struct smbcli_state *cli)
408 : {
409 0 : NTSTATUS status;
410 0 : union smb_open io;
411 5 : const char *fname = BASEDIR "\\stream.txt";
412 0 : const char *sname1, *sname2;
413 5 : bool ret = true;
414 5 : int fnum1 = -1;
415 5 : int fnum2 = -1;
416 :
417 5 : torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
418 :
419 5 : sname1 = talloc_asprintf(tctx, "%s:%s", fname, "Stream One");
420 5 : sname2 = talloc_asprintf(tctx, "%s:%s:$DaTa", fname, "Second Stream");
421 :
422 5 : printf("(%s) testing stream share mode conflicts\n", __location__);
423 5 : io.generic.level = RAW_OPEN_NTCREATEX;
424 5 : io.ntcreatex.in.root_fid.fnum = 0;
425 5 : io.ntcreatex.in.flags = 0;
426 5 : io.ntcreatex.in.access_mask = SEC_FILE_WRITE_DATA;
427 5 : io.ntcreatex.in.create_options = 0;
428 5 : io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
429 5 : io.ntcreatex.in.share_access = 0;
430 5 : io.ntcreatex.in.alloc_size = 0;
431 5 : io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
432 5 : io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
433 5 : io.ntcreatex.in.security_flags = 0;
434 5 : io.ntcreatex.in.fname = sname1;
435 :
436 5 : status = smb_raw_open(cli->tree, tctx, &io);
437 5 : CHECK_STATUS(status, NT_STATUS_OK);
438 5 : fnum1 = io.ntcreatex.out.file.fnum;
439 :
440 : /*
441 : * A different stream does not give a sharing violation
442 : */
443 :
444 5 : io.ntcreatex.in.fname = sname2;
445 5 : status = smb_raw_open(cli->tree, tctx, &io);
446 5 : CHECK_STATUS(status, NT_STATUS_OK);
447 5 : fnum2 = io.ntcreatex.out.file.fnum;
448 :
449 : /*
450 : * ... whereas the same stream does with unchanged access/share_access
451 : * flags
452 : */
453 :
454 5 : io.ntcreatex.in.fname = sname1;
455 5 : io.ntcreatex.in.open_disposition = 0;
456 5 : status = smb_raw_open(cli->tree, tctx, &io);
457 5 : CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
458 :
459 5 : io.ntcreatex.in.fname = sname2;
460 5 : status = smb_raw_open(cli->tree, tctx, &io);
461 5 : CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
462 :
463 5 : done:
464 5 : if (fnum1 != -1) smbcli_close(cli->tree, fnum1);
465 5 : if (fnum2 != -1) smbcli_close(cli->tree, fnum2);
466 5 : status = smbcli_unlink(cli->tree, fname);
467 5 : smbcli_deltree(cli->tree, BASEDIR);
468 5 : return ret;
469 : }
470 :
471 : /*
472 : * Test FILE_SHARE_DELETE on streams
473 : *
474 : * A stream opened with !FILE_SHARE_DELETE prevents the main file to be opened
475 : * with SEC_STD_DELETE.
476 : *
477 : * The main file opened with !FILE_SHARE_DELETE does *not* prevent a stream to
478 : * be opened with SEC_STD_DELETE.
479 : *
480 : * A stream held open with FILE_SHARE_DELETE allows the file to be
481 : * deleted. After the main file is deleted, access to the open file descriptor
482 : * still works, but all name-based access to both the main file as well as the
483 : * stream is denied with DELETE pending.
484 : *
485 : * This means, an open of the main file with SEC_STD_DELETE should walk all
486 : * streams and also open them with SEC_STD_DELETE. If any of these opens gives
487 : * SHARING_VIOLATION, the main open fails.
488 : *
489 : * Closing the main file after delete_on_close has been set does not really
490 : * unlink it but leaves the corresponding share mode entry with
491 : * delete_on_close being set around until all streams are closed.
492 : *
493 : * Opening a stream must also look at the main file's share mode entry, look
494 : * at the delete_on_close bit and potentially return DELETE_PENDING.
495 : */
496 :
497 5 : static bool test_stream_delete(struct torture_context *tctx,
498 : struct smbcli_state *cli)
499 : {
500 0 : NTSTATUS status;
501 0 : union smb_open io;
502 5 : const char *fname = BASEDIR "\\stream.txt";
503 0 : const char *sname1;
504 5 : bool ret = true;
505 5 : int fnum = -1;
506 0 : uint8_t buf[9];
507 0 : ssize_t retsize;
508 0 : union smb_fileinfo finfo;
509 :
510 5 : torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
511 :
512 5 : sname1 = talloc_asprintf(tctx, "%s:%s", fname, "Stream One");
513 :
514 5 : printf("(%s) opening non-existent file stream\n", __location__);
515 5 : io.generic.level = RAW_OPEN_NTCREATEX;
516 5 : io.ntcreatex.in.root_fid.fnum = 0;
517 5 : io.ntcreatex.in.flags = 0;
518 5 : io.ntcreatex.in.access_mask = SEC_FILE_READ_DATA|SEC_FILE_WRITE_DATA;
519 5 : io.ntcreatex.in.create_options = 0;
520 5 : io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
521 5 : io.ntcreatex.in.share_access = 0;
522 5 : io.ntcreatex.in.alloc_size = 0;
523 5 : io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
524 5 : io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
525 5 : io.ntcreatex.in.security_flags = 0;
526 5 : io.ntcreatex.in.fname = sname1;
527 :
528 5 : status = smb_raw_open(cli->tree, tctx, &io);
529 5 : CHECK_STATUS(status, NT_STATUS_OK);
530 5 : fnum = io.ntcreatex.out.file.fnum;
531 :
532 5 : retsize = smbcli_write(cli->tree, fnum, 0, "test data", 0, 9);
533 5 : CHECK_VALUE(retsize, 9);
534 :
535 : /*
536 : * One stream opened without FILE_SHARE_DELETE prevents the main file
537 : * to be deleted or even opened with DELETE access
538 : */
539 :
540 5 : status = smbcli_unlink(cli->tree, fname);
541 5 : CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
542 :
543 5 : io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
544 5 : io.ntcreatex.in.fname = fname;
545 5 : io.ntcreatex.in.access_mask = SEC_STD_DELETE;
546 5 : status = smb_raw_open(cli->tree, tctx, &io);
547 5 : CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
548 :
549 4 : smbcli_close(cli->tree, fnum);
550 :
551 : /*
552 : * ... but unlink works if a stream is opened with FILE_SHARE_DELETE
553 : */
554 :
555 4 : io.ntcreatex.in.fname = sname1;
556 4 : io.ntcreatex.in.access_mask = SEC_FILE_READ_DATA|SEC_FILE_WRITE_DATA;
557 4 : io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE;
558 4 : status = smb_raw_open(cli->tree, tctx, &io);
559 4 : CHECK_STATUS(status, NT_STATUS_OK);
560 4 : fnum = io.ntcreatex.out.file.fnum;
561 :
562 4 : status = smbcli_unlink(cli->tree, fname);
563 4 : CHECK_STATUS(status, NT_STATUS_OK);
564 :
565 : /*
566 : * file access still works on the stream while the main file is closed
567 : */
568 :
569 4 : retsize = smbcli_read(cli->tree, fnum, buf, 0, 9);
570 4 : CHECK_VALUE(retsize, 9);
571 :
572 4 : finfo.generic.level = RAW_FILEINFO_STANDARD;
573 4 : finfo.generic.in.file.path = fname;
574 :
575 : /*
576 : * name-based access to both the main file and the stream does not
577 : * work anymore but gives DELETE_PENDING
578 : */
579 :
580 4 : status = smb_raw_pathinfo(cli->tree, tctx, &finfo);
581 4 : CHECK_STATUS(status, NT_STATUS_DELETE_PENDING);
582 :
583 : /*
584 : * older S3 doesn't do this
585 : */
586 4 : finfo.generic.in.file.path = sname1;
587 4 : status = smb_raw_pathinfo(cli->tree, tctx, &finfo);
588 4 : CHECK_STATUS(status, NT_STATUS_DELETE_PENDING);
589 :
590 : /*
591 : * fd-based qfileinfo on the stream still works, the stream does not
592 : * have the delete-on-close bit set. This could mean that open on the
593 : * stream first opens the main file
594 : */
595 :
596 4 : finfo.all_info.level = RAW_FILEINFO_ALL_INFO;
597 4 : finfo.all_info.in.file.fnum = fnum;
598 :
599 4 : status = smb_raw_fileinfo(cli->tree, tctx, &finfo);
600 4 : CHECK_STATUS(status, NT_STATUS_OK);
601 :
602 : /* w2k and w2k3 return 0 and w2k8 returns 1 */
603 8 : if (TARGET_IS_WINXP(tctx) || TARGET_IS_W2K3(tctx) ||
604 4 : TARGET_IS_SAMBA3(tctx)) {
605 4 : CHECK_VALUE(finfo.all_info.out.delete_pending, 0);
606 : } else {
607 0 : CHECK_VALUE(finfo.all_info.out.delete_pending, 1);
608 : }
609 :
610 4 : smbcli_close(cli->tree, fnum);
611 :
612 : /*
613 : * After closing the stream the file is really gone.
614 : */
615 :
616 4 : finfo.generic.in.file.path = fname;
617 4 : status = smb_raw_pathinfo(cli->tree, tctx, &finfo);
618 4 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
619 :
620 4 : io.ntcreatex.in.access_mask = SEC_FILE_READ_DATA|SEC_FILE_WRITE_DATA
621 : |SEC_STD_DELETE;
622 4 : io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
623 4 : io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
624 4 : status = smb_raw_open(cli->tree, tctx, &io);
625 4 : CHECK_STATUS(status, NT_STATUS_OK);
626 4 : fnum = io.ntcreatex.out.file.fnum;
627 :
628 4 : finfo.generic.in.file.path = fname;
629 4 : status = smb_raw_pathinfo(cli->tree, tctx, &finfo);
630 4 : CHECK_STATUS(status, NT_STATUS_OK);
631 :
632 4 : smbcli_close(cli->tree, fnum);
633 :
634 4 : status = smb_raw_pathinfo(cli->tree, tctx, &finfo);
635 4 : CHECK_STATUS(status, NT_STATUS_OK);
636 5 : done:
637 5 : smbcli_close(cli->tree, fnum);
638 5 : smbcli_unlink(cli->tree, fname);
639 5 : smbcli_deltree(cli->tree, BASEDIR);
640 5 : return ret;
641 : }
642 :
643 : /*
644 : test stream names
645 : */
646 5 : static bool test_stream_names(struct torture_context *tctx,
647 : struct smbcli_state *cli)
648 : {
649 0 : NTSTATUS status;
650 0 : union smb_open io;
651 0 : union smb_fileinfo info;
652 0 : union smb_fileinfo finfo;
653 0 : union smb_fileinfo stinfo;
654 0 : union smb_setfileinfo sinfo;
655 5 : const char *fname = BASEDIR "\\stream_names.txt";
656 0 : const char *sname1, *sname1b, *sname1c, *sname1d;
657 0 : const char *sname2, *snamew, *snamew2;
658 0 : const char *snamer1;
659 5 : bool ret = true;
660 5 : int fnum1 = -1;
661 5 : int fnum2 = -1;
662 5 : int fnum3 = -1;
663 0 : int i;
664 5 : const char *four[4] = {
665 : "::$DATA",
666 : ":\x05Stream\n One:$DATA",
667 : ":MStream Two:$DATA",
668 : ":?Stream*:$DATA"
669 : };
670 5 : const char *five1[5] = {
671 : "::$DATA",
672 : ":\x05Stream\n One:$DATA",
673 : ":BeforeRename:$DATA",
674 : ":MStream Two:$DATA",
675 : ":?Stream*:$DATA"
676 : };
677 5 : const char *five2[5] = {
678 : "::$DATA",
679 : ":\x05Stream\n One:$DATA",
680 : ":AfterRename:$DATA",
681 : ":MStream Two:$DATA",
682 : ":?Stream*:$DATA"
683 : };
684 :
685 5 : torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
686 :
687 5 : sname1 = talloc_asprintf(tctx, "%s:%s", fname, "\x05Stream\n One");
688 5 : sname1b = talloc_asprintf(tctx, "%s:", sname1);
689 5 : sname1c = talloc_asprintf(tctx, "%s:$FOO", sname1);
690 5 : sname1d = talloc_asprintf(tctx, "%s:?D*a", sname1);
691 5 : sname2 = talloc_asprintf(tctx, "%s:%s:$DaTa", fname, "MStream Two");
692 5 : snamew = talloc_asprintf(tctx, "%s:%s:$DATA", fname, "?Stream*");
693 5 : snamew2 = talloc_asprintf(tctx, "%s\\stream*:%s:$DATA", BASEDIR, "?Stream*");
694 5 : snamer1 = talloc_asprintf(tctx, "%s:%s:$DATA", fname, "BeforeRename");
695 :
696 5 : printf("(%s) testing stream names\n", __location__);
697 5 : io.generic.level = RAW_OPEN_NTCREATEX;
698 5 : io.ntcreatex.in.root_fid.fnum = 0;
699 5 : io.ntcreatex.in.flags = 0;
700 5 : io.ntcreatex.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
701 5 : io.ntcreatex.in.create_options = 0;
702 5 : io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
703 5 : io.ntcreatex.in.share_access =
704 : NTCREATEX_SHARE_ACCESS_READ |
705 : NTCREATEX_SHARE_ACCESS_WRITE;
706 5 : io.ntcreatex.in.alloc_size = 0;
707 5 : io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
708 5 : io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
709 5 : io.ntcreatex.in.security_flags = 0;
710 5 : io.ntcreatex.in.fname = fname;
711 :
712 5 : status = smb_raw_open(cli->tree, tctx, &io);
713 5 : CHECK_STATUS(status, NT_STATUS_OK);
714 5 : fnum1 = io.ntcreatex.out.file.fnum;
715 :
716 5 : torture_comment(tctx, "Adding two EAs to base file\n");
717 5 : ZERO_STRUCT(sinfo);
718 5 : sinfo.generic.level = RAW_SFILEINFO_EA_SET;
719 5 : sinfo.generic.in.file.fnum = fnum1;
720 5 : sinfo.ea_set.in.num_eas = 2;
721 5 : sinfo.ea_set.in.eas = talloc_array(tctx, struct ea_struct, 2);
722 5 : sinfo.ea_set.in.eas[0].flags = 0;
723 5 : sinfo.ea_set.in.eas[0].name.s = "EAONE";
724 5 : sinfo.ea_set.in.eas[0].value = data_blob_string_const("VALUE1");
725 5 : sinfo.ea_set.in.eas[1].flags = 0;
726 5 : sinfo.ea_set.in.eas[1].name.s = "SECONDEA";
727 5 : sinfo.ea_set.in.eas[1].value = data_blob_string_const("ValueTwo");
728 :
729 5 : status = smb_raw_setfileinfo(cli->tree, &sinfo);
730 5 : CHECK_STATUS(status, NT_STATUS_OK);
731 :
732 : /*
733 : * Make sure the create time of the streams are different from the
734 : * base file.
735 : */
736 5 : sleep(2);
737 5 : smbcli_close(cli->tree, fnum1);
738 :
739 5 : io.generic.level = RAW_OPEN_NTCREATEX;
740 5 : io.ntcreatex.in.root_fid.fnum = 0;
741 5 : io.ntcreatex.in.flags = 0;
742 5 : io.ntcreatex.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
743 5 : io.ntcreatex.in.create_options = 0;
744 5 : io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
745 5 : io.ntcreatex.in.share_access =
746 : NTCREATEX_SHARE_ACCESS_READ |
747 : NTCREATEX_SHARE_ACCESS_WRITE;
748 5 : io.ntcreatex.in.alloc_size = 0;
749 5 : io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
750 5 : io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
751 5 : io.ntcreatex.in.security_flags = 0;
752 5 : io.ntcreatex.in.fname = sname1;
753 :
754 5 : status = smb_raw_open(cli->tree, tctx, &io);
755 5 : CHECK_STATUS(status, NT_STATUS_OK);
756 5 : fnum1 = io.ntcreatex.out.file.fnum;
757 :
758 5 : torture_comment(tctx, "Adding one EAs to first stream file\n");
759 5 : ZERO_STRUCT(sinfo);
760 5 : sinfo.generic.level = RAW_SFILEINFO_EA_SET;
761 5 : sinfo.generic.in.file.fnum = fnum1;
762 5 : sinfo.ea_set.in.num_eas = 1;
763 5 : sinfo.ea_set.in.eas = talloc_array(tctx, struct ea_struct, 1);
764 5 : sinfo.ea_set.in.eas[0].flags = 0;
765 5 : sinfo.ea_set.in.eas[0].name.s = "STREAMEA";
766 5 : sinfo.ea_set.in.eas[0].value = data_blob_string_const("EA_VALUE1");
767 :
768 5 : status = smb_raw_setfileinfo(cli->tree, &sinfo);
769 5 : CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
770 :
771 5 : status = torture_check_ea(cli, sname1, "STREAMEA", "EA_VALUE1");
772 5 : CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
773 :
774 5 : ZERO_STRUCT(info);
775 5 : info.generic.level = RAW_FILEINFO_ALL_EAS;
776 5 : info.all_eas.in.file.path = sname1;
777 :
778 5 : status = smb_raw_pathinfo(cli->tree, tctx, &info);
779 5 : CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
780 :
781 : /*
782 : * A different stream does not give a sharing violation
783 : */
784 :
785 5 : io.ntcreatex.in.fname = sname2;
786 5 : status = smb_raw_open(cli->tree, tctx, &io);
787 5 : CHECK_STATUS(status, NT_STATUS_OK);
788 5 : fnum2 = io.ntcreatex.out.file.fnum;
789 :
790 : /*
791 : * ... whereas the same stream does with unchanged access/share_access
792 : * flags
793 : */
794 :
795 5 : io.ntcreatex.in.fname = sname1;
796 5 : io.ntcreatex.in.open_disposition = NTCREATEX_DISP_SUPERSEDE;
797 5 : status = smb_raw_open(cli->tree, tctx, &io);
798 5 : CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
799 :
800 5 : io.ntcreatex.in.fname = sname1b;
801 5 : status = smb_raw_open(cli->tree, tctx, &io);
802 5 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
803 :
804 5 : io.ntcreatex.in.fname = sname1c;
805 5 : status = smb_raw_open(cli->tree, tctx, &io);
806 5 : if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
807 : /* w2k returns INVALID_PARAMETER */
808 5 : CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
809 : } else {
810 0 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
811 : }
812 :
813 5 : io.ntcreatex.in.fname = sname1d;
814 5 : status = smb_raw_open(cli->tree, tctx, &io);
815 5 : if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
816 : /* w2k returns INVALID_PARAMETER */
817 5 : CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
818 : } else {
819 0 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
820 : }
821 :
822 5 : io.ntcreatex.in.fname = sname2;
823 5 : status = smb_raw_open(cli->tree, tctx, &io);
824 5 : CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
825 :
826 5 : io.ntcreatex.in.fname = snamew;
827 5 : status = smb_raw_open(cli->tree, tctx, &io);
828 5 : CHECK_STATUS(status, NT_STATUS_OK);
829 5 : fnum3 = io.ntcreatex.out.file.fnum;
830 :
831 5 : io.ntcreatex.in.fname = snamew2;
832 5 : status = smb_raw_open(cli->tree, tctx, &io);
833 5 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
834 :
835 5 : ret &= check_stream_list(tctx, cli, fname, 4, four);
836 :
837 5 : smbcli_close(cli->tree, fnum1);
838 5 : smbcli_close(cli->tree, fnum2);
839 5 : smbcli_close(cli->tree, fnum3);
840 :
841 5 : finfo.generic.level = RAW_FILEINFO_ALL_INFO;
842 5 : finfo.generic.in.file.path = fname;
843 5 : status = smb_raw_pathinfo(cli->tree, tctx, &finfo);
844 5 : CHECK_STATUS(status, NT_STATUS_OK);
845 :
846 5 : ret &= check_stream_list(tctx, cli, fname, 4, four);
847 :
848 25 : for (i=0; i < 4; i++) {
849 0 : NTTIME write_time;
850 0 : uint64_t stream_size;
851 20 : char *path = talloc_asprintf(tctx, "%s%s",
852 : fname, four[i]);
853 :
854 20 : char *rpath = talloc_strdup(path, path);
855 20 : char *p = strrchr(rpath, ':');
856 : /* eat :$DATA */
857 20 : *p = 0;
858 20 : p--;
859 20 : if (*p == ':') {
860 : /* eat ::$DATA */
861 5 : *p = 0;
862 : }
863 20 : printf("(%s): i[%u][%s]\n", __location__, i, path);
864 20 : io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
865 20 : io.ntcreatex.in.access_mask = SEC_FILE_READ_ATTRIBUTE |
866 : SEC_FILE_WRITE_ATTRIBUTE |
867 : SEC_RIGHTS_FILE_ALL;
868 20 : io.ntcreatex.in.fname = path;
869 20 : status = smb_raw_open(cli->tree, tctx, &io);
870 20 : CHECK_STATUS(status, NT_STATUS_OK);
871 20 : fnum1 = io.ntcreatex.out.file.fnum;
872 :
873 20 : finfo.generic.level = RAW_FILEINFO_ALL_INFO;
874 20 : finfo.generic.in.file.path = fname;
875 20 : status = smb_raw_pathinfo(cli->tree, tctx, &finfo);
876 20 : CHECK_STATUS(status, NT_STATUS_OK);
877 :
878 20 : stinfo.generic.level = RAW_FILEINFO_ALL_INFO;
879 20 : stinfo.generic.in.file.fnum = fnum1;
880 20 : status = smb_raw_fileinfo(cli->tree, tctx, &stinfo);
881 20 : CHECK_STATUS(status, NT_STATUS_OK);
882 20 : if (!torture_setting_bool(tctx, "samba3", false)) {
883 4 : CHECK_NTTIME(stinfo.all_info.out.create_time,
884 : finfo.all_info.out.create_time);
885 4 : CHECK_NTTIME(stinfo.all_info.out.access_time,
886 : finfo.all_info.out.access_time);
887 4 : CHECK_NTTIME(stinfo.all_info.out.write_time,
888 : finfo.all_info.out.write_time);
889 4 : CHECK_NTTIME(stinfo.all_info.out.change_time,
890 : finfo.all_info.out.change_time);
891 : }
892 20 : CHECK_VALUE(stinfo.all_info.out.attrib,
893 : finfo.all_info.out.attrib);
894 20 : CHECK_VALUE(stinfo.all_info.out.size,
895 : finfo.all_info.out.size);
896 20 : CHECK_VALUE(stinfo.all_info.out.delete_pending,
897 : finfo.all_info.out.delete_pending);
898 20 : CHECK_VALUE(stinfo.all_info.out.directory,
899 : finfo.all_info.out.directory);
900 20 : CHECK_VALUE(stinfo.all_info.out.ea_size,
901 : finfo.all_info.out.ea_size);
902 :
903 20 : stinfo.generic.level = RAW_FILEINFO_NAME_INFO;
904 20 : stinfo.generic.in.file.fnum = fnum1;
905 20 : status = smb_raw_fileinfo(cli->tree, tctx, &stinfo);
906 20 : CHECK_STATUS(status, NT_STATUS_OK);
907 20 : if (!torture_setting_bool(tctx, "samba3", false)) {
908 4 : CHECK_STR(stinfo.name_info.out.fname.s, rpath);
909 : }
910 :
911 20 : write_time = finfo.all_info.out.write_time;
912 20 : write_time += i*1000000;
913 20 : write_time /= 1000000;
914 20 : write_time *= 1000000;
915 :
916 20 : ZERO_STRUCT(sinfo);
917 20 : sinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFO;
918 20 : sinfo.basic_info.in.file.fnum = fnum1;
919 20 : sinfo.basic_info.in.write_time = write_time;
920 20 : sinfo.basic_info.in.attrib = stinfo.all_info.out.attrib;
921 20 : status = smb_raw_setfileinfo(cli->tree, &sinfo);
922 20 : CHECK_STATUS(status, NT_STATUS_OK);
923 :
924 20 : stream_size = i*8192;
925 :
926 20 : ZERO_STRUCT(sinfo);
927 20 : sinfo.end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFO;
928 20 : sinfo.end_of_file_info.in.file.fnum = fnum1;
929 20 : sinfo.end_of_file_info.in.size = stream_size;
930 20 : status = smb_raw_setfileinfo(cli->tree, &sinfo);
931 20 : CHECK_STATUS(status, NT_STATUS_OK);
932 :
933 20 : stinfo.generic.level = RAW_FILEINFO_ALL_INFO;
934 20 : stinfo.generic.in.file.fnum = fnum1;
935 20 : status = smb_raw_fileinfo(cli->tree, tctx, &stinfo);
936 20 : CHECK_STATUS(status, NT_STATUS_OK);
937 20 : if (!torture_setting_bool(tctx, "samba3", false)) {
938 4 : CHECK_NTTIME(stinfo.all_info.out.write_time,
939 : write_time);
940 4 : CHECK_VALUE(stinfo.all_info.out.attrib,
941 : finfo.all_info.out.attrib);
942 : }
943 20 : CHECK_VALUE(stinfo.all_info.out.size,
944 : stream_size);
945 20 : CHECK_VALUE(stinfo.all_info.out.delete_pending,
946 : finfo.all_info.out.delete_pending);
947 20 : CHECK_VALUE(stinfo.all_info.out.directory,
948 : finfo.all_info.out.directory);
949 20 : CHECK_VALUE(stinfo.all_info.out.ea_size,
950 : finfo.all_info.out.ea_size);
951 :
952 20 : ret &= check_stream_list(tctx, cli, fname, 4, four);
953 :
954 20 : smbcli_close(cli->tree, fnum1);
955 20 : talloc_free(path);
956 : }
957 :
958 5 : printf("(%s): testing stream renames\n", __location__);
959 5 : io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
960 5 : io.ntcreatex.in.access_mask = SEC_FILE_READ_ATTRIBUTE |
961 : SEC_FILE_WRITE_ATTRIBUTE |
962 : SEC_RIGHTS_FILE_ALL;
963 5 : io.ntcreatex.in.fname = snamer1;
964 5 : status = smb_raw_open(cli->tree, tctx, &io);
965 5 : CHECK_STATUS(status, NT_STATUS_OK);
966 5 : fnum1 = io.ntcreatex.out.file.fnum;
967 :
968 5 : ret &= check_stream_list(tctx, cli, fname, 5, five1);
969 :
970 5 : ZERO_STRUCT(sinfo);
971 5 : sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION;
972 5 : sinfo.rename_information.in.file.fnum = fnum1;
973 5 : sinfo.rename_information.in.overwrite = true;
974 5 : sinfo.rename_information.in.root_fid = 0;
975 5 : sinfo.rename_information.in.new_name = ":AfterRename:$DATA";
976 5 : status = smb_raw_setfileinfo(cli->tree, &sinfo);
977 5 : CHECK_STATUS(status, NT_STATUS_OK);
978 :
979 5 : ret &= check_stream_list(tctx, cli, fname, 5, five2);
980 :
981 5 : ZERO_STRUCT(sinfo);
982 5 : sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION;
983 5 : sinfo.rename_information.in.file.fnum = fnum1;
984 5 : sinfo.rename_information.in.overwrite = false;
985 5 : sinfo.rename_information.in.root_fid = 0;
986 5 : sinfo.rename_information.in.new_name = ":MStream Two:$DATA";
987 5 : status = smb_raw_setfileinfo(cli->tree, &sinfo);
988 5 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_COLLISION);
989 :
990 5 : ret &= check_stream_list(tctx, cli, fname, 5, five2);
991 :
992 5 : ZERO_STRUCT(sinfo);
993 5 : sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION;
994 5 : sinfo.rename_information.in.file.fnum = fnum1;
995 5 : sinfo.rename_information.in.overwrite = true;
996 5 : sinfo.rename_information.in.root_fid = 0;
997 5 : sinfo.rename_information.in.new_name = ":MStream Two:$DATA";
998 5 : status = smb_raw_setfileinfo(cli->tree, &sinfo);
999 9 : if (torture_setting_bool(tctx, "samba4", false) ||
1000 4 : torture_setting_bool(tctx, "samba3", false)) {
1001 : /* why should this rename be considered invalid?? */
1002 5 : CHECK_STATUS(status, NT_STATUS_OK);
1003 5 : ret &= check_stream_list(tctx, cli, fname, 4, four);
1004 : } else {
1005 0 : CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
1006 0 : ret &= check_stream_list(tctx, cli, fname, 5, five2);
1007 : }
1008 :
1009 :
1010 : /* TODO: we need to test more rename combinations */
1011 :
1012 5 : done:
1013 5 : if (fnum1 != -1) smbcli_close(cli->tree, fnum1);
1014 5 : if (fnum2 != -1) smbcli_close(cli->tree, fnum2);
1015 5 : if (fnum3 != -1) smbcli_close(cli->tree, fnum3);
1016 5 : status = smbcli_unlink(cli->tree, fname);
1017 5 : smbcli_deltree(cli->tree, BASEDIR);
1018 5 : return ret;
1019 : }
1020 :
1021 : /*
1022 : test stream names
1023 : */
1024 5 : static bool test_stream_names2(struct torture_context *tctx,
1025 : struct smbcli_state *cli)
1026 : {
1027 0 : NTSTATUS status;
1028 0 : union smb_open io;
1029 5 : const char *fname = BASEDIR "\\stream_names2.txt";
1030 5 : bool ret = true;
1031 5 : int fnum1 = -1;
1032 0 : uint8_t i;
1033 :
1034 5 : torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
1035 :
1036 5 : printf("(%s) testing stream names\n", __location__);
1037 5 : io.generic.level = RAW_OPEN_NTCREATEX;
1038 5 : io.ntcreatex.in.root_fid.fnum = 0;
1039 5 : io.ntcreatex.in.flags = 0;
1040 5 : io.ntcreatex.in.access_mask = SEC_FILE_WRITE_DATA;
1041 5 : io.ntcreatex.in.create_options = 0;
1042 5 : io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
1043 5 : io.ntcreatex.in.share_access = 0;
1044 5 : io.ntcreatex.in.alloc_size = 0;
1045 5 : io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
1046 5 : io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
1047 5 : io.ntcreatex.in.security_flags = 0;
1048 5 : io.ntcreatex.in.fname = fname;
1049 5 : status = smb_raw_open(cli->tree, tctx, &io);
1050 5 : CHECK_STATUS(status, NT_STATUS_OK);
1051 5 : fnum1 = io.ntcreatex.out.file.fnum;
1052 :
1053 635 : for (i=0x01; i < 0x7F; i++) {
1054 630 : char *path = talloc_asprintf(tctx, "%s:Stream%c0x%02X:$DATA",
1055 : fname, i, i);
1056 0 : NTSTATUS expected;
1057 :
1058 630 : switch (i) {
1059 15 : case '/':/*0x2F*/
1060 : case ':':/*0x3A*/
1061 : case '\\':/*0x5C*/
1062 15 : expected = NT_STATUS_OBJECT_NAME_INVALID;
1063 15 : break;
1064 615 : default:
1065 615 : expected = NT_STATUS_OBJECT_NAME_NOT_FOUND;
1066 615 : break;
1067 : }
1068 :
1069 :
1070 630 : io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
1071 630 : io.ntcreatex.in.fname = path;
1072 630 : status = smb_raw_open(cli->tree, tctx, &io);
1073 630 : if (!NT_STATUS_EQUAL(status, expected)) {
1074 0 : printf("(%s) %s:Stream%c0x%02X:$DATA%s => expected[%s]\n",
1075 0 : __location__, fname, isprint(i)?(char)i:' ', i,
1076 0 : isprint(i)?"":" (not printable)",
1077 : nt_errstr(expected));
1078 : }
1079 630 : CHECK_STATUS(status, expected);
1080 :
1081 630 : talloc_free(path);
1082 : }
1083 :
1084 5 : done:
1085 5 : if (fnum1 != -1) smbcli_close(cli->tree, fnum1);
1086 5 : status = smbcli_unlink(cli->tree, fname);
1087 5 : smbcli_deltree(cli->tree, BASEDIR);
1088 5 : return ret;
1089 : }
1090 :
1091 : #define CHECK_CALL_FNUM(call, rightstatus) do { \
1092 : sfinfo.generic.level = RAW_SFILEINFO_ ## call; \
1093 : sfinfo.generic.in.file.fnum = fnum; \
1094 : status = smb_raw_setfileinfo(cli->tree, &sfinfo); \
1095 : if (!NT_STATUS_EQUAL(status, rightstatus)) { \
1096 : printf("(%s) %s - %s (should be %s)\n", __location__, #call, \
1097 : nt_errstr(status), nt_errstr(rightstatus)); \
1098 : ret = false; \
1099 : } \
1100 : finfo1.generic.level = RAW_FILEINFO_ALL_INFO; \
1101 : finfo1.generic.in.file.fnum = fnum; \
1102 : status2 = smb_raw_fileinfo(cli->tree, tctx, &finfo1); \
1103 : if (!NT_STATUS_IS_OK(status2)) { \
1104 : printf("(%s) %s pathinfo - %s\n", __location__, #call, nt_errstr(status)); \
1105 : ret = false; \
1106 : }} while (0)
1107 :
1108 : /*
1109 : test stream renames
1110 : */
1111 5 : static bool test_stream_rename(struct torture_context *tctx,
1112 : struct smbcli_state *cli)
1113 : {
1114 0 : NTSTATUS status, status2;
1115 0 : union smb_open io;
1116 5 : const char *fname = BASEDIR "\\stream_rename.txt";
1117 0 : const char *sname1, *sname2;
1118 0 : union smb_fileinfo finfo1;
1119 0 : union smb_setfileinfo sfinfo;
1120 5 : bool ret = true;
1121 5 : int fnum = -1;
1122 :
1123 5 : torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
1124 :
1125 5 : sname1 = talloc_asprintf(tctx, "%s:%s", fname, "Stream One");
1126 5 : sname2 = talloc_asprintf(tctx, "%s:%s:$DaTa", fname, "Second Stream");
1127 :
1128 5 : printf("(%s) testing stream renames\n", __location__);
1129 5 : io.generic.level = RAW_OPEN_NTCREATEX;
1130 5 : io.ntcreatex.in.root_fid.fnum = 0;
1131 5 : io.ntcreatex.in.flags = 0;
1132 5 : io.ntcreatex.in.access_mask = SEC_FILE_READ_ATTRIBUTE |
1133 : SEC_FILE_WRITE_ATTRIBUTE |
1134 : SEC_RIGHTS_FILE_ALL;
1135 5 : io.ntcreatex.in.create_options = 0;
1136 5 : io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
1137 5 : io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE | NTCREATEX_SHARE_ACCESS_DELETE;
1138 5 : io.ntcreatex.in.alloc_size = 0;
1139 5 : io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
1140 5 : io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
1141 5 : io.ntcreatex.in.security_flags = 0;
1142 5 : io.ntcreatex.in.fname = sname1;
1143 :
1144 : /* Create two streams. */
1145 5 : status = smb_raw_open(cli->tree, tctx, &io);
1146 5 : CHECK_STATUS(status, NT_STATUS_OK);
1147 5 : fnum = io.ntcreatex.out.file.fnum;
1148 5 : if (fnum != -1) smbcli_close(cli->tree, fnum);
1149 :
1150 5 : io.ntcreatex.in.fname = sname2;
1151 5 : status = smb_raw_open(cli->tree, tctx, &io);
1152 5 : CHECK_STATUS(status, NT_STATUS_OK);
1153 5 : fnum = io.ntcreatex.out.file.fnum;
1154 :
1155 5 : if (fnum != -1) smbcli_close(cli->tree, fnum);
1156 :
1157 : /*
1158 : * Open the second stream.
1159 : */
1160 :
1161 5 : io.ntcreatex.in.access_mask = SEC_STD_DELETE;
1162 5 : io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
1163 5 : status = smb_raw_open(cli->tree, tctx, &io);
1164 5 : CHECK_STATUS(status, NT_STATUS_OK);
1165 5 : fnum = io.ntcreatex.out.file.fnum;
1166 :
1167 : /*
1168 : * Now rename the second stream onto the first.
1169 : */
1170 :
1171 5 : ZERO_STRUCT(sfinfo);
1172 :
1173 5 : sfinfo.rename_information.in.overwrite = 1;
1174 5 : sfinfo.rename_information.in.root_fid = 0;
1175 5 : sfinfo.rename_information.in.new_name = ":Stream One";
1176 5 : CHECK_CALL_FNUM(RENAME_INFORMATION, NT_STATUS_OK);
1177 :
1178 5 : done:
1179 5 : if (fnum != -1) smbcli_close(cli->tree, fnum);
1180 5 : status = smbcli_unlink(cli->tree, fname);
1181 5 : smbcli_deltree(cli->tree, BASEDIR);
1182 5 : return ret;
1183 : }
1184 :
1185 5 : static bool test_stream_rename2(struct torture_context *tctx,
1186 : struct smbcli_state *cli)
1187 : {
1188 0 : NTSTATUS status;
1189 0 : union smb_open io;
1190 5 : const char *fname1 = BASEDIR "\\stream.txt";
1191 5 : const char *fname2 = BASEDIR "\\stream2.txt";
1192 5 : const char *stream_name1 = ":Stream One:$DATA";
1193 5 : const char *stream_name2 = ":Stream Two:$DATA";
1194 5 : const char *stream_name_default = "::$DATA";
1195 0 : const char *sname1;
1196 0 : const char *sname2;
1197 5 : bool ret = true;
1198 5 : int fnum = -1;
1199 0 : union smb_setfileinfo sinfo;
1200 0 : union smb_rename rio;
1201 :
1202 5 : torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
1203 :
1204 5 : sname1 = talloc_asprintf(tctx, "%s:%s", fname1, "Stream One");
1205 5 : sname2 = talloc_asprintf(tctx, "%s:%s", fname1, "Stream Two");
1206 :
1207 5 : io.generic.level = RAW_OPEN_NTCREATEX;
1208 5 : io.ntcreatex.in.root_fid.fnum = 0;
1209 5 : io.ntcreatex.in.flags = 0;
1210 5 : io.ntcreatex.in.access_mask = (SEC_FILE_READ_DATA|SEC_FILE_WRITE_DATA|
1211 : SEC_STD_DELETE|SEC_FILE_APPEND_DATA|SEC_STD_READ_CONTROL);
1212 5 : io.ntcreatex.in.create_options = 0;
1213 5 : io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
1214 5 : io.ntcreatex.in.share_access = (NTCREATEX_SHARE_ACCESS_READ |
1215 : NTCREATEX_SHARE_ACCESS_WRITE |
1216 : NTCREATEX_SHARE_ACCESS_DELETE);
1217 5 : io.ntcreatex.in.alloc_size = 0;
1218 5 : io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
1219 5 : io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
1220 5 : io.ntcreatex.in.security_flags = 0;
1221 5 : io.ntcreatex.in.fname = sname1;
1222 :
1223 : /* Open/create new stream. */
1224 5 : status = smb_raw_open(cli->tree, tctx, &io);
1225 5 : CHECK_STATUS(status, NT_STATUS_OK);
1226 :
1227 5 : smbcli_close(cli->tree, io.ntcreatex.out.file.fnum);
1228 :
1229 : /*
1230 : * Check raw rename with <base>:<stream>.
1231 : */
1232 5 : printf("(%s) Checking NTRENAME of a stream using <base>:<stream>\n",
1233 : __location__);
1234 5 : rio.generic.level = RAW_RENAME_NTRENAME;
1235 5 : rio.ntrename.in.old_name = sname1;
1236 5 : rio.ntrename.in.new_name = sname2;
1237 5 : rio.ntrename.in.attrib = 0;
1238 5 : rio.ntrename.in.cluster_size = 0;
1239 5 : rio.ntrename.in.flags = RENAME_FLAG_RENAME;
1240 5 : status = smb_raw_rename(cli->tree, &rio);
1241 5 : CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
1242 :
1243 : /*
1244 : * Check raw rename to the default stream using :<stream>.
1245 : */
1246 5 : printf("(%s) Checking NTRENAME to default stream using :<stream>\n",
1247 : __location__);
1248 5 : rio.ntrename.in.new_name = stream_name_default;
1249 5 : status = smb_raw_rename(cli->tree, &rio);
1250 5 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_COLLISION);
1251 :
1252 : /*
1253 : * Check raw rename using :<stream>.
1254 : */
1255 5 : printf("(%s) Checking NTRENAME of a stream using :<stream>\n",
1256 : __location__);
1257 5 : rio.ntrename.in.new_name = stream_name2;
1258 5 : status = smb_raw_rename(cli->tree, &rio);
1259 5 : CHECK_STATUS(status, NT_STATUS_OK);
1260 :
1261 : /*
1262 : * Check raw rename of a stream to a file.
1263 : */
1264 5 : printf("(%s) Checking NTRENAME of a stream to a file\n",
1265 : __location__);
1266 5 : rio.ntrename.in.old_name = sname2;
1267 5 : rio.ntrename.in.new_name = fname2;
1268 5 : status = smb_raw_rename(cli->tree, &rio);
1269 5 : CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
1270 :
1271 : /*
1272 : * Check raw rename of a file to a stream.
1273 : */
1274 5 : printf("(%s) Checking NTRENAME of a file to a stream\n",
1275 : __location__);
1276 :
1277 : /* Create the file. */
1278 5 : io.ntcreatex.in.fname = fname2;
1279 5 : status = smb_raw_open(cli->tree, tctx, &io);
1280 5 : CHECK_STATUS(status, NT_STATUS_OK);
1281 5 : smbcli_close(cli->tree, io.ntcreatex.out.file.fnum);
1282 :
1283 : /* Try the rename. */
1284 5 : rio.ntrename.in.old_name = fname2;
1285 5 : rio.ntrename.in.new_name = sname1;
1286 5 : status = smb_raw_rename(cli->tree, &rio);
1287 5 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
1288 :
1289 : /*
1290 : * Reopen the stream for trans2 renames.
1291 : */
1292 5 : io.ntcreatex.in.fname = sname2;
1293 5 : io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
1294 5 : status = smb_raw_open(cli->tree, tctx, &io);
1295 5 : CHECK_STATUS(status, NT_STATUS_OK);
1296 5 : fnum = io.ntcreatex.out.file.fnum;
1297 :
1298 : /*
1299 : * Check trans2 rename of a stream using :<stream>.
1300 : */
1301 5 : printf("(%s) Checking trans2 rename of a stream using :<stream>\n",
1302 : __location__);
1303 5 : ZERO_STRUCT(sinfo);
1304 5 : sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION;
1305 5 : sinfo.rename_information.in.file.fnum = fnum;
1306 5 : sinfo.rename_information.in.overwrite = 1;
1307 5 : sinfo.rename_information.in.root_fid = 0;
1308 5 : sinfo.rename_information.in.new_name = stream_name1;
1309 5 : status = smb_raw_setfileinfo(cli->tree, &sinfo);
1310 5 : CHECK_STATUS(status, NT_STATUS_OK);
1311 :
1312 : /*
1313 : * Check trans2 rename of an overwriting stream using :<stream>.
1314 : */
1315 5 : printf("(%s) Checking trans2 rename of an overwriting stream using "
1316 : ":<stream>\n", __location__);
1317 :
1318 : /* Create second stream. */
1319 5 : io.ntcreatex.in.fname = sname2;
1320 5 : io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
1321 5 : status = smb_raw_open(cli->tree, tctx, &io);
1322 5 : CHECK_STATUS(status, NT_STATUS_OK);
1323 5 : smbcli_close(cli->tree, io.ntcreatex.out.file.fnum);
1324 :
1325 : /* Rename the first stream onto the second. */
1326 5 : sinfo.rename_information.in.file.fnum = fnum;
1327 5 : sinfo.rename_information.in.new_name = stream_name2;
1328 5 : status = smb_raw_setfileinfo(cli->tree, &sinfo);
1329 5 : CHECK_STATUS(status, NT_STATUS_OK);
1330 :
1331 5 : smbcli_close(cli->tree, fnum);
1332 :
1333 : /*
1334 : * Reopen the stream with the new name.
1335 : */
1336 5 : io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
1337 5 : io.ntcreatex.in.fname = sname2;
1338 5 : status = smb_raw_open(cli->tree, tctx, &io);
1339 5 : CHECK_STATUS(status, NT_STATUS_OK);
1340 5 : fnum = io.ntcreatex.out.file.fnum;
1341 :
1342 : /*
1343 : * Check trans2 rename of a stream using <base>:<stream>.
1344 : */
1345 5 : printf("(%s) Checking trans2 rename of a stream using "
1346 : "<base>:<stream>\n", __location__);
1347 5 : sinfo.rename_information.in.file.fnum = fnum;
1348 5 : sinfo.rename_information.in.new_name = sname1;
1349 5 : status = smb_raw_setfileinfo(cli->tree, &sinfo);
1350 5 : CHECK_STATUS(status, NT_STATUS_NOT_SUPPORTED);
1351 :
1352 : /*
1353 : * Samba3 doesn't currently support renaming a stream to the default
1354 : * stream. This test does pass on windows.
1355 : */
1356 6 : if (torture_setting_bool(tctx, "samba3", false) ||
1357 1 : torture_setting_bool(tctx, "samba4", false)) {
1358 5 : goto done;
1359 : }
1360 :
1361 : /*
1362 : * Check trans2 rename to the default stream using :<stream>.
1363 : */
1364 0 : printf("(%s) Checking trans2 rename to defaualt stream using "
1365 : ":<stream>\n", __location__);
1366 0 : sinfo.rename_information.in.file.fnum = fnum;
1367 0 : sinfo.rename_information.in.new_name = stream_name_default;
1368 0 : status = smb_raw_setfileinfo(cli->tree, &sinfo);
1369 0 : CHECK_STATUS(status, NT_STATUS_OK);
1370 :
1371 0 : smbcli_close(cli->tree, fnum);
1372 :
1373 5 : done:
1374 5 : smbcli_close(cli->tree, fnum);
1375 5 : status = smbcli_unlink(cli->tree, fname1);
1376 5 : status = smbcli_unlink(cli->tree, fname2);
1377 5 : smbcli_deltree(cli->tree, BASEDIR);
1378 5 : return ret;
1379 : }
1380 :
1381 : /*
1382 : test stream renames
1383 : */
1384 5 : static bool test_stream_rename3(struct torture_context *tctx,
1385 : struct smbcli_state *cli)
1386 : {
1387 0 : NTSTATUS status, status2;
1388 0 : union smb_open io;
1389 5 : const char *fname = BASEDIR "\\stream_rename.txt";
1390 0 : const char *sname1, *sname2;
1391 0 : union smb_fileinfo finfo1;
1392 0 : union smb_setfileinfo sfinfo;
1393 5 : bool ret = true;
1394 5 : int fnum = -1;
1395 5 : int fnum2 = -1;
1396 :
1397 5 : torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
1398 :
1399 5 : sname1 = talloc_asprintf(tctx, "%s:%s", fname, "MStream Two:$DATA");
1400 5 : sname2 = talloc_asprintf(tctx, "%s:%s:$DaTa", fname, "Second Stream");
1401 :
1402 5 : printf("(%s) testing stream renames\n", __location__);
1403 5 : io.generic.level = RAW_OPEN_NTCREATEX;
1404 5 : io.ntcreatex.in.root_fid.fnum = 0;
1405 5 : io.ntcreatex.in.flags = 0;
1406 5 : io.ntcreatex.in.access_mask = SEC_FILE_READ_ATTRIBUTE |
1407 : SEC_FILE_WRITE_ATTRIBUTE |
1408 : SEC_RIGHTS_FILE_ALL;
1409 5 : io.ntcreatex.in.create_options = 0;
1410 5 : io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
1411 5 : io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
1412 : NTCREATEX_SHARE_ACCESS_WRITE | NTCREATEX_SHARE_ACCESS_DELETE;
1413 5 : io.ntcreatex.in.alloc_size = 0;
1414 5 : io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
1415 5 : io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
1416 5 : io.ntcreatex.in.security_flags = 0;
1417 5 : io.ntcreatex.in.fname = sname1;
1418 :
1419 : /* Create two streams. */
1420 5 : status = smb_raw_open(cli->tree, tctx, &io);
1421 5 : CHECK_STATUS(status, NT_STATUS_OK);
1422 5 : fnum = io.ntcreatex.out.file.fnum;
1423 5 : if (fnum != -1) smbcli_close(cli->tree, fnum);
1424 :
1425 5 : io.ntcreatex.in.fname = sname2;
1426 5 : status = smb_raw_open(cli->tree, tctx, &io);
1427 5 : CHECK_STATUS(status, NT_STATUS_OK);
1428 5 : fnum = io.ntcreatex.out.file.fnum;
1429 :
1430 5 : if (fnum != -1) smbcli_close(cli->tree, fnum);
1431 :
1432 : /* open the second stream. */
1433 5 : io.ntcreatex.in.access_mask = SEC_STD_DELETE;
1434 5 : io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
1435 5 : status = smb_raw_open(cli->tree, tctx, &io);
1436 5 : CHECK_STATUS(status, NT_STATUS_OK);
1437 5 : fnum = io.ntcreatex.out.file.fnum;
1438 :
1439 : /* Keep a handle to the first stream open. */
1440 5 : io.ntcreatex.in.fname = sname1;
1441 5 : io.ntcreatex.in.access_mask = SEC_STD_DELETE;
1442 5 : io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
1443 5 : status = smb_raw_open(cli->tree, tctx, &io);
1444 5 : CHECK_STATUS(status, NT_STATUS_OK);
1445 5 : fnum2 = io.ntcreatex.out.file.fnum;
1446 :
1447 5 : ZERO_STRUCT(sfinfo);
1448 5 : sfinfo.rename_information.in.overwrite = 1;
1449 5 : sfinfo.rename_information.in.root_fid = 0;
1450 5 : sfinfo.rename_information.in.new_name = ":MStream Two:$DATA";
1451 9 : if (torture_setting_bool(tctx, "samba4", false) ||
1452 4 : torture_setting_bool(tctx, "samba3", false)) {
1453 5 : CHECK_CALL_FNUM(RENAME_INFORMATION, NT_STATUS_OK);
1454 : } else {
1455 0 : CHECK_CALL_FNUM(RENAME_INFORMATION,
1456 : NT_STATUS_INVALID_PARAMETER);
1457 : }
1458 :
1459 :
1460 0 : done:
1461 5 : if (fnum != -1) smbcli_close(cli->tree, fnum);
1462 5 : if (fnum2 != -1) smbcli_close(cli->tree, fnum2);
1463 5 : status = smbcli_unlink(cli->tree, fname);
1464 5 : smbcli_deltree(cli->tree, BASEDIR);
1465 5 : return ret;
1466 : }
1467 :
1468 41 : static bool create_file_with_stream(struct torture_context *tctx,
1469 : struct smbcli_state *cli,
1470 : const char *stream)
1471 : {
1472 0 : NTSTATUS status;
1473 41 : bool ret = true;
1474 0 : union smb_open io;
1475 :
1476 41 : ZERO_STRUCT(io);
1477 :
1478 : /* Create a file with a stream */
1479 41 : io.generic.level = RAW_OPEN_NTCREATEX;
1480 41 : io.ntcreatex.in.root_fid.fnum = 0;
1481 41 : io.ntcreatex.in.flags = 0;
1482 41 : io.ntcreatex.in.access_mask = (SEC_FILE_READ_DATA|SEC_FILE_WRITE_DATA|
1483 : SEC_FILE_APPEND_DATA|SEC_STD_READ_CONTROL);
1484 41 : io.ntcreatex.in.create_options = 0;
1485 41 : io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
1486 41 : io.ntcreatex.in.share_access = 0;
1487 41 : io.ntcreatex.in.alloc_size = 0;
1488 41 : io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
1489 41 : io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
1490 41 : io.ntcreatex.in.security_flags = 0;
1491 41 : io.ntcreatex.in.fname = stream;
1492 :
1493 41 : status = smb_raw_open(cli->tree, tctx, &io);
1494 41 : CHECK_STATUS(status, NT_STATUS_OK);
1495 :
1496 41 : done:
1497 41 : smbcli_close(cli->tree, io.ntcreatex.out.file.fnum);
1498 41 : return ret;
1499 : }
1500 :
1501 : /* Test how streams interact with create dispositions */
1502 5 : static bool test_stream_create_disposition(struct torture_context *tctx,
1503 : struct smbcli_state *cli)
1504 : {
1505 0 : NTSTATUS status;
1506 0 : union smb_open io;
1507 5 : const char *fname = BASEDIR "\\stream.txt";
1508 5 : const char *stream = "Stream One:$DATA";
1509 0 : const char *fname_stream;
1510 5 : const char *default_stream_name = "::$DATA";
1511 0 : const char *stream_list[2];
1512 5 : bool ret = false;
1513 5 : int fnum = -1;
1514 :
1515 5 : torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
1516 :
1517 5 : fname_stream = talloc_asprintf(tctx, "%s:%s", fname, stream);
1518 :
1519 5 : stream_list[0] = talloc_asprintf(tctx, ":%s", stream);
1520 5 : stream_list[1] = default_stream_name;
1521 :
1522 5 : if (!create_file_with_stream(tctx, cli, fname_stream)) {
1523 0 : goto done;
1524 : }
1525 :
1526 : /* Open the base file with OPEN */
1527 5 : io.generic.level = RAW_OPEN_NTCREATEX;
1528 5 : io.ntcreatex.in.root_fid.fnum = 0;
1529 5 : io.ntcreatex.in.flags = 0;
1530 5 : io.ntcreatex.in.access_mask = (SEC_FILE_READ_DATA|SEC_FILE_WRITE_DATA|
1531 : SEC_FILE_APPEND_DATA|SEC_STD_READ_CONTROL);
1532 5 : io.ntcreatex.in.create_options = 0;
1533 5 : io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
1534 5 : io.ntcreatex.in.share_access = 0;
1535 5 : io.ntcreatex.in.alloc_size = 0;
1536 5 : io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
1537 5 : io.ntcreatex.in.security_flags = 0;
1538 5 : io.ntcreatex.in.fname = fname;
1539 :
1540 : /*
1541 : * check ntcreatex open: sanity check
1542 : */
1543 5 : printf("(%s) Checking ntcreatex disp: open\n", __location__);
1544 5 : io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
1545 5 : status = smb_raw_open(cli->tree, tctx, &io);
1546 5 : CHECK_STATUS(status, NT_STATUS_OK);
1547 5 : smbcli_close(cli->tree, io.ntcreatex.out.file.fnum);
1548 5 : if (!check_stream_list(tctx, cli, fname, 2, stream_list)) {
1549 0 : goto done;
1550 : }
1551 :
1552 : /*
1553 : * check ntcreatex overwrite
1554 : */
1555 5 : printf("(%s) Checking ntcreatex disp: overwrite\n", __location__);
1556 5 : io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OVERWRITE;
1557 5 : status = smb_raw_open(cli->tree, tctx, &io);
1558 5 : CHECK_STATUS(status, NT_STATUS_OK);
1559 5 : smbcli_close(cli->tree, io.ntcreatex.out.file.fnum);
1560 5 : if (!check_stream_list(tctx, cli, fname, 1, &default_stream_name)) {
1561 1 : goto done;
1562 : }
1563 :
1564 : /*
1565 : * check ntcreatex overwrite_if
1566 : */
1567 4 : printf("(%s) Checking ntcreatex disp: overwrite_if\n", __location__);
1568 4 : smbcli_unlink(cli->tree, fname);
1569 4 : if (!create_file_with_stream(tctx, cli, fname_stream)) {
1570 0 : goto done;
1571 : }
1572 :
1573 4 : io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OVERWRITE_IF;
1574 4 : status = smb_raw_open(cli->tree, tctx, &io);
1575 4 : CHECK_STATUS(status, NT_STATUS_OK);
1576 4 : smbcli_close(cli->tree, io.ntcreatex.out.file.fnum);
1577 4 : if (!check_stream_list(tctx, cli, fname, 1, &default_stream_name)) {
1578 0 : goto done;
1579 : }
1580 :
1581 : /*
1582 : * check ntcreatex supersede
1583 : */
1584 4 : printf("(%s) Checking ntcreatex disp: supersede\n", __location__);
1585 4 : smbcli_unlink(cli->tree, fname);
1586 4 : if (!create_file_with_stream(tctx, cli, fname_stream)) {
1587 0 : goto done;
1588 : }
1589 :
1590 4 : io.ntcreatex.in.open_disposition = NTCREATEX_DISP_SUPERSEDE;
1591 4 : status = smb_raw_open(cli->tree, tctx, &io);
1592 4 : CHECK_STATUS(status, NT_STATUS_OK);
1593 4 : smbcli_close(cli->tree, io.ntcreatex.out.file.fnum);
1594 4 : if (!check_stream_list(tctx, cli, fname, 1, &default_stream_name)) {
1595 0 : goto done;
1596 : }
1597 :
1598 : /*
1599 : * check ntcreatex overwrite_if on a stream.
1600 : */
1601 4 : printf("(%s) Checking ntcreatex disp: overwrite_if on stream\n",
1602 : __location__);
1603 4 : smbcli_unlink(cli->tree, fname);
1604 4 : if (!create_file_with_stream(tctx, cli, fname_stream)) {
1605 0 : goto done;
1606 : }
1607 :
1608 4 : io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OVERWRITE_IF;
1609 4 : io.ntcreatex.in.fname = fname_stream;
1610 4 : status = smb_raw_open(cli->tree, tctx, &io);
1611 4 : CHECK_STATUS(status, NT_STATUS_OK);
1612 4 : smbcli_close(cli->tree, io.ntcreatex.out.file.fnum);
1613 4 : if (!check_stream_list(tctx, cli, fname, 2, stream_list)) {
1614 0 : goto done;
1615 : }
1616 :
1617 : /*
1618 : * check openx overwrite_if
1619 : */
1620 4 : printf("(%s) Checking openx disp: overwrite_if\n", __location__);
1621 4 : smbcli_unlink(cli->tree, fname);
1622 4 : if (!create_file_with_stream(tctx, cli, fname_stream)) {
1623 0 : goto done;
1624 : }
1625 :
1626 4 : io.openx.level = RAW_OPEN_OPENX;
1627 4 : io.openx.in.flags = OPENX_FLAGS_ADDITIONAL_INFO;
1628 4 : io.openx.in.open_mode = OPENX_MODE_ACCESS_RDWR | OPEN_FLAGS_DENY_NONE;
1629 4 : io.openx.in.search_attrs = 0;
1630 4 : io.openx.in.file_attrs = 0;
1631 4 : io.openx.in.write_time = 0;
1632 4 : io.openx.in.size = 1024*1024;
1633 4 : io.openx.in.timeout = 0;
1634 4 : io.openx.in.fname = fname;
1635 :
1636 4 : io.openx.in.open_func = OPENX_OPEN_FUNC_TRUNC | OPENX_OPEN_FUNC_CREATE;
1637 4 : status = smb_raw_open(cli->tree, tctx, &io);
1638 4 : CHECK_STATUS(status, NT_STATUS_OK);
1639 4 : smbcli_close(cli->tree, io.openx.out.file.fnum);
1640 4 : if (!check_stream_list(tctx, cli, fname, 1, &default_stream_name)) {
1641 0 : goto done;
1642 : }
1643 :
1644 4 : ret = true;
1645 :
1646 5 : done:
1647 5 : smbcli_close(cli->tree, fnum);
1648 5 : smbcli_unlink(cli->tree, fname);
1649 5 : smbcli_deltree(cli->tree, BASEDIR);
1650 5 : return ret;
1651 : }
1652 :
1653 : #if 0
1654 : /* Test streaminfo with enough streams on a file to fill up the buffer. */
1655 : static bool test_stream_large_streaminfo(struct torture_context *tctx,
1656 : struct smbcli_state *cli)
1657 : {
1658 : #define LONG_STREAM_SIZE 2
1659 : char *lstream_name;
1660 : const char *fname = BASEDIR "\\stream.txt";
1661 : const char *fname_stream;
1662 : NTSTATUS status;
1663 : bool ret = true;
1664 : int i;
1665 : union smb_fileinfo finfo;
1666 :
1667 : torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
1668 :
1669 : lstream_name = talloc_array(tctx, char, LONG_STREAM_SIZE);
1670 :
1671 : for (i = 0; i < LONG_STREAM_SIZE - 1; i++) {
1672 : lstream_name[i] = (char)('a' + i%26);
1673 : }
1674 : lstream_name[LONG_STREAM_SIZE - 1] = '\0';
1675 :
1676 : torture_comment(tctx, "(%s) Creating a file with a lot of streams\n", __location__);
1677 : for (i = 0; i < 10000; i++) {
1678 : fname_stream = talloc_asprintf(tctx, "%s:%s%d", fname,
1679 : lstream_name, i);
1680 : ret = create_file_with_stream(tctx, cli, fname_stream);
1681 : if (!ret) {
1682 : goto done;
1683 : }
1684 : }
1685 :
1686 : finfo.generic.level = RAW_FILEINFO_STREAM_INFO;
1687 : finfo.generic.in.file.path = fname;
1688 :
1689 : status = smb_raw_pathinfo(cli->tree, tctx, &finfo);
1690 : CHECK_STATUS(status, STATUS_BUFFER_OVERFLOW);
1691 :
1692 : done:
1693 : smbcli_unlink(cli->tree, fname);
1694 : smbcli_deltree(cli->tree, BASEDIR);
1695 : return ret;
1696 : }
1697 : #endif
1698 :
1699 : /* Test the effect of setting attributes on a stream. */
1700 5 : static bool test_stream_attributes(struct torture_context *tctx,
1701 : struct smbcli_state *cli)
1702 : {
1703 5 : bool ret = true;
1704 0 : NTSTATUS status;
1705 0 : union smb_open io;
1706 5 : const char *fname = BASEDIR "\\stream_attr.txt";
1707 5 : const char *stream = "Stream One:$DATA";
1708 0 : const char *fname_stream;
1709 5 : int fnum = -1;
1710 0 : union smb_fileinfo finfo;
1711 0 : union smb_setfileinfo sfinfo;
1712 5 : time_t basetime = (time(NULL) - 86400) & ~1;
1713 :
1714 5 : torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
1715 :
1716 5 : torture_comment(tctx, "(%s) testing attribute setting on stream\n", __location__);
1717 :
1718 5 : fname_stream = talloc_asprintf(tctx, "%s:%s", fname, stream);
1719 :
1720 : /* Create a file with a stream with attribute FILE_ATTRIBUTE_ARCHIVE. */
1721 5 : ret = create_file_with_stream(tctx, cli, fname_stream);
1722 5 : if (!ret) {
1723 0 : goto done;
1724 : }
1725 :
1726 5 : ZERO_STRUCT(finfo);
1727 5 : finfo.generic.level = RAW_FILEINFO_BASIC_INFO;
1728 5 : finfo.generic.in.file.path = fname;
1729 5 : status = smb_raw_pathinfo(cli->tree, tctx, &finfo);
1730 5 : CHECK_STATUS(status, NT_STATUS_OK);
1731 :
1732 5 : torture_assert_int_equal_goto(tctx, finfo.all_info.out.attrib & ~FILE_ATTRIBUTE_NONINDEXED, FILE_ATTRIBUTE_ARCHIVE, ret, done, "attrib incorrect");
1733 :
1734 : /* Now open the stream name. */
1735 :
1736 5 : io.generic.level = RAW_OPEN_NTCREATEX;
1737 5 : io.ntcreatex.in.root_fid.fnum = 0;
1738 5 : io.ntcreatex.in.flags = 0;
1739 5 : io.ntcreatex.in.access_mask = (SEC_FILE_READ_DATA|SEC_FILE_WRITE_DATA|
1740 : SEC_FILE_APPEND_DATA|SEC_STD_READ_CONTROL|SEC_FILE_WRITE_ATTRIBUTE);
1741 5 : io.ntcreatex.in.create_options = 0;
1742 5 : io.ntcreatex.in.file_attr = 0;
1743 5 : io.ntcreatex.in.share_access = 0;
1744 5 : io.ntcreatex.in.alloc_size = 0;
1745 5 : io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
1746 5 : io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
1747 5 : io.ntcreatex.in.security_flags = 0;
1748 5 : io.ntcreatex.in.fname = fname_stream;
1749 :
1750 5 : status = smb_raw_open(cli->tree, tctx, &io);
1751 5 : CHECK_STATUS(status, NT_STATUS_OK);
1752 :
1753 5 : fnum = io.ntcreatex.out.file.fnum;
1754 :
1755 : /* Change the attributes + time on the stream fnum. */
1756 5 : ZERO_STRUCT(sfinfo);
1757 5 : sfinfo.basic_info.in.attrib = FILE_ATTRIBUTE_READONLY;
1758 5 : unix_to_nt_time(&sfinfo.basic_info.in.write_time, basetime);
1759 :
1760 5 : sfinfo.generic.level = RAW_SFILEINFO_BASIC_INFORMATION;
1761 5 : sfinfo.generic.in.file.fnum = fnum;
1762 5 : status = smb_raw_setfileinfo(cli->tree, &sfinfo);
1763 5 : torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OK, ret, done, "smb_raw_setfileinfo failed");
1764 :
1765 5 : smbcli_close(cli->tree, fnum);
1766 5 : fnum = -1;
1767 :
1768 5 : ZERO_STRUCT(finfo);
1769 5 : finfo.generic.level = RAW_FILEINFO_ALL_INFO;
1770 5 : finfo.generic.in.file.path = fname;
1771 5 : status = smb_raw_pathinfo(cli->tree, tctx, &finfo);
1772 5 : torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OK, ret, done, "smb_raw_pathinfo failed");
1773 :
1774 5 : torture_assert_int_equal_goto(tctx, finfo.all_info.out.attrib & ~FILE_ATTRIBUTE_NONINDEXED, FILE_ATTRIBUTE_READONLY, ret, done, "attrib incorrect");
1775 :
1776 5 : torture_assert_int_equal_goto(tctx, nt_time_to_unix(finfo.all_info.out.write_time), basetime, ret, done, "time incorrect");
1777 :
1778 5 : done:
1779 :
1780 5 : if (fnum != -1) {
1781 0 : smbcli_close(cli->tree, fnum);
1782 : }
1783 5 : smbcli_unlink(cli->tree, fname);
1784 5 : smbcli_deltree(cli->tree, BASEDIR);
1785 5 : return ret;
1786 : }
1787 :
1788 : /**
1789 : * A rough approximation of how a windows client creates the streams for use
1790 : * in the summary tab.
1791 : */
1792 5 : static bool test_stream_summary_tab(struct torture_context *tctx,
1793 : struct smbcli_state *cli)
1794 : {
1795 5 : bool ret = true;
1796 0 : NTSTATUS status;
1797 0 : union smb_open io;
1798 5 : const char *fname = BASEDIR "\\stream_summary.txt";
1799 5 : const char *stream = ":\005SummaryInformation:$DATA";
1800 5 : const char *fname_stream = NULL;
1801 5 : const char *tmp_stream = ":Updt_\005SummaryInformation:$DATA";
1802 5 : const char *fname_tmp_stream = NULL;
1803 5 : int fnum = -1;
1804 0 : union smb_fileinfo finfo;
1805 0 : union smb_rename rio;
1806 0 : ssize_t retsize;
1807 :
1808 5 : torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
1809 :
1810 5 : fname_stream = talloc_asprintf(tctx, "%s%s", fname, stream);
1811 5 : fname_tmp_stream = talloc_asprintf(tctx, "%s%s", fname,
1812 : tmp_stream);
1813 :
1814 : /* Create summary info stream */
1815 5 : ret = create_file_with_stream(tctx, cli, fname_stream);
1816 5 : if (!ret) {
1817 0 : goto done;
1818 : }
1819 :
1820 : /* Create summary info tmp update stream */
1821 5 : ret = create_file_with_stream(tctx, cli, fname_tmp_stream);
1822 5 : if (!ret) {
1823 0 : goto done;
1824 : }
1825 :
1826 : /* Open tmp stream and write to it */
1827 5 : io.generic.level = RAW_OPEN_NTCREATEX;
1828 5 : io.ntcreatex.in.root_fid.fnum = 0;
1829 5 : io.ntcreatex.in.flags = 0;
1830 5 : io.ntcreatex.in.access_mask = SEC_FILE_READ_DATA|SEC_FILE_WRITE_DATA;
1831 5 : io.ntcreatex.in.create_options = 0;
1832 5 : io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
1833 5 : io.ntcreatex.in.share_access = 0;
1834 5 : io.ntcreatex.in.alloc_size = 0;
1835 5 : io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
1836 5 : io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
1837 5 : io.ntcreatex.in.security_flags = 0;
1838 5 : io.ntcreatex.in.fname = fname_tmp_stream;
1839 :
1840 5 : status = smb_raw_open(cli->tree, tctx, &io);
1841 5 : CHECK_STATUS(status, NT_STATUS_OK);
1842 5 : fnum = io.ntcreatex.out.file.fnum;
1843 :
1844 5 : retsize = smbcli_write(cli->tree, fnum, 0, "test data", 0, 9);
1845 5 : CHECK_VALUE(retsize, 9);
1846 :
1847 : /* close the tmp stream. */
1848 5 : smbcli_close(cli->tree, fnum);
1849 5 : fnum = -1;
1850 :
1851 : /* Delete the current stream */
1852 5 : smbcli_unlink(cli->tree, fname_stream);
1853 :
1854 : /* Do the rename. */
1855 5 : rio.generic.level = RAW_RENAME_RENAME;
1856 5 : rio.rename.in.pattern1 = fname_tmp_stream;
1857 5 : rio.rename.in.pattern2 = stream;
1858 5 : rio.rename.in.attrib = FILE_ATTRIBUTE_SYSTEM |
1859 : FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_DIRECTORY;
1860 5 : status = smb_raw_rename(cli->tree, &rio);
1861 5 : CHECK_STATUS(status, NT_STATUS_OK);
1862 :
1863 : /* Try to open the tmp stream that we just renamed away. */
1864 4 : status = smb_raw_open(cli->tree, tctx, &io);
1865 4 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1866 :
1867 : /* Query the base file to make sure it's still there. */
1868 4 : finfo.generic.level = RAW_FILEINFO_BASIC_INFO;
1869 4 : finfo.generic.in.file.path = fname;
1870 :
1871 4 : status = smb_raw_pathinfo(cli->tree, tctx, &finfo);
1872 4 : CHECK_STATUS(status, NT_STATUS_OK);
1873 :
1874 5 : done:
1875 :
1876 5 : if (fnum != -1) {
1877 0 : smbcli_close(cli->tree, fnum);
1878 : }
1879 5 : smbcli_unlink(cli->tree, fname);
1880 :
1881 5 : smbcli_deltree(cli->tree, BASEDIR);
1882 5 : return ret;
1883 : }
1884 :
1885 : /* Test how streams interact with base file permissions */
1886 : /* Regression test for bug:
1887 : https://bugzilla.samba.org/show_bug.cgi?id=10229
1888 : bug #10229 - No access check verification on stream files.
1889 : */
1890 5 : static bool test_stream_permissions(struct torture_context *tctx,
1891 : struct smbcli_state *cli)
1892 : {
1893 0 : NTSTATUS status;
1894 5 : bool ret = true;
1895 0 : union smb_open io;
1896 5 : const char *fname = BASEDIR "\\stream_permissions.txt";
1897 5 : const char *stream = "Stream One:$DATA";
1898 0 : const char *fname_stream;
1899 0 : union smb_fileinfo finfo;
1900 0 : union smb_setfileinfo sfinfo;
1901 5 : int fnum = -1;
1902 0 : union smb_fileinfo q;
1903 0 : union smb_setfileinfo set;
1904 5 : struct security_ace ace = {};
1905 0 : struct security_descriptor *sd;
1906 :
1907 5 : torture_assert(tctx, torture_setup_dir(cli, BASEDIR),
1908 : "Failed to setup up test directory: " BASEDIR);
1909 :
1910 5 : torture_comment(tctx, "(%s) testing permissions on streams\n", __location__);
1911 :
1912 5 : fname_stream = talloc_asprintf(tctx, "%s:%s", fname, stream);
1913 :
1914 : /* Create a file with a stream with attribute FILE_ATTRIBUTE_ARCHIVE. */
1915 5 : ret = create_file_with_stream(tctx, cli, fname_stream);
1916 5 : if (!ret) {
1917 0 : goto done;
1918 : }
1919 :
1920 5 : ZERO_STRUCT(finfo);
1921 5 : finfo.generic.level = RAW_FILEINFO_BASIC_INFO;
1922 5 : finfo.generic.in.file.path = fname;
1923 5 : status = smb_raw_pathinfo(cli->tree, tctx, &finfo);
1924 5 : CHECK_STATUS(status, NT_STATUS_OK);
1925 :
1926 5 : torture_assert_int_equal_goto(tctx,
1927 : finfo.all_info.out.attrib & ~FILE_ATTRIBUTE_NONINDEXED,
1928 : FILE_ATTRIBUTE_ARCHIVE, ret, done, "attrib incorrect");
1929 :
1930 : /* Change the attributes on the base file name. */
1931 5 : ZERO_STRUCT(sfinfo);
1932 5 : sfinfo.generic.level = RAW_SFILEINFO_SETATTR;
1933 5 : sfinfo.generic.in.file.path = fname;
1934 5 : sfinfo.setattr.in.attrib = FILE_ATTRIBUTE_READONLY;
1935 :
1936 5 : status = smb_raw_setpathinfo(cli->tree, &sfinfo);
1937 5 : CHECK_STATUS(status, NT_STATUS_OK);
1938 :
1939 : /* Try and open the stream name for WRITE_DATA. Should
1940 : fail with ACCESS_DENIED. */
1941 :
1942 5 : ZERO_STRUCT(io);
1943 5 : io.generic.level = RAW_OPEN_NTCREATEX;
1944 5 : io.ntcreatex.in.root_fid.fnum = 0;
1945 5 : io.ntcreatex.in.flags = 0;
1946 5 : io.ntcreatex.in.access_mask = SEC_FILE_WRITE_DATA;
1947 5 : io.ntcreatex.in.create_options = 0;
1948 5 : io.ntcreatex.in.file_attr = 0;
1949 5 : io.ntcreatex.in.share_access = 0;
1950 5 : io.ntcreatex.in.alloc_size = 0;
1951 5 : io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
1952 5 : io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
1953 5 : io.ntcreatex.in.security_flags = 0;
1954 5 : io.ntcreatex.in.fname = fname_stream;
1955 :
1956 5 : status = smb_raw_open(cli->tree, tctx, &io);
1957 5 : CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED);
1958 :
1959 : /* Change the attributes on the base file back. */
1960 4 : ZERO_STRUCT(sfinfo);
1961 4 : sfinfo.generic.level = RAW_SFILEINFO_SETATTR;
1962 4 : sfinfo.generic.in.file.path = fname;
1963 4 : sfinfo.setattr.in.attrib = 0;
1964 :
1965 4 : status = smb_raw_setpathinfo(cli->tree, &sfinfo);
1966 4 : CHECK_STATUS(status, NT_STATUS_OK);
1967 :
1968 : /* Re-open the file name. */
1969 :
1970 4 : ZERO_STRUCT(io);
1971 4 : io.generic.level = RAW_OPEN_NTCREATEX;
1972 4 : io.ntcreatex.in.root_fid.fnum = 0;
1973 4 : io.ntcreatex.in.flags = 0;
1974 4 : io.ntcreatex.in.access_mask = (SEC_FILE_READ_DATA|SEC_FILE_WRITE_DATA|
1975 : SEC_STD_READ_CONTROL|SEC_STD_WRITE_DAC|
1976 : SEC_FILE_WRITE_ATTRIBUTE);
1977 4 : io.ntcreatex.in.create_options = 0;
1978 4 : io.ntcreatex.in.file_attr = 0;
1979 4 : io.ntcreatex.in.share_access = 0;
1980 4 : io.ntcreatex.in.alloc_size = 0;
1981 4 : io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
1982 4 : io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
1983 4 : io.ntcreatex.in.security_flags = 0;
1984 4 : io.ntcreatex.in.fname = fname;
1985 :
1986 4 : status = smb_raw_open(cli->tree, tctx, &io);
1987 4 : CHECK_STATUS(status, NT_STATUS_OK);
1988 :
1989 4 : fnum = io.ntcreatex.out.file.fnum;
1990 :
1991 : /* Get the existing security descriptor. */
1992 4 : ZERO_STRUCT(q);
1993 4 : q.query_secdesc.level = RAW_FILEINFO_SEC_DESC;
1994 4 : q.query_secdesc.in.file.fnum = fnum;
1995 4 : q.query_secdesc.in.secinfo_flags =
1996 : SECINFO_OWNER |
1997 : SECINFO_GROUP |
1998 : SECINFO_DACL;
1999 4 : status = smb_raw_fileinfo(cli->tree, tctx, &q);
2000 4 : CHECK_STATUS(status, NT_STATUS_OK);
2001 4 : sd = q.query_secdesc.out.sd;
2002 :
2003 : /* Now add a DENY WRITE security descriptor for Everyone. */
2004 4 : torture_comment(tctx, "add a new ACE to the DACL\n");
2005 :
2006 4 : ace.type = SEC_ACE_TYPE_ACCESS_DENIED;
2007 4 : ace.flags = 0;
2008 4 : ace.access_mask = SEC_FILE_WRITE_DATA;
2009 4 : ace.trustee = global_sid_World;
2010 :
2011 4 : status = security_descriptor_dacl_add(sd, &ace);
2012 4 : CHECK_STATUS(status, NT_STATUS_OK);
2013 :
2014 : /* security_descriptor_dacl_add adds to the *end* of
2015 : the ace array, we need it at the start. Swap.. */
2016 4 : ace = sd->dacl->aces[0];
2017 4 : sd->dacl->aces[0] = sd->dacl->aces[sd->dacl->num_aces-1];
2018 4 : sd->dacl->aces[sd->dacl->num_aces-1] = ace;
2019 :
2020 4 : ZERO_STRUCT(set);
2021 4 : set.set_secdesc.level = RAW_SFILEINFO_SEC_DESC;
2022 4 : set.set_secdesc.in.file.fnum = fnum;
2023 4 : set.set_secdesc.in.secinfo_flags = SECINFO_DACL;
2024 4 : set.set_secdesc.in.sd = sd;
2025 :
2026 4 : status = smb_raw_setfileinfo(cli->tree, &set);
2027 4 : CHECK_STATUS(status, NT_STATUS_OK);
2028 :
2029 4 : smbcli_close(cli->tree, fnum);
2030 4 : fnum = -1;
2031 :
2032 : /* Try and open the stream name for WRITE_DATA. Should
2033 : fail with ACCESS_DENIED. */
2034 :
2035 4 : ZERO_STRUCT(io);
2036 4 : io.generic.level = RAW_OPEN_NTCREATEX;
2037 4 : io.ntcreatex.in.root_fid.fnum = 0;
2038 4 : io.ntcreatex.in.flags = 0;
2039 4 : io.ntcreatex.in.access_mask = SEC_FILE_WRITE_DATA;
2040 4 : io.ntcreatex.in.create_options = 0;
2041 4 : io.ntcreatex.in.file_attr = 0;
2042 4 : io.ntcreatex.in.share_access = 0;
2043 4 : io.ntcreatex.in.alloc_size = 0;
2044 4 : io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
2045 4 : io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
2046 4 : io.ntcreatex.in.security_flags = 0;
2047 4 : io.ntcreatex.in.fname = fname_stream;
2048 :
2049 4 : status = smb_raw_open(cli->tree, tctx, &io);
2050 4 : CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED);
2051 :
2052 5 : done:
2053 :
2054 5 : if (fnum != -1) {
2055 0 : smbcli_close(cli->tree, fnum);
2056 : }
2057 5 : smbcli_unlink(cli->tree, fname);
2058 :
2059 5 : smbcli_deltree(cli->tree, BASEDIR);
2060 5 : return ret;
2061 : }
2062 :
2063 : /*
2064 : basic testing of streams calls
2065 : */
2066 2354 : struct torture_suite *torture_raw_streams(TALLOC_CTX *tctx)
2067 : {
2068 2354 : struct torture_suite *suite = torture_suite_create(tctx, "streams");
2069 :
2070 2354 : torture_suite_add_1smb_test(suite, "dir", test_stream_dir);
2071 2354 : torture_suite_add_1smb_test(suite, "io", test_stream_io);
2072 2354 : torture_suite_add_1smb_test(suite, "sharemodes", test_stream_sharemodes);
2073 2354 : torture_suite_add_1smb_test(suite, "delete", test_stream_delete);
2074 2354 : torture_suite_add_1smb_test(suite, "names", test_stream_names);
2075 2354 : torture_suite_add_1smb_test(suite, "names2", test_stream_names2);
2076 2354 : torture_suite_add_1smb_test(suite, "rename", test_stream_rename);
2077 2354 : torture_suite_add_1smb_test(suite, "rename2", test_stream_rename2);
2078 2354 : torture_suite_add_1smb_test(suite, "rename3", test_stream_rename3);
2079 2354 : torture_suite_add_1smb_test(suite, "createdisp",
2080 : test_stream_create_disposition);
2081 2354 : torture_suite_add_1smb_test(suite, "attr", test_stream_attributes);
2082 2354 : torture_suite_add_1smb_test(suite, "sumtab", test_stream_summary_tab);
2083 2354 : torture_suite_add_1smb_test(suite, "perms", test_stream_permissions);
2084 :
2085 : #if 0
2086 : torture_suite_add_1smb_test(suite, "LARGESTREAMINFO",
2087 : test_stream_large_streaminfo);
2088 : #endif
2089 :
2090 2354 : return suite;
2091 : }
|