1789Sahrens /*
2789Sahrens  * CDDL HEADER START
3789Sahrens  *
4789Sahrens  * The contents of this file are subject to the terms of the
5789Sahrens  * Common Development and Distribution License, Version 1.0 only
6789Sahrens  * (the "License").  You may not use this file except in compliance
7789Sahrens  * with the License.
8789Sahrens  *
9789Sahrens  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10789Sahrens  * or http://www.opensolaris.org/os/licensing.
11789Sahrens  * See the License for the specific language governing permissions
12789Sahrens  * and limitations under the License.
13789Sahrens  *
14789Sahrens  * When distributing Covered Code, include this CDDL HEADER in each
15789Sahrens  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16789Sahrens  * If applicable, add the following below this CDDL HEADER, with the
17789Sahrens  * fields enclosed by brackets "[]" replaced with your own identifying
18789Sahrens  * information: Portions Copyright [yyyy] [name of copyright owner]
19789Sahrens  *
20789Sahrens  * CDDL HEADER END
21789Sahrens  */
22789Sahrens /*
23*1231Smarks  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24789Sahrens  * Use is subject to license terms.
25789Sahrens  */
26789Sahrens 
27789Sahrens #pragma ident	"%Z%%M%	%I%	%E% SMI"
28789Sahrens 
29789Sahrens #include <stdlib.h>
30789Sahrens #include <string.h>
31789Sahrens #include <unistd.h>
32789Sahrens #include <limits.h>
33789Sahrens #include <grp.h>
34789Sahrens #include <pwd.h>
35789Sahrens #include <sys/types.h>
36789Sahrens #include <sys/acl.h>
37789Sahrens #include <errno.h>
38789Sahrens #include <sys/stat.h>
39789Sahrens #include <locale.h>
40789Sahrens #include <aclutils.h>
41789Sahrens #include <acl_common.h>
42789Sahrens 
43789Sahrens #define	ACL_PATH	0
44789Sahrens #define	ACL_FD		1
45789Sahrens 
46789Sahrens #define	ACE_POSIX_SUPPORTED_BITS (ACE_READ_DATA | \
47789Sahrens     ACE_WRITE_DATA | ACE_APPEND_DATA | ACE_EXECUTE | \
48789Sahrens     ACE_READ_ATTRIBUTES | ACE_READ_ACL | ACE_WRITE_ACL)
49789Sahrens 
50789Sahrens 
51789Sahrens #define	ACL_SYNCHRONIZE_SET_ALLOW		0x0000002
52789Sahrens #define	ACL_SYNCHRONIZE_SET_DENY		0x0000001
53789Sahrens 
54789Sahrens #define	ACL_WRITE_OWNER_SET_ALLOW		0x0000020
55789Sahrens #define	ACL_WRITE_OWNER_SET_DENY		0x0000010
56789Sahrens 
57789Sahrens #define	ACL_WRITE_ATTRS_OWNER_SET_ALLOW		0x0002000
58789Sahrens #define	ACL_WRITE_ATTRS_OWNER_SET_DENY		0x0001000
59789Sahrens 
60789Sahrens #define	ACL_WRITE_ATTRS_WRITER_SET_DENY		0x0010000
61789Sahrens 
62789Sahrens #define	ACL_DELETE_SET_ALLOW			0x0000200
63789Sahrens #define	ACL_DELETE_SET_DENY			0x0000100
64789Sahrens 
65789Sahrens #define	ACL_READ_NAMED_READER_SET_ALLOW		0x2000000
66789Sahrens 
67789Sahrens #define	ACL_WRITE_NAMED_WRITER_SET_ALLOW	0x0200000
68789Sahrens #define	ACL_WRITE_NAMED_WRITER_SET_DENY		0x0100000
69789Sahrens 
70789Sahrens #define	ACL_WRITE_ATTRS_OWNER_SET_ALLOW		0x0002000
71789Sahrens #define	ACL_WRITE_ATTRS_WRITER_SET_ALLOW	0x0020000
72789Sahrens 
73789Sahrens #define	ACL_WRITE_OWNER_ERR_DENY		0x0000040
74789Sahrens #define	ACL_READ_NAMED_READER_SET_DENY		0x1000000
75*1231Smarks 
76789Sahrens typedef union {
77789Sahrens 	const char *file;
78789Sahrens 	int  fd;
79789Sahrens } acl_inp;
80789Sahrens 
81789Sahrens acl_t *
82789Sahrens acl_alloc(enum acl_type type)
83789Sahrens {
84789Sahrens 	acl_t *aclp;
85789Sahrens 
86789Sahrens 	aclp = malloc(sizeof (acl_t));
87789Sahrens 
88789Sahrens 	if (aclp == NULL)
89789Sahrens 		return (NULL);
90789Sahrens 
91789Sahrens 	aclp->acl_aclp = NULL;
92789Sahrens 	aclp->acl_cnt = 0;
93789Sahrens 
94789Sahrens 	switch (type) {
95789Sahrens 	case ACE_T:
96789Sahrens 		aclp->acl_type = ACE_T;
97789Sahrens 		aclp->acl_entry_size = sizeof (ace_t);
98789Sahrens 		break;
99789Sahrens 	case ACLENT_T:
100789Sahrens 		aclp->acl_type = ACLENT_T;
101789Sahrens 		aclp->acl_entry_size = sizeof (aclent_t);
102789Sahrens 		break;
103789Sahrens 	default:
104789Sahrens 		acl_free(aclp);
105789Sahrens 		aclp = NULL;
106789Sahrens 	}
107789Sahrens 	return (aclp);
108789Sahrens }
109789Sahrens 
110789Sahrens /*
111789Sahrens  * Free acl_t structure
112789Sahrens  */
113789Sahrens void
114789Sahrens acl_free(acl_t *aclp)
115789Sahrens {
116789Sahrens 	if (aclp == NULL)
117789Sahrens 		return;
118789Sahrens 
119789Sahrens 	if (aclp->acl_aclp)
120789Sahrens 		free(aclp->acl_aclp);
121789Sahrens 	free(aclp);
122789Sahrens }
123789Sahrens 
124789Sahrens /*
125789Sahrens  * Determine whether a file has a trivial ACL
126789Sahrens  * returns: 	0 = trivial
127789Sahrens  *		1 = nontrivial
128789Sahrens  *		<0 some other system failure, such as ENOENT or EPERM
129789Sahrens  */
130789Sahrens int
131789Sahrens acl_trivial(const char *filename)
132789Sahrens {
133789Sahrens 	int acl_flavor;
134789Sahrens 	int aclcnt;
135789Sahrens 	int cntcmd;
136789Sahrens 	int val = 0;
137789Sahrens 	ace_t *acep;
138789Sahrens 
139789Sahrens 	acl_flavor = pathconf(filename, _PC_ACL_ENABLED);
140789Sahrens 	if (acl_flavor == -1)
141789Sahrens 		return (-1);
142789Sahrens 
143789Sahrens 	if (acl_flavor == _ACL_ACE_ENABLED)
144789Sahrens 		cntcmd = ACE_GETACLCNT;
145789Sahrens 	else
146789Sahrens 		cntcmd = GETACLCNT;
147789Sahrens 
148789Sahrens 	aclcnt = acl(filename, cntcmd, 0, NULL);
149789Sahrens 	if (aclcnt > 0) {
150789Sahrens 		if (acl_flavor == _ACL_ACE_ENABLED) {
151*1231Smarks 			acep = malloc(sizeof (ace_t) * aclcnt);
152*1231Smarks 			if (acep == NULL)
153*1231Smarks 				return (-1);
154*1231Smarks 			if (acl(filename, ACE_GETACL,
155*1231Smarks 			    aclcnt, acep) < 0) {
156*1231Smarks 				free(acep);
157*1231Smarks 				return (-1);
158*1231Smarks 			}
159789Sahrens 
160*1231Smarks 			val = ace_trivial(acep, aclcnt);
161*1231Smarks 			free(acep);
162*1231Smarks 
163789Sahrens 		} else if (aclcnt > MIN_ACL_ENTRIES)
164789Sahrens 			val = 1;
165789Sahrens 	}
166789Sahrens 	return (val);
167789Sahrens }
168789Sahrens 
169789Sahrens static uint32_t
170789Sahrens access_mask_set(int haswriteperm, int hasreadperm, int isowner, int isallow)
171789Sahrens {
172789Sahrens 	uint32_t access_mask = 0;
173789Sahrens 	int acl_produce;
174789Sahrens 	int synchronize_set = 0, write_owner_set = 0;
175789Sahrens 	int delete_set = 0, write_attrs_set = 0;
176789Sahrens 	int read_named_set = 0, write_named_set = 0;
177789Sahrens 
178789Sahrens 	acl_produce = (ACL_SYNCHRONIZE_SET_ALLOW |
179789Sahrens 	    ACL_WRITE_ATTRS_OWNER_SET_ALLOW |
180789Sahrens 	    ACL_WRITE_ATTRS_WRITER_SET_DENY);
181789Sahrens 
182789Sahrens 	if (isallow) {
183789Sahrens 		synchronize_set = ACL_SYNCHRONIZE_SET_ALLOW;
184789Sahrens 		write_owner_set = ACL_WRITE_OWNER_SET_ALLOW;
185789Sahrens 		delete_set = ACL_DELETE_SET_ALLOW;
186789Sahrens 		if (hasreadperm)
187789Sahrens 			read_named_set = ACL_READ_NAMED_READER_SET_ALLOW;
188789Sahrens 		if (haswriteperm)
189789Sahrens 			write_named_set = ACL_WRITE_NAMED_WRITER_SET_ALLOW;
190789Sahrens 		if (isowner)
191789Sahrens 			write_attrs_set = ACL_WRITE_ATTRS_OWNER_SET_ALLOW;
192789Sahrens 		else if (haswriteperm)
193789Sahrens 			write_attrs_set = ACL_WRITE_ATTRS_WRITER_SET_ALLOW;
194789Sahrens 	} else {
195789Sahrens 
196789Sahrens 		synchronize_set = ACL_SYNCHRONIZE_SET_DENY;
197789Sahrens 		write_owner_set = ACL_WRITE_OWNER_SET_DENY;
198789Sahrens 		delete_set = ACL_DELETE_SET_DENY;
199789Sahrens 		if (hasreadperm)
200789Sahrens 			read_named_set = ACL_READ_NAMED_READER_SET_DENY;
201789Sahrens 		if (haswriteperm)
202789Sahrens 			write_named_set = ACL_WRITE_NAMED_WRITER_SET_DENY;
203789Sahrens 		if (isowner)
204789Sahrens 			write_attrs_set = ACL_WRITE_ATTRS_OWNER_SET_DENY;
205789Sahrens 		else if (haswriteperm)
206789Sahrens 			write_attrs_set = ACL_WRITE_ATTRS_WRITER_SET_DENY;
207789Sahrens 		else
208789Sahrens 			/*
209789Sahrens 			 * If the entity is not the owner and does not
210789Sahrens 			 * have write permissions ACE_WRITE_ATTRIBUTES will
211789Sahrens 			 * always go in the DENY ACE.
212789Sahrens 			 */
213789Sahrens 			access_mask |= ACE_WRITE_ATTRIBUTES;
214789Sahrens 	}
215789Sahrens 
216789Sahrens 	if (acl_produce & synchronize_set)
217789Sahrens 		access_mask |= ACE_SYNCHRONIZE;
218789Sahrens 	if (acl_produce & write_owner_set)
219789Sahrens 		access_mask |= ACE_WRITE_OWNER;
220789Sahrens 	if (acl_produce & delete_set)
221789Sahrens 		access_mask |= ACE_DELETE;
222789Sahrens 	if (acl_produce & write_attrs_set)
223789Sahrens 		access_mask |= ACE_WRITE_ATTRIBUTES;
224789Sahrens 	if (acl_produce & read_named_set)
225789Sahrens 		access_mask |= ACE_READ_NAMED_ATTRS;
226789Sahrens 	if (acl_produce & write_named_set)
227789Sahrens 		access_mask |= ACE_WRITE_NAMED_ATTRS;
228789Sahrens 
229789Sahrens 	return (access_mask);
230789Sahrens }
231789Sahrens 
232789Sahrens /*
233789Sahrens  * Given an mode_t, convert it into an access_mask as used
234789Sahrens  * by nfsace, assuming aclent_t -> nfsace semantics.
235789Sahrens  */
236789Sahrens static uint32_t
237789Sahrens mode_to_ace_access(mode_t mode, int isdir, int isowner, int isallow)
238789Sahrens {
239789Sahrens 	uint32_t access = 0;
240789Sahrens 	int haswriteperm = 0;
241789Sahrens 	int hasreadperm = 0;
242789Sahrens 
243789Sahrens 	if (isallow) {
244789Sahrens 		haswriteperm = (mode & 02);
245789Sahrens 		hasreadperm = (mode & 04);
246789Sahrens 	} else {
247789Sahrens 		haswriteperm = !(mode & 02);
248789Sahrens 		hasreadperm = !(mode & 04);
249789Sahrens 	}
250789Sahrens 
251789Sahrens 	/*
252789Sahrens 	 * The following call takes care of correctly setting the following
253789Sahrens 	 * mask bits in the access_mask:
254789Sahrens 	 * ACE_SYNCHRONIZE, ACE_WRITE_OWNER, ACE_DELETE,
255789Sahrens 	 * ACE_WRITE_ATTRIBUTES, ACE_WRITE_NAMED_ATTRS, ACE_READ_NAMED_ATTRS
256789Sahrens 	 */
257789Sahrens 	access = access_mask_set(haswriteperm, hasreadperm, isowner, isallow);
258789Sahrens 
259789Sahrens 	if (isallow) {
260789Sahrens 		access |= ACE_READ_ACL | ACE_READ_ATTRIBUTES;
261789Sahrens 		if (isowner)
262789Sahrens 			access |= ACE_WRITE_ACL;
263789Sahrens 	} else {
264789Sahrens 		if (! isowner)
265789Sahrens 			access |= ACE_WRITE_ACL;
266789Sahrens 	}
267789Sahrens 
268789Sahrens 	/* read */
269789Sahrens 	if (mode & 04) {
270789Sahrens 		access |= ACE_READ_DATA;
271789Sahrens 	}
272789Sahrens 	/* write */
273789Sahrens 	if (mode & 02) {
274789Sahrens 		access |= ACE_WRITE_DATA |
275789Sahrens 		    ACE_APPEND_DATA;
276789Sahrens 		if (isdir)
277789Sahrens 			access |= ACE_DELETE_CHILD;
278789Sahrens 	}
279789Sahrens 	/* exec */
280789Sahrens 	if (mode & 01) {
281789Sahrens 		access |= ACE_EXECUTE;
282789Sahrens 	}
283789Sahrens 
284789Sahrens 	return (access);
285789Sahrens }
286789Sahrens 
287789Sahrens /*
288789Sahrens  * Given an nfsace (presumably an ALLOW entry), make a
289789Sahrens  * corresponding DENY entry at the address given.
290789Sahrens  */
291789Sahrens static void
292789Sahrens ace_make_deny(ace_t *allow, ace_t *deny, int isdir, int isowner)
293789Sahrens {
294789Sahrens 	(void) memcpy(deny, allow, sizeof (ace_t));
295789Sahrens 
296789Sahrens 	deny->a_who = allow->a_who;
297789Sahrens 
298789Sahrens 	deny->a_type = ACE_ACCESS_DENIED_ACE_TYPE;
299789Sahrens 	deny->a_access_mask ^= ACE_POSIX_SUPPORTED_BITS;
300789Sahrens 	if (isdir)
301789Sahrens 		deny->a_access_mask ^= ACE_DELETE_CHILD;
302789Sahrens 
303789Sahrens 	deny->a_access_mask &= ~(ACE_SYNCHRONIZE | ACE_WRITE_OWNER |
304789Sahrens 	    ACE_DELETE | ACE_WRITE_ATTRIBUTES | ACE_READ_NAMED_ATTRS |
305789Sahrens 	    ACE_WRITE_NAMED_ATTRS);
306789Sahrens 	deny->a_access_mask |= access_mask_set((allow->a_access_mask &
307789Sahrens 	    ACE_WRITE_DATA), (allow->a_access_mask & ACE_READ_DATA), isowner,
308789Sahrens 	    B_FALSE);
309789Sahrens }
310789Sahrens /*
311789Sahrens  * Make an initial pass over an array of aclent_t's.  Gather
312789Sahrens  * information such as an ACL_MASK (if any), number of users,
313789Sahrens  * number of groups, and whether the array needs to be sorted.
314789Sahrens  */
315789Sahrens static int
316789Sahrens ln_aent_preprocess(aclent_t *aclent, int n,
317789Sahrens     int *hasmask, mode_t *mask,
318789Sahrens     int *numuser, int *numgroup, int *needsort)
319789Sahrens {
320789Sahrens 	int error = 0;
321789Sahrens 	int i;
322789Sahrens 	int curtype = 0;
323789Sahrens 
324789Sahrens 	*hasmask = 0;
325789Sahrens 	*mask = 07;
326789Sahrens 	*needsort = 0;
327789Sahrens 	*numuser = 0;
328789Sahrens 	*numgroup = 0;
329789Sahrens 
330789Sahrens 	for (i = 0; i < n; i++) {
331789Sahrens 		if (aclent[i].a_type < curtype)
332789Sahrens 			*needsort = 1;
333789Sahrens 		else if (aclent[i].a_type > curtype)
334789Sahrens 			curtype = aclent[i].a_type;
335789Sahrens 		if (aclent[i].a_type & USER)
336789Sahrens 			(*numuser)++;
337789Sahrens 		if (aclent[i].a_type & (GROUP | GROUP_OBJ))
338789Sahrens 			(*numgroup)++;
339789Sahrens 		if (aclent[i].a_type & CLASS_OBJ) {
340789Sahrens 			if (*hasmask) {
341789Sahrens 				error = EINVAL;
342789Sahrens 				goto out;
343789Sahrens 			} else {
344789Sahrens 				*hasmask = 1;
345789Sahrens 				*mask = aclent[i].a_perm;
346789Sahrens 			}
347789Sahrens 		}
348789Sahrens 	}
349789Sahrens 
350789Sahrens 	if ((! *hasmask) && (*numuser + *numgroup > 1)) {
351789Sahrens 		error = EINVAL;
352789Sahrens 		goto out;
353789Sahrens 	}
354789Sahrens 
355789Sahrens out:
356789Sahrens 	return (error);
357789Sahrens }
358789Sahrens 
359789Sahrens /*
360789Sahrens  * Convert an array of aclent_t into an array of nfsace entries,
361789Sahrens  * following POSIX draft -> nfsv4 conversion semantics as outlined in
362789Sahrens  * the IETF draft.
363789Sahrens  */
364789Sahrens static int
365789Sahrens ln_aent_to_ace(aclent_t *aclent, int n, ace_t **acepp, int *rescount, int isdir)
366789Sahrens {
367789Sahrens 	int error = 0;
368789Sahrens 	mode_t mask;
369789Sahrens 	int numuser, numgroup, needsort;
370789Sahrens 	int resultsize = 0;
371789Sahrens 	int i, groupi = 0, skip;
372789Sahrens 	ace_t *acep, *result = NULL;
373789Sahrens 	int hasmask;
374789Sahrens 
375789Sahrens 	error = ln_aent_preprocess(aclent, n, &hasmask, &mask,
376789Sahrens 	    &numuser, &numgroup, &needsort);
377789Sahrens 	if (error != 0)
378789Sahrens 		goto out;
379789Sahrens 
380789Sahrens 	/* allow + deny for each aclent */
381789Sahrens 	resultsize = n * 2;
382789Sahrens 	if (hasmask) {
383789Sahrens 		/*
384789Sahrens 		 * stick extra deny on the group_obj and on each
385789Sahrens 		 * user|group for the mask (the group_obj was added
386789Sahrens 		 * into the count for numgroup)
387789Sahrens 		 */
388789Sahrens 		resultsize += numuser + numgroup;
389789Sahrens 		/* ... and don't count the mask itself */
390789Sahrens 		resultsize -= 2;
391789Sahrens 	}
392789Sahrens 
393789Sahrens 	/* sort the source if necessary */
394789Sahrens 	if (needsort)
395789Sahrens 		ksort((caddr_t)aclent, n, sizeof (aclent_t), cmp2acls);
396789Sahrens 
397789Sahrens 	result = acep = calloc(1, resultsize * sizeof (ace_t));
398789Sahrens 	if (result == NULL)
399789Sahrens 		goto out;
400789Sahrens 
401789Sahrens 	for (i = 0; i < n; i++) {
402789Sahrens 		/*
403789Sahrens 		 * don't process CLASS_OBJ (mask); mask was grabbed in
404789Sahrens 		 * ln_aent_preprocess()
405789Sahrens 		 */
406789Sahrens 		if (aclent[i].a_type & CLASS_OBJ)
407789Sahrens 			continue;
408789Sahrens 
409789Sahrens 		/* If we need an ACL_MASK emulator, prepend it now */
410789Sahrens 		if ((hasmask) &&
411789Sahrens 		    (aclent[i].a_type & (USER | GROUP | GROUP_OBJ))) {
412789Sahrens 			acep->a_type = ACE_ACCESS_DENIED_ACE_TYPE;
413789Sahrens 			acep->a_flags = 0;
414789Sahrens 			if (aclent[i].a_type & GROUP_OBJ) {
415789Sahrens 				acep->a_who = -1;
416789Sahrens 				acep->a_flags |=
417789Sahrens 				    (ACE_IDENTIFIER_GROUP|ACE_GROUP);
418789Sahrens 			} else if (aclent[i].a_type & USER) {
419789Sahrens 				acep->a_who = aclent[i].a_id;
420789Sahrens 			} else {
421789Sahrens 				acep->a_who = aclent[i].a_id;
422789Sahrens 				acep->a_flags |= ACE_IDENTIFIER_GROUP;
423789Sahrens 			}
424789Sahrens 			if (aclent[i].a_type & ACL_DEFAULT) {
425789Sahrens 				acep->a_flags |= ACE_INHERIT_ONLY_ACE |
426789Sahrens 				    ACE_FILE_INHERIT_ACE |
427789Sahrens 				    ACE_DIRECTORY_INHERIT_ACE;
428789Sahrens 			}
429789Sahrens 			/*
430789Sahrens 			 * Set the access mask for the prepended deny
431789Sahrens 			 * ace.  To do this, we invert the mask (found
432789Sahrens 			 * in ln_aent_preprocess()) then convert it to an
433789Sahrens 			 * DENY ace access_mask.
434789Sahrens 			 */
435789Sahrens 			acep->a_access_mask = mode_to_ace_access((mask ^ 07),
436789Sahrens 			    isdir, 0, 0);
437789Sahrens 			acep += 1;
438789Sahrens 		}
439789Sahrens 
440789Sahrens 		/* handle a_perm -> access_mask */
441789Sahrens 		acep->a_access_mask = mode_to_ace_access(aclent[i].a_perm,
442789Sahrens 		    isdir, aclent[i].a_type & USER_OBJ, 1);
443789Sahrens 
444789Sahrens 		/* emulate a default aclent */
445789Sahrens 		if (aclent[i].a_type & ACL_DEFAULT) {
446789Sahrens 			acep->a_flags |= ACE_INHERIT_ONLY_ACE |
447789Sahrens 			    ACE_FILE_INHERIT_ACE |
448789Sahrens 			    ACE_DIRECTORY_INHERIT_ACE;
449789Sahrens 		}
450789Sahrens 
451789Sahrens 		/*
452789Sahrens 		 * handle a_perm and a_id
453789Sahrens 		 *
454789Sahrens 		 * this must be done last, since it involves the
455789Sahrens 		 * corresponding deny aces, which are handled
456789Sahrens 		 * differently for each different a_type.
457789Sahrens 		 */
458789Sahrens 		if (aclent[i].a_type & USER_OBJ) {
459789Sahrens 			acep->a_who = -1;
460789Sahrens 			acep->a_flags |= ACE_OWNER;
461789Sahrens 			ace_make_deny(acep, acep + 1, isdir, B_TRUE);
462789Sahrens 			acep += 2;
463789Sahrens 		} else if (aclent[i].a_type & USER) {
464789Sahrens 			acep->a_who = aclent[i].a_id;
465789Sahrens 			ace_make_deny(acep, acep + 1, isdir, B_FALSE);
466789Sahrens 			acep += 2;
467789Sahrens 		} else if (aclent[i].a_type & (GROUP_OBJ | GROUP)) {
468789Sahrens 			if (aclent[i].a_type & GROUP_OBJ) {
469789Sahrens 				acep->a_who = -1;
470789Sahrens 				acep->a_flags |= ACE_GROUP;
471789Sahrens 			} else {
472789Sahrens 				acep->a_who = aclent[i].a_id;
473789Sahrens 			}
474789Sahrens 			acep->a_flags |= ACE_IDENTIFIER_GROUP;
475789Sahrens 			/*
476789Sahrens 			 * Set the corresponding deny for the group ace.
477789Sahrens 			 *
478789Sahrens 			 * The deny aces go after all of the groups, unlike
479789Sahrens 			 * everything else, where they immediately follow
480789Sahrens 			 * the allow ace.
481789Sahrens 			 *
482789Sahrens 			 * We calculate "skip", the number of slots to
483789Sahrens 			 * skip ahead for the deny ace, here.
484789Sahrens 			 *
485789Sahrens 			 * The pattern is:
486789Sahrens 			 * MD1 A1 MD2 A2 MD3 A3 D1 D2 D3
487789Sahrens 			 * thus, skip is
488789Sahrens 			 * (2 * numgroup) - 1 - groupi
489789Sahrens 			 * (2 * numgroup) to account for MD + A
490789Sahrens 			 * - 1 to account for the fact that we're on the
491789Sahrens 			 * access (A), not the mask (MD)
492789Sahrens 			 * - groupi to account for the fact that we have
493789Sahrens 			 * passed up groupi number of MD's.
494789Sahrens 			 */
495789Sahrens 			skip = (2 * numgroup) - 1 - groupi;
496789Sahrens 			ace_make_deny(acep, acep + skip, isdir, B_FALSE);
497789Sahrens 			/*
498789Sahrens 			 * If we just did the last group, skip acep past
499789Sahrens 			 * all of the denies; else, just move ahead one.
500789Sahrens 			 */
501789Sahrens 			if (++groupi >= numgroup)
502789Sahrens 				acep += numgroup + 1;
503789Sahrens 			else
504789Sahrens 				acep += 1;
505789Sahrens 		} else if (aclent[i].a_type & OTHER_OBJ) {
506789Sahrens 			acep->a_who = -1;
507789Sahrens 			acep->a_flags |= ACE_EVERYONE;
508789Sahrens 			ace_make_deny(acep, acep + 1, isdir, B_FALSE);
509789Sahrens 			acep += 2;
510789Sahrens 		} else {
511789Sahrens 			error = EINVAL;
512789Sahrens 			goto out;
513789Sahrens 		}
514789Sahrens 	}
515789Sahrens 
516789Sahrens 	*acepp = result;
517789Sahrens 	*rescount = resultsize;
518789Sahrens 
519789Sahrens out:
520789Sahrens 	if (error != 0) {
521789Sahrens 		if ((result != NULL) && (resultsize > 0)) {
522789Sahrens 			free(result);
523789Sahrens 		}
524789Sahrens 	}
525789Sahrens 
526789Sahrens 	return (error);
527789Sahrens }
528789Sahrens 
529789Sahrens static int
530789Sahrens convert_aent_to_ace(aclent_t *aclentp, int aclcnt, int isdir,
531789Sahrens     ace_t **retacep, int *retacecnt)
532789Sahrens {
533789Sahrens 	ace_t *acep;
534789Sahrens 	ace_t *dfacep;
535789Sahrens 	ace_t *newacep;
536789Sahrens 	int acecnt = 0;
537789Sahrens 	int dfacecnt = 0;
538789Sahrens 	int dfaclstart = 0;
539789Sahrens 	int dfaclcnt = 0;
540789Sahrens 	aclent_t *aclp;
541789Sahrens 	int i;
542789Sahrens 	int error;
543789Sahrens 
544789Sahrens 	ksort((caddr_t)aclentp, aclcnt, sizeof (aclent_t), cmp2acls);
545789Sahrens 
546789Sahrens 	for (i = 0, aclp = aclentp; i < aclcnt; aclp++, i++) {
547789Sahrens 		if (aclp->a_type & ACL_DEFAULT)
548789Sahrens 			break;
549789Sahrens 	}
550789Sahrens 
551789Sahrens 	if (i < aclcnt) {
552789Sahrens 		dfaclstart = aclcnt - i;
553789Sahrens 		dfaclcnt = i;
554789Sahrens 	}
555789Sahrens 
556789Sahrens 	if (dfaclcnt && isdir == 0) {
557789Sahrens 		return (-1);
558789Sahrens 	}
559789Sahrens 
560789Sahrens 	error = ln_aent_to_ace(aclentp, i,  &acep, &acecnt, isdir);
561789Sahrens 	if (error)
562789Sahrens 		return (-1);
563789Sahrens 
564789Sahrens 	if (dfaclcnt) {
565789Sahrens 		error = ln_aent_to_ace(&aclentp[dfaclstart], dfaclcnt,
566789Sahrens 		    &dfacep, &dfacecnt, isdir);
567789Sahrens 		if (error) {
568789Sahrens 			if (acep) {
569789Sahrens 				free(acep);
570789Sahrens 			}
571789Sahrens 			return (-1);
572789Sahrens 		}
573789Sahrens 	}
574789Sahrens 
575789Sahrens 	newacep = malloc(sizeof (ace_t) * (acecnt + dfacecnt));
576789Sahrens 	if (newacep == NULL)
577789Sahrens 		return (-1);
578789Sahrens 
579789Sahrens 	(void) memcpy(newacep, acep, sizeof (ace_t) * acecnt);
580789Sahrens 	if (dfaclcnt) {
581789Sahrens 		(void) memcpy(newacep + acecnt, dfacep,
582789Sahrens 		    sizeof (ace_t) * dfacecnt);
583789Sahrens 	}
584789Sahrens 	free(acep);
585789Sahrens 	if (dfaclcnt)
586789Sahrens 		free(dfacep);
587789Sahrens 
588789Sahrens 	*retacecnt = acecnt + dfacecnt;
589789Sahrens 	*retacep = newacep;
590789Sahrens 	return (0);
591789Sahrens }
592789Sahrens 
593789Sahrens 
594789Sahrens static int
595789Sahrens cacl_get(acl_inp inp, int get_flag, int type, acl_t **aclp)
596789Sahrens {
597789Sahrens 	const char *fname;
598789Sahrens 	int fd;
599789Sahrens 	int ace_acl = 0;
600789Sahrens 	int error;
601789Sahrens 	int getcmd, cntcmd;
602789Sahrens 	acl_t *acl_info;
603789Sahrens 	int	save_errno;
604789Sahrens 	int	stat_error;
605789Sahrens 	struct stat64 statbuf;
606789Sahrens 
607789Sahrens 	*aclp = NULL;
608789Sahrens 	if (type == ACL_PATH) {
609789Sahrens 		fname = inp.file;
610789Sahrens 		ace_acl = pathconf(fname, _PC_ACL_ENABLED);
611789Sahrens 	} else {
612789Sahrens 		fd = inp.fd;
613789Sahrens 		ace_acl = fpathconf(fd, _PC_ACL_ENABLED);
614789Sahrens 	}
615789Sahrens 
616789Sahrens 	if (ace_acl == -1)
617789Sahrens 		return (-1);
618789Sahrens 
619789Sahrens 	/*
620789Sahrens 	 * if acl's aren't supported then
621789Sahrens 	 * send it through the old GETACL interface
622789Sahrens 	 */
623789Sahrens 	if (ace_acl == 0) {
624789Sahrens 		ace_acl = _ACL_ACLENT_ENABLED;
625789Sahrens 	}
626789Sahrens 
627789Sahrens 	if (ace_acl & _ACL_ACE_ENABLED) {
628789Sahrens 		cntcmd = ACE_GETACLCNT;
629789Sahrens 		getcmd = ACE_GETACL;
630789Sahrens 		acl_info = acl_alloc(ACE_T);
631789Sahrens 	} else {
632789Sahrens 		cntcmd = GETACLCNT;
633789Sahrens 		getcmd = GETACL;
634789Sahrens 		acl_info = acl_alloc(ACLENT_T);
635789Sahrens 	}
636789Sahrens 
637789Sahrens 	if (acl_info == NULL)
638789Sahrens 		return (-1);
639789Sahrens 
640789Sahrens 	if (type == ACL_PATH) {
641789Sahrens 		acl_info->acl_cnt = acl(fname, cntcmd, 0, NULL);
642789Sahrens 	} else {
643789Sahrens 		acl_info->acl_cnt = facl(fd, cntcmd, 0, NULL);
644789Sahrens 	}
645789Sahrens 
646789Sahrens 	save_errno = errno;
647789Sahrens 	if (acl_info->acl_cnt < 0) {
648789Sahrens 		acl_free(acl_info);
649789Sahrens 		errno = save_errno;
650789Sahrens 		return (-1);
651789Sahrens 	}
652789Sahrens 
653789Sahrens 	if (acl_info->acl_cnt == 0) {
654789Sahrens 		acl_free(acl_info);
655789Sahrens 		errno = save_errno;
656789Sahrens 		return (0);
657789Sahrens 	}
658789Sahrens 
659789Sahrens 	acl_info->acl_aclp =
660789Sahrens 	    malloc(acl_info->acl_cnt * acl_info->acl_entry_size);
661789Sahrens 	save_errno = errno;
662789Sahrens 
663789Sahrens 	if (acl_info->acl_aclp == NULL) {
664789Sahrens 		acl_free(acl_info);
665789Sahrens 		errno = save_errno;
666789Sahrens 		return (-1);
667789Sahrens 	}
668789Sahrens 
669789Sahrens 	if (type == ACL_PATH) {
670789Sahrens 		stat_error = stat64(fname, &statbuf);
671789Sahrens 		error = acl(fname, getcmd, acl_info->acl_cnt,
672789Sahrens 		    acl_info->acl_aclp);
673789Sahrens 	} else {
674789Sahrens 		stat_error = fstat64(fd, &statbuf);
675789Sahrens 		error = facl(fd, getcmd, acl_info->acl_cnt,
676789Sahrens 		    acl_info->acl_aclp);
677789Sahrens 	}
678789Sahrens 
679789Sahrens 	save_errno = errno;
680789Sahrens 	if (error == -1) {
681789Sahrens 		acl_free(acl_info);
682789Sahrens 		errno = save_errno;
683789Sahrens 		return (-1);
684789Sahrens 	}
685789Sahrens 
686789Sahrens 
687789Sahrens 	if (stat_error == 0) {
688789Sahrens 		acl_info->acl_flags =
689789Sahrens 		    (S_ISDIR(statbuf.st_mode) ? ACL_IS_DIR : 0);
690789Sahrens 	} else
691789Sahrens 		acl_info->acl_flags = 0;
692789Sahrens 
693789Sahrens 	switch (acl_info->acl_type) {
694789Sahrens 	case ACLENT_T:
695789Sahrens 		if (acl_info->acl_cnt <= MIN_ACL_ENTRIES)
696789Sahrens 			acl_info->acl_flags |= ACL_IS_TRIVIAL;
697789Sahrens 		break;
698789Sahrens 	case ACE_T:
699789Sahrens 		if (ace_trivial(acl_info->acl_aclp, acl_info->acl_cnt) == 0)
700789Sahrens 			acl_info->acl_flags |= ACL_IS_TRIVIAL;
701789Sahrens 		break;
702789Sahrens 	default:
703789Sahrens 		errno = EINVAL;
704789Sahrens 		acl_free(acl_info);
705789Sahrens 		return (-1);
706789Sahrens 	}
707789Sahrens 
708789Sahrens 	if ((acl_info->acl_flags & ACL_IS_TRIVIAL) &&
709789Sahrens 	    (get_flag & ACL_NO_TRIVIAL)) {
710789Sahrens 		acl_free(acl_info);
711789Sahrens 		errno = 0;
712789Sahrens 		return (0);
713789Sahrens 	}
714789Sahrens 
715789Sahrens 	*aclp = acl_info;
716789Sahrens 	return (0);
717789Sahrens }
718789Sahrens 
719789Sahrens /*
720789Sahrens  * return -1 on failure, otherwise the number of acl
721789Sahrens  * entries is returned
722789Sahrens  */
723789Sahrens int
724789Sahrens acl_get(const char *path, int get_flag, acl_t **aclp)
725789Sahrens {
726789Sahrens 	acl_inp acl_inp;
727789Sahrens 	acl_inp.file = path;
728789Sahrens 
729789Sahrens 	return (cacl_get(acl_inp, get_flag, ACL_PATH, aclp));
730789Sahrens }
731789Sahrens 
732789Sahrens int
733789Sahrens facl_get(int fd, int get_flag, acl_t **aclp)
734789Sahrens {
735789Sahrens 
736789Sahrens 	acl_inp acl_inp;
737789Sahrens 	acl_inp.fd = fd;
738789Sahrens 
739789Sahrens 	return (cacl_get(acl_inp, get_flag, ACL_FD, aclp));
740789Sahrens }
741789Sahrens 
742789Sahrens /*
743789Sahrens  * Set an ACL, translates acl to ace_t when appropriate.
744789Sahrens  */
745789Sahrens static int
746789Sahrens cacl_set(acl_inp *acl_inp, acl_t *aclp, int type)
747789Sahrens {
748789Sahrens 	int error = 0;
749789Sahrens 	int acl_flavor_target;
750789Sahrens 	ace_t *acep = NULL;
751789Sahrens 	int acecnt;
752789Sahrens 	struct stat64 statbuf;
753789Sahrens 	int stat_error;
754789Sahrens 	int isdir;
755789Sahrens 
756789Sahrens 
757789Sahrens 	if (type == ACL_PATH) {
758789Sahrens 		stat_error = stat64(acl_inp->file, &statbuf);
759789Sahrens 		if (stat_error)
760789Sahrens 			return (-1);
761789Sahrens 		acl_flavor_target = pathconf(acl_inp->file, _PC_ACL_ENABLED);
762789Sahrens 	} else {
763789Sahrens 		stat_error = fstat64(acl_inp->fd, &statbuf);
764789Sahrens 		if (stat_error)
765789Sahrens 			return (-1);
766789Sahrens 		acl_flavor_target = fpathconf(acl_inp->fd, _PC_ACL_ENABLED);
767789Sahrens 	}
768789Sahrens 
769789Sahrens 	isdir = S_ISDIR(statbuf.st_mode);
770789Sahrens 
771789Sahrens 	if (acl_flavor_target == -1)
772789Sahrens 		return (-1);
773789Sahrens 
774789Sahrens 	/*
775789Sahrens 	 * Translate aclent_t ACL's to ACE ACL's.
776789Sahrens 	 */
777789Sahrens 	if (acl_flavor_target ==  _ACL_ACE_ENABLED &&
778789Sahrens 	    aclp->acl_type == ACLENT_T) {
779789Sahrens 		error = convert_aent_to_ace(aclp->acl_aclp,
780789Sahrens 		    aclp->acl_cnt, isdir, &acep, &acecnt);
781789Sahrens 		if (error) {
782789Sahrens 			errno = ENOTSUP;
783789Sahrens 			return (-1);
784789Sahrens 		}
785789Sahrens 		/*
786789Sahrens 		 * replace old acl with newly translated acl
787789Sahrens 		 */
788789Sahrens 		free(aclp->acl_aclp);
789789Sahrens 		aclp->acl_aclp = acep;
790789Sahrens 		aclp->acl_cnt = acecnt;
791789Sahrens 		aclp->acl_type = ACE_T;
792789Sahrens 	}
793789Sahrens 
794789Sahrens 	if (type == ACL_PATH) {
795789Sahrens 		error = acl(acl_inp->file,
796789Sahrens 		    (aclp->acl_type == ACE_T) ? ACE_SETACL : SETACL,
797789Sahrens 		    aclp->acl_cnt, aclp->acl_aclp);
798789Sahrens 	} else {
799789Sahrens 		error = facl(acl_inp->fd,
800789Sahrens 		    (aclp->acl_type == ACE_T) ? ACE_SETACL : SETACL,
801789Sahrens 		    aclp->acl_cnt, aclp->acl_aclp);
802789Sahrens 	}
803789Sahrens 
804789Sahrens 	return (error);
805789Sahrens }
806789Sahrens 
807789Sahrens int
808789Sahrens acl_set(const char *path, acl_t *aclp)
809789Sahrens {
810789Sahrens 	acl_inp acl_inp;
811789Sahrens 
812789Sahrens 	acl_inp.file = path;
813789Sahrens 
814789Sahrens 	return (cacl_set(&acl_inp, aclp, ACL_PATH));
815789Sahrens }
816789Sahrens 
817789Sahrens int
818789Sahrens facl_set(int fd, acl_t *aclp)
819789Sahrens {
820789Sahrens 	acl_inp acl_inp;
821789Sahrens 
822789Sahrens 	acl_inp.fd = fd;
823789Sahrens 
824789Sahrens 	return (cacl_set(&acl_inp, aclp, ACL_FD));
825789Sahrens }
826789Sahrens 
827789Sahrens int
828789Sahrens acl_cnt(acl_t *aclp)
829789Sahrens {
830789Sahrens 	return (aclp->acl_cnt);
831789Sahrens }
832789Sahrens 
833789Sahrens int
834789Sahrens acl_type(acl_t *aclp)
835789Sahrens {
836789Sahrens 	return (aclp->acl_type);
837789Sahrens }
838789Sahrens 
839789Sahrens acl_t *
840789Sahrens acl_dup(acl_t *aclp)
841789Sahrens {
842789Sahrens 	acl_t *newaclp;
843789Sahrens 
844789Sahrens 	newaclp = acl_alloc(aclp->acl_type);
845789Sahrens 	if (newaclp == NULL)
846789Sahrens 		return (NULL);
847789Sahrens 
848789Sahrens 	newaclp->acl_aclp = malloc(aclp->acl_entry_size * aclp->acl_cnt);
849789Sahrens 	if (newaclp->acl_aclp == NULL) {
850789Sahrens 		acl_free(newaclp);
851789Sahrens 		return (NULL);
852789Sahrens 	}
853789Sahrens 
854789Sahrens 	(void) memcpy(newaclp->acl_aclp,
855789Sahrens 	    aclp->acl_aclp, aclp->acl_entry_size * aclp->acl_cnt);
856789Sahrens 	newaclp->acl_cnt = aclp->acl_cnt;
857789Sahrens 
858789Sahrens 	return (newaclp);
859789Sahrens }
860789Sahrens 
861789Sahrens int
862789Sahrens acl_flags(acl_t *aclp)
863789Sahrens {
864789Sahrens 	return (aclp->acl_flags);
865789Sahrens }
866789Sahrens 
867789Sahrens void *
868789Sahrens acl_data(acl_t *aclp)
869789Sahrens {
870789Sahrens 	return (aclp->acl_aclp);
871789Sahrens }
872789Sahrens 
873789Sahrens /*
874789Sahrens  * Remove an ACL from a file and create a trivial ACL based
875789Sahrens  * off of the mode argument.  After acl has been set owner/group
876789Sahrens  * are updated to match owner,group arguments
877789Sahrens  */
878789Sahrens int
879789Sahrens acl_strip(const char *file, uid_t owner, gid_t group, mode_t mode)
880789Sahrens {
881789Sahrens 	int	error = 0;
882789Sahrens 	aclent_t min_acl[MIN_ACL_ENTRIES];
883789Sahrens 	ace_t	min_ace_acl[6];	/* owner, group, everyone + complement denies */
884789Sahrens 	int	acl_flavor;
885789Sahrens 	int	aclcnt;
886789Sahrens 
887789Sahrens 	acl_flavor = pathconf(file, _PC_ACL_ENABLED);
888789Sahrens 
889789Sahrens 	if (acl_flavor == -1)
890789Sahrens 		return (-1);
891789Sahrens 	/*
892789Sahrens 	 * force it through aclent flavor when file system doesn't
893789Sahrens 	 * understand question
894789Sahrens 	 */
895789Sahrens 	if (acl_flavor == 0)
896789Sahrens 		acl_flavor = _ACL_ACLENT_ENABLED;
897789Sahrens 
898789Sahrens 	if (acl_flavor & _ACL_ACLENT_ENABLED) {
899789Sahrens 		min_acl[0].a_type = USER_OBJ;
900789Sahrens 		min_acl[0].a_id   = owner;
901789Sahrens 		min_acl[0].a_perm = ((mode & 0700) >> 6);
902789Sahrens 		min_acl[1].a_type = GROUP_OBJ;
903789Sahrens 		min_acl[1].a_id   = group;
904789Sahrens 		min_acl[1].a_perm = ((mode & 0070) >> 3);
905789Sahrens 		min_acl[2].a_type = CLASS_OBJ;
906789Sahrens 		min_acl[2].a_id   = (uid_t)-1;
907789Sahrens 		min_acl[2].a_perm = ((mode & 0070) >> 3);
908789Sahrens 		min_acl[3].a_type = OTHER_OBJ;
909789Sahrens 		min_acl[3].a_id   = (uid_t)-1;
910789Sahrens 		min_acl[3].a_perm = (mode & 0007);
911789Sahrens 		aclcnt = 4;
912789Sahrens 		error = acl(file, SETACL, aclcnt, min_acl);
913789Sahrens 	} else if (acl_flavor & _ACL_ACE_ENABLED) {
914789Sahrens 		(void) memcpy(min_ace_acl, trivial_acl, sizeof (ace_t) * 6);
915789Sahrens 
916789Sahrens 		/*
917789Sahrens 		 * Make aces match request mode
918789Sahrens 		 */
919789Sahrens 		adjust_ace_pair(&min_ace_acl[0], (mode & 0700) >> 6);
920789Sahrens 		adjust_ace_pair(&min_ace_acl[2], (mode & 0070) >> 3);
921789Sahrens 		adjust_ace_pair(&min_ace_acl[4], mode & 0007);
922789Sahrens 
923789Sahrens 		error = acl(file, ACE_SETACL, 6, min_ace_acl);
924789Sahrens 	} else {
925789Sahrens 		errno = EINVAL;
926789Sahrens 		error = 1;
927789Sahrens 	}
928789Sahrens 
929789Sahrens 	if (error == 0)
930789Sahrens 		error = chown(file, owner, group);
931789Sahrens 	return (error);
932789Sahrens }
933789Sahrens 
934789Sahrens static int
935789Sahrens ace_match(void *entry1, void *entry2)
936789Sahrens {
937789Sahrens 	ace_t *p1 = (ace_t *)entry1;
938789Sahrens 	ace_t *p2 = (ace_t *)entry2;
939789Sahrens 	ace_t ace1, ace2;
940789Sahrens 
941789Sahrens 	ace1 = *p1;
942789Sahrens 	ace2 = *p2;
943789Sahrens 
944789Sahrens 	/*
945789Sahrens 	 * Need to fixup who field for abstrations for
946789Sahrens 	 * accurate comparison, since field is undefined.
947789Sahrens 	 */
948789Sahrens 	if (ace1.a_flags & (ACE_OWNER|ACE_GROUP|ACE_EVERYONE))
949789Sahrens 		ace1.a_who = -1;
950789Sahrens 	if (ace2.a_flags & (ACE_OWNER|ACE_GROUP|ACE_EVERYONE))
951789Sahrens 		ace2.a_who = -1;
952789Sahrens 	return (memcmp(&ace1, &ace2, sizeof (ace_t)));
953789Sahrens }
954789Sahrens 
955789Sahrens static int
956789Sahrens aclent_match(void *entry1, void *entry2)
957789Sahrens {
958789Sahrens 	aclent_t *aclent1 = (aclent_t *)entry1;
959789Sahrens 	aclent_t *aclent2 = (aclent_t *)entry2;
960789Sahrens 
961789Sahrens 	return (memcmp(aclent1, aclent2, sizeof (aclent_t)));
962789Sahrens }
963789Sahrens 
964789Sahrens /*
965789Sahrens  * Find acl entries in acl that correspond to removeacl.  Search
966789Sahrens  * is started from slot.  The flag argument indicates whether to
967789Sahrens  * remove all matches or just the first match.
968789Sahrens  */
969789Sahrens int
970789Sahrens acl_removeentries(acl_t *acl, acl_t *removeacl, int start_slot, int flag)
971789Sahrens {
972789Sahrens 	int i, j;
973789Sahrens 	int match;
974789Sahrens 	int (*acl_match)(void *acl1, void *acl2);
975789Sahrens 	void *acl_entry, *remove_entry;
976789Sahrens 	void *start;
977789Sahrens 	int found = 0;
978789Sahrens 
979789Sahrens 	if (flag != ACL_REMOVE_ALL && flag != ACL_REMOVE_FIRST)
980789Sahrens 		flag = ACL_REMOVE_FIRST;
981789Sahrens 
982789Sahrens 	if (acl == NULL || removeacl == NULL)
983789Sahrens 		return (EACL_NO_ACL_ENTRY);
984789Sahrens 
985789Sahrens 	if (acl->acl_type != removeacl->acl_type)
986789Sahrens 		return (EACL_DIFF_TYPE);
987789Sahrens 
988789Sahrens 	if (acl->acl_type == ACLENT_T)
989789Sahrens 		acl_match = aclent_match;
990789Sahrens 	else
991789Sahrens 		acl_match = ace_match;
992789Sahrens 
993789Sahrens 	for (i = 0, remove_entry = removeacl->acl_aclp;
994789Sahrens 	    i != removeacl->acl_cnt; i++) {
995789Sahrens 
996789Sahrens 		j = 0;
997789Sahrens 		acl_entry = (char *)acl->acl_aclp +
998789Sahrens 		    (acl->acl_entry_size * start_slot);
999789Sahrens 		for (;;) {
1000789Sahrens 			match = acl_match(acl_entry, remove_entry);
1001789Sahrens 			if (match == 0)  {
1002789Sahrens 				found++;
1003789Sahrens 				start = (char *)acl_entry +
1004789Sahrens 				    acl->acl_entry_size;
1005789Sahrens 				(void) memmove(acl_entry, start,
1006789Sahrens 				    acl->acl_entry_size *
1007789Sahrens 				    acl->acl_cnt-- - (j + 1));
1008789Sahrens 
1009789Sahrens 				if (flag == ACL_REMOVE_FIRST)
1010789Sahrens 					break;
1011789Sahrens 				/*
1012789Sahrens 				 * List has changed, restart search from
1013789Sahrens 				 * beginning.
1014789Sahrens 				 */
1015789Sahrens 				acl_entry = acl->acl_aclp;
1016789Sahrens 				j = 0;
1017789Sahrens 				continue;
1018789Sahrens 			}
1019789Sahrens 			acl_entry = ((char *)acl_entry + acl->acl_entry_size);
1020789Sahrens 			if (++j >= acl->acl_cnt) {
1021789Sahrens 				break;
1022789Sahrens 			}
1023789Sahrens 		}
1024789Sahrens 	}
1025789Sahrens 
1026789Sahrens 	return ((found == 0) ? EACL_NO_ACL_ENTRY : 0);
1027789Sahrens }
1028789Sahrens 
1029789Sahrens /*
1030789Sahrens  * Replace entires entries in acl1 with the corresponding entries
1031789Sahrens  * in newentries.  The where argument specifies where to begin
1032789Sahrens  * the replacement.  If the where argument is 1 greater than the
1033789Sahrens  * number of acl entries in acl1 then they are appended.  If the
1034789Sahrens  * where argument is 2+ greater than the number of acl entries then
1035789Sahrens  * EACL_INVALID_SLOT is returned.
1036789Sahrens  */
1037789Sahrens int
1038789Sahrens acl_modifyentries(acl_t *acl1, acl_t *newentries, int where)
1039789Sahrens {
1040789Sahrens 
1041789Sahrens 	int slot;
1042789Sahrens 	int slots_needed;
1043789Sahrens 	int slots_left;
1044789Sahrens 	int newsize;
1045789Sahrens 
1046789Sahrens 	if (acl1 == NULL || newentries == NULL)
1047789Sahrens 		return (EACL_NO_ACL_ENTRY);
1048789Sahrens 
1049789Sahrens 	if (where < 0 || where >= acl1->acl_cnt)
1050789Sahrens 		return (EACL_INVALID_SLOT);
1051789Sahrens 
1052789Sahrens 	if (acl1->acl_type != newentries->acl_type)
1053789Sahrens 		return (EACL_DIFF_TYPE);
1054789Sahrens 
1055789Sahrens 	slot = where;
1056789Sahrens 
1057789Sahrens 	slots_left = acl1->acl_cnt - slot + 1;
1058789Sahrens 	if (slots_left < newentries->acl_cnt) {
1059789Sahrens 		slots_needed = newentries->acl_cnt - slots_left;
1060789Sahrens 		newsize = (acl1->acl_entry_size * acl1->acl_cnt) +
1061789Sahrens 		    (acl1->acl_entry_size * slots_needed);
1062789Sahrens 		acl1->acl_aclp = realloc(acl1->acl_aclp, newsize);
1063789Sahrens 		if (acl1->acl_aclp == NULL)
1064789Sahrens 			return (-1);
1065789Sahrens 	}
1066789Sahrens 	(void) memcpy((char *)acl1->acl_aclp + (acl1->acl_entry_size * slot),
1067789Sahrens 	    newentries->acl_aclp,
1068789Sahrens 	    newentries->acl_entry_size * newentries->acl_cnt);
1069789Sahrens 
1070789Sahrens 	/*
1071789Sahrens 	 * Did ACL grow?
1072789Sahrens 	 */
1073789Sahrens 
1074789Sahrens 	if ((slot + newentries->acl_cnt) > acl1->acl_cnt) {
1075789Sahrens 		acl1->acl_cnt = slot + newentries->acl_cnt;
1076789Sahrens 	}
1077789Sahrens 
1078789Sahrens 	return (0);
1079789Sahrens }
1080789Sahrens 
1081789Sahrens /*
1082789Sahrens  * Add acl2 entries into acl1.  The where argument specifies where
1083789Sahrens  * to add the entries.
1084789Sahrens  */
1085789Sahrens int
1086789Sahrens acl_addentries(acl_t *acl1, acl_t *acl2, int where)
1087789Sahrens {
1088789Sahrens 
1089789Sahrens 	int newsize;
1090789Sahrens 	int len;
1091789Sahrens 	void *start;
1092789Sahrens 	void *to;
1093789Sahrens 
1094789Sahrens 	if (acl1 == NULL || acl2 == NULL)
1095789Sahrens 		return (EACL_NO_ACL_ENTRY);
1096789Sahrens 
1097789Sahrens 	if (acl1->acl_type != acl2->acl_type)
1098789Sahrens 		return (EACL_DIFF_TYPE);
1099789Sahrens 
1100789Sahrens 	/*
1101789Sahrens 	 * allow where to specify 1 past last slot for an append operation
1102789Sahrens 	 * but anything greater is an error.
1103789Sahrens 	 */
1104789Sahrens 	if (where < 0 || where > acl1->acl_cnt)
1105789Sahrens 		return (EACL_INVALID_SLOT);
1106789Sahrens 
1107789Sahrens 	newsize = (acl2->acl_entry_size * acl2->acl_cnt) +
1108789Sahrens 	    (acl1->acl_entry_size * acl1->acl_cnt);
1109789Sahrens 	acl1->acl_aclp = realloc(acl1->acl_aclp, newsize);
1110789Sahrens 	if (acl1->acl_aclp == NULL)
1111789Sahrens 		return (-1);
1112789Sahrens 
1113789Sahrens 	/*
1114789Sahrens 	 * first push down entries where new ones will be inserted
1115789Sahrens 	 */
1116789Sahrens 
1117789Sahrens 	to = (void *)((char *)acl1->acl_aclp +
1118789Sahrens 	    ((where + acl2->acl_cnt) * acl1->acl_entry_size));
1119789Sahrens 
1120789Sahrens 	start = (void *)((char *)acl1->acl_aclp +
1121789Sahrens 	    where * acl1->acl_entry_size);
1122789Sahrens 
1123789Sahrens 	if (where < acl1->acl_cnt) {
1124789Sahrens 		len = (acl1->acl_cnt - where) * acl1->acl_entry_size;
1125789Sahrens 		(void) memmove(to, start, len);
1126789Sahrens 	}
1127789Sahrens 
1128789Sahrens 	/*
1129789Sahrens 	 * now stick in new entries.
1130789Sahrens 	 */
1131789Sahrens 
1132789Sahrens 	(void) memmove(start, acl2->acl_aclp,
1133789Sahrens 	    acl2->acl_cnt * acl2->acl_entry_size);
1134789Sahrens 
1135789Sahrens 	acl1->acl_cnt += acl2->acl_cnt;
1136789Sahrens 	return (0);
1137789Sahrens }
1138789Sahrens 
1139789Sahrens static void
1140789Sahrens aclent_perms(int perm, char *txt_perms)
1141789Sahrens {
1142789Sahrens 	if (perm & S_IROTH)
1143789Sahrens 		txt_perms[0] = 'r';
1144789Sahrens 	else
1145789Sahrens 		txt_perms[0] = '-';
1146789Sahrens 	if (perm & S_IWOTH)
1147789Sahrens 		txt_perms[1] = 'w';
1148789Sahrens 	else
1149789Sahrens 		txt_perms[1] = '-';
1150789Sahrens 	if (perm & S_IXOTH)
1151789Sahrens 		txt_perms[2] = 'x';
1152789Sahrens 	else
1153789Sahrens 		txt_perms[2] = '-';
1154789Sahrens 	txt_perms[3] = '\0';
1155789Sahrens }
1156789Sahrens 
1157789Sahrens static char *
1158789Sahrens pruname(uid_t uid)
1159789Sahrens {
1160789Sahrens 	struct passwd	*passwdp;
1161789Sahrens 	static char	uidp[10];	/* big enough */
1162789Sahrens 
1163789Sahrens 	passwdp = getpwuid(uid);
1164789Sahrens 	if (passwdp == (struct passwd *)NULL) {
1165789Sahrens 		/* could not get passwd information: display uid instead */
1166789Sahrens 		(void) sprintf(uidp, "%ld", (long)uid);
1167789Sahrens 		return (uidp);
1168789Sahrens 	} else
1169789Sahrens 		return (passwdp->pw_name);
1170789Sahrens }
1171789Sahrens 
1172789Sahrens static char *
1173789Sahrens prgname(gid_t gid)
1174789Sahrens {
1175789Sahrens 	struct group	*groupp;
1176789Sahrens 	static char	gidp[10];	/* big enough */
1177789Sahrens 
1178789Sahrens 	groupp = getgrgid(gid);
1179789Sahrens 	if (groupp == (struct group *)NULL) {
1180789Sahrens 		/* could not get group information: display gid instead */
1181789Sahrens 		(void) sprintf(gidp, "%ld", (long)gid);
1182789Sahrens 		return (gidp);
1183789Sahrens 	} else
1184789Sahrens 		return (groupp->gr_name);
1185789Sahrens }
1186789Sahrens static void
1187789Sahrens aclent_printacl(acl_t *aclp)
1188789Sahrens {
1189789Sahrens 	aclent_t *tp;
1190789Sahrens 	int aclcnt;
1191789Sahrens 	int mask;
1192789Sahrens 	int slot = 0;
1193789Sahrens 	char perm[4];
1194789Sahrens 
1195789Sahrens 	/* display ACL: assume it is sorted. */
1196789Sahrens 	aclcnt = aclp->acl_cnt;
1197789Sahrens 	for (tp = aclp->acl_aclp; aclcnt--; tp++) {
1198789Sahrens 		if (tp->a_type == CLASS_OBJ)
1199789Sahrens 			mask = tp->a_perm;
1200789Sahrens 	}
1201789Sahrens 	aclcnt = aclp->acl_cnt;
1202789Sahrens 	for (tp = aclp->acl_aclp; aclcnt--; tp++) {
1203789Sahrens 		(void) printf("     %d:", slot++);
1204789Sahrens 		switch (tp->a_type) {
1205789Sahrens 		case USER:
1206789Sahrens 			aclent_perms(tp->a_perm, perm);
1207789Sahrens 			(void) printf("user:%s:%s\t\t",
1208789Sahrens 			    pruname(tp->a_id), perm);
1209789Sahrens 			aclent_perms((tp->a_perm & mask), perm);
1210789Sahrens 			(void) printf("#effective:%s\n", perm);
1211789Sahrens 			break;
1212789Sahrens 		case USER_OBJ:
1213789Sahrens 			/* no need to display uid */
1214789Sahrens 			aclent_perms(tp->a_perm, perm);
1215789Sahrens 			(void) printf("user::%s\n", perm);
1216789Sahrens 			break;
1217789Sahrens 		case GROUP:
1218789Sahrens 			aclent_perms(tp->a_perm, perm);
1219789Sahrens 			(void) printf("group:%s:%s\t\t",
1220789Sahrens 			    prgname(tp->a_id), perm);
1221789Sahrens 			aclent_perms(tp->a_perm & mask, perm);
1222789Sahrens 			(void) printf("#effective:%s\n", perm);
1223789Sahrens 			break;
1224789Sahrens 		case GROUP_OBJ:
1225789Sahrens 			aclent_perms(tp->a_perm, perm);
1226789Sahrens 			(void) printf("group::%s\t\t", perm);
1227789Sahrens 			aclent_perms(tp->a_perm & mask, perm);
1228789Sahrens 			(void) printf("#effective:%s\n", perm);
1229789Sahrens 			break;
1230789Sahrens 		case CLASS_OBJ:
1231789Sahrens 			aclent_perms(tp->a_perm, perm);
1232789Sahrens 			(void) printf("mask:%s\n", perm);
1233789Sahrens 			break;
1234789Sahrens 		case OTHER_OBJ:
1235789Sahrens 			aclent_perms(tp->a_perm, perm);
1236789Sahrens 			(void) printf("other:%s\n", perm);
1237789Sahrens 			break;
1238789Sahrens 		case DEF_USER:
1239789Sahrens 			aclent_perms(tp->a_perm, perm);
1240789Sahrens 			(void) printf("default:user:%s:%s\n",
1241789Sahrens 			    pruname(tp->a_id), perm);
1242789Sahrens 			break;
1243789Sahrens 		case DEF_USER_OBJ:
1244789Sahrens 			aclent_perms(tp->a_perm, perm);
1245789Sahrens 			(void) printf("default:user::%s\n", perm);
1246789Sahrens 			break;
1247789Sahrens 		case DEF_GROUP:
1248789Sahrens 			aclent_perms(tp->a_perm, perm);
1249789Sahrens 			(void) printf("default:group:%s:%s\n",
1250789Sahrens 			    prgname(tp->a_id), perm);
1251789Sahrens 			break;
1252789Sahrens 		case DEF_GROUP_OBJ:
1253789Sahrens 			aclent_perms(tp->a_perm, perm);
1254789Sahrens 			(void) printf("default:group::%s\n", perm);
1255789Sahrens 			break;
1256789Sahrens 		case DEF_CLASS_OBJ:
1257789Sahrens 			aclent_perms(tp->a_perm, perm);
1258789Sahrens 			(void) printf("default:mask:%s\n", perm);
1259789Sahrens 			break;
1260789Sahrens 		case DEF_OTHER_OBJ:
1261789Sahrens 			aclent_perms(tp->a_perm, perm);
1262789Sahrens 			(void) printf("default:other:%s\n", perm);
1263789Sahrens 			break;
1264789Sahrens 		default:
1265789Sahrens 			(void) fprintf(stderr,
1266789Sahrens 			    gettext("unrecognized entry\n"));
1267789Sahrens 			break;
1268789Sahrens 		}
1269789Sahrens 	}
1270789Sahrens }
1271789Sahrens 
1272789Sahrens static void
1273789Sahrens split_line(char *str, int cols)
1274789Sahrens {
1275789Sahrens 	char *ptr;
1276789Sahrens 	int len;
1277789Sahrens 	int i;
1278789Sahrens 	int last_split;
1279789Sahrens 	char pad[11];
1280789Sahrens 	int pad_len;
1281789Sahrens 
1282789Sahrens 	len = strlen(str);
1283789Sahrens 	ptr = str;
1284789Sahrens 	(void) strcpy(pad, "");
1285789Sahrens 	pad_len = 0;
1286789Sahrens 
1287789Sahrens 	ptr = str;
1288789Sahrens 	last_split = 0;
1289789Sahrens 	for (i = 0; i != len; i++) {
1290789Sahrens 		if ((i + pad_len + 4) >= cols) {
1291789Sahrens 			(void) printf("%s%.*s\n", pad, last_split, ptr);
1292789Sahrens 			ptr = &ptr[last_split];
1293789Sahrens 			len = strlen(ptr);
1294789Sahrens 			i = 0;
1295789Sahrens 			pad_len = 4;
1296789Sahrens 			(void) strcpy(pad, "         ");
1297789Sahrens 		} else {
1298789Sahrens 			if (ptr[i] == '/' || ptr[i] == ':') {
1299789Sahrens 				last_split = i;
1300789Sahrens 			}
1301789Sahrens 		}
1302789Sahrens 	}
1303789Sahrens 	if (i == len) {
1304789Sahrens 		(void) printf("%s%s\n", pad, ptr);
1305789Sahrens 	}
1306789Sahrens }
1307789Sahrens 
1308789Sahrens static void
1309789Sahrens ace_printacl(acl_t *aclp, int cols)
1310789Sahrens {
1311789Sahrens 	int  slot = 0;
1312789Sahrens 	char *token;
1313789Sahrens 	char *acltext;
1314789Sahrens 
1315789Sahrens 	acltext = acl_totext(aclp);
1316789Sahrens 
1317789Sahrens 	if (acltext == NULL)
1318789Sahrens 		return;
1319789Sahrens 
1320789Sahrens 	token = strtok(acltext, ",");
1321789Sahrens 	if (token == NULL) {
1322789Sahrens 		free(acltext);
1323789Sahrens 		return;
1324789Sahrens 	}
1325789Sahrens 
1326789Sahrens 	do {
1327789Sahrens 		(void) printf("     %d:", slot++);
1328789Sahrens 		split_line(token, cols - 5);
1329789Sahrens 	} while (token = strtok(NULL, ","));
1330789Sahrens 	free(acltext);
1331789Sahrens }
1332789Sahrens 
1333789Sahrens /*
1334789Sahrens  * pretty print an ACL.
1335789Sahrens  * For aclent_t ACL's the format is
1336789Sahrens  * similar to the old format used by getfacl,
1337789Sahrens  * with the addition of adding a "slot" number
1338789Sahrens  * before each entry.
1339789Sahrens  *
1340789Sahrens  * for ace_t ACL's the cols variable will break up
1341789Sahrens  * the long lines into multiple lines and will also
1342789Sahrens  * print a "slot" number.
1343789Sahrens  */
1344789Sahrens void
1345789Sahrens acl_printacl(acl_t *aclp, int cols)
1346789Sahrens {
1347789Sahrens 
1348789Sahrens 	switch (aclp->acl_type) {
1349789Sahrens 	case ACLENT_T:
1350789Sahrens 		aclent_printacl(aclp);
1351789Sahrens 		break;
1352789Sahrens 	case ACE_T:
1353789Sahrens 		ace_printacl(aclp, cols);
1354789Sahrens 		break;
1355789Sahrens 	}
1356789Sahrens }
1357789Sahrens 
1358789Sahrens 
1359789Sahrens /*
1360789Sahrens  * return text for an ACL error.
1361789Sahrens  */
1362789Sahrens char *
1363789Sahrens acl_strerror(int errnum)
1364789Sahrens {
1365789Sahrens 	switch (errnum) {
1366789Sahrens 	case EACL_GRP_ERROR:
1367789Sahrens 		return (dgettext(TEXT_DOMAIN,
1368789Sahrens 		    "There is more than one user group owner entry"));
1369789Sahrens 	case EACL_USER_ERROR:
1370789Sahrens 		return (dgettext(TEXT_DOMAIN,
1371789Sahrens 		    "There is more than one user owner entry"));
1372789Sahrens 	case EACL_OTHER_ERROR:
1373789Sahrens 		return (dgettext(TEXT_DOMAIN,
1374789Sahrens 		    "There is more than one other entry"));
1375789Sahrens 	case EACL_CLASS_ERROR:
1376789Sahrens 		return (dgettext(TEXT_DOMAIN,
1377789Sahrens 		    "There is more than one mask entry"));
1378789Sahrens 	case EACL_DUPLICATE_ERROR:
1379789Sahrens 		return (dgettext(TEXT_DOMAIN,
1380789Sahrens 		    "Duplicate user or group entries"));
1381789Sahrens 	case EACL_MISS_ERROR:
1382789Sahrens 		return (dgettext(TEXT_DOMAIN,
1383789Sahrens 		    "Missing user/group owner, other, mask entry"));
1384789Sahrens 	case EACL_MEM_ERROR:
1385789Sahrens 		return (dgettext(TEXT_DOMAIN,
1386789Sahrens 		    "Memory error"));
1387789Sahrens 	case EACL_ENTRY_ERROR:
1388789Sahrens 		return (dgettext(TEXT_DOMAIN,
1389789Sahrens 		    "Unrecognized entry type"));
1390789Sahrens 	case EACL_INHERIT_ERROR:
1391789Sahrens 		return (dgettext(TEXT_DOMAIN,
1392789Sahrens 		    "Invalid inheritance flags"));
1393789Sahrens 	case EACL_FLAGS_ERROR:
1394789Sahrens 		return (dgettext(TEXT_DOMAIN,
1395789Sahrens 		    "Unrecognized entry flags"));
1396789Sahrens 	case EACL_PERM_MASK_ERROR:
1397789Sahrens 		return (dgettext(TEXT_DOMAIN,
1398789Sahrens 		    "Invalid ACL permissions"));
1399789Sahrens 	case EACL_COUNT_ERROR:
1400789Sahrens 		return (dgettext(TEXT_DOMAIN,
1401789Sahrens 		    "Invalid ACL count"));
1402789Sahrens 	case EACL_INVALID_SLOT:
1403789Sahrens 		return (dgettext(TEXT_DOMAIN,
1404789Sahrens 		    "Invalid ACL entry number specified"));
1405789Sahrens 	case EACL_NO_ACL_ENTRY:
1406789Sahrens 		return (dgettext(TEXT_DOMAIN,
1407789Sahrens 		    "ACL entry doesn't exist"));
1408789Sahrens 	case EACL_DIFF_TYPE:
1409789Sahrens 		return (dgettext(TEXT_DOMAIN,
1410789Sahrens 		    "ACL type's are different"));
1411789Sahrens 	case EACL_INVALID_USER_GROUP:
1412789Sahrens 		return (dgettext(TEXT_DOMAIN, "Invalid user or group"));
1413789Sahrens 	case EACL_INVALID_STR:
1414789Sahrens 		return (dgettext(TEXT_DOMAIN, "ACL string is invalid"));
1415789Sahrens 	case EACL_FIELD_NOT_BLANK:
1416789Sahrens 		return (dgettext(TEXT_DOMAIN, "Field expected to be blank"));
1417789Sahrens 	case EACL_INVALID_ACCESS_TYPE:
1418789Sahrens 		return (dgettext(TEXT_DOMAIN, "Invalid access type"));
1419789Sahrens 	case EACL_UNKNOWN_DATA:
1420789Sahrens 		return (dgettext(TEXT_DOMAIN, "Unrecognized entry"));
1421789Sahrens 	case EACL_MISSING_FIELDS:
1422789Sahrens 		return (dgettext(TEXT_DOMAIN,
1423789Sahrens 		    "ACL specification missing required fields"));
1424789Sahrens 	case EACL_INHERIT_NOTDIR:
1425789Sahrens 		return (dgettext(TEXT_DOMAIN,
1426789Sahrens 		    "Inheritance flags are only allowed on directories"));
1427789Sahrens 	case -1:
1428789Sahrens 		return (strerror(errno));
1429789Sahrens 	default:
1430789Sahrens 		errno = EINVAL;
1431789Sahrens 		return (dgettext(TEXT_DOMAIN, "Unknown error"));
1432789Sahrens 	}
1433789Sahrens }
1434