xref: /titanic_50/usr/src/lib/libsec/common/aclutils.c (revision fa9e4066f08beec538e775443c5be79dd423fcab)
1*fa9e4066Sahrens /*
2*fa9e4066Sahrens  * CDDL HEADER START
3*fa9e4066Sahrens  *
4*fa9e4066Sahrens  * The contents of this file are subject to the terms of the
5*fa9e4066Sahrens  * Common Development and Distribution License, Version 1.0 only
6*fa9e4066Sahrens  * (the "License").  You may not use this file except in compliance
7*fa9e4066Sahrens  * with the License.
8*fa9e4066Sahrens  *
9*fa9e4066Sahrens  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*fa9e4066Sahrens  * or http://www.opensolaris.org/os/licensing.
11*fa9e4066Sahrens  * See the License for the specific language governing permissions
12*fa9e4066Sahrens  * and limitations under the License.
13*fa9e4066Sahrens  *
14*fa9e4066Sahrens  * When distributing Covered Code, include this CDDL HEADER in each
15*fa9e4066Sahrens  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*fa9e4066Sahrens  * If applicable, add the following below this CDDL HEADER, with the
17*fa9e4066Sahrens  * fields enclosed by brackets "[]" replaced with your own identifying
18*fa9e4066Sahrens  * information: Portions Copyright [yyyy] [name of copyright owner]
19*fa9e4066Sahrens  *
20*fa9e4066Sahrens  * CDDL HEADER END
21*fa9e4066Sahrens  */
22*fa9e4066Sahrens /*
23*fa9e4066Sahrens  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24*fa9e4066Sahrens  * Use is subject to license terms.
25*fa9e4066Sahrens  */
26*fa9e4066Sahrens 
27*fa9e4066Sahrens #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*fa9e4066Sahrens 
29*fa9e4066Sahrens #include <stdlib.h>
30*fa9e4066Sahrens #include <string.h>
31*fa9e4066Sahrens #include <unistd.h>
32*fa9e4066Sahrens #include <limits.h>
33*fa9e4066Sahrens #include <grp.h>
34*fa9e4066Sahrens #include <pwd.h>
35*fa9e4066Sahrens #include <sys/types.h>
36*fa9e4066Sahrens #include <sys/acl.h>
37*fa9e4066Sahrens #include <errno.h>
38*fa9e4066Sahrens #include <sys/stat.h>
39*fa9e4066Sahrens #include <locale.h>
40*fa9e4066Sahrens #include <aclutils.h>
41*fa9e4066Sahrens #include <acl_common.h>
42*fa9e4066Sahrens 
43*fa9e4066Sahrens #define	ACL_PATH	0
44*fa9e4066Sahrens #define	ACL_FD		1
45*fa9e4066Sahrens 
46*fa9e4066Sahrens #define	ACE_POSIX_SUPPORTED_BITS (ACE_READ_DATA | \
47*fa9e4066Sahrens     ACE_WRITE_DATA | ACE_APPEND_DATA | ACE_EXECUTE | \
48*fa9e4066Sahrens     ACE_READ_ATTRIBUTES | ACE_READ_ACL | ACE_WRITE_ACL)
49*fa9e4066Sahrens 
50*fa9e4066Sahrens 
51*fa9e4066Sahrens #define	ACL_SYNCHRONIZE_SET_ALLOW		0x0000002
52*fa9e4066Sahrens #define	ACL_SYNCHRONIZE_SET_DENY		0x0000001
53*fa9e4066Sahrens 
54*fa9e4066Sahrens #define	ACL_WRITE_OWNER_SET_ALLOW		0x0000020
55*fa9e4066Sahrens #define	ACL_WRITE_OWNER_SET_DENY		0x0000010
56*fa9e4066Sahrens 
57*fa9e4066Sahrens #define	ACL_WRITE_ATTRS_OWNER_SET_ALLOW		0x0002000
58*fa9e4066Sahrens #define	ACL_WRITE_ATTRS_OWNER_SET_DENY		0x0001000
59*fa9e4066Sahrens 
60*fa9e4066Sahrens #define	ACL_WRITE_ATTRS_WRITER_SET_DENY		0x0010000
61*fa9e4066Sahrens 
62*fa9e4066Sahrens #define	ACL_DELETE_SET_ALLOW			0x0000200
63*fa9e4066Sahrens #define	ACL_DELETE_SET_DENY			0x0000100
64*fa9e4066Sahrens 
65*fa9e4066Sahrens #define	ACL_READ_NAMED_READER_SET_ALLOW		0x2000000
66*fa9e4066Sahrens 
67*fa9e4066Sahrens #define	ACL_WRITE_NAMED_WRITER_SET_ALLOW	0x0200000
68*fa9e4066Sahrens #define	ACL_WRITE_NAMED_WRITER_SET_DENY		0x0100000
69*fa9e4066Sahrens 
70*fa9e4066Sahrens #define	ACL_WRITE_ATTRS_OWNER_SET_ALLOW		0x0002000
71*fa9e4066Sahrens #define	ACL_WRITE_ATTRS_WRITER_SET_ALLOW	0x0020000
72*fa9e4066Sahrens 
73*fa9e4066Sahrens #define	ACL_WRITE_OWNER_ERR_DENY		0x0000040
74*fa9e4066Sahrens #define	ACL_READ_NAMED_READER_SET_DENY		0x1000000
75*fa9e4066Sahrens #define	ACL_WRITE_NAMED_WRITER_SET_ALLO		W0x0200000
76*fa9e4066Sahrens typedef union {
77*fa9e4066Sahrens 	const char *file;
78*fa9e4066Sahrens 	int  fd;
79*fa9e4066Sahrens } acl_inp;
80*fa9e4066Sahrens 
81*fa9e4066Sahrens acl_t *
82*fa9e4066Sahrens acl_alloc(enum acl_type type)
83*fa9e4066Sahrens {
84*fa9e4066Sahrens 	acl_t *aclp;
85*fa9e4066Sahrens 
86*fa9e4066Sahrens 	aclp = malloc(sizeof (acl_t));
87*fa9e4066Sahrens 
88*fa9e4066Sahrens 	if (aclp == NULL)
89*fa9e4066Sahrens 		return (NULL);
90*fa9e4066Sahrens 
91*fa9e4066Sahrens 	aclp->acl_aclp = NULL;
92*fa9e4066Sahrens 	aclp->acl_cnt = 0;
93*fa9e4066Sahrens 
94*fa9e4066Sahrens 	switch (type) {
95*fa9e4066Sahrens 	case ACE_T:
96*fa9e4066Sahrens 		aclp->acl_type = ACE_T;
97*fa9e4066Sahrens 		aclp->acl_entry_size = sizeof (ace_t);
98*fa9e4066Sahrens 		break;
99*fa9e4066Sahrens 	case ACLENT_T:
100*fa9e4066Sahrens 		aclp->acl_type = ACLENT_T;
101*fa9e4066Sahrens 		aclp->acl_entry_size = sizeof (aclent_t);
102*fa9e4066Sahrens 		break;
103*fa9e4066Sahrens 	default:
104*fa9e4066Sahrens 		acl_free(aclp);
105*fa9e4066Sahrens 		aclp = NULL;
106*fa9e4066Sahrens 	}
107*fa9e4066Sahrens 	return (aclp);
108*fa9e4066Sahrens }
109*fa9e4066Sahrens 
110*fa9e4066Sahrens /*
111*fa9e4066Sahrens  * Free acl_t structure
112*fa9e4066Sahrens  */
113*fa9e4066Sahrens void
114*fa9e4066Sahrens acl_free(acl_t *aclp)
115*fa9e4066Sahrens {
116*fa9e4066Sahrens 	if (aclp == NULL)
117*fa9e4066Sahrens 		return;
118*fa9e4066Sahrens 
119*fa9e4066Sahrens 	if (aclp->acl_aclp)
120*fa9e4066Sahrens 		free(aclp->acl_aclp);
121*fa9e4066Sahrens 	free(aclp);
122*fa9e4066Sahrens }
123*fa9e4066Sahrens 
124*fa9e4066Sahrens /*
125*fa9e4066Sahrens  * Determine whether a file has a trivial ACL
126*fa9e4066Sahrens  * returns: 	0 = trivial
127*fa9e4066Sahrens  *		1 = nontrivial
128*fa9e4066Sahrens  *		<0 some other system failure, such as ENOENT or EPERM
129*fa9e4066Sahrens  */
130*fa9e4066Sahrens int
131*fa9e4066Sahrens acl_trivial(const char *filename)
132*fa9e4066Sahrens {
133*fa9e4066Sahrens 	int acl_flavor;
134*fa9e4066Sahrens 	int aclcnt;
135*fa9e4066Sahrens 	int cntcmd;
136*fa9e4066Sahrens 	int val = 0;
137*fa9e4066Sahrens 	ace_t *acep;
138*fa9e4066Sahrens 
139*fa9e4066Sahrens 	acl_flavor = pathconf(filename, _PC_ACL_ENABLED);
140*fa9e4066Sahrens 	if (acl_flavor == -1)
141*fa9e4066Sahrens 		return (-1);
142*fa9e4066Sahrens 
143*fa9e4066Sahrens 	if (acl_flavor == _ACL_ACE_ENABLED)
144*fa9e4066Sahrens 		cntcmd = ACE_GETACLCNT;
145*fa9e4066Sahrens 	else
146*fa9e4066Sahrens 		cntcmd = GETACLCNT;
147*fa9e4066Sahrens 
148*fa9e4066Sahrens 	aclcnt = acl(filename, cntcmd, 0, NULL);
149*fa9e4066Sahrens 	if (aclcnt > 0) {
150*fa9e4066Sahrens 		if (acl_flavor == _ACL_ACE_ENABLED) {
151*fa9e4066Sahrens 			if (aclcnt != 6)
152*fa9e4066Sahrens 				val = 1;
153*fa9e4066Sahrens 			else {
154*fa9e4066Sahrens 				acep = malloc(sizeof (ace_t) * aclcnt);
155*fa9e4066Sahrens 				if (acep == NULL)
156*fa9e4066Sahrens 					return (-1);
157*fa9e4066Sahrens 				if (acl(filename, ACE_GETACL,
158*fa9e4066Sahrens 				    aclcnt, acep) < 0) {
159*fa9e4066Sahrens 					free(acep);
160*fa9e4066Sahrens 					return (-1);
161*fa9e4066Sahrens 				}
162*fa9e4066Sahrens 
163*fa9e4066Sahrens 				val = ace_trivial(acep, aclcnt);
164*fa9e4066Sahrens 				free(acep);
165*fa9e4066Sahrens 			}
166*fa9e4066Sahrens 		} else if (aclcnt > MIN_ACL_ENTRIES)
167*fa9e4066Sahrens 			val = 1;
168*fa9e4066Sahrens 	}
169*fa9e4066Sahrens 	return (val);
170*fa9e4066Sahrens }
171*fa9e4066Sahrens 
172*fa9e4066Sahrens static uint32_t
173*fa9e4066Sahrens access_mask_set(int haswriteperm, int hasreadperm, int isowner, int isallow)
174*fa9e4066Sahrens {
175*fa9e4066Sahrens 	uint32_t access_mask = 0;
176*fa9e4066Sahrens 	int acl_produce;
177*fa9e4066Sahrens 	int synchronize_set = 0, write_owner_set = 0;
178*fa9e4066Sahrens 	int delete_set = 0, write_attrs_set = 0;
179*fa9e4066Sahrens 	int read_named_set = 0, write_named_set = 0;
180*fa9e4066Sahrens 
181*fa9e4066Sahrens 	acl_produce = (ACL_SYNCHRONIZE_SET_ALLOW |
182*fa9e4066Sahrens 	    ACL_WRITE_ATTRS_OWNER_SET_ALLOW |
183*fa9e4066Sahrens 	    ACL_WRITE_ATTRS_WRITER_SET_DENY);
184*fa9e4066Sahrens 
185*fa9e4066Sahrens 	if (isallow) {
186*fa9e4066Sahrens 		synchronize_set = ACL_SYNCHRONIZE_SET_ALLOW;
187*fa9e4066Sahrens 		write_owner_set = ACL_WRITE_OWNER_SET_ALLOW;
188*fa9e4066Sahrens 		delete_set = ACL_DELETE_SET_ALLOW;
189*fa9e4066Sahrens 		if (hasreadperm)
190*fa9e4066Sahrens 			read_named_set = ACL_READ_NAMED_READER_SET_ALLOW;
191*fa9e4066Sahrens 		if (haswriteperm)
192*fa9e4066Sahrens 			write_named_set = ACL_WRITE_NAMED_WRITER_SET_ALLOW;
193*fa9e4066Sahrens 		if (isowner)
194*fa9e4066Sahrens 			write_attrs_set = ACL_WRITE_ATTRS_OWNER_SET_ALLOW;
195*fa9e4066Sahrens 		else if (haswriteperm)
196*fa9e4066Sahrens 			write_attrs_set = ACL_WRITE_ATTRS_WRITER_SET_ALLOW;
197*fa9e4066Sahrens 	} else {
198*fa9e4066Sahrens 
199*fa9e4066Sahrens 		synchronize_set = ACL_SYNCHRONIZE_SET_DENY;
200*fa9e4066Sahrens 		write_owner_set = ACL_WRITE_OWNER_SET_DENY;
201*fa9e4066Sahrens 		delete_set = ACL_DELETE_SET_DENY;
202*fa9e4066Sahrens 		if (hasreadperm)
203*fa9e4066Sahrens 			read_named_set = ACL_READ_NAMED_READER_SET_DENY;
204*fa9e4066Sahrens 		if (haswriteperm)
205*fa9e4066Sahrens 			write_named_set = ACL_WRITE_NAMED_WRITER_SET_DENY;
206*fa9e4066Sahrens 		if (isowner)
207*fa9e4066Sahrens 			write_attrs_set = ACL_WRITE_ATTRS_OWNER_SET_DENY;
208*fa9e4066Sahrens 		else if (haswriteperm)
209*fa9e4066Sahrens 			write_attrs_set = ACL_WRITE_ATTRS_WRITER_SET_DENY;
210*fa9e4066Sahrens 		else
211*fa9e4066Sahrens 			/*
212*fa9e4066Sahrens 			 * If the entity is not the owner and does not
213*fa9e4066Sahrens 			 * have write permissions ACE_WRITE_ATTRIBUTES will
214*fa9e4066Sahrens 			 * always go in the DENY ACE.
215*fa9e4066Sahrens 			 */
216*fa9e4066Sahrens 			access_mask |= ACE_WRITE_ATTRIBUTES;
217*fa9e4066Sahrens 	}
218*fa9e4066Sahrens 
219*fa9e4066Sahrens 	if (acl_produce & synchronize_set)
220*fa9e4066Sahrens 		access_mask |= ACE_SYNCHRONIZE;
221*fa9e4066Sahrens 	if (acl_produce & write_owner_set)
222*fa9e4066Sahrens 		access_mask |= ACE_WRITE_OWNER;
223*fa9e4066Sahrens 	if (acl_produce & delete_set)
224*fa9e4066Sahrens 		access_mask |= ACE_DELETE;
225*fa9e4066Sahrens 	if (acl_produce & write_attrs_set)
226*fa9e4066Sahrens 		access_mask |= ACE_WRITE_ATTRIBUTES;
227*fa9e4066Sahrens 	if (acl_produce & read_named_set)
228*fa9e4066Sahrens 		access_mask |= ACE_READ_NAMED_ATTRS;
229*fa9e4066Sahrens 	if (acl_produce & write_named_set)
230*fa9e4066Sahrens 		access_mask |= ACE_WRITE_NAMED_ATTRS;
231*fa9e4066Sahrens 
232*fa9e4066Sahrens 	return (access_mask);
233*fa9e4066Sahrens }
234*fa9e4066Sahrens 
235*fa9e4066Sahrens /*
236*fa9e4066Sahrens  * Given an mode_t, convert it into an access_mask as used
237*fa9e4066Sahrens  * by nfsace, assuming aclent_t -> nfsace semantics.
238*fa9e4066Sahrens  */
239*fa9e4066Sahrens static uint32_t
240*fa9e4066Sahrens mode_to_ace_access(mode_t mode, int isdir, int isowner, int isallow)
241*fa9e4066Sahrens {
242*fa9e4066Sahrens 	uint32_t access = 0;
243*fa9e4066Sahrens 	int haswriteperm = 0;
244*fa9e4066Sahrens 	int hasreadperm = 0;
245*fa9e4066Sahrens 
246*fa9e4066Sahrens 	if (isallow) {
247*fa9e4066Sahrens 		haswriteperm = (mode & 02);
248*fa9e4066Sahrens 		hasreadperm = (mode & 04);
249*fa9e4066Sahrens 	} else {
250*fa9e4066Sahrens 		haswriteperm = !(mode & 02);
251*fa9e4066Sahrens 		hasreadperm = !(mode & 04);
252*fa9e4066Sahrens 	}
253*fa9e4066Sahrens 
254*fa9e4066Sahrens 	/*
255*fa9e4066Sahrens 	 * The following call takes care of correctly setting the following
256*fa9e4066Sahrens 	 * mask bits in the access_mask:
257*fa9e4066Sahrens 	 * ACE_SYNCHRONIZE, ACE_WRITE_OWNER, ACE_DELETE,
258*fa9e4066Sahrens 	 * ACE_WRITE_ATTRIBUTES, ACE_WRITE_NAMED_ATTRS, ACE_READ_NAMED_ATTRS
259*fa9e4066Sahrens 	 */
260*fa9e4066Sahrens 	access = access_mask_set(haswriteperm, hasreadperm, isowner, isallow);
261*fa9e4066Sahrens 
262*fa9e4066Sahrens 	if (isallow) {
263*fa9e4066Sahrens 		access |= ACE_READ_ACL | ACE_READ_ATTRIBUTES;
264*fa9e4066Sahrens 		if (isowner)
265*fa9e4066Sahrens 			access |= ACE_WRITE_ACL;
266*fa9e4066Sahrens 	} else {
267*fa9e4066Sahrens 		if (! isowner)
268*fa9e4066Sahrens 			access |= ACE_WRITE_ACL;
269*fa9e4066Sahrens 	}
270*fa9e4066Sahrens 
271*fa9e4066Sahrens 	/* read */
272*fa9e4066Sahrens 	if (mode & 04) {
273*fa9e4066Sahrens 		access |= ACE_READ_DATA;
274*fa9e4066Sahrens 	}
275*fa9e4066Sahrens 	/* write */
276*fa9e4066Sahrens 	if (mode & 02) {
277*fa9e4066Sahrens 		access |= ACE_WRITE_DATA |
278*fa9e4066Sahrens 		    ACE_APPEND_DATA;
279*fa9e4066Sahrens 		if (isdir)
280*fa9e4066Sahrens 			access |= ACE_DELETE_CHILD;
281*fa9e4066Sahrens 	}
282*fa9e4066Sahrens 	/* exec */
283*fa9e4066Sahrens 	if (mode & 01) {
284*fa9e4066Sahrens 		access |= ACE_EXECUTE;
285*fa9e4066Sahrens 	}
286*fa9e4066Sahrens 
287*fa9e4066Sahrens 	return (access);
288*fa9e4066Sahrens }
289*fa9e4066Sahrens 
290*fa9e4066Sahrens /*
291*fa9e4066Sahrens  * Given an nfsace (presumably an ALLOW entry), make a
292*fa9e4066Sahrens  * corresponding DENY entry at the address given.
293*fa9e4066Sahrens  */
294*fa9e4066Sahrens static void
295*fa9e4066Sahrens ace_make_deny(ace_t *allow, ace_t *deny, int isdir, int isowner)
296*fa9e4066Sahrens {
297*fa9e4066Sahrens 	(void) memcpy(deny, allow, sizeof (ace_t));
298*fa9e4066Sahrens 
299*fa9e4066Sahrens 	deny->a_who = allow->a_who;
300*fa9e4066Sahrens 
301*fa9e4066Sahrens 	deny->a_type = ACE_ACCESS_DENIED_ACE_TYPE;
302*fa9e4066Sahrens 	deny->a_access_mask ^= ACE_POSIX_SUPPORTED_BITS;
303*fa9e4066Sahrens 	if (isdir)
304*fa9e4066Sahrens 		deny->a_access_mask ^= ACE_DELETE_CHILD;
305*fa9e4066Sahrens 
306*fa9e4066Sahrens 	deny->a_access_mask &= ~(ACE_SYNCHRONIZE | ACE_WRITE_OWNER |
307*fa9e4066Sahrens 	    ACE_DELETE | ACE_WRITE_ATTRIBUTES | ACE_READ_NAMED_ATTRS |
308*fa9e4066Sahrens 	    ACE_WRITE_NAMED_ATTRS);
309*fa9e4066Sahrens 	deny->a_access_mask |= access_mask_set((allow->a_access_mask &
310*fa9e4066Sahrens 	    ACE_WRITE_DATA), (allow->a_access_mask & ACE_READ_DATA), isowner,
311*fa9e4066Sahrens 	    B_FALSE);
312*fa9e4066Sahrens }
313*fa9e4066Sahrens /*
314*fa9e4066Sahrens  * Make an initial pass over an array of aclent_t's.  Gather
315*fa9e4066Sahrens  * information such as an ACL_MASK (if any), number of users,
316*fa9e4066Sahrens  * number of groups, and whether the array needs to be sorted.
317*fa9e4066Sahrens  */
318*fa9e4066Sahrens static int
319*fa9e4066Sahrens ln_aent_preprocess(aclent_t *aclent, int n,
320*fa9e4066Sahrens     int *hasmask, mode_t *mask,
321*fa9e4066Sahrens     int *numuser, int *numgroup, int *needsort)
322*fa9e4066Sahrens {
323*fa9e4066Sahrens 	int error = 0;
324*fa9e4066Sahrens 	int i;
325*fa9e4066Sahrens 	int curtype = 0;
326*fa9e4066Sahrens 
327*fa9e4066Sahrens 	*hasmask = 0;
328*fa9e4066Sahrens 	*mask = 07;
329*fa9e4066Sahrens 	*needsort = 0;
330*fa9e4066Sahrens 	*numuser = 0;
331*fa9e4066Sahrens 	*numgroup = 0;
332*fa9e4066Sahrens 
333*fa9e4066Sahrens 	for (i = 0; i < n; i++) {
334*fa9e4066Sahrens 		if (aclent[i].a_type < curtype)
335*fa9e4066Sahrens 			*needsort = 1;
336*fa9e4066Sahrens 		else if (aclent[i].a_type > curtype)
337*fa9e4066Sahrens 			curtype = aclent[i].a_type;
338*fa9e4066Sahrens 		if (aclent[i].a_type & USER)
339*fa9e4066Sahrens 			(*numuser)++;
340*fa9e4066Sahrens 		if (aclent[i].a_type & (GROUP | GROUP_OBJ))
341*fa9e4066Sahrens 			(*numgroup)++;
342*fa9e4066Sahrens 		if (aclent[i].a_type & CLASS_OBJ) {
343*fa9e4066Sahrens 			if (*hasmask) {
344*fa9e4066Sahrens 				error = EINVAL;
345*fa9e4066Sahrens 				goto out;
346*fa9e4066Sahrens 			} else {
347*fa9e4066Sahrens 				*hasmask = 1;
348*fa9e4066Sahrens 				*mask = aclent[i].a_perm;
349*fa9e4066Sahrens 			}
350*fa9e4066Sahrens 		}
351*fa9e4066Sahrens 	}
352*fa9e4066Sahrens 
353*fa9e4066Sahrens 	if ((! *hasmask) && (*numuser + *numgroup > 1)) {
354*fa9e4066Sahrens 		error = EINVAL;
355*fa9e4066Sahrens 		goto out;
356*fa9e4066Sahrens 	}
357*fa9e4066Sahrens 
358*fa9e4066Sahrens out:
359*fa9e4066Sahrens 	return (error);
360*fa9e4066Sahrens }
361*fa9e4066Sahrens 
362*fa9e4066Sahrens /*
363*fa9e4066Sahrens  * Convert an array of aclent_t into an array of nfsace entries,
364*fa9e4066Sahrens  * following POSIX draft -> nfsv4 conversion semantics as outlined in
365*fa9e4066Sahrens  * the IETF draft.
366*fa9e4066Sahrens  */
367*fa9e4066Sahrens static int
368*fa9e4066Sahrens ln_aent_to_ace(aclent_t *aclent, int n, ace_t **acepp, int *rescount, int isdir)
369*fa9e4066Sahrens {
370*fa9e4066Sahrens 	int error = 0;
371*fa9e4066Sahrens 	mode_t mask;
372*fa9e4066Sahrens 	int numuser, numgroup, needsort;
373*fa9e4066Sahrens 	int resultsize = 0;
374*fa9e4066Sahrens 	int i, groupi = 0, skip;
375*fa9e4066Sahrens 	ace_t *acep, *result = NULL;
376*fa9e4066Sahrens 	int hasmask;
377*fa9e4066Sahrens 
378*fa9e4066Sahrens 	error = ln_aent_preprocess(aclent, n, &hasmask, &mask,
379*fa9e4066Sahrens 	    &numuser, &numgroup, &needsort);
380*fa9e4066Sahrens 	if (error != 0)
381*fa9e4066Sahrens 		goto out;
382*fa9e4066Sahrens 
383*fa9e4066Sahrens 	/* allow + deny for each aclent */
384*fa9e4066Sahrens 	resultsize = n * 2;
385*fa9e4066Sahrens 	if (hasmask) {
386*fa9e4066Sahrens 		/*
387*fa9e4066Sahrens 		 * stick extra deny on the group_obj and on each
388*fa9e4066Sahrens 		 * user|group for the mask (the group_obj was added
389*fa9e4066Sahrens 		 * into the count for numgroup)
390*fa9e4066Sahrens 		 */
391*fa9e4066Sahrens 		resultsize += numuser + numgroup;
392*fa9e4066Sahrens 		/* ... and don't count the mask itself */
393*fa9e4066Sahrens 		resultsize -= 2;
394*fa9e4066Sahrens 	}
395*fa9e4066Sahrens 
396*fa9e4066Sahrens 	/* sort the source if necessary */
397*fa9e4066Sahrens 	if (needsort)
398*fa9e4066Sahrens 		ksort((caddr_t)aclent, n, sizeof (aclent_t), cmp2acls);
399*fa9e4066Sahrens 
400*fa9e4066Sahrens 	result = acep = calloc(1, resultsize * sizeof (ace_t));
401*fa9e4066Sahrens 	if (result == NULL)
402*fa9e4066Sahrens 		goto out;
403*fa9e4066Sahrens 
404*fa9e4066Sahrens 	for (i = 0; i < n; i++) {
405*fa9e4066Sahrens 		/*
406*fa9e4066Sahrens 		 * don't process CLASS_OBJ (mask); mask was grabbed in
407*fa9e4066Sahrens 		 * ln_aent_preprocess()
408*fa9e4066Sahrens 		 */
409*fa9e4066Sahrens 		if (aclent[i].a_type & CLASS_OBJ)
410*fa9e4066Sahrens 			continue;
411*fa9e4066Sahrens 
412*fa9e4066Sahrens 		/* If we need an ACL_MASK emulator, prepend it now */
413*fa9e4066Sahrens 		if ((hasmask) &&
414*fa9e4066Sahrens 		    (aclent[i].a_type & (USER | GROUP | GROUP_OBJ))) {
415*fa9e4066Sahrens 			acep->a_type = ACE_ACCESS_DENIED_ACE_TYPE;
416*fa9e4066Sahrens 			acep->a_flags = 0;
417*fa9e4066Sahrens 			if (aclent[i].a_type & GROUP_OBJ) {
418*fa9e4066Sahrens 				acep->a_who = -1;
419*fa9e4066Sahrens 				acep->a_flags |=
420*fa9e4066Sahrens 				    (ACE_IDENTIFIER_GROUP|ACE_GROUP);
421*fa9e4066Sahrens 			} else if (aclent[i].a_type & USER) {
422*fa9e4066Sahrens 				acep->a_who = aclent[i].a_id;
423*fa9e4066Sahrens 			} else {
424*fa9e4066Sahrens 				acep->a_who = aclent[i].a_id;
425*fa9e4066Sahrens 				acep->a_flags |= ACE_IDENTIFIER_GROUP;
426*fa9e4066Sahrens 			}
427*fa9e4066Sahrens 			if (aclent[i].a_type & ACL_DEFAULT) {
428*fa9e4066Sahrens 				acep->a_flags |= ACE_INHERIT_ONLY_ACE |
429*fa9e4066Sahrens 				    ACE_FILE_INHERIT_ACE |
430*fa9e4066Sahrens 				    ACE_DIRECTORY_INHERIT_ACE;
431*fa9e4066Sahrens 			}
432*fa9e4066Sahrens 			/*
433*fa9e4066Sahrens 			 * Set the access mask for the prepended deny
434*fa9e4066Sahrens 			 * ace.  To do this, we invert the mask (found
435*fa9e4066Sahrens 			 * in ln_aent_preprocess()) then convert it to an
436*fa9e4066Sahrens 			 * DENY ace access_mask.
437*fa9e4066Sahrens 			 */
438*fa9e4066Sahrens 			acep->a_access_mask = mode_to_ace_access((mask ^ 07),
439*fa9e4066Sahrens 			    isdir, 0, 0);
440*fa9e4066Sahrens 			acep += 1;
441*fa9e4066Sahrens 		}
442*fa9e4066Sahrens 
443*fa9e4066Sahrens 		/* handle a_perm -> access_mask */
444*fa9e4066Sahrens 		acep->a_access_mask = mode_to_ace_access(aclent[i].a_perm,
445*fa9e4066Sahrens 		    isdir, aclent[i].a_type & USER_OBJ, 1);
446*fa9e4066Sahrens 
447*fa9e4066Sahrens 		/* emulate a default aclent */
448*fa9e4066Sahrens 		if (aclent[i].a_type & ACL_DEFAULT) {
449*fa9e4066Sahrens 			acep->a_flags |= ACE_INHERIT_ONLY_ACE |
450*fa9e4066Sahrens 			    ACE_FILE_INHERIT_ACE |
451*fa9e4066Sahrens 			    ACE_DIRECTORY_INHERIT_ACE;
452*fa9e4066Sahrens 		}
453*fa9e4066Sahrens 
454*fa9e4066Sahrens 		/*
455*fa9e4066Sahrens 		 * handle a_perm and a_id
456*fa9e4066Sahrens 		 *
457*fa9e4066Sahrens 		 * this must be done last, since it involves the
458*fa9e4066Sahrens 		 * corresponding deny aces, which are handled
459*fa9e4066Sahrens 		 * differently for each different a_type.
460*fa9e4066Sahrens 		 */
461*fa9e4066Sahrens 		if (aclent[i].a_type & USER_OBJ) {
462*fa9e4066Sahrens 			acep->a_who = -1;
463*fa9e4066Sahrens 			acep->a_flags |= ACE_OWNER;
464*fa9e4066Sahrens 			ace_make_deny(acep, acep + 1, isdir, B_TRUE);
465*fa9e4066Sahrens 			acep += 2;
466*fa9e4066Sahrens 		} else if (aclent[i].a_type & USER) {
467*fa9e4066Sahrens 			acep->a_who = aclent[i].a_id;
468*fa9e4066Sahrens 			ace_make_deny(acep, acep + 1, isdir, B_FALSE);
469*fa9e4066Sahrens 			acep += 2;
470*fa9e4066Sahrens 		} else if (aclent[i].a_type & (GROUP_OBJ | GROUP)) {
471*fa9e4066Sahrens 			if (aclent[i].a_type & GROUP_OBJ) {
472*fa9e4066Sahrens 				acep->a_who = -1;
473*fa9e4066Sahrens 				acep->a_flags |= ACE_GROUP;
474*fa9e4066Sahrens 			} else {
475*fa9e4066Sahrens 				acep->a_who = aclent[i].a_id;
476*fa9e4066Sahrens 			}
477*fa9e4066Sahrens 			acep->a_flags |= ACE_IDENTIFIER_GROUP;
478*fa9e4066Sahrens 			/*
479*fa9e4066Sahrens 			 * Set the corresponding deny for the group ace.
480*fa9e4066Sahrens 			 *
481*fa9e4066Sahrens 			 * The deny aces go after all of the groups, unlike
482*fa9e4066Sahrens 			 * everything else, where they immediately follow
483*fa9e4066Sahrens 			 * the allow ace.
484*fa9e4066Sahrens 			 *
485*fa9e4066Sahrens 			 * We calculate "skip", the number of slots to
486*fa9e4066Sahrens 			 * skip ahead for the deny ace, here.
487*fa9e4066Sahrens 			 *
488*fa9e4066Sahrens 			 * The pattern is:
489*fa9e4066Sahrens 			 * MD1 A1 MD2 A2 MD3 A3 D1 D2 D3
490*fa9e4066Sahrens 			 * thus, skip is
491*fa9e4066Sahrens 			 * (2 * numgroup) - 1 - groupi
492*fa9e4066Sahrens 			 * (2 * numgroup) to account for MD + A
493*fa9e4066Sahrens 			 * - 1 to account for the fact that we're on the
494*fa9e4066Sahrens 			 * access (A), not the mask (MD)
495*fa9e4066Sahrens 			 * - groupi to account for the fact that we have
496*fa9e4066Sahrens 			 * passed up groupi number of MD's.
497*fa9e4066Sahrens 			 */
498*fa9e4066Sahrens 			skip = (2 * numgroup) - 1 - groupi;
499*fa9e4066Sahrens 			ace_make_deny(acep, acep + skip, isdir, B_FALSE);
500*fa9e4066Sahrens 			/*
501*fa9e4066Sahrens 			 * If we just did the last group, skip acep past
502*fa9e4066Sahrens 			 * all of the denies; else, just move ahead one.
503*fa9e4066Sahrens 			 */
504*fa9e4066Sahrens 			if (++groupi >= numgroup)
505*fa9e4066Sahrens 				acep += numgroup + 1;
506*fa9e4066Sahrens 			else
507*fa9e4066Sahrens 				acep += 1;
508*fa9e4066Sahrens 		} else if (aclent[i].a_type & OTHER_OBJ) {
509*fa9e4066Sahrens 			acep->a_who = -1;
510*fa9e4066Sahrens 			acep->a_flags |= ACE_EVERYONE;
511*fa9e4066Sahrens 			ace_make_deny(acep, acep + 1, isdir, B_FALSE);
512*fa9e4066Sahrens 			acep += 2;
513*fa9e4066Sahrens 		} else {
514*fa9e4066Sahrens 			error = EINVAL;
515*fa9e4066Sahrens 			goto out;
516*fa9e4066Sahrens 		}
517*fa9e4066Sahrens 	}
518*fa9e4066Sahrens 
519*fa9e4066Sahrens 	*acepp = result;
520*fa9e4066Sahrens 	*rescount = resultsize;
521*fa9e4066Sahrens 
522*fa9e4066Sahrens out:
523*fa9e4066Sahrens 	if (error != 0) {
524*fa9e4066Sahrens 		if ((result != NULL) && (resultsize > 0)) {
525*fa9e4066Sahrens 			free(result);
526*fa9e4066Sahrens 		}
527*fa9e4066Sahrens 	}
528*fa9e4066Sahrens 
529*fa9e4066Sahrens 	return (error);
530*fa9e4066Sahrens }
531*fa9e4066Sahrens 
532*fa9e4066Sahrens static int
533*fa9e4066Sahrens convert_aent_to_ace(aclent_t *aclentp, int aclcnt, int isdir,
534*fa9e4066Sahrens     ace_t **retacep, int *retacecnt)
535*fa9e4066Sahrens {
536*fa9e4066Sahrens 	ace_t *acep;
537*fa9e4066Sahrens 	ace_t *dfacep;
538*fa9e4066Sahrens 	ace_t *newacep;
539*fa9e4066Sahrens 	int acecnt = 0;
540*fa9e4066Sahrens 	int dfacecnt = 0;
541*fa9e4066Sahrens 	int dfaclstart = 0;
542*fa9e4066Sahrens 	int dfaclcnt = 0;
543*fa9e4066Sahrens 	aclent_t *aclp;
544*fa9e4066Sahrens 	int i;
545*fa9e4066Sahrens 	int error;
546*fa9e4066Sahrens 
547*fa9e4066Sahrens 	ksort((caddr_t)aclentp, aclcnt, sizeof (aclent_t), cmp2acls);
548*fa9e4066Sahrens 
549*fa9e4066Sahrens 	for (i = 0, aclp = aclentp; i < aclcnt; aclp++, i++) {
550*fa9e4066Sahrens 		if (aclp->a_type & ACL_DEFAULT)
551*fa9e4066Sahrens 			break;
552*fa9e4066Sahrens 	}
553*fa9e4066Sahrens 
554*fa9e4066Sahrens 	if (i < aclcnt) {
555*fa9e4066Sahrens 		dfaclstart = aclcnt - i;
556*fa9e4066Sahrens 		dfaclcnt = i;
557*fa9e4066Sahrens 	}
558*fa9e4066Sahrens 
559*fa9e4066Sahrens 	if (dfaclcnt && isdir == 0) {
560*fa9e4066Sahrens 		return (-1);
561*fa9e4066Sahrens 	}
562*fa9e4066Sahrens 
563*fa9e4066Sahrens 	error = ln_aent_to_ace(aclentp, i,  &acep, &acecnt, isdir);
564*fa9e4066Sahrens 	if (error)
565*fa9e4066Sahrens 		return (-1);
566*fa9e4066Sahrens 
567*fa9e4066Sahrens 	if (dfaclcnt) {
568*fa9e4066Sahrens 		error = ln_aent_to_ace(&aclentp[dfaclstart], dfaclcnt,
569*fa9e4066Sahrens 		    &dfacep, &dfacecnt, isdir);
570*fa9e4066Sahrens 		if (error) {
571*fa9e4066Sahrens 			if (acep) {
572*fa9e4066Sahrens 				free(acep);
573*fa9e4066Sahrens 			}
574*fa9e4066Sahrens 			return (-1);
575*fa9e4066Sahrens 		}
576*fa9e4066Sahrens 	}
577*fa9e4066Sahrens 
578*fa9e4066Sahrens 	newacep = malloc(sizeof (ace_t) * (acecnt + dfacecnt));
579*fa9e4066Sahrens 	if (newacep == NULL)
580*fa9e4066Sahrens 		return (-1);
581*fa9e4066Sahrens 
582*fa9e4066Sahrens 	(void) memcpy(newacep, acep, sizeof (ace_t) * acecnt);
583*fa9e4066Sahrens 	if (dfaclcnt) {
584*fa9e4066Sahrens 		(void) memcpy(newacep + acecnt, dfacep,
585*fa9e4066Sahrens 		    sizeof (ace_t) * dfacecnt);
586*fa9e4066Sahrens 	}
587*fa9e4066Sahrens 	free(acep);
588*fa9e4066Sahrens 	if (dfaclcnt)
589*fa9e4066Sahrens 		free(dfacep);
590*fa9e4066Sahrens 
591*fa9e4066Sahrens 	*retacecnt = acecnt + dfacecnt;
592*fa9e4066Sahrens 	*retacep = newacep;
593*fa9e4066Sahrens 	return (0);
594*fa9e4066Sahrens }
595*fa9e4066Sahrens 
596*fa9e4066Sahrens 
597*fa9e4066Sahrens static int
598*fa9e4066Sahrens cacl_get(acl_inp inp, int get_flag, int type, acl_t **aclp)
599*fa9e4066Sahrens {
600*fa9e4066Sahrens 	const char *fname;
601*fa9e4066Sahrens 	int fd;
602*fa9e4066Sahrens 	int ace_acl = 0;
603*fa9e4066Sahrens 	int error;
604*fa9e4066Sahrens 	int getcmd, cntcmd;
605*fa9e4066Sahrens 	acl_t *acl_info;
606*fa9e4066Sahrens 	int	save_errno;
607*fa9e4066Sahrens 	int	stat_error;
608*fa9e4066Sahrens 	struct stat64 statbuf;
609*fa9e4066Sahrens 
610*fa9e4066Sahrens 	*aclp = NULL;
611*fa9e4066Sahrens 	if (type == ACL_PATH) {
612*fa9e4066Sahrens 		fname = inp.file;
613*fa9e4066Sahrens 		ace_acl = pathconf(fname, _PC_ACL_ENABLED);
614*fa9e4066Sahrens 	} else {
615*fa9e4066Sahrens 		fd = inp.fd;
616*fa9e4066Sahrens 		ace_acl = fpathconf(fd, _PC_ACL_ENABLED);
617*fa9e4066Sahrens 	}
618*fa9e4066Sahrens 
619*fa9e4066Sahrens 	if (ace_acl == -1)
620*fa9e4066Sahrens 		return (-1);
621*fa9e4066Sahrens 
622*fa9e4066Sahrens 	/*
623*fa9e4066Sahrens 	 * if acl's aren't supported then
624*fa9e4066Sahrens 	 * send it through the old GETACL interface
625*fa9e4066Sahrens 	 */
626*fa9e4066Sahrens 	if (ace_acl == 0) {
627*fa9e4066Sahrens 		ace_acl = _ACL_ACLENT_ENABLED;
628*fa9e4066Sahrens 	}
629*fa9e4066Sahrens 
630*fa9e4066Sahrens 	if (ace_acl & _ACL_ACE_ENABLED) {
631*fa9e4066Sahrens 		cntcmd = ACE_GETACLCNT;
632*fa9e4066Sahrens 		getcmd = ACE_GETACL;
633*fa9e4066Sahrens 		acl_info = acl_alloc(ACE_T);
634*fa9e4066Sahrens 	} else {
635*fa9e4066Sahrens 		cntcmd = GETACLCNT;
636*fa9e4066Sahrens 		getcmd = GETACL;
637*fa9e4066Sahrens 		acl_info = acl_alloc(ACLENT_T);
638*fa9e4066Sahrens 	}
639*fa9e4066Sahrens 
640*fa9e4066Sahrens 	if (acl_info == NULL)
641*fa9e4066Sahrens 		return (-1);
642*fa9e4066Sahrens 
643*fa9e4066Sahrens 	if (type == ACL_PATH) {
644*fa9e4066Sahrens 		acl_info->acl_cnt = acl(fname, cntcmd, 0, NULL);
645*fa9e4066Sahrens 	} else {
646*fa9e4066Sahrens 		acl_info->acl_cnt = facl(fd, cntcmd, 0, NULL);
647*fa9e4066Sahrens 	}
648*fa9e4066Sahrens 
649*fa9e4066Sahrens 	save_errno = errno;
650*fa9e4066Sahrens 	if (acl_info->acl_cnt < 0) {
651*fa9e4066Sahrens 		acl_free(acl_info);
652*fa9e4066Sahrens 		errno = save_errno;
653*fa9e4066Sahrens 		return (-1);
654*fa9e4066Sahrens 	}
655*fa9e4066Sahrens 
656*fa9e4066Sahrens 	if (acl_info->acl_cnt == 0) {
657*fa9e4066Sahrens 		acl_free(acl_info);
658*fa9e4066Sahrens 		errno = save_errno;
659*fa9e4066Sahrens 		return (0);
660*fa9e4066Sahrens 	}
661*fa9e4066Sahrens 
662*fa9e4066Sahrens 	acl_info->acl_aclp =
663*fa9e4066Sahrens 	    malloc(acl_info->acl_cnt * acl_info->acl_entry_size);
664*fa9e4066Sahrens 	save_errno = errno;
665*fa9e4066Sahrens 
666*fa9e4066Sahrens 	if (acl_info->acl_aclp == NULL) {
667*fa9e4066Sahrens 		acl_free(acl_info);
668*fa9e4066Sahrens 		errno = save_errno;
669*fa9e4066Sahrens 		return (-1);
670*fa9e4066Sahrens 	}
671*fa9e4066Sahrens 
672*fa9e4066Sahrens 	if (type == ACL_PATH) {
673*fa9e4066Sahrens 		stat_error = stat64(fname, &statbuf);
674*fa9e4066Sahrens 		error = acl(fname, getcmd, acl_info->acl_cnt,
675*fa9e4066Sahrens 		    acl_info->acl_aclp);
676*fa9e4066Sahrens 	} else {
677*fa9e4066Sahrens 		stat_error = fstat64(fd, &statbuf);
678*fa9e4066Sahrens 		error = facl(fd, getcmd, acl_info->acl_cnt,
679*fa9e4066Sahrens 		    acl_info->acl_aclp);
680*fa9e4066Sahrens 	}
681*fa9e4066Sahrens 
682*fa9e4066Sahrens 	save_errno = errno;
683*fa9e4066Sahrens 	if (error == -1) {
684*fa9e4066Sahrens 		acl_free(acl_info);
685*fa9e4066Sahrens 		errno = save_errno;
686*fa9e4066Sahrens 		return (-1);
687*fa9e4066Sahrens 	}
688*fa9e4066Sahrens 
689*fa9e4066Sahrens 
690*fa9e4066Sahrens 	if (stat_error == 0) {
691*fa9e4066Sahrens 		acl_info->acl_flags =
692*fa9e4066Sahrens 		    (S_ISDIR(statbuf.st_mode) ? ACL_IS_DIR : 0);
693*fa9e4066Sahrens 	} else
694*fa9e4066Sahrens 		acl_info->acl_flags = 0;
695*fa9e4066Sahrens 
696*fa9e4066Sahrens 	switch (acl_info->acl_type) {
697*fa9e4066Sahrens 	case ACLENT_T:
698*fa9e4066Sahrens 		if (acl_info->acl_cnt <= MIN_ACL_ENTRIES)
699*fa9e4066Sahrens 			acl_info->acl_flags |= ACL_IS_TRIVIAL;
700*fa9e4066Sahrens 		break;
701*fa9e4066Sahrens 	case ACE_T:
702*fa9e4066Sahrens 		if (ace_trivial(acl_info->acl_aclp, acl_info->acl_cnt) == 0)
703*fa9e4066Sahrens 			acl_info->acl_flags |= ACL_IS_TRIVIAL;
704*fa9e4066Sahrens 		break;
705*fa9e4066Sahrens 	default:
706*fa9e4066Sahrens 		errno = EINVAL;
707*fa9e4066Sahrens 		acl_free(acl_info);
708*fa9e4066Sahrens 		return (-1);
709*fa9e4066Sahrens 	}
710*fa9e4066Sahrens 
711*fa9e4066Sahrens 	if ((acl_info->acl_flags & ACL_IS_TRIVIAL) &&
712*fa9e4066Sahrens 	    (get_flag & ACL_NO_TRIVIAL)) {
713*fa9e4066Sahrens 		acl_free(acl_info);
714*fa9e4066Sahrens 		errno = 0;
715*fa9e4066Sahrens 		return (0);
716*fa9e4066Sahrens 	}
717*fa9e4066Sahrens 
718*fa9e4066Sahrens 	*aclp = acl_info;
719*fa9e4066Sahrens 	return (0);
720*fa9e4066Sahrens }
721*fa9e4066Sahrens 
722*fa9e4066Sahrens /*
723*fa9e4066Sahrens  * return -1 on failure, otherwise the number of acl
724*fa9e4066Sahrens  * entries is returned
725*fa9e4066Sahrens  */
726*fa9e4066Sahrens int
727*fa9e4066Sahrens acl_get(const char *path, int get_flag, acl_t **aclp)
728*fa9e4066Sahrens {
729*fa9e4066Sahrens 	acl_inp acl_inp;
730*fa9e4066Sahrens 	acl_inp.file = path;
731*fa9e4066Sahrens 
732*fa9e4066Sahrens 	return (cacl_get(acl_inp, get_flag, ACL_PATH, aclp));
733*fa9e4066Sahrens }
734*fa9e4066Sahrens 
735*fa9e4066Sahrens int
736*fa9e4066Sahrens facl_get(int fd, int get_flag, acl_t **aclp)
737*fa9e4066Sahrens {
738*fa9e4066Sahrens 
739*fa9e4066Sahrens 	acl_inp acl_inp;
740*fa9e4066Sahrens 	acl_inp.fd = fd;
741*fa9e4066Sahrens 
742*fa9e4066Sahrens 	return (cacl_get(acl_inp, get_flag, ACL_FD, aclp));
743*fa9e4066Sahrens }
744*fa9e4066Sahrens 
745*fa9e4066Sahrens /*
746*fa9e4066Sahrens  * Set an ACL, translates acl to ace_t when appropriate.
747*fa9e4066Sahrens  */
748*fa9e4066Sahrens static int
749*fa9e4066Sahrens cacl_set(acl_inp *acl_inp, acl_t *aclp, int type)
750*fa9e4066Sahrens {
751*fa9e4066Sahrens 	int error = 0;
752*fa9e4066Sahrens 	int acl_flavor_target;
753*fa9e4066Sahrens 	ace_t *acep = NULL;
754*fa9e4066Sahrens 	int acecnt;
755*fa9e4066Sahrens 	struct stat64 statbuf;
756*fa9e4066Sahrens 	int stat_error;
757*fa9e4066Sahrens 	int isdir;
758*fa9e4066Sahrens 
759*fa9e4066Sahrens 
760*fa9e4066Sahrens 	if (type == ACL_PATH) {
761*fa9e4066Sahrens 		stat_error = stat64(acl_inp->file, &statbuf);
762*fa9e4066Sahrens 		if (stat_error)
763*fa9e4066Sahrens 			return (-1);
764*fa9e4066Sahrens 		acl_flavor_target = pathconf(acl_inp->file, _PC_ACL_ENABLED);
765*fa9e4066Sahrens 	} else {
766*fa9e4066Sahrens 		stat_error = fstat64(acl_inp->fd, &statbuf);
767*fa9e4066Sahrens 		if (stat_error)
768*fa9e4066Sahrens 			return (-1);
769*fa9e4066Sahrens 		acl_flavor_target = fpathconf(acl_inp->fd, _PC_ACL_ENABLED);
770*fa9e4066Sahrens 	}
771*fa9e4066Sahrens 
772*fa9e4066Sahrens 	isdir = S_ISDIR(statbuf.st_mode);
773*fa9e4066Sahrens 
774*fa9e4066Sahrens 	if (acl_flavor_target == -1)
775*fa9e4066Sahrens 		return (-1);
776*fa9e4066Sahrens 
777*fa9e4066Sahrens 	/*
778*fa9e4066Sahrens 	 * Translate aclent_t ACL's to ACE ACL's.
779*fa9e4066Sahrens 	 */
780*fa9e4066Sahrens 	if (acl_flavor_target ==  _ACL_ACE_ENABLED &&
781*fa9e4066Sahrens 	    aclp->acl_type == ACLENT_T) {
782*fa9e4066Sahrens 		error = convert_aent_to_ace(aclp->acl_aclp,
783*fa9e4066Sahrens 		    aclp->acl_cnt, isdir, &acep, &acecnt);
784*fa9e4066Sahrens 		if (error) {
785*fa9e4066Sahrens 			errno = ENOTSUP;
786*fa9e4066Sahrens 			return (-1);
787*fa9e4066Sahrens 		}
788*fa9e4066Sahrens 		/*
789*fa9e4066Sahrens 		 * replace old acl with newly translated acl
790*fa9e4066Sahrens 		 */
791*fa9e4066Sahrens 		free(aclp->acl_aclp);
792*fa9e4066Sahrens 		aclp->acl_aclp = acep;
793*fa9e4066Sahrens 		aclp->acl_cnt = acecnt;
794*fa9e4066Sahrens 		aclp->acl_type = ACE_T;
795*fa9e4066Sahrens 	}
796*fa9e4066Sahrens 
797*fa9e4066Sahrens 	if (type == ACL_PATH) {
798*fa9e4066Sahrens 		error = acl(acl_inp->file,
799*fa9e4066Sahrens 		    (aclp->acl_type == ACE_T) ? ACE_SETACL : SETACL,
800*fa9e4066Sahrens 		    aclp->acl_cnt, aclp->acl_aclp);
801*fa9e4066Sahrens 	} else {
802*fa9e4066Sahrens 		error = facl(acl_inp->fd,
803*fa9e4066Sahrens 		    (aclp->acl_type == ACE_T) ? ACE_SETACL : SETACL,
804*fa9e4066Sahrens 		    aclp->acl_cnt, aclp->acl_aclp);
805*fa9e4066Sahrens 	}
806*fa9e4066Sahrens 
807*fa9e4066Sahrens 	return (error);
808*fa9e4066Sahrens }
809*fa9e4066Sahrens 
810*fa9e4066Sahrens int
811*fa9e4066Sahrens acl_set(const char *path, acl_t *aclp)
812*fa9e4066Sahrens {
813*fa9e4066Sahrens 	acl_inp acl_inp;
814*fa9e4066Sahrens 
815*fa9e4066Sahrens 	acl_inp.file = path;
816*fa9e4066Sahrens 
817*fa9e4066Sahrens 	return (cacl_set(&acl_inp, aclp, ACL_PATH));
818*fa9e4066Sahrens }
819*fa9e4066Sahrens 
820*fa9e4066Sahrens int
821*fa9e4066Sahrens facl_set(int fd, acl_t *aclp)
822*fa9e4066Sahrens {
823*fa9e4066Sahrens 	acl_inp acl_inp;
824*fa9e4066Sahrens 
825*fa9e4066Sahrens 	acl_inp.fd = fd;
826*fa9e4066Sahrens 
827*fa9e4066Sahrens 	return (cacl_set(&acl_inp, aclp, ACL_FD));
828*fa9e4066Sahrens }
829*fa9e4066Sahrens 
830*fa9e4066Sahrens int
831*fa9e4066Sahrens acl_cnt(acl_t *aclp)
832*fa9e4066Sahrens {
833*fa9e4066Sahrens 	return (aclp->acl_cnt);
834*fa9e4066Sahrens }
835*fa9e4066Sahrens 
836*fa9e4066Sahrens int
837*fa9e4066Sahrens acl_type(acl_t *aclp)
838*fa9e4066Sahrens {
839*fa9e4066Sahrens 	return (aclp->acl_type);
840*fa9e4066Sahrens }
841*fa9e4066Sahrens 
842*fa9e4066Sahrens acl_t *
843*fa9e4066Sahrens acl_dup(acl_t *aclp)
844*fa9e4066Sahrens {
845*fa9e4066Sahrens 	acl_t *newaclp;
846*fa9e4066Sahrens 
847*fa9e4066Sahrens 	newaclp = acl_alloc(aclp->acl_type);
848*fa9e4066Sahrens 	if (newaclp == NULL)
849*fa9e4066Sahrens 		return (NULL);
850*fa9e4066Sahrens 
851*fa9e4066Sahrens 	newaclp->acl_aclp = malloc(aclp->acl_entry_size * aclp->acl_cnt);
852*fa9e4066Sahrens 	if (newaclp->acl_aclp == NULL) {
853*fa9e4066Sahrens 		acl_free(newaclp);
854*fa9e4066Sahrens 		return (NULL);
855*fa9e4066Sahrens 	}
856*fa9e4066Sahrens 
857*fa9e4066Sahrens 	(void) memcpy(newaclp->acl_aclp,
858*fa9e4066Sahrens 	    aclp->acl_aclp, aclp->acl_entry_size * aclp->acl_cnt);
859*fa9e4066Sahrens 	newaclp->acl_cnt = aclp->acl_cnt;
860*fa9e4066Sahrens 
861*fa9e4066Sahrens 	return (newaclp);
862*fa9e4066Sahrens }
863*fa9e4066Sahrens 
864*fa9e4066Sahrens int
865*fa9e4066Sahrens acl_flags(acl_t *aclp)
866*fa9e4066Sahrens {
867*fa9e4066Sahrens 	return (aclp->acl_flags);
868*fa9e4066Sahrens }
869*fa9e4066Sahrens 
870*fa9e4066Sahrens void *
871*fa9e4066Sahrens acl_data(acl_t *aclp)
872*fa9e4066Sahrens {
873*fa9e4066Sahrens 	return (aclp->acl_aclp);
874*fa9e4066Sahrens }
875*fa9e4066Sahrens 
876*fa9e4066Sahrens /*
877*fa9e4066Sahrens  * Remove an ACL from a file and create a trivial ACL based
878*fa9e4066Sahrens  * off of the mode argument.  After acl has been set owner/group
879*fa9e4066Sahrens  * are updated to match owner,group arguments
880*fa9e4066Sahrens  */
881*fa9e4066Sahrens int
882*fa9e4066Sahrens acl_strip(const char *file, uid_t owner, gid_t group, mode_t mode)
883*fa9e4066Sahrens {
884*fa9e4066Sahrens 	int	error = 0;
885*fa9e4066Sahrens 	aclent_t min_acl[MIN_ACL_ENTRIES];
886*fa9e4066Sahrens 	ace_t	min_ace_acl[6];	/* owner, group, everyone + complement denies */
887*fa9e4066Sahrens 	int	acl_flavor;
888*fa9e4066Sahrens 	int	aclcnt;
889*fa9e4066Sahrens 
890*fa9e4066Sahrens 	acl_flavor = pathconf(file, _PC_ACL_ENABLED);
891*fa9e4066Sahrens 
892*fa9e4066Sahrens 	if (acl_flavor == -1)
893*fa9e4066Sahrens 		return (-1);
894*fa9e4066Sahrens 	/*
895*fa9e4066Sahrens 	 * force it through aclent flavor when file system doesn't
896*fa9e4066Sahrens 	 * understand question
897*fa9e4066Sahrens 	 */
898*fa9e4066Sahrens 	if (acl_flavor == 0)
899*fa9e4066Sahrens 		acl_flavor = _ACL_ACLENT_ENABLED;
900*fa9e4066Sahrens 
901*fa9e4066Sahrens 	if (acl_flavor & _ACL_ACLENT_ENABLED) {
902*fa9e4066Sahrens 		min_acl[0].a_type = USER_OBJ;
903*fa9e4066Sahrens 		min_acl[0].a_id   = owner;
904*fa9e4066Sahrens 		min_acl[0].a_perm = ((mode & 0700) >> 6);
905*fa9e4066Sahrens 		min_acl[1].a_type = GROUP_OBJ;
906*fa9e4066Sahrens 		min_acl[1].a_id   = group;
907*fa9e4066Sahrens 		min_acl[1].a_perm = ((mode & 0070) >> 3);
908*fa9e4066Sahrens 		min_acl[2].a_type = CLASS_OBJ;
909*fa9e4066Sahrens 		min_acl[2].a_id   = (uid_t)-1;
910*fa9e4066Sahrens 		min_acl[2].a_perm = ((mode & 0070) >> 3);
911*fa9e4066Sahrens 		min_acl[3].a_type = OTHER_OBJ;
912*fa9e4066Sahrens 		min_acl[3].a_id   = (uid_t)-1;
913*fa9e4066Sahrens 		min_acl[3].a_perm = (mode & 0007);
914*fa9e4066Sahrens 		aclcnt = 4;
915*fa9e4066Sahrens 		error = acl(file, SETACL, aclcnt, min_acl);
916*fa9e4066Sahrens 	} else if (acl_flavor & _ACL_ACE_ENABLED) {
917*fa9e4066Sahrens 		(void) memcpy(min_ace_acl, trivial_acl, sizeof (ace_t) * 6);
918*fa9e4066Sahrens 
919*fa9e4066Sahrens 		/*
920*fa9e4066Sahrens 		 * Make aces match request mode
921*fa9e4066Sahrens 		 */
922*fa9e4066Sahrens 		adjust_ace_pair(&min_ace_acl[0], (mode & 0700) >> 6);
923*fa9e4066Sahrens 		adjust_ace_pair(&min_ace_acl[2], (mode & 0070) >> 3);
924*fa9e4066Sahrens 		adjust_ace_pair(&min_ace_acl[4], mode & 0007);
925*fa9e4066Sahrens 
926*fa9e4066Sahrens 		error = acl(file, ACE_SETACL, 6, min_ace_acl);
927*fa9e4066Sahrens 	} else {
928*fa9e4066Sahrens 		errno = EINVAL;
929*fa9e4066Sahrens 		error = 1;
930*fa9e4066Sahrens 	}
931*fa9e4066Sahrens 
932*fa9e4066Sahrens 	if (error == 0)
933*fa9e4066Sahrens 		error = chown(file, owner, group);
934*fa9e4066Sahrens 	return (error);
935*fa9e4066Sahrens }
936*fa9e4066Sahrens 
937*fa9e4066Sahrens static int
938*fa9e4066Sahrens ace_match(void *entry1, void *entry2)
939*fa9e4066Sahrens {
940*fa9e4066Sahrens 	ace_t *p1 = (ace_t *)entry1;
941*fa9e4066Sahrens 	ace_t *p2 = (ace_t *)entry2;
942*fa9e4066Sahrens 	ace_t ace1, ace2;
943*fa9e4066Sahrens 
944*fa9e4066Sahrens 	ace1 = *p1;
945*fa9e4066Sahrens 	ace2 = *p2;
946*fa9e4066Sahrens 
947*fa9e4066Sahrens 	/*
948*fa9e4066Sahrens 	 * Need to fixup who field for abstrations for
949*fa9e4066Sahrens 	 * accurate comparison, since field is undefined.
950*fa9e4066Sahrens 	 */
951*fa9e4066Sahrens 	if (ace1.a_flags & (ACE_OWNER|ACE_GROUP|ACE_EVERYONE))
952*fa9e4066Sahrens 		ace1.a_who = -1;
953*fa9e4066Sahrens 	if (ace2.a_flags & (ACE_OWNER|ACE_GROUP|ACE_EVERYONE))
954*fa9e4066Sahrens 		ace2.a_who = -1;
955*fa9e4066Sahrens 	return (memcmp(&ace1, &ace2, sizeof (ace_t)));
956*fa9e4066Sahrens }
957*fa9e4066Sahrens 
958*fa9e4066Sahrens static int
959*fa9e4066Sahrens aclent_match(void *entry1, void *entry2)
960*fa9e4066Sahrens {
961*fa9e4066Sahrens 	aclent_t *aclent1 = (aclent_t *)entry1;
962*fa9e4066Sahrens 	aclent_t *aclent2 = (aclent_t *)entry2;
963*fa9e4066Sahrens 
964*fa9e4066Sahrens 	return (memcmp(aclent1, aclent2, sizeof (aclent_t)));
965*fa9e4066Sahrens }
966*fa9e4066Sahrens 
967*fa9e4066Sahrens /*
968*fa9e4066Sahrens  * Find acl entries in acl that correspond to removeacl.  Search
969*fa9e4066Sahrens  * is started from slot.  The flag argument indicates whether to
970*fa9e4066Sahrens  * remove all matches or just the first match.
971*fa9e4066Sahrens  */
972*fa9e4066Sahrens int
973*fa9e4066Sahrens acl_removeentries(acl_t *acl, acl_t *removeacl, int start_slot, int flag)
974*fa9e4066Sahrens {
975*fa9e4066Sahrens 	int i, j;
976*fa9e4066Sahrens 	int match;
977*fa9e4066Sahrens 	int (*acl_match)(void *acl1, void *acl2);
978*fa9e4066Sahrens 	void *acl_entry, *remove_entry;
979*fa9e4066Sahrens 	void *start;
980*fa9e4066Sahrens 	int found = 0;
981*fa9e4066Sahrens 
982*fa9e4066Sahrens 	if (flag != ACL_REMOVE_ALL && flag != ACL_REMOVE_FIRST)
983*fa9e4066Sahrens 		flag = ACL_REMOVE_FIRST;
984*fa9e4066Sahrens 
985*fa9e4066Sahrens 	if (acl == NULL || removeacl == NULL)
986*fa9e4066Sahrens 		return (EACL_NO_ACL_ENTRY);
987*fa9e4066Sahrens 
988*fa9e4066Sahrens 	if (acl->acl_type != removeacl->acl_type)
989*fa9e4066Sahrens 		return (EACL_DIFF_TYPE);
990*fa9e4066Sahrens 
991*fa9e4066Sahrens 	if (acl->acl_type == ACLENT_T)
992*fa9e4066Sahrens 		acl_match = aclent_match;
993*fa9e4066Sahrens 	else
994*fa9e4066Sahrens 		acl_match = ace_match;
995*fa9e4066Sahrens 
996*fa9e4066Sahrens 	for (i = 0, remove_entry = removeacl->acl_aclp;
997*fa9e4066Sahrens 	    i != removeacl->acl_cnt; i++) {
998*fa9e4066Sahrens 
999*fa9e4066Sahrens 		j = 0;
1000*fa9e4066Sahrens 		acl_entry = (char *)acl->acl_aclp +
1001*fa9e4066Sahrens 		    (acl->acl_entry_size * start_slot);
1002*fa9e4066Sahrens 		for (;;) {
1003*fa9e4066Sahrens 			match = acl_match(acl_entry, remove_entry);
1004*fa9e4066Sahrens 			if (match == 0)  {
1005*fa9e4066Sahrens 				found++;
1006*fa9e4066Sahrens 				start = (char *)acl_entry +
1007*fa9e4066Sahrens 				    acl->acl_entry_size;
1008*fa9e4066Sahrens 				(void) memmove(acl_entry, start,
1009*fa9e4066Sahrens 				    acl->acl_entry_size *
1010*fa9e4066Sahrens 				    acl->acl_cnt-- - (j + 1));
1011*fa9e4066Sahrens 
1012*fa9e4066Sahrens 				if (flag == ACL_REMOVE_FIRST)
1013*fa9e4066Sahrens 					break;
1014*fa9e4066Sahrens 				/*
1015*fa9e4066Sahrens 				 * List has changed, restart search from
1016*fa9e4066Sahrens 				 * beginning.
1017*fa9e4066Sahrens 				 */
1018*fa9e4066Sahrens 				acl_entry = acl->acl_aclp;
1019*fa9e4066Sahrens 				j = 0;
1020*fa9e4066Sahrens 				continue;
1021*fa9e4066Sahrens 			}
1022*fa9e4066Sahrens 			acl_entry = ((char *)acl_entry + acl->acl_entry_size);
1023*fa9e4066Sahrens 			if (++j >= acl->acl_cnt) {
1024*fa9e4066Sahrens 				break;
1025*fa9e4066Sahrens 			}
1026*fa9e4066Sahrens 		}
1027*fa9e4066Sahrens 	}
1028*fa9e4066Sahrens 
1029*fa9e4066Sahrens 	return ((found == 0) ? EACL_NO_ACL_ENTRY : 0);
1030*fa9e4066Sahrens }
1031*fa9e4066Sahrens 
1032*fa9e4066Sahrens /*
1033*fa9e4066Sahrens  * Replace entires entries in acl1 with the corresponding entries
1034*fa9e4066Sahrens  * in newentries.  The where argument specifies where to begin
1035*fa9e4066Sahrens  * the replacement.  If the where argument is 1 greater than the
1036*fa9e4066Sahrens  * number of acl entries in acl1 then they are appended.  If the
1037*fa9e4066Sahrens  * where argument is 2+ greater than the number of acl entries then
1038*fa9e4066Sahrens  * EACL_INVALID_SLOT is returned.
1039*fa9e4066Sahrens  */
1040*fa9e4066Sahrens int
1041*fa9e4066Sahrens acl_modifyentries(acl_t *acl1, acl_t *newentries, int where)
1042*fa9e4066Sahrens {
1043*fa9e4066Sahrens 
1044*fa9e4066Sahrens 	int slot;
1045*fa9e4066Sahrens 	int slots_needed;
1046*fa9e4066Sahrens 	int slots_left;
1047*fa9e4066Sahrens 	int newsize;
1048*fa9e4066Sahrens 
1049*fa9e4066Sahrens 	if (acl1 == NULL || newentries == NULL)
1050*fa9e4066Sahrens 		return (EACL_NO_ACL_ENTRY);
1051*fa9e4066Sahrens 
1052*fa9e4066Sahrens 	if (where < 0 || where >= acl1->acl_cnt)
1053*fa9e4066Sahrens 		return (EACL_INVALID_SLOT);
1054*fa9e4066Sahrens 
1055*fa9e4066Sahrens 	if (acl1->acl_type != newentries->acl_type)
1056*fa9e4066Sahrens 		return (EACL_DIFF_TYPE);
1057*fa9e4066Sahrens 
1058*fa9e4066Sahrens 	slot = where;
1059*fa9e4066Sahrens 
1060*fa9e4066Sahrens 	slots_left = acl1->acl_cnt - slot + 1;
1061*fa9e4066Sahrens 	if (slots_left < newentries->acl_cnt) {
1062*fa9e4066Sahrens 		slots_needed = newentries->acl_cnt - slots_left;
1063*fa9e4066Sahrens 		newsize = (acl1->acl_entry_size * acl1->acl_cnt) +
1064*fa9e4066Sahrens 		    (acl1->acl_entry_size * slots_needed);
1065*fa9e4066Sahrens 		acl1->acl_aclp = realloc(acl1->acl_aclp, newsize);
1066*fa9e4066Sahrens 		if (acl1->acl_aclp == NULL)
1067*fa9e4066Sahrens 			return (-1);
1068*fa9e4066Sahrens 	}
1069*fa9e4066Sahrens 	(void) memcpy((char *)acl1->acl_aclp + (acl1->acl_entry_size * slot),
1070*fa9e4066Sahrens 	    newentries->acl_aclp,
1071*fa9e4066Sahrens 	    newentries->acl_entry_size * newentries->acl_cnt);
1072*fa9e4066Sahrens 
1073*fa9e4066Sahrens 	/*
1074*fa9e4066Sahrens 	 * Did ACL grow?
1075*fa9e4066Sahrens 	 */
1076*fa9e4066Sahrens 
1077*fa9e4066Sahrens 	if ((slot + newentries->acl_cnt) > acl1->acl_cnt) {
1078*fa9e4066Sahrens 		acl1->acl_cnt = slot + newentries->acl_cnt;
1079*fa9e4066Sahrens 	}
1080*fa9e4066Sahrens 
1081*fa9e4066Sahrens 	return (0);
1082*fa9e4066Sahrens }
1083*fa9e4066Sahrens 
1084*fa9e4066Sahrens /*
1085*fa9e4066Sahrens  * Add acl2 entries into acl1.  The where argument specifies where
1086*fa9e4066Sahrens  * to add the entries.
1087*fa9e4066Sahrens  */
1088*fa9e4066Sahrens int
1089*fa9e4066Sahrens acl_addentries(acl_t *acl1, acl_t *acl2, int where)
1090*fa9e4066Sahrens {
1091*fa9e4066Sahrens 
1092*fa9e4066Sahrens 	int newsize;
1093*fa9e4066Sahrens 	int len;
1094*fa9e4066Sahrens 	void *start;
1095*fa9e4066Sahrens 	void *to;
1096*fa9e4066Sahrens 
1097*fa9e4066Sahrens 	if (acl1 == NULL || acl2 == NULL)
1098*fa9e4066Sahrens 		return (EACL_NO_ACL_ENTRY);
1099*fa9e4066Sahrens 
1100*fa9e4066Sahrens 	if (acl1->acl_type != acl2->acl_type)
1101*fa9e4066Sahrens 		return (EACL_DIFF_TYPE);
1102*fa9e4066Sahrens 
1103*fa9e4066Sahrens 	/*
1104*fa9e4066Sahrens 	 * allow where to specify 1 past last slot for an append operation
1105*fa9e4066Sahrens 	 * but anything greater is an error.
1106*fa9e4066Sahrens 	 */
1107*fa9e4066Sahrens 	if (where < 0 || where > acl1->acl_cnt)
1108*fa9e4066Sahrens 		return (EACL_INVALID_SLOT);
1109*fa9e4066Sahrens 
1110*fa9e4066Sahrens 	newsize = (acl2->acl_entry_size * acl2->acl_cnt) +
1111*fa9e4066Sahrens 	    (acl1->acl_entry_size * acl1->acl_cnt);
1112*fa9e4066Sahrens 	acl1->acl_aclp = realloc(acl1->acl_aclp, newsize);
1113*fa9e4066Sahrens 	if (acl1->acl_aclp == NULL)
1114*fa9e4066Sahrens 		return (-1);
1115*fa9e4066Sahrens 
1116*fa9e4066Sahrens 	/*
1117*fa9e4066Sahrens 	 * first push down entries where new ones will be inserted
1118*fa9e4066Sahrens 	 */
1119*fa9e4066Sahrens 
1120*fa9e4066Sahrens 	to = (void *)((char *)acl1->acl_aclp +
1121*fa9e4066Sahrens 	    ((where + acl2->acl_cnt) * acl1->acl_entry_size));
1122*fa9e4066Sahrens 
1123*fa9e4066Sahrens 	start = (void *)((char *)acl1->acl_aclp +
1124*fa9e4066Sahrens 	    where * acl1->acl_entry_size);
1125*fa9e4066Sahrens 
1126*fa9e4066Sahrens 	if (where < acl1->acl_cnt) {
1127*fa9e4066Sahrens 		len = (acl1->acl_cnt - where) * acl1->acl_entry_size;
1128*fa9e4066Sahrens 		(void) memmove(to, start, len);
1129*fa9e4066Sahrens 	}
1130*fa9e4066Sahrens 
1131*fa9e4066Sahrens 	/*
1132*fa9e4066Sahrens 	 * now stick in new entries.
1133*fa9e4066Sahrens 	 */
1134*fa9e4066Sahrens 
1135*fa9e4066Sahrens 	(void) memmove(start, acl2->acl_aclp,
1136*fa9e4066Sahrens 	    acl2->acl_cnt * acl2->acl_entry_size);
1137*fa9e4066Sahrens 
1138*fa9e4066Sahrens 	acl1->acl_cnt += acl2->acl_cnt;
1139*fa9e4066Sahrens 	return (0);
1140*fa9e4066Sahrens }
1141*fa9e4066Sahrens 
1142*fa9e4066Sahrens static void
1143*fa9e4066Sahrens aclent_perms(int perm, char *txt_perms)
1144*fa9e4066Sahrens {
1145*fa9e4066Sahrens 	if (perm & S_IROTH)
1146*fa9e4066Sahrens 		txt_perms[0] = 'r';
1147*fa9e4066Sahrens 	else
1148*fa9e4066Sahrens 		txt_perms[0] = '-';
1149*fa9e4066Sahrens 	if (perm & S_IWOTH)
1150*fa9e4066Sahrens 		txt_perms[1] = 'w';
1151*fa9e4066Sahrens 	else
1152*fa9e4066Sahrens 		txt_perms[1] = '-';
1153*fa9e4066Sahrens 	if (perm & S_IXOTH)
1154*fa9e4066Sahrens 		txt_perms[2] = 'x';
1155*fa9e4066Sahrens 	else
1156*fa9e4066Sahrens 		txt_perms[2] = '-';
1157*fa9e4066Sahrens 	txt_perms[3] = '\0';
1158*fa9e4066Sahrens }
1159*fa9e4066Sahrens 
1160*fa9e4066Sahrens static char *
1161*fa9e4066Sahrens pruname(uid_t uid)
1162*fa9e4066Sahrens {
1163*fa9e4066Sahrens 	struct passwd	*passwdp;
1164*fa9e4066Sahrens 	static char	uidp[10];	/* big enough */
1165*fa9e4066Sahrens 
1166*fa9e4066Sahrens 	passwdp = getpwuid(uid);
1167*fa9e4066Sahrens 	if (passwdp == (struct passwd *)NULL) {
1168*fa9e4066Sahrens 		/* could not get passwd information: display uid instead */
1169*fa9e4066Sahrens 		(void) sprintf(uidp, "%ld", (long)uid);
1170*fa9e4066Sahrens 		return (uidp);
1171*fa9e4066Sahrens 	} else
1172*fa9e4066Sahrens 		return (passwdp->pw_name);
1173*fa9e4066Sahrens }
1174*fa9e4066Sahrens 
1175*fa9e4066Sahrens static char *
1176*fa9e4066Sahrens prgname(gid_t gid)
1177*fa9e4066Sahrens {
1178*fa9e4066Sahrens 	struct group	*groupp;
1179*fa9e4066Sahrens 	static char	gidp[10];	/* big enough */
1180*fa9e4066Sahrens 
1181*fa9e4066Sahrens 	groupp = getgrgid(gid);
1182*fa9e4066Sahrens 	if (groupp == (struct group *)NULL) {
1183*fa9e4066Sahrens 		/* could not get group information: display gid instead */
1184*fa9e4066Sahrens 		(void) sprintf(gidp, "%ld", (long)gid);
1185*fa9e4066Sahrens 		return (gidp);
1186*fa9e4066Sahrens 	} else
1187*fa9e4066Sahrens 		return (groupp->gr_name);
1188*fa9e4066Sahrens }
1189*fa9e4066Sahrens static void
1190*fa9e4066Sahrens aclent_printacl(acl_t *aclp)
1191*fa9e4066Sahrens {
1192*fa9e4066Sahrens 	aclent_t *tp;
1193*fa9e4066Sahrens 	int aclcnt;
1194*fa9e4066Sahrens 	int mask;
1195*fa9e4066Sahrens 	int slot = 0;
1196*fa9e4066Sahrens 	char perm[4];
1197*fa9e4066Sahrens 
1198*fa9e4066Sahrens 	/* display ACL: assume it is sorted. */
1199*fa9e4066Sahrens 	aclcnt = aclp->acl_cnt;
1200*fa9e4066Sahrens 	for (tp = aclp->acl_aclp; aclcnt--; tp++) {
1201*fa9e4066Sahrens 		if (tp->a_type == CLASS_OBJ)
1202*fa9e4066Sahrens 			mask = tp->a_perm;
1203*fa9e4066Sahrens 	}
1204*fa9e4066Sahrens 	aclcnt = aclp->acl_cnt;
1205*fa9e4066Sahrens 	for (tp = aclp->acl_aclp; aclcnt--; tp++) {
1206*fa9e4066Sahrens 		(void) printf("     %d:", slot++);
1207*fa9e4066Sahrens 		switch (tp->a_type) {
1208*fa9e4066Sahrens 		case USER:
1209*fa9e4066Sahrens 			aclent_perms(tp->a_perm, perm);
1210*fa9e4066Sahrens 			(void) printf("user:%s:%s\t\t",
1211*fa9e4066Sahrens 			    pruname(tp->a_id), perm);
1212*fa9e4066Sahrens 			aclent_perms((tp->a_perm & mask), perm);
1213*fa9e4066Sahrens 			(void) printf("#effective:%s\n", perm);
1214*fa9e4066Sahrens 			break;
1215*fa9e4066Sahrens 		case USER_OBJ:
1216*fa9e4066Sahrens 			/* no need to display uid */
1217*fa9e4066Sahrens 			aclent_perms(tp->a_perm, perm);
1218*fa9e4066Sahrens 			(void) printf("user::%s\n", perm);
1219*fa9e4066Sahrens 			break;
1220*fa9e4066Sahrens 		case GROUP:
1221*fa9e4066Sahrens 			aclent_perms(tp->a_perm, perm);
1222*fa9e4066Sahrens 			(void) printf("group:%s:%s\t\t",
1223*fa9e4066Sahrens 			    prgname(tp->a_id), perm);
1224*fa9e4066Sahrens 			aclent_perms(tp->a_perm & mask, perm);
1225*fa9e4066Sahrens 			(void) printf("#effective:%s\n", perm);
1226*fa9e4066Sahrens 			break;
1227*fa9e4066Sahrens 		case GROUP_OBJ:
1228*fa9e4066Sahrens 			aclent_perms(tp->a_perm, perm);
1229*fa9e4066Sahrens 			(void) printf("group::%s\t\t", perm);
1230*fa9e4066Sahrens 			aclent_perms(tp->a_perm & mask, perm);
1231*fa9e4066Sahrens 			(void) printf("#effective:%s\n", perm);
1232*fa9e4066Sahrens 			break;
1233*fa9e4066Sahrens 		case CLASS_OBJ:
1234*fa9e4066Sahrens 			aclent_perms(tp->a_perm, perm);
1235*fa9e4066Sahrens 			(void) printf("mask:%s\n", perm);
1236*fa9e4066Sahrens 			break;
1237*fa9e4066Sahrens 		case OTHER_OBJ:
1238*fa9e4066Sahrens 			aclent_perms(tp->a_perm, perm);
1239*fa9e4066Sahrens 			(void) printf("other:%s\n", perm);
1240*fa9e4066Sahrens 			break;
1241*fa9e4066Sahrens 		case DEF_USER:
1242*fa9e4066Sahrens 			aclent_perms(tp->a_perm, perm);
1243*fa9e4066Sahrens 			(void) printf("default:user:%s:%s\n",
1244*fa9e4066Sahrens 			    pruname(tp->a_id), perm);
1245*fa9e4066Sahrens 			break;
1246*fa9e4066Sahrens 		case DEF_USER_OBJ:
1247*fa9e4066Sahrens 			aclent_perms(tp->a_perm, perm);
1248*fa9e4066Sahrens 			(void) printf("default:user::%s\n", perm);
1249*fa9e4066Sahrens 			break;
1250*fa9e4066Sahrens 		case DEF_GROUP:
1251*fa9e4066Sahrens 			aclent_perms(tp->a_perm, perm);
1252*fa9e4066Sahrens 			(void) printf("default:group:%s:%s\n",
1253*fa9e4066Sahrens 			    prgname(tp->a_id), perm);
1254*fa9e4066Sahrens 			break;
1255*fa9e4066Sahrens 		case DEF_GROUP_OBJ:
1256*fa9e4066Sahrens 			aclent_perms(tp->a_perm, perm);
1257*fa9e4066Sahrens 			(void) printf("default:group::%s\n", perm);
1258*fa9e4066Sahrens 			break;
1259*fa9e4066Sahrens 		case DEF_CLASS_OBJ:
1260*fa9e4066Sahrens 			aclent_perms(tp->a_perm, perm);
1261*fa9e4066Sahrens 			(void) printf("default:mask:%s\n", perm);
1262*fa9e4066Sahrens 			break;
1263*fa9e4066Sahrens 		case DEF_OTHER_OBJ:
1264*fa9e4066Sahrens 			aclent_perms(tp->a_perm, perm);
1265*fa9e4066Sahrens 			(void) printf("default:other:%s\n", perm);
1266*fa9e4066Sahrens 			break;
1267*fa9e4066Sahrens 		default:
1268*fa9e4066Sahrens 			(void) fprintf(stderr,
1269*fa9e4066Sahrens 			    gettext("unrecognized entry\n"));
1270*fa9e4066Sahrens 			break;
1271*fa9e4066Sahrens 		}
1272*fa9e4066Sahrens 	}
1273*fa9e4066Sahrens }
1274*fa9e4066Sahrens 
1275*fa9e4066Sahrens static void
1276*fa9e4066Sahrens split_line(char *str, int cols)
1277*fa9e4066Sahrens {
1278*fa9e4066Sahrens 	char *ptr;
1279*fa9e4066Sahrens 	int len;
1280*fa9e4066Sahrens 	int i;
1281*fa9e4066Sahrens 	int last_split;
1282*fa9e4066Sahrens 	char pad[11];
1283*fa9e4066Sahrens 	int pad_len;
1284*fa9e4066Sahrens 
1285*fa9e4066Sahrens 	len = strlen(str);
1286*fa9e4066Sahrens 	ptr = str;
1287*fa9e4066Sahrens 	(void) strcpy(pad, "");
1288*fa9e4066Sahrens 	pad_len = 0;
1289*fa9e4066Sahrens 
1290*fa9e4066Sahrens 	ptr = str;
1291*fa9e4066Sahrens 	last_split = 0;
1292*fa9e4066Sahrens 	for (i = 0; i != len; i++) {
1293*fa9e4066Sahrens 		if ((i + pad_len + 4) >= cols) {
1294*fa9e4066Sahrens 			(void) printf("%s%.*s\n", pad, last_split, ptr);
1295*fa9e4066Sahrens 			ptr = &ptr[last_split];
1296*fa9e4066Sahrens 			len = strlen(ptr);
1297*fa9e4066Sahrens 			i = 0;
1298*fa9e4066Sahrens 			pad_len = 4;
1299*fa9e4066Sahrens 			(void) strcpy(pad, "         ");
1300*fa9e4066Sahrens 		} else {
1301*fa9e4066Sahrens 			if (ptr[i] == '/' || ptr[i] == ':') {
1302*fa9e4066Sahrens 				last_split = i;
1303*fa9e4066Sahrens 			}
1304*fa9e4066Sahrens 		}
1305*fa9e4066Sahrens 	}
1306*fa9e4066Sahrens 	if (i == len) {
1307*fa9e4066Sahrens 		(void) printf("%s%s\n", pad, ptr);
1308*fa9e4066Sahrens 	}
1309*fa9e4066Sahrens }
1310*fa9e4066Sahrens 
1311*fa9e4066Sahrens static void
1312*fa9e4066Sahrens ace_printacl(acl_t *aclp, int cols)
1313*fa9e4066Sahrens {
1314*fa9e4066Sahrens 	int  slot = 0;
1315*fa9e4066Sahrens 	char *token;
1316*fa9e4066Sahrens 	char *acltext;
1317*fa9e4066Sahrens 
1318*fa9e4066Sahrens 	acltext = acl_totext(aclp);
1319*fa9e4066Sahrens 
1320*fa9e4066Sahrens 	if (acltext == NULL)
1321*fa9e4066Sahrens 		return;
1322*fa9e4066Sahrens 
1323*fa9e4066Sahrens 	token = strtok(acltext, ",");
1324*fa9e4066Sahrens 	if (token == NULL) {
1325*fa9e4066Sahrens 		free(acltext);
1326*fa9e4066Sahrens 		return;
1327*fa9e4066Sahrens 	}
1328*fa9e4066Sahrens 
1329*fa9e4066Sahrens 	do {
1330*fa9e4066Sahrens 		(void) printf("     %d:", slot++);
1331*fa9e4066Sahrens 		split_line(token, cols - 5);
1332*fa9e4066Sahrens 	} while (token = strtok(NULL, ","));
1333*fa9e4066Sahrens 	free(acltext);
1334*fa9e4066Sahrens }
1335*fa9e4066Sahrens 
1336*fa9e4066Sahrens /*
1337*fa9e4066Sahrens  * pretty print an ACL.
1338*fa9e4066Sahrens  * For aclent_t ACL's the format is
1339*fa9e4066Sahrens  * similar to the old format used by getfacl,
1340*fa9e4066Sahrens  * with the addition of adding a "slot" number
1341*fa9e4066Sahrens  * before each entry.
1342*fa9e4066Sahrens  *
1343*fa9e4066Sahrens  * for ace_t ACL's the cols variable will break up
1344*fa9e4066Sahrens  * the long lines into multiple lines and will also
1345*fa9e4066Sahrens  * print a "slot" number.
1346*fa9e4066Sahrens  */
1347*fa9e4066Sahrens void
1348*fa9e4066Sahrens acl_printacl(acl_t *aclp, int cols)
1349*fa9e4066Sahrens {
1350*fa9e4066Sahrens 
1351*fa9e4066Sahrens 	switch (aclp->acl_type) {
1352*fa9e4066Sahrens 	case ACLENT_T:
1353*fa9e4066Sahrens 		aclent_printacl(aclp);
1354*fa9e4066Sahrens 		break;
1355*fa9e4066Sahrens 	case ACE_T:
1356*fa9e4066Sahrens 		ace_printacl(aclp, cols);
1357*fa9e4066Sahrens 		break;
1358*fa9e4066Sahrens 	}
1359*fa9e4066Sahrens }
1360*fa9e4066Sahrens 
1361*fa9e4066Sahrens 
1362*fa9e4066Sahrens /*
1363*fa9e4066Sahrens  * return text for an ACL error.
1364*fa9e4066Sahrens  */
1365*fa9e4066Sahrens char *
1366*fa9e4066Sahrens acl_strerror(int errnum)
1367*fa9e4066Sahrens {
1368*fa9e4066Sahrens 	switch (errnum) {
1369*fa9e4066Sahrens 	case EACL_GRP_ERROR:
1370*fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN,
1371*fa9e4066Sahrens 		    "There is more than one user group owner entry"));
1372*fa9e4066Sahrens 	case EACL_USER_ERROR:
1373*fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN,
1374*fa9e4066Sahrens 		    "There is more than one user owner entry"));
1375*fa9e4066Sahrens 	case EACL_OTHER_ERROR:
1376*fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN,
1377*fa9e4066Sahrens 		    "There is more than one other entry"));
1378*fa9e4066Sahrens 	case EACL_CLASS_ERROR:
1379*fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN,
1380*fa9e4066Sahrens 		    "There is more than one mask entry"));
1381*fa9e4066Sahrens 	case EACL_DUPLICATE_ERROR:
1382*fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN,
1383*fa9e4066Sahrens 		    "Duplicate user or group entries"));
1384*fa9e4066Sahrens 	case EACL_MISS_ERROR:
1385*fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN,
1386*fa9e4066Sahrens 		    "Missing user/group owner, other, mask entry"));
1387*fa9e4066Sahrens 	case EACL_MEM_ERROR:
1388*fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN,
1389*fa9e4066Sahrens 		    "Memory error"));
1390*fa9e4066Sahrens 	case EACL_ENTRY_ERROR:
1391*fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN,
1392*fa9e4066Sahrens 		    "Unrecognized entry type"));
1393*fa9e4066Sahrens 	case EACL_INHERIT_ERROR:
1394*fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN,
1395*fa9e4066Sahrens 		    "Invalid inheritance flags"));
1396*fa9e4066Sahrens 	case EACL_FLAGS_ERROR:
1397*fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN,
1398*fa9e4066Sahrens 		    "Unrecognized entry flags"));
1399*fa9e4066Sahrens 	case EACL_PERM_MASK_ERROR:
1400*fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN,
1401*fa9e4066Sahrens 		    "Invalid ACL permissions"));
1402*fa9e4066Sahrens 	case EACL_COUNT_ERROR:
1403*fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN,
1404*fa9e4066Sahrens 		    "Invalid ACL count"));
1405*fa9e4066Sahrens 	case EACL_INVALID_SLOT:
1406*fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN,
1407*fa9e4066Sahrens 		    "Invalid ACL entry number specified"));
1408*fa9e4066Sahrens 	case EACL_NO_ACL_ENTRY:
1409*fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN,
1410*fa9e4066Sahrens 		    "ACL entry doesn't exist"));
1411*fa9e4066Sahrens 	case EACL_DIFF_TYPE:
1412*fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN,
1413*fa9e4066Sahrens 		    "ACL type's are different"));
1414*fa9e4066Sahrens 	case EACL_INVALID_USER_GROUP:
1415*fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN, "Invalid user or group"));
1416*fa9e4066Sahrens 	case EACL_INVALID_STR:
1417*fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN, "ACL string is invalid"));
1418*fa9e4066Sahrens 	case EACL_FIELD_NOT_BLANK:
1419*fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN, "Field expected to be blank"));
1420*fa9e4066Sahrens 	case EACL_INVALID_ACCESS_TYPE:
1421*fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN, "Invalid access type"));
1422*fa9e4066Sahrens 	case EACL_UNKNOWN_DATA:
1423*fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN, "Unrecognized entry"));
1424*fa9e4066Sahrens 	case EACL_MISSING_FIELDS:
1425*fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN,
1426*fa9e4066Sahrens 		    "ACL specification missing required fields"));
1427*fa9e4066Sahrens 	case EACL_INHERIT_NOTDIR:
1428*fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN,
1429*fa9e4066Sahrens 		    "Inheritance flags are only allowed on directories"));
1430*fa9e4066Sahrens 	case -1:
1431*fa9e4066Sahrens 		return (strerror(errno));
1432*fa9e4066Sahrens 	default:
1433*fa9e4066Sahrens 		errno = EINVAL;
1434*fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN, "Unknown error"));
1435*fa9e4066Sahrens 	}
1436*fa9e4066Sahrens }
1437