acl_support.c revision 180493
1/*-
2 * Copyright (c) 1999-2001, 2008 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: head/lib/libc/posix1e/acl_support.c 180493 2008-07-13 16:37:51Z rwatson $");
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 */
58typedef int (*compare)(const void *, const void *);
59static 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 */
97int
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 */
115int
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 */
136int
137_posix1e_acl_check(acl_t acl)
138{
139	struct acl *acl_int;
140	struct acl_entry	*entry; 	/* current entry */
141	uid_t	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			break;
166
167		case ACL_USER:
168			/* printf("_posix1e_acl_check: %d: ACL_USER\n", i); */
169			if (stage > ACL_USER)
170				return (EINVAL);
171			stage = ACL_USER;
172			if (count_user && (entry->ae_id <= highest_uid))
173				return (EINVAL);
174			highest_uid = entry->ae_id;
175			count_user++;
176			break;
177
178		case ACL_GROUP_OBJ:
179			/* printf("_posix1e_acl_check: %d: ACL_GROUP_OBJ\n",
180			    i); */
181			if (stage > ACL_GROUP_OBJ)
182				return (EINVAL);
183			stage = ACL_GROUP;
184			count_group_obj++;
185			break;
186
187		case ACL_GROUP:
188			/* printf("_posix1e_acl_check: %d: ACL_GROUP\n", i); */
189			if (stage > ACL_GROUP)
190				return (EINVAL);
191			stage = ACL_GROUP;
192			if (count_group && (entry->ae_id <= highest_gid))
193				return (EINVAL);
194			highest_gid = entry->ae_id;
195			count_group++;
196			break;
197
198		case ACL_MASK:
199			/* printf("_posix1e_acl_check: %d: ACL_MASK\n", i); */
200			if (stage > ACL_MASK)
201				return (EINVAL);
202			stage = ACL_MASK;
203			count_mask++;
204			break;
205
206		case ACL_OTHER:
207			/* printf("_posix1e_acl_check: %d: ACL_OTHER\n", i); */
208			if (stage > ACL_OTHER)
209				return (EINVAL);
210			stage = ACL_OTHER;
211			count_other++;
212			break;
213
214		default:
215			/* printf("_posix1e_acl_check: %d: INVALID\n", i); */
216			return (EINVAL);
217		}
218		i++;
219	}
220
221	if (count_user_obj != 1)
222		return (EINVAL);
223
224	if (count_group_obj != 1)
225		return (EINVAL);
226
227	if (count_mask != 0 && count_mask != 1)
228		return (EINVAL);
229
230	if (count_other != 1)
231		return (EINVAL);
232
233	return (0);
234}
235
236
237/*
238 * Given a uid/gid, return a username/groupname for the text form of an ACL.
239 * Note that we truncate user and group names, rather than error out, as
240 * this is consistent with other tools manipulating user and group names.
241 * XXX NOT THREAD SAFE, RELIES ON GETPWUID, GETGRGID
242 * XXX USES *PW* AND *GR* WHICH ARE STATEFUL AND THEREFORE THIS ROUTINE
243 * MAY HAVE SIDE-EFFECTS
244 */
245int
246_posix1e_acl_id_to_name(acl_tag_t tag, uid_t id, ssize_t buf_len, char *buf)
247{
248	struct group	*g;
249	struct passwd	*p;
250	int	i;
251
252	switch(tag) {
253	case ACL_USER:
254		p = getpwuid(id);
255		if (!p)
256			i = snprintf(buf, buf_len, "%d", id);
257		else
258			i = snprintf(buf, buf_len, "%s", p->pw_name);
259
260		if (i < 0) {
261			errno = ENOMEM;
262			return (-1);
263		}
264		return (0);
265
266	case ACL_GROUP:
267		g = getgrgid(id);
268		if (g == NULL)
269			i = snprintf(buf, buf_len, "%d", id);
270		else
271			i = snprintf(buf, buf_len, "%s", g->gr_name);
272
273		if (i < 0) {
274			errno = ENOMEM;
275			return (-1);
276		}
277		return (0);
278
279	default:
280		return (EINVAL);
281	}
282}
283
284/*
285 * Given a right-shifted permission (i.e., direct ACL_PERM_* mask), fill
286 * in a string describing the permissions.
287 */
288int
289_posix1e_acl_perm_to_string(acl_perm_t perm, ssize_t buf_len, char *buf)
290{
291
292	if (buf_len < _POSIX1E_ACL_STRING_PERM_MAXSIZE + 1) {
293		errno = ENOMEM;
294		return (-1);
295	}
296
297	if ((perm | ACL_PERM_BITS) != ACL_PERM_BITS) {
298		errno = EINVAL;
299		return (-1);
300	}
301
302	buf[3] = 0;	/* null terminate */
303
304	if (perm & ACL_READ)
305		buf[0] = ACL_STRING_PERM_READ;
306	else
307		buf[0] = ACL_STRING_PERM_NONE;
308
309	if (perm & ACL_WRITE)
310		buf[1] = ACL_STRING_PERM_WRITE;
311	else
312		buf[1] = ACL_STRING_PERM_NONE;
313
314	if (perm & ACL_EXECUTE)
315		buf[2] = ACL_STRING_PERM_EXEC;
316	else
317		buf[2] = ACL_STRING_PERM_NONE;
318
319	return (0);
320}
321
322/*
323 * given a string, return a permission describing it
324 */
325int
326_posix1e_acl_string_to_perm(char *string, acl_perm_t *perm)
327{
328	acl_perm_t	myperm = ACL_PERM_NONE;
329	char	*ch;
330
331	ch = string;
332	while (*ch) {
333		switch(*ch) {
334		case ACL_STRING_PERM_READ:
335			myperm |= ACL_READ;
336			break;
337		case ACL_STRING_PERM_WRITE:
338			myperm |= ACL_WRITE;
339			break;
340		case ACL_STRING_PERM_EXEC:
341			myperm |= ACL_EXECUTE;
342			break;
343		case ACL_STRING_PERM_NONE:
344			break;
345		default:
346			return (EINVAL);
347		}
348		ch++;
349	}
350
351	*perm = myperm;
352	return (0);
353}
354
355/*
356 * Add an ACL entry without doing much checking, et al
357 */
358int
359_posix1e_acl_add_entry(acl_t acl, acl_tag_t tag, uid_t id, acl_perm_t perm)
360{
361	struct acl		*acl_int;
362	struct acl_entry	*e;
363
364	acl_int = &acl->ats_acl;
365
366	if (acl_int->acl_cnt >= ACL_MAX_ENTRIES) {
367		errno = ENOMEM;
368		return (-1);
369	}
370
371	e = &(acl_int->acl_entry[acl_int->acl_cnt]);
372	e->ae_perm = perm;
373	e->ae_tag = tag;
374	e->ae_id = id;
375	acl_int->acl_cnt++;
376
377	return (0);
378}
379