xref: /titanic_51/usr/src/lib/libsec/common/acltext.c (revision 87c5f7b3eef6309c168257f261ac6ace4581d234)
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 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
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 
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 
54 extern acl_t *acl_alloc(enum acl_type);
55 
56 /*
57  * dynamic string that will increase in size on an
58  * as needed basis.
59  */
60 typedef struct dynaclstr {
61 	size_t d_bufsize;		/* current size of aclexport */
62 	char *d_aclexport;
63 	int d_pos;
64 } dynaclstr_t;
65 
66 static int str_append(dynaclstr_t *, char *);
67 static int aclent_perm_txt(dynaclstr_t *, o_mode_t);
68 
69 static void
70 aclent_perms(int perm, char *txt_perms)
71 {
72 	if (perm & S_IROTH)
73 		txt_perms[0] = 'r';
74 	else
75 		txt_perms[0] = '-';
76 	if (perm & S_IWOTH)
77 		txt_perms[1] = 'w';
78 	else
79 		txt_perms[1] = '-';
80 	if (perm & S_IXOTH)
81 		txt_perms[2] = 'x';
82 	else
83 		txt_perms[2] = '-';
84 	txt_perms[3] = '\0';
85 }
86 
87 static char *
88 pruname(uid_t uid, char *uidp, size_t buflen, int noresolve)
89 {
90 	struct passwd	*passwdp = NULL;
91 
92 	if (noresolve == 0)
93 		passwdp = getpwuid(uid);
94 	if (passwdp == (struct passwd *)NULL) {
95 		/* could not get passwd information: display uid instead */
96 		(void) snprintf(uidp, buflen, "%u", uid);
97 	} else {
98 		(void) strlcpy(uidp, passwdp->pw_name, buflen);
99 	}
100 	return (uidp);
101 }
102 
103 static char *
104 prgname(gid_t gid, char *gidp, size_t buflen, int noresolve)
105 {
106 	struct group	*groupp = NULL;
107 
108 	if (noresolve == 0)
109 		groupp = getgrgid(gid);
110 	if (groupp == (struct group *)NULL) {
111 		/* could not get group information: display gid instead */
112 		(void) snprintf(gidp, buflen, "%u", gid);
113 	} else {
114 		(void) strlcpy(gidp, groupp->gr_name, buflen);
115 	}
116 	return (gidp);
117 }
118 
119 static char *
120 prsidname(uid_t who, boolean_t user, char **sidp, int noresolve)
121 {
122 	idmap_handle_t *idmap_hdl = NULL;
123 	idmap_get_handle_t *get_hdl = NULL;
124 	idmap_stat status;
125 	idmap_rid_t rid;
126 	int error = 1;
127 	int len;
128 	char *domain;
129 	char *name;
130 
131 	if (noresolve) {
132 		len = snprintf(NULL, 0, "%u", who);
133 		*sidp = malloc(len + 1);
134 		(void) snprintf(*sidp, len + 1, "%u", who);
135 		return (*sidp);
136 	}
137 
138 	/*
139 	 * First try and get windows name
140 	 */
141 
142 	if (user)
143 		error = idmap_getwinnamebyuid(who, &name, &domain);
144 	else
145 		error = idmap_getwinnamebygid(who, &name, &domain);
146 
147 	if (error) {
148 		if (idmap_init(&idmap_hdl) == 0 &&
149 		    idmap_get_create(idmap_hdl, &get_hdl) == 0) {
150 			if (user)
151 				error = idmap_get_sidbyuid(get_hdl, who,
152 				    0, &domain, &rid, &status);
153 			else
154 				error = idmap_get_sidbygid(get_hdl, who,
155 				    0, &domain, &rid, &status);
156 			if (error == 0)
157 				error = idmap_get_mappings(get_hdl);
158 		}
159 		if (error == 0) {
160 			len = snprintf(NULL, 0, "%s-%d", domain, rid);
161 			*sidp = malloc(len + 1);
162 			(void) snprintf(*sidp, len + 1, "%s-%d", domain, rid);
163 		} else {
164 			*sidp = NULL;
165 		}
166 		if (get_hdl)
167 			idmap_get_destroy(get_hdl);
168 		if (idmap_hdl)
169 			(void) idmap_fini(idmap_hdl);
170 	} else {
171 		int len;
172 		len = snprintf(NULL, 0, "%s@%d", name, domain);
173 		*sidp = malloc(len + 1);
174 		(void) snprintf(*sidp, len + 1, "%s@%s", name, domain);
175 	}
176 	return (*sidp);
177 }
178 
179 static void
180 aclent_printacl(acl_t *aclp)
181 {
182 	aclent_t *tp;
183 	int aclcnt;
184 	int mask;
185 	int slot = 0;
186 	char perm[4];
187 	char uidp[ID_STR_MAX];
188 	char gidp[ID_STR_MAX];
189 
190 	/* display ACL: assume it is sorted. */
191 	aclcnt = aclp->acl_cnt;
192 	for (tp = aclp->acl_aclp; tp && aclcnt--; tp++) {
193 		if (tp->a_type == CLASS_OBJ)
194 			mask = tp->a_perm;
195 	}
196 	aclcnt = aclp->acl_cnt;
197 	for (tp = aclp->acl_aclp; aclcnt--; tp++) {
198 		(void) printf("     %d:", slot++);
199 		switch (tp->a_type) {
200 		case USER:
201 			aclent_perms(tp->a_perm, perm);
202 			(void) printf("user:%s:%s\t\t",
203 			    pruname(tp->a_id, uidp, sizeof (uidp), 0), perm);
204 			aclent_perms((tp->a_perm & mask), perm);
205 			(void) printf("#effective:%s\n", perm);
206 			break;
207 		case USER_OBJ:
208 			/* no need to display uid */
209 			aclent_perms(tp->a_perm, perm);
210 			(void) printf("user::%s\n", perm);
211 			break;
212 		case GROUP:
213 			aclent_perms(tp->a_perm, perm);
214 			(void) printf("group:%s:%s\t\t",
215 			    prgname(tp->a_id, gidp, sizeof (gidp), 0), perm);
216 			aclent_perms(tp->a_perm & mask, perm);
217 			(void) printf("#effective:%s\n", perm);
218 			break;
219 		case GROUP_OBJ:
220 			aclent_perms(tp->a_perm, perm);
221 			(void) printf("group::%s\t\t", perm);
222 			aclent_perms(tp->a_perm & mask, perm);
223 			(void) printf("#effective:%s\n", perm);
224 			break;
225 		case CLASS_OBJ:
226 			aclent_perms(tp->a_perm, perm);
227 			(void) printf("mask:%s\n", perm);
228 			break;
229 		case OTHER_OBJ:
230 			aclent_perms(tp->a_perm, perm);
231 			(void) printf("other:%s\n", perm);
232 			break;
233 		case DEF_USER:
234 			aclent_perms(tp->a_perm, perm);
235 			(void) printf("default:user:%s:%s\n",
236 			    pruname(tp->a_id, uidp, sizeof (uidp), 0), perm);
237 			break;
238 		case DEF_USER_OBJ:
239 			aclent_perms(tp->a_perm, perm);
240 			(void) printf("default:user::%s\n", perm);
241 			break;
242 		case DEF_GROUP:
243 			aclent_perms(tp->a_perm, perm);
244 			(void) printf("default:group:%s:%s\n",
245 			    prgname(tp->a_id, gidp, sizeof (gidp), 0), perm);
246 			break;
247 		case DEF_GROUP_OBJ:
248 			aclent_perms(tp->a_perm, perm);
249 			(void) printf("default:group::%s\n", perm);
250 			break;
251 		case DEF_CLASS_OBJ:
252 			aclent_perms(tp->a_perm, perm);
253 			(void) printf("default:mask:%s\n", perm);
254 			break;
255 		case DEF_OTHER_OBJ:
256 			aclent_perms(tp->a_perm, perm);
257 			(void) printf("default:other:%s\n", perm);
258 			break;
259 		default:
260 			(void) fprintf(stderr,
261 			    dgettext(TEXT_DOMAIN, "unrecognized entry\n"));
262 			break;
263 		}
264 	}
265 }
266 
267 static void
268 split_line(char *str, int cols)
269 {
270 	char *ptr;
271 	int len;
272 	int i;
273 	int last_split;
274 	char *pad = "";
275 	int pad_len;
276 
277 	len = strlen(str);
278 	ptr = str;
279 	pad_len = 0;
280 
281 	ptr = str;
282 	last_split = 0;
283 	for (i = 0; i != len; i++) {
284 		if ((i + pad_len + 4) >= cols) {
285 			(void) printf("%s%.*s\n", pad, last_split, ptr);
286 			ptr = &ptr[last_split];
287 			len = strlen(ptr);
288 			i = 0;
289 			pad_len = 4;
290 			pad = "         ";
291 		} else {
292 			if (ptr[i] == '/' || ptr[i] == ':') {
293 				last_split = i;
294 			}
295 		}
296 	}
297 	if (i == len) {
298 		(void) printf("%s%s\n", pad, ptr);
299 	}
300 }
301 
302 /*
303  * compute entry type string, such as user:joe, group:staff,...
304  */
305 static int
306 aclent_type_txt(dynaclstr_t *dstr, aclent_t *aclp, int flags)
307 {
308 	char idp[ID_STR_MAX];
309 	int error;
310 
311 	switch (aclp->a_type) {
312 	case DEF_USER_OBJ:
313 	case USER_OBJ:
314 		if (aclp->a_type == USER_OBJ)
315 			error = str_append(dstr, "user::");
316 		else
317 			error = str_append(dstr, "defaultuser::");
318 		break;
319 
320 	case DEF_USER:
321 	case USER:
322 		if (aclp->a_type == USER)
323 			error = str_append(dstr, "user:");
324 		else
325 			error = str_append(dstr, "defaultuser:");
326 		if (error)
327 			break;
328 		error = str_append(dstr, pruname(aclp->a_id, idp,
329 		    sizeof (idp), flags & ACL_NORESOLVE));
330 		if (error == 0)
331 			error = str_append(dstr, ":");
332 		break;
333 
334 	case DEF_GROUP_OBJ:
335 	case GROUP_OBJ:
336 		if (aclp->a_type == GROUP_OBJ)
337 			error = str_append(dstr, "group::");
338 		else
339 			error = str_append(dstr, "defaultgroup::");
340 		break;
341 
342 	case DEF_GROUP:
343 	case GROUP:
344 		if (aclp->a_type == GROUP)
345 			error = str_append(dstr, "group:");
346 		else
347 			error = str_append(dstr, "defaultgroup:");
348 		if (error)
349 			break;
350 		error = str_append(dstr, prgname(aclp->a_id, idp,
351 		    sizeof (idp), flags & ACL_NORESOLVE));
352 		if (error == 0)
353 			error = str_append(dstr, ":");
354 		break;
355 
356 	case DEF_CLASS_OBJ:
357 	case CLASS_OBJ:
358 		if (aclp->a_type == CLASS_OBJ)
359 			error = str_append(dstr, "mask:");
360 		else
361 			error = str_append(dstr, "defaultmask:");
362 		break;
363 
364 	case DEF_OTHER_OBJ:
365 	case OTHER_OBJ:
366 		if (aclp->a_type == OTHER_OBJ)
367 			error = str_append(dstr, "other:");
368 		else
369 			error = str_append(dstr, "defaultother:");
370 		break;
371 
372 	default:
373 		error = 1;
374 		break;
375 	}
376 
377 	return (error);
378 }
379 
380 /*
381  * compute entry type string such as, owner@:, user:joe, group:staff,...
382  */
383 static int
384 ace_type_txt(dynaclstr_t *dynstr, ace_t *acep, int flags)
385 {
386 	char idp[ID_STR_MAX];
387 	int error;
388 	char *sidp = NULL;
389 
390 	switch (acep->a_flags & ACE_TYPE_FLAGS) {
391 	case ACE_OWNER:
392 		error = str_append(dynstr, OWNERAT_TXT);
393 		break;
394 
395 	case ACE_GROUP|ACE_IDENTIFIER_GROUP:
396 		error = str_append(dynstr, GROUPAT_TXT);
397 		break;
398 
399 	case ACE_IDENTIFIER_GROUP:
400 		if ((flags & ACL_SID_FMT) && acep->a_who > MAXUID) {
401 			if (error = str_append(dynstr,
402 			    GROUPSID_TXT))
403 				break;
404 			error = str_append(dynstr, prsidname(acep->a_who,
405 			    B_FALSE, &sidp, flags & ACL_NORESOLVE));
406 		} else {
407 			if (error = str_append(dynstr, GROUP_TXT))
408 				break;
409 			error = str_append(dynstr, prgname(acep->a_who, idp,
410 			    sizeof (idp), flags & ACL_NORESOLVE));
411 		}
412 		if (error == 0)
413 			error = str_append(dynstr, ":");
414 		break;
415 
416 	case ACE_EVERYONE:
417 		error = str_append(dynstr, EVERYONEAT_TXT);
418 		break;
419 
420 	case 0:
421 		if ((flags & ACL_SID_FMT) && acep->a_who > MAXUID) {
422 			if (error = str_append(dynstr, USERSID_TXT))
423 				break;
424 			error = str_append(dynstr, prsidname(acep->a_who,
425 			    B_TRUE, &sidp, flags & ACL_NORESOLVE));
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
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
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
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 *
730 aclent_acltotext(aclent_t  *aclp, int aclcnt, int flags)
731 {
732 	dynaclstr_t 	*dstr;
733 	char		*aclexport;
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 *
782 acltotext(aclent_t *aclp, int aclcnt)
783 {
784 	return (aclent_acltotext(aclp, aclcnt, 0));
785 }
786 
787 
788 aclent_t *
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  * returns a character position index of the start of the newly
810  * appended string.  Returns -1 if operation couldn't be completed.
811  */
812 static int
813 str_append(dynaclstr_t *dstr, char *newstr)
814 {
815 	size_t len = strlen(newstr);
816 
817 	if ((len + dstr->d_pos) >= dstr->d_bufsize) {
818 		dstr->d_aclexport = realloc(dstr->d_aclexport,
819 		    dstr->d_bufsize + len + 1);
820 		if (dstr->d_aclexport == NULL)
821 			return (1);
822 		dstr->d_bufsize += len;
823 	}
824 	(void) strcat(&dstr->d_aclexport[dstr->d_pos], newstr);
825 	dstr->d_pos += len;
826 	return (0);
827 }
828 
829 static int
830 aclent_perm_txt(dynaclstr_t *dstr, o_mode_t perm)
831 {
832 	char buf[4];
833 
834 	if (perm & S_IROTH)
835 		buf[0] = 'r';
836 	else
837 		buf[0] = '-';
838 	if (perm & S_IWOTH)
839 		buf[1] = 'w';
840 	else
841 		buf[1] = '-';
842 	if (perm & S_IXOTH)
843 		buf[2] = 'x';
844 	else
845 		buf[2] = '-';
846 	buf[3] = '\0';
847 	return (str_append(dstr, buf));
848 }
849 
850 /*
851  * ace_acltotext() convert each ace formatted acl to look like this:
852  *
853  * entry_type:uid^gid^name:perms[:flags]:<allow|deny>[:id][,]
854  *
855  * The maximum length of entry_type is 5 ("group")
856  *
857  * The max length of a uid^gid^name entry (in theory) is 8,
858  * however id could be a number so we therefore use ID_STR_MAX
859  *
860  * The length of a perms entry is 144 i.e read_data/write_data...
861  * to each acl entry.
862  *
863  * iflags: file_inherit/dir_inherit/inherit_only/no_propagate/successful_access
864  *         /failed_access
865  *
866  */
867 
868 #define	ACE_ENTRYTYPLEN		6
869 #define	IFLAGS_STR "file_inherit/dir_inherit/inherit_only/no_propagate/" \
870 	"successful_access/failed_access/inherited"
871 #define	IFLAGS_SIZE		(sizeof (IFLAGS_STR) - 1)
872 #define	ACCESS_TYPE_SIZE	7	/* if unknown */
873 #define	COLON_CNT		3
874 #define	PERMS_LEN		216
875 #define	ACE_ENTRY_SIZE	(ACE_ENTRYTYPLEN + ID_STR_MAX + PERMS_LEN + \
876     ACCESS_TYPE_SIZE + IFLAGS_SIZE + COLON_CNT + APPENDED_ID_MAX)
877 
878 static char *
879 ace_acltotext(acl_t *aceaclp, int flags)
880 {
881 	ace_t		*aclp = aceaclp->acl_aclp;
882 	int		aclcnt = aceaclp->acl_cnt;
883 	int		i;
884 	int		error = 0;
885 	int		isdir = (aceaclp->acl_flags & ACL_IS_DIR);
886 	dynaclstr_t 	*dstr;
887 	char		*aclexport;
888 
889 	if (aclp == NULL)
890 		return (NULL);
891 
892 	if ((dstr = malloc(sizeof (dynaclstr_t))) == NULL)
893 		return (NULL);
894 	dstr->d_bufsize = aclcnt * ACL_ENTRY_SIZE;
895 	if ((dstr->d_aclexport = malloc(dstr->d_bufsize)) == NULL) {
896 		free(dstr);
897 		return (NULL);
898 	}
899 	*dstr->d_aclexport = '\0';
900 	dstr->d_pos = 0;
901 
902 	for (i = 0; i < aclcnt; i++, aclp++) {
903 
904 		if (error = ace_type_txt(dstr, aclp, flags))
905 			break;
906 		if (error = ace_perm_txt(dstr, aclp->a_access_mask,
907 		    aclp->a_flags, isdir, flags))
908 			break;
909 		if (error = ace_inherit_txt(dstr, aclp->a_flags, flags))
910 			break;
911 		if (error = ace_access_txt(dstr, aclp->a_type))
912 			break;
913 
914 		if ((flags & ACL_APPEND_ID) &&
915 		    (((aclp->a_flags & ACE_TYPE_FLAGS) == 0) ||
916 		    ((aclp->a_flags & ACE_TYPE_FLAGS) ==
917 		    ACE_IDENTIFIER_GROUP))) {
918 			char id[ID_STR_MAX], *idstr;
919 
920 			if (error = str_append(dstr, ":"))
921 				break;
922 			id[ID_STR_MAX -1] = '\0'; /* null terminate buffer */
923 			idstr = lltostr((aclp->a_who > MAXUID &&
924 			    !(flags & ACL_NORESOLVE)) ? UID_NOBODY :
925 			    aclp->a_who, &id[ID_STR_MAX - 1]);
926 			if (error = str_append(dstr, idstr))
927 				break;
928 		}
929 		if (i < aclcnt - 1) {
930 			if (error = str_append(dstr, ","))
931 				break;
932 		}
933 	}
934 	if (error) {
935 		if (dstr->d_aclexport)
936 			free(dstr->d_aclexport);
937 	} else {
938 		aclexport = dstr->d_aclexport;
939 	}
940 	free(dstr);
941 	return (aclexport);
942 }
943 
944 char *
945 acl_totext(acl_t *aclp, int flags)
946 {
947 	char *txtp;
948 
949 	if (aclp == NULL)
950 		return (NULL);
951 
952 	switch (aclp->acl_type) {
953 	case ACE_T:
954 		txtp = ace_acltotext(aclp, flags);
955 		break;
956 	case ACLENT_T:
957 		txtp = aclent_acltotext(aclp->acl_aclp, aclp->acl_cnt, flags);
958 		break;
959 	}
960 
961 	return (txtp);
962 }
963 
964 int
965 acl_fromtext(const char *acltextp, acl_t **ret_aclp)
966 {
967 	int error;
968 	char *buf;
969 
970 	buf = malloc(strlen(acltextp) + 2);
971 	if (buf == NULL)
972 		return (EACL_MEM_ERROR);
973 	strcpy(buf, acltextp);
974 	strcat(buf, "\n");
975 	yybuf = buf;
976 	yyreset();
977 	error = yyparse();
978 	free(buf);
979 
980 	if (yyacl) {
981 		if (error == 0)
982 			*ret_aclp = yyacl;
983 		else {
984 			acl_free(yyacl);
985 		}
986 		yyacl = NULL;
987 	}
988 	return (error);
989 }
990 
991 int
992 acl_parse(const char *acltextp, acl_t **aclp)
993 {
994 	int error;
995 
996 	yyinteractive = 1;
997 	error = acl_fromtext(acltextp, aclp);
998 	yyinteractive = 0;
999 	return (error);
1000 }
1001 
1002 static void
1003 ace_compact_printacl(acl_t *aclp)
1004 {
1005 	int cnt;
1006 	ace_t *acep;
1007 	dynaclstr_t *dstr;
1008 	int len;
1009 
1010 	if ((dstr = malloc(sizeof (dynaclstr_t))) == NULL)
1011 		return;
1012 	dstr->d_bufsize = ACE_ENTRY_SIZE;
1013 	if ((dstr->d_aclexport = malloc(dstr->d_bufsize)) == NULL) {
1014 		free(dstr);
1015 		return;
1016 	}
1017 	*dstr->d_aclexport = '\0';
1018 
1019 	dstr->d_pos = 0;
1020 	for (cnt = 0, acep = aclp->acl_aclp;
1021 	    cnt != aclp->acl_cnt; cnt++, acep++) {
1022 		dstr->d_aclexport[0] = '\0';
1023 		dstr->d_pos = 0;
1024 
1025 		if (ace_type_txt(dstr, acep, 0))
1026 			break;
1027 		len = strlen(&dstr->d_aclexport[0]);
1028 		if (ace_perm_txt(dstr, acep->a_access_mask, acep->a_flags,
1029 		    aclp->acl_flags & ACL_IS_DIR, ACL_COMPACT_FMT))
1030 			break;
1031 		if (ace_inherit_txt(dstr, acep->a_flags, ACL_COMPACT_FMT))
1032 			break;
1033 		if (ace_access_txt(dstr, acep->a_type) == -1)
1034 			break;
1035 		(void) printf("    %20.*s%s\n", len, dstr->d_aclexport,
1036 		    &dstr->d_aclexport[len]);
1037 	}
1038 
1039 	if (dstr->d_aclexport)
1040 		free(dstr->d_aclexport);
1041 	free(dstr);
1042 }
1043 
1044 static void
1045 ace_printacl(acl_t *aclp, int cols, int compact)
1046 {
1047 	int  slot = 0;
1048 	char *token;
1049 	char *acltext;
1050 
1051 	if (compact) {
1052 		ace_compact_printacl(aclp);
1053 		return;
1054 	}
1055 
1056 	acltext = acl_totext(aclp, 0);
1057 
1058 	if (acltext == NULL)
1059 		return;
1060 
1061 	token = strtok(acltext, ",");
1062 	if (token == NULL) {
1063 		free(acltext);
1064 		return;
1065 	}
1066 
1067 	do {
1068 		(void) printf("     %d:", slot++);
1069 		split_line(token, cols - 5);
1070 	} while (token = strtok(NULL, ","));
1071 	free(acltext);
1072 }
1073 
1074 /*
1075  * pretty print an ACL.
1076  * For aclent_t ACL's the format is
1077  * similar to the old format used by getfacl,
1078  * with the addition of adding a "slot" number
1079  * before each entry.
1080  *
1081  * for ace_t ACL's the cols variable will break up
1082  * the long lines into multiple lines and will also
1083  * print a "slot" number.
1084  */
1085 void
1086 acl_printacl(acl_t *aclp, int cols, int compact)
1087 {
1088 
1089 	switch (aclp->acl_type) {
1090 	case ACLENT_T:
1091 		aclent_printacl(aclp);
1092 		break;
1093 	case ACE_T:
1094 		ace_printacl(aclp, cols, compact);
1095 		break;
1096 	}
1097 }
1098 
1099 typedef struct value_table {
1100 	char		p_letter; /* perm letter such as 'r' */
1101 	uint32_t	p_value; /* value for perm when pletter found */
1102 } value_table_t;
1103 
1104 /*
1105  * The permission tables are laid out in positional order
1106  * a '-' character will indicate a permission at a given
1107  * position is not specified.  The '-' is not part of the
1108  * table, but will be checked for in the permission computation
1109  * routine.
1110  */
1111 value_table_t ace_perm_table[] = {
1112 	{ 'r', ACE_READ_DATA},
1113 	{ 'w', ACE_WRITE_DATA},
1114 	{ 'x', ACE_EXECUTE},
1115 	{ 'p', ACE_APPEND_DATA},
1116 	{ 'd', ACE_DELETE},
1117 	{ 'D', ACE_DELETE_CHILD},
1118 	{ 'a', ACE_READ_ATTRIBUTES},
1119 	{ 'A', ACE_WRITE_ATTRIBUTES},
1120 	{ 'R', ACE_READ_NAMED_ATTRS},
1121 	{ 'W', ACE_WRITE_NAMED_ATTRS},
1122 	{ 'c', ACE_READ_ACL},
1123 	{ 'C', ACE_WRITE_ACL},
1124 	{ 'o', ACE_WRITE_OWNER},
1125 	{ 's', ACE_SYNCHRONIZE}
1126 };
1127 
1128 #define	ACE_PERM_COUNT (sizeof (ace_perm_table) / sizeof (value_table_t))
1129 
1130 value_table_t aclent_perm_table[] = {
1131 	{ 'r', S_IROTH},
1132 	{ 'w', S_IWOTH},
1133 	{ 'x', S_IXOTH}
1134 };
1135 
1136 #define	ACLENT_PERM_COUNT (sizeof (aclent_perm_table) / sizeof (value_table_t))
1137 
1138 value_table_t inherit_table[] = {
1139 	{'f', ACE_FILE_INHERIT_ACE},
1140 	{'d', ACE_DIRECTORY_INHERIT_ACE},
1141 	{'i', ACE_INHERIT_ONLY_ACE},
1142 	{'n', ACE_NO_PROPAGATE_INHERIT_ACE},
1143 	{'S', ACE_SUCCESSFUL_ACCESS_ACE_FLAG},
1144 	{'F', ACE_FAILED_ACCESS_ACE_FLAG},
1145 	{'I', ACE_INHERITED_ACE}
1146 };
1147 
1148 #define	IFLAG_COUNT (sizeof (inherit_table) / sizeof (value_table_t))
1149 #define	IFLAG_COUNT_V1 6 /* Older version compatibility */
1150 
1151 /*
1152  * compute value from a permission table or inheritance table
1153  * based on string passed in.  If positional is set then
1154  * string must match order in permtab, otherwise any order
1155  * is allowed.
1156  */
1157 int
1158 compute_values(value_table_t *permtab, int count,
1159     char *permstr, int positional, uint32_t *mask)
1160 {
1161 	uint32_t perm_val = 0;
1162 	char *pstr;
1163 	int i, found;
1164 
1165 	if (count < 0)
1166 		return (1);
1167 
1168 	if (positional) {
1169 		for (i = 0, pstr = permstr; i != count && pstr &&
1170 		    *pstr; i++, pstr++) {
1171 			if (*pstr == permtab[i].p_letter) {
1172 				perm_val |= permtab[i].p_value;
1173 			} else if (*pstr != '-') {
1174 				return (1);
1175 			}
1176 		}
1177 	} else {  /* random order single letters with no '-' */
1178 		for (pstr = permstr; pstr && *pstr; pstr++) {
1179 			for (found = 0, i = 0; i != count; i++) {
1180 				if (*pstr == permtab[i].p_letter) {
1181 					perm_val |= permtab[i].p_value;
1182 					found = 1;
1183 					break;
1184 				}
1185 			}
1186 			if (found == 0)
1187 				return (1);
1188 		}
1189 	}
1190 
1191 	*mask = perm_val;
1192 	return (0);
1193 }
1194 
1195 
1196 int
1197 ace_inherit_helper(char *str, uint32_t *imask, int table_length)
1198 {
1199 	int rc = 0;
1200 
1201 	if (strlen(str) == table_length) {
1202 		/*
1203 		 * If the string == table_length then first check to see it's
1204 		 * in positional format.  If that fails then see if it's in
1205 		 * non-positional format.
1206 		 */
1207 		if (compute_values(inherit_table, table_length, str,
1208 		    1, imask) && compute_values(inherit_table,
1209 		    table_length, str, 0, imask)) {
1210 			rc = 1;
1211 		}
1212 	} else {
1213 		rc = compute_values(inherit_table, table_length, str, 0, imask);
1214 	}
1215 
1216 	return (rc ? EACL_INHERIT_ERROR : 0);
1217 }
1218 
1219 /*
1220  * compute value for inheritance flags.
1221  */
1222 int
1223 compute_ace_inherit(char *str, uint32_t *imask)
1224 {
1225 	int rc = 0;
1226 
1227 	rc = ace_inherit_helper(str, imask, IFLAG_COUNT);
1228 
1229 	if (rc && strlen(str) != IFLAG_COUNT) {
1230 
1231 		/* is it an old formatted inherit string? */
1232 		rc = ace_inherit_helper(str, imask, IFLAG_COUNT_V1);
1233 	}
1234 
1235 	return (rc);
1236 }
1237 
1238 
1239 /*
1240  * compute value for ACE permissions.
1241  */
1242 int
1243 compute_ace_perms(char *str, uint32_t *mask)
1244 {
1245 	int positional = 0;
1246 	int error;
1247 
1248 	if (strlen(str) == ACE_PERM_COUNT)
1249 		positional = 1;
1250 
1251 	error = compute_values(ace_perm_table, ACE_PERM_COUNT,
1252 	    str, positional, mask);
1253 
1254 	if (error && positional) {
1255 		/*
1256 		 * If positional was set, then make sure permissions
1257 		 * aren't actually valid in non positional case where
1258 		 * all permissions are specified, just in random order.
1259 		 */
1260 		error = compute_values(ace_perm_table,
1261 		    ACE_PERM_COUNT, str, 0, mask);
1262 	}
1263 	if (error)
1264 		error = EACL_PERM_MASK_ERROR;
1265 
1266 	return (error);
1267 }
1268 
1269 
1270 
1271 /*
1272  * compute values for aclent permissions.
1273  */
1274 int
1275 compute_aclent_perms(char *str, o_mode_t *mask)
1276 {
1277 	int error;
1278 	uint32_t pmask;
1279 
1280 	if (strlen(str) != ACLENT_PERM_COUNT)
1281 		return (EACL_PERM_MASK_ERROR);
1282 
1283 	*mask = 0;
1284 	error = compute_values(aclent_perm_table, ACLENT_PERM_COUNT,
1285 	    str, 1, &pmask);
1286 	if (error == 0) {
1287 		*mask = (o_mode_t)pmask;
1288 	} else
1289 		error = EACL_PERM_MASK_ERROR;
1290 	return (error);
1291 }
1292 
1293 /*
1294  * determine ACE permissions.
1295  */
1296 int
1297 ace_perm_mask(struct acl_perm_type *aclperm, uint32_t *mask)
1298 {
1299 	int error;
1300 
1301 	if (aclperm->perm_style == PERM_TYPE_EMPTY) {
1302 		*mask = 0;
1303 		return (0);
1304 	}
1305 
1306 	if (aclperm->perm_style == PERM_TYPE_ACE) {
1307 		*mask = aclperm->perm_val;
1308 		return (0);
1309 	}
1310 
1311 	error = compute_ace_perms(aclperm->perm_str, mask);
1312 	if (error) {
1313 		acl_error(dgettext(TEXT_DOMAIN,
1314 		    "Invalid permission(s) '%s' specified\n"),
1315 		    aclperm->perm_str);
1316 		return (EACL_PERM_MASK_ERROR);
1317 	}
1318 
1319 	return (0);
1320 }
1321