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