xref: /freebsd/lib/libc/posix1e/acl_from_text_nfs4.c (revision 63f537551380d2dab29fa402ad1269feae17e594)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2008, 2009 Edward Tomasz Napierała <trasz@FreeBSD.org>
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27 
28 #include <sys/cdefs.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <unistd.h>
32 #include <errno.h>
33 #include <assert.h>
34 #include <string.h>
35 #include <pwd.h>
36 #include <grp.h>
37 #include <ctype.h>
38 #include <err.h>
39 #include <sys/syscall.h>
40 #include <sys/types.h>
41 #include <sys/acl.h>
42 
43 #include "acl_support.h"
44 
45 #define MAX_ENTRY_LENGTH 512
46 
47 /*
48  * Parse the tag field of ACL entry passed as "str".  If qualifier
49  * needs to follow, then the variable referenced by "need_qualifier"
50  * is set to 1, otherwise it's set to 0.
51  */
52 static int
53 parse_tag(const char *str, acl_entry_t entry, int *need_qualifier)
54 {
55 
56 	assert(need_qualifier != NULL);
57 	*need_qualifier = 0;
58 
59 	if (strcmp(str, "owner@") == 0)
60 		return (acl_set_tag_type(entry, ACL_USER_OBJ));
61 	if (strcmp(str, "group@") == 0)
62 		return (acl_set_tag_type(entry, ACL_GROUP_OBJ));
63 	if (strcmp(str, "everyone@") == 0)
64 		return (acl_set_tag_type(entry, ACL_EVERYONE));
65 
66 	*need_qualifier = 1;
67 
68 	if (strcmp(str, "user") == 0 || strcmp(str, "u") == 0)
69 		return (acl_set_tag_type(entry, ACL_USER));
70 	if (strcmp(str, "group") == 0 || strcmp(str, "g") == 0)
71 		return (acl_set_tag_type(entry, ACL_GROUP));
72 
73 	warnx("malformed ACL: invalid \"tag\" field");
74 
75 	return (-1);
76 }
77 
78 /*
79  * Parse the qualifier field of ACL entry passed as "str".
80  * If user or group name cannot be resolved, then the variable
81  * referenced by "need_qualifier" is set to 1; it will be checked
82  * later to figure out whether the appended_id is required.
83  */
84 static int
85 parse_qualifier(char *str, acl_entry_t entry, int *need_qualifier)
86 {
87 	int qualifier_length, error;
88 	uid_t id;
89 	acl_tag_t tag;
90 
91 	assert(need_qualifier != NULL);
92 	*need_qualifier = 0;
93 
94 	qualifier_length = strlen(str);
95 
96 	if (qualifier_length == 0) {
97 		warnx("malformed ACL: empty \"qualifier\" field");
98 		return (-1);
99 	}
100 
101 	error = acl_get_tag_type(entry, &tag);
102 	if (error)
103 		return (error);
104 
105 	error = _acl_name_to_id(tag, str, &id);
106 	if (error) {
107 		*need_qualifier = 1;
108 		return (0);
109 	}
110 
111 	return (acl_set_qualifier(entry, &id));
112 }
113 
114 static int
115 parse_access_mask(char *str, acl_entry_t entry)
116 {
117 	int error;
118 	acl_perm_t perm;
119 
120 	error = _nfs4_parse_access_mask(str, &perm);
121 	if (error)
122 		return (error);
123 
124 	error = acl_set_permset(entry, &perm);
125 
126 	return (error);
127 }
128 
129 static int
130 parse_flags(char *str, acl_entry_t entry)
131 {
132 	int error;
133 	acl_flag_t flags;
134 
135 	error = _nfs4_parse_flags(str, &flags);
136 	if (error)
137 		return (error);
138 
139 	error = acl_set_flagset_np(entry, &flags);
140 
141 	return (error);
142 }
143 
144 static int
145 parse_entry_type(const char *str, acl_entry_t entry)
146 {
147 
148 	if (strcmp(str, "allow") == 0)
149 		return (acl_set_entry_type_np(entry, ACL_ENTRY_TYPE_ALLOW));
150 	if (strcmp(str, "deny") == 0)
151 		return (acl_set_entry_type_np(entry, ACL_ENTRY_TYPE_DENY));
152 	if (strcmp(str, "audit") == 0)
153 		return (acl_set_entry_type_np(entry, ACL_ENTRY_TYPE_AUDIT));
154 	if (strcmp(str, "alarm") == 0)
155 		return (acl_set_entry_type_np(entry, ACL_ENTRY_TYPE_ALARM));
156 
157 	warnx("malformed ACL: invalid \"type\" field");
158 
159 	return (-1);
160 }
161 
162 static int
163 parse_appended_id(char *str, acl_entry_t entry)
164 {
165 	int qualifier_length;
166 	char *end;
167 	id_t id;
168 
169 	qualifier_length = strlen(str);
170 	if (qualifier_length == 0) {
171 		warnx("malformed ACL: \"appended id\" field present, "
172 	           "but empty");
173 		return (-1);
174 	}
175 
176 	id = strtod(str, &end);
177 	if (end - str != qualifier_length) {
178 		warnx("malformed ACL: appended id is not a number");
179 		return (-1);
180 	}
181 
182 	return (acl_set_qualifier(entry, &id));
183 }
184 
185 static int
186 number_of_colons(const char *str)
187 {
188 	int count = 0;
189 
190 	while (*str != '\0') {
191 		if (*str == ':')
192 			count++;
193 
194 		str++;
195 	}
196 
197 	return (count);
198 }
199 
200 int
201 _nfs4_acl_entry_from_text(acl_t aclp, char *str)
202 {
203 	int error, need_qualifier;
204 	acl_entry_t entry;
205 	char *field, *qualifier_field;
206 
207 	error = acl_create_entry(&aclp, &entry);
208 	if (error)
209 		return (error);
210 
211 	assert(_entry_brand(entry) == ACL_BRAND_NFS4);
212 
213 	if (str == NULL)
214 		goto truncated_entry;
215 	field = strsep(&str, ":");
216 
217 	field = string_skip_whitespace(field);
218 	if ((*field == '\0') && (!str)) {
219 		/*
220 		 * Is an entirely comment line, skip to next
221 		 * comma.
222 		 */
223 		return (0);
224 	}
225 
226 	error = parse_tag(field, entry, &need_qualifier);
227 	if (error)
228 		goto malformed_field;
229 
230 	if (need_qualifier) {
231 		if (str == NULL)
232 			goto truncated_entry;
233 		qualifier_field = field = strsep(&str, ":");
234 		error = parse_qualifier(field, entry, &need_qualifier);
235 		if (error)
236 			goto malformed_field;
237 	}
238 
239 	if (str == NULL)
240 		goto truncated_entry;
241 	field = strsep(&str, ":");
242 	error = parse_access_mask(field, entry);
243 	if (error)
244 		goto malformed_field;
245 
246 	if (str == NULL)
247 		goto truncated_entry;
248 	/* Do we have "flags" field? */
249 	if (number_of_colons(str) > 0) {
250 		field = strsep(&str, ":");
251 		error = parse_flags(field, entry);
252 		if (error)
253 			goto malformed_field;
254 	}
255 
256 	if (str == NULL)
257 		goto truncated_entry;
258 	field = strsep(&str, ":");
259 	error = parse_entry_type(field, entry);
260 	if (error)
261 		goto malformed_field;
262 
263 	if (need_qualifier) {
264 		if (str == NULL) {
265 			warnx("malformed ACL: unknown user or group name "
266 			    "\"%s\"", qualifier_field);
267 			goto truncated_entry;
268 		}
269 
270 		error = parse_appended_id(str, entry);
271 		if (error)
272 			goto malformed_field;
273 	}
274 
275 	return (0);
276 
277 truncated_entry:
278 malformed_field:
279 	acl_delete_entry(aclp, entry);
280 	errno = EINVAL;
281 	return (-1);
282 }
283