xref: /illumos-gate/usr/src/lib/libsec/common/acltext.c (revision 8119dad84d6416f13557b0ba8e2aaf9064cbcfd3)
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) 1993, 2010, Oracle and/or its affiliates. All rights reserved.
23  * Copyright 2016 Nexenta Systems, Inc.  All rights reserved.
24  * Copyright 2024 RackTop Systems, Inc.
25  */
26 
27 /*LINTLIBRARY*/
28 
29 #include <grp.h>
30 #include <pwd.h>
31 #include <string.h>
32 #include <limits.h>
33 #include <stdlib.h>
34 #include <errno.h>
35 #include <sys/param.h>
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include <sys/acl.h>
39 #include <aclutils.h>
40 #include <idmap.h>
41 #include <synch.h>
42 
43 /*
44  * Unix names returned for UID and GID values can actually be longer than
45  * the traditional 8 characters, or even 32 characters, etc.  When using
46  * "ad" in nsswitch.conf they can be quite long.  Let's use a reasonable
47  * compromise value here (80) which is large enough to hold SIDs in the
48  * numeric form (eg. "S-1-5-21-wwwwwwwww-xxxxxxxxx-yyyyyyyyy-zzzzzzzzz")
49  * but not necessarily every crazy long user name.  In addition, places
50  * below that might truncate a long name should detect truncation and
51  * fall-back to a more compact value (SID or UID/GID et).
52  */
53 #define	ID_STR_MAX	80
54 
55 #define	APPENDED_ID_MAX	ID_STR_MAX + 1		/* id + colon */
56 /*
57  * yyinteractive controls whether yyparse should print out
58  * error messages to stderr, and whether or not id's should be
59  * allowed from acl_fromtext().
60  */
61 int	yyinteractive;
62 acl_t	*yyacl;
63 char	*yybuf;
64 mutex_t	yymutex;
65 
66 extern acl_t *acl_alloc(enum acl_type);
67 
68 /*
69  * dynamic string that will increase in size on an
70  * as needed basis.
71  */
72 typedef struct dynaclstr {
73 	size_t d_bufsize;		/* current size of aclexport */
74 	char *d_aclexport;
75 	int d_pos;
76 } dynaclstr_t;
77 
78 static int str_append(dynaclstr_t *, char *);
79 static int aclent_perm_txt(dynaclstr_t *, o_mode_t);
80 
81 static void
82 aclent_perms(int perm, char *txt_perms)
83 {
84 	if (perm & S_IROTH)
85 		txt_perms[0] = 'r';
86 	else
87 		txt_perms[0] = '-';
88 	if (perm & S_IWOTH)
89 		txt_perms[1] = 'w';
90 	else
91 		txt_perms[1] = '-';
92 	if (perm & S_IXOTH)
93 		txt_perms[2] = 'x';
94 	else
95 		txt_perms[2] = '-';
96 	txt_perms[3] = '\0';
97 }
98 
99 /*
100  * Get the user name into the passed buffer.  If it can't be found,
101  * or if the noresolve flag is set, or if what we found will not fit
102  * in the buffer without truncation, just put a numeric ID.
103  */
104 static char *
105 pruname(uid_t uid, char *uidp, size_t buflen, int noresolve)
106 {
107 	struct passwd	*passwdp = NULL;
108 
109 	if (noresolve == 0)
110 		passwdp = getpwuid(uid);
111 	if (passwdp != NULL && strlen(passwdp->pw_name) < buflen) {
112 		(void) strlcpy(uidp, passwdp->pw_name, buflen);
113 	} else {
114 		/* display uid instead */
115 		(void) snprintf(uidp, buflen, "%u", uid);
116 	}
117 	return (uidp);
118 }
119 
120 /*
121  * Get the group name into the passed buffer.  If it can't be found,
122  * or if the noresolve flag is set, or if what we found will not fit
123  * in the buffer without truncation, just put a numeric ID.
124  */
125 static char *
126 prgname(gid_t gid, char *gidp, size_t buflen, int noresolve)
127 {
128 	struct group	*groupp = NULL;
129 
130 	if (noresolve == 0)
131 		groupp = getgrgid(gid);
132 	if (groupp != NULL && strlen(groupp->gr_name) < buflen) {
133 		(void) strlcpy(gidp, groupp->gr_name, buflen);
134 	} else {
135 		/* display gid instead */
136 		(void) snprintf(gidp, buflen, "%u", gid);
137 	}
138 	return (gidp);
139 }
140 
141 static int
142 getsidname(uid_t who, boolean_t user, char **sidp, boolean_t noresolve)
143 {
144 	idmap_get_handle_t *get_hdl = NULL;
145 	idmap_stat status;
146 	idmap_rid_t rid;
147 	int error = IDMAP_ERR_NORESULT;
148 	int len;
149 	char *domain = NULL;
150 
151 	*sidp = NULL;
152 
153 	/*
154 	 * First try and get windows name
155 	 */
156 
157 	if (!noresolve) {
158 		if (user)
159 			error = idmap_getwinnamebyuid(who,
160 			    IDMAP_REQ_FLG_USE_CACHE, sidp, NULL);
161 		else
162 			error = idmap_getwinnamebygid(who,
163 			    IDMAP_REQ_FLG_USE_CACHE, sidp, NULL);
164 	}
165 	if (error != IDMAP_SUCCESS) {
166 		if (idmap_get_create(&get_hdl) == IDMAP_SUCCESS) {
167 			if (user)
168 				error = idmap_get_sidbyuid(get_hdl, who,
169 				    IDMAP_REQ_FLG_USE_CACHE, &domain, &rid,
170 				    &status);
171 			else
172 				error = idmap_get_sidbygid(get_hdl, who,
173 				    IDMAP_REQ_FLG_USE_CACHE, &domain, &rid,
174 				    &status);
175 			if (error == IDMAP_SUCCESS &&
176 			    idmap_get_mappings(get_hdl) == 0) {
177 				if (status == IDMAP_SUCCESS) {
178 					len = snprintf(NULL, 0,
179 					    "%s-%d", domain, rid);
180 					if (*sidp = malloc(len + 1)) {
181 						(void) snprintf(*sidp, len + 1,
182 						    "%s-%d", domain, rid);
183 					}
184 				}
185 			}
186 		}
187 		if (get_hdl)
188 			idmap_get_destroy(get_hdl);
189 	}
190 
191 	free(domain);
192 
193 	return (*sidp ? 0 : 1);
194 }
195 
196 /*
197  * sid_string_by_id() is an exposed interface via -lsec
198  */
199 int
200 sid_string_by_id(uid_t who, boolean_t user, char **sidp, boolean_t noresolve)
201 {
202 	return (getsidname(who, user, sidp, noresolve));
203 }
204 
205 static void
206 aclent_printacl(acl_t *aclp)
207 {
208 	aclent_t *tp;
209 	int aclcnt;
210 	int mask;
211 	int slot = 0;
212 	char perm[4];
213 	char uidp[ID_STR_MAX];
214 	char gidp[ID_STR_MAX];
215 
216 	/* display ACL: assume it is sorted. */
217 	aclcnt = aclp->acl_cnt;
218 	for (tp = aclp->acl_aclp; tp && aclcnt--; tp++) {
219 		if (tp->a_type == CLASS_OBJ)
220 			mask = tp->a_perm;
221 	}
222 	aclcnt = aclp->acl_cnt;
223 	for (tp = aclp->acl_aclp; aclcnt--; tp++) {
224 		(void) printf("     %d:", slot++);
225 		switch (tp->a_type) {
226 		case USER:
227 			aclent_perms(tp->a_perm, perm);
228 			(void) printf("user:%s:%s\t\t",
229 			    pruname(tp->a_id, uidp, sizeof (uidp), 0), perm);
230 			aclent_perms((tp->a_perm & mask), perm);
231 			(void) printf("#effective:%s\n", perm);
232 			break;
233 		case USER_OBJ:
234 			/* no need to display uid */
235 			aclent_perms(tp->a_perm, perm);
236 			(void) printf("user::%s\n", perm);
237 			break;
238 		case GROUP:
239 			aclent_perms(tp->a_perm, perm);
240 			(void) printf("group:%s:%s\t\t",
241 			    prgname(tp->a_id, gidp, sizeof (gidp), 0), perm);
242 			aclent_perms(tp->a_perm & mask, perm);
243 			(void) printf("#effective:%s\n", perm);
244 			break;
245 		case GROUP_OBJ:
246 			aclent_perms(tp->a_perm, perm);
247 			(void) printf("group::%s\t\t", perm);
248 			aclent_perms(tp->a_perm & mask, perm);
249 			(void) printf("#effective:%s\n", perm);
250 			break;
251 		case CLASS_OBJ:
252 			aclent_perms(tp->a_perm, perm);
253 			(void) printf("mask:%s\n", perm);
254 			break;
255 		case OTHER_OBJ:
256 			aclent_perms(tp->a_perm, perm);
257 			(void) printf("other:%s\n", perm);
258 			break;
259 		case DEF_USER:
260 			aclent_perms(tp->a_perm, perm);
261 			(void) printf("default:user:%s:%s\n",
262 			    pruname(tp->a_id, uidp, sizeof (uidp), 0), perm);
263 			break;
264 		case DEF_USER_OBJ:
265 			aclent_perms(tp->a_perm, perm);
266 			(void) printf("default:user::%s\n", perm);
267 			break;
268 		case DEF_GROUP:
269 			aclent_perms(tp->a_perm, perm);
270 			(void) printf("default:group:%s:%s\n",
271 			    prgname(tp->a_id, gidp, sizeof (gidp), 0), perm);
272 			break;
273 		case DEF_GROUP_OBJ:
274 			aclent_perms(tp->a_perm, perm);
275 			(void) printf("default:group::%s\n", perm);
276 			break;
277 		case DEF_CLASS_OBJ:
278 			aclent_perms(tp->a_perm, perm);
279 			(void) printf("default:mask:%s\n", perm);
280 			break;
281 		case DEF_OTHER_OBJ:
282 			aclent_perms(tp->a_perm, perm);
283 			(void) printf("default:other:%s\n", perm);
284 			break;
285 		default:
286 			(void) fprintf(stderr,
287 			    dgettext(TEXT_DOMAIN, "unrecognized entry\n"));
288 			break;
289 		}
290 	}
291 }
292 
293 static void
294 split_line(char *str, int cols)
295 {
296 	char *ptr;
297 	int len;
298 	int i;
299 	int last_split;
300 	char *pad = "";
301 	int pad_len;
302 
303 	len = strlen(str);
304 	ptr = str;
305 	pad_len = 0;
306 
307 	ptr = str;
308 	last_split = 0;
309 	for (i = 0; i != len; i++) {
310 		if ((i + pad_len + 4) >= cols) {
311 			(void) printf("%s%.*s\n", pad, last_split, ptr);
312 			ptr = &ptr[last_split];
313 			len = strlen(ptr);
314 			i = 0;
315 			pad_len = 4;
316 			pad = "         ";
317 		} else {
318 			if (ptr[i] == '/' || ptr[i] == ':') {
319 				last_split = i;
320 			}
321 		}
322 	}
323 	if (i == len) {
324 		(void) printf("%s%s\n", pad, ptr);
325 	}
326 }
327 
328 /*
329  * compute entry type string, such as user:joe, group:staff,...
330  */
331 static int
332 aclent_type_txt(dynaclstr_t *dstr, aclent_t *aclp, int flags)
333 {
334 	char idp[ID_STR_MAX];
335 	int error;
336 
337 	switch (aclp->a_type) {
338 	case DEF_USER_OBJ:
339 	case USER_OBJ:
340 		if (aclp->a_type == USER_OBJ)
341 			error = str_append(dstr, "user::");
342 		else
343 			error = str_append(dstr, "defaultuser::");
344 		break;
345 
346 	case DEF_USER:
347 	case USER:
348 		if (aclp->a_type == USER)
349 			error = str_append(dstr, "user:");
350 		else
351 			error = str_append(dstr, "defaultuser:");
352 		if (error)
353 			break;
354 		error = str_append(dstr, pruname(aclp->a_id, idp,
355 		    sizeof (idp), flags & ACL_NORESOLVE));
356 		if (error == 0)
357 			error = str_append(dstr, ":");
358 		break;
359 
360 	case DEF_GROUP_OBJ:
361 	case GROUP_OBJ:
362 		if (aclp->a_type == GROUP_OBJ)
363 			error = str_append(dstr, "group::");
364 		else
365 			error = str_append(dstr, "defaultgroup::");
366 		break;
367 
368 	case DEF_GROUP:
369 	case GROUP:
370 		if (aclp->a_type == GROUP)
371 			error = str_append(dstr, "group:");
372 		else
373 			error = str_append(dstr, "defaultgroup:");
374 		if (error)
375 			break;
376 		error = str_append(dstr, prgname(aclp->a_id, idp,
377 		    sizeof (idp), flags & ACL_NORESOLVE));
378 		if (error == 0)
379 			error = str_append(dstr, ":");
380 		break;
381 
382 	case DEF_CLASS_OBJ:
383 	case CLASS_OBJ:
384 		if (aclp->a_type == CLASS_OBJ)
385 			error = str_append(dstr, "mask:");
386 		else
387 			error = str_append(dstr, "defaultmask:");
388 		break;
389 
390 	case DEF_OTHER_OBJ:
391 	case OTHER_OBJ:
392 		if (aclp->a_type == OTHER_OBJ)
393 			error = str_append(dstr, "other:");
394 		else
395 			error = str_append(dstr, "defaultother:");
396 		break;
397 
398 	default:
399 		error = 1;
400 		break;
401 	}
402 
403 	return (error);
404 }
405 
406 /*
407  * compute entry type string such as, owner@:, user:joe, group:staff,...
408  */
409 static int
410 ace_type_txt(dynaclstr_t *dynstr, ace_t *acep, int flags)
411 {
412 	char idp[ID_STR_MAX];
413 	int error;
414 	char *sidp = NULL;
415 
416 	switch (acep->a_flags & ACE_TYPE_FLAGS) {
417 	case ACE_OWNER:
418 		error = str_append(dynstr, OWNERAT_TXT);
419 		break;
420 
421 	case ACE_GROUP|ACE_IDENTIFIER_GROUP:
422 		error = str_append(dynstr, GROUPAT_TXT);
423 		break;
424 
425 	case ACE_IDENTIFIER_GROUP:
426 		if ((flags & ACL_SID_FMT) && acep->a_who > MAXUID) {
427 			if (error = str_append(dynstr,
428 			    GROUPSID_TXT))
429 				break;
430 			if (error = getsidname(acep->a_who, B_FALSE,
431 			    &sidp, flags & ACL_NORESOLVE))
432 				break;
433 			error = str_append(dynstr, sidp);
434 		} else {
435 			if (error = str_append(dynstr, GROUP_TXT))
436 				break;
437 			error = str_append(dynstr, prgname(acep->a_who, idp,
438 			    sizeof (idp), flags & ACL_NORESOLVE));
439 		}
440 		if (error == 0)
441 			error = str_append(dynstr, ":");
442 		break;
443 
444 	case ACE_EVERYONE:
445 		error = str_append(dynstr, EVERYONEAT_TXT);
446 		break;
447 
448 	case 0:
449 		if ((flags & ACL_SID_FMT) && acep->a_who > MAXUID) {
450 			if (error = str_append(dynstr, USERSID_TXT))
451 				break;
452 			if (error = getsidname(acep->a_who, B_TRUE,
453 			    &sidp, flags & ACL_NORESOLVE))
454 				break;
455 			error = str_append(dynstr, sidp);
456 		} else {
457 			if (error = str_append(dynstr, USER_TXT))
458 				break;
459 			error = str_append(dynstr, pruname(acep->a_who, idp,
460 			    sizeof (idp), flags & ACL_NORESOLVE));
461 		}
462 		if (error == 0)
463 			error = str_append(dynstr, ":");
464 		break;
465 	default:
466 		error = 0;
467 		break;
468 	}
469 
470 	if (sidp)
471 		free(sidp);
472 	return (error);
473 }
474 
475 /*
476  * compute string of permissions, such as read_data/write_data or
477  * rwxp,...
478  * The format depends on the flags field which indicates whether the compact
479  * or verbose format should be used.
480  */
481 static int
482 ace_perm_txt(dynaclstr_t *dstr, uint32_t mask,
483     uint32_t iflags, int isdir, int flags)
484 {
485 	int error = 0;
486 
487 	if (flags & ACL_COMPACT_FMT) {
488 		char buf[16];
489 
490 		if (mask & ACE_READ_DATA)
491 			buf[0] = 'r';
492 		else
493 			buf[0] = '-';
494 		if (mask & ACE_WRITE_DATA)
495 			buf[1] = 'w';
496 		else
497 			buf[1] = '-';
498 		if (mask & ACE_EXECUTE)
499 			buf[2] = 'x';
500 		else
501 			buf[2] = '-';
502 		if (mask & ACE_APPEND_DATA)
503 			buf[3] = 'p';
504 		else
505 			buf[3] = '-';
506 		if (mask & ACE_DELETE)
507 			buf[4] = 'd';
508 		else
509 			buf[4] = '-';
510 		if (mask & ACE_DELETE_CHILD)
511 			buf[5] = 'D';
512 		else
513 			buf[5] = '-';
514 		if (mask & ACE_READ_ATTRIBUTES)
515 			buf[6] = 'a';
516 		else
517 			buf[6] = '-';
518 		if (mask & ACE_WRITE_ATTRIBUTES)
519 			buf[7] = 'A';
520 		else
521 			buf[7] = '-';
522 		if (mask & ACE_READ_NAMED_ATTRS)
523 			buf[8] = 'R';
524 		else
525 			buf[8] = '-';
526 		if (mask & ACE_WRITE_NAMED_ATTRS)
527 			buf[9] = 'W';
528 		else
529 			buf[9] = '-';
530 		if (mask & ACE_READ_ACL)
531 			buf[10] = 'c';
532 		else
533 			buf[10] = '-';
534 		if (mask & ACE_WRITE_ACL)
535 			buf[11] = 'C';
536 		else
537 			buf[11] = '-';
538 		if (mask & ACE_WRITE_OWNER)
539 			buf[12] = 'o';
540 		else
541 			buf[12] = '-';
542 		if (mask & ACE_SYNCHRONIZE)
543 			buf[13] = 's';
544 		else
545 			buf[13] = '-';
546 		buf[14] = ':';
547 		buf[15] = '\0';
548 		error = str_append(dstr, buf);
549 	} else {
550 		/*
551 		 * If ACE is a directory, but inheritance indicates its
552 		 * for a file then print permissions for file rather than
553 		 * dir.
554 		 */
555 		if (isdir) {
556 			if (mask & ACE_LIST_DIRECTORY) {
557 				if (iflags == ACE_FILE_INHERIT_ACE) {
558 					error = str_append(dstr,
559 					    READ_DATA_TXT);
560 				} else {
561 					error =
562 					    str_append(dstr, READ_DIR_TXT);
563 				}
564 			}
565 			if (error == 0 && (mask & ACE_ADD_FILE)) {
566 				if (iflags == ACE_FILE_INHERIT_ACE) {
567 					error =
568 					    str_append(dstr, WRITE_DATA_TXT);
569 				} else {
570 					error =
571 					    str_append(dstr, ADD_FILE_TXT);
572 				}
573 			}
574 			if (error == 0 && (mask & ACE_ADD_SUBDIRECTORY)) {
575 				if (iflags == ACE_FILE_INHERIT_ACE) {
576 					error = str_append(dstr,
577 					    APPEND_DATA_TXT);
578 				} else {
579 					error = str_append(dstr,
580 					    ADD_DIR_TXT);
581 				}
582 			}
583 		} else {
584 			if (mask & ACE_READ_DATA) {
585 				error = str_append(dstr, READ_DATA_TXT);
586 			}
587 			if (error == 0 && (mask & ACE_WRITE_DATA)) {
588 				error = str_append(dstr, WRITE_DATA_TXT);
589 			}
590 			if (error == 0 && (mask & ACE_APPEND_DATA)) {
591 				error = str_append(dstr, APPEND_DATA_TXT);
592 			}
593 		}
594 		if (error == 0 && (mask & ACE_READ_NAMED_ATTRS)) {
595 			error = str_append(dstr, READ_XATTR_TXT);
596 		}
597 		if (error == 0 && (mask & ACE_WRITE_NAMED_ATTRS)) {
598 			error = str_append(dstr, WRITE_XATTR_TXT);
599 		}
600 		if (error == 0 && (mask & ACE_EXECUTE)) {
601 			error = str_append(dstr, EXECUTE_TXT);
602 		}
603 		if (error == 0 && (mask & ACE_DELETE_CHILD)) {
604 			error = str_append(dstr, DELETE_CHILD_TXT);
605 		}
606 		if (error == 0 && (mask & ACE_READ_ATTRIBUTES)) {
607 			error = str_append(dstr, READ_ATTRIBUTES_TXT);
608 		}
609 		if (error == 0 && (mask & ACE_WRITE_ATTRIBUTES)) {
610 			error = str_append(dstr, WRITE_ATTRIBUTES_TXT);
611 		}
612 		if (error == 0 && (mask & ACE_DELETE)) {
613 			error = str_append(dstr, DELETE_TXT);
614 		}
615 		if (error == 0 && (mask & ACE_READ_ACL)) {
616 			error = str_append(dstr, READ_ACL_TXT);
617 		}
618 		if (error == 0 && (mask & ACE_WRITE_ACL)) {
619 			error = str_append(dstr, WRITE_ACL_TXT);
620 		}
621 		if (error == 0 && (mask & ACE_WRITE_OWNER)) {
622 			error = str_append(dstr, WRITE_OWNER_TXT);
623 		}
624 		if (error == 0 && (mask & ACE_SYNCHRONIZE)) {
625 			error = str_append(dstr, SYNCHRONIZE_TXT);
626 		}
627 		if (error == 0 && dstr->d_aclexport[dstr->d_pos-1] == '/') {
628 			dstr->d_aclexport[--dstr->d_pos] = '\0';
629 		}
630 		if (error == 0)
631 			error = str_append(dstr, ":");
632 	}
633 	return (error);
634 }
635 
636 /*
637  * compute string of access type, such as allow, deny, ...
638  */
639 static int
640 ace_access_txt(dynaclstr_t *dstr, int type)
641 {
642 	int error;
643 
644 	if (type == ACE_ACCESS_ALLOWED_ACE_TYPE)
645 		error = str_append(dstr, ALLOW_TXT);
646 	else if (type == ACE_ACCESS_DENIED_ACE_TYPE)
647 		error = str_append(dstr, DENY_TXT);
648 	else if (type == ACE_SYSTEM_AUDIT_ACE_TYPE)
649 		error = str_append(dstr, AUDIT_TXT);
650 	else if (type == ACE_SYSTEM_ALARM_ACE_TYPE)
651 		error = str_append(dstr, ALARM_TXT);
652 	else
653 		error = str_append(dstr, UNKNOWN_TXT);
654 
655 	return (error);
656 }
657 
658 static int
659 ace_inherit_txt(dynaclstr_t *dstr, uint32_t iflags, int flags)
660 {
661 	int error = 0;
662 
663 	if (flags & ACL_COMPACT_FMT) {
664 		char buf[9];
665 
666 		if (iflags & ACE_FILE_INHERIT_ACE)
667 			buf[0] = 'f';
668 		else
669 			buf[0] = '-';
670 		if (iflags & ACE_DIRECTORY_INHERIT_ACE)
671 			buf[1] = 'd';
672 		else
673 			buf[1] = '-';
674 		if (iflags & ACE_INHERIT_ONLY_ACE)
675 			buf[2] = 'i';
676 		else
677 			buf[2] = '-';
678 		if (iflags & ACE_NO_PROPAGATE_INHERIT_ACE)
679 			buf[3] = 'n';
680 		else
681 			buf[3] = '-';
682 		if (iflags & ACE_SUCCESSFUL_ACCESS_ACE_FLAG)
683 			buf[4] = 'S';
684 		else
685 			buf[4] = '-';
686 		if (iflags & ACE_FAILED_ACCESS_ACE_FLAG)
687 			buf[5] = 'F';
688 		else
689 			buf[5] = '-';
690 		if (iflags & ACE_INHERITED_ACE)
691 			buf[6] = 'I';
692 		else
693 			buf[6] = '-';
694 		buf[7] = ':';
695 		buf[8] = '\0';
696 		error = str_append(dstr, buf);
697 	} else {
698 		if (iflags & ACE_FILE_INHERIT_ACE) {
699 			error = str_append(dstr, FILE_INHERIT_TXT);
700 		}
701 		if (error == 0 && (iflags & ACE_DIRECTORY_INHERIT_ACE)) {
702 			error = str_append(dstr, DIR_INHERIT_TXT);
703 		}
704 		if (error == 0 && (iflags & ACE_NO_PROPAGATE_INHERIT_ACE)) {
705 			error = str_append(dstr, NO_PROPAGATE_TXT);
706 		}
707 		if (error == 0 && (iflags & ACE_INHERIT_ONLY_ACE)) {
708 			error = str_append(dstr, INHERIT_ONLY_TXT);
709 		}
710 		if (error == 0 && (iflags & ACE_SUCCESSFUL_ACCESS_ACE_FLAG)) {
711 			error = str_append(dstr, SUCCESSFUL_ACCESS_TXT);
712 		}
713 		if (error == 0 && (iflags & ACE_FAILED_ACCESS_ACE_FLAG)) {
714 			error = str_append(dstr, FAILED_ACCESS_TXT);
715 		}
716 		if (error == 0 && (iflags & ACE_INHERITED_ACE)) {
717 			error = str_append(dstr, INHERITED_ACE_TXT);
718 		}
719 		if (error == 0 && dstr->d_aclexport[dstr->d_pos-1] == '/') {
720 			dstr->d_aclexport[--dstr->d_pos] = '\0';
721 			error = str_append(dstr, ":");
722 		}
723 	}
724 
725 	return (error);
726 }
727 
728 /*
729  * Convert internal acl representation to external representation.
730  *
731  * The length of a non-owning user name or non-owning group name ie entries
732  * of type DEF_USER, USER, DEF_GROUP or GROUP, can exceed LOGNAME_MAX.  We
733  * thus check the length of these entries, and if greater than LOGNAME_MAX,
734  * we realloc() via increase_length().
735  *
736  * The LOGNAME_MAX, ENTRYTYPELEN and PERMS limits are otherwise always
737  * adhered to.
738  */
739 
740 /*
741  * acltotext() converts each ACL entry to look like this:
742  *
743  *    entry_type:uid^gid^name:perms[:id]
744  *
745  * The maximum length of entry_type is 14 ("defaultgroup::" and
746  * "defaultother::") hence ENTRYTYPELEN is set to 14.
747  *
748  * The max length of a uid^gid^name entry (in theory) is 8, hence we use,
749  * however the ID could be a number so we therefore use ID_STR_MAX
750  *
751  * The length of a perms entry is 4 to allow for the comma appended to each
752  * to each acl entry.  Hence PERMS is set to 4.
753  */
754 
755 #define	ENTRYTYPELEN	14
756 #define	PERMS		4
757 #define	ACL_ENTRY_SIZE	(ENTRYTYPELEN + ID_STR_MAX + PERMS + APPENDED_ID_MAX)
758 
759 char *
760 aclent_acltotext(aclent_t  *aclp, int aclcnt, int flags)
761 {
762 	dynaclstr_t	*dstr;
763 	char		*aclexport = NULL;
764 	int		i;
765 	int		error = 0;
766 
767 	if (aclp == NULL)
768 		return (NULL);
769 	if ((dstr = malloc(sizeof (dynaclstr_t))) == NULL)
770 		return (NULL);
771 	dstr->d_bufsize = aclcnt * ACL_ENTRY_SIZE;
772 	if ((dstr->d_aclexport = malloc(dstr->d_bufsize)) == NULL) {
773 		free(dstr);
774 		return (NULL);
775 	}
776 	*dstr->d_aclexport = '\0';
777 	dstr->d_pos = 0;
778 
779 	for (i = 0; i < aclcnt; i++, aclp++) {
780 		if (error = aclent_type_txt(dstr, aclp, flags))
781 			break;
782 		if (error = aclent_perm_txt(dstr, aclp->a_perm))
783 			break;
784 
785 		if ((flags & ACL_APPEND_ID) && ((aclp->a_type == USER) ||
786 		    (aclp->a_type == DEF_USER) || (aclp->a_type == GROUP) ||
787 		    (aclp->a_type == DEF_GROUP))) {
788 			char id[ID_STR_MAX], *idstr;
789 
790 			if (error = str_append(dstr, ":"))
791 				break;
792 			id[ID_STR_MAX - 1] = '\0'; /* null terminate buffer */
793 			idstr = lltostr(aclp->a_id, &id[ID_STR_MAX - 1]);
794 			if (error = str_append(dstr, idstr))
795 				break;
796 		}
797 		if (i < aclcnt - 1)
798 			if (error = str_append(dstr, ","))
799 				break;
800 	}
801 	if (error) {
802 		if (dstr->d_aclexport)
803 			free(dstr->d_aclexport);
804 	} else {
805 		aclexport = dstr->d_aclexport;
806 	}
807 	free(dstr);
808 	return (aclexport);
809 }
810 
811 char *
812 acltotext(aclent_t *aclp, int aclcnt)
813 {
814 	return (aclent_acltotext(aclp, aclcnt, 0));
815 }
816 
817 
818 aclent_t *
819 aclfromtext(char *aclstr, int *aclcnt)
820 {
821 	acl_t *aclp;
822 	aclent_t *aclentp;
823 	int error;
824 
825 	error = acl_fromtext(aclstr, &aclp);
826 	if (error)
827 		return (NULL);
828 
829 	aclentp = aclp->acl_aclp;
830 	aclp->acl_aclp = NULL;
831 	*aclcnt = aclp->acl_cnt;
832 
833 	acl_free(aclp);
834 	return (aclentp);
835 }
836 
837 
838 /*
839  * Append string onto dynaclstr_t.
840  *
841  * Return 0 on success, 1 for failure.
842  */
843 static int
844 str_append(dynaclstr_t *dstr, char *newstr)
845 {
846 	size_t len = strlen(newstr);
847 
848 	if ((len + dstr->d_pos) >= dstr->d_bufsize) {
849 		dstr->d_aclexport = realloc(dstr->d_aclexport,
850 		    dstr->d_bufsize + len + 1);
851 		if (dstr->d_aclexport == NULL)
852 			return (1);
853 		dstr->d_bufsize += len;
854 	}
855 	(void) strcat(&dstr->d_aclexport[dstr->d_pos], newstr);
856 	dstr->d_pos += len;
857 	return (0);
858 }
859 
860 static int
861 aclent_perm_txt(dynaclstr_t *dstr, o_mode_t perm)
862 {
863 	char buf[4];
864 
865 	if (perm & S_IROTH)
866 		buf[0] = 'r';
867 	else
868 		buf[0] = '-';
869 	if (perm & S_IWOTH)
870 		buf[1] = 'w';
871 	else
872 		buf[1] = '-';
873 	if (perm & S_IXOTH)
874 		buf[2] = 'x';
875 	else
876 		buf[2] = '-';
877 	buf[3] = '\0';
878 	return (str_append(dstr, buf));
879 }
880 
881 /*
882  * ace_acltotext() convert each ace formatted acl to look like this:
883  *
884  * entry_type:uid^gid^name:perms[:flags]:<allow|deny>[:id][,]
885  *
886  * The maximum length of entry_type is 5 ("group")
887  *
888  * The max length of a uid^gid^name entry (in theory) is 8,
889  * however id could be a number so we therefore use ID_STR_MAX
890  *
891  * The length of a perms entry is 144 i.e read_data/write_data...
892  * to each acl entry.
893  *
894  * iflags: file_inherit/dir_inherit/inherit_only/no_propagate/successful_access
895  *         /failed_access
896  *
897  */
898 
899 #define	ACE_ENTRYTYPLEN		6
900 #define	IFLAGS_STR "file_inherit/dir_inherit/inherit_only/no_propagate/" \
901 	"successful_access/failed_access/inherited"
902 #define	IFLAGS_SIZE		(sizeof (IFLAGS_STR) - 1)
903 #define	ACCESS_TYPE_SIZE	7	/* if unknown */
904 #define	COLON_CNT		3
905 #define	PERMS_LEN		216
906 #define	ACE_ENTRY_SIZE	(ACE_ENTRYTYPLEN + ID_STR_MAX + PERMS_LEN + \
907     ACCESS_TYPE_SIZE + IFLAGS_SIZE + COLON_CNT + APPENDED_ID_MAX)
908 
909 static char *
910 ace_acltotext(acl_t *aceaclp, int flags)
911 {
912 	ace_t		*aclp = aceaclp->acl_aclp;
913 	int		aclcnt = aceaclp->acl_cnt;
914 	int		i;
915 	int		error = 0;
916 	int		isdir = (aceaclp->acl_flags & ACL_IS_DIR);
917 	dynaclstr_t	*dstr;
918 	char		*aclexport = NULL;
919 	char		*rawsidp = NULL;
920 
921 	if (aclp == NULL)
922 		return (NULL);
923 
924 	if ((dstr = malloc(sizeof (dynaclstr_t))) == NULL)
925 		return (NULL);
926 	dstr->d_bufsize = aclcnt * ACL_ENTRY_SIZE;
927 	if ((dstr->d_aclexport = malloc(dstr->d_bufsize)) == NULL) {
928 		free(dstr);
929 		return (NULL);
930 	}
931 	*dstr->d_aclexport = '\0';
932 	dstr->d_pos = 0;
933 
934 	for (i = 0; i < aclcnt; i++, aclp++) {
935 
936 		if (error = ace_type_txt(dstr, aclp, flags))
937 			break;
938 		if (error = ace_perm_txt(dstr, aclp->a_access_mask,
939 		    aclp->a_flags, isdir, flags))
940 			break;
941 		if (error = ace_inherit_txt(dstr, aclp->a_flags, flags))
942 			break;
943 		if (error = ace_access_txt(dstr, aclp->a_type))
944 			break;
945 
946 		if ((flags & ACL_APPEND_ID) &&
947 		    (((aclp->a_flags & ACE_TYPE_FLAGS) == 0) ||
948 		    ((aclp->a_flags & ACE_TYPE_FLAGS) ==
949 		    ACE_IDENTIFIER_GROUP))) {
950 			char id[ID_STR_MAX], *idstr;
951 
952 			if (error = str_append(dstr, ":"))
953 				break;
954 
955 			rawsidp = NULL;
956 			id[ID_STR_MAX -1] = '\0'; /* null terminate */
957 			if (aclp->a_who > MAXUID && (flags & ACL_SID_FMT)) {
958 
959 				error = getsidname(aclp->a_who,
960 				    ((aclp->a_flags & ACE_TYPE_FLAGS) == 0) ?
961 				    B_TRUE : B_FALSE, &idstr, 1);
962 				rawsidp = idstr;
963 				if (error)
964 					break;
965 			} else if (aclp->a_who > MAXUID &&
966 			    !(flags & ACL_NORESOLVE)) {
967 				idstr = lltostr(UID_NOBODY,
968 				    &id[ID_STR_MAX - 1]);
969 			} else {
970 				idstr = lltostr(aclp->a_who,
971 				    &id[ID_STR_MAX - 1]);
972 			}
973 			if (error = str_append(dstr, idstr))
974 				break;
975 			if (rawsidp) {
976 				free(rawsidp);
977 				rawsidp = NULL;
978 			}
979 		}
980 		if (i < aclcnt - 1) {
981 			if (error = str_append(dstr, ","))
982 				break;
983 		}
984 	}
985 
986 	if (rawsidp)
987 		free(rawsidp);
988 	if (error) {
989 		if (dstr->d_aclexport)
990 			free(dstr->d_aclexport);
991 	} else {
992 		aclexport = dstr->d_aclexport;
993 	}
994 	free(dstr);
995 	return (aclexport);
996 }
997 
998 char *
999 acl_totext(acl_t *aclp, int flags)
1000 {
1001 	char *txtp;
1002 
1003 	if (aclp == NULL)
1004 		return (NULL);
1005 
1006 	switch (aclp->acl_type) {
1007 	case ACE_T:
1008 		txtp = ace_acltotext(aclp, flags);
1009 		break;
1010 	case ACLENT_T:
1011 		txtp = aclent_acltotext(aclp->acl_aclp, aclp->acl_cnt, flags);
1012 		break;
1013 	}
1014 
1015 	return (txtp);
1016 }
1017 
1018 int
1019 acl_fromtext(const char *acltextp, acl_t **ret_aclp)
1020 {
1021 	int error;
1022 	char *buf;
1023 
1024 	buf = malloc(strlen(acltextp) + 2);
1025 	if (buf == NULL)
1026 		return (EACL_MEM_ERROR);
1027 	strcpy(buf, acltextp);
1028 	strcat(buf, "\n");
1029 
1030 	(void) mutex_lock(&yymutex);
1031 	yybuf = buf;
1032 	yyreset();
1033 	error = yyparse();
1034 	free(buf);
1035 
1036 	if (yyacl) {
1037 		if (error == 0)
1038 			*ret_aclp = yyacl;
1039 		else {
1040 			acl_free(yyacl);
1041 		}
1042 		yyacl = NULL;
1043 	}
1044 	(void) mutex_unlock(&yymutex);
1045 
1046 	return (error);
1047 }
1048 
1049 int
1050 acl_parse(const char *acltextp, acl_t **aclp)
1051 {
1052 	int error;
1053 
1054 	yyinteractive = 1;
1055 	error = acl_fromtext(acltextp, aclp);
1056 	yyinteractive = 0;
1057 	return (error);
1058 }
1059 
1060 static void
1061 ace_compact_printacl(acl_t *aclp, int flgs)
1062 {
1063 	int cnt;
1064 	ace_t *acep;
1065 	dynaclstr_t *dstr;
1066 	int len;
1067 
1068 	if ((dstr = malloc(sizeof (dynaclstr_t))) == NULL)
1069 		return;
1070 	dstr->d_bufsize = ACE_ENTRY_SIZE;
1071 	if ((dstr->d_aclexport = malloc(dstr->d_bufsize)) == NULL) {
1072 		free(dstr);
1073 		return;
1074 	}
1075 	*dstr->d_aclexport = '\0';
1076 
1077 	dstr->d_pos = 0;
1078 	for (cnt = 0, acep = aclp->acl_aclp;
1079 	    cnt != aclp->acl_cnt; cnt++, acep++) {
1080 		dstr->d_aclexport[0] = '\0';
1081 		dstr->d_pos = 0;
1082 
1083 		if (ace_type_txt(dstr, acep, flgs))
1084 			break;
1085 		len = strlen(&dstr->d_aclexport[0]);
1086 		if (ace_perm_txt(dstr, acep->a_access_mask, acep->a_flags,
1087 		    aclp->acl_flags & ACL_IS_DIR, ACL_COMPACT_FMT))
1088 			break;
1089 		if (ace_inherit_txt(dstr, acep->a_flags, ACL_COMPACT_FMT))
1090 			break;
1091 		if (ace_access_txt(dstr, acep->a_type) == -1)
1092 			break;
1093 		(void) printf("    %20.*s%s\n", len, dstr->d_aclexport,
1094 		    &dstr->d_aclexport[len]);
1095 	}
1096 
1097 	if (dstr->d_aclexport)
1098 		free(dstr->d_aclexport);
1099 	free(dstr);
1100 }
1101 
1102 static void
1103 ace_printacl(acl_t *aclp, int cols, int flgs)
1104 {
1105 	int  slot = 0;
1106 	char *token;
1107 	char *acltext;
1108 
1109 	if (flgs & ACL_COMPACT_FMT) {
1110 		ace_compact_printacl(aclp, flgs);
1111 		return;
1112 	}
1113 
1114 	acltext = acl_totext(aclp, flgs);
1115 
1116 	if (acltext == NULL)
1117 		return;
1118 
1119 	token = strtok(acltext, ",");
1120 	if (token == NULL) {
1121 		free(acltext);
1122 		return;
1123 	}
1124 
1125 	do {
1126 		(void) printf("     %d:", slot++);
1127 		split_line(token, cols - 5);
1128 	} while (token = strtok(NULL, ","));
1129 	free(acltext);
1130 }
1131 
1132 /*
1133  * pretty print an ACL.
1134  * For aclent_t ACL's the format is
1135  * similar to the old format used by getfacl,
1136  * with the addition of adding a "slot" number
1137  * before each entry.
1138  *
1139  * for ace_t ACL's the cols variable will break up
1140  * the long lines into multiple lines and will also
1141  * print a "slot" number.
1142  */
1143 void
1144 acl_printacl2(acl_t *aclp, int cols, int flgs)
1145 {
1146 
1147 	switch (aclp->acl_type) {
1148 	case ACLENT_T:
1149 		aclent_printacl(aclp);
1150 		break;
1151 	case ACE_T:
1152 		ace_printacl(aclp, cols, flgs);
1153 		break;
1154 	}
1155 }
1156 
1157 /*
1158  * Historical, compatibility version of the above.
1159  */
1160 void
1161 acl_printacl(acl_t *aclp, int cols, int compact)
1162 {
1163 	int flgs = compact ? ACL_COMPACT_FMT : 0;
1164 
1165 	switch (aclp->acl_type) {
1166 	case ACLENT_T:
1167 		aclent_printacl(aclp);
1168 		break;
1169 	case ACE_T:
1170 		ace_printacl(aclp, cols, flgs);
1171 		break;
1172 	}
1173 }
1174 
1175 typedef struct value_table {
1176 	char		p_letter; /* perm letter such as 'r' */
1177 	uint32_t	p_value; /* value for perm when pletter found */
1178 } value_table_t;
1179 
1180 /*
1181  * The permission tables are laid out in positional order
1182  * a '-' character will indicate a permission at a given
1183  * position is not specified.  The '-' is not part of the
1184  * table, but will be checked for in the permission computation
1185  * routine.
1186  */
1187 value_table_t ace_perm_table[] = {
1188 	{ 'r', ACE_READ_DATA},
1189 	{ 'w', ACE_WRITE_DATA},
1190 	{ 'x', ACE_EXECUTE},
1191 	{ 'p', ACE_APPEND_DATA},
1192 	{ 'd', ACE_DELETE},
1193 	{ 'D', ACE_DELETE_CHILD},
1194 	{ 'a', ACE_READ_ATTRIBUTES},
1195 	{ 'A', ACE_WRITE_ATTRIBUTES},
1196 	{ 'R', ACE_READ_NAMED_ATTRS},
1197 	{ 'W', ACE_WRITE_NAMED_ATTRS},
1198 	{ 'c', ACE_READ_ACL},
1199 	{ 'C', ACE_WRITE_ACL},
1200 	{ 'o', ACE_WRITE_OWNER},
1201 	{ 's', ACE_SYNCHRONIZE}
1202 };
1203 
1204 #define	ACE_PERM_COUNT (sizeof (ace_perm_table) / sizeof (value_table_t))
1205 
1206 value_table_t aclent_perm_table[] = {
1207 	{ 'r', S_IROTH},
1208 	{ 'w', S_IWOTH},
1209 	{ 'x', S_IXOTH}
1210 };
1211 
1212 #define	ACLENT_PERM_COUNT (sizeof (aclent_perm_table) / sizeof (value_table_t))
1213 
1214 value_table_t inherit_table[] = {
1215 	{'f', ACE_FILE_INHERIT_ACE},
1216 	{'d', ACE_DIRECTORY_INHERIT_ACE},
1217 	{'i', ACE_INHERIT_ONLY_ACE},
1218 	{'n', ACE_NO_PROPAGATE_INHERIT_ACE},
1219 	{'S', ACE_SUCCESSFUL_ACCESS_ACE_FLAG},
1220 	{'F', ACE_FAILED_ACCESS_ACE_FLAG},
1221 	{'I', ACE_INHERITED_ACE}
1222 };
1223 
1224 #define	IFLAG_COUNT (sizeof (inherit_table) / sizeof (value_table_t))
1225 #define	IFLAG_COUNT_V1 6 /* Older version compatibility */
1226 
1227 /*
1228  * compute value from a permission table or inheritance table
1229  * based on string passed in.  If positional is set then
1230  * string must match order in permtab, otherwise any order
1231  * is allowed.
1232  */
1233 int
1234 compute_values(value_table_t *permtab, int count,
1235     char *permstr, int positional, uint32_t *mask)
1236 {
1237 	uint32_t perm_val = 0;
1238 	char *pstr;
1239 	int i, found;
1240 
1241 	if (count < 0)
1242 		return (1);
1243 
1244 	if (positional) {
1245 		for (i = 0, pstr = permstr; i != count && pstr &&
1246 		    *pstr; i++, pstr++) {
1247 			if (*pstr == permtab[i].p_letter) {
1248 				perm_val |= permtab[i].p_value;
1249 			} else if (*pstr != '-') {
1250 				return (1);
1251 			}
1252 		}
1253 	} else {  /* random order single letters with no '-' */
1254 		for (pstr = permstr; pstr && *pstr; pstr++) {
1255 			for (found = 0, i = 0; i != count; i++) {
1256 				if (*pstr == permtab[i].p_letter) {
1257 					perm_val |= permtab[i].p_value;
1258 					found = 1;
1259 					break;
1260 				}
1261 			}
1262 			if (found == 0)
1263 				return (1);
1264 		}
1265 	}
1266 
1267 	*mask = perm_val;
1268 	return (0);
1269 }
1270 
1271 
1272 int
1273 ace_inherit_helper(char *str, uint32_t *imask, int table_length)
1274 {
1275 	int rc = 0;
1276 
1277 	if (strlen(str) == table_length) {
1278 		/*
1279 		 * If the string == table_length then first check to see it's
1280 		 * in positional format.  If that fails then see if it's in
1281 		 * non-positional format.
1282 		 */
1283 		if (compute_values(inherit_table, table_length, str,
1284 		    1, imask) && compute_values(inherit_table,
1285 		    table_length, str, 0, imask)) {
1286 			rc = 1;
1287 		}
1288 	} else {
1289 		rc = compute_values(inherit_table, table_length, str, 0, imask);
1290 	}
1291 
1292 	return (rc ? EACL_INHERIT_ERROR : 0);
1293 }
1294 
1295 /*
1296  * compute value for inheritance flags.
1297  */
1298 int
1299 compute_ace_inherit(char *str, uint32_t *imask)
1300 {
1301 	int rc = 0;
1302 
1303 	rc = ace_inherit_helper(str, imask, IFLAG_COUNT);
1304 
1305 	if (rc && strlen(str) != IFLAG_COUNT) {
1306 
1307 		/* is it an old formatted inherit string? */
1308 		rc = ace_inherit_helper(str, imask, IFLAG_COUNT_V1);
1309 	}
1310 
1311 	return (rc);
1312 }
1313 
1314 
1315 /*
1316  * compute value for ACE permissions.
1317  */
1318 int
1319 compute_ace_perms(char *str, uint32_t *mask)
1320 {
1321 	int positional = 0;
1322 	int error;
1323 
1324 	if (strlen(str) == ACE_PERM_COUNT)
1325 		positional = 1;
1326 
1327 	error = compute_values(ace_perm_table, ACE_PERM_COUNT,
1328 	    str, positional, mask);
1329 
1330 	if (error && positional) {
1331 		/*
1332 		 * If positional was set, then make sure permissions
1333 		 * aren't actually valid in non positional case where
1334 		 * all permissions are specified, just in random order.
1335 		 */
1336 		error = compute_values(ace_perm_table,
1337 		    ACE_PERM_COUNT, str, 0, mask);
1338 	}
1339 	if (error)
1340 		error = EACL_PERM_MASK_ERROR;
1341 
1342 	return (error);
1343 }
1344 
1345 
1346 
1347 /*
1348  * compute values for aclent permissions.
1349  */
1350 int
1351 compute_aclent_perms(char *str, o_mode_t *mask)
1352 {
1353 	int error;
1354 	uint32_t pmask;
1355 
1356 	if (strlen(str) != ACLENT_PERM_COUNT)
1357 		return (EACL_PERM_MASK_ERROR);
1358 
1359 	*mask = 0;
1360 	error = compute_values(aclent_perm_table, ACLENT_PERM_COUNT,
1361 	    str, 1, &pmask);
1362 	if (error == 0) {
1363 		*mask = (o_mode_t)pmask;
1364 	} else
1365 		error = EACL_PERM_MASK_ERROR;
1366 	return (error);
1367 }
1368 
1369 /*
1370  * determine ACE permissions.
1371  */
1372 int
1373 ace_perm_mask(struct acl_perm_type *aclperm, uint32_t *mask)
1374 {
1375 	int error;
1376 
1377 	if (aclperm->perm_style == PERM_TYPE_EMPTY) {
1378 		*mask = 0;
1379 		return (0);
1380 	}
1381 
1382 	if (aclperm->perm_style == PERM_TYPE_ACE) {
1383 		*mask = aclperm->perm_val;
1384 		return (0);
1385 	}
1386 
1387 	error = compute_ace_perms(aclperm->perm_str, mask);
1388 	if (error) {
1389 		acl_error(dgettext(TEXT_DOMAIN,
1390 		    "Invalid permission(s) '%s' specified\n"),
1391 		    aclperm->perm_str);
1392 		return (EACL_PERM_MASK_ERROR);
1393 	}
1394 
1395 	return (0);
1396 }
1397