1184588Sdfr/*-
2184588Sdfr * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3184588Sdfr *
4184588Sdfr * Copyright (c) 2008, 2009 Edward Tomasz Napiera��a <trasz@FreeBSD.org>
5184588Sdfr * All rights reserved.
6184588Sdfr *
7184588Sdfr * Redistribution and use in source and binary forms, with or without
8184588Sdfr * modification, are permitted provided that the following conditions
9184588Sdfr * are met:
10184588Sdfr * 1. Redistributions of source code must retain the above copyright
11184588Sdfr *    notice, this list of conditions and the following disclaimer.
12184588Sdfr * 2. Redistributions in binary form must reproduce the above copyright
13184588Sdfr *    notice, this list of conditions and the following disclaimer in the
14184588Sdfr *    documentation and/or other materials provided with the distribution.
15184588Sdfr *
16184588Sdfr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17184588Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18184588Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19184588Sdfr * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20184588Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21184588Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22184588Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23184588Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24184588Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25184588Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26184588Sdfr * SUCH DAMAGE.
27249592Sken */
28249592Sken
29249592Sken#include <sys/cdefs.h>
30249592Sken#if 0
31249592Sken__FBSDID("$FreeBSD: head/lib/libc/posix1e/acl_from_text_nfs4.c 326193 2017-11-25 17:12:48Z pfg $");
32249592Sken#else
33249592Sken__RCSID("$NetBSD: acl_from_text_nfs4.c,v 1.2 2024/01/20 14:52:48 christos Exp $");
34249592Sken#endif
35249592Sken
36249592Sken#include <stdio.h>
37249592Sken#include <stdlib.h>
38261054Smav#include <unistd.h>
39249592Sken#include <errno.h>
40249592Sken#include <assert.h>
41249592Sken#include <string.h>
42249592Sken#include <pwd.h>
43249592Sken#include <grp.h>
44249592Sken#include <ctype.h>
45249592Sken#include <err.h>
46249592Sken#include <sys/syscall.h>
47249592Sken#include <sys/types.h>
48249592Sken#include <sys/acl.h>
49249592Sken
50249592Sken#include "acl_support.h"
51249592Sken
52249592Sken#define MAX_ENTRY_LENGTH 512
53249592Sken
54249592Sken/*
55249592Sken * Parse the tag field of ACL entry passed as "str".  If qualifier
56249592Sken * needs to follow, then the variable referenced by "need_qualifier"
57249592Sken * is set to 1, otherwise it's set to 0.
58249592Sken */
59249592Skenstatic int
60249592Skenparse_tag(const char *str, acl_entry_t entry, int *need_qualifier)
61261054Smav{
62249592Sken
63249592Sken	assert(need_qualifier != NULL);
64249592Sken	*need_qualifier = 0;
65249592Sken
66249592Sken	if (strcmp(str, "owner@") == 0)
67249592Sken		return (acl_set_tag_type(entry, ACL_USER_OBJ));
68249592Sken	if (strcmp(str, "group@") == 0)
69249592Sken		return (acl_set_tag_type(entry, ACL_GROUP_OBJ));
70249592Sken	if (strcmp(str, "everyone@") == 0)
71249592Sken		return (acl_set_tag_type(entry, ACL_EVERYONE));
72261054Smav
73261054Smav	*need_qualifier = 1;
74261054Smav
75261054Smav	if (strcmp(str, "user") == 0 || strcmp(str, "u") == 0)
76261054Smav		return (acl_set_tag_type(entry, ACL_USER));
77249592Sken	if (strcmp(str, "group") == 0 || strcmp(str, "g") == 0)
78249592Sken		return (acl_set_tag_type(entry, ACL_GROUP));
79249592Sken
80249592Sken	warnx("malformed ACL: invalid \"tag\" field");
81249592Sken
82249592Sken	return (-1);
83249592Sken}
84249592Sken
85249592Sken/*
86249592Sken * Parse the qualifier field of ACL entry passed as "str".
87261049Smav * If user or group name cannot be resolved, then the variable
88249592Sken * referenced by "need_qualifier" is set to 1; it will be checked
89249592Sken * later to figure out whether the appended_id is required.
90249592Sken */
91249592Skenstatic int
92249592Skenparse_qualifier(char *str, acl_entry_t entry, int *need_qualifier)
93249592Sken{
94249592Sken	size_t qualifier_length;
95249592Sken	int error;
96249592Sken	uid_t id;
97249592Sken	acl_tag_t tag;
98261054Smav
99249592Sken	assert(need_qualifier != NULL);
100249592Sken	*need_qualifier = 0;
101249592Sken
102249592Sken	qualifier_length = strlen(str);
103249592Sken
104249592Sken	if (qualifier_length == 0) {
105249592Sken		warnx("malformed ACL: empty \"qualifier\" field");
106249592Sken		return (-1);
107184588Sdfr	}
108249592Sken
109249592Sken	error = acl_get_tag_type(entry, &tag);
110249592Sken	if (error)
111249592Sken		return (error);
112249592Sken
113249592Sken	error = _acl_name_to_id(tag, str, &id);
114249592Sken	if (error) {
115		*need_qualifier = 1;
116		return (0);
117	}
118
119	return (acl_set_qualifier(entry, &id));
120}
121
122static int
123parse_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
137static int
138parse_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
152static int
153parse_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
170static int
171parse_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
193static int
194number_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
208int
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
285truncated_entry:
286malformed_field:
287	acl_delete_entry(aclp, entry);
288	errno = EINVAL;
289	return (-1);
290}
291