xref: /netbsd-src/lib/libc/posix1e/acl_from_text_nfs4.c (revision ace5b9b5feb0e7608bd2da7a617428d2e1cf8aa3)
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.2 2024/01/20 14:52:48 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
parse_tag(const char * str,acl_entry_t entry,int * need_qualifier)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
parse_qualifier(char * str,acl_entry_t entry,int * need_qualifier)92 parse_qualifier(char *str, acl_entry_t entry, int *need_qualifier)
93 {
94 	size_t qualifier_length;
95 	int error;
96 	uid_t id;
97 	acl_tag_t tag;
98 
99 	assert(need_qualifier != NULL);
100 	*need_qualifier = 0;
101 
102 	qualifier_length = strlen(str);
103 
104 	if (qualifier_length == 0) {
105 		warnx("malformed ACL: empty \"qualifier\" field");
106 		return (-1);
107 	}
108 
109 	error = acl_get_tag_type(entry, &tag);
110 	if (error)
111 		return (error);
112 
113 	error = _acl_name_to_id(tag, str, &id);
114 	if (error) {
115 		*need_qualifier = 1;
116 		return (0);
117 	}
118 
119 	return (acl_set_qualifier(entry, &id));
120 }
121 
122 static int
parse_access_mask(char * str,acl_entry_t entry)123 parse_access_mask(char *str, acl_entry_t entry)
124 {
125 	int error;
126 	acl_perm_t perm;
127 
128 	error = _nfs4_parse_access_mask(str, &perm);
129 	if (error)
130 		return (error);
131 
132 	error = acl_set_permset(entry, &perm);
133 
134 	return (error);
135 }
136 
137 static int
parse_flags(char * str,acl_entry_t entry)138 parse_flags(char *str, acl_entry_t entry)
139 {
140 	int error;
141 	acl_flag_t flags;
142 
143 	error = _nfs4_parse_flags(str, &flags);
144 	if (error)
145 		return (error);
146 
147 	error = acl_set_flagset_np(entry, &flags);
148 
149 	return (error);
150 }
151 
152 static int
parse_entry_type(const char * str,acl_entry_t entry)153 parse_entry_type(const char *str, acl_entry_t entry)
154 {
155 
156 	if (strcmp(str, "allow") == 0)
157 		return (acl_set_entry_type_np(entry, ACL_ENTRY_TYPE_ALLOW));
158 	if (strcmp(str, "deny") == 0)
159 		return (acl_set_entry_type_np(entry, ACL_ENTRY_TYPE_DENY));
160 	if (strcmp(str, "audit") == 0)
161 		return (acl_set_entry_type_np(entry, ACL_ENTRY_TYPE_AUDIT));
162 	if (strcmp(str, "alarm") == 0)
163 		return (acl_set_entry_type_np(entry, ACL_ENTRY_TYPE_ALARM));
164 
165 	warnx("malformed ACL: invalid \"type\" field");
166 
167 	return (-1);
168 }
169 
170 static int
parse_appended_id(char * str,acl_entry_t entry)171 parse_appended_id(char *str, acl_entry_t entry)
172 {
173 	size_t qualifier_length;
174 	char *end;
175 	id_t id;
176 
177 	qualifier_length = strlen(str);
178 	if (qualifier_length == 0) {
179 		warnx("malformed ACL: \"appended id\" field present, "
180 	           "but empty");
181 		return (-1);
182 	}
183 
184 	id = strtod(str, &end);
185 	if ((size_t)(end - str) != qualifier_length) {
186 		warnx("malformed ACL: appended id is not a number");
187 		return (-1);
188 	}
189 
190 	return (acl_set_qualifier(entry, &id));
191 }
192 
193 static int
number_of_colons(const char * str)194 number_of_colons(const char *str)
195 {
196 	int count = 0;
197 
198 	while (*str != '\0') {
199 		if (*str == ':')
200 			count++;
201 
202 		str++;
203 	}
204 
205 	return (count);
206 }
207 
208 int
_nfs4_acl_entry_from_text(acl_t aclp,char * str)209 _nfs4_acl_entry_from_text(acl_t aclp, char *str)
210 {
211 	int error, need_qualifier;
212 	acl_entry_t entry;
213 	char *field, *qualifier_field = NULL;
214 
215 	error = acl_create_entry(&aclp, &entry);
216 	if (error)
217 		return (error);
218 
219 	assert(_entry_brand(entry) == ACL_BRAND_NFS4);
220 
221 	if (str == NULL)
222 		goto truncated_entry;
223 	field = strsep(&str, ":");
224 
225 	field = string_skip_whitespace(field);
226 	if ((*field == '\0') && (!str)) {
227 		/*
228 		 * Is an entirely comment line, skip to next
229 		 * comma.
230 		 */
231 		return (0);
232 	}
233 
234 	error = parse_tag(field, entry, &need_qualifier);
235 	if (error)
236 		goto malformed_field;
237 
238 	if (need_qualifier) {
239 		if (str == NULL)
240 			goto truncated_entry;
241 		qualifier_field = field = strsep(&str, ":");
242 		error = parse_qualifier(field, entry, &need_qualifier);
243 		if (error)
244 			goto malformed_field;
245 	}
246 
247 	if (str == NULL)
248 		goto truncated_entry;
249 	field = strsep(&str, ":");
250 	error = parse_access_mask(field, entry);
251 	if (error)
252 		goto malformed_field;
253 
254 	if (str == NULL)
255 		goto truncated_entry;
256 	/* Do we have "flags" field? */
257 	if (number_of_colons(str) > 0) {
258 		field = strsep(&str, ":");
259 		error = parse_flags(field, entry);
260 		if (error)
261 			goto malformed_field;
262 	}
263 
264 	if (str == NULL)
265 		goto truncated_entry;
266 	field = strsep(&str, ":");
267 	error = parse_entry_type(field, entry);
268 	if (error)
269 		goto malformed_field;
270 
271 	if (need_qualifier) {
272 		if (str == NULL) {
273 			warnx("malformed ACL: unknown user or group name "
274 			    "\"%s\"", qualifier_field);
275 			goto truncated_entry;
276 		}
277 
278 		error = parse_appended_id(str, entry);
279 		if (error)
280 			goto malformed_field;
281 	}
282 
283 	return (0);
284 
285 truncated_entry:
286 malformed_field:
287 	acl_delete_entry(aclp, entry);
288 	errno = EINVAL;
289 	return (-1);
290 }
291