xref: /freebsd/lib/libc/posix1e/acl_from_text.c (revision 5e3190f700637fcfc1a52daeaa4a031fdd2557c7)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 1999, 2000, 2001 Robert N. M. Watson
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 /*
29  * acl_from_text: Convert a text-form ACL from a string to an acl_t.
30  */
31 
32 #include <sys/cdefs.h>
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 }
314