]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - lib/libc/posix1e/acl_support.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.git] / lib / libc / posix1e / acl_support.c
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$");
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 #include <string.h>
44 #include <assert.h>
45
46 #include "acl_support.h"
47
48 #define ACL_STRING_PERM_WRITE   'w'
49 #define ACL_STRING_PERM_READ    'r'
50 #define ACL_STRING_PERM_EXEC    'x'
51 #define ACL_STRING_PERM_NONE    '-'
52
53 /*
54  * Return 0, if both ACLs are identical.
55  */
56 int
57 _acl_differs(const acl_t a, const acl_t b)
58 {
59         int i;
60         struct acl_entry *entrya, *entryb;
61
62         assert(_acl_brand(a) == _acl_brand(b));
63         assert(_acl_brand(a) != ACL_BRAND_UNKNOWN);
64         assert(_acl_brand(b) != ACL_BRAND_UNKNOWN);
65
66         if (a->ats_acl.acl_cnt != b->ats_acl.acl_cnt)
67                 return (1);
68
69         for (i = 0; i < b->ats_acl.acl_cnt; i++) {
70                 entrya = &(a->ats_acl.acl_entry[i]);
71                 entryb = &(b->ats_acl.acl_entry[i]);
72
73                 if (entrya->ae_tag != entryb->ae_tag ||
74                     entrya->ae_id != entryb->ae_id ||
75                     entrya->ae_perm != entryb->ae_perm ||
76                     entrya->ae_entry_type != entryb->ae_entry_type ||
77                     entrya->ae_flags != entryb->ae_flags)
78                         return (1);
79         }
80
81         return (0);
82 }
83                     
84 /*
85  * _posix1e_acl_entry_compare -- compare two acl_entry structures to
86  * determine the order they should appear in.  Used by _posix1e_acl_sort to
87  * sort ACL entries into the kernel-desired order -- i.e., the order useful
88  * for evaluation and O(n) validity checking.  Beter to have an O(nlogn) sort
89  * in userland and an O(n) in kernel than to have both in kernel.
90  */
91 typedef int (*compare)(const void *, const void *);
92 static int
93 _posix1e_acl_entry_compare(struct acl_entry *a, struct acl_entry *b)
94 {
95
96         assert(_entry_brand(a) == ACL_BRAND_POSIX);
97         assert(_entry_brand(b) == ACL_BRAND_POSIX);
98
99         /*
100          * First, sort between tags -- conveniently defined in the correct
101          * order for verification.
102          */
103         if (a->ae_tag < b->ae_tag)
104                 return (-1);
105         if (a->ae_tag > b->ae_tag)
106                 return (1);
107
108         /*
109          * Next compare uids/gids on appropriate types.
110          */
111
112         if (a->ae_tag == ACL_USER || a->ae_tag == ACL_GROUP) {
113                 if (a->ae_id < b->ae_id)
114                         return (-1);
115                 if (a->ae_id > b->ae_id)
116                         return (1);
117
118                 /* shouldn't be equal, fall through to the invalid case */
119         }
120
121         /*
122          * Don't know how to sort multiple entries of the rest--either it's
123          * a bad entry, or there shouldn't be more than one.  Ignore and the
124          * validity checker can get it later.
125          */
126         return (0);
127 }
128
129 /*
130  * _posix1e_acl_sort -- sort ACL entries in POSIX.1e-formatted ACLs
131  * Give the opportunity to fail, although we don't currently have a way
132  * to fail.
133  */
134 int
135 _posix1e_acl_sort(acl_t acl)
136 {
137         struct acl *acl_int;
138
139         acl_int = &acl->ats_acl;
140
141         qsort(&acl_int->acl_entry[0], acl_int->acl_cnt,
142             sizeof(struct acl_entry), (compare) _posix1e_acl_entry_compare);
143
144         return (0);
145 }
146
147 /*
148  * acl_posix1e -- in what situations should we acl_sort before submission?
149  * We apply posix1e ACL semantics for any ACL of type ACL_TYPE_ACCESS or
150  * ACL_TYPE_DEFAULT
151  */
152 int
153 _posix1e_acl(acl_t acl, acl_type_t type)
154 {
155
156         if (_acl_brand(acl) != ACL_BRAND_POSIX)
157                 return (0);
158
159         return ((type == ACL_TYPE_ACCESS) || (type == ACL_TYPE_DEFAULT));
160 }
161
162 /*
163  * _posix1e_acl_check -- given an ACL, check its validity.  This is mirrored
164  * from code in sys/kern/kern_acl.c, and if changes are made in one, they
165  * should be made in the other also.  This copy of acl_check is made
166  * available * in userland for the benefit of processes wanting to check ACLs
167  * for validity before submitting them to the kernel, or for performing 
168  * in userland file system checking.  Needless to say, the kernel makes
169  * the real checks on calls to get/setacl.
170  *
171  * See the comments in kernel for explanation -- just briefly, it assumes
172  * an already sorted ACL, and checks based on that assumption.  The
173  * POSIX.1e interface, acl_valid(), will perform the sort before calling
174  * this.  Returns 0 on success, EINVAL on failure.
175  */
176 int
177 _posix1e_acl_check(acl_t acl)
178 {
179         struct acl *acl_int;
180         struct acl_entry        *entry;         /* current entry */
181         uid_t   highest_uid=0, highest_gid=0;
182         int     stage = ACL_USER_OBJ;
183         int     i = 0;
184         int     count_user_obj=0, count_user=0, count_group_obj=0,
185                 count_group=0, count_mask=0, count_other=0;
186
187         acl_int = &acl->ats_acl;
188
189         /* printf("_posix1e_acl_check: checking acl with %d entries\n",
190             acl->acl_cnt); */
191         while (i < acl_int->acl_cnt) {
192                 entry = &acl_int->acl_entry[i];
193
194                 if ((entry->ae_perm | ACL_PERM_BITS) != ACL_PERM_BITS)
195                         return (EINVAL);
196
197                 switch(entry->ae_tag) {
198                 case ACL_USER_OBJ:
199                         /* printf("_posix1e_acl_check: %d: ACL_USER_OBJ\n",
200                             i); */
201                         if (stage > ACL_USER_OBJ)
202                                 return (EINVAL);
203                         stage = ACL_USER;
204                         count_user_obj++;
205                         break;
206         
207                 case ACL_USER:
208                         /* printf("_posix1e_acl_check: %d: ACL_USER\n", i); */
209                         if (stage > ACL_USER)
210                                 return (EINVAL);
211                         stage = ACL_USER;
212                         if (count_user && (entry->ae_id <= highest_uid))
213                                 return (EINVAL);
214                         highest_uid = entry->ae_id;
215                         count_user++;
216                         break;  
217         
218                 case ACL_GROUP_OBJ:
219                         /* printf("_posix1e_acl_check: %d: ACL_GROUP_OBJ\n",
220                             i); */
221                         if (stage > ACL_GROUP_OBJ)
222                                 return (EINVAL);
223                         stage = ACL_GROUP;
224                         count_group_obj++;
225                         break;
226         
227                 case ACL_GROUP:
228                         /* printf("_posix1e_acl_check: %d: ACL_GROUP\n", i); */
229                         if (stage > ACL_GROUP)
230                                 return (EINVAL);
231                         stage = ACL_GROUP;
232                         if (count_group && (entry->ae_id <= highest_gid))
233                                 return (EINVAL);
234                         highest_gid = entry->ae_id;
235                         count_group++;
236                         break;
237                         
238                 case ACL_MASK:
239                         /* printf("_posix1e_acl_check: %d: ACL_MASK\n", i); */
240                         if (stage > ACL_MASK)
241                                 return (EINVAL);
242                         stage = ACL_MASK;
243                         count_mask++;
244                         break;
245         
246                 case ACL_OTHER:
247                         /* printf("_posix1e_acl_check: %d: ACL_OTHER\n", i); */
248                         if (stage > ACL_OTHER)
249                                 return (EINVAL);
250                         stage = ACL_OTHER;
251                         count_other++;
252                         break;
253         
254                 default:
255                         /* printf("_posix1e_acl_check: %d: INVALID\n", i); */
256                         return (EINVAL);
257                 }
258                 i++;
259         }
260
261         if (count_user_obj != 1)
262                 return (EINVAL);
263         
264         if (count_group_obj != 1)
265                 return (EINVAL);
266
267         if (count_mask != 0 && count_mask != 1)
268                 return (EINVAL);
269
270         if (count_other != 1)
271                 return (EINVAL);
272
273         return (0);
274 }
275
276
277 /*
278  * Given a uid/gid, return a username/groupname for the text form of an ACL.
279  * Note that we truncate user and group names, rather than error out, as
280  * this is consistent with other tools manipulating user and group names.
281  * XXX NOT THREAD SAFE, RELIES ON GETPWUID, GETGRGID
282  * XXX USES *PW* AND *GR* WHICH ARE STATEFUL AND THEREFORE THIS ROUTINE
283  * MAY HAVE SIDE-EFFECTS
284  */
285 int
286 _posix1e_acl_id_to_name(acl_tag_t tag, uid_t id, ssize_t buf_len, char *buf,
287     int flags)
288 {
289         struct group    *g;
290         struct passwd   *p;
291         int     i;
292
293         switch(tag) {
294         case ACL_USER:
295                 if (flags & ACL_TEXT_NUMERIC_IDS)
296                         p = NULL;
297                 else
298                         p = getpwuid(id);
299                 if (!p)
300                         i = snprintf(buf, buf_len, "%d", id);
301                 else
302                         i = snprintf(buf, buf_len, "%s", p->pw_name);
303
304                 if (i < 0) {
305                         errno = ENOMEM;
306                         return (-1);
307                 }
308                 return (0);
309
310         case ACL_GROUP:
311                 if (flags & ACL_TEXT_NUMERIC_IDS)
312                         g = NULL;
313                 else
314                         g = getgrgid(id);
315                 if (g == NULL) 
316                         i = snprintf(buf, buf_len, "%d", id);
317                 else
318                         i = snprintf(buf, buf_len, "%s", g->gr_name);
319
320                 if (i < 0) {
321                         errno = ENOMEM;
322                         return (-1);
323                 }
324                 return (0);
325
326         default:
327                 return (EINVAL);
328         }
329 }
330
331 /*
332  * Given a right-shifted permission (i.e., direct ACL_PERM_* mask), fill
333  * in a string describing the permissions.
334  */
335 int
336 _posix1e_acl_perm_to_string(acl_perm_t perm, ssize_t buf_len, char *buf)
337 {
338
339         if (buf_len < _POSIX1E_ACL_STRING_PERM_MAXSIZE + 1) {
340                 errno = ENOMEM;
341                 return (-1);
342         }
343
344         if ((perm | ACL_PERM_BITS) != ACL_PERM_BITS) {
345                 errno = EINVAL;
346                 return (-1);
347         }
348
349         buf[3] = 0;     /* null terminate */
350
351         if (perm & ACL_READ)
352                 buf[0] = ACL_STRING_PERM_READ;
353         else
354                 buf[0] = ACL_STRING_PERM_NONE;
355
356         if (perm & ACL_WRITE)
357                 buf[1] = ACL_STRING_PERM_WRITE;
358         else
359                 buf[1] = ACL_STRING_PERM_NONE;
360
361         if (perm & ACL_EXECUTE)
362                 buf[2] = ACL_STRING_PERM_EXEC;
363         else
364                 buf[2] = ACL_STRING_PERM_NONE;
365
366         return (0);
367 }
368
369 /*
370  * given a string, return a permission describing it
371  */
372 int
373 _posix1e_acl_string_to_perm(char *string, acl_perm_t *perm)
374 {
375         acl_perm_t      myperm = ACL_PERM_NONE;
376         char    *ch;
377
378         ch = string;
379         while (*ch) {
380                 switch(*ch) {
381                 case ACL_STRING_PERM_READ:
382                         myperm |= ACL_READ;
383                         break;
384                 case ACL_STRING_PERM_WRITE:
385                         myperm |= ACL_WRITE;
386                         break;
387                 case ACL_STRING_PERM_EXEC:
388                         myperm |= ACL_EXECUTE;
389                         break;
390                 case ACL_STRING_PERM_NONE:
391                         break;
392                 default:
393                         return (EINVAL);
394                 }
395                 ch++;
396         }
397
398         *perm = myperm;
399         return (0);
400 }
401
402 /*
403  * Add an ACL entry without doing much checking, et al
404  */
405 int
406 _posix1e_acl_add_entry(acl_t acl, acl_tag_t tag, uid_t id, acl_perm_t perm)
407 {
408         struct acl              *acl_int;
409         struct acl_entry        *e;
410
411         acl_int = &acl->ats_acl;
412
413         if (acl_int->acl_cnt >= ACL_MAX_ENTRIES) {
414                 errno = ENOMEM;
415                 return (-1);
416         }
417
418         e = &(acl_int->acl_entry[acl_int->acl_cnt]);
419         e->ae_perm = perm;
420         e->ae_tag = tag;
421         e->ae_id = id;
422         acl_int->acl_cnt++;
423
424         return (0);
425 }
426
427 /*
428  * Convert "old" type - ACL_TYPE_{ACCESS,DEFAULT}_OLD - into its "new"
429  * counterpart.  It's neccessary for the old (pre-NFSv4 ACLs) binaries
430  * to work with new libc and kernel.  Fixing 'type' for old binaries with
431  * old libc and new kernel is being done by kern/vfs_acl.c:type_unold().
432  */
433 int
434 _acl_type_unold(acl_type_t type)
435 {
436
437         switch (type) {
438         case ACL_TYPE_ACCESS_OLD:
439                 return (ACL_TYPE_ACCESS);
440         case ACL_TYPE_DEFAULT_OLD:
441                 return (ACL_TYPE_DEFAULT);
442         default:
443                 return (type);
444         }
445 }
446
447 char *
448 string_skip_whitespace(char *string)
449 {
450
451         while (*string && ((*string == ' ') || (*string == '\t')))
452                 string++;
453
454         return (string);
455 }
456
457 void
458 string_trim_trailing_whitespace(char *string)
459 {
460         char    *end;
461
462         if (*string == '\0')
463                 return;
464
465         end = string + strlen(string) - 1;
466
467         while (end != string) {
468                 if ((*end == ' ') || (*end == '\t')) {
469                         *end = '\0';
470                         end--;
471                 } else {
472                         return;
473                 }
474         }
475
476         return;
477 }