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