aboutsummaryrefslogtreecommitdiffstats
path: root/module
diff options
context:
space:
mode:
Diffstat (limited to 'module')
-rw-r--r--module/zfs/Makefile.in1
-rw-r--r--module/zfs/dmu_objset.c1
-rw-r--r--module/zfs/dmu_send.c1
-rw-r--r--module/zfs/dsl_dataset.c1
-rw-r--r--module/zfs/policy.c303
-rw-r--r--module/zfs/zfs_acl.c4
-rw-r--r--module/zfs/zfs_ctldir.c4
-rw-r--r--module/zfs/zfs_fuid.c8
-rw-r--r--module/zfs/zfs_ioctl.c46
9 files changed, 358 insertions, 11 deletions
diff --git a/module/zfs/Makefile.in b/module/zfs/Makefile.in
index f33faf157..33f923ffd 100644
--- a/module/zfs/Makefile.in
+++ b/module/zfs/Makefile.in
@@ -43,6 +43,7 @@ $(MODULE)-objs += lz4.o
$(MODULE)-objs += metaslab.o
$(MODULE)-objs += multilist.o
$(MODULE)-objs += pathname.o
+$(MODULE)-objs += policy.o
$(MODULE)-objs += range_tree.o
$(MODULE)-objs += refcount.o
$(MODULE)-objs += rrwlock.o
diff --git a/module/zfs/dmu_objset.c b/module/zfs/dmu_objset.c
index f9c534eb5..c8a435185 100644
--- a/module/zfs/dmu_objset.c
+++ b/module/zfs/dmu_objset.c
@@ -52,6 +52,7 @@
#include <sys/zfs_onexit.h>
#include <sys/dsl_destroy.h>
#include <sys/vdev.h>
+#include <sys/policy.h>
/*
* Needed to close a window in dnode_move() that allows the objset to be freed
diff --git a/module/zfs/dmu_send.c b/module/zfs/dmu_send.c
index 7dc62dc20..896a84b50 100644
--- a/module/zfs/dmu_send.c
+++ b/module/zfs/dmu_send.c
@@ -56,6 +56,7 @@
#include <sys/zfeature.h>
#include <sys/bqueue.h>
#include <sys/zvol.h>
+#include <sys/policy.h>
/* Set this tunable to TRUE to replace corrupt data with 0x2f5baddb10c */
int zfs_send_corrupt_data = B_FALSE;
diff --git a/module/zfs/dsl_dataset.c b/module/zfs/dsl_dataset.c
index 230027daf..9c275b234 100644
--- a/module/zfs/dsl_dataset.c
+++ b/module/zfs/dsl_dataset.c
@@ -51,6 +51,7 @@
#include <sys/dsl_destroy.h>
#include <sys/dsl_userhold.h>
#include <sys/dsl_bookmark.h>
+#include <sys/policy.h>
/*
* The SPA supports block sizes up to 16MB. However, very large blocks
diff --git a/module/zfs/policy.c b/module/zfs/policy.c
new file mode 100644
index 000000000..81629e0dc
--- /dev/null
+++ b/module/zfs/policy.c
@@ -0,0 +1,303 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * 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.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2013, Joyent, Inc. All rights reserved.
+ * Copyright (C) 2016 Lawrence Livermore National Security, LLC.
+ *
+ * For Linux the vast majority of this enforcement is already handled via
+ * the standard Linux VFS permission checks. However certain administrative
+ * commands which bypass the standard mechanisms may need to make use of
+ * this functionality.
+ */
+
+#include <sys/policy.h>
+#include <linux/security.h>
+#include <linux/vfs_compat.h>
+
+/*
+ * The passed credentials cannot be directly verified because Linux only
+ * provides and interface to check the *current* proces credentials. In
+ * order to handle this the capable() test is only run when the passed
+ * credentials match the current process credentials or the kcred. In
+ * all other cases this function must fail and return the passed err.
+ */
+static int
+priv_policy(const cred_t *cr, int capability, boolean_t all, int err)
+{
+ ASSERT3S(all, ==, B_FALSE);
+
+ if (cr != CRED() && (cr != kcred))
+ return (err);
+
+ if (!capable(capability))
+ return (err);
+
+ return (0);
+}
+
+/*
+ * Checks for operations that are either client-only or are used by
+ * both clients and servers.
+ */
+int
+secpolicy_nfs(const cred_t *cr)
+{
+ return (priv_policy(cr, CAP_SYS_ADMIN, B_FALSE, EPERM));
+}
+
+/*
+ * Catch all system configuration.
+ */
+int
+secpolicy_sys_config(const cred_t *cr, boolean_t checkonly)
+{
+ return (priv_policy(cr, CAP_SYS_ADMIN, B_FALSE, EPERM));
+}
+
+/*
+ * Like secpolicy_vnode_access() but we get the actual wanted mode and the
+ * current mode of the file, not the missing bits.
+ *
+ * Enforced in the Linux VFS.
+ */
+int
+secpolicy_vnode_access2(const cred_t *cr, struct inode *ip, uid_t owner,
+ mode_t curmode, mode_t wantmode)
+{
+ return (0);
+}
+
+/*
+ * This is a special routine for ZFS; it is used to determine whether
+ * any of the privileges in effect allow any form of access to the
+ * file. There's no reason to audit this or any reason to record
+ * this. More work is needed to do the "KPLD" stuff.
+ */
+int
+secpolicy_vnode_any_access(const cred_t *cr, struct inode *ip, uid_t owner)
+{
+ if (crgetuid(cr) == owner)
+ return (0);
+
+ if (zpl_inode_owner_or_capable(ip))
+ return (0);
+
+ if (priv_policy(cr, CAP_DAC_OVERRIDE, B_FALSE, EPERM) == 0)
+ return (0);
+
+ if (priv_policy(cr, CAP_DAC_READ_SEARCH, B_FALSE, EPERM) == 0)
+ return (0);
+
+ return (EPERM);
+}
+
+/*
+ * Determine if subject can chown owner of a file.
+ */
+int
+secpolicy_vnode_chown(const cred_t *cr, uid_t owner)
+{
+ if (crgetuid(cr) == owner)
+ return (0);
+
+ return (priv_policy(cr, CAP_FOWNER, B_FALSE, EPERM));
+}
+
+/*
+ * Determine if subject can change group ownership of a file.
+ */
+int
+secpolicy_vnode_create_gid(const cred_t *cr)
+{
+ return (priv_policy(cr, CAP_SETGID, B_FALSE, EPERM));
+}
+
+/*
+ * Policy determines whether we can remove an entry from a directory,
+ * regardless of permission bits.
+ */
+int
+secpolicy_vnode_remove(const cred_t *cr)
+{
+ return (priv_policy(cr, CAP_FOWNER, B_FALSE, EPERM));
+}
+
+/*
+ * Determine that subject can modify the mode of a file. allzone privilege
+ * needed when modifying root owned object.
+ */
+int
+secpolicy_vnode_setdac(const cred_t *cr, uid_t owner)
+{
+ if (crgetuid(cr) == owner)
+ return (0);
+
+ return (priv_policy(cr, CAP_FOWNER, B_FALSE, EPERM));
+}
+
+/*
+ * Are we allowed to retain the set-uid/set-gid bits when
+ * changing ownership or when writing to a file?
+ * "issuid" should be true when set-uid; only in that case
+ * root ownership is checked (setgid is assumed).
+ *
+ * Enforced in the Linux VFS.
+ */
+int
+secpolicy_vnode_setid_retain(const cred_t *cr, boolean_t issuidroot)
+{
+ return (0);
+}
+
+/*
+ * Determine that subject can set the file setgid flag.
+ */
+int
+secpolicy_vnode_setids_setgids(const cred_t *cr, gid_t gid)
+{
+ if (!groupmember(gid, cr))
+ return (priv_policy(cr, CAP_FSETID, B_FALSE, EPERM));
+
+ return (0);
+}
+
+/*
+ * Determine if the subject can inject faults in the ZFS fault injection
+ * framework. Requires all privileges.
+ */
+int
+secpolicy_zinject(const cred_t *cr)
+{
+ return (priv_policy(cr, CAP_SYS_ADMIN, B_FALSE, EACCES));
+}
+
+/*
+ * Determine if the subject has permission to manipulate ZFS datasets
+ * (not pools). Equivalent to the SYS_MOUNT privilege.
+ */
+int
+secpolicy_zfs(const cred_t *cr)
+{
+ return (priv_policy(cr, CAP_SYS_ADMIN, B_FALSE, EACCES));
+}
+
+void
+secpolicy_setid_clear(vattr_t *vap, cred_t *cr)
+{
+ if ((vap->va_mode & (S_ISUID | S_ISGID)) != 0 &&
+ secpolicy_vnode_setid_retain(cr,
+ (vap->va_mode & S_ISUID) != 0 &&
+ (vap->va_mask & AT_UID) != 0 && vap->va_uid == 0) != 0) {
+ vap->va_mask |= AT_MODE;
+ vap->va_mode &= ~(S_ISUID|S_ISGID);
+ }
+}
+
+/*
+ * Determine that subject can set the file setid flags.
+ */
+static int
+secpolicy_vnode_setid_modify(const cred_t *cr, uid_t owner)
+{
+ if (crgetuid(cr) == owner)
+ return (0);
+
+ return (priv_policy(cr, CAP_FSETID, B_FALSE, EPERM));
+}
+
+/*
+ * Determine that subject can make a file a "sticky".
+ *
+ * Enforced in the Linux VFS.
+ */
+static int
+secpolicy_vnode_stky_modify(const cred_t *cr)
+{
+ return (0);
+}
+
+int
+secpolicy_setid_setsticky_clear(struct inode *ip, vattr_t *vap,
+ const vattr_t *ovap, cred_t *cr)
+{
+ int error;
+
+ if ((vap->va_mode & S_ISUID) != 0 &&
+ (error = secpolicy_vnode_setid_modify(cr,
+ ovap->va_uid)) != 0) {
+ return (error);
+ }
+
+ /*
+ * Check privilege if attempting to set the
+ * sticky bit on a non-directory.
+ */
+ if (!S_ISDIR(ip->i_mode) && (vap->va_mode & S_ISVTX) != 0 &&
+ secpolicy_vnode_stky_modify(cr) != 0) {
+ vap->va_mode &= ~S_ISVTX;
+ }
+
+ /*
+ * Check for privilege if attempting to set the
+ * group-id bit.
+ */
+ if ((vap->va_mode & S_ISGID) != 0 &&
+ secpolicy_vnode_setids_setgids(cr, ovap->va_gid) != 0) {
+ vap->va_mode &= ~S_ISGID;
+ }
+
+ return (0);
+}
+
+/*
+ * Check privileges for setting xvattr attributes
+ */
+int
+secpolicy_xvattr(xvattr_t *xvap, uid_t owner, cred_t *cr, vtype_t vtype)
+{
+ return (secpolicy_vnode_chown(cr, owner));
+}
+
+/*
+ * Check privileges for setattr attributes.
+ *
+ * Enforced in the Linux VFS.
+ */
+int
+secpolicy_vnode_setattr(cred_t *cr, struct inode *ip, struct vattr *vap,
+ const struct vattr *ovap, int flags,
+ int unlocked_access(void *, int, cred_t *), void *node)
+{
+ return (0);
+}
+
+/*
+ * Check privileges for links.
+ *
+ * Enforced in the Linux VFS.
+ */
+int
+secpolicy_basic_link(const cred_t *cr)
+{
+ return (0);
+}
diff --git a/module/zfs/zfs_acl.c b/module/zfs/zfs_acl.c
index 47cfd464b..69a93a8b6 100644
--- a/module/zfs/zfs_acl.c
+++ b/module/zfs/zfs_acl.c
@@ -1744,9 +1744,7 @@ zfs_acl_ids_create(znode_t *dzp, int flag, vattr_t *vap, cred_t *cr,
int error;
zfs_sb_t *zsb = ZTOZSB(dzp);
zfs_acl_t *paclp;
-#ifdef HAVE_KSID
gid_t gid;
-#endif /* HAVE_KSID */
boolean_t need_chmod = B_TRUE;
boolean_t inherited = B_FALSE;
@@ -1760,7 +1758,6 @@ zfs_acl_ids_create(znode_t *dzp, int flag, vattr_t *vap, cred_t *cr,
acl_ids->z_fuid = vap->va_uid;
acl_ids->z_fgid = vap->va_gid;
-#ifdef HAVE_KSID
/*
* Determine uid and gid.
*/
@@ -1812,7 +1809,6 @@ zfs_acl_ids_create(znode_t *dzp, int flag, vattr_t *vap, cred_t *cr,
}
}
}
-#endif /* HAVE_KSID */
/*
* If we're creating a directory, and the parent directory has the
diff --git a/module/zfs/zfs_ctldir.c b/module/zfs/zfs_ctldir.c
index 7d160f23d..e47cfc878 100644
--- a/module/zfs/zfs_ctldir.c
+++ b/module/zfs/zfs_ctldir.c
@@ -109,7 +109,7 @@ static krwlock_t zfs_snapshot_lock;
* Control Directory Tunables (.zfs)
*/
int zfs_expire_snapshot = ZFSCTL_EXPIRE_SNAPSHOT;
-int zfs_admin_snapshot = 0;
+int zfs_admin_snapshot = 1;
/*
* Dedicated task queue for unmounting snapshots.
@@ -490,7 +490,7 @@ zfsctl_inode_alloc(zfs_sb_t *zsb, uint64_t id,
zp->z_is_stale = B_FALSE;
ip->i_generation = 0;
ip->i_ino = id;
- ip->i_mode = (S_IFDIR | S_IRUGO | S_IXUGO);
+ ip->i_mode = (S_IFDIR | S_IRWXUGO);
ip->i_uid = SUID_TO_KUID(0);
ip->i_gid = SGID_TO_KGID(0);
ip->i_blkbits = SPA_MINBLOCKSHIFT;
diff --git a/module/zfs/zfs_fuid.c b/module/zfs/zfs_fuid.c
index 6ca61b872..d4916bf58 100644
--- a/module/zfs/zfs_fuid.c
+++ b/module/zfs/zfs_fuid.c
@@ -488,7 +488,6 @@ zfs_fuid_node_add(zfs_fuid_info_t **fuidpp, const char *domain, uint32_t rid,
}
}
-#ifdef HAVE_KSID
/*
* Create a file system FUID, based on information in the users cred
*
@@ -501,6 +500,7 @@ uint64_t
zfs_fuid_create_cred(zfs_sb_t *zsb, zfs_fuid_type_t type,
cred_t *cr, zfs_fuid_info_t **fuidp)
{
+#ifdef HAVE_KSID
uint64_t idx;
ksid_t *ksid;
uint32_t rid;
@@ -540,8 +540,12 @@ zfs_fuid_create_cred(zfs_sb_t *zsb, zfs_fuid_type_t type,
zfs_fuid_node_add(fuidp, kdomain, rid, idx, id, type);
return (FUID_ENCODE(idx, rid));
-}
+#else
+ VERIFY(type == ZFS_OWNER || type == ZFS_GROUP);
+
+ return ((uint64_t)((type == ZFS_OWNER) ? crgetuid(cr) : crgetgid(cr)));
#endif /* HAVE_KSID */
+}
/*
* Create a file system FUID for an ACL ace
diff --git a/module/zfs/zfs_ioctl.c b/module/zfs/zfs_ioctl.c
index 7969f525e..c63af167a 100644
--- a/module/zfs/zfs_ioctl.c
+++ b/module/zfs/zfs_ioctl.c
@@ -186,12 +186,19 @@
#include <sys/zfeature.h>
#include <linux/miscdevice.h>
+#include <linux/slab.h>
#include "zfs_namecheck.h"
#include "zfs_prop.h"
#include "zfs_deleg.h"
#include "zfs_comutil.h"
+/*
+ * Limit maximum nvlist size. We don't want users passing in insane values
+ * for zc->zc_nvlist_src_size, since we will need to allocate that much memory.
+ */
+#define MAX_NVLIST_SRC_SIZE KMALLOC_MAX_SIZE
+
kmutex_t zfsdev_state_lock;
zfsdev_state_t *zfsdev_state_list;
@@ -3182,8 +3189,25 @@ zfs_ioc_create(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl)
if (error == 0) {
error = zfs_set_prop_nvlist(fsname, ZPROP_SRC_LOCAL,
nvprops, outnvl);
- if (error != 0)
- (void) dsl_destroy_head(fsname);
+ if (error != 0) {
+ spa_t *spa;
+ int error2;
+
+ /*
+ * Volumes will return EBUSY and cannot be destroyed
+ * until all asynchronous minor handling has completed.
+ * Wait for the spa_zvol_taskq to drain then retry.
+ */
+ error2 = dsl_destroy_head(fsname);
+ while ((error2 == EBUSY) && (type == DMU_OST_ZVOL)) {
+ error2 = spa_open(fsname, &spa, FTAG);
+ if (error2 == 0) {
+ taskq_wait(spa->spa_zvol_taskq);
+ spa_close(spa, FTAG);
+ }
+ error2 = dsl_destroy_head(fsname);
+ }
+ }
}
return (error);
}
@@ -5795,7 +5819,23 @@ zfsdev_ioctl(struct file *filp, unsigned cmd, unsigned long arg)
}
zc->zc_iflags = flag & FKIOCTL;
- if (zc->zc_nvlist_src_size != 0) {
+ if (zc->zc_nvlist_src_size > MAX_NVLIST_SRC_SIZE) {
+ /*
+ * Make sure the user doesn't pass in an insane value for
+ * zc_nvlist_src_size. We have to check, since we will end
+ * up allocating that much memory inside of get_nvlist(). This
+ * prevents a nefarious user from allocating tons of kernel
+ * memory.
+ *
+ * Also, we return EINVAL instead of ENOMEM here. The reason
+ * being that returning ENOMEM from an ioctl() has a special
+ * connotation; that the user's size value is too small and
+ * needs to be expanded to hold the nvlist. See
+ * zcmd_expand_dst_nvlist() for details.
+ */
+ error = SET_ERROR(EINVAL); /* User's size too big */
+
+ } else if (zc->zc_nvlist_src_size != 0) {
error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
zc->zc_iflags, &innvl);
if (error != 0)