Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * fs/nfs_common/nfsacl.c
0004  *
0005  *  Copyright (C) 2002-2003 Andreas Gruenbacher <agruen@suse.de>
0006  */
0007 
0008 /*
0009  * The Solaris nfsacl protocol represents some ACLs slightly differently
0010  * than POSIX 1003.1e draft 17 does (and we do):
0011  *
0012  *  - Minimal ACLs always have an ACL_MASK entry, so they have
0013  *    four instead of three entries.
0014  *  - The ACL_MASK entry in such minimal ACLs always has the same
0015  *    permissions as the ACL_GROUP_OBJ entry. (In extended ACLs
0016  *    the ACL_MASK and ACL_GROUP_OBJ entries may differ.)
0017  *  - The identifier fields of the ACL_USER_OBJ and ACL_GROUP_OBJ
0018  *    entries contain the identifiers of the owner and owning group.
0019  *    (In POSIX ACLs we always set them to ACL_UNDEFINED_ID).
0020  *  - ACL entries in the kernel are kept sorted in ascending order
0021  *    of (e_tag, e_id). Solaris ACLs are unsorted.
0022  */
0023 
0024 #include <linux/module.h>
0025 #include <linux/fs.h>
0026 #include <linux/gfp.h>
0027 #include <linux/sunrpc/xdr.h>
0028 #include <linux/nfsacl.h>
0029 #include <linux/nfs3.h>
0030 #include <linux/sort.h>
0031 
0032 MODULE_LICENSE("GPL");
0033 
0034 struct nfsacl_encode_desc {
0035     struct xdr_array2_desc desc;
0036     unsigned int count;
0037     struct posix_acl *acl;
0038     int typeflag;
0039     kuid_t uid;
0040     kgid_t gid;
0041 };
0042 
0043 struct nfsacl_simple_acl {
0044     struct posix_acl acl;
0045     struct posix_acl_entry ace[4];
0046 };
0047 
0048 static int
0049 xdr_nfsace_encode(struct xdr_array2_desc *desc, void *elem)
0050 {
0051     struct nfsacl_encode_desc *nfsacl_desc =
0052         (struct nfsacl_encode_desc *) desc;
0053     __be32 *p = elem;
0054 
0055     struct posix_acl_entry *entry =
0056         &nfsacl_desc->acl->a_entries[nfsacl_desc->count++];
0057 
0058     *p++ = htonl(entry->e_tag | nfsacl_desc->typeflag);
0059     switch(entry->e_tag) {
0060         case ACL_USER_OBJ:
0061             *p++ = htonl(from_kuid(&init_user_ns, nfsacl_desc->uid));
0062             break;
0063         case ACL_GROUP_OBJ:
0064             *p++ = htonl(from_kgid(&init_user_ns, nfsacl_desc->gid));
0065             break;
0066         case ACL_USER:
0067             *p++ = htonl(from_kuid(&init_user_ns, entry->e_uid));
0068             break;
0069         case ACL_GROUP:
0070             *p++ = htonl(from_kgid(&init_user_ns, entry->e_gid));
0071             break;
0072         default:  /* Solaris depends on that! */
0073             *p++ = 0;
0074             break;
0075     }
0076     *p++ = htonl(entry->e_perm & S_IRWXO);
0077     return 0;
0078 }
0079 
0080 /**
0081  * nfsacl_encode - Encode an NFSv3 ACL
0082  *
0083  * @buf: destination xdr_buf to contain XDR encoded ACL
0084  * @base: byte offset in xdr_buf where XDR'd ACL begins
0085  * @inode: inode of file whose ACL this is
0086  * @acl: posix_acl to encode
0087  * @encode_entries: whether to encode ACEs as well
0088  * @typeflag: ACL type: NFS_ACL_DEFAULT or zero
0089  *
0090  * Returns size of encoded ACL in bytes or a negative errno value.
0091  */
0092 int nfsacl_encode(struct xdr_buf *buf, unsigned int base, struct inode *inode,
0093           struct posix_acl *acl, int encode_entries, int typeflag)
0094 {
0095     int entries = (acl && acl->a_count) ? max_t(int, acl->a_count, 4) : 0;
0096     struct nfsacl_encode_desc nfsacl_desc = {
0097         .desc = {
0098             .elem_size = 12,
0099             .array_len = encode_entries ? entries : 0,
0100             .xcode = xdr_nfsace_encode,
0101         },
0102         .acl = acl,
0103         .typeflag = typeflag,
0104         .uid = inode->i_uid,
0105         .gid = inode->i_gid,
0106     };
0107     struct nfsacl_simple_acl aclbuf;
0108     int err;
0109 
0110     if (entries > NFS_ACL_MAX_ENTRIES ||
0111         xdr_encode_word(buf, base, entries))
0112         return -EINVAL;
0113     if (encode_entries && acl && acl->a_count == 3) {
0114         struct posix_acl *acl2 = &aclbuf.acl;
0115 
0116         /* Avoid the use of posix_acl_alloc().  nfsacl_encode() is
0117          * invoked in contexts where a memory allocation failure is
0118          * fatal.  Fortunately this fake ACL is small enough to
0119          * construct on the stack. */
0120         posix_acl_init(acl2, 4);
0121 
0122         /* Insert entries in canonical order: other orders seem
0123          to confuse Solaris VxFS. */
0124         acl2->a_entries[0] = acl->a_entries[0];  /* ACL_USER_OBJ */
0125         acl2->a_entries[1] = acl->a_entries[1];  /* ACL_GROUP_OBJ */
0126         acl2->a_entries[2] = acl->a_entries[1];  /* ACL_MASK */
0127         acl2->a_entries[2].e_tag = ACL_MASK;
0128         acl2->a_entries[3] = acl->a_entries[2];  /* ACL_OTHER */
0129         nfsacl_desc.acl = acl2;
0130     }
0131     err = xdr_encode_array2(buf, base + 4, &nfsacl_desc.desc);
0132     if (!err)
0133         err = 8 + nfsacl_desc.desc.elem_size *
0134               nfsacl_desc.desc.array_len;
0135     return err;
0136 }
0137 EXPORT_SYMBOL_GPL(nfsacl_encode);
0138 
0139 /**
0140  * nfs_stream_encode_acl - Encode an NFSv3 ACL
0141  *
0142  * @xdr: an xdr_stream positioned to receive an encoded ACL
0143  * @inode: inode of file whose ACL this is
0144  * @acl: posix_acl to encode
0145  * @encode_entries: whether to encode ACEs as well
0146  * @typeflag: ACL type: NFS_ACL_DEFAULT or zero
0147  *
0148  * Return values:
0149  *   %false: The ACL could not be encoded
0150  *   %true: @xdr is advanced to the next available position
0151  */
0152 bool nfs_stream_encode_acl(struct xdr_stream *xdr, struct inode *inode,
0153                struct posix_acl *acl, int encode_entries,
0154                int typeflag)
0155 {
0156     const size_t elem_size = XDR_UNIT * 3;
0157     u32 entries = (acl && acl->a_count) ? max_t(int, acl->a_count, 4) : 0;
0158     struct nfsacl_encode_desc nfsacl_desc = {
0159         .desc = {
0160             .elem_size = elem_size,
0161             .array_len = encode_entries ? entries : 0,
0162             .xcode = xdr_nfsace_encode,
0163         },
0164         .acl = acl,
0165         .typeflag = typeflag,
0166         .uid = inode->i_uid,
0167         .gid = inode->i_gid,
0168     };
0169     struct nfsacl_simple_acl aclbuf;
0170     unsigned int base;
0171     int err;
0172 
0173     if (entries > NFS_ACL_MAX_ENTRIES)
0174         return false;
0175     if (xdr_stream_encode_u32(xdr, entries) < 0)
0176         return false;
0177 
0178     if (encode_entries && acl && acl->a_count == 3) {
0179         struct posix_acl *acl2 = &aclbuf.acl;
0180 
0181         /* Avoid the use of posix_acl_alloc().  nfsacl_encode() is
0182          * invoked in contexts where a memory allocation failure is
0183          * fatal.  Fortunately this fake ACL is small enough to
0184          * construct on the stack. */
0185         posix_acl_init(acl2, 4);
0186 
0187         /* Insert entries in canonical order: other orders seem
0188          to confuse Solaris VxFS. */
0189         acl2->a_entries[0] = acl->a_entries[0];  /* ACL_USER_OBJ */
0190         acl2->a_entries[1] = acl->a_entries[1];  /* ACL_GROUP_OBJ */
0191         acl2->a_entries[2] = acl->a_entries[1];  /* ACL_MASK */
0192         acl2->a_entries[2].e_tag = ACL_MASK;
0193         acl2->a_entries[3] = acl->a_entries[2];  /* ACL_OTHER */
0194         nfsacl_desc.acl = acl2;
0195     }
0196 
0197     base = xdr_stream_pos(xdr);
0198     if (!xdr_reserve_space(xdr, XDR_UNIT +
0199                    elem_size * nfsacl_desc.desc.array_len))
0200         return false;
0201     err = xdr_encode_array2(xdr->buf, base, &nfsacl_desc.desc);
0202     if (err)
0203         return false;
0204 
0205     return true;
0206 }
0207 EXPORT_SYMBOL_GPL(nfs_stream_encode_acl);
0208 
0209 
0210 struct nfsacl_decode_desc {
0211     struct xdr_array2_desc desc;
0212     unsigned int count;
0213     struct posix_acl *acl;
0214 };
0215 
0216 static int
0217 xdr_nfsace_decode(struct xdr_array2_desc *desc, void *elem)
0218 {
0219     struct nfsacl_decode_desc *nfsacl_desc =
0220         (struct nfsacl_decode_desc *) desc;
0221     __be32 *p = elem;
0222     struct posix_acl_entry *entry;
0223     unsigned int id;
0224 
0225     if (!nfsacl_desc->acl) {
0226         if (desc->array_len > NFS_ACL_MAX_ENTRIES)
0227             return -EINVAL;
0228         nfsacl_desc->acl = posix_acl_alloc(desc->array_len, GFP_KERNEL);
0229         if (!nfsacl_desc->acl)
0230             return -ENOMEM;
0231         nfsacl_desc->count = 0;
0232     }
0233 
0234     entry = &nfsacl_desc->acl->a_entries[nfsacl_desc->count++];
0235     entry->e_tag = ntohl(*p++) & ~NFS_ACL_DEFAULT;
0236     id = ntohl(*p++);
0237     entry->e_perm = ntohl(*p++);
0238 
0239     switch(entry->e_tag) {
0240         case ACL_USER:
0241             entry->e_uid = make_kuid(&init_user_ns, id);
0242             if (!uid_valid(entry->e_uid))
0243                 return -EINVAL;
0244             break;
0245         case ACL_GROUP:
0246             entry->e_gid = make_kgid(&init_user_ns, id);
0247             if (!gid_valid(entry->e_gid))
0248                 return -EINVAL;
0249             break;
0250         case ACL_USER_OBJ:
0251         case ACL_GROUP_OBJ:
0252         case ACL_OTHER:
0253             if (entry->e_perm & ~S_IRWXO)
0254                 return -EINVAL;
0255             break;
0256         case ACL_MASK:
0257             /* Solaris sometimes sets additional bits in the mask */
0258             entry->e_perm &= S_IRWXO;
0259             break;
0260         default:
0261             return -EINVAL;
0262     }
0263 
0264     return 0;
0265 }
0266 
0267 static int
0268 cmp_acl_entry(const void *x, const void *y)
0269 {
0270     const struct posix_acl_entry *a = x, *b = y;
0271 
0272     if (a->e_tag != b->e_tag)
0273         return a->e_tag - b->e_tag;
0274     else if ((a->e_tag == ACL_USER) && uid_gt(a->e_uid, b->e_uid))
0275         return 1;
0276     else if ((a->e_tag == ACL_USER) && uid_lt(a->e_uid, b->e_uid))
0277         return -1;
0278     else if ((a->e_tag == ACL_GROUP) && gid_gt(a->e_gid, b->e_gid))
0279         return 1;
0280     else if ((a->e_tag == ACL_GROUP) && gid_lt(a->e_gid, b->e_gid))
0281         return -1;
0282     else
0283         return 0;
0284 }
0285 
0286 /*
0287  * Convert from a Solaris ACL to a POSIX 1003.1e draft 17 ACL.
0288  */
0289 static int
0290 posix_acl_from_nfsacl(struct posix_acl *acl)
0291 {
0292     struct posix_acl_entry *pa, *pe,
0293            *group_obj = NULL, *mask = NULL;
0294 
0295     if (!acl)
0296         return 0;
0297 
0298     sort(acl->a_entries, acl->a_count, sizeof(struct posix_acl_entry),
0299          cmp_acl_entry, NULL);
0300 
0301     /* Find the ACL_GROUP_OBJ and ACL_MASK entries. */
0302     FOREACH_ACL_ENTRY(pa, acl, pe) {
0303         switch(pa->e_tag) {
0304             case ACL_USER_OBJ:
0305                 break;
0306             case ACL_GROUP_OBJ:
0307                 group_obj = pa;
0308                 break;
0309             case ACL_MASK:
0310                 mask = pa;
0311                 fallthrough;
0312             case ACL_OTHER:
0313                 break;
0314         }
0315     }
0316     if (acl->a_count == 4 && group_obj && mask &&
0317         mask->e_perm == group_obj->e_perm) {
0318         /* remove bogus ACL_MASK entry */
0319         memmove(mask, mask+1, (3 - (mask - acl->a_entries)) *
0320                       sizeof(struct posix_acl_entry));
0321         acl->a_count = 3;
0322     }
0323     return 0;
0324 }
0325 
0326 /**
0327  * nfsacl_decode - Decode an NFSv3 ACL
0328  *
0329  * @buf: xdr_buf containing XDR'd ACL data to decode
0330  * @base: byte offset in xdr_buf where XDR'd ACL begins
0331  * @aclcnt: count of ACEs in decoded posix_acl
0332  * @pacl: buffer in which to place decoded posix_acl
0333  *
0334  * Returns the length of the decoded ACL in bytes, or a negative errno value.
0335  */
0336 int nfsacl_decode(struct xdr_buf *buf, unsigned int base, unsigned int *aclcnt,
0337           struct posix_acl **pacl)
0338 {
0339     struct nfsacl_decode_desc nfsacl_desc = {
0340         .desc = {
0341             .elem_size = 12,
0342             .xcode = pacl ? xdr_nfsace_decode : NULL,
0343         },
0344     };
0345     u32 entries;
0346     int err;
0347 
0348     if (xdr_decode_word(buf, base, &entries) ||
0349         entries > NFS_ACL_MAX_ENTRIES)
0350         return -EINVAL;
0351     nfsacl_desc.desc.array_maxlen = entries;
0352     err = xdr_decode_array2(buf, base + 4, &nfsacl_desc.desc);
0353     if (err)
0354         return err;
0355     if (pacl) {
0356         if (entries != nfsacl_desc.desc.array_len ||
0357             posix_acl_from_nfsacl(nfsacl_desc.acl) != 0) {
0358             posix_acl_release(nfsacl_desc.acl);
0359             return -EINVAL;
0360         }
0361         *pacl = nfsacl_desc.acl;
0362     }
0363     if (aclcnt)
0364         *aclcnt = entries;
0365     return 8 + nfsacl_desc.desc.elem_size *
0366            nfsacl_desc.desc.array_len;
0367 }
0368 EXPORT_SYMBOL_GPL(nfsacl_decode);
0369 
0370 /**
0371  * nfs_stream_decode_acl - Decode an NFSv3 ACL
0372  *
0373  * @xdr: an xdr_stream positioned at an encoded ACL
0374  * @aclcnt: OUT: count of ACEs in decoded posix_acl
0375  * @pacl: OUT: a dynamically-allocated buffer containing the decoded posix_acl
0376  *
0377  * Return values:
0378  *   %false: The encoded ACL is not valid
0379  *   %true: @pacl contains a decoded ACL, and @xdr is advanced
0380  *
0381  * On a successful return, caller must release *pacl using posix_acl_release().
0382  */
0383 bool nfs_stream_decode_acl(struct xdr_stream *xdr, unsigned int *aclcnt,
0384                struct posix_acl **pacl)
0385 {
0386     const size_t elem_size = XDR_UNIT * 3;
0387     struct nfsacl_decode_desc nfsacl_desc = {
0388         .desc = {
0389             .elem_size = elem_size,
0390             .xcode = pacl ? xdr_nfsace_decode : NULL,
0391         },
0392     };
0393     unsigned int base;
0394     u32 entries;
0395 
0396     if (xdr_stream_decode_u32(xdr, &entries) < 0)
0397         return false;
0398     if (entries > NFS_ACL_MAX_ENTRIES)
0399         return false;
0400 
0401     base = xdr_stream_pos(xdr);
0402     if (!xdr_inline_decode(xdr, XDR_UNIT + elem_size * entries))
0403         return false;
0404     nfsacl_desc.desc.array_maxlen = entries;
0405     if (xdr_decode_array2(xdr->buf, base, &nfsacl_desc.desc))
0406         return false;
0407 
0408     if (pacl) {
0409         if (entries != nfsacl_desc.desc.array_len ||
0410             posix_acl_from_nfsacl(nfsacl_desc.acl) != 0) {
0411             posix_acl_release(nfsacl_desc.acl);
0412             return false;
0413         }
0414         *pacl = nfsacl_desc.acl;
0415     }
0416     if (aclcnt)
0417         *aclcnt = entries;
0418     return true;
0419 }
0420 EXPORT_SYMBOL_GPL(nfs_stream_decode_acl);