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