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