]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libposix1e/acl_support.c
libposix1e provides userland library calls for the POSIX.1e security
[FreeBSD/FreeBSD.git] / lib / libposix1e / acl_support.c
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$
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  */
55 typedef int (*compare)(const void *, const void *);
56 static int
57 acl_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  */
95 int
96 acl_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  */
114 int
115 acl_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  */
162 int
163 acl_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  */
270 int
271 acl_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  */
319 int
320 acl_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  */
354 int
355 acl_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  */
392 int
393 acl_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  */
427 int
428 acl_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 >= MAX_ACL_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