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