Home | History | Annotate | Line # | Download | only in kern
subr_acl_posix1e.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-2006 Robert N. M. Watson
      5  1.1  christos  * All rights reserved.
      6  1.1  christos  *
      7  1.1  christos  * This software was developed by Robert Watson for the TrustedBSD Project.
      8  1.1  christos  *
      9  1.1  christos  * Redistribution and use in source and binary forms, with or without
     10  1.1  christos  * modification, are permitted provided that the following conditions
     11  1.1  christos  * are met:
     12  1.1  christos  * 1. Redistributions of source code must retain the above copyright
     13  1.1  christos  *    notice, this list of conditions and the following disclaimer.
     14  1.1  christos  * 2. Redistributions in binary form must reproduce the above copyright
     15  1.1  christos  *    notice, this list of conditions and the following disclaimer in the
     16  1.1  christos  *    documentation and/or other materials provided with the distribution.
     17  1.1  christos  *
     18  1.1  christos  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
     19  1.1  christos  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     20  1.1  christos  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     21  1.1  christos  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
     22  1.1  christos  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     23  1.1  christos  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     24  1.1  christos  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     25  1.1  christos  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     26  1.1  christos  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     27  1.1  christos  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     28  1.1  christos  * SUCH DAMAGE.
     29  1.1  christos  */
     30  1.1  christos /*
     31  1.1  christos  * Developed by the TrustedBSD Project.
     32  1.1  christos  *
     33  1.1  christos  * ACL support routines specific to POSIX.1e access control lists.  These are
     34  1.1  christos  * utility routines for code common across file systems implementing POSIX.1e
     35  1.1  christos  * ACLs.
     36  1.1  christos  */
     37  1.1  christos 
     38  1.1  christos #include <sys/cdefs.h>
     39  1.1  christos #if 0
     40  1.1  christos __FBSDID("$FreeBSD: head/sys/kern/subr_acl_posix1e.c 341827 2018-12-11 19:32:16Z mjg $");
     41  1.1  christos #endif
     42  1.1  christos __KERNEL_RCSID(0, "$NetBSD: subr_acl_posix1e.c,v 1.1 2020/05/16 18:31:50 christos Exp $");
     43  1.1  christos 
     44  1.1  christos #include <sys/param.h>
     45  1.1  christos #include <sys/kernel.h>
     46  1.1  christos #include <sys/module.h>
     47  1.1  christos #include <sys/systm.h>
     48  1.1  christos #include <sys/mount.h>
     49  1.1  christos #include <sys/vnode.h>
     50  1.1  christos #include <sys/kauth.h>
     51  1.1  christos #include <sys/errno.h>
     52  1.1  christos #include <sys/stat.h>
     53  1.1  christos #include <sys/acl.h>
     54  1.1  christos 
     55  1.1  christos /*
     56  1.1  christos  * For the purposes of filesystems maintaining the _OBJ entries in an inode
     57  1.1  christos  * with a mode_t field, this routine converts a mode_t entry to an
     58  1.1  christos  * acl_perm_t.
     59  1.1  christos  */
     60  1.1  christos acl_perm_t
     61  1.1  christos acl_posix1e_mode_to_perm(acl_tag_t tag, mode_t mode)
     62  1.1  christos {
     63  1.1  christos 	acl_perm_t	perm = 0;
     64  1.1  christos 
     65  1.1  christos 	switch(tag) {
     66  1.1  christos 	case ACL_USER_OBJ:
     67  1.1  christos 		if (mode & S_IXUSR)
     68  1.1  christos 			perm |= ACL_EXECUTE;
     69  1.1  christos 		if (mode & S_IRUSR)
     70  1.1  christos 			perm |= ACL_READ;
     71  1.1  christos 		if (mode & S_IWUSR)
     72  1.1  christos 			perm |= ACL_WRITE;
     73  1.1  christos 		return (perm);
     74  1.1  christos 
     75  1.1  christos 	case ACL_GROUP_OBJ:
     76  1.1  christos 		if (mode & S_IXGRP)
     77  1.1  christos 			perm |= ACL_EXECUTE;
     78  1.1  christos 		if (mode & S_IRGRP)
     79  1.1  christos 			perm |= ACL_READ;
     80  1.1  christos 		if (mode & S_IWGRP)
     81  1.1  christos 			perm |= ACL_WRITE;
     82  1.1  christos 		return (perm);
     83  1.1  christos 
     84  1.1  christos 	case ACL_OTHER:
     85  1.1  christos 		if (mode & S_IXOTH)
     86  1.1  christos 			perm |= ACL_EXECUTE;
     87  1.1  christos 		if (mode & S_IROTH)
     88  1.1  christos 			perm |= ACL_READ;
     89  1.1  christos 		if (mode & S_IWOTH)
     90  1.1  christos 			perm |= ACL_WRITE;
     91  1.1  christos 		return (perm);
     92  1.1  christos 
     93  1.1  christos 	default:
     94  1.1  christos 		printf("%s: invalid tag (%u)\n", __func__, tag);
     95  1.1  christos 		return (0);
     96  1.1  christos 	}
     97  1.1  christos }
     98  1.1  christos 
     99  1.1  christos /*
    100  1.1  christos  * Given inode information (uid, gid, mode), return an acl entry of the
    101  1.1  christos  * appropriate type.
    102  1.1  christos  */
    103  1.1  christos struct acl_entry
    104  1.1  christos acl_posix1e_mode_to_entry(acl_tag_t tag, uid_t uid, gid_t gid, mode_t mode)
    105  1.1  christos {
    106  1.1  christos 	struct acl_entry	acl_entry;
    107  1.1  christos 
    108  1.1  christos 	acl_entry.ae_tag = tag;
    109  1.1  christos 	acl_entry.ae_perm = acl_posix1e_mode_to_perm(tag, mode);
    110  1.1  christos 	acl_entry.ae_entry_type = 0;
    111  1.1  christos 	acl_entry.ae_flags = 0;
    112  1.1  christos 	switch(tag) {
    113  1.1  christos 	case ACL_USER_OBJ:
    114  1.1  christos 		acl_entry.ae_id = uid;
    115  1.1  christos 		break;
    116  1.1  christos 
    117  1.1  christos 	case ACL_GROUP_OBJ:
    118  1.1  christos 		acl_entry.ae_id = gid;
    119  1.1  christos 		break;
    120  1.1  christos 
    121  1.1  christos 	case ACL_OTHER:
    122  1.1  christos 		acl_entry.ae_id = ACL_UNDEFINED_ID;
    123  1.1  christos 		break;
    124  1.1  christos 
    125  1.1  christos 	default:
    126  1.1  christos 		acl_entry.ae_id = ACL_UNDEFINED_ID;
    127  1.1  christos 		printf("acl_posix1e_mode_to_entry: invalid tag (%d)\n", tag);
    128  1.1  christos 	}
    129  1.1  christos 
    130  1.1  christos 	return (acl_entry);
    131  1.1  christos }
    132  1.1  christos 
    133  1.1  christos /*
    134  1.1  christos  * Utility function to generate a file mode given appropriate ACL entries.
    135  1.1  christos  */
    136  1.1  christos mode_t
    137  1.1  christos acl_posix1e_perms_to_mode(struct acl_entry *acl_user_obj_entry,
    138  1.1  christos     struct acl_entry *acl_group_obj_entry, struct acl_entry *acl_other_entry)
    139  1.1  christos {
    140  1.1  christos 	mode_t	mode;
    141  1.1  christos 
    142  1.1  christos 	mode = 0;
    143  1.1  christos 	if (acl_user_obj_entry->ae_perm & ACL_EXECUTE)
    144  1.1  christos 		mode |= S_IXUSR;
    145  1.1  christos 	if (acl_user_obj_entry->ae_perm & ACL_READ)
    146  1.1  christos 		mode |= S_IRUSR;
    147  1.1  christos 	if (acl_user_obj_entry->ae_perm & ACL_WRITE)
    148  1.1  christos 		mode |= S_IWUSR;
    149  1.1  christos 	if (acl_group_obj_entry->ae_perm & ACL_EXECUTE)
    150  1.1  christos 		mode |= S_IXGRP;
    151  1.1  christos 	if (acl_group_obj_entry->ae_perm & ACL_READ)
    152  1.1  christos 		mode |= S_IRGRP;
    153  1.1  christos 	if (acl_group_obj_entry->ae_perm & ACL_WRITE)
    154  1.1  christos 		mode |= S_IWGRP;
    155  1.1  christos 	if (acl_other_entry->ae_perm & ACL_EXECUTE)
    156  1.1  christos 		mode |= S_IXOTH;
    157  1.1  christos 	if (acl_other_entry->ae_perm & ACL_READ)
    158  1.1  christos 		mode |= S_IROTH;
    159  1.1  christos 	if (acl_other_entry->ae_perm & ACL_WRITE)
    160  1.1  christos 		mode |= S_IWOTH;
    161  1.1  christos 
    162  1.1  christos 	return (mode);
    163  1.1  christos }
    164  1.1  christos 
    165  1.1  christos /*
    166  1.1  christos  * Utility function to generate a file mode given a complete POSIX.1e access
    167  1.1  christos  * ACL.  Note that if the ACL is improperly formed, this may result in a
    168  1.1  christos  * panic.
    169  1.1  christos  */
    170  1.1  christos mode_t
    171  1.1  christos acl_posix1e_acl_to_mode(struct acl *acl)
    172  1.1  christos {
    173  1.1  christos 	struct acl_entry *acl_mask, *acl_user_obj, *acl_group_obj, *acl_other;
    174  1.1  christos 	int i;
    175  1.1  christos 
    176  1.1  christos 	/*
    177  1.1  christos 	 * Find the ACL entries relevant to a POSIX permission mode.
    178  1.1  christos 	 */
    179  1.1  christos 	acl_user_obj = acl_group_obj = acl_other = acl_mask = NULL;
    180  1.1  christos 	for (i = 0; i < acl->acl_cnt; i++) {
    181  1.1  christos 		switch (acl->acl_entry[i].ae_tag) {
    182  1.1  christos 		case ACL_USER_OBJ:
    183  1.1  christos 			acl_user_obj = &acl->acl_entry[i];
    184  1.1  christos 			break;
    185  1.1  christos 
    186  1.1  christos 		case ACL_GROUP_OBJ:
    187  1.1  christos 			acl_group_obj = &acl->acl_entry[i];
    188  1.1  christos 			break;
    189  1.1  christos 
    190  1.1  christos 		case ACL_OTHER:
    191  1.1  christos 			acl_other = &acl->acl_entry[i];
    192  1.1  christos 			break;
    193  1.1  christos 
    194  1.1  christos 		case ACL_MASK:
    195  1.1  christos 			acl_mask = &acl->acl_entry[i];
    196  1.1  christos 			break;
    197  1.1  christos 
    198  1.1  christos 		case ACL_USER:
    199  1.1  christos 		case ACL_GROUP:
    200  1.1  christos 			break;
    201  1.1  christos 
    202  1.1  christos 		default:
    203  1.1  christos 			panic("acl_posix1e_acl_to_mode: bad ae_tag");
    204  1.1  christos 		}
    205  1.1  christos 	}
    206  1.1  christos 
    207  1.1  christos 	if (acl_user_obj == NULL || acl_group_obj == NULL || acl_other == NULL)
    208  1.1  christos 		panic("acl_posix1e_acl_to_mode: missing base ae_tags");
    209  1.1  christos 
    210  1.1  christos 	/*
    211  1.1  christos 	 * POSIX.1e specifies that if there is an ACL_MASK entry, we replace
    212  1.1  christos 	 * the mode "group" bits with its permissions.  If there isn't, we
    213  1.1  christos 	 * use the ACL_GROUP_OBJ permissions.
    214  1.1  christos 	 */
    215  1.1  christos 	if (acl_mask != NULL)
    216  1.1  christos 		return (acl_posix1e_perms_to_mode(acl_user_obj, acl_mask,
    217  1.1  christos 		    acl_other));
    218  1.1  christos 	else
    219  1.1  christos 		return (acl_posix1e_perms_to_mode(acl_user_obj, acl_group_obj,
    220  1.1  christos 		    acl_other));
    221  1.1  christos }
    222  1.1  christos 
    223  1.1  christos /*
    224  1.1  christos  * Perform a syntactic check of the ACL, sufficient to allow an implementing
    225  1.1  christos  * filesystem to determine if it should accept this and rely on the POSIX.1e
    226  1.1  christos  * ACL properties.
    227  1.1  christos  */
    228  1.1  christos int
    229  1.1  christos acl_posix1e_check(struct acl *acl)
    230  1.1  christos {
    231  1.1  christos 	int num_acl_user_obj, num_acl_user, num_acl_group_obj, num_acl_group;
    232  1.1  christos 	int num_acl_mask, num_acl_other, i;
    233  1.1  christos 
    234  1.1  christos 	/*
    235  1.1  christos 	 * Verify that the number of entries does not exceed the maximum
    236  1.1  christos 	 * defined for acl_t.
    237  1.1  christos 	 *
    238  1.1  christos 	 * Verify that the correct number of various sorts of ae_tags are
    239  1.1  christos 	 * present:
    240  1.1  christos 	 *   Exactly one ACL_USER_OBJ
    241  1.1  christos 	 *   Exactly one ACL_GROUP_OBJ
    242  1.1  christos 	 *   Exactly one ACL_OTHER
    243  1.1  christos 	 *   If any ACL_USER or ACL_GROUP entries appear, then exactly one
    244  1.1  christos 	 *   ACL_MASK entry must also appear.
    245  1.1  christos 	 *
    246  1.1  christos 	 * Verify that all ae_perm entries are in ACL_PERM_BITS.
    247  1.1  christos 	 *
    248  1.1  christos 	 * Verify all ae_tag entries are understood by this implementation.
    249  1.1  christos 	 *
    250  1.1  christos 	 * Note: Does not check for uniqueness of qualifier (ae_id) field.
    251  1.1  christos 	 */
    252  1.1  christos 	num_acl_user_obj = num_acl_user = num_acl_group_obj = num_acl_group =
    253  1.1  christos 	    num_acl_mask = num_acl_other = 0;
    254  1.1  christos 	if (acl->acl_cnt > ACL_MAX_ENTRIES)
    255  1.1  christos 		return (EINVAL);
    256  1.1  christos 	for (i = 0; i < acl->acl_cnt; i++) {
    257  1.1  christos 		struct acl_entry *ae = &acl->acl_entry[i];
    258  1.1  christos 		/*
    259  1.1  christos 		 * Check for a valid tag.
    260  1.1  christos 		 */
    261  1.1  christos 		switch(ae->ae_tag) {
    262  1.1  christos 		case ACL_USER_OBJ:
    263  1.1  christos 			ae->ae_id = ACL_UNDEFINED_ID; /* XXX */
    264  1.1  christos 			if (ae->ae_id != ACL_UNDEFINED_ID)
    265  1.1  christos 				return (EINVAL);
    266  1.1  christos 			num_acl_user_obj++;
    267  1.1  christos 			break;
    268  1.1  christos 		case ACL_GROUP_OBJ:
    269  1.1  christos 			ae->ae_id = ACL_UNDEFINED_ID; /* XXX */
    270  1.1  christos 			if (ae->ae_id != ACL_UNDEFINED_ID)
    271  1.1  christos 				return (EINVAL);
    272  1.1  christos 			num_acl_group_obj++;
    273  1.1  christos 			break;
    274  1.1  christos 		case ACL_USER:
    275  1.1  christos 			if (ae->ae_id == ACL_UNDEFINED_ID)
    276  1.1  christos 				return (EINVAL);
    277  1.1  christos 			num_acl_user++;
    278  1.1  christos 			break;
    279  1.1  christos 		case ACL_GROUP:
    280  1.1  christos 			if (ae->ae_id == ACL_UNDEFINED_ID)
    281  1.1  christos 				return (EINVAL);
    282  1.1  christos 			num_acl_group++;
    283  1.1  christos 			break;
    284  1.1  christos 		case ACL_OTHER:
    285  1.1  christos 			ae->ae_id = ACL_UNDEFINED_ID; /* XXX */
    286  1.1  christos 			if (ae->ae_id != ACL_UNDEFINED_ID)
    287  1.1  christos 				return (EINVAL);
    288  1.1  christos 			num_acl_other++;
    289  1.1  christos 			break;
    290  1.1  christos 		case ACL_MASK:
    291  1.1  christos 			ae->ae_id = ACL_UNDEFINED_ID; /* XXX */
    292  1.1  christos 			if (ae->ae_id != ACL_UNDEFINED_ID)
    293  1.1  christos 				return (EINVAL);
    294  1.1  christos 			num_acl_mask++;
    295  1.1  christos 			break;
    296  1.1  christos 		default:
    297  1.1  christos 			return (EINVAL);
    298  1.1  christos 		}
    299  1.1  christos 		/*
    300  1.1  christos 		 * Check for valid perm entries.
    301  1.1  christos 		 */
    302  1.1  christos 		if ((acl->acl_entry[i].ae_perm | ACL_PERM_BITS) !=
    303  1.1  christos 		    ACL_PERM_BITS)
    304  1.1  christos 			return (EINVAL);
    305  1.1  christos 	}
    306  1.1  christos 	if ((num_acl_user_obj != 1) || (num_acl_group_obj != 1) ||
    307  1.1  christos 	    (num_acl_other != 1) || (num_acl_mask != 0 && num_acl_mask != 1))
    308  1.1  christos 		return (EINVAL);
    309  1.1  christos 	if (((num_acl_group != 0) || (num_acl_user != 0)) &&
    310  1.1  christos 	    (num_acl_mask != 1))
    311  1.1  christos 		return (EINVAL);
    312  1.1  christos 	return (0);
    313  1.1  christos }
    314  1.1  christos 
    315  1.1  christos /*
    316  1.1  christos  * Given a requested mode for a new object, and a default ACL, combine the
    317  1.1  christos  * two to produce a new mode.  Be careful not to clear any bits that aren't
    318  1.1  christos  * intended to be affected by the POSIX.1e ACL.  Eventually, this might also
    319  1.1  christos  * take the cmask as an argument, if we push that down into
    320  1.1  christos  * per-filesystem-code.
    321  1.1  christos  */
    322  1.1  christos mode_t
    323  1.1  christos acl_posix1e_newfilemode(mode_t cmode, struct acl *dacl)
    324  1.1  christos {
    325  1.1  christos 	mode_t mode;
    326  1.1  christos 
    327  1.1  christos 	mode = cmode;
    328  1.1  christos 	/*
    329  1.1  christos 	 * The current composition policy is that a permission bit must be
    330  1.1  christos 	 * set in *both* the ACL and the requested creation mode for it to
    331  1.1  christos 	 * appear in the resulting mode/ACL.  First clear any possibly
    332  1.1  christos 	 * effected bits, then reconstruct.
    333  1.1  christos 	 */
    334  1.1  christos 	mode &= ACL_PRESERVE_MASK;
    335  1.1  christos 	mode |= (ACL_OVERRIDE_MASK & cmode & acl_posix1e_acl_to_mode(dacl));
    336  1.1  christos 
    337  1.1  christos 	return (mode);
    338  1.1  christos }
    339