From patchwork Sun Apr 9 10:42:08 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Djalal Harouni X-Patchwork-Id: 9671511 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 0C444601EB for ; Sun, 9 Apr 2017 10:44:24 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id DEBAB28474 for ; Sun, 9 Apr 2017 10:44:23 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id D3929284CF; Sun, 9 Apr 2017 10:44:23 +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=-4.1 required=2.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, FREEMAIL_FROM, RCVD_IN_DNSWL_MED, T_DKIM_INVALID autolearn=ham version=3.3.1 Received: from mother.openwall.net (mother.openwall.net [195.42.179.200]) by mail.wl.linuxfoundation.org (Postfix) with SMTP id B4A0C28474 for ; Sun, 9 Apr 2017 10:44:22 +0000 (UTC) Received: (qmail 3944 invoked by uid 550); 9 Apr 2017 10:44:21 -0000 Mailing-List: contact kernel-hardening-help@lists.openwall.com; run by ezmlm Precedence: bulk List-Post: List-Help: List-Unsubscribe: List-Subscribe: List-ID: Delivered-To: mailing list kernel-hardening@lists.openwall.com Received: (qmail 32591 invoked from network); 9 Apr 2017 10:43:37 -0000 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=PMm9pFRu/MhAuIigsHPMJmnrO7knEOvOkjhp0O/ozi8=; b=MjuRuzebvj6ZV3EdB1NduaVoLzxymoaRieUM3qVXL5/ap4cQxsvm23M1DT9VZInk4b UbmqGmAxzflp8c9QwYKQIi9kOyx6YHCjRJQmXXOpT7r2IhbjrL/YJMFOIyhGPNZ/SWJK 4gK75mLR1ENA5J2u5Cx9Xe8q0qZYD9ftwPnYJolwix+uDa/bQHtq4wt4ozFnZIYIxDgM bF983VFlk6rPc9RGpROaekdkfF012e7ksN0J5wSY0jquNyEX9R1/o5Ia9q/vAK4MIguZ FO9JEtg6b+Kbku1+Z0NOpYspldz4Xih3PqqRWOeCAWZ0g6LHAxmP4ICOxUbmJQCjayBA EpAA== 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:in-reply-to :references; bh=PMm9pFRu/MhAuIigsHPMJmnrO7knEOvOkjhp0O/ozi8=; b=LYwvbSbkMdA5jITrYbHBlA9dLH4ta9bO557C7nHc3TBR0Nl39YK2R5A5V9Pdc2dHVb ioSxhrBr2xd+Buwk2rYSgQ+dNvt+iWTIJw5f4RMrgHFURWq3HCae1aypEEk3wgYsJz4J hPTZwOyWcyG/9nUdMYKfXj5Lnjb370cuwadSywej/PKw6F104t9x1iQSzi1jugnNHsRK OiOblGQBWFYyNvg59UC9csOA5KLsYtkpPg3JaDx7nMkCopWHWVIpzY1riFkStP0weGyh SHWs62I4OdZRvwIoSmVctb/DAE6OlK/kQOMFHF5bjKdXYVbrW3M8z0+q502aOdHG6xh9 5wBA== X-Gm-Message-State: AN3rC/7H3IHO1sz3EH9Dlwx/FzOR24yBperbOoGUIBexlD2qTmSJdbV2 MnO0lVYZ3d5PRC1fhb+sAA== X-Received: by 10.28.29.138 with SMTP id d132mr4021432wmd.40.1491734605977; Sun, 09 Apr 2017 03:43:25 -0700 (PDT) From: Djalal Harouni To: Linux Kernel Mailing List , Andy Lutomirski , Kees Cook , Andrew Morton , kernel-hardening@lists.openwall.com, linux-security-module@vger.kernel.org Cc: Linux API , Dongsu Park , Casey Schaufler , James Morris , , Paul Moore , Tetsuo Handa , Greg Kroah-Hartman , Djalal Harouni Date: Sun, 9 Apr 2017 12:42:08 +0200 Message-Id: <1491734530-25002-2-git-send-email-tixxdz@gmail.com> X-Mailer: git-send-email 2.5.5 In-Reply-To: <1491734530-25002-1-git-send-email-tixxdz@gmail.com> References: <1491734530-25002-1-git-send-email-tixxdz@gmail.com> Subject: [kernel-hardening] [PATCH RFC v2 1/3] LSM: Allow per LSM module per "struct task_struct" blob. X-Virus-Scanned: ClamAV using ClamSMTP From: Tetsuo Handa Since several modules are planning to use per "struct task_struct" blob, we need a layer for isolating it. Therefore, this patch introduces per LSM module per "struct task_struct" blob. It would be possible to remember location in security_hook_heads.task_alloc list and undo up to the corresponding security_hook_heads.task_free list when task_alloc hook failed. But this patch calls all task_free hooks without checking whether the corresponding task_alloc hook was called since most modules should be able to handle this correctly. How to calculate amount of needed bytes and allocate memory for initial task is a chicken-or-egg syndrome. We need to know how many bytes needs to be allocated for initial task's security blobs before security_init() is called. But security_reserve_task_blob_index() which calculates amount of needed bytes is called from security_init(). We will need to split module registration into three steps. The first step is call security_reserve_task_blob_index() on all modules which should be activated. The second step is allocate memory for the initial task's security blob. The third step is actually activate all modules which should be activated. Signed-off-by: Djalal Harouni Signed-off-by: Tetsuo Handa --- include/linux/lsm_hooks.h | 36 +++++++++++++++++++++++++++++++++++- security/security.c | 37 ++++++++++++++++++++++++++++++++++++- 2 files changed, 71 insertions(+), 2 deletions(-) diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h index 080f34e..ca19cdb 100644 --- a/include/linux/lsm_hooks.h +++ b/include/linux/lsm_hooks.h @@ -27,6 +27,7 @@ #include #include #include +#include /* "struct task_struct" */ /** * Security hooks for program execution operations. @@ -536,7 +537,10 @@ * @task_alloc: * @task task being allocated. * @clone_flags contains the flags indicating what should be shared. - * Handle allocation of task-related resources. + * Handle allocation of task-related resources. Note that task_free is + * called even if task_alloc failed. This means that all task_free users + * have to be prepared for task_free being called without corresponding + * task_alloc call. * Returns a zero on success, negative values on failure. * @task_free: * @task task about to be freed. @@ -1947,4 +1951,34 @@ void __init loadpin_add_hooks(void); static inline void loadpin_add_hooks(void) { }; #endif +/* + * Per "struct task_struct" security blob is managed using index numbers. + * + * Any user who wants to use per "struct task_struct" security blob reserves an + * index number using security_reserve_task_blob_index() before calling + * security_add_hooks(). + * + * security_reserve_task_blob_index() returns an index number which has to be + * passed to task_security() by that user when fetching security blob of given + * "struct task_struct" for that user. + * + * Security blob for newly allocated "struct task_struct" is allocated and + * initialized with 0 inside security_task_alloc(), before calling each user's + * task_alloc hook. Be careful with task_free hook, for security_task_free() + * can be called when calling each user's task_alloc hook failed. + * + * Note that security_reserve_task_blob_index() uses "u16". It is not a good + * idea to directly reserve large amount. Sum of reserved amount by all users + * should be less than PAGE_SIZE bytes, for per "struct task_struct" security + * blob is allocated for each "struct task_struct" using kcalloc(). + */ +extern u16 __init security_reserve_task_blob_index(const u16 size); +static inline void *task_security(const struct task_struct *task, + const u16 index) +{ + unsigned long *p = task->security; + + return p + index; +} + #endif /* ! __LINUX_LSM_HOOKS_H */ diff --git a/security/security.c b/security/security.c index 549bddc..4dc6bca 100644 --- a/security/security.c +++ b/security/security.c @@ -32,6 +32,7 @@ /* Maximum number of letters for an LSM name string */ #define SECURITY_NAME_MAX 10 +static u16 lsm_max_per_task_blob_index __ro_after_init; struct security_hook_heads security_hook_heads __lsm_ro_after_init; char *lsm_names; /* Boot-time LSM user choice */ @@ -75,6 +76,15 @@ int __init security_init(void) */ do_security_initcalls(); + /* + * Allocate per "struct task_struct" blob for initial task. + * Initialization is done later by each module which needs it. + */ + if (lsm_max_per_task_blob_index) + current->security = kcalloc(lsm_max_per_task_blob_index, + sizeof(unsigned long), + GFP_KERNEL | __GFP_NOFAIL); + return 0; } @@ -86,6 +96,15 @@ static int __init choose_lsm(char *str) } __setup("security=", choose_lsm); +u16 __init security_reserve_task_blob_index(const u16 size) +{ + const u16 index = lsm_max_per_task_blob_index; + u16 requested = DIV_ROUND_UP(size, sizeof(unsigned long)); + + lsm_max_per_task_blob_index += requested; + return index; +} + static int lsm_append(char *new, char **result) { char *cp; @@ -939,12 +958,28 @@ int security_task_create(unsigned long clone_flags) int security_task_alloc(struct task_struct *task, unsigned long clone_flags) { - return call_int_hook(task_alloc, 0, task, clone_flags); + int rc; + const unsigned long index = lsm_max_per_task_blob_index; + + if (index) { + task->security = kcalloc(index, sizeof(unsigned long), + GFP_KERNEL); + if (unlikely(!task->security)) + return -ENOMEM; + } + rc = call_int_hook(task_alloc, 0, task, clone_flags); + if (unlikely(rc)) + security_task_free(task); + return rc; } void security_task_free(struct task_struct *task) { call_void_hook(task_free, task); + if (lsm_max_per_task_blob_index) { + kfree(task->security); + task->security = NULL; + } } int security_cred_alloc_blank(struct cred *cred, gfp_t gfp)