]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libc/posix1e/acl_support.c
This commit was generated by cvs2svn to compensate for changes in r162852,
[FreeBSD/FreeBSD.git] / lib / libc / posix1e / acl_support.c
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  * Note that we truncate user and group names, rather than error out, as
246  * this is consistent with other tools manipulating user and group names.
247  * XXX NOT THREAD SAFE, RELIES ON GETPWUID, GETGRGID
248  * XXX USES *PW* AND *GR* WHICH ARE STATEFUL AND THEREFORE THIS ROUTINE
249  * MAY HAVE SIDE-EFFECTS
250  */
251 int
252 _posix1e_acl_id_to_name(acl_tag_t tag, uid_t id, ssize_t buf_len, char *buf)
253 {
254         struct group    *g;
255         struct passwd   *p;
256         int     i;
257
258         switch(tag) {
259         case ACL_USER:
260                 p = getpwuid(id);
261                 if (!p)
262                         i = snprintf(buf, buf_len, "%d", id);
263                 else
264                         i = snprintf(buf, buf_len, "%s", p->pw_name);
265
266                 if (i < 0) {
267                         errno = ENOMEM;
268                         return (-1);
269                 }
270                 return (0);
271
272         case ACL_GROUP:
273                 g = getgrgid(id);
274                 if (g == NULL) 
275                         i = snprintf(buf, buf_len, "%d", id);
276                 else
277                         i = snprintf(buf, buf_len, "%s", g->gr_name);
278
279                 if (i < 0) {
280                         errno = ENOMEM;
281                         return (-1);
282                 }
283                 return (0);
284
285         default:
286                 return (EINVAL);
287         }
288 }
289
290
291 /*
292  * Given a username/groupname from a text form of an ACL, return the uid/gid
293  * XXX NOT THREAD SAFE, RELIES ON GETPWNAM, GETGRNAM
294  * XXX USES *PW* AND *GR* WHICH ARE STATEFUL AND THEREFORE THIS ROUTINE
295  * MAY HAVE SIDE-EFFECTS
296  *
297  * XXX currently doesn't deal correctly with a numeric uid being passed
298  * instead of a username.  What is correct behavior here?  Check chown.
299  */
300 int
301 _posix1e_acl_name_to_id(acl_tag_t tag, char *name, uid_t *id)
302 {
303         struct group    *g;
304         struct passwd   *p;
305         unsigned long   l;
306         char            *endp;
307
308         switch(tag) {
309         case ACL_USER:
310                 p = getpwnam(name);
311                 if (p == NULL) {
312                         l = strtoul(name, &endp, 0);
313                         if (*endp != '\0' || l != (unsigned long)(uid_t)l) {
314                                 errno = EINVAL;
315                                 return (-1);
316                         }
317                         *id = (uid_t)l;
318                         return (0);
319                 }
320                 *id = p->pw_uid;
321                 return (0);
322
323         case ACL_GROUP:
324                 g = getgrnam(name);
325                 if (g == NULL) {
326                         l = strtoul(name, &endp, 0);
327                         if (*endp != '\0' || l != (unsigned long)(gid_t)l) {
328                                 errno = EINVAL;
329                                 return (-1);
330                         }
331                         *id = (gid_t)l;
332                         return (0);
333                 }
334                 *id = g->gr_gid;
335                 return (0);
336
337         default:
338                 return (EINVAL);
339         }
340 }
341
342
343 /*
344  * Given a right-shifted permission (i.e., direct ACL_PERM_* mask), fill
345  * in a string describing the permissions.
346  */
347 int
348 _posix1e_acl_perm_to_string(acl_perm_t perm, ssize_t buf_len, char *buf)
349 {
350
351         if (buf_len < _POSIX1E_ACL_STRING_PERM_MAXSIZE + 1) {
352                 errno = ENOMEM;
353                 return (-1);
354         }
355
356         if ((perm | ACL_PERM_BITS) != ACL_PERM_BITS) {
357                 errno = EINVAL;
358                 return (-1);
359         }
360
361         buf[3] = 0;     /* null terminate */
362
363         if (perm & ACL_READ)
364                 buf[0] = ACL_STRING_PERM_READ;
365         else
366                 buf[0] = ACL_STRING_PERM_NONE;
367
368         if (perm & ACL_WRITE)
369                 buf[1] = ACL_STRING_PERM_WRITE;
370         else
371                 buf[1] = ACL_STRING_PERM_NONE;
372
373         if (perm & ACL_EXECUTE)
374                 buf[2] = ACL_STRING_PERM_EXEC;
375         else
376                 buf[2] = ACL_STRING_PERM_NONE;
377
378         return (0);
379 }
380
381 /*
382  * given a string, return a permission describing it
383  */
384 int
385 _posix1e_acl_string_to_perm(char *string, acl_perm_t *perm)
386 {
387         acl_perm_t      myperm = ACL_PERM_NONE;
388         char    *ch;
389
390         ch = string;
391         while (*ch) {
392                 switch(*ch) {
393                 case ACL_STRING_PERM_READ:
394                         myperm |= ACL_READ;
395                         break;
396                 case ACL_STRING_PERM_WRITE:
397                         myperm |= ACL_WRITE;
398                         break;
399                 case ACL_STRING_PERM_EXEC:
400                         myperm |= ACL_EXECUTE;
401                         break;
402                 case ACL_STRING_PERM_NONE:
403                         break;
404                 default:
405                         return (EINVAL);
406                 }
407                 ch++;
408         }
409
410         *perm = myperm;
411         return (0);
412 }
413
414 /*
415  * Add an ACL entry without doing much checking, et al
416  */
417 int
418 _posix1e_acl_add_entry(acl_t acl, acl_tag_t tag, uid_t id, acl_perm_t perm)
419 {
420         struct acl              *acl_int;
421         struct acl_entry        *e;
422
423         acl_int = &acl->ats_acl;
424
425         if (acl_int->acl_cnt >= ACL_MAX_ENTRIES) {
426                 errno = ENOMEM;
427                 return (-1);
428         }
429
430         e = &(acl_int->acl_entry[acl_int->acl_cnt]);
431         e->ae_perm = perm;
432         e->ae_tag = tag;
433         e->ae_id = id;
434         acl_int->acl_cnt++;
435
436         return (0);
437 }