xref: /kernel/linux/linux-5.10/fs/nfs_common/nfsacl.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * fs/nfs_common/nfsacl.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Copyright (C) 2002-2003 Andreas Gruenbacher <agruen@suse.de>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci/*
98c2ecf20Sopenharmony_ci * The Solaris nfsacl protocol represents some ACLs slightly differently
108c2ecf20Sopenharmony_ci * than POSIX 1003.1e draft 17 does (and we do):
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci *  - Minimal ACLs always have an ACL_MASK entry, so they have
138c2ecf20Sopenharmony_ci *    four instead of three entries.
148c2ecf20Sopenharmony_ci *  - The ACL_MASK entry in such minimal ACLs always has the same
158c2ecf20Sopenharmony_ci *    permissions as the ACL_GROUP_OBJ entry. (In extended ACLs
168c2ecf20Sopenharmony_ci *    the ACL_MASK and ACL_GROUP_OBJ entries may differ.)
178c2ecf20Sopenharmony_ci *  - The identifier fields of the ACL_USER_OBJ and ACL_GROUP_OBJ
188c2ecf20Sopenharmony_ci *    entries contain the identifiers of the owner and owning group.
198c2ecf20Sopenharmony_ci *    (In POSIX ACLs we always set them to ACL_UNDEFINED_ID).
208c2ecf20Sopenharmony_ci *  - ACL entries in the kernel are kept sorted in ascending order
218c2ecf20Sopenharmony_ci *    of (e_tag, e_id). Solaris ACLs are unsorted.
228c2ecf20Sopenharmony_ci */
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#include <linux/module.h>
258c2ecf20Sopenharmony_ci#include <linux/fs.h>
268c2ecf20Sopenharmony_ci#include <linux/gfp.h>
278c2ecf20Sopenharmony_ci#include <linux/sunrpc/xdr.h>
288c2ecf20Sopenharmony_ci#include <linux/nfsacl.h>
298c2ecf20Sopenharmony_ci#include <linux/nfs3.h>
308c2ecf20Sopenharmony_ci#include <linux/sort.h>
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_cistruct nfsacl_encode_desc {
358c2ecf20Sopenharmony_ci	struct xdr_array2_desc desc;
368c2ecf20Sopenharmony_ci	unsigned int count;
378c2ecf20Sopenharmony_ci	struct posix_acl *acl;
388c2ecf20Sopenharmony_ci	int typeflag;
398c2ecf20Sopenharmony_ci	kuid_t uid;
408c2ecf20Sopenharmony_ci	kgid_t gid;
418c2ecf20Sopenharmony_ci};
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_cistruct nfsacl_simple_acl {
448c2ecf20Sopenharmony_ci	struct posix_acl acl;
458c2ecf20Sopenharmony_ci	struct posix_acl_entry ace[4];
468c2ecf20Sopenharmony_ci};
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cistatic int
498c2ecf20Sopenharmony_cixdr_nfsace_encode(struct xdr_array2_desc *desc, void *elem)
508c2ecf20Sopenharmony_ci{
518c2ecf20Sopenharmony_ci	struct nfsacl_encode_desc *nfsacl_desc =
528c2ecf20Sopenharmony_ci		(struct nfsacl_encode_desc *) desc;
538c2ecf20Sopenharmony_ci	__be32 *p = elem;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	struct posix_acl_entry *entry =
568c2ecf20Sopenharmony_ci		&nfsacl_desc->acl->a_entries[nfsacl_desc->count++];
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	*p++ = htonl(entry->e_tag | nfsacl_desc->typeflag);
598c2ecf20Sopenharmony_ci	switch(entry->e_tag) {
608c2ecf20Sopenharmony_ci		case ACL_USER_OBJ:
618c2ecf20Sopenharmony_ci			*p++ = htonl(from_kuid(&init_user_ns, nfsacl_desc->uid));
628c2ecf20Sopenharmony_ci			break;
638c2ecf20Sopenharmony_ci		case ACL_GROUP_OBJ:
648c2ecf20Sopenharmony_ci			*p++ = htonl(from_kgid(&init_user_ns, nfsacl_desc->gid));
658c2ecf20Sopenharmony_ci			break;
668c2ecf20Sopenharmony_ci		case ACL_USER:
678c2ecf20Sopenharmony_ci			*p++ = htonl(from_kuid(&init_user_ns, entry->e_uid));
688c2ecf20Sopenharmony_ci			break;
698c2ecf20Sopenharmony_ci		case ACL_GROUP:
708c2ecf20Sopenharmony_ci			*p++ = htonl(from_kgid(&init_user_ns, entry->e_gid));
718c2ecf20Sopenharmony_ci			break;
728c2ecf20Sopenharmony_ci		default:  /* Solaris depends on that! */
738c2ecf20Sopenharmony_ci			*p++ = 0;
748c2ecf20Sopenharmony_ci			break;
758c2ecf20Sopenharmony_ci	}
768c2ecf20Sopenharmony_ci	*p++ = htonl(entry->e_perm & S_IRWXO);
778c2ecf20Sopenharmony_ci	return 0;
788c2ecf20Sopenharmony_ci}
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci/**
818c2ecf20Sopenharmony_ci * nfsacl_encode - Encode an NFSv3 ACL
828c2ecf20Sopenharmony_ci *
838c2ecf20Sopenharmony_ci * @buf: destination xdr_buf to contain XDR encoded ACL
848c2ecf20Sopenharmony_ci * @base: byte offset in xdr_buf where XDR'd ACL begins
858c2ecf20Sopenharmony_ci * @inode: inode of file whose ACL this is
868c2ecf20Sopenharmony_ci * @acl: posix_acl to encode
878c2ecf20Sopenharmony_ci * @encode_entries: whether to encode ACEs as well
888c2ecf20Sopenharmony_ci * @typeflag: ACL type: NFS_ACL_DEFAULT or zero
898c2ecf20Sopenharmony_ci *
908c2ecf20Sopenharmony_ci * Returns size of encoded ACL in bytes or a negative errno value.
918c2ecf20Sopenharmony_ci */
928c2ecf20Sopenharmony_ciint nfsacl_encode(struct xdr_buf *buf, unsigned int base, struct inode *inode,
938c2ecf20Sopenharmony_ci		  struct posix_acl *acl, int encode_entries, int typeflag)
948c2ecf20Sopenharmony_ci{
958c2ecf20Sopenharmony_ci	int entries = (acl && acl->a_count) ? max_t(int, acl->a_count, 4) : 0;
968c2ecf20Sopenharmony_ci	struct nfsacl_encode_desc nfsacl_desc = {
978c2ecf20Sopenharmony_ci		.desc = {
988c2ecf20Sopenharmony_ci			.elem_size = 12,
998c2ecf20Sopenharmony_ci			.array_len = encode_entries ? entries : 0,
1008c2ecf20Sopenharmony_ci			.xcode = xdr_nfsace_encode,
1018c2ecf20Sopenharmony_ci		},
1028c2ecf20Sopenharmony_ci		.acl = acl,
1038c2ecf20Sopenharmony_ci		.typeflag = typeflag,
1048c2ecf20Sopenharmony_ci		.uid = inode->i_uid,
1058c2ecf20Sopenharmony_ci		.gid = inode->i_gid,
1068c2ecf20Sopenharmony_ci	};
1078c2ecf20Sopenharmony_ci	struct nfsacl_simple_acl aclbuf;
1088c2ecf20Sopenharmony_ci	int err;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	if (entries > NFS_ACL_MAX_ENTRIES ||
1118c2ecf20Sopenharmony_ci	    xdr_encode_word(buf, base, entries))
1128c2ecf20Sopenharmony_ci		return -EINVAL;
1138c2ecf20Sopenharmony_ci	if (encode_entries && acl && acl->a_count == 3) {
1148c2ecf20Sopenharmony_ci		struct posix_acl *acl2 = &aclbuf.acl;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci		/* Avoid the use of posix_acl_alloc().  nfsacl_encode() is
1178c2ecf20Sopenharmony_ci		 * invoked in contexts where a memory allocation failure is
1188c2ecf20Sopenharmony_ci		 * fatal.  Fortunately this fake ACL is small enough to
1198c2ecf20Sopenharmony_ci		 * construct on the stack. */
1208c2ecf20Sopenharmony_ci		posix_acl_init(acl2, 4);
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci		/* Insert entries in canonical order: other orders seem
1238c2ecf20Sopenharmony_ci		 to confuse Solaris VxFS. */
1248c2ecf20Sopenharmony_ci		acl2->a_entries[0] = acl->a_entries[0];  /* ACL_USER_OBJ */
1258c2ecf20Sopenharmony_ci		acl2->a_entries[1] = acl->a_entries[1];  /* ACL_GROUP_OBJ */
1268c2ecf20Sopenharmony_ci		acl2->a_entries[2] = acl->a_entries[1];  /* ACL_MASK */
1278c2ecf20Sopenharmony_ci		acl2->a_entries[2].e_tag = ACL_MASK;
1288c2ecf20Sopenharmony_ci		acl2->a_entries[3] = acl->a_entries[2];  /* ACL_OTHER */
1298c2ecf20Sopenharmony_ci		nfsacl_desc.acl = acl2;
1308c2ecf20Sopenharmony_ci	}
1318c2ecf20Sopenharmony_ci	err = xdr_encode_array2(buf, base + 4, &nfsacl_desc.desc);
1328c2ecf20Sopenharmony_ci	if (!err)
1338c2ecf20Sopenharmony_ci		err = 8 + nfsacl_desc.desc.elem_size *
1348c2ecf20Sopenharmony_ci			  nfsacl_desc.desc.array_len;
1358c2ecf20Sopenharmony_ci	return err;
1368c2ecf20Sopenharmony_ci}
1378c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nfsacl_encode);
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_cistruct nfsacl_decode_desc {
1408c2ecf20Sopenharmony_ci	struct xdr_array2_desc desc;
1418c2ecf20Sopenharmony_ci	unsigned int count;
1428c2ecf20Sopenharmony_ci	struct posix_acl *acl;
1438c2ecf20Sopenharmony_ci};
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_cistatic int
1468c2ecf20Sopenharmony_cixdr_nfsace_decode(struct xdr_array2_desc *desc, void *elem)
1478c2ecf20Sopenharmony_ci{
1488c2ecf20Sopenharmony_ci	struct nfsacl_decode_desc *nfsacl_desc =
1498c2ecf20Sopenharmony_ci		(struct nfsacl_decode_desc *) desc;
1508c2ecf20Sopenharmony_ci	__be32 *p = elem;
1518c2ecf20Sopenharmony_ci	struct posix_acl_entry *entry;
1528c2ecf20Sopenharmony_ci	unsigned int id;
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	if (!nfsacl_desc->acl) {
1558c2ecf20Sopenharmony_ci		if (desc->array_len > NFS_ACL_MAX_ENTRIES)
1568c2ecf20Sopenharmony_ci			return -EINVAL;
1578c2ecf20Sopenharmony_ci		nfsacl_desc->acl = posix_acl_alloc(desc->array_len, GFP_KERNEL);
1588c2ecf20Sopenharmony_ci		if (!nfsacl_desc->acl)
1598c2ecf20Sopenharmony_ci			return -ENOMEM;
1608c2ecf20Sopenharmony_ci		nfsacl_desc->count = 0;
1618c2ecf20Sopenharmony_ci	}
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	entry = &nfsacl_desc->acl->a_entries[nfsacl_desc->count++];
1648c2ecf20Sopenharmony_ci	entry->e_tag = ntohl(*p++) & ~NFS_ACL_DEFAULT;
1658c2ecf20Sopenharmony_ci	id = ntohl(*p++);
1668c2ecf20Sopenharmony_ci	entry->e_perm = ntohl(*p++);
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	switch(entry->e_tag) {
1698c2ecf20Sopenharmony_ci		case ACL_USER:
1708c2ecf20Sopenharmony_ci			entry->e_uid = make_kuid(&init_user_ns, id);
1718c2ecf20Sopenharmony_ci			if (!uid_valid(entry->e_uid))
1728c2ecf20Sopenharmony_ci				return -EINVAL;
1738c2ecf20Sopenharmony_ci			break;
1748c2ecf20Sopenharmony_ci		case ACL_GROUP:
1758c2ecf20Sopenharmony_ci			entry->e_gid = make_kgid(&init_user_ns, id);
1768c2ecf20Sopenharmony_ci			if (!gid_valid(entry->e_gid))
1778c2ecf20Sopenharmony_ci				return -EINVAL;
1788c2ecf20Sopenharmony_ci			break;
1798c2ecf20Sopenharmony_ci		case ACL_USER_OBJ:
1808c2ecf20Sopenharmony_ci		case ACL_GROUP_OBJ:
1818c2ecf20Sopenharmony_ci		case ACL_OTHER:
1828c2ecf20Sopenharmony_ci			if (entry->e_perm & ~S_IRWXO)
1838c2ecf20Sopenharmony_ci				return -EINVAL;
1848c2ecf20Sopenharmony_ci			break;
1858c2ecf20Sopenharmony_ci		case ACL_MASK:
1868c2ecf20Sopenharmony_ci			/* Solaris sometimes sets additional bits in the mask */
1878c2ecf20Sopenharmony_ci			entry->e_perm &= S_IRWXO;
1888c2ecf20Sopenharmony_ci			break;
1898c2ecf20Sopenharmony_ci		default:
1908c2ecf20Sopenharmony_ci			return -EINVAL;
1918c2ecf20Sopenharmony_ci	}
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	return 0;
1948c2ecf20Sopenharmony_ci}
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_cistatic int
1978c2ecf20Sopenharmony_cicmp_acl_entry(const void *x, const void *y)
1988c2ecf20Sopenharmony_ci{
1998c2ecf20Sopenharmony_ci	const struct posix_acl_entry *a = x, *b = y;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	if (a->e_tag != b->e_tag)
2028c2ecf20Sopenharmony_ci		return a->e_tag - b->e_tag;
2038c2ecf20Sopenharmony_ci	else if ((a->e_tag == ACL_USER) && uid_gt(a->e_uid, b->e_uid))
2048c2ecf20Sopenharmony_ci		return 1;
2058c2ecf20Sopenharmony_ci	else if ((a->e_tag == ACL_USER) && uid_lt(a->e_uid, b->e_uid))
2068c2ecf20Sopenharmony_ci		return -1;
2078c2ecf20Sopenharmony_ci	else if ((a->e_tag == ACL_GROUP) && gid_gt(a->e_gid, b->e_gid))
2088c2ecf20Sopenharmony_ci		return 1;
2098c2ecf20Sopenharmony_ci	else if ((a->e_tag == ACL_GROUP) && gid_lt(a->e_gid, b->e_gid))
2108c2ecf20Sopenharmony_ci		return -1;
2118c2ecf20Sopenharmony_ci	else
2128c2ecf20Sopenharmony_ci		return 0;
2138c2ecf20Sopenharmony_ci}
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci/*
2168c2ecf20Sopenharmony_ci * Convert from a Solaris ACL to a POSIX 1003.1e draft 17 ACL.
2178c2ecf20Sopenharmony_ci */
2188c2ecf20Sopenharmony_cistatic int
2198c2ecf20Sopenharmony_ciposix_acl_from_nfsacl(struct posix_acl *acl)
2208c2ecf20Sopenharmony_ci{
2218c2ecf20Sopenharmony_ci	struct posix_acl_entry *pa, *pe,
2228c2ecf20Sopenharmony_ci	       *group_obj = NULL, *mask = NULL;
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	if (!acl)
2258c2ecf20Sopenharmony_ci		return 0;
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	sort(acl->a_entries, acl->a_count, sizeof(struct posix_acl_entry),
2288c2ecf20Sopenharmony_ci	     cmp_acl_entry, NULL);
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	/* Find the ACL_GROUP_OBJ and ACL_MASK entries. */
2318c2ecf20Sopenharmony_ci	FOREACH_ACL_ENTRY(pa, acl, pe) {
2328c2ecf20Sopenharmony_ci		switch(pa->e_tag) {
2338c2ecf20Sopenharmony_ci			case ACL_USER_OBJ:
2348c2ecf20Sopenharmony_ci				break;
2358c2ecf20Sopenharmony_ci			case ACL_GROUP_OBJ:
2368c2ecf20Sopenharmony_ci				group_obj = pa;
2378c2ecf20Sopenharmony_ci				break;
2388c2ecf20Sopenharmony_ci			case ACL_MASK:
2398c2ecf20Sopenharmony_ci				mask = pa;
2408c2ecf20Sopenharmony_ci				fallthrough;
2418c2ecf20Sopenharmony_ci			case ACL_OTHER:
2428c2ecf20Sopenharmony_ci				break;
2438c2ecf20Sopenharmony_ci		}
2448c2ecf20Sopenharmony_ci	}
2458c2ecf20Sopenharmony_ci	if (acl->a_count == 4 && group_obj && mask &&
2468c2ecf20Sopenharmony_ci	    mask->e_perm == group_obj->e_perm) {
2478c2ecf20Sopenharmony_ci		/* remove bogus ACL_MASK entry */
2488c2ecf20Sopenharmony_ci		memmove(mask, mask+1, (3 - (mask - acl->a_entries)) *
2498c2ecf20Sopenharmony_ci				      sizeof(struct posix_acl_entry));
2508c2ecf20Sopenharmony_ci		acl->a_count = 3;
2518c2ecf20Sopenharmony_ci	}
2528c2ecf20Sopenharmony_ci	return 0;
2538c2ecf20Sopenharmony_ci}
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci/**
2568c2ecf20Sopenharmony_ci * nfsacl_decode - Decode an NFSv3 ACL
2578c2ecf20Sopenharmony_ci *
2588c2ecf20Sopenharmony_ci * @buf: xdr_buf containing XDR'd ACL data to decode
2598c2ecf20Sopenharmony_ci * @base: byte offset in xdr_buf where XDR'd ACL begins
2608c2ecf20Sopenharmony_ci * @aclcnt: count of ACEs in decoded posix_acl
2618c2ecf20Sopenharmony_ci * @pacl: buffer in which to place decoded posix_acl
2628c2ecf20Sopenharmony_ci *
2638c2ecf20Sopenharmony_ci * Returns the length of the decoded ACL in bytes, or a negative errno value.
2648c2ecf20Sopenharmony_ci */
2658c2ecf20Sopenharmony_ciint nfsacl_decode(struct xdr_buf *buf, unsigned int base, unsigned int *aclcnt,
2668c2ecf20Sopenharmony_ci		  struct posix_acl **pacl)
2678c2ecf20Sopenharmony_ci{
2688c2ecf20Sopenharmony_ci	struct nfsacl_decode_desc nfsacl_desc = {
2698c2ecf20Sopenharmony_ci		.desc = {
2708c2ecf20Sopenharmony_ci			.elem_size = 12,
2718c2ecf20Sopenharmony_ci			.xcode = pacl ? xdr_nfsace_decode : NULL,
2728c2ecf20Sopenharmony_ci		},
2738c2ecf20Sopenharmony_ci	};
2748c2ecf20Sopenharmony_ci	u32 entries;
2758c2ecf20Sopenharmony_ci	int err;
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	if (xdr_decode_word(buf, base, &entries) ||
2788c2ecf20Sopenharmony_ci	    entries > NFS_ACL_MAX_ENTRIES)
2798c2ecf20Sopenharmony_ci		return -EINVAL;
2808c2ecf20Sopenharmony_ci	nfsacl_desc.desc.array_maxlen = entries;
2818c2ecf20Sopenharmony_ci	err = xdr_decode_array2(buf, base + 4, &nfsacl_desc.desc);
2828c2ecf20Sopenharmony_ci	if (err)
2838c2ecf20Sopenharmony_ci		return err;
2848c2ecf20Sopenharmony_ci	if (pacl) {
2858c2ecf20Sopenharmony_ci		if (entries != nfsacl_desc.desc.array_len ||
2868c2ecf20Sopenharmony_ci		    posix_acl_from_nfsacl(nfsacl_desc.acl) != 0) {
2878c2ecf20Sopenharmony_ci			posix_acl_release(nfsacl_desc.acl);
2888c2ecf20Sopenharmony_ci			return -EINVAL;
2898c2ecf20Sopenharmony_ci		}
2908c2ecf20Sopenharmony_ci		*pacl = nfsacl_desc.acl;
2918c2ecf20Sopenharmony_ci	}
2928c2ecf20Sopenharmony_ci	if (aclcnt)
2938c2ecf20Sopenharmony_ci		*aclcnt = entries;
2948c2ecf20Sopenharmony_ci	return 8 + nfsacl_desc.desc.elem_size *
2958c2ecf20Sopenharmony_ci		   nfsacl_desc.desc.array_len;
2968c2ecf20Sopenharmony_ci}
2978c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nfsacl_decode);
298