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