From patchwork Thu Jul 21 16:40:07 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Eric W. Biederman" X-Patchwork-Id: 651295 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3rwKj12gVMz9t0l for ; Fri, 22 Jul 2016 02:57:57 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754047AbcGUQ5V (ORCPT ); Thu, 21 Jul 2016 12:57:21 -0400 Received: from out03.mta.xmission.com ([166.70.13.233]:40898 "EHLO out03.mta.xmission.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753568AbcGUQxt (ORCPT ); Thu, 21 Jul 2016 12:53:49 -0400 Received: from in02.mta.xmission.com ([166.70.13.52]) by out03.mta.xmission.com with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.87) (envelope-from ) id 1bQHEI-0000Pz-Vb; Thu, 21 Jul 2016 10:53:47 -0600 Received: from 67-3-204-119.omah.qwest.net ([67.3.204.119] helo=x220.int.ebiederm.org) by in02.mta.xmission.com with esmtpsa (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.87) (envelope-from ) id 1bQHEH-0000M2-Hm; Thu, 21 Jul 2016 10:53:46 -0600 From: "Eric W. Biederman" To: Linux Containers Cc: Andy Lutomirski , Jann Horn , Kees Cook , Nikolay Borisov , "Serge E. Hallyn" , Seth Forshee , linux-fsdevel@vger.kernel.org, netdev@vger.kernel.org, linux-kernel@vger.kernel.org, linux-api@vger.kernel.org, "Eric W. Biederman" Date: Thu, 21 Jul 2016 11:40:07 -0500 Message-Id: <20160721164014.17534-3-ebiederm@xmission.com> X-Mailer: git-send-email 2.8.3 In-Reply-To: <20160721164014.17534-1-ebiederm@xmission.com> References: <87d1m754jc.fsf@x220.int.ebiederm.org> <20160721164014.17534-1-ebiederm@xmission.com> X-XM-SPF: eid=1bQHEH-0000M2-Hm; ; ; mid=<20160721164014.17534-3-ebiederm@xmission.com>; ; ; hst=in02.mta.xmission.com; ; ; ip=67.3.204.119; ; ; frm=ebiederm@xmission.com; ; ; spf=neutral X-XM-AID: U2FsdGVkX1+ZVudxSQ0XsBWM0pfj2BLEqux3FWvTTJI= X-SA-Exim-Connect-IP: 67.3.204.119 X-SA-Exim-Mail-From: ebiederm@xmission.com X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on sa02.xmission.com X-Spam-Level: X-Spam-Status: No, score=0.5 required=8.0 tests=ALL_TRUSTED,BAYES_50, DCC_CHECK_NEGATIVE, TVD_RCVD_IP, XMSubLong autolearn=disabled version=3.4.0 X-Spam-Report: * -1.0 ALL_TRUSTED Passed through trusted hosts only via SMTP * 0.7 XMSubLong Long Subject * 0.0 TVD_RCVD_IP Message was received from an IP address * 0.8 BAYES_50 BODY: Bayes spam probability is 40 to 60% * [score: 0.5000] * -0.0 DCC_CHECK_NEGATIVE Not listed in DCC * [sa02 1397; Body=1 Fuz1=1 Fuz2=1] X-Spam-DCC: XMission; sa02 1397; Body=1 Fuz1=1 Fuz2=1 X-Spam-Combo: ;Linux Containers X-Spam-Relay-Country: X-Spam-Timing: total 960 ms - load_scoreonly_sql: 0.08 (0.0%), signal_user_changed: 4.1 (0.4%), b_tie_ro: 2.9 (0.3%), parse: 1.65 (0.2%), extract_message_metadata: 30 (3.1%), get_uri_detail_list: 6 (0.6%), tests_pri_-1000: 12 (1.2%), tests_pri_-950: 2.3 (0.2%), tests_pri_-900: 1.97 (0.2%), tests_pri_-400: 52 (5.5%), check_bayes: 50 (5.2%), b_tokenize: 19 (2.0%), b_tok_get_all: 14 (1.4%), b_comp_prob: 4.9 (0.5%), b_tok_touch_all: 8 (0.9%), b_finish: 0.91 (0.1%), tests_pri_0: 842 (87.7%), check_dkim_signature: 1.23 (0.1%), check_dkim_adsp: 5 (0.5%), tests_pri_500: 7 (0.8%), rewrite_mail: 0.00 (0.0%) Subject: [PATCH v2 03/10] userns: Add a limit on the number of user namespaces X-Spam-Flag: No X-SA-Exim-Version: 4.2.1 (built Thu, 05 May 2016 13:38:54 -0600) X-SA-Exim-Scanned: Yes (on in02.mta.xmission.com) Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Export the export the maximum number of user namespaces as /proc/sys/userns/max_user_namespaces. Signed-off-by: "Eric W. Biederman" Acked-by: Serge Hallyn --- include/linux/user_namespace.h | 2 ++ kernel/fork.c | 2 ++ kernel/user_namespace.c | 69 +++++++++++++++++++++++++++++++++++++----- 3 files changed, 65 insertions(+), 8 deletions(-) diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h index 7d59af1f08f1..ba6a995178f9 100644 --- a/include/linux/user_namespace.h +++ b/include/linux/user_namespace.h @@ -43,6 +43,8 @@ struct user_namespace { struct ctl_table_set set; struct ctl_table_header *sysctls; #endif + int max_user_namespaces; + atomic_t user_namespaces; }; extern struct user_namespace init_user_ns; diff --git a/kernel/fork.c b/kernel/fork.c index 5c2c355aa97f..95d5498c463f 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -323,6 +323,8 @@ void __init fork_init(void) init_task.signal->rlim[RLIMIT_NPROC].rlim_max = max_threads/2; init_task.signal->rlim[RLIMIT_SIGPENDING] = init_task.signal->rlim[RLIMIT_NPROC]; + + init_user_ns.max_user_namespaces = max_threads; } int __weak arch_dup_task_struct(struct task_struct *dst, diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c index 10afbb55dfc2..0061550e3282 100644 --- a/kernel/user_namespace.c +++ b/kernel/user_namespace.c @@ -29,6 +29,7 @@ static DEFINE_MUTEX(userns_state_mutex); static bool new_idmap_permitted(const struct file *file, struct user_namespace *ns, int cap_setid, struct uid_gid_map *map); +#define COUNT_MAX (INT_MAX - 1) #ifdef CONFIG_SYSCTL static struct ctl_table_set * @@ -63,7 +64,18 @@ static struct ctl_table_root set_root = { .permissions = set_permissions, }; +static int zero = 0; +static int count_max = COUNT_MAX; static struct ctl_table userns_table[] = { + { + .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 = &count_max, + }, { } }; #endif /* CONFIG_SYSCTL */ @@ -75,6 +87,8 @@ static bool setup_userns_sysctls(struct user_namespace *ns) setup_sysctl_set(&ns->set, &set_root, set_is_seen); tbl = kmemdup(userns_table, sizeof(userns_table), GFP_KERNEL); if (tbl) { + tbl[0].data = &ns->max_user_namespaces; + ns->sysctls = __register_sysctl_table(&ns->set, "userns", tbl); } if (!ns->sysctls) { @@ -113,6 +127,34 @@ static void set_cred_user_ns(struct cred *cred, struct user_namespace *user_ns) cred->user_ns = user_ns; } +static bool inc_user_namespaces(struct user_namespace *ns) +{ + struct user_namespace *pos, *bad; + for (pos = ns; pos; pos = pos->parent) { + int max = READ_ONCE(pos->max_user_namespaces); + int sum = atomic_inc_return(&pos->user_namespaces); + if (sum > max) + goto fail; + } + return true; +fail: + bad = pos; + atomic_dec(&pos->user_namespaces); + for (pos = ns; pos != bad; pos = pos->parent) + atomic_dec(&pos->user_namespaces); + + return false; +} + +static void dec_user_namespaces(struct user_namespace *ns) +{ + struct user_namespace *pos; + for (pos = ns; pos; pos = pos->parent) { + int dec = atomic_dec_if_positive(&pos->user_namespaces); + WARN_ON_ONCE(dec < 0); + } +} + /* * Create a new user namespace, deriving the creator from the user in the * passed credentials, and replacing that user with the new root user for the @@ -128,8 +170,12 @@ int create_user_ns(struct cred *new) kgid_t group = new->egid; int ret; + ret = -EUSERS; if (parent_ns->level > 32) - return -EUSERS; + goto fail; + + if (!inc_user_namespaces(parent_ns)) + goto fail; /* * Verify that we can not violate the policy of which files @@ -137,26 +183,27 @@ int create_user_ns(struct cred *new) * by verifing that the root directory is at the root of the * mount namespace which allows all files to be accessed. */ + ret = -EPERM; if (current_chrooted()) - return -EPERM; + goto fail_dec; /* The creator needs a mapping in the parent user namespace * or else we won't be able to reasonably tell userspace who * created a user_namespace. */ + ret = -EPERM; if (!kuid_has_mapping(parent_ns, owner) || !kgid_has_mapping(parent_ns, group)) - return -EPERM; + goto fail_dec; + ret = -ENOMEM; ns = kmem_cache_zalloc(user_ns_cachep, GFP_KERNEL); if (!ns) - return -ENOMEM; + goto fail_dec; ret = ns_alloc_inum(&ns->ns); - if (ret) { - kmem_cache_free(user_ns_cachep, ns); - return ret; - } + if (ret) + goto fail_free; ns->ns.ops = &userns_operations; atomic_set(&ns->count, 1); @@ -165,6 +212,7 @@ int create_user_ns(struct cred *new) ns->level = parent_ns->level + 1; ns->owner = owner; ns->group = group; + ns->max_user_namespaces = COUNT_MAX; /* Inherit USERNS_SETGROUPS_ALLOWED from our parent */ mutex_lock(&userns_state_mutex); @@ -185,7 +233,11 @@ fail_keyring: key_put(ns->persistent_keyring_register); #endif ns_free_inum(&ns->ns); +fail_free: kmem_cache_free(user_ns_cachep, ns); +fail_dec: + dec_user_namespaces(parent_ns); +fail: return ret; } @@ -221,6 +273,7 @@ void free_user_ns(struct user_namespace *ns) #endif ns_free_inum(&ns->ns); kmem_cache_free(user_ns_cachep, ns); + dec_user_namespaces(parent); ns = parent; } while (atomic_dec_and_test(&parent->count)); }