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