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