xref: /freebsd/lib/libc/posix1e/acl_from_text_nfs4.c (revision aa015c8e4ad07fbe76ec137fa491c89856b871e0)
1aa015c8eSEdward Tomasz Napierala /*-
2aa015c8eSEdward Tomasz Napierala  * Copyright (c) 2008, 2009 Edward Tomasz Napierała <trasz@FreeBSD.org>
3aa015c8eSEdward Tomasz Napierala  * All rights reserved.
4aa015c8eSEdward Tomasz Napierala  *
5aa015c8eSEdward Tomasz Napierala  * Redistribution and use in source and binary forms, with or without
6aa015c8eSEdward Tomasz Napierala  * modification, are permitted provided that the following conditions
7aa015c8eSEdward Tomasz Napierala  * are met:
8aa015c8eSEdward Tomasz Napierala  * 1. Redistributions of source code must retain the above copyright
9aa015c8eSEdward Tomasz Napierala  *    notice, this list of conditions and the following disclaimer.
10aa015c8eSEdward Tomasz Napierala  * 2. Redistributions in binary form must reproduce the above copyright
11aa015c8eSEdward Tomasz Napierala  *    notice, this list of conditions and the following disclaimer in the
12aa015c8eSEdward Tomasz Napierala  *    documentation and/or other materials provided with the distribution.
13aa015c8eSEdward Tomasz Napierala  *
14aa015c8eSEdward Tomasz Napierala  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15aa015c8eSEdward Tomasz Napierala  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16aa015c8eSEdward Tomasz Napierala  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17aa015c8eSEdward Tomasz Napierala  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18aa015c8eSEdward Tomasz Napierala  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19aa015c8eSEdward Tomasz Napierala  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20aa015c8eSEdward Tomasz Napierala  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21aa015c8eSEdward Tomasz Napierala  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22aa015c8eSEdward Tomasz Napierala  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23aa015c8eSEdward Tomasz Napierala  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24aa015c8eSEdward Tomasz Napierala  * SUCH DAMAGE.
25aa015c8eSEdward Tomasz Napierala  */
26aa015c8eSEdward Tomasz Napierala 
27aa015c8eSEdward Tomasz Napierala #include <sys/cdefs.h>
28aa015c8eSEdward Tomasz Napierala __FBSDID("$FreeBSD$");
29aa015c8eSEdward Tomasz Napierala 
30aa015c8eSEdward Tomasz Napierala #include <stdio.h>
31aa015c8eSEdward Tomasz Napierala #include <stdlib.h>
32aa015c8eSEdward Tomasz Napierala #include <unistd.h>
33aa015c8eSEdward Tomasz Napierala #include <errno.h>
34aa015c8eSEdward Tomasz Napierala #include <assert.h>
35aa015c8eSEdward Tomasz Napierala #include <string.h>
36aa015c8eSEdward Tomasz Napierala #include <pwd.h>
37aa015c8eSEdward Tomasz Napierala #include <grp.h>
38aa015c8eSEdward Tomasz Napierala #include <ctype.h>
39aa015c8eSEdward Tomasz Napierala #include <err.h>
40aa015c8eSEdward Tomasz Napierala #include <sys/syscall.h>
41aa015c8eSEdward Tomasz Napierala #include <sys/types.h>
42aa015c8eSEdward Tomasz Napierala #include <sys/acl.h>
43aa015c8eSEdward Tomasz Napierala 
44aa015c8eSEdward Tomasz Napierala #include "acl_support.h"
45aa015c8eSEdward Tomasz Napierala 
46aa015c8eSEdward Tomasz Napierala #define MAX_ENTRY_LENGTH 512
47aa015c8eSEdward Tomasz Napierala 
48aa015c8eSEdward Tomasz Napierala /*
49aa015c8eSEdward Tomasz Napierala  * Parse the tag field of ACL entry passed as "str".  If qualifier
50aa015c8eSEdward Tomasz Napierala  * needs to follow, then the variable referenced by "need_qualifier"
51aa015c8eSEdward Tomasz Napierala  * is set to 1, otherwise it's set to 0.
52aa015c8eSEdward Tomasz Napierala  */
53aa015c8eSEdward Tomasz Napierala static int
54aa015c8eSEdward Tomasz Napierala parse_tag(const char *str, acl_entry_t entry, int *need_qualifier)
55aa015c8eSEdward Tomasz Napierala {
56aa015c8eSEdward Tomasz Napierala 
57aa015c8eSEdward Tomasz Napierala 	assert(need_qualifier != NULL);
58aa015c8eSEdward Tomasz Napierala 	*need_qualifier = 0;
59aa015c8eSEdward Tomasz Napierala 
60aa015c8eSEdward Tomasz Napierala 	if (strcmp(str, "owner@") == 0)
61aa015c8eSEdward Tomasz Napierala 		return (acl_set_tag_type(entry, ACL_USER_OBJ));
62aa015c8eSEdward Tomasz Napierala 	if (strcmp(str, "group@") == 0)
63aa015c8eSEdward Tomasz Napierala 		return (acl_set_tag_type(entry, ACL_GROUP_OBJ));
64aa015c8eSEdward Tomasz Napierala 	if (strcmp(str, "everyone@") == 0)
65aa015c8eSEdward Tomasz Napierala 		return (acl_set_tag_type(entry, ACL_EVERYONE));
66aa015c8eSEdward Tomasz Napierala 
67aa015c8eSEdward Tomasz Napierala 	*need_qualifier = 1;
68aa015c8eSEdward Tomasz Napierala 
69aa015c8eSEdward Tomasz Napierala 	if (strcmp(str, "user") == 0 || strcmp(str, "u") == 0)
70aa015c8eSEdward Tomasz Napierala 		return (acl_set_tag_type(entry, ACL_USER));
71aa015c8eSEdward Tomasz Napierala 	if (strcmp(str, "group") == 0 || strcmp(str, "g") == 0)
72aa015c8eSEdward Tomasz Napierala 		return (acl_set_tag_type(entry, ACL_GROUP));
73aa015c8eSEdward Tomasz Napierala 
74aa015c8eSEdward Tomasz Napierala 	warnx("malformed ACL: invalid \"tag\" field");
75aa015c8eSEdward Tomasz Napierala 
76aa015c8eSEdward Tomasz Napierala 	return (-1);
77aa015c8eSEdward Tomasz Napierala }
78aa015c8eSEdward Tomasz Napierala 
79aa015c8eSEdward Tomasz Napierala /*
80aa015c8eSEdward Tomasz Napierala  * Parse the qualifier field of ACL entry passed as "str".
81aa015c8eSEdward Tomasz Napierala  * If user or group name cannot be resolved, then the variable
82aa015c8eSEdward Tomasz Napierala  * referenced by "need_qualifier" is set to 1.
83aa015c8eSEdward Tomasz Napierala  */
84aa015c8eSEdward Tomasz Napierala static int
85aa015c8eSEdward Tomasz Napierala parse_qualifier(char *str, acl_entry_t entry, int *need_qualifier)
86aa015c8eSEdward Tomasz Napierala {
87aa015c8eSEdward Tomasz Napierala 	int qualifier_length, error;
88aa015c8eSEdward Tomasz Napierala 	id_t id;
89aa015c8eSEdward Tomasz Napierala 	char *end;
90aa015c8eSEdward Tomasz Napierala 	struct passwd *pwd;
91aa015c8eSEdward Tomasz Napierala 	struct group *grp;
92aa015c8eSEdward Tomasz Napierala 	acl_tag_t tag;
93aa015c8eSEdward Tomasz Napierala 
94aa015c8eSEdward Tomasz Napierala 	assert(need_qualifier != NULL);
95aa015c8eSEdward Tomasz Napierala 	*need_qualifier = 0;
96aa015c8eSEdward Tomasz Napierala 
97aa015c8eSEdward Tomasz Napierala 	qualifier_length = strlen(str);
98aa015c8eSEdward Tomasz Napierala 
99aa015c8eSEdward Tomasz Napierala 	if (qualifier_length == 0) {
100aa015c8eSEdward Tomasz Napierala 		warnx("malformed ACL: empty \"qualifier\" field");
101aa015c8eSEdward Tomasz Napierala 		return (-1);
102aa015c8eSEdward Tomasz Napierala 	}
103aa015c8eSEdward Tomasz Napierala 
104aa015c8eSEdward Tomasz Napierala 	/* XXX: Can we assume that valid username never begins with a digit? */
105aa015c8eSEdward Tomasz Napierala 	if (isdigit(str[0])) {
106aa015c8eSEdward Tomasz Napierala 		id = strtod(str, &end);
107aa015c8eSEdward Tomasz Napierala 
108aa015c8eSEdward Tomasz Napierala 		if (end - str != qualifier_length) {
109aa015c8eSEdward Tomasz Napierala 			warnx("malformed ACL: trailing characters "
110aa015c8eSEdward Tomasz Napierala 			    "after numerical id");
111aa015c8eSEdward Tomasz Napierala 			return (-1);
112aa015c8eSEdward Tomasz Napierala 		}
113aa015c8eSEdward Tomasz Napierala 
114aa015c8eSEdward Tomasz Napierala 		return (acl_set_qualifier(entry, &id));
115aa015c8eSEdward Tomasz Napierala 	}
116aa015c8eSEdward Tomasz Napierala 
117aa015c8eSEdward Tomasz Napierala 	error = acl_get_tag_type(entry, &tag);
118aa015c8eSEdward Tomasz Napierala 	if (error)
119aa015c8eSEdward Tomasz Napierala 		return (error);
120aa015c8eSEdward Tomasz Napierala 
121aa015c8eSEdward Tomasz Napierala 	assert(tag == ACL_USER || tag == ACL_GROUP);
122aa015c8eSEdward Tomasz Napierala 
123aa015c8eSEdward Tomasz Napierala 	if (tag == ACL_USER) {
124aa015c8eSEdward Tomasz Napierala 		/* XXX: Thread-unsafe. */
125aa015c8eSEdward Tomasz Napierala 		pwd = getpwnam(str);
126aa015c8eSEdward Tomasz Napierala 		if (pwd == NULL) {
127aa015c8eSEdward Tomasz Napierala 			*need_qualifier = 1;
128aa015c8eSEdward Tomasz Napierala 			return (0);
129aa015c8eSEdward Tomasz Napierala 		}
130aa015c8eSEdward Tomasz Napierala 
131aa015c8eSEdward Tomasz Napierala 		return (acl_set_qualifier(entry, &(pwd->pw_uid)));
132aa015c8eSEdward Tomasz Napierala 	}
133aa015c8eSEdward Tomasz Napierala 
134aa015c8eSEdward Tomasz Napierala 	/* XXX: Thread-unsafe. */
135aa015c8eSEdward Tomasz Napierala 	grp = getgrnam(str);
136aa015c8eSEdward Tomasz Napierala 	if (grp == NULL) {
137aa015c8eSEdward Tomasz Napierala 		*need_qualifier = 1;
138aa015c8eSEdward Tomasz Napierala 		return (0);
139aa015c8eSEdward Tomasz Napierala 	}
140aa015c8eSEdward Tomasz Napierala 
141aa015c8eSEdward Tomasz Napierala 	return (acl_set_qualifier(entry, &(grp->gr_gid)));
142aa015c8eSEdward Tomasz Napierala }
143aa015c8eSEdward Tomasz Napierala 
144aa015c8eSEdward Tomasz Napierala static int
145aa015c8eSEdward Tomasz Napierala parse_access_mask(char *str, acl_entry_t entry)
146aa015c8eSEdward Tomasz Napierala {
147aa015c8eSEdward Tomasz Napierala 	int error;
148aa015c8eSEdward Tomasz Napierala 	acl_perm_t perm;
149aa015c8eSEdward Tomasz Napierala 
150aa015c8eSEdward Tomasz Napierala 	error = _nfs4_parse_access_mask(str, &perm);
151aa015c8eSEdward Tomasz Napierala 	if (error)
152aa015c8eSEdward Tomasz Napierala 		return (error);
153aa015c8eSEdward Tomasz Napierala 
154aa015c8eSEdward Tomasz Napierala 	error = acl_set_permset(entry, &perm);
155aa015c8eSEdward Tomasz Napierala 
156aa015c8eSEdward Tomasz Napierala 	return (error);
157aa015c8eSEdward Tomasz Napierala }
158aa015c8eSEdward Tomasz Napierala 
159aa015c8eSEdward Tomasz Napierala static int
160aa015c8eSEdward Tomasz Napierala parse_flags(char *str, acl_entry_t entry)
161aa015c8eSEdward Tomasz Napierala {
162aa015c8eSEdward Tomasz Napierala 	int error;
163aa015c8eSEdward Tomasz Napierala 	acl_flag_t flags;
164aa015c8eSEdward Tomasz Napierala 
165aa015c8eSEdward Tomasz Napierala 	error = _nfs4_parse_flags(str, &flags);
166aa015c8eSEdward Tomasz Napierala 	if (error)
167aa015c8eSEdward Tomasz Napierala 		return (error);
168aa015c8eSEdward Tomasz Napierala 
169aa015c8eSEdward Tomasz Napierala 	error = acl_set_flagset_np(entry, &flags);
170aa015c8eSEdward Tomasz Napierala 
171aa015c8eSEdward Tomasz Napierala 	return (error);
172aa015c8eSEdward Tomasz Napierala }
173aa015c8eSEdward Tomasz Napierala 
174aa015c8eSEdward Tomasz Napierala static int
175aa015c8eSEdward Tomasz Napierala parse_entry_type(const char *str, acl_entry_t entry)
176aa015c8eSEdward Tomasz Napierala {
177aa015c8eSEdward Tomasz Napierala 
178aa015c8eSEdward Tomasz Napierala 	if (strcmp(str, "allow") == 0)
179aa015c8eSEdward Tomasz Napierala 		return (acl_set_entry_type_np(entry, ACL_ENTRY_TYPE_ALLOW));
180aa015c8eSEdward Tomasz Napierala 	if (strcmp(str, "deny") == 0)
181aa015c8eSEdward Tomasz Napierala 		return (acl_set_entry_type_np(entry, ACL_ENTRY_TYPE_DENY));
182aa015c8eSEdward Tomasz Napierala 	if (strcmp(str, "audit") == 0)
183aa015c8eSEdward Tomasz Napierala 		return (acl_set_entry_type_np(entry, ACL_ENTRY_TYPE_AUDIT));
184aa015c8eSEdward Tomasz Napierala 	if (strcmp(str, "alarm") == 0)
185aa015c8eSEdward Tomasz Napierala 		return (acl_set_entry_type_np(entry, ACL_ENTRY_TYPE_ALARM));
186aa015c8eSEdward Tomasz Napierala 
187aa015c8eSEdward Tomasz Napierala 	warnx("malformed ACL: invalid \"type\" field");
188aa015c8eSEdward Tomasz Napierala 
189aa015c8eSEdward Tomasz Napierala 	return (-1);
190aa015c8eSEdward Tomasz Napierala }
191aa015c8eSEdward Tomasz Napierala 
192aa015c8eSEdward Tomasz Napierala static int
193aa015c8eSEdward Tomasz Napierala parse_appended_id(char *str, acl_entry_t entry)
194aa015c8eSEdward Tomasz Napierala {
195aa015c8eSEdward Tomasz Napierala 	int qualifier_length;
196aa015c8eSEdward Tomasz Napierala 	char *end;
197aa015c8eSEdward Tomasz Napierala 	id_t id;
198aa015c8eSEdward Tomasz Napierala 
199aa015c8eSEdward Tomasz Napierala 	qualifier_length = strlen(str);
200aa015c8eSEdward Tomasz Napierala 	if (qualifier_length == 0) {
201aa015c8eSEdward Tomasz Napierala 		warnx("malformed ACL: \"appended id\" field present, "
202aa015c8eSEdward Tomasz Napierala 	           "but empty");
203aa015c8eSEdward Tomasz Napierala 		return (-1);
204aa015c8eSEdward Tomasz Napierala 	}
205aa015c8eSEdward Tomasz Napierala 
206aa015c8eSEdward Tomasz Napierala 	id = strtod(str, &end);
207aa015c8eSEdward Tomasz Napierala 	if (end - str != qualifier_length) {
208aa015c8eSEdward Tomasz Napierala 		warnx("malformed ACL: appended id is not a number");
209aa015c8eSEdward Tomasz Napierala 		return (-1);
210aa015c8eSEdward Tomasz Napierala 	}
211aa015c8eSEdward Tomasz Napierala 
212aa015c8eSEdward Tomasz Napierala 	return (acl_set_qualifier(entry, &id));
213aa015c8eSEdward Tomasz Napierala }
214aa015c8eSEdward Tomasz Napierala 
215aa015c8eSEdward Tomasz Napierala static int
216aa015c8eSEdward Tomasz Napierala number_of_colons(const char *str)
217aa015c8eSEdward Tomasz Napierala {
218aa015c8eSEdward Tomasz Napierala 	int count = 0;
219aa015c8eSEdward Tomasz Napierala 
220aa015c8eSEdward Tomasz Napierala 	while (*str != '\0') {
221aa015c8eSEdward Tomasz Napierala 		if (*str == ':')
222aa015c8eSEdward Tomasz Napierala 			count++;
223aa015c8eSEdward Tomasz Napierala 
224aa015c8eSEdward Tomasz Napierala 		str++;
225aa015c8eSEdward Tomasz Napierala 	}
226aa015c8eSEdward Tomasz Napierala 
227aa015c8eSEdward Tomasz Napierala 	return (count);
228aa015c8eSEdward Tomasz Napierala }
229aa015c8eSEdward Tomasz Napierala 
230aa015c8eSEdward Tomasz Napierala int
231aa015c8eSEdward Tomasz Napierala _nfs4_acl_entry_from_text(acl_t aclp, char *str)
232aa015c8eSEdward Tomasz Napierala {
233aa015c8eSEdward Tomasz Napierala 	int error, need_qualifier;
234aa015c8eSEdward Tomasz Napierala 	acl_entry_t entry;
235aa015c8eSEdward Tomasz Napierala 	char *field, *qualifier_field;
236aa015c8eSEdward Tomasz Napierala 
237aa015c8eSEdward Tomasz Napierala 	error = acl_create_entry(&aclp, &entry);
238aa015c8eSEdward Tomasz Napierala 	if (error)
239aa015c8eSEdward Tomasz Napierala 		return (error);
240aa015c8eSEdward Tomasz Napierala 
241aa015c8eSEdward Tomasz Napierala 	assert(_entry_brand(entry) == ACL_BRAND_NFS4);
242aa015c8eSEdward Tomasz Napierala 
243aa015c8eSEdward Tomasz Napierala 	if (str == NULL)
244aa015c8eSEdward Tomasz Napierala 		goto truncated_entry;
245aa015c8eSEdward Tomasz Napierala 	field = strsep(&str, ":");
246aa015c8eSEdward Tomasz Napierala 
247aa015c8eSEdward Tomasz Napierala 	field = string_skip_whitespace(field);
248aa015c8eSEdward Tomasz Napierala 	if ((*field == '\0') && (!str)) {
249aa015c8eSEdward Tomasz Napierala 		/*
250aa015c8eSEdward Tomasz Napierala 		 * Is an entirely comment line, skip to next
251aa015c8eSEdward Tomasz Napierala 		 * comma.
252aa015c8eSEdward Tomasz Napierala 		 */
253aa015c8eSEdward Tomasz Napierala 		return (0);
254aa015c8eSEdward Tomasz Napierala 	}
255aa015c8eSEdward Tomasz Napierala 
256aa015c8eSEdward Tomasz Napierala 	error = parse_tag(field, entry, &need_qualifier);
257aa015c8eSEdward Tomasz Napierala 	if (error)
258aa015c8eSEdward Tomasz Napierala 		goto malformed_field;
259aa015c8eSEdward Tomasz Napierala 
260aa015c8eSEdward Tomasz Napierala 	if (need_qualifier) {
261aa015c8eSEdward Tomasz Napierala 		if (str == NULL)
262aa015c8eSEdward Tomasz Napierala 			goto truncated_entry;
263aa015c8eSEdward Tomasz Napierala 		qualifier_field = field = strsep(&str, ":");
264aa015c8eSEdward Tomasz Napierala 		error = parse_qualifier(field, entry, &need_qualifier);
265aa015c8eSEdward Tomasz Napierala 		if (error)
266aa015c8eSEdward Tomasz Napierala 			goto malformed_field;
267aa015c8eSEdward Tomasz Napierala 	}
268aa015c8eSEdward Tomasz Napierala 
269aa015c8eSEdward Tomasz Napierala 	if (str == NULL)
270aa015c8eSEdward Tomasz Napierala 		goto truncated_entry;
271aa015c8eSEdward Tomasz Napierala 	field = strsep(&str, ":");
272aa015c8eSEdward Tomasz Napierala 	error = parse_access_mask(field, entry);
273aa015c8eSEdward Tomasz Napierala 	if (error)
274aa015c8eSEdward Tomasz Napierala 		goto malformed_field;
275aa015c8eSEdward Tomasz Napierala 
276aa015c8eSEdward Tomasz Napierala 	if (str == NULL)
277aa015c8eSEdward Tomasz Napierala 		goto truncated_entry;
278aa015c8eSEdward Tomasz Napierala 	/* Do we have "flags" field? */
279aa015c8eSEdward Tomasz Napierala 	if (number_of_colons(str) > 0) {
280aa015c8eSEdward Tomasz Napierala 		field = strsep(&str, ":");
281aa015c8eSEdward Tomasz Napierala 		error = parse_flags(field, entry);
282aa015c8eSEdward Tomasz Napierala 		if (error)
283aa015c8eSEdward Tomasz Napierala 			goto malformed_field;
284aa015c8eSEdward Tomasz Napierala 	}
285aa015c8eSEdward Tomasz Napierala 
286aa015c8eSEdward Tomasz Napierala 	if (str == NULL)
287aa015c8eSEdward Tomasz Napierala 		goto truncated_entry;
288aa015c8eSEdward Tomasz Napierala 	field = strsep(&str, ":");
289aa015c8eSEdward Tomasz Napierala 	error = parse_entry_type(field, entry);
290aa015c8eSEdward Tomasz Napierala 	if (error)
291aa015c8eSEdward Tomasz Napierala 		goto malformed_field;
292aa015c8eSEdward Tomasz Napierala 
293aa015c8eSEdward Tomasz Napierala 	if (need_qualifier) {
294aa015c8eSEdward Tomasz Napierala 		if (str == NULL) {
295aa015c8eSEdward Tomasz Napierala 			warnx("malformed ACL: unknown user or group name "
296aa015c8eSEdward Tomasz Napierala 			    "\"%s\"", qualifier_field);
297aa015c8eSEdward Tomasz Napierala 			goto truncated_entry;
298aa015c8eSEdward Tomasz Napierala 		}
299aa015c8eSEdward Tomasz Napierala 
300aa015c8eSEdward Tomasz Napierala 		error = parse_appended_id(str, entry);
301aa015c8eSEdward Tomasz Napierala 		if (error)
302aa015c8eSEdward Tomasz Napierala 			goto malformed_field;
303aa015c8eSEdward Tomasz Napierala 	}
304aa015c8eSEdward Tomasz Napierala 
305aa015c8eSEdward Tomasz Napierala 	return (0);
306aa015c8eSEdward Tomasz Napierala 
307aa015c8eSEdward Tomasz Napierala truncated_entry:
308aa015c8eSEdward Tomasz Napierala malformed_field:
309aa015c8eSEdward Tomasz Napierala 	acl_delete_entry(aclp, entry);
310aa015c8eSEdward Tomasz Napierala 	errno = EINVAL;
311aa015c8eSEdward Tomasz Napierala 	return (-1);
312aa015c8eSEdward Tomasz Napierala }
313aa015c8eSEdward Tomasz Napierala /*-
314aa015c8eSEdward Tomasz Napierala  * Copyright (c) 2008, 2009 Edward Tomasz Napierała <trasz@FreeBSD.org>
315aa015c8eSEdward Tomasz Napierala  * All rights reserved.
316aa015c8eSEdward Tomasz Napierala  *
317aa015c8eSEdward Tomasz Napierala  * Redistribution and use in source and binary forms, with or without
318aa015c8eSEdward Tomasz Napierala  * modification, are permitted provided that the following conditions
319aa015c8eSEdward Tomasz Napierala  * are met:
320aa015c8eSEdward Tomasz Napierala  * 1. Redistributions of source code must retain the above copyright
321aa015c8eSEdward Tomasz Napierala  *    notice, this list of conditions and the following disclaimer.
322aa015c8eSEdward Tomasz Napierala  * 2. Redistributions in binary form must reproduce the above copyright
323aa015c8eSEdward Tomasz Napierala  *    notice, this list of conditions and the following disclaimer in the
324aa015c8eSEdward Tomasz Napierala  *    documentation and/or other materials provided with the distribution.
325aa015c8eSEdward Tomasz Napierala  *
326aa015c8eSEdward Tomasz Napierala  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
327aa015c8eSEdward Tomasz Napierala  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
328aa015c8eSEdward Tomasz Napierala  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
329aa015c8eSEdward Tomasz Napierala  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
330aa015c8eSEdward Tomasz Napierala  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
331aa015c8eSEdward Tomasz Napierala  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
332aa015c8eSEdward Tomasz Napierala  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
333aa015c8eSEdward Tomasz Napierala  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
334aa015c8eSEdward Tomasz Napierala  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
335aa015c8eSEdward Tomasz Napierala  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
336aa015c8eSEdward Tomasz Napierala  * SUCH DAMAGE.
337aa015c8eSEdward Tomasz Napierala  */
338aa015c8eSEdward Tomasz Napierala 
339aa015c8eSEdward Tomasz Napierala #include <sys/cdefs.h>
340aa015c8eSEdward Tomasz Napierala __FBSDID("$FreeBSD$");
341aa015c8eSEdward Tomasz Napierala 
342aa015c8eSEdward Tomasz Napierala #include <stdio.h>
343aa015c8eSEdward Tomasz Napierala #include <stdlib.h>
344aa015c8eSEdward Tomasz Napierala #include <unistd.h>
345aa015c8eSEdward Tomasz Napierala #include <errno.h>
346aa015c8eSEdward Tomasz Napierala #include <assert.h>
347aa015c8eSEdward Tomasz Napierala #include <string.h>
348aa015c8eSEdward Tomasz Napierala #include <pwd.h>
349aa015c8eSEdward Tomasz Napierala #include <grp.h>
350aa015c8eSEdward Tomasz Napierala #include <ctype.h>
351aa015c8eSEdward Tomasz Napierala #include <err.h>
352aa015c8eSEdward Tomasz Napierala #include <sys/syscall.h>
353aa015c8eSEdward Tomasz Napierala #include <sys/types.h>
354aa015c8eSEdward Tomasz Napierala #include <sys/acl.h>
355aa015c8eSEdward Tomasz Napierala 
356aa015c8eSEdward Tomasz Napierala #include "acl_support.h"
357aa015c8eSEdward Tomasz Napierala 
358aa015c8eSEdward Tomasz Napierala #define MAX_ENTRY_LENGTH 512
359aa015c8eSEdward Tomasz Napierala 
360aa015c8eSEdward Tomasz Napierala /*
361aa015c8eSEdward Tomasz Napierala  * Parse the tag field of ACL entry passed as "str".  If qualifier
362aa015c8eSEdward Tomasz Napierala  * needs to follow, then the variable referenced by "need_qualifier"
363aa015c8eSEdward Tomasz Napierala  * is set to 1, otherwise it's set to 0.
364aa015c8eSEdward Tomasz Napierala  */
365aa015c8eSEdward Tomasz Napierala static int
366aa015c8eSEdward Tomasz Napierala parse_tag(const char *str, acl_entry_t entry, int *need_qualifier)
367aa015c8eSEdward Tomasz Napierala {
368aa015c8eSEdward Tomasz Napierala 
369aa015c8eSEdward Tomasz Napierala 	assert(need_qualifier != NULL);
370aa015c8eSEdward Tomasz Napierala 	*need_qualifier = 0;
371aa015c8eSEdward Tomasz Napierala 
372aa015c8eSEdward Tomasz Napierala 	if (strcmp(str, "owner@") == 0)
373aa015c8eSEdward Tomasz Napierala 		return (acl_set_tag_type(entry, ACL_USER_OBJ));
374aa015c8eSEdward Tomasz Napierala 	if (strcmp(str, "group@") == 0)
375aa015c8eSEdward Tomasz Napierala 		return (acl_set_tag_type(entry, ACL_GROUP_OBJ));
376aa015c8eSEdward Tomasz Napierala 	if (strcmp(str, "everyone@") == 0)
377aa015c8eSEdward Tomasz Napierala 		return (acl_set_tag_type(entry, ACL_EVERYONE));
378aa015c8eSEdward Tomasz Napierala 
379aa015c8eSEdward Tomasz Napierala 	*need_qualifier = 1;
380aa015c8eSEdward Tomasz Napierala 
381aa015c8eSEdward Tomasz Napierala 	if (strcmp(str, "user") == 0 || strcmp(str, "u") == 0)
382aa015c8eSEdward Tomasz Napierala 		return (acl_set_tag_type(entry, ACL_USER));
383aa015c8eSEdward Tomasz Napierala 	if (strcmp(str, "group") == 0 || strcmp(str, "g") == 0)
384aa015c8eSEdward Tomasz Napierala 		return (acl_set_tag_type(entry, ACL_GROUP));
385aa015c8eSEdward Tomasz Napierala 
386aa015c8eSEdward Tomasz Napierala 	warnx("malformed ACL: invalid \"tag\" field");
387aa015c8eSEdward Tomasz Napierala 
388aa015c8eSEdward Tomasz Napierala 	return (-1);
389aa015c8eSEdward Tomasz Napierala }
390aa015c8eSEdward Tomasz Napierala 
391aa015c8eSEdward Tomasz Napierala /*
392aa015c8eSEdward Tomasz Napierala  * Parse the qualifier field of ACL entry passed as "str".
393aa015c8eSEdward Tomasz Napierala  * If user or group name cannot be resolved, then the variable
394aa015c8eSEdward Tomasz Napierala  * referenced by "need_qualifier" is set to 1.
395aa015c8eSEdward Tomasz Napierala  */
396aa015c8eSEdward Tomasz Napierala static int
397aa015c8eSEdward Tomasz Napierala parse_qualifier(char *str, acl_entry_t entry, int *need_qualifier)
398aa015c8eSEdward Tomasz Napierala {
399aa015c8eSEdward Tomasz Napierala 	int qualifier_length, error;
400aa015c8eSEdward Tomasz Napierala 	id_t id;
401aa015c8eSEdward Tomasz Napierala 	char *end;
402aa015c8eSEdward Tomasz Napierala 	struct passwd *pwd;
403aa015c8eSEdward Tomasz Napierala 	struct group *grp;
404aa015c8eSEdward Tomasz Napierala 	acl_tag_t tag;
405aa015c8eSEdward Tomasz Napierala 
406aa015c8eSEdward Tomasz Napierala 	assert(need_qualifier != NULL);
407aa015c8eSEdward Tomasz Napierala 	*need_qualifier = 0;
408aa015c8eSEdward Tomasz Napierala 
409aa015c8eSEdward Tomasz Napierala 	qualifier_length = strlen(str);
410aa015c8eSEdward Tomasz Napierala 
411aa015c8eSEdward Tomasz Napierala 	if (qualifier_length == 0) {
412aa015c8eSEdward Tomasz Napierala 		warnx("malformed ACL: empty \"qualifier\" field");
413aa015c8eSEdward Tomasz Napierala 		return (-1);
414aa015c8eSEdward Tomasz Napierala 	}
415aa015c8eSEdward Tomasz Napierala 
416aa015c8eSEdward Tomasz Napierala 	/* XXX: Can we assume that valid username never begins with a digit? */
417aa015c8eSEdward Tomasz Napierala 	if (isdigit(str[0])) {
418aa015c8eSEdward Tomasz Napierala 		id = strtod(str, &end);
419aa015c8eSEdward Tomasz Napierala 
420aa015c8eSEdward Tomasz Napierala 		if (end - str != qualifier_length) {
421aa015c8eSEdward Tomasz Napierala 			warnx("malformed ACL: trailing characters "
422aa015c8eSEdward Tomasz Napierala 			    "after numerical id");
423aa015c8eSEdward Tomasz Napierala 			return (-1);
424aa015c8eSEdward Tomasz Napierala 		}
425aa015c8eSEdward Tomasz Napierala 
426aa015c8eSEdward Tomasz Napierala 		return (acl_set_qualifier(entry, &id));
427aa015c8eSEdward Tomasz Napierala 	}
428aa015c8eSEdward Tomasz Napierala 
429aa015c8eSEdward Tomasz Napierala 	error = acl_get_tag_type(entry, &tag);
430aa015c8eSEdward Tomasz Napierala 	if (error)
431aa015c8eSEdward Tomasz Napierala 		return (error);
432aa015c8eSEdward Tomasz Napierala 
433aa015c8eSEdward Tomasz Napierala 	assert(tag == ACL_USER || tag == ACL_GROUP);
434aa015c8eSEdward Tomasz Napierala 
435aa015c8eSEdward Tomasz Napierala 	if (tag == ACL_USER) {
436aa015c8eSEdward Tomasz Napierala 		/* XXX: Thread-unsafe. */
437aa015c8eSEdward Tomasz Napierala 		pwd = getpwnam(str);
438aa015c8eSEdward Tomasz Napierala 		if (pwd == NULL) {
439aa015c8eSEdward Tomasz Napierala 			*need_qualifier = 1;
440aa015c8eSEdward Tomasz Napierala 			return (0);
441aa015c8eSEdward Tomasz Napierala 		}
442aa015c8eSEdward Tomasz Napierala 
443aa015c8eSEdward Tomasz Napierala 		return (acl_set_qualifier(entry, &(pwd->pw_uid)));
444aa015c8eSEdward Tomasz Napierala 	}
445aa015c8eSEdward Tomasz Napierala 
446aa015c8eSEdward Tomasz Napierala 	/* XXX: Thread-unsafe. */
447aa015c8eSEdward Tomasz Napierala 	grp = getgrnam(str);
448aa015c8eSEdward Tomasz Napierala 	if (grp == NULL) {
449aa015c8eSEdward Tomasz Napierala 		*need_qualifier = 1;
450aa015c8eSEdward Tomasz Napierala 		return (0);
451aa015c8eSEdward Tomasz Napierala 	}
452aa015c8eSEdward Tomasz Napierala 
453aa015c8eSEdward Tomasz Napierala 	return (acl_set_qualifier(entry, &(grp->gr_gid)));
454aa015c8eSEdward Tomasz Napierala }
455aa015c8eSEdward Tomasz Napierala 
456aa015c8eSEdward Tomasz Napierala static int
457aa015c8eSEdward Tomasz Napierala parse_access_mask(char *str, acl_entry_t entry)
458aa015c8eSEdward Tomasz Napierala {
459aa015c8eSEdward Tomasz Napierala 	int error;
460aa015c8eSEdward Tomasz Napierala 	acl_perm_t perm;
461aa015c8eSEdward Tomasz Napierala 
462aa015c8eSEdward Tomasz Napierala 	error = _nfs4_parse_access_mask(str, &perm);
463aa015c8eSEdward Tomasz Napierala 	if (error)
464aa015c8eSEdward Tomasz Napierala 		return (error);
465aa015c8eSEdward Tomasz Napierala 
466aa015c8eSEdward Tomasz Napierala 	error = acl_set_permset(entry, &perm);
467aa015c8eSEdward Tomasz Napierala 
468aa015c8eSEdward Tomasz Napierala 	return (error);
469aa015c8eSEdward Tomasz Napierala }
470aa015c8eSEdward Tomasz Napierala 
471aa015c8eSEdward Tomasz Napierala static int
472aa015c8eSEdward Tomasz Napierala parse_flags(char *str, acl_entry_t entry)
473aa015c8eSEdward Tomasz Napierala {
474aa015c8eSEdward Tomasz Napierala 	int error;
475aa015c8eSEdward Tomasz Napierala 	acl_flag_t flags;
476aa015c8eSEdward Tomasz Napierala 
477aa015c8eSEdward Tomasz Napierala 	error = _nfs4_parse_flags(str, &flags);
478aa015c8eSEdward Tomasz Napierala 	if (error)
479aa015c8eSEdward Tomasz Napierala 		return (error);
480aa015c8eSEdward Tomasz Napierala 
481aa015c8eSEdward Tomasz Napierala 	error = acl_set_flagset_np(entry, &flags);
482aa015c8eSEdward Tomasz Napierala 
483aa015c8eSEdward Tomasz Napierala 	return (error);
484aa015c8eSEdward Tomasz Napierala }
485aa015c8eSEdward Tomasz Napierala 
486aa015c8eSEdward Tomasz Napierala static int
487aa015c8eSEdward Tomasz Napierala parse_entry_type(const char *str, acl_entry_t entry)
488aa015c8eSEdward Tomasz Napierala {
489aa015c8eSEdward Tomasz Napierala 
490aa015c8eSEdward Tomasz Napierala 	if (strcmp(str, "allow") == 0)
491aa015c8eSEdward Tomasz Napierala 		return (acl_set_entry_type_np(entry, ACL_ENTRY_TYPE_ALLOW));
492aa015c8eSEdward Tomasz Napierala 	if (strcmp(str, "deny") == 0)
493aa015c8eSEdward Tomasz Napierala 		return (acl_set_entry_type_np(entry, ACL_ENTRY_TYPE_DENY));
494aa015c8eSEdward Tomasz Napierala 	if (strcmp(str, "audit") == 0)
495aa015c8eSEdward Tomasz Napierala 		return (acl_set_entry_type_np(entry, ACL_ENTRY_TYPE_AUDIT));
496aa015c8eSEdward Tomasz Napierala 	if (strcmp(str, "alarm") == 0)
497aa015c8eSEdward Tomasz Napierala 		return (acl_set_entry_type_np(entry, ACL_ENTRY_TYPE_ALARM));
498aa015c8eSEdward Tomasz Napierala 
499aa015c8eSEdward Tomasz Napierala 	warnx("malformed ACL: invalid \"type\" field");
500aa015c8eSEdward Tomasz Napierala 
501aa015c8eSEdward Tomasz Napierala 	return (-1);
502aa015c8eSEdward Tomasz Napierala }
503aa015c8eSEdward Tomasz Napierala 
504aa015c8eSEdward Tomasz Napierala static int
505aa015c8eSEdward Tomasz Napierala parse_appended_id(char *str, acl_entry_t entry)
506aa015c8eSEdward Tomasz Napierala {
507aa015c8eSEdward Tomasz Napierala 	int qualifier_length;
508aa015c8eSEdward Tomasz Napierala 	char *end;
509aa015c8eSEdward Tomasz Napierala 	id_t id;
510aa015c8eSEdward Tomasz Napierala 
511aa015c8eSEdward Tomasz Napierala 	qualifier_length = strlen(str);
512aa015c8eSEdward Tomasz Napierala 	if (qualifier_length == 0) {
513aa015c8eSEdward Tomasz Napierala 		warnx("malformed ACL: \"appended id\" field present, "
514aa015c8eSEdward Tomasz Napierala 	           "but empty");
515aa015c8eSEdward Tomasz Napierala 		return (-1);
516aa015c8eSEdward Tomasz Napierala 	}
517aa015c8eSEdward Tomasz Napierala 
518aa015c8eSEdward Tomasz Napierala 	id = strtod(str, &end);
519aa015c8eSEdward Tomasz Napierala 	if (end - str != qualifier_length) {
520aa015c8eSEdward Tomasz Napierala 		warnx("malformed ACL: appended id is not a number");
521aa015c8eSEdward Tomasz Napierala 		return (-1);
522aa015c8eSEdward Tomasz Napierala 	}
523aa015c8eSEdward Tomasz Napierala 
524aa015c8eSEdward Tomasz Napierala 	return (acl_set_qualifier(entry, &id));
525aa015c8eSEdward Tomasz Napierala }
526aa015c8eSEdward Tomasz Napierala 
527aa015c8eSEdward Tomasz Napierala static int
528aa015c8eSEdward Tomasz Napierala number_of_colons(const char *str)
529aa015c8eSEdward Tomasz Napierala {
530aa015c8eSEdward Tomasz Napierala 	int count = 0;
531aa015c8eSEdward Tomasz Napierala 
532aa015c8eSEdward Tomasz Napierala 	while (*str != '\0') {
533aa015c8eSEdward Tomasz Napierala 		if (*str == ':')
534aa015c8eSEdward Tomasz Napierala 			count++;
535aa015c8eSEdward Tomasz Napierala 
536aa015c8eSEdward Tomasz Napierala 		str++;
537aa015c8eSEdward Tomasz Napierala 	}
538aa015c8eSEdward Tomasz Napierala 
539aa015c8eSEdward Tomasz Napierala 	return (count);
540aa015c8eSEdward Tomasz Napierala }
541aa015c8eSEdward Tomasz Napierala 
542aa015c8eSEdward Tomasz Napierala int
543aa015c8eSEdward Tomasz Napierala _nfs4_acl_entry_from_text(acl_t aclp, char *str)
544aa015c8eSEdward Tomasz Napierala {
545aa015c8eSEdward Tomasz Napierala 	int error, need_qualifier;
546aa015c8eSEdward Tomasz Napierala 	acl_entry_t entry;
547aa015c8eSEdward Tomasz Napierala 	char *field, *qualifier_field;
548aa015c8eSEdward Tomasz Napierala 
549aa015c8eSEdward Tomasz Napierala 	error = acl_create_entry(&aclp, &entry);
550aa015c8eSEdward Tomasz Napierala 	if (error)
551aa015c8eSEdward Tomasz Napierala 		return (error);
552aa015c8eSEdward Tomasz Napierala 
553aa015c8eSEdward Tomasz Napierala 	assert(_entry_brand(entry) == ACL_BRAND_NFS4);
554aa015c8eSEdward Tomasz Napierala 
555aa015c8eSEdward Tomasz Napierala 	if (str == NULL)
556aa015c8eSEdward Tomasz Napierala 		goto truncated_entry;
557aa015c8eSEdward Tomasz Napierala 	field = strsep(&str, ":");
558aa015c8eSEdward Tomasz Napierala 
559aa015c8eSEdward Tomasz Napierala 	field = string_skip_whitespace(field);
560aa015c8eSEdward Tomasz Napierala 	if ((*field == '\0') && (!str)) {
561aa015c8eSEdward Tomasz Napierala 		/*
562aa015c8eSEdward Tomasz Napierala 		 * Is an entirely comment line, skip to next
563aa015c8eSEdward Tomasz Napierala 		 * comma.
564aa015c8eSEdward Tomasz Napierala 		 */
565aa015c8eSEdward Tomasz Napierala 		return (0);
566aa015c8eSEdward Tomasz Napierala 	}
567aa015c8eSEdward Tomasz Napierala 
568aa015c8eSEdward Tomasz Napierala 	error = parse_tag(field, entry, &need_qualifier);
569aa015c8eSEdward Tomasz Napierala 	if (error)
570aa015c8eSEdward Tomasz Napierala 		goto malformed_field;
571aa015c8eSEdward Tomasz Napierala 
572aa015c8eSEdward Tomasz Napierala 	if (need_qualifier) {
573aa015c8eSEdward Tomasz Napierala 		if (str == NULL)
574aa015c8eSEdward Tomasz Napierala 			goto truncated_entry;
575aa015c8eSEdward Tomasz Napierala 		qualifier_field = field = strsep(&str, ":");
576aa015c8eSEdward Tomasz Napierala 		error = parse_qualifier(field, entry, &need_qualifier);
577aa015c8eSEdward Tomasz Napierala 		if (error)
578aa015c8eSEdward Tomasz Napierala 			goto malformed_field;
579aa015c8eSEdward Tomasz Napierala 	}
580aa015c8eSEdward Tomasz Napierala 
581aa015c8eSEdward Tomasz Napierala 	if (str == NULL)
582aa015c8eSEdward Tomasz Napierala 		goto truncated_entry;
583aa015c8eSEdward Tomasz Napierala 	field = strsep(&str, ":");
584aa015c8eSEdward Tomasz Napierala 	error = parse_access_mask(field, entry);
585aa015c8eSEdward Tomasz Napierala 	if (error)
586aa015c8eSEdward Tomasz Napierala 		goto malformed_field;
587aa015c8eSEdward Tomasz Napierala 
588aa015c8eSEdward Tomasz Napierala 	if (str == NULL)
589aa015c8eSEdward Tomasz Napierala 		goto truncated_entry;
590aa015c8eSEdward Tomasz Napierala 	/* Do we have "flags" field? */
591aa015c8eSEdward Tomasz Napierala 	if (number_of_colons(str) > 0) {
592aa015c8eSEdward Tomasz Napierala 		field = strsep(&str, ":");
593aa015c8eSEdward Tomasz Napierala 		error = parse_flags(field, entry);
594aa015c8eSEdward Tomasz Napierala 		if (error)
595aa015c8eSEdward Tomasz Napierala 			goto malformed_field;
596aa015c8eSEdward Tomasz Napierala 	}
597aa015c8eSEdward Tomasz Napierala 
598aa015c8eSEdward Tomasz Napierala 	if (str == NULL)
599aa015c8eSEdward Tomasz Napierala 		goto truncated_entry;
600aa015c8eSEdward Tomasz Napierala 	field = strsep(&str, ":");
601aa015c8eSEdward Tomasz Napierala 	error = parse_entry_type(field, entry);
602aa015c8eSEdward Tomasz Napierala 	if (error)
603aa015c8eSEdward Tomasz Napierala 		goto malformed_field;
604aa015c8eSEdward Tomasz Napierala 
605aa015c8eSEdward Tomasz Napierala 	if (need_qualifier) {
606aa015c8eSEdward Tomasz Napierala 		if (str == NULL) {
607aa015c8eSEdward Tomasz Napierala 			warnx("malformed ACL: unknown user or group name "
608aa015c8eSEdward Tomasz Napierala 			    "\"%s\"", qualifier_field);
609aa015c8eSEdward Tomasz Napierala 			goto truncated_entry;
610aa015c8eSEdward Tomasz Napierala 		}
611aa015c8eSEdward Tomasz Napierala 
612aa015c8eSEdward Tomasz Napierala 		error = parse_appended_id(str, entry);
613aa015c8eSEdward Tomasz Napierala 		if (error)
614aa015c8eSEdward Tomasz Napierala 			goto malformed_field;
615aa015c8eSEdward Tomasz Napierala 	}
616aa015c8eSEdward Tomasz Napierala 
617aa015c8eSEdward Tomasz Napierala 	return (0);
618aa015c8eSEdward Tomasz Napierala 
619aa015c8eSEdward Tomasz Napierala truncated_entry:
620aa015c8eSEdward Tomasz Napierala malformed_field:
621aa015c8eSEdward Tomasz Napierala 	acl_delete_entry(aclp, entry);
622aa015c8eSEdward Tomasz Napierala 	errno = EINVAL;
623aa015c8eSEdward Tomasz Napierala 	return (-1);
624aa015c8eSEdward Tomasz Napierala }
625aa015c8eSEdward Tomasz Napierala /*-
626aa015c8eSEdward Tomasz Napierala  * Copyright (c) 2008, 2009 Edward Tomasz Napierała <trasz@FreeBSD.org>
627aa015c8eSEdward Tomasz Napierala  * All rights reserved.
628aa015c8eSEdward Tomasz Napierala  *
629aa015c8eSEdward Tomasz Napierala  * Redistribution and use in source and binary forms, with or without
630aa015c8eSEdward Tomasz Napierala  * modification, are permitted provided that the following conditions
631aa015c8eSEdward Tomasz Napierala  * are met:
632aa015c8eSEdward Tomasz Napierala  * 1. Redistributions of source code must retain the above copyright
633aa015c8eSEdward Tomasz Napierala  *    notice, this list of conditions and the following disclaimer.
634aa015c8eSEdward Tomasz Napierala  * 2. Redistributions in binary form must reproduce the above copyright
635aa015c8eSEdward Tomasz Napierala  *    notice, this list of conditions and the following disclaimer in the
636aa015c8eSEdward Tomasz Napierala  *    documentation and/or other materials provided with the distribution.
637aa015c8eSEdward Tomasz Napierala  *
638aa015c8eSEdward Tomasz Napierala  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
639aa015c8eSEdward Tomasz Napierala  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
640aa015c8eSEdward Tomasz Napierala  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
641aa015c8eSEdward Tomasz Napierala  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
642aa015c8eSEdward Tomasz Napierala  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
643aa015c8eSEdward Tomasz Napierala  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
644aa015c8eSEdward Tomasz Napierala  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
645aa015c8eSEdward Tomasz Napierala  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
646aa015c8eSEdward Tomasz Napierala  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
647aa015c8eSEdward Tomasz Napierala  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
648aa015c8eSEdward Tomasz Napierala  * SUCH DAMAGE.
649aa015c8eSEdward Tomasz Napierala  */
650aa015c8eSEdward Tomasz Napierala 
651aa015c8eSEdward Tomasz Napierala #include <sys/cdefs.h>
652aa015c8eSEdward Tomasz Napierala __FBSDID("$FreeBSD$");
653aa015c8eSEdward Tomasz Napierala 
654aa015c8eSEdward Tomasz Napierala #include <stdio.h>
655aa015c8eSEdward Tomasz Napierala #include <stdlib.h>
656aa015c8eSEdward Tomasz Napierala #include <unistd.h>
657aa015c8eSEdward Tomasz Napierala #include <errno.h>
658aa015c8eSEdward Tomasz Napierala #include <assert.h>
659aa015c8eSEdward Tomasz Napierala #include <string.h>
660aa015c8eSEdward Tomasz Napierala #include <pwd.h>
661aa015c8eSEdward Tomasz Napierala #include <grp.h>
662aa015c8eSEdward Tomasz Napierala #include <ctype.h>
663aa015c8eSEdward Tomasz Napierala #include <err.h>
664aa015c8eSEdward Tomasz Napierala #include <sys/syscall.h>
665aa015c8eSEdward Tomasz Napierala #include <sys/types.h>
666aa015c8eSEdward Tomasz Napierala #include <sys/acl.h>
667aa015c8eSEdward Tomasz Napierala 
668aa015c8eSEdward Tomasz Napierala #include "acl_support.h"
669aa015c8eSEdward Tomasz Napierala 
670aa015c8eSEdward Tomasz Napierala #define MAX_ENTRY_LENGTH 512
671aa015c8eSEdward Tomasz Napierala 
672aa015c8eSEdward Tomasz Napierala /*
673aa015c8eSEdward Tomasz Napierala  * Parse the tag field of ACL entry passed as "str".  If qualifier
674aa015c8eSEdward Tomasz Napierala  * needs to follow, then the variable referenced by "need_qualifier"
675aa015c8eSEdward Tomasz Napierala  * is set to 1, otherwise it's set to 0.
676aa015c8eSEdward Tomasz Napierala  */
677aa015c8eSEdward Tomasz Napierala static int
678aa015c8eSEdward Tomasz Napierala parse_tag(const char *str, acl_entry_t entry, int *need_qualifier)
679aa015c8eSEdward Tomasz Napierala {
680aa015c8eSEdward Tomasz Napierala 
681aa015c8eSEdward Tomasz Napierala 	assert(need_qualifier != NULL);
682aa015c8eSEdward Tomasz Napierala 	*need_qualifier = 0;
683aa015c8eSEdward Tomasz Napierala 
684aa015c8eSEdward Tomasz Napierala 	if (strcmp(str, "owner@") == 0)
685aa015c8eSEdward Tomasz Napierala 		return (acl_set_tag_type(entry, ACL_USER_OBJ));
686aa015c8eSEdward Tomasz Napierala 	if (strcmp(str, "group@") == 0)
687aa015c8eSEdward Tomasz Napierala 		return (acl_set_tag_type(entry, ACL_GROUP_OBJ));
688aa015c8eSEdward Tomasz Napierala 	if (strcmp(str, "everyone@") == 0)
689aa015c8eSEdward Tomasz Napierala 		return (acl_set_tag_type(entry, ACL_EVERYONE));
690aa015c8eSEdward Tomasz Napierala 
691aa015c8eSEdward Tomasz Napierala 	*need_qualifier = 1;
692aa015c8eSEdward Tomasz Napierala 
693aa015c8eSEdward Tomasz Napierala 	if (strcmp(str, "user") == 0 || strcmp(str, "u") == 0)
694aa015c8eSEdward Tomasz Napierala 		return (acl_set_tag_type(entry, ACL_USER));
695aa015c8eSEdward Tomasz Napierala 	if (strcmp(str, "group") == 0 || strcmp(str, "g") == 0)
696aa015c8eSEdward Tomasz Napierala 		return (acl_set_tag_type(entry, ACL_GROUP));
697aa015c8eSEdward Tomasz Napierala 
698aa015c8eSEdward Tomasz Napierala 	warnx("malformed ACL: invalid \"tag\" field");
699aa015c8eSEdward Tomasz Napierala 
700aa015c8eSEdward Tomasz Napierala 	return (-1);
701aa015c8eSEdward Tomasz Napierala }
702aa015c8eSEdward Tomasz Napierala 
703aa015c8eSEdward Tomasz Napierala /*
704aa015c8eSEdward Tomasz Napierala  * Parse the qualifier field of ACL entry passed as "str".
705aa015c8eSEdward Tomasz Napierala  * If user or group name cannot be resolved, then the variable
706aa015c8eSEdward Tomasz Napierala  * referenced by "need_qualifier" is set to 1.
707aa015c8eSEdward Tomasz Napierala  */
708aa015c8eSEdward Tomasz Napierala static int
709aa015c8eSEdward Tomasz Napierala parse_qualifier(char *str, acl_entry_t entry, int *need_qualifier)
710aa015c8eSEdward Tomasz Napierala {
711aa015c8eSEdward Tomasz Napierala 	int qualifier_length, error;
712aa015c8eSEdward Tomasz Napierala 	id_t id;
713aa015c8eSEdward Tomasz Napierala 	char *end;
714aa015c8eSEdward Tomasz Napierala 	struct passwd *pwd;
715aa015c8eSEdward Tomasz Napierala 	struct group *grp;
716aa015c8eSEdward Tomasz Napierala 	acl_tag_t tag;
717aa015c8eSEdward Tomasz Napierala 
718aa015c8eSEdward Tomasz Napierala 	assert(need_qualifier != NULL);
719aa015c8eSEdward Tomasz Napierala 	*need_qualifier = 0;
720aa015c8eSEdward Tomasz Napierala 
721aa015c8eSEdward Tomasz Napierala 	qualifier_length = strlen(str);
722aa015c8eSEdward Tomasz Napierala 
723aa015c8eSEdward Tomasz Napierala 	if (qualifier_length == 0) {
724aa015c8eSEdward Tomasz Napierala 		warnx("malformed ACL: empty \"qualifier\" field");
725aa015c8eSEdward Tomasz Napierala 		return (-1);
726aa015c8eSEdward Tomasz Napierala 	}
727aa015c8eSEdward Tomasz Napierala 
728aa015c8eSEdward Tomasz Napierala 	/* XXX: Can we assume that valid username never begins with a digit? */
729aa015c8eSEdward Tomasz Napierala 	if (isdigit(str[0])) {
730aa015c8eSEdward Tomasz Napierala 		id = strtod(str, &end);
731aa015c8eSEdward Tomasz Napierala 
732aa015c8eSEdward Tomasz Napierala 		if (end - str != qualifier_length) {
733aa015c8eSEdward Tomasz Napierala 			warnx("malformed ACL: trailing characters "
734aa015c8eSEdward Tomasz Napierala 			    "after numerical id");
735aa015c8eSEdward Tomasz Napierala 			return (-1);
736aa015c8eSEdward Tomasz Napierala 		}
737aa015c8eSEdward Tomasz Napierala 
738aa015c8eSEdward Tomasz Napierala 		return (acl_set_qualifier(entry, &id));
739aa015c8eSEdward Tomasz Napierala 	}
740aa015c8eSEdward Tomasz Napierala 
741aa015c8eSEdward Tomasz Napierala 	error = acl_get_tag_type(entry, &tag);
742aa015c8eSEdward Tomasz Napierala 	if (error)
743aa015c8eSEdward Tomasz Napierala 		return (error);
744aa015c8eSEdward Tomasz Napierala 
745aa015c8eSEdward Tomasz Napierala 	assert(tag == ACL_USER || tag == ACL_GROUP);
746aa015c8eSEdward Tomasz Napierala 
747aa015c8eSEdward Tomasz Napierala 	if (tag == ACL_USER) {
748aa015c8eSEdward Tomasz Napierala 		/* XXX: Thread-unsafe. */
749aa015c8eSEdward Tomasz Napierala 		pwd = getpwnam(str);
750aa015c8eSEdward Tomasz Napierala 		if (pwd == NULL) {
751aa015c8eSEdward Tomasz Napierala 			*need_qualifier = 1;
752aa015c8eSEdward Tomasz Napierala 			return (0);
753aa015c8eSEdward Tomasz Napierala 		}
754aa015c8eSEdward Tomasz Napierala 
755aa015c8eSEdward Tomasz Napierala 		return (acl_set_qualifier(entry, &(pwd->pw_uid)));
756aa015c8eSEdward Tomasz Napierala 	}
757aa015c8eSEdward Tomasz Napierala 
758aa015c8eSEdward Tomasz Napierala 	/* XXX: Thread-unsafe. */
759aa015c8eSEdward Tomasz Napierala 	grp = getgrnam(str);
760aa015c8eSEdward Tomasz Napierala 	if (grp == NULL) {
761aa015c8eSEdward Tomasz Napierala 		*need_qualifier = 1;
762aa015c8eSEdward Tomasz Napierala 		return (0);
763aa015c8eSEdward Tomasz Napierala 	}
764aa015c8eSEdward Tomasz Napierala 
765aa015c8eSEdward Tomasz Napierala 	return (acl_set_qualifier(entry, &(grp->gr_gid)));
766aa015c8eSEdward Tomasz Napierala }
767aa015c8eSEdward Tomasz Napierala 
768aa015c8eSEdward Tomasz Napierala static int
769aa015c8eSEdward Tomasz Napierala parse_access_mask(char *str, acl_entry_t entry)
770aa015c8eSEdward Tomasz Napierala {
771aa015c8eSEdward Tomasz Napierala 	int error;
772aa015c8eSEdward Tomasz Napierala 	acl_perm_t perm;
773aa015c8eSEdward Tomasz Napierala 
774aa015c8eSEdward Tomasz Napierala 	error = _nfs4_parse_access_mask(str, &perm);
775aa015c8eSEdward Tomasz Napierala 	if (error)
776aa015c8eSEdward Tomasz Napierala 		return (error);
777aa015c8eSEdward Tomasz Napierala 
778aa015c8eSEdward Tomasz Napierala 	error = acl_set_permset(entry, &perm);
779aa015c8eSEdward Tomasz Napierala 
780aa015c8eSEdward Tomasz Napierala 	return (error);
781aa015c8eSEdward Tomasz Napierala }
782aa015c8eSEdward Tomasz Napierala 
783aa015c8eSEdward Tomasz Napierala static int
784aa015c8eSEdward Tomasz Napierala parse_flags(char *str, acl_entry_t entry)
785aa015c8eSEdward Tomasz Napierala {
786aa015c8eSEdward Tomasz Napierala 	int error;
787aa015c8eSEdward Tomasz Napierala 	acl_flag_t flags;
788aa015c8eSEdward Tomasz Napierala 
789aa015c8eSEdward Tomasz Napierala 	error = _nfs4_parse_flags(str, &flags);
790aa015c8eSEdward Tomasz Napierala 	if (error)
791aa015c8eSEdward Tomasz Napierala 		return (error);
792aa015c8eSEdward Tomasz Napierala 
793aa015c8eSEdward Tomasz Napierala 	error = acl_set_flagset_np(entry, &flags);
794aa015c8eSEdward Tomasz Napierala 
795aa015c8eSEdward Tomasz Napierala 	return (error);
796aa015c8eSEdward Tomasz Napierala }
797aa015c8eSEdward Tomasz Napierala 
798aa015c8eSEdward Tomasz Napierala static int
799aa015c8eSEdward Tomasz Napierala parse_entry_type(const char *str, acl_entry_t entry)
800aa015c8eSEdward Tomasz Napierala {
801aa015c8eSEdward Tomasz Napierala 
802aa015c8eSEdward Tomasz Napierala 	if (strcmp(str, "allow") == 0)
803aa015c8eSEdward Tomasz Napierala 		return (acl_set_entry_type_np(entry, ACL_ENTRY_TYPE_ALLOW));
804aa015c8eSEdward Tomasz Napierala 	if (strcmp(str, "deny") == 0)
805aa015c8eSEdward Tomasz Napierala 		return (acl_set_entry_type_np(entry, ACL_ENTRY_TYPE_DENY));
806aa015c8eSEdward Tomasz Napierala 	if (strcmp(str, "audit") == 0)
807aa015c8eSEdward Tomasz Napierala 		return (acl_set_entry_type_np(entry, ACL_ENTRY_TYPE_AUDIT));
808aa015c8eSEdward Tomasz Napierala 	if (strcmp(str, "alarm") == 0)
809aa015c8eSEdward Tomasz Napierala 		return (acl_set_entry_type_np(entry, ACL_ENTRY_TYPE_ALARM));
810aa015c8eSEdward Tomasz Napierala 
811aa015c8eSEdward Tomasz Napierala 	warnx("malformed ACL: invalid \"type\" field");
812aa015c8eSEdward Tomasz Napierala 
813aa015c8eSEdward Tomasz Napierala 	return (-1);
814aa015c8eSEdward Tomasz Napierala }
815aa015c8eSEdward Tomasz Napierala 
816aa015c8eSEdward Tomasz Napierala static int
817aa015c8eSEdward Tomasz Napierala parse_appended_id(char *str, acl_entry_t entry)
818aa015c8eSEdward Tomasz Napierala {
819aa015c8eSEdward Tomasz Napierala 	int qualifier_length;
820aa015c8eSEdward Tomasz Napierala 	char *end;
821aa015c8eSEdward Tomasz Napierala 	id_t id;
822aa015c8eSEdward Tomasz Napierala 
823aa015c8eSEdward Tomasz Napierala 	qualifier_length = strlen(str);
824aa015c8eSEdward Tomasz Napierala 	if (qualifier_length == 0) {
825aa015c8eSEdward Tomasz Napierala 		warnx("malformed ACL: \"appended id\" field present, "
826aa015c8eSEdward Tomasz Napierala 	           "but empty");
827aa015c8eSEdward Tomasz Napierala 		return (-1);
828aa015c8eSEdward Tomasz Napierala 	}
829aa015c8eSEdward Tomasz Napierala 
830aa015c8eSEdward Tomasz Napierala 	id = strtod(str, &end);
831aa015c8eSEdward Tomasz Napierala 	if (end - str != qualifier_length) {
832aa015c8eSEdward Tomasz Napierala 		warnx("malformed ACL: appended id is not a number");
833aa015c8eSEdward Tomasz Napierala 		return (-1);
834aa015c8eSEdward Tomasz Napierala 	}
835aa015c8eSEdward Tomasz Napierala 
836aa015c8eSEdward Tomasz Napierala 	return (acl_set_qualifier(entry, &id));
837aa015c8eSEdward Tomasz Napierala }
838aa015c8eSEdward Tomasz Napierala 
839aa015c8eSEdward Tomasz Napierala static int
840aa015c8eSEdward Tomasz Napierala number_of_colons(const char *str)
841aa015c8eSEdward Tomasz Napierala {
842aa015c8eSEdward Tomasz Napierala 	int count = 0;
843aa015c8eSEdward Tomasz Napierala 
844aa015c8eSEdward Tomasz Napierala 	while (*str != '\0') {
845aa015c8eSEdward Tomasz Napierala 		if (*str == ':')
846aa015c8eSEdward Tomasz Napierala 			count++;
847aa015c8eSEdward Tomasz Napierala 
848aa015c8eSEdward Tomasz Napierala 		str++;
849aa015c8eSEdward Tomasz Napierala 	}
850aa015c8eSEdward Tomasz Napierala 
851aa015c8eSEdward Tomasz Napierala 	return (count);
852aa015c8eSEdward Tomasz Napierala }
853aa015c8eSEdward Tomasz Napierala 
854aa015c8eSEdward Tomasz Napierala int
855aa015c8eSEdward Tomasz Napierala _nfs4_acl_entry_from_text(acl_t aclp, char *str)
856aa015c8eSEdward Tomasz Napierala {
857aa015c8eSEdward Tomasz Napierala 	int error, need_qualifier;
858aa015c8eSEdward Tomasz Napierala 	acl_entry_t entry;
859aa015c8eSEdward Tomasz Napierala 	char *field, *qualifier_field;
860aa015c8eSEdward Tomasz Napierala 
861aa015c8eSEdward Tomasz Napierala 	error = acl_create_entry(&aclp, &entry);
862aa015c8eSEdward Tomasz Napierala 	if (error)
863aa015c8eSEdward Tomasz Napierala 		return (error);
864aa015c8eSEdward Tomasz Napierala 
865aa015c8eSEdward Tomasz Napierala 	assert(_entry_brand(entry) == ACL_BRAND_NFS4);
866aa015c8eSEdward Tomasz Napierala 
867aa015c8eSEdward Tomasz Napierala 	if (str == NULL)
868aa015c8eSEdward Tomasz Napierala 		goto truncated_entry;
869aa015c8eSEdward Tomasz Napierala 	field = strsep(&str, ":");
870aa015c8eSEdward Tomasz Napierala 
871aa015c8eSEdward Tomasz Napierala 	field = string_skip_whitespace(field);
872aa015c8eSEdward Tomasz Napierala 	if ((*field == '\0') && (!str)) {
873aa015c8eSEdward Tomasz Napierala 		/*
874aa015c8eSEdward Tomasz Napierala 		 * Is an entirely comment line, skip to next
875aa015c8eSEdward Tomasz Napierala 		 * comma.
876aa015c8eSEdward Tomasz Napierala 		 */
877aa015c8eSEdward Tomasz Napierala 		return (0);
878aa015c8eSEdward Tomasz Napierala 	}
879aa015c8eSEdward Tomasz Napierala 
880aa015c8eSEdward Tomasz Napierala 	error = parse_tag(field, entry, &need_qualifier);
881aa015c8eSEdward Tomasz Napierala 	if (error)
882aa015c8eSEdward Tomasz Napierala 		goto malformed_field;
883aa015c8eSEdward Tomasz Napierala 
884aa015c8eSEdward Tomasz Napierala 	if (need_qualifier) {
885aa015c8eSEdward Tomasz Napierala 		if (str == NULL)
886aa015c8eSEdward Tomasz Napierala 			goto truncated_entry;
887aa015c8eSEdward Tomasz Napierala 		qualifier_field = field = strsep(&str, ":");
888aa015c8eSEdward Tomasz Napierala 		error = parse_qualifier(field, entry, &need_qualifier);
889aa015c8eSEdward Tomasz Napierala 		if (error)
890aa015c8eSEdward Tomasz Napierala 			goto malformed_field;
891aa015c8eSEdward Tomasz Napierala 	}
892aa015c8eSEdward Tomasz Napierala 
893aa015c8eSEdward Tomasz Napierala 	if (str == NULL)
894aa015c8eSEdward Tomasz Napierala 		goto truncated_entry;
895aa015c8eSEdward Tomasz Napierala 	field = strsep(&str, ":");
896aa015c8eSEdward Tomasz Napierala 	error = parse_access_mask(field, entry);
897aa015c8eSEdward Tomasz Napierala 	if (error)
898aa015c8eSEdward Tomasz Napierala 		goto malformed_field;
899aa015c8eSEdward Tomasz Napierala 
900aa015c8eSEdward Tomasz Napierala 	if (str == NULL)
901aa015c8eSEdward Tomasz Napierala 		goto truncated_entry;
902aa015c8eSEdward Tomasz Napierala 	/* Do we have "flags" field? */
903aa015c8eSEdward Tomasz Napierala 	if (number_of_colons(str) > 0) {
904aa015c8eSEdward Tomasz Napierala 		field = strsep(&str, ":");
905aa015c8eSEdward Tomasz Napierala 		error = parse_flags(field, entry);
906aa015c8eSEdward Tomasz Napierala 		if (error)
907aa015c8eSEdward Tomasz Napierala 			goto malformed_field;
908aa015c8eSEdward Tomasz Napierala 	}
909aa015c8eSEdward Tomasz Napierala 
910aa015c8eSEdward Tomasz Napierala 	if (str == NULL)
911aa015c8eSEdward Tomasz Napierala 		goto truncated_entry;
912aa015c8eSEdward Tomasz Napierala 	field = strsep(&str, ":");
913aa015c8eSEdward Tomasz Napierala 	error = parse_entry_type(field, entry);
914aa015c8eSEdward Tomasz Napierala 	if (error)
915aa015c8eSEdward Tomasz Napierala 		goto malformed_field;
916aa015c8eSEdward Tomasz Napierala 
917aa015c8eSEdward Tomasz Napierala 	if (need_qualifier) {
918aa015c8eSEdward Tomasz Napierala 		if (str == NULL) {
919aa015c8eSEdward Tomasz Napierala 			warnx("malformed ACL: unknown user or group name "
920aa015c8eSEdward Tomasz Napierala 			    "\"%s\"", qualifier_field);
921aa015c8eSEdward Tomasz Napierala 			goto truncated_entry;
922aa015c8eSEdward Tomasz Napierala 		}
923aa015c8eSEdward Tomasz Napierala 
924aa015c8eSEdward Tomasz Napierala 		error = parse_appended_id(str, entry);
925aa015c8eSEdward Tomasz Napierala 		if (error)
926aa015c8eSEdward Tomasz Napierala 			goto malformed_field;
927aa015c8eSEdward Tomasz Napierala 	}
928aa015c8eSEdward Tomasz Napierala 
929aa015c8eSEdward Tomasz Napierala 	return (0);
930aa015c8eSEdward Tomasz Napierala 
931aa015c8eSEdward Tomasz Napierala truncated_entry:
932aa015c8eSEdward Tomasz Napierala malformed_field:
933aa015c8eSEdward Tomasz Napierala 	acl_delete_entry(aclp, entry);
934aa015c8eSEdward Tomasz Napierala 	errno = EINVAL;
935aa015c8eSEdward Tomasz Napierala 	return (-1);
936aa015c8eSEdward Tomasz Napierala }
937