userns: Generalize the user namespace count into ucount
The same kind of recursive sane default limit and policy countrol that has been implemented for the user namespace is desirable for the other namespaces, so generalize the user namespace refernce count into a ucount. Acked-by: Kees Cook <keescook@chromium.org> Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com>
This commit is contained in:
parent
f6b2db1a3e
commit
25f9c0817c
4 changed files with 49 additions and 25 deletions
|
@ -23,6 +23,12 @@ struct uid_gid_map { /* 64 bytes -- 1 cache line */
|
||||||
#define USERNS_INIT_FLAGS USERNS_SETGROUPS_ALLOWED
|
#define USERNS_INIT_FLAGS USERNS_SETGROUPS_ALLOWED
|
||||||
|
|
||||||
struct ucounts;
|
struct ucounts;
|
||||||
|
|
||||||
|
enum ucount_type {
|
||||||
|
UCOUNT_USER_NAMESPACES,
|
||||||
|
UCOUNT_COUNTS,
|
||||||
|
};
|
||||||
|
|
||||||
struct user_namespace {
|
struct user_namespace {
|
||||||
struct uid_gid_map uid_map;
|
struct uid_gid_map uid_map;
|
||||||
struct uid_gid_map gid_map;
|
struct uid_gid_map gid_map;
|
||||||
|
@ -46,7 +52,7 @@ struct user_namespace {
|
||||||
struct ctl_table_header *sysctls;
|
struct ctl_table_header *sysctls;
|
||||||
#endif
|
#endif
|
||||||
struct ucounts *ucounts;
|
struct ucounts *ucounts;
|
||||||
int max_user_namespaces;
|
int ucount_max[UCOUNT_COUNTS];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ucounts {
|
struct ucounts {
|
||||||
|
@ -54,15 +60,15 @@ struct ucounts {
|
||||||
struct user_namespace *ns;
|
struct user_namespace *ns;
|
||||||
kuid_t uid;
|
kuid_t uid;
|
||||||
atomic_t count;
|
atomic_t count;
|
||||||
atomic_t user_namespaces;
|
atomic_t ucount[UCOUNT_COUNTS];
|
||||||
};
|
};
|
||||||
|
|
||||||
extern struct user_namespace init_user_ns;
|
extern struct user_namespace init_user_ns;
|
||||||
|
|
||||||
bool setup_userns_sysctls(struct user_namespace *ns);
|
bool setup_userns_sysctls(struct user_namespace *ns);
|
||||||
void retire_userns_sysctls(struct user_namespace *ns);
|
void retire_userns_sysctls(struct user_namespace *ns);
|
||||||
struct ucounts *inc_user_namespaces(struct user_namespace *ns, kuid_t uid);
|
struct ucounts *inc_ucount(struct user_namespace *ns, kuid_t uid, enum ucount_type type);
|
||||||
void dec_user_namespaces(struct ucounts *ucounts);
|
void dec_ucount(struct ucounts *ucounts, enum ucount_type type);
|
||||||
|
|
||||||
#ifdef CONFIG_USER_NS
|
#ifdef CONFIG_USER_NS
|
||||||
|
|
||||||
|
|
|
@ -302,6 +302,7 @@ int arch_task_struct_size __read_mostly;
|
||||||
|
|
||||||
void __init fork_init(void)
|
void __init fork_init(void)
|
||||||
{
|
{
|
||||||
|
int i;
|
||||||
#ifndef CONFIG_ARCH_TASK_STRUCT_ALLOCATOR
|
#ifndef CONFIG_ARCH_TASK_STRUCT_ALLOCATOR
|
||||||
#ifndef ARCH_MIN_TASKALIGN
|
#ifndef ARCH_MIN_TASKALIGN
|
||||||
#define ARCH_MIN_TASKALIGN L1_CACHE_BYTES
|
#define ARCH_MIN_TASKALIGN L1_CACHE_BYTES
|
||||||
|
@ -322,7 +323,9 @@ void __init fork_init(void)
|
||||||
init_task.signal->rlim[RLIMIT_SIGPENDING] =
|
init_task.signal->rlim[RLIMIT_SIGPENDING] =
|
||||||
init_task.signal->rlim[RLIMIT_NPROC];
|
init_task.signal->rlim[RLIMIT_NPROC];
|
||||||
|
|
||||||
init_user_ns.max_user_namespaces = max_threads/2;
|
for (i = 0; i < UCOUNT_COUNTS; i++) {
|
||||||
|
init_user_ns.ucount_max[i] = max_threads/2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int __weak arch_dup_task_struct(struct task_struct *dst,
|
int __weak arch_dup_task_struct(struct task_struct *dst,
|
||||||
|
|
|
@ -57,16 +57,17 @@ static struct ctl_table_root set_root = {
|
||||||
|
|
||||||
static int zero = 0;
|
static int zero = 0;
|
||||||
static int int_max = INT_MAX;
|
static int int_max = INT_MAX;
|
||||||
|
#define UCOUNT_ENTRY(name) \
|
||||||
|
{ \
|
||||||
|
.procname = name, \
|
||||||
|
.maxlen = sizeof(int), \
|
||||||
|
.mode = 0644, \
|
||||||
|
.proc_handler = proc_dointvec_minmax, \
|
||||||
|
.extra1 = &zero, \
|
||||||
|
.extra2 = &int_max, \
|
||||||
|
}
|
||||||
static struct ctl_table user_table[] = {
|
static struct ctl_table user_table[] = {
|
||||||
{
|
UCOUNT_ENTRY("max_user_namespaces"),
|
||||||
.procname = "max_user_namespaces",
|
|
||||||
.data = &init_user_ns.max_user_namespaces,
|
|
||||||
.maxlen = sizeof(init_user_ns.max_user_namespaces),
|
|
||||||
.mode = 0644,
|
|
||||||
.proc_handler = proc_dointvec_minmax,
|
|
||||||
.extra1 = &zero,
|
|
||||||
.extra2 = &int_max,
|
|
||||||
},
|
|
||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
#endif /* CONFIG_SYSCTL */
|
#endif /* CONFIG_SYSCTL */
|
||||||
|
@ -78,8 +79,10 @@ bool setup_userns_sysctls(struct user_namespace *ns)
|
||||||
setup_sysctl_set(&ns->set, &set_root, set_is_seen);
|
setup_sysctl_set(&ns->set, &set_root, set_is_seen);
|
||||||
tbl = kmemdup(user_table, sizeof(user_table), GFP_KERNEL);
|
tbl = kmemdup(user_table, sizeof(user_table), GFP_KERNEL);
|
||||||
if (tbl) {
|
if (tbl) {
|
||||||
tbl[0].data = &ns->max_user_namespaces;
|
int i;
|
||||||
|
for (i = 0; i < UCOUNT_COUNTS; i++) {
|
||||||
|
tbl[i].data = &ns->ucount_max[i];
|
||||||
|
}
|
||||||
ns->sysctls = __register_sysctl_table(&ns->set, "user", tbl);
|
ns->sysctls = __register_sysctl_table(&ns->set, "user", tbl);
|
||||||
}
|
}
|
||||||
if (!ns->sysctls) {
|
if (!ns->sysctls) {
|
||||||
|
@ -172,7 +175,8 @@ static inline bool atomic_inc_below(atomic_t *v, int u)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ucounts *inc_user_namespaces(struct user_namespace *ns, kuid_t uid)
|
struct ucounts *inc_ucount(struct user_namespace *ns, kuid_t uid,
|
||||||
|
enum ucount_type type)
|
||||||
{
|
{
|
||||||
struct ucounts *ucounts, *iter, *bad;
|
struct ucounts *ucounts, *iter, *bad;
|
||||||
struct user_namespace *tns;
|
struct user_namespace *tns;
|
||||||
|
@ -180,31 +184,30 @@ struct ucounts *inc_user_namespaces(struct user_namespace *ns, kuid_t uid)
|
||||||
for (iter = ucounts; iter; iter = tns->ucounts) {
|
for (iter = ucounts; iter; iter = tns->ucounts) {
|
||||||
int max;
|
int max;
|
||||||
tns = iter->ns;
|
tns = iter->ns;
|
||||||
max = READ_ONCE(tns->max_user_namespaces);
|
max = READ_ONCE(tns->ucount_max[type]);
|
||||||
if (!atomic_inc_below(&iter->user_namespaces, max))
|
if (!atomic_inc_below(&iter->ucount[type], max))
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
return ucounts;
|
return ucounts;
|
||||||
fail:
|
fail:
|
||||||
bad = iter;
|
bad = iter;
|
||||||
for (iter = ucounts; iter != bad; iter = iter->ns->ucounts)
|
for (iter = ucounts; iter != bad; iter = iter->ns->ucounts)
|
||||||
atomic_dec(&iter->user_namespaces);
|
atomic_dec(&iter->ucount[type]);
|
||||||
|
|
||||||
put_ucounts(ucounts);
|
put_ucounts(ucounts);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void dec_user_namespaces(struct ucounts *ucounts)
|
void dec_ucount(struct ucounts *ucounts, enum ucount_type type)
|
||||||
{
|
{
|
||||||
struct ucounts *iter;
|
struct ucounts *iter;
|
||||||
for (iter = ucounts; iter; iter = iter->ns->ucounts) {
|
for (iter = ucounts; iter; iter = iter->ns->ucounts) {
|
||||||
int dec = atomic_dec_if_positive(&iter->user_namespaces);
|
int dec = atomic_dec_if_positive(&iter->ucount[type]);
|
||||||
WARN_ON_ONCE(dec < 0);
|
WARN_ON_ONCE(dec < 0);
|
||||||
}
|
}
|
||||||
put_ucounts(ucounts);
|
put_ucounts(ucounts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static __init int user_namespace_sysctl_init(void)
|
static __init int user_namespace_sysctl_init(void)
|
||||||
{
|
{
|
||||||
#ifdef CONFIG_SYSCTL
|
#ifdef CONFIG_SYSCTL
|
||||||
|
|
|
@ -31,6 +31,16 @@ static bool new_idmap_permitted(const struct file *file,
|
||||||
struct uid_gid_map *map);
|
struct uid_gid_map *map);
|
||||||
static void free_user_ns(struct work_struct *work);
|
static void free_user_ns(struct work_struct *work);
|
||||||
|
|
||||||
|
static struct ucounts *inc_user_namespaces(struct user_namespace *ns, kuid_t uid)
|
||||||
|
{
|
||||||
|
return inc_ucount(ns, uid, UCOUNT_USER_NAMESPACES);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dec_user_namespaces(struct ucounts *ucounts)
|
||||||
|
{
|
||||||
|
return dec_ucount(ucounts, UCOUNT_USER_NAMESPACES);
|
||||||
|
}
|
||||||
|
|
||||||
static void set_cred_user_ns(struct cred *cred, struct user_namespace *user_ns)
|
static void set_cred_user_ns(struct cred *cred, struct user_namespace *user_ns)
|
||||||
{
|
{
|
||||||
/* Start with the same capabilities as init but useless for doing
|
/* Start with the same capabilities as init but useless for doing
|
||||||
|
@ -64,7 +74,7 @@ int create_user_ns(struct cred *new)
|
||||||
kuid_t owner = new->euid;
|
kuid_t owner = new->euid;
|
||||||
kgid_t group = new->egid;
|
kgid_t group = new->egid;
|
||||||
struct ucounts *ucounts;
|
struct ucounts *ucounts;
|
||||||
int ret;
|
int ret, i;
|
||||||
|
|
||||||
ret = -EUSERS;
|
ret = -EUSERS;
|
||||||
if (parent_ns->level > 32)
|
if (parent_ns->level > 32)
|
||||||
|
@ -110,7 +120,9 @@ int create_user_ns(struct cred *new)
|
||||||
ns->owner = owner;
|
ns->owner = owner;
|
||||||
ns->group = group;
|
ns->group = group;
|
||||||
INIT_WORK(&ns->work, free_user_ns);
|
INIT_WORK(&ns->work, free_user_ns);
|
||||||
ns->max_user_namespaces = INT_MAX;
|
for (i = 0; i < UCOUNT_COUNTS; i++) {
|
||||||
|
ns->ucount_max[i] = INT_MAX;
|
||||||
|
}
|
||||||
ns->ucounts = ucounts;
|
ns->ucounts = ucounts;
|
||||||
|
|
||||||
/* Inherit USERNS_SETGROUPS_ALLOWED from our parent */
|
/* Inherit USERNS_SETGROUPS_ALLOWED from our parent */
|
||||||
|
|
Loading…
Reference in a new issue