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