Merge branch 'linus' of git://git.kernel.org/pub/scm/linux/kernel/git/herbert/crypto-2.6
[linux-2.6-microblaze.git] / fs / cifs / cifsacl.c
index 78eed72..f842944 100644 (file)
 #include "cifsproto.h"
 #include "cifs_debug.h"
 
-static struct key_acl cifs_idmap_key_acl = {
-       .usage  = REFCOUNT_INIT(1),
-       .nr_ace = 2,
-       .possessor_viewable = true,
-       .aces = {
-               KEY_POSSESSOR_ACE(KEY_ACE_VIEW | KEY_ACE_SEARCH | KEY_ACE_READ),
-               KEY_OWNER_ACE(KEY_ACE_VIEW),
-       }
-};
-
-static struct key_acl cifs_idmap_keyring_acl = {
-       .usage  = REFCOUNT_INIT(1),
-       .nr_ace = 2,
-       .aces = {
-               KEY_POSSESSOR_ACE(KEY_ACE_SEARCH | KEY_ACE_WRITE),
-               KEY_OWNER_ACE(KEY_ACE_VIEW | KEY_ACE_READ),
-       }
-};
-
 /* security id for everyone/world system group */
 static const struct cifs_sid sid_everyone = {
        1, 1, {0, 0, 0, 0, 0, 1}, {0} };
@@ -317,8 +298,7 @@ id_to_sid(unsigned int cid, uint sidtype, struct cifs_sid *ssid)
 
        rc = 0;
        saved_cred = override_creds(root_cred);
