subr_acl_posix1e.c revision 76139
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 76139 2001-04-29 19:53:50Z 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> 4054803Srwatson#include <sys/namei.h> 4154803Srwatson#include <sys/file.h> 4254803Srwatson#include <sys/proc.h> 4354803Srwatson#include <sys/sysent.h> 4454803Srwatson#include <sys/errno.h> 4554803Srwatson#include <sys/stat.h> 4654803Srwatson#include <sys/acl.h> 4754803Srwatson 4873890SrwatsonMALLOC_DEFINE(M_ACL, "acl", "access control list"); 4954803Srwatson 5056272Srwatsonstatic int vacl_set_acl(struct proc *p, struct vnode *vp, acl_type_t type, 5156272Srwatson struct acl *aclp); 5256272Srwatsonstatic int vacl_get_acl(struct proc *p, struct vnode *vp, acl_type_t type, 5356272Srwatson struct acl *aclp); 5475571Srwatsonstatic int vacl_aclcheck(struct proc *p, struct vnode *vp, 5575571Srwatson acl_type_t type, struct acl *aclp); 5654803Srwatson 5754803Srwatson/* 5873890Srwatson * Implement a version of vaccess() that understands POSIX.1e ACL semantics. 5973890Srwatson * Return 0 on success, else an errno value. Should be merged into 6073890Srwatson * vaccess() eventually. 6173890Srwatson */ 6273890Srwatsonint 6375571Srwatsonvaccess_acl_posix1e(enum vtype type, uid_t file_uid, gid_t file_gid, 6475571Srwatson struct acl *acl, mode_t acc_mode, struct ucred *cred, int *privused) 6573890Srwatson{ 6673890Srwatson struct acl_entry *acl_other, *acl_mask; 6773890Srwatson mode_t dac_granted; 6873890Srwatson mode_t cap_granted; 6973890Srwatson mode_t acl_mask_granted; 7073890Srwatson int group_matched, i; 7173890Srwatson 7273890Srwatson /* 7373890Srwatson * Look for a normal, non-privileged way to access the file/directory 7473890Srwatson * as requested. If it exists, go with that. Otherwise, attempt 7573890Srwatson * to use privileges granted via cap_granted. In some cases, 7673890Srwatson * which privileges to use may be ambiguous due to "best match", 7773890Srwatson * in which case fall back on first match for the time being. 7873890Srwatson */ 7973890Srwatson if (privused != NULL) 8073890Srwatson *privused = 0; 8173890Srwatson 8273890Srwatson /* 8373890Srwatson * Determine privileges now, but don't apply until we've found 8473890Srwatson * a DAC match that has failed to allow access. 8573890Srwatson */ 8673890Srwatson#ifndef CAPABILITIES 8773890Srwatson if (suser_xxx(cred, NULL, PRISON_ROOT) == 0) 8873890Srwatson cap_granted = (VEXEC | VREAD | VWRITE | VADMIN); 8973890Srwatson else 9073890Srwatson cap_granted = 0; 9173890Srwatson#else 9273890Srwatson cap_granted = 0; 9373890Srwatson 9473890Srwatson if (type == VDIR) { 9573890Srwatson if ((acc_mode & VEXEC) && !cap_check(cred, NULL, 9673890Srwatson CAP_DAC_READ_SEARCH, PRISON_ROOT)) 9773890Srwatson cap_granted |= VEXEC; 9873890Srwatson } else { 9973890Srwatson if ((acc_mode & VEXEC) && !cap_check(cred, NULL, 10073890Srwatson CAP_DAC_EXECUTE, PRISON_ROOT)) 10173890Srwatson cap_granted |= VEXEC; 10273890Srwatson } 10373890Srwatson 10473890Srwatson if ((acc_mode & VREAD) && !cap_check(cred, NULL, CAP_DAC_READ_SEARCH, 10573890Srwatson PRISON_ROOT)) 10673890Srwatson cap_granted |= VREAD; 10773890Srwatson 10873890Srwatson if ((acc_mode & VWRITE) && !cap_check(cred, NULL, CAP_DAC_WRITE, 10973890Srwatson PRISON_ROOT)) 11073890Srwatson cap_granted |= VWRITE; 11173890Srwatson 11273890Srwatson if ((acc_mode & VADMIN) && !cap_check(cred, NULL, CAP_FOWNER, 11373890Srwatson PRISON_ROOT)) 11473890Srwatson cap_granted |= VADMIN; 11573890Srwatson#endif /* CAPABILITIES */ 11673890Srwatson 11773890Srwatson /* 11873890Srwatson * Check the owner. 11973890Srwatson * Also, record locations of ACL_MASK and ACL_OTHER for reference 12073890Srwatson * later if the owner doesn't match. 12173890Srwatson */ 12273890Srwatson acl_mask = acl_other = NULL; 12373890Srwatson for (i = 0; i < acl->acl_cnt; i++) { 12473890Srwatson switch (acl->acl_entry[i].ae_tag) { 12573890Srwatson case ACL_USER_OBJ: 12675571Srwatson if (file_uid != cred->cr_uid) 12773890Srwatson break; 12873890Srwatson dac_granted = 0; 12973890Srwatson dac_granted |= VADMIN; 13075404Sjedgar if (acl->acl_entry[i].ae_perm & ACL_EXECUTE) 13173890Srwatson dac_granted |= VEXEC; 13275404Sjedgar if (acl->acl_entry[i].ae_perm & ACL_READ) 13373890Srwatson dac_granted |= VREAD; 13475404Sjedgar if (acl->acl_entry[i].ae_perm & ACL_WRITE) 13573890Srwatson dac_granted |= VWRITE; 13673890Srwatson if ((acc_mode & dac_granted) == acc_mode) 13773890Srwatson return (0); 13873890Srwatson if ((acc_mode & (dac_granted | cap_granted)) == 13973890Srwatson acc_mode) { 14073890Srwatson if (privused != NULL) 14173890Srwatson *privused = 1; 14273890Srwatson return (0); 14373890Srwatson } 14473890Srwatson goto error; 14573890Srwatson 14673890Srwatson case ACL_MASK: 14773890Srwatson acl_mask = &acl->acl_entry[i]; 14873890Srwatson break; 14973890Srwatson 15073890Srwatson case ACL_OTHER: 15173890Srwatson acl_other = &acl->acl_entry[i]; 15273890Srwatson break; 15373890Srwatson 15473890Srwatson default: 15573890Srwatson } 15673890Srwatson } 15773890Srwatson 15873890Srwatson /* 15973890Srwatson * Checks against ACL_USER, ACL_GROUP_OBJ, and ACL_GROUP fields 16073890Srwatson * are masked by an ACL_MASK entry, if any. As such, first identify 16173890Srwatson * the ACL_MASK field, then iterate through identifying potential 16273890Srwatson * user matches, then group matches. If there is no ACL_MASK, 16373890Srwatson * assume that the mask allows all requests to succeed. 16473890Srwatson * Also keep track of the location of ACL_OTHER for later consumption. 16573890Srwatson */ 16673890Srwatson if (acl_other == NULL) { 16773890Srwatson /* 16873890Srwatson * XXX: This should never happen. Only properly formatted 16973890Srwatson * ACLs should be passed to vaccess_acl_posix1e. 17073890Srwatson * Should make this a panic post-debugging. 17173890Srwatson */ 17273890Srwatson printf("vaccess_acl_posix1e: ACL_OTHER missing\n"); 17373890Srwatson return (EPERM); 17473890Srwatson } 17573890Srwatson if (acl_mask != NULL) { 17673890Srwatson acl_mask_granted = 0; 17775404Sjedgar if (acl_mask->ae_perm & ACL_EXECUTE) 17873890Srwatson acl_mask_granted |= VEXEC; 17975404Sjedgar if (acl_mask->ae_perm & ACL_READ) 18073890Srwatson acl_mask_granted |= VREAD; 18175404Sjedgar if (acl_mask->ae_perm & ACL_WRITE) 18273890Srwatson acl_mask_granted |= VWRITE; 18373890Srwatson } else 18473890Srwatson acl_mask_granted = VEXEC | VREAD | VWRITE; 18573890Srwatson 18673890Srwatson /* 18773890Srwatson * We have to check each type even if we know ACL_MASK will reject, 18873890Srwatson * as we need to know what match there might have been, and 18973890Srwatson * therefore what further types we might be allowed to check. 19073890Srwatson * Do the checks twice -- once without privilege, and a second time 19173890Srwatson * with, if there was a match. 19273890Srwatson */ 19373890Srwatson 19473890Srwatson /* 19573890Srwatson * Check ACL_USER ACL entries. 19673890Srwatson */ 19773890Srwatson for (i = 0; i < acl->acl_cnt; i++) { 19873890Srwatson switch (acl->acl_entry[i].ae_tag) { 19973890Srwatson case ACL_USER: 20073890Srwatson if (acl->acl_entry[i].ae_id != cred->cr_uid) 20173890Srwatson break; 20273890Srwatson dac_granted = 0; 20375404Sjedgar if (acl->acl_entry[i].ae_perm & ACL_EXECUTE) 20473890Srwatson dac_granted |= VEXEC; 20575404Sjedgar if (acl->acl_entry[i].ae_perm & ACL_READ) 20673890Srwatson dac_granted |= VREAD; 20775404Sjedgar if (acl->acl_entry[i].ae_perm & ACL_WRITE) 20873890Srwatson dac_granted |= VWRITE; 20973890Srwatson dac_granted &= acl_mask_granted; 21073890Srwatson if ((acc_mode & dac_granted) == acc_mode) 21173890Srwatson return (0); 21275571Srwatson if ((acc_mode & (dac_granted | cap_granted)) != 21375571Srwatson acc_mode) 21475571Srwatson goto error; 21575571Srwatson 21675571Srwatson if (privused != NULL) 21775571Srwatson *privused = 1; 21875571Srwatson return (0); 21973890Srwatson } 22073890Srwatson } 22173890Srwatson 22273890Srwatson /* 22373890Srwatson * Group match is best-match, not first-match, so find a 22473890Srwatson * "best" match. Iterate across, testing each potential group 22573890Srwatson * match. Make sure we keep track of whether we found a match 22673890Srwatson * or not, so that we know if we can move on to ACL_OTHER. 22773890Srwatson */ 22873890Srwatson group_matched = 0; 22973890Srwatson for (i = 0; i < acl->acl_cnt; i++) { 23073890Srwatson switch (acl->acl_entry[i].ae_tag) { 23173890Srwatson case ACL_GROUP_OBJ: 23275888Stmm if (!groupmember(file_gid, cred)) 23375571Srwatson break; 23475571Srwatson dac_granted = 0; 23575571Srwatson if (acl->acl_entry[i].ae_perm & ACL_EXECUTE) 23675571Srwatson dac_granted |= VEXEC; 23775571Srwatson if (acl->acl_entry[i].ae_perm & ACL_READ) 23875571Srwatson dac_granted |= VREAD; 23975571Srwatson if (acl->acl_entry[i].ae_perm & ACL_WRITE) 24075571Srwatson dac_granted |= VWRITE; 24175571Srwatson dac_granted &= acl_mask_granted; 24275571Srwatson 24375571Srwatson if ((acc_mode & dac_granted) == acc_mode) 24475571Srwatson return (0); 24575571Srwatson 24675571Srwatson group_matched = 1; 24775571Srwatson break; 24875571Srwatson 24973890Srwatson case ACL_GROUP: 25075571Srwatson if (!groupmember(acl->acl_entry[i].ae_id, cred)) 25175571Srwatson break; 25275571Srwatson dac_granted = 0; 25375571Srwatson if (acl->acl_entry[i].ae_perm & ACL_EXECUTE) 25475571Srwatson dac_granted |= VEXEC; 25575571Srwatson if (acl->acl_entry[i].ae_perm & ACL_READ) 25675571Srwatson dac_granted |= VREAD; 25775571Srwatson if (acl->acl_entry[i].ae_perm & ACL_WRITE) 25875571Srwatson dac_granted |= VWRITE; 25975571Srwatson dac_granted &= acl_mask_granted; 26073890Srwatson 26175571Srwatson if ((acc_mode & dac_granted) == acc_mode) 26275571Srwatson return (0); 26373890Srwatson 26475571Srwatson group_matched = 1; 26575571Srwatson break; 26675571Srwatson 26773890Srwatson default: 26873890Srwatson } 26973890Srwatson } 27073890Srwatson 27173890Srwatson if (group_matched == 1) { 27273890Srwatson /* 27373890Srwatson * There was a match, but it did not grant rights via 27473890Srwatson * pure DAC. Try again, this time with privilege. 27573890Srwatson */ 27673890Srwatson for (i = 0; i < acl->acl_cnt; i++) { 27773890Srwatson switch (acl->acl_entry[i].ae_tag) { 27873890Srwatson case ACL_GROUP_OBJ: 27976139Srwatson if (!groupmember(file_gid, cred)) 28075571Srwatson break; 28175571Srwatson dac_granted = 0; 28275571Srwatson if (acl->acl_entry[i].ae_perm & ACL_EXECUTE) 28375571Srwatson dac_granted |= VEXEC; 28475571Srwatson if (acl->acl_entry[i].ae_perm & ACL_READ) 28575571Srwatson dac_granted |= VREAD; 28675571Srwatson if (acl->acl_entry[i].ae_perm & ACL_WRITE) 28775571Srwatson dac_granted |= VWRITE; 28875571Srwatson dac_granted &= acl_mask_granted; 28975571Srwatson 29075571Srwatson if ((acc_mode & (dac_granted | cap_granted)) != 29175571Srwatson acc_mode) 29275571Srwatson break; 29375571Srwatson 29475571Srwatson if (privused != NULL) 29575571Srwatson *privused = 1; 29675571Srwatson return (0); 29775571Srwatson 29873890Srwatson case ACL_GROUP: 29975571Srwatson if (!groupmember(acl->acl_entry[i].ae_id, 30075571Srwatson cred)) 30175571Srwatson break; 30275571Srwatson dac_granted = 0; 30375571Srwatson if (acl->acl_entry[i].ae_perm & ACL_EXECUTE) 30475571Srwatson dac_granted |= VEXEC; 30575571Srwatson if (acl->acl_entry[i].ae_perm & ACL_READ) 30675571Srwatson dac_granted |= VREAD; 30775571Srwatson if (acl->acl_entry[i].ae_perm & ACL_WRITE) 30875571Srwatson dac_granted |= VWRITE; 30975571Srwatson dac_granted &= acl_mask_granted; 31075571Srwatson 31175571Srwatson if ((acc_mode & (dac_granted | cap_granted)) != 31275571Srwatson acc_mode) 31375571Srwatson break; 31475571Srwatson 31575571Srwatson if (privused != NULL) 31675571Srwatson *privused = 1; 31775571Srwatson return (0); 31875571Srwatson 31973890Srwatson default: 32073890Srwatson } 32173890Srwatson } 32273890Srwatson /* 32373890Srwatson * Even with privilege, group membership was not sufficient. 32473890Srwatson * Return failure. 32573890Srwatson */ 32673890Srwatson goto error; 32773890Srwatson } 32873890Srwatson 32973890Srwatson /* 33073890Srwatson * Fall back on ACL_OTHER. ACL_MASK is not applied to ACL_OTHER. 33173890Srwatson */ 33273890Srwatson dac_granted = 0; 33375404Sjedgar if (acl_other->ae_perm & ACL_EXECUTE) 33473890Srwatson dac_granted |= VEXEC; 33575404Sjedgar if (acl_other->ae_perm & ACL_READ) 33673890Srwatson dac_granted |= VREAD; 33775404Sjedgar if (acl_other->ae_perm & ACL_WRITE) 33873890Srwatson dac_granted |= VWRITE; 33973890Srwatson 34073890Srwatson if ((acc_mode & dac_granted) == acc_mode) 34173890Srwatson return (0); 34273890Srwatson if ((acc_mode & (dac_granted | cap_granted)) == acc_mode) { 34373890Srwatson if (privused != NULL) 34473890Srwatson *privused = 1; 34573890Srwatson return (0); 34673890Srwatson } 34773890Srwatson 34873890Srwatsonerror: 34973890Srwatson return ((acc_mode & VADMIN) ? EPERM : EACCES); 35073890Srwatson} 35173890Srwatson 35273890Srwatson/* 35373890Srwatson * For the purposes of file systems maintaining the _OBJ entries in an 35473890Srwatson * inode with a mode_t field, this routine converts a mode_t entry 35573890Srwatson * to an acl_perm_t. 35673890Srwatson */ 35773890Srwatsonacl_perm_t 35873890Srwatsonacl_posix1e_mode_to_perm(acl_tag_t tag, mode_t mode) 35973890Srwatson{ 36073890Srwatson acl_perm_t perm = 0; 36173890Srwatson 36273890Srwatson switch(tag) { 36373890Srwatson case ACL_USER_OBJ: 36473890Srwatson if (mode & S_IXUSR) 36575404Sjedgar perm |= ACL_EXECUTE; 36673890Srwatson if (mode & S_IRUSR) 36775404Sjedgar perm |= ACL_READ; 36873890Srwatson if (mode & S_IWUSR) 36975404Sjedgar perm |= ACL_WRITE; 37073890Srwatson return (perm); 37173890Srwatson 37273890Srwatson case ACL_GROUP_OBJ: 37373890Srwatson if (mode & S_IXGRP) 37475404Sjedgar perm |= ACL_EXECUTE; 37573890Srwatson if (mode & S_IRGRP) 37675404Sjedgar perm |= ACL_READ; 37773890Srwatson if (mode & S_IWGRP) 37875404Sjedgar perm |= ACL_WRITE; 37973890Srwatson return (perm); 38073890Srwatson 38173890Srwatson case ACL_OTHER: 38273890Srwatson if (mode & S_IXOTH) 38375404Sjedgar perm |= ACL_EXECUTE; 38473890Srwatson if (mode & S_IROTH) 38575404Sjedgar perm |= ACL_READ; 38673890Srwatson if (mode & S_IWOTH) 38775404Sjedgar perm |= ACL_WRITE; 38873890Srwatson return (perm); 38973890Srwatson 39073890Srwatson default: 39173890Srwatson printf("acl_posix1e_mode_to_perm: invalid tag (%d)\n", tag); 39273890Srwatson return (0); 39373890Srwatson } 39473890Srwatson} 39573890Srwatson 39673890Srwatson/* 39773890Srwatson * Given inode information (uid, gid, mode), return an acl entry of the 39873890Srwatson * appropriate type. 39973890Srwatson */ 40073890Srwatsonstruct acl_entry 40173890Srwatsonacl_posix1e_mode_to_entry(acl_tag_t tag, uid_t uid, gid_t gid, mode_t mode) 40273890Srwatson{ 40373890Srwatson struct acl_entry acl_entry; 40473890Srwatson 40573890Srwatson acl_entry.ae_tag = tag; 40673890Srwatson acl_entry.ae_perm = acl_posix1e_mode_to_perm(tag, mode); 40773890Srwatson switch(tag) { 40873890Srwatson case ACL_USER_OBJ: 40973890Srwatson acl_entry.ae_id = uid; 41073890Srwatson break; 41173890Srwatson 41273890Srwatson case ACL_GROUP_OBJ: 41373890Srwatson acl_entry.ae_id = gid; 41473890Srwatson break; 41573890Srwatson 41673890Srwatson case ACL_OTHER: 41773890Srwatson acl_entry.ae_id = 0; 41873890Srwatson break; 41973890Srwatson 42073890Srwatson default: 42173890Srwatson acl_entry.ae_id = 0; 42273890Srwatson printf("acl_posix1e_mode_to_entry: invalid tag (%d)\n", tag); 42373890Srwatson } 42473890Srwatson 42573890Srwatson return (acl_entry); 42673890Srwatson} 42773890Srwatson 42873890Srwatson/* 42973890Srwatson * Utility function to generate a file mode given appropriate ACL entries. 43073890Srwatson */ 43173890Srwatsonmode_t 43273890Srwatsonacl_posix1e_perms_to_mode(struct acl_entry *acl_user_obj_entry, 43373890Srwatson struct acl_entry *acl_group_obj_entry, struct acl_entry *acl_other_entry) 43473890Srwatson{ 43573890Srwatson mode_t mode; 43673890Srwatson 43773890Srwatson mode = 0; 43875404Sjedgar if (acl_user_obj_entry->ae_perm & ACL_EXECUTE) 43973890Srwatson mode |= S_IXUSR; 44075404Sjedgar if (acl_user_obj_entry->ae_perm & ACL_READ) 44173890Srwatson mode |= S_IRUSR; 44275404Sjedgar if (acl_user_obj_entry->ae_perm & ACL_WRITE) 44373890Srwatson mode |= S_IWUSR; 44475404Sjedgar if (acl_group_obj_entry->ae_perm & ACL_EXECUTE) 44573890Srwatson mode |= S_IXGRP; 44675404Sjedgar if (acl_group_obj_entry->ae_perm & ACL_READ) 44773890Srwatson mode |= S_IRGRP; 44875404Sjedgar if (acl_group_obj_entry->ae_perm & ACL_WRITE) 44973890Srwatson mode |= S_IWGRP; 45075404Sjedgar if (acl_other_entry->ae_perm & ACL_EXECUTE) 45173890Srwatson mode |= S_IXOTH; 45275404Sjedgar if (acl_other_entry->ae_perm & ACL_READ) 45373890Srwatson mode |= S_IROTH; 45475404Sjedgar if (acl_other_entry->ae_perm & ACL_WRITE) 45573890Srwatson mode |= S_IWOTH; 45673890Srwatson 45773890Srwatson return (mode); 45873890Srwatson} 45973890Srwatson 46073890Srwatson/* 46173890Srwatson * Perform a syntactic check of the ACL, sufficient to allow an 46273890Srwatson * implementing file system to determine if it should accept this and 46373890Srwatson * rely on the POSIX.1e ACL properties. 46473890Srwatson */ 46573890Srwatsonint 46673890Srwatsonacl_posix1e_check(struct acl *acl) 46773890Srwatson{ 46873890Srwatson int num_acl_user_obj, num_acl_user, num_acl_group_obj, num_acl_group; 46973890Srwatson int num_acl_mask, num_acl_other, i; 47073890Srwatson 47173890Srwatson /* 47273890Srwatson * Verify that the number of entries does not exceed the maximum 47373890Srwatson * defined for acl_t. 47473890Srwatson * Verify that the correct number of various sorts of ae_tags are 47573890Srwatson * present: 47673890Srwatson * Exactly one ACL_USER_OBJ 47773890Srwatson * Exactly one ACL_GROUP_OBJ 47873890Srwatson * Exactly one ACL_OTHER 47973890Srwatson * If any ACL_USER or ACL_GROUP entries appear, then exactly one 48073890Srwatson * ACL_MASK entry must also appear. 48173890Srwatson * Verify that all ae_perm entries are in ACL_PERM_BITS. 48273890Srwatson * Verify all ae_tag entries are understood by this implementation. 48373890Srwatson * Note: Does not check for uniqueness of qualifier (ae_id) field. 48473890Srwatson */ 48573890Srwatson num_acl_user_obj = num_acl_user = num_acl_group_obj = num_acl_group = 48673890Srwatson num_acl_mask = num_acl_other = 0; 48773890Srwatson if (acl->acl_cnt > ACL_MAX_ENTRIES || acl->acl_cnt < 0) 48873890Srwatson return (EINVAL); 48973890Srwatson for (i = 0; i < acl->acl_cnt; i++) { 49073890Srwatson /* 49173890Srwatson * Check for a valid tag. 49273890Srwatson */ 49373890Srwatson switch(acl->acl_entry[i].ae_tag) { 49473890Srwatson case ACL_USER_OBJ: 49575571Srwatson acl->acl_entry[i].ae_id = ACL_UNDEFINED_ID; /* XXX */ 49675571Srwatson if (acl->acl_entry[i].ae_id != ACL_UNDEFINED_ID) 49775571Srwatson return (EINVAL); 49873890Srwatson num_acl_user_obj++; 49973890Srwatson break; 50073890Srwatson case ACL_GROUP_OBJ: 50175571Srwatson acl->acl_entry[i].ae_id = ACL_UNDEFINED_ID; /* XXX */ 50275571Srwatson if (acl->acl_entry[i].ae_id != ACL_UNDEFINED_ID) 50375571Srwatson return (EINVAL); 50473890Srwatson num_acl_group_obj++; 50573890Srwatson break; 50673890Srwatson case ACL_USER: 50775571Srwatson if (acl->acl_entry[i].ae_id == ACL_UNDEFINED_ID) 50875571Srwatson return (EINVAL); 50973890Srwatson num_acl_user++; 51073890Srwatson break; 51173890Srwatson case ACL_GROUP: 51275571Srwatson if (acl->acl_entry[i].ae_id == ACL_UNDEFINED_ID) 51375571Srwatson return (EINVAL); 51473890Srwatson num_acl_group++; 51573890Srwatson break; 51673890Srwatson case ACL_OTHER: 51775571Srwatson acl->acl_entry[i].ae_id = ACL_UNDEFINED_ID; /* XXX */ 51875571Srwatson if (acl->acl_entry[i].ae_id != ACL_UNDEFINED_ID) 51975571Srwatson return (EINVAL); 52073890Srwatson num_acl_other++; 52173890Srwatson break; 52273890Srwatson case ACL_MASK: 52375571Srwatson acl->acl_entry[i].ae_id = ACL_UNDEFINED_ID; /* XXX */ 52475571Srwatson if (acl->acl_entry[i].ae_id != ACL_UNDEFINED_ID) 52575571Srwatson return (EINVAL); 52673890Srwatson num_acl_mask++; 52773890Srwatson break; 52873890Srwatson default: 52973890Srwatson return (EINVAL); 53073890Srwatson } 53173890Srwatson /* 53273890Srwatson * Check for valid perm entries. 53373890Srwatson */ 53473890Srwatson if ((acl->acl_entry[i].ae_perm | ACL_PERM_BITS) != 53573890Srwatson ACL_PERM_BITS) 53673890Srwatson return (EINVAL); 53773890Srwatson } 53873890Srwatson if ((num_acl_user_obj != 1) || (num_acl_group_obj != 1) || 53973890Srwatson (num_acl_other != 1) || (num_acl_mask != 0 && num_acl_mask != 1)) 54073890Srwatson return (EINVAL); 54173890Srwatson if (((num_acl_group != 0) || (num_acl_user != 0)) && 54273890Srwatson (num_acl_mask != 1)) 54373890Srwatson return (EINVAL); 54473890Srwatson return (0); 54573890Srwatson} 54673890Srwatson 54773890Srwatson/* 54854803Srwatson * These calls wrap the real vnode operations, and are called by the 54954803Srwatson * syscall code once the syscall has converted the path or file 55054803Srwatson * descriptor to a vnode (unlocked). The aclp pointer is assumed 55154803Srwatson * still to point to userland, so this should not be consumed within 55254803Srwatson * the kernel except by syscall code. Other code should directly 55354803Srwatson * invoke VOP_{SET,GET}ACL. 55454803Srwatson */ 55554803Srwatson 55654803Srwatson/* 55754803Srwatson * Given a vnode, set its ACL. 55854803Srwatson */ 55954803Srwatsonstatic int 56054803Srwatsonvacl_set_acl(struct proc *p, struct vnode *vp, acl_type_t type, 56156272Srwatson struct acl *aclp) 56254803Srwatson{ 56354803Srwatson struct acl inkernacl; 56454803Srwatson int error; 56554803Srwatson 56654803Srwatson error = copyin(aclp, &inkernacl, sizeof(struct acl)); 56754803Srwatson if (error) 56854803Srwatson return(error); 56971699Sjhb VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); 57054803Srwatson vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); 57171699Sjhb error = VOP_SETACL(vp, type, &inkernacl, p->p_ucred, p); 57254803Srwatson VOP_UNLOCK(vp, 0, p); 57371699Sjhb return(error); 57454803Srwatson} 57554803Srwatson 57654803Srwatson/* 57754803Srwatson * Given a vnode, get its ACL. 57854803Srwatson */ 57954803Srwatsonstatic int 58054803Srwatsonvacl_get_acl(struct proc *p, struct vnode *vp, acl_type_t type, 58154803Srwatson struct acl *aclp) 58254803Srwatson{ 58354803Srwatson struct acl inkernelacl; 58454803Srwatson int error; 58554803Srwatson 58671699Sjhb VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); 58766184Srwatson vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); 58871699Sjhb error = VOP_GETACL(vp, type, &inkernelacl, p->p_ucred, p); 58966184Srwatson VOP_UNLOCK(vp, 0, p); 59054803Srwatson if (error == 0) 59154803Srwatson error = copyout(&inkernelacl, aclp, sizeof(struct acl)); 59254803Srwatson return (error); 59354803Srwatson} 59454803Srwatson 59554803Srwatson/* 59654803Srwatson * Given a vnode, delete its ACL. 59754803Srwatson */ 59854803Srwatsonstatic int 59954803Srwatsonvacl_delete(struct proc *p, struct vnode *vp, acl_type_t type) 60054803Srwatson{ 60154803Srwatson int error; 60254803Srwatson 60371699Sjhb VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); 60454803Srwatson vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); 60571699Sjhb error = VOP_SETACL(vp, ACL_TYPE_DEFAULT, 0, p->p_ucred, p); 60654803Srwatson VOP_UNLOCK(vp, 0, p); 60754803Srwatson return (error); 60854803Srwatson} 60954803Srwatson 61054803Srwatson/* 61154803Srwatson * Given a vnode, check whether an ACL is appropriate for it 61254803Srwatson */ 61354803Srwatsonstatic int 61454803Srwatsonvacl_aclcheck(struct proc *p, struct vnode *vp, acl_type_t type, 61554803Srwatson struct acl *aclp) 61654803Srwatson{ 61754803Srwatson struct acl inkernelacl; 61854803Srwatson int error; 61954803Srwatson 62054803Srwatson error = copyin(aclp, &inkernelacl, sizeof(struct acl)); 62154803Srwatson if (error) 62254803Srwatson return(error); 62371699Sjhb error = VOP_ACLCHECK(vp, type, &inkernelacl, p->p_ucred, p); 62454803Srwatson return (error); 62554803Srwatson} 62654803Srwatson 62754803Srwatson/* 62854803Srwatson * syscalls -- convert the path/fd to a vnode, and call vacl_whatever. 62954803Srwatson * Don't need to lock, as the vacl_ code will get/release any locks 63054803Srwatson * required. 63154803Srwatson */ 63254803Srwatson 63354803Srwatson/* 63454803Srwatson * Given a file path, get an ACL for it 63554803Srwatson */ 63654803Srwatsonint 63756272Srwatson__acl_get_file(struct proc *p, struct __acl_get_file_args *uap) 63854803Srwatson{ 63954803Srwatson struct nameidata nd; 64054803Srwatson int error; 64154803Srwatson 64254803Srwatson /* what flags are required here -- possible not LOCKLEAF? */ 64354803Srwatson NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); 64454803Srwatson error = namei(&nd); 64554803Srwatson if (error) 64654803Srwatson return(error); 64754803Srwatson error = vacl_get_acl(p, nd.ni_vp, SCARG(uap, type), SCARG(uap, aclp)); 64854803Srwatson NDFREE(&nd, 0); 64954803Srwatson return (error); 65054803Srwatson} 65154803Srwatson 65254803Srwatson/* 65354803Srwatson * Given a file path, set an ACL for it 65454803Srwatson */ 65554803Srwatsonint 65656272Srwatson__acl_set_file(struct proc *p, struct __acl_set_file_args *uap) 65754803Srwatson{ 65854803Srwatson struct nameidata nd; 65954803Srwatson int error; 66054803Srwatson 66154803Srwatson NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); 66254803Srwatson error = namei(&nd); 66354803Srwatson if (error) 66454803Srwatson return(error); 66554803Srwatson error = vacl_set_acl(p, nd.ni_vp, SCARG(uap, type), SCARG(uap, aclp)); 66654803Srwatson NDFREE(&nd, 0); 66754803Srwatson return (error); 66854803Srwatson} 66954803Srwatson 67054803Srwatson/* 67154803Srwatson * Given a file descriptor, get an ACL for it 67254803Srwatson */ 67354803Srwatsonint 67456272Srwatson__acl_get_fd(struct proc *p, struct __acl_get_fd_args *uap) 67554803Srwatson{ 67654803Srwatson struct file *fp; 67754803Srwatson int error; 67854803Srwatson 67954803Srwatson error = getvnode(p->p_fd, SCARG(uap, filedes), &fp); 68054803Srwatson if (error) 68154803Srwatson return(error); 68254803Srwatson return vacl_get_acl(p, (struct vnode *)fp->f_data, SCARG(uap, type), 68354803Srwatson SCARG(uap, aclp)); 68454803Srwatson} 68554803Srwatson 68654803Srwatson/* 68754803Srwatson * Given a file descriptor, set an ACL for it 68854803Srwatson */ 68954803Srwatsonint 69056272Srwatson__acl_set_fd(struct proc *p, struct __acl_set_fd_args *uap) 69154803Srwatson{ 69254803Srwatson struct file *fp; 69354803Srwatson int error; 69454803Srwatson 69554803Srwatson error = getvnode(p->p_fd, SCARG(uap, filedes), &fp); 69654803Srwatson if (error) 69754803Srwatson return(error); 69854803Srwatson return vacl_set_acl(p, (struct vnode *)fp->f_data, SCARG(uap, type), 69954803Srwatson SCARG(uap, aclp)); 70054803Srwatson} 70154803Srwatson 70254803Srwatson/* 70354803Srwatson * Given a file path, delete an ACL from it. 70454803Srwatson */ 70554803Srwatsonint 70656272Srwatson__acl_delete_file(struct proc *p, struct __acl_delete_file_args *uap) 70754803Srwatson{ 70854803Srwatson struct nameidata nd; 70954803Srwatson int error; 71054803Srwatson 71154803Srwatson NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); 71254803Srwatson error = namei(&nd); 71354803Srwatson if (error) 71454803Srwatson return(error); 71554803Srwatson error = vacl_delete(p, nd.ni_vp, SCARG(uap, type)); 71654803Srwatson NDFREE(&nd, 0); 71754803Srwatson return (error); 71854803Srwatson} 71954803Srwatson 72054803Srwatson/* 72154803Srwatson * Given a file path, delete an ACL from it. 72254803Srwatson */ 72354803Srwatsonint 72456272Srwatson__acl_delete_fd(struct proc *p, struct __acl_delete_fd_args *uap) 72554803Srwatson{ 72654803Srwatson struct file *fp; 72754803Srwatson int error; 72854803Srwatson 72954803Srwatson error = getvnode(p->p_fd, SCARG(uap, filedes), &fp); 73054803Srwatson if (error) 73154803Srwatson return(error); 73254803Srwatson error = vacl_delete(p, (struct vnode *)fp->f_data, SCARG(uap, type)); 73354803Srwatson return (error); 73454803Srwatson} 73554803Srwatson 73654803Srwatson/* 73754803Srwatson * Given a file path, check an ACL for it 73854803Srwatson */ 73954803Srwatsonint 74056272Srwatson__acl_aclcheck_file(struct proc *p, struct __acl_aclcheck_file_args *uap) 74154803Srwatson{ 74254803Srwatson struct nameidata nd; 74354803Srwatson int error; 74454803Srwatson 74554803Srwatson NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); 74654803Srwatson error = namei(&nd); 74754803Srwatson if (error) 74854803Srwatson return(error); 74954803Srwatson error = vacl_aclcheck(p, nd.ni_vp, SCARG(uap, type), SCARG(uap, aclp)); 75054803Srwatson NDFREE(&nd, 0); 75154803Srwatson return (error); 75254803Srwatson} 75354803Srwatson 75454803Srwatson/* 75554803Srwatson * Given a file descriptor, check an ACL for it 75654803Srwatson */ 75754803Srwatsonint 75856272Srwatson__acl_aclcheck_fd(struct proc *p, struct __acl_aclcheck_fd_args *uap) 75954803Srwatson{ 76054803Srwatson struct file *fp; 76154803Srwatson int error; 76254803Srwatson 76354803Srwatson error = getvnode(p->p_fd, SCARG(uap, filedes), &fp); 76454803Srwatson if (error) 76554803Srwatson return(error); 76654803Srwatson return vacl_aclcheck(p, (struct vnode *)fp->f_data, SCARG(uap, type), 76754803Srwatson SCARG(uap, aclp)); 76854803Srwatson} 769