acl_from_text_nfs4.c revision 1.1 1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2008, 2009 Edward Tomasz Napieraa <trasz (at) 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