xref: /illumos-gate/usr/src/lib/libsec/common/acltext.c (revision 43d18f1c320355e93c47399bea0b2e022fe06364)
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 2005 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/acl.h>
39 #include <aclutils.h>
40 #include <libintl.h>
41 
42 
43 extern acl_t *acl_alloc(enum acl_type);
44 
45 /*
46  * acltotext() converts each ACL entry to look like this:
47  *
48  *    entry_type:uid^gid^name:perms
49  *
50  * The maximum length of entry_type is 14 ("defaultgroup::" and
51  * "defaultother::") hence ENTRYTYPELEN is set to 14.
52  *
53  * The max length of a uid^gid^name entry (in theory) is 8, hence we use
54  * LOGNAME_MAX.
55  *
56  * The length of a perms entry is 4 to allow for the comma appended to each
57  * to each acl entry.  Hence PERMS is set to 4.
58  */
59 
60 #define	ENTRYTYPELEN	14
61 #define	PERMS		4
62 #define	ACL_ENTRY_SIZE	(ENTRYTYPELEN + LOGNAME_MAX + PERMS)
63 
64 #define	UPDATE_WHERE	where = dstr->aclexport + strlen(dstr->aclexport)
65 
66 struct dynaclstr {
67 	size_t bufsize;		/* current size of aclexport */
68 	char *aclexport;
69 };
70 
71 static char *strappend(char *, char *);
72 static char *convert_perm(char *, o_mode_t);
73 static int increase_length(struct dynaclstr *, size_t);
74 
75 static int
76 acl_str_to_id(char *str, int *id)
77 {
78 	char *end;
79 	uid_t value;
80 
81 	value = strtol(str, &end, 10);
82 
83 	if (errno != 0 || *end != '\0')
84 		return (EACL_INVALID_USER_GROUP);
85 
86 	*id = value;
87 
88 	return (0);
89 }
90 
91 /*
92  * Convert internal acl representation to external representation.
93  *
94  * The length of a non-owning user name or non-owning group name ie entries
95  * of type DEF_USER, USER, DEF_GROUP or GROUP, can exceed LOGNAME_MAX.  We
96  * thus check the length of these entries, and if greater than LOGNAME_MAX,
97  * we realloc() via increase_length().
98  *
99  * The LOGNAME_MAX, ENTRYTYPELEN and PERMS limits are otherwise always
100  * adhered to.
101  */
102 char *
103 acltotext(aclent_t *aclp, int aclcnt)
104 {
105 	char		*aclexport;
106 	char		*where;
107 	struct group	*groupp;
108 	struct passwd	*passwdp;
109 	struct dynaclstr *dstr;
110 	int		i, rtn;
111 	size_t		excess = 0;
112 
113 	if (aclp == NULL)
114 		return (NULL);
115 	if ((dstr = malloc(sizeof (struct dynaclstr))) == NULL)
116 		return (NULL);
117 	dstr->bufsize = aclcnt * ACL_ENTRY_SIZE;
118 	if ((dstr->aclexport = malloc(dstr->bufsize)) == NULL) {
119 		free(dstr);
120 		return (NULL);
121 	}
122 	*dstr->aclexport = '\0';
123 	where = dstr->aclexport;
124 
125 	for (i = 0; i < aclcnt; i++, aclp++) {
126 		switch (aclp->a_type) {
127 		case DEF_USER_OBJ:
128 		case USER_OBJ:
129 			if (aclp->a_type == USER_OBJ)
130 				where = strappend(where, "user::");
131 			else
132 				where = strappend(where, "defaultuser::");
133 			where = convert_perm(where, aclp->a_perm);
134 			break;
135 		case DEF_USER:
136 		case USER:
137 			if (aclp->a_type == USER)
138 				where = strappend(where, "user:");
139 			else
140 				where = strappend(where, "defaultuser:");
141 			passwdp = getpwuid(aclp->a_id);
142 			if (passwdp == (struct passwd *)NULL) {
143 				/* put in uid instead */
144 				(void) sprintf(where, "%d", aclp->a_id);
145 				UPDATE_WHERE;
146 			} else {
147 				excess = strlen(passwdp->pw_name) - LOGNAME_MAX;
148 				if (excess > 0) {
149 					rtn = increase_length(dstr, excess);
150 					if (rtn == 1) {
151 						UPDATE_WHERE;
152 					} else {
153 						free(dstr->aclexport);
154 						free(dstr);
155 						return (NULL);
156 					}
157 				}
158 				where = strappend(where, passwdp->pw_name);
159 			}
160 			where = strappend(where, ":");
161 			where = convert_perm(where, aclp->a_perm);
162 			break;
163 		case DEF_GROUP_OBJ:
164 		case GROUP_OBJ:
165 			if (aclp->a_type == GROUP_OBJ)
166 				where = strappend(where, "group::");
167 			else
168 				where = strappend(where, "defaultgroup::");
169 			where = convert_perm(where, aclp->a_perm);
170 			break;
171 		case DEF_GROUP:
172 		case GROUP:
173 			if (aclp->a_type == GROUP)
174 				where = strappend(where, "group:");
175 			else
176 				where = strappend(where, "defaultgroup:");
177 			groupp = getgrgid(aclp->a_id);
178 			if (groupp == (struct group *)NULL) {
179 				/* put in gid instead */
180 				(void) sprintf(where, "%d", aclp->a_id);
181 				UPDATE_WHERE;
182 			} else {
183 				excess = strlen(groupp->gr_name) - LOGNAME_MAX;
184 				if (excess > 0) {
185 					rtn = increase_length(dstr, excess);
186 					if (rtn == 1) {
187 						UPDATE_WHERE;
188 					} else {
189 						free(dstr->aclexport);
190 						free(dstr);
191 						return (NULL);
192 					}
193 				}
194 				where = strappend(where, groupp->gr_name);
195 			}
196 			where = strappend(where, ":");
197 			where = convert_perm(where, aclp->a_perm);
198 			break;
199 		case DEF_CLASS_OBJ:
200 		case CLASS_OBJ:
201 			if (aclp->a_type == CLASS_OBJ)
202 				where = strappend(where, "mask:");
203 			else
204 				where = strappend(where, "defaultmask:");
205 			where = convert_perm(where, aclp->a_perm);
206 			break;
207 		case DEF_OTHER_OBJ:
208 		case OTHER_OBJ:
209 			if (aclp->a_type == OTHER_OBJ)
210 				where = strappend(where, "other:");
211 			else
212 				where = strappend(where, "defaultother:");
213 			where = convert_perm(where, aclp->a_perm);
214 			break;
215 		default:
216 			free(dstr->aclexport);
217 			free(dstr);
218 			return (NULL);
219 
220 		}
221 		if (i < aclcnt - 1)
222 			where = strappend(where, ",");
223 	}
224 	aclexport = dstr->aclexport;
225 	free(dstr);
226 	return (aclexport);
227 }
228 
229 /*
230  * Convert external acl representation to internal representation.
231  * The accepted syntax is: <acl_entry>[,<acl_entry>]*[,]
232  * The comma at the end is not prescribed by the man pages.
233  * But it is needed not to break the old programs.
234  */
235 static int
236 aclent_aclfromtext(char *aclstr, acl_t **ret_aclp)
237 {
238 	char		*fieldp;
239 	char		*tp;
240 	char		*nextp;
241 	char		*allocp;
242 	char		*aclimport;
243 	int		entry_type;
244 	int		id;
245 	int		len;
246 	int		error;
247 	o_mode_t	perm;
248 	aclent_t	*tmpaclp;
249 	acl_t		*aclp;
250 	struct group	*groupp;
251 	struct passwd	*passwdp;
252 
253 	aclp = NULL;
254 
255 	if (! aclstr)
256 		return (NULL);
257 
258 	aclp = acl_alloc(ACLENT_T);
259 	if (aclp == NULL) {
260 		return (EACL_MEM_ERROR);
261 	}
262 
263 	*ret_aclp = NULL;
264 
265 	len = strlen(aclstr);
266 
267 	if ((aclimport = allocp = strdup(aclstr)) == NULL) {
268 		return (EACL_MEM_ERROR);
269 	}
270 
271 	if (aclimport[len - 1] == ',')
272 		aclimport[len - 1] = '\0';
273 
274 	for (; aclimport; ) {
275 		/* look for an ACL entry */
276 		tp = strchr(aclimport, ',');
277 		if (tp == NULL) {
278 			nextp = NULL;
279 		} else {
280 			*tp = '\0';
281 			nextp = tp + 1;
282 		}
283 
284 		aclp->acl_cnt += 1;
285 
286 		/*
287 		 * get additional memory:
288 		 * can be more efficient by allocating a bigger block
289 		 * each time.
290 		 */
291 		if (aclp->acl_cnt > 1)
292 			tmpaclp = (aclent_t *)realloc(aclp->acl_aclp,
293 			    sizeof (aclent_t) * (aclp->acl_cnt));
294 		else
295 			tmpaclp = (aclent_t *)malloc(sizeof (aclent_t));
296 		if (tmpaclp == NULL) {
297 			free(allocp);
298 			acl_free(aclp);
299 			return (EACL_MEM_ERROR);
300 		}
301 		aclp->acl_aclp = tmpaclp;
302 		tmpaclp = (aclent_t *)aclp->acl_aclp + (aclp->acl_cnt - 1);
303 
304 		/* look for entry type field */
305 		tp = strchr(aclimport, ':');
306 		if (tp == NULL) {
307 			free(allocp);
308 			if (aclp)
309 				acl_free(aclp);
310 			return (EACL_ENTRY_ERROR);
311 		} else
312 			*tp = '\0';
313 		if (strcmp(aclimport, "user") == 0) {
314 			if (*(tp+1) == ':')
315 				entry_type = USER_OBJ;
316 			else
317 				entry_type = USER;
318 		} else if (strcmp(aclimport, "group") == 0) {
319 			if (*(tp+1) == ':')
320 				entry_type = GROUP_OBJ;
321 			else
322 				entry_type = GROUP;
323 		} else if (strcmp(aclimport, "other") == 0)
324 			entry_type = OTHER_OBJ;
325 		else if (strcmp(aclimport, "mask") == 0)
326 			entry_type = CLASS_OBJ;
327 		else if (strcmp(aclimport, "defaultuser") == 0) {
328 			if (*(tp+1) == ':')
329 				entry_type = DEF_USER_OBJ;
330 			else
331 				entry_type = DEF_USER;
332 		} else if (strcmp(aclimport, "defaultgroup") == 0) {
333 			if (*(tp+1) == ':')
334 				entry_type = DEF_GROUP_OBJ;
335 			else
336 				entry_type = DEF_GROUP;
337 		} else if (strcmp(aclimport, "defaultmask") == 0)
338 			entry_type = DEF_CLASS_OBJ;
339 		else if (strcmp(aclimport, "defaultother") == 0)
340 			entry_type = DEF_OTHER_OBJ;
341 		else {
342 			free(allocp);
343 			acl_free(aclp);
344 			return (EACL_ENTRY_ERROR);
345 		}
346 
347 		/* look for user/group name */
348 		if (entry_type != CLASS_OBJ && entry_type != OTHER_OBJ &&
349 		    entry_type != DEF_CLASS_OBJ &&
350 		    entry_type != DEF_OTHER_OBJ) {
351 			fieldp = tp + 1;
352 			tp = strchr(fieldp, ':');
353 			if (tp == NULL) {
354 				free(allocp);
355 				acl_free(aclp);
356 				return (EACL_INVALID_USER_GROUP);
357 			} else
358 				*tp = '\0';
359 			if (fieldp != tp) {
360 				/*
361 				 * The second field could be empty. We only care
362 				 * when the field has user/group name.
363 				 */
364 				if (entry_type == USER ||
365 				    entry_type == DEF_USER) {
366 					/*
367 					 * The reentrant interface getpwnam_r()
368 					 * is uncommitted and subject to
369 					 * change. Use the friendlier interface
370 					 * getpwnam().
371 					 */
372 					error = 0;
373 					passwdp = getpwnam(fieldp);
374 					if (passwdp == NULL) {
375 						error = acl_str_to_id(fieldp,
376 						    &id);
377 					} else {
378 						id = passwdp->pw_uid;
379 					}
380 
381 					if (error) {
382 						free(allocp);
383 						acl_free(aclp);
384 						return (error);
385 					}
386 
387 				} else {
388 					error = 0;
389 					if (entry_type == GROUP ||
390 					    entry_type == DEF_GROUP) {
391 						groupp = getgrnam(fieldp);
392 						if (groupp == NULL) {
393 							error = acl_str_to_id(
394 							    fieldp, &id);
395 						}
396 						if (error == 0)
397 							id = groupp->gr_gid;
398 					}
399 					if (error) {
400 						free(allocp);
401 						acl_free(aclp);
402 						return (error);
403 					}
404 				}
405 			} else {
406 				/*
407 				 * The second field is empty.
408 				 * Treat it as undefined (-1)
409 				 */
410 				id = -1;
411 			}
412 		} else {
413 			/*
414 			 * Let's not break the old applications
415 			 * that use mask::rwx, other::rwx format,
416 			 * though they violate the man pages.
417 			 */
418 			if (*(tp + 1) == ':')
419 				*++tp = 0;
420 		}
421 
422 		/* next field: permission */
423 		fieldp = tp + 1;
424 		if (strlen(fieldp) != 3) {
425 			/*  not "rwx" format */
426 			free(allocp);
427 			acl_free(aclp);
428 			return (EACL_PERM_MASK_ERROR);
429 		} else {
430 			char	s[] = "rwx";
431 			int	mask = 0x04;
432 			int	i;
433 			perm = 0;
434 
435 			for (i = 0; i < 3; i++, mask /= 2) {
436 				if (fieldp[i] == s[i])
437 					perm |= mask;
438 				else if (fieldp[i] != '-') {
439 					free(allocp);
440 					acl_free(aclp);
441 					return (EACL_PERM_MASK_ERROR);
442 				}
443 			}
444 		}
445 
446 		tmpaclp->a_type = entry_type;
447 		tmpaclp->a_id = id;
448 		tmpaclp->a_perm = perm;
449 		aclimport = nextp;
450 	}
451 	free(allocp);
452 	*ret_aclp = aclp;
453 	return (0);
454 }
455 
456 aclent_t *
457 aclfromtext(char *aclstr, int *aclcnt)
458 {
459 	acl_t *aclp;
460 	aclent_t *aclentp;
461 	int error;
462 
463 	error = aclent_aclfromtext(aclstr, &aclp);
464 	if (error)
465 		return (NULL);
466 
467 	aclentp = aclp->acl_aclp;
468 	aclp->acl_aclp = NULL;
469 	acl_free(aclp);
470 
471 	*aclcnt = aclp->acl_cnt;
472 	return (aclentp);
473 }
474 
475 
476 static char *
477 strappend(char *where, char *newstr)
478 {
479 	(void) strcat(where, newstr);
480 	return (where + strlen(newstr));
481 }
482 
483 static char *
484 convert_perm(char *where, o_mode_t perm)
485 {
486 	if (perm & 04)
487 		where = strappend(where, "r");
488 	else
489 		where = strappend(where, "-");
490 	if (perm & 02)
491 		where = strappend(where, "w");
492 	else
493 		where = strappend(where, "-");
494 	if (perm & 01)
495 		where = strappend(where, "x");
496 	else
497 		where = strappend(where, "-");
498 	/* perm is the last field */
499 	return (where);
500 }
501 
502 static char *
503 ace_convert_perm(char *where, mode_t perm, int isdir, int iflags)
504 {
505 	char *start = where;
506 
507 	/*
508 	 * The following mneumonics all have the
509 	 * same value.  The only difference is the
510 	 * first value is for files and second for directories
511 	 * ACE_READ_DATA/ACE_LIST_DIRECTORY
512 	 * ACE_WRITE_DATA/ACE_ADD_FILE
513 	 * ACE_APPEND_DATA/ACE_ADD_SUBDIRECTORY
514 	 */
515 
516 	/*
517 	 * If ACE is a directory, but inheritance indicates its
518 	 * for a file then print permissions for file rather than
519 	 * dir.
520 	 */
521 	if (isdir) {
522 		if (perm & ACE_LIST_DIRECTORY) {
523 			if (iflags == ACE_FILE_INHERIT_ACE)
524 				where = strappend(where, "read_data/");
525 			else
526 				where = strappend(where,
527 				    "list_directory/read_data/");
528 		}
529 		if (perm & ACE_ADD_FILE) {
530 			if (iflags == ACE_FILE_INHERIT_ACE)
531 				where = strappend(where, "write_data/");
532 			else
533 				where = strappend(where,
534 				    "add_file/write_data/");
535 		}
536 		if (perm & ACE_ADD_SUBDIRECTORY) {
537 			if (iflags == ACE_FILE_INHERIT_ACE)
538 				where = strappend(where, "append_data/");
539 			else
540 				where = strappend(where,
541 				    "add_subdirectory/append_data/");
542 		}
543 	} else {
544 		if (perm & ACE_READ_DATA)
545 			where = strappend(where, "read_data/");
546 		if (perm & ACE_WRITE_DATA)
547 			where = strappend(where, "write_data/");
548 		if (perm & ACE_APPEND_DATA)
549 			where = strappend(where, "append_data/");
550 	}
551 	if (perm & ACE_READ_NAMED_ATTRS)
552 		where = strappend(where, "read_xattr/");
553 	if (perm & ACE_WRITE_NAMED_ATTRS)
554 		where = strappend(where, "write_xattr/");
555 	if (perm & ACE_EXECUTE)
556 		where = strappend(where, "execute/");
557 	if (perm & ACE_DELETE_CHILD)
558 		where = strappend(where, "delete_child/");
559 	if (perm & ACE_READ_ATTRIBUTES)
560 		where = strappend(where, "read_attributes/");
561 	if (perm & ACE_WRITE_ATTRIBUTES)
562 		where = strappend(where, "write_attributes/");
563 	if (perm & ACE_DELETE)
564 		where = strappend(where, "delete/");
565 	if (perm & ACE_READ_ACL)
566 		where = strappend(where, "read_acl/");
567 	if (perm & ACE_WRITE_ACL)
568 		where = strappend(where, "write_acl/");
569 	if (perm & ACE_WRITE_OWNER)
570 		where = strappend(where, "write_owner/");
571 	if (perm & ACE_SYNCHRONIZE)
572 		where = strappend(where, "synchronize");
573 
574 	if (start[strlen(start) - 1] == '/') {
575 		start[strlen(start) - 1] = '\0';
576 		where = start + strlen(start);
577 	}
578 	return (where);
579 }
580 
581 int
582 ace_permask(char *perm_tok, int *perm)
583 {
584 	if (strcmp(perm_tok, "read_data") == 0)
585 		*perm |= ACE_READ_DATA;
586 	else if (strcmp(perm_tok, "list_directory") == 0)
587 		*perm |= ACE_LIST_DIRECTORY;
588 	else if (strcmp(perm_tok, "write_data") == 0)
589 		*perm |= ACE_WRITE_DATA;
590 	else if (strcmp(perm_tok, "add_file") == 0)
591 		*perm |= ACE_ADD_FILE;
592 	else if (strcmp(perm_tok, "append_data") == 0)
593 		*perm |= ACE_APPEND_DATA;
594 	else if (strcmp(perm_tok, "add_subdirectory") == 0)
595 		*perm |= ACE_ADD_SUBDIRECTORY;
596 	else if (strcmp(perm_tok, "read_xattr") == 0)
597 		*perm |= ACE_READ_NAMED_ATTRS;
598 	else if (strcmp(perm_tok, "write_xattr") == 0)
599 		*perm |= ACE_WRITE_NAMED_ATTRS;
600 	else if (strcmp(perm_tok, "execute") == 0)
601 		*perm |= ACE_EXECUTE;
602 	else if (strcmp(perm_tok, "delete_child") == 0)
603 		*perm |= ACE_DELETE_CHILD;
604 	else if (strcmp(perm_tok, "read_attributes") == 0)
605 		*perm |= ACE_READ_ATTRIBUTES;
606 	else if (strcmp(perm_tok, "write_attributes") == 0)
607 		*perm |= ACE_WRITE_ATTRIBUTES;
608 	else if (strcmp(perm_tok, "delete") == 0)
609 		*perm |= ACE_DELETE;
610 	else if (strcmp(perm_tok, "read_acl") == 0)
611 		*perm |= ACE_READ_ACL;
612 	else if (strcmp(perm_tok, "write_acl") == 0)
613 		*perm |= ACE_WRITE_ACL;
614 	else if (strcmp(perm_tok, "write_owner") == 0)
615 		*perm |= ACE_WRITE_OWNER;
616 	else if (strcmp(perm_tok, "synchronize") == 0)
617 		*perm |= ACE_SYNCHRONIZE;
618 	else {
619 		return (1);
620 	}
621 
622 	return (0);
623 }
624 
625 /*
626  * Callers should check the return code as this routine may change the string
627  * pointer in dynaclstr.
628  */
629 static int
630 increase_length(struct dynaclstr *dacl, size_t increase)
631 {
632 	char *tptr;
633 	size_t newsize;
634 
635 	newsize = dacl->bufsize + increase;
636 	tptr = realloc(dacl->aclexport, newsize);
637 	if (tptr != NULL) {
638 		dacl->aclexport = tptr;
639 		dacl->bufsize = newsize;
640 		return (1);
641 	} else
642 		return (0);
643 }
644 
645 /*
646  * ace_acltotext() conver each ace formatted acl to look like this:
647  *
648  * entry_type:uid^gid^name:perms:allow^deny[:flags][,]
649  *
650  * The maximum length of entry_type is 5 ("group")
651  *
652  * The max length of a uid^gid^name entry (in theory) is 8, hence we use
653  * LOGNAME_MAX.
654  *
655  * The length of a perms entry is 144 i.e read_data/write_data...
656  * to each acl entry.
657  *
658  * iflags: file_inherit/dir_inherit/inherit_only/no_propagate
659  *
660  */
661 
662 #define	ACE_ENTRYTYPLEN		6
663 #define	IFLAGS_SIZE		51
664 #define	ACCESS_TYPE_SIZE	5
665 #define	COLON_CNT		3
666 #define	PERMS_LEN		216
667 #define	ACE_ENTRY_SIZE	(ACE_ENTRYTYPLEN + LOGNAME_MAX + PERMS_LEN +\
668     ACCESS_TYPE_SIZE + IFLAGS_SIZE + COLON_CNT)
669 
670 static char *
671 ace_acltotext(acl_t *aceaclp)
672 {
673 	ace_t		*aclp = aceaclp->acl_aclp;
674 	int		aclcnt = aceaclp->acl_cnt;
675 	char		*aclexport;
676 	char		*where;
677 	char		*start;
678 	struct group	*groupp;
679 	struct passwd	*passwdp;
680 	struct dynaclstr *dstr;
681 	int		i, rtn;
682 	int		isdir = (aceaclp->acl_flags & ACL_IS_DIR);
683 	size_t		excess = 0;
684 
685 	if (aclp == NULL)
686 		return (NULL);
687 	if ((dstr = malloc(sizeof (struct dynaclstr))) == NULL)
688 		return (NULL);
689 	dstr->bufsize = aclcnt * ACE_ENTRY_SIZE;
690 	if ((dstr->aclexport = malloc(dstr->bufsize)) == NULL)
691 		return (NULL);
692 	*dstr->aclexport = '\0';
693 	where = dstr->aclexport;
694 
695 	for (i = 0; i < aclcnt; i++, aclp++) {
696 		switch (aclp->a_flags & 0xf040) {
697 		case ACE_OWNER:
698 		case 0:
699 			if ((aclp->a_flags & 0xf040) == ACE_OWNER)
700 				where = strappend(where, "owner@");
701 			else
702 				where = strappend(where, "user:");
703 			if ((aclp->a_flags & 0xf040) == 0) {
704 				passwdp = getpwuid(aclp->a_who);
705 				if (passwdp == (struct passwd *)NULL) {
706 					/* put in uid instead */
707 					(void) sprintf(where, "%d",
708 					    aclp->a_who);
709 				} else {
710 					excess = strlen(passwdp->pw_name) -
711 					    LOGNAME_MAX;
712 					if (excess > 0) {
713 						rtn = increase_length(dstr,
714 						    excess);
715 						if (rtn == 1)
716 							/* reset where */
717 							where =
718 							    dstr->aclexport +
719 							    strlen(
720 							    dstr->aclexport);
721 						else
722 							return (NULL);
723 					}
724 					where = strappend(where,
725 					    passwdp->pw_name);
726 				}
727 			} else {
728 				where = strappend(where, "");
729 			}
730 			where = strappend(where, ":");
731 			break;
732 		case ACE_GROUP|ACE_IDENTIFIER_GROUP:
733 		case ACE_IDENTIFIER_GROUP:
734 			if ((aclp->a_flags & 0xf040) ==
735 			    (ACE_GROUP | ACE_IDENTIFIER_GROUP))
736 				where = strappend(where, "group@");
737 			else
738 				where = strappend(where, "group:");
739 			if (!(aclp->a_flags & ACE_GROUP)) {
740 				groupp = getgrgid(aclp->a_who);
741 				if (groupp == (struct group *)NULL) {
742 					/* put in gid instead */
743 					(void) sprintf(where,
744 					    "%d", aclp->a_who);
745 				} else {
746 					excess = strlen(groupp->gr_name) -
747 					    LOGNAME_MAX;
748 					if (excess > 0) {
749 						rtn = increase_length(dstr,
750 						    excess);
751 						if (rtn == 1)
752 							/* reset where */
753 							where =
754 							    dstr->aclexport +
755 							    strlen(
756 							    dstr->aclexport);
757 						else
758 							return (NULL);
759 					}
760 					where = strappend(where,
761 					    groupp->gr_name);
762 				}
763 			} else {
764 					where = strappend(where, "");
765 			}
766 			where = strappend(where, ":");
767 			break;
768 		case ACE_EVERYONE:
769 			where = strappend(where, "everyone@:");
770 			break;
771 		default:
772 			free(dstr->aclexport);
773 			free(dstr);
774 			return (NULL);
775 
776 		}
777 		where = ace_convert_perm(where, aclp->a_access_mask,
778 		    isdir, (aclp->a_flags &
779 		    (ACE_FILE_INHERIT_ACE|ACE_DIRECTORY_INHERIT_ACE)));
780 		where = strappend(where,
781 		    (aclp->a_type == ACE_ACCESS_ALLOWED_ACE_TYPE) ?
782 		    ":allow" : ":deny");
783 
784 		/*
785 		 * slap on inheritance flags if we have any
786 		 */
787 
788 		if (aclp->a_flags & 0xf) {
789 			where = strappend(where, ":");
790 			start = where;
791 			if (aclp->a_flags & ACE_FILE_INHERIT_ACE)
792 				where = strappend(where, "file_inherit/");
793 			if (aclp->a_flags & ACE_DIRECTORY_INHERIT_ACE)
794 				where = strappend(where, "dir_inherit/");
795 			if (aclp->a_flags & ACE_NO_PROPAGATE_INHERIT_ACE)
796 				where = strappend(where, "no_propagate/");
797 			if (aclp->a_flags & ACE_INHERIT_ONLY_ACE)
798 				where = strappend(where, "inherit_only");
799 
800 			/*
801 			 * chop off trailing slash, if present
802 			 */
803 			if (start[strlen(start) - 1] == '/') {
804 				start[strlen(start) - 1] = '\0';
805 				where = start + strlen(start);
806 			}
807 		}
808 		if (i < aclcnt - 1)
809 			where = strappend(where, ",");
810 	}
811 	aclexport = dstr->aclexport;
812 	free(dstr);
813 	return (aclexport);
814 }
815 
816 static int
817 build_iflags(char *str, int *iflags)
818 {
819 
820 	char *tok;
821 	*iflags = 0;
822 
823 	tok = strtok(str, "/");
824 
825 	if (tok == NULL)
826 		return (1);
827 
828 	do {
829 		if (strcmp(tok, "file_inherit") == 0)
830 			*iflags |= ACE_FILE_INHERIT_ACE;
831 		else if (strcmp(tok, "dir_inherit") == 0)
832 			*iflags |= ACE_DIRECTORY_INHERIT_ACE;
833 		else if (strcmp(tok, "inherit_only") == 0)
834 			*iflags |= ACE_INHERIT_ONLY_ACE;
835 		else if (strcmp(tok, "no_propagate") == 0)
836 			*iflags |= ACE_NO_PROPAGATE_INHERIT_ACE;
837 		else
838 			return (1);
839 	} while (tok = strtok(NULL, "/"));
840 	return (0);
841 }
842 
843 /*
844  * Convert external acl representation to internal representation.
845  * The accepted syntax is: <acl_entry>[,<acl_entry>]*[,]
846  * The comma at the end is not prescribed by the man pages.
847  * But it is needed not to break the old programs.
848  */
849 
850 int
851 ace_aclfromtext(char *aclstr, acl_t **ret_aclp)
852 {
853 	char		*fieldp;
854 	char		*tp;
855 	char		*nextp;
856 	char		*allocp;
857 	char		*aclimport;
858 	char 		*str;
859 	char		*perm_tok;
860 	int		entry_type;
861 	int		id;
862 	int		type;
863 	int		iflags;
864 	int		len;
865 	int		error;
866 	int32_t		perm;
867 	ace_t		*tmpaclp;
868 	acl_t		*aclp;
869 	struct group	*groupp;
870 	struct passwd	*passwdp;
871 
872 	if (! aclstr)
873 		return (EACL_INVALID_STR);
874 
875 	len = strlen(aclstr);
876 
877 	aclp = acl_alloc(ACE_T);
878 	if (aclp == NULL) {
879 		return (EACL_MEM_ERROR);
880 	}
881 
882 	*ret_aclp = NULL;
883 
884 	if ((aclimport = allocp = strdup(aclstr)) == NULL) {
885 		return (EACL_MEM_ERROR);
886 	}
887 
888 
889 	if (aclimport[len - 1] == ',')
890 		aclimport[len - 1] = '\0';
891 
892 	for (; aclimport; ) {
893 		/* look for an ACL entry */
894 		tp = strchr(aclimport, ',');
895 		if (tp == NULL) {
896 			nextp = NULL;
897 		} else {
898 			*tp = '\0';
899 			nextp = tp + 1;
900 		}
901 
902 		aclp->acl_cnt += 1;
903 
904 		/*
905 		 * get additional memory:
906 		 * can be more efficient by allocating a bigger block
907 		 * each time.
908 		 */
909 		if (aclp->acl_cnt > 1)
910 			tmpaclp = (ace_t *)realloc(aclp->acl_aclp,
911 			    sizeof (ace_t) * (aclp->acl_cnt));
912 		else
913 			tmpaclp = (ace_t *)malloc(sizeof (ace_t));
914 		if (tmpaclp == NULL) {
915 			free(allocp);
916 			acl_free(aclp);
917 			return (EACL_MEM_ERROR);
918 		}
919 		aclp->acl_aclp = tmpaclp;
920 		tmpaclp = (ace_t *)aclp->acl_aclp + (aclp->acl_cnt - 1);
921 
922 		/* look for entry type field */
923 		tp = strchr(aclimport, ':');
924 		if (tp == NULL) {
925 			free(allocp);
926 			acl_free(aclp);
927 			return (EACL_ENTRY_ERROR);
928 		} else
929 			*tp = '\0';
930 		if (strcmp(aclimport, "owner@") == 0) {
931 			entry_type = ACE_OWNER;
932 		} else if (strcmp(aclimport, "group@") == 0) {
933 			entry_type = ACE_GROUP | ACE_IDENTIFIER_GROUP;
934 		} else if (strcmp(aclimport, "everyone@") == 0) {
935 			entry_type = ACE_EVERYONE;
936 		} else if (strcmp(aclimport, "group") == 0) {
937 			entry_type = ACE_IDENTIFIER_GROUP;
938 		} else if (strcmp(aclimport, "user") == 0) {
939 			entry_type = 0;
940 		} else {
941 			free(allocp);
942 			acl_free(aclp);
943 			return (EACL_ENTRY_ERROR);
944 		}
945 
946 		/*
947 		 * If not an abstraction owner@, group@ or everyone@
948 		 * then we must have a user/group name next
949 		 */
950 
951 		if (entry_type == 0 || entry_type == ACE_IDENTIFIER_GROUP) {
952 			fieldp = tp + 1;
953 			tp = strchr(fieldp, ':');
954 			if (tp == NULL) {
955 				free(allocp);
956 				acl_free(aclp);
957 				return (EACL_INVALID_USER_GROUP);
958 			} else
959 				*tp = '\0';
960 			if (fieldp != tp) {
961 				/*
962 				 * The second field could be empty. We only care
963 				 * when the field has user/group name.
964 				 */
965 				if (entry_type == 0) {
966 					/*
967 					 * The reentrant interface getpwnam_r()
968 					 * is uncommitted and subject to
969 					 * change. Use the friendlier interface
970 					 * getpwnam().
971 					 */
972 					error = 0;
973 					passwdp = getpwnam(fieldp);
974 					if (passwdp == NULL) {
975 						error = acl_str_to_id(
976 						    fieldp, &id);
977 					} else {
978 						id = passwdp->pw_uid;
979 					}
980 
981 					if (error) {
982 						free(allocp);
983 						acl_free(aclp);
984 						return (error);
985 					}
986 				} else {
987 					error = 0;
988 					if (entry_type ==
989 					    ACE_IDENTIFIER_GROUP) {
990 						groupp = getgrnam(fieldp);
991 						if (groupp == NULL) {
992 							/* no group? */
993 							error = acl_str_to_id(
994 							    fieldp, &id);
995 						} else
996 							id = groupp->gr_gid;
997 
998 					} else if ((entry_type == ACE_OWNER) ||
999 					    (entry_type ==
1000 					    (ACE_IDENTIFIER_GROUP|ACE_GROUP)) ||
1001 					    (entry_type != ACE_EVERYONE)) {
1002 						error = EACL_FIELD_NOT_BLANK;
1003 					} else {
1004 						error = EACL_ENTRY_ERROR;
1005 					}
1006 
1007 					if (error) {
1008 						free(allocp);
1009 						acl_free(aclp);
1010 						return (error);
1011 					}
1012 				}
1013 			}
1014 		} else {
1015 			id = -1;
1016 		}
1017 
1018 		/* next field: permission */
1019 		fieldp = tp + 1;
1020 		tp = strchr(fieldp, ':');
1021 		if (tp == NULL) {
1022 			free(allocp);
1023 			acl_free(aclp);
1024 			return (EACL_PERM_MASK_ERROR);
1025 		} else
1026 			*tp = '\0';
1027 
1028 		perm = 0;
1029 
1030 		perm_tok = strtok(fieldp, "/");
1031 		if (perm_tok == NULL) {
1032 			perm = 0;
1033 		} else {
1034 			do {
1035 				if (ace_permask(perm_tok, &perm) != 0) {
1036 					free(allocp);
1037 					acl_free(aclp);
1038 					return (EACL_PERM_MASK_ERROR);
1039 				}
1040 			} while (perm_tok = strtok(NULL, "/"));
1041 		}
1042 
1043 		/* grab allow/deny */
1044 		fieldp = tp + 1;
1045 		tp = strchr(fieldp, ':');
1046 		if (tp != NULL)
1047 			*tp = '\0';
1048 
1049 		if (strcmp(fieldp, "allow") == 0)
1050 			type = ACE_ACCESS_ALLOWED_ACE_TYPE;
1051 		else if (strcmp(fieldp, "deny") == 0)
1052 			type = ACE_ACCESS_DENIED_ACE_TYPE;
1053 		else {
1054 			free(allocp);
1055 			acl_free(aclp);
1056 			return (EACL_INVALID_ACCESS_TYPE);
1057 		}
1058 
1059 		/* grab option inherit flags */
1060 
1061 		iflags = 0;
1062 		if (tp != NULL) {
1063 			fieldp = tp + 1;
1064 			if (fieldp != NULL) {
1065 				*tp = '\0';
1066 				str = fieldp;
1067 				if (build_iflags(str, &iflags) != 0) {
1068 					free(allocp);
1069 					acl_free(aclp);
1070 					return (EACL_INHERIT_ERROR);
1071 				}
1072 			} else {
1073 				free(allocp);
1074 				acl_free(aclp);
1075 				return (EACL_UNKNOWN_DATA);
1076 			}
1077 		}
1078 		/* slap fields into ace_t structure */
1079 
1080 		tmpaclp->a_flags = entry_type;
1081 		tmpaclp->a_flags |= iflags;
1082 		tmpaclp->a_who = id;
1083 		tmpaclp->a_access_mask = perm;
1084 		tmpaclp->a_type = type;
1085 		aclimport = nextp;
1086 	}
1087 	free(allocp);
1088 	*ret_aclp = aclp;
1089 	return (0);
1090 }
1091 
1092 char
1093 *acl_totext(acl_t *aclp)
1094 {
1095 	if (aclp == NULL)
1096 		return (NULL);
1097 
1098 	switch (aclp->acl_type) {
1099 	case ACE_T:
1100 		return (ace_acltotext(aclp));
1101 	case ACLENT_T:
1102 		return (acltotext(aclp->acl_aclp, aclp->acl_cnt));
1103 	}
1104 	return (NULL);
1105 }
1106 
1107 int
1108 acl_fromtext(const char *acltextp, acl_t **ret_aclp)
1109 {
1110 	acl_t *aclp;
1111 	char *token;
1112 	char *ptr;
1113 	char *textp;
1114 	enum acl_type flavor;
1115 	int colon_cnt = 0;
1116 	int error;
1117 
1118 	/*
1119 	 * first try and detect what type of acl entries we have
1120 	 *
1121 	 * aclent_t can have 1, 2 or 3 colons
1122 	 * if 3 then must have word default:
1123 	 *
1124 	 * ace_t can have 2, 3 or 4
1125 	 * for 2 then must be owner@, group@ or everyone@
1126 	 */
1127 
1128 	textp = strdup(acltextp);
1129 	if (textp == NULL)
1130 		return (-1);
1131 
1132 	token = strtok(textp, ",");
1133 	if (token == NULL) {
1134 		free(textp);
1135 		return (-1);
1136 	}
1137 
1138 	for (ptr = token; *ptr; ptr++) {
1139 		if (*ptr == ':')
1140 			colon_cnt++;
1141 	}
1142 
1143 	if (colon_cnt == 1 || colon_cnt == 2) {
1144 		if ((strncmp(acltextp, "owner@", 6) == 0) ||
1145 		    (strncmp(acltextp, "group@", 6) == 0) ||
1146 		    (strncmp(acltextp, "everyone@", 9) == 0))
1147 			flavor = ACE_T;
1148 		else
1149 			flavor = ACLENT_T;
1150 	} else if (colon_cnt == 3) {
1151 		ptr = strtok(token, ":");
1152 		if (ptr == NULL) {
1153 			free(textp);
1154 			return (EACL_MISSING_FIELDS);
1155 		} else if (strcmp(ptr, "default") == 0) {
1156 			flavor = ACLENT_T;
1157 		} else {
1158 			flavor = ACE_T;
1159 		}
1160 	} else if (colon_cnt == 4) {
1161 		flavor = ACE_T;
1162 	} else {
1163 		free(textp);
1164 		return (EACL_MISSING_FIELDS);
1165 	}
1166 
1167 
1168 	free(textp);
1169 
1170 	if (flavor == ACLENT_T)
1171 		error = aclent_aclfromtext((char *)acltextp, &aclp);
1172 	else
1173 		error = ace_aclfromtext((char *)acltextp, &aclp);
1174 
1175 	*ret_aclp = aclp;
1176 	return (error);
1177 }
1178