1193850Strasz/*- 2216413Strasz * Copyright (c) 2008-2010 Edward Tomasz Napiera��a <trasz@FreeBSD.org> 3193850Strasz * All rights reserved. 4193850Strasz * 5193850Strasz * Redistribution and use in source and binary forms, with or without 6193850Strasz * modification, are permitted provided that the following conditions 7193850Strasz * are met: 8193850Strasz * 1. Redistributions of source code must retain the above copyright 9193850Strasz * notice, this list of conditions and the following disclaimer. 10193850Strasz * 2. Redistributions in binary form must reproduce the above copyright 11193850Strasz * notice, this list of conditions and the following disclaimer in the 12193850Strasz * documentation and/or other materials provided with the distribution. 13193850Strasz * 14193850Strasz * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15193850Strasz * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16193850Strasz * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17193850Strasz * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18193850Strasz * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19193850Strasz * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20193850Strasz * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21193850Strasz * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22193850Strasz * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23193850Strasz * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24193850Strasz * SUCH DAMAGE. 25193850Strasz */ 26193850Strasz 27193850Strasz/* 28193850Strasz * ACL support routines specific to NFSv4 access control lists. These are 29193850Strasz * utility routines for code common across file systems implementing NFSv4 30193850Strasz * ACLs. 31193850Strasz */ 32193850Strasz 33193850Strasz#ifdef _KERNEL 34193850Strasz#include <sys/cdefs.h> 35193850Strasz__FBSDID("$FreeBSD$"); 36193850Strasz 37193850Strasz#include <sys/param.h> 38193850Strasz#include <sys/systm.h> 39193850Strasz#include <sys/mount.h> 40193850Strasz#include <sys/priv.h> 41193850Strasz#include <sys/vnode.h> 42193850Strasz#include <sys/errno.h> 43193850Strasz#include <sys/stat.h> 44216413Strasz#include <sys/sysctl.h> 45193850Strasz#include <sys/acl.h> 46193850Strasz#else 47193850Strasz#include <errno.h> 48193850Strasz#include <assert.h> 49193850Strasz#include <sys/acl.h> 50193850Strasz#include <sys/stat.h> 51193850Strasz#define KASSERT(a, b) assert(a) 52193850Strasz#define CTASSERT(a) 53193850Strasz 54216413Strasz#endif /* !_KERNEL */ 55216413Strasz 56219878Strasz#ifdef _KERNEL 57219878Strasz 58219878Straszstatic void acl_nfs4_trivial_from_mode(struct acl *aclp, mode_t mode); 59219878Strasz 60219880Straszstatic int acl_nfs4_old_semantics = 0; 61216413Strasz 62216413StraszSYSCTL_INT(_vfs, OID_AUTO, acl_nfs4_old_semantics, CTLFLAG_RW, 63219880Strasz &acl_nfs4_old_semantics, 0, "Use pre-PSARC/2010/029 NFSv4 ACL semantics"); 64216413Strasz 65197405Straszstatic struct { 66197405Strasz accmode_t accmode; 67197405Strasz int mask; 68197405Strasz} accmode2mask[] = {{VREAD, ACL_READ_DATA}, 69197405Strasz {VWRITE, ACL_WRITE_DATA}, 70197405Strasz {VAPPEND, ACL_APPEND_DATA}, 71197405Strasz {VEXEC, ACL_EXECUTE}, 72197405Strasz {VREAD_NAMED_ATTRS, ACL_READ_NAMED_ATTRS}, 73197405Strasz {VWRITE_NAMED_ATTRS, ACL_WRITE_NAMED_ATTRS}, 74197405Strasz {VDELETE_CHILD, ACL_DELETE_CHILD}, 75197405Strasz {VREAD_ATTRIBUTES, ACL_READ_ATTRIBUTES}, 76197405Strasz {VWRITE_ATTRIBUTES, ACL_WRITE_ATTRIBUTES}, 77197405Strasz {VDELETE, ACL_DELETE}, 78197405Strasz {VREAD_ACL, ACL_READ_ACL}, 79197405Strasz {VWRITE_ACL, ACL_WRITE_ACL}, 80197405Strasz {VWRITE_OWNER, ACL_WRITE_OWNER}, 81197405Strasz {VSYNCHRONIZE, ACL_SYNCHRONIZE}, 82197405Strasz {0, 0}}; 83197405Strasz 84193850Straszstatic int 85197405Strasz_access_mask_from_accmode(accmode_t accmode) 86197405Strasz{ 87197405Strasz int access_mask = 0, i; 88197405Strasz 89197405Strasz for (i = 0; accmode2mask[i].accmode != 0; i++) { 90197405Strasz if (accmode & accmode2mask[i].accmode) 91197405Strasz access_mask |= accmode2mask[i].mask; 92197405Strasz } 93197405Strasz 94200723Strasz /* 95200723Strasz * VAPPEND is just a modifier for VWRITE; if the caller asked 96200723Strasz * for 'VAPPEND | VWRITE', we want to check for ACL_APPEND_DATA only. 97200723Strasz */ 98200723Strasz if (access_mask & ACL_APPEND_DATA) 99200723Strasz access_mask &= ~ACL_WRITE_DATA; 100200723Strasz 101197405Strasz return (access_mask); 102197405Strasz} 103197405Strasz 104197405Strasz/* 105197405Strasz * Return 0, iff access is allowed, 1 otherwise. 106197405Strasz */ 107197405Straszstatic int 108197405Strasz_acl_denies(const struct acl *aclp, int access_mask, struct ucred *cred, 109197405Strasz int file_uid, int file_gid, int *denied_explicitly) 110197405Strasz{ 111197405Strasz int i; 112197405Strasz const struct acl_entry *entry; 113197405Strasz 114197405Strasz if (denied_explicitly != NULL) 115197405Strasz *denied_explicitly = 0; 116197405Strasz 117197405Strasz KASSERT(aclp->acl_cnt <= ACL_MAX_ENTRIES, 118197405Strasz ("aclp->acl_cnt <= ACL_MAX_ENTRIES")); 119197405Strasz 120197405Strasz for (i = 0; i < aclp->acl_cnt; i++) { 121197405Strasz entry = &(aclp->acl_entry[i]); 122197405Strasz 123197405Strasz if (entry->ae_entry_type != ACL_ENTRY_TYPE_ALLOW && 124197405Strasz entry->ae_entry_type != ACL_ENTRY_TYPE_DENY) 125197405Strasz continue; 126197405Strasz if (entry->ae_flags & ACL_ENTRY_INHERIT_ONLY) 127197405Strasz continue; 128197405Strasz switch (entry->ae_tag) { 129197405Strasz case ACL_USER_OBJ: 130197405Strasz if (file_uid != cred->cr_uid) 131197405Strasz continue; 132197405Strasz break; 133197405Strasz case ACL_USER: 134197405Strasz if (entry->ae_id != cred->cr_uid) 135197405Strasz continue; 136197405Strasz break; 137197405Strasz case ACL_GROUP_OBJ: 138197405Strasz if (!groupmember(file_gid, cred)) 139197405Strasz continue; 140197405Strasz break; 141197405Strasz case ACL_GROUP: 142197405Strasz if (!groupmember(entry->ae_id, cred)) 143197405Strasz continue; 144197405Strasz break; 145197405Strasz default: 146197405Strasz KASSERT(entry->ae_tag == ACL_EVERYONE, 147197405Strasz ("entry->ae_tag == ACL_EVERYONE")); 148197405Strasz } 149197405Strasz 150197405Strasz if (entry->ae_entry_type == ACL_ENTRY_TYPE_DENY) { 151197405Strasz if (entry->ae_perm & access_mask) { 152197405Strasz if (denied_explicitly != NULL) 153197405Strasz *denied_explicitly = 1; 154197405Strasz return (1); 155197405Strasz } 156197405Strasz } 157197405Strasz 158197405Strasz access_mask &= ~(entry->ae_perm); 159197405Strasz if (access_mask == 0) 160197405Strasz return (0); 161197405Strasz } 162197405Strasz 163235890Strasz if (access_mask == 0) 164235890Strasz return (0); 165235890Strasz 166197405Strasz return (1); 167197405Strasz} 168197405Strasz 169197405Straszint 170197405Straszvaccess_acl_nfs4(enum vtype type, uid_t file_uid, gid_t file_gid, 171197405Strasz struct acl *aclp, accmode_t accmode, struct ucred *cred, int *privused) 172197405Strasz{ 173197405Strasz accmode_t priv_granted = 0; 174197405Strasz int denied, explicitly_denied, access_mask, is_directory, 175197405Strasz must_be_owner = 0; 176214522Strasz mode_t file_mode = 0; 177197405Strasz 178201019Strasz KASSERT((accmode & ~(VEXEC | VWRITE | VREAD | VADMIN | VAPPEND | 179201019Strasz VEXPLICIT_DENY | VREAD_NAMED_ATTRS | VWRITE_NAMED_ATTRS | 180201019Strasz VDELETE_CHILD | VREAD_ATTRIBUTES | VWRITE_ATTRIBUTES | VDELETE | 181201019Strasz VREAD_ACL | VWRITE_ACL | VWRITE_OWNER | VSYNCHRONIZE)) == 0, 182201019Strasz ("invalid bit in accmode")); 183201019Strasz KASSERT((accmode & VAPPEND) == 0 || (accmode & VWRITE), 184201019Strasz ("VAPPEND without VWRITE")); 185201019Strasz 186197405Strasz if (privused != NULL) 187197405Strasz *privused = 0; 188197405Strasz 189197405Strasz if (accmode & VADMIN) 190197405Strasz must_be_owner = 1; 191197405Strasz 192197405Strasz /* 193197405Strasz * Ignore VSYNCHRONIZE permission. 194197405Strasz */ 195197405Strasz accmode &= ~VSYNCHRONIZE; 196197405Strasz 197197405Strasz access_mask = _access_mask_from_accmode(accmode); 198197405Strasz 199197405Strasz if (type == VDIR) 200197405Strasz is_directory = 1; 201197405Strasz else 202197405Strasz is_directory = 0; 203197405Strasz 204197405Strasz /* 205197405Strasz * File owner is always allowed to read and write the ACL 206197405Strasz * and basic attributes. This is to prevent a situation 207197405Strasz * where user would change ACL in a way that prevents him 208197405Strasz * from undoing the change. 209197405Strasz */ 210197405Strasz if (file_uid == cred->cr_uid) 211197405Strasz access_mask &= ~(ACL_READ_ACL | ACL_WRITE_ACL | 212197405Strasz ACL_READ_ATTRIBUTES | ACL_WRITE_ATTRIBUTES); 213197405Strasz 214197405Strasz /* 215197405Strasz * Ignore append permission for regular files; use write 216197405Strasz * permission instead. 217197405Strasz */ 218197405Strasz if (!is_directory && (access_mask & ACL_APPEND_DATA)) { 219197405Strasz access_mask &= ~ACL_APPEND_DATA; 220197405Strasz access_mask |= ACL_WRITE_DATA; 221197405Strasz } 222197405Strasz 223197405Strasz denied = _acl_denies(aclp, access_mask, cred, file_uid, file_gid, 224197405Strasz &explicitly_denied); 225197405Strasz 226197405Strasz if (must_be_owner) { 227197405Strasz if (file_uid != cred->cr_uid) 228197405Strasz denied = EPERM; 229197405Strasz } 230197405Strasz 231212002Sjh /* 232212002Sjh * For VEXEC, ensure that at least one execute bit is set for 233212002Sjh * non-directories. We have to check the mode here to stay 234212002Sjh * consistent with execve(2). See the test in 235212002Sjh * exec_check_permissions(). 236212002Sjh */ 237212002Sjh acl_nfs4_sync_mode_from_acl(&file_mode, aclp); 238212002Sjh if (!denied && !is_directory && (accmode & VEXEC) && 239212002Sjh (file_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) == 0) 240212002Sjh denied = EACCES; 241212002Sjh 242197405Strasz if (!denied) 243197405Strasz return (0); 244197405Strasz 245197405Strasz /* 246197405Strasz * Access failed. Iff it was not denied explicitly and 247197405Strasz * VEXPLICIT_DENY flag was specified, allow access. 248197405Strasz */ 249197405Strasz if ((accmode & VEXPLICIT_DENY) && explicitly_denied == 0) 250197405Strasz return (0); 251197405Strasz 252197405Strasz accmode &= ~VEXPLICIT_DENY; 253197405Strasz 254197405Strasz /* 255197405Strasz * No match. Try to use privileges, if there are any. 256197405Strasz */ 257197405Strasz if (is_directory) { 258197405Strasz if ((accmode & VEXEC) && !priv_check_cred(cred, 259197405Strasz PRIV_VFS_LOOKUP, 0)) 260197405Strasz priv_granted |= VEXEC; 261197405Strasz } else { 262212002Sjh /* 263212002Sjh * Ensure that at least one execute bit is on. Otherwise, 264212002Sjh * a privileged user will always succeed, and we don't want 265212002Sjh * this to happen unless the file really is executable. 266212002Sjh */ 267212002Sjh if ((accmode & VEXEC) && (file_mode & 268212002Sjh (S_IXUSR | S_IXGRP | S_IXOTH)) != 0 && 269212002Sjh !priv_check_cred(cred, PRIV_VFS_EXEC, 0)) 270197405Strasz priv_granted |= VEXEC; 271197405Strasz } 272197405Strasz 273197405Strasz if ((accmode & VREAD) && !priv_check_cred(cred, PRIV_VFS_READ, 0)) 274197405Strasz priv_granted |= VREAD; 275197405Strasz 276197405Strasz if ((accmode & (VWRITE | VAPPEND | VDELETE_CHILD)) && 277197405Strasz !priv_check_cred(cred, PRIV_VFS_WRITE, 0)) 278197405Strasz priv_granted |= (VWRITE | VAPPEND | VDELETE_CHILD); 279197405Strasz 280197405Strasz if ((accmode & VADMIN_PERMS) && 281197405Strasz !priv_check_cred(cred, PRIV_VFS_ADMIN, 0)) 282197405Strasz priv_granted |= VADMIN_PERMS; 283197405Strasz 284197405Strasz if ((accmode & VSTAT_PERMS) && 285197405Strasz !priv_check_cred(cred, PRIV_VFS_STAT, 0)) 286197405Strasz priv_granted |= VSTAT_PERMS; 287197405Strasz 288197405Strasz if ((accmode & priv_granted) == accmode) { 289197405Strasz if (privused != NULL) 290197405Strasz *privused = 1; 291197405Strasz 292197405Strasz return (0); 293197405Strasz } 294197405Strasz 295197405Strasz if (accmode & (VADMIN_PERMS | VDELETE_CHILD | VDELETE)) 296197405Strasz denied = EPERM; 297197405Strasz else 298197405Strasz denied = EACCES; 299197405Strasz 300197405Strasz return (denied); 301197405Strasz} 302197405Strasz#endif /* _KERNEL */ 303197405Strasz 304197405Straszstatic int 305193850Strasz_acl_entry_matches(struct acl_entry *entry, acl_tag_t tag, acl_perm_t perm, 306193850Strasz acl_entry_type_t entry_type) 307193850Strasz{ 308193850Strasz if (entry->ae_tag != tag) 309193850Strasz return (0); 310193850Strasz 311193850Strasz if (entry->ae_id != ACL_UNDEFINED_ID) 312193850Strasz return (0); 313193850Strasz 314193850Strasz if (entry->ae_perm != perm) 315193850Strasz return (0); 316193850Strasz 317193850Strasz if (entry->ae_entry_type != entry_type) 318193850Strasz return (0); 319193850Strasz 320193850Strasz if (entry->ae_flags != 0) 321193850Strasz return (0); 322193850Strasz 323193850Strasz return (1); 324193850Strasz} 325193850Strasz 326193850Straszstatic struct acl_entry * 327193850Strasz_acl_append(struct acl *aclp, acl_tag_t tag, acl_perm_t perm, 328193850Strasz acl_entry_type_t entry_type) 329193850Strasz{ 330193850Strasz struct acl_entry *entry; 331193850Strasz 332193850Strasz KASSERT(aclp->acl_cnt + 1 <= ACL_MAX_ENTRIES, 333193850Strasz ("aclp->acl_cnt + 1 <= ACL_MAX_ENTRIES")); 334193850Strasz 335193850Strasz entry = &(aclp->acl_entry[aclp->acl_cnt]); 336193850Strasz aclp->acl_cnt++; 337193850Strasz 338193850Strasz entry->ae_tag = tag; 339193850Strasz entry->ae_id = ACL_UNDEFINED_ID; 340193850Strasz entry->ae_perm = perm; 341193850Strasz entry->ae_entry_type = entry_type; 342193850Strasz entry->ae_flags = 0; 343193850Strasz 344193850Strasz return (entry); 345193850Strasz} 346193850Strasz 347193850Straszstatic struct acl_entry * 348193850Strasz_acl_duplicate_entry(struct acl *aclp, int entry_index) 349193850Strasz{ 350193850Strasz int i; 351193850Strasz 352193850Strasz KASSERT(aclp->acl_cnt + 1 <= ACL_MAX_ENTRIES, 353193850Strasz ("aclp->acl_cnt + 1 <= ACL_MAX_ENTRIES")); 354193850Strasz 355193850Strasz for (i = aclp->acl_cnt; i > entry_index; i--) 356193850Strasz aclp->acl_entry[i] = aclp->acl_entry[i - 1]; 357193850Strasz 358193850Strasz aclp->acl_cnt++; 359193850Strasz 360193850Strasz return (&(aclp->acl_entry[entry_index + 1])); 361193850Strasz} 362193850Strasz 363216413Straszstatic void 364216413Straszacl_nfs4_sync_acl_from_mode_draft(struct acl *aclp, mode_t mode, 365216413Strasz int file_owner_id) 366212906Strasz{ 367193850Strasz int i, meets, must_append; 368193850Strasz struct acl_entry *entry, *copy, *previous, 369193850Strasz *a1, *a2, *a3, *a4, *a5, *a6; 370193850Strasz mode_t amode; 371193850Strasz const int READ = 04; 372193850Strasz const int WRITE = 02; 373193850Strasz const int EXEC = 01; 374193850Strasz 375193850Strasz KASSERT(aclp->acl_cnt <= ACL_MAX_ENTRIES, 376193850Strasz ("aclp->acl_cnt <= ACL_MAX_ENTRIES")); 377193850Strasz 378193850Strasz /* 379193850Strasz * NFSv4 Minor Version 1, draft-ietf-nfsv4-minorversion1-03.txt 380193850Strasz * 381193850Strasz * 3.16.6.3. Applying a Mode to an Existing ACL 382193850Strasz */ 383193850Strasz 384193850Strasz /* 385193850Strasz * 1. For each ACE: 386193850Strasz */ 387193850Strasz for (i = 0; i < aclp->acl_cnt; i++) { 388193850Strasz entry = &(aclp->acl_entry[i]); 389193850Strasz 390193850Strasz /* 391193850Strasz * 1.1. If the type is neither ALLOW or DENY - skip. 392193850Strasz */ 393193850Strasz if (entry->ae_entry_type != ACL_ENTRY_TYPE_ALLOW && 394193850Strasz entry->ae_entry_type != ACL_ENTRY_TYPE_DENY) 395193850Strasz continue; 396193850Strasz 397193850Strasz /* 398193850Strasz * 1.2. If ACL_ENTRY_INHERIT_ONLY is set - skip. 399193850Strasz */ 400193850Strasz if (entry->ae_flags & ACL_ENTRY_INHERIT_ONLY) 401193850Strasz continue; 402193850Strasz 403193850Strasz /* 404193850Strasz * 1.3. If ACL_ENTRY_FILE_INHERIT or ACL_ENTRY_DIRECTORY_INHERIT 405193850Strasz * are set: 406193850Strasz */ 407193850Strasz if (entry->ae_flags & 408193850Strasz (ACL_ENTRY_FILE_INHERIT | ACL_ENTRY_DIRECTORY_INHERIT)) { 409193850Strasz /* 410193850Strasz * 1.3.1. A copy of the current ACE is made, and placed 411193850Strasz * in the ACL immediately following the current 412193850Strasz * ACE. 413193850Strasz */ 414193850Strasz copy = _acl_duplicate_entry(aclp, i); 415193850Strasz 416193850Strasz /* 417193850Strasz * 1.3.2. In the first ACE, the flag 418193850Strasz * ACL_ENTRY_INHERIT_ONLY is set. 419193850Strasz */ 420193850Strasz entry->ae_flags |= ACL_ENTRY_INHERIT_ONLY; 421193850Strasz 422193850Strasz /* 423193850Strasz * 1.3.3. In the second ACE, the following flags 424193850Strasz * are cleared: 425193850Strasz * ACL_ENTRY_FILE_INHERIT, 426193850Strasz * ACL_ENTRY_DIRECTORY_INHERIT, 427193850Strasz * ACL_ENTRY_NO_PROPAGATE_INHERIT. 428193850Strasz */ 429193850Strasz copy->ae_flags &= ~(ACL_ENTRY_FILE_INHERIT | 430193850Strasz ACL_ENTRY_DIRECTORY_INHERIT | 431193850Strasz ACL_ENTRY_NO_PROPAGATE_INHERIT); 432193850Strasz 433193850Strasz /* 434193850Strasz * The algorithm continues on with the second ACE. 435193850Strasz */ 436193850Strasz i++; 437193850Strasz entry = copy; 438193850Strasz } 439193850Strasz 440193850Strasz /* 441193850Strasz * 1.4. If it's owner@, group@ or everyone@ entry, clear 442193850Strasz * ACL_READ_DATA, ACL_WRITE_DATA, ACL_APPEND_DATA 443193850Strasz * and ACL_EXECUTE. Continue to the next entry. 444193850Strasz */ 445193850Strasz if (entry->ae_tag == ACL_USER_OBJ || 446193850Strasz entry->ae_tag == ACL_GROUP_OBJ || 447193850Strasz entry->ae_tag == ACL_EVERYONE) { 448193850Strasz entry->ae_perm &= ~(ACL_READ_DATA | ACL_WRITE_DATA | 449193850Strasz ACL_APPEND_DATA | ACL_EXECUTE); 450193850Strasz continue; 451193850Strasz } 452193850Strasz 453193850Strasz /* 454193850Strasz * 1.5. Otherwise, if the "who" field did not match one 455193850Strasz * of OWNER@, GROUP@, EVERYONE@: 456193850Strasz * 457193850Strasz * 1.5.1. If the type is ALLOW, check the preceding ACE. 458193850Strasz * If it does not meet all of the following criteria: 459193850Strasz */ 460193850Strasz if (entry->ae_entry_type != ACL_ENTRY_TYPE_ALLOW) 461193850Strasz continue; 462193850Strasz 463193850Strasz meets = 0; 464193850Strasz if (i > 0) { 465193850Strasz meets = 1; 466193850Strasz previous = &(aclp->acl_entry[i - 1]); 467193850Strasz 468193850Strasz /* 469193850Strasz * 1.5.1.1. The type field is DENY, 470193850Strasz */ 471193850Strasz if (previous->ae_entry_type != ACL_ENTRY_TYPE_DENY) 472193850Strasz meets = 0; 473193850Strasz 474193850Strasz /* 475193850Strasz * 1.5.1.2. The "who" field is the same as the current 476193850Strasz * ACE, 477193850Strasz * 478193850Strasz * 1.5.1.3. The flag bit ACE4_IDENTIFIER_GROUP 479193850Strasz * is the same as it is in the current ACE, 480193850Strasz * and no other flag bits are set, 481193850Strasz */ 482193850Strasz if (previous->ae_id != entry->ae_id || 483193850Strasz previous->ae_tag != entry->ae_tag) 484193850Strasz meets = 0; 485193850Strasz 486193850Strasz if (previous->ae_flags) 487193850Strasz meets = 0; 488193850Strasz 489193850Strasz /* 490193850Strasz * 1.5.1.4. The mask bits are a subset of the mask bits 491193850Strasz * of the current ACE, and are also subset of 492193850Strasz * the following: ACL_READ_DATA, 493193850Strasz * ACL_WRITE_DATA, ACL_APPEND_DATA, ACL_EXECUTE 494193850Strasz */ 495193850Strasz if (previous->ae_perm & ~(entry->ae_perm)) 496193850Strasz meets = 0; 497193850Strasz 498193850Strasz if (previous->ae_perm & ~(ACL_READ_DATA | 499193850Strasz ACL_WRITE_DATA | ACL_APPEND_DATA | ACL_EXECUTE)) 500193850Strasz meets = 0; 501193850Strasz } 502193850Strasz 503193850Strasz if (!meets) { 504193850Strasz /* 505193850Strasz * Then the ACE of type DENY, with a who equal 506193850Strasz * to the current ACE, flag bits equal to 507193850Strasz * (<current ACE flags> & <ACE_IDENTIFIER_GROUP>) 508193850Strasz * and no mask bits, is prepended. 509193850Strasz */ 510193850Strasz previous = entry; 511193850Strasz entry = _acl_duplicate_entry(aclp, i); 512193850Strasz 513193850Strasz /* Adjust counter, as we've just added an entry. */ 514193850Strasz i++; 515193850Strasz 516193850Strasz previous->ae_tag = entry->ae_tag; 517193850Strasz previous->ae_id = entry->ae_id; 518193850Strasz previous->ae_flags = entry->ae_flags; 519193850Strasz previous->ae_perm = 0; 520193850Strasz previous->ae_entry_type = ACL_ENTRY_TYPE_DENY; 521193850Strasz } 522193850Strasz 523193850Strasz /* 524193850Strasz * 1.5.2. The following modifications are made to the prepended 525193850Strasz * ACE. The intent is to mask the following ACE 526193850Strasz * to disallow ACL_READ_DATA, ACL_WRITE_DATA, 527193850Strasz * ACL_APPEND_DATA, or ACL_EXECUTE, based upon the group 528193850Strasz * permissions of the new mode. As a special case, 529193850Strasz * if the ACE matches the current owner of the file, 530193850Strasz * the owner bits are used, rather than the group bits. 531193850Strasz * This is reflected in the algorithm below. 532193850Strasz */ 533193850Strasz amode = mode >> 3; 534193850Strasz 535193850Strasz /* 536193850Strasz * If ACE4_IDENTIFIER_GROUP is not set, and the "who" field 537193850Strasz * in ACE matches the owner of the file, we shift amode three 538193850Strasz * more bits, in order to have the owner permission bits 539193850Strasz * placed in the three low order bits of amode. 540193850Strasz */ 541193850Strasz if (entry->ae_tag == ACL_USER && entry->ae_id == file_owner_id) 542193850Strasz amode = amode >> 3; 543193850Strasz 544193850Strasz if (entry->ae_perm & ACL_READ_DATA) { 545193850Strasz if (amode & READ) 546193850Strasz previous->ae_perm &= ~ACL_READ_DATA; 547193850Strasz else 548193850Strasz previous->ae_perm |= ACL_READ_DATA; 549193850Strasz } 550193850Strasz 551193850Strasz if (entry->ae_perm & ACL_WRITE_DATA) { 552193850Strasz if (amode & WRITE) 553193850Strasz previous->ae_perm &= ~ACL_WRITE_DATA; 554193850Strasz else 555193850Strasz previous->ae_perm |= ACL_WRITE_DATA; 556193850Strasz } 557193850Strasz 558193850Strasz if (entry->ae_perm & ACL_APPEND_DATA) { 559193850Strasz if (amode & WRITE) 560193850Strasz previous->ae_perm &= ~ACL_APPEND_DATA; 561193850Strasz else 562193850Strasz previous->ae_perm |= ACL_APPEND_DATA; 563193850Strasz } 564193850Strasz 565193850Strasz if (entry->ae_perm & ACL_EXECUTE) { 566193850Strasz if (amode & EXEC) 567193850Strasz previous->ae_perm &= ~ACL_EXECUTE; 568193850Strasz else 569193850Strasz previous->ae_perm |= ACL_EXECUTE; 570193850Strasz } 571193850Strasz 572193850Strasz /* 573193850Strasz * 1.5.3. If ACE4_IDENTIFIER_GROUP is set in the flags 574193850Strasz * of the ALLOW ace: 575193850Strasz * 576193850Strasz * XXX: This point is not there in the Falkner's draft. 577193850Strasz */ 578193850Strasz if (entry->ae_tag == ACL_GROUP && 579193850Strasz entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) { 580193850Strasz mode_t extramode, ownermode; 581193850Strasz extramode = (mode >> 3) & 07; 582193850Strasz ownermode = mode >> 6; 583193850Strasz extramode &= ~ownermode; 584193850Strasz 585193850Strasz if (extramode) { 586193850Strasz if (extramode & READ) { 587193850Strasz entry->ae_perm &= ~ACL_READ_DATA; 588193850Strasz previous->ae_perm &= ~ACL_READ_DATA; 589193850Strasz } 590193850Strasz 591193850Strasz if (extramode & WRITE) { 592193850Strasz entry->ae_perm &= 593193850Strasz ~(ACL_WRITE_DATA | ACL_APPEND_DATA); 594193850Strasz previous->ae_perm &= 595193850Strasz ~(ACL_WRITE_DATA | ACL_APPEND_DATA); 596193850Strasz } 597193850Strasz 598193850Strasz if (extramode & EXEC) { 599193850Strasz entry->ae_perm &= ~ACL_EXECUTE; 600193850Strasz previous->ae_perm &= ~ACL_EXECUTE; 601193850Strasz } 602193850Strasz } 603193850Strasz } 604193850Strasz } 605193850Strasz 606193850Strasz /* 607193850Strasz * 2. If there at least six ACEs, the final six ACEs are examined. 608193850Strasz * If they are not equal to what we want, append six ACEs. 609193850Strasz */ 610193850Strasz must_append = 0; 611193850Strasz if (aclp->acl_cnt < 6) { 612193850Strasz must_append = 1; 613193850Strasz } else { 614193850Strasz a6 = &(aclp->acl_entry[aclp->acl_cnt - 1]); 615193850Strasz a5 = &(aclp->acl_entry[aclp->acl_cnt - 2]); 616193850Strasz a4 = &(aclp->acl_entry[aclp->acl_cnt - 3]); 617193850Strasz a3 = &(aclp->acl_entry[aclp->acl_cnt - 4]); 618193850Strasz a2 = &(aclp->acl_entry[aclp->acl_cnt - 5]); 619193850Strasz a1 = &(aclp->acl_entry[aclp->acl_cnt - 6]); 620193850Strasz 621193850Strasz if (!_acl_entry_matches(a1, ACL_USER_OBJ, 0, 622193850Strasz ACL_ENTRY_TYPE_DENY)) 623193850Strasz must_append = 1; 624193850Strasz if (!_acl_entry_matches(a2, ACL_USER_OBJ, ACL_WRITE_ACL | 625193850Strasz ACL_WRITE_OWNER | ACL_WRITE_ATTRIBUTES | 626193850Strasz ACL_WRITE_NAMED_ATTRS, ACL_ENTRY_TYPE_ALLOW)) 627193850Strasz must_append = 1; 628193850Strasz if (!_acl_entry_matches(a3, ACL_GROUP_OBJ, 0, 629193850Strasz ACL_ENTRY_TYPE_DENY)) 630193850Strasz must_append = 1; 631193850Strasz if (!_acl_entry_matches(a4, ACL_GROUP_OBJ, 0, 632193850Strasz ACL_ENTRY_TYPE_ALLOW)) 633193850Strasz must_append = 1; 634193850Strasz if (!_acl_entry_matches(a5, ACL_EVERYONE, ACL_WRITE_ACL | 635193850Strasz ACL_WRITE_OWNER | ACL_WRITE_ATTRIBUTES | 636193850Strasz ACL_WRITE_NAMED_ATTRS, ACL_ENTRY_TYPE_DENY)) 637193850Strasz must_append = 1; 638193850Strasz if (!_acl_entry_matches(a6, ACL_EVERYONE, ACL_READ_ACL | 639193850Strasz ACL_READ_ATTRIBUTES | ACL_READ_NAMED_ATTRS | 640193850Strasz ACL_SYNCHRONIZE, ACL_ENTRY_TYPE_ALLOW)) 641193850Strasz must_append = 1; 642193850Strasz } 643193850Strasz 644193850Strasz if (must_append) { 645193850Strasz KASSERT(aclp->acl_cnt + 6 <= ACL_MAX_ENTRIES, 646193850Strasz ("aclp->acl_cnt <= ACL_MAX_ENTRIES")); 647193850Strasz 648193850Strasz a1 = _acl_append(aclp, ACL_USER_OBJ, 0, ACL_ENTRY_TYPE_DENY); 649193850Strasz a2 = _acl_append(aclp, ACL_USER_OBJ, ACL_WRITE_ACL | 650193850Strasz ACL_WRITE_OWNER | ACL_WRITE_ATTRIBUTES | 651193850Strasz ACL_WRITE_NAMED_ATTRS, ACL_ENTRY_TYPE_ALLOW); 652193850Strasz a3 = _acl_append(aclp, ACL_GROUP_OBJ, 0, ACL_ENTRY_TYPE_DENY); 653193850Strasz a4 = _acl_append(aclp, ACL_GROUP_OBJ, 0, ACL_ENTRY_TYPE_ALLOW); 654193850Strasz a5 = _acl_append(aclp, ACL_EVERYONE, ACL_WRITE_ACL | 655193850Strasz ACL_WRITE_OWNER | ACL_WRITE_ATTRIBUTES | 656193850Strasz ACL_WRITE_NAMED_ATTRS, ACL_ENTRY_TYPE_DENY); 657193850Strasz a6 = _acl_append(aclp, ACL_EVERYONE, ACL_READ_ACL | 658193850Strasz ACL_READ_ATTRIBUTES | ACL_READ_NAMED_ATTRS | 659193850Strasz ACL_SYNCHRONIZE, ACL_ENTRY_TYPE_ALLOW); 660193850Strasz 661193850Strasz KASSERT(a1 != NULL && a2 != NULL && a3 != NULL && a4 != NULL && 662193850Strasz a5 != NULL && a6 != NULL, ("couldn't append to ACL.")); 663193850Strasz } 664193850Strasz 665193850Strasz /* 666193850Strasz * 3. The final six ACEs are adjusted according to the incoming mode. 667193850Strasz */ 668193850Strasz if (mode & S_IRUSR) 669193850Strasz a2->ae_perm |= ACL_READ_DATA; 670193850Strasz else 671193850Strasz a1->ae_perm |= ACL_READ_DATA; 672193850Strasz if (mode & S_IWUSR) 673193850Strasz a2->ae_perm |= (ACL_WRITE_DATA | ACL_APPEND_DATA); 674193850Strasz else 675193850Strasz a1->ae_perm |= (ACL_WRITE_DATA | ACL_APPEND_DATA); 676193850Strasz if (mode & S_IXUSR) 677193850Strasz a2->ae_perm |= ACL_EXECUTE; 678193850Strasz else 679193850Strasz a1->ae_perm |= ACL_EXECUTE; 680193850Strasz 681193850Strasz if (mode & S_IRGRP) 682193850Strasz a4->ae_perm |= ACL_READ_DATA; 683193850Strasz else 684193850Strasz a3->ae_perm |= ACL_READ_DATA; 685193850Strasz if (mode & S_IWGRP) 686193850Strasz a4->ae_perm |= (ACL_WRITE_DATA | ACL_APPEND_DATA); 687193850Strasz else 688193850Strasz a3->ae_perm |= (ACL_WRITE_DATA | ACL_APPEND_DATA); 689193850Strasz if (mode & S_IXGRP) 690193850Strasz a4->ae_perm |= ACL_EXECUTE; 691193850Strasz else 692193850Strasz a3->ae_perm |= ACL_EXECUTE; 693193850Strasz 694193850Strasz if (mode & S_IROTH) 695193850Strasz a6->ae_perm |= ACL_READ_DATA; 696193850Strasz else 697193850Strasz a5->ae_perm |= ACL_READ_DATA; 698193850Strasz if (mode & S_IWOTH) 699193850Strasz a6->ae_perm |= (ACL_WRITE_DATA | ACL_APPEND_DATA); 700193850Strasz else 701193850Strasz a5->ae_perm |= (ACL_WRITE_DATA | ACL_APPEND_DATA); 702193850Strasz if (mode & S_IXOTH) 703193850Strasz a6->ae_perm |= ACL_EXECUTE; 704193850Strasz else 705193850Strasz a5->ae_perm |= ACL_EXECUTE; 706193850Strasz} 707193850Strasz 708219878Strasz#ifdef _KERNEL 709193850Straszvoid 710216413Straszacl_nfs4_sync_acl_from_mode(struct acl *aclp, mode_t mode, 711216413Strasz int file_owner_id) 712216413Strasz{ 713216413Strasz 714216413Strasz if (acl_nfs4_old_semantics) 715216413Strasz acl_nfs4_sync_acl_from_mode_draft(aclp, mode, file_owner_id); 716216413Strasz else 717216413Strasz acl_nfs4_trivial_from_mode(aclp, mode); 718216413Strasz} 719219878Strasz#endif /* _KERNEL */ 720216413Strasz 721216413Straszvoid 722193850Straszacl_nfs4_sync_mode_from_acl(mode_t *_mode, const struct acl *aclp) 723193850Strasz{ 724193850Strasz int i; 725193850Strasz mode_t old_mode = *_mode, mode = 0, seen = 0; 726193850Strasz const struct acl_entry *entry; 727193850Strasz 728193850Strasz KASSERT(aclp->acl_cnt <= ACL_MAX_ENTRIES, 729193850Strasz ("aclp->acl_cnt <= ACL_MAX_ENTRIES")); 730193850Strasz 731193850Strasz /* 732193850Strasz * NFSv4 Minor Version 1, draft-ietf-nfsv4-minorversion1-03.txt 733193850Strasz * 734193850Strasz * 3.16.6.1. Recomputing mode upon SETATTR of ACL 735193850Strasz */ 736193850Strasz 737193850Strasz for (i = 0; i < aclp->acl_cnt; i++) { 738193850Strasz entry = &(aclp->acl_entry[i]); 739193850Strasz 740193850Strasz if (entry->ae_entry_type != ACL_ENTRY_TYPE_ALLOW && 741193850Strasz entry->ae_entry_type != ACL_ENTRY_TYPE_DENY) 742193850Strasz continue; 743193850Strasz 744193850Strasz if (entry->ae_flags & ACL_ENTRY_INHERIT_ONLY) 745193850Strasz continue; 746193850Strasz 747193850Strasz if (entry->ae_tag == ACL_USER_OBJ) { 748193850Strasz if ((entry->ae_perm & ACL_READ_DATA) && 749193850Strasz ((seen & S_IRUSR) == 0)) { 750193850Strasz seen |= S_IRUSR; 751193850Strasz if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 752193850Strasz mode |= S_IRUSR; 753193850Strasz } 754193850Strasz if ((entry->ae_perm & ACL_WRITE_DATA) && 755193850Strasz ((seen & S_IWUSR) == 0)) { 756193850Strasz seen |= S_IWUSR; 757193850Strasz if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 758193850Strasz mode |= S_IWUSR; 759193850Strasz } 760193850Strasz if ((entry->ae_perm & ACL_EXECUTE) && 761193850Strasz ((seen & S_IXUSR) == 0)) { 762193850Strasz seen |= S_IXUSR; 763193850Strasz if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 764193850Strasz mode |= S_IXUSR; 765193850Strasz } 766193850Strasz } else if (entry->ae_tag == ACL_GROUP_OBJ) { 767193850Strasz if ((entry->ae_perm & ACL_READ_DATA) && 768193850Strasz ((seen & S_IRGRP) == 0)) { 769193850Strasz seen |= S_IRGRP; 770193850Strasz if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 771193850Strasz mode |= S_IRGRP; 772193850Strasz } 773193850Strasz if ((entry->ae_perm & ACL_WRITE_DATA) && 774193850Strasz ((seen & S_IWGRP) == 0)) { 775193850Strasz seen |= S_IWGRP; 776193850Strasz if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 777193850Strasz mode |= S_IWGRP; 778193850Strasz } 779193850Strasz if ((entry->ae_perm & ACL_EXECUTE) && 780193850Strasz ((seen & S_IXGRP) == 0)) { 781193850Strasz seen |= S_IXGRP; 782193850Strasz if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 783193850Strasz mode |= S_IXGRP; 784193850Strasz } 785193850Strasz } else if (entry->ae_tag == ACL_EVERYONE) { 786193850Strasz if (entry->ae_perm & ACL_READ_DATA) { 787193850Strasz if ((seen & S_IRUSR) == 0) { 788193850Strasz seen |= S_IRUSR; 789193850Strasz if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 790193850Strasz mode |= S_IRUSR; 791193850Strasz } 792193850Strasz if ((seen & S_IRGRP) == 0) { 793193850Strasz seen |= S_IRGRP; 794193850Strasz if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 795193850Strasz mode |= S_IRGRP; 796193850Strasz } 797193850Strasz if ((seen & S_IROTH) == 0) { 798193850Strasz seen |= S_IROTH; 799193850Strasz if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 800193850Strasz mode |= S_IROTH; 801193850Strasz } 802193850Strasz } 803193850Strasz if (entry->ae_perm & ACL_WRITE_DATA) { 804193850Strasz if ((seen & S_IWUSR) == 0) { 805193850Strasz seen |= S_IWUSR; 806193850Strasz if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 807193850Strasz mode |= S_IWUSR; 808193850Strasz } 809193850Strasz if ((seen & S_IWGRP) == 0) { 810193850Strasz seen |= S_IWGRP; 811193850Strasz if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 812193850Strasz mode |= S_IWGRP; 813193850Strasz } 814193850Strasz if ((seen & S_IWOTH) == 0) { 815193850Strasz seen |= S_IWOTH; 816193850Strasz if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 817193850Strasz mode |= S_IWOTH; 818193850Strasz } 819193850Strasz } 820193850Strasz if (entry->ae_perm & ACL_EXECUTE) { 821193850Strasz if ((seen & S_IXUSR) == 0) { 822193850Strasz seen |= S_IXUSR; 823193850Strasz if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 824193850Strasz mode |= S_IXUSR; 825193850Strasz } 826193850Strasz if ((seen & S_IXGRP) == 0) { 827193850Strasz seen |= S_IXGRP; 828193850Strasz if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 829193850Strasz mode |= S_IXGRP; 830193850Strasz } 831193850Strasz if ((seen & S_IXOTH) == 0) { 832193850Strasz seen |= S_IXOTH; 833193850Strasz if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 834193850Strasz mode |= S_IXOTH; 835193850Strasz } 836193850Strasz } 837193850Strasz } 838193850Strasz } 839193850Strasz 840193850Strasz *_mode = mode | (old_mode & ACL_PRESERVE_MASK); 841193850Strasz} 842197405Strasz 843219878Strasz#ifdef _KERNEL 844216413Strasz/* 845216413Strasz * Calculate inherited ACL in a manner compatible with NFSv4 Minor Version 1, 846216413Strasz * draft-ietf-nfsv4-minorversion1-03.txt. 847216413Strasz */ 848216413Straszstatic void 849216413Straszacl_nfs4_compute_inherited_acl_draft(const struct acl *parent_aclp, 850197405Strasz struct acl *child_aclp, mode_t mode, int file_owner_id, 851197405Strasz int is_directory) 852197405Strasz{ 853197405Strasz int i, flags; 854197405Strasz const struct acl_entry *parent_entry; 855197405Strasz struct acl_entry *entry, *copy; 856197405Strasz 857197405Strasz KASSERT(child_aclp->acl_cnt == 0, ("child_aclp->acl_cnt == 0")); 858197405Strasz KASSERT(parent_aclp->acl_cnt <= ACL_MAX_ENTRIES, 859197405Strasz ("parent_aclp->acl_cnt <= ACL_MAX_ENTRIES")); 860197405Strasz 861197405Strasz /* 862197405Strasz * NFSv4 Minor Version 1, draft-ietf-nfsv4-minorversion1-03.txt 863197405Strasz * 864197405Strasz * 3.16.6.2. Applying the mode given to CREATE or OPEN 865197405Strasz * to an inherited ACL 866197405Strasz */ 867197405Strasz 868197405Strasz /* 869197405Strasz * 1. Form an ACL that is the concatenation of all inheritable ACEs. 870197405Strasz */ 871197405Strasz for (i = 0; i < parent_aclp->acl_cnt; i++) { 872197405Strasz parent_entry = &(parent_aclp->acl_entry[i]); 873197405Strasz flags = parent_entry->ae_flags; 874197405Strasz 875197405Strasz /* 876197405Strasz * Entry is not inheritable at all. 877197405Strasz */ 878197405Strasz if ((flags & (ACL_ENTRY_DIRECTORY_INHERIT | 879197405Strasz ACL_ENTRY_FILE_INHERIT)) == 0) 880197405Strasz continue; 881197405Strasz 882197405Strasz /* 883197405Strasz * We're creating a file, but entry is not inheritable 884197405Strasz * by files. 885197405Strasz */ 886197405Strasz if (!is_directory && (flags & ACL_ENTRY_FILE_INHERIT) == 0) 887197405Strasz continue; 888197405Strasz 889197405Strasz /* 890197405Strasz * Entry is inheritable only by files, but has NO_PROPAGATE 891197405Strasz * flag set, and we're creating a directory, so it wouldn't 892197405Strasz * propagate to any file in that directory anyway. 893197405Strasz */ 894197405Strasz if (is_directory && 895197405Strasz (flags & ACL_ENTRY_DIRECTORY_INHERIT) == 0 && 896197405Strasz (flags & ACL_ENTRY_NO_PROPAGATE_INHERIT)) 897197405Strasz continue; 898197405Strasz 899197405Strasz KASSERT(child_aclp->acl_cnt + 1 <= ACL_MAX_ENTRIES, 900197405Strasz ("child_aclp->acl_cnt + 1 <= ACL_MAX_ENTRIES")); 901197405Strasz child_aclp->acl_entry[child_aclp->acl_cnt] = *parent_entry; 902197405Strasz child_aclp->acl_cnt++; 903197405Strasz } 904197405Strasz 905197405Strasz /* 906197405Strasz * 2. For each entry in the new ACL, adjust its flags, possibly 907197405Strasz * creating two entries in place of one. 908197405Strasz */ 909197405Strasz for (i = 0; i < child_aclp->acl_cnt; i++) { 910197405Strasz entry = &(child_aclp->acl_entry[i]); 911197405Strasz 912197405Strasz /* 913197405Strasz * This is not in the specification, but SunOS 914197405Strasz * apparently does that. 915197405Strasz */ 916197405Strasz if (((entry->ae_flags & ACL_ENTRY_NO_PROPAGATE_INHERIT) || 917197405Strasz !is_directory) && 918197405Strasz entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 919197405Strasz entry->ae_perm &= ~(ACL_WRITE_ACL | ACL_WRITE_OWNER); 920197405Strasz 921197405Strasz /* 922197405Strasz * 2.A. If the ACL_ENTRY_NO_PROPAGATE_INHERIT is set, or if the object 923197405Strasz * being created is not a directory, then clear the 924197405Strasz * following flags: ACL_ENTRY_NO_PROPAGATE_INHERIT, 925197405Strasz * ACL_ENTRY_FILE_INHERIT, ACL_ENTRY_DIRECTORY_INHERIT, 926197405Strasz * ACL_ENTRY_INHERIT_ONLY. 927197405Strasz */ 928197405Strasz if (entry->ae_flags & ACL_ENTRY_NO_PROPAGATE_INHERIT || 929197405Strasz !is_directory) { 930197405Strasz entry->ae_flags &= ~(ACL_ENTRY_NO_PROPAGATE_INHERIT | 931197405Strasz ACL_ENTRY_FILE_INHERIT | ACL_ENTRY_DIRECTORY_INHERIT | 932197405Strasz ACL_ENTRY_INHERIT_ONLY); 933197405Strasz 934197405Strasz /* 935197405Strasz * Continue on to the next ACE. 936197405Strasz */ 937197405Strasz continue; 938197405Strasz } 939197405Strasz 940197405Strasz /* 941197405Strasz * 2.B. If the object is a directory and ACL_ENTRY_FILE_INHERIT 942197405Strasz * is set, but ACL_ENTRY_NO_PROPAGATE_INHERIT is not set, ensure 943197405Strasz * that ACL_ENTRY_INHERIT_ONLY is set. Continue to the 944197405Strasz * next ACE. Otherwise... 945197405Strasz */ 946197405Strasz /* 947197405Strasz * XXX: Read it again and make sure what does the "otherwise" 948197405Strasz * apply to. 949197405Strasz */ 950197405Strasz if (is_directory && 951197405Strasz (entry->ae_flags & ACL_ENTRY_FILE_INHERIT) && 952197405Strasz ((entry->ae_flags & ACL_ENTRY_DIRECTORY_INHERIT) == 0)) { 953197405Strasz entry->ae_flags |= ACL_ENTRY_INHERIT_ONLY; 954197405Strasz continue; 955197405Strasz } 956197405Strasz 957197405Strasz /* 958197405Strasz * 2.C. If the type of the ACE is neither ALLOW nor deny, 959197405Strasz * then continue. 960197405Strasz */ 961197405Strasz if (entry->ae_entry_type != ACL_ENTRY_TYPE_ALLOW && 962197405Strasz entry->ae_entry_type != ACL_ENTRY_TYPE_DENY) 963197405Strasz continue; 964197405Strasz 965197405Strasz /* 966197405Strasz * 2.D. Copy the original ACE into a second, adjacent ACE. 967197405Strasz */ 968197405Strasz copy = _acl_duplicate_entry(child_aclp, i); 969197405Strasz 970197405Strasz /* 971197405Strasz * 2.E. On the first ACE, ensure that ACL_ENTRY_INHERIT_ONLY 972197405Strasz * is set. 973197405Strasz */ 974197405Strasz entry->ae_flags |= ACL_ENTRY_INHERIT_ONLY; 975197405Strasz 976197405Strasz /* 977197405Strasz * 2.F. On the second ACE, clear the following flags: 978197405Strasz * ACL_ENTRY_NO_PROPAGATE_INHERIT, ACL_ENTRY_FILE_INHERIT, 979197405Strasz * ACL_ENTRY_DIRECTORY_INHERIT, ACL_ENTRY_INHERIT_ONLY. 980197405Strasz */ 981197405Strasz copy->ae_flags &= ~(ACL_ENTRY_NO_PROPAGATE_INHERIT | 982197405Strasz ACL_ENTRY_FILE_INHERIT | ACL_ENTRY_DIRECTORY_INHERIT | 983197405Strasz ACL_ENTRY_INHERIT_ONLY); 984197405Strasz 985197405Strasz /* 986197405Strasz * 2.G. On the second ACE, if the type is ALLOW, 987197405Strasz * an implementation MAY clear the following 988197405Strasz * mask bits: ACL_WRITE_ACL, ACL_WRITE_OWNER. 989197405Strasz */ 990197405Strasz if (copy->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 991197405Strasz copy->ae_perm &= ~(ACL_WRITE_ACL | ACL_WRITE_OWNER); 992197405Strasz 993197405Strasz /* 994197405Strasz * Increment the counter to skip the copied entry. 995197405Strasz */ 996197405Strasz i++; 997197405Strasz } 998197405Strasz 999197405Strasz /* 1000197405Strasz * 3. To ensure that the mode is honored, apply the algorithm describe 1001197405Strasz * in Section 2.16.6.3, using the mode that is to be used for file 1002197405Strasz * creation. 1003197405Strasz */ 1004197405Strasz acl_nfs4_sync_acl_from_mode(child_aclp, mode, file_owner_id); 1005197405Strasz} 1006219878Strasz#endif /* _KERNEL */ 1007197405Strasz 1008216413Strasz/* 1009216413Strasz * Populate the ACL with entries inherited from parent_aclp. 1010216413Strasz */ 1011216413Straszstatic void 1012216413Straszacl_nfs4_inherit_entries(const struct acl *parent_aclp, 1013216413Strasz struct acl *child_aclp, mode_t mode, int file_owner_id, 1014216413Strasz int is_directory) 1015216413Strasz{ 1016216413Strasz int i, flags, tag; 1017216413Strasz const struct acl_entry *parent_entry; 1018216413Strasz struct acl_entry *entry; 1019216413Strasz 1020216413Strasz KASSERT(parent_aclp->acl_cnt <= ACL_MAX_ENTRIES, 1021216413Strasz ("parent_aclp->acl_cnt <= ACL_MAX_ENTRIES")); 1022216413Strasz 1023216413Strasz for (i = 0; i < parent_aclp->acl_cnt; i++) { 1024216413Strasz parent_entry = &(parent_aclp->acl_entry[i]); 1025216413Strasz flags = parent_entry->ae_flags; 1026216413Strasz tag = parent_entry->ae_tag; 1027216413Strasz 1028216413Strasz /* 1029216413Strasz * Don't inherit owner@, group@, or everyone@ entries. 1030216413Strasz */ 1031216413Strasz if (tag == ACL_USER_OBJ || tag == ACL_GROUP_OBJ || 1032216413Strasz tag == ACL_EVERYONE) 1033216413Strasz continue; 1034216413Strasz 1035216413Strasz /* 1036216413Strasz * Entry is not inheritable at all. 1037216413Strasz */ 1038216413Strasz if ((flags & (ACL_ENTRY_DIRECTORY_INHERIT | 1039216413Strasz ACL_ENTRY_FILE_INHERIT)) == 0) 1040216413Strasz continue; 1041216413Strasz 1042216413Strasz /* 1043216413Strasz * We're creating a file, but entry is not inheritable 1044216413Strasz * by files. 1045216413Strasz */ 1046216413Strasz if (!is_directory && (flags & ACL_ENTRY_FILE_INHERIT) == 0) 1047216413Strasz continue; 1048216413Strasz 1049216413Strasz /* 1050216413Strasz * Entry is inheritable only by files, but has NO_PROPAGATE 1051216413Strasz * flag set, and we're creating a directory, so it wouldn't 1052216413Strasz * propagate to any file in that directory anyway. 1053216413Strasz */ 1054216413Strasz if (is_directory && 1055216413Strasz (flags & ACL_ENTRY_DIRECTORY_INHERIT) == 0 && 1056216413Strasz (flags & ACL_ENTRY_NO_PROPAGATE_INHERIT)) 1057216413Strasz continue; 1058216413Strasz 1059216413Strasz /* 1060216413Strasz * Entry qualifies for being inherited. 1061216413Strasz */ 1062216413Strasz KASSERT(child_aclp->acl_cnt + 1 <= ACL_MAX_ENTRIES, 1063216413Strasz ("child_aclp->acl_cnt + 1 <= ACL_MAX_ENTRIES")); 1064216413Strasz entry = &(child_aclp->acl_entry[child_aclp->acl_cnt]); 1065216413Strasz *entry = *parent_entry; 1066216413Strasz child_aclp->acl_cnt++; 1067216413Strasz 1068216413Strasz entry->ae_flags &= ~ACL_ENTRY_INHERIT_ONLY; 1069216413Strasz 1070216413Strasz /* 1071216413Strasz * If the type of the ACE is neither ALLOW nor DENY, 1072216413Strasz * then leave it as it is and proceed to the next one. 1073216413Strasz */ 1074216413Strasz if (entry->ae_entry_type != ACL_ENTRY_TYPE_ALLOW && 1075216413Strasz entry->ae_entry_type != ACL_ENTRY_TYPE_DENY) 1076216413Strasz continue; 1077216413Strasz 1078216413Strasz /* 1079216413Strasz * If the ACL_ENTRY_NO_PROPAGATE_INHERIT is set, or if 1080216413Strasz * the object being created is not a directory, then clear 1081216413Strasz * the following flags: ACL_ENTRY_NO_PROPAGATE_INHERIT, 1082216413Strasz * ACL_ENTRY_FILE_INHERIT, ACL_ENTRY_DIRECTORY_INHERIT, 1083216413Strasz * ACL_ENTRY_INHERIT_ONLY. 1084216413Strasz */ 1085216413Strasz if (entry->ae_flags & ACL_ENTRY_NO_PROPAGATE_INHERIT || 1086216413Strasz !is_directory) { 1087216413Strasz entry->ae_flags &= ~(ACL_ENTRY_NO_PROPAGATE_INHERIT | 1088216413Strasz ACL_ENTRY_FILE_INHERIT | ACL_ENTRY_DIRECTORY_INHERIT | 1089216413Strasz ACL_ENTRY_INHERIT_ONLY); 1090216413Strasz } 1091216413Strasz 1092216413Strasz /* 1093216413Strasz * If the object is a directory and ACL_ENTRY_FILE_INHERIT 1094216413Strasz * is set, but ACL_ENTRY_DIRECTORY_INHERIT is not set, ensure 1095216413Strasz * that ACL_ENTRY_INHERIT_ONLY is set. 1096216413Strasz */ 1097216413Strasz if (is_directory && 1098216413Strasz (entry->ae_flags & ACL_ENTRY_FILE_INHERIT) && 1099216413Strasz ((entry->ae_flags & ACL_ENTRY_DIRECTORY_INHERIT) == 0)) { 1100216413Strasz entry->ae_flags |= ACL_ENTRY_INHERIT_ONLY; 1101216413Strasz } 1102216413Strasz 1103216413Strasz if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW && 1104216413Strasz (entry->ae_flags & ACL_ENTRY_INHERIT_ONLY) == 0) { 1105216413Strasz /* 1106216413Strasz * Some permissions must never be inherited. 1107216413Strasz */ 1108216413Strasz entry->ae_perm &= ~(ACL_WRITE_ACL | ACL_WRITE_OWNER | 1109216413Strasz ACL_WRITE_NAMED_ATTRS | ACL_WRITE_ATTRIBUTES); 1110216413Strasz 1111216413Strasz /* 1112216413Strasz * Others must be masked according to the file mode. 1113216413Strasz */ 1114216413Strasz if ((mode & S_IRGRP) == 0) 1115216413Strasz entry->ae_perm &= ~ACL_READ_DATA; 1116216413Strasz if ((mode & S_IWGRP) == 0) 1117216413Strasz entry->ae_perm &= 1118216413Strasz ~(ACL_WRITE_DATA | ACL_APPEND_DATA); 1119216413Strasz if ((mode & S_IXGRP) == 0) 1120216413Strasz entry->ae_perm &= ~ACL_EXECUTE; 1121216413Strasz } 1122216413Strasz } 1123216413Strasz} 1124216413Strasz 1125216413Strasz/* 1126216413Strasz * Calculate inherited ACL in a manner compatible with PSARC/2010/029. 1127216413Strasz * It's also being used to calculate a trivial ACL, by inheriting from 1128216413Strasz * a NULL ACL. 1129216413Strasz */ 1130216413Straszstatic void 1131216413Straszacl_nfs4_compute_inherited_acl_psarc(const struct acl *parent_aclp, 1132216413Strasz struct acl *aclp, mode_t mode, int file_owner_id, int is_directory) 1133216413Strasz{ 1134216413Strasz acl_perm_t user_allow_first = 0, user_deny = 0, group_deny = 0; 1135216413Strasz acl_perm_t user_allow, group_allow, everyone_allow; 1136216413Strasz 1137216413Strasz KASSERT(aclp->acl_cnt == 0, ("aclp->acl_cnt == 0")); 1138216413Strasz 1139216413Strasz user_allow = group_allow = everyone_allow = ACL_READ_ACL | 1140216413Strasz ACL_READ_ATTRIBUTES | ACL_READ_NAMED_ATTRS | ACL_SYNCHRONIZE; 1141216413Strasz user_allow |= ACL_WRITE_ACL | ACL_WRITE_OWNER | ACL_WRITE_ATTRIBUTES | 1142216413Strasz ACL_WRITE_NAMED_ATTRS; 1143216413Strasz 1144216413Strasz if (mode & S_IRUSR) 1145216413Strasz user_allow |= ACL_READ_DATA; 1146216413Strasz if (mode & S_IWUSR) 1147216413Strasz user_allow |= (ACL_WRITE_DATA | ACL_APPEND_DATA); 1148216413Strasz if (mode & S_IXUSR) 1149216413Strasz user_allow |= ACL_EXECUTE; 1150216413Strasz 1151216413Strasz if (mode & S_IRGRP) 1152216413Strasz group_allow |= ACL_READ_DATA; 1153216413Strasz if (mode & S_IWGRP) 1154216413Strasz group_allow |= (ACL_WRITE_DATA | ACL_APPEND_DATA); 1155216413Strasz if (mode & S_IXGRP) 1156216413Strasz group_allow |= ACL_EXECUTE; 1157216413Strasz 1158216413Strasz if (mode & S_IROTH) 1159216413Strasz everyone_allow |= ACL_READ_DATA; 1160216413Strasz if (mode & S_IWOTH) 1161216413Strasz everyone_allow |= (ACL_WRITE_DATA | ACL_APPEND_DATA); 1162216413Strasz if (mode & S_IXOTH) 1163216413Strasz everyone_allow |= ACL_EXECUTE; 1164216413Strasz 1165216413Strasz user_deny = ((group_allow | everyone_allow) & ~user_allow); 1166216413Strasz group_deny = everyone_allow & ~group_allow; 1167216413Strasz user_allow_first = group_deny & ~user_deny; 1168216413Strasz 1169216413Strasz if (user_allow_first != 0) 1170216413Strasz _acl_append(aclp, ACL_USER_OBJ, user_allow_first, 1171216413Strasz ACL_ENTRY_TYPE_ALLOW); 1172216413Strasz if (user_deny != 0) 1173216413Strasz _acl_append(aclp, ACL_USER_OBJ, user_deny, 1174216413Strasz ACL_ENTRY_TYPE_DENY); 1175216413Strasz if (group_deny != 0) 1176216413Strasz _acl_append(aclp, ACL_GROUP_OBJ, group_deny, 1177216413Strasz ACL_ENTRY_TYPE_DENY); 1178216413Strasz 1179216413Strasz if (parent_aclp != NULL) 1180216413Strasz acl_nfs4_inherit_entries(parent_aclp, aclp, mode, 1181216413Strasz file_owner_id, is_directory); 1182216413Strasz 1183216413Strasz _acl_append(aclp, ACL_USER_OBJ, user_allow, ACL_ENTRY_TYPE_ALLOW); 1184216413Strasz _acl_append(aclp, ACL_GROUP_OBJ, group_allow, ACL_ENTRY_TYPE_ALLOW); 1185216413Strasz _acl_append(aclp, ACL_EVERYONE, everyone_allow, ACL_ENTRY_TYPE_ALLOW); 1186216413Strasz} 1187216413Strasz 1188219878Strasz#ifdef _KERNEL 1189216413Straszvoid 1190216413Straszacl_nfs4_compute_inherited_acl(const struct acl *parent_aclp, 1191216413Strasz struct acl *child_aclp, mode_t mode, int file_owner_id, 1192216413Strasz int is_directory) 1193216413Strasz{ 1194216413Strasz 1195216413Strasz if (acl_nfs4_old_semantics) 1196216413Strasz acl_nfs4_compute_inherited_acl_draft(parent_aclp, child_aclp, 1197216413Strasz mode, file_owner_id, is_directory); 1198216413Strasz else 1199216413Strasz acl_nfs4_compute_inherited_acl_psarc(parent_aclp, child_aclp, 1200216413Strasz mode, file_owner_id, is_directory); 1201216413Strasz} 1202219878Strasz#endif /* _KERNEL */ 1203216413Strasz 1204216413Strasz/* 1205216413Strasz * Calculate trivial ACL in a manner compatible with PSARC/2010/029. 1206216413Strasz * Note that this results in an ACL different from (but semantically 1207216413Strasz * equal to) the "canonical six" trivial ACL computed using algorithm 1208216413Strasz * described in draft-ietf-nfsv4-minorversion1-03.txt, 3.16.6.2. 1209216413Strasz */ 1210219878Straszstatic void 1211216413Straszacl_nfs4_trivial_from_mode(struct acl *aclp, mode_t mode) 1212216413Strasz{ 1213216413Strasz 1214216413Strasz aclp->acl_cnt = 0; 1215216413Strasz acl_nfs4_compute_inherited_acl_psarc(NULL, aclp, mode, -1, -1); 1216216413Strasz} 1217216413Strasz 1218219878Strasz#ifndef _KERNEL 1219219878Strasz/* 1220219878Strasz * This routine is used by libc to implement acl_strip_np(3) 1221219878Strasz * and acl_is_trivial_np(3). 1222219878Strasz */ 1223219878Straszvoid 1224219878Straszacl_nfs4_trivial_from_mode_libc(struct acl *aclp, int mode, int canonical_six) 1225219878Strasz{ 1226219878Strasz 1227219878Strasz aclp->acl_cnt = 0; 1228219878Strasz if (canonical_six) 1229219878Strasz acl_nfs4_sync_acl_from_mode_draft(aclp, mode, -1); 1230219878Strasz else 1231219878Strasz acl_nfs4_trivial_from_mode(aclp, mode); 1232219878Strasz} 1233219878Strasz#endif /* !_KERNEL */ 1234219878Strasz 1235197405Strasz#ifdef _KERNEL 1236197405Straszstatic int 1237197405Strasz_acls_are_equal(const struct acl *a, const struct acl *b) 1238197405Strasz{ 1239197405Strasz int i; 1240197405Strasz const struct acl_entry *entrya, *entryb; 1241197405Strasz 1242197405Strasz if (a->acl_cnt != b->acl_cnt) 1243197405Strasz return (0); 1244197405Strasz 1245197405Strasz for (i = 0; i < b->acl_cnt; i++) { 1246197405Strasz entrya = &(a->acl_entry[i]); 1247197405Strasz entryb = &(b->acl_entry[i]); 1248197405Strasz 1249197405Strasz if (entrya->ae_tag != entryb->ae_tag || 1250197405Strasz entrya->ae_id != entryb->ae_id || 1251197405Strasz entrya->ae_perm != entryb->ae_perm || 1252197405Strasz entrya->ae_entry_type != entryb->ae_entry_type || 1253197405Strasz entrya->ae_flags != entryb->ae_flags) 1254197405Strasz return (0); 1255197405Strasz } 1256197405Strasz 1257197405Strasz return (1); 1258197405Strasz} 1259197405Strasz 1260197405Strasz/* 1261201495Strasz * This routine is used to determine whether to remove extended attribute 1262197405Strasz * that stores ACL contents. 1263197405Strasz */ 1264197405Straszint 1265197405Straszacl_nfs4_is_trivial(const struct acl *aclp, int file_owner_id) 1266197405Strasz{ 1267197405Strasz int trivial; 1268197405Strasz mode_t tmpmode = 0; 1269197405Strasz struct acl *tmpaclp; 1270197405Strasz 1271216413Strasz if (aclp->acl_cnt > 6) 1272197405Strasz return (0); 1273197405Strasz 1274197405Strasz /* 1275197405Strasz * Compute the mode from the ACL, then compute new ACL from that mode. 1276197405Strasz * If the ACLs are identical, then the ACL is trivial. 1277197405Strasz * 1278197405Strasz * XXX: I guess there is a faster way to do this. However, even 1279197405Strasz * this slow implementation significantly speeds things up 1280201495Strasz * for files that don't have non-trivial ACLs - it's critical 1281201495Strasz * for performance to not use EA when they are not needed. 1282216413Strasz * 1283216413Strasz * First try the PSARC/2010/029 semantics. 1284197405Strasz */ 1285197405Strasz tmpaclp = acl_alloc(M_WAITOK | M_ZERO); 1286197405Strasz acl_nfs4_sync_mode_from_acl(&tmpmode, aclp); 1287216413Strasz acl_nfs4_trivial_from_mode(tmpaclp, tmpmode); 1288197405Strasz trivial = _acls_are_equal(aclp, tmpaclp); 1289216413Strasz if (trivial) { 1290216413Strasz acl_free(tmpaclp); 1291216413Strasz return (trivial); 1292216413Strasz } 1293216413Strasz 1294216413Strasz /* 1295216413Strasz * Check if it's a draft-ietf-nfsv4-minorversion1-03.txt trivial ACL. 1296216413Strasz */ 1297216413Strasz tmpaclp->acl_cnt = 0; 1298216413Strasz acl_nfs4_sync_acl_from_mode_draft(tmpaclp, tmpmode, file_owner_id); 1299216413Strasz trivial = _acls_are_equal(aclp, tmpaclp); 1300197405Strasz acl_free(tmpaclp); 1301197405Strasz 1302197405Strasz return (trivial); 1303197405Strasz} 1304197405Strasz#endif /* _KERNEL */ 1305197405Strasz 1306197405Straszint 1307197405Straszacl_nfs4_check(const struct acl *aclp, int is_directory) 1308197405Strasz{ 1309197405Strasz int i; 1310197405Strasz const struct acl_entry *entry; 1311197405Strasz 1312197405Strasz /* 1313197405Strasz * The spec doesn't seem to say anything about ACL validity. 1314197405Strasz * It seems there is not much to do here. There is even no need 1315197405Strasz * to count "owner@" or "everyone@" (ACL_USER_OBJ and ACL_EVERYONE) 1316197405Strasz * entries, as there can be several of them and that's perfectly 1317197405Strasz * valid. There can be none of them too. Really. 1318197405Strasz */ 1319197405Strasz 1320197405Strasz if (aclp->acl_cnt > ACL_MAX_ENTRIES || aclp->acl_cnt <= 0) 1321197405Strasz return (EINVAL); 1322197405Strasz 1323197405Strasz for (i = 0; i < aclp->acl_cnt; i++) { 1324197405Strasz entry = &(aclp->acl_entry[i]); 1325197405Strasz 1326197405Strasz switch (entry->ae_tag) { 1327197405Strasz case ACL_USER_OBJ: 1328197405Strasz case ACL_GROUP_OBJ: 1329197405Strasz case ACL_EVERYONE: 1330197405Strasz if (entry->ae_id != ACL_UNDEFINED_ID) 1331197405Strasz return (EINVAL); 1332197405Strasz break; 1333197405Strasz 1334197405Strasz case ACL_USER: 1335197405Strasz case ACL_GROUP: 1336197405Strasz if (entry->ae_id == ACL_UNDEFINED_ID) 1337197405Strasz return (EINVAL); 1338197405Strasz break; 1339197405Strasz 1340197405Strasz default: 1341197405Strasz return (EINVAL); 1342197405Strasz } 1343197405Strasz 1344197405Strasz if ((entry->ae_perm | ACL_NFS4_PERM_BITS) != ACL_NFS4_PERM_BITS) 1345197405Strasz return (EINVAL); 1346197405Strasz 1347197405Strasz /* 1348197405Strasz * Disallow ACL_ENTRY_TYPE_AUDIT and ACL_ENTRY_TYPE_ALARM for now. 1349197405Strasz */ 1350197405Strasz if (entry->ae_entry_type != ACL_ENTRY_TYPE_ALLOW && 1351197405Strasz entry->ae_entry_type != ACL_ENTRY_TYPE_DENY) 1352197405Strasz return (EINVAL); 1353197405Strasz 1354197405Strasz if ((entry->ae_flags | ACL_FLAGS_BITS) != ACL_FLAGS_BITS) 1355197405Strasz return (EINVAL); 1356197405Strasz 1357197405Strasz /* Disallow unimplemented flags. */ 1358197405Strasz if (entry->ae_flags & (ACL_ENTRY_SUCCESSFUL_ACCESS | 1359197405Strasz ACL_ENTRY_FAILED_ACCESS)) 1360197405Strasz return (EINVAL); 1361197405Strasz 1362197405Strasz /* Disallow flags not allowed for ordinary files. */ 1363197405Strasz if (!is_directory) { 1364197405Strasz if (entry->ae_flags & (ACL_ENTRY_FILE_INHERIT | 1365197405Strasz ACL_ENTRY_DIRECTORY_INHERIT | 1366197405Strasz ACL_ENTRY_NO_PROPAGATE_INHERIT | ACL_ENTRY_INHERIT_ONLY)) 1367197405Strasz return (EINVAL); 1368197405Strasz } 1369197405Strasz } 1370197405Strasz 1371197405Strasz return (0); 1372197405Strasz} 1373