diff options
author | Eric W. Biederman <ebiederm@xmission.com> | 2016-08-08 15:33:23 -0400 |
---|---|---|
committer | Eric W. Biederman <ebiederm@xmission.com> | 2016-08-08 15:42:04 -0400 |
commit | 703286608a220d53584cca5986aad5305eec75ed (patch) | |
tree | 2f0476d44dcd98e891697ef7aaef9653b06b1b4e | |
parent | d08311dd6fd8444e39710dd2fb97562895aed8fa (diff) |
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>
-rw-r--r-- | include/linux/user_namespace.h | 1 | ||||
-rw-r--r-- | include/net/net_namespace.h | 1 | ||||
-rw-r--r-- | kernel/ucount.c | 1 | ||||
-rw-r--r-- | net/core/net_namespace.c | 22 |
4 files changed, 24 insertions, 1 deletions
diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h index d067f0d3038e..c6bc980b06a9 100644 --- a/include/linux/user_namespace.h +++ b/include/linux/user_namespace.h | |||
@@ -29,6 +29,7 @@ enum ucount_type { | |||
29 | UCOUNT_PID_NAMESPACES, | 29 | UCOUNT_PID_NAMESPACES, |
30 | UCOUNT_UTS_NAMESPACES, | 30 | UCOUNT_UTS_NAMESPACES, |
31 | UCOUNT_IPC_NAMESPACES, | 31 | UCOUNT_IPC_NAMESPACES, |
32 | UCOUNT_NET_NAMESPACES, | ||
32 | UCOUNT_CGROUP_NAMESPACES, | 33 | UCOUNT_CGROUP_NAMESPACES, |
33 | UCOUNT_COUNTS, | 34 | UCOUNT_COUNTS, |
34 | }; | 35 | }; |
diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h index 0933c7455a30..fc4f757107df 100644 --- a/include/net/net_namespace.h +++ b/include/net/net_namespace.h | |||
@@ -60,6 +60,7 @@ struct net { | |||
60 | struct list_head exit_list; /* Use only net_mutex */ | 60 | struct list_head exit_list; /* Use only net_mutex */ |
61 | 61 | ||
62 | struct user_namespace *user_ns; /* Owning user namespace */ | 62 | struct user_namespace *user_ns; /* Owning user namespace */ |
63 | struct ucounts *ucounts; | ||
63 | spinlock_t nsid_lock; | 64 | spinlock_t nsid_lock; |
64 | struct idr netns_ids; | 65 | struct idr netns_ids; |
65 | 66 | ||
diff --git a/kernel/ucount.c b/kernel/ucount.c index 335cc5d2cdd7..205f1a07faac 100644 --- a/kernel/ucount.c +++ b/kernel/ucount.c | |||
@@ -71,6 +71,7 @@ static struct ctl_table user_table[] = { | |||
71 | UCOUNT_ENTRY("max_pid_namespaces"), | 71 | UCOUNT_ENTRY("max_pid_namespaces"), |
72 | UCOUNT_ENTRY("max_uts_namespaces"), | 72 | UCOUNT_ENTRY("max_uts_namespaces"), |
73 | UCOUNT_ENTRY("max_ipc_namespaces"), | 73 | UCOUNT_ENTRY("max_ipc_namespaces"), |
74 | UCOUNT_ENTRY("max_net_namespaces"), | ||
74 | UCOUNT_ENTRY("max_cgroup_namespaces"), | 75 | UCOUNT_ENTRY("max_cgroup_namespaces"), |
75 | { } | 76 | { } |
76 | }; | 77 | }; |
diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c index 2c2eb1b629b1..3e2812aeceb7 100644 --- a/net/core/net_namespace.c +++ b/net/core/net_namespace.c | |||
@@ -266,6 +266,16 @@ struct net *get_net_ns_by_id(struct net *net, int id) | |||
266 | return peer; | 266 | return peer; |
267 | } | 267 | } |
268 | 268 | ||
269 | static struct ucounts *inc_net_namespaces(struct user_namespace *ns) | ||
270 | { | ||
271 | return inc_ucount(ns, current_euid(), UCOUNT_NET_NAMESPACES); | ||
272 | } | ||
273 | |||
274 | static void dec_net_namespaces(struct ucounts *ucounts) | ||
275 | { | ||
276 | dec_ucount(ucounts, UCOUNT_NET_NAMESPACES); | ||
277 | } | ||
278 | |||
269 | /* | 279 | /* |
270 | * setup_net runs the initializers for the network namespace object. | 280 | * setup_net runs the initializers for the network namespace object. |
271 | */ | 281 | */ |
@@ -351,19 +361,27 @@ void net_drop_ns(void *p) | |||
351 | struct net *copy_net_ns(unsigned long flags, | 361 | struct net *copy_net_ns(unsigned long flags, |
352 | struct user_namespace *user_ns, struct net *old_net) | 362 | struct user_namespace *user_ns, struct net *old_net) |
353 | { | 363 | { |
364 | struct ucounts *ucounts; | ||
354 | struct net *net; | 365 | struct net *net; |
355 | int rv; | 366 | int rv; |
356 | 367 | ||
357 | if (!(flags & CLONE_NEWNET)) | 368 | if (!(flags & CLONE_NEWNET)) |
358 | return get_net(old_net); | 369 | return get_net(old_net); |
359 | 370 | ||
371 | ucounts = inc_net_namespaces(user_ns); | ||
372 | if (!ucounts) | ||
373 | return ERR_PTR(-ENFILE); | ||
374 | |||
360 | net = net_alloc(); | 375 | net = net_alloc(); |
361 | if (!net) | 376 | if (!net) { |
377 | dec_net_namespaces(ucounts); | ||
362 | return ERR_PTR(-ENOMEM); | 378 | return ERR_PTR(-ENOMEM); |
379 | } | ||
363 | 380 | ||
364 | get_user_ns(user_ns); | 381 | get_user_ns(user_ns); |
365 | 382 | ||
366 | mutex_lock(&net_mutex); | 383 | mutex_lock(&net_mutex); |
384 | net->ucounts = ucounts; | ||
367 | rv = setup_net(net, user_ns); | 385 | rv = setup_net(net, user_ns); |
368 | if (rv == 0) { | 386 | if (rv == 0) { |
369 | rtnl_lock(); | 387 | rtnl_lock(); |
@@ -372,6 +390,7 @@ struct net *copy_net_ns(unsigned long flags, | |||
372 | } | 390 | } |
373 | mutex_unlock(&net_mutex); | 391 | mutex_unlock(&net_mutex); |
374 | if (rv < 0) { | 392 | if (rv < 0) { |
393 | dec_net_namespaces(ucounts); | ||
375 | put_user_ns(user_ns); | 394 | put_user_ns(user_ns); |
376 | net_drop_ns(net); | 395 | net_drop_ns(net); |
377 | return ERR_PTR(rv); | 396 | return ERR_PTR(rv); |
@@ -444,6 +463,7 @@ static void cleanup_net(struct work_struct *work) | |||
444 | /* Finally it is safe to free my network namespace structure */ | 463 | /* Finally it is safe to free my network namespace structure */ |
445 | list_for_each_entry_safe(net, tmp, &net_exit_list, exit_list) { | 464 | list_for_each_entry_safe(net, tmp, &net_exit_list, exit_list) { |
446 | list_del_init(&net->exit_list); | 465 | list_del_init(&net->exit_list); |
466 | dec_net_namespaces(net->ucounts); | ||
447 | put_user_ns(net->user_ns); | 467 | put_user_ns(net->user_ns); |
448 | net_drop_ns(net); | 468 | net_drop_ns(net); |
449 | } | 469 | } |