156055Srwatson/*-
2180493Srwatson * Copyright (c) 1999-2001, 2008 Robert N. M. Watson
356055Srwatson * All rights reserved.
456055Srwatson *
556055Srwatson * Redistribution and use in source and binary forms, with or without
656055Srwatson * modification, are permitted provided that the following conditions
756055Srwatson * are met:
856055Srwatson * 1. Redistributions of source code must retain the above copyright
956055Srwatson *    notice, this list of conditions and the following disclaimer.
1056055Srwatson * 2. Redistributions in binary form must reproduce the above copyright
1156055Srwatson *    notice, this list of conditions and the following disclaimer in the
1256055Srwatson *    documentation and/or other materials provided with the distribution.
1356055Srwatson *
1456055Srwatson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1556055Srwatson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1656055Srwatson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1756055Srwatson * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1856055Srwatson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1956055Srwatson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2056055Srwatson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2156055Srwatson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2256055Srwatson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2356055Srwatson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2456055Srwatson * SUCH DAMAGE.
2556055Srwatson */
2656055Srwatson/*
2756055Srwatson * Support functionality for the POSIX.1e ACL interface
2856055Srwatson * These calls are intended only to be called within the library.
2956055Srwatson */
3056055Srwatson
3192986Sobrien#include <sys/cdefs.h>
3292986Sobrien__FBSDID("$FreeBSD$");
3392986Sobrien
3456055Srwatson#include <sys/types.h>
3575185Stmm#include "namespace.h"
3656055Srwatson#include <sys/acl.h>
3775185Stmm#include "un-namespace.h"
3856055Srwatson#include <errno.h>
3956055Srwatson#include <grp.h>
4056055Srwatson#include <pwd.h>
4156055Srwatson#include <stdio.h>
4256055Srwatson#include <stdlib.h>
43194955Strasz#include <string.h>
44194955Strasz#include <assert.h>
4556055Srwatson
4656055Srwatson#include "acl_support.h"
4756055Srwatson
4856055Srwatson#define ACL_STRING_PERM_WRITE   'w'
4956055Srwatson#define ACL_STRING_PERM_READ    'r'
5056055Srwatson#define ACL_STRING_PERM_EXEC    'x'
5156055Srwatson#define ACL_STRING_PERM_NONE    '-'
5256055Srwatson
5356055Srwatson/*
54194955Strasz * Return 0, if both ACLs are identical.
55194955Strasz */
56194955Straszint
57194955Strasz_acl_differs(const acl_t a, const acl_t b)
58194955Strasz{
59194955Strasz	int i;
60194955Strasz	struct acl_entry *entrya, *entryb;
61194955Strasz
62194955Strasz	assert(_acl_brand(a) == _acl_brand(b));
63194955Strasz	assert(_acl_brand(a) != ACL_BRAND_UNKNOWN);
64194955Strasz	assert(_acl_brand(b) != ACL_BRAND_UNKNOWN);
65194955Strasz
66194955Strasz	if (a->ats_acl.acl_cnt != b->ats_acl.acl_cnt)
67194955Strasz		return (1);
68194955Strasz
69194955Strasz	for (i = 0; i < b->ats_acl.acl_cnt; i++) {
70194955Strasz		entrya = &(a->ats_acl.acl_entry[i]);
71194955Strasz		entryb = &(b->ats_acl.acl_entry[i]);
72194955Strasz
73194955Strasz		if (entrya->ae_tag != entryb->ae_tag ||
74194955Strasz		    entrya->ae_id != entryb->ae_id ||
75194955Strasz		    entrya->ae_perm != entryb->ae_perm ||
76194955Strasz		    entrya->ae_entry_type != entryb->ae_entry_type ||
77194955Strasz		    entrya->ae_flags != entryb->ae_flags)
78194955Strasz			return (1);
79194955Strasz	}
80194955Strasz
81194955Strasz	return (0);
82194955Strasz}
83196638Skientzle
84194955Strasz/*
8574191Srwatson * _posix1e_acl_entry_compare -- compare two acl_entry structures to
8674191Srwatson * determine the order they should appear in.  Used by _posix1e_acl_sort to
8774191Srwatson * sort ACL entries into the kernel-desired order -- i.e., the order useful
8874191Srwatson * for evaluation and O(n) validity checking.  Beter to have an O(nlogn) sort
8974191Srwatson * in userland and an O(n) in kernel than to have both in kernel.
9056055Srwatson */
9156055Srwatsontypedef int (*compare)(const void *, const void *);
9256055Srwatsonstatic int
9374191Srwatson_posix1e_acl_entry_compare(struct acl_entry *a, struct acl_entry *b)
9456055Srwatson{
95194955Strasz
96194955Strasz	assert(_entry_brand(a) == ACL_BRAND_POSIX);
97194955Strasz	assert(_entry_brand(b) == ACL_BRAND_POSIX);
98194955Strasz
9956055Srwatson	/*
10056055Srwatson	 * First, sort between tags -- conveniently defined in the correct
10156055Srwatson	 * order for verification.
10256055Srwatson	 */
10356055Srwatson	if (a->ae_tag < b->ae_tag)
10456055Srwatson		return (-1);
10556055Srwatson	if (a->ae_tag > b->ae_tag)
10656055Srwatson		return (1);
10756055Srwatson
10856055Srwatson	/*
10956055Srwatson	 * Next compare uids/gids on appropriate types.
11056055Srwatson	 */
11156055Srwatson
11256055Srwatson	if (a->ae_tag == ACL_USER || a->ae_tag == ACL_GROUP) {
11356055Srwatson		if (a->ae_id < b->ae_id)
11456055Srwatson			return (-1);
11556055Srwatson		if (a->ae_id > b->ae_id)
11656055Srwatson			return (1);
11756055Srwatson
11856055Srwatson		/* shouldn't be equal, fall through to the invalid case */
11956055Srwatson	}
12056055Srwatson
12156055Srwatson	/*
12256055Srwatson	 * Don't know how to sort multiple entries of the rest--either it's
12356055Srwatson	 * a bad entry, or there shouldn't be more than one.  Ignore and the
12456055Srwatson	 * validity checker can get it later.
12556055Srwatson	 */
12656055Srwatson	return (0);
12756055Srwatson}
12856055Srwatson
12956055Srwatson/*
130208785Strasz * _posix1e_acl_sort -- sort ACL entries in POSIX.1e-formatted ACLs.
13156055Srwatson */
132208785Straszvoid
13374191Srwatson_posix1e_acl_sort(acl_t acl)
13456055Srwatson{
13575928Sjedgar	struct acl *acl_int;
13656055Srwatson
13775928Sjedgar	acl_int = &acl->ats_acl;
13856055Srwatson
13975928Sjedgar	qsort(&acl_int->acl_entry[0], acl_int->acl_cnt,
14075928Sjedgar	    sizeof(struct acl_entry), (compare) _posix1e_acl_entry_compare);
14156055Srwatson}
14256055Srwatson
14356055Srwatson/*
14456625Srwatson * acl_posix1e -- in what situations should we acl_sort before submission?
14556625Srwatson * We apply posix1e ACL semantics for any ACL of type ACL_TYPE_ACCESS or
14656625Srwatson * ACL_TYPE_DEFAULT
14756055Srwatson */
14856055Srwatsonint
14974191Srwatson_posix1e_acl(acl_t acl, acl_type_t type)
15056055Srwatson{
15156055Srwatson
152194955Strasz	if (_acl_brand(acl) != ACL_BRAND_POSIX)
153194955Strasz		return (0);
154194955Strasz
15556625Srwatson	return ((type == ACL_TYPE_ACCESS) || (type == ACL_TYPE_DEFAULT));
15656055Srwatson}
15756055Srwatson
15856055Srwatson/*
15974191Srwatson * _posix1e_acl_check -- given an ACL, check its validity.  This is mirrored
16074191Srwatson * from code in sys/kern/kern_acl.c, and if changes are made in one, they
16174191Srwatson * should be made in the other also.  This copy of acl_check is made
16274191Srwatson * available * in userland for the benefit of processes wanting to check ACLs
163196638Skientzle * for validity before submitting them to the kernel, or for performing
16456055Srwatson * in userland file system checking.  Needless to say, the kernel makes
16556055Srwatson * the real checks on calls to get/setacl.
16656055Srwatson *
16756055Srwatson * See the comments in kernel for explanation -- just briefly, it assumes
16856055Srwatson * an already sorted ACL, and checks based on that assumption.  The
16956055Srwatson * POSIX.1e interface, acl_valid(), will perform the sort before calling
17056055Srwatson * this.  Returns 0 on success, EINVAL on failure.
17156055Srwatson */
17256055Srwatsonint
17375928Sjedgar_posix1e_acl_check(acl_t acl)
17456055Srwatson{
17575928Sjedgar	struct acl *acl_int;
17656055Srwatson	struct acl_entry	*entry; 	/* current entry */
177180493Srwatson	uid_t	highest_uid=0, highest_gid=0;
17856055Srwatson	int	stage = ACL_USER_OBJ;
17956055Srwatson	int	i = 0;
18056055Srwatson	int	count_user_obj=0, count_user=0, count_group_obj=0,
18156055Srwatson		count_group=0, count_mask=0, count_other=0;
18256055Srwatson
18375928Sjedgar	acl_int = &acl->ats_acl;
18475928Sjedgar
18574191Srwatson	/* printf("_posix1e_acl_check: checking acl with %d entries\n",
18674191Srwatson	    acl->acl_cnt); */
18775928Sjedgar	while (i < acl_int->acl_cnt) {
18875928Sjedgar		entry = &acl_int->acl_entry[i];
18956055Srwatson
19056055Srwatson		if ((entry->ae_perm | ACL_PERM_BITS) != ACL_PERM_BITS)
19156055Srwatson			return (EINVAL);
19256055Srwatson
19356055Srwatson		switch(entry->ae_tag) {
19456055Srwatson		case ACL_USER_OBJ:
19574191Srwatson			/* printf("_posix1e_acl_check: %d: ACL_USER_OBJ\n",
19674191Srwatson			    i); */
19756055Srwatson			if (stage > ACL_USER_OBJ)
19856055Srwatson				return (EINVAL);
19956055Srwatson			stage = ACL_USER;
20056055Srwatson			count_user_obj++;
20156055Srwatson			break;
202196638Skientzle
20356055Srwatson		case ACL_USER:
20474191Srwatson			/* printf("_posix1e_acl_check: %d: ACL_USER\n", i); */
20556055Srwatson			if (stage > ACL_USER)
20656055Srwatson				return (EINVAL);
20756055Srwatson			stage = ACL_USER;
20856055Srwatson			if (count_user && (entry->ae_id <= highest_uid))
20956055Srwatson				return (EINVAL);
21056055Srwatson			highest_uid = entry->ae_id;
21156055Srwatson			count_user++;
212196638Skientzle			break;
213196638Skientzle
21456055Srwatson		case ACL_GROUP_OBJ:
21574191Srwatson			/* printf("_posix1e_acl_check: %d: ACL_GROUP_OBJ\n",
21674191Srwatson			    i); */
21756055Srwatson			if (stage > ACL_GROUP_OBJ)
21856055Srwatson				return (EINVAL);
21956055Srwatson			stage = ACL_GROUP;
22056055Srwatson			count_group_obj++;
22156055Srwatson			break;
222196638Skientzle
22356055Srwatson		case ACL_GROUP:
22474191Srwatson			/* printf("_posix1e_acl_check: %d: ACL_GROUP\n", i); */
22556055Srwatson			if (stage > ACL_GROUP)
22656055Srwatson				return (EINVAL);
22756055Srwatson			stage = ACL_GROUP;
22856055Srwatson			if (count_group && (entry->ae_id <= highest_gid))
22956055Srwatson				return (EINVAL);
23056055Srwatson			highest_gid = entry->ae_id;
23156055Srwatson			count_group++;
23256055Srwatson			break;
233196638Skientzle
23456055Srwatson		case ACL_MASK:
23574191Srwatson			/* printf("_posix1e_acl_check: %d: ACL_MASK\n", i); */
23656055Srwatson			if (stage > ACL_MASK)
23756055Srwatson				return (EINVAL);
23856055Srwatson			stage = ACL_MASK;
23956055Srwatson			count_mask++;
24056055Srwatson			break;
241196638Skientzle
24256055Srwatson		case ACL_OTHER:
24374191Srwatson			/* printf("_posix1e_acl_check: %d: ACL_OTHER\n", i); */
24456055Srwatson			if (stage > ACL_OTHER)
24556055Srwatson				return (EINVAL);
24656055Srwatson			stage = ACL_OTHER;
24756055Srwatson			count_other++;
24856055Srwatson			break;
249196638Skientzle
25056055Srwatson		default:
25174191Srwatson			/* printf("_posix1e_acl_check: %d: INVALID\n", i); */
25256055Srwatson			return (EINVAL);
25356055Srwatson		}
25456055Srwatson		i++;
25556055Srwatson	}
25656055Srwatson
25756055Srwatson	if (count_user_obj != 1)
25856055Srwatson		return (EINVAL);
259196638Skientzle
26056055Srwatson	if (count_group_obj != 1)
26156055Srwatson		return (EINVAL);
26256055Srwatson
26356055Srwatson	if (count_mask != 0 && count_mask != 1)
26456055Srwatson		return (EINVAL);
26556055Srwatson
26656055Srwatson	if (count_other != 1)
26756055Srwatson		return (EINVAL);
26856055Srwatson
26956055Srwatson	return (0);
27056055Srwatson}
27156055Srwatson
27256055Srwatson/*
27356055Srwatson * Given a right-shifted permission (i.e., direct ACL_PERM_* mask), fill
27456055Srwatson * in a string describing the permissions.
27556055Srwatson */
27656055Srwatsonint
27774191Srwatson_posix1e_acl_perm_to_string(acl_perm_t perm, ssize_t buf_len, char *buf)
27856055Srwatson{
27956055Srwatson
28074191Srwatson	if (buf_len < _POSIX1E_ACL_STRING_PERM_MAXSIZE + 1) {
28156055Srwatson		errno = ENOMEM;
28256055Srwatson		return (-1);
28356055Srwatson	}
28456055Srwatson
28556055Srwatson	if ((perm | ACL_PERM_BITS) != ACL_PERM_BITS) {
28656055Srwatson		errno = EINVAL;
28756055Srwatson		return (-1);
28856055Srwatson	}
28956055Srwatson
29056055Srwatson	buf[3] = 0;	/* null terminate */
29156055Srwatson
29275404Sjedgar	if (perm & ACL_READ)
29356055Srwatson		buf[0] = ACL_STRING_PERM_READ;
29456055Srwatson	else
29556055Srwatson		buf[0] = ACL_STRING_PERM_NONE;
29656055Srwatson
29775404Sjedgar	if (perm & ACL_WRITE)
29856055Srwatson		buf[1] = ACL_STRING_PERM_WRITE;
29956055Srwatson	else
30056055Srwatson		buf[1] = ACL_STRING_PERM_NONE;
30156055Srwatson
30275404Sjedgar	if (perm & ACL_EXECUTE)
30356055Srwatson		buf[2] = ACL_STRING_PERM_EXEC;
30456055Srwatson	else
30556055Srwatson		buf[2] = ACL_STRING_PERM_NONE;
30656055Srwatson
30756055Srwatson	return (0);
30856055Srwatson}
30956055Srwatson
31056055Srwatson/*
31156055Srwatson * given a string, return a permission describing it
31256055Srwatson */
31356055Srwatsonint
31474191Srwatson_posix1e_acl_string_to_perm(char *string, acl_perm_t *perm)
31556055Srwatson{
31656055Srwatson	acl_perm_t	myperm = ACL_PERM_NONE;
31756055Srwatson	char	*ch;
31856055Srwatson
31956055Srwatson	ch = string;
32056055Srwatson	while (*ch) {
32156055Srwatson		switch(*ch) {
32256055Srwatson		case ACL_STRING_PERM_READ:
32375404Sjedgar			myperm |= ACL_READ;
32456055Srwatson			break;
32556055Srwatson		case ACL_STRING_PERM_WRITE:
32675404Sjedgar			myperm |= ACL_WRITE;
32756055Srwatson			break;
32856055Srwatson		case ACL_STRING_PERM_EXEC:
32975404Sjedgar			myperm |= ACL_EXECUTE;
33056055Srwatson			break;
33156055Srwatson		case ACL_STRING_PERM_NONE:
33256055Srwatson			break;
33356055Srwatson		default:
33456055Srwatson			return (EINVAL);
33556055Srwatson		}
33656055Srwatson		ch++;
33756055Srwatson	}
33856055Srwatson
33956055Srwatson	*perm = myperm;
34056055Srwatson	return (0);
34156055Srwatson}
34256055Srwatson
34356055Srwatson/*
34456055Srwatson * Add an ACL entry without doing much checking, et al
34556055Srwatson */
34656055Srwatsonint
34774191Srwatson_posix1e_acl_add_entry(acl_t acl, acl_tag_t tag, uid_t id, acl_perm_t perm)
34856055Srwatson{
34975928Sjedgar	struct acl		*acl_int;
35056055Srwatson	struct acl_entry	*e;
35156055Srwatson
35275928Sjedgar	acl_int = &acl->ats_acl;
35375928Sjedgar
35475928Sjedgar	if (acl_int->acl_cnt >= ACL_MAX_ENTRIES) {
35556055Srwatson		errno = ENOMEM;
35656055Srwatson		return (-1);
35756055Srwatson	}
35856055Srwatson
35975928Sjedgar	e = &(acl_int->acl_entry[acl_int->acl_cnt]);
36056055Srwatson	e->ae_perm = perm;
36156055Srwatson	e->ae_tag = tag;
36256055Srwatson	e->ae_id = id;
36375928Sjedgar	acl_int->acl_cnt++;
36456055Srwatson
36556055Srwatson	return (0);
36656055Srwatson}
367192586Strasz
368192586Strasz/*
369192586Strasz * Convert "old" type - ACL_TYPE_{ACCESS,DEFAULT}_OLD - into its "new"
370194955Strasz * counterpart.  It's neccessary for the old (pre-NFSv4 ACLs) binaries
371192586Strasz * to work with new libc and kernel.  Fixing 'type' for old binaries with
372192586Strasz * old libc and new kernel is being done by kern/vfs_acl.c:type_unold().
373192586Strasz */
374192586Straszint
375192586Strasz_acl_type_unold(acl_type_t type)
376192586Strasz{
377194955Strasz
378192586Strasz	switch (type) {
379192586Strasz	case ACL_TYPE_ACCESS_OLD:
380192586Strasz		return (ACL_TYPE_ACCESS);
381192586Strasz	case ACL_TYPE_DEFAULT_OLD:
382192586Strasz		return (ACL_TYPE_DEFAULT);
383192586Strasz	default:
384192586Strasz		return (type);
385192586Strasz	}
386192586Strasz}
387194955Strasz
388194955Straszchar *
389194955Straszstring_skip_whitespace(char *string)
390194955Strasz{
391194955Strasz
392194955Strasz	while (*string && ((*string == ' ') || (*string == '\t')))
393194955Strasz		string++;
394194955Strasz
395194955Strasz	return (string);
396194955Strasz}
397194955Strasz
398194955Straszvoid
399194955Straszstring_trim_trailing_whitespace(char *string)
400194955Strasz{
401194955Strasz	char	*end;
402194955Strasz
403194955Strasz	if (*string == '\0')
404194955Strasz		return;
405194955Strasz
406194955Strasz	end = string + strlen(string) - 1;
407194955Strasz
408194955Strasz	while (end != string) {
409194955Strasz		if ((*end == ' ') || (*end == '\t')) {
410194955Strasz			*end = '\0';
411194955Strasz			end--;
412194955Strasz		} else {
413194955Strasz			return;
414194955Strasz		}
415194955Strasz	}
416194955Strasz
417194955Strasz	return;
418194955Strasz}
419