xref: /freebsd/lib/libc/posix1e/acl_support.c (revision 2a4a1db342263067035ce69a4017c645da63455d)
1 /*-
2  * Copyright (c) 1999, 2000, 2001 Robert N. M. Watson
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26 /*
27  * Support functionality for the POSIX.1e ACL interface
28  * These calls are intended only to be called within the library.
29  */
30 
31 #include <sys/cdefs.h>
32 __FBSDID("$FreeBSD$");
33 
34 #include <sys/types.h>
35 #include "namespace.h"
36 #include <sys/acl.h>
37 #include "un-namespace.h"
38 #include <errno.h>
39 #include <grp.h>
40 #include <pwd.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 
44 #include "acl_support.h"
45 
46 #define ACL_STRING_PERM_WRITE   'w'
47 #define ACL_STRING_PERM_READ    'r'
48 #define ACL_STRING_PERM_EXEC    'x'
49 #define ACL_STRING_PERM_NONE    '-'
50 
51 /*
52  * _posix1e_acl_entry_compare -- compare two acl_entry structures to
53  * determine the order they should appear in.  Used by _posix1e_acl_sort to
54  * sort ACL entries into the kernel-desired order -- i.e., the order useful
55  * for evaluation and O(n) validity checking.  Beter to have an O(nlogn) sort
56  * in userland and an O(n) in kernel than to have both in kernel.
57  */
58 typedef int (*compare)(const void *, const void *);
59 static int
60 _posix1e_acl_entry_compare(struct acl_entry *a, struct acl_entry *b)
61 {
62 	/*
63 	 * First, sort between tags -- conveniently defined in the correct
64 	 * order for verification.
65 	 */
66 	if (a->ae_tag < b->ae_tag)
67 		return (-1);
68 	if (a->ae_tag > b->ae_tag)
69 		return (1);
70 
71 	/*
72 	 * Next compare uids/gids on appropriate types.
73 	 */
74 
75 	if (a->ae_tag == ACL_USER || a->ae_tag == ACL_GROUP) {
76 		if (a->ae_id < b->ae_id)
77 			return (-1);
78 		if (a->ae_id > b->ae_id)
79 			return (1);
80 
81 		/* shouldn't be equal, fall through to the invalid case */
82 	}
83 
84 	/*
85 	 * Don't know how to sort multiple entries of the rest--either it's
86 	 * a bad entry, or there shouldn't be more than one.  Ignore and the
87 	 * validity checker can get it later.
88 	 */
89 	return (0);
90 }
91 
92 /*
93  * _posix1e_acl_sort -- sort ACL entries in POSIX.1e-formatted ACLs
94  * Give the opportunity to fail, although we don't currently have a way
95  * to fail.
96  */
97 int
98 _posix1e_acl_sort(acl_t acl)
99 {
100 	struct acl *acl_int;
101 
102 	acl_int = &acl->ats_acl;
103 
104 	qsort(&acl_int->acl_entry[0], acl_int->acl_cnt,
105 	    sizeof(struct acl_entry), (compare) _posix1e_acl_entry_compare);
106 
107 	return (0);
108 }
109 
110 /*
111  * acl_posix1e -- in what situations should we acl_sort before submission?
112  * We apply posix1e ACL semantics for any ACL of type ACL_TYPE_ACCESS or
113  * ACL_TYPE_DEFAULT
114  */
115 int
116 _posix1e_acl(acl_t acl, acl_type_t type)
117 {
118 
119 	return ((type == ACL_TYPE_ACCESS) || (type == ACL_TYPE_DEFAULT));
120 }
121 
122 /*
123  * _posix1e_acl_check -- given an ACL, check its validity.  This is mirrored
124  * from code in sys/kern/kern_acl.c, and if changes are made in one, they
125  * should be made in the other also.  This copy of acl_check is made
126  * available * in userland for the benefit of processes wanting to check ACLs
127  * for validity before submitting them to the kernel, or for performing
128  * in userland file system checking.  Needless to say, the kernel makes
129  * the real checks on calls to get/setacl.
130  *
131  * See the comments in kernel for explanation -- just briefly, it assumes
132  * an already sorted ACL, and checks based on that assumption.  The
133  * POSIX.1e interface, acl_valid(), will perform the sort before calling
134  * this.  Returns 0 on success, EINVAL on failure.
135  */
136 int
137 _posix1e_acl_check(acl_t acl)
138 {
139 	struct acl *acl_int;
140 	struct acl_entry	*entry; 	/* current entry */
141 	uid_t	obj_uid=-1, obj_gid=-1, highest_uid=0, highest_gid=0;
142 	int	stage = ACL_USER_OBJ;
143 	int	i = 0;
144 	int	count_user_obj=0, count_user=0, count_group_obj=0,
145 		count_group=0, count_mask=0, count_other=0;
146 
147 	acl_int = &acl->ats_acl;
148 
149 	/* printf("_posix1e_acl_check: checking acl with %d entries\n",
150 	    acl->acl_cnt); */
151 	while (i < acl_int->acl_cnt) {
152 		entry = &acl_int->acl_entry[i];
153 
154 		if ((entry->ae_perm | ACL_PERM_BITS) != ACL_PERM_BITS)
155 			return (EINVAL);
156 
157 		switch(entry->ae_tag) {
158 		case ACL_USER_OBJ:
159 			/* printf("_posix1e_acl_check: %d: ACL_USER_OBJ\n",
160 			    i); */
161 			if (stage > ACL_USER_OBJ)
162 				return (EINVAL);
163 			stage = ACL_USER;
164 			count_user_obj++;
165 			obj_uid = entry->ae_id;
166 			break;
167 
168 		case ACL_USER:
169 			/* printf("_posix1e_acl_check: %d: ACL_USER\n", i); */
170 			if (stage > ACL_USER)
171 				return (EINVAL);
172 			stage = ACL_USER;
173 			if (entry->ae_id == obj_uid)
174 				return (EINVAL);
175 			if (count_user && (entry->ae_id <= highest_uid))
176 				return (EINVAL);
177 			highest_uid = entry->ae_id;
178 			count_user++;
179 			break;
180 
181 		case ACL_GROUP_OBJ:
182 			/* printf("_posix1e_acl_check: %d: ACL_GROUP_OBJ\n",
183 			    i); */
184 			if (stage > ACL_GROUP_OBJ)
185 				return (EINVAL);
186 			stage = ACL_GROUP;
187 			count_group_obj++;
188 			obj_gid = entry->ae_id;
189 			break;
190 
191 		case ACL_GROUP:
192 			/* printf("_posix1e_acl_check: %d: ACL_GROUP\n", i); */
193 			if (stage > ACL_GROUP)
194 				return (EINVAL);
195 			stage = ACL_GROUP;
196 			if (entry->ae_id == obj_gid)
197 				return (EINVAL);
198 			if (count_group && (entry->ae_id <= highest_gid))
199 				return (EINVAL);
200 			highest_gid = entry->ae_id;
201 			count_group++;
202 			break;
203 
204 		case ACL_MASK:
205 			/* printf("_posix1e_acl_check: %d: ACL_MASK\n", i); */
206 			if (stage > ACL_MASK)
207 				return (EINVAL);
208 			stage = ACL_MASK;
209 			count_mask++;
210 			break;
211 
212 		case ACL_OTHER:
213 			/* printf("_posix1e_acl_check: %d: ACL_OTHER\n", i); */
214 			if (stage > ACL_OTHER)
215 				return (EINVAL);
216 			stage = ACL_OTHER;
217 			count_other++;
218 			break;
219 
220 		default:
221 			/* printf("_posix1e_acl_check: %d: INVALID\n", i); */
222 			return (EINVAL);
223 		}
224 		i++;
225 	}
226 
227 	if (count_user_obj != 1)
228 		return (EINVAL);
229 
230 	if (count_group_obj != 1)
231 		return (EINVAL);
232 
233 	if (count_mask != 0 && count_mask != 1)
234 		return (EINVAL);
235 
236 	if (count_other != 1)
237 		return (EINVAL);
238 
239 	return (0);
240 }
241 
242 
243 /*
244  * Given a uid/gid, return a username/groupname for the text form of an ACL
245  * XXX NOT THREAD SAFE, RELIES ON GETPWUID, GETGRGID
246  * XXX USES *PW* AND *GR* WHICH ARE STATEFUL AND THEREFORE THIS ROUTINE
247  * MAY HAVE SIDE-EFFECTS
248  */
249 int
250 _posix1e_acl_id_to_name(acl_tag_t tag, uid_t id, ssize_t buf_len, char *buf)
251 {
252 	struct group	*g;
253 	struct passwd	*p;
254 	int	i;
255 
256 	switch(tag) {
257 	case ACL_USER:
258 		p = getpwuid(id);
259 		if (!p)
260 			i = snprintf(buf, buf_len, "%d", id);
261 		else
262 			i = snprintf(buf, buf_len, "%s", p->pw_name);
263 
264 		if (i < 0 || i >= buf_len) {
265 			errno = ENOMEM;
266 			return (-1);
267 		}
268 		return (0);
269 
270 	case ACL_GROUP:
271 		g = getgrgid(id);
272 		if (g == NULL)
273 			i = snprintf(buf, buf_len, "%d", id);
274 		else
275 			i = snprintf(buf, buf_len, "%s", g->gr_name);
276 
277 		if (i < 0 || i >= buf_len) {
278 			errno = ENOMEM;
279 			return (-1);
280 		}
281 		return (0);
282 
283 	default:
284 		return (EINVAL);
285 	}
286 }
287 
288 
289 /*
290  * Given a username/groupname from a text form of an ACL, return the uid/gid
291  * XXX NOT THREAD SAFE, RELIES ON GETPWNAM, GETGRNAM
292  * XXX USES *PW* AND *GR* WHICH ARE STATEFUL AND THEREFORE THIS ROUTINE
293  * MAY HAVE SIDE-EFFECTS
294  *
295  * XXX currently doesn't deal correctly with a numeric uid being passed
296  * instead of a username.  What is correct behavior here?  Check chown.
297  */
298 int
299 _posix1e_acl_name_to_id(acl_tag_t tag, char *name, uid_t *id)
300 {
301 	struct group	*g;
302 	struct passwd	*p;
303 	unsigned long	l;
304 	char 		*endp;
305 
306 	switch(tag) {
307 	case ACL_USER:
308 		p = getpwnam(name);
309 		if (p == NULL) {
310 			l = strtoul(name, &endp, 0);
311 			if (*endp != '\0' || l != (unsigned long)(uid_t)l) {
312 				errno = EINVAL;
313 				return (-1);
314 			}
315 			*id = (uid_t)l;
316 			return (0);
317 		}
318 		*id = p->pw_uid;
319 		return (0);
320 
321 	case ACL_GROUP:
322 		g = getgrnam(name);
323 		if (g == NULL) {
324 			l = strtoul(name, &endp, 0);
325 			if (*endp != '\0' || l != (unsigned long)(gid_t)l) {
326 				errno = EINVAL;
327 				return (-1);
328 			}
329 			*id = (gid_t)l;
330 			return (0);
331 		}
332 		*id = g->gr_gid;
333 		return (0);
334 
335 	default:
336 		return (EINVAL);
337 	}
338 }
339 
340 
341 /*
342  * Given a right-shifted permission (i.e., direct ACL_PERM_* mask), fill
343  * in a string describing the permissions.
344  */
345 int
346 _posix1e_acl_perm_to_string(acl_perm_t perm, ssize_t buf_len, char *buf)
347 {
348 
349 	if (buf_len < _POSIX1E_ACL_STRING_PERM_MAXSIZE + 1) {
350 		errno = ENOMEM;
351 		return (-1);
352 	}
353 
354 	if ((perm | ACL_PERM_BITS) != ACL_PERM_BITS) {
355 		errno = EINVAL;
356 		return (-1);
357 	}
358 
359 	buf[3] = 0;	/* null terminate */
360 
361 	if (perm & ACL_READ)
362 		buf[0] = ACL_STRING_PERM_READ;
363 	else
364 		buf[0] = ACL_STRING_PERM_NONE;
365 
366 	if (perm & ACL_WRITE)
367 		buf[1] = ACL_STRING_PERM_WRITE;
368 	else
369 		buf[1] = ACL_STRING_PERM_NONE;
370 
371 	if (perm & ACL_EXECUTE)
372 		buf[2] = ACL_STRING_PERM_EXEC;
373 	else
374 		buf[2] = ACL_STRING_PERM_NONE;
375 
376 	return (0);
377 }
378 
379 /*
380  * given a string, return a permission describing it
381  */
382 int
383 _posix1e_acl_string_to_perm(char *string, acl_perm_t *perm)
384 {
385 	acl_perm_t	myperm = ACL_PERM_NONE;
386 	char	*ch;
387 
388 	ch = string;
389 	while (*ch) {
390 		switch(*ch) {
391 		case ACL_STRING_PERM_READ:
392 			myperm |= ACL_READ;
393 			break;
394 		case ACL_STRING_PERM_WRITE:
395 			myperm |= ACL_WRITE;
396 			break;
397 		case ACL_STRING_PERM_EXEC:
398 			myperm |= ACL_EXECUTE;
399 			break;
400 		case ACL_STRING_PERM_NONE:
401 			break;
402 		default:
403 			return (EINVAL);
404 		}
405 		ch++;
406 	}
407 
408 	*perm = myperm;
409 	return (0);
410 }
411 
412 /*
413  * Add an ACL entry without doing much checking, et al
414  */
415 int
416 _posix1e_acl_add_entry(acl_t acl, acl_tag_t tag, uid_t id, acl_perm_t perm)
417 {
418 	struct acl		*acl_int;
419 	struct acl_entry	*e;
420 
421 	acl_int = &acl->ats_acl;
422 
423 	if (acl_int->acl_cnt >= ACL_MAX_ENTRIES) {
424 		errno = ENOMEM;
425 		return (-1);
426 	}
427 
428 	e = &(acl_int->acl_entry[acl_int->acl_cnt]);
429 	e->ae_perm = perm;
430 	e->ae_tag = tag;
431 	e->ae_id = id;
432 	acl_int->acl_cnt++;
433 
434 	return (0);
435 }
436