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);
421