cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

nfsacl.c (11406B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * fs/nfs_common/nfsacl.c
      4 *
      5 *  Copyright (C) 2002-2003 Andreas Gruenbacher <agruen@suse.de>
      6 */
      7
      8/*
      9 * The Solaris nfsacl protocol represents some ACLs slightly differently
     10 * than POSIX 1003.1e draft 17 does (and we do):
     11 *
     12 *  - Minimal ACLs always have an ACL_MASK entry, so they have
     13 *    four instead of three entries.
     14 *  - The ACL_MASK entry in such minimal ACLs always has the same
     15 *    permissions as the ACL_GROUP_OBJ entry. (In extended ACLs
     16 *    the ACL_MASK and ACL_GROUP_OBJ entries may differ.)
     17 *  - The identifier fields of the ACL_USER_OBJ and ACL_GROUP_OBJ
     18 *    entries contain the identifiers of the owner and owning group.
     19 *    (In POSIX ACLs we always set them to ACL_UNDEFINED_ID).
     20 *  - ACL entries in the kernel are kept sorted in ascending order
     21 *    of (e_tag, e_id). Solaris ACLs are unsorted.
     22 */
     23
     24#include <linux/module.h>
     25#include <linux/fs.h>
     26#include <linux/gfp.h>
     27#include <linux/sunrpc/xdr.h>
     28#include <linux/nfsacl.h>
     29#include <linux/nfs3.h>
     30#include <linux/sort.h>
     31
     32MODULE_LICENSE("GPL");
     33
     34struct nfsacl_encode_desc {
     35	struct xdr_array2_desc desc;
     36	unsigned int count;
     37	struct posix_acl *acl;
     38	int typeflag;
     39	kuid_t uid;
     40	kgid_t gid;
     41};
     42
     43struct nfsacl_simple_acl {
     44	struct posix_acl acl;
     45	struct posix_acl_entry ace[4];
     46};
     47
     48static int
     49xdr_nfsace_encode(struct xdr_array2_desc *desc, void *elem)
     50{
     51	struct nfsacl_encode_desc *nfsacl_desc =
     52		(struct nfsacl_encode_desc *) desc;
     53	__be32 *p = elem;
     54
     55	struct posix_acl_entry *entry =
     56		&nfsacl_desc->acl->a_entries[nfsacl_desc->count++];
     57
     58	*p++ = htonl(entry->e_tag | nfsacl_desc->typeflag);
     59	switch(entry->e_tag) {
     60		case ACL_USER_OBJ:
     61			*p++ = htonl(from_kuid(&init_user_ns, nfsacl_desc->uid));
     62			break;
     63		case ACL_GROUP_OBJ:
     64			*p++ = htonl(from_kgid(&init_user_ns, nfsacl_desc->gid));
     65			break;
     66		case ACL_USER:
     67			*p++ = htonl(from_kuid(&init_user_ns, entry->e_uid));
     68			break;
     69		case ACL_GROUP:
     70			*p++ = htonl(from_kgid(&init_user_ns, entry->e_gid));
     71			break;
     72		default:  /* Solaris depends on that! */
     73			*p++ = 0;
     74			break;
     75	}
     76	*p++ = htonl(entry->e_perm & S_IRWXO);
     77	return 0;
     78}
     79
     80/**
     81 * nfsacl_encode - Encode an NFSv3 ACL
     82 *
     83 * @buf: destination xdr_buf to contain XDR encoded ACL
     84 * @base: byte offset in xdr_buf where XDR'd ACL begins
     85 * @inode: inode of file whose ACL this is
     86 * @acl: posix_acl to encode
     87 * @encode_entries: whether to encode ACEs as well
     88 * @typeflag: ACL type: NFS_ACL_DEFAULT or zero
     89 *
     90 * Returns size of encoded ACL in bytes or a negative errno value.
     91 */
     92int nfsacl_encode(struct xdr_buf *buf, unsigned int base, struct inode *inode,
     93		  struct posix_acl *acl, int encode_entries, int typeflag)
     94{
     95	int entries = (acl && acl->a_count) ? max_t(int, acl->a_count, 4) : 0;
     96	struct nfsacl_encode_desc nfsacl_desc = {
     97		.desc = {
     98			.elem_size = 12,
     99			.array_len = encode_entries ? entries : 0,
    100			.xcode = xdr_nfsace_encode,
    101		},
    102		.acl = acl,
    103		.typeflag = typeflag,
    104		.uid = inode->i_uid,
    105		.gid = inode->i_gid,
    106	};
    107	struct nfsacl_simple_acl aclbuf;
    108	int err;
    109
    110	if (entries > NFS_ACL_MAX_ENTRIES ||
    111	    xdr_encode_word(buf, base, entries))
    112		return -EINVAL;
    113	if (encode_entries && acl && acl->a_count == 3) {
    114		struct posix_acl *acl2 = &aclbuf.acl;
    115
    116		/* Avoid the use of posix_acl_alloc().  nfsacl_encode() is
    117		 * invoked in contexts where a memory allocation failure is
    118		 * fatal.  Fortunately this fake ACL is small enough to
    119		 * construct on the stack. */
    120		posix_acl_init(acl2, 4);
    121
    122		/* Insert entries in canonical order: other orders seem
    123		 to confuse Solaris VxFS. */
    124		acl2->a_entries[0] = acl->a_entries[0];  /* ACL_USER_OBJ */
    125		acl2->a_entries[1] = acl->a_entries[1];  /* ACL_GROUP_OBJ */
    126		acl2->a_entries[2] = acl->a_entries[1];  /* ACL_MASK */
    127		acl2->a_entries[2].e_tag = ACL_MASK;
    128		acl2->a_entries[3] = acl->a_entries[2];  /* ACL_OTHER */
    129		nfsacl_desc.acl = acl2;
    130	}
    131	err = xdr_encode_array2(buf, base + 4, &nfsacl_desc.desc);
    132	if (!err)
    133		err = 8 + nfsacl_desc.desc.elem_size *
    134			  nfsacl_desc.desc.array_len;
    135	return err;
    136}
    137EXPORT_SYMBOL_GPL(nfsacl_encode);
    138
    139/**
    140 * nfs_stream_encode_acl - Encode an NFSv3 ACL
    141 *
    142 * @xdr: an xdr_stream positioned to receive an encoded ACL
    143 * @inode: inode of file whose ACL this is
    144 * @acl: posix_acl to encode
    145 * @encode_entries: whether to encode ACEs as well
    146 * @typeflag: ACL type: NFS_ACL_DEFAULT or zero
    147 *
    148 * Return values:
    149 *   %false: The ACL could not be encoded
    150 *   %true: @xdr is advanced to the next available position
    151 */
    152bool nfs_stream_encode_acl(struct xdr_stream *xdr, struct inode *inode,
    153			   struct posix_acl *acl, int encode_entries,
    154			   int typeflag)
    155{
    156	const size_t elem_size = XDR_UNIT * 3;
    157	u32 entries = (acl && acl->a_count) ? max_t(int, acl->a_count, 4) : 0;
    158	struct nfsacl_encode_desc nfsacl_desc = {
    159		.desc = {
    160			.elem_size = elem_size,
    161			.array_len = encode_entries ? entries : 0,
    162			.xcode = xdr_nfsace_encode,
    163		},
    164		.acl = acl,
    165		.typeflag = typeflag,
    166		.uid = inode->i_uid,
    167		.gid = inode->i_gid,
    168	};
    169	struct nfsacl_simple_acl aclbuf;
    170	unsigned int base;
    171	int err;
    172
    173	if (entries > NFS_ACL_MAX_ENTRIES)
    174		return false;
    175	if (xdr_stream_encode_u32(xdr, entries) < 0)
    176		return false;
    177
    178	if (encode_entries && acl && acl->a_count == 3) {
    179		struct posix_acl *acl2 = &aclbuf.acl;
    180
    181		/* Avoid the use of posix_acl_alloc().  nfsacl_encode() is
    182		 * invoked in contexts where a memory allocation failure is
    183		 * fatal.  Fortunately this fake ACL is small enough to
    184		 * construct on the stack. */
    185		posix_acl_init(acl2, 4);
    186
    187		/* Insert entries in canonical order: other orders seem
    188		 to confuse Solaris VxFS. */
    189		acl2->a_entries[0] = acl->a_entries[0];  /* ACL_USER_OBJ */
    190		acl2->a_entries[1] = acl->a_entries[1];  /* ACL_GROUP_OBJ */
    191		acl2->a_entries[2] = acl->a_entries[1];  /* ACL_MASK */
    192		acl2->a_entries[2].e_tag = ACL_MASK;
    193		acl2->a_entries[3] = acl->a_entries[2];  /* ACL_OTHER */
    194		nfsacl_desc.acl = acl2;
    195	}
    196
    197	base = xdr_stream_pos(xdr);
    198	if (!xdr_reserve_space(xdr, XDR_UNIT +
    199			       elem_size * nfsacl_desc.desc.array_len))
    200		return false;
    201	err = xdr_encode_array2(xdr->buf, base, &nfsacl_desc.desc);
    202	if (err)
    203		return false;
    204
    205	return true;
    206}
    207EXPORT_SYMBOL_GPL(nfs_stream_encode_acl);
    208
    209
    210struct nfsacl_decode_desc {
    211	struct xdr_array2_desc desc;
    212	unsigned int count;
    213	struct posix_acl *acl;
    214};
    215
    216static int
    217xdr_nfsace_decode(struct xdr_array2_desc *desc, void *elem)
    218{
    219	struct nfsacl_decode_desc *nfsacl_desc =
    220		(struct nfsacl_decode_desc *) desc;
    221	__be32 *p = elem;
    222	struct posix_acl_entry *entry;
    223	unsigned int id;
    224
    225	if (!nfsacl_desc->acl) {
    226		if (desc->array_len > NFS_ACL_MAX_ENTRIES)
    227			return -EINVAL;
    228		nfsacl_desc->acl = posix_acl_alloc(desc->array_len, GFP_KERNEL);
    229		if (!nfsacl_desc->acl)
    230			return -ENOMEM;
    231		nfsacl_desc->count = 0;
    232	}
    233
    234	entry = &nfsacl_desc->acl->a_entries[nfsacl_desc->count++];
    235	entry->e_tag = ntohl(*p++) & ~NFS_ACL_DEFAULT;
    236	id = ntohl(*p++);
    237	entry->e_perm = ntohl(*p++);
    238
    239	switch(entry->e_tag) {
    240		case ACL_USER:
    241			entry->e_uid = make_kuid(&init_user_ns, id);
    242			if (!uid_valid(entry->e_uid))
    243				return -EINVAL;
    244			break;
    245		case ACL_GROUP:
    246			entry->e_gid = make_kgid(&init_user_ns, id);
    247			if (!gid_valid(entry->e_gid))
    248				return -EINVAL;
    249			break;
    250		case ACL_USER_OBJ:
    251		case ACL_GROUP_OBJ:
    252		case ACL_OTHER:
    253			if (entry->e_perm & ~S_IRWXO)
    254				return -EINVAL;
    255			break;
    256		case ACL_MASK:
    257			/* Solaris sometimes sets additional bits in the mask */
    258			entry->e_perm &= S_IRWXO;
    259			break;
    260		default:
    261			return -EINVAL;
    262	}
    263
    264	return 0;
    265}
    266
    267static int
    268cmp_acl_entry(const void *x, const void *y)
    269{
    270	const struct posix_acl_entry *a = x, *b = y;
    271
    272	if (a->e_tag != b->e_tag)
    273		return a->e_tag - b->e_tag;
    274	else if ((a->e_tag == ACL_USER) && uid_gt(a->e_uid, b->e_uid))
    275		return 1;
    276	else if ((a->e_tag == ACL_USER) && uid_lt(a->e_uid, b->e_uid))
    277		return -1;
    278	else if ((a->e_tag == ACL_GROUP) && gid_gt(a->e_gid, b->e_gid))
    279		return 1;
    280	else if ((a->e_tag == ACL_GROUP) && gid_lt(a->e_gid, b->e_gid))
    281		return -1;
    282	else
    283		return 0;
    284}
    285
    286/*
    287 * Convert from a Solaris ACL to a POSIX 1003.1e draft 17 ACL.
    288 */
    289static int
    290posix_acl_from_nfsacl(struct posix_acl *acl)
    291{
    292	struct posix_acl_entry *pa, *pe,
    293	       *group_obj = NULL, *mask = NULL;
    294
    295	if (!acl)
    296		return 0;
    297
    298	sort(acl->a_entries, acl->a_count, sizeof(struct posix_acl_entry),
    299	     cmp_acl_entry, NULL);
    300
    301	/* Find the ACL_GROUP_OBJ and ACL_MASK entries. */
    302	FOREACH_ACL_ENTRY(pa, acl, pe) {
    303		switch(pa->e_tag) {
    304			case ACL_USER_OBJ:
    305				break;
    306			case ACL_GROUP_OBJ:
    307				group_obj = pa;
    308				break;
    309			case ACL_MASK:
    310				mask = pa;
    311				fallthrough;
    312			case ACL_OTHER:
    313				break;
    314		}
    315	}
    316	if (acl->a_count == 4 && group_obj && mask &&
    317	    mask->e_perm == group_obj->e_perm) {
    318		/* remove bogus ACL_MASK entry */
    319		memmove(mask, mask+1, (3 - (mask - acl->a_entries)) *
    320				      sizeof(struct posix_acl_entry));
    321		acl->a_count = 3;
    322	}
    323	return 0;
    324}
    325
    326/**
    327 * nfsacl_decode - Decode an NFSv3 ACL
    328 *
    329 * @buf: xdr_buf containing XDR'd ACL data to decode
    330 * @base: byte offset in xdr_buf where XDR'd ACL begins
    331 * @aclcnt: count of ACEs in decoded posix_acl
    332 * @pacl: buffer in which to place decoded posix_acl
    333 *
    334 * Returns the length of the decoded ACL in bytes, or a negative errno value.
    335 */
    336int nfsacl_decode(struct xdr_buf *buf, unsigned int base, unsigned int *aclcnt,
    337		  struct posix_acl **pacl)
    338{
    339	struct nfsacl_decode_desc nfsacl_desc = {
    340		.desc = {
    341			.elem_size = 12,
    342			.xcode = pacl ? xdr_nfsace_decode : NULL,
    343		},
    344	};
    345	u32 entries;
    346	int err;
    347
    348	if (xdr_decode_word(buf, base, &entries) ||
    349	    entries > NFS_ACL_MAX_ENTRIES)
    350		return -EINVAL;
    351	nfsacl_desc.desc.array_maxlen = entries;
    352	err = xdr_decode_array2(buf, base + 4, &nfsacl_desc.desc);
    353	if (err)
    354		return err;
    355	if (pacl) {
    356		if (entries != nfsacl_desc.desc.array_len ||
    357		    posix_acl_from_nfsacl(nfsacl_desc.acl) != 0) {
    358			posix_acl_release(nfsacl_desc.acl);
    359			return -EINVAL;
    360		}
    361		*pacl = nfsacl_desc.acl;
    362	}
    363	if (aclcnt)
    364		*aclcnt = entries;
    365	return 8 + nfsacl_desc.desc.elem_size *
    366		   nfsacl_desc.desc.array_len;
    367}
    368EXPORT_SYMBOL_GPL(nfsacl_decode);
    369
    370/**
    371 * nfs_stream_decode_acl - Decode an NFSv3 ACL
    372 *
    373 * @xdr: an xdr_stream positioned at an encoded ACL
    374 * @aclcnt: OUT: count of ACEs in decoded posix_acl
    375 * @pacl: OUT: a dynamically-allocated buffer containing the decoded posix_acl
    376 *
    377 * Return values:
    378 *   %false: The encoded ACL is not valid
    379 *   %true: @pacl contains a decoded ACL, and @xdr is advanced
    380 *
    381 * On a successful return, caller must release *pacl using posix_acl_release().
    382 */
    383bool nfs_stream_decode_acl(struct xdr_stream *xdr, unsigned int *aclcnt,
    384			   struct posix_acl **pacl)
    385{
    386	const size_t elem_size = XDR_UNIT * 3;
    387	struct nfsacl_decode_desc nfsacl_desc = {
    388		.desc = {
    389			.elem_size = elem_size,
    390			.xcode = pacl ? xdr_nfsace_decode : NULL,
    391		},
    392	};
    393	unsigned int base;
    394	u32 entries;
    395
    396	if (xdr_stream_decode_u32(xdr, &entries) < 0)
    397		return false;
    398	if (entries > NFS_ACL_MAX_ENTRIES)
    399		return false;
    400
    401	base = xdr_stream_pos(xdr);
    402	if (!xdr_inline_decode(xdr, XDR_UNIT + elem_size * entries))
    403		return false;
    404	nfsacl_desc.desc.array_maxlen = entries;
    405	if (xdr_decode_array2(xdr->buf, base, &nfsacl_desc.desc))
    406		return false;
    407
    408	if (pacl) {
    409		if (entries != nfsacl_desc.desc.array_len ||
    410		    posix_acl_from_nfsacl(nfsacl_desc.acl) != 0) {
    411			posix_acl_release(nfsacl_desc.acl);
    412			return false;
    413		}
    414		*pacl = nfsacl_desc.acl;
    415	}
    416	if (aclcnt)
    417		*aclcnt = entries;
    418	return true;
    419}
    420EXPORT_SYMBOL_GPL(nfs_stream_decode_acl);