1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26#pragma ident	"%Z%%M%	%I%	%E% SMI"
27/*LINTLIBRARY*/
28
29/*
30 * aclcheck(): check validity of an ACL
31 *	A valid ACL is defined as follows:
32 *	There must be exactly one USER_OBJ, GROUP_OBJ, and OTHER_OBJ entry.
33 *	If there are any USER entries, then the user id must be unique.
34 *	If there are any GROUP entries, then the group id must be unique.
35 *	If there are any GROUP or USER entries, there must be exactly one
36 *	CLASS_OBJ entry.
37 *	The same rules apply to default ACL entries.
38 */
39
40#include <errno.h>
41#include <stdlib.h>
42#include <string.h>
43#include <sys/types.h>
44#include <sys/acl.h>
45#include <aclutils.h>
46
47struct entry {
48	int	count;
49	uid_t	*id;
50};
51
52struct entry_stat {
53	struct entry	user_obj;
54	struct entry	user;
55	struct entry	group_obj;
56	struct entry	group;
57	struct entry	other_obj;
58	struct entry	class_obj;
59	struct entry	def_user_obj;
60	struct entry	def_user;
61	struct entry	def_group_obj;
62	struct entry	def_group;
63	struct entry	def_other_obj;
64	struct entry	def_class_obj;
65};
66
67static void free_mem(struct entry_stat *);
68static int check_dup(int, uid_t *, uid_t, struct entry_stat *);
69
70static int
71aclent_aclcheck(aclent_t *aclbufp, int nentries,  int *which, int isdir)
72{
73	struct entry_stat	tally;
74	aclent_t		*aclentp;
75	uid_t			**idp;
76	int			cnt;
77
78	*which = -1;
79	memset(&tally, '\0', sizeof (tally));
80
81	for (aclentp = aclbufp; nentries > 0; nentries--, aclentp++) {
82		switch (aclentp->a_type) {
83		case USER_OBJ:
84			/* check uniqueness */
85			if (tally.user_obj.count > 0) {
86				*which = (int)(aclentp - aclbufp);
87				(void) free_mem(&tally);
88				errno = EINVAL;
89				return (EACL_USER_ERROR);
90			}
91			tally.user_obj.count = 1;
92			break;
93
94		case GROUP_OBJ:
95			/* check uniqueness */
96			if (tally.group_obj.count > 0) {
97				*which = (int)(aclentp - aclbufp);
98				(void) free_mem(&tally);
99				errno = EINVAL;
100				return (EACL_GRP_ERROR);
101			}
102			tally.group_obj.count = 1;
103			break;
104
105		case OTHER_OBJ:
106			/* check uniqueness */
107			if (tally.other_obj.count > 0) {
108				*which = (int)(aclentp - aclbufp);
109				(void) free_mem(&tally);
110				errno = EINVAL;
111				return (EACL_OTHER_ERROR);
112			}
113			tally.other_obj.count = 1;
114			break;
115
116		case CLASS_OBJ:
117			/* check uniqueness */
118			if (tally.class_obj.count > 0) {
119				*which = (int)(aclentp - aclbufp);
120				(void) free_mem(&tally);
121				errno = EINVAL;
122				return (EACL_CLASS_ERROR);
123			}
124			tally.class_obj.count = 1;
125			break;
126
127		case USER:
128		case GROUP:
129		case DEF_USER:
130		case DEF_GROUP:
131			/* check duplicate */
132			if (aclentp->a_type == DEF_USER) {
133				cnt = (tally.def_user.count)++;
134				idp = &(tally.def_user.id);
135			} else if (aclentp->a_type == DEF_GROUP) {
136				cnt = (tally.def_group.count)++;
137				idp = &(tally.def_group.id);
138			} else if (aclentp->a_type == USER) {
139				cnt = (tally.user.count)++;
140				idp = &(tally.user.id);
141			} else {
142				cnt = (tally.group.count)++;
143				idp = &(tally.group.id);
144			}
145
146			if (cnt == 0) {
147				*idp = calloc(nentries, sizeof (uid_t));
148				if (*idp == NULL)
149					return (EACL_MEM_ERROR);
150			} else {
151				if (check_dup(cnt, *idp, aclentp->a_id,
152				    &tally) == -1) {
153					*which = (int)(aclentp - aclbufp);
154					return (EACL_DUPLICATE_ERROR);
155				}
156			}
157			(*idp)[cnt] = aclentp->a_id;
158			break;
159
160		case DEF_USER_OBJ:
161			/* check uniqueness */
162			if (tally.def_user_obj.count > 0) {
163				*which = (int)(aclentp - aclbufp);
164				(void) free_mem(&tally);
165				errno = EINVAL;
166				return (EACL_USER_ERROR);
167			}
168			tally.def_user_obj.count = 1;
169			break;
170
171		case DEF_GROUP_OBJ:
172			/* check uniqueness */
173			if (tally.def_group_obj.count > 0) {
174				*which = (int)(aclentp - aclbufp);
175				(void) free_mem(&tally);
176				errno = EINVAL;
177				return (EACL_GRP_ERROR);
178			}
179			tally.def_group_obj.count = 1;
180			break;
181
182		case DEF_OTHER_OBJ:
183			/* check uniqueness */
184			if (tally.def_other_obj.count > 0) {
185				*which = (int)(aclentp - aclbufp);
186				(void) free_mem(&tally);
187				errno = EINVAL;
188				return (EACL_OTHER_ERROR);
189			}
190			tally.def_other_obj.count = 1;
191			break;
192
193		case DEF_CLASS_OBJ:
194			/* check uniqueness */
195			if (tally.def_class_obj.count > 0) {
196				*which = (int)(aclentp - aclbufp);
197				(void) free_mem(&tally);
198				errno = EINVAL;
199				return (EACL_CLASS_ERROR);
200			}
201			tally.def_class_obj.count = 1;
202			break;
203
204		default:
205			(void) free_mem(&tally);
206			errno = EINVAL;
207			*which = (int)(aclentp - aclbufp);
208			return (EACL_ENTRY_ERROR);
209		}
210	}
211	/* If there are group or user entries, there must be one class entry */
212	if (tally.user.count > 0 || tally.group.count > 0)
213		if (tally.class_obj.count != 1) {
214			(void) free_mem(&tally);
215			errno = EINVAL;
216			return (EACL_MISS_ERROR);
217		}
218	/* same is true for default entries */
219	if (tally.def_user.count > 0 || tally.def_group.count > 0)
220		if (tally.def_class_obj.count != 1) {
221			(void) free_mem(&tally);
222			errno = EINVAL;
223			return (EACL_MISS_ERROR);
224		}
225
226	/* there must be exactly one user_obj, group_obj, and other_obj entry */
227	if (tally.user_obj.count != 1 ||
228	    tally.group_obj.count != 1 ||
229	    tally.other_obj.count != 1) {
230		(void) free_mem(&tally);
231		errno = EINVAL;
232		return (EACL_MISS_ERROR);
233	}
234
235	/* has default? same rules apply to default entries */
236	if (tally.def_user.count > 0 || tally.def_user_obj.count > 0 ||
237	    tally.def_group.count > 0 || tally.def_group_obj.count > 0 ||
238	    tally.def_class_obj.count > 0 || tally.def_other_obj.count > 0) {
239
240		/*
241		 * Can't have default ACL's on non-directories
242		 */
243		if (isdir == 0) {
244			(void) free_mem(&tally);
245			errno = EINVAL;
246			return (EACL_INHERIT_NOTDIR);
247		}
248
249		if (tally.def_user_obj.count != 1 ||
250		    tally.def_group_obj.count != 1 ||
251		    tally.def_other_obj.count != 1) {
252			(void) free_mem(&tally);
253			errno = EINVAL;
254			return (EACL_MISS_ERROR);
255		}
256	}
257
258	(void) free_mem(&tally);
259	return (0);
260}
261
262int
263aclcheck(aclent_t *aclbufp, int nentries, int *which)
264{
265	return (aclent_aclcheck(aclbufp, nentries, which, 1));
266}
267
268
269static void
270free_mem(struct entry_stat *tallyp)
271{
272	if ((tallyp->user).count > 0)
273		free((tallyp->user).id);
274	if ((tallyp->group).count > 0)
275		free((tallyp->group).id);
276	if ((tallyp->def_user).count > 0)
277		free((tallyp->def_user).id);
278	if ((tallyp->def_group).count > 0)
279		free((tallyp->def_group).id);
280}
281
282static int
283check_dup(int count, uid_t *ids, uid_t newid, struct entry_stat *tallyp)
284{
285	int	i;
286
287	for (i = 0; i < count; i++) {
288		if (ids[i] == newid) {
289			errno = EINVAL;
290			(void) free_mem(tallyp);
291			return (-1);
292		}
293	}
294	return (0);
295}
296
297#define	IFLAGS	(ACE_FILE_INHERIT_ACE|ACE_DIRECTORY_INHERIT_ACE| \
298    ACE_NO_PROPAGATE_INHERIT_ACE|ACE_INHERIT_ONLY_ACE)
299
300static int
301ace_aclcheck(acl_t *aclp, int isdir)
302{
303	ace_t 	*acep;
304	int 	i;
305	int	error = 0;
306
307	/*
308	 * step through all valid flags.
309	 */
310
311	if (aclp->acl_cnt <= 0 || aclp->acl_cnt > MAX_ACL_ENTRIES)
312		return (EACL_COUNT_ERROR);
313
314	for (i = 0, acep = aclp->acl_aclp;
315	    i != aclp->acl_cnt && error == 0; i++, acep++) {
316		switch (acep->a_flags & 0xf040) {
317		case 0:
318		case ACE_OWNER:
319		case ACE_EVERYONE:
320		case ACE_IDENTIFIER_GROUP:
321		case ACE_GROUP|ACE_IDENTIFIER_GROUP:
322			break;
323		default:
324			errno = EINVAL;
325			return (EACL_FLAGS_ERROR);
326		}
327
328		/*
329		 * INHERIT_ONLY/NO_PROPAGATE need a to INHERIT_FILE
330		 * or INHERIT_DIR also
331		 */
332		if (acep->a_flags &
333		    (ACE_INHERIT_ONLY_ACE|ACE_NO_PROPAGATE_INHERIT_ACE)) {
334			if ((acep->a_flags & (ACE_FILE_INHERIT_ACE|
335			    ACE_DIRECTORY_INHERIT_ACE)) == 0) {
336				errno = EINVAL;
337				return (EACL_INHERIT_ERROR);
338			}
339			break;
340		}
341
342		switch (acep->a_type) {
343		case ACE_ACCESS_ALLOWED_ACE_TYPE:
344		case ACE_ACCESS_DENIED_ACE_TYPE:
345		case ACE_SYSTEM_AUDIT_ACE_TYPE:
346		case ACE_SYSTEM_ALARM_ACE_TYPE:
347			break;
348		default:
349			errno = EINVAL;
350			return (EACL_ENTRY_ERROR);
351		}
352		if (acep->a_access_mask > ACE_ALL_PERMS) {
353			errno = EINVAL;
354			return (EACL_PERM_MASK_ERROR);
355		}
356	}
357
358	return (0);
359}
360
361int
362acl_check(acl_t *aclp, int flag)
363{
364	int error;
365	int where;
366
367	switch (aclp->acl_type) {
368	case ACLENT_T:
369		error = aclent_aclcheck(aclp->acl_aclp, aclp->acl_cnt,
370		    &where, flag);
371		break;
372	case ACE_T:
373		error = ace_aclcheck(aclp, flag);
374		break;
375	default:
376		errno = EINVAL;
377		error = EACL_ENTRY_ERROR;
378	}
379	return (error);
380}
381