10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
50Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
60Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
70Sstevel@tonic-gate  * with the License.
80Sstevel@tonic-gate  *
90Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
100Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
110Sstevel@tonic-gate  * See the License for the specific language governing permissions
120Sstevel@tonic-gate  * and limitations under the License.
130Sstevel@tonic-gate  *
140Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
150Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
160Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
170Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
180Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
190Sstevel@tonic-gate  *
200Sstevel@tonic-gate  * CDDL HEADER END
210Sstevel@tonic-gate  */
220Sstevel@tonic-gate /*
230Sstevel@tonic-gate  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
240Sstevel@tonic-gate  * Use is subject to license terms.
250Sstevel@tonic-gate  */
260Sstevel@tonic-gate 
270Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
280Sstevel@tonic-gate /*LINTLIBRARY*/
290Sstevel@tonic-gate 
300Sstevel@tonic-gate #include <grp.h>
310Sstevel@tonic-gate #include <pwd.h>
320Sstevel@tonic-gate #include <string.h>
330Sstevel@tonic-gate #include <limits.h>
340Sstevel@tonic-gate #include <stdlib.h>
35*789Sahrens #include <errno.h>
360Sstevel@tonic-gate #include <sys/param.h>
370Sstevel@tonic-gate #include <sys/types.h>
380Sstevel@tonic-gate #include <sys/acl.h>
39*789Sahrens #include <aclutils.h>
40*789Sahrens #include <libintl.h>
41*789Sahrens 
42*789Sahrens 
43*789Sahrens extern acl_t *acl_alloc(enum acl_type);
440Sstevel@tonic-gate 
450Sstevel@tonic-gate /*
460Sstevel@tonic-gate  * acltotext() converts each ACL entry to look like this:
470Sstevel@tonic-gate  *
480Sstevel@tonic-gate  *    entry_type:uid^gid^name:perms
490Sstevel@tonic-gate  *
500Sstevel@tonic-gate  * The maximum length of entry_type is 14 ("defaultgroup::" and
510Sstevel@tonic-gate  * "defaultother::") hence ENTRYTYPELEN is set to 14.
520Sstevel@tonic-gate  *
530Sstevel@tonic-gate  * The max length of a uid^gid^name entry (in theory) is 8, hence we use
540Sstevel@tonic-gate  * LOGNAME_MAX.
550Sstevel@tonic-gate  *
560Sstevel@tonic-gate  * The length of a perms entry is 4 to allow for the comma appended to each
570Sstevel@tonic-gate  * to each acl entry.  Hence PERMS is set to 4.
580Sstevel@tonic-gate  */
590Sstevel@tonic-gate 
600Sstevel@tonic-gate #define	ENTRYTYPELEN	14
610Sstevel@tonic-gate #define	PERMS		4
620Sstevel@tonic-gate #define	ACL_ENTRY_SIZE	(ENTRYTYPELEN + LOGNAME_MAX + PERMS)
630Sstevel@tonic-gate 
640Sstevel@tonic-gate struct dynaclstr {
650Sstevel@tonic-gate 	size_t bufsize;		/* current size of aclexport */
660Sstevel@tonic-gate 	char *aclexport;
670Sstevel@tonic-gate };
680Sstevel@tonic-gate 
690Sstevel@tonic-gate static char *strappend(char *, char *);
700Sstevel@tonic-gate static char *convert_perm(char *, o_mode_t);
710Sstevel@tonic-gate static int increase_length(struct dynaclstr *, size_t);
720Sstevel@tonic-gate 
73*789Sahrens static int
74*789Sahrens acl_str_to_id(char *str, int *id)
75*789Sahrens {
76*789Sahrens 	char *end;
77*789Sahrens 	uid_t value;
78*789Sahrens 
79*789Sahrens 	value = strtol(str, &end, 10);
80*789Sahrens 
81*789Sahrens 	if (errno != 0 || *end != '\0')
82*789Sahrens 		return (EACL_INVALID_USER_GROUP);
83*789Sahrens 
84*789Sahrens 	*id = value;
85*789Sahrens 
86*789Sahrens 	return (0);
87*789Sahrens }
880Sstevel@tonic-gate 
890Sstevel@tonic-gate /*
900Sstevel@tonic-gate  * Convert internal acl representation to external representation.
910Sstevel@tonic-gate  *
920Sstevel@tonic-gate  * The length of a non-owning user name or non-owning group name ie entries
930Sstevel@tonic-gate  * of type DEF_USER, USER, DEF_GROUP or GROUP, can exceed LOGNAME_MAX.  We
940Sstevel@tonic-gate  * thus check the length of these entries, and if greater than LOGNAME_MAX,
950Sstevel@tonic-gate  * we realloc() via increase_length().
960Sstevel@tonic-gate  *
970Sstevel@tonic-gate  * The LOGNAME_MAX, ENTRYTYPELEN and PERMS limits are otherwise always
980Sstevel@tonic-gate  * adhered to.
990Sstevel@tonic-gate  */
1000Sstevel@tonic-gate char *
1010Sstevel@tonic-gate acltotext(aclent_t *aclp, int aclcnt)
1020Sstevel@tonic-gate {
1030Sstevel@tonic-gate 	char		*aclexport;
1040Sstevel@tonic-gate 	char		*where;
1050Sstevel@tonic-gate 	struct group	*groupp;
1060Sstevel@tonic-gate 	struct passwd	*passwdp;
1070Sstevel@tonic-gate 	struct dynaclstr *dstr;
1080Sstevel@tonic-gate 	int		i, rtn;
1090Sstevel@tonic-gate 	size_t		excess = 0;
1100Sstevel@tonic-gate 
1110Sstevel@tonic-gate 	if (aclp == NULL)
1120Sstevel@tonic-gate 		return (NULL);
1130Sstevel@tonic-gate 	if ((dstr = malloc(sizeof (struct dynaclstr))) == NULL)
1140Sstevel@tonic-gate 		return (NULL);
1150Sstevel@tonic-gate 	dstr->bufsize = aclcnt * ACL_ENTRY_SIZE;
1160Sstevel@tonic-gate 	if ((dstr->aclexport = malloc(dstr->bufsize)) == NULL) {
1170Sstevel@tonic-gate 		free(dstr);
1180Sstevel@tonic-gate 		return (NULL);
1190Sstevel@tonic-gate 	}
1200Sstevel@tonic-gate 	*dstr->aclexport = '\0';
1210Sstevel@tonic-gate 	where = dstr->aclexport;
1220Sstevel@tonic-gate 
1230Sstevel@tonic-gate 	for (i = 0; i < aclcnt; i++, aclp++) {
1240Sstevel@tonic-gate 		switch (aclp->a_type) {
1250Sstevel@tonic-gate 		case DEF_USER_OBJ:
1260Sstevel@tonic-gate 		case USER_OBJ:
1270Sstevel@tonic-gate 			if (aclp->a_type == USER_OBJ)
1280Sstevel@tonic-gate 				where = strappend(where, "user::");
1290Sstevel@tonic-gate 			else
1300Sstevel@tonic-gate 				where = strappend(where, "defaultuser::");
1310Sstevel@tonic-gate 			where = convert_perm(where, aclp->a_perm);
1320Sstevel@tonic-gate 			break;
1330Sstevel@tonic-gate 		case DEF_USER:
1340Sstevel@tonic-gate 		case USER:
1350Sstevel@tonic-gate 			if (aclp->a_type == USER)
1360Sstevel@tonic-gate 				where = strappend(where, "user:");
1370Sstevel@tonic-gate 			else
1380Sstevel@tonic-gate 				where = strappend(where, "defaultuser:");
1390Sstevel@tonic-gate 			passwdp = getpwuid(aclp->a_id);
1400Sstevel@tonic-gate 			if (passwdp == (struct passwd *)NULL) {
1410Sstevel@tonic-gate 				/* put in uid instead */
1420Sstevel@tonic-gate 				(void) sprintf(where, "%d", aclp->a_id);
1430Sstevel@tonic-gate 			} else {
1440Sstevel@tonic-gate 				excess = strlen(passwdp->pw_name) - LOGNAME_MAX;
1450Sstevel@tonic-gate 				if (excess > 0) {
1460Sstevel@tonic-gate 					rtn = increase_length(dstr, excess);
1470Sstevel@tonic-gate 					if (rtn == 1) {
1480Sstevel@tonic-gate 						/* reset where */
1490Sstevel@tonic-gate 						where = dstr->aclexport +
1500Sstevel@tonic-gate 							strlen(dstr->aclexport);
1510Sstevel@tonic-gate 					} else {
1520Sstevel@tonic-gate 						free(dstr->aclexport);
1530Sstevel@tonic-gate 						free(dstr);
1540Sstevel@tonic-gate 						return (NULL);
1550Sstevel@tonic-gate 					}
1560Sstevel@tonic-gate 				}
1570Sstevel@tonic-gate 				where = strappend(where, passwdp->pw_name);
1580Sstevel@tonic-gate 			}
1590Sstevel@tonic-gate 			where = strappend(where, ":");
1600Sstevel@tonic-gate 			where = convert_perm(where, aclp->a_perm);
1610Sstevel@tonic-gate 			break;
1620Sstevel@tonic-gate 		case DEF_GROUP_OBJ:
1630Sstevel@tonic-gate 		case GROUP_OBJ:
1640Sstevel@tonic-gate 			if (aclp->a_type == GROUP_OBJ)
1650Sstevel@tonic-gate 				where = strappend(where, "group::");
1660Sstevel@tonic-gate 			else
1670Sstevel@tonic-gate 				where = strappend(where, "defaultgroup::");
1680Sstevel@tonic-gate 			where = convert_perm(where, aclp->a_perm);
1690Sstevel@tonic-gate 			break;
1700Sstevel@tonic-gate 		case DEF_GROUP:
1710Sstevel@tonic-gate 		case GROUP:
1720Sstevel@tonic-gate 			if (aclp->a_type == GROUP)
1730Sstevel@tonic-gate 				where = strappend(where, "group:");
1740Sstevel@tonic-gate 			else
1750Sstevel@tonic-gate 				where = strappend(where, "defaultgroup:");
1760Sstevel@tonic-gate 			groupp = getgrgid(aclp->a_id);
1770Sstevel@tonic-gate 			if (groupp == (struct group *)NULL) {
1780Sstevel@tonic-gate 				/* put in gid instead */
1790Sstevel@tonic-gate 				(void) sprintf(where, "%d", aclp->a_id);
1800Sstevel@tonic-gate 			} else {
1810Sstevel@tonic-gate 				excess = strlen(groupp->gr_name) - LOGNAME_MAX;
1820Sstevel@tonic-gate 				if (excess > 0) {
1830Sstevel@tonic-gate 					rtn = increase_length(dstr, excess);
1840Sstevel@tonic-gate 					if (rtn == 1) {
1850Sstevel@tonic-gate 						/* reset where */
1860Sstevel@tonic-gate 						where = dstr->aclexport +
1870Sstevel@tonic-gate 							strlen(dstr->aclexport);
1880Sstevel@tonic-gate 					} else {
1890Sstevel@tonic-gate 						free(dstr->aclexport);
1900Sstevel@tonic-gate 						free(dstr);
1910Sstevel@tonic-gate 						return (NULL);
1920Sstevel@tonic-gate 					}
1930Sstevel@tonic-gate 				}
1940Sstevel@tonic-gate 				where = strappend(where, groupp->gr_name);
1950Sstevel@tonic-gate 			}
1960Sstevel@tonic-gate 			where = strappend(where, ":");
1970Sstevel@tonic-gate 			where = convert_perm(where, aclp->a_perm);
1980Sstevel@tonic-gate 			break;
1990Sstevel@tonic-gate 		case DEF_CLASS_OBJ:
2000Sstevel@tonic-gate 		case CLASS_OBJ:
2010Sstevel@tonic-gate 			if (aclp->a_type == CLASS_OBJ)
2020Sstevel@tonic-gate 				where = strappend(where, "mask:");
2030Sstevel@tonic-gate 			else
2040Sstevel@tonic-gate 				where = strappend(where, "defaultmask:");
2050Sstevel@tonic-gate 			where = convert_perm(where, aclp->a_perm);
2060Sstevel@tonic-gate 			break;
2070Sstevel@tonic-gate 		case DEF_OTHER_OBJ:
2080Sstevel@tonic-gate 		case OTHER_OBJ:
2090Sstevel@tonic-gate 			if (aclp->a_type == OTHER_OBJ)
2100Sstevel@tonic-gate 				where = strappend(where, "other:");
2110Sstevel@tonic-gate 			else
2120Sstevel@tonic-gate 				where = strappend(where, "defaultother:");
2130Sstevel@tonic-gate 			where = convert_perm(where, aclp->a_perm);
2140Sstevel@tonic-gate 			break;
2150Sstevel@tonic-gate 		default:
2160Sstevel@tonic-gate 			free(dstr->aclexport);
2170Sstevel@tonic-gate 			free(dstr);
2180Sstevel@tonic-gate 			return (NULL);
2190Sstevel@tonic-gate 
2200Sstevel@tonic-gate 		}
2210Sstevel@tonic-gate 		if (i < aclcnt - 1)
2220Sstevel@tonic-gate 			where = strappend(where, ",");
2230Sstevel@tonic-gate 	}
2240Sstevel@tonic-gate 	aclexport = dstr->aclexport;
2250Sstevel@tonic-gate 	free(dstr);
2260Sstevel@tonic-gate 	return (aclexport);
2270Sstevel@tonic-gate }
2280Sstevel@tonic-gate 
2290Sstevel@tonic-gate /*
2300Sstevel@tonic-gate  * Convert external acl representation to internal representation.
2310Sstevel@tonic-gate  * The accepted syntax is: <acl_entry>[,<acl_entry>]*[,]
2320Sstevel@tonic-gate  * The comma at the end is not prescribed by the man pages.
2330Sstevel@tonic-gate  * But it is needed not to break the old programs.
2340Sstevel@tonic-gate  */
235*789Sahrens static int
236*789Sahrens aclent_aclfromtext(char *aclstr, acl_t **ret_aclp)
2370Sstevel@tonic-gate {
2380Sstevel@tonic-gate 	char		*fieldp;
2390Sstevel@tonic-gate 	char		*tp;
2400Sstevel@tonic-gate 	char		*nextp;
2410Sstevel@tonic-gate 	char		*allocp;
2420Sstevel@tonic-gate 	char		*aclimport;
2430Sstevel@tonic-gate 	int		entry_type;
2440Sstevel@tonic-gate 	int		id;
2450Sstevel@tonic-gate 	int		len;
246*789Sahrens 	int		error;
2470Sstevel@tonic-gate 	o_mode_t	perm;
2480Sstevel@tonic-gate 	aclent_t	*tmpaclp;
249*789Sahrens 	acl_t		*aclp;
2500Sstevel@tonic-gate 	struct group	*groupp;
2510Sstevel@tonic-gate 	struct passwd	*passwdp;
2520Sstevel@tonic-gate 
2530Sstevel@tonic-gate 	aclp = NULL;
2540Sstevel@tonic-gate 
2550Sstevel@tonic-gate 	if (! aclstr)
2560Sstevel@tonic-gate 		return (NULL);
2570Sstevel@tonic-gate 
258*789Sahrens 	aclp = acl_alloc(ACLENT_T);
259*789Sahrens 	if (aclp == NULL) {
260*789Sahrens 		return (EACL_MEM_ERROR);
261*789Sahrens 	}
262*789Sahrens 
263*789Sahrens 	*ret_aclp = NULL;
264*789Sahrens 
2650Sstevel@tonic-gate 	len = strlen(aclstr);
2660Sstevel@tonic-gate 
2670Sstevel@tonic-gate 	if ((aclimport = allocp = strdup(aclstr)) == NULL) {
268*789Sahrens 		return (EACL_MEM_ERROR);
2690Sstevel@tonic-gate 	}
2700Sstevel@tonic-gate 
2710Sstevel@tonic-gate 	if (aclimport[len - 1] == ',')
2720Sstevel@tonic-gate 		aclimport[len - 1] = '\0';
2730Sstevel@tonic-gate 
2740Sstevel@tonic-gate 	for (; aclimport; ) {
2750Sstevel@tonic-gate 		/* look for an ACL entry */
2760Sstevel@tonic-gate 		tp = strchr(aclimport, ',');
2770Sstevel@tonic-gate 		if (tp == NULL) {
2780Sstevel@tonic-gate 			nextp = NULL;
2790Sstevel@tonic-gate 		} else {
2800Sstevel@tonic-gate 			*tp = '\0';
2810Sstevel@tonic-gate 			nextp = tp + 1;
2820Sstevel@tonic-gate 		}
2830Sstevel@tonic-gate 
284*789Sahrens 		aclp->acl_cnt += 1;
2850Sstevel@tonic-gate 
2860Sstevel@tonic-gate 		/*
2870Sstevel@tonic-gate 		 * get additional memory:
2880Sstevel@tonic-gate 		 * can be more efficient by allocating a bigger block
2890Sstevel@tonic-gate 		 * each time.
2900Sstevel@tonic-gate 		 */
291*789Sahrens 		if (aclp->acl_cnt > 1)
292*789Sahrens 			tmpaclp = (aclent_t *)realloc(aclp->acl_aclp,
293*789Sahrens 			    sizeof (aclent_t) * (aclp->acl_cnt));
2940Sstevel@tonic-gate 		else
2950Sstevel@tonic-gate 			tmpaclp = (aclent_t *)malloc(sizeof (aclent_t));
2960Sstevel@tonic-gate 		if (tmpaclp == NULL) {
2970Sstevel@tonic-gate 			free(allocp);
298*789Sahrens 			acl_free(aclp);
299*789Sahrens 			return (EACL_MEM_ERROR);
3000Sstevel@tonic-gate 		}
301*789Sahrens 		aclp->acl_aclp = tmpaclp;
302*789Sahrens 		tmpaclp = (aclent_t *)aclp->acl_aclp + (aclp->acl_cnt - 1);
3030Sstevel@tonic-gate 
3040Sstevel@tonic-gate 		/* look for entry type field */
3050Sstevel@tonic-gate 		tp = strchr(aclimport, ':');
3060Sstevel@tonic-gate 		if (tp == NULL) {
307*789Sahrens 			free(allocp);
308*789Sahrens 			if (aclp)
309*789Sahrens 				acl_free(aclp);
310*789Sahrens 			return (EACL_ENTRY_ERROR);
3110Sstevel@tonic-gate 		} else
3120Sstevel@tonic-gate 			*tp = '\0';
3130Sstevel@tonic-gate 		if (strcmp(aclimport, "user") == 0) {
3140Sstevel@tonic-gate 			if (*(tp+1) == ':')
3150Sstevel@tonic-gate 				entry_type = USER_OBJ;
3160Sstevel@tonic-gate 			else
3170Sstevel@tonic-gate 				entry_type = USER;
3180Sstevel@tonic-gate 		} else if (strcmp(aclimport, "group") == 0) {
3190Sstevel@tonic-gate 			if (*(tp+1) == ':')
3200Sstevel@tonic-gate 				entry_type = GROUP_OBJ;
3210Sstevel@tonic-gate 			else
3220Sstevel@tonic-gate 				entry_type = GROUP;
3230Sstevel@tonic-gate 		} else if (strcmp(aclimport, "other") == 0)
3240Sstevel@tonic-gate 			entry_type = OTHER_OBJ;
3250Sstevel@tonic-gate 		else if (strcmp(aclimport, "mask") == 0)
3260Sstevel@tonic-gate 			entry_type = CLASS_OBJ;
3270Sstevel@tonic-gate 		else if (strcmp(aclimport, "defaultuser") == 0) {
3280Sstevel@tonic-gate 			if (*(tp+1) == ':')
3290Sstevel@tonic-gate 				entry_type = DEF_USER_OBJ;
3300Sstevel@tonic-gate 			else
3310Sstevel@tonic-gate 				entry_type = DEF_USER;
3320Sstevel@tonic-gate 		} else if (strcmp(aclimport, "defaultgroup") == 0) {
3330Sstevel@tonic-gate 			if (*(tp+1) == ':')
3340Sstevel@tonic-gate 				entry_type = DEF_GROUP_OBJ;
3350Sstevel@tonic-gate 			else
3360Sstevel@tonic-gate 				entry_type = DEF_GROUP;
3370Sstevel@tonic-gate 		} else if (strcmp(aclimport, "defaultmask") == 0)
3380Sstevel@tonic-gate 			entry_type = DEF_CLASS_OBJ;
3390Sstevel@tonic-gate 		else if (strcmp(aclimport, "defaultother") == 0)
3400Sstevel@tonic-gate 			entry_type = DEF_OTHER_OBJ;
3410Sstevel@tonic-gate 		else {
342*789Sahrens 			free(allocp);
343*789Sahrens 			acl_free(aclp);
344*789Sahrens 			return (EACL_ENTRY_ERROR);
3450Sstevel@tonic-gate 		}
3460Sstevel@tonic-gate 
3470Sstevel@tonic-gate 		/* look for user/group name */
3480Sstevel@tonic-gate 		if (entry_type != CLASS_OBJ && entry_type != OTHER_OBJ &&
3490Sstevel@tonic-gate 		    entry_type != DEF_CLASS_OBJ &&
3500Sstevel@tonic-gate 		    entry_type != DEF_OTHER_OBJ) {
3510Sstevel@tonic-gate 			fieldp = tp + 1;
3520Sstevel@tonic-gate 			tp = strchr(fieldp, ':');
3530Sstevel@tonic-gate 			if (tp == NULL) {
354*789Sahrens 				free(allocp);
355*789Sahrens 				acl_free(aclp);
356*789Sahrens 				return (EACL_INVALID_USER_GROUP);
3570Sstevel@tonic-gate 			} else
3580Sstevel@tonic-gate 				*tp = '\0';
3590Sstevel@tonic-gate 			if (fieldp != tp) {
3600Sstevel@tonic-gate 				/*
3610Sstevel@tonic-gate 				 * The second field could be empty. We only care
3620Sstevel@tonic-gate 				 * when the field has user/group name.
3630Sstevel@tonic-gate 				 */
3640Sstevel@tonic-gate 				if (entry_type == USER ||
3650Sstevel@tonic-gate 				    entry_type == DEF_USER) {
3660Sstevel@tonic-gate 					/*
3670Sstevel@tonic-gate 					 * The reentrant interface getpwnam_r()
3680Sstevel@tonic-gate 					 * is uncommitted and subject to
3690Sstevel@tonic-gate 					 * change. Use the friendlier interface
3700Sstevel@tonic-gate 					 * getpwnam().
3710Sstevel@tonic-gate 					 */
372*789Sahrens 					error = 0;
3730Sstevel@tonic-gate 					passwdp = getpwnam(fieldp);
3740Sstevel@tonic-gate 					if (passwdp == NULL) {
375*789Sahrens 						error = acl_str_to_id(fieldp,
376*789Sahrens 						    &id);
377*789Sahrens 					} else {
378*789Sahrens 						id = passwdp->pw_uid;
3790Sstevel@tonic-gate 					}
380*789Sahrens 
381*789Sahrens 					if (error) {
382*789Sahrens 						free(allocp);
383*789Sahrens 						acl_free(aclp);
384*789Sahrens 						return (error);
385*789Sahrens 					}
386*789Sahrens 
3870Sstevel@tonic-gate 				} else {
388*789Sahrens 					error = 0;
3890Sstevel@tonic-gate 					if (entry_type == GROUP ||
3900Sstevel@tonic-gate 					    entry_type == DEF_GROUP) {
3910Sstevel@tonic-gate 						groupp = getgrnam(fieldp);
3920Sstevel@tonic-gate 						if (groupp == NULL) {
393*789Sahrens 							error = acl_str_to_id(
394*789Sahrens 							    fieldp, &id);
3950Sstevel@tonic-gate 						}
396*789Sahrens 						if (error == 0)
3970Sstevel@tonic-gate 							id = groupp->gr_gid;
398*789Sahrens 					}
399*789Sahrens 					if (error) {
400*789Sahrens 						free(allocp);
401*789Sahrens 						acl_free(aclp);
402*789Sahrens 						return (error);
4030Sstevel@tonic-gate 					}
4040Sstevel@tonic-gate 				}
4050Sstevel@tonic-gate 			} else {
4060Sstevel@tonic-gate 				/*
4070Sstevel@tonic-gate 				 * The second field is empty.
4080Sstevel@tonic-gate 				 * Treat it as undefined (-1)
4090Sstevel@tonic-gate 				 */
4100Sstevel@tonic-gate 				id = -1;
4110Sstevel@tonic-gate 			}
4120Sstevel@tonic-gate 		} else {
4130Sstevel@tonic-gate 			/*
4140Sstevel@tonic-gate 			 * Let's not break the old applications
4150Sstevel@tonic-gate 			 * that use mask::rwx, other::rwx format,
4160Sstevel@tonic-gate 			 * though they violate the man pages.
4170Sstevel@tonic-gate 			 */
4180Sstevel@tonic-gate 			if (*(tp + 1) == ':')
4190Sstevel@tonic-gate 				*++tp = 0;
4200Sstevel@tonic-gate 		}
4210Sstevel@tonic-gate 
4220Sstevel@tonic-gate 		/* next field: permission */
4230Sstevel@tonic-gate 		fieldp = tp + 1;
4240Sstevel@tonic-gate 		if (strlen(fieldp) != 3) {
4250Sstevel@tonic-gate 			/*  not "rwx" format */
426*789Sahrens 			free(allocp);
427*789Sahrens 			acl_free(aclp);
428*789Sahrens 			return (EACL_PERM_MASK_ERROR);
4290Sstevel@tonic-gate 		} else {
4300Sstevel@tonic-gate 			char	s[] = "rwx";
4310Sstevel@tonic-gate 			int	mask = 0x04;
4320Sstevel@tonic-gate 			int	i;
4330Sstevel@tonic-gate 			perm = 0;
4340Sstevel@tonic-gate 
4350Sstevel@tonic-gate 			for (i = 0; i < 3; i++, mask /= 2) {
4360Sstevel@tonic-gate 				if (fieldp[i] == s[i])
4370Sstevel@tonic-gate 					perm |= mask;
4380Sstevel@tonic-gate 				else if (fieldp[i] != '-') {
439*789Sahrens 					free(allocp);
440*789Sahrens 					acl_free(aclp);
441*789Sahrens 					return (EACL_PERM_MASK_ERROR);
4420Sstevel@tonic-gate 				}
4430Sstevel@tonic-gate 			}
4440Sstevel@tonic-gate 		}
4450Sstevel@tonic-gate 
4460Sstevel@tonic-gate 		tmpaclp->a_type = entry_type;
4470Sstevel@tonic-gate 		tmpaclp->a_id = id;
4480Sstevel@tonic-gate 		tmpaclp->a_perm = perm;
4490Sstevel@tonic-gate 		aclimport = nextp;
4500Sstevel@tonic-gate 	}
4510Sstevel@tonic-gate 	free(allocp);
452*789Sahrens 	*ret_aclp = aclp;
453*789Sahrens 	return (0);
4540Sstevel@tonic-gate }
4550Sstevel@tonic-gate 
456*789Sahrens aclent_t *
457*789Sahrens aclfromtext(char *aclstr, int *aclcnt)
458*789Sahrens {
459*789Sahrens 	acl_t *aclp;
460*789Sahrens 	aclent_t *aclentp;
461*789Sahrens 	int error;
462*789Sahrens 
463*789Sahrens 	error = aclent_aclfromtext(aclstr, &aclp);
464*789Sahrens 	if (error)
465*789Sahrens 		return (NULL);
466*789Sahrens 
467*789Sahrens 	aclentp = aclp->acl_aclp;
468*789Sahrens 	aclp->acl_aclp = NULL;
469*789Sahrens 	acl_free(aclp);
470*789Sahrens 
471*789Sahrens 	*aclcnt = aclp->acl_cnt;
472*789Sahrens 	return (aclentp);
473*789Sahrens }
474*789Sahrens 
475*789Sahrens 
4760Sstevel@tonic-gate static char *
4770Sstevel@tonic-gate strappend(char *where, char *newstr)
4780Sstevel@tonic-gate {
4790Sstevel@tonic-gate 	(void) strcat(where, newstr);
4800Sstevel@tonic-gate 	return (where + strlen(newstr));
4810Sstevel@tonic-gate }
4820Sstevel@tonic-gate 
4830Sstevel@tonic-gate static char *
4840Sstevel@tonic-gate convert_perm(char *where, o_mode_t perm)
4850Sstevel@tonic-gate {
4860Sstevel@tonic-gate 	if (perm & 04)
4870Sstevel@tonic-gate 		where = strappend(where, "r");
4880Sstevel@tonic-gate 	else
4890Sstevel@tonic-gate 		where = strappend(where, "-");
4900Sstevel@tonic-gate 	if (perm & 02)
4910Sstevel@tonic-gate 		where = strappend(where, "w");
4920Sstevel@tonic-gate 	else
4930Sstevel@tonic-gate 		where = strappend(where, "-");
4940Sstevel@tonic-gate 	if (perm & 01)
4950Sstevel@tonic-gate 		where = strappend(where, "x");
4960Sstevel@tonic-gate 	else
4970Sstevel@tonic-gate 		where = strappend(where, "-");
4980Sstevel@tonic-gate 	/* perm is the last field */
4990Sstevel@tonic-gate 	return (where);
5000Sstevel@tonic-gate }
5010Sstevel@tonic-gate 
502*789Sahrens static char *
503*789Sahrens ace_convert_perm(char *where, mode_t perm, int isdir, int iflags)
504*789Sahrens {
505*789Sahrens 	char *start = where;
506*789Sahrens 
507*789Sahrens 	/*
508*789Sahrens 	 * The following mneumonics all have the
509*789Sahrens 	 * same value.  The only difference is the
510*789Sahrens 	 * first value is for files and second for directories
511*789Sahrens 	 * ACE_READ_DATA/ACE_LIST_DIRECTORY
512*789Sahrens 	 * ACE_WRITE_DATA/ACE_ADD_FILE
513*789Sahrens 	 * ACE_APPEND_DATA/ACE_ADD_SUBDIRECTORY
514*789Sahrens 	 */
515*789Sahrens 
516*789Sahrens 	/*
517*789Sahrens 	 * If ACE is a directory, but inheritance indicates its
518*789Sahrens 	 * for a file then print permissions for file rather than
519*789Sahrens 	 * dir.
520*789Sahrens 	 */
521*789Sahrens 	if (isdir) {
522*789Sahrens 		if (perm & ACE_LIST_DIRECTORY) {
523*789Sahrens 			if (iflags == ACE_FILE_INHERIT_ACE)
524*789Sahrens 				where = strappend(where, "read_data/");
525*789Sahrens 			else
526*789Sahrens 				where = strappend(where,
527*789Sahrens 				    "list_directory/read_data/");
528*789Sahrens 		}
529*789Sahrens 		if (perm & ACE_ADD_FILE) {
530*789Sahrens 			if (iflags == ACE_FILE_INHERIT_ACE)
531*789Sahrens 				where = strappend(where, "write_data/");
532*789Sahrens 			else
533*789Sahrens 				where = strappend(where,
534*789Sahrens 				    "add_file/write_data/");
535*789Sahrens 		}
536*789Sahrens 		if (perm & ACE_ADD_SUBDIRECTORY) {
537*789Sahrens 			if (iflags == ACE_FILE_INHERIT_ACE)
538*789Sahrens 				where = strappend(where, "append_data/");
539*789Sahrens 			else
540*789Sahrens 				where = strappend(where,
541*789Sahrens 				    "add_subdirectory/append_data/");
542*789Sahrens 		}
543*789Sahrens 	} else {
544*789Sahrens 		if (perm & ACE_READ_DATA)
545*789Sahrens 			where = strappend(where, "read_data/");
546*789Sahrens 		if (perm & ACE_WRITE_DATA)
547*789Sahrens 			where = strappend(where, "write_data/");
548*789Sahrens 		if (perm & ACE_APPEND_DATA)
549*789Sahrens 			where = strappend(where, "append_data/");
550*789Sahrens 	}
551*789Sahrens 	if (perm & ACE_READ_NAMED_ATTRS)
552*789Sahrens 		where = strappend(where, "read_xattr/");
553*789Sahrens 	if (perm & ACE_WRITE_NAMED_ATTRS)
554*789Sahrens 		where = strappend(where, "write_xattr/");
555*789Sahrens 	if (perm & ACE_EXECUTE)
556*789Sahrens 		where = strappend(where, "execute/");
557*789Sahrens 	if (perm & ACE_DELETE_CHILD)
558*789Sahrens 		where = strappend(where, "delete_child/");
559*789Sahrens 	if (perm & ACE_READ_ATTRIBUTES)
560*789Sahrens 		where = strappend(where, "read_attributes/");
561*789Sahrens 	if (perm & ACE_WRITE_ATTRIBUTES)
562*789Sahrens 		where = strappend(where, "write_attributes/");
563*789Sahrens 	if (perm & ACE_DELETE)
564*789Sahrens 		where = strappend(where, "delete/");
565*789Sahrens 	if (perm & ACE_READ_ACL)
566*789Sahrens 		where = strappend(where, "read_acl/");
567*789Sahrens 	if (perm & ACE_WRITE_ACL)
568*789Sahrens 		where = strappend(where, "write_acl/");
569*789Sahrens 	if (perm & ACE_WRITE_OWNER)
570*789Sahrens 		where = strappend(where, "write_owner/");
571*789Sahrens 	if (perm & ACE_SYNCHRONIZE)
572*789Sahrens 		where = strappend(where, "synchronize");
573*789Sahrens 
574*789Sahrens 	if (start[strlen(start) - 1] == '/') {
575*789Sahrens 		start[strlen(start) - 1] = '\0';
576*789Sahrens 		where = start + strlen(start);
577*789Sahrens 	}
578*789Sahrens 	return (where);
579*789Sahrens }
580*789Sahrens 
581*789Sahrens int
582*789Sahrens ace_permask(char *perm_tok, int *perm)
583*789Sahrens {
584*789Sahrens 	if (strcmp(perm_tok, "read_data") == 0)
585*789Sahrens 		*perm |= ACE_READ_DATA;
586*789Sahrens 	else if (strcmp(perm_tok, "list_directory") == 0)
587*789Sahrens 		*perm |= ACE_LIST_DIRECTORY;
588*789Sahrens 	else if (strcmp(perm_tok, "write_data") == 0)
589*789Sahrens 		*perm |= ACE_WRITE_DATA;
590*789Sahrens 	else if (strcmp(perm_tok, "add_file") == 0)
591*789Sahrens 		*perm |= ACE_ADD_FILE;
592*789Sahrens 	else if (strcmp(perm_tok, "append_data") == 0)
593*789Sahrens 		*perm |= ACE_APPEND_DATA;
594*789Sahrens 	else if (strcmp(perm_tok, "add_subdirectory") == 0)
595*789Sahrens 		*perm |= ACE_ADD_SUBDIRECTORY;
596*789Sahrens 	else if (strcmp(perm_tok, "read_xattr") == 0)
597*789Sahrens 		*perm |= ACE_READ_NAMED_ATTRS;
598*789Sahrens 	else if (strcmp(perm_tok, "write_xattr") == 0)
599*789Sahrens 		*perm |= ACE_WRITE_NAMED_ATTRS;
600*789Sahrens 	else if (strcmp(perm_tok, "execute") == 0)
601*789Sahrens 		*perm |= ACE_EXECUTE;
602*789Sahrens 	else if (strcmp(perm_tok, "delete_child") == 0)
603*789Sahrens 		*perm |= ACE_DELETE_CHILD;
604*789Sahrens 	else if (strcmp(perm_tok, "read_attributes") == 0)
605*789Sahrens 		*perm |= ACE_READ_ATTRIBUTES;
606*789Sahrens 	else if (strcmp(perm_tok, "write_attributes") == 0)
607*789Sahrens 		*perm |= ACE_WRITE_ATTRIBUTES;
608*789Sahrens 	else if (strcmp(perm_tok, "delete") == 0)
609*789Sahrens 		*perm |= ACE_DELETE;
610*789Sahrens 	else if (strcmp(perm_tok, "read_acl") == 0)
611*789Sahrens 		*perm |= ACE_READ_ACL;
612*789Sahrens 	else if (strcmp(perm_tok, "write_acl") == 0)
613*789Sahrens 		*perm |= ACE_WRITE_ACL;
614*789Sahrens 	else if (strcmp(perm_tok, "write_owner") == 0)
615*789Sahrens 		*perm |= ACE_WRITE_OWNER;
616*789Sahrens 	else if (strcmp(perm_tok, "synchronize") == 0)
617*789Sahrens 		*perm |= ACE_SYNCHRONIZE;
618*789Sahrens 	else {
619*789Sahrens 		return (1);
620*789Sahrens 	}
621*789Sahrens 
622*789Sahrens 	return (0);
623*789Sahrens }
624*789Sahrens 
6250Sstevel@tonic-gate /*
6260Sstevel@tonic-gate  * Callers should check the return code as this routine may change the string
6270Sstevel@tonic-gate  * pointer in dynaclstr.
6280Sstevel@tonic-gate  */
6290Sstevel@tonic-gate static int
6300Sstevel@tonic-gate increase_length(struct dynaclstr *dacl, size_t increase)
6310Sstevel@tonic-gate {
6320Sstevel@tonic-gate 	char *tptr;
6330Sstevel@tonic-gate 	size_t newsize;
6340Sstevel@tonic-gate 
6350Sstevel@tonic-gate 	newsize = dacl->bufsize + increase;
6360Sstevel@tonic-gate 	tptr = realloc(dacl->aclexport, newsize);
6370Sstevel@tonic-gate 	if (tptr != NULL) {
6380Sstevel@tonic-gate 		dacl->aclexport = tptr;
6390Sstevel@tonic-gate 		dacl->bufsize = newsize;
6400Sstevel@tonic-gate 		return (1);
6410Sstevel@tonic-gate 	} else
6420Sstevel@tonic-gate 		return (0);
6430Sstevel@tonic-gate }
644*789Sahrens 
645*789Sahrens /*
646*789Sahrens  * ace_acltotext() conver each ace formatted acl to look like this:
647*789Sahrens  *
648*789Sahrens  * entry_type:uid^gid^name:perms:allow^deny[:flags][,]
649*789Sahrens  *
650*789Sahrens  * The maximum length of entry_type is 5 ("group")
651*789Sahrens  *
652*789Sahrens  * The max length of a uid^gid^name entry (in theory) is 8, hence we use
653*789Sahrens  * LOGNAME_MAX.
654*789Sahrens  *
655*789Sahrens  * The length of a perms entry is 144 i.e read_data/write_data...
656*789Sahrens  * to each acl entry.
657*789Sahrens  *
658*789Sahrens  * iflags: file_inherit/dir_inherit/inherit_only/no_propagate
659*789Sahrens  *
660*789Sahrens  */
661*789Sahrens 
662*789Sahrens #define	ACE_ENTRYTYPLEN		6
663*789Sahrens #define	IFLAGS_SIZE		51
664*789Sahrens #define	ACCESS_TYPE_SIZE	5
665*789Sahrens #define	COLON_CNT		3
666*789Sahrens #define	PERMS_LEN		216
667*789Sahrens #define	ACE_ENTRY_SIZE	(ACE_ENTRYTYPLEN + LOGNAME_MAX + PERMS_LEN +\
668*789Sahrens     ACCESS_TYPE_SIZE + IFLAGS_SIZE + COLON_CNT)
669*789Sahrens 
670*789Sahrens static char *
671*789Sahrens ace_acltotext(acl_t *aceaclp)
672*789Sahrens {
673*789Sahrens 	ace_t		*aclp = aceaclp->acl_aclp;
674*789Sahrens 	int		aclcnt = aceaclp->acl_cnt;
675*789Sahrens 	char		*aclexport;
676*789Sahrens 	char		*where;
677*789Sahrens 	char		*start;
678*789Sahrens 	struct group	*groupp;
679*789Sahrens 	struct passwd	*passwdp;
680*789Sahrens 	struct dynaclstr *dstr;
681*789Sahrens 	int		i, rtn;
682*789Sahrens 	int		isdir = (aceaclp->acl_flags & ACL_IS_DIR);
683*789Sahrens 	size_t		excess = 0;
684*789Sahrens 
685*789Sahrens 	if (aclp == NULL)
686*789Sahrens 		return (NULL);
687*789Sahrens 	if ((dstr = malloc(sizeof (struct dynaclstr))) == NULL)
688*789Sahrens 		return (NULL);
689*789Sahrens 	dstr->bufsize = aclcnt * ACE_ENTRY_SIZE;
690*789Sahrens 	if ((dstr->aclexport = malloc(dstr->bufsize)) == NULL)
691*789Sahrens 		return (NULL);
692*789Sahrens 	*dstr->aclexport = '\0';
693*789Sahrens 	where = dstr->aclexport;
694*789Sahrens 
695*789Sahrens 	for (i = 0; i < aclcnt; i++, aclp++) {
696*789Sahrens 		switch (aclp->a_flags & 0xf040) {
697*789Sahrens 		case ACE_OWNER:
698*789Sahrens 		case 0:
699*789Sahrens 			if ((aclp->a_flags & 0xf040) == ACE_OWNER)
700*789Sahrens 				where = strappend(where, "owner@");
701*789Sahrens 			else
702*789Sahrens 				where = strappend(where, "user:");
703*789Sahrens 			if ((aclp->a_flags & 0xf040) == 0) {
704*789Sahrens 				passwdp = getpwuid(aclp->a_who);
705*789Sahrens 				if (passwdp == (struct passwd *)NULL) {
706*789Sahrens 					/* put in uid instead */
707*789Sahrens 					(void) sprintf(where, "%d",
708*789Sahrens 					    aclp->a_who);
709*789Sahrens 				} else {
710*789Sahrens 					excess = strlen(passwdp->pw_name) -
711*789Sahrens 					    LOGNAME_MAX;
712*789Sahrens 					if (excess > 0) {
713*789Sahrens 						rtn = increase_length(dstr,
714*789Sahrens 						    excess);
715*789Sahrens 						if (rtn == 1)
716*789Sahrens 							/* reset where */
717*789Sahrens 							where =
718*789Sahrens 							    dstr->aclexport +
719*789Sahrens 							    strlen(
720*789Sahrens 							    dstr->aclexport);
721*789Sahrens 						else
722*789Sahrens 							return (NULL);
723*789Sahrens 					}
724*789Sahrens 					where = strappend(where,
725*789Sahrens 					    passwdp->pw_name);
726*789Sahrens 				}
727*789Sahrens 			} else {
728*789Sahrens 				where = strappend(where, "");
729*789Sahrens 			}
730*789Sahrens 			where = strappend(where, ":");
731*789Sahrens 			break;
732*789Sahrens 		case ACE_GROUP|ACE_IDENTIFIER_GROUP:
733*789Sahrens 		case ACE_IDENTIFIER_GROUP:
734*789Sahrens 			if ((aclp->a_flags & 0xf040) ==
735*789Sahrens 			    (ACE_GROUP | ACE_IDENTIFIER_GROUP))
736*789Sahrens 				where = strappend(where, "group@");
737*789Sahrens 			else
738*789Sahrens 				where = strappend(where, "group:");
739*789Sahrens 			if (!(aclp->a_flags & ACE_GROUP)) {
740*789Sahrens 				groupp = getgrgid(aclp->a_who);
741*789Sahrens 				if (groupp == (struct group *)NULL) {
742*789Sahrens 					/* put in gid instead */
743*789Sahrens 					(void) sprintf(where,
744*789Sahrens 					    "%d", aclp->a_who);
745*789Sahrens 				} else {
746*789Sahrens 					excess = strlen(groupp->gr_name) -
747*789Sahrens 					    LOGNAME_MAX;
748*789Sahrens 					if (excess > 0) {
749*789Sahrens 						rtn = increase_length(dstr,
750*789Sahrens 						    excess);
751*789Sahrens 						if (rtn == 1)
752*789Sahrens 							/* reset where */
753*789Sahrens 							where =
754*789Sahrens 							    dstr->aclexport +
755*789Sahrens 							    strlen(
756*789Sahrens 							    dstr->aclexport);
757*789Sahrens 						else
758*789Sahrens 							return (NULL);
759*789Sahrens 					}
760*789Sahrens 					where = strappend(where,
761*789Sahrens 					    groupp->gr_name);
762*789Sahrens 				}
763*789Sahrens 			} else {
764*789Sahrens 					where = strappend(where, "");
765*789Sahrens 			}
766*789Sahrens 			where = strappend(where, ":");
767*789Sahrens 			break;
768*789Sahrens 		case ACE_EVERYONE:
769*789Sahrens 			where = strappend(where, "everyone@:");
770*789Sahrens 			break;
771*789Sahrens 		default:
772*789Sahrens 			free(dstr->aclexport);
773*789Sahrens 			free(dstr);
774*789Sahrens 			return (NULL);
775*789Sahrens 
776*789Sahrens 		}
777*789Sahrens 		where = ace_convert_perm(where, aclp->a_access_mask,
778*789Sahrens 		    isdir, (aclp->a_flags &
779*789Sahrens 		    (ACE_FILE_INHERIT_ACE|ACE_DIRECTORY_INHERIT_ACE)));
780*789Sahrens 		where = strappend(where,
781*789Sahrens 		    (aclp->a_type == ACE_ACCESS_ALLOWED_ACE_TYPE) ?
782*789Sahrens 		    ":allow" : ":deny");
783*789Sahrens 
784*789Sahrens 		/*
785*789Sahrens 		 * slap on inheritance flags if we have any
786*789Sahrens 		 */
787*789Sahrens 
788*789Sahrens 		if (aclp->a_flags & 0xf) {
789*789Sahrens 			where = strappend(where, ":");
790*789Sahrens 			start = where;
791*789Sahrens 			if (aclp->a_flags & ACE_FILE_INHERIT_ACE)
792*789Sahrens 				where = strappend(where, "file_inherit/");
793*789Sahrens 			if (aclp->a_flags & ACE_DIRECTORY_INHERIT_ACE)
794*789Sahrens 				where = strappend(where, "dir_inherit/");
795*789Sahrens 			if (aclp->a_flags & ACE_NO_PROPAGATE_INHERIT_ACE)
796*789Sahrens 				where = strappend(where, "no_propagate/");
797*789Sahrens 			if (aclp->a_flags & ACE_INHERIT_ONLY_ACE)
798*789Sahrens 				where = strappend(where, "inherit_only");
799*789Sahrens 
800*789Sahrens 			/*
801*789Sahrens 			 * chop off trailing slash, if present
802*789Sahrens 			 */
803*789Sahrens 			if (start[strlen(start) - 1] == '/') {
804*789Sahrens 				start[strlen(start) - 1] = '\0';
805*789Sahrens 				where = start + strlen(start);
806*789Sahrens 			}
807*789Sahrens 		}
808*789Sahrens 		if (i < aclcnt - 1)
809*789Sahrens 			where = strappend(where, ",");
810*789Sahrens 	}
811*789Sahrens 	aclexport = dstr->aclexport;
812*789Sahrens 	free(dstr);
813*789Sahrens 	return (aclexport);
814*789Sahrens }
815*789Sahrens 
816*789Sahrens static int
817*789Sahrens build_iflags(char *str, int *iflags)
818*789Sahrens {
819*789Sahrens 
820*789Sahrens 	char *tok;
821*789Sahrens 	*iflags = 0;
822*789Sahrens 
823*789Sahrens 	tok = strtok(str, "/");
824*789Sahrens 
825*789Sahrens 	if (tok == NULL)
826*789Sahrens 		return (1);
827*789Sahrens 
828*789Sahrens 	do {
829*789Sahrens 		if (strcmp(tok, "file_inherit") == 0)
830*789Sahrens 			*iflags |= ACE_FILE_INHERIT_ACE;
831*789Sahrens 		else if (strcmp(tok, "dir_inherit") == 0)
832*789Sahrens 			*iflags |= ACE_DIRECTORY_INHERIT_ACE;
833*789Sahrens 		else if (strcmp(tok, "inherit_only") == 0)
834*789Sahrens 			*iflags |= ACE_INHERIT_ONLY_ACE;
835*789Sahrens 		else if (strcmp(tok, "no_propagate") == 0)
836*789Sahrens 			*iflags |= ACE_NO_PROPAGATE_INHERIT_ACE;
837*789Sahrens 		else
838*789Sahrens 			return (1);
839*789Sahrens 	} while (tok = strtok(NULL, "/"));
840*789Sahrens 	return (0);
841*789Sahrens }
842*789Sahrens 
843*789Sahrens /*
844*789Sahrens  * Convert external acl representation to internal representation.
845*789Sahrens  * The accepted syntax is: <acl_entry>[,<acl_entry>]*[,]
846*789Sahrens  * The comma at the end is not prescribed by the man pages.
847*789Sahrens  * But it is needed not to break the old programs.
848*789Sahrens  */
849*789Sahrens 
850*789Sahrens int
851*789Sahrens ace_aclfromtext(char *aclstr, acl_t **ret_aclp)
852*789Sahrens {
853*789Sahrens 	char		*fieldp;
854*789Sahrens 	char		*tp;
855*789Sahrens 	char		*nextp;
856*789Sahrens 	char		*allocp;
857*789Sahrens 	char		*aclimport;
858*789Sahrens 	char 		*str;
859*789Sahrens 	char		*perm_tok;
860*789Sahrens 	int		entry_type;
861*789Sahrens 	int		id;
862*789Sahrens 	int		type;
863*789Sahrens 	int		iflags;
864*789Sahrens 	int		len;
865*789Sahrens 	int		error;
866*789Sahrens 	int32_t		perm;
867*789Sahrens 	ace_t		*tmpaclp;
868*789Sahrens 	acl_t		*aclp;
869*789Sahrens 	struct group	*groupp;
870*789Sahrens 	struct passwd	*passwdp;
871*789Sahrens 
872*789Sahrens 	if (! aclstr)
873*789Sahrens 		return (EACL_INVALID_STR);
874*789Sahrens 
875*789Sahrens 	len = strlen(aclstr);
876*789Sahrens 
877*789Sahrens 	aclp = acl_alloc(ACE_T);
878*789Sahrens 	if (aclp == NULL) {
879*789Sahrens 		return (EACL_MEM_ERROR);
880*789Sahrens 	}
881*789Sahrens 
882*789Sahrens 	*ret_aclp = NULL;
883*789Sahrens 
884*789Sahrens 	if ((aclimport = allocp = strdup(aclstr)) == NULL) {
885*789Sahrens 		return (EACL_MEM_ERROR);
886*789Sahrens 	}
887*789Sahrens 
888*789Sahrens 
889*789Sahrens 	if (aclimport[len - 1] == ',')
890*789Sahrens 		aclimport[len - 1] = '\0';
891*789Sahrens 
892*789Sahrens 	for (; aclimport; ) {
893*789Sahrens 		/* look for an ACL entry */
894*789Sahrens 		tp = strchr(aclimport, ',');
895*789Sahrens 		if (tp == NULL) {
896*789Sahrens 			nextp = NULL;
897*789Sahrens 		} else {
898*789Sahrens 			*tp = '\0';
899*789Sahrens 			nextp = tp + 1;
900*789Sahrens 		}
901*789Sahrens 
902*789Sahrens 		aclp->acl_cnt += 1;
903*789Sahrens 
904*789Sahrens 		/*
905*789Sahrens 		 * get additional memory:
906*789Sahrens 		 * can be more efficient by allocating a bigger block
907*789Sahrens 		 * each time.
908*789Sahrens 		 */
909*789Sahrens 		if (aclp->acl_cnt > 1)
910*789Sahrens 			tmpaclp = (ace_t *)realloc(aclp->acl_aclp,
911*789Sahrens 			    sizeof (ace_t) * (aclp->acl_cnt));
912*789Sahrens 		else
913*789Sahrens 			tmpaclp = (ace_t *)malloc(sizeof (ace_t));
914*789Sahrens 		if (tmpaclp == NULL) {
915*789Sahrens 			free(allocp);
916*789Sahrens 			acl_free(aclp);
917*789Sahrens 			return (EACL_MEM_ERROR);
918*789Sahrens 		}
919*789Sahrens 		aclp->acl_aclp = tmpaclp;
920*789Sahrens 		tmpaclp = (ace_t *)aclp->acl_aclp + (aclp->acl_cnt - 1);
921*789Sahrens 
922*789Sahrens 		/* look for entry type field */
923*789Sahrens 		tp = strchr(aclimport, ':');
924*789Sahrens 		if (tp == NULL) {
925*789Sahrens 			free(allocp);
926*789Sahrens 			acl_free(aclp);
927*789Sahrens 			return (EACL_ENTRY_ERROR);
928*789Sahrens 		} else
929*789Sahrens 			*tp = '\0';
930*789Sahrens 		if (strcmp(aclimport, "owner@") == 0) {
931*789Sahrens 			entry_type = ACE_OWNER;
932*789Sahrens 		} else if (strcmp(aclimport, "group@") == 0) {
933*789Sahrens 			entry_type = ACE_GROUP | ACE_IDENTIFIER_GROUP;
934*789Sahrens 		} else if (strcmp(aclimport, "everyone@") == 0) {
935*789Sahrens 			entry_type = ACE_EVERYONE;
936*789Sahrens 		} else if (strcmp(aclimport, "group") == 0) {
937*789Sahrens 			entry_type = ACE_IDENTIFIER_GROUP;
938*789Sahrens 		} else if (strcmp(aclimport, "user") == 0) {
939*789Sahrens 			entry_type = 0;
940*789Sahrens 		} else {
941*789Sahrens 			free(allocp);
942*789Sahrens 			acl_free(aclp);
943*789Sahrens 			return (EACL_ENTRY_ERROR);
944*789Sahrens 		}
945*789Sahrens 
946*789Sahrens 		/*
947*789Sahrens 		 * If not an abstraction owner@, group@ or everyone@
948*789Sahrens 		 * then we must have a user/group name next
949*789Sahrens 		 */
950*789Sahrens 
951*789Sahrens 		if (entry_type == 0 || entry_type == ACE_IDENTIFIER_GROUP) {
952*789Sahrens 			fieldp = tp + 1;
953*789Sahrens 			tp = strchr(fieldp, ':');
954*789Sahrens 			if (tp == NULL) {
955*789Sahrens 				free(allocp);
956*789Sahrens 				acl_free(aclp);
957*789Sahrens 				return (EACL_INVALID_USER_GROUP);
958*789Sahrens 			} else
959*789Sahrens 				*tp = '\0';
960*789Sahrens 			if (fieldp != tp) {
961*789Sahrens 				/*
962*789Sahrens 				 * The second field could be empty. We only care
963*789Sahrens 				 * when the field has user/group name.
964*789Sahrens 				 */
965*789Sahrens 				if (entry_type == 0) {
966*789Sahrens 					/*
967*789Sahrens 					 * The reentrant interface getpwnam_r()
968*789Sahrens 					 * is uncommitted and subject to
969*789Sahrens 					 * change. Use the friendlier interface
970*789Sahrens 					 * getpwnam().
971*789Sahrens 					 */
972*789Sahrens 					error = 0;
973*789Sahrens 					passwdp = getpwnam(fieldp);
974*789Sahrens 					if (passwdp == NULL) {
975*789Sahrens 						error = acl_str_to_id(
976*789Sahrens 						    fieldp, &id);
977*789Sahrens 					} else {
978*789Sahrens 						id = passwdp->pw_uid;
979*789Sahrens 					}
980*789Sahrens 
981*789Sahrens 					if (error) {
982*789Sahrens 						free(allocp);
983*789Sahrens 						acl_free(aclp);
984*789Sahrens 						return (error);
985*789Sahrens 					}
986*789Sahrens 				} else {
987*789Sahrens 					error = 0;
988*789Sahrens 					if (entry_type ==
989*789Sahrens 					    ACE_IDENTIFIER_GROUP) {
990*789Sahrens 						groupp = getgrnam(fieldp);
991*789Sahrens 						if (groupp == NULL) {
992*789Sahrens 							/* no group? */
993*789Sahrens 							error = acl_str_to_id(
994*789Sahrens 							    fieldp, &id);
995*789Sahrens 						} else
996*789Sahrens 							id = groupp->gr_gid;
997*789Sahrens 
998*789Sahrens 					} else if ((entry_type == ACE_OWNER) ||
999*789Sahrens 					    (entry_type ==
1000*789Sahrens 					    (ACE_IDENTIFIER_GROUP|ACE_GROUP)) ||
1001*789Sahrens 					    (entry_type != ACE_EVERYONE)) {
1002*789Sahrens 						error = EACL_FIELD_NOT_BLANK;
1003*789Sahrens 					} else {
1004*789Sahrens 						error = EACL_ENTRY_ERROR;
1005*789Sahrens 					}
1006*789Sahrens 
1007*789Sahrens 					if (error) {
1008*789Sahrens 						free(allocp);
1009*789Sahrens 						acl_free(aclp);
1010*789Sahrens 						return (error);
1011*789Sahrens 					}
1012*789Sahrens 				}
1013*789Sahrens 			}
1014*789Sahrens 		} else {
1015*789Sahrens 			id = -1;
1016*789Sahrens 		}
1017*789Sahrens 
1018*789Sahrens 		/* next field: permission */
1019*789Sahrens 		fieldp = tp + 1;
1020*789Sahrens 		tp = strchr(fieldp, ':');
1021*789Sahrens 		if (tp == NULL) {
1022*789Sahrens 			free(allocp);
1023*789Sahrens 			acl_free(aclp);
1024*789Sahrens 			return (EACL_PERM_MASK_ERROR);
1025*789Sahrens 		} else
1026*789Sahrens 			*tp = '\0';
1027*789Sahrens 
1028*789Sahrens 		perm = 0;
1029*789Sahrens 
1030*789Sahrens 		perm_tok = strtok(fieldp, "/");
1031*789Sahrens 		if (perm_tok == NULL) {
1032*789Sahrens 			perm = 0;
1033*789Sahrens 		} else {
1034*789Sahrens 			do {
1035*789Sahrens 				if (ace_permask(perm_tok, &perm) != 0) {
1036*789Sahrens 					free(allocp);
1037*789Sahrens 					acl_free(aclp);
1038*789Sahrens 					return (EACL_PERM_MASK_ERROR);
1039*789Sahrens 				}
1040*789Sahrens 			} while (perm_tok = strtok(NULL, "/"));
1041*789Sahrens 		}
1042*789Sahrens 
1043*789Sahrens 		/* grab allow/deny */
1044*789Sahrens 		fieldp = tp + 1;
1045*789Sahrens 		tp = strchr(fieldp, ':');
1046*789Sahrens 		if (tp != NULL)
1047*789Sahrens 			*tp = '\0';
1048*789Sahrens 
1049*789Sahrens 		if (strcmp(fieldp, "allow") == 0)
1050*789Sahrens 			type = ACE_ACCESS_ALLOWED_ACE_TYPE;
1051*789Sahrens 		else if (strcmp(fieldp, "deny") == 0)
1052*789Sahrens 			type = ACE_ACCESS_DENIED_ACE_TYPE;
1053*789Sahrens 		else {
1054*789Sahrens 			free(allocp);
1055*789Sahrens 			acl_free(aclp);
1056*789Sahrens 			return (EACL_INVALID_ACCESS_TYPE);
1057*789Sahrens 		}
1058*789Sahrens 
1059*789Sahrens 		/* grab option inherit flags */
1060*789Sahrens 
1061*789Sahrens 		iflags = 0;
1062*789Sahrens 		if (tp != NULL) {
1063*789Sahrens 			fieldp = tp + 1;
1064*789Sahrens 			if (fieldp != NULL) {
1065*789Sahrens 				*tp = '\0';
1066*789Sahrens 				str = fieldp;
1067*789Sahrens 				if (build_iflags(str, &iflags) != 0) {
1068*789Sahrens 					free(allocp);
1069*789Sahrens 					acl_free(aclp);
1070*789Sahrens 					return (EACL_INHERIT_ERROR);
1071*789Sahrens 				}
1072*789Sahrens 			} else {
1073*789Sahrens 				free(allocp);
1074*789Sahrens 				acl_free(aclp);
1075*789Sahrens 				return (EACL_UNKNOWN_DATA);
1076*789Sahrens 			}
1077*789Sahrens 		}
1078*789Sahrens 		/* slap fields into ace_t structure */
1079*789Sahrens 
1080*789Sahrens 		tmpaclp->a_flags = entry_type;
1081*789Sahrens 		tmpaclp->a_flags |= iflags;
1082*789Sahrens 		tmpaclp->a_who = id;
1083*789Sahrens 		tmpaclp->a_access_mask = perm;
1084*789Sahrens 		tmpaclp->a_type = type;
1085*789Sahrens 		aclimport = nextp;
1086*789Sahrens 	}
1087*789Sahrens 	free(allocp);
1088*789Sahrens 	*ret_aclp = aclp;
1089*789Sahrens 	return (0);
1090*789Sahrens }
1091*789Sahrens 
1092*789Sahrens char
1093*789Sahrens *acl_totext(acl_t *aclp)
1094*789Sahrens {
1095*789Sahrens 	if (aclp == NULL)
1096*789Sahrens 		return (NULL);
1097*789Sahrens 
1098*789Sahrens 	switch (aclp->acl_type) {
1099*789Sahrens 	case ACE_T:
1100*789Sahrens 		return (ace_acltotext(aclp));
1101*789Sahrens 	case ACLENT_T:
1102*789Sahrens 		return (acltotext(aclp->acl_aclp, aclp->acl_cnt));
1103*789Sahrens 	}
1104*789Sahrens 	return (NULL);
1105*789Sahrens }
1106*789Sahrens 
1107*789Sahrens int
1108*789Sahrens acl_fromtext(const char *acltextp, acl_t **ret_aclp)
1109*789Sahrens {
1110*789Sahrens 	acl_t *aclp;
1111*789Sahrens 	char *token;
1112*789Sahrens 	char *ptr;
1113*789Sahrens 	char *textp;
1114*789Sahrens 	enum acl_type flavor;
1115*789Sahrens 	int colon_cnt = 0;
1116*789Sahrens 	int error;
1117*789Sahrens 
1118*789Sahrens 	/*
1119*789Sahrens 	 * first try and detect what type of acl entries we have
1120*789Sahrens 	 *
1121*789Sahrens 	 * aclent_t can have 1, 2 or 3 colons
1122*789Sahrens 	 * if 3 then must have word default:
1123*789Sahrens 	 *
1124*789Sahrens 	 * ace_t can have 2, 3 or 4
1125*789Sahrens 	 * for 2 then must be owner@, group@ or everyone@
1126*789Sahrens 	 */
1127*789Sahrens 
1128*789Sahrens 	textp = strdup(acltextp);
1129*789Sahrens 	if (textp == NULL)
1130*789Sahrens 		return (-1);
1131*789Sahrens 
1132*789Sahrens 	token = strtok(textp, ",");
1133*789Sahrens 	if (token == NULL) {
1134*789Sahrens 		free(textp);
1135*789Sahrens 		return (-1);
1136*789Sahrens 	}
1137*789Sahrens 
1138*789Sahrens 	for (ptr = token; *ptr; ptr++) {
1139*789Sahrens 		if (*ptr == ':')
1140*789Sahrens 			colon_cnt++;
1141*789Sahrens 	}
1142*789Sahrens 
1143*789Sahrens 	if (colon_cnt == 1 || colon_cnt == 2) {
1144*789Sahrens 		if ((strncmp(acltextp, "owner@", 6) == 0) ||
1145*789Sahrens 		    (strncmp(acltextp, "group@", 6) == 0) ||
1146*789Sahrens 		    (strncmp(acltextp, "everyone@", 9) == 0))
1147*789Sahrens 			flavor = ACE_T;
1148*789Sahrens 		else
1149*789Sahrens 			flavor = ACLENT_T;
1150*789Sahrens 	} else if (colon_cnt == 3) {
1151*789Sahrens 		ptr = strtok(token, ":");
1152*789Sahrens 		if (ptr == NULL) {
1153*789Sahrens 			free(textp);
1154*789Sahrens 			return (EACL_MISSING_FIELDS);
1155*789Sahrens 		} else if (strcmp(ptr, "default") == 0) {
1156*789Sahrens 			flavor = ACLENT_T;
1157*789Sahrens 		} else {
1158*789Sahrens 			flavor = ACE_T;
1159*789Sahrens 		}
1160*789Sahrens 	} else if (colon_cnt == 4) {
1161*789Sahrens 		flavor = ACE_T;
1162*789Sahrens 	} else {
1163*789Sahrens 		free(textp);
1164*789Sahrens 		return (EACL_MISSING_FIELDS);
1165*789Sahrens 	}
1166*789Sahrens 
1167*789Sahrens 
1168*789Sahrens 	free(textp);
1169*789Sahrens 
1170*789Sahrens 	if (flavor == ACLENT_T)
1171*789Sahrens 		error = aclent_aclfromtext((char *)acltextp, &aclp);
1172*789Sahrens 	else
1173*789Sahrens 		error = ace_aclfromtext((char *)acltextp, &aclp);
1174*789Sahrens 
1175*789Sahrens 	*ret_aclp = aclp;
1176*789Sahrens 	return (error);
1177*789Sahrens }
1178