1/*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 1999-2001, 2008 Robert N. M. Watson
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 * Support functionality for the POSIX.1e ACL interface
30 * These calls are intended only to be called within the library.
31 */
32
33#include <sys/types.h>
34#include "namespace.h"
35#include <sys/acl.h>
36#include "un-namespace.h"
37#include <errno.h>
38#include <grp.h>
39#include <pwd.h>
40#include <stdio.h>
41#include <stdlib.h>
42#include <string.h>
43#include <assert.h>
44
45#include "acl_support.h"
46
47#define ACL_STRING_PERM_WRITE   'w'
48#define ACL_STRING_PERM_READ    'r'
49#define ACL_STRING_PERM_EXEC    'x'
50#define ACL_STRING_PERM_NONE    '-'
51
52/*
53 * Return 0, if both ACLs are identical.
54 */
55int
56_acl_differs(const acl_t a, const acl_t b)
57{
58	int i;
59	struct acl_entry *entrya, *entryb;
60
61	assert(_acl_brand(a) == _acl_brand(b));
62
63	if (a->ats_acl.acl_cnt != b->ats_acl.acl_cnt)
64		return (1);
65
66	for (i = 0; i < b->ats_acl.acl_cnt; i++) {
67		entrya = &(a->ats_acl.acl_entry[i]);
68		entryb = &(b->ats_acl.acl_entry[i]);
69
70		if (entrya->ae_tag != entryb->ae_tag ||
71		    entrya->ae_id != entryb->ae_id ||
72		    entrya->ae_perm != entryb->ae_perm ||
73		    entrya->ae_entry_type != entryb->ae_entry_type ||
74		    entrya->ae_flags != entryb->ae_flags)
75			return (1);
76	}
77
78	return (0);
79}
80
81/*
82 * _posix1e_acl_entry_compare -- compare two acl_entry structures to
83 * determine the order they should appear in.  Used by _posix1e_acl_sort to
84 * sort ACL entries into the kernel-desired order -- i.e., the order useful
85 * for evaluation and O(n) validity checking.  Beter to have an O(nlogn) sort
86 * in userland and an O(n) in kernel than to have both in kernel.
87 */
88typedef int (*compare)(const void *, const void *);
89static int
90_posix1e_acl_entry_compare(struct acl_entry *a, struct acl_entry *b)
91{
92
93	assert(_entry_brand(a) == ACL_BRAND_POSIX);
94	assert(_entry_brand(b) == ACL_BRAND_POSIX);
95
96	/*
97	 * First, sort between tags -- conveniently defined in the correct
98	 * order for verification.
99	 */
100	if (a->ae_tag < b->ae_tag)
101		return (-1);
102	if (a->ae_tag > b->ae_tag)
103		return (1);
104
105	/*
106	 * Next compare uids/gids on appropriate types.
107	 */
108
109	if (a->ae_tag == ACL_USER || a->ae_tag == ACL_GROUP) {
110		if (a->ae_id < b->ae_id)
111			return (-1);
112		if (a->ae_id > b->ae_id)
113			return (1);
114
115		/* shouldn't be equal, fall through to the invalid case */
116	}
117
118	/*
119	 * Don't know how to sort multiple entries of the rest--either it's
120	 * a bad entry, or there shouldn't be more than one.  Ignore and the
121	 * validity checker can get it later.
122	 */
123	return (0);
124}
125
126/*
127 * _posix1e_acl_sort -- sort ACL entries in POSIX.1e-formatted ACLs.
128 */
129void
130_posix1e_acl_sort(acl_t acl)
131{
132	struct acl *acl_int;
133
134	acl_int = &acl->ats_acl;
135
136	qsort(&acl_int->acl_entry[0], acl_int->acl_cnt,
137	    sizeof(struct acl_entry), (compare) _posix1e_acl_entry_compare);
138}
139
140/*
141 * acl_posix1e -- in what situations should we acl_sort before submission?
142 * We apply posix1e ACL semantics for any ACL of type ACL_TYPE_ACCESS or
143 * ACL_TYPE_DEFAULT
144 */
145int
146_posix1e_acl(acl_t acl, acl_type_t type)
147{
148
149	if (_acl_brand(acl) != ACL_BRAND_POSIX)
150		return (0);
151
152	return ((type == ACL_TYPE_ACCESS) || (type == ACL_TYPE_DEFAULT));
153}
154
155/*
156 * _posix1e_acl_check -- given an ACL, check its validity.  This is mirrored
157 * from code in sys/kern/kern_acl.c, and if changes are made in one, they
158 * should be made in the other also.  This copy of acl_check is made
159 * available * in userland for the benefit of processes wanting to check ACLs
160 * for validity before submitting them to the kernel, or for performing
161 * in userland file system checking.  Needless to say, the kernel makes
162 * the real checks on calls to get/setacl.
163 *
164 * See the comments in kernel for explanation -- just briefly, it assumes
165 * an already sorted ACL, and checks based on that assumption.  The
166 * POSIX.1e interface, acl_valid(), will perform the sort before calling
167 * this.  Returns 0 on success, EINVAL on failure.
168 */
169int
170_posix1e_acl_check(acl_t acl)
171{
172	struct acl *acl_int;
173	struct acl_entry	*entry; 	/* current entry */
174	uid_t	highest_uid=0, highest_gid=0;
175	int	stage = ACL_USER_OBJ;
176	int	i = 0;
177	int	count_user_obj=0, count_user=0, count_group_obj=0,
178		count_group=0, count_mask=0, count_other=0;
179
180	acl_int = &acl->ats_acl;
181
182	/* printf("_posix1e_acl_check: checking acl with %d entries\n",
183	    acl->acl_cnt); */
184	while (i < acl_int->acl_cnt) {
185		entry = &acl_int->acl_entry[i];
186
187		if ((entry->ae_perm | ACL_PERM_BITS) != ACL_PERM_BITS)
188			return (EINVAL);
189
190		switch(entry->ae_tag) {
191		case ACL_USER_OBJ:
192			/* printf("_posix1e_acl_check: %d: ACL_USER_OBJ\n",
193			    i); */
194			if (stage > ACL_USER_OBJ)
195				return (EINVAL);
196			stage = ACL_USER;
197			count_user_obj++;
198			break;
199
200		case ACL_USER:
201			/* printf("_posix1e_acl_check: %d: ACL_USER\n", i); */
202			if (stage > ACL_USER)
203				return (EINVAL);
204			stage = ACL_USER;
205			if (count_user && (entry->ae_id <= highest_uid))
206				return (EINVAL);
207			highest_uid = entry->ae_id;
208			count_user++;
209			break;
210
211		case ACL_GROUP_OBJ:
212			/* printf("_posix1e_acl_check: %d: ACL_GROUP_OBJ\n",
213			    i); */
214			if (stage > ACL_GROUP_OBJ)
215				return (EINVAL);
216			stage = ACL_GROUP;
217			count_group_obj++;
218			break;
219
220		case ACL_GROUP:
221			/* printf("_posix1e_acl_check: %d: ACL_GROUP\n", i); */
222			if (stage > ACL_GROUP)
223				return (EINVAL);
224			stage = ACL_GROUP;
225			if (count_group && (entry->ae_id <= highest_gid))
226				return (EINVAL);
227			highest_gid = entry->ae_id;
228			count_group++;
229			break;
230
231		case ACL_MASK:
232			/* printf("_posix1e_acl_check: %d: ACL_MASK\n", i); */
233			if (stage > ACL_MASK)
234				return (EINVAL);
235			stage = ACL_MASK;
236			count_mask++;
237			break;
238
239		case ACL_OTHER:
240			/* printf("_posix1e_acl_check: %d: ACL_OTHER\n", i); */
241			if (stage > ACL_OTHER)
242				return (EINVAL);
243			stage = ACL_OTHER;
244			count_other++;
245			break;
246
247		default:
248			/* printf("_posix1e_acl_check: %d: INVALID\n", i); */
249			return (EINVAL);
250		}
251		i++;
252	}
253
254	if (count_user_obj != 1)
255		return (EINVAL);
256
257	if (count_group_obj != 1)
258		return (EINVAL);
259
260	if (count_mask != 0 && count_mask != 1)
261		return (EINVAL);
262
263	if (count_other != 1)
264		return (EINVAL);
265
266	return (0);
267}
268
269/*
270 * Given a right-shifted permission (i.e., direct ACL_PERM_* mask), fill
271 * in a string describing the permissions.
272 */
273int
274_posix1e_acl_perm_to_string(acl_perm_t perm, ssize_t buf_len, char *buf)
275{
276
277	if (buf_len < _POSIX1E_ACL_STRING_PERM_MAXSIZE + 1) {
278		errno = ENOMEM;
279		return (-1);
280	}
281
282	if ((perm | ACL_PERM_BITS) != ACL_PERM_BITS) {
283		errno = EINVAL;
284		return (-1);
285	}
286
287	buf[3] = 0;	/* null terminate */
288
289	if (perm & ACL_READ)
290		buf[0] = ACL_STRING_PERM_READ;
291	else
292		buf[0] = ACL_STRING_PERM_NONE;
293
294	if (perm & ACL_WRITE)
295		buf[1] = ACL_STRING_PERM_WRITE;
296	else
297		buf[1] = ACL_STRING_PERM_NONE;
298
299	if (perm & ACL_EXECUTE)
300		buf[2] = ACL_STRING_PERM_EXEC;
301	else
302		buf[2] = ACL_STRING_PERM_NONE;
303
304	return (0);
305}
306
307/*
308 * given a string, return a permission describing it
309 */
310int
311_posix1e_acl_string_to_perm(char *string, acl_perm_t *perm)
312{
313	acl_perm_t	myperm = ACL_PERM_NONE;
314	char	*ch;
315
316	ch = string;
317	while (*ch) {
318		switch(*ch) {
319		case ACL_STRING_PERM_READ:
320			myperm |= ACL_READ;
321			break;
322		case ACL_STRING_PERM_WRITE:
323			myperm |= ACL_WRITE;
324			break;
325		case ACL_STRING_PERM_EXEC:
326			myperm |= ACL_EXECUTE;
327			break;
328		case ACL_STRING_PERM_NONE:
329			break;
330		default:
331			return (EINVAL);
332		}
333		ch++;
334	}
335
336	*perm = myperm;
337	return (0);
338}
339
340/*
341 * Add an ACL entry without doing much checking, et al
342 */
343int
344_posix1e_acl_add_entry(acl_t acl, acl_tag_t tag, uid_t id, acl_perm_t perm)
345{
346	struct acl		*acl_int;
347	struct acl_entry	*e;
348
349	acl_int = &acl->ats_acl;
350
351	if (acl_int->acl_cnt >= ACL_MAX_ENTRIES) {
352		errno = ENOMEM;
353		return (-1);
354	}
355
356	e = &(acl_int->acl_entry[acl_int->acl_cnt]);
357	e->ae_perm = perm;
358	e->ae_tag = tag;
359	e->ae_id = id;
360	acl_int->acl_cnt++;
361
362	return (0);
363}
364
365/*
366 * Convert "old" type - ACL_TYPE_{ACCESS,DEFAULT}_OLD - into its "new"
367 * counterpart.  It's necessary for the old (pre-NFSv4 ACLs) binaries
368 * to work with new libc and kernel.  Fixing 'type' for old binaries with
369 * old libc and new kernel is being done by kern/vfs_acl.c:type_unold().
370 */
371int
372_acl_type_unold(acl_type_t type)
373{
374
375	switch (type) {
376	case ACL_TYPE_ACCESS_OLD:
377		return (ACL_TYPE_ACCESS);
378	case ACL_TYPE_DEFAULT_OLD:
379		return (ACL_TYPE_DEFAULT);
380	default:
381		return (type);
382	}
383}
384
385char *
386string_skip_whitespace(char *string)
387{
388
389	while (*string && ((*string == ' ') || (*string == '\t')))
390		string++;
391
392	return (string);
393}
394
395void
396string_trim_trailing_whitespace(char *string)
397{
398	char	*end;
399
400	if (*string == '\0')
401		return;
402
403	end = string + strlen(string) - 1;
404
405	while (end != string) {
406		if ((*end == ' ') || (*end == '\t')) {
407			*end = '\0';
408			end--;
409		} else {
410			return;
411		}
412	}
413
414	return;
415}
416