fs/smb/client: implement chmod() for SMB3 POSIX Extensions
authorRalph Boehme <slow@samba.org>
Thu, 14 Nov 2024 10:05:13 +0000 (11:05 +0100)
committerSteve French <stfrench@microsoft.com>
Thu, 21 Nov 2024 16:43:01 +0000 (10:43 -0600)
The NT ACL format for an SMB3 POSIX Extensions chmod() is a single ACE with the
magic S-1-5-88-3-mode SID:

  NT Security Descriptor
      Revision: 1
      Type: 0x8004, Self Relative, DACL Present
      Offset to owner SID: 56
      Offset to group SID: 124
      Offset to SACL: 0
      Offset to DACL: 20
      Owner: S-1-5-21-3177838999-3893657415-1037673384-1000
      Group: S-1-22-2-1000
      NT User (DACL) ACL
          Revision: NT4 (2)
          Size: 36
          Num ACEs: 1
          NT ACE: S-1-5-88-3-438, flags 0x00, Access Allowed, mask 0x00000000
              Type: Access Allowed
              NT ACE Flags: 0x00
              Size: 28
              Access required: 0x00000000
              SID: S-1-5-88-3-438

Owner and Group should be NULL, but the server is not required to fail the
request if they are present.

Signed-off-by: Ralph Boehme <slow@samba.org>
Cc: stable@vger.kernel.org
Signed-off-by: Steve French <stfrench@microsoft.com>
fs/smb/client/cifsacl.c
fs/smb/client/cifsproto.h
fs/smb/client/inode.c
fs/smb/client/smb2pdu.c

index 1d294d53f662479c0323d5f5a645478c6f590062..c68ad526a4de1b82b1e8fef6505bcc13cd2beacf 100644 (file)
@@ -885,12 +885,17 @@ unsigned int setup_authusers_ACE(struct smb_ace *pntace)
  * Fill in the special SID based on the mode. See
  * https://technet.microsoft.com/en-us/library/hh509017(v=ws.10).aspx
  */
