SMB3: Add support for getting and setting SACLs
authorBoris Protopopov <pboris@amazon.com>
Fri, 18 Dec 2020 17:30:12 +0000 (11:30 -0600)
committerSteve French <stfrench@microsoft.com>
Fri, 18 Dec 2020 19:25:57 +0000 (13:25 -0600)
Add SYSTEM_SECURITY access flag and use with smb2 when opening
files for getting/setting SACLs. Add "system.cifs_ntsd_full"
extended attribute to allow user-space access to the functionality.
Avoid multiple server calls when setting owner, DACL, and SACL.

Signed-off-by: Boris Protopopov <pboris@amazon.com>
Signed-off-by: Steve French <stfrench@microsoft.com>
fs/cifs/cifsacl.c
fs/cifs/cifsglob.h
fs/cifs/cifspdu.h
fs/cifs/cifsproto.h
fs/cifs/smb2ops.c
fs/cifs/smb2pdu.c
fs/cifs/smb2proto.h
fs/cifs/xattr.c

index 2f21f89..562913e 100644 (file)
@@ -1195,7 +1195,8 @@ static int build_sec_desc(struct cifs_ntsd *pntsd, struct cifs_ntsd *pnntsd,
 }
 
 struct cifs_ntsd *get_cifs_acl_by_fid(struct cifs_sb_info *cifs_sb,
-               const struct cifs_fid *cifsfid, u32 *pacllen)
+                                     const struct cifs_fid *cifsfid, u32 *pacllen,
+                                     u32 __maybe_unused unused)
 {
        struct cifs_ntsd *pntsd = NULL;
        unsigned int xid;
@@ -1263,7 +1264,7 @@ static struct cifs_ntsd *get_cifs_acl_by_path(struct cifs_sb_info *cifs_sb,
 /* Retrieve an ACL from the server */
 struct cifs_ntsd *get_cifs_acl(struct cifs_sb_info *cifs_sb,
                                      struct inode *inode, const char *path,
-                                     u32 *pacllen)
+                              u32 *pacllen, u32 info)
 {
        struct cifs_ntsd *pntsd = NULL;
        struct cifsFileInfo *open_file = NULL;
@@ -1273,7 +1274,7 @@ struct cifs_ntsd *get_cifs_acl(struct cifs_sb_info *cifs_sb,
        if (!open_file)
                return get_cifs_acl_by_path(cifs_sb, path, pacllen);
 
-       pntsd = get_cifs_acl_by_fid(cifs_sb, &open_file->fid, pacllen);
+       pntsd = get_cifs_acl_by_fid(cifs_sb, &open_file->fid, pacllen, info);
        cifsFileInfo_put(open_file);
        return pntsd;
 }
@@ -1338,6 +1339,7 @@ cifs_acl_to_fattr(struct cifs_sb_info *cifs_sb, struct cifs_fattr *fattr,
        int rc = 0;
        struct tcon_link *tlink = cifs_sb_tlink(cifs_sb);
        struct smb_version_operations *ops;
+       const u32 info = 0;
 
        cifs_dbg(NOISY, "converting ACL to mode for %s\n", path);
 
@@ -1347,9 +1349,9 @@ cifs_acl_to_fattr(struct cifs_sb_info *cifs_sb, struct cifs_fattr *fattr,
        ops = tlink_tcon(tlink)->ses->server->ops;
 
        if (pfid && (ops->get_acl_by_fid))
-               pntsd = ops->get_acl_by_fid(cifs_sb, pfid, &acllen);
+               pntsd = ops->get_acl_by_fid(cifs_sb, pfid, &acllen, info);
        else if (ops->get_acl)
-               pntsd = ops->get_acl(cifs_sb, inode, path, &acllen);
+               pntsd = ops->get_acl(cifs_sb, inode, path, &acllen, info);
        else {
                cifs_put_tlink(tlink);
                return -EOPNOTSUPP;
@@ -1388,6 +1390,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;
+       const u32 info = 0;
 
        if (IS_ERR(tlink))
                return PTR_ERR(tlink);
@@ -1403,7 +1406,7 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 *pnmode,
                return -EOPNOTSUPP;
        }
 
-       pntsd = ops->get_acl(cifs_sb, inode, path, &secdesclen);
+       pntsd = ops->get_acl(cifs_sb, inode, path, &secdesclen, info);
        if (IS_ERR(pntsd)) {
                rc = PTR_ERR(pntsd);
                cifs_dbg(VFS, "%s: error %d getting sec desc\n", __func__, rc);
index 720d0f6..50fcb65 100644 (file)
@@ -456,9 +456,9 @@ struct smb_version_operations {
                        const char *, const void *, const __u16,
                        const struct nls_table *, struct cifs_sb_info *);
        struct cifs_ntsd * (*get_acl)(struct cifs_sb_info *, struct inode *,
-                       const char *, u32 *);
+                       const char *, u32 *, u32);
        struct cifs_ntsd * (*get_acl_by_fid)(struct cifs_sb_info *,
-                       const struct cifs_fid *, u32 *);
+                       const struct cifs_fid *, u32 *, u32);
        int (*set_acl)(struct cifs_ntsd *, __u32, struct inode *, const char *,
                        int);
        /* writepages retry size */
index ce51183..64fe5a4 100644 (file)
 #define SYNCHRONIZE           0x00100000  /* The file handle can waited on to */
                                          /* synchronize with the completion  */
                                          /* of an input/output request       */
+#define SYSTEM_SECURITY       0x01000000  /* The system access control list   */
+                                         /* can be read and changed          */
 #define GENERIC_ALL           0x10000000
 #define GENERIC_EXECUTE       0x20000000
 #define GENERIC_WRITE         0x40000000
index bd1c9b0..340ff81 100644 (file)
@@ -218,9 +218,9 @@ extern int cifs_acl_to_fattr(struct cifs_sb_info *cifs_sb,
 extern int id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 *pnmode,
                                        kuid_t uid, kgid_t gid);
 extern struct cifs_ntsd *get_cifs_acl(struct cifs_sb_info *, struct inode *,
-                                       const char *, u32 *);
+                                     const char *, u32 *, u32);
 extern struct cifs_ntsd *get_cifs_acl_by_fid(struct cifs_sb_info *,
-                                               const struct cifs_fid *, u32 *);
+                               const struct cifs_fid *, u32 *, u32);
 extern int set_cifs_acl(struct cifs_ntsd *, __u32, struct inode *,
                                const char *, int);
 extern unsigned int setup_authusers_ACE(struct cifs_ace *pace);
index 949cd11..4a17611 100644 (file)
@@ -3214,7 +3214,7 @@ smb2_query_reparse_tag(const unsigned int xid, struct cifs_tcon *tcon,
 
 static struct cifs_ntsd *
 get_smb2_acl_by_fid(struct cifs_sb_info *cifs_sb,
-               const struct cifs_fid *cifsfid, u32 *pacllen)
+                   const struct cifs_fid *cifsfid, u32 *pacllen, u32 info)
 {
        struct cifs_ntsd *pntsd = NULL;
        unsigned int xid;
@@ -3228,7 +3228,8 @@ get_smb2_acl_by_fid(struct cifs_sb_info *cifs_sb,
        cifs_dbg(FYI, "trying to get acl\n");
 
        rc = SMB2_query_acl(xid, tlink_tcon(tlink), cifsfid->persistent_fid,
-                           cifsfid->volatile_fid, (void **)&pntsd, pacllen);
+                           cifsfid->volatile_fid, (void **)&pntsd, pacllen,
+                           info);
        free_xid(xid);
 
        cifs_put_tlink(tlink);
@@ -3242,7 +3243,7 @@ get_smb2_acl_by_fid(struct cifs_sb_info *cifs_sb,
 
 static struct cifs_ntsd *
 get_smb2_acl_by_path(struct cifs_sb_info *cifs_sb,
-               const char *path, u32 *pacllen)
+                    const char *path, u32 *pacllen, u32 info)
 {
        struct cifs_ntsd *pntsd = NULL;
        u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
@@ -3280,12 +3281,16 @@ get_smb2_acl_by_path(struct cifs_sb_info *cifs_sb,
        oparms.fid = &fid;
        oparms.reconnect = false;
 
+       if (info & SACL_SECINFO)
+               oparms.desired_access |= SYSTEM_SECURITY;
+
        rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL, NULL,
                       NULL);
        kfree(utf16_path);
        if (!rc) {
                rc = SMB2_query_acl(xid, tlink_tcon(tlink), fid.persistent_fid,
-                           fid.volatile_fid, (void **)&pntsd, pacllen);
+                                   fid.volatile_fid, (void **)&pntsd, pacllen,
+                                   info);
                SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
        }
 
@@ -3319,10 +3324,12 @@ set_smb2_acl(struct cifs_ntsd *pnntsd, __u32 acllen,
        tcon = tlink_tcon(tlink);
        xid = get_xid();
 
-       if (aclflag == CIFS_ACL_OWNER || aclflag == CIFS_ACL_GROUP)
-               access_flags = WRITE_OWNER;
-       else
-               access_flags = WRITE_DAC;
+       if (aclflag & CIFS_ACL_OWNER || aclflag & CIFS_ACL_GROUP)
+               access_flags |= WRITE_OWNER;
+       if (aclflag & CIFS_ACL_SACL)
+               access_flags |= SYSTEM_SECURITY;
+       if (aclflag & CIFS_ACL_DACL)
+               access_flags |= WRITE_DAC;
 
        utf16_path = cifs_convert_path_to_utf16(path, cifs_sb);
        if (!utf16_path) {
@@ -3356,8 +3363,8 @@ set_smb2_acl(struct cifs_ntsd *pnntsd, __u32 acllen,
 /* Retrieve an ACL from the server */
 static struct cifs_ntsd *
 get_smb2_acl(struct cifs_sb_info *cifs_sb,
-                                     struct inode *inode, const char *path,
-                                     u32 *pacllen)
+            struct inode *inode, const char *path,
+            u32 *pacllen, u32 info)
 {
        struct cifs_ntsd *pntsd = NULL;
        struct cifsFileInfo *open_file = NULL;
@@ -3365,9 +3372,9 @@ get_smb2_acl(struct cifs_sb_info *cifs_sb,
        if (inode)
                open_file = find_readable_file(CIFS_I(inode), true);
        if (!open_file)
-               return get_smb2_acl_by_path(cifs_sb, path, pacllen);
+               return get_smb2_acl_by_path(cifs_sb, path, pacllen, info);
 
-       pntsd = get_smb2_acl_by_fid(cifs_sb, &open_file->fid, pacllen);
+       pntsd = get_smb2_acl_by_fid(cifs_sb, &open_file->fid, pacllen, info);
        cifsFileInfo_put(open_file);
        return pntsd;
 }
index fc06c76..202d874 100644 (file)
@@ -3479,10 +3479,9 @@ SMB311_posix_query_info(const unsigned int xid, struct cifs_tcon *tcon,
 
 int
 SMB2_query_acl(const unsigned int xid, struct cifs_tcon *tcon,
-               u64 persistent_fid, u64 volatile_fid,
-               void **data, u32 *plen)
+              u64 persistent_fid, u64 volatile_fid,
+              void **data, u32 *plen, u32 additional_info)
 {
-       __u32 additional_info = OWNER_SECINFO | GROUP_SECINFO | DACL_SECINFO;
        *plen = 0;
 
        return query_info(xid, tcon, persistent_fid, volatile_fid,
index d411044..9565e27 100644 (file)
@@ -200,8 +200,8 @@ extern int SMB2_query_info_init(struct cifs_tcon *tcon,
                                size_t input_len, void *input);
 extern void SMB2_query_info_free(struct smb_rqst *rqst);
 extern int SMB2_query_acl(const unsigned int xid, struct cifs_tcon *tcon,
-                          u64 persistent_file_id, u64 volatile_file_id,
-                          void **data, unsigned int *plen);
+                         u64 persistent_file_id, u64 volatile_file_id,
+                         void **data, unsigned int *plen, u32 info);
 extern int SMB2_get_srv_num(const unsigned int xid, struct cifs_tcon *tcon,
                            u64 persistent_fid, u64 volatile_fid,
                            __le64 *uniqueid);
index b829917..9318a2a 100644 (file)
@@ -34,6 +34,7 @@
 #define MAX_EA_VALUE_SIZE CIFSMaxBufSize
 #define CIFS_XATTR_CIFS_ACL "system.cifs_acl" /* DACL only */
 #define CIFS_XATTR_CIFS_NTSD "system.cifs_ntsd" /* owner plus DACL */
+#define CIFS_XATTR_CIFS_NTSD_FULL "system.cifs_ntsd_full" /* owner/DACL/SACL */
 #define CIFS_XATTR_ATTRIB "cifs.dosattrib"  /* full name: user.cifs.dosattrib */
 #define CIFS_XATTR_CREATETIME "cifs.creationtime"  /* user.cifs.creationtime */
 /*
  */
 #define SMB3_XATTR_CIFS_ACL "system.smb3_acl" /* DACL only */
 #define SMB3_XATTR_CIFS_NTSD "system.smb3_ntsd" /* owner plus DACL */
+#define SMB3_XATTR_CIFS_NTSD_FULL "system.smb3_ntsd_full" /* owner/DACL/SACL */
 #define SMB3_XATTR_ATTRIB "smb3.dosattrib"  /* full name: user.smb3.dosattrib */
 #define SMB3_XATTR_CREATETIME "smb3.creationtime"  /* user.smb3.creationtime */
 /* BB need to add server (Samba e.g) support for security and trusted prefix */
 
 enum { XATTR_USER, XATTR_CIFS_ACL, XATTR_ACL_ACCESS, XATTR_ACL_DEFAULT,
-       XATTR_CIFS_NTSD };
+       XATTR_CIFS_NTSD, XATTR_CIFS_NTSD_FULL };
 
 static int cifs_attrib_set(unsigned int xid, struct cifs_tcon *pTcon,
                           struct inode *inode, char *full_path,
@@ -164,7 +166,8 @@ static int cifs_xattr_set(const struct xattr_handler *handler,
                break;
 
        case XATTR_CIFS_ACL:
-       case XATTR_CIFS_NTSD: {
+       case XATTR_CIFS_NTSD:
+       case XATTR_CIFS_NTSD_FULL: {
                struct cifs_ntsd *pacl;
 
                if (!value)
@@ -174,23 +177,27 @@ static int cifs_xattr_set(const struct xattr_handler *handler,
                        rc = -ENOMEM;
                } else {
                        memcpy(pacl, value, size);
-                       if (value &&
-                           pTcon->ses->server->ops->set_acl) {
+                       if (pTcon->ses->server->ops->set_acl) {
+                               int aclflags = 0;
                                rc = 0;
-                               if (handler->flags == XATTR_CIFS_NTSD) {
-                                       /* set owner and DACL */
-                                       rc = pTcon->ses->server->ops->set_acl(
-                                                       pacl, size, inode,
-                                                       full_path,
-                                                       CIFS_ACL_OWNER);
-                               }
-                               if (rc == 0) {
-                                       /* set DACL */
-                                       rc = pTcon->ses->server->ops->set_acl(
-                                                       pacl, size, inode,
-                                                       full_path,
-                                                       CIFS_ACL_DACL);
+
+                               switch (handler->flags) {
+                               case XATTR_CIFS_NTSD_FULL:
+                                       aclflags = (CIFS_ACL_OWNER |
+                                                   CIFS_ACL_DACL |
+                                                   CIFS_ACL_SACL);
+                                       break;
+                               case XATTR_CIFS_NTSD:
+                                       aclflags = (CIFS_ACL_OWNER |
+                                                   CIFS_ACL_DACL);
+                                       break;
+                               case XATTR_CIFS_ACL:
+                               default:
+                                       aclflags = CIFS_ACL_DACL;
                                }
+
+                               rc = pTcon->ses->server->ops->set_acl(pacl,
+                                       size, inode, full_path, aclflags);
                        } else {
                                rc = -EOPNOTSUPP;
                        }
@@ -327,16 +334,27 @@ static int cifs_xattr_get(const struct xattr_handler *handler,
                break;
 
        case XATTR_CIFS_ACL:
-       case XATTR_CIFS_NTSD: {
-               /* the whole ntsd is fetched regardless */
-               u32 acllen;
+       case XATTR_CIFS_NTSD:
+       case XATTR_CIFS_NTSD_FULL: {
+               /*
+                * fetch owner, DACL, and SACL if asked for full descriptor,
+                * fetch owner and DACL otherwise
+                */
+               u32 acllen, additional_info = 0;
                struct cifs_ntsd *pacl;
 
                if (pTcon->ses->server->ops->get_acl == NULL)
                        goto out; /* rc already EOPNOTSUPP */
 
+               if (handler->flags == XATTR_CIFS_NTSD_FULL) {
+                       additional_info = OWNER_SECINFO | GROUP_SECINFO |
+                               DACL_SECINFO | SACL_SECINFO;
+               } else {
+                       additional_info = OWNER_SECINFO | GROUP_SECINFO |
+                               DACL_SECINFO;
+               }
                pacl = pTcon->ses->server->ops->get_acl(cifs_sb,
-                               inode, full_path, &acllen);
+                               inode, full_path, &acllen, additional_info);
                if (IS_ERR(pacl)) {
                        rc = PTR_ERR(pacl);
                        cifs_dbg(VFS, "%s: error %zd getting sec desc\n",
@@ -486,6 +504,27 @@ static const struct xattr_handler smb3_ntsd_xattr_handler = {
        .set = cifs_xattr_set,
 };
 
+static const struct xattr_handler cifs_cifs_ntsd_full_xattr_handler = {
+       .name = CIFS_XATTR_CIFS_NTSD_FULL,
+       .flags = XATTR_CIFS_NTSD_FULL,
+       .get = cifs_xattr_get,
+       .set = cifs_xattr_set,
+};
+
+/*
+ * Although this is just an alias for the above, need to move away from
+ * confusing users and using the 20 year old term 'cifs' when it is no
+ * longer secure and was replaced by SMB2/SMB3 a long time ago, and
+ * SMB3 and later are highly secure.
+ */
+static const struct xattr_handler smb3_ntsd_full_xattr_handler = {
+       .name = SMB3_XATTR_CIFS_NTSD_FULL,
+       .flags = XATTR_CIFS_NTSD_FULL,
+       .get = cifs_xattr_get,
+       .set = cifs_xattr_set,
+};
+
+
 static const struct xattr_handler cifs_posix_acl_access_xattr_handler = {
        .name = XATTR_NAME_POSIX_ACL_ACCESS,
        .flags = XATTR_ACL_ACCESS,
@@ -507,6 +546,8 @@ const struct xattr_handler *cifs_xattr_handlers[] = {
        &smb3_acl_xattr_handler, /* alias for above since avoiding "cifs" */
        &cifs_cifs_ntsd_xattr_handler,
        &smb3_ntsd_xattr_handler, /* alias for above since avoiding "cifs" */
+       &cifs_cifs_ntsd_full_xattr_handler,
+       &smb3_ntsd_full_xattr_handler, /* alias for above since avoiding "cifs" */
        &cifs_posix_acl_access_xattr_handler,
        &cifs_posix_acl_default_xattr_handler,
        NULL