-       sidkey = request_key(&cifs_idmap_key_type, desc, "",
-                            &cifs_idmap_key_acl);
+       sidkey = request_key(&cifs_idmap_key_type, desc, "");
        if (IS_ERR(sidkey)) {
                rc = -EINVAL;
                cifs_dbg(FYI, "%s: Can't map %cid %u to a SID\n",
@@ -423,8 +403,7 @@ try_upcall_to_get_id:
                return -ENOMEM;
 
        saved_cred = override_creds(root_cred);
-       sidkey = request_key(&cifs_idmap_key_type, sidstr, "",
-                            &cifs_idmap_key_acl);
+       sidkey = request_key(&cifs_idmap_key_type, sidstr, "");
        if (IS_ERR(sidkey)) {
                rc = -EINVAL;
                cifs_dbg(FYI, "%s: Can't map SID %s to a %cid\n",
@@ -502,7 +481,8 @@ init_cifs_idmap(void)
 
        keyring = keyring_alloc(".cifs_idmap",
                                GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred,
-                               &cifs_idmap_keyring_acl,
+                               (KEY_POS_ALL & ~KEY_POS_SETATTR) |
+                               KEY_USR_VIEW | KEY_USR_READ,
                                KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL);
        if (IS_ERR(keyring)) {
                ret = PTR_ERR(keyring);
@@ -721,10 +701,9 @@ static void dump_ace(struct cifs_ace *pace, char *end_of_acl)
 }
 #endif
 
-
 static void parse_dacl(struct cifs_acl *pdacl, char *end_of_acl,
                       struct cifs_sid *pownersid, struct cifs_sid *pgrpsid,
-                      struct cifs_fattr *fattr)
+                      struct cifs_fattr *fattr, bool mode_from_special_sid)
 {
        int i;
        int num_aces = 0;
@@ -777,22 +756,34 @@ static void parse_dacl(struct cifs_acl *pdacl, char *end_of_acl,
 #ifdef CONFIG_CIFS_DEBUG2
                        dump_ace(ppace[i], end_of_acl);
 #endif
-                       if (compare_sids(&(ppace[i]->sid), pownersid) == 0)
+                       if (mode_from_special_sid &&
+                           (compare_sids(&(ppace[i]->sid),
+                                         &sid_unix_NFS_mode) == 0)) {
+                               /*
+                                * Full permissions are:
+                                * 07777 = S_ISUID | S_ISGID | S_ISVTX |
+                                *         S_IRWXU | S_IRWXG | S_IRWXO
+                                */
+                               fattr->cf_mode &= ~07777;
+                               fattr->cf_mode |=
+                                       le32_to_cpu(ppace[i]->sid.sub_auth[2]);
+                               break;
+                       } else if (compare_sids(&(ppace[i]->sid), pownersid) == 0)
                                access_flags_to_mode(ppace[i]->access_req,
                                                     ppace[i]->type,
                                                     &fattr->cf_mode,
                                                     &user_mask);
-                       if (compare_sids(&(ppace[i]->sid), pgrpsid) == 0)
+                       else if (compare_sids(&(ppace[i]->sid), pgrpsid) == 0)
                                access_flags_to_mode(ppace[i]->access_req,
                                                     ppace[i]->type,
                                                     &fattr->cf_mode,
                                                     &group_mask);
-                       if (compare_sids(&(ppace[i]->sid), &sid_everyone) == 0)
+                       else if (compare_sids(&(ppace[i]->sid), &sid_everyone) == 0)
                                access_flags_to_mode(ppace[i]->access_req,
                                                     ppace[i]->type,
                                                     &fattr->cf_mode,
                                                     &other_mask);
-                       if (compare_sids(&(ppace[i]->sid), &sid_authusers) == 0)
+                       else if (compare_sids(&(ppace[i]->sid), &sid_authusers) == 0)
                                access_flags_to_mode(ppace[i]->access_req,
                                                     ppace[i]->type,
                                                     &fattr->cf_mode,
@@ -815,22 +806,49 @@ 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;
+       u32 num_aces = 0;
        struct cifs_acl *pnndacl;
 
        pnndacl = (struct cifs_acl *)((char *)pndacl + sizeof(struct cifs_acl));
 
+       if (modefromsid) {
+               struct cifs_ace *pntace =
+                       (struct cifs_ace *)((char *)pnndacl + size);
+               int i;
+
+               pntace->type = ACCESS_ALLOWED;
+               pntace->flags = 0x0;
+               pntace->access_req = 0;
+               pntace->sid.num_subauth = 3;
+               pntace->sid.revision = 1;
+               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);
+
+               /* size = 1 + 1 + 2 + 4 + 1 + 1 + 6 + (psid->num_subauth*4) */
+               pntace->size = cpu_to_le16(28);
+               size += 28;
+               num_aces++;
+       }
+
        size += fill_ace_for_sid((struct cifs_ace *) ((char *)pnndacl + size),
                                        pownersid, nmode, S_IRWXU);
+       num_aces++;
        size += fill_ace_for_sid((struct cifs_ace *)((char *)pnndacl + size),
                                        pgrpsid, nmode, S_IRWXG);
+       num_aces++;
        size += fill_ace_for_sid((struct cifs_ace *)((char *)pnndacl + size),
                                         &sid_everyone, nmode, S_IRWXO);
+       num_aces++;
 
+       pndacl->num_aces = cpu_to_le32(num_aces);
        pndacl->size = cpu_to_le16(size + sizeof(struct cifs_acl));
-       pndacl->num_aces = cpu_to_le32(3);
 
        return 0;
 }
@@ -871,7 +889,8 @@ static int parse_sid(struct cifs_sid *psid, char *end_of_acl)
 
 /* Convert CIFS ACL to POSIX form */
 static int parse_sec_desc(struct cifs_sb_info *cifs_sb,
-               struct cifs_ntsd *pntsd, int acl_len, struct cifs_fattr *fattr)
+               struct cifs_ntsd *pntsd, int acl_len, struct cifs_fattr *fattr,
+               bool get_mode_from_special_sid)
 {
        int rc = 0;
        struct cifs_sid *owner_sid_ptr, *group_sid_ptr;
@@ -920,7 +939,7 @@ static int parse_sec_desc(struct cifs_sb_info *cifs_sb,
 
        if (dacloffset)
                parse_dacl(dacl_ptr, end_of_acl, owner_sid_ptr,
-                          group_sid_ptr, fattr);
+                          group_sid_ptr, fattr, get_mode_from_special_sid);
        else
                cifs_dbg(FYI, "no ACL\n"); /* BB grant all or default perms? */
 
@@ -929,7 +948,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;
@@ -954,7 +974,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);
@@ -1148,8 +1168,8 @@ out:
 /* Translate the CIFS ACL (similar to NTFS ACL) for a file into mode bits */
 int
 cifs_acl_to_fattr(struct cifs_sb_info *cifs_sb, struct cifs_fattr *fattr,
-                 struct inode *inode, const char *path,
-                 const struct cifs_fid *pfid)
+                 struct inode *inode, bool mode_from_special_sid,
+                 const char *path, const struct cifs_fid *pfid)
 {
        struct cifs_ntsd *pntsd = NULL;
        u32 acllen = 0;
@@ -1176,8 +1196,11 @@ cifs_acl_to_fattr(struct cifs_sb_info *cifs_sb, struct cifs_fattr *fattr,
        if (IS_ERR(pntsd)) {
                rc = PTR_ERR(pntsd);
                cifs_dbg(VFS, "%s: error %d getting sec desc\n", __func__, rc);
+       } else if (mode_from_special_sid) {
+               rc = parse_sec_desc(cifs_sb, pntsd, acllen, fattr, true);
        } else {
-               rc = parse_sec_desc(cifs_sb, pntsd, acllen, fattr);
+               /* get approximated mode from ACL */
+               rc = parse_sec_desc(cifs_sb, pntsd, acllen, fattr, false);
                kfree(pntsd);
                if (rc)
                        cifs_dbg(VFS, "parse sec desc failed rc = %d\n", rc);
@@ -1201,6 +1224,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);
@@ -1238,8 +1262,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);