From patchwork Mon Mar 26 19:24:34 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sargun Dhillon X-Patchwork-Id: 10308553 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 C34FE60386 for ; Mon, 26 Mar 2018 19:24:45 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id A5ADA28BD3 for ; Mon, 26 Mar 2018 19:24:45 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 9A11928D61; Mon, 26 Mar 2018 19:24:45 +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.8 required=2.0 tests=BAYES_00,DKIM_SIGNED, RCVD_IN_DNSWL_HI,T_DKIM_INVALID autolearn=ham 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 0E98B28E1F for ; Mon, 26 Mar 2018 19:24:39 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751374AbeCZTYi (ORCPT ); Mon, 26 Mar 2018 15:24:38 -0400 Received: from mail-io0-f194.google.com ([209.85.223.194]:40258 "EHLO mail-io0-f194.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751024AbeCZTYh (ORCPT ); Mon, 26 Mar 2018 15:24:37 -0400 Received: by mail-io0-f194.google.com with SMTP id e79so24608441ioi.7 for ; Mon, 26 Mar 2018 12:24:37 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sargun.me; s=google; h=date:from:to:cc:subject:message-id:references:mime-version :content-disposition:in-reply-to:user-agent; bh=b4KPxrHeTM6fNaq7cd+yFUZVEihGwJIgtl4XM+YxOK4=; b=CPKGy4LWVrHgMOnc90fn/xr8+78L7xkUPih+BcGkeft6oowqB8o9fJVRYgVRompUR/ Hm9SFqTbUurVHETTVy6sGF1qTaR74CayMWkpMeYKv0JD27F/Mn95Qvafy2ZpWNO0drDL KVwENXJvj0WNvvVcxs5o5lVpy5Tk9T0rXEdv4= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:from:to:cc:subject:message-id:references :mime-version:content-disposition:in-reply-to:user-agent; bh=b4KPxrHeTM6fNaq7cd+yFUZVEihGwJIgtl4XM+YxOK4=; b=SRhcbCj5lPK2Z38AI2W3o/IQNxwcS4tDUqBqOu4+YUmocJbCYGpSj6PuEWr0r0M04b 8Qj/gFvzb+a5uQNTbR1KrGC94jO7wDm2o8LqVQ6TNm3ltao/6VFYqhAz2kuNPMl3speM ZrLaVghnjuQziKFJQBgF7r0NeUdJF4QIkPOaZV4nFwiRKOnnl1myyyG9cGTbiDe9xo+3 nwL0wHOUj3lCpLcXhDWS2WQBAc7Ne5P8AoVcdZVVRL+LWz37o3Fwr2hVUqVPq1Fr7App WWuVYxzwLQ5r0pE5MdtkKahKQzJ02pkUxYrPFO7CcVMaF3g6HUfiwU9oaJd3vgwQ/18f jHoA== X-Gm-Message-State: AElRT7EZXLKDqmMAKhlwayaW+UZszkjf20C/216jk6blP1X4f6jLDIh3 akcHrT7IvRIFecz3JoUALE8LFIu0JRHVdw== X-Google-Smtp-Source: AG47ELtJyss6ck7njc9DEu1nVQ7vvwOCIuuC76s/seADtiFByqGVrrG21JDfBPLf1/Lk9QgiGtPOeA== X-Received: by 10.107.138.26 with SMTP id m26mr41992203iod.81.1522092276627; Mon, 26 Mar 2018 12:24:36 -0700 (PDT) Received: from ircssh-2.c.rugged-nimbus-611.internal (80.60.198.104.bc.googleusercontent.com. [104.198.60.80]) by smtp.gmail.com with ESMTPSA id r66-v6sm4725177itc.29.2018.03.26.12.24.35 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Mon, 26 Mar 2018 12:24:35 -0700 (PDT) Date: Mon, 26 Mar 2018 19:24:34 +0000 From: Sargun Dhillon To: linux-security-module@vger.kernel.org Cc: penguin-kernel@i-love.sakura.ne.jp, keescook@chromium.org, igor.stoppa@huawei.com, casey@schaufler-ca.com Subject: [RFC PATCH 2/2] security: Add mechanism to (un)load LSMs after boot time Message-ID: References: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: User-Agent: Mutt/1.5.24 (2015-08-30) Sender: owner-linux-security-module@vger.kernel.org Precedence: bulk List-ID: X-Virus-Scanned: ClamAV using ClamSMTP This patch introduces a mechanism to add mutable hooks at the end of the callback chain for each LSM hook. It allows for built-in kernel LSMs to be unloaded, as well as modular LSMs to be loaded after boot-time. It also does not compromise the security of hooks which are never meant to be unloaded. Signed-off-by: Sargun Dhillon --- include/linux/lsm_hooks.h | 7 ++-- security/apparmor/lsm.c | 2 +- security/commoncap.c | 2 +- security/security.c | 87 +++++++++++++++++++++++++++++++++++++++++----- security/selinux/hooks.c | 5 +-- security/smack/smack_lsm.c | 3 +- security/tomoyo/tomoyo.c | 3 +- security/yama/yama_lsm.c | 2 +- 8 files changed, 92 insertions(+), 19 deletions(-) diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h index 09bc60fb35f1..c0c98758b1bf 100644 --- a/include/linux/lsm_hooks.h +++ b/include/linux/lsm_hooks.h @@ -1981,7 +1981,7 @@ extern struct security_hook_heads security_hook_heads; extern char *lsm_names; extern void security_add_hooks(struct security_hook_list *hooks, int count, - char *lsm); + char *lsm, bool is_mutable); #ifdef CONFIG_SECURITY_SELINUX_DISABLE /* @@ -2006,11 +2006,12 @@ static inline void security_delete_hooks(struct security_hook_list *hooks, } #endif /* CONFIG_SECURITY_SELINUX_DISABLE */ +#define __lsm_ro_after_init __ro_after_init /* Currently required to handle SELinux runtime hook disable. */ #ifdef CONFIG_SECURITY_WRITABLE_HOOKS -#define __lsm_ro_after_init +#define __lsm_mutable_after_init #else -#define __lsm_ro_after_init __ro_after_init +#define __lsm_mutable_after_init __ro_after_init #endif /* CONFIG_SECURITY_WRITABLE_HOOKS */ extern int __init security_module_enable(const char *module); diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 9a65eeaf7dfa..d6cca8169df0 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -1155,7 +1155,7 @@ static int __init apparmor_init(void) goto buffers_out; } security_add_hooks(apparmor_hooks, ARRAY_SIZE(apparmor_hooks), - "apparmor"); + "apparmor", false); /* Report that AppArmor successfully initialized */ apparmor_initialized = 1; diff --git a/security/commoncap.c b/security/commoncap.c index 48620c93d697..fe4b0d9d44ce 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -1363,7 +1363,7 @@ struct security_hook_list capability_hooks[] __lsm_ro_after_init = { void __init capability_add_hooks(void) { security_add_hooks(capability_hooks, ARRAY_SIZE(capability_hooks), - "capability"); + "capability", false); } #endif /* CONFIG_SECURITY */ diff --git a/security/security.c b/security/security.c index 3cafff61b049..c021a34ffe4c 100644 --- a/security/security.c +++ b/security/security.c @@ -30,12 +30,16 @@ #include #include +#define SECURITY_HOOK_COUNT \ + (sizeof(security_hook_heads) / sizeof(struct hlist_head)) #define MAX_LSM_EVM_XATTR 2 /* Maximum number of letters for an LSM name string */ #define SECURITY_NAME_MAX 10 struct security_hook_heads security_hook_heads __lsm_ro_after_init; +EXPORT_SYMBOL_GPL(security_hook_heads); + static ATOMIC_NOTIFIER_HEAD(lsm_notifier_chain); char *lsm_names; @@ -53,6 +57,55 @@ static void __init do_security_initcalls(void) } } +#ifdef CONFIG_SECURITY_WRITABLE_HOOKS +static void security_add_hook(struct security_hook_list *hook, bool is_mutable) +{ + struct security_hook_list *mutable_hook; + union { + void *cb_ptr; + union security_list_options slo; + } hook_options; + + hlist_for_each_entry(mutable_hook, hook->head, list) { + hook_options.slo = mutable_hook->hook; + if (hook_options.cb_ptr) + continue; + + if (is_mutable) + hlist_add_behind_rcu(&hook->list, &mutable_hook->list); + else + hlist_add_before_rcu(&hook->list, &mutable_hook->list); + return; + } + + panic("Unable to install hook, cannot find mutable hook\n"); +} + +static void __init add_mutable_hooks(void) +{ + struct hlist_head *list = (struct hlist_head *) &security_hook_heads; + struct security_hook_list *shl; + int i; + + for (i = 0; i < SECURITY_HOOK_COUNT; i++) { + shl = kzalloc(sizeof(*shl), GFP_KERNEL); + if (!shl) + panic("Unable to allocate memory for mutable hooks\n"); + shl->head = &list[i]; + hlist_add_head_rcu(&shl->list, shl->head); + } +} +#else +static void security_add_hook(struct security_hook_list *hook, bool is_mutable) +{ + WARN_ONCE(is_mutable, + "Mutable hook loaded with writable hooks disabled"); + hlist_add_tail_rcu(hook.list, hooks.head); +} + +static void __init add_mutable_hooks(void) {} +#endif /* CONFIG_SECURITY_WRITABLE_HOOKS */ + /** * security_init - initializes the security framework * @@ -63,11 +116,11 @@ int __init security_init(void) int i; struct hlist_head *list = (struct hlist_head *) &security_hook_heads; - for (i = 0; i < sizeof(security_hook_heads) / sizeof(struct hlist_head); - i++) + for (i = 0; i < SECURITY_HOOK_COUNT; i++) INIT_HLIST_HEAD(&list[i]); pr_info("Security Framework initialized\n"); + add_mutable_hooks(); /* * Load minor LSMs, with the capability module always first. */ @@ -153,21 +206,24 @@ int __init security_module_enable(const char *module) * @hooks: the hooks to add * @count: the number of hooks to add * @lsm: the name of the security module + * @is_mutable: is this hook mutable after kernel init * * Each LSM has to register its hooks with the infrastructure. */ -void __init security_add_hooks(struct security_hook_list *hooks, int count, - char *lsm) +void security_add_hooks(struct security_hook_list *hooks, int count, + char *lsm, bool is_mutable) { int i; for (i = 0; i < count; i++) { hooks[i].lsm = lsm; - hlist_add_tail_rcu(&hooks[i].list, hooks[i].head); + security_add_hook(&hooks[i], is_mutable); } + if (lsm_append(lsm, &lsm_names) < 0) panic("%s - Cannot get early memory.\n", __func__); } +EXPORT_SYMBOL_GPL(security_add_hooks); int call_lsm_notifier(enum lsm_event event, void *data) { @@ -202,7 +258,8 @@ EXPORT_SYMBOL(unregister_lsm_notifier); struct security_hook_list *P; \ \ hlist_for_each_entry(P, &security_hook_heads.FUNC, list) \ - P->hook.FUNC(__VA_ARGS__); \ + if (P->hook.FUNC) \ + P->hook.FUNC(__VA_ARGS__); \ } while (0) #define call_int_hook(FUNC, IRC, ...) ({ \ @@ -211,9 +268,11 @@ EXPORT_SYMBOL(unregister_lsm_notifier); struct security_hook_list *P; \ \ hlist_for_each_entry(P, &security_hook_heads.FUNC, list) { \ - RC = P->hook.FUNC(__VA_ARGS__); \ - if (RC != 0) \ - break; \ + if (P->hook.FUNC) { \ + RC = P->hook.FUNC(__VA_ARGS__); \ + if (RC != 0) \ + break; \ + } \ } \ } while (0); \ RC; \ @@ -318,6 +377,8 @@ int security_vm_enough_memory_mm(struct mm_struct *mm, long pages) * thinks it should not be set it won't. */ hlist_for_each_entry(hp, &security_hook_heads.vm_enough_memory, list) { + if (!hp->hook.vm_enough_memory) + continue; rc = hp->hook.vm_enough_memory(mm, pages); if (rc <= 0) { cap_sys_admin = 0; @@ -806,6 +867,8 @@ int security_inode_getsecurity(struct inode *inode, const char *name, void **buf * Only one module will provide an attribute with a given name. */ hlist_for_each_entry(hp, &security_hook_heads.inode_getsecurity, list) { + if (!hp->hook.inode_getsecurity) + continue; rc = hp->hook.inode_getsecurity(inode, name, buffer, alloc); if (rc != -EOPNOTSUPP) return rc; @@ -824,6 +887,8 @@ int security_inode_setsecurity(struct inode *inode, const char *name, const void * Only one module will provide an attribute with a given name. */ hlist_for_each_entry(hp, &security_hook_heads.inode_setsecurity, list) { + if (!hp->hook.inode_setsecurity) + continue; rc = hp->hook.inode_setsecurity(inode, name, value, size, flags); if (rc != -EOPNOTSUPP) @@ -1127,6 +1192,8 @@ int security_task_prctl(int option, unsigned long arg2, unsigned long arg3, struct security_hook_list *hp; hlist_for_each_entry(hp, &security_hook_heads.task_prctl, list) { + if (!hp->hook.task_prctl) + continue; thisrc = hp->hook.task_prctl(option, arg2, arg3, arg4, arg5); if (thisrc != -ENOSYS) { rc = thisrc; @@ -1631,6 +1698,8 @@ int security_xfrm_state_pol_flow_match(struct xfrm_state *x, */ hlist_for_each_entry(hp, &security_hook_heads.xfrm_state_pol_flow_match, list) { + if (!hp->hook.xfrm_state_pol_flow_match) + continue; rc = hp->hook.xfrm_state_pol_flow_match(x, xp, fl); break; } diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 8644d864e3c1..f05184a3a7a6 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -6393,7 +6393,7 @@ static void selinux_bpf_prog_free(struct bpf_prog_aux *aux) } #endif -static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = { +static struct security_hook_list selinux_hooks[] __lsm_mutable_after_init = { LSM_HOOK_INIT(binder_set_context_mgr, selinux_binder_set_context_mgr), LSM_HOOK_INIT(binder_transaction, selinux_binder_transaction), LSM_HOOK_INIT(binder_transfer_binder, selinux_binder_transfer_binder), @@ -6651,7 +6651,8 @@ static __init int selinux_init(void) 0, SLAB_PANIC, NULL); avc_init(); - security_add_hooks(selinux_hooks, ARRAY_SIZE(selinux_hooks), "selinux"); + security_add_hooks(selinux_hooks, ARRAY_SIZE(selinux_hooks), "selinux", + IS_ENABLED(CONFIG_SECURITY_SELINUX_DISABLE)); if (avc_add_callback(selinux_netcache_avc_callback, AVC_CALLBACK_RESET)) panic("SELinux: Unable to register AVC netcache callback\n"); diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 03fdecba93bb..7a9f1bb06c8e 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -4902,7 +4902,8 @@ static __init int smack_init(void) /* * Register with LSM */ - security_add_hooks(smack_hooks, ARRAY_SIZE(smack_hooks), "smack"); + security_add_hooks(smack_hooks, ARRAY_SIZE(smack_hooks), "smack", + false); return 0; } diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c index 213b8c593668..ba74fab0e9a5 100644 --- a/security/tomoyo/tomoyo.c +++ b/security/tomoyo/tomoyo.c @@ -543,7 +543,8 @@ static int __init tomoyo_init(void) if (!security_module_enable("tomoyo")) return 0; /* register ourselves with the security framework */ - security_add_hooks(tomoyo_hooks, ARRAY_SIZE(tomoyo_hooks), "tomoyo"); + security_add_hooks(tomoyo_hooks, ARRAY_SIZE(tomoyo_hooks), "tomoyo", + false); printk(KERN_INFO "TOMOYO Linux initialized\n"); cred->security = &tomoyo_kernel_domain; tomoyo_mm_init(); diff --git a/security/yama/yama_lsm.c b/security/yama/yama_lsm.c index ffda91a4a1aa..04c9aed9e951 100644 --- a/security/yama/yama_lsm.c +++ b/security/yama/yama_lsm.c @@ -480,6 +480,6 @@ static inline void yama_init_sysctl(void) { } void __init yama_add_hooks(void) { pr_info("Yama: becoming mindful.\n"); - security_add_hooks(yama_hooks, ARRAY_SIZE(yama_hooks), "yama"); + security_add_hooks(yama_hooks, ARRAY_SIZE(yama_hooks), "yama", false); yama_init_sysctl(); }