Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3486740a authored by Serge E. Hallyn's avatar Serge E. Hallyn Committed by Linus Torvalds
Browse files

userns: security: make capabilities relative to the user namespace



- Introduce ns_capable to test for a capability in a non-default
  user namespace.
- Teach cap_capable to handle capabilities in a non-default
  user namespace.

The motivation is to get to the unprivileged creation of new
namespaces.  It looks like this gets us 90% of the way there, with
only potential uid confusion issues left.

I still need to handle getting all caps after creation but otherwise I
think I have a good starter patch that achieves all of your goals.

Changelog:
	11/05/2010: [serge] add apparmor
	12/14/2010: [serge] fix capabilities to created user namespaces
	Without this, if user serge creates a user_ns, he won't have
	capabilities to the user_ns he created.  THis is because we
	were first checking whether his effective caps had the caps
	he needed and returning -EPERM if not, and THEN checking whether
	he was the creator.  Reverse those checks.
	12/16/2010: [serge] security_real_capable needs ns argument in !security case
	01/11/2011: [serge] add task_ns_capable helper
	01/11/2011: [serge] add nsown_capable() helper per Bastian Blank suggestion
	02/16/2011: [serge] fix a logic bug: the root user is always creator of
		    init_user_ns, but should not always have capabilities to
		    it!  Fix the check in cap_capable().
	02/21/2011: Add the required user_ns parameter to security_capable,
		    fixing a compile failure.
	02/23/2011: Convert some macros to functions as per akpm comments.  Some
		    couldn't be converted because we can't easily forward-declare
		    them (they are inline if !SECURITY, extern if SECURITY).  Add
		    a current_user_ns function so we can use it in capability.h
		    without #including cred.h.  Move all forward declarations
		    together to the top of the #ifdef __KERNEL__ section, and use
		    kernel-doc format.
	02/23/2011: Per dhowells, clean up comment in cap_capable().
	02/23/2011: Per akpm, remove unreachable 'return -EPERM' in cap_capable.

(Original written and signed off by Eric;  latest, modified version
acked by him)

