xref: /titanic_50/usr/src/lib/libsec/common/aclutils.c (revision 6be61d4ea129a94bdfe33533b2bc265d4447f05c)
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_get_handle_t *get_hdl = NULL;
744  	char *rid_start = NULL;
745  	idmap_stat status;
746  	char *end;
747  	int error = 1;
748  	char *domain_start;
749  
750  	if ((domain_start = strchr(sid, '@')) == NULL) {
751  		idmap_rid_t rid;
752  
753  		if ((rid_start = strrchr(sid, '-')) == NULL)
754  			return (1);
755  		*rid_start++ = '\0';
756  		errno = 0;
757  		rid = strtoul(rid_start--, &end, 10);
758  		if (errno == 0 && *end == '\0') {
759  			if (idmap_get_create(&get_hdl) ==
760  			    IDMAP_SUCCESS) {
761  				if (user)
762  					error = idmap_get_uidbysid(get_hdl,
763  					    sid, rid, IDMAP_REQ_FLG_USE_CACHE,
764  					    id, &status);
765  				else
766  					error = idmap_get_gidbysid(get_hdl,
767  					    sid, rid, IDMAP_REQ_FLG_USE_CACHE,
768  					    id, &status);
769  				if (error == IDMAP_SUCCESS) {
770  					error = idmap_get_mappings(get_hdl);
771  					if (error == IDMAP_SUCCESS &&
772  					    status != IDMAP_SUCCESS)
773  						error = 1;
774  					else
775  						error = 0;
776  				}
777  			} else {
778  				error = 1;
779  			}
780  			if (get_hdl)
781  				idmap_get_destroy(get_hdl);
782  		} else {
783  			error = 1;
784  		}
785  		*rid_start = '-'; /* putback character removed earlier */
786  	} else {
787  		char *name = sid;
788  		*domain_start++ = '\0';
789  
790  		if (user)
791  			error = idmap_getuidbywinname(name, domain_start,
792  			    IDMAP_REQ_FLG_USE_CACHE, id);
793  		else
794  			error = idmap_getgidbywinname(name, domain_start,
795  			    IDMAP_REQ_FLG_USE_CACHE, id);
796  		*--domain_start = '@';
797  		error = (error == IDMAP_SUCCESS) ? 0 : 1;
798  	}
799  
800  	return (error);
801  }
802