acl_support.c revision 56274
1/*-
2 * Copyright (c) 1999 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 *	$FreeBSD: head/lib/libc/posix1e/acl_support.c 56274 2000-01-19 06:13:59Z rwatson $
27 */
28/*
29 * Support functionality for the POSIX.1e ACL interface
30 * These calls are intended only to be called within the library.
31 */
32
33#include <sys/types.h>
34#include <sys/acl.h>
35#include <errno.h>
36#include <grp.h>
37#include <pwd.h>
38#include <stdio.h>
39#include <stdlib.h>
40
41#include "acl_support.h"
42
43#define ACL_STRING_PERM_WRITE   'w'
44#define ACL_STRING_PERM_READ    'r'
45#define ACL_STRING_PERM_EXEC    'x'
46#define ACL_STRING_PERM_NONE    '-'
47
48/*
49 * acl_entry_compare -- compare two acl_entry structures to determine the
50 * order they should appear in.  Used by acl_sort to sort ACL entries into
51 * the kernel-desired order -- i.e., the order useful for evaluation and
52 * O(n) validity checking.  Beter to have an O(nlogn) sort in userland and
53 * an O(n) in kernel than to have both in kernel.
54 */
55typedef int (*compare)(const void *, const void *);
56static int
57acl_entry_compare(struct acl_entry *a, struct acl_entry *b)
58{
59	/*
60	 * First, sort between tags -- conveniently defined in the correct
61	 * order for verification.
62	 */
63	if (a->ae_tag < b->ae_tag)
64		return (-1);
65	if (a->ae_tag > b->ae_tag)
66		return (1);
67
68	/*
69	 * Next compare uids/gids on appropriate types.
70	 */
71
72	if (a->ae_tag == ACL_USER || a->ae_tag == ACL_GROUP) {
73		if (a->ae_id < b->ae_id)
74			return (-1);
75		if (a->ae_id > b->ae_id)
76			return (1);
77
78		/* shouldn't be equal, fall through to the invalid case */
79	}
80
81	/*
82	 * Don't know how to sort multiple entries of the rest--either it's
83	 * a bad entry, or there shouldn't be more than one.  Ignore and the
84	 * validity checker can get it later.
85	 */
86	return (0);
87}
88
89
90/*
91 * acl_sort -- sort ACL entries.
92 * Give the opportunity to fail, althouh we don't currently have a way
93 * to fail.
94 */
95int
96acl_sort(acl_t acl)
97{
98
99	qsort(&acl->acl_entry[0], acl->acl_cnt, sizeof(struct acl_entry),
100	    (compare) acl_entry_compare);
101
102	return (0);
103}
104
105
106/*
107 * acl_posix1e -- use a heuristic to determine if this is a POSIX.1e
108 * semantics ACL.  This will be used by other routines to determine if
109 * they should call acl_sort() on the ACL before submitting to the kernel,
110 * as the POSIX.1e ACL semantics code requires sorted ACL submission.
111 * Also, acl_valid will use this to determine if it understands the
112 * semantics enough to check that the ACL is correct.
113 */
114int
115acl_posix1e(acl_t acl)
116{
117	int	i;
118
119	/* assume it's POSIX.1e, and return 0 if otherwise */
120
121	for (i = 0; i < acl->acl_cnt; i++) {
122		/* is the tag type POSIX.1e? */
123		switch(acl->acl_entry[i].ae_tag) {
124		case ACL_USER_OBJ:
125		case ACL_USER:
126		case ACL_GROUP_OBJ:
127		case ACL_GROUP:
128		case ACL_MASK:
129		case ACL_OTHER:
130			break;
131
132		default:
133			return (0);
134		}
135
136		/* are the permissions POSIX.1e, or FreeBSD extensions? */
137		if (((acl->acl_entry[i].ae_perm | ACL_POSIX1E_BITS) !=
138		    ACL_POSIX1E_BITS) &&
139		    ((acl->acl_entry[i].ae_perm | ACL_PERM_BITS) !=
140		    ACL_PERM_BITS))
141			return (0);
142	}
143
144	return(1);
145}
146
147
148/*
149 * acl_check -- given an ACL, check its validity.  This is mirrored from
150 * code in sys/kern/kern_acl.c, and if changes are made in one, they should
151 * be made in the other also.  This copy of acl_check is made available
152 * in userland for the benefit of processes wanting to check ACLs for
153 * validity before submitting them to the kernel, or for performing
154 * in userland file system checking.  Needless to say, the kernel makes
155 * the real checks on calls to get/setacl.
156 *
157 * See the comments in kernel for explanation -- just briefly, it assumes
158 * an already sorted ACL, and checks based on that assumption.  The
159 * POSIX.1e interface, acl_valid(), will perform the sort before calling
160 * this.  Returns 0 on success, EINVAL on failure.
161 */
162int
163acl_check(struct acl *acl)
164{
165	struct acl_entry	*entry; 	/* current entry */
166	uid_t	obj_uid=-1, obj_gid=-1, highest_uid=0, highest_gid=0;
167	int	stage = ACL_USER_OBJ;
168	int	i = 0;
169	int	count_user_obj=0, count_user=0, count_group_obj=0,
170		count_group=0, count_mask=0, count_other=0;
171
172	/* printf("acl_check: checking acl with %d entries\n", acl->acl_cnt); */
173	while (i < acl->acl_cnt) {
174
175		entry = &acl->acl_entry[i];
176
177		if ((entry->ae_perm | ACL_PERM_BITS) != ACL_PERM_BITS)
178			return (EINVAL);
179
180		switch(entry->ae_tag) {
181		case ACL_USER_OBJ:
182			/* printf("acl_check: %d: ACL_USER_OBJ\n", i); */
183			if (stage > ACL_USER_OBJ)
184				return (EINVAL);
185			stage = ACL_USER;
186			count_user_obj++;
187			obj_uid = entry->ae_id;
188			break;
189
190		case ACL_USER:
191			/* printf("acl_check: %d: ACL_USER\n", i); */
192			if (stage > ACL_USER)
193				return (EINVAL);
194			stage = ACL_USER;
195			if (entry->ae_id == obj_uid)
196				return (EINVAL);
197			if (count_user && (entry->ae_id <= highest_uid))
198				return (EINVAL);
199			highest_uid = entry->ae_id;
200			count_user++;
201			break;
202
203		case ACL_GROUP_OBJ:
204			/* printf("acl_check: %d: ACL_GROUP_OBJ\n", i); */
205			if (stage > ACL_GROUP_OBJ)
206				return (EINVAL);
207			stage = ACL_GROUP;
208			count_group_obj++;
209			obj_gid = entry->ae_id;
210			break;
211
212		case ACL_GROUP:
213			/* printf("acl_check: %d: ACL_GROUP\n", i); */
214			if (stage > ACL_GROUP)
215				return (EINVAL);
216			stage = ACL_GROUP;
217			if (entry->ae_id == obj_gid)
218				return (EINVAL);
219			if (count_group && (entry->ae_id <= highest_gid))
220				return (EINVAL);
221			highest_gid = entry->ae_id;
222			count_group++;
223			break;
224
225		case ACL_MASK:
226			/* printf("acl_check: %d: ACL_MASK\n", i); */
227			if (stage > ACL_MASK)
228				return (EINVAL);
229			stage = ACL_MASK;
230			count_mask++;
231			break;
232
233		case ACL_OTHER:
234			/* printf("acl_check: %d: ACL_OTHER\n", i); */
235			if (stage > ACL_OTHER)
236				return (EINVAL);
237			stage = ACL_OTHER;
238			count_other++;
239			break;
240
241		default:
242			/* printf("acl_check: %d: INVALID\n", i); */
243			return (EINVAL);
244		}
245		i++;
246	}
247
248	if (count_user_obj != 1)
249		return (EINVAL);
250
251	if (count_group_obj != 1)
252		return (EINVAL);
253
254	if (count_mask != 0 && count_mask != 1)
255		return (EINVAL);
256
257	if (count_other != 1)
258		return (EINVAL);
259
260	return (0);
261}
262
263
264/*
265 * Given a uid/gid, return a username/groupname for the text form of an ACL
266 * XXX NOT THREAD SAFE, RELIES ON GETPWUID, GETGRGID
267 * XXX USES *PW* AND *GR* WHICH ARE STATEFUL AND THEREFORE THIS ROUTINE
268 * MAY HAVE SIDE-EFFECTS
269 */
270int
271acl_id_to_name(acl_tag_t tag, uid_t id, ssize_t buf_len, char *buf)
272{
273	struct group	*g;
274	struct passwd	*p;
275	int	i;
276
277	switch(tag) {
278	case ACL_USER:
279		p = getpwuid(id);
280		if (!p)
281			i = snprintf(buf, buf_len, "%d", id);
282		else
283			i = snprintf(buf, buf_len, "%s", p->pw_name);
284
285		if (i >= buf_len) {
286			errno = ENOMEM;
287			return (-1);
288		}
289		return (0);
290
291	case ACL_GROUP:
292		g = getgrgid(id);
293		if (!g)
294			i = snprintf(buf, buf_len, "%d", id);
295		else
296			i = snprintf(buf, buf_len, "%s", g->gr_name);
297
298		if (i >= buf_len) {
299			errno = ENOMEM;
300			return (-1);
301		}
302		return (0);
303
304	default:
305		return (EINVAL);
306	}
307}
308
309
310/*
311 * Given a username/groupname from a text form of an ACL, return the uid/gid
312 * XXX NOT THREAD SAFE, RELIES ON GETPWNAM, GETGRNAM
313 * XXX USES *PW* AND *GR* WHICH ARE STATEFUL AND THEREFORE THIS ROUTINE
314 * MAY HAVE SIDE-EFFECTS
315 *
316 * XXX currently doesn't deal correctly with a numeric uid being passed
317 * instead of a username.  What is correct behavior here?  Check chown.
318 */
319int
320acl_name_to_id(acl_tag_t tag, char *name, uid_t *id)
321{
322	struct group	*g;
323	struct passwd	*p;
324
325	switch(tag) {
326	case ACL_USER:
327		p = getpwnam(name);
328		if (!p) {
329			errno = EINVAL;
330			return (-1);
331		}
332		*id = p->pw_uid;
333		return (0);
334
335	case ACL_GROUP:
336		g = getgrnam(name);
337		if (g) {
338			errno = EINVAL;
339			return (-1);
340		}
341		*id = g->gr_gid;
342		return (0);
343
344	default:
345		return (EINVAL);
346	}
347}
348
349
350/*
351 * Given a right-shifted permission (i.e., direct ACL_PERM_* mask), fill
352 * in a string describing the permissions.
353 */
354int
355acl_perm_to_string(acl_perm_t perm, ssize_t buf_len, char *buf)
356{
357
358	if (buf_len < ACL_STRING_PERM_MAXSIZE + 1) {
359		errno = ENOMEM;
360		return (-1);
361	}
362
363	if ((perm | ACL_PERM_BITS) != ACL_PERM_BITS) {
364		errno = EINVAL;
365		return (-1);
366	}
367
368	buf[3] = 0;	/* null terminate */
369
370	if (perm & ACL_PERM_READ)
371		buf[0] = ACL_STRING_PERM_READ;
372	else
373		buf[0] = ACL_STRING_PERM_NONE;
374
375	if (perm & ACL_PERM_WRITE)
376		buf[1] = ACL_STRING_PERM_WRITE;
377	else
378		buf[1] = ACL_STRING_PERM_NONE;
379
380	if (perm & ACL_PERM_EXEC)
381		buf[2] = ACL_STRING_PERM_EXEC;
382	else
383		buf[2] = ACL_STRING_PERM_NONE;
384
385	return (0);
386}
387
388
389/*
390 * given a string, return a permission describing it
391 */
392int
393acl_string_to_perm(char *string, acl_perm_t *perm)
394{
395	acl_perm_t	myperm = ACL_PERM_NONE;
396	char	*ch;
397
398	ch = string;
399	while (*ch) {
400		switch(*ch) {
401		case ACL_STRING_PERM_READ:
402			myperm |= ACL_PERM_READ;
403			break;
404		case ACL_STRING_PERM_WRITE:
405			myperm |= ACL_PERM_WRITE;
406			break;
407		case ACL_STRING_PERM_EXEC:
408			myperm |= ACL_PERM_EXEC;
409			break;
410		case ACL_STRING_PERM_NONE:
411			break;
412		default:
413			return (EINVAL);
414		}
415		ch++;
416	}
417
418	*perm = myperm;
419	return (0);
420}
421
422
423
424/*
425 * Add an ACL entry without doing much checking, et al
426 */
427int
428acl_add_entry(acl_t acl, acl_tag_t tag, uid_t id, acl_perm_t perm)
429{
430	struct acl_entry	*e;
431
432	if (acl->acl_cnt >= ACL_MAX_ENTRIES) {
433		errno = ENOMEM;
434		return (-1);
435	}
436
437	e = &(acl->acl_entry[acl->acl_cnt]);
438	e->ae_perm = perm;
439	e->ae_tag = tag;
440	e->ae_id = id;
441	acl->acl_cnt++;
442
443	return (0);
444}
445
446
447
448
449