[akpm@linux-foundation.org: fix build]
[akpm@linux-foundation.org: export current_user_ns() for ecryptfs]
[serge.hallyn@canonical.com: remove unneeded extra argument in selinux's task_has_capability]
Signed-off-by: default avatarEric W. Biederman <ebiederm@xmission.com>
Signed-off-by: default avatarSerge E. Hallyn <serge.hallyn@canonical.com>
Acked-by: default avatar"Eric W. Biederman" <ebiederm@xmission.com>
Acked-by: default avatarDaniel Lezcano <daniel.lezcano@free.fr>
Acked-by: default avatarDavid Howells <dhowells@redhat.com>
Cc: James Morris <jmorris@namei.org>
Signed-off-by: default avatarSerge E. Hallyn <serge.hallyn@canonical.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 59607db3
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -369,7 +369,7 @@ pci_read_config(struct file *filp, struct kobject *kobj,
	u8 *data = (u8*) buf;

	/* Several chips lock up trying to read undefined config space */
	if (security_capable(filp->f_cred, CAP_SYS_ADMIN) == 0) {
	if (security_capable(&init_user_ns, filp->f_cred, CAP_SYS_ADMIN) == 0) {
		size = dev->cfg_size;
	} else if (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS) {
		size = 128;
+28 −8
Original line number Diff line number Diff line
@@ -368,6 +368,17 @@ struct cpu_vfs_cap_data {

#ifdef __KERNEL__

struct dentry;
struct user_namespace;

extern struct user_namespace init_user_ns;

struct user_namespace *current_user_ns(void);

extern const kernel_cap_t __cap_empty_set;
extern const kernel_cap_t __cap_full_set;
extern const kernel_cap_t __cap_init_eff_set;

/*
 * Internal kernel functions only
 */
@@ -530,10 +541,6 @@ static inline kernel_cap_t cap_raise_nfsd_set(const kernel_cap_t a,
			   cap_intersect(permitted, __cap_nfsd_set));
}

extern const kernel_cap_t __cap_empty_set;
extern const kernel_cap_t __cap_full_set;
extern const kernel_cap_t __cap_init_eff_set;

/**
 * has_capability - Determine if a task has a superior capability available
 * @t: The task in question
@@ -544,7 +551,7 @@ extern const kernel_cap_t __cap_init_eff_set;
 *
 * Note that this does not set PF_SUPERPRIV on the task.
 */
#define has_capability(t, cap) (security_real_capable((t), (cap)) == 0)
#define has_capability(t, cap) (security_real_capable((t), &init_user_ns, (cap)) == 0)

/**
 * has_capability_noaudit - Determine if a task has a superior capability available (unaudited)
@@ -558,12 +565,25 @@ extern const kernel_cap_t __cap_init_eff_set;
 * Note that this does not set PF_SUPERPRIV on the task.
 */
#define has_capability_noaudit(t, cap) \
	(security_real_capable_noaudit((t), (cap)) == 0)
	(security_real_capable_noaudit((t), &init_user_ns, (cap)) == 0)

extern int capable(int cap);
extern bool capable(int cap);
extern bool ns_capable(struct user_namespace *ns, int cap);
extern bool task_ns_capable(struct task_struct *t, int cap);

/**
 * nsown_capable - Check superior capability to one's own user_ns
 * @cap: The capability in question
 *
 * Return true if the current task has the given superior capability
 * targeted at its own user namespace.
 */
static inline bool nsown_capable(int cap)
{
	return ns_capable(current_user_ns(), cap);
}

/* audit system wants to get cap info from files as well */
struct dentry;
extern int get_vfs_caps_from_disk(const struct dentry *dentry, struct cpu_vfs_cap_data *cpu_caps);

#endif /* __KERNEL__ */
+3 −1
Original line number Diff line number Diff line
@@ -354,9 +354,11 @@ static inline void put_cred(const struct cred *_cred)
#define current_fsgid() 	(current_cred_xxx(fsgid))
#define current_cap()		(current_cred_xxx(cap_effective))
#define current_user()		(current_cred_xxx(user))
#define current_user_ns()	(current_cred_xxx(user)->user_ns)
#define _current_user_ns()	(current_cred_xxx(user)->user_ns)
#define current_security()	(current_cred_xxx(security))

extern struct user_namespace *current_user_ns(void);

#define current_uid_gid(_uid, _gid)		\
do {						\
	const struct cred *__cred;		\
+17 −11
Original line number Diff line number Diff line
@@ -47,13 +47,14 @@

struct ctl_table;
struct audit_krule;
struct user_namespace;

/*
 * These functions are in security/capability.c and are used
 * as the default capabilities functions
 */
extern int cap_capable(struct task_struct *tsk, const struct cred *cred,
		       int cap, int audit);
		       struct user_namespace *ns, int cap, int audit);
extern int cap_settime(const struct timespec *ts, const struct timezone *tz);
extern int cap_ptrace_access_check(struct task_struct *child, unsigned int mode);
extern int cap_ptrace_traceme(struct task_struct *parent);
@@ -1262,6 +1263,7 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
 *	credentials.
 *	@tsk contains the task_struct for the process.
 *	@cred contains the credentials to use.
 *      @ns contains the user namespace we want the capability in
 *	@cap contains the capability <include/linux/capability.h>.
 *	@audit: Whether to write an audit message or not
 *	Return 0 if the capability is granted for @tsk.
@@ -1384,7 +1386,7 @@ struct security_operations {
		       const kernel_cap_t *inheritable,
		       const kernel_cap_t *permitted);
	int (*capable) (struct task_struct *tsk, const struct cred *cred,
			int cap, int audit);
			struct user_namespace *ns, int cap, int audit);
	int (*quotactl) (int cmds, int type, int id, struct super_block *sb);
	int (*quota_on) (struct dentry *dentry);
	int (*syslog) (int type);
@@ -1665,9 +1667,12 @@ int security_capset(struct cred *new, const struct cred *old,
		    const kernel_cap_t *effective,
		    const kernel_cap_t *inheritable,
		    const kernel_cap_t *permitted);
int security_capable(const struct cred *cred, int cap);
int security_real_capable(struct task_struct *tsk, int cap);
int security_real_capable_noaudit(struct task_struct *tsk, int cap);
int security_capable(struct user_namespace *ns, const struct cred *cred,
			int cap);
int security_real_capable(struct task_struct *tsk, struct user_namespace *ns,
			int cap);
int security_real_capable_noaudit(struct task_struct *tsk,
			struct user_namespace *ns, int cap);
int security_quotactl(int cmds, int type, int id, struct super_block *sb);
int security_quota_on(struct dentry *dentry);
int security_syslog(int type);
@@ -1860,28 +1865,29 @@ static inline int security_capset(struct cred *new,
	return cap_capset(new, old, effective, inheritable, permitted);
}

static inline int security_capable(const struct cred *cred, int cap)
static inline int security_capable(struct user_namespace *ns,
				   const struct cred *cred, int cap)
{
	return cap_capable(current, cred, cap, SECURITY_CAP_AUDIT);
	return cap_capable(current, cred, ns, cap, SECURITY_CAP_AUDIT);
}

static inline int security_real_capable(struct task_struct *tsk, int cap)
static inline int security_real_capable(struct task_struct *tsk, struct user_namespace *ns, int cap)
{
	int ret;

	rcu_read_lock();
	ret = cap_capable(tsk, __task_cred(tsk), cap, SECURITY_CAP_AUDIT);
	ret = cap_capable(tsk, __task_cred(tsk), ns, cap, SECURITY_CAP_AUDIT);
	rcu_read_unlock();
	return ret;
}

static inline
int security_real_capable_noaudit(struct task_struct *tsk, int cap)
int security_real_capable_noaudit(struct task_struct *tsk, struct user_namespace *ns, int cap)
{
	int ret;

	rcu_read_lock();
	ret = cap_capable(tsk, __task_cred(tsk), cap,
	ret = cap_capable(tsk, __task_cred(tsk), ns, cap,
			       SECURITY_CAP_NOAUDIT);
	rcu_read_unlock();
	return ret;
+37 −5
Original line number Diff line number Diff line
@@ -14,6 +14,7 @@
#include <linux/security.h>
#include <linux/syscalls.h>
#include <linux/pid_namespace.h>
#include <linux/user_namespace.h>
#include <asm/uaccess.h>

/*
@@ -299,17 +300,48 @@ SYSCALL_DEFINE2(capset, cap_user_header_t, header, const cap_user_data_t, data)
 * This sets PF_SUPERPRIV on the task if the capability is available on the
 * assumption that it's about to be used.
 */
int capable(int cap)
bool capable(int cap)
{
	return ns_capable(&init_user_ns, cap);
}
EXPORT_SYMBOL(capable);

/**
 * ns_capable - Determine if the current task has a superior capability in effect
 * @ns:  The usernamespace we want the capability in
 * @cap: The capability to be tested for
 *
 * Return true if the current task has the given superior capability currently
 * available for use, false if not.
 *
 * This sets PF_SUPERPRIV on the task if the capability is available on the
 * assumption that it's about to be used.
 */
bool ns_capable(struct user_namespace *ns, int cap)
{
	if (unlikely(!cap_valid(cap))) {
		printk(KERN_CRIT "capable() called with invalid cap=%u\n", cap);
		BUG();
	}

	if (security_capable(current_cred(), cap) == 0) {
	if (security_capable(ns, current_cred(), cap) == 0) {
		current->flags |= PF_SUPERPRIV;
		return 1;
		return true;
	}
	return 0;
	return false;
}
EXPORT_SYMBOL(capable);
EXPORT_SYMBOL(ns_capable);

/**
 * task_ns_capable - Determine whether current task has a superior
 * capability targeted at a specific task's user namespace.
 * @t: The task whose user namespace is targeted.
 * @cap: The capability in question.
 *
 *  Return true if it does, false otherwise.
 */
bool task_ns_capable(struct task_struct *t, int cap)
{
	return ns_capable(task_cred_xxx(t, user)->user_ns, cap);
}
EXPORT_SYMBOL(task_ns_capable);
Loading