acl_from_text.c revision 1.1 1 1.1 christos /*-
2 1.1 christos * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 1.1 christos *
4 1.1 christos * Copyright (c) 1999, 2000, 2001 Robert N. M. Watson
5 1.1 christos * All rights reserved.
6 1.1 christos *
7 1.1 christos * Redistribution and use in source and binary forms, with or without
8 1.1 christos * modification, are permitted provided that the following conditions
9 1.1 christos * are met:
10 1.1 christos * 1. Redistributions of source code must retain the above copyright
11 1.1 christos * notice, this list of conditions and the following disclaimer.
12 1.1 christos * 2. Redistributions in binary form must reproduce the above copyright
13 1.1 christos * notice, this list of conditions and the following disclaimer in the
14 1.1 christos * documentation and/or other materials provided with the distribution.
15 1.1 christos *
16 1.1 christos * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 1.1 christos * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 1.1 christos * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 1.1 christos * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 1.1 christos * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 1.1 christos * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 1.1 christos * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 1.1 christos * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 1.1 christos * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 1.1 christos * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 1.1 christos * SUCH DAMAGE.
27 1.1 christos */
28 1.1 christos /*
29 1.1 christos * acl_from_text: Convert a text-form ACL from a string to an acl_t.
30 1.1 christos */
31 1.1 christos
32 1.1 christos #include <sys/cdefs.h>
33 1.1 christos #if 0
34 1.1 christos __FBSDID("$FreeBSD: head/lib/libc/posix1e/acl_from_text.c 326193 2017-11-25 17:12:48Z pfg $");
35 1.1 christos #else
36 1.1 christos __RCSID("$NetBSD: acl_from_text.c,v 1.1 2020/05/16 18:31:47 christos Exp $");
37 1.1 christos #endif
38 1.1 christos
39 1.1 christos #include "namespace.h"
40 1.1 christos #include <sys/types.h>
41 1.1 christos #include <sys/acl.h>
42 1.1 christos #include <errno.h>
43 1.1 christos #include <grp.h>
44 1.1 christos #include <pwd.h>
45 1.1 christos #include <stdio.h>
46 1.1 christos #include <stdlib.h>
47 1.1 christos #include <string.h>
48 1.1 christos #include <assert.h>
49 1.1 christos
50 1.1 christos #include "acl_support.h"
51 1.1 christos
52 1.1 christos static acl_tag_t acl_string_to_tag(char *tag, char *qualifier);
53 1.1 christos
54 1.1 christos int _nfs4_acl_entry_from_text(acl_t aclp, char *entry);
55 1.1 christos int _text_could_be_nfs4_acl(const char *entry);
56 1.1 christos
57 1.1 christos static acl_tag_t
58 1.1 christos acl_string_to_tag(char *tag, char *qualifier)
59 1.1 christos {
60 1.1 christos
61 1.1 christos if (*qualifier == '\0') {
62 1.1 christos if ((!strcmp(tag, "user")) || (!strcmp(tag, "u"))) {
63 1.1 christos return (ACL_USER_OBJ);
64 1.1 christos } else
65 1.1 christos if ((!strcmp(tag, "group")) || (!strcmp(tag, "g"))) {
66 1.1 christos return (ACL_GROUP_OBJ);
67 1.1 christos } else
68 1.1 christos if ((!strcmp(tag, "mask")) || (!strcmp(tag, "m"))) {
69 1.1 christos return (ACL_MASK);
70 1.1 christos } else
71 1.1 christos if ((!strcmp(tag, "other")) || (!strcmp(tag, "o"))) {
72 1.1 christos return (ACL_OTHER);
73 1.1 christos } else
74 1.1 christos return(-1);
75 1.1 christos } else {
76 1.1 christos if ((!strcmp(tag, "user")) || (!strcmp(tag, "u"))) {
77 1.1 christos return(ACL_USER);
78 1.1 christos } else
79 1.1 christos if ((!strcmp(tag, "group")) || (!strcmp(tag, "g"))) {
80 1.1 christos return(ACL_GROUP);
81 1.1 christos } else
82 1.1 christos return(-1);
83 1.1 christos }
84 1.1 christos }
85 1.1 christos
86 1.1 christos static int
87 1.1 christos _posix1e_acl_entry_from_text(acl_t aclp, char *entry)
88 1.1 christos {
89 1.1 christos acl_tag_t t;
90 1.1 christos acl_perm_t p;
91 1.1 christos char *tag, *qualifier, *permission;
92 1.1 christos uid_t id;
93 1.1 christos int error;
94 1.1 christos
95 1.1 christos assert(_acl_brand(aclp) == ACL_BRAND_POSIX);
96 1.1 christos
97 1.1 christos /* Split into three ':' delimited fields. */
98 1.1 christos tag = strsep(&entry, ":");
99 1.1 christos if (tag == NULL) {
100 1.1 christos errno = EINVAL;
101 1.1 christos return (-1);
102 1.1 christos }
103 1.1 christos tag = string_skip_whitespace(tag);
104 1.1 christos if ((*tag == '\0') && (!entry)) {
105 1.1 christos /*
106 1.1 christos * Is an entirely comment line, skip to next
107 1.1 christos * comma.
108 1.1 christos */
109 1.1 christos return (0);
110 1.1 christos }
111 1.1 christos string_trim_trailing_whitespace(tag);
112 1.1 christos
113 1.1 christos qualifier = strsep(&entry, ":");
114 1.1 christos if (qualifier == NULL) {
115 1.1 christos errno = EINVAL;
116 1.1 christos return (-1);
117 1.1 christos }
118 1.1 christos qualifier = string_skip_whitespace(qualifier);
119 1.1 christos string_trim_trailing_whitespace(qualifier);
120 1.1 christos
121 1.1 christos permission = strsep(&entry, ":");
122 1.1 christos if (permission == NULL || entry) {
123 1.1 christos errno = EINVAL;
124 1.1 christos return (-1);
125 1.1 christos }
126 1.1 christos permission = string_skip_whitespace(permission);
127 1.1 christos string_trim_trailing_whitespace(permission);
128 1.1 christos
129 1.1 christos t = acl_string_to_tag(tag, qualifier);
130 1.1 christos if (t == (acl_tag_t)-1) {
131 1.1 christos errno = EINVAL;
132 1.1 christos return (-1);
133 1.1 christos }
134 1.1 christos
135 1.1 christos error = _posix1e_acl_string_to_perm(permission, &p);
136 1.1 christos if (error == -1) {
137 1.1 christos errno = EINVAL;
138 1.1 christos return (-1);
139 1.1 christos }
140 1.1 christos
141 1.1 christos switch(t) {
142 1.1 christos case ACL_USER_OBJ:
143 1.1 christos case ACL_GROUP_OBJ:
144 1.1 christos case ACL_MASK:
145 1.1 christos case ACL_OTHER:
146 1.1 christos if (*qualifier != '\0') {
147 1.1 christos errno = EINVAL;
148 1.1 christos return (-1);
149 1.1 christos }
150 1.1 christos id = 0;
151 1.1 christos break;
152 1.1 christos
153 1.1 christos case ACL_USER:
154 1.1 christos case ACL_GROUP:
155 1.1 christos error = _acl_name_to_id(t, qualifier, &id);
156 1.1 christos if (error == -1)
157 1.1 christos return (-1);
158 1.1 christos break;
159 1.1 christos
160 1.1 christos default:
161 1.1 christos errno = EINVAL;
162 1.1 christos return (-1);
163 1.1 christos }
164 1.1 christos
165 1.1 christos error = _posix1e_acl_add_entry(aclp, t, id, p);
166 1.1 christos if (error == -1)
167 1.1 christos return (-1);
168 1.1 christos
169 1.1 christos return (0);
170 1.1 christos }
171 1.1 christos
172 1.1 christos static int
173 1.1 christos _text_is_nfs4_entry(const char *entry)
174 1.1 christos {
175 1.1 christos int count = 0;
176 1.1 christos
177 1.1 christos assert(strlen(entry) > 0);
178 1.1 christos
179 1.1 christos while (*entry != '\0') {
180 1.1 christos if (*entry == ':' || *entry == '@')
181 1.1 christos count++;
182 1.1 christos entry++;
183 1.1 christos }
184 1.1 christos
185 1.1 christos if (count <= 2)
186 1.1 christos return (0);
187 1.1 christos
188 1.1 christos return (1);
189 1.1 christos }
190 1.1 christos
191 1.1 christos /*
192 1.1 christos * acl_from_text -- Convert a string into an ACL.
193 1.1 christos * Postpone most validity checking until the end and call acl_valid() to do
194 1.1 christos * that.
195 1.1 christos */
196 1.1 christos acl_t
197 1.1 christos acl_from_text(const char *buf_p)
198 1.1 christos {
199 1.1 christos acl_t acl;
200 1.1 christos char *mybuf_p, *line, *cur, *notcomment, *comment, *entry;
201 1.1 christos int error;
202 1.1 christos
203 1.1 christos /* Local copy we can mess up. */
204 1.1 christos mybuf_p = strdup(buf_p);
205 1.1 christos if (mybuf_p == NULL)
206 1.1 christos return(NULL);
207 1.1 christos
208 1.1 christos acl = acl_init(3); /* XXX: WTF, 3? */
209 1.1 christos if (acl == NULL) {
210 1.1 christos free(mybuf_p);
211 1.1 christos return(NULL);
212 1.1 christos }
213 1.1 christos
214 1.1 christos /* Outer loop: delimit at \n boundaries. */
215 1.1 christos cur = mybuf_p;
216 1.1 christos while ((line = strsep(&cur, "\n"))) {
217 1.1 christos /* Now split the line on the first # to strip out comments. */
218 1.1 christos comment = line;
219 1.1 christos notcomment = strsep(&comment, "#");
220 1.1 christos
221 1.1 christos /* Inner loop: delimit at ',' boundaries. */
222 1.1 christos while ((entry = strsep(¬comment, ","))) {
223 1.1 christos
224 1.1 christos /* Skip empty lines. */
225 1.1 christos if (strlen(string_skip_whitespace(entry)) == 0)
226 1.1 christos continue;
227 1.1 christos
228 1.1 christos if (_acl_brand(acl) == ACL_BRAND_UNKNOWN) {
229 1.1 christos if (_text_is_nfs4_entry(entry))
230 1.1 christos _acl_brand_as(acl, ACL_BRAND_NFS4);
231 1.1 christos else
232 1.1 christos _acl_brand_as(acl, ACL_BRAND_POSIX);
233 1.1 christos }
234 1.1 christos
235 1.1 christos switch (_acl_brand(acl)) {
236 1.1 christos case ACL_BRAND_NFS4:
237 1.1 christos error = _nfs4_acl_entry_from_text(acl, entry);
238 1.1 christos break;
239 1.1 christos
240 1.1 christos case ACL_BRAND_POSIX:
241 1.1 christos error = _posix1e_acl_entry_from_text(acl, entry);
242 1.1 christos break;
243 1.1 christos
244 1.1 christos default:
245 1.1 christos error = EINVAL;
246 1.1 christos break;
247 1.1 christos }
248 1.1 christos
249 1.1 christos if (error)
250 1.1 christos goto error_label;
251 1.1 christos }
252 1.1 christos }
253 1.1 christos
254 1.1 christos #if 0
255 1.1 christos /* XXX Should we only return ACLs valid according to acl_valid? */
256 1.1 christos /* Verify validity of the ACL we read in. */
257 1.1 christos if (acl_valid(acl) == -1) {
258 1.1 christos errno = EINVAL;
259 1.1 christos goto error_label;
260 1.1 christos }
261 1.1 christos #endif
262 1.1 christos
263 1.1 christos free(mybuf_p);
264 1.1 christos return(acl);
265 1.1 christos
266 1.1 christos error_label:
267 1.1 christos acl_free(acl);
268 1.1 christos free(mybuf_p);
269 1.1 christos return(NULL);
270 1.1 christos }
271 1.1 christos
272 1.1 christos /*
273 1.1 christos * Given a username/groupname from a text form of an ACL, return the uid/gid
274 1.1 christos * XXX NOT THREAD SAFE, RELIES ON GETPWNAM, GETGRNAM
275 1.1 christos * XXX USES *PW* AND *GR* WHICH ARE STATEFUL AND THEREFORE THIS ROUTINE
276 1.1 christos * MAY HAVE SIDE-EFFECTS
277 1.1 christos */
278 1.1 christos int
279 1.1 christos _acl_name_to_id(acl_tag_t tag, char *name, uid_t *id)
280 1.1 christos {
281 1.1 christos struct group *g;
282 1.1 christos struct passwd *p;
283 1.1 christos unsigned long l;
284 1.1 christos char *endp;
285 1.1 christos
286 1.1 christos switch(tag) {
287 1.1 christos case ACL_USER:
288 1.1 christos p = getpwnam(name);
289 1.1 christos if (p == NULL) {
290 1.1 christos l = strtoul(name, &endp, 0);
291 1.1 christos if (*endp != '\0' || l != (unsigned long)(uid_t)l) {
292 1.1 christos errno = EINVAL;
293 1.1 christos return (-1);
294 1.1 christos }
295 1.1 christos *id = (uid_t)l;
296 1.1 christos return (0);
297 1.1 christos }
298 1.1 christos *id = p->pw_uid;
299 1.1 christos return (0);
300 1.1 christos
301 1.1 christos case ACL_GROUP:
302 1.1 christos g = getgrnam(name);
303 1.1 christos if (g == NULL) {
304 1.1 christos l = strtoul(name, &endp, 0);
305 1.1 christos if (*endp != '\0' || l != (unsigned long)(gid_t)l) {
306 1.1 christos errno = EINVAL;
307 1.1 christos return (-1);
308 1.1 christos }
309 1.1 christos *id = (gid_t)l;
310 1.1 christos return (0);
311 1.1 christos }
312 1.1 christos *id = g->gr_gid;
313 1.1 christos return (0);
314 1.1 christos
315 1.1 christos default:
316 1.1 christos return (EINVAL);
317 1.1 christos }
318 1.1 christos }
319