1/*-
2 * Copyright (c) 2017, Fedor Uporov
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 * $FreeBSD: stable/11/sys/fs/ext2fs/ext2_acl.c 322711 2017-08-20 01:08:23Z pfg $
27 */
28
29#include <sys/param.h>
30#include <sys/systm.h>
31#include <sys/types.h>
32#include <sys/stat.h>
33#include <sys/kernel.h>
34#include <sys/malloc.h>
35#include <sys/vnode.h>
36#include <sys/bio.h>
37#include <sys/buf.h>
38#include <sys/endian.h>
39#include <sys/conf.h>
40#include <sys/mount.h>
41#include <sys/extattr.h>
42
43#include <fs/ext2fs/fs.h>
44#include <fs/ext2fs/ext2fs.h>
45#include <fs/ext2fs/inode.h>
46#include <fs/ext2fs/ext2_acl.h>
47#include <fs/ext2fs/ext2_extattr.h>
48#include <fs/ext2fs/ext2_extern.h>
49#include <fs/ext2fs/ext2_dinode.h>
50#include <fs/ext2fs/ext2_mount.h>
51
52#ifdef UFS_ACL
53
54void
55ext2_sync_acl_from_inode(struct inode *ip, struct acl *acl)
56{
57	struct acl_entry	*acl_mask, *acl_group_obj;
58	int	i;
59
60	/*
61	 * Update ACL_USER_OBJ, ACL_OTHER, but simply identify ACL_MASK
62	 * and ACL_GROUP_OBJ for use after we know whether ACL_MASK is
63	 * present.
64	 */
65	acl_mask = NULL;
66	acl_group_obj = NULL;
67	for (i = 0; i < acl->acl_cnt; i++) {
68		switch (acl->acl_entry[i].ae_tag) {
69		case ACL_USER_OBJ:
70			acl->acl_entry[i].ae_perm = acl_posix1e_mode_to_perm(
71			    ACL_USER_OBJ, ip->i_mode);
72			acl->acl_entry[i].ae_id = ACL_UNDEFINED_ID;
73			break;
74
75		case ACL_GROUP_OBJ:
76			acl_group_obj = &acl->acl_entry[i];
77			acl->acl_entry[i].ae_id = ACL_UNDEFINED_ID;
78			break;
79
80		case ACL_OTHER:
81			acl->acl_entry[i].ae_perm = acl_posix1e_mode_to_perm(
82			    ACL_OTHER, ip->i_mode);
83			acl->acl_entry[i].ae_id = ACL_UNDEFINED_ID;
84			break;
85
86		case ACL_MASK:
87			acl_mask = &acl->acl_entry[i];
88			acl->acl_entry[i].ae_id = ACL_UNDEFINED_ID;
89			break;
90
91		case ACL_USER:
92		case ACL_GROUP:
93			break;
94
95		default:
96			panic("ext2_sync_acl_from_inode(): bad ae_tag");
97		}
98	}
99
100	if (acl_group_obj == NULL)
101		panic("ext2_sync_acl_from_inode(): no ACL_GROUP_OBJ");
102
103	if (acl_mask == NULL) {
104		/*
105		 * There is no ACL_MASK, so update ACL_GROUP_OBJ.
106		 */
107		acl_group_obj->ae_perm = acl_posix1e_mode_to_perm(
108		    ACL_GROUP_OBJ, ip->i_mode);
109	} else {
110		/*
111		 * Update the ACL_MASK entry instead of ACL_GROUP_OBJ.
112		 */
113		acl_mask->ae_perm = acl_posix1e_mode_to_perm(ACL_GROUP_OBJ,
114		    ip->i_mode);
115	}
116}
117
118static void
119ext2_sync_inode_from_acl(struct acl *acl, struct inode *ip)
120{
121
122	ip->i_mode &= ACL_PRESERVE_MASK;
123	ip->i_mode |= acl_posix1e_acl_to_mode(acl);
124}
125
126/*
127 * Convert from filesystem to in-memory representation.
128 */
129static int
130ext4_acl_from_disk(char *value, size_t size, struct acl *acl)
131{
132	const char *end;
133	int n, count, s;
134
135	if (value == NULL)
136		return (EINVAL);
137
138	end = value + size;
139
140	if (((struct ext2_acl_header *)value)->a_version != EXT4_ACL_VERSION)
141		return (EINVAL);
142
143	if (size < sizeof(struct ext2_acl_header))
144		return (EINVAL);
145
146	s = size - sizeof(struct ext2_acl_header);
147	s -= 4 * sizeof(struct ext2_acl_entry_short);
148	if (s < 0)
149		if ((size - sizeof(struct ext2_acl_header)) %
150		    sizeof(struct ext2_acl_entry_short))
151			count = -1;
152		else
153			count = (size - sizeof(struct ext2_acl_header)) /
154			    sizeof(struct ext2_acl_entry_short);
155	else
156		if (s % sizeof(struct ext2_acl_entry))
157			count = -1;
158		else
159			count = s / sizeof(struct ext2_acl_entry) + 4;
160
161	if (count <= 0 || count > acl->acl_maxcnt)
162		return (EINVAL);
163
164	value = value + sizeof(struct ext2_acl_header);
165
166	for (n = 0; n < count; n++) {
167		struct ext2_acl_entry *entry = (struct ext2_acl_entry *)value;
168		if ((char *)value + sizeof(struct ext2_acl_entry_short) > end)
169			return (EINVAL);
170
171		acl->acl_entry[n].ae_tag  = entry->ae_tag;
172		acl->acl_entry[n].ae_perm = entry->ae_perm;
173
174		switch (acl->acl_entry[n].ae_tag) {
175		case ACL_USER_OBJ:
176		case ACL_GROUP_OBJ:
177		case ACL_MASK:
178		case ACL_OTHER:
179			value = (char *)value + sizeof(struct ext2_acl_entry_short);
180			break;
181
182		case ACL_USER:
183			value = (char *)value + sizeof(struct ext2_acl_entry);
184			if ((char *)value > end)
185				return (EINVAL);
186
187			acl->acl_entry[n].ae_id = entry->ae_id;
188			break;
189
190		case ACL_GROUP:
191			value = (char *)value + sizeof(struct ext2_acl_entry);
192			if ((char *)value > end)
193				return (EINVAL);
194
195			acl->acl_entry[n].ae_id = entry->ae_id;
196			break;
197
198		default:
199			return (EINVAL);
200		}
201	}
202
203	if (value != end)
204		return (EINVAL);
205
206	acl->acl_cnt = count;
207
208	return (0);
209}
210
211static int
212ext2_getacl_posix1e(struct vop_getacl_args *ap)
213{
214	int attrnamespace;
215	const char *attrname;
216	char *value;
217	int len;
218	int error;
219
220	switch (ap->a_type) {
221	case ACL_TYPE_DEFAULT:
222		attrnamespace = POSIX1E_ACL_DEFAULT_EXTATTR_NAMESPACE;
223		attrname = POSIX1E_ACL_DEFAULT_EXTATTR_NAME;
224		break;
225	case ACL_TYPE_ACCESS:
226		attrnamespace = POSIX1E_ACL_ACCESS_EXTATTR_NAMESPACE;
227		attrname = POSIX1E_ACL_ACCESS_EXTATTR_NAME;
228		break;
229	default:
230		return (EINVAL);
231	}
232
233	len = sizeof(*ap->a_aclp) + sizeof(struct ext2_acl_header);
234	value = malloc(len, M_ACL, M_WAITOK);
235	if (!value)
236		return (ENOMEM);
237
238	error = vn_extattr_get(ap->a_vp, IO_NODELOCKED, attrnamespace, attrname,
239	    &len, value, ap->a_td);
240	if (error == ENOATTR) {
241		switch (ap->a_type) {
242		case ACL_TYPE_ACCESS:
243			ap->a_aclp->acl_cnt = 3;
244			ap->a_aclp->acl_entry[0].ae_tag = ACL_USER_OBJ;
245			ap->a_aclp->acl_entry[0].ae_id = ACL_UNDEFINED_ID;
246			ap->a_aclp->acl_entry[0].ae_perm = ACL_PERM_NONE;
247			ap->a_aclp->acl_entry[1].ae_tag = ACL_GROUP_OBJ;
248			ap->a_aclp->acl_entry[1].ae_id = ACL_UNDEFINED_ID;
249			ap->a_aclp->acl_entry[1].ae_perm = ACL_PERM_NONE;
250			ap->a_aclp->acl_entry[2].ae_tag = ACL_OTHER;
251			ap->a_aclp->acl_entry[2].ae_id = ACL_UNDEFINED_ID;
252			ap->a_aclp->acl_entry[2].ae_perm = ACL_PERM_NONE;
253			break;
254
255		case ACL_TYPE_DEFAULT:
256			ap->a_aclp->acl_cnt = 0;
257			break;
258		}
259	} else if (error != 0)
260		goto out;
261
262	if (!error) {
263		error = ext4_acl_from_disk(value, len, ap->a_aclp);
264		if (error)
265			goto out;
266	}
267
268	if (error == ENOATTR)
269		error = 0;
270
271	if (ap->a_type == ACL_TYPE_ACCESS)
272		ext2_sync_acl_from_inode(VTOI(ap->a_vp), ap->a_aclp);
273
274out:
275	free(value, M_TEMP);
276	return (error);
277}
278
279int
280ext2_getacl(struct vop_getacl_args *ap)
281{
282
283	if (((ap->a_vp->v_mount->mnt_flag & MNT_ACLS) == 0) ||
284	    ((ap->a_vp->v_mount->mnt_flag & MNT_NFS4ACLS) != 0))
285		return (EOPNOTSUPP);
286
287	if (ap->a_type == ACL_TYPE_NFS4)
288		return (ENOTSUP);
289
290	return (ext2_getacl_posix1e(ap));
291}
292
293/*
294 * Convert from in-memory to filesystem representation.
295 */
296static int
297ext4_acl_to_disk(const struct acl *acl, size_t *size, char *value)
298{
299	struct ext2_acl_header *ext_acl;
300	int disk_size;
301	char *e;
302	size_t n;
303
304	if (acl->acl_cnt <= 4)
305		disk_size = sizeof(struct ext2_acl_header) +
306		   acl->acl_cnt * sizeof(struct ext2_acl_entry_short);
307	else
308		disk_size = sizeof(struct ext2_acl_header) +
309		    4 * sizeof(struct ext2_acl_entry_short) +
310		    (acl->acl_cnt - 4) * sizeof(struct ext2_acl_entry);
311
312	if (disk_size > *size)
313		return (EINVAL);
314
315	*size = disk_size;
316	ext_acl = (struct ext2_acl_header *)value;
317
318	ext_acl->a_version = EXT4_ACL_VERSION;
319	e = (char *)ext_acl + sizeof(struct ext2_acl_header);
320	for (n = 0; n < acl->acl_cnt; n++) {
321		const struct acl_entry *acl_e = &acl->acl_entry[n];
322		struct ext2_acl_entry *entry = (struct ext2_acl_entry *)e;
323		entry->ae_tag  = acl_e->ae_tag;
324		entry->ae_perm = acl_e->ae_perm;
325		switch (acl_e->ae_tag) {
326		case ACL_USER:
327			entry->ae_id = acl_e->ae_id;
328			e += sizeof(struct ext2_acl_entry);
329			break;
330
331		case ACL_GROUP:
332			entry->ae_id = acl_e->ae_id;
333			e += sizeof(struct ext2_acl_entry);
334			break;
335
336		case ACL_USER_OBJ:
337		case ACL_GROUP_OBJ:
338		case ACL_MASK:
339		case ACL_OTHER:
340			e += sizeof(struct ext2_acl_entry_short);
341			break;
342
343		default:
344			return (EINVAL);
345		}
346	}
347
348	return (0);
349}
350
351static int
352ext2_setacl_posix1e(struct vop_setacl_args *ap)
353{
354	struct inode *ip = VTOI(ap->a_vp);
355	char *value;
356	size_t len;
357	int error;
358
359	if ((ap->a_vp->v_mount->mnt_flag & MNT_ACLS) == 0)
360		return (EINVAL);
361
362	/*
363	 * If this is a set operation rather than a delete operation,
364	 * invoke VOP_ACLCHECK() on the passed ACL to determine if it is
365	 * valid for the target.  This will include a check on ap->a_type.
366	 */
367	if (ap->a_aclp != NULL) {
368		/*
369		 * Set operation.
370		 */
371		error = VOP_ACLCHECK(ap->a_vp, ap->a_type, ap->a_aclp,
372		    ap->a_cred, ap->a_td);
373		if (error)
374			return (error);
375	} else {
376		/*
377		 * Delete operation.
378		 * POSIX.1e allows only deletion of the default ACL on a
379		 * directory (ACL_TYPE_DEFAULT).
380		 */
381		if (ap->a_type != ACL_TYPE_DEFAULT)
382			return (EINVAL);
383		if (ap->a_vp->v_type != VDIR)
384			return (ENOTDIR);
385	}
386
387	if (ap->a_vp->v_mount->mnt_flag & MNT_RDONLY)
388		return (EROFS);
389
390	/*
391	 * Authorize the ACL operation.
392	 */
393	if (ip->i_flags & (IMMUTABLE | APPEND))
394		return (EPERM);
395
396	/*
397	 * Must hold VADMIN (be file owner) or have appropriate privilege.
398	 */
399	if ((error = VOP_ACCESS(ap->a_vp, VADMIN, ap->a_cred, ap->a_td)))
400		return (error);
401
402	switch (ap->a_type) {
403	case ACL_TYPE_ACCESS:
404		len = sizeof(*ap->a_aclp) + sizeof(struct ext2_acl_header);
405		value = malloc(len, M_ACL, M_WAITOK | M_ZERO);
406		error = ext4_acl_to_disk(ap->a_aclp, &len, value);
407		if (error == 0)
408			error = vn_extattr_set(ap->a_vp, IO_NODELOCKED,
409			    POSIX1E_ACL_ACCESS_EXTATTR_NAMESPACE,
410			    POSIX1E_ACL_ACCESS_EXTATTR_NAME, len,
411			    value, ap->a_td);
412
413		free(value, M_ACL);
414		break;
415
416	case ACL_TYPE_DEFAULT:
417		if (ap->a_aclp == NULL) {
418			error = vn_extattr_rm(ap->a_vp, IO_NODELOCKED,
419			    POSIX1E_ACL_DEFAULT_EXTATTR_NAMESPACE,
420			    POSIX1E_ACL_DEFAULT_EXTATTR_NAME, ap->a_td);
421
422			/*
423			 * Attempting to delete a non-present default ACL
424			 * will return success for portability purposes.
425			 * (TRIX)
426			 *
427			 * XXX: Note that since we can't distinguish
428			 * "that EA is not supported" from "that EA is not
429			 * defined", the success case here overlaps the
430			 * the ENOATTR->EOPNOTSUPP case below.
431			 */
432			if (error == ENOATTR)
433				error = 0;
434		} else {
435			len = sizeof(*ap->a_aclp) + sizeof(struct ext2_acl_header);
436			value = malloc(len, M_ACL, M_WAITOK | M_ZERO);
437			error = ext4_acl_to_disk(ap->a_aclp, &len, value);
438			if (error == 0)
439				error = vn_extattr_set(ap->a_vp, IO_NODELOCKED,
440				    POSIX1E_ACL_DEFAULT_EXTATTR_NAMESPACE,
441				    POSIX1E_ACL_DEFAULT_EXTATTR_NAME, len,
442				    value, ap->a_td);
443
444			free(value, M_ACL);
445		}
446		break;
447
448	default:
449		error = EINVAL;
450	}
451
452	/*
453	 * Map lack of attribute definition in UFS_EXTATTR into lack of
454	 * support for ACLs on the filesystem.
455	 */
456	if (error == ENOATTR)
457		return (EOPNOTSUPP);
458
459	if (error != 0)
460		return (error);
461
462	if (ap->a_type == ACL_TYPE_ACCESS) {
463		/*
464		 * Now that the EA is successfully updated, update the
465		 * inode and mark it as changed.
466		 */
467		ext2_sync_inode_from_acl(ap->a_aclp, ip);
468		ip->i_flag |= IN_CHANGE;
469		error = ext2_update(ip->i_vnode, 1);
470	}
471
472	VN_KNOTE_UNLOCKED(ap->a_vp, NOTE_ATTRIB);
473
474	return (error);
475}
476
477int
478ext2_setacl(struct vop_setacl_args *ap)
479{
480	if (((ap->a_vp->v_mount->mnt_flag & MNT_ACLS) == 0) ||
481	    ((ap->a_vp->v_mount->mnt_flag & MNT_NFS4ACLS) != 0))
482		return (EOPNOTSUPP);
483
484	if (ap->a_type == ACL_TYPE_NFS4)
485		return (ENOTSUP);
486
487	return (ext2_setacl_posix1e(ap));
488}
489
490/*
491 * Check the validity of an ACL for a file.
492 */
493int
494ext2_aclcheck(struct vop_aclcheck_args *ap)
495{
496
497	if (((ap->a_vp->v_mount->mnt_flag & MNT_ACLS) == 0) ||
498	    ((ap->a_vp->v_mount->mnt_flag & MNT_NFS4ACLS) != 0))
499		return (EOPNOTSUPP);
500
501	if (ap->a_type == ACL_TYPE_NFS4)
502		return (ENOTSUP);
503
504	if ((ap->a_vp->v_mount->mnt_flag & MNT_ACLS) == 0)
505		return (EINVAL);
506
507	/*
508	 * Verify we understand this type of ACL, and that it applies
509	 * to this kind of object.
510	 * Rely on the acl_posix1e_check() routine to verify the contents.
511	 */
512	switch (ap->a_type) {
513		case ACL_TYPE_ACCESS:
514		break;
515
516		case ACL_TYPE_DEFAULT:
517			if (ap->a_vp->v_type != VDIR)
518				return (EINVAL);
519		break;
520
521		default:
522			return (EINVAL);
523	}
524
525	return (acl_posix1e_check(ap->a_aclp));
526}
527
528#endif /* UFS_ACL */
529