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