netns: Add a limit on the number of net namespaces

Acked-by: Kees Cook <keescook@chromium.org>
Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com>
This commit is contained in:
Eric W. Biederman 2016-08-08 14:33:23 -05:00
parent d08311dd6f
commit 703286608a
4 changed files with 24 additions and 1 deletions

View file

@ -29,6 +29,7 @@ enum ucount_type {
UCOUNT_PID_NAMESPACES, UCOUNT_PID_NAMESPACES,
UCOUNT_UTS_NAMESPACES, UCOUNT_UTS_NAMESPACES,
UCOUNT_IPC_NAMESPACES, UCOUNT_IPC_NAMESPACES,
UCOUNT_NET_NAMESPACES,
UCOUNT_CGROUP_NAMESPACES, UCOUNT_CGROUP_NAMESPACES,
UCOUNT_COUNTS, UCOUNT_COUNTS,
}; };

View file

@ -60,6 +60,7 @@ struct net {
struct list_head exit_list; /* Use only net_mutex */ struct list_head exit_list; /* Use only net_mutex */
struct user_namespace *user_ns; /* Owning user namespace */ struct user_namespace *user_ns; /* Owning user namespace */
struct ucounts *ucounts;
spinlock_t nsid_lock; spinlock_t nsid_lock;
struct idr netns_ids; struct idr netns_ids;

View file

@ -71,6 +71,7 @@ static struct ctl_table user_table[] = {
UCOUNT_ENTRY("max_pid_namespaces"), UCOUNT_ENTRY("max_pid_namespaces"),
UCOUNT_ENTRY("max_uts_namespaces"), UCOUNT_ENTRY("max_uts_namespaces"),
UCOUNT_ENTRY("max_ipc_namespaces"), UCOUNT_ENTRY("max_ipc_namespaces"),
UCOUNT_ENTRY("max_net_namespaces"),
UCOUNT_ENTRY("max_cgroup_namespaces"), UCOUNT_ENTRY("max_cgroup_namespaces"),
{ } { }
}; };

View file

@ -266,6 +266,16 @@ struct net *get_net_ns_by_id(struct net *net, int id)
return peer; return peer;
} }
static struct ucounts *inc_net_namespaces(struct user_namespace *ns)
{
return inc_ucount(ns, current_euid(), UCOUNT_NET_NAMESPACES);
}
static void dec_net_namespaces(struct ucounts *ucounts)
{
dec_ucount(ucounts, UCOUNT_NET_NAMESPACES);
}
/* /*
* setup_net runs the initializers for the network namespace object. * setup_net runs the initializers for the network namespace object.
*/ */
@ -351,19 +361,27 @@ void net_drop_ns(void *p)
struct net *copy_net_ns(unsigned long flags, struct net *copy_net_ns(unsigned long flags,
struct user_namespace *user_ns, struct net *old_net) struct user_namespace *user_ns, struct net *old_net)
{ {
struct ucounts *ucounts;
struct net *net; struct net *net;
int rv; int rv;
if (!(flags & CLONE_NEWNET)) if (!(flags & CLONE_NEWNET))
return get_net(old_net); return get_net(old_net);
ucounts = inc_net_namespaces(user_ns);
if (!ucounts)
return ERR_PTR(-ENFILE);
net = net_alloc(); net = net_alloc();
if (!net) if (!net) {
dec_net_namespaces(ucounts);
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
}
get_user_ns(user_ns); get_user_ns(user_ns);
mutex_lock(&net_mutex); mutex_lock(&net_mutex);
net->ucounts = ucounts;
rv = setup_net(net, user_ns); rv = setup_net(net, user_ns);
if (rv == 0) { if (rv == 0) {
rtnl_lock(); rtnl_lock();
@ -372,6 +390,7 @@ struct net *copy_net_ns(unsigned long flags,
} }
mutex_unlock(&net_mutex); mutex_unlock(&net_mutex);
if (rv < 0) { if (rv < 0) {
dec_net_namespaces(ucounts);
put_user_ns(user_ns); put_user_ns(user_ns);
net_drop_ns(net); net_drop_ns(net);
return ERR_PTR(rv); return ERR_PTR(rv);
@ -444,6 +463,7 @@ static void cleanup_net(struct work_struct *work)
/* Finally it is safe to free my network namespace structure */ /* Finally it is safe to free my network namespace structure */
list_for_each_entry_safe(net, tmp, &net_exit_list, exit_list) { list_for_each_entry_safe(net, tmp, &net_exit_list, exit_list) {
list_del_init(&net->exit_list); list_del_init(&net->exit_list);
dec_net_namespaces(net->ucounts);
put_user_ns(net->user_ns); put_user_ns(net->user_ns);
net_drop_ns(net); net_drop_ns(net);
} }