Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 :
4 : test delete-on-close in more detail
5 :
6 : Copyright (C) Richard Sharpe, 2013
7 :
8 : This program is free software; you can redistribute it and/or modify
9 : it under the terms of the GNU General Public License as published by
10 : the Free Software Foundation; either version 3 of the License, or
11 : (at your option) any later version.
12 :
13 : This program is distributed in the hope that it will be useful,
14 : but WITHOUT ANY WARRANTY; without even the implied warranty of
15 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 : GNU General Public License for more details.
17 :
18 : You should have received a copy of the GNU General Public License
19 : along with this program. If not, see <http://www.gnu.org/licenses/>.
20 : */
21 :
22 : #include "includes.h"
23 : #include "libcli/smb2/smb2.h"
24 : #include "libcli/smb2/smb2_calls.h"
25 : #include "torture/torture.h"
26 : #include "torture/util.h"
27 : #include "torture/smb2/proto.h"
28 : #include "libcli/security/security.h"
29 : #include "librpc/gen_ndr/ndr_security.h"
30 :
31 : #define DNAME "test_dir"
32 : #define FNAME DNAME "\\test_create.dat"
33 :
34 : #define CHECK_STATUS(status, correct) do { \
35 : if (!NT_STATUS_EQUAL(status, correct)) { \
36 : torture_result(tctx, TORTURE_FAIL, \
37 : "(%s) Incorrect status %s - should be %s\n", \
38 : __location__, nt_errstr(status), nt_errstr(correct)); \
39 : return false; \
40 : }} while (0)
41 :
42 49 : static bool create_dir(struct torture_context *tctx, struct smb2_tree *tree)
43 : {
44 0 : NTSTATUS status;
45 0 : struct smb2_create io;
46 0 : struct smb2_handle handle;
47 0 : union smb_fileinfo q;
48 0 : union smb_setfileinfo set;
49 0 : struct security_descriptor *sd, *sd_orig;
50 0 : const char *owner_sid;
51 49 : uint32_t perms = 0;
52 :
53 49 : torture_comment(tctx, "Creating Directory for testing: %s\n", DNAME);
54 :
55 49 : ZERO_STRUCT(io);
56 49 : io.level = RAW_OPEN_SMB2;
57 49 : io.in.create_flags = 0;
58 49 : io.in.desired_access =
59 : SEC_STD_READ_CONTROL |
60 : SEC_STD_WRITE_DAC |
61 : SEC_STD_WRITE_OWNER;
62 49 : io.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
63 49 : io.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
64 49 : io.in.share_access =
65 : NTCREATEX_SHARE_ACCESS_READ |
66 : NTCREATEX_SHARE_ACCESS_WRITE;
67 49 : io.in.alloc_size = 0;
68 49 : io.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
69 49 : io.in.impersonation_level = NTCREATEX_IMPERSONATION_ANONYMOUS;
70 49 : io.in.security_flags = 0;
71 49 : io.in.fname = DNAME;
72 49 : status = smb2_create(tree, tctx, &io);
73 49 : CHECK_STATUS(status, NT_STATUS_OK);
74 49 : handle = io.out.file.handle;
75 :
76 49 : torture_comment(tctx, "get the original sd\n");
77 49 : q.query_secdesc.level = RAW_FILEINFO_SEC_DESC;
78 49 : q.query_secdesc.in.file.handle = handle;
79 49 : q.query_secdesc.in.secinfo_flags = SECINFO_DACL | SECINFO_OWNER;
80 49 : status = smb2_getinfo_file(tree, tctx, &q);
81 49 : CHECK_STATUS(status, NT_STATUS_OK);
82 49 : sd_orig = q.query_secdesc.out.sd;
83 :
84 49 : owner_sid = dom_sid_string(tctx, sd_orig->owner_sid);
85 :
86 : /*
87 : * We create an SD that allows us to do most things but we do not
88 : * get DELETE and DELETE CHILD access!
89 : */
90 :
91 49 : perms = SEC_STD_SYNCHRONIZE | SEC_STD_WRITE_OWNER |
92 : SEC_STD_WRITE_DAC | SEC_STD_READ_CONTROL |
93 : SEC_DIR_WRITE_ATTRIBUTE | SEC_DIR_READ_ATTRIBUTE |
94 : SEC_DIR_TRAVERSE | SEC_DIR_WRITE_EA |
95 : SEC_FILE_READ_EA | SEC_FILE_APPEND_DATA |
96 : SEC_FILE_WRITE_DATA | SEC_FILE_READ_DATA;
97 :
98 49 : torture_comment(tctx, "Setting permissions on dir to 0x1e01bf\n");
99 49 : sd = security_descriptor_dacl_create(tctx,
100 : 0, owner_sid, NULL,
101 : owner_sid,
102 : SEC_ACE_TYPE_ACCESS_ALLOWED,
103 : perms,
104 : SEC_ACE_FLAG_OBJECT_INHERIT,
105 : NULL);
106 :
107 49 : set.set_secdesc.level = RAW_SFILEINFO_SEC_DESC;
108 49 : set.set_secdesc.in.file.handle = handle;
109 49 : set.set_secdesc.in.secinfo_flags = SECINFO_DACL | SECINFO_OWNER;
110 49 : set.set_secdesc.in.sd = sd;
111 :
112 49 : status = smb2_setinfo_file(tree, &set);
113 49 : CHECK_STATUS(status, NT_STATUS_OK);
114 :
115 49 : status = smb2_util_close(tree, handle);
116 :
117 49 : return true;
118 : }
119 :
120 49 : static bool set_dir_delete_perms(struct torture_context *tctx, struct smb2_tree *tree)
121 : {
122 0 : NTSTATUS status;
123 0 : struct smb2_create io;
124 0 : struct smb2_handle handle;
125 0 : union smb_fileinfo q;
126 0 : union smb_setfileinfo set;
127 0 : struct security_descriptor *sd, *sd_orig;
128 0 : const char *owner_sid;
129 49 : uint32_t perms = 0;
130 :
131 49 : torture_comment(tctx, "Opening Directory for setting new SD: %s\n", DNAME);
132 :
133 49 : ZERO_STRUCT(io);
134 49 : io.level = RAW_OPEN_SMB2;
135 49 : io.in.create_flags = 0;
136 49 : io.in.desired_access =
137 : SEC_STD_READ_CONTROL |
138 : SEC_STD_WRITE_DAC |
139 : SEC_STD_WRITE_OWNER;
140 49 : io.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
141 49 : io.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
142 49 : io.in.share_access =
143 : NTCREATEX_SHARE_ACCESS_READ |
144 : NTCREATEX_SHARE_ACCESS_WRITE;
145 49 : io.in.alloc_size = 0;
146 49 : io.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
147 49 : io.in.impersonation_level = NTCREATEX_IMPERSONATION_ANONYMOUS;
148 49 : io.in.security_flags = 0;
149 49 : io.in.fname = DNAME;
150 49 : status = smb2_create(tree, tctx, &io);
151 49 : CHECK_STATUS(status, NT_STATUS_OK);
152 49 : handle = io.out.file.handle;
153 :
154 49 : torture_comment(tctx, "get the original sd\n");
155 49 : q.query_secdesc.level = RAW_FILEINFO_SEC_DESC;
156 49 : q.query_secdesc.in.file.handle = handle;
157 49 : q.query_secdesc.in.secinfo_flags = SECINFO_DACL | SECINFO_OWNER;
158 49 : status = smb2_getinfo_file(tree, tctx, &q);
159 49 : CHECK_STATUS(status, NT_STATUS_OK);
160 49 : sd_orig = q.query_secdesc.out.sd;
161 :
162 49 : owner_sid = dom_sid_string(tctx, sd_orig->owner_sid);
163 :
164 : /*
165 : * We create an SD that allows us to do most things including
166 : * get DELETE and DELETE CHILD access!
167 : */
168 :
169 49 : perms = SEC_STD_SYNCHRONIZE | SEC_STD_WRITE_OWNER |
170 : SEC_STD_WRITE_DAC | SEC_STD_READ_CONTROL |
171 : SEC_DIR_WRITE_ATTRIBUTE | SEC_DIR_READ_ATTRIBUTE |
172 : SEC_DIR_TRAVERSE | SEC_DIR_WRITE_EA |
173 : SEC_FILE_READ_EA | SEC_FILE_APPEND_DATA |
174 : SEC_DIR_DELETE_CHILD | SEC_STD_DELETE |
175 : SEC_FILE_WRITE_DATA | SEC_FILE_READ_DATA;
176 :
177 49 : torture_comment(tctx, "Setting permissions on dir to 0x%0x\n", perms);
178 49 : sd = security_descriptor_dacl_create(tctx,
179 : 0, owner_sid, NULL,
180 : owner_sid,
181 : SEC_ACE_TYPE_ACCESS_ALLOWED,
182 : perms,
183 : 0,
184 : NULL);
185 :
186 49 : set.set_secdesc.level = RAW_SFILEINFO_SEC_DESC;
187 49 : set.set_secdesc.in.file.handle = handle;
188 49 : set.set_secdesc.in.secinfo_flags = SECINFO_DACL | SECINFO_OWNER;
189 49 : set.set_secdesc.in.sd = sd;
190 :
191 49 : status = smb2_setinfo_file(tree, &set);
192 49 : CHECK_STATUS(status, NT_STATUS_OK);
193 :
194 49 : status = smb2_util_close(tree, handle);
195 :
196 49 : return true;
197 : }
198 :
199 7 : static bool test_doc_overwrite_if(struct torture_context *tctx, struct smb2_tree *tree)
200 : {
201 0 : struct smb2_create io;
202 0 : NTSTATUS status;
203 7 : uint32_t perms = 0;
204 :
205 : /* File should not exist for this first test, so make sure */
206 7 : set_dir_delete_perms(tctx, tree);
207 :
208 7 : smb2_deltree(tree, DNAME);
209 :
210 7 : create_dir(tctx, tree);
211 :
212 7 : torture_comment(tctx, "Create file with DeleteOnClose on non-existent file (OVERWRITE_IF)\n");
213 7 : torture_comment(tctx, "We expect NT_STATUS_OK\n");
214 :
215 7 : perms = SEC_STD_SYNCHRONIZE | SEC_STD_READ_CONTROL | SEC_STD_DELETE |
216 : SEC_DIR_WRITE_ATTRIBUTE | SEC_DIR_READ_ATTRIBUTE |
217 : SEC_DIR_WRITE_EA | SEC_FILE_APPEND_DATA |
218 : SEC_FILE_WRITE_DATA;
219 :
220 7 : ZERO_STRUCT(io);
221 7 : io.in.desired_access = perms;
222 7 : io.in.file_attributes = 0;
223 7 : io.in.create_disposition = NTCREATEX_DISP_OVERWRITE_IF;
224 7 : io.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE;
225 7 : io.in.create_options = NTCREATEX_OPTIONS_DELETE_ON_CLOSE |
226 : NTCREATEX_OPTIONS_NON_DIRECTORY_FILE;
227 7 : io.in.fname = FNAME;
228 :
229 7 : status = smb2_create(tree, tctx, &io);
230 7 : CHECK_STATUS(status, NT_STATUS_OK);
231 :
232 7 : status = smb2_util_close(tree, io.out.file.handle);
233 :
234 : /* Check it was deleted */
235 7 : ZERO_STRUCT(io);
236 7 : io.in.desired_access = perms;
237 7 : io.in.file_attributes = 0;
238 7 : io.in.create_disposition = NTCREATEX_DISP_OPEN;
239 7 : io.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE;
240 7 : io.in.create_options = 0;
241 7 : io.in.fname = FNAME;
242 :
243 7 : torture_comment(tctx, "Testing if the file was deleted when closed\n");
244 7 : torture_comment(tctx, "We expect NT_STATUS_OBJECT_NAME_NOT_FOUND\n");
245 :
246 7 : status = smb2_create(tree, tctx, &io);
247 7 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
248 :
249 7 : return true;
250 : }
251 :
252 7 : static bool test_doc_overwrite_if_exist(struct torture_context *tctx, struct smb2_tree *tree)
253 : {
254 0 : struct smb2_create io;
255 0 : NTSTATUS status;
256 7 : uint32_t perms = 0;
257 :
258 : /* File should not exist for this first test, so make sure */
259 : /* And set the SEC Descriptor appropriately */
260 7 : set_dir_delete_perms(tctx, tree);
261 :
262 7 : smb2_deltree(tree, DNAME);
263 :
264 7 : create_dir(tctx, tree);
265 :
266 7 : torture_comment(tctx, "Create file with DeleteOnClose on existing file (OVERWRITE_IF)\n");
267 7 : torture_comment(tctx, "We expect NT_STATUS_ACCESS_DENIED\n");
268 :
269 7 : perms = SEC_STD_SYNCHRONIZE | SEC_STD_READ_CONTROL | SEC_STD_DELETE |
270 : SEC_DIR_WRITE_ATTRIBUTE | SEC_DIR_READ_ATTRIBUTE |
271 : SEC_DIR_WRITE_EA | SEC_FILE_APPEND_DATA |
272 : SEC_FILE_WRITE_DATA;
273 :
274 : /* First, create this file ... */
275 7 : ZERO_STRUCT(io);
276 7 : io.in.desired_access = perms;
277 7 : io.in.file_attributes = 0;
278 7 : io.in.create_disposition = NTCREATEX_DISP_OVERWRITE_IF;
279 7 : io.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE;
280 7 : io.in.create_options = 0x0;
281 7 : io.in.fname = FNAME;
282 :
283 7 : status = smb2_create(tree, tctx, &io);
284 7 : CHECK_STATUS(status, NT_STATUS_OK);
285 :
286 7 : status = smb2_util_close(tree, io.out.file.handle);
287 :
288 : /* Next, try to open it for Delete On Close */
289 7 : ZERO_STRUCT(io);
290 7 : io.in.desired_access = perms;
291 7 : io.in.file_attributes = 0;
292 7 : io.in.create_disposition = NTCREATEX_DISP_OVERWRITE_IF;
293 7 : io.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE;
294 7 : io.in.create_options = NTCREATEX_OPTIONS_DELETE_ON_CLOSE |
295 : NTCREATEX_OPTIONS_NON_DIRECTORY_FILE;
296 7 : io.in.fname = FNAME;
297 :
298 7 : status = smb2_create(tree, tctx, &io);
299 7 : CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED);
300 :
301 7 : status = smb2_util_close(tree, io.out.file.handle);
302 :
303 7 : return true;
304 : }
305 :
306 7 : static bool test_doc_create(struct torture_context *tctx, struct smb2_tree *tree)
307 : {
308 0 : struct smb2_create io;
309 0 : NTSTATUS status;
310 7 : uint32_t perms = 0;
311 :
312 : /* File should not exist for this first test, so make sure */
313 7 : set_dir_delete_perms(tctx, tree);
314 :
315 7 : smb2_deltree(tree, DNAME);
316 :
317 7 : create_dir(tctx, tree);
318 :
319 7 : torture_comment(tctx, "Create file with DeleteOnClose on non-existent file (CREATE) \n");
320 7 : torture_comment(tctx, "We expect NT_STATUS_OK\n");
321 :
322 7 : perms = SEC_STD_SYNCHRONIZE | SEC_STD_READ_CONTROL | SEC_STD_DELETE |
323 : SEC_DIR_WRITE_ATTRIBUTE | SEC_DIR_READ_ATTRIBUTE |
324 : SEC_DIR_WRITE_EA | SEC_FILE_APPEND_DATA |
325 : SEC_FILE_WRITE_DATA;
326 :
327 7 : ZERO_STRUCT(io);
328 7 : io.in.desired_access = perms;
329 7 : io.in.file_attributes = 0;
330 7 : io.in.create_disposition = NTCREATEX_DISP_CREATE;
331 7 : io.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE;
332 7 : io.in.create_options = NTCREATEX_OPTIONS_DELETE_ON_CLOSE |
333 : NTCREATEX_OPTIONS_NON_DIRECTORY_FILE;
334 7 : io.in.fname = FNAME;
335 :
336 7 : status = smb2_create(tree, tctx, &io);
337 7 : CHECK_STATUS(status, NT_STATUS_OK);
338 :
339 7 : status = smb2_util_close(tree, io.out.file.handle);
340 :
341 : /* Check it was deleted */
342 7 : ZERO_STRUCT(io);
343 7 : io.in.desired_access = perms;
344 7 : io.in.file_attributes = 0;
345 7 : io.in.create_disposition = NTCREATEX_DISP_OPEN;
346 7 : io.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE;
347 7 : io.in.create_options = 0;
348 7 : io.in.fname = FNAME;
349 :
350 7 : torture_comment(tctx, "Testing if the file was deleted when closed\n");
351 7 : torture_comment(tctx, "We expect NT_STATUS_OBJECT_NAME_NOT_FOUND\n");
352 :
353 7 : status = smb2_create(tree, tctx, &io);
354 7 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
355 :
356 7 : return true;
357 : }
358 :
359 7 : static bool test_doc_create_exist(struct torture_context *tctx, struct smb2_tree *tree)
360 : {
361 0 : struct smb2_create io;
362 0 : NTSTATUS status;
363 7 : uint32_t perms = 0;
364 :
365 : /* File should not exist for this first test, so make sure */
366 7 : set_dir_delete_perms(tctx, tree);
367 :
368 7 : smb2_deltree(tree, DNAME);
369 :
370 7 : create_dir(tctx, tree);
371 :
372 7 : torture_comment(tctx, "Create file with DeleteOnClose on non-existent file (CREATE) \n");
373 7 : torture_comment(tctx, "We expect NT_STATUS_OBJECT_NAME_COLLISION\n");
374 :
375 7 : perms = SEC_STD_SYNCHRONIZE | SEC_STD_READ_CONTROL | SEC_STD_DELETE |
376 : SEC_DIR_WRITE_ATTRIBUTE | SEC_DIR_READ_ATTRIBUTE |
377 : SEC_DIR_WRITE_EA | SEC_FILE_APPEND_DATA |
378 : SEC_FILE_WRITE_DATA;
379 :
380 : /* First, create the file */
381 7 : ZERO_STRUCT(io);
382 7 : io.in.desired_access = perms;
383 7 : io.in.file_attributes = 0;
384 7 : io.in.create_disposition = NTCREATEX_DISP_CREATE;
385 7 : io.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE;
386 7 : io.in.create_options = 0x0;
387 7 : io.in.fname = FNAME;
388 :
389 7 : status = smb2_create(tree, tctx, &io);
390 7 : CHECK_STATUS(status, NT_STATUS_OK);
391 :
392 7 : status = smb2_util_close(tree, io.out.file.handle);
393 :
394 : /* Next, try to open it for Delete on Close */
395 7 : status = smb2_util_close(tree, io.out.file.handle);
396 7 : ZERO_STRUCT(io);
397 7 : io.in.desired_access = perms;
398 7 : io.in.file_attributes = 0;
399 7 : io.in.create_disposition = NTCREATEX_DISP_CREATE;
400 7 : io.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE;
401 7 : io.in.create_options = NTCREATEX_OPTIONS_DELETE_ON_CLOSE |
402 : NTCREATEX_OPTIONS_NON_DIRECTORY_FILE;
403 7 : io.in.fname = FNAME;
404 :
405 7 : status = smb2_create(tree, tctx, &io);
406 7 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_COLLISION);
407 :
408 7 : status = smb2_util_close(tree, io.out.file.handle);
409 :
410 7 : return true;
411 : }
412 :
413 7 : static bool test_doc_create_if(struct torture_context *tctx, struct smb2_tree *tree)
414 : {
415 0 : struct smb2_create io;
416 0 : NTSTATUS status;
417 7 : uint32_t perms = 0;
418 :
419 : /* File should not exist for this first test, so make sure */
420 7 : set_dir_delete_perms(tctx, tree);
421 :
422 7 : smb2_deltree(tree, DNAME);
423 :
424 7 : create_dir(tctx, tree);
425 :
426 7 : torture_comment(tctx, "Create file with DeleteOnClose on non-existent file (OPEN_IF)\n");
427 7 : torture_comment(tctx, "We expect NT_STATUS_OK\n");
428 :
429 7 : perms = SEC_STD_SYNCHRONIZE | SEC_STD_READ_CONTROL | SEC_STD_DELETE |
430 : SEC_DIR_WRITE_ATTRIBUTE | SEC_DIR_READ_ATTRIBUTE |
431 : SEC_DIR_WRITE_EA | SEC_FILE_APPEND_DATA |
432 : SEC_FILE_WRITE_DATA;
433 :
434 7 : ZERO_STRUCT(io);
435 7 : io.in.desired_access = perms;
436 7 : io.in.file_attributes = 0;
437 7 : io.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
438 7 : io.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE;
439 7 : io.in.create_options = NTCREATEX_OPTIONS_DELETE_ON_CLOSE |
440 : NTCREATEX_OPTIONS_NON_DIRECTORY_FILE;
441 7 : io.in.fname = FNAME;
442 :
443 7 : status = smb2_create(tree, tctx, &io);
444 7 : CHECK_STATUS(status, NT_STATUS_OK);
445 :
446 7 : status = smb2_util_close(tree, io.out.file.handle);
447 :
448 : /* Check it was deleted */
449 7 : ZERO_STRUCT(io);
450 7 : io.in.desired_access = perms;
451 7 : io.in.file_attributes = 0;
452 7 : io.in.create_disposition = NTCREATEX_DISP_OPEN;
453 7 : io.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE;
454 7 : io.in.create_options = 0;
455 7 : io.in.fname = FNAME;
456 :
457 7 : torture_comment(tctx, "Testing if the file was deleted when closed\n");
458 7 : torture_comment(tctx, "We expect NT_STATUS_OBJECT_NAME_NOT_FOUND\n");
459 :
460 7 : status = smb2_create(tree, tctx, &io);
461 7 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
462 :
463 7 : return true;
464 : }
465 :
466 7 : static bool test_doc_create_if_exist(struct torture_context *tctx, struct smb2_tree *tree)
467 : {
468 0 : struct smb2_create io;
469 0 : NTSTATUS status;
470 7 : uint32_t perms = 0;
471 :
472 : /* File should not exist for this first test, so make sure */
473 7 : set_dir_delete_perms(tctx, tree);
474 :
475 7 : smb2_deltree(tree, DNAME);
476 :
477 7 : create_dir(tctx, tree);
478 :
479 7 : torture_comment(tctx, "Create file with DeleteOnClose on existing file (OPEN_IF)\n");
480 7 : torture_comment(tctx, "We expect NT_STATUS_ACCESS_DENIED\n");
481 :
482 7 : perms = SEC_STD_SYNCHRONIZE | SEC_STD_READ_CONTROL | SEC_STD_DELETE |
483 : SEC_DIR_WRITE_ATTRIBUTE | SEC_DIR_READ_ATTRIBUTE |
484 : SEC_DIR_WRITE_EA | SEC_FILE_APPEND_DATA |
485 : SEC_FILE_WRITE_DATA;
486 :
487 : /* Create the file first */
488 7 : ZERO_STRUCT(io);
489 7 : io.in.desired_access = perms;
490 7 : io.in.file_attributes = 0;
491 7 : io.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
492 7 : io.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE;
493 7 : io.in.create_options = 0x0;
494 7 : io.in.fname = FNAME;
495 :
496 7 : status = smb2_create(tree, tctx, &io);
497 7 : CHECK_STATUS(status, NT_STATUS_OK);
498 :
499 7 : status = smb2_util_close(tree, io.out.file.handle);
500 :
501 : /* Now try to create it for delete on close */
502 7 : ZERO_STRUCT(io);
503 7 : io.in.desired_access = 0x130196;
504 7 : io.in.file_attributes = 0;
505 7 : io.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
506 7 : io.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE;
507 7 : io.in.create_options = NTCREATEX_OPTIONS_DELETE_ON_CLOSE |
508 : NTCREATEX_OPTIONS_NON_DIRECTORY_FILE;
509 7 : io.in.fname = FNAME;
510 :
511 7 : status = smb2_create(tree, tctx, &io);
512 7 : CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED);
513 :
514 7 : status = smb2_util_close(tree, io.out.file.handle);
515 :
516 7 : return true;
517 : }
518 :
519 7 : static bool test_doc_find_and_set_doc(struct torture_context *tctx, struct smb2_tree *tree)
520 : {
521 0 : struct smb2_create io;
522 0 : struct smb2_find find;
523 0 : NTSTATUS status;
524 0 : union smb_search_data *d;
525 0 : union smb_setfileinfo sfinfo;
526 0 : unsigned int count;
527 7 : uint32_t perms = 0;
528 :
529 7 : perms = SEC_STD_SYNCHRONIZE | SEC_STD_READ_CONTROL | SEC_STD_DELETE |
530 : SEC_DIR_WRITE_ATTRIBUTE | SEC_DIR_READ_ATTRIBUTE |
531 : SEC_DIR_WRITE_EA | SEC_FILE_APPEND_DATA |
532 : SEC_FILE_WRITE_DATA | SEC_DIR_LIST;
533 :
534 : /* File should not exist for this first test, so make sure */
535 7 : set_dir_delete_perms(tctx, tree);
536 :
537 7 : smb2_deltree(tree, DNAME);
538 :
539 7 : create_dir(tctx, tree);
540 :
541 7 : torture_comment(tctx, "FIND and delete directory\n");
542 7 : torture_comment(tctx, "We expect NT_STATUS_OK\n");
543 :
544 : /* open the directory first */
545 7 : ZERO_STRUCT(io);
546 7 : io.in.desired_access = perms;
547 7 : io.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
548 7 : io.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
549 7 : io.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
550 : NTCREATEX_SHARE_ACCESS_DELETE;
551 7 : io.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
552 7 : io.in.fname = DNAME;
553 :
554 7 : status = smb2_create(tree, tctx, &io);
555 7 : CHECK_STATUS(status, NT_STATUS_OK);
556 :
557 : /* list directory */
558 7 : ZERO_STRUCT(find);
559 7 : find.in.file.handle = io.out.file.handle;
560 7 : find.in.pattern = "*";
561 7 : find.in.continue_flags = SMB2_CONTINUE_FLAG_SINGLE;
562 7 : find.in.max_response_size = 0x100;
563 7 : find.in.level = SMB2_FIND_BOTH_DIRECTORY_INFO;
564 :
565 : /* start enumeration on directory */
566 7 : status = smb2_find_level(tree, tree, &find, &count, &d);
567 7 : CHECK_STATUS(status, NT_STATUS_OK);
568 :
569 : /* set delete-on-close */
570 7 : ZERO_STRUCT(sfinfo);
571 7 : sfinfo.generic.level = RAW_SFILEINFO_DISPOSITION_INFORMATION;
572 7 : sfinfo.disposition_info.in.delete_on_close = 1;
573 7 : sfinfo.generic.in.file.handle = io.out.file.handle;
574 7 : status = smb2_setinfo_file(tree, &sfinfo);
575 7 : CHECK_STATUS(status, NT_STATUS_OK);
576 :
577 : /* close directory */
578 7 : status = smb2_util_close(tree, io.out.file.handle);
579 7 : CHECK_STATUS(status, NT_STATUS_OK);
580 7 : return true;
581 : }
582 :
583 7 : static bool test_doc_read_only(struct torture_context *tctx,
584 : struct smb2_tree *tree)
585 : {
586 0 : struct smb2_handle dir_handle;
587 7 : union smb_setfileinfo sfinfo = {{0}};
588 7 : struct smb2_create create = {0};
589 7 : struct smb2_close close = {0};
590 0 : NTSTATUS status, expected_status;
591 7 : bool ret = true, delete_readonly;
592 :
593 : /*
594 : * Allow testing of the Samba 'delete readonly' option.
595 : */
596 7 : delete_readonly = torture_setting_bool(tctx, "delete_readonly", false);
597 7 : expected_status = delete_readonly ?
598 1 : NT_STATUS_OK : NT_STATUS_CANNOT_DELETE;
599 :
600 7 : smb2_deltree(tree, DNAME);
601 :
602 7 : status = torture_smb2_testdir(tree, DNAME, &dir_handle);
603 7 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
604 : "CREATE directory failed\n");
605 :
606 7 : create = (struct smb2_create) {0};
607 7 : create.in.desired_access = SEC_RIGHTS_DIR_ALL;
608 7 : create.in.create_options = NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
609 : NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
610 7 : create.in.file_attributes = FILE_ATTRIBUTE_READONLY;
611 7 : create.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
612 : NTCREATEX_SHARE_ACCESS_WRITE |
613 : NTCREATEX_SHARE_ACCESS_DELETE;
614 7 : create.in.create_disposition = NTCREATEX_DISP_CREATE;
615 7 : create.in.fname = FNAME;
616 7 : status = smb2_create(tree, tctx, &create);
617 7 : torture_assert_ntstatus_equal_goto(tctx, status, expected_status, ret,
618 : done, "Unexpected status for CREATE "
619 : "of new file.\n");
620 :
621 7 : if (delete_readonly) {
622 2 : close.in.file.handle = create.out.file.handle;
623 2 : status = smb2_close(tree, &close);
624 2 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
625 : "CLOSE of READONLY file "
626 : "failed.\n");
627 : }
628 :
629 7 : torture_comment(tctx, "Creating file with READ_ONLY attribute.\n");
630 :
631 7 : create = (struct smb2_create) {0};
632 7 : create.in.desired_access = SEC_RIGHTS_DIR_ALL;
633 7 : create.in.create_options = NTCREATEX_OPTIONS_NON_DIRECTORY_FILE;
634 7 : create.in.file_attributes = FILE_ATTRIBUTE_READONLY;
635 7 : create.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
636 : NTCREATEX_SHARE_ACCESS_WRITE |
637 : NTCREATEX_SHARE_ACCESS_DELETE;
638 7 : create.in.create_disposition = NTCREATEX_DISP_CREATE;
639 7 : create.in.fname = FNAME;
640 7 : status = smb2_create(tree, tctx, &create);
641 7 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
642 : "CREATE of READONLY file failed.\n");
643 :
644 7 : close.in.file.handle = create.out.file.handle;
645 7 : status = smb2_close(tree, &close);
646 7 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
647 : "CLOSE of READONLY file failed.\n");
648 :
649 7 : torture_comment(tctx, "Testing CREATE with DELETE_ON_CLOSE on "
650 : "READ_ONLY attribute file.\n");
651 :
652 7 : create = (struct smb2_create) {0};
653 7 : create.in.desired_access = SEC_RIGHTS_FILE_READ | SEC_STD_DELETE;
654 7 : create.in.create_options = NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
655 7 : create.in.file_attributes = 0;
656 7 : create.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
657 : NTCREATEX_SHARE_ACCESS_WRITE |
658 : NTCREATEX_SHARE_ACCESS_DELETE;
659 7 : create.in.create_disposition = NTCREATEX_DISP_OPEN;
660 7 : create.in.fname = FNAME;
661 7 : status = smb2_create(tree, tctx, &create);
662 7 : torture_assert_ntstatus_equal_goto(tctx, status,
663 : expected_status, ret, done,
664 : "CREATE returned unexpected "
665 : "status.\n");
666 :
667 7 : torture_comment(tctx, "Testing setting DELETE_ON_CLOSE disposition on "
668 : " file with READONLY attribute.\n");
669 :
670 7 : create = (struct smb2_create) {0};
671 7 : create.in.desired_access = SEC_RIGHTS_FILE_READ | SEC_STD_DELETE;;
672 7 : create.in.create_options = 0;
673 7 : create.in.file_attributes = 0;
674 7 : create.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
675 : NTCREATEX_SHARE_ACCESS_WRITE |
676 : NTCREATEX_SHARE_ACCESS_DELETE;
677 7 : create.in.create_disposition = NTCREATEX_DISP_OPEN;
678 7 : create.in.fname = FNAME;
679 7 : status = smb2_create(tree, tctx, &create);
680 7 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
681 : "Opening file failed.\n");
682 :
683 7 : sfinfo.disposition_info.in.delete_on_close = 1;
684 7 : sfinfo.generic.level = RAW_SFILEINFO_DISPOSITION_INFORMATION;
685 7 : sfinfo.generic.in.file.handle = create.out.file.handle;
686 :
687 7 : status = smb2_setinfo_file(tree, &sfinfo);
688 7 : torture_assert_ntstatus_equal(tctx, status, expected_status,
689 : "Set DELETE_ON_CLOSE disposition "
690 : "returned un expected status.\n");
691 :
692 7 : status = smb2_util_close(tree, create.out.file.handle);
693 7 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
694 : "CLOSE failed\n");
695 :
696 7 : done:
697 7 : smb2_deltree(tree, DNAME);
698 7 : return ret;
699 : }
700 :
701 : /*
702 : * This is a regression test for
703 : * https://bugzilla.samba.org/show_bug.cgi?id=14427
704 : *
705 : * It's not really a delete-on-close specific test.
706 : */
707 7 : static bool test_doc_bug14427(struct torture_context *tctx, struct smb2_tree *tree1)
708 : {
709 7 : struct smb2_tree *tree2 = NULL;
710 0 : NTSTATUS status;
711 0 : char fname[256];
712 7 : bool ret = false;
713 0 : bool ok;
714 :
715 : /* Add some random component to the file name. */
716 7 : snprintf(fname, sizeof(fname), "doc_bug14427_%s.dat",
717 : generate_random_str(tctx, 8));
718 :
719 7 : ok = torture_smb2_tree_connect(tctx, tree1->session, tctx, &tree2);
720 7 : torture_assert_goto(tctx, ok, ret, done,
721 : "torture_smb2_tree_connect() failed.\n");
722 :
723 7 : status = torture_setup_simple_file(tctx, tree1, fname);
724 7 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
725 : "torture_setup_simple_file() failed on tree1.\n");
726 :
727 7 : status = smb2_util_unlink(tree2, fname);
728 7 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
729 : "smb2_util_unlink() failed on tree2.\n");
730 7 : TALLOC_FREE(tree2);
731 7 : ret = true;
732 7 : done:
733 7 : if (tree2 != NULL) {
734 0 : TALLOC_FREE(tree2);
735 0 : smb2_util_unlink(tree1, fname);
736 : }
737 :
738 7 : TALLOC_FREE(tree1);
739 7 : return ret;
740 : }
741 :
742 : /*
743 : * Extreme testing of Delete On Close and permissions
744 : */
745 2354 : struct torture_suite *torture_smb2_doc_init(TALLOC_CTX *ctx)
746 : {
747 2354 : struct torture_suite *suite = torture_suite_create(ctx, "delete-on-close-perms");
748 :
749 2354 : torture_suite_add_1smb2_test(suite, "OVERWRITE_IF", test_doc_overwrite_if);
750 2354 : torture_suite_add_1smb2_test(suite, "OVERWRITE_IF Existing", test_doc_overwrite_if_exist);
751 2354 : torture_suite_add_1smb2_test(suite, "CREATE", test_doc_create);
752 2354 : torture_suite_add_1smb2_test(suite, "CREATE Existing", test_doc_create_exist);
753 2354 : torture_suite_add_1smb2_test(suite, "CREATE_IF", test_doc_create_if);
754 2354 : torture_suite_add_1smb2_test(suite, "CREATE_IF Existing", test_doc_create_if_exist);
755 2354 : torture_suite_add_1smb2_test(suite, "FIND_and_set_DOC", test_doc_find_and_set_doc);
756 2354 : torture_suite_add_1smb2_test(suite, "READONLY", test_doc_read_only);
757 2354 : torture_suite_add_1smb2_test(suite, "BUG14427", test_doc_bug14427);
758 :
759 2354 : suite->description = talloc_strdup(suite, "SMB2-Delete-on-Close-Perms tests");
760 :
761 2354 : return suite;
762 : }
|