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