xref: /illumos-gate/usr/src/lib/libsec/common/aclutils.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
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 (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 
27 #include <stdlib.h>
28 #include <string.h>
29 #include <unistd.h>
30 #include <limits.h>
31 #include <grp.h>
32 #include <pwd.h>
33 #include <strings.h>
34 #include <sys/types.h>
35 #include <errno.h>
36 #include <sys/stat.h>
37 #include <sys/varargs.h>
38 #include <locale.h>
39 #include <aclutils.h>
40 #include <sys/avl.h>
41 #include <acl_common.h>
42 #include <idmap.h>
43 
44 #define	ACL_PATH	0
45 #define	ACL_FD		1
46 
47 
48 typedef union {
49 	const char *file;
50 	int  fd;
51 } acl_inp;
52 
53 
54 /*
55  * Determine whether a file has a trivial ACL
56  * returns: 	0 = trivial
57  *		1 = nontrivial
58  *		<0 some other system failure, such as ENOENT or EPERM
59  */
60 int
61 acl_trivial(const char *filename)
62 {
63 	int acl_flavor;
64 	int aclcnt;
65 	int cntcmd;
66 	int val = 0;
67 	ace_t *acep;
68 
69 	acl_flavor = pathconf(filename, _PC_ACL_ENABLED);
70 
71 	if (acl_flavor == _ACL_ACE_ENABLED)
72 		cntcmd = ACE_GETACLCNT;
73 	else
74 		cntcmd = GETACLCNT;
75 
76 	aclcnt = acl(filename, cntcmd, 0, NULL);
77 	if (aclcnt > 0) {
78 		if (acl_flavor == _ACL_ACE_ENABLED) {
79 			acep = malloc(sizeof (ace_t) * aclcnt);
80 			if (acep == NULL)
81 				return (-1);
82 			if (acl(filename, ACE_GETACL,
83 			    aclcnt, acep) < 0) {
84 				free(acep);
85 				return (-1);
86 			}
87 
88 			val = ace_trivial(acep, aclcnt);
89 			free(acep);
90 
91 		} else if (aclcnt > MIN_ACL_ENTRIES)
92 			val = 1;
93 	}
94 	return (val);
95 }
96 
97 
98 static int
99 cacl_get(acl_inp inp, int get_flag, int type, acl_t **aclp)
100 {
101 	const char *fname;
102 	int fd;
103 	int ace_acl = 0;
104 	int error;
105 	int getcmd, cntcmd;
106 	acl_t *acl_info;
107 	int	save_errno;
108 	int	stat_error;
109 	struct stat64 statbuf;
110 
111 	*aclp = NULL;
112 	if (type == ACL_PATH) {
113 		fname = inp.file;
114 		ace_acl = pathconf(fname, _PC_ACL_ENABLED);
115 	} else {
116 		fd = inp.fd;
117 		ace_acl = fpathconf(fd, _PC_ACL_ENABLED);
118 	}
119 
120 	/*
121 	 * if acl's aren't supported then
122 	 * send it through the old GETACL interface
123 	 */
124 	if (ace_acl == 0 || ace_acl == -1) {
125 		ace_acl = _ACL_ACLENT_ENABLED;
126 	}
127 
128 	if (ace_acl & _ACL_ACE_ENABLED) {
129 		cntcmd = ACE_GETACLCNT;
130 		getcmd = ACE_GETACL;
131 		acl_info = acl_alloc(ACE_T);
132 	} else {
133 		cntcmd = GETACLCNT;
134 		getcmd = GETACL;
135 		acl_info = acl_alloc(ACLENT_T);
136 	}
137 
138 	if (acl_info == NULL)
139 		return (-1);
140 
141 	if (type == ACL_PATH) {
142 		acl_info->acl_cnt = acl(fname, cntcmd, 0, NULL);
143 	} else {
144 		acl_info->acl_cnt = facl(fd, cntcmd, 0, NULL);
145 	}
146 
147 	save_errno = errno;
148 	if (acl_info->acl_cnt < 0) {
149 		acl_free(acl_info);
150 		errno = save_errno;
151 		return (-1);
152 	}
153 
154 	if (acl_info->acl_cnt == 0) {
155 		acl_free(acl_info);
156 		errno = save_errno;
157 		return (0);
158 	}
159 
160 	acl_info->acl_aclp =
161 	    malloc(acl_info->acl_cnt * acl_info->acl_entry_size);
162 	save_errno = errno;
163 
164 	if (acl_info->acl_aclp == NULL) {
165 		acl_free(acl_info);
166 		errno = save_errno;
167 		return (-1);
168 	}
169 
170 	if (type == ACL_PATH) {
171 		stat_error = stat64(fname, &statbuf);
172 		error = acl(fname, getcmd, acl_info->acl_cnt,
173 		    acl_info->acl_aclp);
174 	} else {
175 		stat_error = fstat64(fd, &statbuf);
176 		error = facl(fd, getcmd, acl_info->acl_cnt,
177 		    acl_info->acl_aclp);
178 	}
179 
180 	save_errno = errno;
181 	if (error == -1) {
182 		acl_free(acl_info);
183 		errno = save_errno;
184 		return (-1);
185 	}
186 
187 
188 	if (stat_error == 0) {
189 		acl_info->acl_flags =
190 		    (S_ISDIR(statbuf.st_mode) ? ACL_IS_DIR : 0);
191 	} else
192 		acl_info->acl_flags = 0;
193 
194 	switch (acl_info->acl_type) {
195 	case ACLENT_T:
196 		if (acl_info->acl_cnt <= MIN_ACL_ENTRIES)
197 			acl_info->acl_flags |= ACL_IS_TRIVIAL;
198 		break;
199 	case ACE_T:
200 		if (ace_trivial(acl_info->acl_aclp, acl_info->acl_cnt) == 0)
201 			acl_info->acl_flags |= ACL_IS_TRIVIAL;
202 		break;
203 	default:
204 		errno = EINVAL;
205 		acl_free(acl_info);
206 		return (-1);
207 	}
208 
209 	if ((acl_info->acl_flags & ACL_IS_TRIVIAL) &&
210 	    (get_flag & ACL_NO_TRIVIAL)) {
211 		acl_free(acl_info);
212 		errno = 0;
213 		return (0);
214 	}
215 
216 	*aclp = acl_info;
217 	return (0);
218 }
219 
220 /*
221  * return -1 on failure, otherwise the number of acl
222  * entries is returned
223  */
224 int
225 acl_get(const char *path, int get_flag, acl_t **aclp)
226 {
227 	acl_inp acl_inp;
228 	acl_inp.file = path;
229 
230 	return (cacl_get(acl_inp, get_flag, ACL_PATH, aclp));
231 }
232 
233 int
234 facl_get(int fd, int get_flag, acl_t **aclp)
235 {
236 
237 	acl_inp acl_inp;
238 	acl_inp.fd = fd;
239 
240 	return (cacl_get(acl_inp, get_flag, ACL_FD, aclp));
241 }
242 
243 /*
244  * Set an ACL, translates acl to ace_t when appropriate.
245  */
246 static int
247 cacl_set(acl_inp *acl_inp, acl_t *aclp, int type)
248 {
249 	int error = 0;
250 	int acl_flavor_target;
251 	struct stat64 statbuf;
252 	int stat_error;
253 	int isdir;
254 
255 
256 	if (type == ACL_PATH) {
257 		stat_error = stat64(acl_inp->file, &statbuf);
258 		if (stat_error)
259 			return (-1);
260 		acl_flavor_target = pathconf(acl_inp->file, _PC_ACL_ENABLED);
261 	} else {
262 		stat_error = fstat64(acl_inp->fd, &statbuf);
263 		if (stat_error)
264 			return (-1);
265 		acl_flavor_target = fpathconf(acl_inp->fd, _PC_ACL_ENABLED);
266 	}
267 
268 	/*
269 	 * If target returns an error or 0 from pathconf call then
270 	 * fall back to UFS/POSIX Draft interface.
271 	 * In the case of 0 we will then fail in either acl(2) or
272 	 * acl_translate().  We could erroneously get 0 back from
273 	 * a file system that is using fs_pathconf() and not answering
274 	 * the _PC_ACL_ENABLED question itself.
275 	 */
276 	if (acl_flavor_target == 0 || acl_flavor_target == -1)
277 		acl_flavor_target = _ACL_ACLENT_ENABLED;
278 
279 	isdir = S_ISDIR(statbuf.st_mode);
280 
281 	if ((error = acl_translate(aclp, acl_flavor_target, isdir,
282 	    statbuf.st_uid, statbuf.st_gid)) != 0) {
283 		return (error);
284 	}
285 
286 	if (type == ACL_PATH) {
287 		error = acl(acl_inp->file,
288 		    (aclp->acl_type == ACE_T) ? ACE_SETACL : SETACL,
289 		    aclp->acl_cnt, aclp->acl_aclp);
290 	} else {
291 		error = facl(acl_inp->fd,
292 		    (aclp->acl_type == ACE_T) ? ACE_SETACL : SETACL,
293 		    aclp->acl_cnt, aclp->acl_aclp);
294 	}
295 
296 	return (error);
297 }
298 
299 int
300 acl_set(const char *path, acl_t *aclp)
301 {
302 	acl_inp acl_inp;
303 
304 	acl_inp.file = path;
305 
306 	return (cacl_set(&acl_inp, aclp, ACL_PATH));
307 }
308 
309 int
310 facl_set(int fd, acl_t *aclp)
311 {
312 	acl_inp acl_inp;
313 
314 	acl_inp.fd = fd;
315 
316 	return (cacl_set(&acl_inp, aclp, ACL_FD));
317 }
318 
319 int
320 acl_cnt(acl_t *aclp)
321 {
322 	return (aclp->acl_cnt);
323 }
324 
325 int
326 acl_type(acl_t *aclp)
327 {
328 	return (aclp->acl_type);
329 }
330 
331 acl_t *
332 acl_dup(acl_t *aclp)
333 {
334 	acl_t *newaclp;
335 
336 	newaclp = acl_alloc(aclp->acl_type);
337 	if (newaclp == NULL)
338 		return (NULL);
339 
340 	newaclp->acl_aclp = malloc(aclp->acl_entry_size * aclp->acl_cnt);
341 	if (newaclp->acl_aclp == NULL) {
342 		acl_free(newaclp);
343 		return (NULL);
344 	}
345 
346 	(void) memcpy(newaclp->acl_aclp,
347 	    aclp->acl_aclp, aclp->acl_entry_size * aclp->acl_cnt);
348 	newaclp->acl_cnt = aclp->acl_cnt;
349 
350 	return (newaclp);
351 }
352 
353 int
354 acl_flags(acl_t *aclp)
355 {
356 	return (aclp->acl_flags);
357 }
358 
359 void *
360 acl_data(acl_t *aclp)
361 {
362 	return (aclp->acl_aclp);
363 }
364 
365 /*
366  * Take an acl array and build an acl_t.
367  */
368 acl_t *
369 acl_to_aclp(enum acl_type type, void *acl, int count)
370 {
371 	acl_t *aclp;
372 
373 
374 	aclp = acl_alloc(type);
375 	if (aclp == NULL)
376 		return (aclp);
377 
378 	aclp->acl_aclp = acl;
379 	aclp->acl_cnt = count;
380 
381 	return (aclp);
382 }
383 
384 /*
385  * Remove an ACL from a file and create a trivial ACL based
386  * off of the mode argument.  After acl has been set owner/group
387  * are updated to match owner,group arguments
388  */
389 int
390 acl_strip(const char *file, uid_t owner, gid_t group, mode_t mode)
391 {
392 	int	error = 0;
393 	aclent_t min_acl[MIN_ACL_ENTRIES];
394 	ace_t	min_ace_acl[6];	/* owner, group, everyone + complement denies */
395 	int	acl_flavor;
396 	int	aclcnt;
397 
398 	acl_flavor = pathconf(file, _PC_ACL_ENABLED);
399 
400 	/*
401 	 * force it through aclent flavor when file system doesn't
402 	 * understand question
403 	 */
404 	if (acl_flavor == 0 || acl_flavor == -1)
405 		acl_flavor = _ACL_ACLENT_ENABLED;
406 
407 	if (acl_flavor & _ACL_ACLENT_ENABLED) {
408 		min_acl[0].a_type = USER_OBJ;
409 		min_acl[0].a_id   = owner;
410 		min_acl[0].a_perm = ((mode & 0700) >> 6);
411 		min_acl[1].a_type = GROUP_OBJ;
412 		min_acl[1].a_id   = group;
413 		min_acl[1].a_perm = ((mode & 0070) >> 3);
414 		min_acl[2].a_type = CLASS_OBJ;
415 		min_acl[2].a_id   = (uid_t)-1;
416 		min_acl[2].a_perm = ((mode & 0070) >> 3);
417 		min_acl[3].a_type = OTHER_OBJ;
418 		min_acl[3].a_id   = (uid_t)-1;
419 		min_acl[3].a_perm = (mode & 0007);
420 		aclcnt = 4;
421 		error = acl(file, SETACL, aclcnt, min_acl);
422 	} else if (acl_flavor & _ACL_ACE_ENABLED) {
423 		(void) memcpy(min_ace_acl, trivial_acl, sizeof (ace_t) * 6);
424 
425 		/*
426 		 * Make aces match request mode
427 		 */
428 		adjust_ace_pair(&min_ace_acl[0], (mode & 0700) >> 6);
429 		adjust_ace_pair(&min_ace_acl[2], (mode & 0070) >> 3);
430 		adjust_ace_pair(&min_ace_acl[4], mode & 0007);
431 
432 		error = acl(file, ACE_SETACL, 6, min_ace_acl);
433 	} else {
434 		errno = EINVAL;
435 		error = 1;
436 	}
437 
438 	if (error == 0)
439 		error = chown(file, owner, group);
440 	return (error);
441 }
442 
443 static int
444 ace_match(void *entry1, void *entry2)
445 {
446 	ace_t *p1 = (ace_t *)entry1;
447 	ace_t *p2 = (ace_t *)entry2;
448 	ace_t ace1, ace2;
449 
450 	ace1 = *p1;
451 	ace2 = *p2;
452 
453 	/*
454 	 * Need to fixup who field for abstrations for
455 	 * accurate comparison, since field is undefined.
456 	 */
457 	if (ace1.a_flags & (ACE_OWNER|ACE_GROUP|ACE_EVERYONE))
458 		ace1.a_who = (uid_t)-1;
459 	if (ace2.a_flags & (ACE_OWNER|ACE_GROUP|ACE_EVERYONE))
460 		ace2.a_who = (uid_t)-1;
461 	return (memcmp(&ace1, &ace2, sizeof (ace_t)));
462 }
463 
464 static int
465 aclent_match(void *entry1, void *entry2)
466 {
467 	aclent_t *aclent1 = (aclent_t *)entry1;
468 	aclent_t *aclent2 = (aclent_t *)entry2;
469 
470 	return (memcmp(aclent1, aclent2, sizeof (aclent_t)));
471 }
472 
473 /*
474  * Find acl entries in acl that correspond to removeacl.  Search
475  * is started from slot.  The flag argument indicates whether to
476  * remove all matches or just the first match.
477  */
478 int
479 acl_removeentries(acl_t *acl, acl_t *removeacl, int start_slot, int flag)
480 {
481 	int i, j;
482 	int match;
483 	int (*acl_match)(void *acl1, void *acl2);
484 	void *acl_entry, *remove_entry;
485 	void *start;
486 	int found = 0;
487 
488 	if (flag != ACL_REMOVE_ALL && flag != ACL_REMOVE_FIRST)
489 		flag = ACL_REMOVE_FIRST;
490 
491 	if (acl == NULL || removeacl == NULL)
492 		return (EACL_NO_ACL_ENTRY);
493 
494 	if (acl->acl_type != removeacl->acl_type)
495 		return (EACL_DIFF_TYPE);
496 
497 	if (acl->acl_type == ACLENT_T)
498 		acl_match = aclent_match;
499 	else
500 		acl_match = ace_match;
501 
502 	for (i = 0, remove_entry = removeacl->acl_aclp;
503 	    i != removeacl->acl_cnt; i++) {
504 
505 		j = 0;
506 		acl_entry = (char *)acl->acl_aclp +
507 		    (acl->acl_entry_size * start_slot);
508 		for (;;) {
509 			match = acl_match(acl_entry, remove_entry);
510 			if (match == 0)  {
511 				found++;
512 
513 				/* avoid memmove if last entry */
514 				if (acl->acl_cnt == (j + 1)) {
515 					acl->acl_cnt--;
516 					break;
517 				}
518 
519 				start = (char *)acl_entry +
520 				    acl->acl_entry_size;
521 				(void) memmove(acl_entry, start,
522 				    acl->acl_entry_size *
523 				    (acl->acl_cnt-- - (j + 1)));
524 
525 				if (flag == ACL_REMOVE_FIRST)
526 					break;
527 				/*
528 				 * List has changed, just continue so this
529 				 * slot gets checked with it's new contents.
530 				 */
531 				continue;
532 			}
533 			acl_entry = ((char *)acl_entry + acl->acl_entry_size);
534 			if (++j >= acl->acl_cnt) {
535 				break;
536 			}
537 		}
538 		remove_entry = (char *)remove_entry + removeacl->acl_entry_size;
539 	}
540 
541 	return ((found == 0) ? EACL_NO_ACL_ENTRY : 0);
542 }
543 
544 /*
545  * Replace entires entries in acl1 with the corresponding entries
546  * in newentries.  The where argument specifies where to begin
547  * the replacement.  If the where argument is 1 greater than the
548  * number of acl entries in acl1 then they are appended.  If the
549  * where argument is 2+ greater than the number of acl entries then
550  * EACL_INVALID_SLOT is returned.
551  */
552 int
553 acl_modifyentries(acl_t *acl1, acl_t *newentries, int where)
554 {
555 
556 	int slot;
557 	int slots_needed;
558 	int slots_left;
559 	int newsize;
560 
561 	if (acl1 == NULL || newentries == NULL)
562 		return (EACL_NO_ACL_ENTRY);
563 
564 	if (where < 0 || where >= acl1->acl_cnt)
565 		return (EACL_INVALID_SLOT);
566 
567 	if (acl1->acl_type != newentries->acl_type)
568 		return (EACL_DIFF_TYPE);
569 
570 	slot = where;
571 
572 	slots_left = acl1->acl_cnt - slot + 1;
573 	if (slots_left < newentries->acl_cnt) {
574 		slots_needed = newentries->acl_cnt - slots_left;
575 		newsize = (acl1->acl_entry_size * acl1->acl_cnt) +
576 		    (acl1->acl_entry_size * slots_needed);
577 		acl1->acl_aclp = realloc(acl1->acl_aclp, newsize);
578 		if (acl1->acl_aclp == NULL)
579 			return (-1);
580 	}
581 	(void) memcpy((char *)acl1->acl_aclp + (acl1->acl_entry_size * slot),
582 	    newentries->acl_aclp,
583 	    newentries->acl_entry_size * newentries->acl_cnt);
584 
585 	/*
586 	 * Did ACL grow?
587 	 */
588 
589 	if ((slot + newentries->acl_cnt) > acl1->acl_cnt) {
590 		acl1->acl_cnt = slot + newentries->acl_cnt;
591 	}
592 
593 	return (0);
594 }
595 
596 /*
597  * Add acl2 entries into acl1.  The where argument specifies where
598  * to add the entries.
599  */
600 int
601 acl_addentries(acl_t *acl1, acl_t *acl2, int where)
602 {
603 
604 	int newsize;
605 	int len;
606 	void *start;
607 	void *to;
608 
609 	if (acl1 == NULL || acl2 == NULL)
610 		return (EACL_NO_ACL_ENTRY);
611 
612 	if (acl1->acl_type != acl2->acl_type)
613 		return (EACL_DIFF_TYPE);
614 
615 	/*
616 	 * allow where to specify 1 past last slot for an append operation
617 	 * but anything greater is an error.
618 	 */
619 	if (where < 0 || where > acl1->acl_cnt)
620 		return (EACL_INVALID_SLOT);
621 
622 	newsize = (acl2->acl_entry_size * acl2->acl_cnt) +
623 	    (acl1->acl_entry_size * acl1->acl_cnt);
624 	acl1->acl_aclp = realloc(acl1->acl_aclp, newsize);
625 	if (acl1->acl_aclp == NULL)
626 		return (-1);
627 
628 	/*
629 	 * first push down entries where new ones will be inserted
630 	 */
631 
632 	to = (void *)((char *)acl1->acl_aclp +
633 	    ((where + acl2->acl_cnt) * acl1->acl_entry_size));
634 
635 	start = (void *)((char *)acl1->acl_aclp +
636 	    where * acl1->acl_entry_size);
637 
638 	if (where < acl1->acl_cnt) {
639 		len = (acl1->acl_cnt - where) * acl1->acl_entry_size;
640 		(void) memmove(to, start, len);
641 	}
642 
643 	/*
644 	 * now stick in new entries.
645 	 */
646 
647 	(void) memmove(start, acl2->acl_aclp,
648 	    acl2->acl_cnt * acl2->acl_entry_size);
649 
650 	acl1->acl_cnt += acl2->acl_cnt;
651 	return (0);
652 }
653 
654 /*
655  * return text for an ACL error.
656  */
657 char *
658 acl_strerror(int errnum)
659 {
660 	switch (errnum) {
661 	case EACL_GRP_ERROR:
662 		return (dgettext(TEXT_DOMAIN,
663 		    "There is more than one group or default group entry"));
664 	case EACL_USER_ERROR:
665 		return (dgettext(TEXT_DOMAIN,
666 		    "There is more than one user or default user entry"));
667 	case EACL_OTHER_ERROR:
668 		return (dgettext(TEXT_DOMAIN,
669 		    "There is more than one other entry"));
670 	case EACL_CLASS_ERROR:
671 		return (dgettext(TEXT_DOMAIN,
672 		    "There is more than one mask entry"));
673 	case EACL_DUPLICATE_ERROR:
674 		return (dgettext(TEXT_DOMAIN,
675 		    "Duplicate user or group entries"));
676 	case EACL_MISS_ERROR:
677 		return (dgettext(TEXT_DOMAIN,
678 		    "Missing user/group owner, other, mask entry"));
679 	case EACL_MEM_ERROR:
680 		return (dgettext(TEXT_DOMAIN,
681 		    "Memory error"));
682 	case EACL_ENTRY_ERROR:
683 		return (dgettext(TEXT_DOMAIN,
684 		    "Unrecognized entry type"));
685 	case EACL_INHERIT_ERROR:
686 		return (dgettext(TEXT_DOMAIN,
687 		    "Invalid inheritance flags"));
688 	case EACL_FLAGS_ERROR:
689 		return (dgettext(TEXT_DOMAIN,
690 		    "Unrecognized entry flags"));
691 	case EACL_PERM_MASK_ERROR:
692 		return (dgettext(TEXT_DOMAIN,
693 		    "Invalid ACL permissions"));
694 	case EACL_COUNT_ERROR:
695 		return (dgettext(TEXT_DOMAIN,
696 		    "Invalid ACL count"));
697 	case EACL_INVALID_SLOT:
698 		return (dgettext(TEXT_DOMAIN,
699 		    "Invalid ACL entry number specified"));
700 	case EACL_NO_ACL_ENTRY:
701 		return (dgettext(TEXT_DOMAIN,
702 		    "ACL entry doesn't exist"));
703 	case EACL_DIFF_TYPE:
704 		return (dgettext(TEXT_DOMAIN,
705 		    "Different file system ACL types cannot be merged"));
706 	case EACL_INVALID_USER_GROUP:
707 		return (dgettext(TEXT_DOMAIN, "Invalid user or group"));
708 	case EACL_INVALID_STR:
709 		return (dgettext(TEXT_DOMAIN, "ACL string is invalid"));
710 	case EACL_FIELD_NOT_BLANK:
711 		return (dgettext(TEXT_DOMAIN, "Field expected to be blank"));
712 	case EACL_INVALID_ACCESS_TYPE:
713 		return (dgettext(TEXT_DOMAIN, "Invalid access type"));
714 	case EACL_UNKNOWN_DATA:
715 		return (dgettext(TEXT_DOMAIN, "Unrecognized entry"));
716 	case EACL_MISSING_FIELDS:
717 		return (dgettext(TEXT_DOMAIN,
718 		    "ACL specification missing required fields"));
719 	case EACL_INHERIT_NOTDIR:
720 		return (dgettext(TEXT_DOMAIN,
721 		    "Inheritance flags are only allowed on directories"));
722 	case -1:
723 		return (strerror(errno));
724 	default:
725 		errno = EINVAL;
726 		return (dgettext(TEXT_DOMAIN, "Unknown error"));
727 	}
728 }
729 
730 extern int yyinteractive;
731 
732 /* PRINTFLIKE1 */
733 void
734 acl_error(const char *fmt, ...)
735 {
736 	va_list va;
737 
738 	if (yyinteractive == 0)
739 		return;
740 
741 	va_start(va, fmt);
742 	(void) vfprintf(stderr, fmt, va);
743 	va_end(va);
744 }
745 
746 int
747 sid_to_id(char *sid, boolean_t user, uid_t *id)
748 {
749 	idmap_handle_t *idmap_hdl = NULL;
750 	idmap_get_handle_t *get_hdl = NULL;
751 	char *rid_start = NULL;
752 	idmap_stat status;
753 	char *end;
754 	int error = 1;
755 	char *domain_start;
756 
757 	if ((domain_start = strchr(sid, '@')) == NULL) {
758 		idmap_rid_t rid;
759 
760 		if ((rid_start = strrchr(sid, '-')) == NULL)
761 			return (1);
762 		*rid_start++ = '\0';
763 		errno = 0;
764 		rid = strtoul(rid_start--, &end, 10);
765 		if (errno == 0 && *end == '\0') {
766 			if (idmap_init(&idmap_hdl) == IDMAP_SUCCESS &&
767 			    idmap_get_create(idmap_hdl, &get_hdl) ==
768 			    IDMAP_SUCCESS) {
769 				if (user)
770 					error = idmap_get_uidbysid(get_hdl,
771 					    sid, rid, IDMAP_REQ_FLG_USE_CACHE,
772 					    id, &status);
773 				else
774 					error = idmap_get_gidbysid(get_hdl,
775 					    sid, rid, IDMAP_REQ_FLG_USE_CACHE,
776 					    id, &status);
777 				if (error == IDMAP_SUCCESS) {
778 					error = idmap_get_mappings(get_hdl);
779 					if (error == IDMAP_SUCCESS &&
780 					    status != IDMAP_SUCCESS)
781 						error = 1;
782 					else
783 						error = 0;
784 				}
785 			} else {
786 				error = 1;
787 			}
788 			if (get_hdl)
789 				idmap_get_destroy(get_hdl);
790 			if (idmap_hdl)
791 				(void) idmap_fini(idmap_hdl);
792 		} else {
793 			error = 1;
794 		}
795 		*rid_start = '-'; /* putback character removed earlier */
796 	} else {
797 		char *name = sid;
798 		*domain_start++ = '\0';
799 
800 		if (user)
801 			error = idmap_getuidbywinname(name, domain_start,
802 			    IDMAP_REQ_FLG_USE_CACHE, id);
803 		else
804 			error = idmap_getgidbywinname(name, domain_start,
805 			    IDMAP_REQ_FLG_USE_CACHE, id);
806 		*--domain_start = '@';
807 		error = (error == IDMAP_SUCCESS) ? 0 : 1;
808 	}
809 
810 	return (error);
811 }
812