1 /*
2 File: acl_from_text.c
3
4 Copyright (C) 1999, 2000, 2001
5 Andreas Gruenbacher, <andreas.gruenbacher@gmail.com>
6
7 This program is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Lesser General Public
9 License as published by the Free Software Foundation; either
10 version 2.1 of the License, or (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public
18 License along with this library; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 */
21
22 #include "config.h"
23 #include <string.h>
24 #include <pwd.h>
25 #include <grp.h>
26 #include "libacl.h"
27 #include "misc.h"
28
29
30 #define SKIP_WS(x) do { \
31 while (*(x)==' ' || *(x)=='\t' || *(x)=='\n' || *(x)=='\r') \
32 (x)++; \
33 if (*(x)=='#') { \
34 while (*(x)!='\n' && *(x)!='\0') \
35 (x)++; \
36 } \
37 } while (0)
38
39
40 static int parse_acl_entry(const char **text_p, acl_t *acl_p);
41
42
43 /* 23.4.13 */
44 acl_t
45 acl_from_text(const char *buf_p)
46 {
47 acl_t acl;
48 acl = acl_init(0);
49 if (!acl)
50 return NULL;
51 if (!buf_p) {
52 errno = EINVAL;
53 return NULL;
54 }
55 while (*buf_p != '\0') {
56 if (parse_acl_entry(&buf_p, &acl) != 0)
57 goto fail;
58 SKIP_WS(buf_p);
59 if (*buf_p == ',') {
60 buf_p++;
61 SKIP_WS(buf_p);
62 }
63 }
64 if (*buf_p != '\0') {
65 errno = EINVAL;
66 goto fail;
67 }
68
69 return acl;
70
71 fail:
72 acl_free(acl);
73 return NULL;
74 }
75
76
77 static int
78 skip_tag_name(const char **text_p, const char *token)
79 {
80 size_t len = strlen(token);
81 const char *text = *text_p;
82
83 SKIP_WS(text);
84 if (strncmp(text, token, len) == 0) {
85 text += len;
86 goto delimiter;
87 }
88 if (*text == *token) {
89 text++;
90 goto delimiter;
91 }
92 return 0;
93
94 delimiter:
95 SKIP_WS(text);
96 if (*text == ':')
97 text++;
98 *text_p = text;
99 return 1;
100 }
101
102
103 static char *
104 get_token(const char **text_p)
105 {
106 char *token = NULL;
107 const char *ep;
108
109 ep = *text_p;
110 SKIP_WS(ep);
111
112 while (*ep!='\0' && *ep!='\r' && *ep!='\n' && *ep!=':' && *ep!=',')
113 ep++;
114 if (ep == *text_p)
115 goto after_token;
116 token = (char*)malloc(ep - *text_p + 1);
117 if (token == 0)
118 goto after_token;
119 memcpy(token, *text_p, (ep - *text_p));
120 token[ep - *text_p] = '\0';
121 after_token:
122 if (*ep == ':')
123 ep++;
124 *text_p = ep;
125 return token;
126 }
127
128
129 static int
130 get_id(const char *token, id_t *id_p)
131 {
132 char *ep;
133 long l;
134 l = strtol(token, &ep, 0);
135 if (*ep != '\0')
136 return -1;
137 if (l < 0) {
138 /*
139 Negative values are interpreted as 16-bit numbers,
140 so that id -2 maps to 65534 (nobody/nogroup), etc.
141 */
142 l &= 0xFFFF;
143 }
144 *id_p = l;
145 return 0;
146 }
147
148
149 static int
150 get_uid(const char *token, uid_t *uid_p)
151 {
152 struct passwd *passwd;
153
154 if (get_id(token, uid_p) == 0)
155 return 0;
156 errno = 0;
157 passwd = getpwnam(token);
158 if (passwd) {
159 *uid_p = passwd->pw_uid;
160 return 0;
161 }
162 if (errno == 0)
163 errno = EINVAL;
164 return -1;
165 }
166
167
168 static int
169 get_gid(const char *token, gid_t *gid_p)
170 {
171 struct group *group;
172
173 if (get_id(token, (uid_t *)gid_p) == 0)
174 return 0;
175 errno = 0;
176 group = getgrnam(token);
177 if (group) {
178 *gid_p = group->gr_gid;
179 return 0;
180 }
181 if (errno == 0)
182 errno = EINVAL;
183 return -1;
184 }
185
186
187 /*
188 Parses the next acl entry in text_p.
189
190 Returns:
191 -1 on error, 0 on success.
192 */
193
194 static int
195 parse_acl_entry(const char **text_p, acl_t *acl_p)
196 {
197 acl_entry_obj entry_obj;
198 acl_entry_t entry_d;
199 char *str;
200 const char *backup;
201 int error, perm_chars;
202
203 new_obj_p_here(acl_entry, &entry_obj);
204 init_acl_entry_obj(entry_obj);
205
206 /* parse acl entry type */
207 SKIP_WS(*text_p);
208 switch (**text_p) {
209 case 'u': /* user */
210 if (!skip_tag_name(text_p, "user"))
211 goto fail;
212 backup = *text_p;
213 str = get_token(text_p);
214 if (str) {
215 entry_obj.etag = ACL_USER;
216 error = get_uid(__acl_unquote(str),
217 &entry_obj.eid.qid);
218 free(str);
219 if (error) {
220 *text_p = backup;
221 return -1;
222 }
223 } else {
224 entry_obj.etag = ACL_USER_OBJ;
225 }
226 break;
227
228 case 'g': /* group */
229 if (!skip_tag_name(text_p, "group"))
230 goto fail;
231 backup = *text_p;
232 str = get_token(text_p);
233 if (str) {
234 entry_obj.etag = ACL_GROUP;
235 error = get_gid(__acl_unquote(str),
236 &entry_obj.eid.qid);
237 free(str);
238 if (error) {
239 *text_p = backup;
240 return -1;
241 }
242 } else {
243 entry_obj.etag = ACL_GROUP_OBJ;
244 }
245 break;
246
247 case 'm': /* mask */
248 if (!skip_tag_name(text_p, "mask"))
249 goto fail;
250 /* skip empty entry qualifier field (this field may
251 be missing for compatibility with Solaris.) */
252 SKIP_WS(*text_p);
253 if (**text_p == ':')
254 (*text_p)++;
255 entry_obj.etag = ACL_MASK;
256 break;
257
258 case 'o': /* other */
259 if (!skip_tag_name(text_p, "other"))
260 goto fail;
261 /* skip empty entry qualifier field (this field may
262 be missing for compatibility with Solaris.) */
263 SKIP_WS(*text_p);
264 if (**text_p == ':')
265 (*text_p)++;
266 entry_obj.etag = ACL_OTHER;
267 break;
268
269 default:
270 goto fail;
271 }
272
273 for (perm_chars=0; perm_chars<3; perm_chars++, (*text_p)++) {
274 switch(**text_p) {
275 case 'r':
276 if (entry_obj.eperm.sperm & ACL_READ)
277 goto fail;
278 entry_obj.eperm.sperm |= ACL_READ;
279 break;
280
281 case 'w':
282 if (entry_obj.eperm.sperm & ACL_WRITE)
283 goto fail;
284 entry_obj.eperm.sperm |= ACL_WRITE;
285 break;
286
287 case 'x':
288 if (entry_obj.eperm.sperm & ACL_EXECUTE)
289 goto fail;
290 entry_obj.eperm.sperm |= ACL_EXECUTE;
291 break;
292
293 case '-':
294 /* ignore */
295 break;
296
297 default:
298 if (perm_chars == 0)
299 goto fail;
300 goto create_entry;
301 }
302 }
303
304 create_entry:
305 if (acl_create_entry(acl_p, &entry_d) != 0)
306 return -1;
307 #pragma GCC diagnostic push
308 #pragma GCC diagnostic ignored "-Waddress"
309 if (acl_copy_entry(entry_d, int2ext(&entry_obj)) != 0)
310 return -1;
311 #pragma GCC diagnostic pop
312 return 0;
313
314 fail:
315 errno = EINVAL;
316 return -1;
317 }
318