]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - lib/libc/posix1e/acl_support.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.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  */
132 void
133 _posix1e_acl_sort(acl_t acl)
134 {
135         struct acl *acl_int;
136
137         acl_int = &acl->ats_acl;
138
139         qsort(&acl_int->acl_entry[0], acl_int->acl_cnt,
140             sizeof(struct acl_entry), (compare) _posix1e_acl_entry_compare);
141 }
142
143 /*
144  * acl_posix1e -- in what situations should we acl_sort before submission?
145  * We apply posix1e ACL semantics for any ACL of type ACL_TYPE_ACCESS or
146  * ACL_TYPE_DEFAULT
147  */
148 int
149 _posix1e_acl(acl_t acl, acl_type_t type)
150 {
151
152         if (_acl_brand(acl) != ACL_BRAND_POSIX)
153                 return (0);
154
155         return ((type == ACL_TYPE_ACCESS) || (type == ACL_TYPE_DEFAULT));
156 }
157
158 /*
159  * _posix1e_acl_check -- given an ACL, check its validity.  This is mirrored
160  * from code in sys/kern/kern_acl.c, and if changes are made in one, they
161  * should be made in the other also.  This copy of acl_check is made
162  * available * in userland for the benefit of processes wanting to check ACLs
163  * for validity before submitting them to the kernel, or for performing
164  * in userland file system checking.  Needless to say, the kernel makes
165  * the real checks on calls to get/setacl.
166  *
167  * See the comments in kernel for explanation -- just briefly, it assumes
168  * an already sorted ACL, and checks based on that assumption.  The
169  * POSIX.1e interface, acl_valid(), will perform the sort before calling
170  * this.  Returns 0 on success, EINVAL on failure.
171  */
172 int
173 _posix1e_acl_check(acl_t acl)
174 {
175         struct acl *acl_int;
176         struct acl_entry        *entry;         /* current entry */
177         uid_t   highest_uid=0, highest_gid=0;
178         int     stage = ACL_USER_OBJ;
179         int     i = 0;
180         int     count_user_obj=0, count_user=0, count_group_obj=0,
181                 count_group=0, count_mask=0, count_other=0;
182
183         acl_int = &acl->ats_acl;
184
185         /* printf("_posix1e_acl_check: checking acl with %d entries\n",
186             acl->acl_cnt); */
187         while (i < acl_int->acl_cnt) {
188                 entry = &acl_int->acl_entry[i];
189
190                 if ((entry->ae_perm | ACL_PERM_BITS) != ACL_PERM_BITS)
191                         return (EINVAL);
192
193                 switch(entry->ae_tag) {
194                 case ACL_USER_OBJ:
195                         /* printf("_posix1e_acl_check: %d: ACL_USER_OBJ\n",
196                             i); */
197                         if (stage > ACL_USER_OBJ)
198                                 return (EINVAL);
199                         stage = ACL_USER;
200                         count_user_obj++;
201                         break;
202
203                 case ACL_USER:
204                         /* printf("_posix1e_acl_check: %d: ACL_USER\n", i); */
205                         if (stage > ACL_USER)
206                                 return (EINVAL);
207                         stage = ACL_USER;
208                         if (count_user && (entry->ae_id <= highest_uid))
209                                 return (EINVAL);
210                         highest_uid = entry->ae_id;
211                         count_user++;
212                         break;
213
214                 case ACL_GROUP_OBJ:
215                         /* printf("_posix1e_acl_check: %d: ACL_GROUP_OBJ\n",
216                             i); */
217                         if (stage > ACL_GROUP_OBJ)
218                                 return (EINVAL);
219                         stage = ACL_GROUP;
220                         count_group_obj++;
221                         break;
222
223                 case ACL_GROUP:
224                         /* printf("_posix1e_acl_check: %d: ACL_GROUP\n", i); */
225                         if (stage > ACL_GROUP)
226                                 return (EINVAL);
227                         stage = ACL_GROUP;
228                         if (count_group && (entry->ae_id <= highest_gid))
229                                 return (EINVAL);
230                         highest_gid = entry->ae_id;
231                         count_group++;
232                         break;
233
234                 case ACL_MASK:
235                         /* printf("_posix1e_acl_check: %d: ACL_MASK\n", i); */
236                         if (stage > ACL_MASK)
237                                 return (EINVAL);
238                         stage = ACL_MASK;
239                         count_mask++;
240                         break;
241
242                 case ACL_OTHER:
243                         /* printf("_posix1e_acl_check: %d: ACL_OTHER\n", i); */
244                         if (stage > ACL_OTHER)
245                                 return (EINVAL);
246                         stage = ACL_OTHER;
247                         count_other++;
248                         break;
249
250                 default:
251                         /* printf("_posix1e_acl_check: %d: INVALID\n", i); */
252                         return (EINVAL);
253                 }
254                 i++;
255         }
256
257         if (count_user_obj != 1)
258                 return (EINVAL);
259
260         if (count_group_obj != 1)
261                 return (EINVAL);
262
263         if (count_mask != 0 && count_mask != 1)
264                 return (EINVAL);
265
266         if (count_other != 1)
267                 return (EINVAL);
268
269         return (0);
270 }
271
272 /*
273  * Given a right-shifted permission (i.e., direct ACL_PERM_* mask), fill
274  * in a string describing the permissions.
275  */
276 int
277 _posix1e_acl_perm_to_string(acl_perm_t perm, ssize_t buf_len, char *buf)
278 {
279
280         if (buf_len < _POSIX1E_ACL_STRING_PERM_MAXSIZE + 1) {
281                 errno = ENOMEM;
282                 return (-1);
283         }
284
285         if ((perm | ACL_PERM_BITS) != ACL_PERM_BITS) {
286                 errno = EINVAL;
287                 return (-1);
288         }
289
290         buf[3] = 0;     /* null terminate */
291
292         if (perm & ACL_READ)
293                 buf[0] = ACL_STRING_PERM_READ;
294         else
295                 buf[0] = ACL_STRING_PERM_NONE;
296
297         if (perm & ACL_WRITE)
298                 buf[1] = ACL_STRING_PERM_WRITE;
299         else
300                 buf[1] = ACL_STRING_PERM_NONE;
301
302         if (perm & ACL_EXECUTE)
303                 buf[2] = ACL_STRING_PERM_EXEC;
304         else
305                 buf[2] = ACL_STRING_PERM_NONE;
306
307         return (0);
308 }
309
310 /*
311  * given a string, return a permission describing it
312  */
313 int
314 _posix1e_acl_string_to_perm(char *string, acl_perm_t *perm)
315 {
316         acl_perm_t      myperm = ACL_PERM_NONE;
317         char    *ch;
318
319         ch = string;
320         while (*ch) {
321                 switch(*ch) {
322                 case ACL_STRING_PERM_READ:
323                         myperm |= ACL_READ;
324                         break;
325                 case ACL_STRING_PERM_WRITE:
326                         myperm |= ACL_WRITE;
327                         break;
328                 case ACL_STRING_PERM_EXEC:
329                         myperm |= ACL_EXECUTE;
330                         break;
331                 case ACL_STRING_PERM_NONE:
332                         break;
333                 default:
334                         return (EINVAL);
335                 }
336                 ch++;
337         }
338
339         *perm = myperm;
340         return (0);
341 }
342
343 /*
344  * Add an ACL entry without doing much checking, et al
345  */
346 int
347 _posix1e_acl_add_entry(acl_t acl, acl_tag_t tag, uid_t id, acl_perm_t perm)
348 {
349         struct acl              *acl_int;
350         struct acl_entry        *e;
351
352         acl_int = &acl->ats_acl;
353
354         if (acl_int->acl_cnt >= ACL_MAX_ENTRIES) {
355                 errno = ENOMEM;
356                 return (-1);
357         }
358
359         e = &(acl_int->acl_entry[acl_int->acl_cnt]);
360         e->ae_perm = perm;
361         e->ae_tag = tag;
362         e->ae_id = id;
363         acl_int->acl_cnt++;
364
365         return (0);
366 }
367
368 /*
369  * Convert "old" type - ACL_TYPE_{ACCESS,DEFAULT}_OLD - into its "new"
370  * counterpart.  It's neccessary for the old (pre-NFSv4 ACLs) binaries
371  * to work with new libc and kernel.  Fixing 'type' for old binaries with
372  * old libc and new kernel is being done by kern/vfs_acl.c:type_unold().
373  */
374 int
375 _acl_type_unold(acl_type_t type)
376 {
377
378         switch (type) {
379         case ACL_TYPE_ACCESS_OLD:
380                 return (ACL_TYPE_ACCESS);
381         case ACL_TYPE_DEFAULT_OLD:
382                 return (ACL_TYPE_DEFAULT);
383         default:
384                 return (type);
385         }
386 }
387
388 char *
389 string_skip_whitespace(char *string)
390 {
391
392         while (*string && ((*string == ' ') || (*string == '\t')))
393                 string++;
394
395         return (string);
396 }
397
398 void
399 string_trim_trailing_whitespace(char *string)
400 {
401         char    *end;
402
403         if (*string == '\0')
404                 return;
405
406         end = string + strlen(string) - 1;
407
408         while (end != string) {
409                 if ((*end == ' ') || (*end == '\t')) {
410                         *end = '\0';
411                         end--;
412                 } else {
413                         return;
414                 }
415         }
416
417         return;
418 }