From 5b7b11949563a608f493ec61e87d3e4eb47b31a2 Mon Sep 17 00:00:00 2001 From: Wolfgang Bumiller Date: Tue, 31 Oct 2017 09:08:42 +0100 Subject: [PATCH] Take user namespaces into account in policy checks Change file related checks to use user namespaces and make sure involved uids/gids are mappable in the current namespace. Note that checks without file ownership information will still not take user namespaces into account, as some of these should be handled via 'zfs allow' (otherwise root in a user namespace could issue commands such as `zpool export`). Signed-off-by: Wolfgang Bumiller Closes #6800 --- module/zfs/policy.c | 43 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/module/zfs/policy.c b/module/zfs/policy.c index 03e8f748b746..14c56813ddd1 100644 --- a/module/zfs/policy.c +++ b/module/zfs/policy.c @@ -42,19 +42,32 @@ * 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) +priv_policy_ns(const cred_t *cr, int capability, boolean_t all, int err, + struct user_namespace *ns) { ASSERT3S(all, ==, B_FALSE); if (cr != CRED() && (cr != kcred)) return (err); - if (!capable(capability)) + if (!(ns ? ns_capable(ns, capability) : capable(capability))) return (err); return (0); } +static int +priv_policy(const cred_t *cr, int capability, boolean_t all, int err) +{ + return priv_policy_ns(cr, capability, all, err, NULL); +} + +static int +priv_policy_user(const cred_t *cr, int capability, boolean_t all, int err) +{ + return priv_policy_ns(cr, capability, all, err, cr->user_ns); +} + /* * Checks for operations that are either client-only or are used by * both clients and servers. @@ -102,10 +115,13 @@ secpolicy_vnode_any_access(const cred_t *cr, struct inode *ip, uid_t owner) if (zpl_inode_owner_or_capable(ip)) return (0); - if (priv_policy(cr, CAP_DAC_OVERRIDE, B_FALSE, EPERM) == 0) + if (!kuid_has_mapping(cr->user_ns, SUID_TO_KUID(owner))) + return (EPERM); + + if (priv_policy_user(cr, CAP_DAC_OVERRIDE, B_FALSE, EPERM) == 0) return (0); - if (priv_policy(cr, CAP_DAC_READ_SEARCH, B_FALSE, EPERM) == 0) + if (priv_policy_user(cr, CAP_DAC_READ_SEARCH, B_FALSE, EPERM) == 0) return (0); return (EPERM); @@ -120,7 +136,10 @@ secpolicy_vnode_chown(const cred_t *cr, uid_t owner) if (crgetfsuid(cr) == owner) return (0); - return (priv_policy(cr, CAP_FOWNER, B_FALSE, EPERM)); + if (!kuid_has_mapping(cr->user_ns, SUID_TO_KUID(owner))) + return (EPERM); + + return (priv_policy_user(cr, CAP_FOWNER, B_FALSE, EPERM)); } /* @@ -152,7 +171,10 @@ secpolicy_vnode_setdac(const cred_t *cr, uid_t owner) if (crgetfsuid(cr) == owner) return (0); - return (priv_policy(cr, CAP_FOWNER, B_FALSE, EPERM)); + if (!kuid_has_mapping(cr->user_ns, SUID_TO_KUID(owner))) + return (EPERM); + + return (priv_policy_user(cr, CAP_FOWNER, B_FALSE, EPERM)); } /* @@ -175,8 +197,10 @@ secpolicy_vnode_setid_retain(const cred_t *cr, boolean_t issuidroot) int secpolicy_vnode_setids_setgids(const cred_t *cr, gid_t gid) { + if (!kgid_has_mapping(cr->user_ns, SGID_TO_KGID(gid))) + return (EPERM); if (crgetfsgid(cr) != gid && !groupmember(gid, cr)) - return (priv_policy(cr, CAP_FSETID, B_FALSE, EPERM)); + return (priv_policy_user(cr, CAP_FSETID, B_FALSE, EPERM)); return (0); } @@ -222,7 +246,10 @@ secpolicy_vnode_setid_modify(const cred_t *cr, uid_t owner) if (crgetfsuid(cr) == owner) return (0); - return (priv_policy(cr, CAP_FSETID, B_FALSE, EPERM)); + if (!kuid_has_mapping(cr->user_ns, SUID_TO_KUID(owner))) + return (EPERM); + + return (priv_policy_user(cr, CAP_FSETID, B_FALSE, EPERM)); } /*