vfs_acl.c revision 107855
154803Srwatson/*- 285583Srwatson * Copyright (c) 1999-2001 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 * $FreeBSD: head/sys/kern/vfs_acl.c 107855 2002-12-14 08:18:06Z alfred $ 2954803Srwatson */ 3054803Srwatson/* 3173890Srwatson * Developed by the TrustedBSD Project. 3273890Srwatson * Support for POSIX.1e access control lists. 3354803Srwatson */ 3454803Srwatson 35101122Srwatson#include "opt_mac.h" 36101122Srwatson 3754803Srwatson#include <sys/param.h> 3854803Srwatson#include <sys/systm.h> 3954803Srwatson#include <sys/sysproto.h> 4054803Srwatson#include <sys/kernel.h> 41101122Srwatson#include <sys/mac.h> 4254803Srwatson#include <sys/malloc.h> 4354803Srwatson#include <sys/vnode.h> 4454803Srwatson#include <sys/lock.h> 4582713Sdillon#include <sys/mutex.h> 4654803Srwatson#include <sys/namei.h> 4754803Srwatson#include <sys/file.h> 4854803Srwatson#include <sys/proc.h> 4954803Srwatson#include <sys/sysent.h> 5054803Srwatson#include <sys/errno.h> 5154803Srwatson#include <sys/stat.h> 5254803Srwatson#include <sys/acl.h> 5354803Srwatson 5473890SrwatsonMALLOC_DEFINE(M_ACL, "acl", "access control list"); 5554803Srwatson 5698927Srwatsonstatic int vacl_set_acl(struct thread *td, struct vnode *vp, 5798927Srwatson acl_type_t type, struct acl *aclp); 5898927Srwatsonstatic int vacl_get_acl(struct thread *td, struct vnode *vp, 5998927Srwatson acl_type_t type, struct acl *aclp); 6085582Srwatsonstatic int vacl_aclcheck(struct thread *td, struct vnode *vp, 6198927Srwatson acl_type_t type, struct acl *aclp); 6254803Srwatson 6354803Srwatson/* 6473890Srwatson * Implement a version of vaccess() that understands POSIX.1e ACL semantics. 6573890Srwatson * Return 0 on success, else an errno value. Should be merged into 6673890Srwatson * vaccess() eventually. 6773890Srwatson */ 6873890Srwatsonint 6975571Srwatsonvaccess_acl_posix1e(enum vtype type, uid_t file_uid, gid_t file_gid, 7075571Srwatson struct acl *acl, mode_t acc_mode, struct ucred *cred, int *privused) 7173890Srwatson{ 7273890Srwatson struct acl_entry *acl_other, *acl_mask; 7373890Srwatson mode_t dac_granted; 7473890Srwatson mode_t cap_granted; 7573890Srwatson mode_t acl_mask_granted; 7673890Srwatson int group_matched, i; 7773890Srwatson 7873890Srwatson /* 7973890Srwatson * Look for a normal, non-privileged way to access the file/directory 8073890Srwatson * as requested. If it exists, go with that. Otherwise, attempt 8173890Srwatson * to use privileges granted via cap_granted. In some cases, 8273890Srwatson * which privileges to use may be ambiguous due to "best match", 8373890Srwatson * in which case fall back on first match for the time being. 8473890Srwatson */ 8573890Srwatson if (privused != NULL) 8673890Srwatson *privused = 0; 8773890Srwatson 8873890Srwatson /* 8973890Srwatson * Determine privileges now, but don't apply until we've found 9082255Srwatson * a DAC entry that matches but has failed to allow access. 9173890Srwatson */ 9273890Srwatson#ifndef CAPABILITIES 9393593Sjhb if (suser_cred(cred, PRISON_ROOT) == 0) 94100481Srwatson cap_granted = VALLPERM; 9573890Srwatson else 9673890Srwatson cap_granted = 0; 9773890Srwatson#else 9873890Srwatson cap_granted = 0; 9973890Srwatson 10073890Srwatson if (type == VDIR) { 10173890Srwatson if ((acc_mode & VEXEC) && !cap_check(cred, NULL, 10273890Srwatson CAP_DAC_READ_SEARCH, PRISON_ROOT)) 10373890Srwatson cap_granted |= VEXEC; 10473890Srwatson } else { 10573890Srwatson if ((acc_mode & VEXEC) && !cap_check(cred, NULL, 10673890Srwatson CAP_DAC_EXECUTE, PRISON_ROOT)) 10773890Srwatson cap_granted |= VEXEC; 10873890Srwatson } 10973890Srwatson 11073890Srwatson if ((acc_mode & VREAD) && !cap_check(cred, NULL, CAP_DAC_READ_SEARCH, 11173890Srwatson PRISON_ROOT)) 11273890Srwatson cap_granted |= VREAD; 11373890Srwatson 114100481Srwatson if (((acc_mode & VWRITE) || (acc_mode & VAPPEND)) && 115100481Srwatson !cap_check(cred, NULL, CAP_DAC_WRITE, PRISON_ROOT)) 116100481Srwatson cap_granted |= (VWRITE | VAPPEND); 11773890Srwatson 11873890Srwatson if ((acc_mode & VADMIN) && !cap_check(cred, NULL, CAP_FOWNER, 11973890Srwatson PRISON_ROOT)) 12073890Srwatson cap_granted |= VADMIN; 12173890Srwatson#endif /* CAPABILITIES */ 12273890Srwatson 12373890Srwatson /* 12482255Srwatson * The owner matches if the effective uid associated with the 12582255Srwatson * credential matches that of the ACL_USER_OBJ entry. While we're 12682255Srwatson * doing the first scan, also cache the location of the ACL_MASK 12782255Srwatson * and ACL_OTHER entries, preventing some future iterations. 12873890Srwatson */ 12973890Srwatson acl_mask = acl_other = NULL; 13073890Srwatson for (i = 0; i < acl->acl_cnt; i++) { 13173890Srwatson switch (acl->acl_entry[i].ae_tag) { 13273890Srwatson case ACL_USER_OBJ: 13375571Srwatson if (file_uid != cred->cr_uid) 13473890Srwatson break; 13573890Srwatson dac_granted = 0; 13673890Srwatson dac_granted |= VADMIN; 13775404Sjedgar if (acl->acl_entry[i].ae_perm & ACL_EXECUTE) 13873890Srwatson dac_granted |= VEXEC; 13975404Sjedgar if (acl->acl_entry[i].ae_perm & ACL_READ) 14073890Srwatson dac_granted |= VREAD; 14175404Sjedgar if (acl->acl_entry[i].ae_perm & ACL_WRITE) 142100481Srwatson dac_granted |= (VWRITE | VAPPEND); 14373890Srwatson if ((acc_mode & dac_granted) == acc_mode) 14473890Srwatson return (0); 14573890Srwatson if ((acc_mode & (dac_granted | cap_granted)) == 14673890Srwatson acc_mode) { 14773890Srwatson if (privused != NULL) 14873890Srwatson *privused = 1; 14973890Srwatson return (0); 15073890Srwatson } 15173890Srwatson goto error; 15273890Srwatson 15373890Srwatson case ACL_MASK: 15473890Srwatson acl_mask = &acl->acl_entry[i]; 15573890Srwatson break; 15673890Srwatson 15773890Srwatson case ACL_OTHER: 15873890Srwatson acl_other = &acl->acl_entry[i]; 15973890Srwatson break; 16073890Srwatson 16173890Srwatson default: 16292666Speter break; 16373890Srwatson } 16473890Srwatson } 16573890Srwatson 16673890Srwatson /* 16782255Srwatson * An ACL_OTHER entry should always exist in a valid access 16882255Srwatson * ACL. If it doesn't, then generate a serious failure. For now, 16982255Srwatson * this means a debugging message and EPERM, but in the future 17082255Srwatson * should probably be a panic. 17173890Srwatson */ 17273890Srwatson if (acl_other == NULL) { 17373890Srwatson /* 17482255Srwatson * XXX This should never happen 17573890Srwatson */ 17673890Srwatson printf("vaccess_acl_posix1e: ACL_OTHER missing\n"); 17773890Srwatson return (EPERM); 17873890Srwatson } 17982255Srwatson 18082255Srwatson /* 18182255Srwatson * Checks against ACL_USER, ACL_GROUP_OBJ, and ACL_GROUP fields 18282255Srwatson * are masked by an ACL_MASK entry, if any. As such, first identify 18382255Srwatson * the ACL_MASK field, then iterate through identifying potential 18482255Srwatson * user matches, then group matches. If there is no ACL_MASK, 18582255Srwatson * assume that the mask allows all requests to succeed. 18682255Srwatson */ 18773890Srwatson if (acl_mask != NULL) { 18873890Srwatson acl_mask_granted = 0; 18975404Sjedgar if (acl_mask->ae_perm & ACL_EXECUTE) 19073890Srwatson acl_mask_granted |= VEXEC; 19175404Sjedgar if (acl_mask->ae_perm & ACL_READ) 19273890Srwatson acl_mask_granted |= VREAD; 19375404Sjedgar if (acl_mask->ae_perm & ACL_WRITE) 194100481Srwatson acl_mask_granted |= (VWRITE | VAPPEND); 19573890Srwatson } else 196100481Srwatson acl_mask_granted = VEXEC | VREAD | VWRITE | VAPPEND; 19773890Srwatson 19873890Srwatson /* 19982255Srwatson * Iterate through user ACL entries. Do checks twice, first 20082255Srwatson * without privilege, and then if a match is found but failed, 20182255Srwatson * a second time with privilege. 20273890Srwatson */ 20373890Srwatson 20473890Srwatson /* 20573890Srwatson * Check ACL_USER ACL entries. 20673890Srwatson */ 20773890Srwatson for (i = 0; i < acl->acl_cnt; i++) { 20873890Srwatson switch (acl->acl_entry[i].ae_tag) { 20973890Srwatson case ACL_USER: 21073890Srwatson if (acl->acl_entry[i].ae_id != cred->cr_uid) 21173890Srwatson break; 21273890Srwatson dac_granted = 0; 21375404Sjedgar if (acl->acl_entry[i].ae_perm & ACL_EXECUTE) 21473890Srwatson dac_granted |= VEXEC; 21575404Sjedgar if (acl->acl_entry[i].ae_perm & ACL_READ) 21673890Srwatson dac_granted |= VREAD; 21775404Sjedgar if (acl->acl_entry[i].ae_perm & ACL_WRITE) 218100481Srwatson dac_granted |= (VWRITE | VAPPEND); 21973890Srwatson dac_granted &= acl_mask_granted; 22073890Srwatson if ((acc_mode & dac_granted) == acc_mode) 22173890Srwatson return (0); 22275571Srwatson if ((acc_mode & (dac_granted | cap_granted)) != 22375571Srwatson acc_mode) 22475571Srwatson goto error; 22575571Srwatson 22675571Srwatson if (privused != NULL) 22775571Srwatson *privused = 1; 22875571Srwatson return (0); 22973890Srwatson } 23073890Srwatson } 23173890Srwatson 23273890Srwatson /* 23373890Srwatson * Group match is best-match, not first-match, so find a 23473890Srwatson * "best" match. Iterate across, testing each potential group 23573890Srwatson * match. Make sure we keep track of whether we found a match 23682255Srwatson * or not, so that we know if we should try again with any 23782255Srwatson * available privilege, or if we should move on to ACL_OTHER. 23873890Srwatson */ 23973890Srwatson group_matched = 0; 24073890Srwatson for (i = 0; i < acl->acl_cnt; i++) { 24173890Srwatson switch (acl->acl_entry[i].ae_tag) { 24273890Srwatson case ACL_GROUP_OBJ: 24375888Stmm if (!groupmember(file_gid, cred)) 24475571Srwatson break; 24575571Srwatson dac_granted = 0; 24675571Srwatson if (acl->acl_entry[i].ae_perm & ACL_EXECUTE) 24775571Srwatson dac_granted |= VEXEC; 24875571Srwatson if (acl->acl_entry[i].ae_perm & ACL_READ) 24975571Srwatson dac_granted |= VREAD; 25075571Srwatson if (acl->acl_entry[i].ae_perm & ACL_WRITE) 251100481Srwatson dac_granted |= (VWRITE | VAPPEND); 25275571Srwatson dac_granted &= acl_mask_granted; 25375571Srwatson 25475571Srwatson if ((acc_mode & dac_granted) == acc_mode) 25575571Srwatson return (0); 25675571Srwatson 25775571Srwatson group_matched = 1; 25875571Srwatson break; 25975571Srwatson 26073890Srwatson case ACL_GROUP: 26175571Srwatson if (!groupmember(acl->acl_entry[i].ae_id, cred)) 26275571Srwatson break; 26375571Srwatson dac_granted = 0; 26475571Srwatson if (acl->acl_entry[i].ae_perm & ACL_EXECUTE) 26575571Srwatson dac_granted |= VEXEC; 26675571Srwatson if (acl->acl_entry[i].ae_perm & ACL_READ) 26775571Srwatson dac_granted |= VREAD; 26875571Srwatson if (acl->acl_entry[i].ae_perm & ACL_WRITE) 269100481Srwatson dac_granted |= (VWRITE | VAPPEND); 27075571Srwatson dac_granted &= acl_mask_granted; 27173890Srwatson 27275571Srwatson if ((acc_mode & dac_granted) == acc_mode) 27375571Srwatson return (0); 27473890Srwatson 27575571Srwatson group_matched = 1; 27675571Srwatson break; 27775571Srwatson 27873890Srwatson default: 27992666Speter break; 28073890Srwatson } 28173890Srwatson } 28273890Srwatson 28373890Srwatson if (group_matched == 1) { 28473890Srwatson /* 28573890Srwatson * There was a match, but it did not grant rights via 28673890Srwatson * pure DAC. Try again, this time with privilege. 28773890Srwatson */ 28873890Srwatson for (i = 0; i < acl->acl_cnt; i++) { 28973890Srwatson switch (acl->acl_entry[i].ae_tag) { 29073890Srwatson case ACL_GROUP_OBJ: 29176139Srwatson if (!groupmember(file_gid, cred)) 29275571Srwatson break; 29375571Srwatson dac_granted = 0; 29475571Srwatson if (acl->acl_entry[i].ae_perm & ACL_EXECUTE) 29575571Srwatson dac_granted |= VEXEC; 29675571Srwatson if (acl->acl_entry[i].ae_perm & ACL_READ) 29775571Srwatson dac_granted |= VREAD; 29875571Srwatson if (acl->acl_entry[i].ae_perm & ACL_WRITE) 299100481Srwatson dac_granted |= (VWRITE | VAPPEND); 30075571Srwatson dac_granted &= acl_mask_granted; 30175571Srwatson 30275571Srwatson if ((acc_mode & (dac_granted | cap_granted)) != 30375571Srwatson acc_mode) 30475571Srwatson break; 30575571Srwatson 30675571Srwatson if (privused != NULL) 30775571Srwatson *privused = 1; 30875571Srwatson return (0); 30975571Srwatson 31073890Srwatson case ACL_GROUP: 31175571Srwatson if (!groupmember(acl->acl_entry[i].ae_id, 31275571Srwatson cred)) 31375571Srwatson break; 31475571Srwatson dac_granted = 0; 31575571Srwatson if (acl->acl_entry[i].ae_perm & ACL_EXECUTE) 31675571Srwatson dac_granted |= VEXEC; 31775571Srwatson if (acl->acl_entry[i].ae_perm & ACL_READ) 31875571Srwatson dac_granted |= VREAD; 31975571Srwatson if (acl->acl_entry[i].ae_perm & ACL_WRITE) 320100481Srwatson dac_granted |= (VWRITE | VAPPEND); 32175571Srwatson dac_granted &= acl_mask_granted; 32275571Srwatson 32375571Srwatson if ((acc_mode & (dac_granted | cap_granted)) != 32475571Srwatson acc_mode) 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 35373890Srwatson if ((acc_mode & dac_granted) == acc_mode) 35473890Srwatson return (0); 35573890Srwatson if ((acc_mode & (dac_granted | cap_granted)) == acc_mode) { 35673890Srwatson if (privused != NULL) 35773890Srwatson *privused = 1; 35873890Srwatson return (0); 35973890Srwatson } 36073890Srwatson 36173890Srwatsonerror: 36273890Srwatson return ((acc_mode & VADMIN) ? EPERM : EACCES); 36373890Srwatson} 36473890Srwatson 36573890Srwatson/* 36696755Strhodes * For the purposes of filesystems maintaining the _OBJ entries in an 36773890Srwatson * inode with a mode_t field, this routine converts a mode_t entry 36873890Srwatson * to an acl_perm_t. 36973890Srwatson */ 37073890Srwatsonacl_perm_t 37173890Srwatsonacl_posix1e_mode_to_perm(acl_tag_t tag, mode_t mode) 37273890Srwatson{ 37373890Srwatson acl_perm_t perm = 0; 37473890Srwatson 37573890Srwatson switch(tag) { 37673890Srwatson case ACL_USER_OBJ: 37773890Srwatson if (mode & S_IXUSR) 37875404Sjedgar perm |= ACL_EXECUTE; 37973890Srwatson if (mode & S_IRUSR) 38075404Sjedgar perm |= ACL_READ; 38173890Srwatson if (mode & S_IWUSR) 38275404Sjedgar perm |= ACL_WRITE; 38373890Srwatson return (perm); 38473890Srwatson 38573890Srwatson case ACL_GROUP_OBJ: 38673890Srwatson if (mode & S_IXGRP) 38775404Sjedgar perm |= ACL_EXECUTE; 38873890Srwatson if (mode & S_IRGRP) 38975404Sjedgar perm |= ACL_READ; 39073890Srwatson if (mode & S_IWGRP) 39175404Sjedgar perm |= ACL_WRITE; 39273890Srwatson return (perm); 39373890Srwatson 39473890Srwatson case ACL_OTHER: 39573890Srwatson if (mode & S_IXOTH) 39675404Sjedgar perm |= ACL_EXECUTE; 39773890Srwatson if (mode & S_IROTH) 39875404Sjedgar perm |= ACL_READ; 39973890Srwatson if (mode & S_IWOTH) 40075404Sjedgar perm |= ACL_WRITE; 40173890Srwatson return (perm); 40273890Srwatson 40373890Srwatson default: 40473890Srwatson printf("acl_posix1e_mode_to_perm: invalid tag (%d)\n", tag); 40573890Srwatson return (0); 40673890Srwatson } 40773890Srwatson} 40873890Srwatson 40973890Srwatson/* 41073890Srwatson * Given inode information (uid, gid, mode), return an acl entry of the 41173890Srwatson * appropriate type. 41273890Srwatson */ 41373890Srwatsonstruct acl_entry 41473890Srwatsonacl_posix1e_mode_to_entry(acl_tag_t tag, uid_t uid, gid_t gid, mode_t mode) 41573890Srwatson{ 41673890Srwatson struct acl_entry acl_entry; 41773890Srwatson 41873890Srwatson acl_entry.ae_tag = tag; 41973890Srwatson acl_entry.ae_perm = acl_posix1e_mode_to_perm(tag, mode); 42073890Srwatson switch(tag) { 42173890Srwatson case ACL_USER_OBJ: 42273890Srwatson acl_entry.ae_id = uid; 42373890Srwatson break; 42473890Srwatson 42573890Srwatson case ACL_GROUP_OBJ: 42673890Srwatson acl_entry.ae_id = gid; 42773890Srwatson break; 42873890Srwatson 42973890Srwatson case ACL_OTHER: 43082769Sjedgar acl_entry.ae_id = ACL_UNDEFINED_ID; 43173890Srwatson break; 43273890Srwatson 43373890Srwatson default: 43482769Sjedgar acl_entry.ae_id = ACL_UNDEFINED_ID; 43573890Srwatson printf("acl_posix1e_mode_to_entry: invalid tag (%d)\n", tag); 43673890Srwatson } 43773890Srwatson 43873890Srwatson return (acl_entry); 43973890Srwatson} 44073890Srwatson 44173890Srwatson/* 44273890Srwatson * Utility function to generate a file mode given appropriate ACL entries. 44373890Srwatson */ 44473890Srwatsonmode_t 44573890Srwatsonacl_posix1e_perms_to_mode(struct acl_entry *acl_user_obj_entry, 44673890Srwatson struct acl_entry *acl_group_obj_entry, struct acl_entry *acl_other_entry) 44773890Srwatson{ 44873890Srwatson mode_t mode; 44973890Srwatson 45073890Srwatson mode = 0; 45175404Sjedgar if (acl_user_obj_entry->ae_perm & ACL_EXECUTE) 45273890Srwatson mode |= S_IXUSR; 45375404Sjedgar if (acl_user_obj_entry->ae_perm & ACL_READ) 45473890Srwatson mode |= S_IRUSR; 45575404Sjedgar if (acl_user_obj_entry->ae_perm & ACL_WRITE) 45673890Srwatson mode |= S_IWUSR; 45775404Sjedgar if (acl_group_obj_entry->ae_perm & ACL_EXECUTE) 45873890Srwatson mode |= S_IXGRP; 45975404Sjedgar if (acl_group_obj_entry->ae_perm & ACL_READ) 46073890Srwatson mode |= S_IRGRP; 46175404Sjedgar if (acl_group_obj_entry->ae_perm & ACL_WRITE) 46273890Srwatson mode |= S_IWGRP; 46375404Sjedgar if (acl_other_entry->ae_perm & ACL_EXECUTE) 46473890Srwatson mode |= S_IXOTH; 46575404Sjedgar if (acl_other_entry->ae_perm & ACL_READ) 46673890Srwatson mode |= S_IROTH; 46775404Sjedgar if (acl_other_entry->ae_perm & ACL_WRITE) 46873890Srwatson mode |= S_IWOTH; 46973890Srwatson 47073890Srwatson return (mode); 47173890Srwatson} 47273890Srwatson 47373890Srwatson/* 47473890Srwatson * Perform a syntactic check of the ACL, sufficient to allow an 47596755Strhodes * implementing filesystem to determine if it should accept this and 47673890Srwatson * rely on the POSIX.1e ACL properties. 47773890Srwatson */ 47873890Srwatsonint 47973890Srwatsonacl_posix1e_check(struct acl *acl) 48073890Srwatson{ 48173890Srwatson int num_acl_user_obj, num_acl_user, num_acl_group_obj, num_acl_group; 48273890Srwatson int num_acl_mask, num_acl_other, i; 48373890Srwatson 48473890Srwatson /* 48573890Srwatson * Verify that the number of entries does not exceed the maximum 48673890Srwatson * defined for acl_t. 48773890Srwatson * Verify that the correct number of various sorts of ae_tags are 48873890Srwatson * present: 48973890Srwatson * Exactly one ACL_USER_OBJ 49073890Srwatson * Exactly one ACL_GROUP_OBJ 49173890Srwatson * Exactly one ACL_OTHER 49273890Srwatson * If any ACL_USER or ACL_GROUP entries appear, then exactly one 49373890Srwatson * ACL_MASK entry must also appear. 49473890Srwatson * Verify that all ae_perm entries are in ACL_PERM_BITS. 49573890Srwatson * Verify all ae_tag entries are understood by this implementation. 49673890Srwatson * Note: Does not check for uniqueness of qualifier (ae_id) field. 49773890Srwatson */ 49873890Srwatson num_acl_user_obj = num_acl_user = num_acl_group_obj = num_acl_group = 49973890Srwatson num_acl_mask = num_acl_other = 0; 50073890Srwatson if (acl->acl_cnt > ACL_MAX_ENTRIES || acl->acl_cnt < 0) 50173890Srwatson return (EINVAL); 50273890Srwatson for (i = 0; i < acl->acl_cnt; i++) { 50373890Srwatson /* 50473890Srwatson * Check for a valid tag. 50573890Srwatson */ 50673890Srwatson switch(acl->acl_entry[i].ae_tag) { 50773890Srwatson case ACL_USER_OBJ: 50875571Srwatson acl->acl_entry[i].ae_id = ACL_UNDEFINED_ID; /* XXX */ 50975571Srwatson if (acl->acl_entry[i].ae_id != ACL_UNDEFINED_ID) 51075571Srwatson return (EINVAL); 51173890Srwatson num_acl_user_obj++; 51273890Srwatson break; 51373890Srwatson case ACL_GROUP_OBJ: 51475571Srwatson acl->acl_entry[i].ae_id = ACL_UNDEFINED_ID; /* XXX */ 51575571Srwatson if (acl->acl_entry[i].ae_id != ACL_UNDEFINED_ID) 51675571Srwatson return (EINVAL); 51773890Srwatson num_acl_group_obj++; 51873890Srwatson break; 51973890Srwatson case ACL_USER: 52075571Srwatson if (acl->acl_entry[i].ae_id == ACL_UNDEFINED_ID) 52175571Srwatson return (EINVAL); 52273890Srwatson num_acl_user++; 52373890Srwatson break; 52473890Srwatson case ACL_GROUP: 52575571Srwatson if (acl->acl_entry[i].ae_id == ACL_UNDEFINED_ID) 52675571Srwatson return (EINVAL); 52773890Srwatson num_acl_group++; 52873890Srwatson break; 52973890Srwatson case ACL_OTHER: 53075571Srwatson acl->acl_entry[i].ae_id = ACL_UNDEFINED_ID; /* XXX */ 53175571Srwatson if (acl->acl_entry[i].ae_id != ACL_UNDEFINED_ID) 53275571Srwatson return (EINVAL); 53373890Srwatson num_acl_other++; 53473890Srwatson break; 53573890Srwatson case ACL_MASK: 53675571Srwatson acl->acl_entry[i].ae_id = ACL_UNDEFINED_ID; /* XXX */ 53775571Srwatson if (acl->acl_entry[i].ae_id != ACL_UNDEFINED_ID) 53875571Srwatson return (EINVAL); 53973890Srwatson num_acl_mask++; 54073890Srwatson break; 54173890Srwatson default: 54273890Srwatson return (EINVAL); 54373890Srwatson } 54473890Srwatson /* 54573890Srwatson * Check for valid perm entries. 54673890Srwatson */ 54773890Srwatson if ((acl->acl_entry[i].ae_perm | ACL_PERM_BITS) != 54873890Srwatson ACL_PERM_BITS) 54973890Srwatson return (EINVAL); 55073890Srwatson } 55173890Srwatson if ((num_acl_user_obj != 1) || (num_acl_group_obj != 1) || 55273890Srwatson (num_acl_other != 1) || (num_acl_mask != 0 && num_acl_mask != 1)) 55373890Srwatson return (EINVAL); 55473890Srwatson if (((num_acl_group != 0) || (num_acl_user != 0)) && 55573890Srwatson (num_acl_mask != 1)) 55673890Srwatson return (EINVAL); 55773890Srwatson return (0); 55873890Srwatson} 55973890Srwatson 56073890Srwatson/* 56154803Srwatson * These calls wrap the real vnode operations, and are called by the 56254803Srwatson * syscall code once the syscall has converted the path or file 56354803Srwatson * descriptor to a vnode (unlocked). The aclp pointer is assumed 56454803Srwatson * still to point to userland, so this should not be consumed within 56554803Srwatson * the kernel except by syscall code. Other code should directly 56654803Srwatson * invoke VOP_{SET,GET}ACL. 56754803Srwatson */ 56854803Srwatson 56954803Srwatson/* 57054803Srwatson * Given a vnode, set its ACL. 57154803Srwatson */ 57254803Srwatsonstatic int 57385582Srwatsonvacl_set_acl(struct thread *td, struct vnode *vp, acl_type_t type, 57456272Srwatson struct acl *aclp) 57554803Srwatson{ 57654803Srwatson struct acl inkernacl; 57790202Srwatson struct mount *mp; 57854803Srwatson int error; 57954803Srwatson 58054803Srwatson error = copyin(aclp, &inkernacl, sizeof(struct acl)); 58154803Srwatson if (error) 58254803Srwatson return(error); 58390202Srwatson error = vn_start_write(vp, &mp, V_WAIT | PCATCH); 58490202Srwatson if (error != 0) 58590202Srwatson return (error); 58691406Sjhb VOP_LEASE(vp, td, td->td_ucred, LEASE_WRITE); 58783366Sjulian vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); 588101122Srwatson#ifdef MAC 589101122Srwatson error = mac_check_vnode_setacl(td->td_ucred, vp, type, &inkernacl); 590101122Srwatson if (error != 0) 591101122Srwatson goto out; 592101122Srwatson#endif 59391406Sjhb error = VOP_SETACL(vp, type, &inkernacl, td->td_ucred, td); 594101122Srwatson#ifdef MAC 595101122Srwatsonout: 596101122Srwatson#endif 59783366Sjulian VOP_UNLOCK(vp, 0, td); 59890202Srwatson vn_finished_write(mp); 59971699Sjhb return(error); 60054803Srwatson} 60154803Srwatson 60254803Srwatson/* 60354803Srwatson * Given a vnode, get its ACL. 60454803Srwatson */ 60554803Srwatsonstatic int 60685582Srwatsonvacl_get_acl(struct thread *td, struct vnode *vp, acl_type_t type, 60754803Srwatson struct acl *aclp) 60854803Srwatson{ 60954803Srwatson struct acl inkernelacl; 61054803Srwatson int error; 61154803Srwatson 61291406Sjhb VOP_LEASE(vp, td, td->td_ucred, LEASE_WRITE); 61383366Sjulian vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); 614101122Srwatson#ifdef MAC 615101122Srwatson error = mac_check_vnode_getacl(td->td_ucred, vp, type); 616101122Srwatson if (error != 0) 617101122Srwatson goto out; 618101122Srwatson#endif 61991406Sjhb error = VOP_GETACL(vp, type, &inkernelacl, td->td_ucred, td); 620101122Srwatson#ifdef MAC 621101122Srwatsonout: 622101122Srwatson#endif 62383366Sjulian VOP_UNLOCK(vp, 0, td); 62454803Srwatson if (error == 0) 62554803Srwatson error = copyout(&inkernelacl, aclp, sizeof(struct acl)); 62654803Srwatson return (error); 62754803Srwatson} 62854803Srwatson 62954803Srwatson/* 63054803Srwatson * Given a vnode, delete its ACL. 63154803Srwatson */ 63254803Srwatsonstatic int 63385582Srwatsonvacl_delete(struct thread *td, struct vnode *vp, acl_type_t type) 63454803Srwatson{ 63590202Srwatson struct mount *mp; 63654803Srwatson int error; 63754803Srwatson 63890202Srwatson error = vn_start_write(vp, &mp, V_WAIT | PCATCH); 63990202Srwatson if (error) 64090202Srwatson return (error); 64191406Sjhb VOP_LEASE(vp, td, td->td_ucred, LEASE_WRITE); 64283366Sjulian vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); 643101122Srwatson#ifdef MAC 644101122Srwatson error = mac_check_vnode_deleteacl(td->td_ucred, vp, type); 645101122Srwatson if (error) 646101122Srwatson goto out; 647101122Srwatson#endif 648101122Srwatson error = VOP_SETACL(vp, type, 0, td->td_ucred, td); 649101122Srwatson#ifdef MAC 650101122Srwatsonout: 651101122Srwatson#endif 65283366Sjulian VOP_UNLOCK(vp, 0, td); 65390202Srwatson vn_finished_write(mp); 65454803Srwatson return (error); 65554803Srwatson} 65654803Srwatson 65754803Srwatson/* 65854803Srwatson * Given a vnode, check whether an ACL is appropriate for it 65954803Srwatson */ 66054803Srwatsonstatic int 66185582Srwatsonvacl_aclcheck(struct thread *td, struct vnode *vp, acl_type_t type, 66254803Srwatson struct acl *aclp) 66354803Srwatson{ 66454803Srwatson struct acl inkernelacl; 66554803Srwatson int error; 66654803Srwatson 66754803Srwatson error = copyin(aclp, &inkernelacl, sizeof(struct acl)); 66854803Srwatson if (error) 66954803Srwatson return(error); 67091406Sjhb error = VOP_ACLCHECK(vp, type, &inkernelacl, td->td_ucred, td); 67154803Srwatson return (error); 67254803Srwatson} 67354803Srwatson 67454803Srwatson/* 67554803Srwatson * syscalls -- convert the path/fd to a vnode, and call vacl_whatever. 67654803Srwatson * Don't need to lock, as the vacl_ code will get/release any locks 67754803Srwatson * required. 67854803Srwatson */ 67954803Srwatson 68054803Srwatson/* 68154803Srwatson * Given a file path, get an ACL for it 68282713Sdillon * 68382713Sdillon * MPSAFE 68454803Srwatson */ 68554803Srwatsonint 68685582Srwatson__acl_get_file(struct thread *td, struct __acl_get_file_args *uap) 68754803Srwatson{ 68854803Srwatson struct nameidata nd; 68954803Srwatson int error; 69054803Srwatson 69182713Sdillon mtx_lock(&Giant); 692107849Salfred NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, td); 69354803Srwatson error = namei(&nd); 69482713Sdillon if (error == 0) { 695107855Salfred error = vacl_get_acl(td, nd.ni_vp, uap->type, uap->aclp); 69682713Sdillon NDFREE(&nd, 0); 69782713Sdillon } 69882713Sdillon mtx_unlock(&Giant); 69954803Srwatson return (error); 70054803Srwatson} 70154803Srwatson 70254803Srwatson/* 70354803Srwatson * Given a file path, set an ACL for it 70482713Sdillon * 70582713Sdillon * MPSAFE 70654803Srwatson */ 70754803Srwatsonint 70885582Srwatson__acl_set_file(struct thread *td, struct __acl_set_file_args *uap) 70954803Srwatson{ 71054803Srwatson struct nameidata nd; 71154803Srwatson int error; 71254803Srwatson 71382713Sdillon mtx_lock(&Giant); 714107849Salfred NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, td); 71554803Srwatson error = namei(&nd); 71682713Sdillon if (error == 0) { 717107855Salfred error = vacl_set_acl(td, nd.ni_vp, uap->type, uap->aclp); 71882713Sdillon NDFREE(&nd, 0); 71982713Sdillon } 72082713Sdillon mtx_unlock(&Giant); 72154803Srwatson return (error); 72254803Srwatson} 72354803Srwatson 72454803Srwatson/* 72554803Srwatson * Given a file descriptor, get an ACL for it 72682713Sdillon * 72782713Sdillon * MPSAFE 72854803Srwatson */ 72954803Srwatsonint 73085582Srwatson__acl_get_fd(struct thread *td, struct __acl_get_fd_args *uap) 73154803Srwatson{ 73254803Srwatson struct file *fp; 73354803Srwatson int error; 73454803Srwatson 73582713Sdillon mtx_lock(&Giant); 736107849Salfred error = getvnode(td->td_proc->p_fd, uap->filedes, &fp); 73782713Sdillon if (error == 0) { 73883366Sjulian error = vacl_get_acl(td, (struct vnode *)fp->f_data, 739107849Salfred uap->type, uap->aclp); 74089306Salfred fdrop(fp, td); 74182713Sdillon } 74282713Sdillon mtx_unlock(&Giant); 74382713Sdillon return (error); 74454803Srwatson} 74554803Srwatson 74654803Srwatson/* 74754803Srwatson * Given a file descriptor, set an ACL for it 74882713Sdillon * 74982713Sdillon * MPSAFE 75054803Srwatson */ 75154803Srwatsonint 75285582Srwatson__acl_set_fd(struct thread *td, struct __acl_set_fd_args *uap) 75354803Srwatson{ 75454803Srwatson struct file *fp; 75554803Srwatson int error; 75654803Srwatson 75782713Sdillon mtx_lock(&Giant); 758107849Salfred error = getvnode(td->td_proc->p_fd, uap->filedes, &fp); 75982713Sdillon if (error == 0) { 76083366Sjulian error = vacl_set_acl(td, (struct vnode *)fp->f_data, 761107849Salfred uap->type, uap->aclp); 76289306Salfred fdrop(fp, td); 76382713Sdillon } 76482713Sdillon mtx_unlock(&Giant); 76582713Sdillon return (error); 76654803Srwatson} 76754803Srwatson 76854803Srwatson/* 76954803Srwatson * Given a file path, delete an ACL from it. 77082713Sdillon * 77182713Sdillon * MPSAFE 77254803Srwatson */ 77354803Srwatsonint 77485582Srwatson__acl_delete_file(struct thread *td, struct __acl_delete_file_args *uap) 77554803Srwatson{ 77654803Srwatson struct nameidata nd; 77754803Srwatson int error; 77854803Srwatson 77982713Sdillon mtx_lock(&Giant); 780107849Salfred NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, td); 78154803Srwatson error = namei(&nd); 78282713Sdillon if (error == 0) { 783107849Salfred error = vacl_delete(td, nd.ni_vp, uap->type); 78482713Sdillon NDFREE(&nd, 0); 78582713Sdillon } 78682713Sdillon mtx_unlock(&Giant); 78754803Srwatson return (error); 78854803Srwatson} 78954803Srwatson 79054803Srwatson/* 79154803Srwatson * Given a file path, delete an ACL from it. 79282713Sdillon * 79382713Sdillon * MPSAFE 79454803Srwatson */ 79554803Srwatsonint 79685582Srwatson__acl_delete_fd(struct thread *td, struct __acl_delete_fd_args *uap) 79754803Srwatson{ 79854803Srwatson struct file *fp; 79954803Srwatson int error; 80054803Srwatson 80182713Sdillon mtx_lock(&Giant); 802107849Salfred error = getvnode(td->td_proc->p_fd, uap->filedes, &fp); 80382713Sdillon if (error == 0) { 80483366Sjulian error = vacl_delete(td, (struct vnode *)fp->f_data, 805107849Salfred uap->type); 80689306Salfred fdrop(fp, td); 80782713Sdillon } 80882713Sdillon mtx_unlock(&Giant); 80954803Srwatson return (error); 81054803Srwatson} 81154803Srwatson 81254803Srwatson/* 81354803Srwatson * Given a file path, check an ACL for it 81482713Sdillon * 81582713Sdillon * MPSAFE 81654803Srwatson */ 81754803Srwatsonint 81885582Srwatson__acl_aclcheck_file(struct thread *td, struct __acl_aclcheck_file_args *uap) 81954803Srwatson{ 82054803Srwatson struct nameidata nd; 82154803Srwatson int error; 82254803Srwatson 82382713Sdillon mtx_lock(&Giant); 824107849Salfred NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, td); 82554803Srwatson error = namei(&nd); 82682713Sdillon if (error == 0) { 827107855Salfred error = vacl_aclcheck(td, nd.ni_vp, uap->type, uap->aclp); 82882713Sdillon NDFREE(&nd, 0); 82982713Sdillon } 83082713Sdillon mtx_unlock(&Giant); 83154803Srwatson return (error); 83254803Srwatson} 83354803Srwatson 83454803Srwatson/* 83554803Srwatson * Given a file descriptor, check an ACL for it 83682713Sdillon * 83782713Sdillon * MPSAFE 83854803Srwatson */ 83954803Srwatsonint 84085582Srwatson__acl_aclcheck_fd(struct thread *td, struct __acl_aclcheck_fd_args *uap) 84154803Srwatson{ 84254803Srwatson struct file *fp; 84354803Srwatson int error; 84454803Srwatson 84582713Sdillon mtx_lock(&Giant); 846107849Salfred error = getvnode(td->td_proc->p_fd, uap->filedes, &fp); 84782713Sdillon if (error == 0) { 84883366Sjulian error = vacl_aclcheck(td, (struct vnode *)fp->f_data, 849107849Salfred uap->type, uap->aclp); 85089306Salfred fdrop(fp, td); 85182713Sdillon } 85282713Sdillon mtx_unlock(&Giant); 85382713Sdillon return (error); 85454803Srwatson} 855