154803Srwatson/*- 2160146Srwatson * Copyright (c) 1999-2006 Robert N. M. Watson 354803Srwatson * All rights reserved. 454803Srwatson * 585845Srwatson * This software was developed by Robert Watson for the TrustedBSD Project. 685845Srwatson * 754803Srwatson * Redistribution and use in source and binary forms, with or without 854803Srwatson * modification, are permitted provided that the following conditions 954803Srwatson * are met: 1054803Srwatson * 1. Redistributions of source code must retain the above copyright 1154803Srwatson * notice, this list of conditions and the following disclaimer. 1254803Srwatson * 2. Redistributions in binary form must reproduce the above copyright 1354803Srwatson * notice, this list of conditions and the following disclaimer in the 1454803Srwatson * documentation and/or other materials provided with the distribution. 1554803Srwatson * 1654803Srwatson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1754803Srwatson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1854803Srwatson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1954803Srwatson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2054803Srwatson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2154803Srwatson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2254803Srwatson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2354803Srwatson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2454803Srwatson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2554803Srwatson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2654803Srwatson * SUCH DAMAGE. 2754803Srwatson */ 2854803Srwatson/* 2973890Srwatson * Developed by the TrustedBSD Project. 30160146Srwatson * 31160146Srwatson * ACL support routines specific to POSIX.1e access control lists. These are 32160146Srwatson * utility routines for code common across file systems implementing POSIX.1e 33160146Srwatson * ACLs. 3454803Srwatson */ 3554803Srwatson 36116182Sobrien#include <sys/cdefs.h> 37116182Sobrien__FBSDID("$FreeBSD$"); 38116182Sobrien 3954803Srwatson#include <sys/param.h> 4054803Srwatson#include <sys/systm.h> 41150262Scsjp#include <sys/mount.h> 42164033Srwatson#include <sys/priv.h> 4354803Srwatson#include <sys/vnode.h> 4454803Srwatson#include <sys/errno.h> 4554803Srwatson#include <sys/stat.h> 4654803Srwatson#include <sys/acl.h> 4754803Srwatson 4854803Srwatson/* 49160597Srwatson * Implement a version of vaccess() that understands POSIX.1e ACL semantics; 50164033Srwatson * the access ACL has already been prepared for evaluation by the file system 51164033Srwatson * and is passed via 'uid', 'gid', and 'acl'. Return 0 on success, else an 52164033Srwatson * errno value. 5373890Srwatson */ 5473890Srwatsonint 5575571Srwatsonvaccess_acl_posix1e(enum vtype type, uid_t file_uid, gid_t file_gid, 56184427Strasz struct acl *acl, accmode_t accmode, struct ucred *cred, int *privused) 5773890Srwatson{ 5873890Srwatson struct acl_entry *acl_other, *acl_mask; 59184413Strasz accmode_t dac_granted; 60184413Strasz accmode_t priv_granted; 61184413Strasz accmode_t acl_mask_granted; 6273890Srwatson int group_matched, i; 6373890Srwatson 64197680Strasz KASSERT((accmode & ~(VEXEC | VWRITE | VREAD | VADMIN | VAPPEND)) == 0, 65197680Strasz ("invalid bit in accmode")); 66201019Strasz KASSERT((accmode & VAPPEND) == 0 || (accmode & VWRITE), 67201019Strasz ("VAPPEND without VWRITE")); 68197680Strasz 6973890Srwatson /* 7073890Srwatson * Look for a normal, non-privileged way to access the file/directory 71160597Srwatson * as requested. If it exists, go with that. Otherwise, attempt to 72164033Srwatson * use privileges granted via priv_granted. In some cases, which 73160597Srwatson * privileges to use may be ambiguous due to "best match", in which 74160597Srwatson * case fall back on first match for the time being. 7573890Srwatson */ 7673890Srwatson if (privused != NULL) 7773890Srwatson *privused = 0; 7873890Srwatson 7973890Srwatson /* 80160597Srwatson * Determine privileges now, but don't apply until we've found a DAC 81164033Srwatson * entry that matches but has failed to allow access. 82164033Srwatson * 83164033Srwatson * XXXRW: Ideally, we'd determine the privileges required before 84164033Srwatson * asking for them. 8573890Srwatson */ 86164033Srwatson priv_granted = 0; 8773890Srwatson 8873890Srwatson if (type == VDIR) { 89184427Strasz if ((accmode & VEXEC) && !priv_check_cred(cred, 90170587Srwatson PRIV_VFS_LOOKUP, 0)) 91164033Srwatson priv_granted |= VEXEC; 9273890Srwatson } else { 93212002Sjh /* 94212002Sjh * Ensure that at least one execute bit is on. Otherwise, 95212002Sjh * a privileged user will always succeed, and we don't want 96212002Sjh * this to happen unless the file really is executable. 97212002Sjh */ 98212002Sjh if ((accmode & VEXEC) && (acl_posix1e_acl_to_mode(acl) & 99212002Sjh (S_IXUSR | S_IXGRP | S_IXOTH)) != 0 && 100212002Sjh !priv_check_cred(cred, PRIV_VFS_EXEC, 0)) 101164033Srwatson priv_granted |= VEXEC; 10273890Srwatson } 10373890Srwatson 104184427Strasz if ((accmode & VREAD) && !priv_check_cred(cred, PRIV_VFS_READ, 0)) 105164033Srwatson priv_granted |= VREAD; 10673890Srwatson 107184427Strasz if (((accmode & VWRITE) || (accmode & VAPPEND)) && 108170587Srwatson !priv_check_cred(cred, PRIV_VFS_WRITE, 0)) 109164033Srwatson priv_granted |= (VWRITE | VAPPEND); 11073890Srwatson 111184427Strasz if ((accmode & VADMIN) && !priv_check_cred(cred, PRIV_VFS_ADMIN, 0)) 112164033Srwatson priv_granted |= VADMIN; 11373890Srwatson 11473890Srwatson /* 11582255Srwatson * The owner matches if the effective uid associated with the 11682255Srwatson * credential matches that of the ACL_USER_OBJ entry. While we're 117160597Srwatson * doing the first scan, also cache the location of the ACL_MASK and 118160597Srwatson * ACL_OTHER entries, preventing some future iterations. 11973890Srwatson */ 12073890Srwatson acl_mask = acl_other = NULL; 12173890Srwatson for (i = 0; i < acl->acl_cnt; i++) { 12273890Srwatson switch (acl->acl_entry[i].ae_tag) { 12373890Srwatson case ACL_USER_OBJ: 12475571Srwatson if (file_uid != cred->cr_uid) 12573890Srwatson break; 12673890Srwatson dac_granted = 0; 12773890Srwatson dac_granted |= VADMIN; 12875404Sjedgar if (acl->acl_entry[i].ae_perm & ACL_EXECUTE) 12973890Srwatson dac_granted |= VEXEC; 13075404Sjedgar if (acl->acl_entry[i].ae_perm & ACL_READ) 13173890Srwatson dac_granted |= VREAD; 13275404Sjedgar if (acl->acl_entry[i].ae_perm & ACL_WRITE) 133100481Srwatson dac_granted |= (VWRITE | VAPPEND); 134184427Strasz if ((accmode & dac_granted) == accmode) 13573890Srwatson return (0); 136164033Srwatson 137164033Srwatson /* 138164033Srwatson * XXXRW: Do privilege lookup here. 139164033Srwatson */ 140184427Strasz if ((accmode & (dac_granted | priv_granted)) == 141184427Strasz accmode) { 14273890Srwatson if (privused != NULL) 14373890Srwatson *privused = 1; 14473890Srwatson return (0); 14573890Srwatson } 14673890Srwatson goto error; 14773890Srwatson 14873890Srwatson case ACL_MASK: 14973890Srwatson acl_mask = &acl->acl_entry[i]; 15073890Srwatson break; 15173890Srwatson 15273890Srwatson case ACL_OTHER: 15373890Srwatson acl_other = &acl->acl_entry[i]; 15473890Srwatson break; 15573890Srwatson 15673890Srwatson default: 15792666Speter break; 15873890Srwatson } 15973890Srwatson } 16073890Srwatson 16173890Srwatson /* 162160597Srwatson * An ACL_OTHER entry should always exist in a valid access ACL. If 163160597Srwatson * it doesn't, then generate a serious failure. For now, this means 164160597Srwatson * a debugging message and EPERM, but in the future should probably 165160597Srwatson * be a panic. 16673890Srwatson */ 16773890Srwatson if (acl_other == NULL) { 16873890Srwatson /* 16982255Srwatson * XXX This should never happen 17073890Srwatson */ 17173890Srwatson printf("vaccess_acl_posix1e: ACL_OTHER missing\n"); 17273890Srwatson return (EPERM); 17373890Srwatson } 17482255Srwatson 17582255Srwatson /* 176160597Srwatson * Checks against ACL_USER, ACL_GROUP_OBJ, and ACL_GROUP fields are 177160597Srwatson * masked by an ACL_MASK entry, if any. As such, first identify the 178160597Srwatson * ACL_MASK field, then iterate through identifying potential user 179160597Srwatson * matches, then group matches. If there is no ACL_MASK, assume that 180160597Srwatson * the mask allows all requests to succeed. 18182255Srwatson */ 18273890Srwatson if (acl_mask != NULL) { 18373890Srwatson acl_mask_granted = 0; 18475404Sjedgar if (acl_mask->ae_perm & ACL_EXECUTE) 18573890Srwatson acl_mask_granted |= VEXEC; 18675404Sjedgar if (acl_mask->ae_perm & ACL_READ) 18773890Srwatson acl_mask_granted |= VREAD; 18875404Sjedgar if (acl_mask->ae_perm & ACL_WRITE) 189100481Srwatson acl_mask_granted |= (VWRITE | VAPPEND); 19073890Srwatson } else 191100481Srwatson acl_mask_granted = VEXEC | VREAD | VWRITE | VAPPEND; 19273890Srwatson 19373890Srwatson /* 194164033Srwatson * Check ACL_USER ACL entries. There will either be one or no 195164033Srwatson * matches; if there is one, we accept or rejected based on the 196164033Srwatson * match; otherwise, we continue on to groups. 19773890Srwatson */ 19873890Srwatson for (i = 0; i < acl->acl_cnt; i++) { 19973890Srwatson switch (acl->acl_entry[i].ae_tag) { 20073890Srwatson case ACL_USER: 20173890Srwatson if (acl->acl_entry[i].ae_id != cred->cr_uid) 20273890Srwatson break; 20373890Srwatson dac_granted = 0; 20475404Sjedgar if (acl->acl_entry[i].ae_perm & ACL_EXECUTE) 20573890Srwatson dac_granted |= VEXEC; 20675404Sjedgar if (acl->acl_entry[i].ae_perm & ACL_READ) 20773890Srwatson dac_granted |= VREAD; 20875404Sjedgar if (acl->acl_entry[i].ae_perm & ACL_WRITE) 209100481Srwatson dac_granted |= (VWRITE | VAPPEND); 21073890Srwatson dac_granted &= acl_mask_granted; 211184427Strasz if ((accmode & dac_granted) == accmode) 21273890Srwatson return (0); 213164033Srwatson /* 214164033Srwatson * XXXRW: Do privilege lookup here. 215164033Srwatson */ 216184427Strasz if ((accmode & (dac_granted | priv_granted)) != 217184427Strasz accmode) 21875571Srwatson goto error; 21975571Srwatson 22075571Srwatson if (privused != NULL) 22175571Srwatson *privused = 1; 22275571Srwatson return (0); 22373890Srwatson } 22473890Srwatson } 22573890Srwatson 22673890Srwatson /* 227160597Srwatson * Group match is best-match, not first-match, so find a "best" 228160597Srwatson * match. Iterate across, testing each potential group match. Make 229160597Srwatson * sure we keep track of whether we found a match or not, so that we 230160597Srwatson * know if we should try again with any available privilege, or if we 231160597Srwatson * should move on to ACL_OTHER. 23273890Srwatson */ 23373890Srwatson group_matched = 0; 23473890Srwatson for (i = 0; i < acl->acl_cnt; i++) { 23573890Srwatson switch (acl->acl_entry[i].ae_tag) { 23673890Srwatson case ACL_GROUP_OBJ: 23775888Stmm if (!groupmember(file_gid, cred)) 23875571Srwatson break; 23975571Srwatson dac_granted = 0; 24075571Srwatson if (acl->acl_entry[i].ae_perm & ACL_EXECUTE) 24175571Srwatson dac_granted |= VEXEC; 24275571Srwatson if (acl->acl_entry[i].ae_perm & ACL_READ) 24375571Srwatson dac_granted |= VREAD; 24475571Srwatson if (acl->acl_entry[i].ae_perm & ACL_WRITE) 245100481Srwatson dac_granted |= (VWRITE | VAPPEND); 24675571Srwatson dac_granted &= acl_mask_granted; 24775571Srwatson 248184427Strasz if ((accmode & dac_granted) == accmode) 24975571Srwatson return (0); 25075571Srwatson 25175571Srwatson group_matched = 1; 25275571Srwatson break; 25375571Srwatson 25473890Srwatson case ACL_GROUP: 25575571Srwatson if (!groupmember(acl->acl_entry[i].ae_id, cred)) 25675571Srwatson break; 25775571Srwatson dac_granted = 0; 25875571Srwatson if (acl->acl_entry[i].ae_perm & ACL_EXECUTE) 25975571Srwatson dac_granted |= VEXEC; 26075571Srwatson if (acl->acl_entry[i].ae_perm & ACL_READ) 26175571Srwatson dac_granted |= VREAD; 26275571Srwatson if (acl->acl_entry[i].ae_perm & ACL_WRITE) 263100481Srwatson dac_granted |= (VWRITE | VAPPEND); 26475571Srwatson dac_granted &= acl_mask_granted; 26573890Srwatson 266184427Strasz if ((accmode & dac_granted) == accmode) 26775571Srwatson return (0); 26873890Srwatson 26975571Srwatson group_matched = 1; 27075571Srwatson break; 27175571Srwatson 27273890Srwatson default: 27392666Speter break; 27473890Srwatson } 27573890Srwatson } 27673890Srwatson 27773890Srwatson if (group_matched == 1) { 27873890Srwatson /* 279160597Srwatson * There was a match, but it did not grant rights via pure 280160597Srwatson * DAC. Try again, this time with privilege. 28173890Srwatson */ 28273890Srwatson for (i = 0; i < acl->acl_cnt; i++) { 28373890Srwatson switch (acl->acl_entry[i].ae_tag) { 28473890Srwatson case ACL_GROUP_OBJ: 28576139Srwatson if (!groupmember(file_gid, cred)) 28675571Srwatson break; 28775571Srwatson dac_granted = 0; 28875571Srwatson if (acl->acl_entry[i].ae_perm & ACL_EXECUTE) 28975571Srwatson dac_granted |= VEXEC; 29075571Srwatson if (acl->acl_entry[i].ae_perm & ACL_READ) 29175571Srwatson dac_granted |= VREAD; 29275571Srwatson if (acl->acl_entry[i].ae_perm & ACL_WRITE) 293100481Srwatson dac_granted |= (VWRITE | VAPPEND); 29475571Srwatson dac_granted &= acl_mask_granted; 29575571Srwatson 296164033Srwatson /* 297164033Srwatson * XXXRW: Do privilege lookup here. 298164033Srwatson */ 299184427Strasz if ((accmode & (dac_granted | priv_granted)) 300184427Strasz != accmode) 30175571Srwatson break; 30275571Srwatson 30375571Srwatson if (privused != NULL) 30475571Srwatson *privused = 1; 30575571Srwatson return (0); 30675571Srwatson 30773890Srwatson case ACL_GROUP: 30875571Srwatson if (!groupmember(acl->acl_entry[i].ae_id, 30975571Srwatson cred)) 31075571Srwatson break; 31175571Srwatson dac_granted = 0; 31275571Srwatson if (acl->acl_entry[i].ae_perm & ACL_EXECUTE) 31375571Srwatson dac_granted |= VEXEC; 31475571Srwatson if (acl->acl_entry[i].ae_perm & ACL_READ) 31575571Srwatson dac_granted |= VREAD; 31675571Srwatson if (acl->acl_entry[i].ae_perm & ACL_WRITE) 317100481Srwatson dac_granted |= (VWRITE | VAPPEND); 31875571Srwatson dac_granted &= acl_mask_granted; 31975571Srwatson 320164033Srwatson /* 321164033Srwatson * XXXRW: Do privilege lookup here. 322164033Srwatson */ 323184427Strasz if ((accmode & (dac_granted | priv_granted)) 324184427Strasz != accmode) 32575571Srwatson break; 32675571Srwatson 32775571Srwatson if (privused != NULL) 32875571Srwatson *privused = 1; 32975571Srwatson return (0); 33075571Srwatson 33173890Srwatson default: 33292666Speter break; 33373890Srwatson } 33473890Srwatson } 33573890Srwatson /* 33673890Srwatson * Even with privilege, group membership was not sufficient. 33773890Srwatson * Return failure. 33873890Srwatson */ 33973890Srwatson goto error; 34073890Srwatson } 34173890Srwatson 34273890Srwatson /* 34373890Srwatson * Fall back on ACL_OTHER. ACL_MASK is not applied to ACL_OTHER. 34473890Srwatson */ 34573890Srwatson dac_granted = 0; 34675404Sjedgar if (acl_other->ae_perm & ACL_EXECUTE) 34773890Srwatson dac_granted |= VEXEC; 34875404Sjedgar if (acl_other->ae_perm & ACL_READ) 34973890Srwatson dac_granted |= VREAD; 35075404Sjedgar if (acl_other->ae_perm & ACL_WRITE) 351100481Srwatson dac_granted |= (VWRITE | VAPPEND); 35273890Srwatson 353184427Strasz if ((accmode & dac_granted) == accmode) 35473890Srwatson return (0); 355164033Srwatson /* 356164033Srwatson * XXXRW: Do privilege lookup here. 357164033Srwatson */ 358184427Strasz if ((accmode & (dac_granted | priv_granted)) == accmode) { 35973890Srwatson if (privused != NULL) 36073890Srwatson *privused = 1; 36173890Srwatson return (0); 36273890Srwatson } 36373890Srwatson 36473890Srwatsonerror: 365184427Strasz return ((accmode & VADMIN) ? EPERM : EACCES); 36673890Srwatson} 36773890Srwatson 36873890Srwatson/* 369160597Srwatson * For the purposes of filesystems maintaining the _OBJ entries in an inode 370160597Srwatson * with a mode_t field, this routine converts a mode_t entry to an 371160597Srwatson * acl_perm_t. 37273890Srwatson */ 37373890Srwatsonacl_perm_t 37473890Srwatsonacl_posix1e_mode_to_perm(acl_tag_t tag, mode_t mode) 37573890Srwatson{ 37673890Srwatson acl_perm_t perm = 0; 37773890Srwatson 37873890Srwatson switch(tag) { 37973890Srwatson case ACL_USER_OBJ: 38073890Srwatson if (mode & S_IXUSR) 38175404Sjedgar perm |= ACL_EXECUTE; 38273890Srwatson if (mode & S_IRUSR) 38375404Sjedgar perm |= ACL_READ; 38473890Srwatson if (mode & S_IWUSR) 38575404Sjedgar perm |= ACL_WRITE; 38673890Srwatson return (perm); 38773890Srwatson 38873890Srwatson case ACL_GROUP_OBJ: 38973890Srwatson if (mode & S_IXGRP) 39075404Sjedgar perm |= ACL_EXECUTE; 39173890Srwatson if (mode & S_IRGRP) 39275404Sjedgar perm |= ACL_READ; 39373890Srwatson if (mode & S_IWGRP) 39475404Sjedgar perm |= ACL_WRITE; 39573890Srwatson return (perm); 39673890Srwatson 39773890Srwatson case ACL_OTHER: 39873890Srwatson if (mode & S_IXOTH) 39975404Sjedgar perm |= ACL_EXECUTE; 40073890Srwatson if (mode & S_IROTH) 40175404Sjedgar perm |= ACL_READ; 40273890Srwatson if (mode & S_IWOTH) 40375404Sjedgar perm |= ACL_WRITE; 40473890Srwatson return (perm); 40573890Srwatson 40673890Srwatson default: 40773890Srwatson printf("acl_posix1e_mode_to_perm: invalid tag (%d)\n", tag); 40873890Srwatson return (0); 40973890Srwatson } 41073890Srwatson} 41173890Srwatson 41273890Srwatson/* 41373890Srwatson * Given inode information (uid, gid, mode), return an acl entry of the 41473890Srwatson * appropriate type. 41573890Srwatson */ 41673890Srwatsonstruct acl_entry 41773890Srwatsonacl_posix1e_mode_to_entry(acl_tag_t tag, uid_t uid, gid_t gid, mode_t mode) 41873890Srwatson{ 41973890Srwatson struct acl_entry acl_entry; 42073890Srwatson 42173890Srwatson acl_entry.ae_tag = tag; 42273890Srwatson acl_entry.ae_perm = acl_posix1e_mode_to_perm(tag, mode); 423192586Strasz acl_entry.ae_entry_type = 0; 424192586Strasz acl_entry.ae_flags = 0; 42573890Srwatson switch(tag) { 42673890Srwatson case ACL_USER_OBJ: 42773890Srwatson acl_entry.ae_id = uid; 42873890Srwatson break; 42973890Srwatson 43073890Srwatson case ACL_GROUP_OBJ: 43173890Srwatson acl_entry.ae_id = gid; 43273890Srwatson break; 43373890Srwatson 43473890Srwatson case ACL_OTHER: 43582769Sjedgar acl_entry.ae_id = ACL_UNDEFINED_ID; 43673890Srwatson break; 43773890Srwatson 43873890Srwatson default: 43982769Sjedgar acl_entry.ae_id = ACL_UNDEFINED_ID; 44073890Srwatson printf("acl_posix1e_mode_to_entry: invalid tag (%d)\n", tag); 44173890Srwatson } 44273890Srwatson 44373890Srwatson return (acl_entry); 44473890Srwatson} 44573890Srwatson 44673890Srwatson/* 44773890Srwatson * Utility function to generate a file mode given appropriate ACL entries. 44873890Srwatson */ 44973890Srwatsonmode_t 45073890Srwatsonacl_posix1e_perms_to_mode(struct acl_entry *acl_user_obj_entry, 45173890Srwatson struct acl_entry *acl_group_obj_entry, struct acl_entry *acl_other_entry) 45273890Srwatson{ 45373890Srwatson mode_t mode; 45473890Srwatson 45573890Srwatson mode = 0; 45675404Sjedgar if (acl_user_obj_entry->ae_perm & ACL_EXECUTE) 45773890Srwatson mode |= S_IXUSR; 45875404Sjedgar if (acl_user_obj_entry->ae_perm & ACL_READ) 45973890Srwatson mode |= S_IRUSR; 46075404Sjedgar if (acl_user_obj_entry->ae_perm & ACL_WRITE) 46173890Srwatson mode |= S_IWUSR; 46275404Sjedgar if (acl_group_obj_entry->ae_perm & ACL_EXECUTE) 46373890Srwatson mode |= S_IXGRP; 46475404Sjedgar if (acl_group_obj_entry->ae_perm & ACL_READ) 46573890Srwatson mode |= S_IRGRP; 46675404Sjedgar if (acl_group_obj_entry->ae_perm & ACL_WRITE) 46773890Srwatson mode |= S_IWGRP; 46875404Sjedgar if (acl_other_entry->ae_perm & ACL_EXECUTE) 46973890Srwatson mode |= S_IXOTH; 47075404Sjedgar if (acl_other_entry->ae_perm & ACL_READ) 47173890Srwatson mode |= S_IROTH; 47275404Sjedgar if (acl_other_entry->ae_perm & ACL_WRITE) 47373890Srwatson mode |= S_IWOTH; 47473890Srwatson 47573890Srwatson return (mode); 47673890Srwatson} 47773890Srwatson 47873890Srwatson/* 479160597Srwatson * Utility function to generate a file mode given a complete POSIX.1e access 480160597Srwatson * ACL. Note that if the ACL is improperly formed, this may result in a 481160597Srwatson * panic. 482118407Srwatson */ 483118407Srwatsonmode_t 484118407Srwatsonacl_posix1e_acl_to_mode(struct acl *acl) 485118407Srwatson{ 486118407Srwatson struct acl_entry *acl_mask, *acl_user_obj, *acl_group_obj, *acl_other; 487118407Srwatson int i; 488118407Srwatson 489118407Srwatson /* 490118407Srwatson * Find the ACL entries relevant to a POSIX permission mode. 491118407Srwatson */ 492118407Srwatson acl_user_obj = acl_group_obj = acl_other = acl_mask = NULL; 493118407Srwatson for (i = 0; i < acl->acl_cnt; i++) { 494118407Srwatson switch (acl->acl_entry[i].ae_tag) { 495118407Srwatson case ACL_USER_OBJ: 496118407Srwatson acl_user_obj = &acl->acl_entry[i]; 497118407Srwatson break; 498118407Srwatson 499118407Srwatson case ACL_GROUP_OBJ: 500118407Srwatson acl_group_obj = &acl->acl_entry[i]; 501118407Srwatson break; 502118407Srwatson 503118407Srwatson case ACL_OTHER: 504118407Srwatson acl_other = &acl->acl_entry[i]; 505118407Srwatson break; 506118407Srwatson 507118407Srwatson case ACL_MASK: 508118407Srwatson acl_mask = &acl->acl_entry[i]; 509118407Srwatson break; 510118407Srwatson 511118407Srwatson case ACL_USER: 512118407Srwatson case ACL_GROUP: 513118407Srwatson break; 514118407Srwatson 515118407Srwatson default: 516118407Srwatson panic("acl_posix1e_acl_to_mode: bad ae_tag"); 517118407Srwatson } 518118407Srwatson } 519118407Srwatson 520118407Srwatson if (acl_user_obj == NULL || acl_group_obj == NULL || acl_other == NULL) 521118407Srwatson panic("acl_posix1e_acl_to_mode: missing base ae_tags"); 522118407Srwatson 523118407Srwatson /* 524118407Srwatson * POSIX.1e specifies that if there is an ACL_MASK entry, we replace 525118407Srwatson * the mode "group" bits with its permissions. If there isn't, we 526118407Srwatson * use the ACL_GROUP_OBJ permissions. 527118407Srwatson */ 528118407Srwatson if (acl_mask != NULL) 529118407Srwatson return (acl_posix1e_perms_to_mode(acl_user_obj, acl_mask, 530118407Srwatson acl_other)); 531118407Srwatson else 532118407Srwatson return (acl_posix1e_perms_to_mode(acl_user_obj, acl_group_obj, 533118407Srwatson acl_other)); 534118407Srwatson} 535118407Srwatson 536118407Srwatson/* 537160597Srwatson * Perform a syntactic check of the ACL, sufficient to allow an implementing 538160597Srwatson * filesystem to determine if it should accept this and rely on the POSIX.1e 539160597Srwatson * ACL properties. 54073890Srwatson */ 54173890Srwatsonint 54273890Srwatsonacl_posix1e_check(struct acl *acl) 54373890Srwatson{ 54473890Srwatson int num_acl_user_obj, num_acl_user, num_acl_group_obj, num_acl_group; 54573890Srwatson int num_acl_mask, num_acl_other, i; 54673890Srwatson 54773890Srwatson /* 54873890Srwatson * Verify that the number of entries does not exceed the maximum 54973890Srwatson * defined for acl_t. 550160597Srwatson * 55173890Srwatson * Verify that the correct number of various sorts of ae_tags are 55273890Srwatson * present: 55373890Srwatson * Exactly one ACL_USER_OBJ 55473890Srwatson * Exactly one ACL_GROUP_OBJ 55573890Srwatson * Exactly one ACL_OTHER 55673890Srwatson * If any ACL_USER or ACL_GROUP entries appear, then exactly one 55773890Srwatson * ACL_MASK entry must also appear. 558160597Srwatson * 55973890Srwatson * Verify that all ae_perm entries are in ACL_PERM_BITS. 560160597Srwatson * 56173890Srwatson * Verify all ae_tag entries are understood by this implementation. 562160597Srwatson * 56373890Srwatson * Note: Does not check for uniqueness of qualifier (ae_id) field. 56473890Srwatson */ 56573890Srwatson num_acl_user_obj = num_acl_user = num_acl_group_obj = num_acl_group = 56673890Srwatson num_acl_mask = num_acl_other = 0; 567208780Strasz if (acl->acl_cnt > ACL_MAX_ENTRIES) 56873890Srwatson return (EINVAL); 56973890Srwatson for (i = 0; i < acl->acl_cnt; i++) { 57073890Srwatson /* 57173890Srwatson * Check for a valid tag. 57273890Srwatson */ 57373890Srwatson switch(acl->acl_entry[i].ae_tag) { 57473890Srwatson case ACL_USER_OBJ: 57575571Srwatson acl->acl_entry[i].ae_id = ACL_UNDEFINED_ID; /* XXX */ 57675571Srwatson if (acl->acl_entry[i].ae_id != ACL_UNDEFINED_ID) 57775571Srwatson return (EINVAL); 57873890Srwatson num_acl_user_obj++; 57973890Srwatson break; 58073890Srwatson case ACL_GROUP_OBJ: 58175571Srwatson acl->acl_entry[i].ae_id = ACL_UNDEFINED_ID; /* XXX */ 58275571Srwatson if (acl->acl_entry[i].ae_id != ACL_UNDEFINED_ID) 58375571Srwatson return (EINVAL); 58473890Srwatson num_acl_group_obj++; 58573890Srwatson break; 58673890Srwatson case ACL_USER: 58775571Srwatson if (acl->acl_entry[i].ae_id == ACL_UNDEFINED_ID) 58875571Srwatson return (EINVAL); 58973890Srwatson num_acl_user++; 59073890Srwatson break; 59173890Srwatson case ACL_GROUP: 59275571Srwatson if (acl->acl_entry[i].ae_id == ACL_UNDEFINED_ID) 59375571Srwatson return (EINVAL); 59473890Srwatson num_acl_group++; 59573890Srwatson break; 59673890Srwatson case ACL_OTHER: 59775571Srwatson acl->acl_entry[i].ae_id = ACL_UNDEFINED_ID; /* XXX */ 59875571Srwatson if (acl->acl_entry[i].ae_id != ACL_UNDEFINED_ID) 59975571Srwatson return (EINVAL); 60073890Srwatson num_acl_other++; 60173890Srwatson break; 60273890Srwatson case ACL_MASK: 60375571Srwatson acl->acl_entry[i].ae_id = ACL_UNDEFINED_ID; /* XXX */ 60475571Srwatson if (acl->acl_entry[i].ae_id != ACL_UNDEFINED_ID) 60575571Srwatson return (EINVAL); 60673890Srwatson num_acl_mask++; 60773890Srwatson break; 60873890Srwatson default: 60973890Srwatson return (EINVAL); 61073890Srwatson } 61173890Srwatson /* 61273890Srwatson * Check for valid perm entries. 61373890Srwatson */ 61473890Srwatson if ((acl->acl_entry[i].ae_perm | ACL_PERM_BITS) != 61573890Srwatson ACL_PERM_BITS) 61673890Srwatson return (EINVAL); 61773890Srwatson } 61873890Srwatson if ((num_acl_user_obj != 1) || (num_acl_group_obj != 1) || 61973890Srwatson (num_acl_other != 1) || (num_acl_mask != 0 && num_acl_mask != 1)) 62073890Srwatson return (EINVAL); 62173890Srwatson if (((num_acl_group != 0) || (num_acl_user != 0)) && 62273890Srwatson (num_acl_mask != 1)) 62373890Srwatson return (EINVAL); 62473890Srwatson return (0); 62573890Srwatson} 62673890Srwatson 62773890Srwatson/* 628160597Srwatson * Given a requested mode for a new object, and a default ACL, combine the 629160597Srwatson * two to produce a new mode. Be careful not to clear any bits that aren't 630160597Srwatson * intended to be affected by the POSIX.1e ACL. Eventually, this might also 631160597Srwatson * take the cmask as an argument, if we push that down into 632160597Srwatson * per-filesystem-code. 633118407Srwatson */ 634118407Srwatsonmode_t 635118407Srwatsonacl_posix1e_newfilemode(mode_t cmode, struct acl *dacl) 636118407Srwatson{ 637118407Srwatson mode_t mode; 638118407Srwatson 639118407Srwatson mode = cmode; 640118407Srwatson /* 641160597Srwatson * The current composition policy is that a permission bit must be 642160597Srwatson * set in *both* the ACL and the requested creation mode for it to 643160597Srwatson * appear in the resulting mode/ACL. First clear any possibly 644160597Srwatson * effected bits, then reconstruct. 645118407Srwatson */ 646118407Srwatson mode &= ACL_PRESERVE_MASK; 647118407Srwatson mode |= (ACL_OVERRIDE_MASK & cmode & acl_posix1e_acl_to_mode(dacl)); 648118407Srwatson 649118407Srwatson return (mode); 650118407Srwatson} 651