]> git.ipfire.org Git - thirdparty/samba.git/commitdiff
smbtorture: add test smb2.maximum_allowed.read_only_dir
authorRalph Boehme <slow@samba.org>
Wed, 1 Apr 2026 10:28:55 +0000 (12:28 +0200)
committerStefan Metzmacher <metze@samba.org>
Wed, 29 Apr 2026 10:56:35 +0000 (10:56 +0000)
Verifies that FILE_ATTRIBUTE_READONLY is effectively ignored on directories.

Passes against Windows, fails against Samba: Samba enforces read-only access in
fsp->access_mask and "MxAC" create context response for directories with
FILE_ATTRIBUTE_READONLY. This is wrong, Windows doesn't do this.

Note that MS-FSA doesn't quite has all these details right, the correct
behaviour was taken from a modern Windows server.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=16030

Signed-off-by: Ralph Boehme <slow@samba.org>
Reviewed-by: Stefan Metzmacher <metze@samba.org>
selftest/knownfail.d/smb2.maximum_allowed
source4/torture/smb2/max_allowed.c

index 7e29a84ab4fd933cbce0600460c96229beaabe81..fdaa58a67cc7955ab0cf79da738e5e855cacfe7a 100644 (file)
@@ -1 +1,2 @@
 ^samba3.smb2.maximum_allowed.read_only_file\(.*\)
+^samba3.smb2.maximum_allowed.read_only_dir\(.*\)
index 4f1a1ca3a4028571345d0c699a99b785f1c6e3d5..1f42765ed6004f11c3591a9855c1d2b4e28723b1 100644 (file)
@@ -319,11 +319,143 @@ done:
        return ret;
 }
 
+static bool torture_smb2_read_only_dir(struct torture_context *tctx,
+                                      struct smb2_tree *tree)
+{
+       struct smb2_create c;
+       struct smb2_handle h = {};
+       union smb_fileinfo getinfo;
+       bool ret = true;
+       NTSTATUS status;
+
+       smb2_deltree(tree, MAXIMUM_ALLOWED_FILE);
+
+       c = (struct smb2_create) {
+               .in.desired_access = SEC_RIGHTS_FILE_ALL,
+               .in.file_attributes = FILE_ATTRIBUTE_READONLY|NTCREATEX_OPTIONS_DIRECTORY,
+               .in.create_disposition = NTCREATEX_DISP_CREATE,
+               .in.create_options = NTCREATEX_OPTIONS_DIRECTORY,
+               .in.fname = MAXIMUM_ALLOWED_FILE,
+       };
+
+       status = smb2_create(tree, tctx, &c);
+       torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+                                       "smb2_create failed\n");
+       h = c.out.file.handle;
+       smb2_util_close(tree, h);
+       ZERO_STRUCT(h);
+
+       c = (struct smb2_create) {
+               .in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED,
+               .in.file_attributes = FILE_ATTRIBUTE_READONLY|NTCREATEX_OPTIONS_DIRECTORY,
+               .in.create_disposition = NTCREATEX_DISP_OPEN,
+               .in.fname = MAXIMUM_ALLOWED_FILE,
+               .in.query_maximal_access = true,
+       };
+
+       status = smb2_create(tree, tctx, &c);
+       torture_assert_ntstatus_ok_goto(
+               tctx, status, ret, done,
+               "Failed to open READ-ONLY file with SEC_FLAG_MAXIMUM_ALLOWED\n");
+       h = c.out.file.handle;
+
+       /*
+        * Verify maximum access from create context
+        */
+
+       torture_assert_u32_equal_goto(tctx,
+                                     c.out.maximal_access,
+                                     SEC_RIGHTS_FILE_ALL,
+                                     ret, done,
+                                     "Wrong maxaccess\n");
+
+       /*
+        * Verify actual access mask from infolevel
+        */
+
+       ZERO_STRUCT(getinfo);
+       getinfo.generic.level = RAW_FILEINFO_ACCESS_INFORMATION;
+       getinfo.generic.in.file.handle = h;
+
+       status = smb2_getinfo_file(tree, tree, &getinfo);
+       torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+                                       "smb2_getinfo_file\n");
+
+       torture_assert_u32_equal_goto(
+               tctx,
+               getinfo.access_information.out.access_flags,
+               SEC_RIGHTS_FILE_ALL,
+               ret, done,
+               "Bad access mask\n");
+
+       smb2_util_close(tree, h);
+       ZERO_STRUCT(h);
+
+       /*
+        * Verify we can create and delete a file in the directory
+        */
+
+       c = (struct smb2_create) {
+               .in.desired_access = SEC_RIGHTS_FILE_ALL,
+               .in.create_disposition = NTCREATEX_DISP_CREATE,
+               .in.fname = MAXIMUM_ALLOWED_FILE "\\file",
+       };
+
+       status = smb2_create(tree, tctx, &c);
+       torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+                                       "smb2_create failed\n");
+       h = c.out.file.handle;
+
+
+       status = smb2_util_close(tree, h);
+       torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+                                       "smb2_close failed\n");
+       ZERO_STRUCT(h);
+
+       status = smb2_util_unlink(tree, MAXIMUM_ALLOWED_FILE "\\file");
+       torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+                                       "smb2_util_unlink failed\n");
+
+       /*
+        * Verify we can create and delete a directory in the directory
+        */
+
+       c = (struct smb2_create) {
+               .in.desired_access = SEC_RIGHTS_FILE_ALL,
+               .in.create_disposition = NTCREATEX_DISP_CREATE,
+               .in.create_options = NTCREATEX_OPTIONS_DIRECTORY,
+               .in.fname = MAXIMUM_ALLOWED_FILE "\\dir",
+       };
+
+       status = smb2_create(tree, tctx, &c);
+       torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+                                       "smb2_create failed\n");
+       h = c.out.file.handle;
+
+       status = smb2_util_close(tree, h);
+       torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+                                       "smb2_close failed\n");
+       ZERO_STRUCT(h);
+
+       status = smb2_util_rmdir(tree, MAXIMUM_ALLOWED_FILE "\\dir");
+       torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+                                       "smb2_util_unlink failed\n");
+
+
+done:
+       if (!smb2_util_handle_empty(h)) {
+               smb2_util_close(tree, h);
+       }
+       smb2_deltree(tree, MAXIMUM_ALLOWED_FILE);
+       return ret;
+}
+
 struct torture_suite *torture_smb2_max_allowed(TALLOC_CTX *ctx)
 {
        struct torture_suite *suite = torture_suite_create(ctx, "maximum_allowed");
 
        torture_suite_add_1smb2_test(suite, "maximum_allowed", torture_smb2_maximum_allowed);
        torture_suite_add_1smb2_test(suite, "read_only_file", torture_smb2_read_only_file);
+       torture_suite_add_1smb2_test(suite, "read_only_dir", torture_smb2_read_only_dir);
        return suite;
 }