174822Srwatson/*- 2165890Srwatson * Copyright (c) 1999-2003 Robert N. M. Watson 374822Srwatson * All rights reserved. 474822Srwatson * 585845Srwatson * This software was developed by Robert Watson for the TrustedBSD Project. 685845Srwatson * 774822Srwatson * Redistribution and use in source and binary forms, with or without 874822Srwatson * modification, are permitted provided that the following conditions 974822Srwatson * are met: 1074822Srwatson * 1. Redistributions of source code must retain the above copyright 1174822Srwatson * notice, this list of conditions and the following disclaimer. 1274822Srwatson * 2. Redistributions in binary form must reproduce the above copyright 1374822Srwatson * notice, this list of conditions and the following disclaimer in the 1474822Srwatson * documentation and/or other materials provided with the distribution. 1574822Srwatson * 1674822Srwatson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1774822Srwatson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1874822Srwatson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1974822Srwatson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2074822Srwatson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2174822Srwatson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2274822Srwatson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2374822Srwatson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2474822Srwatson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2574822Srwatson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2674822Srwatson * SUCH DAMAGE. 2774822Srwatson */ 28116192Sobrien 2974822Srwatson/* 3074822Srwatson * Support for POSIX.1e access control lists: UFS-specific support functions. 3174822Srwatson */ 3274822Srwatson 33116192Sobrien#include <sys/cdefs.h> 34116192Sobrien__FBSDID("$FreeBSD: stable/11/sys/ufs/ufs/ufs_acl.c 306553 2016-10-01 09:19:43Z kib $"); 35116192Sobrien 3674822Srwatson#include "opt_ufs.h" 3774822Srwatson#include "opt_quota.h" 3874822Srwatson 3974822Srwatson#include <sys/param.h> 4074822Srwatson#include <sys/systm.h> 4174822Srwatson#include <sys/stat.h> 4274822Srwatson#include <sys/mount.h> 4374822Srwatson#include <sys/vnode.h> 4474822Srwatson#include <sys/types.h> 4574822Srwatson#include <sys/acl.h> 4674822Srwatson#include <sys/event.h> 4774822Srwatson#include <sys/extattr.h> 48274906Sglebius#include <sys/proc.h> 4974822Srwatson 5074822Srwatson#include <ufs/ufs/quota.h> 5174822Srwatson#include <ufs/ufs/inode.h> 5274822Srwatson#include <ufs/ufs/acl.h> 5374822Srwatson#include <ufs/ufs/extattr.h> 5474822Srwatson#include <ufs/ufs/dir.h> 5574822Srwatson#include <ufs/ufs/ufsmount.h> 5674822Srwatson#include <ufs/ufs/ufs_extern.h> 57184629Strasz#include <ufs/ffs/fs.h> 5874822Srwatson 5974822Srwatson#ifdef UFS_ACL 6074822Srwatson 61218485SnetchildFEATURE(ufs_acl, "ACL support for UFS"); 62218485Snetchild 6374822Srwatson/* 6474822Srwatson * Synchronize an ACL and an inode by copying over appropriate inode fields 6574822Srwatson * to the passed ACL. Assumes an ACL that would satisfy acl_posix1e_check(), 6674822Srwatson * and may panic if not. 6774822Srwatson */ 6874822Srwatsonvoid 6974822Srwatsonufs_sync_acl_from_inode(struct inode *ip, struct acl *acl) 7074822Srwatson{ 7174822Srwatson struct acl_entry *acl_mask, *acl_group_obj; 7274822Srwatson int i; 7374822Srwatson 7474822Srwatson /* 7574822Srwatson * Update ACL_USER_OBJ, ACL_OTHER, but simply identify ACL_MASK 7674822Srwatson * and ACL_GROUP_OBJ for use after we know whether ACL_MASK is 7774822Srwatson * present. 7874822Srwatson */ 7974822Srwatson acl_mask = NULL; 8074822Srwatson acl_group_obj = NULL; 8174822Srwatson for (i = 0; i < acl->acl_cnt; i++) { 8274822Srwatson switch (acl->acl_entry[i].ae_tag) { 8374822Srwatson case ACL_USER_OBJ: 8474822Srwatson acl->acl_entry[i].ae_perm = acl_posix1e_mode_to_perm( 8574822Srwatson ACL_USER_OBJ, ip->i_mode); 8675571Srwatson acl->acl_entry[i].ae_id = ACL_UNDEFINED_ID; 8774822Srwatson break; 8874822Srwatson 8974822Srwatson case ACL_GROUP_OBJ: 9074822Srwatson acl_group_obj = &acl->acl_entry[i]; 9175571Srwatson acl->acl_entry[i].ae_id = ACL_UNDEFINED_ID; 9274822Srwatson break; 9374822Srwatson 9474822Srwatson case ACL_OTHER: 9574822Srwatson acl->acl_entry[i].ae_perm = acl_posix1e_mode_to_perm( 9674822Srwatson ACL_OTHER, ip->i_mode); 9775571Srwatson acl->acl_entry[i].ae_id = ACL_UNDEFINED_ID; 9874822Srwatson break; 9974822Srwatson 10074822Srwatson case ACL_MASK: 10174822Srwatson acl_mask = &acl->acl_entry[i]; 10275571Srwatson acl->acl_entry[i].ae_id = ACL_UNDEFINED_ID; 10374822Srwatson break; 10474822Srwatson 10574822Srwatson case ACL_USER: 10674822Srwatson case ACL_GROUP: 10774822Srwatson break; 10874822Srwatson 10974822Srwatson default: 11074822Srwatson panic("ufs_sync_acl_from_inode(): bad ae_tag"); 11174822Srwatson } 11274822Srwatson } 11374822Srwatson 11474822Srwatson if (acl_group_obj == NULL) 11574822Srwatson panic("ufs_sync_acl_from_inode(): no ACL_GROUP_OBJ"); 11674822Srwatson 11774822Srwatson if (acl_mask == NULL) { 11874822Srwatson /* 11974822Srwatson * There is no ACL_MASK, so update ACL_GROUP_OBJ. 12074822Srwatson */ 12174822Srwatson acl_group_obj->ae_perm = acl_posix1e_mode_to_perm( 12274822Srwatson ACL_GROUP_OBJ, ip->i_mode); 12374822Srwatson } else { 12474822Srwatson /* 12574822Srwatson * Update the ACL_MASK entry instead of ACL_GROUP_OBJ. 12674822Srwatson */ 12774822Srwatson acl_mask->ae_perm = acl_posix1e_mode_to_perm(ACL_GROUP_OBJ, 12874822Srwatson ip->i_mode); 12974822Srwatson } 13074822Srwatson} 13174822Srwatson 13274822Srwatson/* 133118411Srwatson * Calculate what the inode mode should look like based on an authoritative 134118411Srwatson * ACL for the inode. Replace only the fields in the inode that the ACL 135118411Srwatson * can represent. 13674822Srwatson */ 13774822Srwatsonvoid 138118411Srwatsonufs_sync_inode_from_acl(struct acl *acl, struct inode *ip) 13974822Srwatson{ 14074822Srwatson 141118411Srwatson ip->i_mode &= ACL_PRESERVE_MASK; 142118411Srwatson ip->i_mode |= acl_posix1e_acl_to_mode(acl); 143132775Skan DIP_SET(ip, i_mode, ip->i_mode); 14474822Srwatson} 14574822Srwatson 14674822Srwatson/* 147200796Strasz * Retrieve NFSv4 ACL, skipping access checks. Must be used in UFS code 148200796Strasz * instead of VOP_GETACL() when we don't want to be restricted by the user 149200796Strasz * not having ACL_READ_ACL permission, e.g. when calculating inherited ACL 150200796Strasz * or in ufs_vnops.c:ufs_accessx(). 151200796Strasz */ 152200796Straszint 153200796Straszufs_getacl_nfs4_internal(struct vnode *vp, struct acl *aclp, struct thread *td) 154200796Strasz{ 155200796Strasz int error, len; 156200796Strasz struct inode *ip = VTOI(vp); 157200796Strasz 158200796Strasz len = sizeof(*aclp); 159200796Strasz bzero(aclp, len); 160200796Strasz 161200796Strasz error = vn_extattr_get(vp, IO_NODELOCKED, 162200796Strasz NFS4_ACL_EXTATTR_NAMESPACE, NFS4_ACL_EXTATTR_NAME, 163200796Strasz &len, (char *) aclp, td); 164200796Strasz aclp->acl_maxcnt = ACL_MAX_ENTRIES; 165200796Strasz if (error == ENOATTR) { 166200796Strasz /* 167200796Strasz * Legitimately no ACL set on object, purely 168200796Strasz * emulate it through the inode. 169200796Strasz */ 170200796Strasz acl_nfs4_sync_acl_from_mode(aclp, ip->i_mode, ip->i_uid); 171200796Strasz 172200796Strasz return (0); 173200796Strasz } 174200796Strasz 175200796Strasz if (error) 176200796Strasz return (error); 177200796Strasz 178200796Strasz if (len != sizeof(*aclp)) { 179200796Strasz /* 180200796Strasz * A short (or long) read, meaning that for 181200796Strasz * some reason the ACL is corrupted. Return 182200796Strasz * EPERM since the object DAC protections 183200796Strasz * are unsafe. 184200796Strasz */ 185200796Strasz printf("ufs_getacl_nfs4(): Loaded invalid ACL (" 186241011Smdf "%d bytes), inumber %ju on %s\n", len, 187306553Skib (uintmax_t)ip->i_number, ITOFS(ip)->fs_fsmnt); 188200796Strasz 189200796Strasz return (EPERM); 190200796Strasz } 191200796Strasz 192200796Strasz error = acl_nfs4_check(aclp, vp->v_type == VDIR); 193200796Strasz if (error) { 194200796Strasz printf("ufs_getacl_nfs4(): Loaded invalid ACL " 195241011Smdf "(failed acl_nfs4_check), inumber %ju on %s\n", 196306553Skib (uintmax_t)ip->i_number, ITOFS(ip)->fs_fsmnt); 197200796Strasz 198200796Strasz return (EPERM); 199200796Strasz } 200200796Strasz 201200796Strasz return (0); 202200796Strasz} 203200796Strasz 204200796Straszstatic int 205200796Straszufs_getacl_nfs4(struct vop_getacl_args *ap) 206200796Strasz{ 207200796Strasz int error; 208200796Strasz 209200796Strasz if ((ap->a_vp->v_mount->mnt_flag & MNT_NFS4ACLS) == 0) 210200796Strasz return (EINVAL); 211200796Strasz 212200796Strasz error = VOP_ACCESSX(ap->a_vp, VREAD_ACL, ap->a_td->td_ucred, ap->a_td); 213200796Strasz if (error) 214200796Strasz return (error); 215200796Strasz 216200796Strasz error = ufs_getacl_nfs4_internal(ap->a_vp, ap->a_aclp, ap->a_td); 217200796Strasz 218200796Strasz return (error); 219200796Strasz} 220200796Strasz 221200796Strasz/* 222192586Strasz * Read POSIX.1e ACL from an EA. Return error if its not found 223298804Spfg * or if any other error has occurred. 224192586Strasz */ 225192586Straszstatic int 226192586Straszufs_get_oldacl(acl_type_t type, struct oldacl *old, struct vnode *vp, 227192586Strasz struct thread *td) 228192586Strasz{ 229192586Strasz int error, len; 230192586Strasz struct inode *ip = VTOI(vp); 231192586Strasz 232192586Strasz len = sizeof(*old); 233192586Strasz 234192586Strasz switch (type) { 235192586Strasz case ACL_TYPE_ACCESS: 236192586Strasz error = vn_extattr_get(vp, IO_NODELOCKED, 237192586Strasz POSIX1E_ACL_ACCESS_EXTATTR_NAMESPACE, 238192586Strasz POSIX1E_ACL_ACCESS_EXTATTR_NAME, &len, (char *) old, 239192586Strasz td); 240192586Strasz break; 241192586Strasz case ACL_TYPE_DEFAULT: 242192586Strasz if (vp->v_type != VDIR) 243192586Strasz return (EINVAL); 244192586Strasz error = vn_extattr_get(vp, IO_NODELOCKED, 245192586Strasz POSIX1E_ACL_DEFAULT_EXTATTR_NAMESPACE, 246192586Strasz POSIX1E_ACL_DEFAULT_EXTATTR_NAME, &len, (char *) old, 247192586Strasz td); 248192586Strasz break; 249192586Strasz default: 250192586Strasz return (EINVAL); 251192586Strasz } 252192586Strasz 253192586Strasz if (error != 0) 254192586Strasz return (error); 255192586Strasz 256192586Strasz if (len != sizeof(*old)) { 257192586Strasz /* 258192586Strasz * A short (or long) read, meaning that for some reason 259192586Strasz * the ACL is corrupted. Return EPERM since the object 260192586Strasz * DAC protections are unsafe. 261192586Strasz */ 262192586Strasz printf("ufs_get_oldacl(): Loaded invalid ACL " 263241011Smdf "(len = %d), inumber %ju on %s\n", len, 264306553Skib (uintmax_t)ip->i_number, ITOFS(ip)->fs_fsmnt); 265192586Strasz return (EPERM); 266192586Strasz } 267192586Strasz 268192586Strasz return (0); 269192586Strasz} 270192586Strasz 271192586Strasz/* 27274822Srwatson * Retrieve the ACL on a file. 27374822Srwatson * 27474822Srwatson * As part of the ACL is stored in the inode, and the rest in an EA, 27574822Srwatson * assemble both into a final ACL product. Right now this is not done 27674822Srwatson * very efficiently. 27774822Srwatson */ 278192586Straszstatic int 279192586Straszufs_getacl_posix1e(struct vop_getacl_args *ap) 28074822Srwatson{ 28174822Srwatson struct inode *ip = VTOI(ap->a_vp); 282192586Strasz int error; 283192586Strasz struct oldacl *old; 28474822Srwatson 285105179Srwatson /* 286105179Srwatson * XXX: If ufs_getacl() should work on file systems not supporting 287105179Srwatson * ACLs, remove this check. 288105179Srwatson */ 289105179Srwatson if ((ap->a_vp->v_mount->mnt_flag & MNT_ACLS) == 0) 290200796Strasz return (EINVAL); 29174822Srwatson 292192586Strasz old = malloc(sizeof(*old), M_ACL, M_WAITOK | M_ZERO); 293192586Strasz 29474822Srwatson /* 295192586Strasz * Attempt to retrieve the ACL from the extended attributes. 29674822Srwatson */ 297192586Strasz error = ufs_get_oldacl(ap->a_type, old, ap->a_vp, ap->a_td); 298192586Strasz switch (error) { 299192586Strasz /* 300192586Strasz * XXX: If ufs_getacl() should work on filesystems 301192586Strasz * without the EA configured, add case EOPNOTSUPP here. 302192586Strasz */ 303192586Strasz case ENOATTR: 304192586Strasz switch (ap->a_type) { 305192586Strasz case ACL_TYPE_ACCESS: 30674822Srwatson /* 30774822Srwatson * Legitimately no ACL set on object, purely 30874822Srwatson * emulate it through the inode. These fields will 30974822Srwatson * be updated when the ACL is synchronized with 31074822Srwatson * the inode later. 31174822Srwatson */ 312192586Strasz old->acl_cnt = 3; 313192586Strasz old->acl_entry[0].ae_tag = ACL_USER_OBJ; 314192586Strasz old->acl_entry[0].ae_id = ACL_UNDEFINED_ID; 315192586Strasz old->acl_entry[0].ae_perm = ACL_PERM_NONE; 316192586Strasz old->acl_entry[1].ae_tag = ACL_GROUP_OBJ; 317192586Strasz old->acl_entry[1].ae_id = ACL_UNDEFINED_ID; 318192586Strasz old->acl_entry[1].ae_perm = ACL_PERM_NONE; 319192586Strasz old->acl_entry[2].ae_tag = ACL_OTHER; 320192586Strasz old->acl_entry[2].ae_id = ACL_UNDEFINED_ID; 321192586Strasz old->acl_entry[2].ae_perm = ACL_PERM_NONE; 32274822Srwatson break; 32374822Srwatson 324192586Strasz case ACL_TYPE_DEFAULT: 325192586Strasz /* 326192586Strasz * Unlike ACL_TYPE_ACCESS, there is no relationship 327192586Strasz * between the inode contents and the ACL, and it is 328192586Strasz * therefore possible for the request for the ACL 329192586Strasz * to fail since the ACL is undefined. In this 330192586Strasz * situation, return success and an empty ACL, 331192586Strasz * as required by POSIX.1e. 332192586Strasz */ 333192586Strasz old->acl_cnt = 0; 33474822Srwatson break; 33574822Srwatson } 336192586Strasz /* FALLTHROUGH */ 337192586Strasz case 0: 338192586Strasz error = acl_copy_oldacl_into_acl(old, ap->a_aclp); 339192586Strasz if (error != 0) 34074822Srwatson break; 34174822Srwatson 342192586Strasz if (ap->a_type == ACL_TYPE_ACCESS) 343192586Strasz ufs_sync_acl_from_inode(ip, ap->a_aclp); 344192586Strasz default: 34574822Srwatson break; 34674822Srwatson } 34774822Srwatson 348192586Strasz free(old, M_ACL); 34974822Srwatson return (error); 35074822Srwatson} 35174822Srwatson 352192586Straszint 353192586Straszufs_getacl(ap) 354192586Strasz struct vop_getacl_args /* { 355192586Strasz struct vnode *vp; 356192586Strasz acl_type_t type; 357192586Strasz struct acl *aclp; 358192586Strasz struct ucred *cred; 359192586Strasz struct thread *td; 360192586Strasz } */ *ap; 361192586Strasz{ 362192586Strasz 363200796Strasz if ((ap->a_vp->v_mount->mnt_flag & (MNT_ACLS | MNT_NFS4ACLS)) == 0) 364200796Strasz return (EOPNOTSUPP); 365200796Strasz 366200796Strasz if (ap->a_type == ACL_TYPE_NFS4) 367200796Strasz return (ufs_getacl_nfs4(ap)); 368200796Strasz 369192586Strasz return (ufs_getacl_posix1e(ap)); 370192586Strasz} 371192586Strasz 37274822Srwatson/* 373200796Strasz * Set NFSv4 ACL without doing any access checking. This is required 374200796Strasz * e.g. by the UFS code that implements ACL inheritance, or from 375200796Strasz * ufs_vnops.c:ufs_chmod(), as some of the checks have to be skipped 376200796Strasz * in that case, and others are redundant. 377200796Strasz */ 378200796Straszint 379200796Straszufs_setacl_nfs4_internal(struct vnode *vp, struct acl *aclp, struct thread *td) 380200796Strasz{ 381200796Strasz int error; 382200796Strasz mode_t mode; 383200796Strasz struct inode *ip = VTOI(vp); 384200796Strasz 385200796Strasz KASSERT(acl_nfs4_check(aclp, vp->v_type == VDIR) == 0, 386200796Strasz ("invalid ACL passed to ufs_setacl_nfs4_internal")); 387200796Strasz 388200796Strasz if (acl_nfs4_is_trivial(aclp, ip->i_uid)) { 389200796Strasz error = vn_extattr_rm(vp, IO_NODELOCKED, 390200796Strasz NFS4_ACL_EXTATTR_NAMESPACE, NFS4_ACL_EXTATTR_NAME, td); 391200796Strasz 392200796Strasz /* 393200796Strasz * An attempt to remove ACL from a file that didn't have 394200796Strasz * any extended entries is not an error. 395200796Strasz */ 396200796Strasz if (error == ENOATTR) 397200796Strasz error = 0; 398200796Strasz 399200796Strasz } else { 400200796Strasz error = vn_extattr_set(vp, IO_NODELOCKED, 401200796Strasz NFS4_ACL_EXTATTR_NAMESPACE, NFS4_ACL_EXTATTR_NAME, 402200796Strasz sizeof(*aclp), (char *) aclp, td); 403200796Strasz } 404200796Strasz 405200796Strasz /* 406200796Strasz * Map lack of attribute definition in UFS_EXTATTR into lack of 407200796Strasz * support for ACLs on the filesystem. 408200796Strasz */ 409200796Strasz if (error == ENOATTR) 410200796Strasz return (EOPNOTSUPP); 411200796Strasz 412200796Strasz if (error) 413200796Strasz return (error); 414200796Strasz 415200796Strasz mode = ip->i_mode; 416200796Strasz 417200796Strasz acl_nfs4_sync_mode_from_acl(&mode, aclp); 418200796Strasz 419200796Strasz ip->i_mode &= ACL_PRESERVE_MASK; 420200796Strasz ip->i_mode |= mode; 421200796Strasz DIP_SET(ip, i_mode, ip->i_mode); 422200796Strasz ip->i_flag |= IN_CHANGE; 423200796Strasz 424200796Strasz VN_KNOTE_UNLOCKED(vp, NOTE_ATTRIB); 425200796Strasz 426231122Skib error = UFS_UPDATE(vp, 0); 427231122Skib return (error); 428200796Strasz} 429200796Strasz 430200796Straszstatic int 431200796Straszufs_setacl_nfs4(struct vop_setacl_args *ap) 432200796Strasz{ 433200796Strasz int error; 434200796Strasz struct inode *ip = VTOI(ap->a_vp); 435200796Strasz 436200796Strasz if ((ap->a_vp->v_mount->mnt_flag & MNT_NFS4ACLS) == 0) 437200796Strasz return (EINVAL); 438200796Strasz 439200796Strasz if (ap->a_vp->v_mount->mnt_flag & MNT_RDONLY) 440200796Strasz return (EROFS); 441200796Strasz 442200796Strasz if (ap->a_aclp == NULL) 443200796Strasz return (EINVAL); 444200796Strasz 445200796Strasz error = VOP_ACLCHECK(ap->a_vp, ap->a_type, ap->a_aclp, ap->a_cred, 446200796Strasz ap->a_td); 447200796Strasz if (error) 448200796Strasz return (error); 449200796Strasz 450200796Strasz /* 451200796Strasz * Authorize the ACL operation. 452200796Strasz */ 453200796Strasz if (ip->i_flags & (IMMUTABLE | APPEND)) 454200796Strasz return (EPERM); 455200796Strasz 456200796Strasz /* 457200796Strasz * Must hold VWRITE_ACL or have appropriate privilege. 458200796Strasz */ 459200796Strasz if ((error = VOP_ACCESSX(ap->a_vp, VWRITE_ACL, ap->a_cred, ap->a_td))) 460200796Strasz return (error); 461200796Strasz 462200796Strasz /* 463200796Strasz * With NFSv4 ACLs, chmod(2) may need to add additional entries. 464200796Strasz * Make sure it has enough room for that - splitting every entry 465200796Strasz * into two and appending "canonical six" entries at the end. 466200796Strasz */ 467200796Strasz if (ap->a_aclp->acl_cnt > (ACL_MAX_ENTRIES - 6) / 2) 468200796Strasz return (ENOSPC); 469200796Strasz 470200796Strasz error = ufs_setacl_nfs4_internal(ap->a_vp, ap->a_aclp, ap->a_td); 471200796Strasz 472202971Strasz return (error); 473200796Strasz} 474200796Strasz 475200796Strasz/* 47674822Srwatson * Set the ACL on a file. 47774822Srwatson * 47874822Srwatson * As part of the ACL is stored in the inode, and the rest in an EA, 47974822Srwatson * this is necessarily non-atomic, and has complex authorization. 48074822Srwatson * As ufs_setacl() includes elements of ufs_chown() and ufs_chmod(), 48174822Srwatson * a fair number of different access checks may be required to go ahead 48274822Srwatson * with the operation at all. 48374822Srwatson */ 484192586Straszstatic int 485192586Straszufs_setacl_posix1e(struct vop_setacl_args *ap) 48674822Srwatson{ 48774822Srwatson struct inode *ip = VTOI(ap->a_vp); 48875571Srwatson int error; 489192586Strasz struct oldacl *old; 49074822Srwatson 491105179Srwatson if ((ap->a_vp->v_mount->mnt_flag & MNT_ACLS) == 0) 492200796Strasz return (EINVAL); 493105179Srwatson 49474822Srwatson /* 49574822Srwatson * If this is a set operation rather than a delete operation, 49674822Srwatson * invoke VOP_ACLCHECK() on the passed ACL to determine if it is 49774822Srwatson * valid for the target. This will include a check on ap->a_type. 49874822Srwatson */ 49974822Srwatson if (ap->a_aclp != NULL) { 50074822Srwatson /* 50174822Srwatson * Set operation. 50274822Srwatson */ 50374822Srwatson error = VOP_ACLCHECK(ap->a_vp, ap->a_type, ap->a_aclp, 50483366Sjulian ap->a_cred, ap->a_td); 50574822Srwatson if (error != 0) 50674822Srwatson return (error); 50774822Srwatson } else { 50874822Srwatson /* 50974822Srwatson * Delete operation. 51074822Srwatson * POSIX.1e allows only deletion of the default ACL on a 51174822Srwatson * directory (ACL_TYPE_DEFAULT). 51274822Srwatson */ 51374822Srwatson if (ap->a_type != ACL_TYPE_DEFAULT) 51474822Srwatson return (EINVAL); 51574822Srwatson if (ap->a_vp->v_type != VDIR) 51674822Srwatson return (ENOTDIR); 51774822Srwatson } 51874822Srwatson 51974822Srwatson if (ap->a_vp->v_mount->mnt_flag & MNT_RDONLY) 52074822Srwatson return (EROFS); 52174822Srwatson 52274822Srwatson /* 52374822Srwatson * Authorize the ACL operation. 52474822Srwatson */ 52574822Srwatson if (ip->i_flags & (IMMUTABLE | APPEND)) 52674822Srwatson return (EPERM); 52774822Srwatson 52874822Srwatson /* 52974822Srwatson * Must hold VADMIN (be file owner) or have appropriate privilege. 53074822Srwatson */ 53183366Sjulian if ((error = VOP_ACCESS(ap->a_vp, VADMIN, ap->a_cred, ap->a_td))) 53274822Srwatson return (error); 53374822Srwatson 53474822Srwatson switch(ap->a_type) { 53574822Srwatson case ACL_TYPE_ACCESS: 536192586Strasz old = malloc(sizeof(*old), M_ACL, M_WAITOK | M_ZERO); 537192586Strasz error = acl_copy_acl_into_oldacl(ap->a_aclp, old); 538192586Strasz if (error == 0) { 539192586Strasz error = vn_extattr_set(ap->a_vp, IO_NODELOCKED, 540192586Strasz POSIX1E_ACL_ACCESS_EXTATTR_NAMESPACE, 541192586Strasz POSIX1E_ACL_ACCESS_EXTATTR_NAME, sizeof(*old), 542192586Strasz (char *) old, ap->a_td); 543192586Strasz } 544192586Strasz free(old, M_ACL); 54574822Srwatson break; 54674822Srwatson 54774822Srwatson case ACL_TYPE_DEFAULT: 54874822Srwatson if (ap->a_aclp == NULL) { 54974822Srwatson error = vn_extattr_rm(ap->a_vp, IO_NODELOCKED, 55074822Srwatson POSIX1E_ACL_DEFAULT_EXTATTR_NAMESPACE, 55183366Sjulian POSIX1E_ACL_DEFAULT_EXTATTR_NAME, ap->a_td); 55274822Srwatson /* 55374822Srwatson * Attempting to delete a non-present default ACL 55474822Srwatson * will return success for portability purposes. 55574822Srwatson * (TRIX) 55685581Srwatson * 55785581Srwatson * XXX: Note that since we can't distinguish 55885581Srwatson * "that EA is not supported" from "that EA is not 55985581Srwatson * defined", the success case here overlaps the 56091814Sgreen * the ENOATTR->EOPNOTSUPP case below. 56174822Srwatson */ 56291814Sgreen if (error == ENOATTR) 56374822Srwatson error = 0; 564192586Strasz } else { 565192586Strasz old = malloc(sizeof(*old), M_ACL, M_WAITOK | M_ZERO); 566192586Strasz error = acl_copy_acl_into_oldacl(ap->a_aclp, old); 567192586Strasz if (error == 0) { 568192586Strasz error = vn_extattr_set(ap->a_vp, IO_NODELOCKED, 569192586Strasz POSIX1E_ACL_DEFAULT_EXTATTR_NAMESPACE, 570192586Strasz POSIX1E_ACL_DEFAULT_EXTATTR_NAME, 571192586Strasz sizeof(*old), (char *) old, ap->a_td); 572192586Strasz } 573192586Strasz free(old, M_ACL); 574192586Strasz } 57574822Srwatson break; 57674822Srwatson 57774822Srwatson default: 57874822Srwatson error = EINVAL; 57974822Srwatson } 58074822Srwatson /* 58174822Srwatson * Map lack of attribute definition in UFS_EXTATTR into lack of 58296755Strhodes * support for ACLs on the filesystem. 58374822Srwatson */ 58491814Sgreen if (error == ENOATTR) 58574822Srwatson return (EOPNOTSUPP); 58674822Srwatson if (error != 0) 58774822Srwatson return (error); 58874822Srwatson 58974822Srwatson if (ap->a_type == ACL_TYPE_ACCESS) { 59074822Srwatson /* 59174822Srwatson * Now that the EA is successfully updated, update the 59274822Srwatson * inode and mark it as changed. 59374822Srwatson */ 594118411Srwatson ufs_sync_inode_from_acl(ap->a_aclp, ip); 59574822Srwatson ip->i_flag |= IN_CHANGE; 596231122Skib error = UFS_UPDATE(ap->a_vp, 0); 59774822Srwatson } 59874822Srwatson 599133741Sjmg VN_KNOTE_UNLOCKED(ap->a_vp, NOTE_ATTRIB); 600231122Skib return (error); 60174822Srwatson} 60274822Srwatson 60374822Srwatsonint 604192586Straszufs_setacl(ap) 605192586Strasz struct vop_setacl_args /* { 60674822Srwatson struct vnode *vp; 60774822Srwatson acl_type_t type; 60874822Srwatson struct acl *aclp; 60974822Srwatson struct ucred *cred; 61083366Sjulian struct thread *td; 61174822Srwatson } */ *ap; 61274822Srwatson{ 613200796Strasz if ((ap->a_vp->v_mount->mnt_flag & (MNT_ACLS | MNT_NFS4ACLS)) == 0) 614200796Strasz return (EOPNOTSUPP); 61574822Srwatson 616200796Strasz if (ap->a_type == ACL_TYPE_NFS4) 617200796Strasz return (ufs_setacl_nfs4(ap)); 618200796Strasz 619192586Strasz return (ufs_setacl_posix1e(ap)); 620192586Strasz} 621192586Strasz 622192586Straszstatic int 623200796Straszufs_aclcheck_nfs4(struct vop_aclcheck_args *ap) 624200796Strasz{ 625200796Strasz int is_directory = 0; 626200796Strasz 627200796Strasz if ((ap->a_vp->v_mount->mnt_flag & MNT_NFS4ACLS) == 0) 628200796Strasz return (EINVAL); 629200796Strasz 630200796Strasz /* 631200796Strasz * With NFSv4 ACLs, chmod(2) may need to add additional entries. 632200796Strasz * Make sure it has enough room for that - splitting every entry 633200796Strasz * into two and appending "canonical six" entries at the end. 634200796Strasz */ 635200796Strasz if (ap->a_aclp->acl_cnt > (ACL_MAX_ENTRIES - 6) / 2) 636200796Strasz return (ENOSPC); 637200796Strasz 638200796Strasz if (ap->a_vp->v_type == VDIR) 639200796Strasz is_directory = 1; 640200796Strasz 641200796Strasz return (acl_nfs4_check(ap->a_aclp, is_directory)); 642200796Strasz} 643200796Strasz 644200796Straszstatic int 645192586Straszufs_aclcheck_posix1e(struct vop_aclcheck_args *ap) 646192586Strasz{ 647192586Strasz 648105179Srwatson if ((ap->a_vp->v_mount->mnt_flag & MNT_ACLS) == 0) 649200796Strasz return (EINVAL); 650105179Srwatson 65174822Srwatson /* 65274822Srwatson * Verify we understand this type of ACL, and that it applies 65374822Srwatson * to this kind of object. 65474822Srwatson * Rely on the acl_posix1e_check() routine to verify the contents. 65574822Srwatson */ 65674822Srwatson switch(ap->a_type) { 65774822Srwatson case ACL_TYPE_ACCESS: 65874822Srwatson break; 65974822Srwatson 66074822Srwatson case ACL_TYPE_DEFAULT: 66174822Srwatson if (ap->a_vp->v_type != VDIR) 66274822Srwatson return (EINVAL); 66374822Srwatson break; 66474822Srwatson 66574822Srwatson default: 66674822Srwatson return (EINVAL); 66774822Srwatson } 668192586Strasz 669192586Strasz if (ap->a_aclp->acl_cnt > OLDACL_MAX_ENTRIES) 670192586Strasz return (EINVAL); 671192586Strasz 67274822Srwatson return (acl_posix1e_check(ap->a_aclp)); 67374822Srwatson} 67474822Srwatson 675192586Strasz/* 676192586Strasz * Check the validity of an ACL for a file. 677192586Strasz */ 678192586Straszint 679192586Straszufs_aclcheck(ap) 680192586Strasz struct vop_aclcheck_args /* { 681192586Strasz struct vnode *vp; 682192586Strasz acl_type_t type; 683192586Strasz struct acl *aclp; 684192586Strasz struct ucred *cred; 685192586Strasz struct thread *td; 686192586Strasz } */ *ap; 687192586Strasz{ 688192586Strasz 689200796Strasz if ((ap->a_vp->v_mount->mnt_flag & (MNT_ACLS | MNT_NFS4ACLS)) == 0) 690200796Strasz return (EOPNOTSUPP); 691200796Strasz 692200796Strasz if (ap->a_type == ACL_TYPE_NFS4) 693200796Strasz return (ufs_aclcheck_nfs4(ap)); 694200796Strasz 695192586Strasz return (ufs_aclcheck_posix1e(ap)); 696192586Strasz} 697192586Strasz 69874822Srwatson#endif /* !UFS_ACL */ 699