zfs_acl.c revision b87f3af36bb994656da117319f5129ddfd05ed21
fa9e4066f08beec538e775443c5be79dd423fcabahrens * CDDL HEADER START
fa9e4066f08beec538e775443c5be79dd423fcabahrens * The contents of this file are subject to the terms of the
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock * Common Development and Distribution License (the "License").
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock * You may not use this file except in compliance with the License.
fa9e4066f08beec538e775443c5be79dd423fcabahrens * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
fa9e4066f08beec538e775443c5be79dd423fcabahrens * See the License for the specific language governing permissions
fa9e4066f08beec538e775443c5be79dd423fcabahrens * and limitations under the License.
fa9e4066f08beec538e775443c5be79dd423fcabahrens * When distributing Covered Code, include this CDDL HEADER in each
fa9e4066f08beec538e775443c5be79dd423fcabahrens * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
fa9e4066f08beec538e775443c5be79dd423fcabahrens * If applicable, add the following below this CDDL HEADER, with the
fa9e4066f08beec538e775443c5be79dd423fcabahrens * fields enclosed by brackets "[]" replaced with your own identifying
fa9e4066f08beec538e775443c5be79dd423fcabahrens * information: Portions Copyright [yyyy] [name of copyright owner]
fa9e4066f08beec538e775443c5be79dd423fcabahrens * CDDL HEADER END
e6032be1b8a5a1d03081e0d62b624db95c4cf8b7marks * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Use is subject to license terms.
fa9e4066f08beec538e775443c5be79dd423fcabahrens#pragma ident "%Z%%M% %I% %E% SMI"
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw#define MAX_ACE_TYPE ACE_SYSTEM_ALARM_CALLBACK_OBJECT_ACE_TYPE
fa9e4066f08beec538e775443c5be79dd423fcabahrens#define EVERYONE_ALLOW_MASK (ACE_READ_ACL|ACE_READ_ATTRIBUTES | \
fa9e4066f08beec538e775443c5be79dd423fcabahrens#define EVERYONE_DENY_MASK (ACE_WRITE_ACL|ACE_WRITE_OWNER | \
fa9e4066f08beec538e775443c5be79dd423fcabahrens#define OWNER_ALLOW_MASK (ACE_WRITE_ACL | ACE_WRITE_OWNER | \
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw#define WRITE_MASK_DATA (ACE_WRITE_DATA|ACE_APPEND_DATA|ACE_WRITE_NAMED_ATTRS)
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw#define ZFS_CHECKED_MASKS (ACE_READ_ACL|ACE_READ_ATTRIBUTES|ACE_READ_DATA| \
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw ACE_READ_NAMED_ATTRS|ACE_WRITE_DATA|ACE_WRITE_ATTRIBUTES| \
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw ACE_WRITE_NAMED_ATTRS|ACE_APPEND_DATA|ACE_EXECUTE|ACE_WRITE_OWNER| \
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw ACE_WRITE_ACL|ACE_DELETE|ACE_DELETE_CHILD|ACE_SYNCHRONIZE)
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw#define WRITE_MASK (WRITE_MASK_DATA|ACE_WRITE_ATTRIBUTES|ACE_WRITE_ACL|\
fa9e4066f08beec538e775443c5be79dd423fcabahrens#define OGE_CLEAR (ACE_READ_DATA|ACE_LIST_DIRECTORY|ACE_WRITE_DATA| \
fa9e4066f08beec538e775443c5be79dd423fcabahrens ACE_ADD_FILE|ACE_APPEND_DATA|ACE_ADD_SUBDIRECTORY|ACE_EXECUTE)
fa9e4066f08beec538e775443c5be79dd423fcabahrens#define OKAY_MASK_BITS (ACE_READ_DATA|ACE_LIST_DIRECTORY|ACE_WRITE_DATA| \
fa9e4066f08beec538e775443c5be79dd423fcabahrens ACE_ADD_FILE|ACE_APPEND_DATA|ACE_ADD_SUBDIRECTORY|ACE_EXECUTE)
fa9e4066f08beec538e775443c5be79dd423fcabahrens#define ALL_INHERIT (ACE_FILE_INHERIT_ACE|ACE_DIRECTORY_INHERIT_ACE | \
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw ACE_NO_PROPAGATE_INHERIT_ACE|ACE_INHERIT_ONLY_ACE|ACE_INHERITED_ACE)
b3d141f8c7a5335d670721a81f797b1834ee327bmarks#define RESTRICTED_CLEAR (ACE_WRITE_ACL|ACE_WRITE_OWNER)
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw#define V4_ACL_WIDE_FLAGS (ZFS_ACL_AUTO_INHERIT|ZFS_ACL_DEFAULTED|\
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw#define ZFS_ACL_WIDE_FLAGS (V4_ACL_WIDE_FLAGS|ZFS_ACL_TRIVIAL|ZFS_INHERIT_ACE|\
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amwstatic void
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amwstatic void
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amwstatic void
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amwstatic void
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw/*ARGSUSED*/
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw return (sizeof (zfs_oldace_t));
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw return (sizeof (zfs_oldace_t));
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw/*ARGSUSED*/
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw return (0);
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw if (entry_type == ACE_OWNER || entry_type == OWNING_GROUP ||
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw return (-1);
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amwstatic void
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amwstatic void
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amwstatic void
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amwstatic void
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw uint16_t entry_type = acep->z_hdr.z_flags & ACE_TYPE_FLAGS;
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw if (entry_type == ACE_OWNER || entry_type == OWNING_GROUP ||
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw return (sizeof (zfs_object_ace_t));
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw return (sizeof (zfs_ace_hdr_t));
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw /*FALLTHROUGH*/
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw return (sizeof (zfs_ace_t));
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw return (sizeof (zfs_ace_hdr_t));
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw return (sizeof (zfs_object_ace_t) - sizeof (zfs_ace_t));
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw return (0);
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw aclnode = kmem_zalloc(sizeof (zfs_acl_node_t), KM_SLEEP);
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amwstatic void
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amwzfs_ace_valid(vtype_t obj_type, zfs_acl_t *aclp, uint16_t type, uint16_t iflags)
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * first check type of entry
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw case 0: /* User entry */
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * next check inheritance level flags
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw switch (type) {
b249c65cf0a7400e86a36ddab5c3fce085809859marks (iflags & (ACE_FILE_INHERIT_ACE|ACE_DIRECTORY_INHERIT_ACE)))
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw if (iflags & (ACE_INHERIT_ONLY_ACE|ACE_NO_PROPAGATE_INHERIT_ACE)) {
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amwstatic void *
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amwzfs_acl_next_ace(zfs_acl_t *aclp, void *start, uint64_t *who,
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw uint32_t *access_mask, uint16_t *iflags, uint16_t *type)
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw return ((void *)acep);
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw/*ARGSUSED*/
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw zfs_ace_hdr_t *acep = (zfs_ace_hdr_t *)(uintptr_t)cookie;
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * Copy ACE to internal ZFS format.
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * While processing the ACL each ACE will be validated for correctness.
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * ACE FUIDs will be created later.
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amwzfs_copy_ace_2_fuid(vtype_t obj_type, zfs_acl_t *aclp, void *datap,
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw for (i = 0; i != aclcnt; i++) {
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw if (entry_type != ACE_OWNER && entry_type != OWNING_GROUP &&
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * Make sure ACE is valid
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw acep = (ace_t *)((caddr_t)acep + sizeof (ace_object_t));
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw return (0);
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * Copy ZFS ACEs to fixed size ace_t layout
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amwstatic void
bda89588bd7667394a834e8a9a34612cce2ae9c3jpzfs_copy_fuid_2_ace(zfsvfs_t *zfsvfs, zfs_acl_t *aclp, cred_t *cr,
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw switch (type) {
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amwzfs_copy_ace_2_oldace(vtype_t obj_type, zfs_acl_t *aclp, ace_t *acep,
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * Make sure ACE is valid
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw return (0);
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * convert old ACL format to new
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * First create the ACE in a contiguous piece of memory
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * for zfs_copy_ace_2_fuid().
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * We only convert an ACL once, so this won't happen
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * everytime.
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw oldaclp = kmem_alloc(sizeof (zfs_oldace_t) * aclp->z_acl_count,
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw VERIFY(zfs_copy_ace_2_fuid(ZTOV(zp)->v_type, aclp, oldaclp,
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw kmem_free(oldaclp, aclp->z_acl_count * sizeof (zfs_oldace_t));
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * Release all previous ACL nodes
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Convert unix access mask to v4 access mask
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amwzfs_set_ace(zfs_acl_t *aclp, void *acep, uint32_t access_mask,
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw uint16_t access_type, uint64_t fuid, uint16_t entry_type)
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw if ((type != ACE_OWNER && type != (ACE_GROUP | ACE_IDENTIFIER_GROUP) &&
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * Determine mode of file based on ACL.
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * Also, create FUIDs for any User/Group ACEs
bda89588bd7667394a834e8a9a34612cce2ae9c3jpzfs_mode_fuid_compute(znode_t *zp, zfs_acl_t *aclp, cred_t *cr,
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw mode = (zp->z_phys->zp_mode & (S_IFMT | S_ISUID | S_ISGID | S_ISVTX));
29a0b7379cd3103841d05e3ed04486412049acccmarks * Skip over inherit only ACEs
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * Now handle FUID create for user/group ACEs
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw if (entry_type == 0 || entry_type == ACE_IDENTIFIER_GROUP) {
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amwzfs_acl_node_read_internal(znode_t *zp, boolean_t will_modify)
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * Version 0 to 1 znode_acl_phys has the size/count fields swapped.
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * Version 0 didn't have a size field, only a count.
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw if (zp->z_phys->zp_acl.z_acl_version == ZFS_ACL_VERSION_INITIAL) {
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw aclnode = zfs_acl_node_alloc(will_modify ? aclp->z_acl_bytes : 0);
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw bcopy(zp->z_phys->zp_acl.z_ace_data, aclnode->z_acldata,
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Read an external acl object.
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amwzfs_acl_node_read(znode_t *zp, zfs_acl_t **aclpp, boolean_t will_modify)
fa9e4066f08beec538e775443c5be79dd423fcabahrens uint64_t extacl = zp->z_phys->zp_acl.z_acl_extern_obj;
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock return (0);
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw if (zp->z_phys->zp_acl.z_acl_version == ZFS_ACL_VERSION_INITIAL) {
b87f3af36bb994656da117319f5129ddfd05ed21perrin /* convert checksum errors into IO errors */
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock return (0);
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * common code for setting ACLs.
fa9e4066f08beec538e775443c5be79dd423fcabahrens * This function is called from zfs_mode_update, zfs_perm_init, and zfs_setacl.
fa9e4066f08beec538e775443c5be79dd423fcabahrens * zfs_setacl passes a non-NULL inherit pointer (ihp) to indicate that it's
fa9e4066f08beec538e775443c5be79dd423fcabahrens * already checked the acl and knows whether to inherit.
bda89588bd7667394a834e8a9a34612cce2ae9c3jpzfs_aclset_common(znode_t *zp, zfs_acl_t *aclp, cred_t *cr,
bda89588bd7667394a834e8a9a34612cce2ae9c3jp zphys->zp_mode = zfs_mode_fuid_compute(zp, aclp, cr, fuidp, tx);
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * Decide which opbject type to use. If we are forced to
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * use old ACL format than transform ACL into zfs_oldace_t
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * If ACL was previously external and we are now
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * converting to new ACL format then release old
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * ACL object and create a new one.
fa9e4066f08beec538e775443c5be79dd423fcabahrens if (aoid == 0) {
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Migrating back embedded?
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * If Old version then swap count/bytes to match old
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * layout of znode_acl_phys_t.
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * Replace ACL wide bits, but first clear them.
fa9e4066f08beec538e775443c5be79dd423fcabahrens return (0);
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Update access mask for prepended ACE
fa9e4066f08beec538e775443c5be79dd423fcabahrens * This applies the "groupmask" value for aclmode property.
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amwzfs_acl_prepend_fixup(zfs_acl_t *aclp, void *acep, void *origacep,
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Apply mode to canonical six ACEs.
fa9e4066f08beec538e775443c5be79dd423fcabahrenszfs_acl_fixup_canonical_six(zfs_acl_t *aclp, mode_t mode)
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Fixup final ACEs to match the mode
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amwzfs_acl_ace_match(zfs_acl_t *aclp, void *acep, int allow_deny,
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Can prepended ACE be reused?
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amwzfs_reuse_deny(zfs_acl_t *aclp, void *acep, void *prevacep)
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * Insert new ACL node into chain of zfs_acl_node_t's
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * This will result in two possible results.
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * 1. If the ACL is currently just a single zfs_acl_node and
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * we are prepending the entry then current acl node will have
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * a new node inserted above it.
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * 2. If we are inserting in the middle of current acl node then
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * the current node will be split in two and new node will be inserted
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * in between the two split nodes.
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw newnode = zfs_acl_node_alloc(aclp->z_ops.ace_size(acep));
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw currnode->z_size = (caddr_t)acep - (caddr_t)currnode->z_acldata;
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Prepend deny ACE
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amwstatic void *
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amwzfs_acl_prepend_deny(znode_t *zp, zfs_acl_t *aclp, void *acep,
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw zfs_set_ace(aclp, newacep, 0, DENY, fuid, (flags & ACE_TYPE_FLAGS));
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw zfs_acl_prepend_fixup(aclp, newacep, acep, mode, zp->z_phys->zp_uid);
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Split an inherited ACE into inherit_only ACE
fa9e4066f08beec538e775443c5be79dd423fcabahrens * and original ACE with inheritance flags stripped off.
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw aclp->z_ops.ace_flags_set(newacep, flags | ACE_INHERIT_ONLY_ACE);
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Are ACES started at index i, the canonical six ACES?
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw int i = 0;
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw return (0);
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw aclnode->z_size - (aclp->z_ops.ace_abstract_size() * 6));
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw if ((zfs_acl_ace_match(aclp, (caddr_t)acep + (abstract_size * i++),
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw zfs_acl_ace_match(aclp, (caddr_t)acep + (abstract_size * i++),
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw zfs_acl_ace_match(aclp, (caddr_t)acep + (abstract_size * i++), DENY,
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw OWNING_GROUP, 0) && zfs_acl_ace_match(aclp, (caddr_t)acep +
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw zfs_acl_ace_match(aclp, (caddr_t)acep + (abstract_size * i++),
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw zfs_acl_ace_match(aclp, (caddr_t)acep + (abstract_size * i++),
fa9e4066f08beec538e775443c5be79dd423fcabahrens return (1);
fa9e4066f08beec538e775443c5be79dd423fcabahrens return (0);
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Apply step 1g, to group entries
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Need to deal with corner case where group may have
fa9e4066f08beec538e775443c5be79dd423fcabahrens * greater permissions than owner. If so then limit
fa9e4066f08beec538e775443c5be79dd423fcabahrens * group permissions, based on what extra permissions
fa9e4066f08beec538e775443c5be79dd423fcabahrens * group has.
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amwzfs_fixup_group_entries(zfs_acl_t *aclp, void *acep, void *prevacep,
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw uint16_t prevflags = aclp->z_ops.ace_flags_get(prevacep);
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Apply the chmod algorithm as described
fa9e4066f08beec538e775443c5be79dd423fcabahrens * in PSARC/2002/240
4c841f6070b4f88f9dc008de526b313bbebd4e32markszfs_acl_chmod(znode_t *zp, uint64_t mode, zfs_acl_t *aclp)
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw aclp->z_hints = (zp->z_phys->zp_flags & V4_ACL_WIDE_FLAGS);
2459a9eaca6b6525c76289d22ffe4c96be1956d6marks * If discard then just discard all ACL nodes which
2459a9eaca6b6525c76289d22ffe4c96be1956d6marks * represent the ACEs.
2459a9eaca6b6525c76289d22ffe4c96be1956d6marks * New owner@/group@/everone@ ACEs will be added
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw while (acep = zfs_acl_next_ace(aclp, acep, &who, &access_mask,
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw switch (type) {
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Need to split ace into two?
fa9e4066f08beec538e775443c5be79dd423fcabahrens if (entry_type == ACE_OWNER || entry_type == ACE_EVERYONE ||
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Check preceding ACE if any, to see
fa9e4066f08beec538e775443c5be79dd423fcabahrens * if we need to prepend a DENY ACE.
fa9e4066f08beec538e775443c5be79dd423fcabahrens * This is only applicable when the acl_mode
fa9e4066f08beec538e775443c5be79dd423fcabahrens * property == groupmask.
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Check out last six aces, if we have six.
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw zfs_set_ace(aclp, (caddr_t)zacep + (abstract_size * i++),
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw zfs_set_ace(aclp, (caddr_t)zacep + (abstract_size * i++),
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw zfs_set_ace(aclp, (caddr_t)zacep + (abstract_size * i++), 0,
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw zfs_set_ace(aclp, (caddr_t)zacep + (abstract_size * i++), 0,
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw zfs_set_ace(aclp, (caddr_t)zacep + (abstract_size * i++),
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw zfs_set_ace(aclp, (caddr_t)zacep + (abstract_size * i++),
4c841f6070b4f88f9dc008de526b313bbebd4e32markszfs_acl_chmod_setattr(znode_t *zp, zfs_acl_t **aclp, uint64_t mode)
fa9e4066f08beec538e775443c5be79dd423fcabahrens * strip off write_owner and write_acl
b3d141f8c7a5335d670721a81f797b1834ee327bmarkszfs_restricted_update(zfsvfs_t *zfsvfs, zfs_acl_t *aclp, void *acep)
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * Should ACE be inherited?
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw if ((vtype == VDIR) && (iflags & ACE_DIRECTORY_INHERIT_ACE))
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw return (1);
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw return (0);
fa9e4066f08beec538e775443c5be79dd423fcabahrens * inherit inheritable ACEs from parent
b3d141f8c7a5335d670721a81f797b1834ee327bmarkszfs_acl_inherit(znode_t *zp, zfs_acl_t *paclp, boolean_t *need_chmod)
b3d141f8c7a5335d670721a81f797b1834ee327bmarks * If owner@, group@, or everyone@ inheritable
b3d141f8c7a5335d670721a81f797b1834ee327bmarks * then zfs_acl_chmod() isn't needed.
b3d141f8c7a5335d670721a81f797b1834ee327bmarks * Copy special opaque data if any
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * Copy special opaque data if any
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw &data1)) != 0) {
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Create file system object initial permissions
fa9e4066f08beec538e775443c5be79dd423fcabahrens * including inheritable ACEs.
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Determine uid and gid.
e0d35c4478bf9fd4080951b5b9d1f9a38948ba69marks fuid = zfs_fuid_create_cred(zfsvfs, ZFS_OWNER, tx, cr, fuidp);
e0d35c4478bf9fd4080951b5b9d1f9a38948ba69marks if (fgid == 0) {
fa9e4066f08beec538e775443c5be79dd423fcabahrens * If we're creating a directory, and the parent directory has the
fa9e4066f08beec538e775443c5be79dd423fcabahrens * set-GID bit set, set in on the new directory.
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Otherwise, if the user is neither privileged nor a member of the
fa9e4066f08beec538e775443c5be79dd423fcabahrens * file's new group, clear the file's set-GID bit.
e0d35c4478bf9fd4080951b5b9d1f9a38948ba69marks if ((parent->z_phys->zp_mode & S_ISGID) && (vap->va_type == VDIR)) {
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw VERIFY(0 == zfs_acl_node_read(parent, &paclp, B_FALSE));
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw /* Force auto_inherit on all new directory objects */
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw /* Set optional attributes if any */
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Retrieve a files ACL
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amwzfs_getacl(znode_t *zp, vsecattr_t *vsecp, boolean_t skipaclchk, cred_t *cr)
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw if (error = zfs_zaccess(zp, ACE_READ_ACL, 0, skipaclchk, cr))
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * Scan ACL to determine number of ACEs
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw switch (type) {
fa9e4066f08beec538e775443c5be79dd423fcabahrens return (0);
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw if (vsecp->vsa_aclcnt > MAX_ACL_ENTRIES || vsecp->vsa_aclcnt <= 0)
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw aclp = zfs_acl_alloc(zfs_acl_version(zfsvfs->z_version));
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw aclnode = zfs_acl_node_alloc(aclcnt * sizeof (zfs_object_ace_t));
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * If flags are being set then add them to z_hints
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw return (0);
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Set a files ACL
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amwzfs_setacl(znode_t *zp, vsecattr_t *vsecp, boolean_t skipaclchk, cred_t *cr)
fa9e4066f08beec538e775443c5be79dd423fcabahrens ulong_t mask = vsecp->vsa_mask & (VSA_ACE | VSA_ACECNT);
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw if (error = zfs_zaccess(zp, ACE_WRITE_ACL, 0, skipaclchk, cr))
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw error = zfs_vsec_2_aclp(zfsvfs, ZTOV(zp)->v_type, vsecp, &aclp);
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * If ACL wide flags aren't being set then preserve any
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * existing flags.
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw aclp->z_hints |= (zp->z_phys->zp_flags & V4_ACL_WIDE_FLAGS);
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw if (error = zfs_zaccess(zp, ACE_WRITE_ACL, 0, skipaclchk, cr)) {
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw /* Are we upgrading ACL? */
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw dmu_tx_hold_write(tx, DMU_NEW_OBJECT, 0, aclp->z_acl_bytes);
fa9e4066f08beec538e775443c5be79dd423fcabahrens if (error == ERESTART && zfsvfs->z_assign == TXG_NOWAIT) {
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * working_mode returns the permissions that were not granted
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amwzfs_zaccess_common(znode_t *zp, uint32_t v4_mode, uint32_t *working_mode,
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw boolean_t *check_privs, boolean_t skipaclchk, cred_t *cr)
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * Short circuit empty requests
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw return (0);
b19a79ec1a527828a60c4d325ccd8dcbeb2b2e8bperrin if (zfsvfs->z_assign >= TXG_INITIAL) { /* ZIL replay */
fa9e4066f08beec538e775443c5be79dd423fcabahrens return (0);
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * Only check for READONLY on non-directories.
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw (zp->z_phys->zp_flags & (ZFS_READONLY | ZFS_IMMUTABLE))) ||
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * The caller requested that the ACL check be skipped. This
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * would only happen if the caller checked VOP_ACCESS() with a
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * 32 bit ACE mask and already had the appropriate permissions.
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw return (0);
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw while (acep = zfs_acl_next_ace(aclp, acep, &who, &access_mask,
b249c65cf0a7400e86a36ddab5c3fce085809859marks if (ZTOV(zp)->v_type == VDIR && (iflags & ACE_INHERIT_ONLY_ACE))
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw /*FALLTHROUGH*/
fa9e4066f08beec538e775443c5be79dd423fcabahrens /* USER Entry */
90fafcf0df314d4b61e54f05ce41f7e1c06a025amarks uint32_t mask_matched = (access_mask & *working_mode);
90fafcf0df314d4b61e54f05ce41f7e1c06a025amarks /* Are we done? */
90fafcf0df314d4b61e54f05ce41f7e1c06a025amarks /* Put the found 'denies' back on the working mode */
7ed7e920b3b51c792a1541c93ef3d50f4a07763emarks } else if (*working_mode) {
7ed7e920b3b51c792a1541c93ef3d50f4a07763emarks return (-1);
90fafcf0df314d4b61e54f05ce41f7e1c06a025amarks return (0);
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amwzfs_zaccess_append(znode_t *zp, uint32_t *working_mode, boolean_t *check_privs,
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw return (zfs_zaccess_common(zp, ACE_APPEND_DATA, working_mode,
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Determine whether Access should be granted/denied, invoking least
fa9e4066f08beec538e775443c5be79dd423fcabahrens * priv subsytem when a deny is determined.
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amwzfs_zaccess(znode_t *zp, int mode, int flags, boolean_t skipaclchk, cred_t *cr)
fa9e4066f08beec538e775443c5be79dd423fcabahrens * If attribute then validate against base file
fa9e4066f08beec538e775443c5be79dd423fcabahrens * fixup mode to map to xattr perms
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw if ((error = zfs_zaccess_common(check_zp, mode, &working_mode,
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw return (0);
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw error = zfs_zaccess_append(zp, &working_mode, &check_privs, cr);
e0d35c4478bf9fd4080951b5b9d1f9a38948ba69marks owner = zfs_fuid_map_id(zfsvfs, check_zp->z_phys->zp_uid, cr,
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * First check for implicit owner permission on
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw if ((working_mode & (ACE_READ_ACL|ACE_READ_ATTRIBUTES) &&
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw if (working_mode & (ACE_WRITE_DATA|ACE_WRITE_NAMED_ATTRS|
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw if (error == 0) {
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * See if any bits other than those already checked
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * for are still present. If so then return EACCES
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * Translate traditional unix VREAD/VWRITE/VEXEC mode into
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * native ACL format and call zfs_zaccess()
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amwzfs_zaccess_rwx(znode_t *zp, mode_t mode, int flags, cred_t *cr)
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw return (zfs_zaccess(zp, zfs_unix_to_v4(mode >> 6), flags, B_FALSE, cr));
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * Access function for secpolicy_vnode_setattr
e0d35c4478bf9fd4080951b5b9d1f9a38948ba69marks downer = zfs_fuid_map_id(zfsvfs, dzp->z_phys->zp_uid, cr, ZFS_OWNER);
23d5bb1f2275e3b549733444de8e92fb47d0631bmarks error = secpolicy_vnode_access(cr, ZTOV(dzp), downer, missing_perms);
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Determine whether Access should be granted/deny, without
fa9e4066f08beec538e775443c5be79dd423fcabahrens * consulting least priv subsystem.
fa9e4066f08beec538e775443c5be79dd423fcabahrens * The following chart is the recommended NFSv4 enforcement for
fa9e4066f08beec538e775443c5be79dd423fcabahrens * ability to delete an object.
fa9e4066f08beec538e775443c5be79dd423fcabahrens * -------------------------------------------------------
fa9e4066f08beec538e775443c5be79dd423fcabahrens * | Parent Dir | Target Object Permissions |
fa9e4066f08beec538e775443c5be79dd423fcabahrens * | permissions | |
fa9e4066f08beec538e775443c5be79dd423fcabahrens * -------------------------------------------------------
fa9e4066f08beec538e775443c5be79dd423fcabahrens * | | ACL Allows | ACL Denies| Delete |
fa9e4066f08beec538e775443c5be79dd423fcabahrens * | | Delete | Delete | unspecified|
fa9e4066f08beec538e775443c5be79dd423fcabahrens * -------------------------------------------------------
fa9e4066f08beec538e775443c5be79dd423fcabahrens * | ACL Allows | Permit | Permit | Permit |
fa9e4066f08beec538e775443c5be79dd423fcabahrens * | DELETE_CHILD | |
fa9e4066f08beec538e775443c5be79dd423fcabahrens * -------------------------------------------------------
fa9e4066f08beec538e775443c5be79dd423fcabahrens * | ACL Denies | Permit | Deny | Deny |
fa9e4066f08beec538e775443c5be79dd423fcabahrens * | DELETE_CHILD | | | |
fa9e4066f08beec538e775443c5be79dd423fcabahrens * -------------------------------------------------------
fa9e4066f08beec538e775443c5be79dd423fcabahrens * | ACL specifies | | | |
fa9e4066f08beec538e775443c5be79dd423fcabahrens * | only allow | Permit | Permit | Permit |
fa9e4066f08beec538e775443c5be79dd423fcabahrens * | write and | | | |
fa9e4066f08beec538e775443c5be79dd423fcabahrens * | execute | | | |
fa9e4066f08beec538e775443c5be79dd423fcabahrens * -------------------------------------------------------
fa9e4066f08beec538e775443c5be79dd423fcabahrens * | ACL denies | | | |
fa9e4066f08beec538e775443c5be79dd423fcabahrens * | write and | Permit | Deny | Deny |
fa9e4066f08beec538e775443c5be79dd423fcabahrens * | execute | | | |
fa9e4066f08beec538e775443c5be79dd423fcabahrens * -------------------------------------------------------
fa9e4066f08beec538e775443c5be79dd423fcabahrens * No search privilege, can't even look up file?
fa9e4066f08beec538e775443c5be79dd423fcabahrenszfs_zaccess_delete(znode_t *dzp, znode_t *zp, cred_t *cr)
23d5bb1f2275e3b549733444de8e92fb47d0631bmarks * We want specific DELETE permissions to
fa9e4066f08beec538e775443c5be79dd423fcabahrens * take precedence over WRITE/EXECUTE. We don't
fa9e4066f08beec538e775443c5be79dd423fcabahrens * want an ACL such as this to mess us up.
47db7e746eb0bef03d3708b9d5b48f4ca49968bamarks * user:joe:write_data:deny,user:joe:delete:allow
fa9e4066f08beec538e775443c5be79dd423fcabahrens * However, deny permissions may ultimately be overridden
fa9e4066f08beec538e775443c5be79dd423fcabahrens * by secpolicy_vnode_access().
23d5bb1f2275e3b549733444de8e92fb47d0631bmarks * We will ask for all of the necessary permissions and then
23d5bb1f2275e3b549733444de8e92fb47d0631bmarks * look at the working modes from the directory and target object
23d5bb1f2275e3b549733444de8e92fb47d0631bmarks * to determine what was found.
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw if (zp->z_phys->zp_flags & (ZFS_IMMUTABLE | ZFS_NOUNLINK))
7ed7e920b3b51c792a1541c93ef3d50f4a07763emarks * First row
23d5bb1f2275e3b549733444de8e92fb47d0631bmarks * If the directory permissions allow the delete, we are done.
7ed7e920b3b51c792a1541c93ef3d50f4a07763emarks if ((dzp_error = zfs_zaccess_common(dzp, ACE_DELETE_CHILD,
23d5bb1f2275e3b549733444de8e92fb47d0631bmarks &dzp_working_mode, &dzpcheck_privs, B_FALSE, cr)) == 0)
23d5bb1f2275e3b549733444de8e92fb47d0631bmarks return (0);
23d5bb1f2275e3b549733444de8e92fb47d0631bmarks * If target object has delete permission then we are done
23d5bb1f2275e3b549733444de8e92fb47d0631bmarks if ((zp_error = zfs_zaccess_common(zp, ACE_DELETE, &zp_working_mode,
23d5bb1f2275e3b549733444de8e92fb47d0631bmarks return (0);
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Second row
7ed7e920b3b51c792a1541c93ef3d50f4a07763emarks * If directory returns EACCES then delete_child was denied
7ed7e920b3b51c792a1541c93ef3d50f4a07763emarks * due to deny delete_child. In this case send the request through
7ed7e920b3b51c792a1541c93ef3d50f4a07763emarks * secpolicy_vnode_remove(). We don't use zfs_delete_final_check()
7ed7e920b3b51c792a1541c93ef3d50f4a07763emarks * since that *could* allow the delete based on write/execute permission
7ed7e920b3b51c792a1541c93ef3d50f4a07763emarks * and we want delete permissions to override write/execute.
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Third Row
23d5bb1f2275e3b549733444de8e92fb47d0631bmarks * only need to see if we have write/execute on directory.
7ed7e920b3b51c792a1541c93ef3d50f4a07763emarks if ((dzp_error = zfs_zaccess_common(dzp, ACE_EXECUTE|ACE_WRITE_DATA,
7ed7e920b3b51c792a1541c93ef3d50f4a07763emarks &dzp_working_mode, &dzpcheck_privs, B_FALSE, cr)) == 0)
7ed7e920b3b51c792a1541c93ef3d50f4a07763emarks * Fourth row
7ed7e920b3b51c792a1541c93ef3d50f4a07763emarks missing_perms = (dzp_working_mode & ACE_WRITE_DATA) ? VWRITE : 0;
7ed7e920b3b51c792a1541c93ef3d50f4a07763emarks missing_perms |= (dzp_working_mode & ACE_EXECUTE) ? VEXEC : 0;
23d5bb1f2275e3b549733444de8e92fb47d0631bmarks return (zfs_delete_final_check(zp, dzp, missing_perms, cr));
fa9e4066f08beec538e775443c5be79dd423fcabahrenszfs_zaccess_rename(znode_t *sdzp, znode_t *szp, znode_t *tdzp,
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Rename permissions are combination of delete permission +
fa9e4066f08beec538e775443c5be79dd423fcabahrens * add file/subdir permission.
fa9e4066f08beec538e775443c5be79dd423fcabahrens * first make sure we do the delete portion.
fa9e4066f08beec538e775443c5be79dd423fcabahrens * If that succeeds then check for add_file/add_subdir permissions
fa9e4066f08beec538e775443c5be79dd423fcabahrens * If we have a tzp, see if we can delete it?
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Now check for add permissions