subr_acl_nfs4.c revision 214522
1250125Sjkim/*- 2228072Sbapt * Copyright (c) 2008-2009 Edward Tomasz Napiera��a <trasz@FreeBSD.org> 3250125Sjkim * All rights reserved. 4250125Sjkim * 5250125Sjkim * Redistribution and use in source and binary forms, with or without 6250125Sjkim * modification, are permitted provided that the following conditions 7250125Sjkim * are met: 8250125Sjkim * 1. Redistributions of source code must retain the above copyright 9250125Sjkim * notice, this list of conditions and the following disclaimer. 10250125Sjkim * 2. Redistributions in binary form must reproduce the above copyright 11250125Sjkim * notice, this list of conditions and the following disclaimer in the 12250125Sjkim * documentation and/or other materials provided with the distribution. 13250125Sjkim * 14250125Sjkim * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15250125Sjkim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16250125Sjkim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17250125Sjkim * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18250125Sjkim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19250125Sjkim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20250125Sjkim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21250125Sjkim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22250125Sjkim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23250125Sjkim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24250125Sjkim * SUCH DAMAGE. 25250125Sjkim */ 26250125Sjkim 27250125Sjkim/* 28250125Sjkim * ACL support routines specific to NFSv4 access control lists. These are 29250125Sjkim * utility routines for code common across file systems implementing NFSv4 30250125Sjkim * ACLs. 31250125Sjkim */ 32250125Sjkim 33250125Sjkim#ifdef _KERNEL 34250125Sjkim#include <sys/cdefs.h> 35250125Sjkim__FBSDID("$FreeBSD: head/sys/kern/subr_acl_nfs4.c 214522 2010-10-29 19:07:36Z trasz $"); 36250125Sjkim 37250125Sjkim#include <sys/param.h> 38250125Sjkim#include <sys/systm.h> 39250125Sjkim#include <sys/mount.h> 40250125Sjkim#include <sys/priv.h> 41250125Sjkim#include <sys/vnode.h> 42250125Sjkim#include <sys/errno.h> 43250125Sjkim#include <sys/stat.h> 44250125Sjkim#include <sys/acl.h> 45250125Sjkim#else 46250125Sjkim#include <errno.h> 47250125Sjkim#include <assert.h> 48250125Sjkim#include <sys/acl.h> 49250125Sjkim#include <sys/stat.h> 50250125Sjkim#define KASSERT(a, b) assert(a) 51250125Sjkim#define CTASSERT(a) 52250125Sjkim#endif /* _KERNEL */ 53250125Sjkim 54250125Sjkim#ifdef _KERNEL 55250125Sjkim 56250125Sjkimstatic struct { 57250125Sjkim accmode_t accmode; 58250125Sjkim int mask; 59250125Sjkim} accmode2mask[] = {{VREAD, ACL_READ_DATA}, 60250125Sjkim {VWRITE, ACL_WRITE_DATA}, 61250125Sjkim {VAPPEND, ACL_APPEND_DATA}, 62250125Sjkim {VEXEC, ACL_EXECUTE}, 63250125Sjkim {VREAD_NAMED_ATTRS, ACL_READ_NAMED_ATTRS}, 64250125Sjkim {VWRITE_NAMED_ATTRS, ACL_WRITE_NAMED_ATTRS}, 65250125Sjkim {VDELETE_CHILD, ACL_DELETE_CHILD}, 66250125Sjkim {VREAD_ATTRIBUTES, ACL_READ_ATTRIBUTES}, 67250125Sjkim {VWRITE_ATTRIBUTES, ACL_WRITE_ATTRIBUTES}, 68250125Sjkim {VDELETE, ACL_DELETE}, 69250125Sjkim {VREAD_ACL, ACL_READ_ACL}, 70250125Sjkim {VWRITE_ACL, ACL_WRITE_ACL}, 71250125Sjkim {VWRITE_OWNER, ACL_WRITE_OWNER}, 72250125Sjkim {VSYNCHRONIZE, ACL_SYNCHRONIZE}, 73250125Sjkim {0, 0}}; 74250125Sjkim 75250125Sjkimstatic int 76250125Sjkim_access_mask_from_accmode(accmode_t accmode) 77250125Sjkim{ 78250125Sjkim int access_mask = 0, i; 79250125Sjkim 80250125Sjkim for (i = 0; accmode2mask[i].accmode != 0; i++) { 81250125Sjkim if (accmode & accmode2mask[i].accmode) 82250125Sjkim access_mask |= accmode2mask[i].mask; 83250125Sjkim } 84250125Sjkim 85250125Sjkim /* 86250125Sjkim * VAPPEND is just a modifier for VWRITE; if the caller asked 87250125Sjkim * for 'VAPPEND | VWRITE', we want to check for ACL_APPEND_DATA only. 88250125Sjkim */ 89250125Sjkim if (access_mask & ACL_APPEND_DATA) 90250125Sjkim access_mask &= ~ACL_WRITE_DATA; 91250125Sjkim 92250125Sjkim return (access_mask); 93250125Sjkim} 94250125Sjkim 95250125Sjkim/* 96250125Sjkim * Return 0, iff access is allowed, 1 otherwise. 97250125Sjkim */ 98250125Sjkimstatic int 99250125Sjkim_acl_denies(const struct acl *aclp, int access_mask, struct ucred *cred, 100250125Sjkim int file_uid, int file_gid, int *denied_explicitly) 101250125Sjkim{ 102250125Sjkim int i; 103250125Sjkim const struct acl_entry *entry; 104250125Sjkim 105250125Sjkim if (denied_explicitly != NULL) 106250125Sjkim *denied_explicitly = 0; 107250125Sjkim 108250125Sjkim KASSERT(aclp->acl_cnt > 0, ("aclp->acl_cnt > 0")); 109250125Sjkim KASSERT(aclp->acl_cnt <= ACL_MAX_ENTRIES, 110250125Sjkim ("aclp->acl_cnt <= ACL_MAX_ENTRIES")); 111250125Sjkim 112250125Sjkim for (i = 0; i < aclp->acl_cnt; i++) { 113250125Sjkim entry = &(aclp->acl_entry[i]); 114250125Sjkim 115250125Sjkim if (entry->ae_entry_type != ACL_ENTRY_TYPE_ALLOW && 116250125Sjkim entry->ae_entry_type != ACL_ENTRY_TYPE_DENY) 117250125Sjkim continue; 118250125Sjkim if (entry->ae_flags & ACL_ENTRY_INHERIT_ONLY) 119250125Sjkim continue; 120250125Sjkim switch (entry->ae_tag) { 121250125Sjkim case ACL_USER_OBJ: 122250125Sjkim if (file_uid != cred->cr_uid) 123250125Sjkim continue; 124250125Sjkim break; 125250125Sjkim case ACL_USER: 126250125Sjkim if (entry->ae_id != cred->cr_uid) 127250125Sjkim continue; 128250125Sjkim break; 129250125Sjkim case ACL_GROUP_OBJ: 130250125Sjkim if (!groupmember(file_gid, cred)) 131250125Sjkim continue; 132250125Sjkim break; 133250125Sjkim case ACL_GROUP: 134250125Sjkim if (!groupmember(entry->ae_id, cred)) 135250125Sjkim continue; 136250125Sjkim break; 137250125Sjkim default: 138250125Sjkim KASSERT(entry->ae_tag == ACL_EVERYONE, 139250125Sjkim ("entry->ae_tag == ACL_EVERYONE")); 140250125Sjkim } 141250125Sjkim 142250125Sjkim if (entry->ae_entry_type == ACL_ENTRY_TYPE_DENY) { 143250125Sjkim if (entry->ae_perm & access_mask) { 144250125Sjkim if (denied_explicitly != NULL) 145250125Sjkim *denied_explicitly = 1; 146250125Sjkim return (1); 147250125Sjkim } 148250125Sjkim } 149250125Sjkim 150250125Sjkim access_mask &= ~(entry->ae_perm); 151250125Sjkim if (access_mask == 0) 152250125Sjkim return (0); 153250125Sjkim } 154250125Sjkim 155250125Sjkim return (1); 156250125Sjkim} 157250125Sjkim 158250125Sjkimint 159250125Sjkimvaccess_acl_nfs4(enum vtype type, uid_t file_uid, gid_t file_gid, 160250125Sjkim struct acl *aclp, accmode_t accmode, struct ucred *cred, int *privused) 161250125Sjkim{ 162250125Sjkim accmode_t priv_granted = 0; 163250125Sjkim int denied, explicitly_denied, access_mask, is_directory, 164250125Sjkim must_be_owner = 0; 165250125Sjkim mode_t file_mode = 0; 166250125Sjkim 167250125Sjkim KASSERT((accmode & ~(VEXEC | VWRITE | VREAD | VADMIN | VAPPEND | 168250125Sjkim VEXPLICIT_DENY | VREAD_NAMED_ATTRS | VWRITE_NAMED_ATTRS | 169250125Sjkim VDELETE_CHILD | VREAD_ATTRIBUTES | VWRITE_ATTRIBUTES | VDELETE | 170250125Sjkim VREAD_ACL | VWRITE_ACL | VWRITE_OWNER | VSYNCHRONIZE)) == 0, 171250125Sjkim ("invalid bit in accmode")); 172250125Sjkim KASSERT((accmode & VAPPEND) == 0 || (accmode & VWRITE), 173250125Sjkim ("VAPPEND without VWRITE")); 174250125Sjkim 175250125Sjkim if (privused != NULL) 176250125Sjkim *privused = 0; 177250125Sjkim 178250125Sjkim if (accmode & VADMIN) 179250125Sjkim must_be_owner = 1; 180250125Sjkim 181250125Sjkim /* 182250125Sjkim * Ignore VSYNCHRONIZE permission. 183250125Sjkim */ 184250125Sjkim accmode &= ~VSYNCHRONIZE; 185250125Sjkim 186250125Sjkim access_mask = _access_mask_from_accmode(accmode); 187250125Sjkim 188250125Sjkim if (type == VDIR) 189250125Sjkim is_directory = 1; 190250125Sjkim else 191250125Sjkim is_directory = 0; 192250125Sjkim 193250125Sjkim /* 194250125Sjkim * File owner is always allowed to read and write the ACL 195250125Sjkim * and basic attributes. This is to prevent a situation 196250125Sjkim * where user would change ACL in a way that prevents him 197250125Sjkim * from undoing the change. 198250125Sjkim */ 199250125Sjkim if (file_uid == cred->cr_uid) 200250125Sjkim access_mask &= ~(ACL_READ_ACL | ACL_WRITE_ACL | 201250125Sjkim ACL_READ_ATTRIBUTES | ACL_WRITE_ATTRIBUTES); 202250125Sjkim 203250125Sjkim /* 204250125Sjkim * Ignore append permission for regular files; use write 205250125Sjkim * permission instead. 206250125Sjkim */ 207250125Sjkim if (!is_directory && (access_mask & ACL_APPEND_DATA)) { 208250125Sjkim access_mask &= ~ACL_APPEND_DATA; 209250125Sjkim access_mask |= ACL_WRITE_DATA; 210250125Sjkim } 211250125Sjkim 212250125Sjkim denied = _acl_denies(aclp, access_mask, cred, file_uid, file_gid, 213250125Sjkim &explicitly_denied); 214250125Sjkim 215250125Sjkim if (must_be_owner) { 216250125Sjkim if (file_uid != cred->cr_uid) 217250125Sjkim denied = EPERM; 218250125Sjkim } 219250125Sjkim 220250125Sjkim /* 221250125Sjkim * For VEXEC, ensure that at least one execute bit is set for 222250125Sjkim * non-directories. We have to check the mode here to stay 223250125Sjkim * consistent with execve(2). See the test in 224250125Sjkim * exec_check_permissions(). 225250125Sjkim */ 226250125Sjkim acl_nfs4_sync_mode_from_acl(&file_mode, aclp); 227250125Sjkim if (!denied && !is_directory && (accmode & VEXEC) && 228250125Sjkim (file_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) == 0) 229250125Sjkim denied = EACCES; 230250125Sjkim 231250125Sjkim if (!denied) 232250125Sjkim return (0); 233250125Sjkim 234250125Sjkim /* 235250125Sjkim * Access failed. Iff it was not denied explicitly and 236250125Sjkim * VEXPLICIT_DENY flag was specified, allow access. 237250125Sjkim */ 238250125Sjkim if ((accmode & VEXPLICIT_DENY) && explicitly_denied == 0) 239250125Sjkim return (0); 240250125Sjkim 241250125Sjkim accmode &= ~VEXPLICIT_DENY; 242250125Sjkim 243250125Sjkim /* 244250125Sjkim * No match. Try to use privileges, if there are any. 245250125Sjkim */ 246250125Sjkim if (is_directory) { 247250125Sjkim if ((accmode & VEXEC) && !priv_check_cred(cred, 248250125Sjkim PRIV_VFS_LOOKUP, 0)) 249250125Sjkim priv_granted |= VEXEC; 250250125Sjkim } else { 251250125Sjkim /* 252250125Sjkim * Ensure that at least one execute bit is on. Otherwise, 253250125Sjkim * a privileged user will always succeed, and we don't want 254250125Sjkim * this to happen unless the file really is executable. 255250125Sjkim */ 256250125Sjkim if ((accmode & VEXEC) && (file_mode & 257250125Sjkim (S_IXUSR | S_IXGRP | S_IXOTH)) != 0 && 258250125Sjkim !priv_check_cred(cred, PRIV_VFS_EXEC, 0)) 259250125Sjkim priv_granted |= VEXEC; 260250125Sjkim } 261250125Sjkim 262250125Sjkim if ((accmode & VREAD) && !priv_check_cred(cred, PRIV_VFS_READ, 0)) 263250125Sjkim priv_granted |= VREAD; 264250125Sjkim 265250125Sjkim if ((accmode & (VWRITE | VAPPEND | VDELETE_CHILD)) && 266250125Sjkim !priv_check_cred(cred, PRIV_VFS_WRITE, 0)) 267250125Sjkim priv_granted |= (VWRITE | VAPPEND | VDELETE_CHILD); 268250125Sjkim 269250125Sjkim if ((accmode & VADMIN_PERMS) && 270250125Sjkim !priv_check_cred(cred, PRIV_VFS_ADMIN, 0)) 271250125Sjkim priv_granted |= VADMIN_PERMS; 272250125Sjkim 273250125Sjkim if ((accmode & VSTAT_PERMS) && 274250125Sjkim !priv_check_cred(cred, PRIV_VFS_STAT, 0)) 275250125Sjkim priv_granted |= VSTAT_PERMS; 276250125Sjkim 277250125Sjkim if ((accmode & priv_granted) == accmode) { 278250125Sjkim if (privused != NULL) 279250125Sjkim *privused = 1; 280250125Sjkim 281250125Sjkim return (0); 282250125Sjkim } 283250125Sjkim 284250125Sjkim if (accmode & (VADMIN_PERMS | VDELETE_CHILD | VDELETE)) 285250125Sjkim denied = EPERM; 286250125Sjkim else 287250125Sjkim denied = EACCES; 288250125Sjkim 289250125Sjkim return (denied); 290250125Sjkim} 291250125Sjkim#endif /* _KERNEL */ 292250125Sjkim 293250125Sjkimstatic int 294250125Sjkim_acl_entry_matches(struct acl_entry *entry, acl_tag_t tag, acl_perm_t perm, 295250125Sjkim acl_entry_type_t entry_type) 296250125Sjkim{ 297250125Sjkim if (entry->ae_tag != tag) 298250125Sjkim return (0); 299250125Sjkim 300250125Sjkim if (entry->ae_id != ACL_UNDEFINED_ID) 301250125Sjkim return (0); 302250125Sjkim 303250125Sjkim if (entry->ae_perm != perm) 304250125Sjkim return (0); 305250125Sjkim 306250125Sjkim if (entry->ae_entry_type != entry_type) 307250125Sjkim return (0); 308250125Sjkim 309250125Sjkim if (entry->ae_flags != 0) 310250125Sjkim return (0); 311250125Sjkim 312250125Sjkim return (1); 313250125Sjkim} 314250125Sjkim 315250125Sjkimstatic struct acl_entry * 316250125Sjkim_acl_append(struct acl *aclp, acl_tag_t tag, acl_perm_t perm, 317250125Sjkim acl_entry_type_t entry_type) 318250125Sjkim{ 319250125Sjkim struct acl_entry *entry; 320250125Sjkim 321250125Sjkim KASSERT(aclp->acl_cnt + 1 <= ACL_MAX_ENTRIES, 322250125Sjkim ("aclp->acl_cnt + 1 <= ACL_MAX_ENTRIES")); 323250125Sjkim 324250125Sjkim entry = &(aclp->acl_entry[aclp->acl_cnt]); 325250125Sjkim aclp->acl_cnt++; 326250125Sjkim 327250125Sjkim entry->ae_tag = tag; 328250125Sjkim entry->ae_id = ACL_UNDEFINED_ID; 329250125Sjkim entry->ae_perm = perm; 330250125Sjkim entry->ae_entry_type = entry_type; 331250125Sjkim entry->ae_flags = 0; 332250125Sjkim 333250125Sjkim return (entry); 334250125Sjkim} 335250125Sjkim 336250125Sjkimstatic struct acl_entry * 337250125Sjkim_acl_duplicate_entry(struct acl *aclp, int entry_index) 338250125Sjkim{ 339250125Sjkim int i; 340250125Sjkim 341250125Sjkim KASSERT(aclp->acl_cnt + 1 <= ACL_MAX_ENTRIES, 342250125Sjkim ("aclp->acl_cnt + 1 <= ACL_MAX_ENTRIES")); 343250125Sjkim 344250125Sjkim for (i = aclp->acl_cnt; i > entry_index; i--) 345250125Sjkim aclp->acl_entry[i] = aclp->acl_entry[i - 1]; 346250125Sjkim 347250125Sjkim aclp->acl_cnt++; 348250125Sjkim 349250125Sjkim return (&(aclp->acl_entry[entry_index + 1])); 350250125Sjkim} 351250125Sjkim 352250125Sjkim/* 353250125Sjkim * Calculate trivial ACL in a manner compatible with PSARC/2010/029. 354250125Sjkim * Note that this results in an ACL different from (but semantically 355250125Sjkim * equal to) the "canonical six" trivial ACL computed using algorithm 356250125Sjkim * described in draft-ietf-nfsv4-minorversion1-03.txt, 3.16.6.2. 357250125Sjkim */ 358250125Sjkimvoid 359250125Sjkimacl_nfs4_trivial_from_mode(struct acl *aclp, mode_t mode) 360250125Sjkim{ 361250125Sjkim acl_perm_t user_allow_first = 0, user_deny = 0, group_deny = 0; 362250125Sjkim acl_perm_t user_allow, group_allow, everyone_allow; 363250125Sjkim 364250125Sjkim KASSERT(aclp->acl_cnt == 0, ("aclp->acl_cnt == 0")); 365250125Sjkim 366250125Sjkim user_allow = group_allow = everyone_allow = ACL_READ_ACL | 367250125Sjkim ACL_READ_ATTRIBUTES | ACL_READ_NAMED_ATTRS | ACL_SYNCHRONIZE; 368250125Sjkim user_allow |= ACL_WRITE_ACL | ACL_WRITE_OWNER | ACL_WRITE_ATTRIBUTES | 369250125Sjkim ACL_WRITE_NAMED_ATTRS; 370250125Sjkim 371250125Sjkim if (mode & S_IRUSR) 372250125Sjkim user_allow |= ACL_READ_DATA; 373250125Sjkim if (mode & S_IWUSR) 374250125Sjkim user_allow |= (ACL_WRITE_DATA | ACL_APPEND_DATA); 375250125Sjkim if (mode & S_IXUSR) 376250125Sjkim user_allow |= ACL_EXECUTE; 377250125Sjkim 378250125Sjkim if (mode & S_IRGRP) 379250125Sjkim group_allow |= ACL_READ_DATA; 380250125Sjkim if (mode & S_IWGRP) 381250125Sjkim group_allow |= (ACL_WRITE_DATA | ACL_APPEND_DATA); 382250125Sjkim if (mode & S_IXGRP) 383250125Sjkim group_allow |= ACL_EXECUTE; 384250125Sjkim 385250125Sjkim if (mode & S_IROTH) 386250125Sjkim everyone_allow |= ACL_READ_DATA; 387250125Sjkim if (mode & S_IWOTH) 388250125Sjkim everyone_allow |= (ACL_WRITE_DATA | ACL_APPEND_DATA); 389250125Sjkim if (mode & S_IXOTH) 390250125Sjkim everyone_allow |= ACL_EXECUTE; 391250125Sjkim 392250125Sjkim user_deny = ((group_allow | everyone_allow) & ~user_allow); 393250125Sjkim group_deny = everyone_allow & ~group_allow; 394250125Sjkim user_allow_first = group_deny & ~user_deny; 395250125Sjkim 396250125Sjkim if (user_allow_first != 0) 397250125Sjkim _acl_append(aclp, ACL_USER_OBJ, user_allow_first, ACL_ENTRY_TYPE_ALLOW); 398250125Sjkim if (user_deny != 0) 399250125Sjkim _acl_append(aclp, ACL_USER_OBJ, user_deny, ACL_ENTRY_TYPE_DENY); 400250125Sjkim if (group_deny != 0) 401250125Sjkim _acl_append(aclp, ACL_GROUP_OBJ, group_deny, ACL_ENTRY_TYPE_DENY); 402250125Sjkim _acl_append(aclp, ACL_USER_OBJ, user_allow, ACL_ENTRY_TYPE_ALLOW); 403250125Sjkim _acl_append(aclp, ACL_GROUP_OBJ, group_allow, ACL_ENTRY_TYPE_ALLOW); 404250125Sjkim _acl_append(aclp, ACL_EVERYONE, everyone_allow, ACL_ENTRY_TYPE_ALLOW); 405250125Sjkim} 406250125Sjkim 407250125Sjkimvoid 408250125Sjkimacl_nfs4_sync_acl_from_mode(struct acl *aclp, mode_t mode, int file_owner_id) 409250125Sjkim{ 410250125Sjkim int i, meets, must_append; 411250125Sjkim struct acl_entry *entry, *copy, *previous, 412250125Sjkim *a1, *a2, *a3, *a4, *a5, *a6; 413250125Sjkim mode_t amode; 414250125Sjkim const int READ = 04; 415250125Sjkim const int WRITE = 02; 416250125Sjkim const int EXEC = 01; 417250125Sjkim 418250125Sjkim KASSERT(aclp->acl_cnt <= ACL_MAX_ENTRIES, 419228072Sbapt ("aclp->acl_cnt <= ACL_MAX_ENTRIES")); 420228072Sbapt 421250125Sjkim /* 422228072Sbapt * NFSv4 Minor Version 1, draft-ietf-nfsv4-minorversion1-03.txt 423228072Sbapt * 424228072Sbapt * 3.16.6.3. Applying a Mode to an Existing ACL 425228072Sbapt */ 426250125Sjkim 427228072Sbapt /* 428228072Sbapt * 1. For each ACE: 429228072Sbapt */ 430250125Sjkim for (i = 0; i < aclp->acl_cnt; i++) { 431228072Sbapt entry = &(aclp->acl_entry[i]); 432228072Sbapt 433228072Sbapt /* 434250125Sjkim * 1.1. If the type is neither ALLOW or DENY - skip. 435228072Sbapt */ 436228072Sbapt if (entry->ae_entry_type != ACL_ENTRY_TYPE_ALLOW && 437228072Sbapt entry->ae_entry_type != ACL_ENTRY_TYPE_DENY) 438250125Sjkim continue; 439228072Sbapt 440228072Sbapt /* 441228072Sbapt * 1.2. If ACL_ENTRY_INHERIT_ONLY is set - skip. 442250125Sjkim */ 443228072Sbapt if (entry->ae_flags & ACL_ENTRY_INHERIT_ONLY) 444228072Sbapt continue; 445228072Sbapt 446250125Sjkim /* 447228072Sbapt * 1.3. If ACL_ENTRY_FILE_INHERIT or ACL_ENTRY_DIRECTORY_INHERIT 448228072Sbapt * are set: 449228072Sbapt */ 450228072Sbapt if (entry->ae_flags & 451250125Sjkim (ACL_ENTRY_FILE_INHERIT | ACL_ENTRY_DIRECTORY_INHERIT)) { 452228072Sbapt /* 453228072Sbapt * 1.3.1. A copy of the current ACE is made, and placed 454228072Sbapt * in the ACL immediately following the current 455228072Sbapt * ACE. 456250125Sjkim */ 457228072Sbapt copy = _acl_duplicate_entry(aclp, i); 458228072Sbapt 459228072Sbapt /* 460250125Sjkim * 1.3.2. In the first ACE, the flag 461228072Sbapt * ACL_ENTRY_INHERIT_ONLY is set. 462250125Sjkim */ 463228072Sbapt entry->ae_flags |= ACL_ENTRY_INHERIT_ONLY; 464250125Sjkim 465228072Sbapt /* 466228072Sbapt * 1.3.3. In the second ACE, the following flags 467228072Sbapt * are cleared: 468250125Sjkim * ACL_ENTRY_FILE_INHERIT, 469228072Sbapt * ACL_ENTRY_DIRECTORY_INHERIT, 470228072Sbapt * ACL_ENTRY_NO_PROPAGATE_INHERIT. 471228072Sbapt */ 472250125Sjkim copy->ae_flags &= ~(ACL_ENTRY_FILE_INHERIT | 473228072Sbapt ACL_ENTRY_DIRECTORY_INHERIT | 474250125Sjkim ACL_ENTRY_NO_PROPAGATE_INHERIT); 475250125Sjkim 476228072Sbapt /* 477250125Sjkim * The algorithm continues on with the second ACE. 478228072Sbapt */ 479228072Sbapt i++; 480228072Sbapt entry = copy; 481250125Sjkim } 482228072Sbapt 483228072Sbapt /* 484228072Sbapt * 1.4. If it's owner@, group@ or everyone@ entry, clear 485250125Sjkim * ACL_READ_DATA, ACL_WRITE_DATA, ACL_APPEND_DATA 486228072Sbapt * and ACL_EXECUTE. Continue to the next entry. 487228072Sbapt */ 488228072Sbapt if (entry->ae_tag == ACL_USER_OBJ || 489228072Sbapt entry->ae_tag == ACL_GROUP_OBJ || 490250125Sjkim entry->ae_tag == ACL_EVERYONE) { 491228072Sbapt entry->ae_perm &= ~(ACL_READ_DATA | ACL_WRITE_DATA | 492228072Sbapt ACL_APPEND_DATA | ACL_EXECUTE); 493228072Sbapt continue; 494250125Sjkim } 495228072Sbapt 496228072Sbapt /* 497228072Sbapt * 1.5. Otherwise, if the "who" field did not match one 498250125Sjkim * of OWNER@, GROUP@, EVERYONE@: 499228072Sbapt * 500228072Sbapt * 1.5.1. If the type is ALLOW, check the preceding ACE. 501228072Sbapt * If it does not meet all of the following criteria: 502228072Sbapt */ 503250125Sjkim if (entry->ae_entry_type != ACL_ENTRY_TYPE_ALLOW) 504228072Sbapt continue; 505228072Sbapt 506228072Sbapt meets = 0; 507250125Sjkim if (i > 0) { 508228072Sbapt meets = 1; 509228072Sbapt previous = &(aclp->acl_entry[i - 1]); 510228072Sbapt 511228072Sbapt /* 512250125Sjkim * 1.5.1.1. The type field is DENY, 513228072Sbapt */ 514228072Sbapt if (previous->ae_entry_type != ACL_ENTRY_TYPE_DENY) 515228072Sbapt meets = 0; 516228072Sbapt 517250125Sjkim /* 518228072Sbapt * 1.5.1.2. The "who" field is the same as the current 519250125Sjkim * ACE, 520250125Sjkim * 521250125Sjkim * 1.5.1.3. The flag bit ACE4_IDENTIFIER_GROUP 522250125Sjkim * is the same as it is in the current ACE, 523228072Sbapt * and no other flag bits are set, 524250125Sjkim */ 525228072Sbapt if (previous->ae_id != entry->ae_id || 526228072Sbapt previous->ae_tag != entry->ae_tag) 527228072Sbapt meets = 0; 528250125Sjkim 529228072Sbapt if (previous->ae_flags) 530228072Sbapt meets = 0; 531228072Sbapt 532250125Sjkim /* 533228072Sbapt * 1.5.1.4. The mask bits are a subset of the mask bits 534228072Sbapt * of the current ACE, and are also subset of 535228072Sbapt * the following: ACL_READ_DATA, 536228072Sbapt * ACL_WRITE_DATA, ACL_APPEND_DATA, ACL_EXECUTE 537228072Sbapt */ 538228072Sbapt if (previous->ae_perm & ~(entry->ae_perm)) 539250125Sjkim meets = 0; 540228072Sbapt 541228072Sbapt if (previous->ae_perm & ~(ACL_READ_DATA | 542228072Sbapt ACL_WRITE_DATA | ACL_APPEND_DATA | ACL_EXECUTE)) 543228072Sbapt meets = 0; 544250125Sjkim } 545228072Sbapt 546228072Sbapt if (!meets) { 547228072Sbapt /* 548228072Sbapt * Then the ACE of type DENY, with a who equal 549250125Sjkim * to the current ACE, flag bits equal to 550228072Sbapt * (<current ACE flags> & <ACE_IDENTIFIER_GROUP>) 551228072Sbapt * and no mask bits, is prepended. 552228072Sbapt */ 553250125Sjkim previous = entry; 554228072Sbapt entry = _acl_duplicate_entry(aclp, i); 555228072Sbapt 556228072Sbapt /* Adjust counter, as we've just added an entry. */ 557228072Sbapt i++; 558250125Sjkim 559228072Sbapt previous->ae_tag = entry->ae_tag; 560228072Sbapt previous->ae_id = entry->ae_id; 561228072Sbapt previous->ae_flags = entry->ae_flags; 562228072Sbapt previous->ae_perm = 0; 563250125Sjkim previous->ae_entry_type = ACL_ENTRY_TYPE_DENY; 564228072Sbapt } 565228072Sbapt 566228072Sbapt /* 567250125Sjkim * 1.5.2. The following modifications are made to the prepended 568228072Sbapt * ACE. The intent is to mask the following ACE 569228072Sbapt * to disallow ACL_READ_DATA, ACL_WRITE_DATA, 570228072Sbapt * ACL_APPEND_DATA, or ACL_EXECUTE, based upon the group 571228072Sbapt * permissions of the new mode. As a special case, 572250125Sjkim * if the ACE matches the current owner of the file, 573228072Sbapt * the owner bits are used, rather than the group bits. 574250125Sjkim * This is reflected in the algorithm below. 575250125Sjkim */ 576228072Sbapt amode = mode >> 3; 577250125Sjkim 578228072Sbapt /* 579228072Sbapt * If ACE4_IDENTIFIER_GROUP is not set, and the "who" field 580228072Sbapt * in ACE matches the owner of the file, we shift amode three 581228072Sbapt * more bits, in order to have the owner permission bits 582250125Sjkim * placed in the three low order bits of amode. 583228072Sbapt */ 584228072Sbapt if (entry->ae_tag == ACL_USER && entry->ae_id == file_owner_id) 585228072Sbapt amode = amode >> 3; 586228072Sbapt 587250125Sjkim if (entry->ae_perm & ACL_READ_DATA) { 588228072Sbapt if (amode & READ) 589228072Sbapt previous->ae_perm &= ~ACL_READ_DATA; 590228072Sbapt else 591228072Sbapt previous->ae_perm |= ACL_READ_DATA; 592250125Sjkim } 593228072Sbapt 594250125Sjkim if (entry->ae_perm & ACL_WRITE_DATA) { 595228072Sbapt if (amode & WRITE) 596228072Sbapt previous->ae_perm &= ~ACL_WRITE_DATA; 597250125Sjkim else 598228072Sbapt previous->ae_perm |= ACL_WRITE_DATA; 599228072Sbapt } 600228072Sbapt 601228072Sbapt if (entry->ae_perm & ACL_APPEND_DATA) { 602250125Sjkim if (amode & WRITE) 603228072Sbapt previous->ae_perm &= ~ACL_APPEND_DATA; 604228072Sbapt else 605228072Sbapt previous->ae_perm |= ACL_APPEND_DATA; 606250125Sjkim } 607228072Sbapt 608228072Sbapt if (entry->ae_perm & ACL_EXECUTE) { 609228072Sbapt if (amode & EXEC) 610250125Sjkim previous->ae_perm &= ~ACL_EXECUTE; 611228072Sbapt else 612250125Sjkim previous->ae_perm |= ACL_EXECUTE; 613250125Sjkim } 614228072Sbapt 615250125Sjkim /* 616228072Sbapt * 1.5.3. If ACE4_IDENTIFIER_GROUP is set in the flags 617228072Sbapt * of the ALLOW ace: 618228072Sbapt * 619228072Sbapt * XXX: This point is not there in the Falkner's draft. 620250125Sjkim */ 621228072Sbapt if (entry->ae_tag == ACL_GROUP && 622228072Sbapt entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) { 623228072Sbapt mode_t extramode, ownermode; 624228072Sbapt extramode = (mode >> 3) & 07; 625250125Sjkim ownermode = mode >> 6; 626228072Sbapt extramode &= ~ownermode; 627250125Sjkim 628250125Sjkim if (extramode) { 629250125Sjkim if (extramode & READ) { 630228072Sbapt entry->ae_perm &= ~ACL_READ_DATA; 631250125Sjkim previous->ae_perm &= ~ACL_READ_DATA; 632228072Sbapt } 633228072Sbapt 634228072Sbapt if (extramode & WRITE) { 635250125Sjkim entry->ae_perm &= 636228072Sbapt ~(ACL_WRITE_DATA | ACL_APPEND_DATA); 637228072Sbapt previous->ae_perm &= 638228072Sbapt ~(ACL_WRITE_DATA | ACL_APPEND_DATA); 639228072Sbapt } 640250125Sjkim 641228072Sbapt if (extramode & EXEC) { 642228072Sbapt entry->ae_perm &= ~ACL_EXECUTE; 643228072Sbapt previous->ae_perm &= ~ACL_EXECUTE; 644228072Sbapt } 645250125Sjkim } 646228072Sbapt } 647250125Sjkim } 648250125Sjkim 649250125Sjkim /* 650250125Sjkim * 2. If there at least six ACEs, the final six ACEs are examined. 651228072Sbapt * If they are not equal to what we want, append six ACEs. 652228072Sbapt */ 653228072Sbapt must_append = 0; 654250125Sjkim if (aclp->acl_cnt < 6) { 655228072Sbapt must_append = 1; 656228072Sbapt } else { 657228072Sbapt a6 = &(aclp->acl_entry[aclp->acl_cnt - 1]); 658228072Sbapt a5 = &(aclp->acl_entry[aclp->acl_cnt - 2]); 659250125Sjkim a4 = &(aclp->acl_entry[aclp->acl_cnt - 3]); 660228072Sbapt a3 = &(aclp->acl_entry[aclp->acl_cnt - 4]); 661228072Sbapt a2 = &(aclp->acl_entry[aclp->acl_cnt - 5]); 662228072Sbapt a1 = &(aclp->acl_entry[aclp->acl_cnt - 6]); 663250125Sjkim 664228072Sbapt if (!_acl_entry_matches(a1, ACL_USER_OBJ, 0, 665228072Sbapt ACL_ENTRY_TYPE_DENY)) 666228072Sbapt must_append = 1; 667250125Sjkim if (!_acl_entry_matches(a2, ACL_USER_OBJ, ACL_WRITE_ACL | 668228072Sbapt ACL_WRITE_OWNER | ACL_WRITE_ATTRIBUTES | 669228072Sbapt ACL_WRITE_NAMED_ATTRS, ACL_ENTRY_TYPE_ALLOW)) 670228072Sbapt must_append = 1; 671250125Sjkim if (!_acl_entry_matches(a3, ACL_GROUP_OBJ, 0, 672228072Sbapt ACL_ENTRY_TYPE_DENY)) 673228072Sbapt must_append = 1; 674228072Sbapt if (!_acl_entry_matches(a4, ACL_GROUP_OBJ, 0, 675250125Sjkim ACL_ENTRY_TYPE_ALLOW)) 676228072Sbapt must_append = 1; 677250125Sjkim if (!_acl_entry_matches(a5, ACL_EVERYONE, ACL_WRITE_ACL | 678250125Sjkim ACL_WRITE_OWNER | ACL_WRITE_ATTRIBUTES | 679228072Sbapt ACL_WRITE_NAMED_ATTRS, ACL_ENTRY_TYPE_DENY)) 680250125Sjkim must_append = 1; 681228072Sbapt if (!_acl_entry_matches(a6, ACL_EVERYONE, ACL_READ_ACL | 682228072Sbapt ACL_READ_ATTRIBUTES | ACL_READ_NAMED_ATTRS | 683228072Sbapt ACL_SYNCHRONIZE, ACL_ENTRY_TYPE_ALLOW)) 684250125Sjkim must_append = 1; 685228072Sbapt } 686228072Sbapt 687228072Sbapt if (must_append) { 688250125Sjkim KASSERT(aclp->acl_cnt + 6 <= ACL_MAX_ENTRIES, 689228072Sbapt ("aclp->acl_cnt <= ACL_MAX_ENTRIES")); 690228072Sbapt 691228072Sbapt a1 = _acl_append(aclp, ACL_USER_OBJ, 0, ACL_ENTRY_TYPE_DENY); 692228072Sbapt a2 = _acl_append(aclp, ACL_USER_OBJ, ACL_WRITE_ACL | 693250125Sjkim ACL_WRITE_OWNER | ACL_WRITE_ATTRIBUTES | 694228072Sbapt ACL_WRITE_NAMED_ATTRS, ACL_ENTRY_TYPE_ALLOW); 695228072Sbapt a3 = _acl_append(aclp, ACL_GROUP_OBJ, 0, ACL_ENTRY_TYPE_DENY); 696228072Sbapt a4 = _acl_append(aclp, ACL_GROUP_OBJ, 0, ACL_ENTRY_TYPE_ALLOW); 697250125Sjkim a5 = _acl_append(aclp, ACL_EVERYONE, ACL_WRITE_ACL | 698228072Sbapt ACL_WRITE_OWNER | ACL_WRITE_ATTRIBUTES | 699228072Sbapt ACL_WRITE_NAMED_ATTRS, ACL_ENTRY_TYPE_DENY); 700228072Sbapt a6 = _acl_append(aclp, ACL_EVERYONE, ACL_READ_ACL | 701250125Sjkim ACL_READ_ATTRIBUTES | ACL_READ_NAMED_ATTRS | 702228072Sbapt ACL_SYNCHRONIZE, ACL_ENTRY_TYPE_ALLOW); 703228072Sbapt 704228072Sbapt KASSERT(a1 != NULL && a2 != NULL && a3 != NULL && a4 != NULL && 705250125Sjkim a5 != NULL && a6 != NULL, ("couldn't append to ACL.")); 706228072Sbapt } 707228072Sbapt 708228072Sbapt /* 709228072Sbapt * 3. The final six ACEs are adjusted according to the incoming mode. 710250125Sjkim */ 711228072Sbapt if (mode & S_IRUSR) 712228072Sbapt a2->ae_perm |= ACL_READ_DATA; 713228072Sbapt else 714228072Sbapt a1->ae_perm |= ACL_READ_DATA; 715250125Sjkim if (mode & S_IWUSR) 716228072Sbapt a2->ae_perm |= (ACL_WRITE_DATA | ACL_APPEND_DATA); 717228072Sbapt else 718228072Sbapt a1->ae_perm |= (ACL_WRITE_DATA | ACL_APPEND_DATA); 719228072Sbapt if (mode & S_IXUSR) 720250125Sjkim a2->ae_perm |= ACL_EXECUTE; 721228072Sbapt else 722228072Sbapt a1->ae_perm |= ACL_EXECUTE; 723228072Sbapt 724228072Sbapt if (mode & S_IRGRP) 725250125Sjkim a4->ae_perm |= ACL_READ_DATA; 726228072Sbapt else 727228072Sbapt a3->ae_perm |= ACL_READ_DATA; 728228072Sbapt if (mode & S_IWGRP) 729228072Sbapt a4->ae_perm |= (ACL_WRITE_DATA | ACL_APPEND_DATA); 730250125Sjkim else 731228072Sbapt a3->ae_perm |= (ACL_WRITE_DATA | ACL_APPEND_DATA); 732228072Sbapt if (mode & S_IXGRP) 733228072Sbapt a4->ae_perm |= ACL_EXECUTE; 734250125Sjkim else 735228072Sbapt a3->ae_perm |= ACL_EXECUTE; 736228072Sbapt 737228072Sbapt if (mode & S_IROTH) 738250125Sjkim a6->ae_perm |= ACL_READ_DATA; 739228072Sbapt else 740228072Sbapt a5->ae_perm |= ACL_READ_DATA; 741228072Sbapt if (mode & S_IWOTH) 742250125Sjkim a6->ae_perm |= (ACL_WRITE_DATA | ACL_APPEND_DATA); 743228072Sbapt else 744228072Sbapt a5->ae_perm |= (ACL_WRITE_DATA | ACL_APPEND_DATA); 745228072Sbapt if (mode & S_IXOTH) 746250125Sjkim a6->ae_perm |= ACL_EXECUTE; 747228072Sbapt else 748228072Sbapt a5->ae_perm |= ACL_EXECUTE; 749228072Sbapt} 750250125Sjkim 751228072Sbaptvoid 752228072Sbaptacl_nfs4_sync_mode_from_acl(mode_t *_mode, const struct acl *aclp) 753228072Sbapt{ 754250125Sjkim int i; 755228072Sbapt mode_t old_mode = *_mode, mode = 0, seen = 0; 756228072Sbapt const struct acl_entry *entry; 757228072Sbapt 758250125Sjkim KASSERT(aclp->acl_cnt > 0, ("aclp->acl_cnt > 0")); 759228072Sbapt KASSERT(aclp->acl_cnt <= ACL_MAX_ENTRIES, 760228072Sbapt ("aclp->acl_cnt <= ACL_MAX_ENTRIES")); 761228072Sbapt 762228072Sbapt /* 763250125Sjkim * NFSv4 Minor Version 1, draft-ietf-nfsv4-minorversion1-03.txt 764228072Sbapt * 765228072Sbapt * 3.16.6.1. Recomputing mode upon SETATTR of ACL 766228072Sbapt */ 767250125Sjkim 768228072Sbapt for (i = 0; i < aclp->acl_cnt; i++) { 769228072Sbapt entry = &(aclp->acl_entry[i]); 770228072Sbapt 771250125Sjkim if (entry->ae_entry_type != ACL_ENTRY_TYPE_ALLOW && 772228072Sbapt entry->ae_entry_type != ACL_ENTRY_TYPE_DENY) 773250125Sjkim continue; 774250125Sjkim 775250125Sjkim if (entry->ae_flags & ACL_ENTRY_INHERIT_ONLY) 776228072Sbapt continue; 777250125Sjkim 778228072Sbapt if (entry->ae_tag == ACL_USER_OBJ) { 779228072Sbapt if ((entry->ae_perm & ACL_READ_DATA) && 780228072Sbapt ((seen & S_IRUSR) == 0)) { 781228072Sbapt seen |= S_IRUSR; 782250125Sjkim if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 783228072Sbapt mode |= S_IRUSR; 784250125Sjkim } 785250125Sjkim if ((entry->ae_perm & ACL_WRITE_DATA) && 786250125Sjkim ((seen & S_IWUSR) == 0)) { 787250125Sjkim seen |= S_IWUSR; 788250125Sjkim if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 789250125Sjkim mode |= S_IWUSR; 790250125Sjkim } 791250125Sjkim if ((entry->ae_perm & ACL_EXECUTE) && 792250125Sjkim ((seen & S_IXUSR) == 0)) { 793250125Sjkim seen |= S_IXUSR; 794250125Sjkim if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 795250125Sjkim mode |= S_IXUSR; 796250125Sjkim } 797250125Sjkim } else if (entry->ae_tag == ACL_GROUP_OBJ) { 798250125Sjkim if ((entry->ae_perm & ACL_READ_DATA) && 799250125Sjkim ((seen & S_IRGRP) == 0)) { 800250125Sjkim seen |= S_IRGRP; 801250125Sjkim if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 802250125Sjkim mode |= S_IRGRP; 803250125Sjkim } 804250125Sjkim if ((entry->ae_perm & ACL_WRITE_DATA) && 805250125Sjkim ((seen & S_IWGRP) == 0)) { 806250125Sjkim seen |= S_IWGRP; 807250125Sjkim if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 808250125Sjkim mode |= S_IWGRP; 809250125Sjkim } 810250125Sjkim if ((entry->ae_perm & ACL_EXECUTE) && 811250125Sjkim ((seen & S_IXGRP) == 0)) { 812250125Sjkim seen |= S_IXGRP; 813250125Sjkim if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 814228072Sbapt mode |= S_IXGRP; 815250125Sjkim } 816228072Sbapt } else if (entry->ae_tag == ACL_EVERYONE) { 817228072Sbapt if (entry->ae_perm & ACL_READ_DATA) { 818228072Sbapt if ((seen & S_IRUSR) == 0) { 819250125Sjkim seen |= S_IRUSR; 820228072Sbapt if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 821228072Sbapt mode |= S_IRUSR; 822228072Sbapt } 823228072Sbapt if ((seen & S_IRGRP) == 0) { 824250125Sjkim seen |= S_IRGRP; 825228072Sbapt if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 826228072Sbapt mode |= S_IRGRP; 827228072Sbapt } 828228072Sbapt if ((seen & S_IROTH) == 0) { 829250125Sjkim seen |= S_IROTH; 830228072Sbapt if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 831228072Sbapt mode |= S_IROTH; 832228072Sbapt } 833250125Sjkim } 834228072Sbapt if (entry->ae_perm & ACL_WRITE_DATA) { 835228072Sbapt if ((seen & S_IWUSR) == 0) { 836228072Sbapt seen |= S_IWUSR; 837250125Sjkim if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 838228072Sbapt mode |= S_IWUSR; 839228072Sbapt } 840228072Sbapt if ((seen & S_IWGRP) == 0) { 841228072Sbapt seen |= S_IWGRP; 842228072Sbapt if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 843250125Sjkim mode |= S_IWGRP; 844228072Sbapt } 845250125Sjkim if ((seen & S_IWOTH) == 0) { 846250125Sjkim seen |= S_IWOTH; 847228072Sbapt if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 848250125Sjkim mode |= S_IWOTH; 849228072Sbapt } 850250125Sjkim } 851228072Sbapt if (entry->ae_perm & ACL_EXECUTE) { 852228072Sbapt if ((seen & S_IXUSR) == 0) { 853250125Sjkim seen |= S_IXUSR; 854228072Sbapt if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 855250125Sjkim mode |= S_IXUSR; 856228072Sbapt } 857228072Sbapt if ((seen & S_IXGRP) == 0) { 858228072Sbapt seen |= S_IXGRP; 859228072Sbapt if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 860228072Sbapt mode |= S_IXGRP; 861250125Sjkim } 862228072Sbapt if ((seen & S_IXOTH) == 0) { 863228072Sbapt seen |= S_IXOTH; 864228072Sbapt if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 865228072Sbapt mode |= S_IXOTH; 866250125Sjkim } 867228072Sbapt } 868228072Sbapt } 869228072Sbapt } 870250125Sjkim 871228072Sbapt *_mode = mode | (old_mode & ACL_PRESERVE_MASK); 872228072Sbapt} 873228072Sbapt 874250125Sjkimvoid 875228072Sbaptacl_nfs4_compute_inherited_acl(const struct acl *parent_aclp, 876250125Sjkim struct acl *child_aclp, mode_t mode, int file_owner_id, 877250125Sjkim int is_directory) 878250125Sjkim{ 879250125Sjkim int i, flags; 880228072Sbapt const struct acl_entry *parent_entry; 881228072Sbapt struct acl_entry *entry, *copy; 882250125Sjkim 883228072Sbapt KASSERT(child_aclp->acl_cnt == 0, ("child_aclp->acl_cnt == 0")); 884250125Sjkim KASSERT(parent_aclp->acl_cnt > 0, ("parent_aclp->acl_cnt > 0")); 885228072Sbapt KASSERT(parent_aclp->acl_cnt <= ACL_MAX_ENTRIES, 886250125Sjkim ("parent_aclp->acl_cnt <= ACL_MAX_ENTRIES")); 887250125Sjkim 888228072Sbapt /* 889228072Sbapt * NFSv4 Minor Version 1, draft-ietf-nfsv4-minorversion1-03.txt 890250125Sjkim * 891228072Sbapt * 3.16.6.2. Applying the mode given to CREATE or OPEN 892250125Sjkim * to an inherited ACL 893228072Sbapt */ 894228072Sbapt 895228072Sbapt /* 896250125Sjkim * 1. Form an ACL that is the concatenation of all inheritable ACEs. 897228072Sbapt */ 898250125Sjkim for (i = 0; i < parent_aclp->acl_cnt; i++) { 899228072Sbapt parent_entry = &(parent_aclp->acl_entry[i]); 900228072Sbapt flags = parent_entry->ae_flags; 901228072Sbapt 902250125Sjkim /* 903228072Sbapt * Entry is not inheritable at all. 904250125Sjkim */ 905250125Sjkim if ((flags & (ACL_ENTRY_DIRECTORY_INHERIT | 906228072Sbapt ACL_ENTRY_FILE_INHERIT)) == 0) 907228072Sbapt continue; 908228072Sbapt 909250125Sjkim /* 910250125Sjkim * We're creating a file, but entry is not inheritable 911228072Sbapt * by files. 912250125Sjkim */ 913228072Sbapt if (!is_directory && (flags & ACL_ENTRY_FILE_INHERIT) == 0) 914250125Sjkim continue; 915250125Sjkim 916228072Sbapt /* 917250125Sjkim * Entry is inheritable only by files, but has NO_PROPAGATE 918228072Sbapt * flag set, and we're creating a directory, so it wouldn't 919250125Sjkim * propagate to any file in that directory anyway. 920228072Sbapt */ 921228072Sbapt if (is_directory && 922228072Sbapt (flags & ACL_ENTRY_DIRECTORY_INHERIT) == 0 && 923250125Sjkim (flags & ACL_ENTRY_NO_PROPAGATE_INHERIT)) 924250125Sjkim continue; 925228072Sbapt 926250125Sjkim KASSERT(child_aclp->acl_cnt + 1 <= ACL_MAX_ENTRIES, 927228072Sbapt ("child_aclp->acl_cnt + 1 <= ACL_MAX_ENTRIES")); 928250125Sjkim child_aclp->acl_entry[child_aclp->acl_cnt] = *parent_entry; 929228072Sbapt child_aclp->acl_cnt++; 930228072Sbapt } 931250125Sjkim 932228072Sbapt /* 933228072Sbapt * 2. For each entry in the new ACL, adjust its flags, possibly 934228072Sbapt * creating two entries in place of one. 935250125Sjkim */ 936228072Sbapt for (i = 0; i < child_aclp->acl_cnt; i++) { 937228072Sbapt entry = &(child_aclp->acl_entry[i]); 938228072Sbapt 939250125Sjkim /* 940228072Sbapt * This is not in the specification, but SunOS 941250125Sjkim * apparently does that. 942228072Sbapt */ 943250125Sjkim if (((entry->ae_flags & ACL_ENTRY_NO_PROPAGATE_INHERIT) || 944228072Sbapt !is_directory) && 945228072Sbapt entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 946228072Sbapt entry->ae_perm &= ~(ACL_WRITE_ACL | ACL_WRITE_OWNER); 947250125Sjkim 948228072Sbapt /* 949228072Sbapt * 2.A. If the ACL_ENTRY_NO_PROPAGATE_INHERIT is set, or if the object 950228072Sbapt * being created is not a directory, then clear the 951250125Sjkim * following flags: ACL_ENTRY_NO_PROPAGATE_INHERIT, 952228072Sbapt * ACL_ENTRY_FILE_INHERIT, ACL_ENTRY_DIRECTORY_INHERIT, 953228072Sbapt * ACL_ENTRY_INHERIT_ONLY. 954228072Sbapt */ 955228072Sbapt if (entry->ae_flags & ACL_ENTRY_NO_PROPAGATE_INHERIT || 956228072Sbapt !is_directory) { 957250125Sjkim entry->ae_flags &= ~(ACL_ENTRY_NO_PROPAGATE_INHERIT | 958228072Sbapt ACL_ENTRY_FILE_INHERIT | ACL_ENTRY_DIRECTORY_INHERIT | 959250125Sjkim ACL_ENTRY_INHERIT_ONLY); 960228072Sbapt 961228072Sbapt /* 962228072Sbapt * Continue on to the next ACE. 963250125Sjkim */ 964250125Sjkim continue; 965228072Sbapt } 966250125Sjkim 967228072Sbapt /* 968250125Sjkim * 2.B. If the object is a directory and ACL_ENTRY_FILE_INHERIT 969228072Sbapt * is set, but ACL_ENTRY_NO_PROPAGATE_INHERIT is not set, ensure 970228072Sbapt * that ACL_ENTRY_INHERIT_ONLY is set. Continue to the 971250125Sjkim * next ACE. Otherwise... 972228072Sbapt */ 973228072Sbapt /* 974228072Sbapt * XXX: Read it again and make sure what does the "otherwise" 975250125Sjkim * apply to. 976228072Sbapt */ 977228072Sbapt if (is_directory && 978228072Sbapt (entry->ae_flags & ACL_ENTRY_FILE_INHERIT) && 979228072Sbapt ((entry->ae_flags & ACL_ENTRY_DIRECTORY_INHERIT) == 0)) { 980228072Sbapt entry->ae_flags |= ACL_ENTRY_INHERIT_ONLY; 981228072Sbapt continue; 982250125Sjkim } 983228072Sbapt 984228072Sbapt /* 985228072Sbapt * 2.C. If the type of the ACE is neither ALLOW nor deny, 986250125Sjkim * then continue. 987228072Sbapt */ 988228072Sbapt if (entry->ae_entry_type != ACL_ENTRY_TYPE_ALLOW && 989228072Sbapt entry->ae_entry_type != ACL_ENTRY_TYPE_DENY) 990228072Sbapt continue; 991228072Sbapt 992250125Sjkim /* 993228072Sbapt * 2.D. Copy the original ACE into a second, adjacent ACE. 994250125Sjkim */ 995250125Sjkim copy = _acl_duplicate_entry(child_aclp, i); 996228072Sbapt 997250125Sjkim /* 998228072Sbapt * 2.E. On the first ACE, ensure that ACL_ENTRY_INHERIT_ONLY 999250125Sjkim * is set. 1000250125Sjkim */ 1001228072Sbapt entry->ae_flags |= ACL_ENTRY_INHERIT_ONLY; 1002250125Sjkim 1003228072Sbapt /* 1004228072Sbapt * 2.F. On the second ACE, clear the following flags: 1005228072Sbapt * ACL_ENTRY_NO_PROPAGATE_INHERIT, ACL_ENTRY_FILE_INHERIT, 1006228072Sbapt * ACL_ENTRY_DIRECTORY_INHERIT, ACL_ENTRY_INHERIT_ONLY. 1007250125Sjkim */ 1008228072Sbapt copy->ae_flags &= ~(ACL_ENTRY_NO_PROPAGATE_INHERIT | 1009250125Sjkim ACL_ENTRY_FILE_INHERIT | ACL_ENTRY_DIRECTORY_INHERIT | 1010250125Sjkim ACL_ENTRY_INHERIT_ONLY); 1011228072Sbapt 1012250125Sjkim /* 1013228072Sbapt * 2.G. On the second ACE, if the type is ALLOW, 1014228072Sbapt * an implementation MAY clear the following 1015228072Sbapt * mask bits: ACL_WRITE_ACL, ACL_WRITE_OWNER. 1016228072Sbapt */ 1017250125Sjkim if (copy->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 1018228072Sbapt copy->ae_perm &= ~(ACL_WRITE_ACL | ACL_WRITE_OWNER); 1019228072Sbapt 1020228072Sbapt /* 1021250125Sjkim * Increment the counter to skip the copied entry. 1022228072Sbapt */ 1023228072Sbapt i++; 1024228072Sbapt } 1025250125Sjkim 1026228072Sbapt /* 1027228072Sbapt * 3. To ensure that the mode is honored, apply the algorithm describe 1028228072Sbapt * in Section 2.16.6.3, using the mode that is to be used for file 1029250125Sjkim * creation. 1030228072Sbapt */ 1031228072Sbapt acl_nfs4_sync_acl_from_mode(child_aclp, mode, file_owner_id); 1032228072Sbapt} 1033228072Sbapt 1034250125Sjkim#ifdef _KERNEL 1035228072Sbaptstatic int 1036228072Sbapt_acls_are_equal(const struct acl *a, const struct acl *b) 1037228072Sbapt{ 1038228072Sbapt int i; 1039250125Sjkim const struct acl_entry *entrya, *entryb; 1040228072Sbapt 1041228072Sbapt if (a->acl_cnt != b->acl_cnt) 1042228072Sbapt return (0); 1043250125Sjkim 1044228072Sbapt for (i = 0; i < b->acl_cnt; i++) { 1045228072Sbapt entrya = &(a->acl_entry[i]); 1046228072Sbapt entryb = &(b->acl_entry[i]); 1047250125Sjkim 1048228072Sbapt if (entrya->ae_tag != entryb->ae_tag || 1049228072Sbapt entrya->ae_id != entryb->ae_id || 1050228072Sbapt entrya->ae_perm != entryb->ae_perm || 1051228072Sbapt entrya->ae_entry_type != entryb->ae_entry_type || 1052250125Sjkim entrya->ae_flags != entryb->ae_flags) 1053228072Sbapt return (0); 1054228072Sbapt } 1055228072Sbapt 1056250125Sjkim return (1); 1057228072Sbapt} 1058228072Sbapt 1059228072Sbapt/* 1060250125Sjkim * This routine is used to determine whether to remove extended attribute 1061228072Sbapt * that stores ACL contents. 1062228072Sbapt */ 1063228072Sbaptint 1064228072Sbaptacl_nfs4_is_trivial(const struct acl *aclp, int file_owner_id) 1065250125Sjkim{ 1066228072Sbapt int trivial; 1067250125Sjkim mode_t tmpmode = 0; 1068228072Sbapt struct acl *tmpaclp; 1069228072Sbapt 1070250125Sjkim if (aclp->acl_cnt != 6) 1071228072Sbapt return (0); 1072228072Sbapt 1073228072Sbapt /* 1074250125Sjkim * Compute the mode from the ACL, then compute new ACL from that mode. 1075228072Sbapt * If the ACLs are identical, then the ACL is trivial. 1076228072Sbapt * 1077228072Sbapt * XXX: I guess there is a faster way to do this. However, even 1078228072Sbapt * this slow implementation significantly speeds things up 1079250125Sjkim * for files that don't have non-trivial ACLs - it's critical 1080228072Sbapt * for performance to not use EA when they are not needed. 1081228072Sbapt */ 1082228072Sbapt tmpaclp = acl_alloc(M_WAITOK | M_ZERO); 1083250125Sjkim acl_nfs4_sync_mode_from_acl(&tmpmode, aclp); 1084228072Sbapt acl_nfs4_sync_acl_from_mode(tmpaclp, tmpmode, file_owner_id); 1085228072Sbapt trivial = _acls_are_equal(aclp, tmpaclp); 1086228072Sbapt acl_free(tmpaclp); 1087228072Sbapt 1088250125Sjkim return (trivial); 1089228072Sbapt} 1090250125Sjkim#endif /* _KERNEL */ 1091228072Sbapt 1092250125Sjkimint 1093228072Sbaptacl_nfs4_check(const struct acl *aclp, int is_directory) 1094250125Sjkim{ 1095250125Sjkim int i; 1096250125Sjkim const struct acl_entry *entry; 1097250125Sjkim 1098250125Sjkim /* 1099250125Sjkim * The spec doesn't seem to say anything about ACL validity. 1100228072Sbapt * It seems there is not much to do here. There is even no need 1101250125Sjkim * to count "owner@" or "everyone@" (ACL_USER_OBJ and ACL_EVERYONE) 1102228072Sbapt * entries, as there can be several of them and that's perfectly 1103250125Sjkim * valid. There can be none of them too. Really. 1104228072Sbapt */ 1105250125Sjkim 1106228072Sbapt if (aclp->acl_cnt > ACL_MAX_ENTRIES || aclp->acl_cnt <= 0) 1107250125Sjkim return (EINVAL); 1108228072Sbapt 1109228072Sbapt for (i = 0; i < aclp->acl_cnt; i++) { 1110228072Sbapt entry = &(aclp->acl_entry[i]); 1111250125Sjkim 1112228072Sbapt switch (entry->ae_tag) { 1113228072Sbapt case ACL_USER_OBJ: 1114228072Sbapt case ACL_GROUP_OBJ: 1115228072Sbapt case ACL_EVERYONE: 1116250125Sjkim if (entry->ae_id != ACL_UNDEFINED_ID) 1117228072Sbapt return (EINVAL); 1118250125Sjkim break; 1119250125Sjkim 1120228072Sbapt case ACL_USER: 1121250125Sjkim case ACL_GROUP: 1122228072Sbapt if (entry->ae_id == ACL_UNDEFINED_ID) 1123228072Sbapt return (EINVAL); 1124228072Sbapt break; 1125250125Sjkim 1126228072Sbapt default: 1127228072Sbapt return (EINVAL); 1128228072Sbapt } 1129228072Sbapt 1130250125Sjkim if ((entry->ae_perm | ACL_NFS4_PERM_BITS) != ACL_NFS4_PERM_BITS) 1131250125Sjkim return (EINVAL); 1132228072Sbapt 1133250125Sjkim /* 1134228072Sbapt * Disallow ACL_ENTRY_TYPE_AUDIT and ACL_ENTRY_TYPE_ALARM for now. 1135228072Sbapt */ 1136228072Sbapt if (entry->ae_entry_type != ACL_ENTRY_TYPE_ALLOW && 1137250125Sjkim entry->ae_entry_type != ACL_ENTRY_TYPE_DENY) 1138228072Sbapt return (EINVAL); 1139228072Sbapt 1140228072Sbapt if ((entry->ae_flags | ACL_FLAGS_BITS) != ACL_FLAGS_BITS) 1141250125Sjkim return (EINVAL); 1142228072Sbapt 1143228072Sbapt /* Disallow unimplemented flags. */ 1144228072Sbapt if (entry->ae_flags & (ACL_ENTRY_SUCCESSFUL_ACCESS | 1145250125Sjkim ACL_ENTRY_FAILED_ACCESS)) 1146228072Sbapt return (EINVAL); 1147250125Sjkim 1148228072Sbapt /* Disallow flags not allowed for ordinary files. */ 1149228072Sbapt if (!is_directory) { 1150250125Sjkim if (entry->ae_flags & (ACL_ENTRY_FILE_INHERIT | 1151228072Sbapt ACL_ENTRY_DIRECTORY_INHERIT | 1152250125Sjkim ACL_ENTRY_NO_PROPAGATE_INHERIT | ACL_ENTRY_INHERIT_ONLY)) 1153228072Sbapt return (EINVAL); 1154250125Sjkim } 1155228072Sbapt } 1156228072Sbapt 1157228072Sbapt return (0); 1158250125Sjkim} 1159228072Sbapt