subr_acl_posix1e.c revision 164033
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 164033 2006-11-06 13:42:10Z rwatson $"); 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, 5675571Srwatson struct acl *acl, mode_t acc_mode, struct ucred *cred, int *privused) 5773890Srwatson{ 5873890Srwatson struct acl_entry *acl_other, *acl_mask; 5973890Srwatson mode_t dac_granted; 60164033Srwatson mode_t priv_granted; 6173890Srwatson mode_t acl_mask_granted; 6273890Srwatson int group_matched, i; 6373890Srwatson 6473890Srwatson /* 6573890Srwatson * Look for a normal, non-privileged way to access the file/directory 66160597Srwatson * as requested. If it exists, go with that. Otherwise, attempt to 67164033Srwatson * use privileges granted via priv_granted. In some cases, which 68160597Srwatson * privileges to use may be ambiguous due to "best match", in which 69160597Srwatson * case fall back on first match for the time being. 7073890Srwatson */ 7173890Srwatson if (privused != NULL) 7273890Srwatson *privused = 0; 7373890Srwatson 7473890Srwatson /* 75160597Srwatson * Determine privileges now, but don't apply until we've found a DAC 76164033Srwatson * entry that matches but has failed to allow access. 77164033Srwatson * 78164033Srwatson * XXXRW: Ideally, we'd determine the privileges required before 79164033Srwatson * asking for them. 8073890Srwatson */ 81164033Srwatson priv_granted = 0; 8273890Srwatson 8373890Srwatson if (type == VDIR) { 84164033Srwatson if ((acc_mode & VEXEC) && !priv_check_cred(cred, 85164033Srwatson PRIV_VFS_LOOKUP, SUSER_ALLOWJAIL)) 86164033Srwatson priv_granted |= VEXEC; 8773890Srwatson } else { 88164033Srwatson if ((acc_mode & VEXEC) && !priv_check_cred(cred, 89164033Srwatson PRIV_VFS_EXEC, SUSER_ALLOWJAIL)) 90164033Srwatson priv_granted |= VEXEC; 9173890Srwatson } 9273890Srwatson 93164033Srwatson if ((acc_mode & VREAD) && !priv_check_cred(cred, PRIV_VFS_READ, 94132653Scperciva SUSER_ALLOWJAIL)) 95164033Srwatson priv_granted |= VREAD; 9673890Srwatson 97100481Srwatson if (((acc_mode & VWRITE) || (acc_mode & VAPPEND)) && 98164033Srwatson !priv_check_cred(cred, PRIV_VFS_WRITE, SUSER_ALLOWJAIL)) 99164033Srwatson priv_granted |= (VWRITE | VAPPEND); 10073890Srwatson 101164033Srwatson if ((acc_mode & VADMIN) && !priv_check_cred(cred, PRIV_VFS_ADMIN, 102132653Scperciva SUSER_ALLOWJAIL)) 103164033Srwatson priv_granted |= VADMIN; 10473890Srwatson 10573890Srwatson /* 10682255Srwatson * The owner matches if the effective uid associated with the 10782255Srwatson * credential matches that of the ACL_USER_OBJ entry. While we're 108160597Srwatson * doing the first scan, also cache the location of the ACL_MASK and 109160597Srwatson * ACL_OTHER entries, preventing some future iterations. 11073890Srwatson */ 11173890Srwatson acl_mask = acl_other = NULL; 11273890Srwatson for (i = 0; i < acl->acl_cnt; i++) { 11373890Srwatson switch (acl->acl_entry[i].ae_tag) { 11473890Srwatson case ACL_USER_OBJ: 11575571Srwatson if (file_uid != cred->cr_uid) 11673890Srwatson break; 11773890Srwatson dac_granted = 0; 11873890Srwatson dac_granted |= VADMIN; 11975404Sjedgar if (acl->acl_entry[i].ae_perm & ACL_EXECUTE) 12073890Srwatson dac_granted |= VEXEC; 12175404Sjedgar if (acl->acl_entry[i].ae_perm & ACL_READ) 12273890Srwatson dac_granted |= VREAD; 12375404Sjedgar if (acl->acl_entry[i].ae_perm & ACL_WRITE) 124100481Srwatson dac_granted |= (VWRITE | VAPPEND); 12573890Srwatson if ((acc_mode & dac_granted) == acc_mode) 12673890Srwatson return (0); 127164033Srwatson 128164033Srwatson /* 129164033Srwatson * XXXRW: Do privilege lookup here. 130164033Srwatson */ 131164033Srwatson if ((acc_mode & (dac_granted | priv_granted)) == 13273890Srwatson acc_mode) { 13373890Srwatson if (privused != NULL) 13473890Srwatson *privused = 1; 13573890Srwatson return (0); 13673890Srwatson } 13773890Srwatson goto error; 13873890Srwatson 13973890Srwatson case ACL_MASK: 14073890Srwatson acl_mask = &acl->acl_entry[i]; 14173890Srwatson break; 14273890Srwatson 14373890Srwatson case ACL_OTHER: 14473890Srwatson acl_other = &acl->acl_entry[i]; 14573890Srwatson break; 14673890Srwatson 14773890Srwatson default: 14892666Speter break; 14973890Srwatson } 15073890Srwatson } 15173890Srwatson 15273890Srwatson /* 153160597Srwatson * An ACL_OTHER entry should always exist in a valid access ACL. If 154160597Srwatson * it doesn't, then generate a serious failure. For now, this means 155160597Srwatson * a debugging message and EPERM, but in the future should probably 156160597Srwatson * be a panic. 15773890Srwatson */ 15873890Srwatson if (acl_other == NULL) { 15973890Srwatson /* 16082255Srwatson * XXX This should never happen 16173890Srwatson */ 16273890Srwatson printf("vaccess_acl_posix1e: ACL_OTHER missing\n"); 16373890Srwatson return (EPERM); 16473890Srwatson } 16582255Srwatson 16682255Srwatson /* 167160597Srwatson * Checks against ACL_USER, ACL_GROUP_OBJ, and ACL_GROUP fields are 168160597Srwatson * masked by an ACL_MASK entry, if any. As such, first identify the 169160597Srwatson * ACL_MASK field, then iterate through identifying potential user 170160597Srwatson * matches, then group matches. If there is no ACL_MASK, assume that 171160597Srwatson * the mask allows all requests to succeed. 17282255Srwatson */ 17373890Srwatson if (acl_mask != NULL) { 17473890Srwatson acl_mask_granted = 0; 17575404Sjedgar if (acl_mask->ae_perm & ACL_EXECUTE) 17673890Srwatson acl_mask_granted |= VEXEC; 17775404Sjedgar if (acl_mask->ae_perm & ACL_READ) 17873890Srwatson acl_mask_granted |= VREAD; 17975404Sjedgar if (acl_mask->ae_perm & ACL_WRITE) 180100481Srwatson acl_mask_granted |= (VWRITE | VAPPEND); 18173890Srwatson } else 182100481Srwatson acl_mask_granted = VEXEC | VREAD | VWRITE | VAPPEND; 18373890Srwatson 18473890Srwatson /* 185164033Srwatson * Check ACL_USER ACL entries. There will either be one or no 186164033Srwatson * matches; if there is one, we accept or rejected based on the 187164033Srwatson * match; otherwise, we continue on to groups. 18873890Srwatson */ 18973890Srwatson for (i = 0; i < acl->acl_cnt; i++) { 19073890Srwatson switch (acl->acl_entry[i].ae_tag) { 19173890Srwatson case ACL_USER: 19273890Srwatson if (acl->acl_entry[i].ae_id != cred->cr_uid) 19373890Srwatson break; 19473890Srwatson dac_granted = 0; 19575404Sjedgar if (acl->acl_entry[i].ae_perm & ACL_EXECUTE) 19673890Srwatson dac_granted |= VEXEC; 19775404Sjedgar if (acl->acl_entry[i].ae_perm & ACL_READ) 19873890Srwatson dac_granted |= VREAD; 19975404Sjedgar if (acl->acl_entry[i].ae_perm & ACL_WRITE) 200100481Srwatson dac_granted |= (VWRITE | VAPPEND); 20173890Srwatson dac_granted &= acl_mask_granted; 20273890Srwatson if ((acc_mode & dac_granted) == acc_mode) 20373890Srwatson return (0); 204164033Srwatson /* 205164033Srwatson * XXXRW: Do privilege lookup here. 206164033Srwatson */ 207164033Srwatson if ((acc_mode & (dac_granted | priv_granted)) != 20875571Srwatson acc_mode) 20975571Srwatson goto error; 21075571Srwatson 21175571Srwatson if (privused != NULL) 21275571Srwatson *privused = 1; 21375571Srwatson return (0); 21473890Srwatson } 21573890Srwatson } 21673890Srwatson 21773890Srwatson /* 218160597Srwatson * Group match is best-match, not first-match, so find a "best" 219160597Srwatson * match. Iterate across, testing each potential group match. Make 220160597Srwatson * sure we keep track of whether we found a match or not, so that we 221160597Srwatson * know if we should try again with any available privilege, or if we 222160597Srwatson * should move on to ACL_OTHER. 22373890Srwatson */ 22473890Srwatson group_matched = 0; 22573890Srwatson for (i = 0; i < acl->acl_cnt; i++) { 22673890Srwatson switch (acl->acl_entry[i].ae_tag) { 22773890Srwatson case ACL_GROUP_OBJ: 22875888Stmm if (!groupmember(file_gid, cred)) 22975571Srwatson break; 23075571Srwatson dac_granted = 0; 23175571Srwatson if (acl->acl_entry[i].ae_perm & ACL_EXECUTE) 23275571Srwatson dac_granted |= VEXEC; 23375571Srwatson if (acl->acl_entry[i].ae_perm & ACL_READ) 23475571Srwatson dac_granted |= VREAD; 23575571Srwatson if (acl->acl_entry[i].ae_perm & ACL_WRITE) 236100481Srwatson dac_granted |= (VWRITE | VAPPEND); 23775571Srwatson dac_granted &= acl_mask_granted; 23875571Srwatson 23975571Srwatson if ((acc_mode & dac_granted) == acc_mode) 24075571Srwatson return (0); 24175571Srwatson 24275571Srwatson group_matched = 1; 24375571Srwatson break; 24475571Srwatson 24573890Srwatson case ACL_GROUP: 24675571Srwatson if (!groupmember(acl->acl_entry[i].ae_id, cred)) 24775571Srwatson break; 24875571Srwatson dac_granted = 0; 24975571Srwatson if (acl->acl_entry[i].ae_perm & ACL_EXECUTE) 25075571Srwatson dac_granted |= VEXEC; 25175571Srwatson if (acl->acl_entry[i].ae_perm & ACL_READ) 25275571Srwatson dac_granted |= VREAD; 25375571Srwatson if (acl->acl_entry[i].ae_perm & ACL_WRITE) 254100481Srwatson dac_granted |= (VWRITE | VAPPEND); 25575571Srwatson dac_granted &= acl_mask_granted; 25673890Srwatson 25775571Srwatson if ((acc_mode & dac_granted) == acc_mode) 25875571Srwatson return (0); 25973890Srwatson 26075571Srwatson group_matched = 1; 26175571Srwatson break; 26275571Srwatson 26373890Srwatson default: 26492666Speter break; 26573890Srwatson } 26673890Srwatson } 26773890Srwatson 26873890Srwatson if (group_matched == 1) { 26973890Srwatson /* 270160597Srwatson * There was a match, but it did not grant rights via pure 271160597Srwatson * DAC. Try again, this time with privilege. 27273890Srwatson */ 27373890Srwatson for (i = 0; i < acl->acl_cnt; i++) { 27473890Srwatson switch (acl->acl_entry[i].ae_tag) { 27573890Srwatson case ACL_GROUP_OBJ: 27676139Srwatson if (!groupmember(file_gid, cred)) 27775571Srwatson break; 27875571Srwatson dac_granted = 0; 27975571Srwatson if (acl->acl_entry[i].ae_perm & ACL_EXECUTE) 28075571Srwatson dac_granted |= VEXEC; 28175571Srwatson if (acl->acl_entry[i].ae_perm & ACL_READ) 28275571Srwatson dac_granted |= VREAD; 28375571Srwatson if (acl->acl_entry[i].ae_perm & ACL_WRITE) 284100481Srwatson dac_granted |= (VWRITE | VAPPEND); 28575571Srwatson dac_granted &= acl_mask_granted; 28675571Srwatson 287164033Srwatson /* 288164033Srwatson * XXXRW: Do privilege lookup here. 289164033Srwatson */ 290164033Srwatson if ((acc_mode & (dac_granted | priv_granted)) 291164033Srwatson != acc_mode) 29275571Srwatson break; 29375571Srwatson 29475571Srwatson if (privused != NULL) 29575571Srwatson *privused = 1; 29675571Srwatson return (0); 29775571Srwatson 29873890Srwatson case ACL_GROUP: 29975571Srwatson if (!groupmember(acl->acl_entry[i].ae_id, 30075571Srwatson cred)) 30175571Srwatson break; 30275571Srwatson dac_granted = 0; 30375571Srwatson if (acl->acl_entry[i].ae_perm & ACL_EXECUTE) 30475571Srwatson dac_granted |= VEXEC; 30575571Srwatson if (acl->acl_entry[i].ae_perm & ACL_READ) 30675571Srwatson dac_granted |= VREAD; 30775571Srwatson if (acl->acl_entry[i].ae_perm & ACL_WRITE) 308100481Srwatson dac_granted |= (VWRITE | VAPPEND); 30975571Srwatson dac_granted &= acl_mask_granted; 31075571Srwatson 311164033Srwatson /* 312164033Srwatson * XXXRW: Do privilege lookup here. 313164033Srwatson */ 314164033Srwatson if ((acc_mode & (dac_granted | priv_granted)) 315164033Srwatson != acc_mode) 31675571Srwatson break; 31775571Srwatson 31875571Srwatson if (privused != NULL) 31975571Srwatson *privused = 1; 32075571Srwatson return (0); 32175571Srwatson 32273890Srwatson default: 32392666Speter break; 32473890Srwatson } 32573890Srwatson } 32673890Srwatson /* 32773890Srwatson * Even with privilege, group membership was not sufficient. 32873890Srwatson * Return failure. 32973890Srwatson */ 33073890Srwatson goto error; 33173890Srwatson } 33273890Srwatson 33373890Srwatson /* 33473890Srwatson * Fall back on ACL_OTHER. ACL_MASK is not applied to ACL_OTHER. 33573890Srwatson */ 33673890Srwatson dac_granted = 0; 33775404Sjedgar if (acl_other->ae_perm & ACL_EXECUTE) 33873890Srwatson dac_granted |= VEXEC; 33975404Sjedgar if (acl_other->ae_perm & ACL_READ) 34073890Srwatson dac_granted |= VREAD; 34175404Sjedgar if (acl_other->ae_perm & ACL_WRITE) 342100481Srwatson dac_granted |= (VWRITE | VAPPEND); 34373890Srwatson 34473890Srwatson if ((acc_mode & dac_granted) == acc_mode) 34573890Srwatson return (0); 346164033Srwatson /* 347164033Srwatson * XXXRW: Do privilege lookup here. 348164033Srwatson */ 349164033Srwatson if ((acc_mode & (dac_granted | priv_granted)) == acc_mode) { 35073890Srwatson if (privused != NULL) 35173890Srwatson *privused = 1; 35273890Srwatson return (0); 35373890Srwatson } 35473890Srwatson 35573890Srwatsonerror: 35673890Srwatson return ((acc_mode & VADMIN) ? EPERM : EACCES); 35773890Srwatson} 35873890Srwatson 35973890Srwatson/* 360160597Srwatson * For the purposes of filesystems maintaining the _OBJ entries in an inode 361160597Srwatson * with a mode_t field, this routine converts a mode_t entry to an 362160597Srwatson * acl_perm_t. 36373890Srwatson */ 36473890Srwatsonacl_perm_t 36573890Srwatsonacl_posix1e_mode_to_perm(acl_tag_t tag, mode_t mode) 36673890Srwatson{ 36773890Srwatson acl_perm_t perm = 0; 36873890Srwatson 36973890Srwatson switch(tag) { 37073890Srwatson case ACL_USER_OBJ: 37173890Srwatson if (mode & S_IXUSR) 37275404Sjedgar perm |= ACL_EXECUTE; 37373890Srwatson if (mode & S_IRUSR) 37475404Sjedgar perm |= ACL_READ; 37573890Srwatson if (mode & S_IWUSR) 37675404Sjedgar perm |= ACL_WRITE; 37773890Srwatson return (perm); 37873890Srwatson 37973890Srwatson case ACL_GROUP_OBJ: 38073890Srwatson if (mode & S_IXGRP) 38175404Sjedgar perm |= ACL_EXECUTE; 38273890Srwatson if (mode & S_IRGRP) 38375404Sjedgar perm |= ACL_READ; 38473890Srwatson if (mode & S_IWGRP) 38575404Sjedgar perm |= ACL_WRITE; 38673890Srwatson return (perm); 38773890Srwatson 38873890Srwatson case ACL_OTHER: 38973890Srwatson if (mode & S_IXOTH) 39075404Sjedgar perm |= ACL_EXECUTE; 39173890Srwatson if (mode & S_IROTH) 39275404Sjedgar perm |= ACL_READ; 39373890Srwatson if (mode & S_IWOTH) 39475404Sjedgar perm |= ACL_WRITE; 39573890Srwatson return (perm); 39673890Srwatson 39773890Srwatson default: 39873890Srwatson printf("acl_posix1e_mode_to_perm: invalid tag (%d)\n", tag); 39973890Srwatson return (0); 40073890Srwatson } 40173890Srwatson} 40273890Srwatson 40373890Srwatson/* 40473890Srwatson * Given inode information (uid, gid, mode), return an acl entry of the 40573890Srwatson * appropriate type. 40673890Srwatson */ 40773890Srwatsonstruct acl_entry 40873890Srwatsonacl_posix1e_mode_to_entry(acl_tag_t tag, uid_t uid, gid_t gid, mode_t mode) 40973890Srwatson{ 41073890Srwatson struct acl_entry acl_entry; 41173890Srwatson 41273890Srwatson acl_entry.ae_tag = tag; 41373890Srwatson acl_entry.ae_perm = acl_posix1e_mode_to_perm(tag, mode); 41473890Srwatson switch(tag) { 41573890Srwatson case ACL_USER_OBJ: 41673890Srwatson acl_entry.ae_id = uid; 41773890Srwatson break; 41873890Srwatson 41973890Srwatson case ACL_GROUP_OBJ: 42073890Srwatson acl_entry.ae_id = gid; 42173890Srwatson break; 42273890Srwatson 42373890Srwatson case ACL_OTHER: 42482769Sjedgar acl_entry.ae_id = ACL_UNDEFINED_ID; 42573890Srwatson break; 42673890Srwatson 42773890Srwatson default: 42882769Sjedgar acl_entry.ae_id = ACL_UNDEFINED_ID; 42973890Srwatson printf("acl_posix1e_mode_to_entry: invalid tag (%d)\n", tag); 43073890Srwatson } 43173890Srwatson 43273890Srwatson return (acl_entry); 43373890Srwatson} 43473890Srwatson 43573890Srwatson/* 43673890Srwatson * Utility function to generate a file mode given appropriate ACL entries. 43773890Srwatson */ 43873890Srwatsonmode_t 43973890Srwatsonacl_posix1e_perms_to_mode(struct acl_entry *acl_user_obj_entry, 44073890Srwatson struct acl_entry *acl_group_obj_entry, struct acl_entry *acl_other_entry) 44173890Srwatson{ 44273890Srwatson mode_t mode; 44373890Srwatson 44473890Srwatson mode = 0; 44575404Sjedgar if (acl_user_obj_entry->ae_perm & ACL_EXECUTE) 44673890Srwatson mode |= S_IXUSR; 44775404Sjedgar if (acl_user_obj_entry->ae_perm & ACL_READ) 44873890Srwatson mode |= S_IRUSR; 44975404Sjedgar if (acl_user_obj_entry->ae_perm & ACL_WRITE) 45073890Srwatson mode |= S_IWUSR; 45175404Sjedgar if (acl_group_obj_entry->ae_perm & ACL_EXECUTE) 45273890Srwatson mode |= S_IXGRP; 45375404Sjedgar if (acl_group_obj_entry->ae_perm & ACL_READ) 45473890Srwatson mode |= S_IRGRP; 45575404Sjedgar if (acl_group_obj_entry->ae_perm & ACL_WRITE) 45673890Srwatson mode |= S_IWGRP; 45775404Sjedgar if (acl_other_entry->ae_perm & ACL_EXECUTE) 45873890Srwatson mode |= S_IXOTH; 45975404Sjedgar if (acl_other_entry->ae_perm & ACL_READ) 46073890Srwatson mode |= S_IROTH; 46175404Sjedgar if (acl_other_entry->ae_perm & ACL_WRITE) 46273890Srwatson mode |= S_IWOTH; 46373890Srwatson 46473890Srwatson return (mode); 46573890Srwatson} 46673890Srwatson 46773890Srwatson/* 468160597Srwatson * Utility function to generate a file mode given a complete POSIX.1e access 469160597Srwatson * ACL. Note that if the ACL is improperly formed, this may result in a 470160597Srwatson * panic. 471118407Srwatson */ 472118407Srwatsonmode_t 473118407Srwatsonacl_posix1e_acl_to_mode(struct acl *acl) 474118407Srwatson{ 475118407Srwatson struct acl_entry *acl_mask, *acl_user_obj, *acl_group_obj, *acl_other; 476118407Srwatson int i; 477118407Srwatson 478118407Srwatson /* 479118407Srwatson * Find the ACL entries relevant to a POSIX permission mode. 480118407Srwatson */ 481118407Srwatson acl_user_obj = acl_group_obj = acl_other = acl_mask = NULL; 482118407Srwatson for (i = 0; i < acl->acl_cnt; i++) { 483118407Srwatson switch (acl->acl_entry[i].ae_tag) { 484118407Srwatson case ACL_USER_OBJ: 485118407Srwatson acl_user_obj = &acl->acl_entry[i]; 486118407Srwatson break; 487118407Srwatson 488118407Srwatson case ACL_GROUP_OBJ: 489118407Srwatson acl_group_obj = &acl->acl_entry[i]; 490118407Srwatson break; 491118407Srwatson 492118407Srwatson case ACL_OTHER: 493118407Srwatson acl_other = &acl->acl_entry[i]; 494118407Srwatson break; 495118407Srwatson 496118407Srwatson case ACL_MASK: 497118407Srwatson acl_mask = &acl->acl_entry[i]; 498118407Srwatson break; 499118407Srwatson 500118407Srwatson case ACL_USER: 501118407Srwatson case ACL_GROUP: 502118407Srwatson break; 503118407Srwatson 504118407Srwatson default: 505118407Srwatson panic("acl_posix1e_acl_to_mode: bad ae_tag"); 506118407Srwatson } 507118407Srwatson } 508118407Srwatson 509118407Srwatson if (acl_user_obj == NULL || acl_group_obj == NULL || acl_other == NULL) 510118407Srwatson panic("acl_posix1e_acl_to_mode: missing base ae_tags"); 511118407Srwatson 512118407Srwatson /* 513118407Srwatson * POSIX.1e specifies that if there is an ACL_MASK entry, we replace 514118407Srwatson * the mode "group" bits with its permissions. If there isn't, we 515118407Srwatson * use the ACL_GROUP_OBJ permissions. 516118407Srwatson */ 517118407Srwatson if (acl_mask != NULL) 518118407Srwatson return (acl_posix1e_perms_to_mode(acl_user_obj, acl_mask, 519118407Srwatson acl_other)); 520118407Srwatson else 521118407Srwatson return (acl_posix1e_perms_to_mode(acl_user_obj, acl_group_obj, 522118407Srwatson acl_other)); 523118407Srwatson} 524118407Srwatson 525118407Srwatson/* 526160597Srwatson * Perform a syntactic check of the ACL, sufficient to allow an implementing 527160597Srwatson * filesystem to determine if it should accept this and rely on the POSIX.1e 528160597Srwatson * ACL properties. 52973890Srwatson */ 53073890Srwatsonint 53173890Srwatsonacl_posix1e_check(struct acl *acl) 53273890Srwatson{ 53373890Srwatson int num_acl_user_obj, num_acl_user, num_acl_group_obj, num_acl_group; 53473890Srwatson int num_acl_mask, num_acl_other, i; 53573890Srwatson 53673890Srwatson /* 53773890Srwatson * Verify that the number of entries does not exceed the maximum 53873890Srwatson * defined for acl_t. 539160597Srwatson * 54073890Srwatson * Verify that the correct number of various sorts of ae_tags are 54173890Srwatson * present: 54273890Srwatson * Exactly one ACL_USER_OBJ 54373890Srwatson * Exactly one ACL_GROUP_OBJ 54473890Srwatson * Exactly one ACL_OTHER 54573890Srwatson * If any ACL_USER or ACL_GROUP entries appear, then exactly one 54673890Srwatson * ACL_MASK entry must also appear. 547160597Srwatson * 54873890Srwatson * Verify that all ae_perm entries are in ACL_PERM_BITS. 549160597Srwatson * 55073890Srwatson * Verify all ae_tag entries are understood by this implementation. 551160597Srwatson * 55273890Srwatson * Note: Does not check for uniqueness of qualifier (ae_id) field. 55373890Srwatson */ 55473890Srwatson num_acl_user_obj = num_acl_user = num_acl_group_obj = num_acl_group = 55573890Srwatson num_acl_mask = num_acl_other = 0; 55673890Srwatson if (acl->acl_cnt > ACL_MAX_ENTRIES || acl->acl_cnt < 0) 55773890Srwatson return (EINVAL); 55873890Srwatson for (i = 0; i < acl->acl_cnt; i++) { 55973890Srwatson /* 56073890Srwatson * Check for a valid tag. 56173890Srwatson */ 56273890Srwatson switch(acl->acl_entry[i].ae_tag) { 56373890Srwatson case ACL_USER_OBJ: 56475571Srwatson acl->acl_entry[i].ae_id = ACL_UNDEFINED_ID; /* XXX */ 56575571Srwatson if (acl->acl_entry[i].ae_id != ACL_UNDEFINED_ID) 56675571Srwatson return (EINVAL); 56773890Srwatson num_acl_user_obj++; 56873890Srwatson break; 56973890Srwatson case ACL_GROUP_OBJ: 57075571Srwatson acl->acl_entry[i].ae_id = ACL_UNDEFINED_ID; /* XXX */ 57175571Srwatson if (acl->acl_entry[i].ae_id != ACL_UNDEFINED_ID) 57275571Srwatson return (EINVAL); 57373890Srwatson num_acl_group_obj++; 57473890Srwatson break; 57573890Srwatson case ACL_USER: 57675571Srwatson if (acl->acl_entry[i].ae_id == ACL_UNDEFINED_ID) 57775571Srwatson return (EINVAL); 57873890Srwatson num_acl_user++; 57973890Srwatson break; 58073890Srwatson case ACL_GROUP: 58175571Srwatson if (acl->acl_entry[i].ae_id == ACL_UNDEFINED_ID) 58275571Srwatson return (EINVAL); 58373890Srwatson num_acl_group++; 58473890Srwatson break; 58573890Srwatson case ACL_OTHER: 58675571Srwatson acl->acl_entry[i].ae_id = ACL_UNDEFINED_ID; /* XXX */ 58775571Srwatson if (acl->acl_entry[i].ae_id != ACL_UNDEFINED_ID) 58875571Srwatson return (EINVAL); 58973890Srwatson num_acl_other++; 59073890Srwatson break; 59173890Srwatson case ACL_MASK: 59275571Srwatson acl->acl_entry[i].ae_id = ACL_UNDEFINED_ID; /* XXX */ 59375571Srwatson if (acl->acl_entry[i].ae_id != ACL_UNDEFINED_ID) 59475571Srwatson return (EINVAL); 59573890Srwatson num_acl_mask++; 59673890Srwatson break; 59773890Srwatson default: 59873890Srwatson return (EINVAL); 59973890Srwatson } 60073890Srwatson /* 60173890Srwatson * Check for valid perm entries. 60273890Srwatson */ 60373890Srwatson if ((acl->acl_entry[i].ae_perm | ACL_PERM_BITS) != 60473890Srwatson ACL_PERM_BITS) 60573890Srwatson return (EINVAL); 60673890Srwatson } 60773890Srwatson if ((num_acl_user_obj != 1) || (num_acl_group_obj != 1) || 60873890Srwatson (num_acl_other != 1) || (num_acl_mask != 0 && num_acl_mask != 1)) 60973890Srwatson return (EINVAL); 61073890Srwatson if (((num_acl_group != 0) || (num_acl_user != 0)) && 61173890Srwatson (num_acl_mask != 1)) 61273890Srwatson return (EINVAL); 61373890Srwatson return (0); 61473890Srwatson} 61573890Srwatson 61673890Srwatson/* 617160597Srwatson * Given a requested mode for a new object, and a default ACL, combine the 618160597Srwatson * two to produce a new mode. Be careful not to clear any bits that aren't 619160597Srwatson * intended to be affected by the POSIX.1e ACL. Eventually, this might also 620160597Srwatson * take the cmask as an argument, if we push that down into 621160597Srwatson * per-filesystem-code. 622118407Srwatson */ 623118407Srwatsonmode_t 624118407Srwatsonacl_posix1e_newfilemode(mode_t cmode, struct acl *dacl) 625118407Srwatson{ 626118407Srwatson mode_t mode; 627118407Srwatson 628118407Srwatson mode = cmode; 629118407Srwatson /* 630160597Srwatson * The current composition policy is that a permission bit must be 631160597Srwatson * set in *both* the ACL and the requested creation mode for it to 632160597Srwatson * appear in the resulting mode/ACL. First clear any possibly 633160597Srwatson * effected bits, then reconstruct. 634118407Srwatson */ 635118407Srwatson mode &= ACL_PRESERVE_MASK; 636118407Srwatson mode |= (ACL_OVERRIDE_MASK & cmode & acl_posix1e_acl_to_mode(dacl)); 637118407Srwatson 638118407Srwatson return (mode); 639118407Srwatson} 640