xref: /onnv-gate/usr/src/lib/libsec/common/aclcheck.c (revision 7057:d3fa1d6dbef7)
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
5*7057Smarks  * Common Development and Distribution License (the "License").
6*7057Smarks  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
22*7057Smarks  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23789Sahrens  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
270Sstevel@tonic-gate /*LINTLIBRARY*/
280Sstevel@tonic-gate 
290Sstevel@tonic-gate /*
300Sstevel@tonic-gate  * aclcheck(): check validity of an ACL
310Sstevel@tonic-gate  *	A valid ACL is defined as follows:
320Sstevel@tonic-gate  *	There must be exactly one USER_OBJ, GROUP_OBJ, and OTHER_OBJ entry.
330Sstevel@tonic-gate  *	If there are any USER entries, then the user id must be unique.
340Sstevel@tonic-gate  *	If there are any GROUP entries, then the group id must be unique.
350Sstevel@tonic-gate  *	If there are any GROUP or USER entries, there must be exactly one
360Sstevel@tonic-gate  *	CLASS_OBJ entry.
370Sstevel@tonic-gate  *	The same rules apply to default ACL entries.
380Sstevel@tonic-gate  */
390Sstevel@tonic-gate 
400Sstevel@tonic-gate #include <errno.h>
410Sstevel@tonic-gate #include <stdlib.h>
420Sstevel@tonic-gate #include <string.h>
430Sstevel@tonic-gate #include <sys/types.h>
440Sstevel@tonic-gate #include <sys/acl.h>
45789Sahrens #include <aclutils.h>
460Sstevel@tonic-gate 
470Sstevel@tonic-gate struct entry {
480Sstevel@tonic-gate 	int	count;
490Sstevel@tonic-gate 	uid_t	*id;
500Sstevel@tonic-gate };
510Sstevel@tonic-gate 
520Sstevel@tonic-gate struct entry_stat {
530Sstevel@tonic-gate 	struct entry	user_obj;
540Sstevel@tonic-gate 	struct entry	user;
550Sstevel@tonic-gate 	struct entry	group_obj;
560Sstevel@tonic-gate 	struct entry	group;
570Sstevel@tonic-gate 	struct entry	other_obj;
580Sstevel@tonic-gate 	struct entry	class_obj;
590Sstevel@tonic-gate 	struct entry	def_user_obj;
600Sstevel@tonic-gate 	struct entry	def_user;
610Sstevel@tonic-gate 	struct entry	def_group_obj;
620Sstevel@tonic-gate 	struct entry	def_group;
630Sstevel@tonic-gate 	struct entry	def_other_obj;
640Sstevel@tonic-gate 	struct entry	def_class_obj;
650Sstevel@tonic-gate };
660Sstevel@tonic-gate 
670Sstevel@tonic-gate static void free_mem(struct entry_stat *);
680Sstevel@tonic-gate static int check_dup(int, uid_t *, uid_t, struct entry_stat *);
690Sstevel@tonic-gate 
70789Sahrens static int
aclent_aclcheck(aclent_t * aclbufp,int nentries,int * which,int isdir)71789Sahrens aclent_aclcheck(aclent_t *aclbufp, int nentries,  int *which, int isdir)
720Sstevel@tonic-gate {
730Sstevel@tonic-gate 	struct entry_stat	tally;
740Sstevel@tonic-gate 	aclent_t		*aclentp;
750Sstevel@tonic-gate 	uid_t			**idp;
760Sstevel@tonic-gate 	int			cnt;
770Sstevel@tonic-gate 
780Sstevel@tonic-gate 	*which = -1;
790Sstevel@tonic-gate 	memset(&tally, '\0', sizeof (tally));
800Sstevel@tonic-gate 
810Sstevel@tonic-gate 	for (aclentp = aclbufp; nentries > 0; nentries--, aclentp++) {
820Sstevel@tonic-gate 		switch (aclentp->a_type) {
830Sstevel@tonic-gate 		case USER_OBJ:
840Sstevel@tonic-gate 			/* check uniqueness */
850Sstevel@tonic-gate 			if (tally.user_obj.count > 0) {
86789Sahrens 				*which = (int)(aclentp - aclbufp);
870Sstevel@tonic-gate 				(void) free_mem(&tally);
880Sstevel@tonic-gate 				errno = EINVAL;
89789Sahrens 				return (EACL_USER_ERROR);
900Sstevel@tonic-gate 			}
910Sstevel@tonic-gate 			tally.user_obj.count = 1;
920Sstevel@tonic-gate 			break;
930Sstevel@tonic-gate 
940Sstevel@tonic-gate 		case GROUP_OBJ:
950Sstevel@tonic-gate 			/* check uniqueness */
960Sstevel@tonic-gate 			if (tally.group_obj.count > 0) {
97789Sahrens 				*which = (int)(aclentp - aclbufp);
980Sstevel@tonic-gate 				(void) free_mem(&tally);
990Sstevel@tonic-gate 				errno = EINVAL;
100789Sahrens 				return (EACL_GRP_ERROR);
1010Sstevel@tonic-gate 			}
1020Sstevel@tonic-gate 			tally.group_obj.count = 1;
1030Sstevel@tonic-gate 			break;
1040Sstevel@tonic-gate 
1050Sstevel@tonic-gate 		case OTHER_OBJ:
1060Sstevel@tonic-gate 			/* check uniqueness */
1070Sstevel@tonic-gate 			if (tally.other_obj.count > 0) {
108789Sahrens 				*which = (int)(aclentp - aclbufp);
1090Sstevel@tonic-gate 				(void) free_mem(&tally);
1100Sstevel@tonic-gate 				errno = EINVAL;
111789Sahrens 				return (EACL_OTHER_ERROR);
1120Sstevel@tonic-gate 			}
1130Sstevel@tonic-gate 			tally.other_obj.count = 1;
1140Sstevel@tonic-gate 			break;
1150Sstevel@tonic-gate 
1160Sstevel@tonic-gate 		case CLASS_OBJ:
1170Sstevel@tonic-gate 			/* check uniqueness */
1180Sstevel@tonic-gate 			if (tally.class_obj.count > 0) {
119789Sahrens 				*which = (int)(aclentp - aclbufp);
1200Sstevel@tonic-gate 				(void) free_mem(&tally);
1210Sstevel@tonic-gate 				errno = EINVAL;
122789Sahrens 				return (EACL_CLASS_ERROR);
1230Sstevel@tonic-gate 			}
1240Sstevel@tonic-gate 			tally.class_obj.count = 1;
1250Sstevel@tonic-gate 			break;
1260Sstevel@tonic-gate 
1270Sstevel@tonic-gate 		case USER:
1280Sstevel@tonic-gate 		case GROUP:
1290Sstevel@tonic-gate 		case DEF_USER:
1300Sstevel@tonic-gate 		case DEF_GROUP:
1310Sstevel@tonic-gate 			/* check duplicate */
1320Sstevel@tonic-gate 			if (aclentp->a_type == DEF_USER) {
1330Sstevel@tonic-gate 				cnt = (tally.def_user.count)++;
1340Sstevel@tonic-gate 				idp = &(tally.def_user.id);
1350Sstevel@tonic-gate 			} else if (aclentp->a_type == DEF_GROUP) {
1360Sstevel@tonic-gate 				cnt = (tally.def_group.count)++;
1370Sstevel@tonic-gate 				idp = &(tally.def_group.id);
1380Sstevel@tonic-gate 			} else if (aclentp->a_type == USER) {
1390Sstevel@tonic-gate 				cnt = (tally.user.count)++;
1400Sstevel@tonic-gate 				idp = &(tally.user.id);
1410Sstevel@tonic-gate 			} else {
1420Sstevel@tonic-gate 				cnt = (tally.group.count)++;
1430Sstevel@tonic-gate 				idp = &(tally.group.id);
1440Sstevel@tonic-gate 			}
1450Sstevel@tonic-gate 
1460Sstevel@tonic-gate 			if (cnt == 0) {
1470Sstevel@tonic-gate 				*idp = calloc(nentries, sizeof (uid_t));
1480Sstevel@tonic-gate 				if (*idp == NULL)
149789Sahrens 					return (EACL_MEM_ERROR);
1500Sstevel@tonic-gate 			} else {
1510Sstevel@tonic-gate 				if (check_dup(cnt, *idp, aclentp->a_id,
1520Sstevel@tonic-gate 				    &tally) == -1) {
153789Sahrens 					*which = (int)(aclentp - aclbufp);
154789Sahrens 					return (EACL_DUPLICATE_ERROR);
1550Sstevel@tonic-gate 				}
1560Sstevel@tonic-gate 			}
1570Sstevel@tonic-gate 			(*idp)[cnt] = aclentp->a_id;
1580Sstevel@tonic-gate 			break;
1590Sstevel@tonic-gate 
1600Sstevel@tonic-gate 		case DEF_USER_OBJ:
1610Sstevel@tonic-gate 			/* check uniqueness */
1620Sstevel@tonic-gate 			if (tally.def_user_obj.count > 0) {
163789Sahrens 				*which = (int)(aclentp - aclbufp);
1640Sstevel@tonic-gate 				(void) free_mem(&tally);
1650Sstevel@tonic-gate 				errno = EINVAL;
166789Sahrens 				return (EACL_USER_ERROR);
1670Sstevel@tonic-gate 			}
1680Sstevel@tonic-gate 			tally.def_user_obj.count = 1;
1690Sstevel@tonic-gate 			break;
1700Sstevel@tonic-gate 
1710Sstevel@tonic-gate 		case DEF_GROUP_OBJ:
1720Sstevel@tonic-gate 			/* check uniqueness */
1730Sstevel@tonic-gate 			if (tally.def_group_obj.count > 0) {
174789Sahrens 				*which = (int)(aclentp - aclbufp);
1750Sstevel@tonic-gate 				(void) free_mem(&tally);
1760Sstevel@tonic-gate 				errno = EINVAL;
177789Sahrens 				return (EACL_GRP_ERROR);
1780Sstevel@tonic-gate 			}
1790Sstevel@tonic-gate 			tally.def_group_obj.count = 1;
1800Sstevel@tonic-gate 			break;
1810Sstevel@tonic-gate 
1820Sstevel@tonic-gate 		case DEF_OTHER_OBJ:
1830Sstevel@tonic-gate 			/* check uniqueness */
1840Sstevel@tonic-gate 			if (tally.def_other_obj.count > 0) {
185789Sahrens 				*which = (int)(aclentp - aclbufp);
1860Sstevel@tonic-gate 				(void) free_mem(&tally);
1870Sstevel@tonic-gate 				errno = EINVAL;
188789Sahrens 				return (EACL_OTHER_ERROR);
1890Sstevel@tonic-gate 			}
1900Sstevel@tonic-gate 			tally.def_other_obj.count = 1;
1910Sstevel@tonic-gate 			break;
1920Sstevel@tonic-gate 
1930Sstevel@tonic-gate 		case DEF_CLASS_OBJ:
1940Sstevel@tonic-gate 			/* check uniqueness */
1950Sstevel@tonic-gate 			if (tally.def_class_obj.count > 0) {
196789Sahrens 				*which = (int)(aclentp - aclbufp);
1970Sstevel@tonic-gate 				(void) free_mem(&tally);
1980Sstevel@tonic-gate 				errno = EINVAL;
199789Sahrens 				return (EACL_CLASS_ERROR);
2000Sstevel@tonic-gate 			}
2010Sstevel@tonic-gate 			tally.def_class_obj.count = 1;
2020Sstevel@tonic-gate 			break;
2030Sstevel@tonic-gate 
2040Sstevel@tonic-gate 		default:
2050Sstevel@tonic-gate 			(void) free_mem(&tally);
2060Sstevel@tonic-gate 			errno = EINVAL;
207789Sahrens 			*which = (int)(aclentp - aclbufp);
208789Sahrens 			return (EACL_ENTRY_ERROR);
2090Sstevel@tonic-gate 		}
2100Sstevel@tonic-gate 	}
2110Sstevel@tonic-gate 	/* If there are group or user entries, there must be one class entry */
2120Sstevel@tonic-gate 	if (tally.user.count > 0 || tally.group.count > 0)
2130Sstevel@tonic-gate 		if (tally.class_obj.count != 1) {
2140Sstevel@tonic-gate 			(void) free_mem(&tally);
2150Sstevel@tonic-gate 			errno = EINVAL;
216789Sahrens 			return (EACL_MISS_ERROR);
2170Sstevel@tonic-gate 		}
2180Sstevel@tonic-gate 	/* same is true for default entries */
2190Sstevel@tonic-gate 	if (tally.def_user.count > 0 || tally.def_group.count > 0)
2200Sstevel@tonic-gate 		if (tally.def_class_obj.count != 1) {
2210Sstevel@tonic-gate 			(void) free_mem(&tally);
2220Sstevel@tonic-gate 			errno = EINVAL;
223789Sahrens 			return (EACL_MISS_ERROR);
2240Sstevel@tonic-gate 		}
2250Sstevel@tonic-gate 
2260Sstevel@tonic-gate 	/* there must be exactly one user_obj, group_obj, and other_obj entry */
2270Sstevel@tonic-gate 	if (tally.user_obj.count != 1 ||
2280Sstevel@tonic-gate 	    tally.group_obj.count != 1 ||
229*7057Smarks 	    tally.other_obj.count != 1) {
2300Sstevel@tonic-gate 		(void) free_mem(&tally);
2310Sstevel@tonic-gate 		errno = EINVAL;
232789Sahrens 		return (EACL_MISS_ERROR);
2330Sstevel@tonic-gate 	}
2340Sstevel@tonic-gate 
2350Sstevel@tonic-gate 	/* has default? same rules apply to default entries */
236789Sahrens 	if (tally.def_user.count > 0 || tally.def_user_obj.count > 0 ||
237789Sahrens 	    tally.def_group.count > 0 || tally.def_group_obj.count > 0 ||
238789Sahrens 	    tally.def_class_obj.count > 0 || tally.def_other_obj.count > 0) {
239789Sahrens 
240789Sahrens 		/*
241789Sahrens 		 * Can't have default ACL's on non-directories
242789Sahrens 		 */
243789Sahrens 		if (isdir == 0) {
244789Sahrens 			(void) free_mem(&tally);
245789Sahrens 			errno = EINVAL;
246789Sahrens 			return (EACL_INHERIT_NOTDIR);
247789Sahrens 		}
248789Sahrens 
2490Sstevel@tonic-gate 		if (tally.def_user_obj.count != 1 ||
2500Sstevel@tonic-gate 		    tally.def_group_obj.count != 1 ||
2510Sstevel@tonic-gate 		    tally.def_other_obj.count != 1) {
2520Sstevel@tonic-gate 			(void) free_mem(&tally);
2530Sstevel@tonic-gate 			errno = EINVAL;
254789Sahrens 			return (EACL_MISS_ERROR);
2550Sstevel@tonic-gate 		}
256789Sahrens 	}
257789Sahrens 
2580Sstevel@tonic-gate 	(void) free_mem(&tally);
2590Sstevel@tonic-gate 	return (0);
2600Sstevel@tonic-gate }
2610Sstevel@tonic-gate 
262789Sahrens int
aclcheck(aclent_t * aclbufp,int nentries,int * which)263789Sahrens aclcheck(aclent_t *aclbufp, int nentries, int *which)
264789Sahrens {
265789Sahrens 	return (aclent_aclcheck(aclbufp, nentries, which, 1));
266789Sahrens }
267789Sahrens 
268789Sahrens 
2690Sstevel@tonic-gate static void
free_mem(struct entry_stat * tallyp)2700Sstevel@tonic-gate free_mem(struct entry_stat *tallyp)
2710Sstevel@tonic-gate {
2720Sstevel@tonic-gate 	if ((tallyp->user).count > 0)
2730Sstevel@tonic-gate 		free((tallyp->user).id);
2740Sstevel@tonic-gate 	if ((tallyp->group).count > 0)
2750Sstevel@tonic-gate 		free((tallyp->group).id);
2760Sstevel@tonic-gate 	if ((tallyp->def_user).count > 0)
2770Sstevel@tonic-gate 		free((tallyp->def_user).id);
2780Sstevel@tonic-gate 	if ((tallyp->def_group).count > 0)
2790Sstevel@tonic-gate 		free((tallyp->def_group).id);
2800Sstevel@tonic-gate }
2810Sstevel@tonic-gate 
2820Sstevel@tonic-gate static int
check_dup(int count,uid_t * ids,uid_t newid,struct entry_stat * tallyp)2830Sstevel@tonic-gate check_dup(int count, uid_t *ids, uid_t newid, struct entry_stat *tallyp)
2840Sstevel@tonic-gate {
2850Sstevel@tonic-gate 	int	i;
2860Sstevel@tonic-gate 
2870Sstevel@tonic-gate 	for (i = 0; i < count; i++) {
2880Sstevel@tonic-gate 		if (ids[i] == newid) {
2890Sstevel@tonic-gate 			errno = EINVAL;
2900Sstevel@tonic-gate 			(void) free_mem(tallyp);
2910Sstevel@tonic-gate 			return (-1);
2920Sstevel@tonic-gate 		}
2930Sstevel@tonic-gate 	}
2940Sstevel@tonic-gate 	return (0);
2950Sstevel@tonic-gate }
296789Sahrens 
297789Sahrens #define	IFLAGS	(ACE_FILE_INHERIT_ACE|ACE_DIRECTORY_INHERIT_ACE| \
298789Sahrens     ACE_NO_PROPAGATE_INHERIT_ACE|ACE_INHERIT_ONLY_ACE)
299789Sahrens 
300789Sahrens static int
ace_aclcheck(acl_t * aclp,int isdir)301789Sahrens ace_aclcheck(acl_t *aclp, int isdir)
302789Sahrens {
303789Sahrens 	ace_t 	*acep;
304789Sahrens 	int 	i;
305789Sahrens 	int	error = 0;
306789Sahrens 
307789Sahrens 	/*
308789Sahrens 	 * step through all valid flags.
309789Sahrens 	 */
310789Sahrens 
311789Sahrens 	if (aclp->acl_cnt <= 0 || aclp->acl_cnt > MAX_ACL_ENTRIES)
312789Sahrens 		return (EACL_COUNT_ERROR);
313789Sahrens 
314789Sahrens 	for (i = 0, acep = aclp->acl_aclp;
315789Sahrens 	    i != aclp->acl_cnt && error == 0; i++, acep++) {
316789Sahrens 		switch (acep->a_flags & 0xf040) {
317789Sahrens 		case 0:
318789Sahrens 		case ACE_OWNER:
319789Sahrens 		case ACE_EVERYONE:
320789Sahrens 		case ACE_IDENTIFIER_GROUP:
321789Sahrens 		case ACE_GROUP|ACE_IDENTIFIER_GROUP:
322789Sahrens 			break;
323789Sahrens 		default:
324789Sahrens 			errno = EINVAL;
325789Sahrens 			return (EACL_FLAGS_ERROR);
326789Sahrens 		}
327789Sahrens 
328789Sahrens 		/*
329789Sahrens 		 * INHERIT_ONLY/NO_PROPAGATE need a to INHERIT_FILE
330789Sahrens 		 * or INHERIT_DIR also
331789Sahrens 		 */
332789Sahrens 		if (acep->a_flags &
333789Sahrens 		    (ACE_INHERIT_ONLY_ACE|ACE_NO_PROPAGATE_INHERIT_ACE)) {
334789Sahrens 			if ((acep->a_flags & (ACE_FILE_INHERIT_ACE|
335789Sahrens 			    ACE_DIRECTORY_INHERIT_ACE)) == 0) {
336789Sahrens 				errno = EINVAL;
337789Sahrens 				return (EACL_INHERIT_ERROR);
338789Sahrens 			}
339789Sahrens 			break;
340789Sahrens 		}
341789Sahrens 
342789Sahrens 		switch (acep->a_type) {
343789Sahrens 		case ACE_ACCESS_ALLOWED_ACE_TYPE:
344789Sahrens 		case ACE_ACCESS_DENIED_ACE_TYPE:
345789Sahrens 		case ACE_SYSTEM_AUDIT_ACE_TYPE:
346789Sahrens 		case ACE_SYSTEM_ALARM_ACE_TYPE:
347789Sahrens 			break;
348789Sahrens 		default:
349789Sahrens 			errno = EINVAL;
350789Sahrens 			return (EACL_ENTRY_ERROR);
351789Sahrens 		}
352789Sahrens 		if (acep->a_access_mask > ACE_ALL_PERMS) {
353789Sahrens 			errno = EINVAL;
354789Sahrens 			return (EACL_PERM_MASK_ERROR);
355789Sahrens 		}
356789Sahrens 	}
357789Sahrens 
358789Sahrens 	return (0);
359789Sahrens }
360789Sahrens 
361789Sahrens int
acl_check(acl_t * aclp,int flag)362789Sahrens acl_check(acl_t *aclp, int flag)
363789Sahrens {
364789Sahrens 	int error;
365789Sahrens 	int where;
366789Sahrens 
367789Sahrens 	switch (aclp->acl_type) {
368789Sahrens 	case ACLENT_T:
369789Sahrens 		error = aclent_aclcheck(aclp->acl_aclp, aclp->acl_cnt,
370789Sahrens 		    &where, flag);
371789Sahrens 		break;
372789Sahrens 	case ACE_T:
373789Sahrens 		error = ace_aclcheck(aclp, flag);
374789Sahrens 		break;
375789Sahrens 	default:
376789Sahrens 		errno = EINVAL;
377789Sahrens 		error = EACL_ENTRY_ERROR;
378789Sahrens 	}
379789Sahrens 	return (error);
380789Sahrens }
381