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