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