* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
- * or http://www.opensolaris.org/os/licensing.
+ * or https://opensource.org/licenses/CDDL-1.0.
* See the License for the specific language governing permissions
* and limitations under the License.
*
((zfs_oldace_t *)acep)->z_fuid = who;
}
-/*ARGSUSED*/
static size_t
zfs_ace_v0_size(void *acep)
{
+ (void) acep;
return (sizeof (zfs_oldace_t));
}
return (offsetof(zfs_oldace_t, z_access_mask));
}
-/*ARGSUSED*/
static int
zfs_ace_v0_data(void *acep, void **datap)
{
+ (void) acep;
*datap = NULL;
return (0);
}
-static acl_ops_t zfs_acl_v0_ops = {
+static const acl_ops_t zfs_acl_v0_ops = {
.ace_mask_get = zfs_ace_v0_get_mask,
.ace_mask_set = zfs_ace_v0_set_mask,
.ace_flags_get = zfs_ace_v0_get_flags,
entry_type == OWNING_GROUP ||
entry_type == ACE_EVERYONE)
return (sizeof (zfs_ace_hdr_t));
- /*FALLTHROUGH*/
+ zfs_fallthrough;
default:
return (sizeof (zfs_ace_t));
}
}
}
-static acl_ops_t zfs_acl_fuid_ops = {
+static const acl_ops_t zfs_acl_fuid_ops = {
.ace_mask_get = zfs_ace_fuid_get_mask,
.ace_mask_set = zfs_ace_fuid_set_mask,
.ace_flags_get = zfs_ace_fuid_get_flags,
{
zfs_acl_node_t *aclnode;
- while ((aclnode = list_head(&aclp->z_acl))) {
- list_remove(&aclp->z_acl, aclnode);
+ while ((aclnode = list_remove_head(&aclp->z_acl)))
zfs_acl_node_free(aclnode);
- }
aclp->z_acl_count = 0;
aclp->z_acl_bytes = 0;
}
entry_type == ACE_EVERYONE || entry_type == 0 ||
entry_type == ACE_IDENTIFIER_GROUP);
default:
- if (type >= MIN_ACE_TYPE && type <= MAX_ACE_TYPE)
+ if (type <= MAX_ACE_TYPE)
return (B_TRUE);
}
return (B_FALSE);
return (NULL);
}
-/*ARGSUSED*/
-static uint64_t
-zfs_ace_walk(void *datap, uint64_t cookie, int aclcnt,
+static uintptr_t
+zfs_ace_walk(void *datap, uintptr_t cookie, int aclcnt,
uint16_t *flags, uint16_t *type, uint32_t *mask)
{
+ (void) aclcnt;
zfs_acl_t *aclp = datap;
- zfs_ace_hdr_t *acep = (zfs_ace_hdr_t *)(uintptr_t)cookie;
+ zfs_ace_hdr_t *acep = (zfs_ace_hdr_t *)cookie;
uint64_t who;
acep = zfs_acl_next_ace(aclp, acep, &who, mask,
flags, type);
- return ((uint64_t)(uintptr_t)acep);
+ return ((uintptr_t)acep);
}
/*
zobjacep = (zfs_object_ace_t *)aceptr;
aceobjp = (ace_object_t *)acep;
- bcopy(aceobjp->a_obj_type, zobjacep->z_object_type,
+ memcpy(zobjacep->z_object_type, aceobjp->a_obj_type,
sizeof (aceobjp->a_obj_type));
- bcopy(aceobjp->a_inherit_obj_type,
- zobjacep->z_inherit_type,
+ memcpy(zobjacep->z_inherit_type,
+ aceobjp->a_inherit_obj_type,
sizeof (aceobjp->a_inherit_obj_type));
acep = (ace_t *)((caddr_t)acep + sizeof (ace_object_t));
break;
}
zobjacep = (zfs_object_ace_t *)zacep;
objacep = (ace_object_t *)acep;
- bcopy(zobjacep->z_object_type,
- objacep->a_obj_type,
+ memcpy(objacep->a_obj_type,
+ zobjacep->z_object_type,
sizeof (zobjacep->z_object_type));
- bcopy(zobjacep->z_inherit_type,
- objacep->a_inherit_obj_type,
+ memcpy(objacep->a_inherit_obj_type,
+ zobjacep->z_inherit_type,
sizeof (zobjacep->z_inherit_type));
ace_size = sizeof (ace_object_t);
break;
return (new_mask);
}
+
+static int
+zfs_v4_to_unix(uint32_t access_mask, int *unmapped)
+{
+ int new_mask = 0;
+
+ *unmapped = access_mask &
+ (ACE_WRITE_OWNER | ACE_WRITE_ACL | ACE_DELETE);
+
+ if (access_mask & WRITE_MASK)
+ new_mask |= S_IWOTH;
+ if (access_mask & ACE_READ_DATA)
+ new_mask |= S_IROTH;
+ if (access_mask & ACE_EXECUTE)
+ new_mask |= S_IXOTH;
+
+ return (new_mask);
+}
+
+
static void
zfs_set_ace(zfs_acl_t *aclp, void *acep, uint32_t access_mask,
uint16_t access_type, uint64_t fuid, uint16_t entry_type)
znode_acl.z_acl_extern_obj, 0, aclnode->z_size,
aclnode->z_acldata, DMU_READ_PREFETCH);
} else {
- bcopy(znode_acl.z_ace_data, aclnode->z_acldata,
+ memcpy(aclnode->z_acldata, znode_acl.z_ace_data,
aclnode->z_size);
}
} else {
return (error);
}
-/*ARGSUSED*/
void
zfs_acl_data_locator(void **dataptr, uint32_t *length, uint32_t buflen,
boolean_t start, void *userdata)
{
+ (void) buflen;
zfs_acl_locator_cb_t *cb = (zfs_acl_locator_cb_t *)userdata;
if (start) {
cb->cb_acl_node = list_next(&cb->cb_aclp->z_acl,
cb->cb_acl_node);
}
+ ASSERT3P(cb->cb_acl_node, !=, NULL);
*dataptr = cb->cb_acl_node->z_acldata;
*length = cb->cb_acl_node->z_size;
}
int error;
zfs_acl_t *aclp;
- if (ZTOZSB(zp)->z_acl_type == ZFS_ACLTYPE_POSIXACL)
+ if (ZTOZSB(zp)->z_acl_type == ZFS_ACLTYPE_POSIX)
return (0);
ASSERT(MUTEX_HELD(&zp->z_lock));
*/
static int
ace_trivial_common(void *acep, int aclcnt,
- uint64_t (*walk)(void *, uint64_t, int aclcnt,
+ uintptr_t (*walk)(void *, uintptr_t, int,
uint16_t *, uint16_t *, uint32_t *))
{
uint16_t flags;
aclnode = list_next(&aclp->z_acl, aclnode)) {
if (aclnode->z_ace_count == 0)
continue;
- bcopy(aclnode->z_acldata, start,
+ memcpy(start, aclnode->z_acldata,
aclnode->z_size);
start = (caddr_t)start + aclnode->z_size;
}
if ((data1sz = paclp->z_ops->ace_data(pacep, &data1)) != 0) {
VERIFY((data2sz = aclp->z_ops->ace_data(acep,
&data2)) == data1sz);
- bcopy(data1, data2, data2sz);
+ memcpy(data2, data1, data2sz);
}
aclp->z_acl_count++;
*/
int
zfs_acl_ids_create(znode_t *dzp, int flag, vattr_t *vap, cred_t *cr,
- vsecattr_t *vsecp, zfs_acl_ids_t *acl_ids)
+ vsecattr_t *vsecp, zfs_acl_ids_t *acl_ids, zidmap_t *mnt_ns)
{
int error;
zfsvfs_t *zfsvfs = ZTOZSB(dzp);
boolean_t trim = B_FALSE;
boolean_t inherited = B_FALSE;
- bzero(acl_ids, sizeof (zfs_acl_ids_t));
+ memset(acl_ids, 0, sizeof (zfs_acl_ids_t));
acl_ids->z_mode = vap->va_mode;
if (vsecp)
acl_ids->z_mode |= S_ISGID;
} else {
if ((acl_ids->z_mode & S_ISGID) &&
- secpolicy_vnode_setids_setgids(cr, gid) != 0)
+ secpolicy_vnode_setids_setgids(cr, gid, mnt_ns,
+ zfs_i_user_ns(ZTOI(dzp))) != 0) {
acl_ids->z_mode &= ~S_ISGID;
+ }
}
if (acl_ids->z_aclp == NULL) {
zfsvfs->z_acl_inherit != ZFS_ACL_PASSTHROUGH &&
zfsvfs->z_acl_inherit != ZFS_ACL_PASSTHROUGH_X)
trim = B_TRUE;
- zfs_acl_chmod(vap->va_mode, acl_ids->z_mode, B_FALSE,
- trim, acl_ids->z_aclp);
+ zfs_acl_chmod(S_ISDIR(vap->va_mode), acl_ids->z_mode,
+ B_FALSE, trim, acl_ids->z_aclp);
}
}
if (mask == 0)
return (SET_ERROR(ENOSYS));
- if ((error = zfs_zaccess(zp, ACE_READ_ACL, 0, skipaclchk, cr)))
+ if ((error = zfs_zaccess(zp, ACE_READ_ACL, 0, skipaclchk, cr,
+ zfs_init_idmap)))
return (error);
mutex_enter(&zp->z_acl_lock);
for (aclnode = list_head(&aclp->z_acl); aclnode;
aclnode = list_next(&aclp->z_acl, aclnode)) {
- bcopy(aclnode->z_acldata, start,
+ memcpy(start, aclnode->z_acldata,
aclnode->z_size);
start = (caddr_t)start + aclnode->z_size;
}
if (zp->z_pflags & ZFS_IMMUTABLE)
return (SET_ERROR(EPERM));
- if ((error = zfs_zaccess(zp, ACE_WRITE_ACL, 0, skipaclchk, cr)))
+ if ((error = zfs_zaccess(zp, ACE_WRITE_ACL, 0, skipaclchk, cr,
+ zfs_init_idmap)))
return (error);
error = zfs_vsec_2_aclp(zfsvfs, ZTOI(zp)->i_mode, vsecp, cr, &fuidp,
zfs_zaccess_dataset_check(znode_t *zp, uint32_t v4_mode)
{
if ((v4_mode & WRITE_MASK) && (zfs_is_readonly(ZTOZSB(zp))) &&
- (!Z_ISDEV(ZTOI(zp)->i_mode) ||
- (Z_ISDEV(ZTOI(zp)->i_mode) && (v4_mode & WRITE_MASK_ATTRS)))) {
+ (!Z_ISDEV(ZTOI(zp)->i_mode) || (v4_mode & WRITE_MASK_ATTRS))) {
return (SET_ERROR(EROFS));
}
/*
- * Only check for READONLY on non-directories.
+ * Intentionally allow ZFS_READONLY through here.
+ * See zfs_zaccess_common().
*/
if ((v4_mode & WRITE_MASK_DATA) &&
- ((!S_ISDIR(ZTOI(zp)->i_mode) &&
- (zp->z_pflags & (ZFS_READONLY | ZFS_IMMUTABLE))) ||
- (S_ISDIR(ZTOI(zp)->i_mode) &&
- (zp->z_pflags & ZFS_IMMUTABLE)))) {
+ (zp->z_pflags & ZFS_IMMUTABLE)) {
return (SET_ERROR(EPERM));
}
*/
static int
zfs_zaccess_aces_check(znode_t *zp, uint32_t *working_mode,
- boolean_t anyaccess, cred_t *cr)
+ boolean_t anyaccess, cred_t *cr, zidmap_t *mnt_ns)
{
zfsvfs_t *zfsvfs = ZTOZSB(zp);
zfs_acl_t *aclp;
uid_t gowner;
uid_t fowner;
- zfs_fuid_map_ids(zp, cr, &fowner, &gowner);
+ if (mnt_ns) {
+ fowner = zfs_uid_to_vfsuid(mnt_ns, zfs_i_user_ns(ZTOI(zp)),
+ KUID_TO_SUID(ZTOI(zp)->i_uid));
+ gowner = zfs_gid_to_vfsgid(mnt_ns, zfs_i_user_ns(ZTOI(zp)),
+ KGID_TO_SGID(ZTOI(zp)->i_gid));
+ } else
+ zfs_fuid_map_ids(zp, cr, &fowner, &gowner);
mutex_enter(&zp->z_acl_lock);
break;
case OWNING_GROUP:
who = gowner;
- /*FALLTHROUGH*/
+ zfs_fallthrough;
case ACE_IDENTIFIER_GROUP:
checkit = zfs_groupmember(zfsvfs, who, cr);
break;
{
uint32_t have = ACE_ALL_PERMS;
- if (zfs_zaccess_aces_check(zp, &have, B_TRUE, cr) != 0) {
+ if (zfs_zaccess_aces_check(zp, &have, B_TRUE, cr,
+ zfs_init_idmap) != 0) {
uid_t owner;
owner = zfs_fuid_map_id(ZTOZSB(zp),
return (B_TRUE);
}
+/*
+ * Simplified access check for case where ACL is known to not contain
+ * information beyond what is defined in the mode. In this case, we
+ * can pass along to the kernel / vfs generic_permission() check, which
+ * evaluates the mode and POSIX ACL.
+ *
+ * NFSv4 ACLs allow granting permissions that are usually relegated only
+ * to the file owner or superuser. Examples are ACE_WRITE_OWNER (chown),
+ * ACE_WRITE_ACL(chmod), and ACE_DELETE. ACE_DELETE requests must fail
+ * because with conventional posix permissions, right to delete file
+ * is determined by write bit on the parent dir.
+ *
+ * If unmappable perms are requested, then we must return EPERM
+ * and include those bits in the working_mode so that the caller of
+ * zfs_zaccess_common() can decide whether to perform additional
+ * policy / capability checks. EACCES is used in zfs_zaccess_aces_check()
+ * to indicate access check failed due to explicit DENY entry, and so
+ * we want to avoid that here.
+ */
+static int
+zfs_zaccess_trivial(znode_t *zp, uint32_t *working_mode, cred_t *cr,
+ zidmap_t *mnt_ns)
+{
+ int err, mask;
+ int unmapped = 0;
+
+ ASSERT(zp->z_pflags & ZFS_ACL_TRIVIAL);
+
+ mask = zfs_v4_to_unix(*working_mode, &unmapped);
+ if (mask == 0 || unmapped) {
+ *working_mode = unmapped;
+ return (unmapped ? SET_ERROR(EPERM) : 0);
+ }
+
+#if (defined(HAVE_IOPS_PERMISSION_USERNS) || \
+ defined(HAVE_IOPS_PERMISSION_IDMAP))
+ err = generic_permission(mnt_ns, ZTOI(zp), mask);
+#else
+ err = generic_permission(ZTOI(zp), mask);
+#endif
+ if (err != 0) {
+ return (SET_ERROR(EPERM));
+ }
+
+ *working_mode = unmapped;
+
+ return (0);
+}
+
static int
zfs_zaccess_common(znode_t *zp, uint32_t v4_mode, uint32_t *working_mode,
- boolean_t *check_privs, boolean_t skipaclchk, cred_t *cr)
+ boolean_t *check_privs, boolean_t skipaclchk, cred_t *cr, zidmap_t *mnt_ns)
{
zfsvfs_t *zfsvfs = ZTOZSB(zp);
int err;
return (0);
}
- return (zfs_zaccess_aces_check(zp, working_mode, B_FALSE, cr));
+ /*
+ * Note: ZFS_READONLY represents the "DOS R/O" attribute.
+ * When that flag is set, we should behave as if write access
+ * were not granted by anything in the ACL. In particular:
+ * We _must_ allow writes after opening the file r/w, then
+ * setting the DOS R/O attribute, and writing some more.
+ * (Similar to how you can write after fchmod(fd, 0444).)
+ *
+ * Therefore ZFS_READONLY is ignored in the dataset check
+ * above, and checked here as if part of the ACL check.
+ * Also note: DOS R/O is ignored for directories.
+ */
+ if ((v4_mode & WRITE_MASK_DATA) &&
+ S_ISDIR(ZTOI(zp)->i_mode) &&
+ (zp->z_pflags & ZFS_READONLY)) {
+ return (SET_ERROR(EPERM));
+ }
+
+ if (zp->z_pflags & ZFS_ACL_TRIVIAL)
+ return (zfs_zaccess_trivial(zp, working_mode, cr, mnt_ns));
+
+ return (zfs_zaccess_aces_check(zp, working_mode, B_FALSE, cr, mnt_ns));
}
static int
zfs_zaccess_append(znode_t *zp, uint32_t *working_mode, boolean_t *check_privs,
- cred_t *cr)
+ cred_t *cr, zidmap_t *mnt_ns)
{
if (*working_mode != ACE_WRITE_DATA)
return (SET_ERROR(EACCES));
return (zfs_zaccess_common(zp, ACE_APPEND_DATA, working_mode,
- check_privs, B_FALSE, cr));
+ check_privs, B_FALSE, cr, mnt_ns));
}
int
}
if (uid == KUID_TO_SUID(ZTOI(zdp)->i_uid)) {
- owner = B_TRUE;
if (zdp->z_mode & S_IXUSR) {
mutex_exit(&zdp->z_acl_lock);
return (0);
}
}
if (groupmember(KGID_TO_SGID(ZTOI(zdp)->i_gid), cr)) {
- groupmbr = B_TRUE;
if (zdp->z_mode & S_IXGRP) {
mutex_exit(&zdp->z_acl_lock);
return (0);
slow:
DTRACE_PROBE(zfs__fastpath__execute__access__miss);
- ZFS_ENTER(ZTOZSB(zdp));
- error = zfs_zaccess(zdp, ACE_EXECUTE, 0, B_FALSE, cr);
- ZFS_EXIT(ZTOZSB(zdp));
+ if ((error = zfs_enter(ZTOZSB(zdp), FTAG)) != 0)
+ return (error);
+ error = zfs_zaccess(zdp, ACE_EXECUTE, 0, B_FALSE, cr,
+ zfs_init_idmap);
+ zfs_exit(ZTOZSB(zdp), FTAG);
return (error);
}
* can define any form of access.
*/
int
-zfs_zaccess(znode_t *zp, int mode, int flags, boolean_t skipaclchk, cred_t *cr)
+zfs_zaccess(znode_t *zp, int mode, int flags, boolean_t skipaclchk, cred_t *cr,
+ zidmap_t *mnt_ns)
{
uint32_t working_mode;
int error;
}
}
- owner = zfs_fuid_map_id(ZTOZSB(zp), KUID_TO_SUID(ZTOI(zp)->i_uid),
- cr, ZFS_OWNER);
+ owner = zfs_uid_to_vfsuid(mnt_ns, zfs_i_user_ns(ZTOI(zp)),
+ KUID_TO_SUID(ZTOI(zp)->i_uid));
+ owner = zfs_fuid_map_id(ZTOZSB(zp), owner, cr, ZFS_OWNER);
+
/*
* Map the bits required to the standard inode flags
* S_IRUSR|S_IWUSR|S_IXUSR in the needed_bits. Map the bits
needed_bits |= S_IXUSR;
if ((error = zfs_zaccess_common(check_zp, mode, &working_mode,
- &check_privs, skipaclchk, cr)) == 0) {
+ &check_privs, skipaclchk, cr, mnt_ns)) == 0) {
if (is_attr)
zrele(xzp);
return (secpolicy_vnode_access2(cr, ZTOI(zp), owner,
}
if (error && (flags & V_APPEND)) {
- error = zfs_zaccess_append(zp, &working_mode, &check_privs, cr);
+ error = zfs_zaccess_append(zp, &working_mode, &check_privs, cr,
+ mnt_ns);
}
if (error && check_privs) {
* read_acl/read_attributes
*/
- error = 0;
ASSERT(working_mode != 0);
if ((working_mode & (ACE_READ_ACL|ACE_READ_ATTRIBUTES) &&
/*
* Translate traditional unix S_IRUSR/S_IWUSR/S_IXUSR mode into
- * native ACL format and call zfs_zaccess()
+ * NFSv4-style ZFS ACL format and call zfs_zaccess()
*/
int
-zfs_zaccess_rwx(znode_t *zp, mode_t mode, int flags, cred_t *cr)
+zfs_zaccess_rwx(znode_t *zp, mode_t mode, int flags, cred_t *cr,
+ zidmap_t *mnt_ns)
{
- return (zfs_zaccess(zp, zfs_unix_to_v4(mode >> 6), flags, B_FALSE, cr));
+ return (zfs_zaccess(zp, zfs_unix_to_v4(mode >> 6), flags, B_FALSE, cr,
+ mnt_ns));
}
/*
* Access function for secpolicy_vnode_setattr
*/
int
-zfs_zaccess_unix(znode_t *zp, mode_t mode, cred_t *cr)
+zfs_zaccess_unix(void *zp, int mode, cred_t *cr)
{
int v4_mode = zfs_unix_to_v4(mode >> 6);
- return (zfs_zaccess(zp, v4_mode, 0, B_FALSE, cr));
+ return (zfs_zaccess(zp, v4_mode, 0, B_FALSE, cr, zfs_init_idmap));
}
/* See zfs_zaccess_delete() */
-int zfs_write_implies_delete_child = 1;
+static const boolean_t zfs_write_implies_delete_child = B_TRUE;
/*
* Determine whether delete access should be granted.
* zfs_write_implies_delete_child
*/
int
-zfs_zaccess_delete(znode_t *dzp, znode_t *zp, cred_t *cr)
+zfs_zaccess_delete(znode_t *dzp, znode_t *zp, cred_t *cr, zidmap_t *mnt_ns)
{
uint32_t wanted_dirperms;
uint32_t dzp_working_mode = 0;
* (This is part of why we're checking the target first.)
*/
zp_error = zfs_zaccess_common(zp, ACE_DELETE, &zp_working_mode,
- &zpcheck_privs, B_FALSE, cr);
+ &zpcheck_privs, B_FALSE, cr, mnt_ns);
if (zp_error == EACCES) {
/* We hit a DENY ACE. */
if (!zpcheck_privs)
if (zfs_write_implies_delete_child)
wanted_dirperms |= ACE_WRITE_DATA;
dzp_error = zfs_zaccess_common(dzp, wanted_dirperms,
- &dzp_working_mode, &dzpcheck_privs, B_FALSE, cr);
+ &dzp_working_mode, &dzpcheck_privs, B_FALSE, cr, mnt_ns);
if (dzp_error == EACCES) {
/* We hit a DENY ACE. */
if (!dzpcheck_privs)
int
zfs_zaccess_rename(znode_t *sdzp, znode_t *szp, znode_t *tdzp,
- znode_t *tzp, cred_t *cr)
+ znode_t *tzp, cred_t *cr, zidmap_t *mnt_ns)
{
int add_perm;
int error;
* If that succeeds then check for add_file/add_subdir permissions
*/
- if ((error = zfs_zaccess_delete(sdzp, szp, cr)))
+ if ((error = zfs_zaccess_delete(sdzp, szp, cr, mnt_ns)))
return (error);
/*
* If we have a tzp, see if we can delete it?
*/
if (tzp) {
- if ((error = zfs_zaccess_delete(tdzp, tzp, cr)))
+ if ((error = zfs_zaccess_delete(tdzp, tzp, cr, mnt_ns)))
return (error);
}
/*
* Now check for add permissions
*/
- error = zfs_zaccess(tdzp, add_perm, 0, B_FALSE, cr);
+ error = zfs_zaccess(tdzp, add_perm, 0, B_FALSE, cr, mnt_ns);
return (error);
}