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