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) 2008-2010 Edward Tomasz Napieraa <trasz (at) FreeBSD.org> 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 /* 30 1.1 christos * ACL support routines specific to NFSv4 access control lists. These are 31 1.1 christos * utility routines for code common across file systems implementing NFSv4 32 1.1 christos * ACLs. 33 1.1 christos */ 34 1.1 christos 35 1.1 christos #ifdef _KERNEL 36 1.1 christos #include <sys/cdefs.h> 37 1.1 christos #if 0 38 1.1 christos __FBSDID("$FreeBSD: head/sys/kern/subr_acl_nfs4.c 341827 2018-12-11 19:32:16Z mjg $"); 39 1.1 christos #endif 40 1.2 christos __KERNEL_RCSID(0, "$NetBSD: subr_acl_nfs4.c,v 1.2 2024/01/19 19:07:38 christos Exp $"); 41 1.1 christos 42 1.1 christos #include <sys/param.h> 43 1.1 christos #include <sys/kernel.h> 44 1.1 christos #include <sys/module.h> 45 1.1 christos #include <sys/systm.h> 46 1.1 christos #include <sys/mount.h> 47 1.1 christos #include <sys/vnode.h> 48 1.1 christos #include <sys/errno.h> 49 1.1 christos #include <sys/stat.h> 50 1.1 christos #include <sys/sysctl.h> 51 1.1 christos #include <sys/acl.h> 52 1.1 christos #include <sys/kauth.h> 53 1.1 christos 54 1.1 christos static void acl_nfs4_trivial_from_mode(struct acl *aclp, mode_t mode); 55 1.1 christos 56 1.1 christos 57 1.1 christos #else 58 1.1 christos 59 1.1 christos #include <errno.h> 60 1.1 christos #include <assert.h> 61 1.1 christos #include <sys/acl.h> 62 1.1 christos #include <sys/stat.h> 63 1.1 christos #define KASSERT(a) assert(a) 64 1.1 christos 65 1.1 christos #endif /* !_KERNEL */ 66 1.1 christos 67 1.1 christos #if 0 68 1.1 christos static int 69 1.1 christos _acl_entry_matches(struct acl_entry *ae, acl_tag_t tag, acl_perm_t perm, 70 1.1 christos acl_entry_type_t entry_type) 71 1.1 christos { 72 1.1 christos if (ae->ae_tag != tag) 73 1.1 christos return (0); 74 1.1 christos 75 1.1 christos if (ae->ae_id != ACL_UNDEFINED_ID) 76 1.1 christos return (0); 77 1.1 christos 78 1.1 christos if (ae->ae_perm != perm) 79 1.1 christos return (0); 80 1.1 christos 81 1.1 christos if (ae->ae_entry_type != entry_type) 82 1.1 christos return (0); 83 1.1 christos 84 1.1 christos if (ae->ae_flags != 0) 85 1.1 christos return (0); 86 1.1 christos 87 1.1 christos return (1); 88 1.1 christos } 89 1.1 christos #endif 90 1.1 christos 91 1.1 christos static struct acl_entry * 92 1.1 christos _acl_append(struct acl *aclp, acl_tag_t tag, acl_perm_t perm, 93 1.1 christos acl_entry_type_t entry_type) 94 1.1 christos { 95 1.1 christos struct acl_entry *ae; 96 1.1 christos 97 1.1 christos KASSERT(aclp->acl_cnt + 1 <= ACL_MAX_ENTRIES); 98 1.1 christos 99 1.1 christos ae = &(aclp->acl_entry[aclp->acl_cnt]); 100 1.1 christos aclp->acl_cnt++; 101 1.1 christos 102 1.1 christos ae->ae_tag = tag; 103 1.1 christos ae->ae_id = ACL_UNDEFINED_ID; 104 1.1 christos ae->ae_perm = perm; 105 1.1 christos ae->ae_entry_type = entry_type; 106 1.1 christos ae->ae_flags = 0; 107 1.1 christos 108 1.1 christos return (ae); 109 1.1 christos } 110 1.1 christos 111 1.1 christos #if 0 112 1.1 christos static struct acl_entry * 113 1.1 christos _acl_duplicate_entry(struct acl *aclp, unsigned int entry_index) 114 1.1 christos { 115 1.1 christos size_t i; 116 1.1 christos 117 1.1 christos KASSERT(aclp->acl_cnt + 1 <= ACL_MAX_ENTRIES); 118 1.1 christos 119 1.1 christos for (i = aclp->acl_cnt; i > entry_index; i--) 120 1.1 christos aclp->acl_entry[i] = aclp->acl_entry[i - 1]; 121 1.1 christos 122 1.1 christos aclp->acl_cnt++; 123 1.1 christos 124 1.1 christos return (&(aclp->acl_entry[entry_index + 1])); 125 1.1 christos } 126 1.1 christos #endif 127 1.1 christos 128 1.1 christos 129 1.1 christos #ifdef _KERNEL 130 1.1 christos void 131 1.1 christos acl_nfs4_sync_acl_from_mode(struct acl *aclp, mode_t mode, 132 1.1 christos int file_owner_id) 133 1.1 christos { 134 1.1 christos 135 1.1 christos acl_nfs4_trivial_from_mode(aclp, mode); 136 1.1 christos } 137 1.1 christos #endif /* _KERNEL */ 138 1.1 christos 139 1.1 christos void 140 1.1 christos __acl_nfs4_sync_mode_from_acl(mode_t *_mode, const struct acl *aclp) 141 1.1 christos { 142 1.1 christos size_t i; 143 1.1 christos mode_t old_mode = *_mode, mode = 0, seen = 0; 144 1.1 christos const struct acl_entry *ae; 145 1.1 christos 146 1.1 christos KASSERT(aclp->acl_cnt <= ACL_MAX_ENTRIES); 147 1.1 christos 148 1.1 christos /* 149 1.1 christos * NFSv4 Minor Version 1, draft-ietf-nfsv4-minorversion1-03.txt 150 1.1 christos * 151 1.1 christos * 3.16.6.1. Recomputing mode upon SETATTR of ACL 152 1.1 christos */ 153 1.1 christos 154 1.1 christos for (i = 0; i < aclp->acl_cnt; i++) { 155 1.1 christos ae = &(aclp->acl_entry[i]); 156 1.1 christos 157 1.1 christos if (ae->ae_entry_type != ACL_ENTRY_TYPE_ALLOW && 158 1.1 christos ae->ae_entry_type != ACL_ENTRY_TYPE_DENY) 159 1.1 christos continue; 160 1.1 christos 161 1.1 christos if (ae->ae_flags & ACL_ENTRY_INHERIT_ONLY) 162 1.1 christos continue; 163 1.1 christos 164 1.1 christos if (ae->ae_tag == ACL_USER_OBJ) { 165 1.1 christos if ((ae->ae_perm & ACL_READ_DATA) && 166 1.1 christos ((seen & S_IRUSR) == 0)) { 167 1.1 christos seen |= S_IRUSR; 168 1.1 christos if (ae->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 169 1.1 christos mode |= S_IRUSR; 170 1.1 christos } 171 1.1 christos if ((ae->ae_perm & ACL_WRITE_DATA) && 172 1.1 christos ((seen & S_IWUSR) == 0)) { 173 1.1 christos seen |= S_IWUSR; 174 1.1 christos if (ae->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 175 1.1 christos mode |= S_IWUSR; 176 1.1 christos } 177 1.1 christos if ((ae->ae_perm & ACL_EXECUTE) && 178 1.1 christos ((seen & S_IXUSR) == 0)) { 179 1.1 christos seen |= S_IXUSR; 180 1.1 christos if (ae->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 181 1.1 christos mode |= S_IXUSR; 182 1.1 christos } 183 1.1 christos } else if (ae->ae_tag == ACL_GROUP_OBJ) { 184 1.1 christos if ((ae->ae_perm & ACL_READ_DATA) && 185 1.1 christos ((seen & S_IRGRP) == 0)) { 186 1.1 christos seen |= S_IRGRP; 187 1.1 christos if (ae->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 188 1.1 christos mode |= S_IRGRP; 189 1.1 christos } 190 1.1 christos if ((ae->ae_perm & ACL_WRITE_DATA) && 191 1.1 christos ((seen & S_IWGRP) == 0)) { 192 1.1 christos seen |= S_IWGRP; 193 1.1 christos if (ae->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 194 1.1 christos mode |= S_IWGRP; 195 1.1 christos } 196 1.1 christos if ((ae->ae_perm & ACL_EXECUTE) && 197 1.1 christos ((seen & S_IXGRP) == 0)) { 198 1.1 christos seen |= S_IXGRP; 199 1.1 christos if (ae->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 200 1.1 christos mode |= S_IXGRP; 201 1.1 christos } 202 1.1 christos } else if (ae->ae_tag == ACL_EVERYONE) { 203 1.1 christos if (ae->ae_perm & ACL_READ_DATA) { 204 1.1 christos if ((seen & S_IRUSR) == 0) { 205 1.1 christos seen |= S_IRUSR; 206 1.1 christos if (ae->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 207 1.1 christos mode |= S_IRUSR; 208 1.1 christos } 209 1.1 christos if ((seen & S_IRGRP) == 0) { 210 1.1 christos seen |= S_IRGRP; 211 1.1 christos if (ae->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 212 1.1 christos mode |= S_IRGRP; 213 1.1 christos } 214 1.1 christos if ((seen & S_IROTH) == 0) { 215 1.1 christos seen |= S_IROTH; 216 1.1 christos if (ae->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 217 1.1 christos mode |= S_IROTH; 218 1.1 christos } 219 1.1 christos } 220 1.1 christos if (ae->ae_perm & ACL_WRITE_DATA) { 221 1.1 christos if ((seen & S_IWUSR) == 0) { 222 1.1 christos seen |= S_IWUSR; 223 1.1 christos if (ae->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 224 1.1 christos mode |= S_IWUSR; 225 1.1 christos } 226 1.1 christos if ((seen & S_IWGRP) == 0) { 227 1.1 christos seen |= S_IWGRP; 228 1.1 christos if (ae->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 229 1.1 christos mode |= S_IWGRP; 230 1.1 christos } 231 1.1 christos if ((seen & S_IWOTH) == 0) { 232 1.1 christos seen |= S_IWOTH; 233 1.1 christos if (ae->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 234 1.1 christos mode |= S_IWOTH; 235 1.1 christos } 236 1.1 christos } 237 1.1 christos if (ae->ae_perm & ACL_EXECUTE) { 238 1.1 christos if ((seen & S_IXUSR) == 0) { 239 1.1 christos seen |= S_IXUSR; 240 1.1 christos if (ae->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 241 1.1 christos mode |= S_IXUSR; 242 1.1 christos } 243 1.1 christos if ((seen & S_IXGRP) == 0) { 244 1.1 christos seen |= S_IXGRP; 245 1.1 christos if (ae->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 246 1.1 christos mode |= S_IXGRP; 247 1.1 christos } 248 1.1 christos if ((seen & S_IXOTH) == 0) { 249 1.1 christos seen |= S_IXOTH; 250 1.1 christos if (ae->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 251 1.1 christos mode |= S_IXOTH; 252 1.1 christos } 253 1.1 christos } 254 1.1 christos } 255 1.1 christos } 256 1.1 christos 257 1.1 christos *_mode = mode | (old_mode & ACL_PRESERVE_MASK); 258 1.1 christos } 259 1.1 christos 260 1.1 christos /* 261 1.1 christos * Populate the ACL with entries inherited from parent_aclp. 262 1.1 christos */ 263 1.1 christos static void 264 1.2 christos /*ARGSUSED*/ 265 1.1 christos acl_nfs4_inherit_entries(const struct acl *parent_aclp, 266 1.1 christos struct acl *child_aclp, mode_t mode, int file_owner_id, 267 1.1 christos int is_directory) 268 1.1 christos { 269 1.1 christos int flags, tag; 270 1.1 christos size_t i; 271 1.1 christos const struct acl_entry *parent_entry; 272 1.1 christos struct acl_entry *ae; 273 1.1 christos 274 1.1 christos KASSERT(parent_aclp->acl_cnt <= ACL_MAX_ENTRIES); 275 1.1 christos 276 1.1 christos for (i = 0; i < parent_aclp->acl_cnt; i++) { 277 1.1 christos parent_entry = &(parent_aclp->acl_entry[i]); 278 1.1 christos flags = parent_entry->ae_flags; 279 1.1 christos tag = parent_entry->ae_tag; 280 1.1 christos 281 1.1 christos /* 282 1.1 christos * Don't inherit owner@, group@, or everyone@ entries. 283 1.1 christos */ 284 1.1 christos if (tag == ACL_USER_OBJ || tag == ACL_GROUP_OBJ || 285 1.1 christos tag == ACL_EVERYONE) 286 1.1 christos continue; 287 1.1 christos 288 1.1 christos /* 289 1.1 christos * Entry is not inheritable at all. 290 1.1 christos */ 291 1.1 christos if ((flags & (ACL_ENTRY_DIRECTORY_INHERIT | 292 1.1 christos ACL_ENTRY_FILE_INHERIT)) == 0) 293 1.1 christos continue; 294 1.1 christos 295 1.1 christos /* 296 1.1 christos * We're creating a file, but ae is not inheritable 297 1.1 christos * by files. 298 1.1 christos */ 299 1.1 christos if (!is_directory && (flags & ACL_ENTRY_FILE_INHERIT) == 0) 300 1.1 christos continue; 301 1.1 christos 302 1.1 christos /* 303 1.1 christos * Entry is inheritable only by files, but has NO_PROPAGATE 304 1.1 christos * flag set, and we're creating a directory, so it wouldn't 305 1.1 christos * propagate to any file in that directory anyway. 306 1.1 christos */ 307 1.1 christos if (is_directory && 308 1.1 christos (flags & ACL_ENTRY_DIRECTORY_INHERIT) == 0 && 309 1.1 christos (flags & ACL_ENTRY_NO_PROPAGATE_INHERIT)) 310 1.1 christos continue; 311 1.1 christos 312 1.1 christos /* 313 1.1 christos * Entry qualifies for being inherited. 314 1.1 christos */ 315 1.1 christos KASSERT(child_aclp->acl_cnt + 1 <= ACL_MAX_ENTRIES); 316 1.1 christos ae = &(child_aclp->acl_entry[child_aclp->acl_cnt]); 317 1.1 christos *ae = *parent_entry; 318 1.1 christos child_aclp->acl_cnt++; 319 1.1 christos 320 1.1 christos ae->ae_flags &= ~ACL_ENTRY_INHERIT_ONLY; 321 1.1 christos ae->ae_flags |= ACL_ENTRY_INHERITED; 322 1.1 christos 323 1.1 christos /* 324 1.1 christos * If the type of the ACE is neither ALLOW nor DENY, 325 1.1 christos * then leave it as it is and proceed to the next one. 326 1.1 christos */ 327 1.1 christos if (ae->ae_entry_type != ACL_ENTRY_TYPE_ALLOW && 328 1.1 christos ae->ae_entry_type != ACL_ENTRY_TYPE_DENY) 329 1.1 christos continue; 330 1.1 christos 331 1.1 christos /* 332 1.1 christos * If the ACL_ENTRY_NO_PROPAGATE_INHERIT is set, or if 333 1.1 christos * the object being created is not a directory, then clear 334 1.1 christos * the following flags: ACL_ENTRY_NO_PROPAGATE_INHERIT, 335 1.1 christos * ACL_ENTRY_FILE_INHERIT, ACL_ENTRY_DIRECTORY_INHERIT, 336 1.1 christos * ACL_ENTRY_INHERIT_ONLY. 337 1.1 christos */ 338 1.1 christos if (ae->ae_flags & ACL_ENTRY_NO_PROPAGATE_INHERIT || 339 1.1 christos !is_directory) { 340 1.1 christos ae->ae_flags &= ~(ACL_ENTRY_NO_PROPAGATE_INHERIT | 341 1.1 christos ACL_ENTRY_FILE_INHERIT | ACL_ENTRY_DIRECTORY_INHERIT | 342 1.1 christos ACL_ENTRY_INHERIT_ONLY); 343 1.1 christos } 344 1.1 christos 345 1.1 christos /* 346 1.1 christos * If the object is a directory and ACL_ENTRY_FILE_INHERIT 347 1.1 christos * is set, but ACL_ENTRY_DIRECTORY_INHERIT is not set, ensure 348 1.1 christos * that ACL_ENTRY_INHERIT_ONLY is set. 349 1.1 christos */ 350 1.1 christos if (is_directory && 351 1.1 christos (ae->ae_flags & ACL_ENTRY_FILE_INHERIT) && 352 1.1 christos ((ae->ae_flags & ACL_ENTRY_DIRECTORY_INHERIT) == 0)) { 353 1.1 christos ae->ae_flags |= ACL_ENTRY_INHERIT_ONLY; 354 1.1 christos } 355 1.1 christos 356 1.1 christos if (ae->ae_entry_type == ACL_ENTRY_TYPE_ALLOW && 357 1.1 christos (ae->ae_flags & ACL_ENTRY_INHERIT_ONLY) == 0) { 358 1.1 christos /* 359 1.1 christos * Some permissions must never be inherited. 360 1.1 christos */ 361 1.1 christos ae->ae_perm &= ~(ACL_WRITE_ACL | ACL_WRITE_OWNER | 362 1.1 christos ACL_WRITE_NAMED_ATTRS | ACL_WRITE_ATTRIBUTES); 363 1.1 christos 364 1.1 christos /* 365 1.1 christos * Others must be masked according to the file mode. 366 1.1 christos */ 367 1.1 christos if ((mode & S_IRGRP) == 0) 368 1.1 christos ae->ae_perm &= ~ACL_READ_DATA; 369 1.1 christos if ((mode & S_IWGRP) == 0) 370 1.1 christos ae->ae_perm &= 371 1.1 christos ~(ACL_WRITE_DATA | ACL_APPEND_DATA); 372 1.1 christos if ((mode & S_IXGRP) == 0) 373 1.1 christos ae->ae_perm &= ~ACL_EXECUTE; 374 1.1 christos } 375 1.1 christos } 376 1.1 christos } 377 1.1 christos 378 1.1 christos /* 379 1.1 christos * Calculate inherited ACL in a manner compatible with PSARC/2010/029. 380 1.1 christos * It's also being used to calculate a trivial ACL, by inheriting from 381 1.1 christos * a NULL ACL. 382 1.1 christos */ 383 1.1 christos static void 384 1.1 christos acl_nfs4_compute_inherited_acl_psarc(const struct acl *parent_aclp, 385 1.1 christos struct acl *aclp, mode_t mode, int file_owner_id, int is_directory) 386 1.1 christos { 387 1.1 christos acl_perm_t user_allow_first = 0, user_deny = 0, group_deny = 0; 388 1.1 christos acl_perm_t user_allow, group_allow, everyone_allow; 389 1.1 christos 390 1.1 christos KASSERT(aclp->acl_cnt == 0); 391 1.1 christos 392 1.1 christos user_allow = group_allow = everyone_allow = ACL_READ_ACL | 393 1.1 christos ACL_READ_ATTRIBUTES | ACL_READ_NAMED_ATTRS | ACL_SYNCHRONIZE; 394 1.1 christos user_allow |= ACL_WRITE_ACL | ACL_WRITE_OWNER | ACL_WRITE_ATTRIBUTES | 395 1.1 christos ACL_WRITE_NAMED_ATTRS; 396 1.1 christos 397 1.1 christos if (mode & S_IRUSR) 398 1.1 christos user_allow |= ACL_READ_DATA; 399 1.1 christos if (mode & S_IWUSR) 400 1.1 christos user_allow |= (ACL_WRITE_DATA | ACL_APPEND_DATA); 401 1.1 christos if (mode & S_IXUSR) 402 1.1 christos user_allow |= ACL_EXECUTE; 403 1.1 christos 404 1.1 christos if (mode & S_IRGRP) 405 1.1 christos group_allow |= ACL_READ_DATA; 406 1.1 christos if (mode & S_IWGRP) 407 1.1 christos group_allow |= (ACL_WRITE_DATA | ACL_APPEND_DATA); 408 1.1 christos if (mode & S_IXGRP) 409 1.1 christos group_allow |= ACL_EXECUTE; 410 1.1 christos 411 1.1 christos if (mode & S_IROTH) 412 1.1 christos everyone_allow |= ACL_READ_DATA; 413 1.1 christos if (mode & S_IWOTH) 414 1.1 christos everyone_allow |= (ACL_WRITE_DATA | ACL_APPEND_DATA); 415 1.1 christos if (mode & S_IXOTH) 416 1.1 christos everyone_allow |= ACL_EXECUTE; 417 1.1 christos 418 1.1 christos user_deny = ((group_allow | everyone_allow) & ~user_allow); 419 1.1 christos group_deny = everyone_allow & ~group_allow; 420 1.1 christos user_allow_first = group_deny & ~user_deny; 421 1.1 christos 422 1.1 christos if (user_allow_first != 0) 423 1.1 christos _acl_append(aclp, ACL_USER_OBJ, user_allow_first, 424 1.1 christos ACL_ENTRY_TYPE_ALLOW); 425 1.1 christos if (user_deny != 0) 426 1.1 christos _acl_append(aclp, ACL_USER_OBJ, user_deny, 427 1.1 christos ACL_ENTRY_TYPE_DENY); 428 1.1 christos if (group_deny != 0) 429 1.1 christos _acl_append(aclp, ACL_GROUP_OBJ, group_deny, 430 1.1 christos ACL_ENTRY_TYPE_DENY); 431 1.1 christos 432 1.1 christos if (parent_aclp != NULL) 433 1.1 christos acl_nfs4_inherit_entries(parent_aclp, aclp, mode, 434 1.1 christos file_owner_id, is_directory); 435 1.1 christos 436 1.1 christos _acl_append(aclp, ACL_USER_OBJ, user_allow, ACL_ENTRY_TYPE_ALLOW); 437 1.1 christos _acl_append(aclp, ACL_GROUP_OBJ, group_allow, ACL_ENTRY_TYPE_ALLOW); 438 1.1 christos _acl_append(aclp, ACL_EVERYONE, everyone_allow, ACL_ENTRY_TYPE_ALLOW); 439 1.1 christos } 440 1.1 christos 441 1.1 christos #ifdef _KERNEL 442 1.1 christos void 443 1.1 christos acl_nfs4_compute_inherited_acl(const struct acl *parent_aclp, 444 1.1 christos struct acl *child_aclp, mode_t mode, int file_owner_id, 445 1.1 christos int is_directory) 446 1.1 christos { 447 1.1 christos 448 1.1 christos acl_nfs4_compute_inherited_acl_psarc(parent_aclp, child_aclp, 449 1.1 christos mode, file_owner_id, is_directory); 450 1.1 christos } 451 1.1 christos #endif /* _KERNEL */ 452 1.1 christos 453 1.1 christos /* 454 1.1 christos * Calculate trivial ACL in a manner compatible with PSARC/2010/029. 455 1.1 christos * Note that this results in an ACL different from (but semantically 456 1.1 christos * equal to) the "canonical six" trivial ACL computed using algorithm 457 1.1 christos * described in draft-ietf-nfsv4-minorversion1-03.txt, 3.16.6.2. 458 1.1 christos */ 459 1.1 christos static void 460 1.1 christos acl_nfs4_trivial_from_mode(struct acl *aclp, mode_t mode) 461 1.1 christos { 462 1.1 christos 463 1.1 christos aclp->acl_cnt = 0; 464 1.1 christos acl_nfs4_compute_inherited_acl_psarc(NULL, aclp, mode, -1, -1); 465 1.1 christos } 466 1.1 christos 467 1.1 christos #ifndef _KERNEL 468 1.1 christos /* 469 1.1 christos * This routine is used by libc to implement acl_strip_np(3) 470 1.1 christos * and acl_is_trivial_np(3). 471 1.1 christos */ 472 1.1 christos void 473 1.2 christos /*ARGSUSED*/ 474 1.1 christos __acl_nfs4_trivial_from_mode_libc(struct acl *aclp, int mode, int canonical_six) 475 1.1 christos { 476 1.1 christos 477 1.1 christos aclp->acl_cnt = 0; 478 1.1 christos acl_nfs4_trivial_from_mode(aclp, mode); 479 1.1 christos } 480 1.1 christos #endif /* !_KERNEL */ 481 1.1 christos 482 1.1 christos #ifdef _KERNEL 483 1.1 christos static int 484 1.1 christos _acls_are_equal(const struct acl *a, const struct acl *b) 485 1.1 christos { 486 1.1 christos int i; 487 1.1 christos const struct acl_entry *entrya, *entryb; 488 1.1 christos 489 1.1 christos if (a->acl_cnt != b->acl_cnt) 490 1.1 christos return (0); 491 1.1 christos 492 1.1 christos for (i = 0; i < b->acl_cnt; i++) { 493 1.1 christos entrya = &(a->acl_entry[i]); 494 1.1 christos entryb = &(b->acl_entry[i]); 495 1.1 christos 496 1.1 christos if (entrya->ae_tag != entryb->ae_tag || 497 1.1 christos entrya->ae_id != entryb->ae_id || 498 1.1 christos entrya->ae_perm != entryb->ae_perm || 499 1.1 christos entrya->ae_entry_type != entryb->ae_entry_type || 500 1.1 christos entrya->ae_flags != entryb->ae_flags) 501 1.1 christos return (0); 502 1.1 christos } 503 1.1 christos 504 1.1 christos return (1); 505 1.1 christos } 506 1.1 christos 507 1.1 christos /* 508 1.1 christos * This routine is used to determine whether to remove extended attribute 509 1.1 christos * that stores ACL contents. 510 1.1 christos */ 511 1.1 christos int 512 1.1 christos acl_nfs4_is_trivial(const struct acl *aclp, int file_owner_id) 513 1.1 christos { 514 1.1 christos int trivial; 515 1.1 christos mode_t tmpmode = 0; 516 1.1 christos struct acl *tmpaclp; 517 1.1 christos 518 1.1 christos if (aclp->acl_cnt > 6) 519 1.1 christos return (0); 520 1.1 christos 521 1.1 christos /* 522 1.1 christos * Compute the mode from the ACL, then compute new ACL from that mode. 523 1.1 christos * If the ACLs are identical, then the ACL is trivial. 524 1.1 christos * 525 1.1 christos * XXX: I guess there is a faster way to do this. However, even 526 1.1 christos * this slow implementation significantly speeds things up 527 1.1 christos * for files that don't have non-trivial ACLs - it's critical 528 1.1 christos * for performance to not use EA when they are not needed. 529 1.1 christos * 530 1.1 christos * First try the PSARC/2010/029 semantics. 531 1.1 christos */ 532 1.1 christos tmpaclp = acl_alloc(KM_SLEEP); 533 1.1 christos __acl_nfs4_sync_mode_from_acl(&tmpmode, aclp); 534 1.1 christos acl_nfs4_trivial_from_mode(tmpaclp, tmpmode); 535 1.1 christos trivial = _acls_are_equal(aclp, tmpaclp); 536 1.1 christos if (trivial) { 537 1.1 christos acl_free(tmpaclp); 538 1.1 christos return (trivial); 539 1.1 christos } 540 1.1 christos 541 1.1 christos /* 542 1.1 christos * Check if it's a draft-ietf-nfsv4-minorversion1-03.txt trivial ACL. 543 1.1 christos */ 544 1.1 christos acl_free(tmpaclp); 545 1.1 christos 546 1.1 christos return (trivial); 547 1.1 christos } 548 1.1 christos 549 1.1 christos int 550 1.1 christos acl_nfs4_check(const struct acl *aclp, int is_directory) 551 1.1 christos { 552 1.1 christos size_t i; 553 1.1 christos const struct acl_entry *ae; 554 1.1 christos 555 1.1 christos /* 556 1.1 christos * The spec doesn't seem to say anything about ACL validity. 557 1.1 christos * It seems there is not much to do here. There is even no need 558 1.1 christos * to count "owner@" or "everyone@" (ACL_USER_OBJ and ACL_EVERYONE) 559 1.1 christos * entries, as there can be several of them and that's perfectly 560 1.1 christos * valid. There can be none of them too. Really. 561 1.1 christos */ 562 1.1 christos 563 1.1 christos if (aclp->acl_cnt > ACL_MAX_ENTRIES || aclp->acl_cnt <= 0) 564 1.1 christos return (EINVAL); 565 1.1 christos 566 1.1 christos for (i = 0; i < aclp->acl_cnt; i++) { 567 1.1 christos ae = &(aclp->acl_entry[i]); 568 1.1 christos 569 1.1 christos switch (ae->ae_tag) { 570 1.1 christos case ACL_USER_OBJ: 571 1.1 christos case ACL_GROUP_OBJ: 572 1.1 christos case ACL_EVERYONE: 573 1.1 christos if (ae->ae_id != ACL_UNDEFINED_ID) 574 1.1 christos return (EINVAL); 575 1.1 christos break; 576 1.1 christos 577 1.1 christos case ACL_USER: 578 1.1 christos case ACL_GROUP: 579 1.1 christos if (ae->ae_id == ACL_UNDEFINED_ID) 580 1.1 christos return (EINVAL); 581 1.1 christos break; 582 1.1 christos 583 1.1 christos default: 584 1.1 christos return (EINVAL); 585 1.1 christos } 586 1.1 christos 587 1.1 christos if ((ae->ae_perm | ACL_NFS4_PERM_BITS) != ACL_NFS4_PERM_BITS) 588 1.1 christos return (EINVAL); 589 1.1 christos 590 1.1 christos /* 591 1.1 christos * Disallow ACL_ENTRY_TYPE_AUDIT and ACL_ENTRY_TYPE_ALARM for now. 592 1.1 christos */ 593 1.1 christos if (ae->ae_entry_type != ACL_ENTRY_TYPE_ALLOW && 594 1.1 christos ae->ae_entry_type != ACL_ENTRY_TYPE_DENY) 595 1.1 christos return (EINVAL); 596 1.1 christos 597 1.1 christos if ((ae->ae_flags | ACL_FLAGS_BITS) != ACL_FLAGS_BITS) 598 1.1 christos return (EINVAL); 599 1.1 christos 600 1.1 christos /* Disallow unimplemented flags. */ 601 1.1 christos if (ae->ae_flags & (ACL_ENTRY_SUCCESSFUL_ACCESS | 602 1.1 christos ACL_ENTRY_FAILED_ACCESS)) 603 1.1 christos return (EINVAL); 604 1.1 christos 605 1.1 christos /* Disallow flags not allowed for ordinary files. */ 606 1.1 christos if (!is_directory) { 607 1.1 christos if (ae->ae_flags & (ACL_ENTRY_FILE_INHERIT | 608 1.1 christos ACL_ENTRY_DIRECTORY_INHERIT | 609 1.1 christos ACL_ENTRY_NO_PROPAGATE_INHERIT | ACL_ENTRY_INHERIT_ONLY)) 610 1.1 christos return (EINVAL); 611 1.1 christos } 612 1.1 christos } 613 1.1 christos 614 1.1 christos return (0); 615 1.1 christos } 616 1.1 christos #endif 617