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