subr_acl_posix1e.c revision 85582
154803Srwatson/*- 273890Srwatson * Copyright (c) 1999, 2000, 2001 Robert N. M. Watson 354803Srwatson * All rights reserved. 454803Srwatson * 554803Srwatson * Redistribution and use in source and binary forms, with or without 654803Srwatson * modification, are permitted provided that the following conditions 754803Srwatson * are met: 854803Srwatson * 1. Redistributions of source code must retain the above copyright 954803Srwatson * notice, this list of conditions and the following disclaimer. 1054803Srwatson * 2. Redistributions in binary form must reproduce the above copyright 1154803Srwatson * notice, this list of conditions and the following disclaimer in the 1254803Srwatson * documentation and/or other materials provided with the distribution. 1354803Srwatson * 1454803Srwatson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1554803Srwatson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1654803Srwatson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1754803Srwatson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1854803Srwatson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1954803Srwatson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2054803Srwatson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2154803Srwatson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2254803Srwatson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2354803Srwatson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2454803Srwatson * SUCH DAMAGE. 2554803Srwatson * 2654803Srwatson * $FreeBSD: head/sys/kern/subr_acl_posix1e.c 85582 2001-10-27 05:45:42Z rwatson $ 2754803Srwatson */ 2854803Srwatson/* 2973890Srwatson * Developed by the TrustedBSD Project. 3073890Srwatson * Support for POSIX.1e access control lists. 3154803Srwatson */ 3254803Srwatson 3354803Srwatson#include <sys/param.h> 3454803Srwatson#include <sys/systm.h> 3554803Srwatson#include <sys/sysproto.h> 3654803Srwatson#include <sys/kernel.h> 3754803Srwatson#include <sys/malloc.h> 3854803Srwatson#include <sys/vnode.h> 3954803Srwatson#include <sys/lock.h> 4082713Sdillon#include <sys/mutex.h> 4154803Srwatson#include <sys/namei.h> 4254803Srwatson#include <sys/file.h> 4354803Srwatson#include <sys/proc.h> 4454803Srwatson#include <sys/sysent.h> 4554803Srwatson#include <sys/errno.h> 4654803Srwatson#include <sys/stat.h> 4754803Srwatson#include <sys/acl.h> 4854803Srwatson 4973890SrwatsonMALLOC_DEFINE(M_ACL, "acl", "access control list"); 5054803Srwatson 5185582Srwatsonstatic int vacl_set_acl(struct thread *td, struct vnode *vp, acl_type_t type, 5256272Srwatson struct acl *aclp); 5385582Srwatsonstatic int vacl_get_acl(struct thread *td, struct vnode *vp, acl_type_t type, 5456272Srwatson struct acl *aclp); 5585582Srwatsonstatic int vacl_aclcheck(struct thread *td, struct vnode *vp, 5675571Srwatson acl_type_t type, struct acl *aclp); 5754803Srwatson 5854803Srwatson/* 5973890Srwatson * Implement a version of vaccess() that understands POSIX.1e ACL semantics. 6073890Srwatson * Return 0 on success, else an errno value. Should be merged into 6173890Srwatson * vaccess() eventually. 6273890Srwatson */ 6373890Srwatsonint 6475571Srwatsonvaccess_acl_posix1e(enum vtype type, uid_t file_uid, gid_t file_gid, 6575571Srwatson struct acl *acl, mode_t acc_mode, struct ucred *cred, int *privused) 6673890Srwatson{ 6773890Srwatson struct acl_entry *acl_other, *acl_mask; 6873890Srwatson mode_t dac_granted; 6973890Srwatson mode_t cap_granted; 7073890Srwatson mode_t acl_mask_granted; 7173890Srwatson int group_matched, i; 7273890Srwatson 7373890Srwatson /* 7473890Srwatson * Look for a normal, non-privileged way to access the file/directory 7573890Srwatson * as requested. If it exists, go with that. Otherwise, attempt 7673890Srwatson * to use privileges granted via cap_granted. In some cases, 7773890Srwatson * which privileges to use may be ambiguous due to "best match", 7873890Srwatson * in which case fall back on first match for the time being. 7973890Srwatson */ 8073890Srwatson if (privused != NULL) 8173890Srwatson *privused = 0; 8273890Srwatson 8373890Srwatson /* 8473890Srwatson * Determine privileges now, but don't apply until we've found 8582255Srwatson * a DAC entry that matches but has failed to allow access. 8673890Srwatson */ 8773890Srwatson#ifndef CAPABILITIES 8873890Srwatson if (suser_xxx(cred, NULL, PRISON_ROOT) == 0) 8973890Srwatson cap_granted = (VEXEC | VREAD | VWRITE | VADMIN); 9073890Srwatson else 9173890Srwatson cap_granted = 0; 9273890Srwatson#else 9373890Srwatson cap_granted = 0; 9473890Srwatson 9573890Srwatson if (type == VDIR) { 9673890Srwatson if ((acc_mode & VEXEC) && !cap_check(cred, NULL, 9773890Srwatson CAP_DAC_READ_SEARCH, PRISON_ROOT)) 9873890Srwatson cap_granted |= VEXEC; 9973890Srwatson } else { 10073890Srwatson if ((acc_mode & VEXEC) && !cap_check(cred, NULL, 10173890Srwatson CAP_DAC_EXECUTE, PRISON_ROOT)) 10273890Srwatson cap_granted |= VEXEC; 10373890Srwatson } 10473890Srwatson 10573890Srwatson if ((acc_mode & VREAD) && !cap_check(cred, NULL, CAP_DAC_READ_SEARCH, 10673890Srwatson PRISON_ROOT)) 10773890Srwatson cap_granted |= VREAD; 10873890Srwatson 10973890Srwatson if ((acc_mode & VWRITE) && !cap_check(cred, NULL, CAP_DAC_WRITE, 11073890Srwatson PRISON_ROOT)) 11173890Srwatson cap_granted |= VWRITE; 11273890Srwatson 11373890Srwatson if ((acc_mode & VADMIN) && !cap_check(cred, NULL, CAP_FOWNER, 11473890Srwatson PRISON_ROOT)) 11573890Srwatson cap_granted |= VADMIN; 11673890Srwatson#endif /* CAPABILITIES */ 11773890Srwatson 11873890Srwatson /* 11982255Srwatson * The owner matches if the effective uid associated with the 12082255Srwatson * credential matches that of the ACL_USER_OBJ entry. While we're 12182255Srwatson * doing the first scan, also cache the location of the ACL_MASK 12282255Srwatson * and ACL_OTHER entries, preventing some future iterations. 12373890Srwatson */ 12473890Srwatson acl_mask = acl_other = NULL; 12573890Srwatson for (i = 0; i < acl->acl_cnt; i++) { 12673890Srwatson switch (acl->acl_entry[i].ae_tag) { 12773890Srwatson case ACL_USER_OBJ: 12875571Srwatson if (file_uid != cred->cr_uid) 12973890Srwatson break; 13073890Srwatson dac_granted = 0; 13173890Srwatson dac_granted |= VADMIN; 13275404Sjedgar if (acl->acl_entry[i].ae_perm & ACL_EXECUTE) 13373890Srwatson dac_granted |= VEXEC; 13475404Sjedgar if (acl->acl_entry[i].ae_perm & ACL_READ) 13573890Srwatson dac_granted |= VREAD; 13675404Sjedgar if (acl->acl_entry[i].ae_perm & ACL_WRITE) 13773890Srwatson dac_granted |= VWRITE; 13873890Srwatson if ((acc_mode & dac_granted) == acc_mode) 13973890Srwatson return (0); 14073890Srwatson if ((acc_mode & (dac_granted | cap_granted)) == 14173890Srwatson acc_mode) { 14273890Srwatson if (privused != NULL) 14373890Srwatson *privused = 1; 14473890Srwatson return (0); 14573890Srwatson } 14673890Srwatson goto error; 14773890Srwatson 14873890Srwatson case ACL_MASK: 14973890Srwatson acl_mask = &acl->acl_entry[i]; 15073890Srwatson break; 15173890Srwatson 15273890Srwatson case ACL_OTHER: 15373890Srwatson acl_other = &acl->acl_entry[i]; 15473890Srwatson break; 15573890Srwatson 15673890Srwatson default: 15773890Srwatson } 15873890Srwatson } 15973890Srwatson 16073890Srwatson /* 16182255Srwatson * An ACL_OTHER entry should always exist in a valid access 16282255Srwatson * ACL. If it doesn't, then generate a serious failure. For now, 16382255Srwatson * this means a debugging message and EPERM, but in the future 16482255Srwatson * should probably be a panic. 16573890Srwatson */ 16673890Srwatson if (acl_other == NULL) { 16773890Srwatson /* 16882255Srwatson * XXX This should never happen 16973890Srwatson */ 17073890Srwatson printf("vaccess_acl_posix1e: ACL_OTHER missing\n"); 17173890Srwatson return (EPERM); 17273890Srwatson } 17382255Srwatson 17482255Srwatson /* 17582255Srwatson * Checks against ACL_USER, ACL_GROUP_OBJ, and ACL_GROUP fields 17682255Srwatson * are masked by an ACL_MASK entry, if any. As such, first identify 17782255Srwatson * the ACL_MASK field, then iterate through identifying potential 17882255Srwatson * user matches, then group matches. If there is no ACL_MASK, 17982255Srwatson * assume that the mask allows all requests to succeed. 18082255Srwatson */ 18173890Srwatson if (acl_mask != NULL) { 18273890Srwatson acl_mask_granted = 0; 18375404Sjedgar if (acl_mask->ae_perm & ACL_EXECUTE) 18473890Srwatson acl_mask_granted |= VEXEC; 18575404Sjedgar if (acl_mask->ae_perm & ACL_READ) 18673890Srwatson acl_mask_granted |= VREAD; 18775404Sjedgar if (acl_mask->ae_perm & ACL_WRITE) 18873890Srwatson acl_mask_granted |= VWRITE; 18973890Srwatson } else 19073890Srwatson acl_mask_granted = VEXEC | VREAD | VWRITE; 19173890Srwatson 19273890Srwatson /* 19382255Srwatson * Iterate through user ACL entries. Do checks twice, first 19482255Srwatson * without privilege, and then if a match is found but failed, 19582255Srwatson * a second time with privilege. 19673890Srwatson */ 19773890Srwatson 19873890Srwatson /* 19973890Srwatson * Check ACL_USER ACL entries. 20073890Srwatson */ 20173890Srwatson for (i = 0; i < acl->acl_cnt; i++) { 20273890Srwatson switch (acl->acl_entry[i].ae_tag) { 20373890Srwatson case ACL_USER: 20473890Srwatson if (acl->acl_entry[i].ae_id != cred->cr_uid) 20573890Srwatson break; 20673890Srwatson dac_granted = 0; 20775404Sjedgar if (acl->acl_entry[i].ae_perm & ACL_EXECUTE) 20873890Srwatson dac_granted |= VEXEC; 20975404Sjedgar if (acl->acl_entry[i].ae_perm & ACL_READ) 21073890Srwatson dac_granted |= VREAD; 21175404Sjedgar if (acl->acl_entry[i].ae_perm & ACL_WRITE) 21273890Srwatson dac_granted |= VWRITE; 21373890Srwatson dac_granted &= acl_mask_granted; 21473890Srwatson if ((acc_mode & dac_granted) == acc_mode) 21573890Srwatson return (0); 21675571Srwatson if ((acc_mode & (dac_granted | cap_granted)) != 21775571Srwatson acc_mode) 21875571Srwatson goto error; 21975571Srwatson 22075571Srwatson if (privused != NULL) 22175571Srwatson *privused = 1; 22275571Srwatson return (0); 22373890Srwatson } 22473890Srwatson } 22573890Srwatson 22673890Srwatson /* 22773890Srwatson * Group match is best-match, not first-match, so find a 22873890Srwatson * "best" match. Iterate across, testing each potential group 22973890Srwatson * match. Make sure we keep track of whether we found a match 23082255Srwatson * or not, so that we know if we should try again with any 23182255Srwatson * available privilege, or if we should move on to ACL_OTHER. 23273890Srwatson */ 23373890Srwatson group_matched = 0; 23473890Srwatson for (i = 0; i < acl->acl_cnt; i++) { 23573890Srwatson switch (acl->acl_entry[i].ae_tag) { 23673890Srwatson case ACL_GROUP_OBJ: 23775888Stmm if (!groupmember(file_gid, cred)) 23875571Srwatson break; 23975571Srwatson dac_granted = 0; 24075571Srwatson if (acl->acl_entry[i].ae_perm & ACL_EXECUTE) 24175571Srwatson dac_granted |= VEXEC; 24275571Srwatson if (acl->acl_entry[i].ae_perm & ACL_READ) 24375571Srwatson dac_granted |= VREAD; 24475571Srwatson if (acl->acl_entry[i].ae_perm & ACL_WRITE) 24575571Srwatson dac_granted |= VWRITE; 24675571Srwatson dac_granted &= acl_mask_granted; 24775571Srwatson 24875571Srwatson if ((acc_mode & dac_granted) == acc_mode) 24975571Srwatson return (0); 25075571Srwatson 25175571Srwatson group_matched = 1; 25275571Srwatson break; 25375571Srwatson 25473890Srwatson case ACL_GROUP: 25575571Srwatson if (!groupmember(acl->acl_entry[i].ae_id, cred)) 25675571Srwatson break; 25775571Srwatson dac_granted = 0; 25875571Srwatson if (acl->acl_entry[i].ae_perm & ACL_EXECUTE) 25975571Srwatson dac_granted |= VEXEC; 26075571Srwatson if (acl->acl_entry[i].ae_perm & ACL_READ) 26175571Srwatson dac_granted |= VREAD; 26275571Srwatson if (acl->acl_entry[i].ae_perm & ACL_WRITE) 26375571Srwatson dac_granted |= VWRITE; 26475571Srwatson dac_granted &= acl_mask_granted; 26573890Srwatson 26675571Srwatson if ((acc_mode & dac_granted) == acc_mode) 26775571Srwatson return (0); 26873890Srwatson 26975571Srwatson group_matched = 1; 27075571Srwatson break; 27175571Srwatson 27273890Srwatson default: 27373890Srwatson } 27473890Srwatson } 27573890Srwatson 27673890Srwatson if (group_matched == 1) { 27773890Srwatson /* 27873890Srwatson * There was a match, but it did not grant rights via 27973890Srwatson * pure DAC. Try again, this time with privilege. 28073890Srwatson */ 28173890Srwatson for (i = 0; i < acl->acl_cnt; i++) { 28273890Srwatson switch (acl->acl_entry[i].ae_tag) { 28373890Srwatson case ACL_GROUP_OBJ: 28476139Srwatson if (!groupmember(file_gid, cred)) 28575571Srwatson break; 28675571Srwatson dac_granted = 0; 28775571Srwatson if (acl->acl_entry[i].ae_perm & ACL_EXECUTE) 28875571Srwatson dac_granted |= VEXEC; 28975571Srwatson if (acl->acl_entry[i].ae_perm & ACL_READ) 29075571Srwatson dac_granted |= VREAD; 29175571Srwatson if (acl->acl_entry[i].ae_perm & ACL_WRITE) 29275571Srwatson dac_granted |= VWRITE; 29375571Srwatson dac_granted &= acl_mask_granted; 29475571Srwatson 29575571Srwatson if ((acc_mode & (dac_granted | cap_granted)) != 29675571Srwatson acc_mode) 29775571Srwatson break; 29875571Srwatson 29975571Srwatson if (privused != NULL) 30075571Srwatson *privused = 1; 30175571Srwatson return (0); 30275571Srwatson 30373890Srwatson case ACL_GROUP: 30475571Srwatson if (!groupmember(acl->acl_entry[i].ae_id, 30575571Srwatson cred)) 30675571Srwatson break; 30775571Srwatson dac_granted = 0; 30875571Srwatson if (acl->acl_entry[i].ae_perm & ACL_EXECUTE) 30975571Srwatson dac_granted |= VEXEC; 31075571Srwatson if (acl->acl_entry[i].ae_perm & ACL_READ) 31175571Srwatson dac_granted |= VREAD; 31275571Srwatson if (acl->acl_entry[i].ae_perm & ACL_WRITE) 31375571Srwatson dac_granted |= VWRITE; 31475571Srwatson dac_granted &= acl_mask_granted; 31575571Srwatson 31675571Srwatson if ((acc_mode & (dac_granted | cap_granted)) != 31775571Srwatson acc_mode) 31875571Srwatson break; 31975571Srwatson 32075571Srwatson if (privused != NULL) 32175571Srwatson *privused = 1; 32275571Srwatson return (0); 32375571Srwatson 32473890Srwatson default: 32573890Srwatson } 32673890Srwatson } 32773890Srwatson /* 32873890Srwatson * Even with privilege, group membership was not sufficient. 32973890Srwatson * Return failure. 33073890Srwatson */ 33173890Srwatson goto error; 33273890Srwatson } 33373890Srwatson 33473890Srwatson /* 33573890Srwatson * Fall back on ACL_OTHER. ACL_MASK is not applied to ACL_OTHER. 33673890Srwatson */ 33773890Srwatson dac_granted = 0; 33875404Sjedgar if (acl_other->ae_perm & ACL_EXECUTE) 33973890Srwatson dac_granted |= VEXEC; 34075404Sjedgar if (acl_other->ae_perm & ACL_READ) 34173890Srwatson dac_granted |= VREAD; 34275404Sjedgar if (acl_other->ae_perm & ACL_WRITE) 34373890Srwatson dac_granted |= VWRITE; 34473890Srwatson 34573890Srwatson if ((acc_mode & dac_granted) == acc_mode) 34673890Srwatson return (0); 34773890Srwatson if ((acc_mode & (dac_granted | cap_granted)) == acc_mode) { 34873890Srwatson if (privused != NULL) 34973890Srwatson *privused = 1; 35073890Srwatson return (0); 35173890Srwatson } 35273890Srwatson 35373890Srwatsonerror: 35473890Srwatson return ((acc_mode & VADMIN) ? EPERM : EACCES); 35573890Srwatson} 35673890Srwatson 35773890Srwatson/* 35873890Srwatson * For the purposes of file systems maintaining the _OBJ entries in an 35973890Srwatson * inode with a mode_t field, this routine converts a mode_t entry 36073890Srwatson * to an acl_perm_t. 36173890Srwatson */ 36273890Srwatsonacl_perm_t 36373890Srwatsonacl_posix1e_mode_to_perm(acl_tag_t tag, mode_t mode) 36473890Srwatson{ 36573890Srwatson acl_perm_t perm = 0; 36673890Srwatson 36773890Srwatson switch(tag) { 36873890Srwatson case ACL_USER_OBJ: 36973890Srwatson if (mode & S_IXUSR) 37075404Sjedgar perm |= ACL_EXECUTE; 37173890Srwatson if (mode & S_IRUSR) 37275404Sjedgar perm |= ACL_READ; 37373890Srwatson if (mode & S_IWUSR) 37475404Sjedgar perm |= ACL_WRITE; 37573890Srwatson return (perm); 37673890Srwatson 37773890Srwatson case ACL_GROUP_OBJ: 37873890Srwatson if (mode & S_IXGRP) 37975404Sjedgar perm |= ACL_EXECUTE; 38073890Srwatson if (mode & S_IRGRP) 38175404Sjedgar perm |= ACL_READ; 38273890Srwatson if (mode & S_IWGRP) 38375404Sjedgar perm |= ACL_WRITE; 38473890Srwatson return (perm); 38573890Srwatson 38673890Srwatson case ACL_OTHER: 38773890Srwatson if (mode & S_IXOTH) 38875404Sjedgar perm |= ACL_EXECUTE; 38973890Srwatson if (mode & S_IROTH) 39075404Sjedgar perm |= ACL_READ; 39173890Srwatson if (mode & S_IWOTH) 39275404Sjedgar perm |= ACL_WRITE; 39373890Srwatson return (perm); 39473890Srwatson 39573890Srwatson default: 39673890Srwatson printf("acl_posix1e_mode_to_perm: invalid tag (%d)\n", tag); 39773890Srwatson return (0); 39873890Srwatson } 39973890Srwatson} 40073890Srwatson 40173890Srwatson/* 40273890Srwatson * Given inode information (uid, gid, mode), return an acl entry of the 40373890Srwatson * appropriate type. 40473890Srwatson */ 40573890Srwatsonstruct acl_entry 40673890Srwatsonacl_posix1e_mode_to_entry(acl_tag_t tag, uid_t uid, gid_t gid, mode_t mode) 40773890Srwatson{ 40873890Srwatson struct acl_entry acl_entry; 40973890Srwatson 41073890Srwatson acl_entry.ae_tag = tag; 41173890Srwatson acl_entry.ae_perm = acl_posix1e_mode_to_perm(tag, mode); 41273890Srwatson switch(tag) { 41373890Srwatson case ACL_USER_OBJ: 41473890Srwatson acl_entry.ae_id = uid; 41573890Srwatson break; 41673890Srwatson 41773890Srwatson case ACL_GROUP_OBJ: 41873890Srwatson acl_entry.ae_id = gid; 41973890Srwatson break; 42073890Srwatson 42173890Srwatson case ACL_OTHER: 42282769Sjedgar acl_entry.ae_id = ACL_UNDEFINED_ID; 42373890Srwatson break; 42473890Srwatson 42573890Srwatson default: 42682769Sjedgar acl_entry.ae_id = ACL_UNDEFINED_ID; 42773890Srwatson printf("acl_posix1e_mode_to_entry: invalid tag (%d)\n", tag); 42873890Srwatson } 42973890Srwatson 43073890Srwatson return (acl_entry); 43173890Srwatson} 43273890Srwatson 43373890Srwatson/* 43473890Srwatson * Utility function to generate a file mode given appropriate ACL entries. 43573890Srwatson */ 43673890Srwatsonmode_t 43773890Srwatsonacl_posix1e_perms_to_mode(struct acl_entry *acl_user_obj_entry, 43873890Srwatson struct acl_entry *acl_group_obj_entry, struct acl_entry *acl_other_entry) 43973890Srwatson{ 44073890Srwatson mode_t mode; 44173890Srwatson 44273890Srwatson mode = 0; 44375404Sjedgar if (acl_user_obj_entry->ae_perm & ACL_EXECUTE) 44473890Srwatson mode |= S_IXUSR; 44575404Sjedgar if (acl_user_obj_entry->ae_perm & ACL_READ) 44673890Srwatson mode |= S_IRUSR; 44775404Sjedgar if (acl_user_obj_entry->ae_perm & ACL_WRITE) 44873890Srwatson mode |= S_IWUSR; 44975404Sjedgar if (acl_group_obj_entry->ae_perm & ACL_EXECUTE) 45073890Srwatson mode |= S_IXGRP; 45175404Sjedgar if (acl_group_obj_entry->ae_perm & ACL_READ) 45273890Srwatson mode |= S_IRGRP; 45375404Sjedgar if (acl_group_obj_entry->ae_perm & ACL_WRITE) 45473890Srwatson mode |= S_IWGRP; 45575404Sjedgar if (acl_other_entry->ae_perm & ACL_EXECUTE) 45673890Srwatson mode |= S_IXOTH; 45775404Sjedgar if (acl_other_entry->ae_perm & ACL_READ) 45873890Srwatson mode |= S_IROTH; 45975404Sjedgar if (acl_other_entry->ae_perm & ACL_WRITE) 46073890Srwatson mode |= S_IWOTH; 46173890Srwatson 46273890Srwatson return (mode); 46373890Srwatson} 46473890Srwatson 46573890Srwatson/* 46673890Srwatson * Perform a syntactic check of the ACL, sufficient to allow an 46773890Srwatson * implementing file system to determine if it should accept this and 46873890Srwatson * rely on the POSIX.1e ACL properties. 46973890Srwatson */ 47073890Srwatsonint 47173890Srwatsonacl_posix1e_check(struct acl *acl) 47273890Srwatson{ 47373890Srwatson int num_acl_user_obj, num_acl_user, num_acl_group_obj, num_acl_group; 47473890Srwatson int num_acl_mask, num_acl_other, i; 47573890Srwatson 47673890Srwatson /* 47773890Srwatson * Verify that the number of entries does not exceed the maximum 47873890Srwatson * defined for acl_t. 47973890Srwatson * Verify that the correct number of various sorts of ae_tags are 48073890Srwatson * present: 48173890Srwatson * Exactly one ACL_USER_OBJ 48273890Srwatson * Exactly one ACL_GROUP_OBJ 48373890Srwatson * Exactly one ACL_OTHER 48473890Srwatson * If any ACL_USER or ACL_GROUP entries appear, then exactly one 48573890Srwatson * ACL_MASK entry must also appear. 48673890Srwatson * Verify that all ae_perm entries are in ACL_PERM_BITS. 48773890Srwatson * Verify all ae_tag entries are understood by this implementation. 48873890Srwatson * Note: Does not check for uniqueness of qualifier (ae_id) field. 48973890Srwatson */ 49073890Srwatson num_acl_user_obj = num_acl_user = num_acl_group_obj = num_acl_group = 49173890Srwatson num_acl_mask = num_acl_other = 0; 49273890Srwatson if (acl->acl_cnt > ACL_MAX_ENTRIES || acl->acl_cnt < 0) 49373890Srwatson return (EINVAL); 49473890Srwatson for (i = 0; i < acl->acl_cnt; i++) { 49573890Srwatson /* 49673890Srwatson * Check for a valid tag. 49773890Srwatson */ 49873890Srwatson switch(acl->acl_entry[i].ae_tag) { 49973890Srwatson case ACL_USER_OBJ: 50075571Srwatson acl->acl_entry[i].ae_id = ACL_UNDEFINED_ID; /* XXX */ 50175571Srwatson if (acl->acl_entry[i].ae_id != ACL_UNDEFINED_ID) 50275571Srwatson return (EINVAL); 50373890Srwatson num_acl_user_obj++; 50473890Srwatson break; 50573890Srwatson case ACL_GROUP_OBJ: 50675571Srwatson acl->acl_entry[i].ae_id = ACL_UNDEFINED_ID; /* XXX */ 50775571Srwatson if (acl->acl_entry[i].ae_id != ACL_UNDEFINED_ID) 50875571Srwatson return (EINVAL); 50973890Srwatson num_acl_group_obj++; 51073890Srwatson break; 51173890Srwatson case ACL_USER: 51275571Srwatson if (acl->acl_entry[i].ae_id == ACL_UNDEFINED_ID) 51375571Srwatson return (EINVAL); 51473890Srwatson num_acl_user++; 51573890Srwatson break; 51673890Srwatson case ACL_GROUP: 51775571Srwatson if (acl->acl_entry[i].ae_id == ACL_UNDEFINED_ID) 51875571Srwatson return (EINVAL); 51973890Srwatson num_acl_group++; 52073890Srwatson break; 52173890Srwatson case ACL_OTHER: 52275571Srwatson acl->acl_entry[i].ae_id = ACL_UNDEFINED_ID; /* XXX */ 52375571Srwatson if (acl->acl_entry[i].ae_id != ACL_UNDEFINED_ID) 52475571Srwatson return (EINVAL); 52573890Srwatson num_acl_other++; 52673890Srwatson break; 52773890Srwatson case ACL_MASK: 52875571Srwatson acl->acl_entry[i].ae_id = ACL_UNDEFINED_ID; /* XXX */ 52975571Srwatson if (acl->acl_entry[i].ae_id != ACL_UNDEFINED_ID) 53075571Srwatson return (EINVAL); 53173890Srwatson num_acl_mask++; 53273890Srwatson break; 53373890Srwatson default: 53473890Srwatson return (EINVAL); 53573890Srwatson } 53673890Srwatson /* 53773890Srwatson * Check for valid perm entries. 53873890Srwatson */ 53973890Srwatson if ((acl->acl_entry[i].ae_perm | ACL_PERM_BITS) != 54073890Srwatson ACL_PERM_BITS) 54173890Srwatson return (EINVAL); 54273890Srwatson } 54373890Srwatson if ((num_acl_user_obj != 1) || (num_acl_group_obj != 1) || 54473890Srwatson (num_acl_other != 1) || (num_acl_mask != 0 && num_acl_mask != 1)) 54573890Srwatson return (EINVAL); 54673890Srwatson if (((num_acl_group != 0) || (num_acl_user != 0)) && 54773890Srwatson (num_acl_mask != 1)) 54873890Srwatson return (EINVAL); 54973890Srwatson return (0); 55073890Srwatson} 55173890Srwatson 55273890Srwatson/* 55354803Srwatson * These calls wrap the real vnode operations, and are called by the 55454803Srwatson * syscall code once the syscall has converted the path or file 55554803Srwatson * descriptor to a vnode (unlocked). The aclp pointer is assumed 55654803Srwatson * still to point to userland, so this should not be consumed within 55754803Srwatson * the kernel except by syscall code. Other code should directly 55854803Srwatson * invoke VOP_{SET,GET}ACL. 55954803Srwatson */ 56054803Srwatson 56154803Srwatson/* 56254803Srwatson * Given a vnode, set its ACL. 56354803Srwatson */ 56454803Srwatsonstatic int 56585582Srwatsonvacl_set_acl(struct thread *td, struct vnode *vp, acl_type_t type, 56656272Srwatson struct acl *aclp) 56754803Srwatson{ 56854803Srwatson struct acl inkernacl; 56954803Srwatson int error; 57054803Srwatson 57154803Srwatson error = copyin(aclp, &inkernacl, sizeof(struct acl)); 57254803Srwatson if (error) 57354803Srwatson return(error); 57483366Sjulian VOP_LEASE(vp, td, td->td_proc->p_ucred, LEASE_WRITE); 57583366Sjulian vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); 57683366Sjulian error = VOP_SETACL(vp, type, &inkernacl, td->td_proc->p_ucred, td); 57783366Sjulian VOP_UNLOCK(vp, 0, td); 57871699Sjhb return(error); 57954803Srwatson} 58054803Srwatson 58154803Srwatson/* 58254803Srwatson * Given a vnode, get its ACL. 58354803Srwatson */ 58454803Srwatsonstatic int 58585582Srwatsonvacl_get_acl(struct thread *td, struct vnode *vp, acl_type_t type, 58654803Srwatson struct acl *aclp) 58754803Srwatson{ 58854803Srwatson struct acl inkernelacl; 58954803Srwatson int error; 59054803Srwatson 59183366Sjulian VOP_LEASE(vp, td, td->td_proc->p_ucred, LEASE_WRITE); 59283366Sjulian vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); 59383366Sjulian error = VOP_GETACL(vp, type, &inkernelacl, td->td_proc->p_ucred, td); 59483366Sjulian VOP_UNLOCK(vp, 0, td); 59554803Srwatson if (error == 0) 59654803Srwatson error = copyout(&inkernelacl, aclp, sizeof(struct acl)); 59754803Srwatson return (error); 59854803Srwatson} 59954803Srwatson 60054803Srwatson/* 60154803Srwatson * Given a vnode, delete its ACL. 60254803Srwatson */ 60354803Srwatsonstatic int 60485582Srwatsonvacl_delete(struct thread *td, struct vnode *vp, acl_type_t type) 60554803Srwatson{ 60654803Srwatson int error; 60754803Srwatson 60883366Sjulian VOP_LEASE(vp, td, td->td_proc->p_ucred, LEASE_WRITE); 60983366Sjulian vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); 61085582Srwatson error = VOP_SETACL(vp, ACL_TYPE_DEFAULT, 0, td->td_proc->p_ucred, 61185582Srwatson td); 61283366Sjulian VOP_UNLOCK(vp, 0, td); 61354803Srwatson return (error); 61454803Srwatson} 61554803Srwatson 61654803Srwatson/* 61754803Srwatson * Given a vnode, check whether an ACL is appropriate for it 61854803Srwatson */ 61954803Srwatsonstatic int 62085582Srwatsonvacl_aclcheck(struct thread *td, struct vnode *vp, acl_type_t type, 62154803Srwatson struct acl *aclp) 62254803Srwatson{ 62354803Srwatson struct acl inkernelacl; 62454803Srwatson int error; 62554803Srwatson 62654803Srwatson error = copyin(aclp, &inkernelacl, sizeof(struct acl)); 62754803Srwatson if (error) 62854803Srwatson return(error); 62985582Srwatson error = VOP_ACLCHECK(vp, type, &inkernelacl, td->td_proc->p_ucred, 63085582Srwatson td); 63154803Srwatson return (error); 63254803Srwatson} 63354803Srwatson 63454803Srwatson/* 63554803Srwatson * syscalls -- convert the path/fd to a vnode, and call vacl_whatever. 63654803Srwatson * Don't need to lock, as the vacl_ code will get/release any locks 63754803Srwatson * required. 63854803Srwatson */ 63954803Srwatson 64054803Srwatson/* 64154803Srwatson * Given a file path, get an ACL for it 64282713Sdillon * 64382713Sdillon * MPSAFE 64454803Srwatson */ 64554803Srwatsonint 64685582Srwatson__acl_get_file(struct thread *td, struct __acl_get_file_args *uap) 64754803Srwatson{ 64854803Srwatson struct nameidata nd; 64954803Srwatson int error; 65054803Srwatson 65182713Sdillon mtx_lock(&Giant); 65283366Sjulian NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), td); 65354803Srwatson error = namei(&nd); 65482713Sdillon if (error == 0) { 65583366Sjulian error = vacl_get_acl(td, nd.ni_vp, SCARG(uap, type), 65682713Sdillon SCARG(uap, aclp)); 65782713Sdillon NDFREE(&nd, 0); 65882713Sdillon } 65982713Sdillon mtx_unlock(&Giant); 66054803Srwatson return (error); 66154803Srwatson} 66254803Srwatson 66354803Srwatson/* 66454803Srwatson * Given a file path, set an ACL for it 66582713Sdillon * 66682713Sdillon * MPSAFE 66754803Srwatson */ 66854803Srwatsonint 66985582Srwatson__acl_set_file(struct thread *td, struct __acl_set_file_args *uap) 67054803Srwatson{ 67154803Srwatson struct nameidata nd; 67254803Srwatson int error; 67354803Srwatson 67482713Sdillon mtx_lock(&Giant); 67583366Sjulian NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), td); 67654803Srwatson error = namei(&nd); 67782713Sdillon if (error == 0) { 67883366Sjulian error = vacl_set_acl(td, nd.ni_vp, SCARG(uap, type), 67982713Sdillon SCARG(uap, aclp)); 68082713Sdillon NDFREE(&nd, 0); 68182713Sdillon } 68282713Sdillon mtx_unlock(&Giant); 68354803Srwatson return (error); 68454803Srwatson} 68554803Srwatson 68654803Srwatson/* 68754803Srwatson * Given a file descriptor, get an ACL for it 68882713Sdillon * 68982713Sdillon * MPSAFE 69054803Srwatson */ 69154803Srwatsonint 69285582Srwatson__acl_get_fd(struct thread *td, struct __acl_get_fd_args *uap) 69354803Srwatson{ 69454803Srwatson struct file *fp; 69554803Srwatson int error; 69654803Srwatson 69782713Sdillon mtx_lock(&Giant); 69883366Sjulian error = getvnode(td->td_proc->p_fd, SCARG(uap, filedes), &fp); 69982713Sdillon if (error == 0) { 70083366Sjulian error = vacl_get_acl(td, (struct vnode *)fp->f_data, 70182713Sdillon SCARG(uap, type), SCARG(uap, aclp)); 70282713Sdillon } 70382713Sdillon mtx_unlock(&Giant); 70482713Sdillon return (error); 70554803Srwatson} 70654803Srwatson 70754803Srwatson/* 70854803Srwatson * Given a file descriptor, set an ACL for it 70982713Sdillon * 71082713Sdillon * MPSAFE 71154803Srwatson */ 71254803Srwatsonint 71385582Srwatson__acl_set_fd(struct thread *td, struct __acl_set_fd_args *uap) 71454803Srwatson{ 71554803Srwatson struct file *fp; 71654803Srwatson int error; 71754803Srwatson 71882713Sdillon mtx_lock(&Giant); 71983366Sjulian error = getvnode(td->td_proc->p_fd, SCARG(uap, filedes), &fp); 72082713Sdillon if (error == 0) { 72183366Sjulian error = vacl_set_acl(td, (struct vnode *)fp->f_data, 72282713Sdillon SCARG(uap, type), SCARG(uap, aclp)); 72382713Sdillon } 72482713Sdillon mtx_unlock(&Giant); 72582713Sdillon return (error); 72654803Srwatson} 72754803Srwatson 72854803Srwatson/* 72954803Srwatson * Given a file path, delete an ACL from it. 73082713Sdillon * 73182713Sdillon * MPSAFE 73254803Srwatson */ 73354803Srwatsonint 73485582Srwatson__acl_delete_file(struct thread *td, struct __acl_delete_file_args *uap) 73554803Srwatson{ 73654803Srwatson struct nameidata nd; 73754803Srwatson int error; 73854803Srwatson 73982713Sdillon mtx_lock(&Giant); 74083366Sjulian NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), td); 74154803Srwatson error = namei(&nd); 74282713Sdillon if (error == 0) { 74383366Sjulian error = vacl_delete(td, nd.ni_vp, SCARG(uap, type)); 74482713Sdillon NDFREE(&nd, 0); 74582713Sdillon } 74682713Sdillon mtx_unlock(&Giant); 74754803Srwatson return (error); 74854803Srwatson} 74954803Srwatson 75054803Srwatson/* 75154803Srwatson * Given a file path, delete an ACL from it. 75282713Sdillon * 75382713Sdillon * MPSAFE 75454803Srwatson */ 75554803Srwatsonint 75685582Srwatson__acl_delete_fd(struct thread *td, struct __acl_delete_fd_args *uap) 75754803Srwatson{ 75854803Srwatson struct file *fp; 75954803Srwatson int error; 76054803Srwatson 76182713Sdillon mtx_lock(&Giant); 76283366Sjulian error = getvnode(td->td_proc->p_fd, SCARG(uap, filedes), &fp); 76382713Sdillon if (error == 0) { 76483366Sjulian error = vacl_delete(td, (struct vnode *)fp->f_data, 76582713Sdillon SCARG(uap, type)); 76682713Sdillon } 76782713Sdillon mtx_unlock(&Giant); 76854803Srwatson return (error); 76954803Srwatson} 77054803Srwatson 77154803Srwatson/* 77254803Srwatson * Given a file path, check an ACL for it 77382713Sdillon * 77482713Sdillon * MPSAFE 77554803Srwatson */ 77654803Srwatsonint 77785582Srwatson__acl_aclcheck_file(struct thread *td, struct __acl_aclcheck_file_args *uap) 77854803Srwatson{ 77954803Srwatson struct nameidata nd; 78054803Srwatson int error; 78154803Srwatson 78282713Sdillon mtx_lock(&Giant); 78383366Sjulian NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), td); 78454803Srwatson error = namei(&nd); 78582713Sdillon if (error == 0) { 78683366Sjulian error = vacl_aclcheck(td, nd.ni_vp, SCARG(uap, type), 78782713Sdillon SCARG(uap, aclp)); 78882713Sdillon NDFREE(&nd, 0); 78982713Sdillon } 79082713Sdillon mtx_unlock(&Giant); 79154803Srwatson return (error); 79254803Srwatson} 79354803Srwatson 79454803Srwatson/* 79554803Srwatson * Given a file descriptor, check an ACL for it 79682713Sdillon * 79782713Sdillon * MPSAFE 79854803Srwatson */ 79954803Srwatsonint 80085582Srwatson__acl_aclcheck_fd(struct thread *td, struct __acl_aclcheck_fd_args *uap) 80154803Srwatson{ 80254803Srwatson struct file *fp; 80354803Srwatson int error; 80454803Srwatson 80582713Sdillon mtx_lock(&Giant); 80683366Sjulian error = getvnode(td->td_proc->p_fd, SCARG(uap, filedes), &fp); 80782713Sdillon if (error == 0) { 80883366Sjulian error = vacl_aclcheck(td, (struct vnode *)fp->f_data, 80982713Sdillon SCARG(uap, type), SCARG(uap, aclp)); 81082713Sdillon } 81182713Sdillon mtx_unlock(&Giant); 81282713Sdillon return (error); 81354803Srwatson} 814