Merge tag 'media/v6.12-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab...
[linux-2.6-block.git] / fs / nfs_common / nfsacl.c
CommitLineData
09c434b8 1// SPDX-License-Identifier: GPL-2.0-only
a257cdd0
AG
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>
5a0e3ad6 26#include <linux/gfp.h>
a257cdd0
AG
27#include <linux/sunrpc/xdr.h>
28#include <linux/nfsacl.h>
29#include <linux/nfs3.h>
30#include <linux/sort.h>
31
d3318990 32MODULE_DESCRIPTION("NFS ACL support");
a257cdd0
AG
33MODULE_LICENSE("GPL");
34
a257cdd0
AG
35struct nfsacl_encode_desc {
36 struct xdr_array2_desc desc;
37 unsigned int count;
38 struct posix_acl *acl;
39 int typeflag;
ddca4e17
EB
40 kuid_t uid;
41 kgid_t gid;
a257cdd0
AG
42};
43
f61f6da0
CL
44struct nfsacl_simple_acl {
45 struct posix_acl acl;
46 struct posix_acl_entry ace[4];
47};
48
a257cdd0
AG
49static int
50xdr_nfsace_encode(struct xdr_array2_desc *desc, void *elem)
51{
52 struct nfsacl_encode_desc *nfsacl_desc =
53 (struct nfsacl_encode_desc *) desc;
83bbe2ef 54 __be32 *p = elem;
a257cdd0 55
22c1ea44
AG
56 struct posix_acl_entry *entry =
57 &nfsacl_desc->acl->a_entries[nfsacl_desc->count++];
a257cdd0 58
22c1ea44
AG
59 *p++ = htonl(entry->e_tag | nfsacl_desc->typeflag);
60 switch(entry->e_tag) {
61 case ACL_USER_OBJ:
ddca4e17 62 *p++ = htonl(from_kuid(&init_user_ns, nfsacl_desc->uid));
22c1ea44
AG
63 break;
64 case ACL_GROUP_OBJ:
ddca4e17 65 *p++ = htonl(from_kgid(&init_user_ns, nfsacl_desc->gid));
22c1ea44
AG
66 break;
67 case ACL_USER:
ddca4e17
EB
68 *p++ = htonl(from_kuid(&init_user_ns, entry->e_uid));
69 break;
22c1ea44 70 case ACL_GROUP:
ddca4e17 71 *p++ = htonl(from_kgid(&init_user_ns, entry->e_gid));
22c1ea44
AG
72 break;
73 default: /* Solaris depends on that! */
74 *p++ = 0;
75 break;
a257cdd0 76 }
22c1ea44 77 *p++ = htonl(entry->e_perm & S_IRWXO);
a257cdd0
AG
78 return 0;
79}
80
731f3f48
CL
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 */
93int nfsacl_encode(struct xdr_buf *buf, unsigned int base, struct inode *inode,
94 struct posix_acl *acl, int encode_entries, int typeflag)
a257cdd0
AG
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 };
f61f6da0 108 struct nfsacl_simple_acl aclbuf;
a257cdd0
AG
109 int err;
110
111 if (entries > NFS_ACL_MAX_ENTRIES ||
112 xdr_encode_word(buf, base, entries))
113 return -EINVAL;
22c1ea44 114 if (encode_entries && acl && acl->a_count == 3) {
f61f6da0
CL
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. */
f61f6da0
CL
121 posix_acl_init(acl2, 4);
122
22c1ea44
AG
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 }
a257cdd0
AG
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}
57b696fb 138EXPORT_SYMBOL_GPL(nfsacl_encode);
a257cdd0 139
8edc0648
CL
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 */
153bool 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}
208EXPORT_SYMBOL_GPL(nfs_stream_encode_acl);
209
210
a257cdd0
AG
211struct nfsacl_decode_desc {
212 struct xdr_array2_desc desc;
213 unsigned int count;
214 struct posix_acl *acl;
215};
216
217static int
218xdr_nfsace_decode(struct xdr_array2_desc *desc, void *elem)
219{
220 struct nfsacl_decode_desc *nfsacl_desc =
221 (struct nfsacl_decode_desc *) desc;
83bbe2ef 222 __be32 *p = elem;
a257cdd0 223 struct posix_acl_entry *entry;
ddca4e17 224 unsigned int id;
a257cdd0
AG
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;
ddca4e17 237 id = ntohl(*p++);
a257cdd0
AG
238 entry->e_perm = ntohl(*p++);
239
240 switch(entry->e_tag) {
a257cdd0 241 case ACL_USER:
ddca4e17
EB
242 entry->e_uid = make_kuid(&init_user_ns, id);
243 if (!uid_valid(entry->e_uid))
244 return -EINVAL;
245 break;
a257cdd0 246 case ACL_GROUP:
ddca4e17
EB
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:
a257cdd0
AG
253 case ACL_OTHER:
254 if (entry->e_perm & ~S_IRWXO)
255 return -EINVAL;
256 break;
257 case ACL_MASK:
25985edc 258 /* Solaris sometimes sets additional bits in the mask */
a257cdd0
AG
259 entry->e_perm &= S_IRWXO;
260 break;
261 default:
262 return -EINVAL;
263 }
264
265 return 0;
266}
267
268static int
269cmp_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;
ddca4e17
EB
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))
a257cdd0 280 return 1;
ddca4e17 281 else if ((a->e_tag == ACL_GROUP) && gid_lt(a->e_gid, b->e_gid))
a257cdd0
AG
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 */
290static int
291posix_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
ddca4e17 302 /* Find the ACL_GROUP_OBJ and ACL_MASK entries. */
a257cdd0
AG
303 FOREACH_ACL_ENTRY(pa, acl, pe) {
304 switch(pa->e_tag) {
305 case ACL_USER_OBJ:
a257cdd0
AG
306 break;
307 case ACL_GROUP_OBJ:
a257cdd0
AG
308 group_obj = pa;
309 break;
310 case ACL_MASK:
311 mask = pa;
df561f66 312 fallthrough;
a257cdd0 313 case ACL_OTHER:
a257cdd0
AG
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
731f3f48
CL
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 */
337int nfsacl_decode(struct xdr_buf *buf, unsigned int base, unsigned int *aclcnt,
338 struct posix_acl **pacl)
a257cdd0
AG
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;
58fcb8df 352 nfsacl_desc.desc.array_maxlen = entries;
a257cdd0
AG
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}
57b696fb 369EXPORT_SYMBOL_GPL(nfsacl_decode);
6bb844b4
CL
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 */
384bool 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}
421EXPORT_SYMBOL_GPL(nfs_stream_decode_acl);