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