xref: /freebsd/lib/libc/posix1e/acl_from_text_nfs4.c (revision aa64588d28258aef88cc33b8043112e8856948d0)
1 /*-
2  * Copyright (c) 2008, 2009 Edward Tomasz Napierała <trasz@FreeBSD.org>
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 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29 
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <unistd.h>
33 #include <errno.h>
34 #include <assert.h>
35 #include <string.h>
36 #include <pwd.h>
37 #include <grp.h>
38 #include <ctype.h>
39 #include <err.h>
40 #include <sys/syscall.h>
41 #include <sys/types.h>
42 #include <sys/acl.h>
43 
44 #include "acl_support.h"
45 
46 #define MAX_ENTRY_LENGTH 512
47 
48 /*
49  * Parse the tag field of ACL entry passed as "str".  If qualifier
50  * needs to follow, then the variable referenced by "need_qualifier"
51  * is set to 1, otherwise it's set to 0.
52  */
53 static int
54 parse_tag(const char *str, acl_entry_t entry, int *need_qualifier)
55 {
56 
57 	assert(need_qualifier != NULL);
58 	*need_qualifier = 0;
59 
60 	if (strcmp(str, "owner@") == 0)
61 		return (acl_set_tag_type(entry, ACL_USER_OBJ));
62 	if (strcmp(str, "group@") == 0)
63 		return (acl_set_tag_type(entry, ACL_GROUP_OBJ));
64 	if (strcmp(str, "everyone@") == 0)
65 		return (acl_set_tag_type(entry, ACL_EVERYONE));
66 
67 	*need_qualifier = 1;
68 
69 	if (strcmp(str, "user") == 0 || strcmp(str, "u") == 0)
70 		return (acl_set_tag_type(entry, ACL_USER));
71 	if (strcmp(str, "group") == 0 || strcmp(str, "g") == 0)
72 		return (acl_set_tag_type(entry, ACL_GROUP));
73 
74 	warnx("malformed ACL: invalid \"tag\" field");
75 
76 	return (-1);
77 }
78 
79 /*
80  * Parse the qualifier field of ACL entry passed as "str".
81  * If user or group name cannot be resolved, then the variable
82  * referenced by "need_qualifier" is set to 1.
83  */
84 static int
85 parse_qualifier(char *str, acl_entry_t entry, int *need_qualifier)
86 {
87 	int qualifier_length, error;
88 	id_t id;
89 	char *end;
90 	struct passwd *pwd;
91 	struct group *grp;
92 	acl_tag_t tag;
93 
94 	assert(need_qualifier != NULL);
95 	*need_qualifier = 0;
96 
97 	qualifier_length = strlen(str);
98 
99 	if (qualifier_length == 0) {
100 		warnx("malformed ACL: empty \"qualifier\" field");
101 		return (-1);
102 	}
103 
104 	/* XXX: Can we assume that valid username never begins with a digit? */
105 	if (isdigit(str[0])) {
106 		id = strtod(str, &end);
107 
108 		if (end - str != qualifier_length) {
109 			warnx("malformed ACL: trailing characters "
110 			    "after numerical id");
111 			return (-1);
112 		}
113 
114 		return (acl_set_qualifier(entry, &id));
115 	}
116 
117 	error = acl_get_tag_type(entry, &tag);
118 	if (error)
119 		return (error);
120 
121 	assert(tag == ACL_USER || tag == ACL_GROUP);
122 
123 	if (tag == ACL_USER) {
124 		/* XXX: Thread-unsafe. */
125 		pwd = getpwnam(str);
126 		if (pwd == NULL) {
127 			*need_qualifier = 1;
128 			return (0);
129 		}
130 
131 		return (acl_set_qualifier(entry, &(pwd->pw_uid)));
132 	}
133 
134 	/* XXX: Thread-unsafe. */
135 	grp = getgrnam(str);
136 	if (grp == NULL) {
137 		*need_qualifier = 1;
138 		return (0);
139 	}
140 
141 	return (acl_set_qualifier(entry, &(grp->gr_gid)));
142 }
143 
144 static int
145 parse_access_mask(char *str, acl_entry_t entry)
146 {
147 	int error;
148 	acl_perm_t perm;
149 
150 	error = _nfs4_parse_access_mask(str, &perm);
151 	if (error)
152 		return (error);
153 
154 	error = acl_set_permset(entry, &perm);
155 
156 	return (error);
157 }
158 
159 static int
160 parse_flags(char *str, acl_entry_t entry)
161 {
162 	int error;
163 	acl_flag_t flags;
164 
165 	error = _nfs4_parse_flags(str, &flags);
166 	if (error)
167 		return (error);
168 
169 	error = acl_set_flagset_np(entry, &flags);
170 
171 	return (error);
172 }
173 
174 static int
175 parse_entry_type(const char *str, acl_entry_t entry)
176 {
177 
178 	if (strcmp(str, "allow") == 0)
179 		return (acl_set_entry_type_np(entry, ACL_ENTRY_TYPE_ALLOW));
180 	if (strcmp(str, "deny") == 0)
181 		return (acl_set_entry_type_np(entry, ACL_ENTRY_TYPE_DENY));
182 	if (strcmp(str, "audit") == 0)
183 		return (acl_set_entry_type_np(entry, ACL_ENTRY_TYPE_AUDIT));
184 	if (strcmp(str, "alarm") == 0)
185 		return (acl_set_entry_type_np(entry, ACL_ENTRY_TYPE_ALARM));
186 
187 	warnx("malformed ACL: invalid \"type\" field");
188 
189 	return (-1);
190 }
191 
192 static int
193 parse_appended_id(char *str, acl_entry_t entry)
194 {
195 	int qualifier_length;
196 	char *end;
197 	id_t id;
198 
199 	qualifier_length = strlen(str);
200 	if (qualifier_length == 0) {
201 		warnx("malformed ACL: \"appended id\" field present, "
202 	           "but empty");
203 		return (-1);
204 	}
205 
206 	id = strtod(str, &end);
207 	if (end - str != qualifier_length) {
208 		warnx("malformed ACL: appended id is not a number");
209 		return (-1);
210 	}
211 
212 	return (acl_set_qualifier(entry, &id));
213 }
214 
215 static int
216 number_of_colons(const char *str)
217 {
218 	int count = 0;
219 
220 	while (*str != '\0') {
221 		if (*str == ':')
222 			count++;
223 
224 		str++;
225 	}
226 
227 	return (count);
228 }
229 
230 int
231 _nfs4_acl_entry_from_text(acl_t aclp, char *str)
232 {
233 	int error, need_qualifier;
234 	acl_entry_t entry;
235 	char *field, *qualifier_field;
236 
237 	error = acl_create_entry(&aclp, &entry);
238 	if (error)
239 		return (error);
240 
241 	assert(_entry_brand(entry) == ACL_BRAND_NFS4);
242 
243 	if (str == NULL)
244 		goto truncated_entry;
245 	field = strsep(&str, ":");
246 
247 	field = string_skip_whitespace(field);
248 	if ((*field == '\0') && (!str)) {
249 		/*
250 		 * Is an entirely comment line, skip to next
251 		 * comma.
252 		 */
253 		return (0);
254 	}
255 
256 	error = parse_tag(field, entry, &need_qualifier);
257 	if (error)
258 		goto malformed_field;
259 
260 	if (need_qualifier) {
261 		if (str == NULL)
262 			goto truncated_entry;
263 		qualifier_field = field = strsep(&str, ":");
264 		error = parse_qualifier(field, entry, &need_qualifier);
265 		if (error)
266 			goto malformed_field;
267 	}
268 
269 	if (str == NULL)
270 		goto truncated_entry;
271 	field = strsep(&str, ":");
272 	error = parse_access_mask(field, entry);
273 	if (error)
274 		goto malformed_field;
275 
276 	if (str == NULL)
277 		goto truncated_entry;
278 	/* Do we have "flags" field? */
279 	if (number_of_colons(str) > 0) {
280 		field = strsep(&str, ":");
281 		error = parse_flags(field, entry);
282 		if (error)
283 			goto malformed_field;
284 	}
285 
286 	if (str == NULL)
287 		goto truncated_entry;
288 	field = strsep(&str, ":");
289 	error = parse_entry_type(field, entry);
290 	if (error)
291 		goto malformed_field;
292 
293 	if (need_qualifier) {
294 		if (str == NULL) {
295 			warnx("malformed ACL: unknown user or group name "
296 			    "\"%s\"", qualifier_field);
297 			goto truncated_entry;
298 		}
299 
300 		error = parse_appended_id(str, entry);
301 		if (error)
302 			goto malformed_field;
303 	}
304 
305 	return (0);
306 
307 truncated_entry:
308 malformed_field:
309 	acl_delete_entry(aclp, entry);
310 	errno = EINVAL;
311 	return (-1);
312 }
313