xref: /onnv-gate/usr/src/common/acl/acl_common.c (revision 12164)
1789Sahrens /*
2789Sahrens  * CDDL HEADER START
3789Sahrens  *
4789Sahrens  * The contents of this file are subject to the terms of the
54321Scasper  * Common Development and Distribution License (the "License").
64321Scasper  * You may not use this file except in compliance with the License.
7789Sahrens  *
8789Sahrens  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9789Sahrens  * or http://www.opensolaris.org/os/licensing.
10789Sahrens  * See the License for the specific language governing permissions
11789Sahrens  * and limitations under the License.
12789Sahrens  *
13789Sahrens  * When distributing Covered Code, include this CDDL HEADER in each
14789Sahrens  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15789Sahrens  * If applicable, add the following below this CDDL HEADER, with the
16789Sahrens  * fields enclosed by brackets "[]" replaced with your own identifying
17789Sahrens  * information: Portions Copyright [yyyy] [name of copyright owner]
18789Sahrens  *
19789Sahrens  * CDDL HEADER END
20789Sahrens  */
21789Sahrens /*
22*12164SMark.Shellenbaum@Sun.COM  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
23789Sahrens  */
24789Sahrens 
25789Sahrens #include <sys/types.h>
26789Sahrens #include <sys/stat.h>
275331Samw #include <sys/avl.h>
28789Sahrens #if defined(_KERNEL)
29789Sahrens #include <sys/systm.h>
305331Samw #include <sys/sysmacros.h>
315331Samw #include <acl/acl_common.h>
32789Sahrens #else
33789Sahrens #include <errno.h>
34789Sahrens #include <stdlib.h>
355331Samw #include <stddef.h>
36789Sahrens #include <strings.h>
375331Samw #include <unistd.h>
38789Sahrens #include <assert.h>
395331Samw #include <grp.h>
405331Samw #include <pwd.h>
415331Samw #include <acl_common.h>
42789Sahrens #define	ASSERT	assert
43789Sahrens #endif
44789Sahrens 
455331Samw #define	ACE_POSIX_SUPPORTED_BITS (ACE_READ_DATA | \
465331Samw     ACE_WRITE_DATA | ACE_APPEND_DATA | ACE_EXECUTE | \
475331Samw     ACE_READ_ATTRIBUTES | ACE_READ_ACL | ACE_WRITE_ACL)
485331Samw 
495331Samw 
505331Samw #define	ACL_SYNCHRONIZE_SET_DENY		0x0000001
515331Samw #define	ACL_SYNCHRONIZE_SET_ALLOW		0x0000002
525331Samw #define	ACL_SYNCHRONIZE_ERR_DENY		0x0000004
535331Samw #define	ACL_SYNCHRONIZE_ERR_ALLOW		0x0000008
545331Samw 
555331Samw #define	ACL_WRITE_OWNER_SET_DENY		0x0000010
565331Samw #define	ACL_WRITE_OWNER_SET_ALLOW		0x0000020
575331Samw #define	ACL_WRITE_OWNER_ERR_DENY		0x0000040
585331Samw #define	ACL_WRITE_OWNER_ERR_ALLOW		0x0000080
595331Samw 
605331Samw #define	ACL_DELETE_SET_DENY			0x0000100
615331Samw #define	ACL_DELETE_SET_ALLOW			0x0000200
625331Samw #define	ACL_DELETE_ERR_DENY			0x0000400
635331Samw #define	ACL_DELETE_ERR_ALLOW			0x0000800
645331Samw 
655331Samw #define	ACL_WRITE_ATTRS_OWNER_SET_DENY		0x0001000
665331Samw #define	ACL_WRITE_ATTRS_OWNER_SET_ALLOW		0x0002000
675331Samw #define	ACL_WRITE_ATTRS_OWNER_ERR_DENY		0x0004000
685331Samw #define	ACL_WRITE_ATTRS_OWNER_ERR_ALLOW		0x0008000
695331Samw 
705331Samw #define	ACL_WRITE_ATTRS_WRITER_SET_DENY		0x0010000
715331Samw #define	ACL_WRITE_ATTRS_WRITER_SET_ALLOW	0x0020000
725331Samw #define	ACL_WRITE_ATTRS_WRITER_ERR_DENY		0x0040000
735331Samw #define	ACL_WRITE_ATTRS_WRITER_ERR_ALLOW	0x0080000
745331Samw 
755331Samw #define	ACL_WRITE_NAMED_WRITER_SET_DENY		0x0100000
765331Samw #define	ACL_WRITE_NAMED_WRITER_SET_ALLOW	0x0200000
775331Samw #define	ACL_WRITE_NAMED_WRITER_ERR_DENY		0x0400000
785331Samw #define	ACL_WRITE_NAMED_WRITER_ERR_ALLOW	0x0800000
795331Samw 
805331Samw #define	ACL_READ_NAMED_READER_SET_DENY		0x1000000
815331Samw #define	ACL_READ_NAMED_READER_SET_ALLOW		0x2000000
825331Samw #define	ACL_READ_NAMED_READER_ERR_DENY		0x4000000
835331Samw #define	ACL_READ_NAMED_READER_ERR_ALLOW		0x8000000
845331Samw 
855331Samw 
865331Samw #define	ACE_VALID_MASK_BITS (\
875331Samw     ACE_READ_DATA | \
885331Samw     ACE_LIST_DIRECTORY | \
895331Samw     ACE_WRITE_DATA | \
905331Samw     ACE_ADD_FILE | \
915331Samw     ACE_APPEND_DATA | \
925331Samw     ACE_ADD_SUBDIRECTORY | \
935331Samw     ACE_READ_NAMED_ATTRS | \
945331Samw     ACE_WRITE_NAMED_ATTRS | \
955331Samw     ACE_EXECUTE | \
965331Samw     ACE_DELETE_CHILD | \
975331Samw     ACE_READ_ATTRIBUTES | \
985331Samw     ACE_WRITE_ATTRIBUTES | \
995331Samw     ACE_DELETE | \
1005331Samw     ACE_READ_ACL | \
1015331Samw     ACE_WRITE_ACL | \
1025331Samw     ACE_WRITE_OWNER | \
1035331Samw     ACE_SYNCHRONIZE)
1045331Samw 
1055331Samw #define	ACE_MASK_UNDEFINED			0x80000000
1065331Samw 
1075331Samw #define	ACE_VALID_FLAG_BITS (ACE_FILE_INHERIT_ACE | \
1085331Samw     ACE_DIRECTORY_INHERIT_ACE | \
1095331Samw     ACE_NO_PROPAGATE_INHERIT_ACE | ACE_INHERIT_ONLY_ACE | \
1105331Samw     ACE_SUCCESSFUL_ACCESS_ACE_FLAG | ACE_FAILED_ACCESS_ACE_FLAG | \
1115331Samw     ACE_IDENTIFIER_GROUP | ACE_OWNER | ACE_GROUP | ACE_EVERYONE)
1125331Samw 
1135331Samw /*
1145331Samw  * ACL conversion helpers
1155331Samw  */
1165331Samw 
1175331Samw typedef enum {
1185331Samw 	ace_unused,
1195331Samw 	ace_user_obj,
1205331Samw 	ace_user,
1215331Samw 	ace_group, /* includes GROUP and GROUP_OBJ */
1225331Samw 	ace_other_obj
1235331Samw } ace_to_aent_state_t;
1245331Samw 
1255331Samw typedef struct acevals {
1265331Samw 	uid_t key;
1275331Samw 	avl_node_t avl;
1285331Samw 	uint32_t mask;
1295331Samw 	uint32_t allowed;
1305331Samw 	uint32_t denied;
1315331Samw 	int aent_type;
1325331Samw } acevals_t;
1335331Samw 
1345331Samw typedef struct ace_list {
1355331Samw 	acevals_t user_obj;
1365331Samw 	avl_tree_t user;
1375331Samw 	int numusers;
1385331Samw 	acevals_t group_obj;
1395331Samw 	avl_tree_t group;
1405331Samw 	int numgroups;
1415331Samw 	acevals_t other_obj;
1425331Samw 	uint32_t acl_mask;
1435331Samw 	int hasmask;
1445331Samw 	int dfacl_flag;
1455331Samw 	ace_to_aent_state_t state;
1465331Samw 	int seen; /* bitmask of all aclent_t a_type values seen */
1475331Samw } ace_list_t;
148789Sahrens 
149789Sahrens /*
150789Sahrens  * Generic shellsort, from K&R (1st ed, p 58.), somewhat modified.
151789Sahrens  * v = Ptr to array/vector of objs
152789Sahrens  * n = # objs in the array
153789Sahrens  * s = size of each obj (must be multiples of a word size)
154789Sahrens  * f = ptr to function to compare two objs
155789Sahrens  *	returns (-1 = less than, 0 = equal, 1 = greater than
156789Sahrens  */
157789Sahrens void
158789Sahrens ksort(caddr_t v, int n, int s, int (*f)())
159789Sahrens {
160789Sahrens 	int g, i, j, ii;
161789Sahrens 	unsigned int *p1, *p2;
162789Sahrens 	unsigned int tmp;
163789Sahrens 
164789Sahrens 	/* No work to do */
165789Sahrens 	if (v == NULL || n <= 1)
166789Sahrens 		return;
167789Sahrens 
168789Sahrens 	/* Sanity check on arguments */
169789Sahrens 	ASSERT(((uintptr_t)v & 0x3) == 0 && (s & 0x3) == 0);
170789Sahrens 	ASSERT(s > 0);
171789Sahrens 	for (g = n / 2; g > 0; g /= 2) {
172789Sahrens 		for (i = g; i < n; i++) {
173789Sahrens 			for (j = i - g; j >= 0 &&
1745331Samw 			    (*f)(v + j * s, v + (j + g) * s) == 1;
1755331Samw 			    j -= g) {
176789Sahrens 				p1 = (void *)(v + j * s);
177789Sahrens 				p2 = (void *)(v + (j + g) * s);
178789Sahrens 				for (ii = 0; ii < s / 4; ii++) {
179789Sahrens 					tmp = *p1;
180789Sahrens 					*p1++ = *p2;
181789Sahrens 					*p2++ = tmp;
182789Sahrens 				}
183789Sahrens 			}
184789Sahrens 		}
185789Sahrens 	}
186789Sahrens }
187789Sahrens 
188789Sahrens /*
189789Sahrens  * Compare two acls, all fields.  Returns:
190789Sahrens  * -1 (less than)
191789Sahrens  *  0 (equal)
192789Sahrens  * +1 (greater than)
193789Sahrens  */
194789Sahrens int
195789Sahrens cmp2acls(void *a, void *b)
196789Sahrens {
197789Sahrens 	aclent_t *x = (aclent_t *)a;
198789Sahrens 	aclent_t *y = (aclent_t *)b;
199789Sahrens 
200789Sahrens 	/* Compare types */
201789Sahrens 	if (x->a_type < y->a_type)
202789Sahrens 		return (-1);
203789Sahrens 	if (x->a_type > y->a_type)
204789Sahrens 		return (1);
205789Sahrens 	/* Equal types; compare id's */
206789Sahrens 	if (x->a_id < y->a_id)
207789Sahrens 		return (-1);
208789Sahrens 	if (x->a_id > y->a_id)
209789Sahrens 		return (1);
210789Sahrens 	/* Equal ids; compare perms */
211789Sahrens 	if (x->a_perm < y->a_perm)
212789Sahrens 		return (-1);
213789Sahrens 	if (x->a_perm > y->a_perm)
214789Sahrens 		return (1);
215789Sahrens 	/* Totally equal */
216789Sahrens 	return (0);
217789Sahrens }
2185331Samw 
2195331Samw /*ARGSUSED*/
2205331Samw static void *
2215331Samw cacl_realloc(void *ptr, size_t size, size_t new_size)
2225331Samw {
2235331Samw #if defined(_KERNEL)
2245331Samw 	void *tmp;
2255331Samw 
2265331Samw 	tmp = kmem_alloc(new_size, KM_SLEEP);
2275331Samw 	(void) memcpy(tmp, ptr, (size < new_size) ? size : new_size);
2285331Samw 	kmem_free(ptr, size);
2295331Samw 	return (tmp);
2305331Samw #else
2315331Samw 	return (realloc(ptr, new_size));
2325331Samw #endif
2335331Samw }
2345331Samw 
2355331Samw static int
2365331Samw cacl_malloc(void **ptr, size_t size)
2375331Samw {
2385331Samw #if defined(_KERNEL)
2395331Samw 	*ptr = kmem_zalloc(size, KM_SLEEP);
2405331Samw 	return (0);
2415331Samw #else
2425331Samw 	*ptr = calloc(1, size);
2435331Samw 	if (*ptr == NULL)
2445331Samw 		return (errno);
2455331Samw 
2465331Samw 	return (0);
2475331Samw #endif
2485331Samw }
2495331Samw 
2505331Samw /*ARGSUSED*/
2515331Samw static void
2525331Samw cacl_free(void *ptr, size_t size)
2535331Samw {
2545331Samw #if defined(_KERNEL)
2555331Samw 	kmem_free(ptr, size);
2565331Samw #else
2575331Samw 	free(ptr);
2585331Samw #endif
2595331Samw }
2605331Samw 
2615331Samw acl_t *
2625331Samw acl_alloc(enum acl_type type)
2635331Samw {
2645331Samw 	acl_t *aclp;
2655331Samw 
2665331Samw 	if (cacl_malloc((void **)&aclp, sizeof (acl_t)) != 0)
2675331Samw 		return (NULL);
2685331Samw 
2695331Samw 	aclp->acl_aclp = NULL;
2705331Samw 	aclp->acl_cnt = 0;
2715331Samw 
2725331Samw 	switch (type) {
2735331Samw 	case ACE_T:
2745331Samw 		aclp->acl_type = ACE_T;
2755331Samw 		aclp->acl_entry_size = sizeof (ace_t);
2765331Samw 		break;
2775331Samw 	case ACLENT_T:
2785331Samw 		aclp->acl_type = ACLENT_T;
2795331Samw 		aclp->acl_entry_size = sizeof (aclent_t);
2805331Samw 		break;
2815331Samw 	default:
2825331Samw 		acl_free(aclp);
2835331Samw 		aclp = NULL;
2845331Samw 	}
2855331Samw 	return (aclp);
2865331Samw }
2875331Samw 
2885331Samw /*
2895331Samw  * Free acl_t structure
2905331Samw  */
2915331Samw void
2925331Samw acl_free(acl_t *aclp)
2935331Samw {
2945331Samw 	int acl_size;
2955331Samw 
2965331Samw 	if (aclp == NULL)
2975331Samw 		return;
2985331Samw 
2995331Samw 	if (aclp->acl_aclp) {
3005331Samw 		acl_size = aclp->acl_cnt * aclp->acl_entry_size;
3015331Samw 		cacl_free(aclp->acl_aclp, acl_size);
3025331Samw 	}
3035331Samw 
3045331Samw 	cacl_free(aclp, sizeof (acl_t));
3055331Samw }
3065331Samw 
3075331Samw static uint32_t
3085331Samw access_mask_set(int haswriteperm, int hasreadperm, int isowner, int isallow)
3095331Samw {
3105331Samw 	uint32_t access_mask = 0;
3115331Samw 	int acl_produce;
3125331Samw 	int synchronize_set = 0, write_owner_set = 0;
3135331Samw 	int delete_set = 0, write_attrs_set = 0;
3145331Samw 	int read_named_set = 0, write_named_set = 0;
3155331Samw 
3165331Samw 	acl_produce = (ACL_SYNCHRONIZE_SET_ALLOW |
3175331Samw 	    ACL_WRITE_ATTRS_OWNER_SET_ALLOW |
3185331Samw 	    ACL_WRITE_ATTRS_WRITER_SET_DENY);
3195331Samw 
3205331Samw 	if (isallow) {
3215331Samw 		synchronize_set = ACL_SYNCHRONIZE_SET_ALLOW;
3225331Samw 		write_owner_set = ACL_WRITE_OWNER_SET_ALLOW;
3235331Samw 		delete_set = ACL_DELETE_SET_ALLOW;
3245331Samw 		if (hasreadperm)
3255331Samw 			read_named_set = ACL_READ_NAMED_READER_SET_ALLOW;
3265331Samw 		if (haswriteperm)
3275331Samw 			write_named_set = ACL_WRITE_NAMED_WRITER_SET_ALLOW;
3285331Samw 		if (isowner)
3295331Samw 			write_attrs_set = ACL_WRITE_ATTRS_OWNER_SET_ALLOW;
3305331Samw 		else if (haswriteperm)
3315331Samw 			write_attrs_set = ACL_WRITE_ATTRS_WRITER_SET_ALLOW;
3325331Samw 	} else {
3335331Samw 
3345331Samw 		synchronize_set = ACL_SYNCHRONIZE_SET_DENY;
3355331Samw 		write_owner_set = ACL_WRITE_OWNER_SET_DENY;
3365331Samw 		delete_set = ACL_DELETE_SET_DENY;
3375331Samw 		if (hasreadperm)
3385331Samw 			read_named_set = ACL_READ_NAMED_READER_SET_DENY;
3395331Samw 		if (haswriteperm)
3405331Samw 			write_named_set = ACL_WRITE_NAMED_WRITER_SET_DENY;
3415331Samw 		if (isowner)
3425331Samw 			write_attrs_set = ACL_WRITE_ATTRS_OWNER_SET_DENY;
3435331Samw 		else if (haswriteperm)
3445331Samw 			write_attrs_set = ACL_WRITE_ATTRS_WRITER_SET_DENY;
3455331Samw 		else
3465331Samw 			/*
3475331Samw 			 * If the entity is not the owner and does not
3485331Samw 			 * have write permissions ACE_WRITE_ATTRIBUTES will
3495331Samw 			 * always go in the DENY ACE.
3505331Samw 			 */
3515331Samw 			access_mask |= ACE_WRITE_ATTRIBUTES;
3525331Samw 	}
3535331Samw 
3545331Samw 	if (acl_produce & synchronize_set)
3555331Samw 		access_mask |= ACE_SYNCHRONIZE;
3565331Samw 	if (acl_produce & write_owner_set)
3575331Samw 		access_mask |= ACE_WRITE_OWNER;
3585331Samw 	if (acl_produce & delete_set)
3595331Samw 		access_mask |= ACE_DELETE;
3605331Samw 	if (acl_produce & write_attrs_set)
3615331Samw 		access_mask |= ACE_WRITE_ATTRIBUTES;
3625331Samw 	if (acl_produce & read_named_set)
3635331Samw 		access_mask |= ACE_READ_NAMED_ATTRS;
3645331Samw 	if (acl_produce & write_named_set)
3655331Samw 		access_mask |= ACE_WRITE_NAMED_ATTRS;
3665331Samw 
3675331Samw 	return (access_mask);
3685331Samw }
3695331Samw 
3705331Samw /*
3715331Samw  * Given an mode_t, convert it into an access_mask as used
3725331Samw  * by nfsace, assuming aclent_t -> nfsace semantics.
3735331Samw  */
3745331Samw static uint32_t
3755331Samw mode_to_ace_access(mode_t mode, int isdir, int isowner, int isallow)
3765331Samw {
3775331Samw 	uint32_t access = 0;
3785331Samw 	int haswriteperm = 0;
3795331Samw 	int hasreadperm = 0;
3805331Samw 
3815331Samw 	if (isallow) {
3825331Samw 		haswriteperm = (mode & S_IWOTH);
3835331Samw 		hasreadperm = (mode & S_IROTH);
3845331Samw 	} else {
3855331Samw 		haswriteperm = !(mode & S_IWOTH);
3865331Samw 		hasreadperm = !(mode & S_IROTH);
3875331Samw 	}
3885331Samw 
3895331Samw 	/*
3905331Samw 	 * The following call takes care of correctly setting the following
3915331Samw 	 * mask bits in the access_mask:
3925331Samw 	 * ACE_SYNCHRONIZE, ACE_WRITE_OWNER, ACE_DELETE,
3935331Samw 	 * ACE_WRITE_ATTRIBUTES, ACE_WRITE_NAMED_ATTRS, ACE_READ_NAMED_ATTRS
3945331Samw 	 */
3955331Samw 	access = access_mask_set(haswriteperm, hasreadperm, isowner, isallow);
3965331Samw 
3975331Samw 	if (isallow) {
3985331Samw 		access |= ACE_READ_ACL | ACE_READ_ATTRIBUTES;
3995331Samw 		if (isowner)
4005331Samw 			access |= ACE_WRITE_ACL;
4015331Samw 	} else {
4025331Samw 		if (! isowner)
4035331Samw 			access |= ACE_WRITE_ACL;
4045331Samw 	}
4055331Samw 
4065331Samw 	/* read */
4075331Samw 	if (mode & S_IROTH) {
4085331Samw 		access |= ACE_READ_DATA;
4095331Samw 	}
4105331Samw 	/* write */
4115331Samw 	if (mode & S_IWOTH) {
4125331Samw 		access |= ACE_WRITE_DATA |
4135331Samw 		    ACE_APPEND_DATA;
4145331Samw 		if (isdir)
4155331Samw 			access |= ACE_DELETE_CHILD;
4165331Samw 	}
4175331Samw 	/* exec */
4185331Samw 	if (mode & 01) {
4195331Samw 		access |= ACE_EXECUTE;
4205331Samw 	}
4215331Samw 
4225331Samw 	return (access);
4235331Samw }
4245331Samw 
4255331Samw /*
4265331Samw  * Given an nfsace (presumably an ALLOW entry), make a
4275331Samw  * corresponding DENY entry at the address given.
4285331Samw  */
4295331Samw static void
4305331Samw ace_make_deny(ace_t *allow, ace_t *deny, int isdir, int isowner)
4315331Samw {
4325331Samw 	(void) memcpy(deny, allow, sizeof (ace_t));
4335331Samw 
4345331Samw 	deny->a_who = allow->a_who;
4355331Samw 
4365331Samw 	deny->a_type = ACE_ACCESS_DENIED_ACE_TYPE;
4375331Samw 	deny->a_access_mask ^= ACE_POSIX_SUPPORTED_BITS;
4385331Samw 	if (isdir)
4395331Samw 		deny->a_access_mask ^= ACE_DELETE_CHILD;
4405331Samw 
4415331Samw 	deny->a_access_mask &= ~(ACE_SYNCHRONIZE | ACE_WRITE_OWNER |
4425331Samw 	    ACE_DELETE | ACE_WRITE_ATTRIBUTES | ACE_READ_NAMED_ATTRS |
4435331Samw 	    ACE_WRITE_NAMED_ATTRS);
4445331Samw 	deny->a_access_mask |= access_mask_set((allow->a_access_mask &
4455331Samw 	    ACE_WRITE_DATA), (allow->a_access_mask & ACE_READ_DATA), isowner,
4465331Samw 	    B_FALSE);
4475331Samw }
4485331Samw /*
4495331Samw  * Make an initial pass over an array of aclent_t's.  Gather
4505331Samw  * information such as an ACL_MASK (if any), number of users,
4515331Samw  * number of groups, and whether the array needs to be sorted.
4525331Samw  */
4535331Samw static int
4545331Samw ln_aent_preprocess(aclent_t *aclent, int n,
4555331Samw     int *hasmask, mode_t *mask,
4565331Samw     int *numuser, int *numgroup, int *needsort)
4575331Samw {
4585331Samw 	int error = 0;
4595331Samw 	int i;
4605331Samw 	int curtype = 0;
4615331Samw 
4625331Samw 	*hasmask = 0;
4635331Samw 	*mask = 07;
4645331Samw 	*needsort = 0;
4655331Samw 	*numuser = 0;
4665331Samw 	*numgroup = 0;
4675331Samw 
4685331Samw 	for (i = 0; i < n; i++) {
4695331Samw 		if (aclent[i].a_type < curtype)
4705331Samw 			*needsort = 1;
4715331Samw 		else if (aclent[i].a_type > curtype)
4725331Samw 			curtype = aclent[i].a_type;
4735331Samw 		if (aclent[i].a_type & USER)
4745331Samw 			(*numuser)++;
4755331Samw 		if (aclent[i].a_type & (GROUP | GROUP_OBJ))
4765331Samw 			(*numgroup)++;
4775331Samw 		if (aclent[i].a_type & CLASS_OBJ) {
4785331Samw 			if (*hasmask) {
4795331Samw 				error = EINVAL;
4805331Samw 				goto out;
4815331Samw 			} else {
4825331Samw 				*hasmask = 1;
4835331Samw 				*mask = aclent[i].a_perm;
4845331Samw 			}
4855331Samw 		}
4865331Samw 	}
4875331Samw 
4885331Samw 	if ((! *hasmask) && (*numuser + *numgroup > 1)) {
4895331Samw 		error = EINVAL;
4905331Samw 		goto out;
4915331Samw 	}
4925331Samw 
4935331Samw out:
4945331Samw 	return (error);
4955331Samw }
4965331Samw 
4975331Samw /*
4985331Samw  * Convert an array of aclent_t into an array of nfsace entries,
4995331Samw  * following POSIX draft -> nfsv4 conversion semantics as outlined in
5005331Samw  * the IETF draft.
5015331Samw  */
5025331Samw static int
5035331Samw ln_aent_to_ace(aclent_t *aclent, int n, ace_t **acepp, int *rescount, int isdir)
5045331Samw {
5055331Samw 	int error = 0;
5065331Samw 	mode_t mask;
5075331Samw 	int numuser, numgroup, needsort;
5085331Samw 	int resultsize = 0;
5095331Samw 	int i, groupi = 0, skip;
5105331Samw 	ace_t *acep, *result = NULL;
5115331Samw 	int hasmask;
5125331Samw 
5135331Samw 	error = ln_aent_preprocess(aclent, n, &hasmask, &mask,
5145331Samw 	    &numuser, &numgroup, &needsort);
5155331Samw 	if (error != 0)
5165331Samw 		goto out;
5175331Samw 
5185331Samw 	/* allow + deny for each aclent */
5195331Samw 	resultsize = n * 2;
5205331Samw 	if (hasmask) {
5215331Samw 		/*
5225331Samw 		 * stick extra deny on the group_obj and on each
5235331Samw 		 * user|group for the mask (the group_obj was added
5245331Samw 		 * into the count for numgroup)
5255331Samw 		 */
5265331Samw 		resultsize += numuser + numgroup;
5275331Samw 		/* ... and don't count the mask itself */
5285331Samw 		resultsize -= 2;
5295331Samw 	}
5305331Samw 
5315331Samw 	/* sort the source if necessary */
5325331Samw 	if (needsort)
5335331Samw 		ksort((caddr_t)aclent, n, sizeof (aclent_t), cmp2acls);
5345331Samw 
5355331Samw 	if (cacl_malloc((void **)&result, resultsize * sizeof (ace_t)) != 0)
5365331Samw 		goto out;
5375331Samw 
5385331Samw 	acep = result;
5395331Samw 
5405331Samw 	for (i = 0; i < n; i++) {
5415331Samw 		/*
5425331Samw 		 * don't process CLASS_OBJ (mask); mask was grabbed in
5435331Samw 		 * ln_aent_preprocess()
5445331Samw 		 */
5455331Samw 		if (aclent[i].a_type & CLASS_OBJ)
5465331Samw 			continue;
5475331Samw 
5485331Samw 		/* If we need an ACL_MASK emulator, prepend it now */
5495331Samw 		if ((hasmask) &&
5505331Samw 		    (aclent[i].a_type & (USER | GROUP | GROUP_OBJ))) {
5515331Samw 			acep->a_type = ACE_ACCESS_DENIED_ACE_TYPE;
5525331Samw 			acep->a_flags = 0;
5535331Samw 			if (aclent[i].a_type & GROUP_OBJ) {
5545331Samw 				acep->a_who = (uid_t)-1;
5555331Samw 				acep->a_flags |=
5565331Samw 				    (ACE_IDENTIFIER_GROUP|ACE_GROUP);
5575331Samw 			} else if (aclent[i].a_type & USER) {
5585331Samw 				acep->a_who = aclent[i].a_id;
5595331Samw 			} else {
5605331Samw 				acep->a_who = aclent[i].a_id;
5615331Samw 				acep->a_flags |= ACE_IDENTIFIER_GROUP;
5625331Samw 			}
5635331Samw 			if (aclent[i].a_type & ACL_DEFAULT) {
5645331Samw 				acep->a_flags |= ACE_INHERIT_ONLY_ACE |
5655331Samw 				    ACE_FILE_INHERIT_ACE |
5665331Samw 				    ACE_DIRECTORY_INHERIT_ACE;
5675331Samw 			}
5685331Samw 			/*
5695331Samw 			 * Set the access mask for the prepended deny
5705331Samw 			 * ace.  To do this, we invert the mask (found
5715331Samw 			 * in ln_aent_preprocess()) then convert it to an
5725331Samw 			 * DENY ace access_mask.
5735331Samw 			 */
5745331Samw 			acep->a_access_mask = mode_to_ace_access((mask ^ 07),
5755331Samw 			    isdir, 0, 0);
5765331Samw 			acep += 1;
5775331Samw 		}
5785331Samw 
5795331Samw 		/* handle a_perm -> access_mask */
5805331Samw 		acep->a_access_mask = mode_to_ace_access(aclent[i].a_perm,
5815331Samw 		    isdir, aclent[i].a_type & USER_OBJ, 1);
5825331Samw 
5835331Samw 		/* emulate a default aclent */
5845331Samw 		if (aclent[i].a_type & ACL_DEFAULT) {
5855331Samw 			acep->a_flags |= ACE_INHERIT_ONLY_ACE |
5865331Samw 			    ACE_FILE_INHERIT_ACE |
5875331Samw 			    ACE_DIRECTORY_INHERIT_ACE;
5885331Samw 		}
5895331Samw 
5905331Samw 		/*
5915331Samw 		 * handle a_perm and a_id
5925331Samw 		 *
5935331Samw 		 * this must be done last, since it involves the
5945331Samw 		 * corresponding deny aces, which are handled
5955331Samw 		 * differently for each different a_type.
5965331Samw 		 */
5975331Samw 		if (aclent[i].a_type & USER_OBJ) {
5985331Samw 			acep->a_who = (uid_t)-1;
5995331Samw 			acep->a_flags |= ACE_OWNER;
6005331Samw 			ace_make_deny(acep, acep + 1, isdir, B_TRUE);
6015331Samw 			acep += 2;
6025331Samw 		} else if (aclent[i].a_type & USER) {
6035331Samw 			acep->a_who = aclent[i].a_id;
6045331Samw 			ace_make_deny(acep, acep + 1, isdir, B_FALSE);
6055331Samw 			acep += 2;
6065331Samw 		} else if (aclent[i].a_type & (GROUP_OBJ | GROUP)) {
6075331Samw 			if (aclent[i].a_type & GROUP_OBJ) {
6085331Samw 				acep->a_who = (uid_t)-1;
6095331Samw 				acep->a_flags |= ACE_GROUP;
6105331Samw 			} else {
6115331Samw 				acep->a_who = aclent[i].a_id;
6125331Samw 			}
6135331Samw 			acep->a_flags |= ACE_IDENTIFIER_GROUP;
6145331Samw 			/*
6155331Samw 			 * Set the corresponding deny for the group ace.
6165331Samw 			 *
6175331Samw 			 * The deny aces go after all of the groups, unlike
6185331Samw 			 * everything else, where they immediately follow
6195331Samw 			 * the allow ace.
6205331Samw 			 *
6215331Samw 			 * We calculate "skip", the number of slots to
6225331Samw 			 * skip ahead for the deny ace, here.
6235331Samw 			 *
6245331Samw 			 * The pattern is:
6255331Samw 			 * MD1 A1 MD2 A2 MD3 A3 D1 D2 D3
6265331Samw 			 * thus, skip is
6275331Samw 			 * (2 * numgroup) - 1 - groupi
6285331Samw 			 * (2 * numgroup) to account for MD + A
6295331Samw 			 * - 1 to account for the fact that we're on the
6305331Samw 			 * access (A), not the mask (MD)
6315331Samw 			 * - groupi to account for the fact that we have
6325331Samw 			 * passed up groupi number of MD's.
6335331Samw 			 */
6345331Samw 			skip = (2 * numgroup) - 1 - groupi;
6355331Samw 			ace_make_deny(acep, acep + skip, isdir, B_FALSE);
6365331Samw 			/*
6375331Samw 			 * If we just did the last group, skip acep past
6385331Samw 			 * all of the denies; else, just move ahead one.
6395331Samw 			 */
6405331Samw 			if (++groupi >= numgroup)
6415331Samw 				acep += numgroup + 1;
6425331Samw 			else
6435331Samw 				acep += 1;
6445331Samw 		} else if (aclent[i].a_type & OTHER_OBJ) {
6455331Samw 			acep->a_who = (uid_t)-1;
6465331Samw 			acep->a_flags |= ACE_EVERYONE;
6475331Samw 			ace_make_deny(acep, acep + 1, isdir, B_FALSE);
6485331Samw 			acep += 2;
6495331Samw 		} else {
6505331Samw 			error = EINVAL;
6515331Samw 			goto out;
6525331Samw 		}
6535331Samw 	}
6545331Samw 
6555331Samw 	*acepp = result;
6565331Samw 	*rescount = resultsize;
6575331Samw 
6585331Samw out:
6595331Samw 	if (error != 0) {
6605331Samw 		if ((result != NULL) && (resultsize > 0)) {
6615331Samw 			cacl_free(result, resultsize * sizeof (ace_t));
6625331Samw 		}
6635331Samw 	}
6645331Samw 
6655331Samw 	return (error);
6665331Samw }
6675331Samw 
6685331Samw static int
6695331Samw convert_aent_to_ace(aclent_t *aclentp, int aclcnt, int isdir,
6705331Samw     ace_t **retacep, int *retacecnt)
6715331Samw {
6725331Samw 	ace_t *acep;
6735331Samw 	ace_t *dfacep;
6745331Samw 	int acecnt = 0;
6755331Samw 	int dfacecnt = 0;
6765331Samw 	int dfaclstart = 0;
6775331Samw 	int dfaclcnt = 0;
6785331Samw 	aclent_t *aclp;
6795331Samw 	int i;
6805331Samw 	int error;
6815331Samw 	int acesz, dfacesz;
6825331Samw 
6835331Samw 	ksort((caddr_t)aclentp, aclcnt, sizeof (aclent_t), cmp2acls);
6845331Samw 
6855331Samw 	for (i = 0, aclp = aclentp; i < aclcnt; aclp++, i++) {
6865331Samw 		if (aclp->a_type & ACL_DEFAULT)
6875331Samw 			break;
6885331Samw 	}
6895331Samw 
6905331Samw 	if (i < aclcnt) {
6915331Samw 		dfaclstart = i;
6925331Samw 		dfaclcnt = aclcnt - i;
6935331Samw 	}
6945331Samw 
6955331Samw 	if (dfaclcnt && isdir == 0) {
6965331Samw 		return (EINVAL);
6975331Samw 	}
6985331Samw 
6995331Samw 	error = ln_aent_to_ace(aclentp, i,  &acep, &acecnt, isdir);
7005331Samw 	if (error)
7015331Samw 		return (error);
7025331Samw 
7035331Samw 	if (dfaclcnt) {
7045331Samw 		error = ln_aent_to_ace(&aclentp[dfaclstart], dfaclcnt,
7055331Samw 		    &dfacep, &dfacecnt, isdir);
7065331Samw 		if (error) {
7075331Samw 			if (acep) {
7085331Samw 				cacl_free(acep, acecnt * sizeof (ace_t));
7095331Samw 			}
7105331Samw 			return (error);
7115331Samw 		}
7125331Samw 	}
7135331Samw 
7145331Samw 	if (dfacecnt != 0) {
7155331Samw 		acesz = sizeof (ace_t) * acecnt;
7165331Samw 		dfacesz = sizeof (ace_t) * dfacecnt;
7175331Samw 		acep = cacl_realloc(acep, acesz, acesz + dfacesz);
7185331Samw 		if (acep == NULL)
7195331Samw 			return (ENOMEM);
7205331Samw 		if (dfaclcnt) {
7215331Samw 			(void) memcpy(acep + acecnt, dfacep, dfacesz);
7225331Samw 		}
7235331Samw 	}
7245331Samw 	if (dfaclcnt)
7255331Samw 		cacl_free(dfacep, dfacecnt * sizeof (ace_t));
7265331Samw 
7275331Samw 	*retacecnt = acecnt + dfacecnt;
7285331Samw 	*retacep = acep;
7295331Samw 	return (0);
7305331Samw }
7315331Samw 
7325331Samw static int
7335331Samw ace_mask_to_mode(uint32_t  mask, o_mode_t *modep, int isdir)
7345331Samw {
7355331Samw 	int error = 0;
7365331Samw 	o_mode_t mode = 0;
7375331Samw 	uint32_t bits, wantbits;
7385331Samw 
7395331Samw 	/* read */
7405331Samw 	if (mask & ACE_READ_DATA)
7415331Samw 		mode |= S_IROTH;
7425331Samw 
7435331Samw 	/* write */
7445331Samw 	wantbits = (ACE_WRITE_DATA | ACE_APPEND_DATA);
7455331Samw 	if (isdir)
7465331Samw 		wantbits |= ACE_DELETE_CHILD;
7475331Samw 	bits = mask & wantbits;
7485331Samw 	if (bits != 0) {
7495331Samw 		if (bits != wantbits) {
7505331Samw 			error = ENOTSUP;
7515331Samw 			goto out;
7525331Samw 		}
7535331Samw 		mode |= S_IWOTH;
7545331Samw 	}
7555331Samw 
7565331Samw 	/* exec */
7575331Samw 	if (mask & ACE_EXECUTE) {
7585331Samw 		mode |= S_IXOTH;
7595331Samw 	}
7605331Samw 
7615331Samw 	*modep = mode;
7625331Samw 
7635331Samw out:
7645331Samw 	return (error);
7655331Samw }
7665331Samw 
7675331Samw static void
7685331Samw acevals_init(acevals_t *vals, uid_t key)
7695331Samw {
7705331Samw 	bzero(vals, sizeof (*vals));
7715331Samw 	vals->allowed = ACE_MASK_UNDEFINED;
7725331Samw 	vals->denied = ACE_MASK_UNDEFINED;
7735331Samw 	vals->mask = ACE_MASK_UNDEFINED;
7745331Samw 	vals->key = key;
7755331Samw }
7765331Samw 
7775331Samw static void
7785331Samw ace_list_init(ace_list_t *al, int dfacl_flag)
7795331Samw {
7805331Samw 	acevals_init(&al->user_obj, NULL);
7815331Samw 	acevals_init(&al->group_obj, NULL);
7825331Samw 	acevals_init(&al->other_obj, NULL);
7835331Samw 	al->numusers = 0;
7845331Samw 	al->numgroups = 0;
7855331Samw 	al->acl_mask = 0;
7865331Samw 	al->hasmask = 0;
7875331Samw 	al->state = ace_unused;
7885331Samw 	al->seen = 0;
7895331Samw 	al->dfacl_flag = dfacl_flag;
7905331Samw }
7915331Samw 
7925331Samw /*
7935331Samw  * Find or create an acevals holder for a given id and avl tree.
7945331Samw  *
7955331Samw  * Note that only one thread will ever touch these avl trees, so
7965331Samw  * there is no need for locking.
7975331Samw  */
7985331Samw static acevals_t *
7995331Samw acevals_find(ace_t *ace, avl_tree_t *avl, int *num)
8005331Samw {
8015331Samw 	acevals_t key, *rc;
8025331Samw 	avl_index_t where;
8035331Samw 
8045331Samw 	key.key = ace->a_who;
8055331Samw 	rc = avl_find(avl, &key, &where);
8065331Samw 	if (rc != NULL)
8075331Samw 		return (rc);
8085331Samw 
8095331Samw 	/* this memory is freed by ln_ace_to_aent()->ace_list_free() */
8105331Samw 	if (cacl_malloc((void **)&rc, sizeof (acevals_t)) != 0)
8115331Samw 		return (NULL);
8125331Samw 
8135331Samw 	acevals_init(rc, ace->a_who);
8145331Samw 	avl_insert(avl, rc, where);
8155331Samw 	(*num)++;
8165331Samw 
8175331Samw 	return (rc);
8185331Samw }
8195331Samw 
8205331Samw static int
8215331Samw access_mask_check(ace_t *acep, int mask_bit, int isowner)
8225331Samw {
8235331Samw 	int set_deny, err_deny;
8245331Samw 	int set_allow, err_allow;
8255331Samw 	int acl_consume;
8265331Samw 	int haswriteperm, hasreadperm;
8275331Samw 
8285331Samw 	if (acep->a_type == ACE_ACCESS_DENIED_ACE_TYPE) {
8295331Samw 		haswriteperm = (acep->a_access_mask & ACE_WRITE_DATA) ? 0 : 1;
8305331Samw 		hasreadperm = (acep->a_access_mask & ACE_READ_DATA) ? 0 : 1;
8315331Samw 	} else {
8325331Samw 		haswriteperm = (acep->a_access_mask & ACE_WRITE_DATA) ? 1 : 0;
8335331Samw 		hasreadperm = (acep->a_access_mask & ACE_READ_DATA) ? 1 : 0;
8345331Samw 	}
8355331Samw 
8365331Samw 	acl_consume = (ACL_SYNCHRONIZE_ERR_DENY |
8375331Samw 	    ACL_DELETE_ERR_DENY |
8385331Samw 	    ACL_WRITE_OWNER_ERR_DENY |
8395331Samw 	    ACL_WRITE_OWNER_ERR_ALLOW |
8405331Samw 	    ACL_WRITE_ATTRS_OWNER_SET_ALLOW |
8415331Samw 	    ACL_WRITE_ATTRS_OWNER_ERR_DENY |
8425331Samw 	    ACL_WRITE_ATTRS_WRITER_SET_DENY |
8435331Samw 	    ACL_WRITE_ATTRS_WRITER_ERR_ALLOW |
8445331Samw 	    ACL_WRITE_NAMED_WRITER_ERR_DENY |
8455331Samw 	    ACL_READ_NAMED_READER_ERR_DENY);
8465331Samw 
8475331Samw 	if (mask_bit == ACE_SYNCHRONIZE) {
8485331Samw 		set_deny = ACL_SYNCHRONIZE_SET_DENY;
8495331Samw 		err_deny =  ACL_SYNCHRONIZE_ERR_DENY;
8505331Samw 		set_allow = ACL_SYNCHRONIZE_SET_ALLOW;
8515331Samw 		err_allow = ACL_SYNCHRONIZE_ERR_ALLOW;
8525331Samw 	} else if (mask_bit == ACE_WRITE_OWNER) {
8535331Samw 		set_deny = ACL_WRITE_OWNER_SET_DENY;
8545331Samw 		err_deny =  ACL_WRITE_OWNER_ERR_DENY;
8555331Samw 		set_allow = ACL_WRITE_OWNER_SET_ALLOW;
8565331Samw 		err_allow = ACL_WRITE_OWNER_ERR_ALLOW;
8575331Samw 	} else if (mask_bit == ACE_DELETE) {
8585331Samw 		set_deny = ACL_DELETE_SET_DENY;
8595331Samw 		err_deny =  ACL_DELETE_ERR_DENY;
8605331Samw 		set_allow = ACL_DELETE_SET_ALLOW;
8615331Samw 		err_allow = ACL_DELETE_ERR_ALLOW;
8625331Samw 	} else if (mask_bit == ACE_WRITE_ATTRIBUTES) {
8635331Samw 		if (isowner) {
8645331Samw 			set_deny = ACL_WRITE_ATTRS_OWNER_SET_DENY;
8655331Samw 			err_deny =  ACL_WRITE_ATTRS_OWNER_ERR_DENY;
8665331Samw 			set_allow = ACL_WRITE_ATTRS_OWNER_SET_ALLOW;
8675331Samw 			err_allow = ACL_WRITE_ATTRS_OWNER_ERR_ALLOW;
8685331Samw 		} else if (haswriteperm) {
8695331Samw 			set_deny = ACL_WRITE_ATTRS_WRITER_SET_DENY;
8705331Samw 			err_deny =  ACL_WRITE_ATTRS_WRITER_ERR_DENY;
8715331Samw 			set_allow = ACL_WRITE_ATTRS_WRITER_SET_ALLOW;
8725331Samw 			err_allow = ACL_WRITE_ATTRS_WRITER_ERR_ALLOW;
8735331Samw 		} else {
8745331Samw 			if ((acep->a_access_mask & mask_bit) &&
8755331Samw 			    (acep->a_type & ACE_ACCESS_ALLOWED_ACE_TYPE)) {
8765331Samw 				return (ENOTSUP);
8775331Samw 			}
8785331Samw 			return (0);
8795331Samw 		}
8805331Samw 	} else if (mask_bit == ACE_READ_NAMED_ATTRS) {
8815331Samw 		if (!hasreadperm)
8825331Samw 			return (0);
8835331Samw 
8845331Samw 		set_deny = ACL_READ_NAMED_READER_SET_DENY;
8855331Samw 		err_deny = ACL_READ_NAMED_READER_ERR_DENY;
8865331Samw 		set_allow = ACL_READ_NAMED_READER_SET_ALLOW;
8875331Samw 		err_allow = ACL_READ_NAMED_READER_ERR_ALLOW;
8885331Samw 	} else if (mask_bit == ACE_WRITE_NAMED_ATTRS) {
8895331Samw 		if (!haswriteperm)
8905331Samw 			return (0);
8915331Samw 
8925331Samw 		set_deny = ACL_WRITE_NAMED_WRITER_SET_DENY;
8935331Samw 		err_deny = ACL_WRITE_NAMED_WRITER_ERR_DENY;
8945331Samw 		set_allow = ACL_WRITE_NAMED_WRITER_SET_ALLOW;
8955331Samw 		err_allow = ACL_WRITE_NAMED_WRITER_ERR_ALLOW;
8965331Samw 	} else {
8975331Samw 		return (EINVAL);
8985331Samw 	}
8995331Samw 
9005331Samw 	if (acep->a_type == ACE_ACCESS_DENIED_ACE_TYPE) {
9015331Samw 		if (acl_consume & set_deny) {
9025331Samw 			if (!(acep->a_access_mask & mask_bit)) {
9035331Samw 				return (ENOTSUP);
9045331Samw 			}
9055331Samw 		} else if (acl_consume & err_deny) {
9065331Samw 			if (acep->a_access_mask & mask_bit) {
9075331Samw 				return (ENOTSUP);
9085331Samw 			}
9095331Samw 		}
9105331Samw 	} else {
9115331Samw 		/* ACE_ACCESS_ALLOWED_ACE_TYPE */
9125331Samw 		if (acl_consume & set_allow) {
9135331Samw 			if (!(acep->a_access_mask & mask_bit)) {
9145331Samw 				return (ENOTSUP);
9155331Samw 			}
9165331Samw 		} else if (acl_consume & err_allow) {
9175331Samw 			if (acep->a_access_mask & mask_bit) {
9185331Samw 				return (ENOTSUP);
9195331Samw 			}
9205331Samw 		}
9215331Samw 	}
9225331Samw 	return (0);
9235331Samw }
9245331Samw 
9255331Samw static int
9265331Samw ace_to_aent_legal(ace_t *acep)
9275331Samw {
9285331Samw 	int error = 0;
9295331Samw 	int isowner;
9305331Samw 
9315331Samw 	/* only ALLOW or DENY */
9325331Samw 	if ((acep->a_type != ACE_ACCESS_ALLOWED_ACE_TYPE) &&
9335331Samw 	    (acep->a_type != ACE_ACCESS_DENIED_ACE_TYPE)) {
9345331Samw 		error = ENOTSUP;
9355331Samw 		goto out;
9365331Samw 	}
9375331Samw 
9385331Samw 	/* check for invalid flags */
9395331Samw 	if (acep->a_flags & ~(ACE_VALID_FLAG_BITS)) {
9405331Samw 		error = EINVAL;
9415331Samw 		goto out;
9425331Samw 	}
9435331Samw 
9445331Samw 	/* some flags are illegal */
9455331Samw 	if (acep->a_flags & (ACE_SUCCESSFUL_ACCESS_ACE_FLAG |
9465331Samw 	    ACE_FAILED_ACCESS_ACE_FLAG |
9475331Samw 	    ACE_NO_PROPAGATE_INHERIT_ACE)) {
9485331Samw 		error = ENOTSUP;
9495331Samw 		goto out;
9505331Samw 	}
9515331Samw 
9525331Samw 	/* check for invalid masks */
9535331Samw 	if (acep->a_access_mask & ~(ACE_VALID_MASK_BITS)) {
9545331Samw 		error = EINVAL;
9555331Samw 		goto out;
9565331Samw 	}
9575331Samw 
9585331Samw 	if ((acep->a_flags & ACE_OWNER)) {
9595331Samw 		isowner = 1;
9605331Samw 	} else {
9615331Samw 		isowner = 0;
9625331Samw 	}
9635331Samw 
9645331Samw 	error = access_mask_check(acep, ACE_SYNCHRONIZE, isowner);
9655331Samw 	if (error)
9665331Samw 		goto out;
9675331Samw 
9685331Samw 	error = access_mask_check(acep, ACE_WRITE_OWNER, isowner);
9695331Samw 	if (error)
9705331Samw 		goto out;
9715331Samw 
9725331Samw 	error = access_mask_check(acep, ACE_DELETE, isowner);
9735331Samw 	if (error)
9745331Samw 		goto out;
9755331Samw 
9765331Samw 	error = access_mask_check(acep, ACE_WRITE_ATTRIBUTES, isowner);
9775331Samw 	if (error)
9785331Samw 		goto out;
9795331Samw 
9805331Samw 	error = access_mask_check(acep, ACE_READ_NAMED_ATTRS, isowner);
9815331Samw 	if (error)
9825331Samw 		goto out;
9835331Samw 
9845331Samw 	error = access_mask_check(acep, ACE_WRITE_NAMED_ATTRS, isowner);
9855331Samw 	if (error)
9865331Samw 		goto out;
9875331Samw 
9885331Samw 	/* more detailed checking of masks */
9895331Samw 	if (acep->a_type == ACE_ACCESS_ALLOWED_ACE_TYPE) {
9905331Samw 		if (! (acep->a_access_mask & ACE_READ_ATTRIBUTES)) {
9915331Samw 			error = ENOTSUP;
9925331Samw 			goto out;
9935331Samw 		}
9945331Samw 		if ((acep->a_access_mask & ACE_WRITE_DATA) &&
9955331Samw 		    (! (acep->a_access_mask & ACE_APPEND_DATA))) {
9965331Samw 			error = ENOTSUP;
9975331Samw 			goto out;
9985331Samw 		}
9995331Samw 		if ((! (acep->a_access_mask & ACE_WRITE_DATA)) &&
10005331Samw 		    (acep->a_access_mask & ACE_APPEND_DATA)) {
10015331Samw 			error = ENOTSUP;
10025331Samw 			goto out;
10035331Samw 		}
10045331Samw 	}
10055331Samw 
10065331Samw 	/* ACL enforcement */
10075331Samw 	if ((acep->a_access_mask & ACE_READ_ACL) &&
10085331Samw 	    (acep->a_type != ACE_ACCESS_ALLOWED_ACE_TYPE)) {
10095331Samw 		error = ENOTSUP;
10105331Samw 		goto out;
10115331Samw 	}
10125331Samw 	if (acep->a_access_mask & ACE_WRITE_ACL) {
10135331Samw 		if ((acep->a_type == ACE_ACCESS_DENIED_ACE_TYPE) &&
10145331Samw 		    (isowner)) {
10155331Samw 			error = ENOTSUP;
10165331Samw 			goto out;
10175331Samw 		}
10185331Samw 		if ((acep->a_type == ACE_ACCESS_ALLOWED_ACE_TYPE) &&
10195331Samw 		    (! isowner)) {
10205331Samw 			error = ENOTSUP;
10215331Samw 			goto out;
10225331Samw 		}
10235331Samw 	}
10245331Samw 
10255331Samw out:
10265331Samw 	return (error);
10275331Samw }
10285331Samw 
10295331Samw static int
10305331Samw ace_allow_to_mode(uint32_t mask, o_mode_t *modep, int isdir)
10315331Samw {
10325331Samw 	/* ACE_READ_ACL and ACE_READ_ATTRIBUTES must both be set */
10335331Samw 	if ((mask & (ACE_READ_ACL | ACE_READ_ATTRIBUTES)) !=
10345331Samw 	    (ACE_READ_ACL | ACE_READ_ATTRIBUTES)) {
10355331Samw 		return (ENOTSUP);
10365331Samw 	}
10375331Samw 
10385331Samw 	return (ace_mask_to_mode(mask, modep, isdir));
10395331Samw }
10405331Samw 
10415331Samw static int
10425331Samw acevals_to_aent(acevals_t *vals, aclent_t *dest, ace_list_t *list,
10435331Samw     uid_t owner, gid_t group, int isdir)
10445331Samw {
10455331Samw 	int error;
10465331Samw 	uint32_t  flips = ACE_POSIX_SUPPORTED_BITS;
10475331Samw 
10485331Samw 	if (isdir)
10495331Samw 		flips |= ACE_DELETE_CHILD;
10505331Samw 	if (vals->allowed != (vals->denied ^ flips)) {
10515331Samw 		error = ENOTSUP;
10525331Samw 		goto out;
10535331Samw 	}
10545331Samw 	if ((list->hasmask) && (list->acl_mask != vals->mask) &&
10555331Samw 	    (vals->aent_type & (USER | GROUP | GROUP_OBJ))) {
10565331Samw 		error = ENOTSUP;
10575331Samw 		goto out;
10585331Samw 	}
10595331Samw 	error = ace_allow_to_mode(vals->allowed, &dest->a_perm, isdir);
10605331Samw 	if (error != 0)
10615331Samw 		goto out;
10625331Samw 	dest->a_type = vals->aent_type;
10635331Samw 	if (dest->a_type & (USER | GROUP)) {
10645331Samw 		dest->a_id = vals->key;
10655331Samw 	} else if (dest->a_type & USER_OBJ) {
10665331Samw 		dest->a_id = owner;
10675331Samw 	} else if (dest->a_type & GROUP_OBJ) {
10685331Samw 		dest->a_id = group;
10695331Samw 	} else if (dest->a_type & OTHER_OBJ) {
10705331Samw 		dest->a_id = 0;
10715331Samw 	} else {
10725331Samw 		error = EINVAL;
10735331Samw 		goto out;
10745331Samw 	}
10755331Samw 
10765331Samw out:
10775331Samw 	return (error);
10785331Samw }
10795331Samw 
10805331Samw 
10815331Samw static int
10825331Samw ace_list_to_aent(ace_list_t *list, aclent_t **aclentp, int *aclcnt,
10835331Samw     uid_t owner, gid_t group, int isdir)
10845331Samw {
10855331Samw 	int error = 0;
10865331Samw 	aclent_t *aent, *result = NULL;
10875331Samw 	acevals_t *vals;
10885331Samw 	int resultcount;
10895331Samw 
10905331Samw 	if ((list->seen & (USER_OBJ | GROUP_OBJ | OTHER_OBJ)) !=
10915331Samw 	    (USER_OBJ | GROUP_OBJ | OTHER_OBJ)) {
10925331Samw 		error = ENOTSUP;
10935331Samw 		goto out;
10945331Samw 	}
10955331Samw 	if ((! list->hasmask) && (list->numusers + list->numgroups > 0)) {
10965331Samw 		error = ENOTSUP;
10975331Samw 		goto out;
10985331Samw 	}
10995331Samw 
11005331Samw 	resultcount = 3 + list->numusers + list->numgroups;
11015331Samw 	/*
11025331Samw 	 * This must be the same condition as below, when we add the CLASS_OBJ
11035331Samw 	 * (aka ACL mask)
11045331Samw 	 */
11055331Samw 	if ((list->hasmask) || (! list->dfacl_flag))
11065331Samw 		resultcount += 1;
11075331Samw 
11085331Samw 	if (cacl_malloc((void **)&result,
11095331Samw 	    resultcount * sizeof (aclent_t)) != 0) {
11105331Samw 		error = ENOMEM;
11115331Samw 		goto out;
11125331Samw 	}
11135331Samw 	aent = result;
11145331Samw 
11155331Samw 	/* USER_OBJ */
11165331Samw 	if (!(list->user_obj.aent_type & USER_OBJ)) {
11175331Samw 		error = EINVAL;
11185331Samw 		goto out;
11195331Samw 	}
11205331Samw 
11215331Samw 	error = acevals_to_aent(&list->user_obj, aent, list, owner, group,
11225331Samw 	    isdir);
11235331Samw 
11245331Samw 	if (error != 0)
11255331Samw 		goto out;
11265331Samw 	++aent;
11275331Samw 	/* USER */
11285331Samw 	vals = NULL;
11295331Samw 	for (vals = avl_first(&list->user); vals != NULL;
11305331Samw 	    vals = AVL_NEXT(&list->user, vals)) {
11315331Samw 		if (!(vals->aent_type & USER)) {
11325331Samw 			error = EINVAL;
11335331Samw 			goto out;
11345331Samw 		}
11355331Samw 		error = acevals_to_aent(vals, aent, list, owner, group,
11365331Samw 		    isdir);
11375331Samw 		if (error != 0)
11385331Samw 			goto out;
11395331Samw 		++aent;
11405331Samw 	}
11415331Samw 	/* GROUP_OBJ */
11425331Samw 	if (!(list->group_obj.aent_type & GROUP_OBJ)) {
11435331Samw 		error = EINVAL;
11445331Samw 		goto out;
11455331Samw 	}
11465331Samw 	error = acevals_to_aent(&list->group_obj, aent, list, owner, group,
11475331Samw 	    isdir);
11485331Samw 	if (error != 0)
11495331Samw 		goto out;
11505331Samw 	++aent;
11515331Samw 	/* GROUP */
11525331Samw 	vals = NULL;
11535331Samw 	for (vals = avl_first(&list->group); vals != NULL;
11545331Samw 	    vals = AVL_NEXT(&list->group, vals)) {
11555331Samw 		if (!(vals->aent_type & GROUP)) {
11565331Samw 			error = EINVAL;
11575331Samw 			goto out;
11585331Samw 		}
11595331Samw 		error = acevals_to_aent(vals, aent, list, owner, group,
11605331Samw 		    isdir);
11615331Samw 		if (error != 0)
11625331Samw 			goto out;
11635331Samw 		++aent;
11645331Samw 	}
11655331Samw 	/*
11665331Samw 	 * CLASS_OBJ (aka ACL_MASK)
11675331Samw 	 *
11685331Samw 	 * An ACL_MASK is not fabricated if the ACL is a default ACL.
11695331Samw 	 * This is to follow UFS's behavior.
11705331Samw 	 */
11715331Samw 	if ((list->hasmask) || (! list->dfacl_flag)) {
11725331Samw 		if (list->hasmask) {
11735331Samw 			uint32_t flips = ACE_POSIX_SUPPORTED_BITS;
11745331Samw 			if (isdir)
11755331Samw 				flips |= ACE_DELETE_CHILD;
11765331Samw 			error = ace_mask_to_mode(list->acl_mask ^ flips,
11775331Samw 			    &aent->a_perm, isdir);
11785331Samw 			if (error != 0)
11795331Samw 				goto out;
11805331Samw 		} else {
11815331Samw 			/* fabricate the ACL_MASK from the group permissions */
11825331Samw 			error = ace_mask_to_mode(list->group_obj.allowed,
11835331Samw 			    &aent->a_perm, isdir);
11845331Samw 			if (error != 0)
11855331Samw 				goto out;
11865331Samw 		}
11875331Samw 		aent->a_id = 0;
11885331Samw 		aent->a_type = CLASS_OBJ | list->dfacl_flag;
11895331Samw 		++aent;
11905331Samw 	}
11915331Samw 	/* OTHER_OBJ */
11925331Samw 	if (!(list->other_obj.aent_type & OTHER_OBJ)) {
11935331Samw 		error = EINVAL;
11945331Samw 		goto out;
11955331Samw 	}
11965331Samw 	error = acevals_to_aent(&list->other_obj, aent, list, owner, group,
11975331Samw 	    isdir);
11985331Samw 	if (error != 0)
11995331Samw 		goto out;
12005331Samw 	++aent;
12015331Samw 
12025331Samw 	*aclentp = result;
12035331Samw 	*aclcnt = resultcount;
12045331Samw 
12055331Samw out:
12065331Samw 	if (error != 0) {
12075331Samw 		if (result != NULL)
12085331Samw 			cacl_free(result, resultcount * sizeof (aclent_t));
12095331Samw 	}
12105331Samw 
12115331Samw 	return (error);
12125331Samw }
12135331Samw 
12145331Samw 
12155331Samw /*
12165331Samw  * free all data associated with an ace_list
12175331Samw  */
12185331Samw static void
12195331Samw ace_list_free(ace_list_t *al)
12205331Samw {
12215331Samw 	acevals_t *node;
12225331Samw 	void *cookie;
12235331Samw 
12245331Samw 	if (al == NULL)
12255331Samw 		return;
12265331Samw 
12275331Samw 	cookie = NULL;
12285331Samw 	while ((node = avl_destroy_nodes(&al->user, &cookie)) != NULL)
12295331Samw 		cacl_free(node, sizeof (acevals_t));
12305331Samw 	cookie = NULL;
12315331Samw 	while ((node = avl_destroy_nodes(&al->group, &cookie)) != NULL)
12325331Samw 		cacl_free(node, sizeof (acevals_t));
12335331Samw 
12345331Samw 	avl_destroy(&al->user);
12355331Samw 	avl_destroy(&al->group);
12365331Samw 
12375331Samw 	/* free the container itself */
12385331Samw 	cacl_free(al, sizeof (ace_list_t));
12395331Samw }
12405331Samw 
12415331Samw static int
12425331Samw acevals_compare(const void *va, const void *vb)
12435331Samw {
12445331Samw 	const acevals_t *a = va, *b = vb;
12455331Samw 
12465331Samw 	if (a->key == b->key)
12475331Samw 		return (0);
12485331Samw 
12495331Samw 	if (a->key > b->key)
12505331Samw 		return (1);
12515331Samw 
12525331Samw 	else
12535331Samw 		return (-1);
12545331Samw }
12555331Samw 
12565331Samw /*
12575331Samw  * Convert a list of ace_t entries to equivalent regular and default
12585331Samw  * aclent_t lists.  Return error (ENOTSUP) when conversion is not possible.
12595331Samw  */
12605331Samw static int
12615331Samw ln_ace_to_aent(ace_t *ace, int n, uid_t owner, gid_t group,
12625331Samw     aclent_t **aclentp, int *aclcnt, aclent_t **dfaclentp, int *dfaclcnt,
12635331Samw     int isdir)
12645331Samw {
12655331Samw 	int error = 0;
12665331Samw 	ace_t *acep;
12675331Samw 	uint32_t bits;
12685331Samw 	int i;
12695331Samw 	ace_list_t *normacl = NULL, *dfacl = NULL, *acl;
12705331Samw 	acevals_t *vals;
12715331Samw 
12725331Samw 	*aclentp = NULL;
12735331Samw 	*aclcnt = 0;
12745331Samw 	*dfaclentp = NULL;
12755331Samw 	*dfaclcnt = 0;
12765331Samw 
12775331Samw 	/* we need at least user_obj, group_obj, and other_obj */
12785331Samw 	if (n < 6) {
12795331Samw 		error = ENOTSUP;
12805331Samw 		goto out;
12815331Samw 	}
12825331Samw 	if (ace == NULL) {
12835331Samw 		error = EINVAL;
12845331Samw 		goto out;
12855331Samw 	}
12865331Samw 
12875331Samw 	error = cacl_malloc((void **)&normacl, sizeof (ace_list_t));
12885331Samw 	if (error != 0)
12895331Samw 		goto out;
12905331Samw 
12915331Samw 	avl_create(&normacl->user, acevals_compare, sizeof (acevals_t),
12925331Samw 	    offsetof(acevals_t, avl));
12935331Samw 	avl_create(&normacl->group, acevals_compare, sizeof (acevals_t),
12945331Samw 	    offsetof(acevals_t, avl));
12955331Samw 
12965331Samw 	ace_list_init(normacl, 0);
12975331Samw 
12985331Samw 	error = cacl_malloc((void **)&dfacl, sizeof (ace_list_t));
12995331Samw 	if (error != 0)
13005331Samw 		goto out;
13015331Samw 
13025331Samw 	avl_create(&dfacl->user, acevals_compare, sizeof (acevals_t),
13035331Samw 	    offsetof(acevals_t, avl));
13045331Samw 	avl_create(&dfacl->group, acevals_compare, sizeof (acevals_t),
13055331Samw 	    offsetof(acevals_t, avl));
13065331Samw 	ace_list_init(dfacl, ACL_DEFAULT);
13075331Samw 
13085331Samw 	/* process every ace_t... */
13095331Samw 	for (i = 0; i < n; i++) {
13105331Samw 		acep = &ace[i];
13115331Samw 
13125331Samw 		/* rule out certain cases quickly */
13135331Samw 		error = ace_to_aent_legal(acep);
13145331Samw 		if (error != 0)
13155331Samw 			goto out;
13165331Samw 
13175331Samw 		/*
13185331Samw 		 * Turn off these bits in order to not have to worry about
13195331Samw 		 * them when doing the checks for compliments.
13205331Samw 		 */
13215331Samw 		acep->a_access_mask &= ~(ACE_WRITE_OWNER | ACE_DELETE |
13225331Samw 		    ACE_SYNCHRONIZE | ACE_WRITE_ATTRIBUTES |
13235331Samw 		    ACE_READ_NAMED_ATTRS | ACE_WRITE_NAMED_ATTRS);
13245331Samw 
13255331Samw 		/* see if this should be a regular or default acl */
13265331Samw 		bits = acep->a_flags &
13275331Samw 		    (ACE_INHERIT_ONLY_ACE |
13285331Samw 		    ACE_FILE_INHERIT_ACE |
13295331Samw 		    ACE_DIRECTORY_INHERIT_ACE);
13305331Samw 		if (bits != 0) {
13315331Samw 			/* all or nothing on these inherit bits */
13325331Samw 			if (bits != (ACE_INHERIT_ONLY_ACE |
13335331Samw 			    ACE_FILE_INHERIT_ACE |
13345331Samw 			    ACE_DIRECTORY_INHERIT_ACE)) {
13355331Samw 				error = ENOTSUP;
13365331Samw 				goto out;
13375331Samw 			}
13385331Samw 			acl = dfacl;
13395331Samw 		} else {
13405331Samw 			acl = normacl;
13415331Samw 		}
13425331Samw 
13435331Samw 		if ((acep->a_flags & ACE_OWNER)) {
13445331Samw 			if (acl->state > ace_user_obj) {
13455331Samw 				error = ENOTSUP;
13465331Samw 				goto out;
13475331Samw 			}
13485331Samw 			acl->state = ace_user_obj;
13495331Samw 			acl->seen |= USER_OBJ;
13505331Samw 			vals = &acl->user_obj;
13515331Samw 			vals->aent_type = USER_OBJ | acl->dfacl_flag;
13525331Samw 		} else if ((acep->a_flags & ACE_EVERYONE)) {
13535331Samw 			acl->state = ace_other_obj;
13545331Samw 			acl->seen |= OTHER_OBJ;
13555331Samw 			vals = &acl->other_obj;
13565331Samw 			vals->aent_type = OTHER_OBJ | acl->dfacl_flag;
13575331Samw 		} else if (acep->a_flags & ACE_IDENTIFIER_GROUP) {
13585331Samw 			if (acl->state > ace_group) {
13595331Samw 				error = ENOTSUP;
13605331Samw 				goto out;
13615331Samw 			}
13625331Samw 			if ((acep->a_flags & ACE_GROUP)) {
13635331Samw 				acl->seen |= GROUP_OBJ;
13645331Samw 				vals = &acl->group_obj;
13655331Samw 				vals->aent_type = GROUP_OBJ | acl->dfacl_flag;
13665331Samw 			} else {
13675331Samw 				acl->seen |= GROUP;
13685331Samw 				vals = acevals_find(acep, &acl->group,
13695331Samw 				    &acl->numgroups);
13705331Samw 				if (vals == NULL) {
13715331Samw 					error = ENOMEM;
13725331Samw 					goto out;
13735331Samw 				}
13745331Samw 				vals->aent_type = GROUP | acl->dfacl_flag;
13755331Samw 			}
13765331Samw 			acl->state = ace_group;
13775331Samw 		} else {
13785331Samw 			if (acl->state > ace_user) {
13795331Samw 				error = ENOTSUP;
13805331Samw 				goto out;
13815331Samw 			}
13825331Samw 			acl->state = ace_user;
13835331Samw 			acl->seen |= USER;
13845331Samw 			vals = acevals_find(acep, &acl->user,
13855331Samw 			    &acl->numusers);
13865331Samw 			if (vals == NULL) {
13875331Samw 				error = ENOMEM;
13885331Samw 				goto out;
13895331Samw 			}
13905331Samw 			vals->aent_type = USER | acl->dfacl_flag;
13915331Samw 		}
13925331Samw 
13935331Samw 		if (!(acl->state > ace_unused)) {
13945331Samw 			error = EINVAL;
13955331Samw 			goto out;
13965331Samw 		}
13975331Samw 
13985331Samw 		if (acep->a_type == ACE_ACCESS_ALLOWED_ACE_TYPE) {
13995331Samw 			/* no more than one allowed per aclent_t */
14005331Samw 			if (vals->allowed != ACE_MASK_UNDEFINED) {
14015331Samw 				error = ENOTSUP;
14025331Samw 				goto out;
14035331Samw 			}
14045331Samw 			vals->allowed = acep->a_access_mask;
14055331Samw 		} else {
14065331Samw 			/*
14075331Samw 			 * it's a DENY; if there was a previous DENY, it
14085331Samw 			 * must have been an ACL_MASK.
14095331Samw 			 */
14105331Samw 			if (vals->denied != ACE_MASK_UNDEFINED) {
14115331Samw 				/* ACL_MASK is for USER and GROUP only */
14125331Samw 				if ((acl->state != ace_user) &&
14135331Samw 				    (acl->state != ace_group)) {
14145331Samw 					error = ENOTSUP;
14155331Samw 					goto out;
14165331Samw 				}
14175331Samw 
14185331Samw 				if (! acl->hasmask) {
14195331Samw 					acl->hasmask = 1;
14205331Samw 					acl->acl_mask = vals->denied;
14215331Samw 				/* check for mismatched ACL_MASK emulations */
14225331Samw 				} else if (acl->acl_mask != vals->denied) {
14235331Samw 					error = ENOTSUP;
14245331Samw 					goto out;
14255331Samw 				}
14265331Samw 				vals->mask = vals->denied;
14275331Samw 			}
14285331Samw 			vals->denied = acep->a_access_mask;
14295331Samw 		}
14305331Samw 	}
14315331Samw 
14325331Samw 	/* done collating; produce the aclent_t lists */
14335331Samw 	if (normacl->state != ace_unused) {
14345331Samw 		error = ace_list_to_aent(normacl, aclentp, aclcnt,
14355331Samw 		    owner, group, isdir);
14365331Samw 		if (error != 0) {
14375331Samw 			goto out;
14385331Samw 		}
14395331Samw 	}
14405331Samw 	if (dfacl->state != ace_unused) {
14415331Samw 		error = ace_list_to_aent(dfacl, dfaclentp, dfaclcnt,
14425331Samw 		    owner, group, isdir);
14435331Samw 		if (error != 0) {
14445331Samw 			goto out;
14455331Samw 		}
14465331Samw 	}
14475331Samw 
14485331Samw out:
14495331Samw 	if (normacl != NULL)
14505331Samw 		ace_list_free(normacl);
14515331Samw 	if (dfacl != NULL)
14525331Samw 		ace_list_free(dfacl);
14535331Samw 
14545331Samw 	return (error);
14555331Samw }
14565331Samw 
14575331Samw static int
14585331Samw convert_ace_to_aent(ace_t *acebufp, int acecnt, int isdir,
14595331Samw     uid_t owner, gid_t group, aclent_t **retaclentp, int *retaclcnt)
14605331Samw {
14615331Samw 	int error = 0;
14625331Samw 	aclent_t *aclentp, *dfaclentp;
14635331Samw 	int aclcnt, dfaclcnt;
14645331Samw 	int aclsz, dfaclsz;
14655331Samw 
14665331Samw 	error = ln_ace_to_aent(acebufp, acecnt, owner, group,
14675331Samw 	    &aclentp, &aclcnt, &dfaclentp, &dfaclcnt, isdir);
14685331Samw 
14695331Samw 	if (error)
14705331Samw 		return (error);
14715331Samw 
14725331Samw 
14735331Samw 	if (dfaclcnt != 0) {
14745331Samw 		/*
14755331Samw 		 * Slap aclentp and dfaclentp into a single array.
14765331Samw 		 */
14775331Samw 		aclsz = sizeof (aclent_t) * aclcnt;
14785331Samw 		dfaclsz = sizeof (aclent_t) * dfaclcnt;
14795331Samw 		aclentp = cacl_realloc(aclentp, aclsz, aclsz + dfaclsz);
14805331Samw 		if (aclentp != NULL) {
14815331Samw 			(void) memcpy(aclentp + aclcnt, dfaclentp, dfaclsz);
14825331Samw 		} else {
14835331Samw 			error = ENOMEM;
14845331Samw 		}
14855331Samw 	}
14865331Samw 
14875331Samw 	if (aclentp) {
14885331Samw 		*retaclentp = aclentp;
14895331Samw 		*retaclcnt = aclcnt + dfaclcnt;
14905331Samw 	}
14915331Samw 
14925331Samw 	if (dfaclentp)
14935331Samw 		cacl_free(dfaclentp, dfaclsz);
14945331Samw 
14955331Samw 	return (error);
14965331Samw }
14975331Samw 
14985331Samw 
14995331Samw int
15005331Samw acl_translate(acl_t *aclp, int target_flavor, int isdir, uid_t owner,
15015331Samw     gid_t group)
15025331Samw {
15035331Samw 	int aclcnt;
15045331Samw 	void *acldata;
15055331Samw 	int error;
15065331Samw 
15075331Samw 	/*
15085331Samw 	 * See if we need to translate
15095331Samw 	 */
15105331Samw 	if ((target_flavor == _ACL_ACE_ENABLED && aclp->acl_type == ACE_T) ||
15115331Samw 	    (target_flavor == _ACL_ACLENT_ENABLED &&
15125331Samw 	    aclp->acl_type == ACLENT_T))
15135331Samw 		return (0);
15145331Samw 
15155331Samw 	if (target_flavor == -1) {
15165331Samw 		error = EINVAL;
15175331Samw 		goto out;
15185331Samw 	}
15195331Samw 
15205331Samw 	if (target_flavor ==  _ACL_ACE_ENABLED &&
15215331Samw 	    aclp->acl_type == ACLENT_T) {
15225331Samw 		error = convert_aent_to_ace(aclp->acl_aclp,
15235331Samw 		    aclp->acl_cnt, isdir, (ace_t **)&acldata, &aclcnt);
15245331Samw 		if (error)
15255331Samw 			goto out;
15265331Samw 
15275331Samw 	} else if (target_flavor == _ACL_ACLENT_ENABLED &&
15285331Samw 	    aclp->acl_type == ACE_T) {
15295331Samw 		error = convert_ace_to_aent(aclp->acl_aclp, aclp->acl_cnt,
15305331Samw 		    isdir, owner, group, (aclent_t **)&acldata, &aclcnt);
15315331Samw 		if (error)
15325331Samw 			goto out;
15335331Samw 	} else {
15345331Samw 		error = ENOTSUP;
15355331Samw 		goto out;
15365331Samw 	}
15375331Samw 
15385331Samw 	/*
15395331Samw 	 * replace old acl with newly translated acl
15405331Samw 	 */
15415331Samw 	cacl_free(aclp->acl_aclp, aclp->acl_cnt * aclp->acl_entry_size);
15425331Samw 	aclp->acl_aclp = acldata;
15435331Samw 	aclp->acl_cnt = aclcnt;
15445331Samw 	if (target_flavor == _ACL_ACE_ENABLED) {
15455331Samw 		aclp->acl_type = ACE_T;
15465331Samw 		aclp->acl_entry_size = sizeof (ace_t);
15475331Samw 	} else {
15485331Samw 		aclp->acl_type = ACLENT_T;
15495331Samw 		aclp->acl_entry_size = sizeof (aclent_t);
15505331Samw 	}
15515331Samw 	return (0);
15525331Samw 
15535331Samw out:
15545331Samw 
15555331Samw #if !defined(_KERNEL)
15565331Samw 	errno = error;
15575331Samw 	return (-1);
15585331Samw #else
15595331Samw 	return (error);
15605331Samw #endif
15615331Samw }
1562*12164SMark.Shellenbaum@Sun.COM 
1563*12164SMark.Shellenbaum@Sun.COM #define	SET_ACE(acl, index, who, mask, type, flags) { \
1564*12164SMark.Shellenbaum@Sun.COM 	acl[0][index].a_who = (uint32_t)who; \
1565*12164SMark.Shellenbaum@Sun.COM 	acl[0][index].a_type = type; \
1566*12164SMark.Shellenbaum@Sun.COM 	acl[0][index].a_flags = flags; \
1567*12164SMark.Shellenbaum@Sun.COM 	acl[0][index++].a_access_mask = mask; \
1568*12164SMark.Shellenbaum@Sun.COM }
1569*12164SMark.Shellenbaum@Sun.COM 
1570*12164SMark.Shellenbaum@Sun.COM void
1571*12164SMark.Shellenbaum@Sun.COM acl_trivial_access_masks(mode_t mode, uint32_t *allow0, uint32_t *deny1,
1572*12164SMark.Shellenbaum@Sun.COM     uint32_t *deny2, uint32_t *owner, uint32_t *group, uint32_t *everyone)
1573*12164SMark.Shellenbaum@Sun.COM {
1574*12164SMark.Shellenbaum@Sun.COM 	*deny1 = *deny2 = *allow0 = *group = 0;
1575*12164SMark.Shellenbaum@Sun.COM 
1576*12164SMark.Shellenbaum@Sun.COM 	if (!(mode & S_IRUSR) && (mode & (S_IRGRP|S_IROTH)))
1577*12164SMark.Shellenbaum@Sun.COM 		*deny1 |= ACE_READ_DATA;
1578*12164SMark.Shellenbaum@Sun.COM 	if (!(mode & S_IWUSR) && (mode & (S_IWGRP|S_IWOTH)))
1579*12164SMark.Shellenbaum@Sun.COM 		*deny1 |= ACE_WRITE_DATA;
1580*12164SMark.Shellenbaum@Sun.COM 	if (!(mode & S_IXUSR) && (mode & (S_IXGRP|S_IXOTH)))
1581*12164SMark.Shellenbaum@Sun.COM 		*deny1 |= ACE_EXECUTE;
1582*12164SMark.Shellenbaum@Sun.COM 
1583*12164SMark.Shellenbaum@Sun.COM 	if (!(mode & S_IRGRP) && (mode & S_IROTH))
1584*12164SMark.Shellenbaum@Sun.COM 		*deny2 = ACE_READ_DATA;
1585*12164SMark.Shellenbaum@Sun.COM 	if (!(mode & S_IWGRP) && (mode & S_IWOTH))
1586*12164SMark.Shellenbaum@Sun.COM 		*deny2 |= ACE_WRITE_DATA;
1587*12164SMark.Shellenbaum@Sun.COM 	if (!(mode & S_IXGRP) && (mode & S_IXOTH))
1588*12164SMark.Shellenbaum@Sun.COM 		*deny2 |= ACE_EXECUTE;
1589*12164SMark.Shellenbaum@Sun.COM 
1590*12164SMark.Shellenbaum@Sun.COM 	if ((mode & S_IRUSR) && (!(mode & S_IRGRP) && (mode & S_IROTH)))
1591*12164SMark.Shellenbaum@Sun.COM 		*allow0 |= ACE_READ_DATA;
1592*12164SMark.Shellenbaum@Sun.COM 	if ((mode & S_IWUSR) && (!(mode & S_IWGRP) && (mode & S_IWOTH)))
1593*12164SMark.Shellenbaum@Sun.COM 		*allow0 |= ACE_WRITE_DATA;
1594*12164SMark.Shellenbaum@Sun.COM 	if ((mode & S_IXUSR) && (!(mode & S_IXGRP) && (mode & S_IXOTH)))
1595*12164SMark.Shellenbaum@Sun.COM 		*allow0 |= ACE_EXECUTE;
1596*12164SMark.Shellenbaum@Sun.COM 
1597*12164SMark.Shellenbaum@Sun.COM 	*owner = ACE_WRITE_ATTRIBUTES|ACE_WRITE_OWNER|ACE_WRITE_ACL|
1598*12164SMark.Shellenbaum@Sun.COM 	    ACE_WRITE_NAMED_ATTRS|ACE_READ_ACL|ACE_READ_ATTRIBUTES|
1599*12164SMark.Shellenbaum@Sun.COM 	    ACE_READ_NAMED_ATTRS|ACE_SYNCHRONIZE;
1600*12164SMark.Shellenbaum@Sun.COM 	if (mode & S_IRUSR)
1601*12164SMark.Shellenbaum@Sun.COM 		*owner |= ACE_READ_DATA;
1602*12164SMark.Shellenbaum@Sun.COM 	if (mode & S_IWUSR)
1603*12164SMark.Shellenbaum@Sun.COM 		*owner |= ACE_WRITE_DATA|ACE_APPEND_DATA;
1604*12164SMark.Shellenbaum@Sun.COM 	if (mode & S_IXUSR)
1605*12164SMark.Shellenbaum@Sun.COM 		*owner |= ACE_EXECUTE;
1606*12164SMark.Shellenbaum@Sun.COM 
1607*12164SMark.Shellenbaum@Sun.COM 	*group = ACE_READ_ACL|ACE_READ_ATTRIBUTES| ACE_READ_NAMED_ATTRS|
1608*12164SMark.Shellenbaum@Sun.COM 	    ACE_SYNCHRONIZE;
1609*12164SMark.Shellenbaum@Sun.COM 	if (mode & S_IRGRP)
1610*12164SMark.Shellenbaum@Sun.COM 		*group |= ACE_READ_DATA;
1611*12164SMark.Shellenbaum@Sun.COM 	if (mode & S_IWGRP)
1612*12164SMark.Shellenbaum@Sun.COM 		*group |= ACE_WRITE_DATA|ACE_APPEND_DATA;
1613*12164SMark.Shellenbaum@Sun.COM 	if (mode & S_IXGRP)
1614*12164SMark.Shellenbaum@Sun.COM 		*group |= ACE_EXECUTE;
1615*12164SMark.Shellenbaum@Sun.COM 
1616*12164SMark.Shellenbaum@Sun.COM 	*everyone = ACE_READ_ACL|ACE_READ_ATTRIBUTES| ACE_READ_NAMED_ATTRS|
1617*12164SMark.Shellenbaum@Sun.COM 	    ACE_SYNCHRONIZE;
1618*12164SMark.Shellenbaum@Sun.COM 	if (mode & S_IROTH)
1619*12164SMark.Shellenbaum@Sun.COM 		*everyone |= ACE_READ_DATA;
1620*12164SMark.Shellenbaum@Sun.COM 	if (mode & S_IWOTH)
1621*12164SMark.Shellenbaum@Sun.COM 		*everyone |= ACE_WRITE_DATA|ACE_APPEND_DATA;
1622*12164SMark.Shellenbaum@Sun.COM 	if (mode & S_IXOTH)
1623*12164SMark.Shellenbaum@Sun.COM 		*everyone |= ACE_EXECUTE;
1624*12164SMark.Shellenbaum@Sun.COM }
1625*12164SMark.Shellenbaum@Sun.COM 
1626*12164SMark.Shellenbaum@Sun.COM int
1627*12164SMark.Shellenbaum@Sun.COM acl_trivial_create(mode_t mode, ace_t **acl, int *count)
1628*12164SMark.Shellenbaum@Sun.COM {
1629*12164SMark.Shellenbaum@Sun.COM 	uint32_t	deny1, deny2;
1630*12164SMark.Shellenbaum@Sun.COM 	uint32_t	allow0;
1631*12164SMark.Shellenbaum@Sun.COM 	uint32_t	owner, group, everyone;
1632*12164SMark.Shellenbaum@Sun.COM 	int 		index = 0;
1633*12164SMark.Shellenbaum@Sun.COM 	int		error;
1634*12164SMark.Shellenbaum@Sun.COM 
1635*12164SMark.Shellenbaum@Sun.COM 	*count = 3;
1636*12164SMark.Shellenbaum@Sun.COM 	acl_trivial_access_masks(mode, &allow0, &deny1, &deny2, &owner, &group,
1637*12164SMark.Shellenbaum@Sun.COM 	    &everyone);
1638*12164SMark.Shellenbaum@Sun.COM 
1639*12164SMark.Shellenbaum@Sun.COM 	if (allow0)
1640*12164SMark.Shellenbaum@Sun.COM 		(*count)++;
1641*12164SMark.Shellenbaum@Sun.COM 	if (deny1)
1642*12164SMark.Shellenbaum@Sun.COM 		(*count)++;
1643*12164SMark.Shellenbaum@Sun.COM 	if (deny2)
1644*12164SMark.Shellenbaum@Sun.COM 		(*count)++;
1645*12164SMark.Shellenbaum@Sun.COM 
1646*12164SMark.Shellenbaum@Sun.COM 	if ((error = cacl_malloc((void **)acl, *count * sizeof (ace_t))) != 0)
1647*12164SMark.Shellenbaum@Sun.COM 		return (error);
1648*12164SMark.Shellenbaum@Sun.COM 
1649*12164SMark.Shellenbaum@Sun.COM 	if (allow0) {
1650*12164SMark.Shellenbaum@Sun.COM 		SET_ACE(acl, index, -1, allow0, ACE_ACCESS_ALLOWED_ACE_TYPE,
1651*12164SMark.Shellenbaum@Sun.COM 		    ACE_OWNER);
1652*12164SMark.Shellenbaum@Sun.COM 	}
1653*12164SMark.Shellenbaum@Sun.COM 	if (deny1) {
1654*12164SMark.Shellenbaum@Sun.COM 		SET_ACE(acl, index, -1, deny1, ACE_ACCESS_DENIED_ACE_TYPE,
1655*12164SMark.Shellenbaum@Sun.COM 		    ACE_OWNER);
1656*12164SMark.Shellenbaum@Sun.COM 	}
1657*12164SMark.Shellenbaum@Sun.COM 	if (deny2) {
1658*12164SMark.Shellenbaum@Sun.COM 		SET_ACE(acl, index, -1, deny2, ACE_ACCESS_DENIED_ACE_TYPE,
1659*12164SMark.Shellenbaum@Sun.COM 		    ACE_GROUP|ACE_IDENTIFIER_GROUP);
1660*12164SMark.Shellenbaum@Sun.COM 	}
1661*12164SMark.Shellenbaum@Sun.COM 
1662*12164SMark.Shellenbaum@Sun.COM 	SET_ACE(acl, index, -1, owner, ACE_ACCESS_ALLOWED_ACE_TYPE, ACE_OWNER);
1663*12164SMark.Shellenbaum@Sun.COM 	SET_ACE(acl, index, -1, group, ACE_ACCESS_ALLOWED_ACE_TYPE,
1664*12164SMark.Shellenbaum@Sun.COM 	    ACE_IDENTIFIER_GROUP|ACE_GROUP);
1665*12164SMark.Shellenbaum@Sun.COM 	SET_ACE(acl, index, -1, everyone, ACE_ACCESS_ALLOWED_ACE_TYPE,
1666*12164SMark.Shellenbaum@Sun.COM 	    ACE_EVERYONE);
1667*12164SMark.Shellenbaum@Sun.COM 
1668*12164SMark.Shellenbaum@Sun.COM 	return (0);
1669*12164SMark.Shellenbaum@Sun.COM }
1670*12164SMark.Shellenbaum@Sun.COM 
1671*12164SMark.Shellenbaum@Sun.COM /*
1672*12164SMark.Shellenbaum@Sun.COM  * ace_trivial:
1673*12164SMark.Shellenbaum@Sun.COM  * determine whether an ace_t acl is trivial
1674*12164SMark.Shellenbaum@Sun.COM  *
1675*12164SMark.Shellenbaum@Sun.COM  * Trivialness implies that the acl is composed of only
1676*12164SMark.Shellenbaum@Sun.COM  * owner, group, everyone entries.  ACL can't
1677*12164SMark.Shellenbaum@Sun.COM  * have read_acl denied, and write_owner/write_acl/write_attributes
1678*12164SMark.Shellenbaum@Sun.COM  * can only be owner@ entry.
1679*12164SMark.Shellenbaum@Sun.COM  */
1680*12164SMark.Shellenbaum@Sun.COM int
1681*12164SMark.Shellenbaum@Sun.COM ace_trivial_common(void *acep, int aclcnt,
1682*12164SMark.Shellenbaum@Sun.COM     uint64_t (*walk)(void *, uint64_t, int aclcnt,
1683*12164SMark.Shellenbaum@Sun.COM     uint16_t *, uint16_t *, uint32_t *))
1684*12164SMark.Shellenbaum@Sun.COM {
1685*12164SMark.Shellenbaum@Sun.COM 	uint16_t flags;
1686*12164SMark.Shellenbaum@Sun.COM 	uint32_t mask;
1687*12164SMark.Shellenbaum@Sun.COM 	uint16_t type;
1688*12164SMark.Shellenbaum@Sun.COM 	uint64_t cookie = 0;
1689*12164SMark.Shellenbaum@Sun.COM 
1690*12164SMark.Shellenbaum@Sun.COM 	while (cookie = walk(acep, cookie, aclcnt, &flags, &type, &mask)) {
1691*12164SMark.Shellenbaum@Sun.COM 		switch (flags & ACE_TYPE_FLAGS) {
1692*12164SMark.Shellenbaum@Sun.COM 		case ACE_OWNER:
1693*12164SMark.Shellenbaum@Sun.COM 		case ACE_GROUP|ACE_IDENTIFIER_GROUP:
1694*12164SMark.Shellenbaum@Sun.COM 		case ACE_EVERYONE:
1695*12164SMark.Shellenbaum@Sun.COM 			break;
1696*12164SMark.Shellenbaum@Sun.COM 		default:
1697*12164SMark.Shellenbaum@Sun.COM 			return (1);
1698*12164SMark.Shellenbaum@Sun.COM 
1699*12164SMark.Shellenbaum@Sun.COM 		}
1700*12164SMark.Shellenbaum@Sun.COM 
1701*12164SMark.Shellenbaum@Sun.COM 		if (flags & (ACE_FILE_INHERIT_ACE|
1702*12164SMark.Shellenbaum@Sun.COM 		    ACE_DIRECTORY_INHERIT_ACE|ACE_NO_PROPAGATE_INHERIT_ACE|
1703*12164SMark.Shellenbaum@Sun.COM 		    ACE_INHERIT_ONLY_ACE))
1704*12164SMark.Shellenbaum@Sun.COM 			return (1);
1705*12164SMark.Shellenbaum@Sun.COM 
1706*12164SMark.Shellenbaum@Sun.COM 		/*
1707*12164SMark.Shellenbaum@Sun.COM 		 * Special check for some special bits
1708*12164SMark.Shellenbaum@Sun.COM 		 *
1709*12164SMark.Shellenbaum@Sun.COM 		 * Don't allow anybody to deny reading basic
1710*12164SMark.Shellenbaum@Sun.COM 		 * attributes or a files ACL.
1711*12164SMark.Shellenbaum@Sun.COM 		 */
1712*12164SMark.Shellenbaum@Sun.COM 		if ((mask & (ACE_READ_ACL|ACE_READ_ATTRIBUTES)) &&
1713*12164SMark.Shellenbaum@Sun.COM 		    (type == ACE_ACCESS_DENIED_ACE_TYPE))
1714*12164SMark.Shellenbaum@Sun.COM 			return (1);
1715*12164SMark.Shellenbaum@Sun.COM 
1716*12164SMark.Shellenbaum@Sun.COM 		/*
1717*12164SMark.Shellenbaum@Sun.COM 		 * Delete permissions are never set by default
1718*12164SMark.Shellenbaum@Sun.COM 		 */
1719*12164SMark.Shellenbaum@Sun.COM 		if (mask & (ACE_DELETE|ACE_DELETE_CHILD))
1720*12164SMark.Shellenbaum@Sun.COM 			return (1);
1721*12164SMark.Shellenbaum@Sun.COM 		/*
1722*12164SMark.Shellenbaum@Sun.COM 		 * only allow owner@ to have
1723*12164SMark.Shellenbaum@Sun.COM 		 * write_acl/write_owner/write_attributes/write_xattr/
1724*12164SMark.Shellenbaum@Sun.COM 		 */
1725*12164SMark.Shellenbaum@Sun.COM 		if (type == ACE_ACCESS_ALLOWED_ACE_TYPE &&
1726*12164SMark.Shellenbaum@Sun.COM 		    (!(flags & ACE_OWNER) && (mask &
1727*12164SMark.Shellenbaum@Sun.COM 		    (ACE_WRITE_OWNER|ACE_WRITE_ACL| ACE_WRITE_ATTRIBUTES|
1728*12164SMark.Shellenbaum@Sun.COM 		    ACE_WRITE_NAMED_ATTRS))))
1729*12164SMark.Shellenbaum@Sun.COM 			return (1);
1730*12164SMark.Shellenbaum@Sun.COM 
1731*12164SMark.Shellenbaum@Sun.COM 	}
1732*12164SMark.Shellenbaum@Sun.COM 	return (0);
1733*12164SMark.Shellenbaum@Sun.COM }
1734*12164SMark.Shellenbaum@Sun.COM 
1735*12164SMark.Shellenbaum@Sun.COM uint64_t
1736*12164SMark.Shellenbaum@Sun.COM ace_walk(void *datap, uint64_t cookie, int aclcnt, uint16_t *flags,
1737*12164SMark.Shellenbaum@Sun.COM     uint16_t *type, uint32_t *mask)
1738*12164SMark.Shellenbaum@Sun.COM {
1739*12164SMark.Shellenbaum@Sun.COM 	ace_t *acep = datap;
1740*12164SMark.Shellenbaum@Sun.COM 
1741*12164SMark.Shellenbaum@Sun.COM 	if (cookie >= aclcnt)
1742*12164SMark.Shellenbaum@Sun.COM 		return (0);
1743*12164SMark.Shellenbaum@Sun.COM 
1744*12164SMark.Shellenbaum@Sun.COM 	*flags = acep[cookie].a_flags;
1745*12164SMark.Shellenbaum@Sun.COM 	*type = acep[cookie].a_type;
1746*12164SMark.Shellenbaum@Sun.COM 	*mask = acep[cookie++].a_access_mask;
1747*12164SMark.Shellenbaum@Sun.COM 
1748*12164SMark.Shellenbaum@Sun.COM 	return (cookie);
1749*12164SMark.Shellenbaum@Sun.COM }
1750*12164SMark.Shellenbaum@Sun.COM 
1751*12164SMark.Shellenbaum@Sun.COM int
1752*12164SMark.Shellenbaum@Sun.COM ace_trivial(ace_t *acep, int aclcnt)
1753*12164SMark.Shellenbaum@Sun.COM {
1754*12164SMark.Shellenbaum@Sun.COM 	return (ace_trivial_common(acep, aclcnt, ace_walk));
1755*12164SMark.Shellenbaum@Sun.COM }
1756