2 Unix SMB/CIFS implementation.
4 SMB2 setinfo individual test suite
6 Copyright (C) Andrew Tridgell 2005
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.
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.
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/>.
23 #include "system/time.h"
24 #include "libcli/smb2/smb2.h"
25 #include "libcli/smb2/smb2_calls.h"
27 #include "torture/torture.h"
28 #include "torture/smb2/proto.h"
30 #include "libcli/security/security.h"
31 #include "librpc/gen_ndr/ndr_security.h"
33 static bool find_returned_ea(union smb_fileinfo
*finfo2
,
38 unsigned int num_eas
= finfo2
->all_eas
.out
.num_eas
;
39 struct ea_struct
*eas
= finfo2
->all_eas
.out
.eas
;
41 for (i
= 0; i
< num_eas
; i
++) {
42 if (eas
[i
].name
.s
== NULL
) {
45 /* Windows capitalizes returned EA names. */
46 if (strcasecmp_m(eas
[i
].name
.s
, eaname
)) {
49 if (eavalue
== NULL
&& eas
[i
].value
.length
== 0) {
50 /* Null value, found it ! */
53 if (eas
[i
].value
.length
== strlen(eavalue
) &&
54 memcmp(eas
[i
].value
.data
,
56 strlen(eavalue
)) == 0) {
65 #define FAIL_UNLESS(__cond) \
67 if (__cond) {} else { \
68 torture_result(tctx, TORTURE_FAIL, "%s) condition violated: %s\n", \
69 __location__, #__cond); \
70 ret = false; goto done; \
74 /* basic testing of all SMB2 setinfo calls
75 for each call we test that it succeeds, and where possible test
76 for consistency between the calls.
78 bool torture_smb2_setinfo(struct torture_context
*tctx
)
80 struct smb2_tree
*tree
;
82 struct smb2_handle handle
;
84 union smb_fileinfo finfo2
;
85 union smb_setfileinfo sfinfo
;
86 struct security_ace ace
;
87 struct security_descriptor
*sd
;
88 struct dom_sid
*test_sid
;
89 NTSTATUS status
, status2
=NT_STATUS_OK
;
90 const char *call_name
;
91 time_t basetime
= (time(NULL
) - 86400) & ~1;
92 int n
= time(NULL
) % 100;
97 fname
= talloc_asprintf(tctx
, BASEDIR
"fnum_test_%d.txt", n
);
99 if (!torture_smb2_connection(tctx
, &tree
)) {
103 #define RECREATE_FILE(fname) do { \
104 smb2_util_close(tree, handle); \
105 status = smb2_create_complex_file(tree, fname, &handle); \
106 if (!NT_STATUS_IS_OK(status)) { \
107 torture_result(tctx, TORTURE_FAIL, "(%s) ERROR: open of %s failed (%s)\n", \
108 __location__, fname, nt_errstr(status)); \
113 #define RECREATE_BOTH do { \
114 RECREATE_FILE(fname); \
119 #define CHECK_CALL(call, rightstatus) do { \
121 sfinfo.generic.level = RAW_SFILEINFO_ ## call; \
122 sfinfo.generic.in.file.handle = handle; \
123 status = smb2_setinfo_file(tree, &sfinfo); \
124 if (!NT_STATUS_EQUAL(status, rightstatus)) { \
125 torture_result(tctx, TORTURE_FAIL, "(%s) %s - %s (should be %s)\n", __location__, #call, \
126 nt_errstr(status), nt_errstr(rightstatus)); \
132 #define CHECK1(call) \
133 do { if (NT_STATUS_IS_OK(status)) { \
134 finfo2.generic.level = RAW_FILEINFO_ ## call; \
135 finfo2.generic.in.file.handle = handle; \
136 status2 = smb2_getinfo_file(tree, tctx, &finfo2); \
137 if (!NT_STATUS_IS_OK(status2)) { \
138 torture_result(tctx, TORTURE_FAIL, "(%s) %s - %s\n", __location__, #call, nt_errstr(status2)); \
144 #define CHECK_VALUE(call, stype, field, value) do { \
146 if (NT_STATUS_IS_OK(status) && NT_STATUS_IS_OK(status2) && finfo2.stype.out.field != value) { \
147 torture_result(tctx, TORTURE_FAIL, "(%s) %s - %s/%s should be 0x%x - 0x%x\n", __location__, \
148 call_name, #stype, #field, \
149 (unsigned int)value, (unsigned int)finfo2.stype.out.field); \
150 torture_smb2_all_info(tree, handle); \
155 #define CHECK_TIME(call, stype, field, value) do { \
157 if (NT_STATUS_IS_OK(status) && NT_STATUS_IS_OK(status2) && nt_time_to_unix(finfo2.stype.out.field) != value) { \
158 torture_result(tctx, TORTURE_FAIL, "(%s) %s - %s/%s should be 0x%x - 0x%x\n", __location__, \
159 call_name, #stype, #field, \
160 (unsigned int)value, \
161 (unsigned int)nt_time_to_unix(finfo2.stype.out.field)); \
162 torture_warning(tctx, "\t%s", timestring(tctx, value)); \
163 torture_warning(tctx, "\t%s\n", nt_time_string(tctx, finfo2.stype.out.field)); \
164 torture_smb2_all_info(tree, handle); \
169 #define CHECK_STATUS(status, correct) do { \
170 if (!NT_STATUS_EQUAL(status, correct)) { \
171 torture_result(tctx, TORTURE_FAIL, "(%s) Incorrect status %s - should be %s\n", \
172 __location__, nt_errstr(status), nt_errstr(correct)); \
177 torture_smb2_all_info(tree
, handle
);
179 torture_comment(tctx
, "Test basic_information level\n");
181 unix_to_nt_time(&sfinfo
.basic_info
.in
.create_time
, basetime
+ 100);
182 unix_to_nt_time(&sfinfo
.basic_info
.in
.access_time
, basetime
+ 200);
183 unix_to_nt_time(&sfinfo
.basic_info
.in
.write_time
, basetime
+ 300);
184 unix_to_nt_time(&sfinfo
.basic_info
.in
.change_time
, basetime
+ 400);
185 sfinfo
.basic_info
.in
.attrib
= FILE_ATTRIBUTE_READONLY
;
186 CHECK_CALL(BASIC_INFORMATION
, NT_STATUS_OK
);
187 CHECK_TIME(SMB2_ALL_INFORMATION
, all_info2
, create_time
, basetime
+ 100);
188 CHECK_TIME(SMB2_ALL_INFORMATION
, all_info2
, access_time
, basetime
+ 200);
189 CHECK_TIME(SMB2_ALL_INFORMATION
, all_info2
, write_time
, basetime
+ 300);
190 CHECK_TIME(SMB2_ALL_INFORMATION
, all_info2
, change_time
, basetime
+ 400);
191 CHECK_VALUE(SMB2_ALL_INFORMATION
, all_info2
, attrib
, FILE_ATTRIBUTE_READONLY
);
193 torture_comment(tctx
, "a zero time means don't change\n");
194 unix_to_nt_time(&sfinfo
.basic_info
.in
.create_time
, 0);
195 unix_to_nt_time(&sfinfo
.basic_info
.in
.access_time
, 0);
196 unix_to_nt_time(&sfinfo
.basic_info
.in
.write_time
, 0);
197 unix_to_nt_time(&sfinfo
.basic_info
.in
.change_time
, 0);
198 sfinfo
.basic_info
.in
.attrib
= FILE_ATTRIBUTE_NORMAL
;
199 CHECK_CALL(BASIC_INFORMATION
, NT_STATUS_OK
);
200 CHECK_TIME(SMB2_ALL_INFORMATION
, all_info2
, create_time
, basetime
+ 100);
201 CHECK_TIME(SMB2_ALL_INFORMATION
, all_info2
, access_time
, basetime
+ 200);
202 CHECK_TIME(SMB2_ALL_INFORMATION
, all_info2
, write_time
, basetime
+ 300);
203 CHECK_TIME(SMB2_ALL_INFORMATION
, all_info2
, change_time
, basetime
+ 400);
204 CHECK_VALUE(SMB2_ALL_INFORMATION
, all_info2
, attrib
, FILE_ATTRIBUTE_NORMAL
);
206 torture_comment(tctx
, "change the attribute\n");
207 sfinfo
.basic_info
.in
.attrib
= FILE_ATTRIBUTE_HIDDEN
;
208 CHECK_CALL(BASIC_INFORMATION
, NT_STATUS_OK
);
209 CHECK_VALUE(SMB2_ALL_INFORMATION
, all_info2
, attrib
, FILE_ATTRIBUTE_HIDDEN
);
211 torture_comment(tctx
, "zero attrib means don't change\n");
212 sfinfo
.basic_info
.in
.attrib
= 0;
213 CHECK_CALL(BASIC_INFORMATION
, NT_STATUS_OK
);
214 CHECK_VALUE(SMB2_ALL_INFORMATION
, all_info2
, attrib
, FILE_ATTRIBUTE_HIDDEN
);
216 torture_comment(tctx
, "can't change a file to a directory\n");
217 sfinfo
.basic_info
.in
.attrib
= FILE_ATTRIBUTE_DIRECTORY
;
218 CHECK_CALL(BASIC_INFORMATION
, NT_STATUS_INVALID_PARAMETER
);
220 torture_comment(tctx
, "restore attribute\n");
221 sfinfo
.basic_info
.in
.attrib
= FILE_ATTRIBUTE_NORMAL
;
222 CHECK_CALL(BASIC_INFORMATION
, NT_STATUS_OK
);
223 CHECK_VALUE(SMB2_ALL_INFORMATION
, all_info2
, attrib
, FILE_ATTRIBUTE_NORMAL
);
225 torture_comment(tctx
, "Test disposition_information level\n");
226 sfinfo
.disposition_info
.in
.delete_on_close
= 1;
227 CHECK_CALL(DISPOSITION_INFORMATION
, NT_STATUS_OK
);
228 CHECK_VALUE(SMB2_ALL_INFORMATION
, all_info2
, delete_pending
, 1);
229 CHECK_VALUE(SMB2_ALL_INFORMATION
, all_info2
, nlink
, 0);
231 sfinfo
.disposition_info
.in
.delete_on_close
= 0;
232 CHECK_CALL(DISPOSITION_INFORMATION
, NT_STATUS_OK
);
233 CHECK_VALUE(SMB2_ALL_INFORMATION
, all_info2
, delete_pending
, 0);
234 CHECK_VALUE(SMB2_ALL_INFORMATION
, all_info2
, nlink
, 1);
236 torture_comment(tctx
, "Test allocation_information level\n");
237 sfinfo
.allocation_info
.in
.alloc_size
= 0;
238 CHECK_CALL(ALLOCATION_INFORMATION
, NT_STATUS_OK
);
239 CHECK_VALUE(SMB2_ALL_INFORMATION
, all_info2
, size
, 0);
240 CHECK_VALUE(SMB2_ALL_INFORMATION
, all_info2
, alloc_size
, 0);
242 sfinfo
.allocation_info
.in
.alloc_size
= 4096;
243 CHECK_CALL(ALLOCATION_INFORMATION
, NT_STATUS_OK
);
244 CHECK_VALUE(SMB2_ALL_INFORMATION
, all_info2
, alloc_size
, 4096);
245 CHECK_VALUE(SMB2_ALL_INFORMATION
, all_info2
, size
, 0);
247 torture_comment(tctx
, "Test end_of_file_info level\n");
248 sfinfo
.end_of_file_info
.in
.size
= 37;
249 CHECK_CALL(END_OF_FILE_INFORMATION
, NT_STATUS_OK
);
250 CHECK_VALUE(SMB2_ALL_INFORMATION
, all_info2
, size
, 37);
252 sfinfo
.end_of_file_info
.in
.size
= 7;
253 CHECK_CALL(END_OF_FILE_INFORMATION
, NT_STATUS_OK
);
254 CHECK_VALUE(SMB2_ALL_INFORMATION
, all_info2
, size
, 7);
256 torture_comment(tctx
, "Test position_information level\n");
257 sfinfo
.position_information
.in
.position
= 123456;
258 CHECK_CALL(POSITION_INFORMATION
, NT_STATUS_OK
);
259 CHECK_VALUE(POSITION_INFORMATION
, position_information
, position
, 123456);
260 CHECK_VALUE(SMB2_ALL_INFORMATION
, all_info2
, position
, 123456);
262 torture_comment(tctx
, "Test mode_information level\n");
263 sfinfo
.mode_information
.in
.mode
= 2;
264 CHECK_CALL(MODE_INFORMATION
, NT_STATUS_OK
);
265 CHECK_VALUE(MODE_INFORMATION
, mode_information
, mode
, 2);
266 CHECK_VALUE(SMB2_ALL_INFORMATION
, all_info2
, mode
, 2);
268 sfinfo
.mode_information
.in
.mode
= 1;
269 CHECK_CALL(MODE_INFORMATION
, NT_STATUS_INVALID_PARAMETER
);
271 sfinfo
.mode_information
.in
.mode
= 0;
272 CHECK_CALL(MODE_INFORMATION
, NT_STATUS_OK
);
273 CHECK_VALUE(MODE_INFORMATION
, mode_information
, mode
, 0);
275 torture_comment(tctx
, "Test sec_desc level\n");
277 finfo2
.query_secdesc
.in
.secinfo_flags
=
282 sd
= finfo2
.query_secdesc
.out
.sd
;
284 test_sid
= dom_sid_parse_talloc(tctx
, SID_NT_AUTHENTICATED_USERS
);
286 ace
.type
= SEC_ACE_TYPE_ACCESS_ALLOWED
;
288 ace
.access_mask
= SEC_STD_ALL
;
289 ace
.trustee
= *test_sid
;
290 status
= security_descriptor_dacl_add(sd
, &ace
);
291 CHECK_STATUS(status
, NT_STATUS_OK
);
293 torture_comment(tctx
, "add a new ACE to the DACL\n");
295 sfinfo
.set_secdesc
.in
.secinfo_flags
= finfo2
.query_secdesc
.in
.secinfo_flags
;
296 sfinfo
.set_secdesc
.in
.sd
= sd
;
297 CHECK_CALL(SEC_DESC
, NT_STATUS_OK
);
298 FAIL_UNLESS(smb2_util_verify_sd(tctx
, tree
, handle
, sd
));
300 torture_comment(tctx
, "remove it again\n");
302 status
= security_descriptor_dacl_del(sd
, test_sid
);
303 CHECK_STATUS(status
, NT_STATUS_OK
);
305 sfinfo
.set_secdesc
.in
.secinfo_flags
= finfo2
.query_secdesc
.in
.secinfo_flags
;
306 sfinfo
.set_secdesc
.in
.sd
= sd
;
307 CHECK_CALL(SEC_DESC
, NT_STATUS_OK
);
308 FAIL_UNLESS(smb2_util_verify_sd(tctx
, tree
, handle
, sd
));
310 torture_comment(tctx
, "Check zero length EA's behavior\n");
313 sfinfo
.full_ea_information
.in
.eas
.num_eas
= 1;
315 ea
.name
.private_length
= 6;
317 ea
.value
= data_blob_string_const("testme");
318 sfinfo
.full_ea_information
.in
.eas
.eas
= &ea
;
319 CHECK_CALL(FULL_EA_INFORMATION
, NT_STATUS_OK
);
321 /* Does it still exist ? */
322 finfo2
.generic
.level
= RAW_FILEINFO_SMB2_ALL_EAS
;
323 finfo2
.generic
.in
.file
.handle
= handle
;
324 finfo2
.all_eas
.in
.continue_flags
= 1;
325 status2
= smb2_getinfo_file(tree
, tctx
, &finfo2
);
326 if (!NT_STATUS_IS_OK(status2
)) {
327 torture_result(tctx
, TORTURE_FAIL
, "(%s) %s - %s\n", __location__
,
328 "SMB2_ALL_EAS", nt_errstr(status2
));
333 /* Note on Windows EA name is returned capitalized. */
334 if (!find_returned_ea(&finfo2
, "NewEA", "testme")) {
335 torture_result(tctx
, TORTURE_FAIL
, "(%s) Missing EA 'NewEA'\n", __location__
);
339 /* Now zero it out (should delete it) */
340 sfinfo
.full_ea_information
.in
.eas
.num_eas
= 1;
342 ea
.name
.private_length
= 6;
344 ea
.value
= data_blob_null
;
345 sfinfo
.full_ea_information
.in
.eas
.eas
= &ea
;
346 CHECK_CALL(FULL_EA_INFORMATION
, NT_STATUS_OK
);
348 /* Does it still exist ? */
349 finfo2
.generic
.level
= RAW_FILEINFO_SMB2_ALL_EAS
;
350 finfo2
.generic
.in
.file
.handle
= handle
;
351 finfo2
.all_eas
.in
.continue_flags
= 1;
352 status2
= smb2_getinfo_file(tree
, tctx
, &finfo2
);
353 if (!NT_STATUS_IS_OK(status2
)) {
354 torture_result(tctx
, TORTURE_FAIL
, "(%s) %s - %s\n", __location__
,
355 "SMB2_ALL_EAS", nt_errstr(status2
));
360 if (find_returned_ea(&finfo2
, "NewEA", NULL
)) {
361 torture_result(tctx
, TORTURE_FAIL
, "(%s) EA 'NewEA' should be deleted\n", __location__
);
365 /* Set a zero length EA. */
366 sfinfo
.full_ea_information
.in
.eas
.num_eas
= 1;
368 ea
.name
.private_length
= 6;
369 ea
.name
.s
= "ZeroEA";
370 ea
.value
= data_blob_null
;
371 sfinfo
.full_ea_information
.in
.eas
.eas
= &ea
;
372 CHECK_CALL(FULL_EA_INFORMATION
, NT_STATUS_OK
);
374 /* Does it still exist ? */
375 finfo2
.generic
.level
= RAW_FILEINFO_SMB2_ALL_EAS
;
376 finfo2
.generic
.in
.file
.handle
= handle
;
377 finfo2
.all_eas
.in
.continue_flags
= 1;
378 status2
= smb2_getinfo_file(tree
, tctx
, &finfo2
);
379 if (!NT_STATUS_IS_OK(status2
)) {
380 torture_result(tctx
, TORTURE_FAIL
, "(%s) %s - %s\n", __location__
,
381 "SMB2_ALL_EAS", nt_errstr(status2
));
386 /* Over SMB2 ZeroEA should not exist. */
387 if (!find_returned_ea(&finfo2
, "EAONE", "VALUE1")) {
388 torture_result(tctx
, TORTURE_FAIL
, "(%s) Missing EA 'EAONE'\n", __location__
);
391 if (!find_returned_ea(&finfo2
, "SECONDEA", "ValueTwo")) {
392 torture_result(tctx
, TORTURE_FAIL
, "(%s) Missing EA 'SECONDEA'\n", __location__
);
395 if (find_returned_ea(&finfo2
, "ZeroEA", NULL
)) {
396 torture_result(tctx
, TORTURE_FAIL
, "(%s) Found null EA 'ZeroEA'\n", __location__
);
401 status
= smb2_util_close(tree
, handle
);
402 if (NT_STATUS_IS_ERR(status
)) {
403 torture_warning(tctx
, "Failed to delete %s - %s\n", fname
, nt_errstr(status
));
405 smb2_util_unlink(tree
, fname
);