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