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