]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - lib/libc/posix1e/acl_from_text.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / lib / libc / posix1e / acl_from_text.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  * acl_from_text: Convert a text-form ACL from a string to an acl_t.
28  */
29
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32
33 #include <sys/types.h>
34 #include "namespace.h"
35 #include <sys/acl.h>
36 #include "un-namespace.h"
37 #include <sys/errno.h>
38 #include <grp.h>
39 #include <pwd.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <assert.h>
44
45 #include "acl_support.h"
46
47 static acl_tag_t acl_string_to_tag(char *tag, char *qualifier);
48
49 int _nfs4_acl_entry_from_text(acl_t aclp, char *entry);
50 int _text_could_be_nfs4_acl(const char *entry);
51
52 static acl_tag_t
53 acl_string_to_tag(char *tag, char *qualifier)
54 {
55
56         if (*qualifier == '\0') {
57                 if ((!strcmp(tag, "user")) || (!strcmp(tag, "u"))) {
58                         return (ACL_USER_OBJ);
59                 } else
60                 if ((!strcmp(tag, "group")) || (!strcmp(tag, "g"))) {
61                         return (ACL_GROUP_OBJ);
62                 } else
63                 if ((!strcmp(tag, "mask")) || (!strcmp(tag, "m"))) {
64                         return (ACL_MASK);
65                 } else
66                 if ((!strcmp(tag, "other")) || (!strcmp(tag, "o"))) {
67                         return (ACL_OTHER);
68                 } else
69                         return(-1);
70         } else {
71                 if ((!strcmp(tag, "user")) || (!strcmp(tag, "u"))) {
72                         return(ACL_USER);
73                 } else
74                 if ((!strcmp(tag, "group")) || (!strcmp(tag, "g"))) {
75                         return(ACL_GROUP);
76                 } else
77                         return(-1);
78         }
79 }
80
81 static int
82 _posix1e_acl_entry_from_text(acl_t aclp, char *entry)
83 {
84         acl_tag_t        t;
85         acl_perm_t       p;
86         char            *tag, *qualifier, *permission;
87         uid_t            id;
88         int              error;
89
90         assert(_acl_brand(aclp) == ACL_BRAND_POSIX);
91
92         /* Split into three ':' delimited fields. */
93         tag = strsep(&entry, ":");
94         if (tag == NULL) {
95                 errno = EINVAL;
96                 return (-1);
97         }
98         tag = string_skip_whitespace(tag);
99         if ((*tag == '\0') && (!entry)) {
100                 /*
101                  * Is an entirely comment line, skip to next
102                  * comma.
103                  */
104                 return (0);
105         }
106         string_trim_trailing_whitespace(tag);
107
108         qualifier = strsep(&entry, ":");
109         if (qualifier == NULL) {
110                 errno = EINVAL;
111                 return (-1);
112         }
113         qualifier = string_skip_whitespace(qualifier);
114         string_trim_trailing_whitespace(qualifier);
115
116         permission = strsep(&entry, ":");
117         if (permission == NULL || entry) {
118                 errno = EINVAL;
119                 return (-1);
120         }
121         permission = string_skip_whitespace(permission);
122         string_trim_trailing_whitespace(permission);
123
124         t = acl_string_to_tag(tag, qualifier);
125         if (t == -1) {
126                 errno = EINVAL;
127                 return (-1);
128         }
129
130         error = _posix1e_acl_string_to_perm(permission, &p);
131         if (error == -1) {
132                 errno = EINVAL;
133                 return (-1);
134         }               
135
136         switch(t) {
137                 case ACL_USER_OBJ:
138                 case ACL_GROUP_OBJ:
139                 case ACL_MASK:
140                 case ACL_OTHER:
141                         if (*qualifier != '\0') {
142                                 errno = EINVAL;
143                                 return (-1);
144                         }
145                         id = 0;
146                         break;
147
148                 case ACL_USER:
149                 case ACL_GROUP:
150                         error = _acl_name_to_id(t, qualifier, &id);
151                         if (error == -1)
152                                 return (-1);
153                         break;
154
155                 default:
156                         errno = EINVAL;
157                         return (-1);
158         }
159
160         error = _posix1e_acl_add_entry(aclp, t, id, p);
161         if (error == -1)
162                 return (-1);
163
164         return (0);
165 }
166
167 static int
168 _text_is_nfs4_entry(const char *entry)
169 {
170         int count = 0;
171
172         assert(strlen(entry) > 0);
173
174         while (*entry != '\0') {
175                 if (*entry == ':' || *entry == '@')
176                         count++;
177                 entry++;
178         }
179
180         if (count <= 2)
181                 return (0);
182
183         return (1);
184 }
185
186 /*
187  * acl_from_text -- Convert a string into an ACL.
188  * Postpone most validity checking until the end and call acl_valid() to do
189  * that.
190  */
191 acl_t
192 acl_from_text(const char *buf_p)
193 {
194         acl_t            acl;
195         char            *mybuf_p, *line, *cur, *notcomment, *comment, *entry;
196         int              error;
197
198         /* Local copy we can mess up. */
199         mybuf_p = strdup(buf_p);
200         if (mybuf_p == NULL)
201                 return(NULL);
202
203         acl = acl_init(3); /* XXX: WTF, 3? */
204         if (acl == NULL) {
205                 free(mybuf_p);
206                 return(NULL);
207         }
208
209         /* Outer loop: delimit at \n boundaries. */
210         cur = mybuf_p;
211         while ((line = strsep(&cur, "\n"))) {
212                 /* Now split the line on the first # to strip out comments. */
213                 comment = line;
214                 notcomment = strsep(&comment, "#");
215
216                 /* Inner loop: delimit at ',' boundaries. */
217                 while ((entry = strsep(&notcomment, ","))) {
218
219                         /* Skip empty lines. */
220                         if (strlen(string_skip_whitespace(entry)) == 0)
221                                 continue;
222
223                         if (_acl_brand(acl) == ACL_BRAND_UNKNOWN) {
224                                 if (_text_is_nfs4_entry(entry))
225                                         _acl_brand_as(acl, ACL_BRAND_NFS4);
226                                 else
227                                         _acl_brand_as(acl, ACL_BRAND_POSIX);
228                         }
229
230                         switch (_acl_brand(acl)) {
231                         case ACL_BRAND_NFS4:
232                                 error = _nfs4_acl_entry_from_text(acl, entry);
233                                 break;
234
235                         case ACL_BRAND_POSIX:
236                                 error = _posix1e_acl_entry_from_text(acl, entry);
237                                 break;
238
239                         default:
240                                 error = EINVAL;
241                                 break;
242                         }
243
244                         if (error)
245                                 goto error_label;
246                 }
247         }
248
249 #if 0
250         /* XXX Should we only return ACLs valid according to acl_valid? */
251         /* Verify validity of the ACL we read in. */
252         if (acl_valid(acl) == -1) {
253                 errno = EINVAL;
254                 goto error_label;
255         }
256 #endif
257
258         free(mybuf_p);
259         return(acl);
260
261 error_label:
262         acl_free(acl);
263         free(mybuf_p);
264         return(NULL);
265 }
266
267 /*
268  * Given a username/groupname from a text form of an ACL, return the uid/gid
269  * XXX NOT THREAD SAFE, RELIES ON GETPWNAM, GETGRNAM
270  * XXX USES *PW* AND *GR* WHICH ARE STATEFUL AND THEREFORE THIS ROUTINE
271  * MAY HAVE SIDE-EFFECTS
272  */
273 int
274 _acl_name_to_id(acl_tag_t tag, char *name, uid_t *id)
275 {
276         struct group    *g;
277         struct passwd   *p;
278         unsigned long   l;
279         char            *endp;
280
281         switch(tag) {
282         case ACL_USER:
283                 p = getpwnam(name);
284                 if (p == NULL) {
285                         l = strtoul(name, &endp, 0);
286                         if (*endp != '\0' || l != (unsigned long)(uid_t)l) {
287                                 errno = EINVAL;
288                                 return (-1);
289                         }
290                         *id = (uid_t)l;
291                         return (0);
292                 }
293                 *id = p->pw_uid;
294                 return (0);
295
296         case ACL_GROUP:
297                 g = getgrnam(name);
298                 if (g == NULL) {
299                         l = strtoul(name, &endp, 0);
300                         if (*endp != '\0' || l != (unsigned long)(gid_t)l) {
301                                 errno = EINVAL;
302                                 return (-1);
303                         }
304                         *id = (gid_t)l;
305                         return (0);
306                 }
307                 *id = g->gr_gid;
308                 return (0);
309
310         default:
311                 return (EINVAL);
312         }
313 }