From patchwork Mon Apr 3 21:16:59 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eric Biggers X-Patchwork-Id: 9660617 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id A237C6032D for ; Mon, 3 Apr 2017 21:17:57 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 9432F2849D for ; Mon, 3 Apr 2017 21:17:57 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 87405284BA; Mon, 3 Apr 2017 21:17:57 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.3 required=2.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, FREEMAIL_FROM, RCVD_IN_DNSWL_HI, RCVD_IN_SORBS_SPAM, T_DKIM_INVALID autolearn=unavailable version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 1332A2849D for ; Mon, 3 Apr 2017 21:17:57 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751792AbdDCVRp (ORCPT ); Mon, 3 Apr 2017 17:17:45 -0400 Received: from mail-pg0-f68.google.com ([74.125.83.68]:33792 "EHLO mail-pg0-f68.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751406AbdDCVRo (ORCPT ); Mon, 3 Apr 2017 17:17:44 -0400 Received: by mail-pg0-f68.google.com with SMTP id o123so32337961pga.1; Mon, 03 Apr 2017 14:17:43 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id; bh=3YfBceik17xKQhOp/cy/IjPPR7glOcv4SBj1ROtWvTg=; b=mieg1qPII84PM1vsl+yHlOygaJeh4yhBWpMSf0phgxuBEOXjMmk8InPGsk+vSEXa45 RuKK0NtsArfR9buD4pND5zPhVTtHFNW4N+QeUUcTFf5OuXWF9hLpDxKXBqJVCNtvnb/d JvXmCras9EIP6B1OhbT0wpH7Z5sLcr63qI+6f9UCClXfxfMr0kCWIBBECdEcVPTBQ+x6 5uF5eJSThQL/PuHyf2XbIDH6hSoEAk/i4XGe3eiw9OHkps4QZYQCUoyHdbkAJ+HnKI5I a48Ty98uZNa547sdqVYqa2fb+7nmaok73cNKQg0Vet9Ni4g9iDytP8LCC7p+6u17JkmO pt5g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id; bh=3YfBceik17xKQhOp/cy/IjPPR7glOcv4SBj1ROtWvTg=; b=SCsmjK9O+kkirGvmHW0DgTaPSovthkc4Y8c5e/p2rRX/fPwOg0gF+E42quYJfcQDI9 MHQHxTD2SOLF4eJJcRPMxshgDLZwgWjOfENv3O0VSTL3sdBn+42J0sq/7OUHQZW4gX22 bDGOesr33d7vhCbO0fOYnU2nPuPQWfRAeObfBeR8LID2yb48YHTIKpQQ3EdcysD9UilX q3xbVt+SxERK8KqxiizI26cMap55njcEUP3+G87KX1ydwUsJaSRLVTOe7KG9STxNcBxZ MmsiPN76N1igZLyZeSrtMbt7Uvn4C1GPw7D/kzBd8cI2/eGNt1zdHMvR2iFDeMETB9pv qRjA== X-Gm-Message-State: AFeK/H2nxDbIwsvo4QNGnRPCm+jhfjf3EFN6fDcBf9dRT9GPCR1OhomGQ4MP1UzSGvbgQA== X-Received: by 10.98.209.73 with SMTP id t9mr19368592pfl.203.1491254263037; Mon, 03 Apr 2017 14:17:43 -0700 (PDT) Received: from ebiggers-linuxstation.kir.corp.google.com ([100.119.30.131]) by smtp.gmail.com with ESMTPSA id c75sm27718152pga.22.2017.04.03.14.17.42 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 03 Apr 2017 14:17:42 -0700 (PDT) From: Eric Biggers To: keyrings@vger.kernel.org Cc: David Howells , linux-security-module@vger.kernel.org, linux-kernel@vger.kernel.org, Eric Biggers , stable@vger.kernel.org Subject: [PATCH v2] KEYS: fix keyctl_set_reqkey_keyring() to not leak thread keyrings Date: Mon, 3 Apr 2017 14:16:59 -0700 Message-Id: <20170403211659.1637-1-ebiggers3@gmail.com> X-Mailer: git-send-email 2.12.2.715.g7642488e1d-goog Sender: owner-linux-security-module@vger.kernel.org Precedence: bulk List-ID: X-Virus-Scanned: ClamAV using ClamSMTP From: Eric Biggers Running the following program as an unprivileged user exhausts kernel memory by leaking thread keyrings: #include int main() { for (;;) keyctl_set_reqkey_keyring(KEY_REQKEY_DEFL_THREAD_KEYRING); } Fix it by only creating a new thread keyring if there wasn't one before. To make things more consistent, make install_thread_keyring_to_cred() and install_process_keyring_to_cred() both return 0 if the corresponding keyring is already present. Fixes: d84f4f992cbd ("CRED: Inaugurate COW credentials") Cc: stable@vger.kernel.org # 2.6.29+ Signed-off-by: Eric Biggers --- security/keys/keyctl.c | 11 ++++------- security/keys/process_keys.c | 44 +++++++++++++++++++++++++++----------------- 2 files changed, 31 insertions(+), 24 deletions(-) diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index 52c34532c785..44c1f0e70ac7 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -1253,8 +1253,8 @@ long keyctl_reject_key(key_serial_t id, unsigned timeout, unsigned error, * Read or set the default keyring in which request_key() will cache keys and * return the old setting. * - * If a process keyring is specified then this will be created if it doesn't - * yet exist. The old setting will be returned if successful. + * If a thread or process keyring is specified then it will be created if it + * doesn't yet exist. The old setting will be returned if successful. */ long keyctl_set_reqkey_keyring(int reqkey_defl) { @@ -1279,11 +1279,8 @@ long keyctl_set_reqkey_keyring(int reqkey_defl) case KEY_REQKEY_DEFL_PROCESS_KEYRING: ret = install_process_keyring_to_cred(new); - if (ret < 0) { - if (ret != -EEXIST) - goto error; - ret = 0; - } + if (ret < 0) + goto error; goto set; case KEY_REQKEY_DEFL_DEFAULT: diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c index b6fdd22205b1..9139b18fc863 100644 --- a/security/keys/process_keys.c +++ b/security/keys/process_keys.c @@ -128,13 +128,18 @@ int install_user_keyrings(void) } /* - * Install a fresh thread keyring directly to new credentials. This keyring is - * allowed to overrun the quota. + * Install a thread keyring to the given credentials struct if it didn't have + * one already. This is allowed to overrun the quota. + * + * Return: 0 if a thread keyring is now present; -errno on failure. */ int install_thread_keyring_to_cred(struct cred *new) { struct key *keyring; + if (new->thread_keyring) + return 0; + keyring = keyring_alloc("_tid", new->uid, new->gid, new, KEY_POS_ALL | KEY_USR_VIEW, KEY_ALLOC_QUOTA_OVERRUN, @@ -147,7 +152,9 @@ int install_thread_keyring_to_cred(struct cred *new) } /* - * Install a fresh thread keyring, discarding the old one. + * Install a thread keyring to the current task if it didn't have one already. + * + * Return: 0 if a thread keyring is now present; -errno on failure. */ static int install_thread_keyring(void) { @@ -158,8 +165,6 @@ static int install_thread_keyring(void) if (!new) return -ENOMEM; - BUG_ON(new->thread_keyring); - ret = install_thread_keyring_to_cred(new); if (ret < 0) { abort_creds(new); @@ -170,17 +175,17 @@ static int install_thread_keyring(void) } /* - * Install a process keyring directly to a credentials struct. + * Install a process keyring to the given credentials struct if it didn't have + * one already. This is allowed to overrun the quota. * - * Returns -EEXIST if there was already a process keyring, 0 if one installed, - * and other value on any other error + * Return: 0 if a process keyring is now present; -errno on failure. */ int install_process_keyring_to_cred(struct cred *new) { struct key *keyring; if (new->process_keyring) - return -EEXIST; + return 0; keyring = keyring_alloc("_pid", new->uid, new->gid, new, KEY_POS_ALL | KEY_USR_VIEW, @@ -194,11 +199,9 @@ int install_process_keyring_to_cred(struct cred *new) } /* - * Make sure a process keyring is installed for the current process. The - * existing process keyring is not replaced. + * Install a process keyring to the current task if it didn't have one already. * - * Returns 0 if there is a process keyring by the end of this function, some - * error otherwise. + * Return: 0 if a process keyring is now present; -errno on failure. */ static int install_process_keyring(void) { @@ -212,14 +215,18 @@ static int install_process_keyring(void) ret = install_process_keyring_to_cred(new); if (ret < 0) { abort_creds(new); - return ret != -EEXIST ? ret : 0; + return ret; } return commit_creds(new); } /* - * Install a session keyring directly to a credentials struct. + * Install the given keyring as the session keyring of the given credentials + * struct, replacing the existing one if any. If the given keyring is NULL, + * then install a new anonymous session keyring. + * + * Return: 0 on success; -errno on failure. */ int install_session_keyring_to_cred(struct cred *cred, struct key *keyring) { @@ -254,8 +261,11 @@ int install_session_keyring_to_cred(struct cred *cred, struct key *keyring) } /* - * Install a session keyring, discarding the old one. If a keyring is not - * supplied, an empty one is invented. + * Install the given keyring as the session keyring of the current task, + * replacing the existing one if any. If the given keyring is NULL, then + * install a new anonymous session keyring. + * + * Return: 0 on success; -errno on failure. */ static int install_session_keyring(struct key *keyring) {