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