From 22442179a5bb8c62c978cdb686cfb3a8f6d110d8 Mon Sep 17 00:00:00 2001 From: Steve French Date: Fri, 19 Jul 2019 08:15:55 +0000 Subject: cifs: allow chmod to set mode bits using special sid When mounting with "modefromsid" set mode bits (chmod) by adding ACE with special SID (S-1-5-88-3-) to the ACL. Subsequent patch will fix setting default mode on file create and mkdir. See See e.g. https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2008-R2-and-2008/hh509017(v=ws.10) Signed-off-by: Steve French --- fs/cifs/cifsacl.c | 42 +++++++++++++++++++++++++++++++++++++----- fs/cifs/inode.c | 6 ++++-- 2 files changed, 41 insertions(+), 7 deletions(-) diff --git a/fs/cifs/cifsacl.c b/fs/cifs/cifsacl.c index 7fe4509e6c03..3e0c5ed9ca20 100644 --- a/fs/cifs/cifsacl.c +++ b/fs/cifs/cifsacl.c @@ -806,7 +806,7 @@ static void parse_dacl(struct cifs_acl *pdacl, char *end_of_acl, static int set_chmod_dacl(struct cifs_acl *pndacl, struct cifs_sid *pownersid, - struct cifs_sid *pgrpsid, __u64 nmode) + struct cifs_sid *pgrpsid, __u64 nmode, bool modefromsid) { u16 size = 0; struct cifs_acl *pnndacl; @@ -820,8 +820,33 @@ static int set_chmod_dacl(struct cifs_acl *pndacl, struct cifs_sid *pownersid, size += fill_ace_for_sid((struct cifs_ace *)((char *)pnndacl + size), &sid_everyone, nmode, S_IRWXO); + /* TBD: Move this ACE to the top of ACE list instead of bottom */ + if (modefromsid) { + struct cifs_ace *pntace = + (struct cifs_ace *)((char *)pnndacl + size); + int i; + + pntace->type = ACCESS_DENIED; + pntace->flags = 0x0; + pntace->sid.num_subauth = 3; + pntace->sid.revision = 1; + /* size = 1 + 1 + 2 + 4 + 1 + 1 + 6 + (psid->num_subauth * 4) */ + pntace->size = cpu_to_le16(28); + size += 28; + for (i = 0; i < NUM_AUTHS; i++) + pntace->sid.authority[i] = + sid_unix_NFS_mode.authority[i]; + pntace->sid.sub_auth[0] = sid_unix_NFS_mode.sub_auth[0]; + pntace->sid.sub_auth[1] = sid_unix_NFS_mode.sub_auth[1]; + pntace->sid.sub_auth[2] = cpu_to_le32(nmode & 07777); + + pndacl->num_aces = cpu_to_le32(4); + size += fill_ace_for_sid((struct cifs_ace *)((char *)pnndacl + size), + &sid_unix_NFS_mode, nmode, S_IRWXO); + } else + pndacl->num_aces = cpu_to_le32(3); + pndacl->size = cpu_to_le16(size + sizeof(struct cifs_acl)); - pndacl->num_aces = cpu_to_le32(3); return 0; } @@ -921,7 +946,8 @@ 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 cifs_ntsd *pntsd, struct cifs_ntsd *pnntsd, - __u32 secdesclen, __u64 nmode, kuid_t uid, kgid_t gid, int *aclflag) + __u32 secdesclen, __u64 nmode, kuid_t uid, kgid_t gid, + bool mode_from_sid, int *aclflag) { int rc = 0; __u32 dacloffset; @@ -946,7 +972,7 @@ static int build_sec_desc(struct cifs_ntsd *pntsd, struct cifs_ntsd *pnntsd, ndacl_ptr->num_aces = 0; rc = set_chmod_dacl(ndacl_ptr, owner_sid_ptr, group_sid_ptr, - nmode); + nmode, mode_from_sid); sidsoffset = ndacloffset + le16_to_cpu(ndacl_ptr->size); /* copy sec desc control portion & owner and group sids */ copy_sec_desc(pntsd, pnntsd, sidsoffset); @@ -1196,6 +1222,7 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 nmode, struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); struct tcon_link *tlink = cifs_sb_tlink(cifs_sb); struct smb_version_operations *ops; + bool mode_from_sid; if (IS_ERR(tlink)) return PTR_ERR(tlink); @@ -1233,8 +1260,13 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 nmode, return -ENOMEM; } + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MODE_FROM_SID) + mode_from_sid = true; + else + mode_from_sid = false; + rc = build_sec_desc(pntsd, pnntsd, secdesclen, nmode, uid, gid, - &aclflag); + mode_from_sid, &aclflag); cifs_dbg(NOISY, "build_sec_desc rc: %d\n", rc); diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index c1e620ebcf7c..26cdfbf1e164 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -2489,7 +2489,8 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs) if (attrs->ia_valid & ATTR_GID) gid = attrs->ia_gid; - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) { + if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) || + (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MODE_FROM_SID)) { if (uid_valid(uid) || gid_valid(gid)) { rc = id_mode_to_cifs_acl(inode, full_path, NO_CHANGE_64, uid, gid); @@ -2510,7 +2511,8 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs) if (attrs->ia_valid & ATTR_MODE) { mode = attrs->ia_mode; rc = 0; - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) { + if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) || + (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MODE_FROM_SID)) { rc = id_mode_to_cifs_acl(inode, full_path, mode, INVALID_UID, INVALID_GID); if (rc) { -- cgit v1.2.3