subr_acl_posix1e.c revision 201019
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 201019 2009-12-26 11:36:10Z trasz $"); 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 { 93184427Strasz if ((accmode & VEXEC) && !priv_check_cred(cred, 94170587Srwatson PRIV_VFS_EXEC, 0)) 95164033Srwatson priv_granted |= VEXEC; 9673890Srwatson } 9773890Srwatson 98184427Strasz if ((accmode & VREAD) && !priv_check_cred(cred, PRIV_VFS_READ, 0)) 99164033Srwatson priv_granted |= VREAD; 10073890Srwatson 101184427Strasz if (((accmode & VWRITE) || (accmode & VAPPEND)) && 102170587Srwatson !priv_check_cred(cred, PRIV_VFS_WRITE, 0)) 103164033Srwatson priv_granted |= (VWRITE | VAPPEND); 10473890Srwatson 105184427Strasz if ((accmode & VADMIN) && !priv_check_cred(cred, PRIV_VFS_ADMIN, 0)) 106164033Srwatson priv_granted |= VADMIN; 10773890Srwatson 10873890Srwatson /* 10982255Srwatson * The owner matches if the effective uid associated with the 11082255Srwatson * credential matches that of the ACL_USER_OBJ entry. While we're 111160597Srwatson * doing the first scan, also cache the location of the ACL_MASK and 112160597Srwatson * ACL_OTHER entries, preventing some future iterations. 11373890Srwatson */ 11473890Srwatson acl_mask = acl_other = NULL; 11573890Srwatson for (i = 0; i < acl->acl_cnt; i++) { 11673890Srwatson switch (acl->acl_entry[i].ae_tag) { 11773890Srwatson case ACL_USER_OBJ: 11875571Srwatson if (file_uid != cred->cr_uid) 11973890Srwatson break; 12073890Srwatson dac_granted = 0; 12173890Srwatson dac_granted |= VADMIN; 12275404Sjedgar if (acl->acl_entry[i].ae_perm & ACL_EXECUTE) 12373890Srwatson dac_granted |= VEXEC; 12475404Sjedgar if (acl->acl_entry[i].ae_perm & ACL_READ) 12573890Srwatson dac_granted |= VREAD; 12675404Sjedgar if (acl->acl_entry[i].ae_perm & ACL_WRITE) 127100481Srwatson dac_granted |= (VWRITE | VAPPEND); 128184427Strasz if ((accmode & dac_granted) == accmode) 12973890Srwatson return (0); 130164033Srwatson 131164033Srwatson /* 132164033Srwatson * XXXRW: Do privilege lookup here. 133164033Srwatson */ 134184427Strasz if ((accmode & (dac_granted | priv_granted)) == 135184427Strasz accmode) { 13673890Srwatson if (privused != NULL) 13773890Srwatson *privused = 1; 13873890Srwatson return (0); 13973890Srwatson } 14073890Srwatson goto error; 14173890Srwatson 14273890Srwatson case ACL_MASK: 14373890Srwatson acl_mask = &acl->acl_entry[i]; 14473890Srwatson break; 14573890Srwatson 14673890Srwatson case ACL_OTHER: 14773890Srwatson acl_other = &acl->acl_entry[i]; 14873890Srwatson break; 14973890Srwatson 15073890Srwatson default: 15192666Speter break; 15273890Srwatson } 15373890Srwatson } 15473890Srwatson 15573890Srwatson /* 156160597Srwatson * An ACL_OTHER entry should always exist in a valid access ACL. If 157160597Srwatson * it doesn't, then generate a serious failure. For now, this means 158160597Srwatson * a debugging message and EPERM, but in the future should probably 159160597Srwatson * be a panic. 16073890Srwatson */ 16173890Srwatson if (acl_other == NULL) { 16273890Srwatson /* 16382255Srwatson * XXX This should never happen 16473890Srwatson */ 16573890Srwatson printf("vaccess_acl_posix1e: ACL_OTHER missing\n"); 16673890Srwatson return (EPERM); 16773890Srwatson } 16882255Srwatson 16982255Srwatson /* 170160597Srwatson * Checks against ACL_USER, ACL_GROUP_OBJ, and ACL_GROUP fields are 171160597Srwatson * masked by an ACL_MASK entry, if any. As such, first identify the 172160597Srwatson * ACL_MASK field, then iterate through identifying potential user 173160597Srwatson * matches, then group matches. If there is no ACL_MASK, assume that 174160597Srwatson * the mask allows all requests to succeed. 17582255Srwatson */ 17673890Srwatson if (acl_mask != NULL) { 17773890Srwatson acl_mask_granted = 0; 17875404Sjedgar if (acl_mask->ae_perm & ACL_EXECUTE) 17973890Srwatson acl_mask_granted |= VEXEC; 18075404Sjedgar if (acl_mask->ae_perm & ACL_READ) 18173890Srwatson acl_mask_granted |= VREAD; 18275404Sjedgar if (acl_mask->ae_perm & ACL_WRITE) 183100481Srwatson acl_mask_granted |= (VWRITE | VAPPEND); 18473890Srwatson } else 185100481Srwatson acl_mask_granted = VEXEC | VREAD | VWRITE | VAPPEND; 18673890Srwatson 18773890Srwatson /* 188164033Srwatson * Check ACL_USER ACL entries. There will either be one or no 189164033Srwatson * matches; if there is one, we accept or rejected based on the 190164033Srwatson * match; otherwise, we continue on to groups. 19173890Srwatson */ 19273890Srwatson for (i = 0; i < acl->acl_cnt; i++) { 19373890Srwatson switch (acl->acl_entry[i].ae_tag) { 19473890Srwatson case ACL_USER: 19573890Srwatson if (acl->acl_entry[i].ae_id != cred->cr_uid) 19673890Srwatson break; 19773890Srwatson dac_granted = 0; 19875404Sjedgar if (acl->acl_entry[i].ae_perm & ACL_EXECUTE) 19973890Srwatson dac_granted |= VEXEC; 20075404Sjedgar if (acl->acl_entry[i].ae_perm & ACL_READ) 20173890Srwatson dac_granted |= VREAD; 20275404Sjedgar if (acl->acl_entry[i].ae_perm & ACL_WRITE) 203100481Srwatson dac_granted |= (VWRITE | VAPPEND); 20473890Srwatson dac_granted &= acl_mask_granted; 205184427Strasz if ((accmode & dac_granted) == accmode) 20673890Srwatson return (0); 207164033Srwatson /* 208164033Srwatson * XXXRW: Do privilege lookup here. 209164033Srwatson */ 210184427Strasz if ((accmode & (dac_granted | priv_granted)) != 211184427Strasz accmode) 21275571Srwatson goto error; 21375571Srwatson 21475571Srwatson if (privused != NULL) 21575571Srwatson *privused = 1; 21675571Srwatson return (0); 21773890Srwatson } 21873890Srwatson } 21973890Srwatson 22073890Srwatson /* 221160597Srwatson * Group match is best-match, not first-match, so find a "best" 222160597Srwatson * match. Iterate across, testing each potential group match. Make 223160597Srwatson * sure we keep track of whether we found a match or not, so that we 224160597Srwatson * know if we should try again with any available privilege, or if we 225160597Srwatson * should move on to ACL_OTHER. 22673890Srwatson */ 22773890Srwatson group_matched = 0; 22873890Srwatson for (i = 0; i < acl->acl_cnt; i++) { 22973890Srwatson switch (acl->acl_entry[i].ae_tag) { 23073890Srwatson case ACL_GROUP_OBJ: 23175888Stmm if (!groupmember(file_gid, cred)) 23275571Srwatson break; 23375571Srwatson dac_granted = 0; 23475571Srwatson if (acl->acl_entry[i].ae_perm & ACL_EXECUTE) 23575571Srwatson dac_granted |= VEXEC; 23675571Srwatson if (acl->acl_entry[i].ae_perm & ACL_READ) 23775571Srwatson dac_granted |= VREAD; 23875571Srwatson if (acl->acl_entry[i].ae_perm & ACL_WRITE) 239100481Srwatson dac_granted |= (VWRITE | VAPPEND); 24075571Srwatson dac_granted &= acl_mask_granted; 24175571Srwatson 242184427Strasz if ((accmode & dac_granted) == accmode) 24375571Srwatson return (0); 24475571Srwatson 24575571Srwatson group_matched = 1; 24675571Srwatson break; 24775571Srwatson 24873890Srwatson case ACL_GROUP: 24975571Srwatson if (!groupmember(acl->acl_entry[i].ae_id, cred)) 25075571Srwatson break; 25175571Srwatson dac_granted = 0; 25275571Srwatson if (acl->acl_entry[i].ae_perm & ACL_EXECUTE) 25375571Srwatson dac_granted |= VEXEC; 25475571Srwatson if (acl->acl_entry[i].ae_perm & ACL_READ) 25575571Srwatson dac_granted |= VREAD; 25675571Srwatson if (acl->acl_entry[i].ae_perm & ACL_WRITE) 257100481Srwatson dac_granted |= (VWRITE | VAPPEND); 25875571Srwatson dac_granted &= acl_mask_granted; 25973890Srwatson 260184427Strasz if ((accmode & dac_granted) == accmode) 26175571Srwatson return (0); 26273890Srwatson 26375571Srwatson group_matched = 1; 26475571Srwatson break; 26575571Srwatson 26673890Srwatson default: 26792666Speter break; 26873890Srwatson } 26973890Srwatson } 27073890Srwatson 27173890Srwatson if (group_matched == 1) { 27273890Srwatson /* 273160597Srwatson * There was a match, but it did not grant rights via pure 274160597Srwatson * DAC. Try again, this time with privilege. 27573890Srwatson */ 27673890Srwatson for (i = 0; i < acl->acl_cnt; i++) { 27773890Srwatson switch (acl->acl_entry[i].ae_tag) { 27873890Srwatson case ACL_GROUP_OBJ: 27976139Srwatson if (!groupmember(file_gid, cred)) 28075571Srwatson break; 28175571Srwatson dac_granted = 0; 28275571Srwatson if (acl->acl_entry[i].ae_perm & ACL_EXECUTE) 28375571Srwatson dac_granted |= VEXEC; 28475571Srwatson if (acl->acl_entry[i].ae_perm & ACL_READ) 28575571Srwatson dac_granted |= VREAD; 28675571Srwatson if (acl->acl_entry[i].ae_perm & ACL_WRITE) 287100481Srwatson dac_granted |= (VWRITE | VAPPEND); 28875571Srwatson dac_granted &= acl_mask_granted; 28975571Srwatson 290164033Srwatson /* 291164033Srwatson * XXXRW: Do privilege lookup here. 292164033Srwatson */ 293184427Strasz if ((accmode & (dac_granted | priv_granted)) 294184427Strasz != accmode) 29575571Srwatson break; 29675571Srwatson 29775571Srwatson if (privused != NULL) 29875571Srwatson *privused = 1; 29975571Srwatson return (0); 30075571Srwatson 30173890Srwatson case ACL_GROUP: 30275571Srwatson if (!groupmember(acl->acl_entry[i].ae_id, 30375571Srwatson cred)) 30475571Srwatson break; 30575571Srwatson dac_granted = 0; 30675571Srwatson if (acl->acl_entry[i].ae_perm & ACL_EXECUTE) 30775571Srwatson dac_granted |= VEXEC; 30875571Srwatson if (acl->acl_entry[i].ae_perm & ACL_READ) 30975571Srwatson dac_granted |= VREAD; 31075571Srwatson if (acl->acl_entry[i].ae_perm & ACL_WRITE) 311100481Srwatson dac_granted |= (VWRITE | VAPPEND); 31275571Srwatson dac_granted &= acl_mask_granted; 31375571Srwatson 314164033Srwatson /* 315164033Srwatson * XXXRW: Do privilege lookup here. 316164033Srwatson */ 317184427Strasz if ((accmode & (dac_granted | priv_granted)) 318184427Strasz != accmode) 31975571Srwatson break; 32075571Srwatson 32175571Srwatson if (privused != NULL) 32275571Srwatson *privused = 1; 32375571Srwatson return (0); 32475571Srwatson 32573890Srwatson default: 32692666Speter break; 32773890Srwatson } 32873890Srwatson } 32973890Srwatson /* 33073890Srwatson * Even with privilege, group membership was not sufficient. 33173890Srwatson * Return failure. 33273890Srwatson */ 33373890Srwatson goto error; 33473890Srwatson } 33573890Srwatson 33673890Srwatson /* 33773890Srwatson * Fall back on ACL_OTHER. ACL_MASK is not applied to ACL_OTHER. 33873890Srwatson */ 33973890Srwatson dac_granted = 0; 34075404Sjedgar if (acl_other->ae_perm & ACL_EXECUTE) 34173890Srwatson dac_granted |= VEXEC; 34275404Sjedgar if (acl_other->ae_perm & ACL_READ) 34373890Srwatson dac_granted |= VREAD; 34475404Sjedgar if (acl_other->ae_perm & ACL_WRITE) 345100481Srwatson dac_granted |= (VWRITE | VAPPEND); 34673890Srwatson 347184427Strasz if ((accmode & dac_granted) == accmode) 34873890Srwatson return (0); 349164033Srwatson /* 350164033Srwatson * XXXRW: Do privilege lookup here. 351164033Srwatson */ 352184427Strasz if ((accmode & (dac_granted | priv_granted)) == accmode) { 35373890Srwatson if (privused != NULL) 35473890Srwatson *privused = 1; 35573890Srwatson return (0); 35673890Srwatson } 35773890Srwatson 35873890Srwatsonerror: 359184427Strasz return ((accmode & VADMIN) ? EPERM : EACCES); 36073890Srwatson} 36173890Srwatson 36273890Srwatson/* 363160597Srwatson * For the purposes of filesystems maintaining the _OBJ entries in an inode 364160597Srwatson * with a mode_t field, this routine converts a mode_t entry to an 365160597Srwatson * acl_perm_t. 36673890Srwatson */ 36773890Srwatsonacl_perm_t 36873890Srwatsonacl_posix1e_mode_to_perm(acl_tag_t tag, mode_t mode) 36973890Srwatson{ 37073890Srwatson acl_perm_t perm = 0; 37173890Srwatson 37273890Srwatson switch(tag) { 37373890Srwatson case ACL_USER_OBJ: 37473890Srwatson if (mode & S_IXUSR) 37575404Sjedgar perm |= ACL_EXECUTE; 37673890Srwatson if (mode & S_IRUSR) 37775404Sjedgar perm |= ACL_READ; 37873890Srwatson if (mode & S_IWUSR) 37975404Sjedgar perm |= ACL_WRITE; 38073890Srwatson return (perm); 38173890Srwatson 38273890Srwatson case ACL_GROUP_OBJ: 38373890Srwatson if (mode & S_IXGRP) 38475404Sjedgar perm |= ACL_EXECUTE; 38573890Srwatson if (mode & S_IRGRP) 38675404Sjedgar perm |= ACL_READ; 38773890Srwatson if (mode & S_IWGRP) 38875404Sjedgar perm |= ACL_WRITE; 38973890Srwatson return (perm); 39073890Srwatson 39173890Srwatson case ACL_OTHER: 39273890Srwatson if (mode & S_IXOTH) 39375404Sjedgar perm |= ACL_EXECUTE; 39473890Srwatson if (mode & S_IROTH) 39575404Sjedgar perm |= ACL_READ; 39673890Srwatson if (mode & S_IWOTH) 39775404Sjedgar perm |= ACL_WRITE; 39873890Srwatson return (perm); 39973890Srwatson 40073890Srwatson default: 40173890Srwatson printf("acl_posix1e_mode_to_perm: invalid tag (%d)\n", tag); 40273890Srwatson return (0); 40373890Srwatson } 40473890Srwatson} 40573890Srwatson 40673890Srwatson/* 40773890Srwatson * Given inode information (uid, gid, mode), return an acl entry of the 40873890Srwatson * appropriate type. 40973890Srwatson */ 41073890Srwatsonstruct acl_entry 41173890Srwatsonacl_posix1e_mode_to_entry(acl_tag_t tag, uid_t uid, gid_t gid, mode_t mode) 41273890Srwatson{ 41373890Srwatson struct acl_entry acl_entry; 41473890Srwatson 41573890Srwatson acl_entry.ae_tag = tag; 41673890Srwatson acl_entry.ae_perm = acl_posix1e_mode_to_perm(tag, mode); 417192586Strasz acl_entry.ae_entry_type = 0; 418192586Strasz acl_entry.ae_flags = 0; 41973890Srwatson switch(tag) { 42073890Srwatson case ACL_USER_OBJ: 42173890Srwatson acl_entry.ae_id = uid; 42273890Srwatson break; 42373890Srwatson 42473890Srwatson case ACL_GROUP_OBJ: 42573890Srwatson acl_entry.ae_id = gid; 42673890Srwatson break; 42773890Srwatson 42873890Srwatson case ACL_OTHER: 42982769Sjedgar acl_entry.ae_id = ACL_UNDEFINED_ID; 43073890Srwatson break; 43173890Srwatson 43273890Srwatson default: 43382769Sjedgar acl_entry.ae_id = ACL_UNDEFINED_ID; 43473890Srwatson printf("acl_posix1e_mode_to_entry: invalid tag (%d)\n", tag); 43573890Srwatson } 43673890Srwatson 43773890Srwatson return (acl_entry); 43873890Srwatson} 43973890Srwatson 44073890Srwatson/* 44173890Srwatson * Utility function to generate a file mode given appropriate ACL entries. 44273890Srwatson */ 44373890Srwatsonmode_t 44473890Srwatsonacl_posix1e_perms_to_mode(struct acl_entry *acl_user_obj_entry, 44573890Srwatson struct acl_entry *acl_group_obj_entry, struct acl_entry *acl_other_entry) 44673890Srwatson{ 44773890Srwatson mode_t mode; 44873890Srwatson 44973890Srwatson mode = 0; 45075404Sjedgar if (acl_user_obj_entry->ae_perm & ACL_EXECUTE) 45173890Srwatson mode |= S_IXUSR; 45275404Sjedgar if (acl_user_obj_entry->ae_perm & ACL_READ) 45373890Srwatson mode |= S_IRUSR; 45475404Sjedgar if (acl_user_obj_entry->ae_perm & ACL_WRITE) 45573890Srwatson mode |= S_IWUSR; 45675404Sjedgar if (acl_group_obj_entry->ae_perm & ACL_EXECUTE) 45773890Srwatson mode |= S_IXGRP; 45875404Sjedgar if (acl_group_obj_entry->ae_perm & ACL_READ) 45973890Srwatson mode |= S_IRGRP; 46075404Sjedgar if (acl_group_obj_entry->ae_perm & ACL_WRITE) 46173890Srwatson mode |= S_IWGRP; 46275404Sjedgar if (acl_other_entry->ae_perm & ACL_EXECUTE) 46373890Srwatson mode |= S_IXOTH; 46475404Sjedgar if (acl_other_entry->ae_perm & ACL_READ) 46573890Srwatson mode |= S_IROTH; 46675404Sjedgar if (acl_other_entry->ae_perm & ACL_WRITE) 46773890Srwatson mode |= S_IWOTH; 46873890Srwatson 46973890Srwatson return (mode); 47073890Srwatson} 47173890Srwatson 47273890Srwatson/* 473160597Srwatson * Utility function to generate a file mode given a complete POSIX.1e access 474160597Srwatson * ACL. Note that if the ACL is improperly formed, this may result in a 475160597Srwatson * panic. 476118407Srwatson */ 477118407Srwatsonmode_t 478118407Srwatsonacl_posix1e_acl_to_mode(struct acl *acl) 479118407Srwatson{ 480118407Srwatson struct acl_entry *acl_mask, *acl_user_obj, *acl_group_obj, *acl_other; 481118407Srwatson int i; 482118407Srwatson 483118407Srwatson /* 484118407Srwatson * Find the ACL entries relevant to a POSIX permission mode. 485118407Srwatson */ 486118407Srwatson acl_user_obj = acl_group_obj = acl_other = acl_mask = NULL; 487118407Srwatson for (i = 0; i < acl->acl_cnt; i++) { 488118407Srwatson switch (acl->acl_entry[i].ae_tag) { 489118407Srwatson case ACL_USER_OBJ: 490118407Srwatson acl_user_obj = &acl->acl_entry[i]; 491118407Srwatson break; 492118407Srwatson 493118407Srwatson case ACL_GROUP_OBJ: 494118407Srwatson acl_group_obj = &acl->acl_entry[i]; 495118407Srwatson break; 496118407Srwatson 497118407Srwatson case ACL_OTHER: 498118407Srwatson acl_other = &acl->acl_entry[i]; 499118407Srwatson break; 500118407Srwatson 501118407Srwatson case ACL_MASK: 502118407Srwatson acl_mask = &acl->acl_entry[i]; 503118407Srwatson break; 504118407Srwatson 505118407Srwatson case ACL_USER: 506118407Srwatson case ACL_GROUP: 507118407Srwatson break; 508118407Srwatson 509118407Srwatson default: 510118407Srwatson panic("acl_posix1e_acl_to_mode: bad ae_tag"); 511118407Srwatson } 512118407Srwatson } 513118407Srwatson 514118407Srwatson if (acl_user_obj == NULL || acl_group_obj == NULL || acl_other == NULL) 515118407Srwatson panic("acl_posix1e_acl_to_mode: missing base ae_tags"); 516118407Srwatson 517118407Srwatson /* 518118407Srwatson * POSIX.1e specifies that if there is an ACL_MASK entry, we replace 519118407Srwatson * the mode "group" bits with its permissions. If there isn't, we 520118407Srwatson * use the ACL_GROUP_OBJ permissions. 521118407Srwatson */ 522118407Srwatson if (acl_mask != NULL) 523118407Srwatson return (acl_posix1e_perms_to_mode(acl_user_obj, acl_mask, 524118407Srwatson acl_other)); 525118407Srwatson else 526118407Srwatson return (acl_posix1e_perms_to_mode(acl_user_obj, acl_group_obj, 527118407Srwatson acl_other)); 528118407Srwatson} 529118407Srwatson 530118407Srwatson/* 531160597Srwatson * Perform a syntactic check of the ACL, sufficient to allow an implementing 532160597Srwatson * filesystem to determine if it should accept this and rely on the POSIX.1e 533160597Srwatson * ACL properties. 53473890Srwatson */ 53573890Srwatsonint 53673890Srwatsonacl_posix1e_check(struct acl *acl) 53773890Srwatson{ 53873890Srwatson int num_acl_user_obj, num_acl_user, num_acl_group_obj, num_acl_group; 53973890Srwatson int num_acl_mask, num_acl_other, i; 54073890Srwatson 54173890Srwatson /* 54273890Srwatson * Verify that the number of entries does not exceed the maximum 54373890Srwatson * defined for acl_t. 544160597Srwatson * 54573890Srwatson * Verify that the correct number of various sorts of ae_tags are 54673890Srwatson * present: 54773890Srwatson * Exactly one ACL_USER_OBJ 54873890Srwatson * Exactly one ACL_GROUP_OBJ 54973890Srwatson * Exactly one ACL_OTHER 55073890Srwatson * If any ACL_USER or ACL_GROUP entries appear, then exactly one 55173890Srwatson * ACL_MASK entry must also appear. 552160597Srwatson * 55373890Srwatson * Verify that all ae_perm entries are in ACL_PERM_BITS. 554160597Srwatson * 55573890Srwatson * Verify all ae_tag entries are understood by this implementation. 556160597Srwatson * 55773890Srwatson * Note: Does not check for uniqueness of qualifier (ae_id) field. 55873890Srwatson */ 55973890Srwatson num_acl_user_obj = num_acl_user = num_acl_group_obj = num_acl_group = 56073890Srwatson num_acl_mask = num_acl_other = 0; 56173890Srwatson if (acl->acl_cnt > ACL_MAX_ENTRIES || acl->acl_cnt < 0) 56273890Srwatson return (EINVAL); 56373890Srwatson for (i = 0; i < acl->acl_cnt; i++) { 56473890Srwatson /* 56573890Srwatson * Check for a valid tag. 56673890Srwatson */ 56773890Srwatson switch(acl->acl_entry[i].ae_tag) { 56873890Srwatson case ACL_USER_OBJ: 56975571Srwatson acl->acl_entry[i].ae_id = ACL_UNDEFINED_ID; /* XXX */ 57075571Srwatson if (acl->acl_entry[i].ae_id != ACL_UNDEFINED_ID) 57175571Srwatson return (EINVAL); 57273890Srwatson num_acl_user_obj++; 57373890Srwatson break; 57473890Srwatson case ACL_GROUP_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_group_obj++; 57973890Srwatson break; 58073890Srwatson case ACL_USER: 58175571Srwatson if (acl->acl_entry[i].ae_id == ACL_UNDEFINED_ID) 58275571Srwatson return (EINVAL); 58373890Srwatson num_acl_user++; 58473890Srwatson break; 58573890Srwatson case ACL_GROUP: 58675571Srwatson if (acl->acl_entry[i].ae_id == ACL_UNDEFINED_ID) 58775571Srwatson return (EINVAL); 58873890Srwatson num_acl_group++; 58973890Srwatson break; 59073890Srwatson case ACL_OTHER: 59175571Srwatson acl->acl_entry[i].ae_id = ACL_UNDEFINED_ID; /* XXX */ 59275571Srwatson if (acl->acl_entry[i].ae_id != ACL_UNDEFINED_ID) 59375571Srwatson return (EINVAL); 59473890Srwatson num_acl_other++; 59573890Srwatson break; 59673890Srwatson case ACL_MASK: 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_mask++; 60173890Srwatson break; 60273890Srwatson default: 60373890Srwatson return (EINVAL); 60473890Srwatson } 60573890Srwatson /* 60673890Srwatson * Check for valid perm entries. 60773890Srwatson */ 60873890Srwatson if ((acl->acl_entry[i].ae_perm | ACL_PERM_BITS) != 60973890Srwatson ACL_PERM_BITS) 61073890Srwatson return (EINVAL); 61173890Srwatson } 61273890Srwatson if ((num_acl_user_obj != 1) || (num_acl_group_obj != 1) || 61373890Srwatson (num_acl_other != 1) || (num_acl_mask != 0 && num_acl_mask != 1)) 61473890Srwatson return (EINVAL); 61573890Srwatson if (((num_acl_group != 0) || (num_acl_user != 0)) && 61673890Srwatson (num_acl_mask != 1)) 61773890Srwatson return (EINVAL); 61873890Srwatson return (0); 61973890Srwatson} 62073890Srwatson 62173890Srwatson/* 622160597Srwatson * Given a requested mode for a new object, and a default ACL, combine the 623160597Srwatson * two to produce a new mode. Be careful not to clear any bits that aren't 624160597Srwatson * intended to be affected by the POSIX.1e ACL. Eventually, this might also 625160597Srwatson * take the cmask as an argument, if we push that down into 626160597Srwatson * per-filesystem-code. 627118407Srwatson */ 628118407Srwatsonmode_t 629118407Srwatsonacl_posix1e_newfilemode(mode_t cmode, struct acl *dacl) 630118407Srwatson{ 631118407Srwatson mode_t mode; 632118407Srwatson 633118407Srwatson mode = cmode; 634118407Srwatson /* 635160597Srwatson * The current composition policy is that a permission bit must be 636160597Srwatson * set in *both* the ACL and the requested creation mode for it to 637160597Srwatson * appear in the resulting mode/ACL. First clear any possibly 638160597Srwatson * effected bits, then reconstruct. 639118407Srwatson */ 640118407Srwatson mode &= ACL_PRESERVE_MASK; 641118407Srwatson mode |= (ACL_OVERRIDE_MASK & cmode & acl_posix1e_acl_to_mode(dacl)); 642118407Srwatson 643118407Srwatson return (mode); 644118407Srwatson} 645