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