subr_acl_posix1e.c revision 232936
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: head/sys/kern/subr_acl_posix1e.c 232936 2012-03-13 20:27:48Z adrian $"); 38116182Sobrien 3954803Srwatson#include <sys/param.h> 40232936Sadrian#include <sys/kernel.h> 41232936Sadrian#include <sys/module.h> 4254803Srwatson#include <sys/systm.h> 43150262Scsjp#include <sys/mount.h> 44164033Srwatson#include <sys/priv.h> 4554803Srwatson#include <sys/vnode.h> 4654803Srwatson#include <sys/errno.h> 4754803Srwatson#include <sys/stat.h> 4854803Srwatson#include <sys/acl.h> 4954803Srwatson 5054803Srwatson/* 51160597Srwatson * Implement a version of vaccess() that understands POSIX.1e ACL semantics; 52164033Srwatson * the access ACL has already been prepared for evaluation by the file system 53164033Srwatson * and is passed via 'uid', 'gid', and 'acl'. Return 0 on success, else an 54164033Srwatson * errno value. 5573890Srwatson */ 5673890Srwatsonint 5775571Srwatsonvaccess_acl_posix1e(enum vtype type, uid_t file_uid, gid_t file_gid, 58184427Strasz struct acl *acl, accmode_t accmode, struct ucred *cred, int *privused) 5973890Srwatson{ 6073890Srwatson struct acl_entry *acl_other, *acl_mask; 61184413Strasz accmode_t dac_granted; 62184413Strasz accmode_t priv_granted; 63184413Strasz accmode_t acl_mask_granted; 6473890Srwatson int group_matched, i; 6573890Srwatson 66197680Strasz KASSERT((accmode & ~(VEXEC | VWRITE | VREAD | VADMIN | VAPPEND)) == 0, 67197680Strasz ("invalid bit in accmode")); 68201019Strasz KASSERT((accmode & VAPPEND) == 0 || (accmode & VWRITE), 69201019Strasz ("VAPPEND without VWRITE")); 70197680Strasz 7173890Srwatson /* 7273890Srwatson * Look for a normal, non-privileged way to access the file/directory 73160597Srwatson * as requested. If it exists, go with that. Otherwise, attempt to 74164033Srwatson * use privileges granted via priv_granted. In some cases, which 75160597Srwatson * privileges to use may be ambiguous due to "best match", in which 76160597Srwatson * case fall back on first match for the time being. 7773890Srwatson */ 7873890Srwatson if (privused != NULL) 7973890Srwatson *privused = 0; 8073890Srwatson 8173890Srwatson /* 82160597Srwatson * Determine privileges now, but don't apply until we've found a DAC 83164033Srwatson * entry that matches but has failed to allow access. 84164033Srwatson * 85164033Srwatson * XXXRW: Ideally, we'd determine the privileges required before 86164033Srwatson * asking for them. 8773890Srwatson */ 88164033Srwatson priv_granted = 0; 8973890Srwatson 9073890Srwatson if (type == VDIR) { 91184427Strasz if ((accmode & VEXEC) && !priv_check_cred(cred, 92170587Srwatson PRIV_VFS_LOOKUP, 0)) 93164033Srwatson priv_granted |= VEXEC; 9473890Srwatson } else { 95212002Sjh /* 96212002Sjh * Ensure that at least one execute bit is on. Otherwise, 97212002Sjh * a privileged user will always succeed, and we don't want 98212002Sjh * this to happen unless the file really is executable. 99212002Sjh */ 100212002Sjh if ((accmode & VEXEC) && (acl_posix1e_acl_to_mode(acl) & 101212002Sjh (S_IXUSR | S_IXGRP | S_IXOTH)) != 0 && 102212002Sjh !priv_check_cred(cred, PRIV_VFS_EXEC, 0)) 103164033Srwatson priv_granted |= VEXEC; 10473890Srwatson } 10573890Srwatson 106184427Strasz if ((accmode & VREAD) && !priv_check_cred(cred, PRIV_VFS_READ, 0)) 107164033Srwatson priv_granted |= VREAD; 10873890Srwatson 109184427Strasz if (((accmode & VWRITE) || (accmode & VAPPEND)) && 110170587Srwatson !priv_check_cred(cred, PRIV_VFS_WRITE, 0)) 111164033Srwatson priv_granted |= (VWRITE | VAPPEND); 11273890Srwatson 113184427Strasz if ((accmode & VADMIN) && !priv_check_cred(cred, PRIV_VFS_ADMIN, 0)) 114164033Srwatson priv_granted |= VADMIN; 11573890Srwatson 11673890Srwatson /* 11782255Srwatson * The owner matches if the effective uid associated with the 11882255Srwatson * credential matches that of the ACL_USER_OBJ entry. While we're 119160597Srwatson * doing the first scan, also cache the location of the ACL_MASK and 120160597Srwatson * ACL_OTHER entries, preventing some future iterations. 12173890Srwatson */ 12273890Srwatson acl_mask = acl_other = NULL; 12373890Srwatson for (i = 0; i < acl->acl_cnt; i++) { 12473890Srwatson switch (acl->acl_entry[i].ae_tag) { 12573890Srwatson case ACL_USER_OBJ: 12675571Srwatson if (file_uid != cred->cr_uid) 12773890Srwatson break; 12873890Srwatson dac_granted = 0; 12973890Srwatson dac_granted |= VADMIN; 13075404Sjedgar if (acl->acl_entry[i].ae_perm & ACL_EXECUTE) 13173890Srwatson dac_granted |= VEXEC; 13275404Sjedgar if (acl->acl_entry[i].ae_perm & ACL_READ) 13373890Srwatson dac_granted |= VREAD; 13475404Sjedgar if (acl->acl_entry[i].ae_perm & ACL_WRITE) 135100481Srwatson dac_granted |= (VWRITE | VAPPEND); 136184427Strasz if ((accmode & dac_granted) == accmode) 13773890Srwatson return (0); 138164033Srwatson 139164033Srwatson /* 140164033Srwatson * XXXRW: Do privilege lookup here. 141164033Srwatson */ 142184427Strasz if ((accmode & (dac_granted | priv_granted)) == 143184427Strasz accmode) { 14473890Srwatson if (privused != NULL) 14573890Srwatson *privused = 1; 14673890Srwatson return (0); 14773890Srwatson } 14873890Srwatson goto error; 14973890Srwatson 15073890Srwatson case ACL_MASK: 15173890Srwatson acl_mask = &acl->acl_entry[i]; 15273890Srwatson break; 15373890Srwatson 15473890Srwatson case ACL_OTHER: 15573890Srwatson acl_other = &acl->acl_entry[i]; 15673890Srwatson break; 15773890Srwatson 15873890Srwatson default: 15992666Speter break; 16073890Srwatson } 16173890Srwatson } 16273890Srwatson 16373890Srwatson /* 164160597Srwatson * An ACL_OTHER entry should always exist in a valid access ACL. If 165160597Srwatson * it doesn't, then generate a serious failure. For now, this means 166160597Srwatson * a debugging message and EPERM, but in the future should probably 167160597Srwatson * be a panic. 16873890Srwatson */ 16973890Srwatson if (acl_other == NULL) { 17073890Srwatson /* 17182255Srwatson * XXX This should never happen 17273890Srwatson */ 17373890Srwatson printf("vaccess_acl_posix1e: ACL_OTHER missing\n"); 17473890Srwatson return (EPERM); 17573890Srwatson } 17682255Srwatson 17782255Srwatson /* 178160597Srwatson * Checks against ACL_USER, ACL_GROUP_OBJ, and ACL_GROUP fields are 179160597Srwatson * masked by an ACL_MASK entry, if any. As such, first identify the 180160597Srwatson * ACL_MASK field, then iterate through identifying potential user 181160597Srwatson * matches, then group matches. If there is no ACL_MASK, assume that 182160597Srwatson * the mask allows all requests to succeed. 18382255Srwatson */ 18473890Srwatson if (acl_mask != NULL) { 18573890Srwatson acl_mask_granted = 0; 18675404Sjedgar if (acl_mask->ae_perm & ACL_EXECUTE) 18773890Srwatson acl_mask_granted |= VEXEC; 18875404Sjedgar if (acl_mask->ae_perm & ACL_READ) 18973890Srwatson acl_mask_granted |= VREAD; 19075404Sjedgar if (acl_mask->ae_perm & ACL_WRITE) 191100481Srwatson acl_mask_granted |= (VWRITE | VAPPEND); 19273890Srwatson } else 193100481Srwatson acl_mask_granted = VEXEC | VREAD | VWRITE | VAPPEND; 19473890Srwatson 19573890Srwatson /* 196164033Srwatson * Check ACL_USER ACL entries. There will either be one or no 197164033Srwatson * matches; if there is one, we accept or rejected based on the 198164033Srwatson * match; otherwise, we continue on to groups. 19973890Srwatson */ 20073890Srwatson for (i = 0; i < acl->acl_cnt; i++) { 20173890Srwatson switch (acl->acl_entry[i].ae_tag) { 20273890Srwatson case ACL_USER: 20373890Srwatson if (acl->acl_entry[i].ae_id != cred->cr_uid) 20473890Srwatson break; 20573890Srwatson dac_granted = 0; 20675404Sjedgar if (acl->acl_entry[i].ae_perm & ACL_EXECUTE) 20773890Srwatson dac_granted |= VEXEC; 20875404Sjedgar if (acl->acl_entry[i].ae_perm & ACL_READ) 20973890Srwatson dac_granted |= VREAD; 21075404Sjedgar if (acl->acl_entry[i].ae_perm & ACL_WRITE) 211100481Srwatson dac_granted |= (VWRITE | VAPPEND); 21273890Srwatson dac_granted &= acl_mask_granted; 213184427Strasz if ((accmode & dac_granted) == accmode) 21473890Srwatson return (0); 215164033Srwatson /* 216164033Srwatson * XXXRW: Do privilege lookup here. 217164033Srwatson */ 218184427Strasz if ((accmode & (dac_granted | priv_granted)) != 219184427Strasz accmode) 22075571Srwatson goto error; 22175571Srwatson 22275571Srwatson if (privused != NULL) 22375571Srwatson *privused = 1; 22475571Srwatson return (0); 22573890Srwatson } 22673890Srwatson } 22773890Srwatson 22873890Srwatson /* 229160597Srwatson * Group match is best-match, not first-match, so find a "best" 230160597Srwatson * match. Iterate across, testing each potential group match. Make 231160597Srwatson * sure we keep track of whether we found a match or not, so that we 232160597Srwatson * know if we should try again with any available privilege, or if we 233160597Srwatson * should move on to ACL_OTHER. 23473890Srwatson */ 23573890Srwatson group_matched = 0; 23673890Srwatson for (i = 0; i < acl->acl_cnt; i++) { 23773890Srwatson switch (acl->acl_entry[i].ae_tag) { 23873890Srwatson case ACL_GROUP_OBJ: 23975888Stmm if (!groupmember(file_gid, cred)) 24075571Srwatson break; 24175571Srwatson dac_granted = 0; 24275571Srwatson if (acl->acl_entry[i].ae_perm & ACL_EXECUTE) 24375571Srwatson dac_granted |= VEXEC; 24475571Srwatson if (acl->acl_entry[i].ae_perm & ACL_READ) 24575571Srwatson dac_granted |= VREAD; 24675571Srwatson if (acl->acl_entry[i].ae_perm & ACL_WRITE) 247100481Srwatson dac_granted |= (VWRITE | VAPPEND); 24875571Srwatson dac_granted &= acl_mask_granted; 24975571Srwatson 250184427Strasz if ((accmode & dac_granted) == accmode) 25175571Srwatson return (0); 25275571Srwatson 25375571Srwatson group_matched = 1; 25475571Srwatson break; 25575571Srwatson 25673890Srwatson case ACL_GROUP: 25775571Srwatson if (!groupmember(acl->acl_entry[i].ae_id, cred)) 25875571Srwatson break; 25975571Srwatson dac_granted = 0; 26075571Srwatson if (acl->acl_entry[i].ae_perm & ACL_EXECUTE) 26175571Srwatson dac_granted |= VEXEC; 26275571Srwatson if (acl->acl_entry[i].ae_perm & ACL_READ) 26375571Srwatson dac_granted |= VREAD; 26475571Srwatson if (acl->acl_entry[i].ae_perm & ACL_WRITE) 265100481Srwatson dac_granted |= (VWRITE | VAPPEND); 26675571Srwatson dac_granted &= acl_mask_granted; 26773890Srwatson 268184427Strasz if ((accmode & dac_granted) == accmode) 26975571Srwatson return (0); 27073890Srwatson 27175571Srwatson group_matched = 1; 27275571Srwatson break; 27375571Srwatson 27473890Srwatson default: 27592666Speter break; 27673890Srwatson } 27773890Srwatson } 27873890Srwatson 27973890Srwatson if (group_matched == 1) { 28073890Srwatson /* 281160597Srwatson * There was a match, but it did not grant rights via pure 282160597Srwatson * DAC. Try again, this time with privilege. 28373890Srwatson */ 28473890Srwatson for (i = 0; i < acl->acl_cnt; i++) { 28573890Srwatson switch (acl->acl_entry[i].ae_tag) { 28673890Srwatson case ACL_GROUP_OBJ: 28776139Srwatson if (!groupmember(file_gid, cred)) 28875571Srwatson break; 28975571Srwatson dac_granted = 0; 29075571Srwatson if (acl->acl_entry[i].ae_perm & ACL_EXECUTE) 29175571Srwatson dac_granted |= VEXEC; 29275571Srwatson if (acl->acl_entry[i].ae_perm & ACL_READ) 29375571Srwatson dac_granted |= VREAD; 29475571Srwatson if (acl->acl_entry[i].ae_perm & ACL_WRITE) 295100481Srwatson dac_granted |= (VWRITE | VAPPEND); 29675571Srwatson dac_granted &= acl_mask_granted; 29775571Srwatson 298164033Srwatson /* 299164033Srwatson * XXXRW: Do privilege lookup here. 300164033Srwatson */ 301184427Strasz if ((accmode & (dac_granted | priv_granted)) 302184427Strasz != accmode) 30375571Srwatson break; 30475571Srwatson 30575571Srwatson if (privused != NULL) 30675571Srwatson *privused = 1; 30775571Srwatson return (0); 30875571Srwatson 30973890Srwatson case ACL_GROUP: 31075571Srwatson if (!groupmember(acl->acl_entry[i].ae_id, 31175571Srwatson cred)) 31275571Srwatson break; 31375571Srwatson dac_granted = 0; 31475571Srwatson if (acl->acl_entry[i].ae_perm & ACL_EXECUTE) 31575571Srwatson dac_granted |= VEXEC; 31675571Srwatson if (acl->acl_entry[i].ae_perm & ACL_READ) 31775571Srwatson dac_granted |= VREAD; 31875571Srwatson if (acl->acl_entry[i].ae_perm & ACL_WRITE) 319100481Srwatson dac_granted |= (VWRITE | VAPPEND); 32075571Srwatson dac_granted &= acl_mask_granted; 32175571Srwatson 322164033Srwatson /* 323164033Srwatson * XXXRW: Do privilege lookup here. 324164033Srwatson */ 325184427Strasz if ((accmode & (dac_granted | priv_granted)) 326184427Strasz != accmode) 32775571Srwatson break; 32875571Srwatson 32975571Srwatson if (privused != NULL) 33075571Srwatson *privused = 1; 33175571Srwatson return (0); 33275571Srwatson 33373890Srwatson default: 33492666Speter break; 33573890Srwatson } 33673890Srwatson } 33773890Srwatson /* 33873890Srwatson * Even with privilege, group membership was not sufficient. 33973890Srwatson * Return failure. 34073890Srwatson */ 34173890Srwatson goto error; 34273890Srwatson } 34373890Srwatson 34473890Srwatson /* 34573890Srwatson * Fall back on ACL_OTHER. ACL_MASK is not applied to ACL_OTHER. 34673890Srwatson */ 34773890Srwatson dac_granted = 0; 34875404Sjedgar if (acl_other->ae_perm & ACL_EXECUTE) 34973890Srwatson dac_granted |= VEXEC; 35075404Sjedgar if (acl_other->ae_perm & ACL_READ) 35173890Srwatson dac_granted |= VREAD; 35275404Sjedgar if (acl_other->ae_perm & ACL_WRITE) 353100481Srwatson dac_granted |= (VWRITE | VAPPEND); 35473890Srwatson 355184427Strasz if ((accmode & dac_granted) == accmode) 35673890Srwatson return (0); 357164033Srwatson /* 358164033Srwatson * XXXRW: Do privilege lookup here. 359164033Srwatson */ 360184427Strasz if ((accmode & (dac_granted | priv_granted)) == accmode) { 36173890Srwatson if (privused != NULL) 36273890Srwatson *privused = 1; 36373890Srwatson return (0); 36473890Srwatson } 36573890Srwatson 36673890Srwatsonerror: 367184427Strasz return ((accmode & VADMIN) ? EPERM : EACCES); 36873890Srwatson} 36973890Srwatson 37073890Srwatson/* 371160597Srwatson * For the purposes of filesystems maintaining the _OBJ entries in an inode 372160597Srwatson * with a mode_t field, this routine converts a mode_t entry to an 373160597Srwatson * acl_perm_t. 37473890Srwatson */ 37573890Srwatsonacl_perm_t 37673890Srwatsonacl_posix1e_mode_to_perm(acl_tag_t tag, mode_t mode) 37773890Srwatson{ 37873890Srwatson acl_perm_t perm = 0; 37973890Srwatson 38073890Srwatson switch(tag) { 38173890Srwatson case ACL_USER_OBJ: 38273890Srwatson if (mode & S_IXUSR) 38375404Sjedgar perm |= ACL_EXECUTE; 38473890Srwatson if (mode & S_IRUSR) 38575404Sjedgar perm |= ACL_READ; 38673890Srwatson if (mode & S_IWUSR) 38775404Sjedgar perm |= ACL_WRITE; 38873890Srwatson return (perm); 38973890Srwatson 39073890Srwatson case ACL_GROUP_OBJ: 39173890Srwatson if (mode & S_IXGRP) 39275404Sjedgar perm |= ACL_EXECUTE; 39373890Srwatson if (mode & S_IRGRP) 39475404Sjedgar perm |= ACL_READ; 39573890Srwatson if (mode & S_IWGRP) 39675404Sjedgar perm |= ACL_WRITE; 39773890Srwatson return (perm); 39873890Srwatson 39973890Srwatson case ACL_OTHER: 40073890Srwatson if (mode & S_IXOTH) 40175404Sjedgar perm |= ACL_EXECUTE; 40273890Srwatson if (mode & S_IROTH) 40375404Sjedgar perm |= ACL_READ; 40473890Srwatson if (mode & S_IWOTH) 40575404Sjedgar perm |= ACL_WRITE; 40673890Srwatson return (perm); 40773890Srwatson 40873890Srwatson default: 40973890Srwatson printf("acl_posix1e_mode_to_perm: invalid tag (%d)\n", tag); 41073890Srwatson return (0); 41173890Srwatson } 41273890Srwatson} 41373890Srwatson 41473890Srwatson/* 41573890Srwatson * Given inode information (uid, gid, mode), return an acl entry of the 41673890Srwatson * appropriate type. 41773890Srwatson */ 41873890Srwatsonstruct acl_entry 41973890Srwatsonacl_posix1e_mode_to_entry(acl_tag_t tag, uid_t uid, gid_t gid, mode_t mode) 42073890Srwatson{ 42173890Srwatson struct acl_entry acl_entry; 42273890Srwatson 42373890Srwatson acl_entry.ae_tag = tag; 42473890Srwatson acl_entry.ae_perm = acl_posix1e_mode_to_perm(tag, mode); 425192586Strasz acl_entry.ae_entry_type = 0; 426192586Strasz acl_entry.ae_flags = 0; 42773890Srwatson switch(tag) { 42873890Srwatson case ACL_USER_OBJ: 42973890Srwatson acl_entry.ae_id = uid; 43073890Srwatson break; 43173890Srwatson 43273890Srwatson case ACL_GROUP_OBJ: 43373890Srwatson acl_entry.ae_id = gid; 43473890Srwatson break; 43573890Srwatson 43673890Srwatson case ACL_OTHER: 43782769Sjedgar acl_entry.ae_id = ACL_UNDEFINED_ID; 43873890Srwatson break; 43973890Srwatson 44073890Srwatson default: 44182769Sjedgar acl_entry.ae_id = ACL_UNDEFINED_ID; 44273890Srwatson printf("acl_posix1e_mode_to_entry: invalid tag (%d)\n", tag); 44373890Srwatson } 44473890Srwatson 44573890Srwatson return (acl_entry); 44673890Srwatson} 44773890Srwatson 44873890Srwatson/* 44973890Srwatson * Utility function to generate a file mode given appropriate ACL entries. 45073890Srwatson */ 45173890Srwatsonmode_t 45273890Srwatsonacl_posix1e_perms_to_mode(struct acl_entry *acl_user_obj_entry, 45373890Srwatson struct acl_entry *acl_group_obj_entry, struct acl_entry *acl_other_entry) 45473890Srwatson{ 45573890Srwatson mode_t mode; 45673890Srwatson 45773890Srwatson mode = 0; 45875404Sjedgar if (acl_user_obj_entry->ae_perm & ACL_EXECUTE) 45973890Srwatson mode |= S_IXUSR; 46075404Sjedgar if (acl_user_obj_entry->ae_perm & ACL_READ) 46173890Srwatson mode |= S_IRUSR; 46275404Sjedgar if (acl_user_obj_entry->ae_perm & ACL_WRITE) 46373890Srwatson mode |= S_IWUSR; 46475404Sjedgar if (acl_group_obj_entry->ae_perm & ACL_EXECUTE) 46573890Srwatson mode |= S_IXGRP; 46675404Sjedgar if (acl_group_obj_entry->ae_perm & ACL_READ) 46773890Srwatson mode |= S_IRGRP; 46875404Sjedgar if (acl_group_obj_entry->ae_perm & ACL_WRITE) 46973890Srwatson mode |= S_IWGRP; 47075404Sjedgar if (acl_other_entry->ae_perm & ACL_EXECUTE) 47173890Srwatson mode |= S_IXOTH; 47275404Sjedgar if (acl_other_entry->ae_perm & ACL_READ) 47373890Srwatson mode |= S_IROTH; 47475404Sjedgar if (acl_other_entry->ae_perm & ACL_WRITE) 47573890Srwatson mode |= S_IWOTH; 47673890Srwatson 47773890Srwatson return (mode); 47873890Srwatson} 47973890Srwatson 48073890Srwatson/* 481160597Srwatson * Utility function to generate a file mode given a complete POSIX.1e access 482160597Srwatson * ACL. Note that if the ACL is improperly formed, this may result in a 483160597Srwatson * panic. 484118407Srwatson */ 485118407Srwatsonmode_t 486118407Srwatsonacl_posix1e_acl_to_mode(struct acl *acl) 487118407Srwatson{ 488118407Srwatson struct acl_entry *acl_mask, *acl_user_obj, *acl_group_obj, *acl_other; 489118407Srwatson int i; 490118407Srwatson 491118407Srwatson /* 492118407Srwatson * Find the ACL entries relevant to a POSIX permission mode. 493118407Srwatson */ 494118407Srwatson acl_user_obj = acl_group_obj = acl_other = acl_mask = NULL; 495118407Srwatson for (i = 0; i < acl->acl_cnt; i++) { 496118407Srwatson switch (acl->acl_entry[i].ae_tag) { 497118407Srwatson case ACL_USER_OBJ: 498118407Srwatson acl_user_obj = &acl->acl_entry[i]; 499118407Srwatson break; 500118407Srwatson 501118407Srwatson case ACL_GROUP_OBJ: 502118407Srwatson acl_group_obj = &acl->acl_entry[i]; 503118407Srwatson break; 504118407Srwatson 505118407Srwatson case ACL_OTHER: 506118407Srwatson acl_other = &acl->acl_entry[i]; 507118407Srwatson break; 508118407Srwatson 509118407Srwatson case ACL_MASK: 510118407Srwatson acl_mask = &acl->acl_entry[i]; 511118407Srwatson break; 512118407Srwatson 513118407Srwatson case ACL_USER: 514118407Srwatson case ACL_GROUP: 515118407Srwatson break; 516118407Srwatson 517118407Srwatson default: 518118407Srwatson panic("acl_posix1e_acl_to_mode: bad ae_tag"); 519118407Srwatson } 520118407Srwatson } 521118407Srwatson 522118407Srwatson if (acl_user_obj == NULL || acl_group_obj == NULL || acl_other == NULL) 523118407Srwatson panic("acl_posix1e_acl_to_mode: missing base ae_tags"); 524118407Srwatson 525118407Srwatson /* 526118407Srwatson * POSIX.1e specifies that if there is an ACL_MASK entry, we replace 527118407Srwatson * the mode "group" bits with its permissions. If there isn't, we 528118407Srwatson * use the ACL_GROUP_OBJ permissions. 529118407Srwatson */ 530118407Srwatson if (acl_mask != NULL) 531118407Srwatson return (acl_posix1e_perms_to_mode(acl_user_obj, acl_mask, 532118407Srwatson acl_other)); 533118407Srwatson else 534118407Srwatson return (acl_posix1e_perms_to_mode(acl_user_obj, acl_group_obj, 535118407Srwatson acl_other)); 536118407Srwatson} 537118407Srwatson 538118407Srwatson/* 539160597Srwatson * Perform a syntactic check of the ACL, sufficient to allow an implementing 540160597Srwatson * filesystem to determine if it should accept this and rely on the POSIX.1e 541160597Srwatson * ACL properties. 54273890Srwatson */ 54373890Srwatsonint 54473890Srwatsonacl_posix1e_check(struct acl *acl) 54573890Srwatson{ 54673890Srwatson int num_acl_user_obj, num_acl_user, num_acl_group_obj, num_acl_group; 54773890Srwatson int num_acl_mask, num_acl_other, i; 54873890Srwatson 54973890Srwatson /* 55073890Srwatson * Verify that the number of entries does not exceed the maximum 55173890Srwatson * defined for acl_t. 552160597Srwatson * 55373890Srwatson * Verify that the correct number of various sorts of ae_tags are 55473890Srwatson * present: 55573890Srwatson * Exactly one ACL_USER_OBJ 55673890Srwatson * Exactly one ACL_GROUP_OBJ 55773890Srwatson * Exactly one ACL_OTHER 55873890Srwatson * If any ACL_USER or ACL_GROUP entries appear, then exactly one 55973890Srwatson * ACL_MASK entry must also appear. 560160597Srwatson * 56173890Srwatson * Verify that all ae_perm entries are in ACL_PERM_BITS. 562160597Srwatson * 56373890Srwatson * Verify all ae_tag entries are understood by this implementation. 564160597Srwatson * 56573890Srwatson * Note: Does not check for uniqueness of qualifier (ae_id) field. 56673890Srwatson */ 56773890Srwatson num_acl_user_obj = num_acl_user = num_acl_group_obj = num_acl_group = 56873890Srwatson num_acl_mask = num_acl_other = 0; 569208780Strasz if (acl->acl_cnt > ACL_MAX_ENTRIES) 57073890Srwatson return (EINVAL); 57173890Srwatson for (i = 0; i < acl->acl_cnt; i++) { 57273890Srwatson /* 57373890Srwatson * Check for a valid tag. 57473890Srwatson */ 57573890Srwatson switch(acl->acl_entry[i].ae_tag) { 57673890Srwatson case ACL_USER_OBJ: 57775571Srwatson acl->acl_entry[i].ae_id = ACL_UNDEFINED_ID; /* XXX */ 57875571Srwatson if (acl->acl_entry[i].ae_id != ACL_UNDEFINED_ID) 57975571Srwatson return (EINVAL); 58073890Srwatson num_acl_user_obj++; 58173890Srwatson break; 58273890Srwatson case ACL_GROUP_OBJ: 58375571Srwatson acl->acl_entry[i].ae_id = ACL_UNDEFINED_ID; /* XXX */ 58475571Srwatson if (acl->acl_entry[i].ae_id != ACL_UNDEFINED_ID) 58575571Srwatson return (EINVAL); 58673890Srwatson num_acl_group_obj++; 58773890Srwatson break; 58873890Srwatson case ACL_USER: 58975571Srwatson if (acl->acl_entry[i].ae_id == ACL_UNDEFINED_ID) 59075571Srwatson return (EINVAL); 59173890Srwatson num_acl_user++; 59273890Srwatson break; 59373890Srwatson case ACL_GROUP: 59475571Srwatson if (acl->acl_entry[i].ae_id == ACL_UNDEFINED_ID) 59575571Srwatson return (EINVAL); 59673890Srwatson num_acl_group++; 59773890Srwatson break; 59873890Srwatson case ACL_OTHER: 59975571Srwatson acl->acl_entry[i].ae_id = ACL_UNDEFINED_ID; /* XXX */ 60075571Srwatson if (acl->acl_entry[i].ae_id != ACL_UNDEFINED_ID) 60175571Srwatson return (EINVAL); 60273890Srwatson num_acl_other++; 60373890Srwatson break; 60473890Srwatson case ACL_MASK: 60575571Srwatson acl->acl_entry[i].ae_id = ACL_UNDEFINED_ID; /* XXX */ 60675571Srwatson if (acl->acl_entry[i].ae_id != ACL_UNDEFINED_ID) 60775571Srwatson return (EINVAL); 60873890Srwatson num_acl_mask++; 60973890Srwatson break; 61073890Srwatson default: 61173890Srwatson return (EINVAL); 61273890Srwatson } 61373890Srwatson /* 61473890Srwatson * Check for valid perm entries. 61573890Srwatson */ 61673890Srwatson if ((acl->acl_entry[i].ae_perm | ACL_PERM_BITS) != 61773890Srwatson ACL_PERM_BITS) 61873890Srwatson return (EINVAL); 61973890Srwatson } 62073890Srwatson if ((num_acl_user_obj != 1) || (num_acl_group_obj != 1) || 62173890Srwatson (num_acl_other != 1) || (num_acl_mask != 0 && num_acl_mask != 1)) 62273890Srwatson return (EINVAL); 62373890Srwatson if (((num_acl_group != 0) || (num_acl_user != 0)) && 62473890Srwatson (num_acl_mask != 1)) 62573890Srwatson return (EINVAL); 62673890Srwatson return (0); 62773890Srwatson} 62873890Srwatson 62973890Srwatson/* 630160597Srwatson * Given a requested mode for a new object, and a default ACL, combine the 631160597Srwatson * two to produce a new mode. Be careful not to clear any bits that aren't 632160597Srwatson * intended to be affected by the POSIX.1e ACL. Eventually, this might also 633160597Srwatson * take the cmask as an argument, if we push that down into 634160597Srwatson * per-filesystem-code. 635118407Srwatson */ 636118407Srwatsonmode_t 637118407Srwatsonacl_posix1e_newfilemode(mode_t cmode, struct acl *dacl) 638118407Srwatson{ 639118407Srwatson mode_t mode; 640118407Srwatson 641118407Srwatson mode = cmode; 642118407Srwatson /* 643160597Srwatson * The current composition policy is that a permission bit must be 644160597Srwatson * set in *both* the ACL and the requested creation mode for it to 645160597Srwatson * appear in the resulting mode/ACL. First clear any possibly 646160597Srwatson * effected bits, then reconstruct. 647118407Srwatson */ 648118407Srwatson mode &= ACL_PRESERVE_MASK; 649118407Srwatson mode |= (ACL_OVERRIDE_MASK & cmode & acl_posix1e_acl_to_mode(dacl)); 650118407Srwatson 651118407Srwatson return (mode); 652118407Srwatson} 653232936Sadrian 654232936Sadrian 655232936Sadrianstatic int 656232936Sadrianacl_posix1e_modload(module_t mod, int what, void *arg) 657232936Sadrian{ 658232936Sadrian int ret; 659232936Sadrian 660232936Sadrian ret = 0; 661232936Sadrian 662232936Sadrian switch (what) { 663232936Sadrian case MOD_LOAD: 664232936Sadrian case MOD_SHUTDOWN: 665232936Sadrian break; 666232936Sadrian 667232936Sadrian case MOD_QUIESCE: 668232936Sadrian /* XXX TODO */ 669232936Sadrian ret = 0; 670232936Sadrian break; 671232936Sadrian 672232936Sadrian case MOD_UNLOAD: 673232936Sadrian /* XXX TODO */ 674232936Sadrian ret = 0; 675232936Sadrian break; 676232936Sadrian default: 677232936Sadrian ret = EINVAL; 678232936Sadrian break; 679232936Sadrian } 680232936Sadrian 681232936Sadrian return (ret); 682232936Sadrian} 683232936Sadrian 684232936Sadrianstatic moduledata_t acl_posix1e_mod = { 685232936Sadrian "acl_posix1e", 686232936Sadrian acl_posix1e_modload, 687232936Sadrian NULL 688232936Sadrian}; 689232936Sadrian 690232936SadrianDECLARE_MODULE(acl_posix1e, acl_posix1e_mod, SI_SUB_VFS, SI_ORDER_FIRST); 691232936SadrianMODULE_VERSION(acl_posix1e, 1); 692