subr_acl_posix1e.c revision 85845
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/subr_acl_posix1e.c 85845 2001-11-01 21:37:07Z rwatson $ 2954803Srwatson */ 3054803Srwatson/* 3173890Srwatson * Developed by the TrustedBSD Project. 3273890Srwatson * Support for POSIX.1e access control lists. 3354803Srwatson */ 3454803Srwatson 3554803Srwatson#include <sys/param.h> 3654803Srwatson#include <sys/systm.h> 3754803Srwatson#include <sys/sysproto.h> 3854803Srwatson#include <sys/kernel.h> 3954803Srwatson#include <sys/malloc.h> 4054803Srwatson#include <sys/vnode.h> 4154803Srwatson#include <sys/lock.h> 4282713Sdillon#include <sys/mutex.h> 4354803Srwatson#include <sys/namei.h> 4454803Srwatson#include <sys/file.h> 4554803Srwatson#include <sys/proc.h> 4654803Srwatson#include <sys/sysent.h> 4754803Srwatson#include <sys/errno.h> 4854803Srwatson#include <sys/stat.h> 4954803Srwatson#include <sys/acl.h> 5054803Srwatson 5173890SrwatsonMALLOC_DEFINE(M_ACL, "acl", "access control list"); 5254803Srwatson 5385582Srwatsonstatic int vacl_set_acl(struct thread *td, struct vnode *vp, acl_type_t type, 5456272Srwatson struct acl *aclp); 5585582Srwatsonstatic int vacl_get_acl(struct thread *td, struct vnode *vp, acl_type_t type, 5656272Srwatson struct acl *aclp); 5785582Srwatsonstatic int vacl_aclcheck(struct thread *td, struct vnode *vp, 5875571Srwatson acl_type_t type, struct acl *aclp); 5954803Srwatson 6054803Srwatson/* 6173890Srwatson * Implement a version of vaccess() that understands POSIX.1e ACL semantics. 6273890Srwatson * Return 0 on success, else an errno value. Should be merged into 6373890Srwatson * vaccess() eventually. 6473890Srwatson */ 6573890Srwatsonint 6675571Srwatsonvaccess_acl_posix1e(enum vtype type, uid_t file_uid, gid_t file_gid, 6775571Srwatson struct acl *acl, mode_t acc_mode, struct ucred *cred, int *privused) 6873890Srwatson{ 6973890Srwatson struct acl_entry *acl_other, *acl_mask; 7073890Srwatson mode_t dac_granted; 7173890Srwatson mode_t cap_granted; 7273890Srwatson mode_t acl_mask_granted; 7373890Srwatson int group_matched, i; 7473890Srwatson 7573890Srwatson /* 7673890Srwatson * Look for a normal, non-privileged way to access the file/directory 7773890Srwatson * as requested. If it exists, go with that. Otherwise, attempt 7873890Srwatson * to use privileges granted via cap_granted. In some cases, 7973890Srwatson * which privileges to use may be ambiguous due to "best match", 8073890Srwatson * in which case fall back on first match for the time being. 8173890Srwatson */ 8273890Srwatson if (privused != NULL) 8373890Srwatson *privused = 0; 8473890Srwatson 8573890Srwatson /* 8673890Srwatson * Determine privileges now, but don't apply until we've found 8782255Srwatson * a DAC entry that matches but has failed to allow access. 8873890Srwatson */ 8973890Srwatson#ifndef CAPABILITIES 9073890Srwatson if (suser_xxx(cred, NULL, PRISON_ROOT) == 0) 9173890Srwatson cap_granted = (VEXEC | VREAD | VWRITE | VADMIN); 9273890Srwatson else 9373890Srwatson cap_granted = 0; 9473890Srwatson#else 9573890Srwatson cap_granted = 0; 9673890Srwatson 9773890Srwatson if (type == VDIR) { 9873890Srwatson if ((acc_mode & VEXEC) && !cap_check(cred, NULL, 9973890Srwatson CAP_DAC_READ_SEARCH, PRISON_ROOT)) 10073890Srwatson cap_granted |= VEXEC; 10173890Srwatson } else { 10273890Srwatson if ((acc_mode & VEXEC) && !cap_check(cred, NULL, 10373890Srwatson CAP_DAC_EXECUTE, PRISON_ROOT)) 10473890Srwatson cap_granted |= VEXEC; 10573890Srwatson } 10673890Srwatson 10773890Srwatson if ((acc_mode & VREAD) && !cap_check(cred, NULL, CAP_DAC_READ_SEARCH, 10873890Srwatson PRISON_ROOT)) 10973890Srwatson cap_granted |= VREAD; 11073890Srwatson 11173890Srwatson if ((acc_mode & VWRITE) && !cap_check(cred, NULL, CAP_DAC_WRITE, 11273890Srwatson PRISON_ROOT)) 11373890Srwatson cap_granted |= VWRITE; 11473890Srwatson 11573890Srwatson if ((acc_mode & VADMIN) && !cap_check(cred, NULL, CAP_FOWNER, 11673890Srwatson PRISON_ROOT)) 11773890Srwatson cap_granted |= VADMIN; 11873890Srwatson#endif /* CAPABILITIES */ 11973890Srwatson 12073890Srwatson /* 12182255Srwatson * The owner matches if the effective uid associated with the 12282255Srwatson * credential matches that of the ACL_USER_OBJ entry. While we're 12382255Srwatson * doing the first scan, also cache the location of the ACL_MASK 12482255Srwatson * and ACL_OTHER entries, preventing some future iterations. 12573890Srwatson */ 12673890Srwatson acl_mask = acl_other = NULL; 12773890Srwatson for (i = 0; i < acl->acl_cnt; i++) { 12873890Srwatson switch (acl->acl_entry[i].ae_tag) { 12973890Srwatson case ACL_USER_OBJ: 13075571Srwatson if (file_uid != cred->cr_uid) 13173890Srwatson break; 13273890Srwatson dac_granted = 0; 13373890Srwatson dac_granted |= VADMIN; 13475404Sjedgar if (acl->acl_entry[i].ae_perm & ACL_EXECUTE) 13573890Srwatson dac_granted |= VEXEC; 13675404Sjedgar if (acl->acl_entry[i].ae_perm & ACL_READ) 13773890Srwatson dac_granted |= VREAD; 13875404Sjedgar if (acl->acl_entry[i].ae_perm & ACL_WRITE) 13973890Srwatson dac_granted |= VWRITE; 14073890Srwatson if ((acc_mode & dac_granted) == acc_mode) 14173890Srwatson return (0); 14273890Srwatson if ((acc_mode & (dac_granted | cap_granted)) == 14373890Srwatson acc_mode) { 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: 15973890Srwatson } 16073890Srwatson } 16173890Srwatson 16273890Srwatson /* 16382255Srwatson * An ACL_OTHER entry should always exist in a valid access 16482255Srwatson * ACL. If it doesn't, then generate a serious failure. For now, 16582255Srwatson * this means a debugging message and EPERM, but in the future 16682255Srwatson * should probably be a panic. 16773890Srwatson */ 16873890Srwatson if (acl_other == NULL) { 16973890Srwatson /* 17082255Srwatson * XXX This should never happen 17173890Srwatson */ 17273890Srwatson printf("vaccess_acl_posix1e: ACL_OTHER missing\n"); 17373890Srwatson return (EPERM); 17473890Srwatson } 17582255Srwatson 17682255Srwatson /* 17782255Srwatson * Checks against ACL_USER, ACL_GROUP_OBJ, and ACL_GROUP fields 17882255Srwatson * are masked by an ACL_MASK entry, if any. As such, first identify 17982255Srwatson * the ACL_MASK field, then iterate through identifying potential 18082255Srwatson * user matches, then group matches. If there is no ACL_MASK, 18182255Srwatson * assume that the mask allows all requests to succeed. 18282255Srwatson */ 18373890Srwatson if (acl_mask != NULL) { 18473890Srwatson acl_mask_granted = 0; 18575404Sjedgar if (acl_mask->ae_perm & ACL_EXECUTE) 18673890Srwatson acl_mask_granted |= VEXEC; 18775404Sjedgar if (acl_mask->ae_perm & ACL_READ) 18873890Srwatson acl_mask_granted |= VREAD; 18975404Sjedgar if (acl_mask->ae_perm & ACL_WRITE) 19073890Srwatson acl_mask_granted |= VWRITE; 19173890Srwatson } else 19273890Srwatson acl_mask_granted = VEXEC | VREAD | VWRITE; 19373890Srwatson 19473890Srwatson /* 19582255Srwatson * Iterate through user ACL entries. Do checks twice, first 19682255Srwatson * without privilege, and then if a match is found but failed, 19782255Srwatson * a second time with privilege. 19873890Srwatson */ 19973890Srwatson 20073890Srwatson /* 20173890Srwatson * Check ACL_USER ACL entries. 20273890Srwatson */ 20373890Srwatson for (i = 0; i < acl->acl_cnt; i++) { 20473890Srwatson switch (acl->acl_entry[i].ae_tag) { 20573890Srwatson case ACL_USER: 20673890Srwatson if (acl->acl_entry[i].ae_id != cred->cr_uid) 20773890Srwatson break; 20873890Srwatson dac_granted = 0; 20975404Sjedgar if (acl->acl_entry[i].ae_perm & ACL_EXECUTE) 21073890Srwatson dac_granted |= VEXEC; 21175404Sjedgar if (acl->acl_entry[i].ae_perm & ACL_READ) 21273890Srwatson dac_granted |= VREAD; 21375404Sjedgar if (acl->acl_entry[i].ae_perm & ACL_WRITE) 21473890Srwatson dac_granted |= VWRITE; 21573890Srwatson dac_granted &= acl_mask_granted; 21673890Srwatson if ((acc_mode & dac_granted) == acc_mode) 21773890Srwatson return (0); 21875571Srwatson if ((acc_mode & (dac_granted | cap_granted)) != 21975571Srwatson acc_mode) 22075571Srwatson goto error; 22175571Srwatson 22275571Srwatson if (privused != NULL) 22375571Srwatson *privused = 1; 22475571Srwatson return (0); 22573890Srwatson } 22673890Srwatson } 22773890Srwatson 22873890Srwatson /* 22973890Srwatson * Group match is best-match, not first-match, so find a 23073890Srwatson * "best" match. Iterate across, testing each potential group 23173890Srwatson * match. Make sure we keep track of whether we found a match 23282255Srwatson * or not, so that we know if we should try again with any 23382255Srwatson * available privilege, or if we 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) 24775571Srwatson dac_granted |= VWRITE; 24875571Srwatson dac_granted &= acl_mask_granted; 24975571Srwatson 25075571Srwatson if ((acc_mode & dac_granted) == acc_mode) 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) 26575571Srwatson dac_granted |= VWRITE; 26675571Srwatson dac_granted &= acl_mask_granted; 26773890Srwatson 26875571Srwatson if ((acc_mode & dac_granted) == acc_mode) 26975571Srwatson return (0); 27073890Srwatson 27175571Srwatson group_matched = 1; 27275571Srwatson break; 27375571Srwatson 27473890Srwatson default: 27573890Srwatson } 27673890Srwatson } 27773890Srwatson 27873890Srwatson if (group_matched == 1) { 27973890Srwatson /* 28073890Srwatson * There was a match, but it did not grant rights via 28173890Srwatson * pure DAC. Try again, this time with privilege. 28273890Srwatson */ 28373890Srwatson for (i = 0; i < acl->acl_cnt; i++) { 28473890Srwatson switch (acl->acl_entry[i].ae_tag) { 28573890Srwatson case ACL_GROUP_OBJ: 28676139Srwatson if (!groupmember(file_gid, cred)) 28775571Srwatson break; 28875571Srwatson dac_granted = 0; 28975571Srwatson if (acl->acl_entry[i].ae_perm & ACL_EXECUTE) 29075571Srwatson dac_granted |= VEXEC; 29175571Srwatson if (acl->acl_entry[i].ae_perm & ACL_READ) 29275571Srwatson dac_granted |= VREAD; 29375571Srwatson if (acl->acl_entry[i].ae_perm & ACL_WRITE) 29475571Srwatson dac_granted |= VWRITE; 29575571Srwatson dac_granted &= acl_mask_granted; 29675571Srwatson 29775571Srwatson if ((acc_mode & (dac_granted | cap_granted)) != 29875571Srwatson acc_mode) 29975571Srwatson break; 30075571Srwatson 30175571Srwatson if (privused != NULL) 30275571Srwatson *privused = 1; 30375571Srwatson return (0); 30475571Srwatson 30573890Srwatson case ACL_GROUP: 30675571Srwatson if (!groupmember(acl->acl_entry[i].ae_id, 30775571Srwatson cred)) 30875571Srwatson break; 30975571Srwatson dac_granted = 0; 31075571Srwatson if (acl->acl_entry[i].ae_perm & ACL_EXECUTE) 31175571Srwatson dac_granted |= VEXEC; 31275571Srwatson if (acl->acl_entry[i].ae_perm & ACL_READ) 31375571Srwatson dac_granted |= VREAD; 31475571Srwatson if (acl->acl_entry[i].ae_perm & ACL_WRITE) 31575571Srwatson dac_granted |= VWRITE; 31675571Srwatson dac_granted &= acl_mask_granted; 31775571Srwatson 31875571Srwatson if ((acc_mode & (dac_granted | cap_granted)) != 31975571Srwatson acc_mode) 32075571Srwatson break; 32175571Srwatson 32275571Srwatson if (privused != NULL) 32375571Srwatson *privused = 1; 32475571Srwatson return (0); 32575571Srwatson 32673890Srwatson default: 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) 34573890Srwatson dac_granted |= VWRITE; 34673890Srwatson 34773890Srwatson if ((acc_mode & dac_granted) == acc_mode) 34873890Srwatson return (0); 34973890Srwatson if ((acc_mode & (dac_granted | cap_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/* 36073890Srwatson * For the purposes of file systems maintaining the _OBJ entries in an 36173890Srwatson * inode with a mode_t field, this routine converts a mode_t entry 36273890Srwatson * to an 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/* 46873890Srwatson * Perform a syntactic check of the ACL, sufficient to allow an 46973890Srwatson * implementing file system to determine if it should accept this and 47073890Srwatson * rely on the POSIX.1e ACL properties. 47173890Srwatson */ 47273890Srwatsonint 47373890Srwatsonacl_posix1e_check(struct acl *acl) 47473890Srwatson{ 47573890Srwatson int num_acl_user_obj, num_acl_user, num_acl_group_obj, num_acl_group; 47673890Srwatson int num_acl_mask, num_acl_other, i; 47773890Srwatson 47873890Srwatson /* 47973890Srwatson * Verify that the number of entries does not exceed the maximum 48073890Srwatson * defined for acl_t. 48173890Srwatson * Verify that the correct number of various sorts of ae_tags are 48273890Srwatson * present: 48373890Srwatson * Exactly one ACL_USER_OBJ 48473890Srwatson * Exactly one ACL_GROUP_OBJ 48573890Srwatson * Exactly one ACL_OTHER 48673890Srwatson * If any ACL_USER or ACL_GROUP entries appear, then exactly one 48773890Srwatson * ACL_MASK entry must also appear. 48873890Srwatson * Verify that all ae_perm entries are in ACL_PERM_BITS. 48973890Srwatson * Verify all ae_tag entries are understood by this implementation. 49073890Srwatson * Note: Does not check for uniqueness of qualifier (ae_id) field. 49173890Srwatson */ 49273890Srwatson num_acl_user_obj = num_acl_user = num_acl_group_obj = num_acl_group = 49373890Srwatson num_acl_mask = num_acl_other = 0; 49473890Srwatson if (acl->acl_cnt > ACL_MAX_ENTRIES || acl->acl_cnt < 0) 49573890Srwatson return (EINVAL); 49673890Srwatson for (i = 0; i < acl->acl_cnt; i++) { 49773890Srwatson /* 49873890Srwatson * Check for a valid tag. 49973890Srwatson */ 50073890Srwatson switch(acl->acl_entry[i].ae_tag) { 50173890Srwatson case ACL_USER_OBJ: 50275571Srwatson acl->acl_entry[i].ae_id = ACL_UNDEFINED_ID; /* XXX */ 50375571Srwatson if (acl->acl_entry[i].ae_id != ACL_UNDEFINED_ID) 50475571Srwatson return (EINVAL); 50573890Srwatson num_acl_user_obj++; 50673890Srwatson break; 50773890Srwatson case ACL_GROUP_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_group_obj++; 51273890Srwatson break; 51373890Srwatson case ACL_USER: 51475571Srwatson if (acl->acl_entry[i].ae_id == ACL_UNDEFINED_ID) 51575571Srwatson return (EINVAL); 51673890Srwatson num_acl_user++; 51773890Srwatson break; 51873890Srwatson case ACL_GROUP: 51975571Srwatson if (acl->acl_entry[i].ae_id == ACL_UNDEFINED_ID) 52075571Srwatson return (EINVAL); 52173890Srwatson num_acl_group++; 52273890Srwatson break; 52373890Srwatson case ACL_OTHER: 52475571Srwatson acl->acl_entry[i].ae_id = ACL_UNDEFINED_ID; /* XXX */ 52575571Srwatson if (acl->acl_entry[i].ae_id != ACL_UNDEFINED_ID) 52675571Srwatson return (EINVAL); 52773890Srwatson num_acl_other++; 52873890Srwatson break; 52973890Srwatson case ACL_MASK: 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_mask++; 53473890Srwatson break; 53573890Srwatson default: 53673890Srwatson return (EINVAL); 53773890Srwatson } 53873890Srwatson /* 53973890Srwatson * Check for valid perm entries. 54073890Srwatson */ 54173890Srwatson if ((acl->acl_entry[i].ae_perm | ACL_PERM_BITS) != 54273890Srwatson ACL_PERM_BITS) 54373890Srwatson return (EINVAL); 54473890Srwatson } 54573890Srwatson if ((num_acl_user_obj != 1) || (num_acl_group_obj != 1) || 54673890Srwatson (num_acl_other != 1) || (num_acl_mask != 0 && num_acl_mask != 1)) 54773890Srwatson return (EINVAL); 54873890Srwatson if (((num_acl_group != 0) || (num_acl_user != 0)) && 54973890Srwatson (num_acl_mask != 1)) 55073890Srwatson return (EINVAL); 55173890Srwatson return (0); 55273890Srwatson} 55373890Srwatson 55473890Srwatson/* 55554803Srwatson * These calls wrap the real vnode operations, and are called by the 55654803Srwatson * syscall code once the syscall has converted the path or file 55754803Srwatson * descriptor to a vnode (unlocked). The aclp pointer is assumed 55854803Srwatson * still to point to userland, so this should not be consumed within 55954803Srwatson * the kernel except by syscall code. Other code should directly 56054803Srwatson * invoke VOP_{SET,GET}ACL. 56154803Srwatson */ 56254803Srwatson 56354803Srwatson/* 56454803Srwatson * Given a vnode, set its ACL. 56554803Srwatson */ 56654803Srwatsonstatic int 56785582Srwatsonvacl_set_acl(struct thread *td, struct vnode *vp, acl_type_t type, 56856272Srwatson struct acl *aclp) 56954803Srwatson{ 57054803Srwatson struct acl inkernacl; 57154803Srwatson int error; 57254803Srwatson 57354803Srwatson error = copyin(aclp, &inkernacl, sizeof(struct acl)); 57454803Srwatson if (error) 57554803Srwatson return(error); 57683366Sjulian VOP_LEASE(vp, td, td->td_proc->p_ucred, LEASE_WRITE); 57783366Sjulian vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); 57883366Sjulian error = VOP_SETACL(vp, type, &inkernacl, td->td_proc->p_ucred, td); 57983366Sjulian VOP_UNLOCK(vp, 0, td); 58071699Sjhb return(error); 58154803Srwatson} 58254803Srwatson 58354803Srwatson/* 58454803Srwatson * Given a vnode, get its ACL. 58554803Srwatson */ 58654803Srwatsonstatic int 58785582Srwatsonvacl_get_acl(struct thread *td, struct vnode *vp, acl_type_t type, 58854803Srwatson struct acl *aclp) 58954803Srwatson{ 59054803Srwatson struct acl inkernelacl; 59154803Srwatson int error; 59254803Srwatson 59383366Sjulian VOP_LEASE(vp, td, td->td_proc->p_ucred, LEASE_WRITE); 59483366Sjulian vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); 59583366Sjulian error = VOP_GETACL(vp, type, &inkernelacl, td->td_proc->p_ucred, td); 59683366Sjulian VOP_UNLOCK(vp, 0, td); 59754803Srwatson if (error == 0) 59854803Srwatson error = copyout(&inkernelacl, aclp, sizeof(struct acl)); 59954803Srwatson return (error); 60054803Srwatson} 60154803Srwatson 60254803Srwatson/* 60354803Srwatson * Given a vnode, delete its ACL. 60454803Srwatson */ 60554803Srwatsonstatic int 60685582Srwatsonvacl_delete(struct thread *td, struct vnode *vp, acl_type_t type) 60754803Srwatson{ 60854803Srwatson int error; 60954803Srwatson 61083366Sjulian VOP_LEASE(vp, td, td->td_proc->p_ucred, LEASE_WRITE); 61183366Sjulian vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); 61285582Srwatson error = VOP_SETACL(vp, ACL_TYPE_DEFAULT, 0, td->td_proc->p_ucred, 61385582Srwatson td); 61483366Sjulian VOP_UNLOCK(vp, 0, td); 61554803Srwatson return (error); 61654803Srwatson} 61754803Srwatson 61854803Srwatson/* 61954803Srwatson * Given a vnode, check whether an ACL is appropriate for it 62054803Srwatson */ 62154803Srwatsonstatic int 62285582Srwatsonvacl_aclcheck(struct thread *td, struct vnode *vp, acl_type_t type, 62354803Srwatson struct acl *aclp) 62454803Srwatson{ 62554803Srwatson struct acl inkernelacl; 62654803Srwatson int error; 62754803Srwatson 62854803Srwatson error = copyin(aclp, &inkernelacl, sizeof(struct acl)); 62954803Srwatson if (error) 63054803Srwatson return(error); 63185582Srwatson error = VOP_ACLCHECK(vp, type, &inkernelacl, td->td_proc->p_ucred, 63285582Srwatson td); 63354803Srwatson return (error); 63454803Srwatson} 63554803Srwatson 63654803Srwatson/* 63754803Srwatson * syscalls -- convert the path/fd to a vnode, and call vacl_whatever. 63854803Srwatson * Don't need to lock, as the vacl_ code will get/release any locks 63954803Srwatson * required. 64054803Srwatson */ 64154803Srwatson 64254803Srwatson/* 64354803Srwatson * Given a file path, get an ACL for it 64482713Sdillon * 64582713Sdillon * MPSAFE 64654803Srwatson */ 64754803Srwatsonint 64885582Srwatson__acl_get_file(struct thread *td, struct __acl_get_file_args *uap) 64954803Srwatson{ 65054803Srwatson struct nameidata nd; 65154803Srwatson int error; 65254803Srwatson 65382713Sdillon mtx_lock(&Giant); 65483366Sjulian NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), td); 65554803Srwatson error = namei(&nd); 65682713Sdillon if (error == 0) { 65783366Sjulian error = vacl_get_acl(td, nd.ni_vp, SCARG(uap, type), 65882713Sdillon SCARG(uap, aclp)); 65982713Sdillon NDFREE(&nd, 0); 66082713Sdillon } 66182713Sdillon mtx_unlock(&Giant); 66254803Srwatson return (error); 66354803Srwatson} 66454803Srwatson 66554803Srwatson/* 66654803Srwatson * Given a file path, set an ACL for it 66782713Sdillon * 66882713Sdillon * MPSAFE 66954803Srwatson */ 67054803Srwatsonint 67185582Srwatson__acl_set_file(struct thread *td, struct __acl_set_file_args *uap) 67254803Srwatson{ 67354803Srwatson struct nameidata nd; 67454803Srwatson int error; 67554803Srwatson 67682713Sdillon mtx_lock(&Giant); 67783366Sjulian NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), td); 67854803Srwatson error = namei(&nd); 67982713Sdillon if (error == 0) { 68083366Sjulian error = vacl_set_acl(td, nd.ni_vp, SCARG(uap, type), 68182713Sdillon SCARG(uap, aclp)); 68282713Sdillon NDFREE(&nd, 0); 68382713Sdillon } 68482713Sdillon mtx_unlock(&Giant); 68554803Srwatson return (error); 68654803Srwatson} 68754803Srwatson 68854803Srwatson/* 68954803Srwatson * Given a file descriptor, get an ACL for it 69082713Sdillon * 69182713Sdillon * MPSAFE 69254803Srwatson */ 69354803Srwatsonint 69485582Srwatson__acl_get_fd(struct thread *td, struct __acl_get_fd_args *uap) 69554803Srwatson{ 69654803Srwatson struct file *fp; 69754803Srwatson int error; 69854803Srwatson 69982713Sdillon mtx_lock(&Giant); 70083366Sjulian error = getvnode(td->td_proc->p_fd, SCARG(uap, filedes), &fp); 70182713Sdillon if (error == 0) { 70283366Sjulian error = vacl_get_acl(td, (struct vnode *)fp->f_data, 70382713Sdillon SCARG(uap, type), SCARG(uap, aclp)); 70482713Sdillon } 70582713Sdillon mtx_unlock(&Giant); 70682713Sdillon return (error); 70754803Srwatson} 70854803Srwatson 70954803Srwatson/* 71054803Srwatson * Given a file descriptor, set an ACL for it 71182713Sdillon * 71282713Sdillon * MPSAFE 71354803Srwatson */ 71454803Srwatsonint 71585582Srwatson__acl_set_fd(struct thread *td, struct __acl_set_fd_args *uap) 71654803Srwatson{ 71754803Srwatson struct file *fp; 71854803Srwatson int error; 71954803Srwatson 72082713Sdillon mtx_lock(&Giant); 72183366Sjulian error = getvnode(td->td_proc->p_fd, SCARG(uap, filedes), &fp); 72282713Sdillon if (error == 0) { 72383366Sjulian error = vacl_set_acl(td, (struct vnode *)fp->f_data, 72482713Sdillon SCARG(uap, type), SCARG(uap, aclp)); 72582713Sdillon } 72682713Sdillon mtx_unlock(&Giant); 72782713Sdillon return (error); 72854803Srwatson} 72954803Srwatson 73054803Srwatson/* 73154803Srwatson * Given a file path, delete an ACL from it. 73282713Sdillon * 73382713Sdillon * MPSAFE 73454803Srwatson */ 73554803Srwatsonint 73685582Srwatson__acl_delete_file(struct thread *td, struct __acl_delete_file_args *uap) 73754803Srwatson{ 73854803Srwatson struct nameidata nd; 73954803Srwatson int error; 74054803Srwatson 74182713Sdillon mtx_lock(&Giant); 74283366Sjulian NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), td); 74354803Srwatson error = namei(&nd); 74482713Sdillon if (error == 0) { 74583366Sjulian error = vacl_delete(td, nd.ni_vp, SCARG(uap, type)); 74682713Sdillon NDFREE(&nd, 0); 74782713Sdillon } 74882713Sdillon mtx_unlock(&Giant); 74954803Srwatson return (error); 75054803Srwatson} 75154803Srwatson 75254803Srwatson/* 75354803Srwatson * Given a file path, delete an ACL from it. 75482713Sdillon * 75582713Sdillon * MPSAFE 75654803Srwatson */ 75754803Srwatsonint 75885582Srwatson__acl_delete_fd(struct thread *td, struct __acl_delete_fd_args *uap) 75954803Srwatson{ 76054803Srwatson struct file *fp; 76154803Srwatson int error; 76254803Srwatson 76382713Sdillon mtx_lock(&Giant); 76483366Sjulian error = getvnode(td->td_proc->p_fd, SCARG(uap, filedes), &fp); 76582713Sdillon if (error == 0) { 76683366Sjulian error = vacl_delete(td, (struct vnode *)fp->f_data, 76782713Sdillon SCARG(uap, type)); 76882713Sdillon } 76982713Sdillon mtx_unlock(&Giant); 77054803Srwatson return (error); 77154803Srwatson} 77254803Srwatson 77354803Srwatson/* 77454803Srwatson * Given a file path, check an ACL for it 77582713Sdillon * 77682713Sdillon * MPSAFE 77754803Srwatson */ 77854803Srwatsonint 77985582Srwatson__acl_aclcheck_file(struct thread *td, struct __acl_aclcheck_file_args *uap) 78054803Srwatson{ 78154803Srwatson struct nameidata nd; 78254803Srwatson int error; 78354803Srwatson 78482713Sdillon mtx_lock(&Giant); 78583366Sjulian NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), td); 78654803Srwatson error = namei(&nd); 78782713Sdillon if (error == 0) { 78883366Sjulian error = vacl_aclcheck(td, nd.ni_vp, SCARG(uap, type), 78982713Sdillon SCARG(uap, aclp)); 79082713Sdillon NDFREE(&nd, 0); 79182713Sdillon } 79282713Sdillon mtx_unlock(&Giant); 79354803Srwatson return (error); 79454803Srwatson} 79554803Srwatson 79654803Srwatson/* 79754803Srwatson * Given a file descriptor, check an ACL for it 79882713Sdillon * 79982713Sdillon * MPSAFE 80054803Srwatson */ 80154803Srwatsonint 80285582Srwatson__acl_aclcheck_fd(struct thread *td, struct __acl_aclcheck_fd_args *uap) 80354803Srwatson{ 80454803Srwatson struct file *fp; 80554803Srwatson int error; 80654803Srwatson 80782713Sdillon mtx_lock(&Giant); 80883366Sjulian error = getvnode(td->td_proc->p_fd, SCARG(uap, filedes), &fp); 80982713Sdillon if (error == 0) { 81083366Sjulian error = vacl_aclcheck(td, (struct vnode *)fp->f_data, 81182713Sdillon SCARG(uap, type), SCARG(uap, aclp)); 81282713Sdillon } 81382713Sdillon mtx_unlock(&Giant); 81482713Sdillon return (error); 81554803Srwatson} 816