2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* The "user cache".
|
|
|
|
*
|
|
|
|
* (C) Copyright 1991-2000 Linus Torvalds
|
|
|
|
*
|
|
|
|
* We have a per-user structure to keep track of how many
|
|
|
|
* processes, files etc the user has claimed, in order to be
|
|
|
|
* able to have per-user limits for system resources.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/sched.h>
|
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/bitops.h>
|
|
|
|
#include <linux/key.h>
|
2006-01-25 22:23:07 +08:00
|
|
|
#include <linux/interrupt.h>
|
2011-05-24 02:51:41 +08:00
|
|
|
#include <linux/export.h>
|
2007-07-16 14:40:59 +08:00
|
|
|
#include <linux/user_namespace.h>
|
2011-06-16 01:21:48 +08:00
|
|
|
#include <linux/proc_fs.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
|
userns: add a user_namespace as creator/owner of uts_namespace
The expected course of development for user namespaces targeted
capabilities is laid out at https://wiki.ubuntu.com/UserNamespace.
Goals:
- Make it safe for an unprivileged user to unshare namespaces. They
will be privileged with respect to the new namespace, but this should
only include resources which the unprivileged user already owns.
- Provide separate limits and accounting for userids in different
namespaces.
Status:
Currently (as of 2.6.38) you can clone with the CLONE_NEWUSER flag to
get a new user namespace if you have the CAP_SYS_ADMIN, CAP_SETUID, and
CAP_SETGID capabilities. What this gets you is a whole new set of
userids, meaning that user 500 will have a different 'struct user' in
your namespace than in other namespaces. So any accounting information
stored in struct user will be unique to your namespace.
However, throughout the kernel there are checks which
- simply check for a capability. Since root in a child namespace
has all capabilities, this means that a child namespace is not
constrained.
- simply compare uid1 == uid2. Since these are the integer uids,
uid 500 in namespace 1 will be said to be equal to uid 500 in
namespace 2.
As a result, the lxc implementation at lxc.sf.net does not use user
namespaces. This is actually helpful because it leaves us free to
develop user namespaces in such a way that, for some time, user
namespaces may be unuseful.
Bugs aside, this patchset is supposed to not at all affect systems which
are not actively using user namespaces, and only restrict what tasks in
child user namespace can do. They begin to limit privilege to a user
namespace, so that root in a container cannot kill or ptrace tasks in the
parent user namespace, and can only get world access rights to files.
Since all files currently belong to the initila user namespace, that means
that child user namespaces can only get world access rights to *all*
files. While this temporarily makes user namespaces bad for system
containers, it starts to get useful for some sandboxing.
I've run the 'runltplite.sh' with and without this patchset and found no
difference.
This patch:
copy_process() handles CLONE_NEWUSER before the rest of the namespaces.
So in the case of clone(CLONE_NEWUSER|CLONE_NEWUTS) the new uts namespace
will have the new user namespace as its owner. That is what we want,
since we want root in that new userns to be able to have privilege over
it.
Changelog:
Feb 15: don't set uts_ns->user_ns if we didn't create
a new uts_ns.
Feb 23: Move extern init_user_ns declaration from
init/version.c to utsname.h.
Signed-off-by: Serge E. Hallyn <serge.hallyn@canonical.com>
Acked-by: "Eric W. Biederman" <ebiederm@xmission.com>
Acked-by: Daniel Lezcano <daniel.lezcano@free.fr>
Acked-by: David Howells <dhowells@redhat.com>
Cc: James Morris <jmorris@namei.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2011-03-24 07:43:16 +08:00
|
|
|
/*
|
|
|
|
* userns count is 1 for root user, 1 for init_uts_ns,
|
|
|
|
* and 1 for... ?
|
|
|
|
*/
|
2008-02-08 20:18:23 +08:00
|
|
|
struct user_namespace init_user_ns = {
|
2011-11-17 16:11:58 +08:00
|
|
|
.uid_map = {
|
|
|
|
.nr_extents = 1,
|
|
|
|
.extent[0] = {
|
|
|
|
.first = 0,
|
|
|
|
.lower_first = 0,
|
2012-05-20 05:44:06 +08:00
|
|
|
.count = 4294967295U,
|
2011-11-17 16:11:58 +08:00
|
|
|
},
|
|
|
|
},
|
|
|
|
.gid_map = {
|
|
|
|
.nr_extents = 1,
|
|
|
|
.extent[0] = {
|
|
|
|
.first = 0,
|
|
|
|
.lower_first = 0,
|
2012-05-20 05:44:06 +08:00
|
|
|
.count = 4294967295U,
|
2011-11-17 16:11:58 +08:00
|
|
|
},
|
|
|
|
},
|
2012-08-30 16:24:05 +08:00
|
|
|
.projid_map = {
|
|
|
|
.nr_extents = 1,
|
|
|
|
.extent[0] = {
|
|
|
|
.first = 0,
|
|
|
|
.lower_first = 0,
|
|
|
|
.count = 4294967295U,
|
|
|
|
},
|
|
|
|
},
|
2012-12-29 10:58:39 +08:00
|
|
|
.count = ATOMIC_INIT(3),
|
2011-11-17 17:32:59 +08:00
|
|
|
.owner = GLOBAL_ROOT_UID,
|
|
|
|
.group = GLOBAL_ROOT_GID,
|
2011-06-16 01:21:48 +08:00
|
|
|
.proc_inum = PROC_USER_INIT_INO,
|
2008-02-08 20:18:23 +08:00
|
|
|
};
|
|
|
|
EXPORT_SYMBOL_GPL(init_user_ns);
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* UID task count cache, to get fast user lookup in "alloc_uid"
|
|
|
|
* when changing user ID's (ie setuid() and friends).
|
|
|
|
*/
|
|
|
|
|
2011-11-17 15:20:58 +08:00
|
|
|
#define UIDHASH_BITS (CONFIG_BASE_SMALL ? 3 : 7)
|
|
|
|
#define UIDHASH_SZ (1 << UIDHASH_BITS)
|
2005-04-17 06:20:36 +08:00
|
|
|
#define UIDHASH_MASK (UIDHASH_SZ - 1)
|
|
|
|
#define __uidhashfn(uid) (((uid >> UIDHASH_BITS) + uid) & UIDHASH_MASK)
|
2011-11-17 15:20:58 +08:00
|
|
|
#define uidhashentry(uid) (uidhash_table + __uidhashfn((__kuid_val(uid))))
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-12-07 12:33:20 +08:00
|
|
|
static struct kmem_cache *uid_cachep;
|
2011-11-17 15:20:58 +08:00
|
|
|
struct hlist_head uidhash_table[UIDHASH_SZ];
|
2006-01-25 22:23:07 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* The uidhash_lock is mostly taken from process context, but it is
|
|
|
|
* occasionally also taken from softirq/tasklet context, when
|
|
|
|
* task-structs get RCU-freed. Hence all locking must be softirq-safe.
|
2006-02-01 08:34:26 +08:00
|
|
|
* But free_uid() is also called with local interrupts disabled, and running
|
|
|
|
* local_bh_enable() with local interrupts disabled is an error - we'll run
|
|
|
|
* softirq callbacks, and they can unconditionally enable interrupts, and
|
|
|
|
* the caller of free_uid() didn't expect that..
|
2006-01-25 22:23:07 +08:00
|
|
|
*/
|
2005-04-17 06:20:36 +08:00
|
|
|
static DEFINE_SPINLOCK(uidhash_lock);
|
|
|
|
|
2011-11-17 17:32:59 +08:00
|
|
|
/* root_user.__count is 1, for init task cred */
|
2005-04-17 06:20:36 +08:00
|
|
|
struct user_struct root_user = {
|
2011-11-17 17:32:59 +08:00
|
|
|
.__count = ATOMIC_INIT(1),
|
2005-04-17 06:20:36 +08:00
|
|
|
.processes = ATOMIC_INIT(1),
|
|
|
|
.files = ATOMIC_INIT(0),
|
|
|
|
.sigpending = ATOMIC_INIT(0),
|
|
|
|
.locked_shm = 0,
|
2011-11-17 15:20:58 +08:00
|
|
|
.uid = GLOBAL_ROOT_UID,
|
2005-04-17 06:20:36 +08:00
|
|
|
};
|
|
|
|
|
2007-10-15 23:00:14 +08:00
|
|
|
/*
|
|
|
|
* These routines must be called with the uidhash spinlock held!
|
|
|
|
*/
|
2007-10-17 14:30:09 +08:00
|
|
|
static void uid_hash_insert(struct user_struct *up, struct hlist_head *hashent)
|
2007-10-15 23:00:14 +08:00
|
|
|
{
|
|
|
|
hlist_add_head(&up->uidhash_node, hashent);
|
|
|
|
}
|
|
|
|
|
2007-10-17 14:30:09 +08:00
|
|
|
static void uid_hash_remove(struct user_struct *up)
|
2007-10-15 23:00:14 +08:00
|
|
|
{
|
|
|
|
hlist_del_init(&up->uidhash_node);
|
|
|
|
}
|
|
|
|
|
2011-11-17 15:20:58 +08:00
|
|
|
static struct user_struct *uid_hash_find(kuid_t uid, struct hlist_head *hashent)
|
sched: delayed cleanup of user_struct
During bootup performance tracing we see repeated occurrences of
/sys/kernel/uid/* events for the same uid, leading to a,
in this case, rather pointless userspace processing for the
same uid over and over.
This is usually caused by tools which change their uid to "nobody",
to run without privileges to read data supplied by untrusted users.
This change delays the execution of the (already existing) scheduled
work, to cleanup the uid after one second, so the allocated and announced
uid can possibly be re-used by another process.
This is the current behavior, where almost every invocation of a
binary, which changes the uid, creates two events:
$ read START < /sys/kernel/uevent_seqnum; \
for i in `seq 100`; do su --shell=/bin/true bin; done; \
read END < /sys/kernel/uevent_seqnum; \
echo $(($END - $START))
178
With the delayed cleanup, we get only two events, and userspace finishes
a bit faster too:
$ read START < /sys/kernel/uevent_seqnum; \
for i in `seq 100`; do su --shell=/bin/true bin; done; \
read END < /sys/kernel/uevent_seqnum; \
echo $(($END - $START))
1
Acked-by: Dhaval Giani <dhaval@linux.vnet.ibm.com>
Signed-off-by: Kay Sievers <kay.sievers@vrfy.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2009-03-24 22:43:30 +08:00
|
|
|
{
|
|
|
|
struct user_struct *user;
|
|
|
|
struct hlist_node *h;
|
|
|
|
|
|
|
|
hlist_for_each_entry(user, h, hashent, uidhash_node) {
|
2011-11-17 15:20:58 +08:00
|
|
|
if (uid_eq(user->uid, uid)) {
|
sched: delayed cleanup of user_struct
During bootup performance tracing we see repeated occurrences of
/sys/kernel/uid/* events for the same uid, leading to a,
in this case, rather pointless userspace processing for the
same uid over and over.
This is usually caused by tools which change their uid to "nobody",
to run without privileges to read data supplied by untrusted users.
This change delays the execution of the (already existing) scheduled
work, to cleanup the uid after one second, so the allocated and announced
uid can possibly be re-used by another process.
This is the current behavior, where almost every invocation of a
binary, which changes the uid, creates two events:
$ read START < /sys/kernel/uevent_seqnum; \
for i in `seq 100`; do su --shell=/bin/true bin; done; \
read END < /sys/kernel/uevent_seqnum; \
echo $(($END - $START))
178
With the delayed cleanup, we get only two events, and userspace finishes
a bit faster too:
$ read START < /sys/kernel/uevent_seqnum; \
for i in `seq 100`; do su --shell=/bin/true bin; done; \
read END < /sys/kernel/uevent_seqnum; \
echo $(($END - $START))
1
Acked-by: Dhaval Giani <dhaval@linux.vnet.ibm.com>
Signed-off-by: Kay Sievers <kay.sievers@vrfy.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2009-03-24 22:43:30 +08:00
|
|
|
atomic_inc(&user->__count);
|
|
|
|
return user;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2007-10-15 23:00:14 +08:00
|
|
|
/* IRQs are disabled and uidhash_lock is held upon function entry.
|
|
|
|
* IRQ state (as stored in flags) is restored and uidhash_lock released
|
|
|
|
* upon function exit.
|
|
|
|
*/
|
2008-10-16 05:38:45 +08:00
|
|
|
static void free_user(struct user_struct *up, unsigned long flags)
|
2010-10-27 05:22:43 +08:00
|
|
|
__releases(&uidhash_lock)
|
2007-10-15 23:00:14 +08:00
|
|
|
{
|
|
|
|
uid_hash_remove(up);
|
|
|
|
spin_unlock_irqrestore(&uidhash_lock, flags);
|
|
|
|
key_put(up->uid_keyring);
|
|
|
|
key_put(up->session_keyring);
|
|
|
|
kmem_cache_free(uid_cachep, up);
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* Locate the user_struct for the passed UID. If found, take a ref on it. The
|
|
|
|
* caller must undo that ref with free_uid().
|
|
|
|
*
|
|
|
|
* If the user_struct could not be found, return NULL.
|
|
|
|
*/
|
2011-11-17 15:20:58 +08:00
|
|
|
struct user_struct *find_user(kuid_t uid)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct user_struct *ret;
|
2006-02-01 08:34:26 +08:00
|
|
|
unsigned long flags;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-02-01 08:34:26 +08:00
|
|
|
spin_lock_irqsave(&uidhash_lock, flags);
|
2011-11-17 15:20:58 +08:00
|
|
|
ret = uid_hash_find(uid, uidhashentry(uid));
|
2006-02-01 08:34:26 +08:00
|
|
|
spin_unlock_irqrestore(&uidhash_lock, flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
void free_uid(struct user_struct *up)
|
|
|
|
{
|
2006-02-01 08:34:26 +08:00
|
|
|
unsigned long flags;
|
|
|
|
|
2006-03-24 19:15:47 +08:00
|
|
|
if (!up)
|
|
|
|
return;
|
|
|
|
|
2006-02-01 08:34:26 +08:00
|
|
|
local_irq_save(flags);
|
2007-10-15 23:00:14 +08:00
|
|
|
if (atomic_dec_and_lock(&up->__count, &uidhash_lock))
|
|
|
|
free_user(up, flags);
|
|
|
|
else
|
2006-03-24 19:15:47 +08:00
|
|
|
local_irq_restore(flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2011-11-17 15:20:58 +08:00
|
|
|
struct user_struct *alloc_uid(kuid_t uid)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2011-11-17 15:20:58 +08:00
|
|
|
struct hlist_head *hashent = uidhashentry(uid);
|
2008-01-26 04:08:26 +08:00
|
|
|
struct user_struct *up, *new;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-02-01 08:34:26 +08:00
|
|
|
spin_lock_irq(&uidhash_lock);
|
2005-04-17 06:20:36 +08:00
|
|
|
up = uid_hash_find(uid, hashent);
|
2006-02-01 08:34:26 +08:00
|
|
|
spin_unlock_irq(&uidhash_lock);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (!up) {
|
2008-04-30 15:54:54 +08:00
|
|
|
new = kmem_cache_zalloc(uid_cachep, GFP_KERNEL);
|
2008-01-26 04:08:26 +08:00
|
|
|
if (!new)
|
|
|
|
goto out_unlock;
|
2007-11-27 04:21:49 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
new->uid = uid;
|
|
|
|
atomic_set(&new->__count, 1);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Before adding this, check whether we raced
|
|
|
|
* on adding the same user already..
|
|
|
|
*/
|
2006-02-01 08:34:26 +08:00
|
|
|
spin_lock_irq(&uidhash_lock);
|
2005-04-17 06:20:36 +08:00
|
|
|
up = uid_hash_find(uid, hashent);
|
|
|
|
if (up) {
|
|
|
|
key_put(new->uid_keyring);
|
|
|
|
key_put(new->session_keyring);
|
|
|
|
kmem_cache_free(uid_cachep, new);
|
|
|
|
} else {
|
|
|
|
uid_hash_insert(new, hashent);
|
|
|
|
up = new;
|
|
|
|
}
|
2006-02-01 08:34:26 +08:00
|
|
|
spin_unlock_irq(&uidhash_lock);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2007-10-15 23:00:14 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
return up;
|
2008-01-26 04:08:26 +08:00
|
|
|
|
|
|
|
out_unlock:
|
|
|
|
return NULL;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int __init uid_cache_init(void)
|
|
|
|
{
|
|
|
|
int n;
|
|
|
|
|
|
|
|
uid_cachep = kmem_cache_create("uid_cache", sizeof(struct user_struct),
|
2007-07-20 09:11:58 +08:00
|
|
|
0, SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
for(n = 0; n < UIDHASH_SZ; ++n)
|
2011-11-17 15:20:58 +08:00
|
|
|
INIT_HLIST_HEAD(uidhash_table + n);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* Insert the root user immediately (init already runs as root) */
|
2006-02-01 08:34:26 +08:00
|
|
|
spin_lock_irq(&uidhash_lock);
|
2011-11-17 15:20:58 +08:00
|
|
|
uid_hash_insert(&root_user, uidhashentry(GLOBAL_ROOT_UID));
|
2006-02-01 08:34:26 +08:00
|
|
|
spin_unlock_irq(&uidhash_lock);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
module_init(uid_cache_init);
|