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