-unsigned int setup_special_mode_ACE(struct smb_ace *pntace, __u64 nmode)
+unsigned int setup_special_mode_ACE(struct smb_ace *pntace,
+                                   bool posix,
+                                   __u64 nmode)
 {
        int i;
        unsigned int ace_size = 28;
 
-       pntace->type = ACCESS_DENIED_ACE_TYPE;
+       if (posix)
+               pntace->type = ACCESS_ALLOWED_ACE_TYPE;
+       else
+               pntace->type = ACCESS_DENIED_ACE_TYPE;
        pntace->flags = 0x0;
        pntace->access_req = 0;
        pntace->sid.num_subauth = 3;
@@ -933,7 +938,8 @@ static void populate_new_aces(char *nacl_base,
                struct smb_sid *pownersid,
                struct smb_sid *pgrpsid,
                __u64 *pnmode, u32 *pnum_aces, u16 *pnsize,
-               bool modefromsid)
+               bool modefromsid,
+               bool posix)
 {
        __u64 nmode;
        u32 num_aces = 0;
@@ -950,13 +956,15 @@ static void populate_new_aces(char *nacl_base,
        num_aces = *pnum_aces;
        nsize = *pnsize;
 
-       if (modefromsid) {
-               pnntace = (struct smb_ace *) (nacl_base + nsize);
-               nsize += setup_special_mode_ACE(pnntace, nmode);
-               num_aces++;
+       if (modefromsid || posix) {
                pnntace = (struct smb_ace *) (nacl_base + nsize);
-               nsize += setup_authusers_ACE(pnntace);
+               nsize += setup_special_mode_ACE(pnntace, posix, nmode);
                num_aces++;
+               if (modefromsid) {
+                       pnntace = (struct smb_ace *) (nacl_base + nsize);
+                       nsize += setup_authusers_ACE(pnntace);
+                       num_aces++;
+               }
                goto set_size;
        }
 
@@ -1076,7 +1084,7 @@ static __u16 replace_sids_and_copy_aces(struct smb_acl *pdacl, struct smb_acl *p
 
 static int set_chmod_dacl(struct smb_acl *pdacl, struct smb_acl *pndacl,
                struct smb_sid *pownersid,      struct smb_sid *pgrpsid,
-               __u64 *pnmode, bool mode_from_sid)
+               __u64 *pnmode, bool mode_from_sid, bool posix)
 {
        int i;
        u16 size = 0;
@@ -1094,11 +1102,11 @@ static int set_chmod_dacl(struct smb_acl *pdacl, struct smb_acl *pndacl,
        nsize = sizeof(struct smb_acl);
 
        /* If pdacl is NULL, we don't have a src. Simply populate new ACL. */
-       if (!pdacl) {
+       if (!pdacl || posix) {
                populate_new_aces(nacl_base,
                                pownersid, pgrpsid,
                                pnmode, &num_aces, &nsize,
-                               mode_from_sid);
+                               mode_from_sid, posix);
                goto finalize_dacl;
        }
 
@@ -1115,7 +1123,7 @@ static int set_chmod_dacl(struct smb_acl *pdacl, struct smb_acl *pndacl,
                        populate_new_aces(nacl_base,
                                        pownersid, pgrpsid,
                                        pnmode, &num_aces, &nsize,
-                                       mode_from_sid);
+                                       mode_from_sid, posix);
 
                        new_aces_set = true;
                }
@@ -1144,7 +1152,7 @@ next_ace:
                populate_new_aces(nacl_base,
                                pownersid, pgrpsid,
                                pnmode, &num_aces, &nsize,
-                               mode_from_sid);
+                               mode_from_sid, posix);
 
                new_aces_set = true;
        }
@@ -1251,7 +1259,7 @@ static int parse_sec_desc(struct cifs_sb_info *cifs_sb,
 /* Convert permission bits from mode to equivalent CIFS ACL */
 static int build_sec_desc(struct smb_ntsd *pntsd, struct smb_ntsd *pnntsd,
        __u32 secdesclen, __u32 *pnsecdesclen, __u64 *pnmode, kuid_t uid, kgid_t gid,
-       bool mode_from_sid, bool id_from_sid, int *aclflag)
+       bool mode_from_sid, bool id_from_sid, bool posix, int *aclflag)
 {
        int rc = 0;
        __u32 dacloffset;
@@ -1288,7 +1296,7 @@ static int build_sec_desc(struct smb_ntsd *pntsd, struct smb_ntsd *pnntsd,
                ndacl_ptr->num_aces = cpu_to_le32(0);
 
                rc = set_chmod_dacl(dacl_ptr, ndacl_ptr, owner_sid_ptr, group_sid_ptr,
-                                   pnmode, mode_from_sid);
+                                   pnmode, mode_from_sid, posix);
 
                sidsoffset = ndacloffset + le16_to_cpu(ndacl_ptr->size);
                /* copy the non-dacl portion of secdesc */
@@ -1587,6 +1595,7 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 *pnmode,
        struct tcon_link *tlink = cifs_sb_tlink(cifs_sb);
        struct smb_version_operations *ops;
        bool mode_from_sid, id_from_sid;
+       bool posix = tlink_tcon(tlink)->posix_extensions;
        const u32 info = 0;
 
        if (IS_ERR(tlink))
@@ -1622,12 +1631,13 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 *pnmode,
                id_from_sid = false;
 
        /* Potentially, five new ACEs can be added to the ACL for U,G,O mapping */
-       nsecdesclen = secdesclen;
        if (pnmode && *pnmode != NO_CHANGE_64) { /* chmod */
-               if (mode_from_sid)
-                       nsecdesclen += 2 * sizeof(struct smb_ace);
+               if (posix)
+                       nsecdesclen = 1 * sizeof(struct smb_ace);
+               else if (mode_from_sid)
+                       nsecdesclen = secdesclen + (2 * sizeof(struct smb_ace));
                else /* cifsacl */
-                       nsecdesclen += 5 * sizeof(struct smb_ace);
+                       nsecdesclen = secdesclen + (5 * sizeof(struct smb_ace));
        } else { /* chown */
                /* When ownership changes, changes new owner sid length could be different */
                nsecdesclen = sizeof(struct smb_ntsd) + (sizeof(struct smb_sid) * 2);
@@ -1657,7 +1667,7 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 *pnmode,
        }
 
        rc = build_sec_desc(pntsd, pnntsd, secdesclen, &nsecdesclen, pnmode, uid, gid,
-                           mode_from_sid, id_from_sid, &aclflag);
+                           mode_from_sid, id_from_sid, posix, &aclflag);
 
        cifs_dbg(NOISY, "build_sec_desc rc: %d\n", rc);
 
index 8235b5a0aa2b55872975e818df4e030459fbd406..075985bfb13a8c5296c562b9d596b43a9e4edceb 100644 (file)
@@ -244,7 +244,9 @@ extern int cifs_set_acl(struct mnt_idmap *idmap,
 extern int set_cifs_acl(struct smb_ntsd *pntsd, __u32 len, struct inode *ino,
                                const char *path, int flag);
 extern unsigned int setup_authusers_ACE(struct smb_ace *pace);
-extern unsigned int setup_special_mode_ACE(struct smb_ace *pace, __u64 nmode);
+extern unsigned int setup_special_mode_ACE(struct smb_ace *pace,
+                                          bool posix,
+                                          __u64 nmode);
 extern unsigned int setup_special_user_owner_ACE(struct smb_ace *pace);
 
 extern void dequeue_mid(struct mid_q_entry *mid, bool malformed);
index eff3f57235eef3212fa9f7e510335baeecda4485..72ebd72dd02b2da6abcd9761d242ca0a5395c00a 100644 (file)
@@ -3062,6 +3062,7 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs)
        int rc = -EACCES;
        __u32 dosattr = 0;
        __u64 mode = NO_CHANGE_64;
+       bool posix = cifs_sb_master_tcon(cifs_sb)->posix_extensions;
 
        xid = get_xid();
 
@@ -3152,7 +3153,8 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs)
                mode = attrs->ia_mode;
                rc = 0;
                if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) ||
-                   (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MODE_FROM_SID)) {
+                   (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MODE_FROM_SID) ||
+                   posix) {
                        rc = id_mode_to_cifs_acl(inode, full_path, &mode,
                                                INVALID_UID, INVALID_GID);
                        if (rc) {
index 6584b5cddc280ab3a43f206511e1516cc3e67dc4..ab3a2ca66be3bf027ab28afdd6b44917eb050e23 100644 (file)
@@ -2683,7 +2683,7 @@ create_sd_buf(umode_t mode, bool set_owner, unsigned int *len)
        ptr += sizeof(struct smb3_acl);
 
        /* create one ACE to hold the mode embedded in reserved special SID */
-       acelen = setup_special_mode_ACE((struct smb_ace *)ptr, (__u64)mode);
+       acelen = setup_special_mode_ACE((struct smb_ace *)ptr, false, (__u64)mode);
        ptr += acelen;
        acl_size = acelen + sizeof(struct smb3_acl);
        ace_count = 1;