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