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 
47 struct entry {
48 	int	count;
49 	uid_t	*id;
50 };
51 
52 struct 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 
67 static void free_mem(struct entry_stat *);
68 static int check_dup(int, uid_t *, uid_t, struct entry_stat *);
69 
70 static int
71 aclent_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 
262 int
263 aclcheck(aclent_t *aclbufp, int nentries, int *which)
264 {
265 	return (aclent_aclcheck(aclbufp, nentries, which, 1));
266 }
267 
268 
269 static void
270 free_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 
282 static int
283 check_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 
300 static int
301 ace_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 
361 int
362 acl_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