subr_acl_nfs4.c revision 214522
1193850Strasz/*- 2197405Strasz * Copyright (c) 2008-2009 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: head/sys/kern/subr_acl_nfs4.c 214522 2010-10-29 19:07:36Z trasz $"); 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> 44193850Strasz#include <sys/acl.h> 45193850Strasz#else 46193850Strasz#include <errno.h> 47193850Strasz#include <assert.h> 48193850Strasz#include <sys/acl.h> 49193850Strasz#include <sys/stat.h> 50193850Strasz#define KASSERT(a, b) assert(a) 51193850Strasz#define CTASSERT(a) 52197405Strasz#endif /* _KERNEL */ 53193850Strasz 54197405Strasz#ifdef _KERNEL 55197405Strasz 56197405Straszstatic struct { 57197405Strasz accmode_t accmode; 58197405Strasz int mask; 59197405Strasz} accmode2mask[] = {{VREAD, ACL_READ_DATA}, 60197405Strasz {VWRITE, ACL_WRITE_DATA}, 61197405Strasz {VAPPEND, ACL_APPEND_DATA}, 62197405Strasz {VEXEC, ACL_EXECUTE}, 63197405Strasz {VREAD_NAMED_ATTRS, ACL_READ_NAMED_ATTRS}, 64197405Strasz {VWRITE_NAMED_ATTRS, ACL_WRITE_NAMED_ATTRS}, 65197405Strasz {VDELETE_CHILD, ACL_DELETE_CHILD}, 66197405Strasz {VREAD_ATTRIBUTES, ACL_READ_ATTRIBUTES}, 67197405Strasz {VWRITE_ATTRIBUTES, ACL_WRITE_ATTRIBUTES}, 68197405Strasz {VDELETE, ACL_DELETE}, 69197405Strasz {VREAD_ACL, ACL_READ_ACL}, 70197405Strasz {VWRITE_ACL, ACL_WRITE_ACL}, 71197405Strasz {VWRITE_OWNER, ACL_WRITE_OWNER}, 72197405Strasz {VSYNCHRONIZE, ACL_SYNCHRONIZE}, 73197405Strasz {0, 0}}; 74197405Strasz 75193850Straszstatic int 76197405Strasz_access_mask_from_accmode(accmode_t accmode) 77197405Strasz{ 78197405Strasz int access_mask = 0, i; 79197405Strasz 80197405Strasz for (i = 0; accmode2mask[i].accmode != 0; i++) { 81197405Strasz if (accmode & accmode2mask[i].accmode) 82197405Strasz access_mask |= accmode2mask[i].mask; 83197405Strasz } 84197405Strasz 85200723Strasz /* 86200723Strasz * VAPPEND is just a modifier for VWRITE; if the caller asked 87200723Strasz * for 'VAPPEND | VWRITE', we want to check for ACL_APPEND_DATA only. 88200723Strasz */ 89200723Strasz if (access_mask & ACL_APPEND_DATA) 90200723Strasz access_mask &= ~ACL_WRITE_DATA; 91200723Strasz 92197405Strasz return (access_mask); 93197405Strasz} 94197405Strasz 95197405Strasz/* 96197405Strasz * Return 0, iff access is allowed, 1 otherwise. 97197405Strasz */ 98197405Straszstatic int 99197405Strasz_acl_denies(const struct acl *aclp, int access_mask, struct ucred *cred, 100197405Strasz int file_uid, int file_gid, int *denied_explicitly) 101197405Strasz{ 102197405Strasz int i; 103197405Strasz const struct acl_entry *entry; 104197405Strasz 105197405Strasz if (denied_explicitly != NULL) 106197405Strasz *denied_explicitly = 0; 107197405Strasz 108197405Strasz KASSERT(aclp->acl_cnt > 0, ("aclp->acl_cnt > 0")); 109197405Strasz KASSERT(aclp->acl_cnt <= ACL_MAX_ENTRIES, 110197405Strasz ("aclp->acl_cnt <= ACL_MAX_ENTRIES")); 111197405Strasz 112197405Strasz for (i = 0; i < aclp->acl_cnt; i++) { 113197405Strasz entry = &(aclp->acl_entry[i]); 114197405Strasz 115197405Strasz if (entry->ae_entry_type != ACL_ENTRY_TYPE_ALLOW && 116197405Strasz entry->ae_entry_type != ACL_ENTRY_TYPE_DENY) 117197405Strasz continue; 118197405Strasz if (entry->ae_flags & ACL_ENTRY_INHERIT_ONLY) 119197405Strasz continue; 120197405Strasz switch (entry->ae_tag) { 121197405Strasz case ACL_USER_OBJ: 122197405Strasz if (file_uid != cred->cr_uid) 123197405Strasz continue; 124197405Strasz break; 125197405Strasz case ACL_USER: 126197405Strasz if (entry->ae_id != cred->cr_uid) 127197405Strasz continue; 128197405Strasz break; 129197405Strasz case ACL_GROUP_OBJ: 130197405Strasz if (!groupmember(file_gid, cred)) 131197405Strasz continue; 132197405Strasz break; 133197405Strasz case ACL_GROUP: 134197405Strasz if (!groupmember(entry->ae_id, cred)) 135197405Strasz continue; 136197405Strasz break; 137197405Strasz default: 138197405Strasz KASSERT(entry->ae_tag == ACL_EVERYONE, 139197405Strasz ("entry->ae_tag == ACL_EVERYONE")); 140197405Strasz } 141197405Strasz 142197405Strasz if (entry->ae_entry_type == ACL_ENTRY_TYPE_DENY) { 143197405Strasz if (entry->ae_perm & access_mask) { 144197405Strasz if (denied_explicitly != NULL) 145197405Strasz *denied_explicitly = 1; 146197405Strasz return (1); 147197405Strasz } 148197405Strasz } 149197405Strasz 150197405Strasz access_mask &= ~(entry->ae_perm); 151197405Strasz if (access_mask == 0) 152197405Strasz return (0); 153197405Strasz } 154197405Strasz 155197405Strasz return (1); 156197405Strasz} 157197405Strasz 158197405Straszint 159197405Straszvaccess_acl_nfs4(enum vtype type, uid_t file_uid, gid_t file_gid, 160197405Strasz struct acl *aclp, accmode_t accmode, struct ucred *cred, int *privused) 161197405Strasz{ 162197405Strasz accmode_t priv_granted = 0; 163197405Strasz int denied, explicitly_denied, access_mask, is_directory, 164197405Strasz must_be_owner = 0; 165214522Strasz mode_t file_mode = 0; 166197405Strasz 167201019Strasz KASSERT((accmode & ~(VEXEC | VWRITE | VREAD | VADMIN | VAPPEND | 168201019Strasz VEXPLICIT_DENY | VREAD_NAMED_ATTRS | VWRITE_NAMED_ATTRS | 169201019Strasz VDELETE_CHILD | VREAD_ATTRIBUTES | VWRITE_ATTRIBUTES | VDELETE | 170201019Strasz VREAD_ACL | VWRITE_ACL | VWRITE_OWNER | VSYNCHRONIZE)) == 0, 171201019Strasz ("invalid bit in accmode")); 172201019Strasz KASSERT((accmode & VAPPEND) == 0 || (accmode & VWRITE), 173201019Strasz ("VAPPEND without VWRITE")); 174201019Strasz 175197405Strasz if (privused != NULL) 176197405Strasz *privused = 0; 177197405Strasz 178197405Strasz if (accmode & VADMIN) 179197405Strasz must_be_owner = 1; 180197405Strasz 181197405Strasz /* 182197405Strasz * Ignore VSYNCHRONIZE permission. 183197405Strasz */ 184197405Strasz accmode &= ~VSYNCHRONIZE; 185197405Strasz 186197405Strasz access_mask = _access_mask_from_accmode(accmode); 187197405Strasz 188197405Strasz if (type == VDIR) 189197405Strasz is_directory = 1; 190197405Strasz else 191197405Strasz is_directory = 0; 192197405Strasz 193197405Strasz /* 194197405Strasz * File owner is always allowed to read and write the ACL 195197405Strasz * and basic attributes. This is to prevent a situation 196197405Strasz * where user would change ACL in a way that prevents him 197197405Strasz * from undoing the change. 198197405Strasz */ 199197405Strasz if (file_uid == cred->cr_uid) 200197405Strasz access_mask &= ~(ACL_READ_ACL | ACL_WRITE_ACL | 201197405Strasz ACL_READ_ATTRIBUTES | ACL_WRITE_ATTRIBUTES); 202197405Strasz 203197405Strasz /* 204197405Strasz * Ignore append permission for regular files; use write 205197405Strasz * permission instead. 206197405Strasz */ 207197405Strasz if (!is_directory && (access_mask & ACL_APPEND_DATA)) { 208197405Strasz access_mask &= ~ACL_APPEND_DATA; 209197405Strasz access_mask |= ACL_WRITE_DATA; 210197405Strasz } 211197405Strasz 212197405Strasz denied = _acl_denies(aclp, access_mask, cred, file_uid, file_gid, 213197405Strasz &explicitly_denied); 214197405Strasz 215197405Strasz if (must_be_owner) { 216197405Strasz if (file_uid != cred->cr_uid) 217197405Strasz denied = EPERM; 218197405Strasz } 219197405Strasz 220212002Sjh /* 221212002Sjh * For VEXEC, ensure that at least one execute bit is set for 222212002Sjh * non-directories. We have to check the mode here to stay 223212002Sjh * consistent with execve(2). See the test in 224212002Sjh * exec_check_permissions(). 225212002Sjh */ 226212002Sjh acl_nfs4_sync_mode_from_acl(&file_mode, aclp); 227212002Sjh if (!denied && !is_directory && (accmode & VEXEC) && 228212002Sjh (file_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) == 0) 229212002Sjh denied = EACCES; 230212002Sjh 231197405Strasz if (!denied) 232197405Strasz return (0); 233197405Strasz 234197405Strasz /* 235197405Strasz * Access failed. Iff it was not denied explicitly and 236197405Strasz * VEXPLICIT_DENY flag was specified, allow access. 237197405Strasz */ 238197405Strasz if ((accmode & VEXPLICIT_DENY) && explicitly_denied == 0) 239197405Strasz return (0); 240197405Strasz 241197405Strasz accmode &= ~VEXPLICIT_DENY; 242197405Strasz 243197405Strasz /* 244197405Strasz * No match. Try to use privileges, if there are any. 245197405Strasz */ 246197405Strasz if (is_directory) { 247197405Strasz if ((accmode & VEXEC) && !priv_check_cred(cred, 248197405Strasz PRIV_VFS_LOOKUP, 0)) 249197405Strasz priv_granted |= VEXEC; 250197405Strasz } else { 251212002Sjh /* 252212002Sjh * Ensure that at least one execute bit is on. Otherwise, 253212002Sjh * a privileged user will always succeed, and we don't want 254212002Sjh * this to happen unless the file really is executable. 255212002Sjh */ 256212002Sjh if ((accmode & VEXEC) && (file_mode & 257212002Sjh (S_IXUSR | S_IXGRP | S_IXOTH)) != 0 && 258212002Sjh !priv_check_cred(cred, PRIV_VFS_EXEC, 0)) 259197405Strasz priv_granted |= VEXEC; 260197405Strasz } 261197405Strasz 262197405Strasz if ((accmode & VREAD) && !priv_check_cred(cred, PRIV_VFS_READ, 0)) 263197405Strasz priv_granted |= VREAD; 264197405Strasz 265197405Strasz if ((accmode & (VWRITE | VAPPEND | VDELETE_CHILD)) && 266197405Strasz !priv_check_cred(cred, PRIV_VFS_WRITE, 0)) 267197405Strasz priv_granted |= (VWRITE | VAPPEND | VDELETE_CHILD); 268197405Strasz 269197405Strasz if ((accmode & VADMIN_PERMS) && 270197405Strasz !priv_check_cred(cred, PRIV_VFS_ADMIN, 0)) 271197405Strasz priv_granted |= VADMIN_PERMS; 272197405Strasz 273197405Strasz if ((accmode & VSTAT_PERMS) && 274197405Strasz !priv_check_cred(cred, PRIV_VFS_STAT, 0)) 275197405Strasz priv_granted |= VSTAT_PERMS; 276197405Strasz 277197405Strasz if ((accmode & priv_granted) == accmode) { 278197405Strasz if (privused != NULL) 279197405Strasz *privused = 1; 280197405Strasz 281197405Strasz return (0); 282197405Strasz } 283197405Strasz 284197405Strasz if (accmode & (VADMIN_PERMS | VDELETE_CHILD | VDELETE)) 285197405Strasz denied = EPERM; 286197405Strasz else 287197405Strasz denied = EACCES; 288197405Strasz 289197405Strasz return (denied); 290197405Strasz} 291197405Strasz#endif /* _KERNEL */ 292197405Strasz 293197405Straszstatic int 294193850Strasz_acl_entry_matches(struct acl_entry *entry, acl_tag_t tag, acl_perm_t perm, 295193850Strasz acl_entry_type_t entry_type) 296193850Strasz{ 297193850Strasz if (entry->ae_tag != tag) 298193850Strasz return (0); 299193850Strasz 300193850Strasz if (entry->ae_id != ACL_UNDEFINED_ID) 301193850Strasz return (0); 302193850Strasz 303193850Strasz if (entry->ae_perm != perm) 304193850Strasz return (0); 305193850Strasz 306193850Strasz if (entry->ae_entry_type != entry_type) 307193850Strasz return (0); 308193850Strasz 309193850Strasz if (entry->ae_flags != 0) 310193850Strasz return (0); 311193850Strasz 312193850Strasz return (1); 313193850Strasz} 314193850Strasz 315193850Straszstatic struct acl_entry * 316193850Strasz_acl_append(struct acl *aclp, acl_tag_t tag, acl_perm_t perm, 317193850Strasz acl_entry_type_t entry_type) 318193850Strasz{ 319193850Strasz struct acl_entry *entry; 320193850Strasz 321193850Strasz KASSERT(aclp->acl_cnt + 1 <= ACL_MAX_ENTRIES, 322193850Strasz ("aclp->acl_cnt + 1 <= ACL_MAX_ENTRIES")); 323193850Strasz 324193850Strasz entry = &(aclp->acl_entry[aclp->acl_cnt]); 325193850Strasz aclp->acl_cnt++; 326193850Strasz 327193850Strasz entry->ae_tag = tag; 328193850Strasz entry->ae_id = ACL_UNDEFINED_ID; 329193850Strasz entry->ae_perm = perm; 330193850Strasz entry->ae_entry_type = entry_type; 331193850Strasz entry->ae_flags = 0; 332193850Strasz 333193850Strasz return (entry); 334193850Strasz} 335193850Strasz 336193850Straszstatic struct acl_entry * 337193850Strasz_acl_duplicate_entry(struct acl *aclp, int entry_index) 338193850Strasz{ 339193850Strasz int i; 340193850Strasz 341193850Strasz KASSERT(aclp->acl_cnt + 1 <= ACL_MAX_ENTRIES, 342193850Strasz ("aclp->acl_cnt + 1 <= ACL_MAX_ENTRIES")); 343193850Strasz 344193850Strasz for (i = aclp->acl_cnt; i > entry_index; i--) 345193850Strasz aclp->acl_entry[i] = aclp->acl_entry[i - 1]; 346193850Strasz 347193850Strasz aclp->acl_cnt++; 348193850Strasz 349193850Strasz return (&(aclp->acl_entry[entry_index + 1])); 350193850Strasz} 351193850Strasz 352212906Strasz/* 353212906Strasz * Calculate trivial ACL in a manner compatible with PSARC/2010/029. 354212906Strasz * Note that this results in an ACL different from (but semantically 355212906Strasz * equal to) the "canonical six" trivial ACL computed using algorithm 356212906Strasz * described in draft-ietf-nfsv4-minorversion1-03.txt, 3.16.6.2. 357212906Strasz */ 358193850Straszvoid 359212906Straszacl_nfs4_trivial_from_mode(struct acl *aclp, mode_t mode) 360212906Strasz{ 361212906Strasz acl_perm_t user_allow_first = 0, user_deny = 0, group_deny = 0; 362212906Strasz acl_perm_t user_allow, group_allow, everyone_allow; 363212906Strasz 364212906Strasz KASSERT(aclp->acl_cnt == 0, ("aclp->acl_cnt == 0")); 365212906Strasz 366212906Strasz user_allow = group_allow = everyone_allow = ACL_READ_ACL | 367212906Strasz ACL_READ_ATTRIBUTES | ACL_READ_NAMED_ATTRS | ACL_SYNCHRONIZE; 368212906Strasz user_allow |= ACL_WRITE_ACL | ACL_WRITE_OWNER | ACL_WRITE_ATTRIBUTES | 369212906Strasz ACL_WRITE_NAMED_ATTRS; 370212906Strasz 371212906Strasz if (mode & S_IRUSR) 372212906Strasz user_allow |= ACL_READ_DATA; 373212906Strasz if (mode & S_IWUSR) 374212906Strasz user_allow |= (ACL_WRITE_DATA | ACL_APPEND_DATA); 375212906Strasz if (mode & S_IXUSR) 376212906Strasz user_allow |= ACL_EXECUTE; 377212906Strasz 378212906Strasz if (mode & S_IRGRP) 379212906Strasz group_allow |= ACL_READ_DATA; 380212906Strasz if (mode & S_IWGRP) 381212906Strasz group_allow |= (ACL_WRITE_DATA | ACL_APPEND_DATA); 382212906Strasz if (mode & S_IXGRP) 383212906Strasz group_allow |= ACL_EXECUTE; 384212906Strasz 385212906Strasz if (mode & S_IROTH) 386212906Strasz everyone_allow |= ACL_READ_DATA; 387212906Strasz if (mode & S_IWOTH) 388212906Strasz everyone_allow |= (ACL_WRITE_DATA | ACL_APPEND_DATA); 389212906Strasz if (mode & S_IXOTH) 390212906Strasz everyone_allow |= ACL_EXECUTE; 391212906Strasz 392212906Strasz user_deny = ((group_allow | everyone_allow) & ~user_allow); 393212906Strasz group_deny = everyone_allow & ~group_allow; 394212906Strasz user_allow_first = group_deny & ~user_deny; 395212906Strasz 396212906Strasz if (user_allow_first != 0) 397212906Strasz _acl_append(aclp, ACL_USER_OBJ, user_allow_first, ACL_ENTRY_TYPE_ALLOW); 398212906Strasz if (user_deny != 0) 399212906Strasz _acl_append(aclp, ACL_USER_OBJ, user_deny, ACL_ENTRY_TYPE_DENY); 400212906Strasz if (group_deny != 0) 401212906Strasz _acl_append(aclp, ACL_GROUP_OBJ, group_deny, ACL_ENTRY_TYPE_DENY); 402212906Strasz _acl_append(aclp, ACL_USER_OBJ, user_allow, ACL_ENTRY_TYPE_ALLOW); 403212906Strasz _acl_append(aclp, ACL_GROUP_OBJ, group_allow, ACL_ENTRY_TYPE_ALLOW); 404212906Strasz _acl_append(aclp, ACL_EVERYONE, everyone_allow, ACL_ENTRY_TYPE_ALLOW); 405212906Strasz} 406212906Strasz 407212906Straszvoid 408193850Straszacl_nfs4_sync_acl_from_mode(struct acl *aclp, mode_t mode, int file_owner_id) 409193850Strasz{ 410193850Strasz int i, meets, must_append; 411193850Strasz struct acl_entry *entry, *copy, *previous, 412193850Strasz *a1, *a2, *a3, *a4, *a5, *a6; 413193850Strasz mode_t amode; 414193850Strasz const int READ = 04; 415193850Strasz const int WRITE = 02; 416193850Strasz const int EXEC = 01; 417193850Strasz 418193850Strasz KASSERT(aclp->acl_cnt <= ACL_MAX_ENTRIES, 419193850Strasz ("aclp->acl_cnt <= ACL_MAX_ENTRIES")); 420193850Strasz 421193850Strasz /* 422193850Strasz * NFSv4 Minor Version 1, draft-ietf-nfsv4-minorversion1-03.txt 423193850Strasz * 424193850Strasz * 3.16.6.3. Applying a Mode to an Existing ACL 425193850Strasz */ 426193850Strasz 427193850Strasz /* 428193850Strasz * 1. For each ACE: 429193850Strasz */ 430193850Strasz for (i = 0; i < aclp->acl_cnt; i++) { 431193850Strasz entry = &(aclp->acl_entry[i]); 432193850Strasz 433193850Strasz /* 434193850Strasz * 1.1. If the type is neither ALLOW or DENY - skip. 435193850Strasz */ 436193850Strasz if (entry->ae_entry_type != ACL_ENTRY_TYPE_ALLOW && 437193850Strasz entry->ae_entry_type != ACL_ENTRY_TYPE_DENY) 438193850Strasz continue; 439193850Strasz 440193850Strasz /* 441193850Strasz * 1.2. If ACL_ENTRY_INHERIT_ONLY is set - skip. 442193850Strasz */ 443193850Strasz if (entry->ae_flags & ACL_ENTRY_INHERIT_ONLY) 444193850Strasz continue; 445193850Strasz 446193850Strasz /* 447193850Strasz * 1.3. If ACL_ENTRY_FILE_INHERIT or ACL_ENTRY_DIRECTORY_INHERIT 448193850Strasz * are set: 449193850Strasz */ 450193850Strasz if (entry->ae_flags & 451193850Strasz (ACL_ENTRY_FILE_INHERIT | ACL_ENTRY_DIRECTORY_INHERIT)) { 452193850Strasz /* 453193850Strasz * 1.3.1. A copy of the current ACE is made, and placed 454193850Strasz * in the ACL immediately following the current 455193850Strasz * ACE. 456193850Strasz */ 457193850Strasz copy = _acl_duplicate_entry(aclp, i); 458193850Strasz 459193850Strasz /* 460193850Strasz * 1.3.2. In the first ACE, the flag 461193850Strasz * ACL_ENTRY_INHERIT_ONLY is set. 462193850Strasz */ 463193850Strasz entry->ae_flags |= ACL_ENTRY_INHERIT_ONLY; 464193850Strasz 465193850Strasz /* 466193850Strasz * 1.3.3. In the second ACE, the following flags 467193850Strasz * are cleared: 468193850Strasz * ACL_ENTRY_FILE_INHERIT, 469193850Strasz * ACL_ENTRY_DIRECTORY_INHERIT, 470193850Strasz * ACL_ENTRY_NO_PROPAGATE_INHERIT. 471193850Strasz */ 472193850Strasz copy->ae_flags &= ~(ACL_ENTRY_FILE_INHERIT | 473193850Strasz ACL_ENTRY_DIRECTORY_INHERIT | 474193850Strasz ACL_ENTRY_NO_PROPAGATE_INHERIT); 475193850Strasz 476193850Strasz /* 477193850Strasz * The algorithm continues on with the second ACE. 478193850Strasz */ 479193850Strasz i++; 480193850Strasz entry = copy; 481193850Strasz } 482193850Strasz 483193850Strasz /* 484193850Strasz * 1.4. If it's owner@, group@ or everyone@ entry, clear 485193850Strasz * ACL_READ_DATA, ACL_WRITE_DATA, ACL_APPEND_DATA 486193850Strasz * and ACL_EXECUTE. Continue to the next entry. 487193850Strasz */ 488193850Strasz if (entry->ae_tag == ACL_USER_OBJ || 489193850Strasz entry->ae_tag == ACL_GROUP_OBJ || 490193850Strasz entry->ae_tag == ACL_EVERYONE) { 491193850Strasz entry->ae_perm &= ~(ACL_READ_DATA | ACL_WRITE_DATA | 492193850Strasz ACL_APPEND_DATA | ACL_EXECUTE); 493193850Strasz continue; 494193850Strasz } 495193850Strasz 496193850Strasz /* 497193850Strasz * 1.5. Otherwise, if the "who" field did not match one 498193850Strasz * of OWNER@, GROUP@, EVERYONE@: 499193850Strasz * 500193850Strasz * 1.5.1. If the type is ALLOW, check the preceding ACE. 501193850Strasz * If it does not meet all of the following criteria: 502193850Strasz */ 503193850Strasz if (entry->ae_entry_type != ACL_ENTRY_TYPE_ALLOW) 504193850Strasz continue; 505193850Strasz 506193850Strasz meets = 0; 507193850Strasz if (i > 0) { 508193850Strasz meets = 1; 509193850Strasz previous = &(aclp->acl_entry[i - 1]); 510193850Strasz 511193850Strasz /* 512193850Strasz * 1.5.1.1. The type field is DENY, 513193850Strasz */ 514193850Strasz if (previous->ae_entry_type != ACL_ENTRY_TYPE_DENY) 515193850Strasz meets = 0; 516193850Strasz 517193850Strasz /* 518193850Strasz * 1.5.1.2. The "who" field is the same as the current 519193850Strasz * ACE, 520193850Strasz * 521193850Strasz * 1.5.1.3. The flag bit ACE4_IDENTIFIER_GROUP 522193850Strasz * is the same as it is in the current ACE, 523193850Strasz * and no other flag bits are set, 524193850Strasz */ 525193850Strasz if (previous->ae_id != entry->ae_id || 526193850Strasz previous->ae_tag != entry->ae_tag) 527193850Strasz meets = 0; 528193850Strasz 529193850Strasz if (previous->ae_flags) 530193850Strasz meets = 0; 531193850Strasz 532193850Strasz /* 533193850Strasz * 1.5.1.4. The mask bits are a subset of the mask bits 534193850Strasz * of the current ACE, and are also subset of 535193850Strasz * the following: ACL_READ_DATA, 536193850Strasz * ACL_WRITE_DATA, ACL_APPEND_DATA, ACL_EXECUTE 537193850Strasz */ 538193850Strasz if (previous->ae_perm & ~(entry->ae_perm)) 539193850Strasz meets = 0; 540193850Strasz 541193850Strasz if (previous->ae_perm & ~(ACL_READ_DATA | 542193850Strasz ACL_WRITE_DATA | ACL_APPEND_DATA | ACL_EXECUTE)) 543193850Strasz meets = 0; 544193850Strasz } 545193850Strasz 546193850Strasz if (!meets) { 547193850Strasz /* 548193850Strasz * Then the ACE of type DENY, with a who equal 549193850Strasz * to the current ACE, flag bits equal to 550193850Strasz * (<current ACE flags> & <ACE_IDENTIFIER_GROUP>) 551193850Strasz * and no mask bits, is prepended. 552193850Strasz */ 553193850Strasz previous = entry; 554193850Strasz entry = _acl_duplicate_entry(aclp, i); 555193850Strasz 556193850Strasz /* Adjust counter, as we've just added an entry. */ 557193850Strasz i++; 558193850Strasz 559193850Strasz previous->ae_tag = entry->ae_tag; 560193850Strasz previous->ae_id = entry->ae_id; 561193850Strasz previous->ae_flags = entry->ae_flags; 562193850Strasz previous->ae_perm = 0; 563193850Strasz previous->ae_entry_type = ACL_ENTRY_TYPE_DENY; 564193850Strasz } 565193850Strasz 566193850Strasz /* 567193850Strasz * 1.5.2. The following modifications are made to the prepended 568193850Strasz * ACE. The intent is to mask the following ACE 569193850Strasz * to disallow ACL_READ_DATA, ACL_WRITE_DATA, 570193850Strasz * ACL_APPEND_DATA, or ACL_EXECUTE, based upon the group 571193850Strasz * permissions of the new mode. As a special case, 572193850Strasz * if the ACE matches the current owner of the file, 573193850Strasz * the owner bits are used, rather than the group bits. 574193850Strasz * This is reflected in the algorithm below. 575193850Strasz */ 576193850Strasz amode = mode >> 3; 577193850Strasz 578193850Strasz /* 579193850Strasz * If ACE4_IDENTIFIER_GROUP is not set, and the "who" field 580193850Strasz * in ACE matches the owner of the file, we shift amode three 581193850Strasz * more bits, in order to have the owner permission bits 582193850Strasz * placed in the three low order bits of amode. 583193850Strasz */ 584193850Strasz if (entry->ae_tag == ACL_USER && entry->ae_id == file_owner_id) 585193850Strasz amode = amode >> 3; 586193850Strasz 587193850Strasz if (entry->ae_perm & ACL_READ_DATA) { 588193850Strasz if (amode & READ) 589193850Strasz previous->ae_perm &= ~ACL_READ_DATA; 590193850Strasz else 591193850Strasz previous->ae_perm |= ACL_READ_DATA; 592193850Strasz } 593193850Strasz 594193850Strasz if (entry->ae_perm & ACL_WRITE_DATA) { 595193850Strasz if (amode & WRITE) 596193850Strasz previous->ae_perm &= ~ACL_WRITE_DATA; 597193850Strasz else 598193850Strasz previous->ae_perm |= ACL_WRITE_DATA; 599193850Strasz } 600193850Strasz 601193850Strasz if (entry->ae_perm & ACL_APPEND_DATA) { 602193850Strasz if (amode & WRITE) 603193850Strasz previous->ae_perm &= ~ACL_APPEND_DATA; 604193850Strasz else 605193850Strasz previous->ae_perm |= ACL_APPEND_DATA; 606193850Strasz } 607193850Strasz 608193850Strasz if (entry->ae_perm & ACL_EXECUTE) { 609193850Strasz if (amode & EXEC) 610193850Strasz previous->ae_perm &= ~ACL_EXECUTE; 611193850Strasz else 612193850Strasz previous->ae_perm |= ACL_EXECUTE; 613193850Strasz } 614193850Strasz 615193850Strasz /* 616193850Strasz * 1.5.3. If ACE4_IDENTIFIER_GROUP is set in the flags 617193850Strasz * of the ALLOW ace: 618193850Strasz * 619193850Strasz * XXX: This point is not there in the Falkner's draft. 620193850Strasz */ 621193850Strasz if (entry->ae_tag == ACL_GROUP && 622193850Strasz entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) { 623193850Strasz mode_t extramode, ownermode; 624193850Strasz extramode = (mode >> 3) & 07; 625193850Strasz ownermode = mode >> 6; 626193850Strasz extramode &= ~ownermode; 627193850Strasz 628193850Strasz if (extramode) { 629193850Strasz if (extramode & READ) { 630193850Strasz entry->ae_perm &= ~ACL_READ_DATA; 631193850Strasz previous->ae_perm &= ~ACL_READ_DATA; 632193850Strasz } 633193850Strasz 634193850Strasz if (extramode & WRITE) { 635193850Strasz entry->ae_perm &= 636193850Strasz ~(ACL_WRITE_DATA | ACL_APPEND_DATA); 637193850Strasz previous->ae_perm &= 638193850Strasz ~(ACL_WRITE_DATA | ACL_APPEND_DATA); 639193850Strasz } 640193850Strasz 641193850Strasz if (extramode & EXEC) { 642193850Strasz entry->ae_perm &= ~ACL_EXECUTE; 643193850Strasz previous->ae_perm &= ~ACL_EXECUTE; 644193850Strasz } 645193850Strasz } 646193850Strasz } 647193850Strasz } 648193850Strasz 649193850Strasz /* 650193850Strasz * 2. If there at least six ACEs, the final six ACEs are examined. 651193850Strasz * If they are not equal to what we want, append six ACEs. 652193850Strasz */ 653193850Strasz must_append = 0; 654193850Strasz if (aclp->acl_cnt < 6) { 655193850Strasz must_append = 1; 656193850Strasz } else { 657193850Strasz a6 = &(aclp->acl_entry[aclp->acl_cnt - 1]); 658193850Strasz a5 = &(aclp->acl_entry[aclp->acl_cnt - 2]); 659193850Strasz a4 = &(aclp->acl_entry[aclp->acl_cnt - 3]); 660193850Strasz a3 = &(aclp->acl_entry[aclp->acl_cnt - 4]); 661193850Strasz a2 = &(aclp->acl_entry[aclp->acl_cnt - 5]); 662193850Strasz a1 = &(aclp->acl_entry[aclp->acl_cnt - 6]); 663193850Strasz 664193850Strasz if (!_acl_entry_matches(a1, ACL_USER_OBJ, 0, 665193850Strasz ACL_ENTRY_TYPE_DENY)) 666193850Strasz must_append = 1; 667193850Strasz if (!_acl_entry_matches(a2, ACL_USER_OBJ, ACL_WRITE_ACL | 668193850Strasz ACL_WRITE_OWNER | ACL_WRITE_ATTRIBUTES | 669193850Strasz ACL_WRITE_NAMED_ATTRS, ACL_ENTRY_TYPE_ALLOW)) 670193850Strasz must_append = 1; 671193850Strasz if (!_acl_entry_matches(a3, ACL_GROUP_OBJ, 0, 672193850Strasz ACL_ENTRY_TYPE_DENY)) 673193850Strasz must_append = 1; 674193850Strasz if (!_acl_entry_matches(a4, ACL_GROUP_OBJ, 0, 675193850Strasz ACL_ENTRY_TYPE_ALLOW)) 676193850Strasz must_append = 1; 677193850Strasz if (!_acl_entry_matches(a5, ACL_EVERYONE, ACL_WRITE_ACL | 678193850Strasz ACL_WRITE_OWNER | ACL_WRITE_ATTRIBUTES | 679193850Strasz ACL_WRITE_NAMED_ATTRS, ACL_ENTRY_TYPE_DENY)) 680193850Strasz must_append = 1; 681193850Strasz if (!_acl_entry_matches(a6, ACL_EVERYONE, ACL_READ_ACL | 682193850Strasz ACL_READ_ATTRIBUTES | ACL_READ_NAMED_ATTRS | 683193850Strasz ACL_SYNCHRONIZE, ACL_ENTRY_TYPE_ALLOW)) 684193850Strasz must_append = 1; 685193850Strasz } 686193850Strasz 687193850Strasz if (must_append) { 688193850Strasz KASSERT(aclp->acl_cnt + 6 <= ACL_MAX_ENTRIES, 689193850Strasz ("aclp->acl_cnt <= ACL_MAX_ENTRIES")); 690193850Strasz 691193850Strasz a1 = _acl_append(aclp, ACL_USER_OBJ, 0, ACL_ENTRY_TYPE_DENY); 692193850Strasz a2 = _acl_append(aclp, ACL_USER_OBJ, ACL_WRITE_ACL | 693193850Strasz ACL_WRITE_OWNER | ACL_WRITE_ATTRIBUTES | 694193850Strasz ACL_WRITE_NAMED_ATTRS, ACL_ENTRY_TYPE_ALLOW); 695193850Strasz a3 = _acl_append(aclp, ACL_GROUP_OBJ, 0, ACL_ENTRY_TYPE_DENY); 696193850Strasz a4 = _acl_append(aclp, ACL_GROUP_OBJ, 0, ACL_ENTRY_TYPE_ALLOW); 697193850Strasz a5 = _acl_append(aclp, ACL_EVERYONE, ACL_WRITE_ACL | 698193850Strasz ACL_WRITE_OWNER | ACL_WRITE_ATTRIBUTES | 699193850Strasz ACL_WRITE_NAMED_ATTRS, ACL_ENTRY_TYPE_DENY); 700193850Strasz a6 = _acl_append(aclp, ACL_EVERYONE, ACL_READ_ACL | 701193850Strasz ACL_READ_ATTRIBUTES | ACL_READ_NAMED_ATTRS | 702193850Strasz ACL_SYNCHRONIZE, ACL_ENTRY_TYPE_ALLOW); 703193850Strasz 704193850Strasz KASSERT(a1 != NULL && a2 != NULL && a3 != NULL && a4 != NULL && 705193850Strasz a5 != NULL && a6 != NULL, ("couldn't append to ACL.")); 706193850Strasz } 707193850Strasz 708193850Strasz /* 709193850Strasz * 3. The final six ACEs are adjusted according to the incoming mode. 710193850Strasz */ 711193850Strasz if (mode & S_IRUSR) 712193850Strasz a2->ae_perm |= ACL_READ_DATA; 713193850Strasz else 714193850Strasz a1->ae_perm |= ACL_READ_DATA; 715193850Strasz if (mode & S_IWUSR) 716193850Strasz a2->ae_perm |= (ACL_WRITE_DATA | ACL_APPEND_DATA); 717193850Strasz else 718193850Strasz a1->ae_perm |= (ACL_WRITE_DATA | ACL_APPEND_DATA); 719193850Strasz if (mode & S_IXUSR) 720193850Strasz a2->ae_perm |= ACL_EXECUTE; 721193850Strasz else 722193850Strasz a1->ae_perm |= ACL_EXECUTE; 723193850Strasz 724193850Strasz if (mode & S_IRGRP) 725193850Strasz a4->ae_perm |= ACL_READ_DATA; 726193850Strasz else 727193850Strasz a3->ae_perm |= ACL_READ_DATA; 728193850Strasz if (mode & S_IWGRP) 729193850Strasz a4->ae_perm |= (ACL_WRITE_DATA | ACL_APPEND_DATA); 730193850Strasz else 731193850Strasz a3->ae_perm |= (ACL_WRITE_DATA | ACL_APPEND_DATA); 732193850Strasz if (mode & S_IXGRP) 733193850Strasz a4->ae_perm |= ACL_EXECUTE; 734193850Strasz else 735193850Strasz a3->ae_perm |= ACL_EXECUTE; 736193850Strasz 737193850Strasz if (mode & S_IROTH) 738193850Strasz a6->ae_perm |= ACL_READ_DATA; 739193850Strasz else 740193850Strasz a5->ae_perm |= ACL_READ_DATA; 741193850Strasz if (mode & S_IWOTH) 742193850Strasz a6->ae_perm |= (ACL_WRITE_DATA | ACL_APPEND_DATA); 743193850Strasz else 744193850Strasz a5->ae_perm |= (ACL_WRITE_DATA | ACL_APPEND_DATA); 745193850Strasz if (mode & S_IXOTH) 746193850Strasz a6->ae_perm |= ACL_EXECUTE; 747193850Strasz else 748193850Strasz a5->ae_perm |= ACL_EXECUTE; 749193850Strasz} 750193850Strasz 751193850Straszvoid 752193850Straszacl_nfs4_sync_mode_from_acl(mode_t *_mode, const struct acl *aclp) 753193850Strasz{ 754193850Strasz int i; 755193850Strasz mode_t old_mode = *_mode, mode = 0, seen = 0; 756193850Strasz const struct acl_entry *entry; 757193850Strasz 758193850Strasz KASSERT(aclp->acl_cnt > 0, ("aclp->acl_cnt > 0")); 759193850Strasz KASSERT(aclp->acl_cnt <= ACL_MAX_ENTRIES, 760193850Strasz ("aclp->acl_cnt <= ACL_MAX_ENTRIES")); 761193850Strasz 762193850Strasz /* 763193850Strasz * NFSv4 Minor Version 1, draft-ietf-nfsv4-minorversion1-03.txt 764193850Strasz * 765193850Strasz * 3.16.6.1. Recomputing mode upon SETATTR of ACL 766193850Strasz */ 767193850Strasz 768193850Strasz for (i = 0; i < aclp->acl_cnt; i++) { 769193850Strasz entry = &(aclp->acl_entry[i]); 770193850Strasz 771193850Strasz if (entry->ae_entry_type != ACL_ENTRY_TYPE_ALLOW && 772193850Strasz entry->ae_entry_type != ACL_ENTRY_TYPE_DENY) 773193850Strasz continue; 774193850Strasz 775193850Strasz if (entry->ae_flags & ACL_ENTRY_INHERIT_ONLY) 776193850Strasz continue; 777193850Strasz 778193850Strasz if (entry->ae_tag == ACL_USER_OBJ) { 779193850Strasz if ((entry->ae_perm & ACL_READ_DATA) && 780193850Strasz ((seen & S_IRUSR) == 0)) { 781193850Strasz seen |= S_IRUSR; 782193850Strasz if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 783193850Strasz mode |= S_IRUSR; 784193850Strasz } 785193850Strasz if ((entry->ae_perm & ACL_WRITE_DATA) && 786193850Strasz ((seen & S_IWUSR) == 0)) { 787193850Strasz seen |= S_IWUSR; 788193850Strasz if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 789193850Strasz mode |= S_IWUSR; 790193850Strasz } 791193850Strasz if ((entry->ae_perm & ACL_EXECUTE) && 792193850Strasz ((seen & S_IXUSR) == 0)) { 793193850Strasz seen |= S_IXUSR; 794193850Strasz if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 795193850Strasz mode |= S_IXUSR; 796193850Strasz } 797193850Strasz } else if (entry->ae_tag == ACL_GROUP_OBJ) { 798193850Strasz if ((entry->ae_perm & ACL_READ_DATA) && 799193850Strasz ((seen & S_IRGRP) == 0)) { 800193850Strasz seen |= S_IRGRP; 801193850Strasz if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 802193850Strasz mode |= S_IRGRP; 803193850Strasz } 804193850Strasz if ((entry->ae_perm & ACL_WRITE_DATA) && 805193850Strasz ((seen & S_IWGRP) == 0)) { 806193850Strasz seen |= S_IWGRP; 807193850Strasz if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 808193850Strasz mode |= S_IWGRP; 809193850Strasz } 810193850Strasz if ((entry->ae_perm & ACL_EXECUTE) && 811193850Strasz ((seen & S_IXGRP) == 0)) { 812193850Strasz seen |= S_IXGRP; 813193850Strasz if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 814193850Strasz mode |= S_IXGRP; 815193850Strasz } 816193850Strasz } else if (entry->ae_tag == ACL_EVERYONE) { 817193850Strasz if (entry->ae_perm & ACL_READ_DATA) { 818193850Strasz if ((seen & S_IRUSR) == 0) { 819193850Strasz seen |= S_IRUSR; 820193850Strasz if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 821193850Strasz mode |= S_IRUSR; 822193850Strasz } 823193850Strasz if ((seen & S_IRGRP) == 0) { 824193850Strasz seen |= S_IRGRP; 825193850Strasz if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 826193850Strasz mode |= S_IRGRP; 827193850Strasz } 828193850Strasz if ((seen & S_IROTH) == 0) { 829193850Strasz seen |= S_IROTH; 830193850Strasz if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 831193850Strasz mode |= S_IROTH; 832193850Strasz } 833193850Strasz } 834193850Strasz if (entry->ae_perm & ACL_WRITE_DATA) { 835193850Strasz if ((seen & S_IWUSR) == 0) { 836193850Strasz seen |= S_IWUSR; 837193850Strasz if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 838193850Strasz mode |= S_IWUSR; 839193850Strasz } 840193850Strasz if ((seen & S_IWGRP) == 0) { 841193850Strasz seen |= S_IWGRP; 842193850Strasz if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 843193850Strasz mode |= S_IWGRP; 844193850Strasz } 845193850Strasz if ((seen & S_IWOTH) == 0) { 846193850Strasz seen |= S_IWOTH; 847193850Strasz if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 848193850Strasz mode |= S_IWOTH; 849193850Strasz } 850193850Strasz } 851193850Strasz if (entry->ae_perm & ACL_EXECUTE) { 852193850Strasz if ((seen & S_IXUSR) == 0) { 853193850Strasz seen |= S_IXUSR; 854193850Strasz if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 855193850Strasz mode |= S_IXUSR; 856193850Strasz } 857193850Strasz if ((seen & S_IXGRP) == 0) { 858193850Strasz seen |= S_IXGRP; 859193850Strasz if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 860193850Strasz mode |= S_IXGRP; 861193850Strasz } 862193850Strasz if ((seen & S_IXOTH) == 0) { 863193850Strasz seen |= S_IXOTH; 864193850Strasz if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 865193850Strasz mode |= S_IXOTH; 866193850Strasz } 867193850Strasz } 868193850Strasz } 869193850Strasz } 870193850Strasz 871193850Strasz *_mode = mode | (old_mode & ACL_PRESERVE_MASK); 872193850Strasz} 873197405Strasz 874197405Straszvoid 875197405Straszacl_nfs4_compute_inherited_acl(const struct acl *parent_aclp, 876197405Strasz struct acl *child_aclp, mode_t mode, int file_owner_id, 877197405Strasz int is_directory) 878197405Strasz{ 879197405Strasz int i, flags; 880197405Strasz const struct acl_entry *parent_entry; 881197405Strasz struct acl_entry *entry, *copy; 882197405Strasz 883197405Strasz KASSERT(child_aclp->acl_cnt == 0, ("child_aclp->acl_cnt == 0")); 884197405Strasz KASSERT(parent_aclp->acl_cnt > 0, ("parent_aclp->acl_cnt > 0")); 885197405Strasz KASSERT(parent_aclp->acl_cnt <= ACL_MAX_ENTRIES, 886197405Strasz ("parent_aclp->acl_cnt <= ACL_MAX_ENTRIES")); 887197405Strasz 888197405Strasz /* 889197405Strasz * NFSv4 Minor Version 1, draft-ietf-nfsv4-minorversion1-03.txt 890197405Strasz * 891197405Strasz * 3.16.6.2. Applying the mode given to CREATE or OPEN 892197405Strasz * to an inherited ACL 893197405Strasz */ 894197405Strasz 895197405Strasz /* 896197405Strasz * 1. Form an ACL that is the concatenation of all inheritable ACEs. 897197405Strasz */ 898197405Strasz for (i = 0; i < parent_aclp->acl_cnt; i++) { 899197405Strasz parent_entry = &(parent_aclp->acl_entry[i]); 900197405Strasz flags = parent_entry->ae_flags; 901197405Strasz 902197405Strasz /* 903197405Strasz * Entry is not inheritable at all. 904197405Strasz */ 905197405Strasz if ((flags & (ACL_ENTRY_DIRECTORY_INHERIT | 906197405Strasz ACL_ENTRY_FILE_INHERIT)) == 0) 907197405Strasz continue; 908197405Strasz 909197405Strasz /* 910197405Strasz * We're creating a file, but entry is not inheritable 911197405Strasz * by files. 912197405Strasz */ 913197405Strasz if (!is_directory && (flags & ACL_ENTRY_FILE_INHERIT) == 0) 914197405Strasz continue; 915197405Strasz 916197405Strasz /* 917197405Strasz * Entry is inheritable only by files, but has NO_PROPAGATE 918197405Strasz * flag set, and we're creating a directory, so it wouldn't 919197405Strasz * propagate to any file in that directory anyway. 920197405Strasz */ 921197405Strasz if (is_directory && 922197405Strasz (flags & ACL_ENTRY_DIRECTORY_INHERIT) == 0 && 923197405Strasz (flags & ACL_ENTRY_NO_PROPAGATE_INHERIT)) 924197405Strasz continue; 925197405Strasz 926197405Strasz KASSERT(child_aclp->acl_cnt + 1 <= ACL_MAX_ENTRIES, 927197405Strasz ("child_aclp->acl_cnt + 1 <= ACL_MAX_ENTRIES")); 928197405Strasz child_aclp->acl_entry[child_aclp->acl_cnt] = *parent_entry; 929197405Strasz child_aclp->acl_cnt++; 930197405Strasz } 931197405Strasz 932197405Strasz /* 933197405Strasz * 2. For each entry in the new ACL, adjust its flags, possibly 934197405Strasz * creating two entries in place of one. 935197405Strasz */ 936197405Strasz for (i = 0; i < child_aclp->acl_cnt; i++) { 937197405Strasz entry = &(child_aclp->acl_entry[i]); 938197405Strasz 939197405Strasz /* 940197405Strasz * This is not in the specification, but SunOS 941197405Strasz * apparently does that. 942197405Strasz */ 943197405Strasz if (((entry->ae_flags & ACL_ENTRY_NO_PROPAGATE_INHERIT) || 944197405Strasz !is_directory) && 945197405Strasz entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 946197405Strasz entry->ae_perm &= ~(ACL_WRITE_ACL | ACL_WRITE_OWNER); 947197405Strasz 948197405Strasz /* 949197405Strasz * 2.A. If the ACL_ENTRY_NO_PROPAGATE_INHERIT is set, or if the object 950197405Strasz * being created is not a directory, then clear the 951197405Strasz * following flags: ACL_ENTRY_NO_PROPAGATE_INHERIT, 952197405Strasz * ACL_ENTRY_FILE_INHERIT, ACL_ENTRY_DIRECTORY_INHERIT, 953197405Strasz * ACL_ENTRY_INHERIT_ONLY. 954197405Strasz */ 955197405Strasz if (entry->ae_flags & ACL_ENTRY_NO_PROPAGATE_INHERIT || 956197405Strasz !is_directory) { 957197405Strasz entry->ae_flags &= ~(ACL_ENTRY_NO_PROPAGATE_INHERIT | 958197405Strasz ACL_ENTRY_FILE_INHERIT | ACL_ENTRY_DIRECTORY_INHERIT | 959197405Strasz ACL_ENTRY_INHERIT_ONLY); 960197405Strasz 961197405Strasz /* 962197405Strasz * Continue on to the next ACE. 963197405Strasz */ 964197405Strasz continue; 965197405Strasz } 966197405Strasz 967197405Strasz /* 968197405Strasz * 2.B. If the object is a directory and ACL_ENTRY_FILE_INHERIT 969197405Strasz * is set, but ACL_ENTRY_NO_PROPAGATE_INHERIT is not set, ensure 970197405Strasz * that ACL_ENTRY_INHERIT_ONLY is set. Continue to the 971197405Strasz * next ACE. Otherwise... 972197405Strasz */ 973197405Strasz /* 974197405Strasz * XXX: Read it again and make sure what does the "otherwise" 975197405Strasz * apply to. 976197405Strasz */ 977197405Strasz if (is_directory && 978197405Strasz (entry->ae_flags & ACL_ENTRY_FILE_INHERIT) && 979197405Strasz ((entry->ae_flags & ACL_ENTRY_DIRECTORY_INHERIT) == 0)) { 980197405Strasz entry->ae_flags |= ACL_ENTRY_INHERIT_ONLY; 981197405Strasz continue; 982197405Strasz } 983197405Strasz 984197405Strasz /* 985197405Strasz * 2.C. If the type of the ACE is neither ALLOW nor deny, 986197405Strasz * then continue. 987197405Strasz */ 988197405Strasz if (entry->ae_entry_type != ACL_ENTRY_TYPE_ALLOW && 989197405Strasz entry->ae_entry_type != ACL_ENTRY_TYPE_DENY) 990197405Strasz continue; 991197405Strasz 992197405Strasz /* 993197405Strasz * 2.D. Copy the original ACE into a second, adjacent ACE. 994197405Strasz */ 995197405Strasz copy = _acl_duplicate_entry(child_aclp, i); 996197405Strasz 997197405Strasz /* 998197405Strasz * 2.E. On the first ACE, ensure that ACL_ENTRY_INHERIT_ONLY 999197405Strasz * is set. 1000197405Strasz */ 1001197405Strasz entry->ae_flags |= ACL_ENTRY_INHERIT_ONLY; 1002197405Strasz 1003197405Strasz /* 1004197405Strasz * 2.F. On the second ACE, clear the following flags: 1005197405Strasz * ACL_ENTRY_NO_PROPAGATE_INHERIT, ACL_ENTRY_FILE_INHERIT, 1006197405Strasz * ACL_ENTRY_DIRECTORY_INHERIT, ACL_ENTRY_INHERIT_ONLY. 1007197405Strasz */ 1008197405Strasz copy->ae_flags &= ~(ACL_ENTRY_NO_PROPAGATE_INHERIT | 1009197405Strasz ACL_ENTRY_FILE_INHERIT | ACL_ENTRY_DIRECTORY_INHERIT | 1010197405Strasz ACL_ENTRY_INHERIT_ONLY); 1011197405Strasz 1012197405Strasz /* 1013197405Strasz * 2.G. On the second ACE, if the type is ALLOW, 1014197405Strasz * an implementation MAY clear the following 1015197405Strasz * mask bits: ACL_WRITE_ACL, ACL_WRITE_OWNER. 1016197405Strasz */ 1017197405Strasz if (copy->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 1018197405Strasz copy->ae_perm &= ~(ACL_WRITE_ACL | ACL_WRITE_OWNER); 1019197405Strasz 1020197405Strasz /* 1021197405Strasz * Increment the counter to skip the copied entry. 1022197405Strasz */ 1023197405Strasz i++; 1024197405Strasz } 1025197405Strasz 1026197405Strasz /* 1027197405Strasz * 3. To ensure that the mode is honored, apply the algorithm describe 1028197405Strasz * in Section 2.16.6.3, using the mode that is to be used for file 1029197405Strasz * creation. 1030197405Strasz */ 1031197405Strasz acl_nfs4_sync_acl_from_mode(child_aclp, mode, file_owner_id); 1032197405Strasz} 1033197405Strasz 1034197405Strasz#ifdef _KERNEL 1035197405Straszstatic int 1036197405Strasz_acls_are_equal(const struct acl *a, const struct acl *b) 1037197405Strasz{ 1038197405Strasz int i; 1039197405Strasz const struct acl_entry *entrya, *entryb; 1040197405Strasz 1041197405Strasz if (a->acl_cnt != b->acl_cnt) 1042197405Strasz return (0); 1043197405Strasz 1044197405Strasz for (i = 0; i < b->acl_cnt; i++) { 1045197405Strasz entrya = &(a->acl_entry[i]); 1046197405Strasz entryb = &(b->acl_entry[i]); 1047197405Strasz 1048197405Strasz if (entrya->ae_tag != entryb->ae_tag || 1049197405Strasz entrya->ae_id != entryb->ae_id || 1050197405Strasz entrya->ae_perm != entryb->ae_perm || 1051197405Strasz entrya->ae_entry_type != entryb->ae_entry_type || 1052197405Strasz entrya->ae_flags != entryb->ae_flags) 1053197405Strasz return (0); 1054197405Strasz } 1055197405Strasz 1056197405Strasz return (1); 1057197405Strasz} 1058197405Strasz 1059197405Strasz/* 1060201495Strasz * This routine is used to determine whether to remove extended attribute 1061197405Strasz * that stores ACL contents. 1062197405Strasz */ 1063197405Straszint 1064197405Straszacl_nfs4_is_trivial(const struct acl *aclp, int file_owner_id) 1065197405Strasz{ 1066197405Strasz int trivial; 1067197405Strasz mode_t tmpmode = 0; 1068197405Strasz struct acl *tmpaclp; 1069197405Strasz 1070197405Strasz if (aclp->acl_cnt != 6) 1071197405Strasz return (0); 1072197405Strasz 1073197405Strasz /* 1074197405Strasz * Compute the mode from the ACL, then compute new ACL from that mode. 1075197405Strasz * If the ACLs are identical, then the ACL is trivial. 1076197405Strasz * 1077197405Strasz * XXX: I guess there is a faster way to do this. However, even 1078197405Strasz * this slow implementation significantly speeds things up 1079201495Strasz * for files that don't have non-trivial ACLs - it's critical 1080201495Strasz * for performance to not use EA when they are not needed. 1081197405Strasz */ 1082197405Strasz tmpaclp = acl_alloc(M_WAITOK | M_ZERO); 1083197405Strasz acl_nfs4_sync_mode_from_acl(&tmpmode, aclp); 1084197405Strasz acl_nfs4_sync_acl_from_mode(tmpaclp, tmpmode, file_owner_id); 1085197405Strasz trivial = _acls_are_equal(aclp, tmpaclp); 1086197405Strasz acl_free(tmpaclp); 1087197405Strasz 1088197405Strasz return (trivial); 1089197405Strasz} 1090197405Strasz#endif /* _KERNEL */ 1091197405Strasz 1092197405Straszint 1093197405Straszacl_nfs4_check(const struct acl *aclp, int is_directory) 1094197405Strasz{ 1095197405Strasz int i; 1096197405Strasz const struct acl_entry *entry; 1097197405Strasz 1098197405Strasz /* 1099197405Strasz * The spec doesn't seem to say anything about ACL validity. 1100197405Strasz * It seems there is not much to do here. There is even no need 1101197405Strasz * to count "owner@" or "everyone@" (ACL_USER_OBJ and ACL_EVERYONE) 1102197405Strasz * entries, as there can be several of them and that's perfectly 1103197405Strasz * valid. There can be none of them too. Really. 1104197405Strasz */ 1105197405Strasz 1106197405Strasz if (aclp->acl_cnt > ACL_MAX_ENTRIES || aclp->acl_cnt <= 0) 1107197405Strasz return (EINVAL); 1108197405Strasz 1109197405Strasz for (i = 0; i < aclp->acl_cnt; i++) { 1110197405Strasz entry = &(aclp->acl_entry[i]); 1111197405Strasz 1112197405Strasz switch (entry->ae_tag) { 1113197405Strasz case ACL_USER_OBJ: 1114197405Strasz case ACL_GROUP_OBJ: 1115197405Strasz case ACL_EVERYONE: 1116197405Strasz if (entry->ae_id != ACL_UNDEFINED_ID) 1117197405Strasz return (EINVAL); 1118197405Strasz break; 1119197405Strasz 1120197405Strasz case ACL_USER: 1121197405Strasz case ACL_GROUP: 1122197405Strasz if (entry->ae_id == ACL_UNDEFINED_ID) 1123197405Strasz return (EINVAL); 1124197405Strasz break; 1125197405Strasz 1126197405Strasz default: 1127197405Strasz return (EINVAL); 1128197405Strasz } 1129197405Strasz 1130197405Strasz if ((entry->ae_perm | ACL_NFS4_PERM_BITS) != ACL_NFS4_PERM_BITS) 1131197405Strasz return (EINVAL); 1132197405Strasz 1133197405Strasz /* 1134197405Strasz * Disallow ACL_ENTRY_TYPE_AUDIT and ACL_ENTRY_TYPE_ALARM for now. 1135197405Strasz */ 1136197405Strasz if (entry->ae_entry_type != ACL_ENTRY_TYPE_ALLOW && 1137197405Strasz entry->ae_entry_type != ACL_ENTRY_TYPE_DENY) 1138197405Strasz return (EINVAL); 1139197405Strasz 1140197405Strasz if ((entry->ae_flags | ACL_FLAGS_BITS) != ACL_FLAGS_BITS) 1141197405Strasz return (EINVAL); 1142197405Strasz 1143197405Strasz /* Disallow unimplemented flags. */ 1144197405Strasz if (entry->ae_flags & (ACL_ENTRY_SUCCESSFUL_ACCESS | 1145197405Strasz ACL_ENTRY_FAILED_ACCESS)) 1146197405Strasz return (EINVAL); 1147197405Strasz 1148197405Strasz /* Disallow flags not allowed for ordinary files. */ 1149197405Strasz if (!is_directory) { 1150197405Strasz if (entry->ae_flags & (ACL_ENTRY_FILE_INHERIT | 1151197405Strasz ACL_ENTRY_DIRECTORY_INHERIT | 1152197405Strasz ACL_ENTRY_NO_PROPAGATE_INHERIT | ACL_ENTRY_INHERIT_ONLY)) 1153197405Strasz return (EINVAL); 1154197405Strasz } 1155197405Strasz } 1156197405Strasz 1157197405Strasz return (0); 1158197405Strasz} 1159