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_to_text_nfs4.c 326193 2017-11-25 17:12:48Z pfg $");
32#else
33__RCSID("$NetBSD: acl_to_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 <sys/syscall.h>
45#include <sys/types.h>
46#include <sys/acl.h>
47
48#include "acl_support.h"
49
50#define MAX_ENTRY_LENGTH 512
51
52static int
53format_who(char *str, size_t size, const acl_entry_t entry, int numeric)
54{
55	int error;
56	acl_tag_t tag;
57	struct passwd *pwd;
58	struct group *grp;
59	uid_t *id;
60
61	error = acl_get_tag_type(entry, &tag);
62	if (error)
63		return (error);
64
65	switch (tag) {
66	case ACL_USER_OBJ:
67		snprintf(str, size, "owner@");
68		break;
69
70	case ACL_USER:
71		id = (uid_t *)acl_get_qualifier(entry);
72		if (id == NULL)
73			return (-1);
74		/* XXX: Thread-unsafe. */
75		if (!numeric)
76			pwd = getpwuid(*id);
77		else
78			pwd = NULL;
79		if (pwd == NULL)
80			snprintf(str, size, "user:%d", (unsigned int)*id);
81		else
82			snprintf(str, size, "user:%s", pwd->pw_name);
83		break;
84
85	case ACL_GROUP_OBJ:
86		snprintf(str, size, "group@");
87		break;
88
89	case ACL_GROUP:
90		id = (uid_t *)acl_get_qualifier(entry);
91		if (id == NULL)
92			return (-1);
93		/* XXX: Thread-unsafe. */
94		if (!numeric)
95			grp = getgrgid(*id);
96		else
97			grp = NULL;
98		if (grp == NULL)
99			snprintf(str, size, "group:%d", (unsigned int)*id);
100		else
101			snprintf(str, size, "group:%s", grp->gr_name);
102		break;
103
104	case ACL_EVERYONE:
105		snprintf(str, size, "everyone@");
106		break;
107
108	default:
109		return (-1);
110	}
111
112	return (0);
113}
114
115static int
116format_entry_type(char *str, size_t size, const acl_entry_t entry)
117{
118	int error;
119	acl_entry_type_t entry_type;
120
121	error = acl_get_entry_type_np(entry, &entry_type);
122	if (error)
123		return (error);
124
125	switch (entry_type) {
126	case ACL_ENTRY_TYPE_ALLOW:
127		snprintf(str, size, "allow");
128		break;
129	case ACL_ENTRY_TYPE_DENY:
130		snprintf(str, size, "deny");
131		break;
132	case ACL_ENTRY_TYPE_AUDIT:
133		snprintf(str, size, "audit");
134		break;
135	case ACL_ENTRY_TYPE_ALARM:
136		snprintf(str, size, "alarm");
137		break;
138	default:
139		return (-1);
140	}
141
142	return (0);
143}
144
145static int
146format_additional_id(char *str, size_t size, const acl_entry_t entry)
147{
148	int error;
149	acl_tag_t tag;
150	uid_t *id;
151
152	error = acl_get_tag_type(entry, &tag);
153	if (error)
154		return (error);
155
156	switch (tag) {
157	case ACL_USER_OBJ:
158	case ACL_GROUP_OBJ:
159	case ACL_EVERYONE:
160		str[0] = '\0';
161		break;
162
163	default:
164		id = (uid_t *)acl_get_qualifier(entry);
165		if (id == NULL)
166			return (-1);
167		snprintf(str, size, ":%d", (unsigned int)*id);
168	}
169
170	return (0);
171}
172
173static int
174format_entry(char *str, size_t size, const acl_entry_t entry, int flags)
175{
176	size_t off = 0, min_who_field_length = 18;
177	acl_permset_t permset;
178	acl_flagset_t flagset;
179	int error;
180	size_t len;
181	char buf[MAX_ENTRY_LENGTH + 1];
182
183	assert(_entry_brand(entry) == ACL_BRAND_NFS4);
184
185	error = acl_get_flagset_np(entry, &flagset);
186	if (error)
187		return (error);
188
189	error = acl_get_permset(entry, &permset);
190	if (error)
191		return (error);
192
193	error = format_who(buf, sizeof(buf), entry,
194	    flags & ACL_TEXT_NUMERIC_IDS);
195	if (error)
196		return (error);
197	len = strlen(buf);
198	if (len < min_who_field_length)
199		len = min_who_field_length;
200	off += snprintf(str + off, size - off, "%*s:", (int)len, buf);
201
202	error = _nfs4_format_access_mask(buf, sizeof(buf), *permset,
203	    flags & ACL_TEXT_VERBOSE);
204	if (error)
205		return (error);
206	off += snprintf(str + off, size - off, "%s:", buf);
207
208	error = _nfs4_format_flags(buf, sizeof(buf), *flagset,
209	    flags & ACL_TEXT_VERBOSE);
210	if (error)
211		return (error);
212	off += snprintf(str + off, size - off, "%s:", buf);
213
214	error = format_entry_type(buf, sizeof(buf), entry);
215	if (error)
216		return (error);
217	off += snprintf(str + off, size - off, "%s", buf);
218
219	if (flags & ACL_TEXT_APPEND_ID) {
220		error = format_additional_id(buf, sizeof(buf), entry);
221		if (error)
222			return (error);
223		off += snprintf(str + off, size - off, "%s", buf);
224	}
225
226	off += snprintf(str + off, size - off, "\n");
227
228	/* Make sure we didn't truncate anything. */
229	assert (off < size);
230
231	return (0);
232}
233
234char *
235_nfs4_acl_to_text_np(const acl_t aclp, ssize_t *len_p, int flags)
236{
237	int error, entry_id = ACL_FIRST_ENTRY;
238	size_t off = 0, size;
239	char *str;
240	acl_entry_t entry;
241
242	if (aclp->ats_acl.acl_cnt == 0)
243		return strdup("");
244
245	size = aclp->ats_acl.acl_cnt * MAX_ENTRY_LENGTH;
246	str = malloc(size);
247	if (str == NULL)
248		return (NULL);
249
250	while (acl_get_entry(aclp, entry_id, &entry) == 1) {
251		entry_id = ACL_NEXT_ENTRY;
252
253		assert(off < size);
254
255		error = format_entry(str + off, size - off, entry, flags);
256		if (error) {
257			free(str);
258			errno = EINVAL;
259			return (NULL);
260		}
261
262		off = strlen(str);
263	}
264
265	assert(off < size);
266	str[off] = '\0';
267
268	if (len_p != NULL)
269		*len_p = off;
270
271	return (str);
272}
273