subr_acl_posix1e.c revision 85582
1200590Sluigi/*- 2200673Sru * Copyright (c) 1999, 2000, 2001 Robert N. M. Watson 3272840Smelifaro * All rights reserved. 4272840Smelifaro * 5200590Sluigi * Redistribution and use in source and binary forms, with or without 6200590Sluigi * modification, are permitted provided that the following conditions 7200590Sluigi * are met: 8200590Sluigi * 1. Redistributions of source code must retain the above copyright 9200590Sluigi * notice, this list of conditions and the following disclaimer. 10200590Sluigi * 2. Redistributions in binary form must reproduce the above copyright 11200590Sluigi * notice, this list of conditions and the following disclaimer in the 12200590Sluigi * documentation and/or other materials provided with the distribution. 13200590Sluigi * 14200590Sluigi * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15200590Sluigi * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16200590Sluigi * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17200590Sluigi * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18200590Sluigi * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19200590Sluigi * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20200590Sluigi * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21200590Sluigi * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22200590Sluigi * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23200590Sluigi * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24200590Sluigi * SUCH DAMAGE. 25200590Sluigi * 26200590Sluigi * $FreeBSD: head/sys/kern/subr_acl_posix1e.c 85582 2001-10-27 05:45:42Z rwatson $ 27200590Sluigi */ 28200590Sluigi/* 29200590Sluigi * Developed by the TrustedBSD Project. 30200590Sluigi * Support for POSIX.1e access control lists. 31200590Sluigi */ 32272840Smelifaro 33200601Sluigi#include <sys/param.h> 34272840Smelifaro#include <sys/systm.h> 35272840Smelifaro#include <sys/sysproto.h> 36200838Sluigi#include <sys/kernel.h> 37272840Smelifaro#include <sys/malloc.h> 38272840Smelifaro#include <sys/vnode.h> 39272840Smelifaro#include <sys/lock.h> 40272840Smelifaro#include <sys/mutex.h> 41200590Sluigi#include <sys/namei.h> 42200590Sluigi#include <sys/file.h> 43225518Sjhb#include <sys/proc.h> 44200590Sluigi#include <sys/sysent.h> 45200590Sluigi#include <sys/errno.h> 46200590Sluigi#include <sys/stat.h> 47200590Sluigi#include <sys/acl.h> 48200590Sluigi 49200590SluigiMALLOC_DEFINE(M_ACL, "acl", "access control list"); 50200590Sluigi 51272840Smelifarostatic int vacl_set_acl(struct thread *td, struct vnode *vp, acl_type_t type, 52200590Sluigi struct acl *aclp); 53272840Smelifarostatic int vacl_get_acl(struct thread *td, struct vnode *vp, acl_type_t type, 54240494Sglebius struct acl *aclp); 55200590Sluigistatic int vacl_aclcheck(struct thread *td, struct vnode *vp, 56200590Sluigi acl_type_t type, struct acl *aclp); 57200590Sluigi 58201732Sluigi/* 59200590Sluigi * Implement a version of vaccess() that understands POSIX.1e ACL semantics. 60200590Sluigi * Return 0 on success, else an errno value. Should be merged into 61240494Sglebius * vaccess() eventually. 62272840Smelifaro */ 63240494Sglebiusint 64272840Smelifarovaccess_acl_posix1e(enum vtype type, uid_t file_uid, gid_t file_gid, 65272840Smelifaro struct acl *acl, mode_t acc_mode, struct ucred *cred, int *privused) 66272840Smelifaro{ 67272840Smelifaro struct acl_entry *acl_other, *acl_mask; 68272840Smelifaro mode_t dac_granted; 69272840Smelifaro mode_t cap_granted; 70272840Smelifaro mode_t acl_mask_granted; 71272840Smelifaro int group_matched, i; 72272840Smelifaro 73272840Smelifaro /* 74272840Smelifaro * Look for a normal, non-privileged way to access the file/directory 75272840Smelifaro * as requested. If it exists, go with that. Otherwise, attempt 76272840Smelifaro * to use privileges granted via cap_granted. In some cases, 77272840Smelifaro * which privileges to use may be ambiguous due to "best match", 78272840Smelifaro * in which case fall back on first match for the time being. 79272840Smelifaro */ 80272840Smelifaro if (privused != NULL) 81272840Smelifaro *privused = 0; 82272840Smelifaro 83272840Smelifaro /* 84272840Smelifaro * Determine privileges now, but don't apply until we've found 85272840Smelifaro * a DAC entry that matches but has failed to allow access. 86272840Smelifaro */ 87272840Smelifaro#ifndef CAPABILITIES 88272840Smelifaro if (suser_xxx(cred, NULL, PRISON_ROOT) == 0) 89272840Smelifaro cap_granted = (VEXEC | VREAD | VWRITE | VADMIN); 90272840Smelifaro else 91200590Sluigi cap_granted = 0; 92272840Smelifaro#else 93272840Smelifaro cap_granted = 0; 94272840Smelifaro 95272840Smelifaro if (type == VDIR) { 96272840Smelifaro if ((acc_mode & VEXEC) && !cap_check(cred, NULL, 97272840Smelifaro CAP_DAC_READ_SEARCH, PRISON_ROOT)) 98272840Smelifaro cap_granted |= VEXEC; 99272840Smelifaro } else { 100272840Smelifaro if ((acc_mode & VEXEC) && !cap_check(cred, NULL, 101272840Smelifaro CAP_DAC_EXECUTE, PRISON_ROOT)) 102272840Smelifaro cap_granted |= VEXEC; 103272840Smelifaro } 104272840Smelifaro 105272840Smelifaro if ((acc_mode & VREAD) && !cap_check(cred, NULL, CAP_DAC_READ_SEARCH, 106272840Smelifaro PRISON_ROOT)) 107272840Smelifaro cap_granted |= VREAD; 108272840Smelifaro 109272840Smelifaro if ((acc_mode & VWRITE) && !cap_check(cred, NULL, CAP_DAC_WRITE, 110272840Smelifaro PRISON_ROOT)) 111272840Smelifaro cap_granted |= VWRITE; 112200590Sluigi 113272840Smelifaro if ((acc_mode & VADMIN) && !cap_check(cred, NULL, CAP_FOWNER, 114272840Smelifaro PRISON_ROOT)) 115200590Sluigi cap_granted |= VADMIN; 116272840Smelifaro#endif /* CAPABILITIES */ 117272840Smelifaro 118272840Smelifaro /* 119232865Smelifaro * The owner matches if the effective uid associated with the 120272840Smelifaro * credential matches that of the ACL_USER_OBJ entry. While we're 121272840Smelifaro * doing the first scan, also cache the location of the ACL_MASK 122232865Smelifaro * and ACL_OTHER entries, preventing some future iterations. 123272840Smelifaro */ 124272840Smelifaro acl_mask = acl_other = NULL; 125272840Smelifaro for (i = 0; i < acl->acl_cnt; i++) { 126272840Smelifaro switch (acl->acl_entry[i].ae_tag) { 127272840Smelifaro case ACL_USER_OBJ: 128272840Smelifaro if (file_uid != cred->cr_uid) 129272840Smelifaro break; 130272840Smelifaro dac_granted = 0; 131272840Smelifaro dac_granted |= VADMIN; 132272840Smelifaro if (acl->acl_entry[i].ae_perm & ACL_EXECUTE) 133272840Smelifaro dac_granted |= VEXEC; 134272840Smelifaro if (acl->acl_entry[i].ae_perm & ACL_READ) 135272840Smelifaro dac_granted |= VREAD; 136272840Smelifaro if (acl->acl_entry[i].ae_perm & ACL_WRITE) 137272840Smelifaro dac_granted |= VWRITE; 138272840Smelifaro if ((acc_mode & dac_granted) == acc_mode) 139272840Smelifaro return (0); 140272840Smelifaro if ((acc_mode & (dac_granted | cap_granted)) == 141272840Smelifaro acc_mode) { 142272840Smelifaro if (privused != NULL) 143272840Smelifaro *privused = 1; 144272840Smelifaro return (0); 145272840Smelifaro } 146272840Smelifaro goto error; 147272840Smelifaro 148272840Smelifaro case ACL_MASK: 149272840Smelifaro acl_mask = &acl->acl_entry[i]; 150272840Smelifaro break; 151272840Smelifaro 152272840Smelifaro case ACL_OTHER: 153272840Smelifaro acl_other = &acl->acl_entry[i]; 154272840Smelifaro break; 155272840Smelifaro 156272840Smelifaro default: 157272840Smelifaro } 158272840Smelifaro } 159272840Smelifaro 160272840Smelifaro /* 161272840Smelifaro * An ACL_OTHER entry should always exist in a valid access 162272840Smelifaro * ACL. If it doesn't, then generate a serious failure. For now, 163272840Smelifaro * this means a debugging message and EPERM, but in the future 164272840Smelifaro * should probably be a panic. 165272840Smelifaro */ 166272840Smelifaro if (acl_other == NULL) { 167272840Smelifaro /* 168272840Smelifaro * XXX This should never happen 169272840Smelifaro */ 170272840Smelifaro printf("vaccess_acl_posix1e: ACL_OTHER missing\n"); 171272840Smelifaro return (EPERM); 172272840Smelifaro } 173272840Smelifaro 174272840Smelifaro /* 175272840Smelifaro * Checks against ACL_USER, ACL_GROUP_OBJ, and ACL_GROUP fields 176272840Smelifaro * are masked by an ACL_MASK entry, if any. As such, first identify 177272840Smelifaro * the ACL_MASK field, then iterate through identifying potential 178272840Smelifaro * user matches, then group matches. If there is no ACL_MASK, 179272840Smelifaro * assume that the mask allows all requests to succeed. 180272840Smelifaro */ 181272840Smelifaro if (acl_mask != NULL) { 182272840Smelifaro acl_mask_granted = 0; 183272840Smelifaro if (acl_mask->ae_perm & ACL_EXECUTE) 184272840Smelifaro acl_mask_granted |= VEXEC; 185272840Smelifaro if (acl_mask->ae_perm & ACL_READ) 186201120Sluigi acl_mask_granted |= VREAD; 187272840Smelifaro if (acl_mask->ae_perm & ACL_WRITE) 188272840Smelifaro acl_mask_granted |= VWRITE; 189272840Smelifaro } else 190272840Smelifaro acl_mask_granted = VEXEC | VREAD | VWRITE; 191272840Smelifaro 192272840Smelifaro /* 193201120Sluigi * Iterate through user ACL entries. Do checks twice, first 194272840Smelifaro * without privilege, and then if a match is found but failed, 195272840Smelifaro * a second time with privilege. 196272840Smelifaro */ 197272840Smelifaro 198272840Smelifaro /* 199272840Smelifaro * Check ACL_USER ACL entries. 200272840Smelifaro */ 201272840Smelifaro for (i = 0; i < acl->acl_cnt; i++) { 202272840Smelifaro switch (acl->acl_entry[i].ae_tag) { 203272840Smelifaro case ACL_USER: 204272840Smelifaro if (acl->acl_entry[i].ae_id != cred->cr_uid) 205272840Smelifaro break; 206272840Smelifaro dac_granted = 0; 207272840Smelifaro if (acl->acl_entry[i].ae_perm & ACL_EXECUTE) 208272840Smelifaro dac_granted |= VEXEC; 209272840Smelifaro if (acl->acl_entry[i].ae_perm & ACL_READ) 210272840Smelifaro dac_granted |= VREAD; 211272840Smelifaro if (acl->acl_entry[i].ae_perm & ACL_WRITE) 212272840Smelifaro dac_granted |= VWRITE; 213272840Smelifaro dac_granted &= acl_mask_granted; 214272840Smelifaro if ((acc_mode & dac_granted) == acc_mode) 215272840Smelifaro return (0); 216272840Smelifaro if ((acc_mode & (dac_granted | cap_granted)) != 217272840Smelifaro acc_mode) 218232865Smelifaro goto error; 219272840Smelifaro 220272840Smelifaro if (privused != NULL) 221232865Smelifaro *privused = 1; 222272840Smelifaro return (0); 223272840Smelifaro } 224272840Smelifaro } 225272840Smelifaro 226201120Sluigi /* 227272840Smelifaro * Group match is best-match, not first-match, so find a 228232865Smelifaro * "best" match. Iterate across, testing each potential group 229272840Smelifaro * match. Make sure we keep track of whether we found a match 230272840Smelifaro * or not, so that we know if we should try again with any 231272840Smelifaro * available privilege, or if we should move on to ACL_OTHER. 232272840Smelifaro */ 233272840Smelifaro group_matched = 0; 234272840Smelifaro for (i = 0; i < acl->acl_cnt; i++) { 235272840Smelifaro switch (acl->acl_entry[i].ae_tag) { 236272840Smelifaro case ACL_GROUP_OBJ: 237272840Smelifaro if (!groupmember(file_gid, cred)) 238272840Smelifaro break; 239272840Smelifaro dac_granted = 0; 240272840Smelifaro if (acl->acl_entry[i].ae_perm & ACL_EXECUTE) 241272840Smelifaro dac_granted |= VEXEC; 242272840Smelifaro if (acl->acl_entry[i].ae_perm & ACL_READ) 243272840Smelifaro dac_granted |= VREAD; 244272840Smelifaro if (acl->acl_entry[i].ae_perm & ACL_WRITE) 245232865Smelifaro dac_granted |= VWRITE; 246272840Smelifaro dac_granted &= acl_mask_granted; 247272840Smelifaro 248272840Smelifaro if ((acc_mode & dac_granted) == acc_mode) 249272840Smelifaro return (0); 250272840Smelifaro 251272840Smelifaro group_matched = 1; 252272840Smelifaro break; 253272840Smelifaro 254272840Smelifaro case ACL_GROUP: 255272840Smelifaro if (!groupmember(acl->acl_entry[i].ae_id, cred)) 256272840Smelifaro break; 257272840Smelifaro dac_granted = 0; 258272840Smelifaro if (acl->acl_entry[i].ae_perm & ACL_EXECUTE) 259272840Smelifaro dac_granted |= VEXEC; 260232865Smelifaro if (acl->acl_entry[i].ae_perm & ACL_READ) 261272840Smelifaro dac_granted |= VREAD; 262272840Smelifaro if (acl->acl_entry[i].ae_perm & ACL_WRITE) 263232865Smelifaro dac_granted |= VWRITE; 264272840Smelifaro dac_granted &= acl_mask_granted; 265272840Smelifaro 266272840Smelifaro if ((acc_mode & dac_granted) == acc_mode) 267272840Smelifaro return (0); 268272840Smelifaro 269272840Smelifaro group_matched = 1; 270272840Smelifaro break; 271272840Smelifaro 272272840Smelifaro default: 273232865Smelifaro } 274232865Smelifaro } 275272840Smelifaro 276272840Smelifaro if (group_matched == 1) { 277272840Smelifaro /* 278272840Smelifaro * There was a match, but it did not grant rights via 279272840Smelifaro * pure DAC. Try again, this time with privilege. 280272840Smelifaro */ 281272840Smelifaro for (i = 0; i < acl->acl_cnt; i++) { 282272840Smelifaro switch (acl->acl_entry[i].ae_tag) { 283272840Smelifaro case ACL_GROUP_OBJ: 284272840Smelifaro if (!groupmember(file_gid, cred)) 285272840Smelifaro break; 286272840Smelifaro dac_granted = 0; 287272840Smelifaro if (acl->acl_entry[i].ae_perm & ACL_EXECUTE) 288200590Sluigi dac_granted |= VEXEC; 289272840Smelifaro if (acl->acl_entry[i].ae_perm & ACL_READ) 290272840Smelifaro dac_granted |= VREAD; 291272840Smelifaro if (acl->acl_entry[i].ae_perm & ACL_WRITE) 292272840Smelifaro dac_granted |= VWRITE; 293200590Sluigi dac_granted &= acl_mask_granted; 294272840Smelifaro 295232865Smelifaro if ((acc_mode & (dac_granted | cap_granted)) != 296272840Smelifaro acc_mode) 297272840Smelifaro break; 298272840Smelifaro 299272840Smelifaro if (privused != NULL) 300272840Smelifaro *privused = 1; 301232865Smelifaro return (0); 302272840Smelifaro 303272840Smelifaro case ACL_GROUP: 304272840Smelifaro if (!groupmember(acl->acl_entry[i].ae_id, 305272840Smelifaro cred)) 306272840Smelifaro break; 307272840Smelifaro dac_granted = 0; 308272840Smelifaro if (acl->acl_entry[i].ae_perm & ACL_EXECUTE) 309272840Smelifaro dac_granted |= VEXEC; 310272840Smelifaro if (acl->acl_entry[i].ae_perm & ACL_READ) 311272840Smelifaro dac_granted |= VREAD; 312272840Smelifaro if (acl->acl_entry[i].ae_perm & ACL_WRITE) 313272840Smelifaro dac_granted |= VWRITE; 314272840Smelifaro dac_granted &= acl_mask_granted; 315272840Smelifaro 316272840Smelifaro if ((acc_mode & (dac_granted | cap_granted)) != 317272840Smelifaro acc_mode) 318272840Smelifaro break; 319272840Smelifaro 320272840Smelifaro if (privused != NULL) 321272840Smelifaro *privused = 1; 322272840Smelifaro return (0); 323272840Smelifaro 324272840Smelifaro default: 325272840Smelifaro } 326272840Smelifaro } 327272840Smelifaro /* 328272840Smelifaro * Even with privilege, group membership was not sufficient. 329272840Smelifaro * Return failure. 330272840Smelifaro */ 331272840Smelifaro goto error; 332272840Smelifaro } 333272840Smelifaro 334272840Smelifaro /* 335272840Smelifaro * Fall back on ACL_OTHER. ACL_MASK is not applied to ACL_OTHER. 336272840Smelifaro */ 337272840Smelifaro dac_granted = 0; 338272840Smelifaro if (acl_other->ae_perm & ACL_EXECUTE) 339272840Smelifaro dac_granted |= VEXEC; 340272840Smelifaro if (acl_other->ae_perm & ACL_READ) 341272840Smelifaro dac_granted |= VREAD; 342272840Smelifaro if (acl_other->ae_perm & ACL_WRITE) 343272840Smelifaro dac_granted |= VWRITE; 344272840Smelifaro 345272840Smelifaro if ((acc_mode & dac_granted) == acc_mode) 346272840Smelifaro return (0); 347272840Smelifaro if ((acc_mode & (dac_granted | cap_granted)) == acc_mode) { 348272840Smelifaro if (privused != NULL) 349272840Smelifaro *privused = 1; 350272840Smelifaro return (0); 351272840Smelifaro } 352272840Smelifaro 353272840Smelifaroerror: 354272840Smelifaro return ((acc_mode & VADMIN) ? EPERM : EACCES); 355272840Smelifaro} 356272840Smelifaro 357272840Smelifaro/* 358272840Smelifaro * For the purposes of file systems maintaining the _OBJ entries in an 359272840Smelifaro * inode with a mode_t field, this routine converts a mode_t entry 360272840Smelifaro * to an acl_perm_t. 361272840Smelifaro */ 362272840Smelifaroacl_perm_t 363272840Smelifaroacl_posix1e_mode_to_perm(acl_tag_t tag, mode_t mode) 364272840Smelifaro{ 365272840Smelifaro acl_perm_t perm = 0; 366272840Smelifaro 367272840Smelifaro switch(tag) { 368272840Smelifaro case ACL_USER_OBJ: 369272840Smelifaro if (mode & S_IXUSR) 370272840Smelifaro perm |= ACL_EXECUTE; 371272840Smelifaro if (mode & S_IRUSR) 372272840Smelifaro perm |= ACL_READ; 373272840Smelifaro if (mode & S_IWUSR) 374272840Smelifaro perm |= ACL_WRITE; 375272840Smelifaro return (perm); 376272840Smelifaro 377232865Smelifaro case ACL_GROUP_OBJ: 378272840Smelifaro if (mode & S_IXGRP) 379272840Smelifaro perm |= ACL_EXECUTE; 380272840Smelifaro if (mode & S_IRGRP) 381272840Smelifaro perm |= ACL_READ; 382272840Smelifaro if (mode & S_IWGRP) 383272840Smelifaro perm |= ACL_WRITE; 384272840Smelifaro return (perm); 385272840Smelifaro 386272840Smelifaro case ACL_OTHER: 387272840Smelifaro if (mode & S_IXOTH) 388272840Smelifaro perm |= ACL_EXECUTE; 389272840Smelifaro if (mode & S_IROTH) 390272840Smelifaro perm |= ACL_READ; 391272840Smelifaro if (mode & S_IWOTH) 392272840Smelifaro perm |= ACL_WRITE; 393272840Smelifaro return (perm); 394272840Smelifaro 395272840Smelifaro default: 396272840Smelifaro printf("acl_posix1e_mode_to_perm: invalid tag (%d)\n", tag); 397272840Smelifaro return (0); 398272840Smelifaro } 399272840Smelifaro} 400272840Smelifaro 401272840Smelifaro/* 402272840Smelifaro * Given inode information (uid, gid, mode), return an acl entry of the 403272840Smelifaro * appropriate type. 404272840Smelifaro */ 405272840Smelifarostruct acl_entry 406272840Smelifaroacl_posix1e_mode_to_entry(acl_tag_t tag, uid_t uid, gid_t gid, mode_t mode) 407272840Smelifaro{ 408272840Smelifaro struct acl_entry acl_entry; 409272840Smelifaro 410272840Smelifaro acl_entry.ae_tag = tag; 411272840Smelifaro acl_entry.ae_perm = acl_posix1e_mode_to_perm(tag, mode); 412272840Smelifaro switch(tag) { 413272840Smelifaro case ACL_USER_OBJ: 414272840Smelifaro acl_entry.ae_id = uid; 415272840Smelifaro break; 416272840Smelifaro 417272840Smelifaro case ACL_GROUP_OBJ: 418272840Smelifaro acl_entry.ae_id = gid; 419272840Smelifaro break; 420272840Smelifaro 421272840Smelifaro case ACL_OTHER: 422272840Smelifaro acl_entry.ae_id = ACL_UNDEFINED_ID; 423272840Smelifaro break; 424272840Smelifaro 425272840Smelifaro default: 426272840Smelifaro acl_entry.ae_id = ACL_UNDEFINED_ID; 427272840Smelifaro printf("acl_posix1e_mode_to_entry: invalid tag (%d)\n", tag); 428272840Smelifaro } 429272840Smelifaro 430272840Smelifaro return (acl_entry); 431272840Smelifaro} 432272840Smelifaro 433272840Smelifaro/* 434272840Smelifaro * Utility function to generate a file mode given appropriate ACL entries. 435272840Smelifaro */ 436272840Smelifaromode_t 437272840Smelifaroacl_posix1e_perms_to_mode(struct acl_entry *acl_user_obj_entry, 438272840Smelifaro struct acl_entry *acl_group_obj_entry, struct acl_entry *acl_other_entry) 439272840Smelifaro{ 440272840Smelifaro mode_t mode; 441272840Smelifaro 442272840Smelifaro mode = 0; 443272840Smelifaro if (acl_user_obj_entry->ae_perm & ACL_EXECUTE) 444272840Smelifaro mode |= S_IXUSR; 445272840Smelifaro if (acl_user_obj_entry->ae_perm & ACL_READ) 446272840Smelifaro mode |= S_IRUSR; 447272840Smelifaro if (acl_user_obj_entry->ae_perm & ACL_WRITE) 448272840Smelifaro mode |= S_IWUSR; 449272840Smelifaro if (acl_group_obj_entry->ae_perm & ACL_EXECUTE) 450272840Smelifaro mode |= S_IXGRP; 451272840Smelifaro if (acl_group_obj_entry->ae_perm & ACL_READ) 452272840Smelifaro mode |= S_IRGRP; 453272840Smelifaro if (acl_group_obj_entry->ae_perm & ACL_WRITE) 454272840Smelifaro mode |= S_IWGRP; 455272840Smelifaro if (acl_other_entry->ae_perm & ACL_EXECUTE) 456272840Smelifaro mode |= S_IXOTH; 457272840Smelifaro if (acl_other_entry->ae_perm & ACL_READ) 458272840Smelifaro mode |= S_IROTH; 459272840Smelifaro if (acl_other_entry->ae_perm & ACL_WRITE) 460272840Smelifaro mode |= S_IWOTH; 461272840Smelifaro 462272840Smelifaro return (mode); 463272840Smelifaro} 464272840Smelifaro 465272840Smelifaro/* 466272840Smelifaro * Perform a syntactic check of the ACL, sufficient to allow an 467272840Smelifaro * implementing file system to determine if it should accept this and 468272840Smelifaro * rely on the POSIX.1e ACL properties. 469272840Smelifaro */ 470272840Smelifaroint 471272840Smelifaroacl_posix1e_check(struct acl *acl) 472272840Smelifaro{ 473272840Smelifaro int num_acl_user_obj, num_acl_user, num_acl_group_obj, num_acl_group; 474272840Smelifaro int num_acl_mask, num_acl_other, i; 475272840Smelifaro 476272840Smelifaro /* 477272840Smelifaro * Verify that the number of entries does not exceed the maximum 478272840Smelifaro * defined for acl_t. 479272840Smelifaro * Verify that the correct number of various sorts of ae_tags are 480272840Smelifaro * present: 481272840Smelifaro * Exactly one ACL_USER_OBJ 482272840Smelifaro * Exactly one ACL_GROUP_OBJ 483272840Smelifaro * Exactly one ACL_OTHER 484272840Smelifaro * If any ACL_USER or ACL_GROUP entries appear, then exactly one 485272840Smelifaro * ACL_MASK entry must also appear. 486272840Smelifaro * Verify that all ae_perm entries are in ACL_PERM_BITS. 487272840Smelifaro * Verify all ae_tag entries are understood by this implementation. 488272840Smelifaro * Note: Does not check for uniqueness of qualifier (ae_id) field. 489272840Smelifaro */ 490272840Smelifaro num_acl_user_obj = num_acl_user = num_acl_group_obj = num_acl_group = 491272840Smelifaro num_acl_mask = num_acl_other = 0; 492272840Smelifaro if (acl->acl_cnt > ACL_MAX_ENTRIES || acl->acl_cnt < 0) 493272840Smelifaro return (EINVAL); 494272840Smelifaro for (i = 0; i < acl->acl_cnt; i++) { 495272840Smelifaro /* 496272840Smelifaro * Check for a valid tag. 497272840Smelifaro */ 498272840Smelifaro switch(acl->acl_entry[i].ae_tag) { 499272840Smelifaro case ACL_USER_OBJ: 500272840Smelifaro acl->acl_entry[i].ae_id = ACL_UNDEFINED_ID; /* XXX */ 501272840Smelifaro if (acl->acl_entry[i].ae_id != ACL_UNDEFINED_ID) 502272840Smelifaro return (EINVAL); 503272840Smelifaro num_acl_user_obj++; 504272840Smelifaro break; 505272840Smelifaro case ACL_GROUP_OBJ: 506272840Smelifaro acl->acl_entry[i].ae_id = ACL_UNDEFINED_ID; /* XXX */ 507272840Smelifaro if (acl->acl_entry[i].ae_id != ACL_UNDEFINED_ID) 508272840Smelifaro return (EINVAL); 509272840Smelifaro num_acl_group_obj++; 510272840Smelifaro break; 511272840Smelifaro case ACL_USER: 512272840Smelifaro if (acl->acl_entry[i].ae_id == ACL_UNDEFINED_ID) 513272840Smelifaro return (EINVAL); 514272840Smelifaro num_acl_user++; 515272840Smelifaro break; 516272840Smelifaro case ACL_GROUP: 517272840Smelifaro if (acl->acl_entry[i].ae_id == ACL_UNDEFINED_ID) 518272840Smelifaro return (EINVAL); 519272840Smelifaro num_acl_group++; 520272840Smelifaro break; 521272840Smelifaro case ACL_OTHER: 522272840Smelifaro acl->acl_entry[i].ae_id = ACL_UNDEFINED_ID; /* XXX */ 523272840Smelifaro if (acl->acl_entry[i].ae_id != ACL_UNDEFINED_ID) 524272840Smelifaro return (EINVAL); 525272840Smelifaro num_acl_other++; 526272840Smelifaro break; 527272840Smelifaro case ACL_MASK: 528272840Smelifaro acl->acl_entry[i].ae_id = ACL_UNDEFINED_ID; /* XXX */ 529272840Smelifaro if (acl->acl_entry[i].ae_id != ACL_UNDEFINED_ID) 530272840Smelifaro return (EINVAL); 531272840Smelifaro num_acl_mask++; 532272840Smelifaro break; 533272840Smelifaro default: 534272840Smelifaro return (EINVAL); 535272840Smelifaro } 536272840Smelifaro /* 537272840Smelifaro * Check for valid perm entries. 538272840Smelifaro */ 539272840Smelifaro if ((acl->acl_entry[i].ae_perm | ACL_PERM_BITS) != 540272840Smelifaro ACL_PERM_BITS) 541272840Smelifaro return (EINVAL); 542272840Smelifaro } 543272840Smelifaro if ((num_acl_user_obj != 1) || (num_acl_group_obj != 1) || 544272840Smelifaro (num_acl_other != 1) || (num_acl_mask != 0 && num_acl_mask != 1)) 545272840Smelifaro return (EINVAL); 546272840Smelifaro if (((num_acl_group != 0) || (num_acl_user != 0)) && 547272840Smelifaro (num_acl_mask != 1)) 548272840Smelifaro return (EINVAL); 549272840Smelifaro return (0); 550272840Smelifaro} 551272840Smelifaro 552272840Smelifaro/* 553272840Smelifaro * These calls wrap the real vnode operations, and are called by the 554272840Smelifaro * syscall code once the syscall has converted the path or file 555272840Smelifaro * descriptor to a vnode (unlocked). The aclp pointer is assumed 556272840Smelifaro * still to point to userland, so this should not be consumed within 557272840Smelifaro * the kernel except by syscall code. Other code should directly 558272840Smelifaro * invoke VOP_{SET,GET}ACL. 559272840Smelifaro */ 560272840Smelifaro 561272840Smelifaro/* 562272840Smelifaro * Given a vnode, set its ACL. 563272840Smelifaro */ 564272840Smelifarostatic int 565272840Smelifarovacl_set_acl(struct thread *td, struct vnode *vp, acl_type_t type, 566272840Smelifaro struct acl *aclp) 567272840Smelifaro{ 568272840Smelifaro struct acl inkernacl; 569272840Smelifaro int error; 570272840Smelifaro 571272840Smelifaro error = copyin(aclp, &inkernacl, sizeof(struct acl)); 572272840Smelifaro if (error) 573272840Smelifaro return(error); 574272840Smelifaro VOP_LEASE(vp, td, td->td_proc->p_ucred, LEASE_WRITE); 575272840Smelifaro vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); 576272840Smelifaro error = VOP_SETACL(vp, type, &inkernacl, td->td_proc->p_ucred, td); 577272840Smelifaro VOP_UNLOCK(vp, 0, td); 578272840Smelifaro return(error); 579272840Smelifaro} 580272840Smelifaro 581272840Smelifaro/* 582272840Smelifaro * Given a vnode, get its ACL. 583272840Smelifaro */ 584272840Smelifarostatic int 585272840Smelifarovacl_get_acl(struct thread *td, struct vnode *vp, acl_type_t type, 586272840Smelifaro struct acl *aclp) 587272840Smelifaro{ 588272840Smelifaro struct acl inkernelacl; 589272840Smelifaro int error; 590272840Smelifaro 591272840Smelifaro VOP_LEASE(vp, td, td->td_proc->p_ucred, LEASE_WRITE); 592272840Smelifaro vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); 593272840Smelifaro error = VOP_GETACL(vp, type, &inkernelacl, td->td_proc->p_ucred, td); 594272840Smelifaro VOP_UNLOCK(vp, 0, td); 595272840Smelifaro if (error == 0) 596272840Smelifaro error = copyout(&inkernelacl, aclp, sizeof(struct acl)); 597272840Smelifaro return (error); 598272840Smelifaro} 599272840Smelifaro 600272840Smelifaro/* 601272840Smelifaro * Given a vnode, delete its ACL. 602272840Smelifaro */ 603272840Smelifarostatic int 604272840Smelifarovacl_delete(struct thread *td, struct vnode *vp, acl_type_t type) 605272840Smelifaro{ 606272840Smelifaro int error; 607272840Smelifaro 608272840Smelifaro VOP_LEASE(vp, td, td->td_proc->p_ucred, LEASE_WRITE); 609272840Smelifaro vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); 610272840Smelifaro error = VOP_SETACL(vp, ACL_TYPE_DEFAULT, 0, td->td_proc->p_ucred, 611272840Smelifaro td); 612272840Smelifaro VOP_UNLOCK(vp, 0, td); 613272840Smelifaro return (error); 614272840Smelifaro} 615272840Smelifaro 616272840Smelifaro/* 617272840Smelifaro * Given a vnode, check whether an ACL is appropriate for it 618272840Smelifaro */ 619272840Smelifarostatic int 620272840Smelifarovacl_aclcheck(struct thread *td, struct vnode *vp, acl_type_t type, 621272840Smelifaro struct acl *aclp) 622272840Smelifaro{ 623272840Smelifaro struct acl inkernelacl; 624272840Smelifaro int error; 625272840Smelifaro 626272840Smelifaro error = copyin(aclp, &inkernelacl, sizeof(struct acl)); 627272840Smelifaro if (error) 628272840Smelifaro return(error); 629272840Smelifaro error = VOP_ACLCHECK(vp, type, &inkernelacl, td->td_proc->p_ucred, 630272840Smelifaro td); 631272840Smelifaro return (error); 632272840Smelifaro} 633272840Smelifaro 634272840Smelifaro/* 635272840Smelifaro * syscalls -- convert the path/fd to a vnode, and call vacl_whatever. 636272840Smelifaro * Don't need to lock, as the vacl_ code will get/release any locks 637272840Smelifaro * required. 638272840Smelifaro */ 639272840Smelifaro 640272840Smelifaro/* 641272840Smelifaro * Given a file path, get an ACL for it 642272840Smelifaro * 643272840Smelifaro * MPSAFE 644272840Smelifaro */ 645272840Smelifaroint 646272840Smelifaro__acl_get_file(struct thread *td, struct __acl_get_file_args *uap) 647272840Smelifaro{ 648272840Smelifaro struct nameidata nd; 649272840Smelifaro int error; 650272840Smelifaro 651272840Smelifaro mtx_lock(&Giant); 652272840Smelifaro NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), td); 653272840Smelifaro error = namei(&nd); 654272840Smelifaro if (error == 0) { 655272840Smelifaro error = vacl_get_acl(td, nd.ni_vp, SCARG(uap, type), 656272840Smelifaro SCARG(uap, aclp)); 657272840Smelifaro NDFREE(&nd, 0); 658272840Smelifaro } 659272840Smelifaro mtx_unlock(&Giant); 660272840Smelifaro return (error); 661272840Smelifaro} 662272840Smelifaro 663272840Smelifaro/* 664272840Smelifaro * Given a file path, set an ACL for it 665272840Smelifaro * 666272840Smelifaro * MPSAFE 667272840Smelifaro */ 668272840Smelifaroint 669272840Smelifaro__acl_set_file(struct thread *td, struct __acl_set_file_args *uap) 670272840Smelifaro{ 671272840Smelifaro struct nameidata nd; 672272840Smelifaro int error; 673272840Smelifaro 674272840Smelifaro mtx_lock(&Giant); 675272840Smelifaro NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), td); 676272840Smelifaro error = namei(&nd); 677272840Smelifaro if (error == 0) { 678232865Smelifaro error = vacl_set_acl(td, nd.ni_vp, SCARG(uap, type), 679272840Smelifaro SCARG(uap, aclp)); 680272840Smelifaro NDFREE(&nd, 0); 681272840Smelifaro } 682272840Smelifaro mtx_unlock(&Giant); 683272840Smelifaro return (error); 684272840Smelifaro} 685272840Smelifaro 686272840Smelifaro/* 687272840Smelifaro * Given a file descriptor, get an ACL for it 688272840Smelifaro * 689272840Smelifaro * MPSAFE 690272840Smelifaro */ 691272840Smelifaroint 692272840Smelifaro__acl_get_fd(struct thread *td, struct __acl_get_fd_args *uap) 693272840Smelifaro{ 694272840Smelifaro struct file *fp; 695272840Smelifaro int error; 696232865Smelifaro 697272840Smelifaro mtx_lock(&Giant); 698272840Smelifaro error = getvnode(td->td_proc->p_fd, SCARG(uap, filedes), &fp); 699232865Smelifaro if (error == 0) { 700272840Smelifaro error = vacl_get_acl(td, (struct vnode *)fp->f_data, 701272840Smelifaro SCARG(uap, type), SCARG(uap, aclp)); 702272840Smelifaro } 703272840Smelifaro mtx_unlock(&Giant); 704272840Smelifaro return (error); 705272840Smelifaro} 706272840Smelifaro 707272840Smelifaro/* 708272840Smelifaro * Given a file descriptor, set an ACL for it 709272840Smelifaro * 710272840Smelifaro * MPSAFE 711272840Smelifaro */ 712272840Smelifaroint 713272840Smelifaro__acl_set_fd(struct thread *td, struct __acl_set_fd_args *uap) 714272840Smelifaro{ 715272840Smelifaro struct file *fp; 716272840Smelifaro int error; 717232865Smelifaro 718272840Smelifaro mtx_lock(&Giant); 719272840Smelifaro error = getvnode(td->td_proc->p_fd, SCARG(uap, filedes), &fp); 720272840Smelifaro if (error == 0) { 721272840Smelifaro error = vacl_set_acl(td, (struct vnode *)fp->f_data, 722272840Smelifaro SCARG(uap, type), SCARG(uap, aclp)); 723272840Smelifaro } 724272840Smelifaro mtx_unlock(&Giant); 725272840Smelifaro return (error); 726272840Smelifaro} 727272840Smelifaro 728272840Smelifaro/* 729232865Smelifaro * Given a file path, delete an ACL from it. 730272840Smelifaro * 731272840Smelifaro * MPSAFE 732272840Smelifaro */ 733272840Smelifaroint 734272840Smelifaro__acl_delete_file(struct thread *td, struct __acl_delete_file_args *uap) 735272840Smelifaro{ 736272840Smelifaro struct nameidata nd; 737272840Smelifaro int error; 738272840Smelifaro 739272840Smelifaro mtx_lock(&Giant); 740272840Smelifaro NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), td); 741272840Smelifaro error = namei(&nd); 742272840Smelifaro if (error == 0) { 743272840Smelifaro error = vacl_delete(td, nd.ni_vp, SCARG(uap, type)); 744272840Smelifaro NDFREE(&nd, 0); 745272840Smelifaro } 746272840Smelifaro mtx_unlock(&Giant); 747272840Smelifaro return (error); 748272840Smelifaro} 749272840Smelifaro 750232865Smelifaro/* 751232865Smelifaro * Given a file path, delete an ACL from it. 752272840Smelifaro * 753272840Smelifaro * MPSAFE 754272840Smelifaro */ 755272840Smelifaroint 756200590Sluigi__acl_delete_fd(struct thread *td, struct __acl_delete_fd_args *uap) 757272840Smelifaro{ 758272840Smelifaro struct file *fp; 759272840Smelifaro int error; 760272840Smelifaro 761272840Smelifaro mtx_lock(&Giant); 762272840Smelifaro error = getvnode(td->td_proc->p_fd, SCARG(uap, filedes), &fp); 763272840Smelifaro if (error == 0) { 764272840Smelifaro error = vacl_delete(td, (struct vnode *)fp->f_data, 765272840Smelifaro SCARG(uap, type)); 766272840Smelifaro } 767272840Smelifaro mtx_unlock(&Giant); 768272840Smelifaro return (error); 769272840Smelifaro} 770272840Smelifaro 771232865Smelifaro/* 772272840Smelifaro * Given a file path, check an ACL for it 773272840Smelifaro * 774272840Smelifaro * MPSAFE 775272840Smelifaro */ 776272840Smelifaroint 777272840Smelifaro__acl_aclcheck_file(struct thread *td, struct __acl_aclcheck_file_args *uap) 778232865Smelifaro{ 779232865Smelifaro struct nameidata nd; 780272840Smelifaro int error; 781272840Smelifaro 782272840Smelifaro mtx_lock(&Giant); 783272840Smelifaro NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), td); 784272840Smelifaro error = namei(&nd); 785272840Smelifaro if (error == 0) { 786272840Smelifaro error = vacl_aclcheck(td, nd.ni_vp, SCARG(uap, type), 787272840Smelifaro SCARG(uap, aclp)); 788272840Smelifaro NDFREE(&nd, 0); 789272840Smelifaro } 790272840Smelifaro mtx_unlock(&Giant); 791272840Smelifaro return (error); 792272840Smelifaro} 793272840Smelifaro 794272840Smelifaro/* 795272840Smelifaro * Given a file descriptor, check an ACL for it 796272840Smelifaro * 797272840Smelifaro * MPSAFE 798272840Smelifaro */ 799272840Smelifaroint 800272840Smelifaro__acl_aclcheck_fd(struct thread *td, struct __acl_aclcheck_fd_args *uap) 801272840Smelifaro{ 802272840Smelifaro struct file *fp; 803272840Smelifaro int error; 804272840Smelifaro 805272840Smelifaro mtx_lock(&Giant); 806272840Smelifaro error = getvnode(td->td_proc->p_fd, SCARG(uap, filedes), &fp); 807272840Smelifaro if (error == 0) { 808272840Smelifaro error = vacl_aclcheck(td, (struct vnode *)fp->f_data, 809272840Smelifaro SCARG(uap, type), SCARG(uap, aclp)); 810272840Smelifaro } 811272840Smelifaro mtx_unlock(&Giant); 812272840Smelifaro return (error); 813272840Smelifaro} 814272840Smelifaro