diff options
-rw-r--r-- | sys/conf/files | 1 | ||||
-rw-r--r-- | sys/conf/options | 5 | ||||
-rw-r--r-- | sys/ufs/ufs/acl.h | 47 | ||||
-rw-r--r-- | sys/ufs/ufs/ufs_acl.c | 559 | ||||
-rw-r--r-- | sys/ufs/ufs/ufs_vnops.c | 218 |
5 files changed, 825 insertions, 5 deletions
diff --git a/sys/conf/files b/sys/conf/files index e0d09f4..e9062c1 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1187,6 +1187,7 @@ ufs/ffs/ffs_vnops.c optional ifs ufs/ffs/ffs_vnops.c optional mfs ufs/mfs/mfs_vfsops.c optional mfs ufs/mfs/mfs_vnops.c optional mfs +ufs/ufs/ufs_acl.c standard ufs/ufs/ufs_bmap.c standard ufs/ufs/ufs_extattr.c standard ufs/ufs/ufs_ihash.c standard diff --git a/sys/conf/options b/sys/conf/options index 2a3f9f5..d4c375a 100644 --- a/sys/conf/options +++ b/sys/conf/options @@ -143,6 +143,11 @@ NFS # otherwise a STUB module will be compiled in. SOFTUPDATES opt_ffs.h +# Enabling this option turns on support for Access Control Lists in UFS, +# which can be used to support high security configurations. Depends on +# UFS_EXTATTR. +UFS_ACL opt_ufs.h + # Enabling this option turns on support for extended attributes in UFS-based # file systems, which can be used to support high security configurations # as well as new file system features. diff --git a/sys/ufs/ufs/acl.h b/sys/ufs/ufs/acl.h new file mode 100644 index 0000000..f9f1e37 --- /dev/null +++ b/sys/ufs/ufs/acl.h @@ -0,0 +1,47 @@ +/*- + * Copyright (c) 1999, 2000, 2001 Robert N. M. Watson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ +/* + * TrustedBSD Project - ACL support for the UFS file system. + */ + +#ifndef _UFS_UFS_ACL_H_ +#define _UFS_UFS_ACL_H_ + +#ifdef _KERNEL + +void ufs_sync_acl_from_inode(struct inode *ip, struct acl *acl); +void ufs_sync_inode_from_acl(struct acl *acl, struct inode *ip, + mode_t preserve_mask); + +int ufs_getacl __P((struct vop_getacl_args *)); +int ufs_setacl __P((struct vop_setacl_args *)); +int ufs_aclcheck __P((struct vop_aclcheck_args *)); + +#endif /* !_KERNEL */ + +#endif /* !_UFS_UFS_ACL_H */ diff --git a/sys/ufs/ufs/ufs_acl.c b/sys/ufs/ufs/ufs_acl.c new file mode 100644 index 0000000..753dbde --- /dev/null +++ b/sys/ufs/ufs/ufs_acl.c @@ -0,0 +1,559 @@ +/*- + * Copyright (c) 1999, 2000, 2001 Robert N. M. Watson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ +/* + * Developed by the TrustedBSD Project. + * Support for POSIX.1e access control lists: UFS-specific support functions. + */ + +#include "opt_ufs.h" +#include "opt_quota.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/stat.h> +#include <sys/mount.h> +#include <sys/vnode.h> +#include <sys/types.h> +#include <sys/acl.h> +#include <sys/event.h> +#include <sys/extattr.h> + +#include <ufs/ufs/quota.h> +#include <ufs/ufs/inode.h> +#include <ufs/ufs/acl.h> +#include <ufs/ufs/extattr.h> +#include <ufs/ufs/dir.h> +#include <ufs/ufs/ufsmount.h> +#include <ufs/ufs/ufs_extern.h> + +#define VN_KNOTE(vp, b) \ + KNOTE(&vp->v_pollinfo.vpi_selinfo.si_note, (b)) + +#ifdef UFS_ACL + +/* + * Synchronize an ACL and an inode by copying over appropriate inode fields + * to the passed ACL. Assumes an ACL that would satisfy acl_posix1e_check(), + * and may panic if not. + */ +void +ufs_sync_acl_from_inode(struct inode *ip, struct acl *acl) +{ + struct acl_entry *acl_mask, *acl_group_obj; + int i; + + /* + * Update ACL_USER_OBJ, ACL_OTHER, but simply identify ACL_MASK + * and ACL_GROUP_OBJ for use after we know whether ACL_MASK is + * present. + */ + acl_mask = NULL; + acl_group_obj = NULL; + for (i = 0; i < acl->acl_cnt; i++) { + switch (acl->acl_entry[i].ae_tag) { + case ACL_USER_OBJ: + acl->acl_entry[i].ae_perm = acl_posix1e_mode_to_perm( + ACL_USER_OBJ, ip->i_mode); + acl->acl_entry[i].ae_id = ip->i_uid; + break; + + case ACL_GROUP_OBJ: + acl_group_obj = &acl->acl_entry[i]; + acl->acl_entry[i].ae_id = ip->i_gid; + break; + + case ACL_OTHER: + acl->acl_entry[i].ae_perm = acl_posix1e_mode_to_perm( + ACL_OTHER, ip->i_mode); + acl->acl_entry[i].ae_id = 0; + break; + + case ACL_MASK: + acl_mask = &acl->acl_entry[i]; + acl->acl_entry[i].ae_id = 0; + break; + + case ACL_USER: + case ACL_GROUP: + break; + + default: + panic("ufs_sync_acl_from_inode(): bad ae_tag"); + } + } + + if (acl_group_obj == NULL) + panic("ufs_sync_acl_from_inode(): no ACL_GROUP_OBJ"); + + if (acl_mask == NULL) { + /* + * There is no ACL_MASK, so update ACL_GROUP_OBJ. + */ + acl_group_obj->ae_perm = acl_posix1e_mode_to_perm( + ACL_GROUP_OBJ, ip->i_mode); + } else { + /* + * Update the ACL_MASK entry instead of ACL_GROUP_OBJ. + */ + acl_mask->ae_perm = acl_posix1e_mode_to_perm(ACL_GROUP_OBJ, + ip->i_mode); + } +} + +/* + * Synchronize an inode and an ACL by copying over appropriate ACL fields to + * the passed inode. Assumes an ACL that would satisfy acl_posix1e_check(), + * and may panic if not. This code will preserve existing use of the + * sticky, setugid, and non-permission bits in the mode field. It may + * be that the caller wishes to have previously authorized these changes, + * and may also want to clear the setugid bits in some situations. + */ +void +ufs_sync_inode_from_acl(struct acl *acl, struct inode *ip, + mode_t preserve_mask) +{ + struct acl_entry *acl_mask, *acl_user_obj, *acl_group_obj; + struct acl_entry *acl_other; + mode_t preserve_mode; + int i; + + /* + * Preserve old mode so we can restore appropriate bits of it. + */ + preserve_mode = (ip->i_mode & preserve_mask); + + /* + * Identify the ACL_MASK and all other entries appearing in the + * inode mode. + */ + acl_user_obj = NULL; + acl_group_obj = NULL; + acl_other = NULL; + acl_mask = NULL; + for (i = 0; i < acl->acl_cnt; i++) { + switch (acl->acl_entry[i].ae_tag) { + case ACL_USER_OBJ: + acl_user_obj = &acl->acl_entry[i]; + ip->i_uid = acl->acl_entry[i].ae_id; + break; + + case ACL_GROUP_OBJ: + acl_group_obj = &acl->acl_entry[i]; + ip->i_gid = acl->acl_entry[i].ae_id; + break; + + case ACL_OTHER: + acl_other = &acl->acl_entry[i]; + break; + + case ACL_MASK: + acl_mask = &acl->acl_entry[i]; + break; + + case ACL_USER: + case ACL_GROUP: + break; + + default: + panic("ufs_sync_inode_from_acl(): bad ae_tag"); + } + } + + if (acl_user_obj == NULL || acl_group_obj == NULL || acl_other == NULL) + panic("ufs_sync_inode_from_acl(): missing ae_tags"); + + if (acl_mask == NULL) { + /* + * There is no ACL_MASK, so use the ACL_GROUP_OBJ entry. + */ + ip->i_mode &= ~ALLPERMS; + ip->i_mode |= acl_posix1e_perms_to_mode(acl_user_obj, + acl_group_obj, acl_other); + } else { + /* + * Use the ACL_MASK entry. + */ + ip->i_mode &= ~ALLPERMS; + ip->i_mode |= acl_posix1e_perms_to_mode(acl_user_obj, + acl_mask, acl_other); + } + ip->i_mode |= preserve_mode; +} + +/* + * Retrieve the ACL on a file. + * + * As part of the ACL is stored in the inode, and the rest in an EA, + * assemble both into a final ACL product. Right now this is not done + * very efficiently. + */ +int +ufs_getacl(ap) + struct vop_getacl_args /* { + struct vnode *vp; + struct acl_type_t type; + struct acl *aclp; + struct ucred *cred; + struct proc *p; + } */ *ap; +{ + struct inode *ip = VTOI(ap->a_vp); + int error, len; + + + /* + * Attempt to retrieve the ACL based on the ACL type. + */ + bzero(ap->a_aclp, sizeof(*ap->a_aclp)); + switch(ap->a_type) { + case ACL_TYPE_ACCESS: + /* + * ACL_TYPE_ACCESS ACLs may or may not be stored in the + * EA, as they are in fact a combination of the inode + * ownership/permissions and the EA contents. If the + * EA is present, merge the two in a temporary ACL + * storage, otherwise just return the inode contents. + */ + len = sizeof(*ap->a_aclp); + error = vn_extattr_get(ap->a_vp, IO_NODELOCKED, + POSIX1E_ACL_ACCESS_EXTATTR_NAMESPACE, + POSIX1E_ACL_ACCESS_EXTATTR_NAME, &len, (char *) ap->a_aclp, + ap->a_p); + switch (error) { + /* XXX: Will be ENOATTR. */ + /* XXX: If ufs_getacl() should work on file systems without + * the EA configured, add case EOPNOTSUPP here. */ + case ENOENT: + /* + * Legitimately no ACL set on object, purely + * emulate it through the inode. These fields will + * be updated when the ACL is synchronized with + * the inode later. + */ + ap->a_aclp->acl_cnt = 3; + ap->a_aclp->acl_entry[0].ae_tag = ACL_USER_OBJ; + ap->a_aclp->acl_entry[0].ae_id = 0; + ap->a_aclp->acl_entry[0].ae_perm = 0; + ap->a_aclp->acl_entry[1].ae_tag = ACL_GROUP_OBJ; + ap->a_aclp->acl_entry[1].ae_id = 0; + ap->a_aclp->acl_entry[1].ae_perm = 0; + ap->a_aclp->acl_entry[2].ae_tag = ACL_OTHER; + ap->a_aclp->acl_entry[2].ae_id = 0; + ap->a_aclp->acl_entry[2].ae_perm = 0; + ufs_sync_acl_from_inode(ip, ap->a_aclp); + error = 0; + break; + + case 0: + if (len != sizeof(*ap->a_aclp)) { + /* + * A short (or long) read, meaning that for + * some reason the ACL is corrupted. Return + * EPERM since the object DAC protections + * are unsafe. + */ + printf("ufs_getacl(): Loaded invalid ACL (" + "%d bytes)\n", len); + return (EPERM); + } + ufs_sync_acl_from_inode(ip, ap->a_aclp); + break; + + default: + } + break; + + case ACL_TYPE_DEFAULT: + if (ap->a_vp->v_type != VDIR) { + error = EINVAL; + break; + } + bzero(ap->a_aclp, sizeof(*ap->a_aclp)); + error = vn_extattr_get(ap->a_vp, IO_NODELOCKED, + POSIX1E_ACL_DEFAULT_EXTATTR_NAMESPACE, + POSIX1E_ACL_DEFAULT_EXTATTR_NAME, &len, + (char *) ap->a_aclp, ap->a_p); + /* + * Unlike ACL_TYPE_ACCESS, there is no relationship between + * the inode contents and the ACL, and it is therefore + * possible for the request for the ACL to fail since the + * ACL is undefined. In this situation, return success + * and an empty ACL, as required by POSIX.1e. + */ + switch (error) { + /* XXX: Will be ENOATTR. */ + /* XXX: If ufs_getacl() should work on file systems without + * the EA configured, add case EOPNOTSUPP here. */ + case ENOENT: + bzero(ap->a_aclp, sizeof(*ap->a_aclp)); + ap->a_aclp->acl_cnt = 0; + error = 0; + break; + + case 0: + break; + + default: + } + break; + + default: + error = EINVAL; + } + + return (error); +} + +/* + * Set the ACL on a file. + * + * As part of the ACL is stored in the inode, and the rest in an EA, + * this is necessarily non-atomic, and has complex authorization. + * As ufs_setacl() includes elements of ufs_chown() and ufs_chmod(), + * a fair number of different access checks may be required to go ahead + * with the operation at all. + */ +int +ufs_setacl(ap) + struct vop_setacl_args /* { + struct vnode *vp; + acl_type_t type; + struct acl *aclp; + struct ucred *cred; + struct proc *p; + } */ *ap; +{ + struct inode *ip = VTOI(ap->a_vp); + struct acl_entry *acl_user_obj, *acl_group_obj, *acl_other; + mode_t old_mode, preserve_mask; + uid_t old_uid, new_uid = 0; + gid_t old_gid, new_gid = 0; + int error, i; + + /* + * If this is a set operation rather than a delete operation, + * invoke VOP_ACLCHECK() on the passed ACL to determine if it is + * valid for the target. This will include a check on ap->a_type. + */ + if (ap->a_aclp != NULL) { + /* + * Set operation. + */ + error = VOP_ACLCHECK(ap->a_vp, ap->a_type, ap->a_aclp, + ap->a_cred, ap->a_p); + if (error != 0) + return (error); + } else { + /* + * Delete operation. + * POSIX.1e allows only deletion of the default ACL on a + * directory (ACL_TYPE_DEFAULT). + */ + if (ap->a_type != ACL_TYPE_DEFAULT) + return (EINVAL); + if (ap->a_vp->v_type != VDIR) + return (ENOTDIR); + } + + if (ap->a_vp->v_mount->mnt_flag & MNT_RDONLY) + return (EROFS); + + /* + * Authorize the ACL operation. + */ + if (ip->i_flags & (IMMUTABLE | APPEND)) + return (EPERM); + + /* + * Must hold VADMIN (be file owner) or have appropriate privilege. + */ + if ((error = VOP_ACCESS(ap->a_vp, VADMIN, ap->a_cred, ap->a_p))) + return (error); + + /* + * ACL_TYPE_ACCESS may involve the changing of ownership, sticky + * bit, setugid bits on the file or directory. As such, it requires + * special handling to identify these changes, and to authorize + * them. + * ACL_TYPE_DEFAULT does not require this, and ap->a_aclp should + * not be dereferenced without a NULL check, as it may be a delete + * operation. + */ + switch(ap->a_type) { + case ACL_TYPE_ACCESS: + /* + * Identify ACL_USER_OBJ, ACL_GROUP_OBJ, and determine if + * they have changed. If so, authorize in the style of + * ufs_chown(). While we're at it, identify ACL_OTHER. + */ + acl_user_obj = acl_group_obj = acl_other = NULL; + for (i = 0; i < ap->a_aclp->acl_cnt; i++) + switch(ap->a_aclp->acl_entry[i].ae_tag) { + case ACL_USER_OBJ: + acl_user_obj = &ap->a_aclp->acl_entry[i]; + new_uid = acl_user_obj->ae_id; + break; + case ACL_GROUP_OBJ: + acl_group_obj = &ap->a_aclp->acl_entry[i]; + new_gid = acl_group_obj->ae_id; + break; + case ACL_OTHER: + acl_other = &ap->a_aclp->acl_entry[i]; + break; + default: + } + old_uid = ip->i_uid; + old_gid = ip->i_gid; + + /* + * Authorize changes to base object ownership in the style + * of ufs_chown(). + */ + if (new_uid != old_uid && (error = suser_xxx(ap->a_cred, + ap->a_p, PRISON_ROOT))) + return (error); + if (new_gid != old_gid && !groupmember(new_gid, ap->a_cred) && + (error = suser_xxx(ap->a_cred, ap->a_p, PRISON_ROOT))) + return (error); + + case ACL_TYPE_DEFAULT: + /* + * ACL_TYPE_DEFAULT can literally be written straight into + * the EA unhindered, as it has gone through sanity checking + * already. + */ + break; + + default: + panic("ufs_setacl(): unknown acl type\n"); + } + + switch(ap->a_type) { + case ACL_TYPE_ACCESS: + error = vn_extattr_set(ap->a_vp, IO_NODELOCKED, + POSIX1E_ACL_ACCESS_EXTATTR_NAMESPACE, + POSIX1E_ACL_ACCESS_EXTATTR_NAME, sizeof(*ap->a_aclp), + (char *) ap->a_aclp, ap->a_p); + break; + + case ACL_TYPE_DEFAULT: + if (ap->a_aclp == NULL) { + error = vn_extattr_rm(ap->a_vp, IO_NODELOCKED, + POSIX1E_ACL_DEFAULT_EXTATTR_NAMESPACE, + POSIX1E_ACL_DEFAULT_EXTATTR_NAME, ap->a_p); + /* + * Attempting to delete a non-present default ACL + * will return success for portability purposes. + * (TRIX) + */ + /* XXX: the ENOENT here will eventually be ENOATTR. */ + if (error == EINVAL) + error = 0; + } else + error = vn_extattr_set(ap->a_vp, IO_NODELOCKED, + POSIX1E_ACL_DEFAULT_EXTATTR_NAMESPACE, + POSIX1E_ACL_DEFAULT_EXTATTR_NAME, + sizeof(*ap->a_aclp), (char *) ap->a_aclp, ap->a_p); + break; + + default: + error = EINVAL; + } + /* + * Map lack of attribute definition in UFS_EXTATTR into lack of + * support for ACLs on the file system. + */ + /* XXX: ENOENT here will eventually be ENOATTR. */ + if (error == ENOENT) + return (EOPNOTSUPP); + if (error != 0) + return (error); + + if (ap->a_type == ACL_TYPE_ACCESS) { + /* + * Now that the EA is successfully updated, update the + * inode and mark it as changed. + */ + old_uid = ip->i_uid; + old_gid = ip->i_gid; + old_mode = ip->i_mode; + preserve_mask = ISVTX | ISGID | ISUID; + ufs_sync_inode_from_acl(ap->a_aclp, ip, preserve_mask); + + /* + * Clear the ISGID and ISUID bits if the ownership has + * changed, or appropriate privilege is not available. + * XXX: This should probably be a check for broadening + * availability of the bits, but it's not clear from the + * spec. + */ + if (suser_xxx(ap->a_cred, NULL, PRISON_ROOT) && + (ip->i_gid != old_gid || ip->i_uid != old_uid)) + ip->i_mode &= ~(ISUID | ISGID); + ip->i_flag |= IN_CHANGE; + } + + VN_KNOTE(ap->a_vp, NOTE_ATTRIB); + return (0); +} + +/* + * Check the validity of an ACL for a file. + */ +int +ufs_aclcheck(ap) + struct vop_aclcheck_args /* { + struct vnode *vp; + acl_type_t type; + struct acl *aclp; + struct ucred *cred; + struct proc *p; + } */ *ap; +{ + + /* + * Verify we understand this type of ACL, and that it applies + * to this kind of object. + * Rely on the acl_posix1e_check() routine to verify the contents. + */ + switch(ap->a_type) { + case ACL_TYPE_ACCESS: + break; + + case ACL_TYPE_DEFAULT: + if (ap->a_vp->v_type != VDIR) + return (EINVAL); + break; + + default: + return (EINVAL); + } + return (acl_posix1e_check(ap->a_aclp)); +} + +#endif /* !UFS_ACL */ diff --git a/sys/ufs/ufs/ufs_vnops.c b/sys/ufs/ufs/ufs_vnops.c index 44fe3c8..9db19e2 100644 --- a/sys/ufs/ufs/ufs_vnops.c +++ b/sys/ufs/ufs/ufs_vnops.c @@ -41,6 +41,7 @@ #include "opt_quota.h" #include "opt_suiddir.h" +#include "opt_ufs.h" #include <sys/param.h> #include <sys/systm.h> @@ -52,12 +53,13 @@ #include <sys/buf.h> #include <sys/mount.h> #include <sys/unistd.h> -#include <sys/vnode.h> #include <sys/malloc.h> +#include <sys/vnode.h> #include <sys/dirent.h> #include <sys/lockf.h> #include <sys/event.h> #include <sys/conf.h> +#include <sys/acl.h> #include <machine/mutex.h> @@ -68,6 +70,7 @@ #include <miscfs/fifofs/fifo.h> +#include <ufs/ufs/acl.h> #include <ufs/ufs/extattr.h> #include <ufs/ufs/quota.h> #include <ufs/ufs/inode.h> @@ -308,8 +311,10 @@ ufs_access(ap) struct vnode *vp = ap->a_vp; struct inode *ip = VTOI(vp); mode_t mode = ap->a_mode; -#ifdef QUOTA int error; +#ifdef UFS_ACL + struct acl *acl; + int len; #endif /* @@ -338,8 +343,35 @@ ufs_access(ap) if ((mode & VWRITE) && (ip->i_flags & (IMMUTABLE | SF_SNAPSHOT))) return (EPERM); - return (vaccess(vp->v_type, ip->i_mode, ip->i_uid, ip->i_gid, - ap->a_mode, ap->a_cred, NULL)); +#ifdef UFS_ACL + MALLOC(acl, struct acl *, sizeof(*acl), M_ACL, M_WAITOK); + len = sizeof(*acl); + error = VOP_GETACL(vp, ACL_TYPE_ACCESS, acl, ap->a_cred, ap->a_p); + switch (error) { + case EOPNOTSUPP: + error = vaccess(vp->v_type, ip->i_mode, ip->i_uid, ip->i_gid, + ap->a_mode, ap->a_cred, NULL); + break; + case 0: + error = vaccess_acl_posix1e(vp->v_type, acl, ap->a_mode, + ap->a_cred, NULL); + break; + default: + printf("ufs_access(): Error retrieving ACL on object (%d).\n", + error); + /* + * XXX: Fall back until debugged. Should eventually + * possibly log an error, and return EPERM for safety. + */ + error = vaccess(vp->v_type, ip->i_mode, ip->i_uid, ip->i_gid, + ap->a_mode, ap->a_cred, NULL); + } + FREE(acl, M_ACL); +#else + error = vaccess(vp->v_type, ip->i_mode, ip->i_uid, ip->i_gid, + ap->a_mode, ap->a_cred, NULL); +#endif + return (error); } /* ARGSUSED */ @@ -1279,6 +1311,9 @@ ufs_mkdir(ap) struct buf *bp; struct dirtemplate dirtemplate, *dtp; struct direct newdir; +#ifdef UFS_ACL + struct acl *acl, *dacl; +#endif int error, dmode; long blkoff; @@ -1360,7 +1395,54 @@ ufs_mkdir(ap) #endif #endif /* !SUIDDIR */ ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE; +#ifdef UFS_ACL + MALLOC(acl, struct acl *, sizeof(*acl), M_ACL, M_WAITOK); + MALLOC(dacl, struct acl *, sizeof(*acl), M_ACL, M_WAITOK); + + /* + * Retrieve default ACL from parent, if any. + */ + error = VOP_GETACL(dvp, ACL_TYPE_DEFAULT, acl, cnp->cn_cred, + cnp->cn_proc); + switch (error) { + case 0: + /* + * Retrieved a default ACL, so merge mode and ACL if + * necessary. + */ + if (acl->acl_cnt != 0) { + /* + * Two possible ways for default ACL to not be + * present. First, the EA can be undefined, + * or second, the default ACL can be blank. + * If it's blank, fall through to the it's + * not defined case. + */ + ip->i_mode = dmode; + *dacl = *acl; + ufs_sync_acl_from_inode(ip, acl); + break; + } + /* FALLTHROUGH */ + + case EOPNOTSUPP: + /* + * Just use the mode as-is. + */ + ip->i_mode = dmode; + FREE(acl, M_ACL); + FREE(dacl, M_ACL); + dacl = acl = NULL; + break; + + default: + UFS_VFREE(tvp, ip->i_number, dmode); + vput(tvp); + return (error); + } +#else /* !UFS_ACL */ ip->i_mode = dmode; +#endif /* !UFS_ACL */ tvp->v_type = VDIR; /* Rest init'd in getnewvnode(). */ ip->i_effnlink = 2; ip->i_nlink = 2; @@ -1382,6 +1464,41 @@ ufs_mkdir(ap) error = UFS_UPDATE(tvp, !(DOINGSOFTDEP(dvp) | DOINGASYNC(dvp))); if (error) goto bad; +#ifdef UFS_ACL + if (acl != NULL) { + /* + * XXX: If we abort now, will Soft Updates notify the extattr + * code that the EAs for the file need to be released? + */ + error = VOP_SETACL(tvp, ACL_TYPE_ACCESS, acl, cnp->cn_cred, + cnp->cn_proc); + if (error == 0) + error = VOP_SETACL(tvp, ACL_TYPE_DEFAULT, dacl, + cnp->cn_cred, cnp->cn_proc); + switch (error) { + case 0: + break; + + case EOPNOTSUPP: + /* + * XXX: This should not happen, as EOPNOTSUPP above + * was supposed to free acl. + */ + printf("ufs_mkdir: VOP_GETACL() but no VOP_SETACL()\n"); + /* + panic("ufs_mkdir: VOP_GETACL() but no VOP_SETACL()"); + */ + break; + + default: + FREE(acl, M_ACL); + FREE(dacl, M_ACL); + goto bad; + } + FREE(acl, M_ACL); + FREE(dacl, M_ACL); + } +#endif /* !UFS_ACL */ /* * Initialize directory with "." and ".." from static template. @@ -2049,6 +2166,7 @@ ufs_vinit(mntp, specops, fifoops, vpp) /* * Allocate a new inode. + * Vnode dvp must be locked. */ int ufs_makeinode(mode, dvp, vpp, cnp) @@ -2060,6 +2178,9 @@ ufs_makeinode(mode, dvp, vpp, cnp) register struct inode *ip, *pdir; struct direct newdir; struct vnode *tvp; +#ifdef UFS_ACL + struct acl *acl; +#endif int error; pdir = VTOI(dvp); @@ -2132,7 +2253,49 @@ ufs_makeinode(mode, dvp, vpp, cnp) #endif #endif /* !SUIDDIR */ ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE; +#ifdef UFS_ACL + MALLOC(acl, struct acl *, sizeof(*acl), M_ACL, M_WAITOK); + /* + * Retrieve default ACL for parent, if any. + */ + error = VOP_GETACL(dvp, ACL_TYPE_DEFAULT, acl, cnp->cn_cred, + cnp->cn_proc); + switch (error) { + case 0: + /* + * Retrieved a default ACL, so merge mode and ACL if + * necessary. + */ + if (acl->acl_cnt != 0) { + /* + * Two possible ways for default ACL to not be + * present. First, the EA can be undefined, + * or second, the default ACL can be blank. + * If it's blank, fall through to the it's + * not defined case. + */ + ip->i_mode = mode; + ufs_sync_acl_from_inode(ip, acl); + break; + } + + case EOPNOTSUPP: + /* + * Just use the mode as-is. + */ + ip->i_mode = mode; + FREE(acl, M_ACL); + acl = NULL; + break; + + default: + UFS_VFREE(tvp, ip->i_number, mode); + vput(tvp); + return (error); + } +#else /* !UFS_ACL */ ip->i_mode = mode; +#endif /* !UFS_ACL */ tvp->v_type = IFTOVT(mode); /* Rest init'd in getnewvnode(). */ ip->i_effnlink = 1; ip->i_nlink = 1; @@ -2151,6 +2314,36 @@ ufs_makeinode(mode, dvp, vpp, cnp) error = UFS_UPDATE(tvp, !(DOINGSOFTDEP(tvp) | DOINGASYNC(tvp))); if (error) goto bad; +#ifdef UFS_ACL + if (acl != NULL) { + /* + * XXX: If we abort now, will Soft Updates notify the extattr + * code that the EAs for the file need to be released? + */ + error = VOP_SETACL(tvp, ACL_TYPE_ACCESS, acl, cnp->cn_cred, + cnp->cn_proc); + switch (error) { + case 0: + break; + + case EOPNOTSUPP: + /* + * XXX: This should not happen, as EOPNOTSUPP above was + * supposed to free acl. + */ + printf("ufs_makeinode: VOP_GETACL() but no " + "VOP_SETACL()\n"); + /* panic("ufs_makeinode: VOP_GETACL() but no " + "VOP_SETACL()"); */ + break; + + default: + FREE(acl, M_ACL); + goto bad; + } + FREE(acl, M_ACL); + } +#endif /* !UFS_ACL */ ufs_makedirentry(ip, cnp, &newdir); error = ufs_direnter(dvp, tvp, &newdir, cnp, NULL); if (error) @@ -2299,6 +2492,11 @@ static struct vnodeopv_entry_desc ufs_vnodeop_entries[] = { { &vop_symlink_desc, (vop_t *) ufs_symlink }, { &vop_unlock_desc, (vop_t *) vop_stdunlock }, { &vop_whiteout_desc, (vop_t *) ufs_whiteout }, +#ifdef UFS_ACL + { &vop_getacl_desc, (vop_t *) ufs_getacl }, + { &vop_setacl_desc, (vop_t *) ufs_setacl }, + { &vop_aclcheck_desc, (vop_t *) ufs_aclcheck }, +#endif { NULL, NULL } }; static struct vnodeopv_desc ufs_vnodeop_opv_desc = @@ -2320,7 +2518,12 @@ static struct vnodeopv_entry_desc ufs_specop_entries[] = { { &vop_setattr_desc, (vop_t *) ufs_setattr }, { &vop_unlock_desc, (vop_t *) vop_stdunlock }, { &vop_write_desc, (vop_t *) ufsspec_write }, - { NULL, NULL } +#ifdef UFS_ACL + { &vop_getacl_desc, (vop_t *) ufs_getacl }, + { &vop_setacl_desc, (vop_t *) ufs_setacl }, + { &vop_aclcheck_desc, (vop_t *) ufs_aclcheck }, +#endif + {NULL, NULL} }; static struct vnodeopv_desc ufs_specop_opv_desc = { &ufs_specop_p, ufs_specop_entries }; @@ -2341,6 +2544,11 @@ static struct vnodeopv_entry_desc ufs_fifoop_entries[] = { { &vop_setattr_desc, (vop_t *) ufs_setattr }, { &vop_unlock_desc, (vop_t *) vop_stdunlock }, { &vop_write_desc, (vop_t *) ufsfifo_write }, +#ifdef UFS_ACL + { &vop_getacl_desc, (vop_t *) ufs_getacl }, + { &vop_setacl_desc, (vop_t *) ufs_setacl }, + { &vop_aclcheck_desc, (vop_t *) ufs_aclcheck }, +#endif { NULL, NULL } }; static struct vnodeopv_desc ufs_fifoop_opv_desc = |