From patchwork Thu Aug 29 23:29:22 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Casey Schaufler X-Patchwork-Id: 11122639 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 5E802112C for ; Thu, 29 Aug 2019 23:30:06 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 32AE72173E for ; Thu, 29 Aug 2019 23:30:06 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=yahoo.com header.i=@yahoo.com header.b="VzGpQPbZ" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728121AbfH2XaG (ORCPT ); Thu, 29 Aug 2019 19:30:06 -0400 Received: from sonic311-23.consmr.mail.bf2.yahoo.com ([74.6.131.197]:41822 "EHLO sonic311-23.consmr.mail.bf2.yahoo.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728129AbfH2XaF (ORCPT ); Thu, 29 Aug 2019 19:30:05 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yahoo.com; s=s2048; t=1567121402; bh=LOGjl+xuGgyPyNl5cHaq2z1YN7D7LvdGIufbOcZuT70=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From:Subject; b=VzGpQPbZckov/cDWtpBwlqtB8+MaWYkq1WzKO0RXwfED1uihSI3bNjkm6Rhh+APq5db+nzYT37YwoGe0tiXde0B/gnlNzlc0O47wtQMkNLuZ16XqCsL+rIUffPC5F+mKSXwABpKtlVEdTtk4FMblLup9BU7D+zA4MdaH66vYfysyHrzBPLKAD794lzaSaTNtlgsRf2U+lrJtm3aZL/VWvXddS5WqDMXHNdU8fON0JuUU6y7uQkJ3mhUDvBHdvYnlQIriB1BlduynfRtBbCMl/XK8Jj5fKw47e7//Wi5Jq94xw2vAUdk705bWlG42ymKowi7asoZGH/2qbazEAtc3/g== X-YMail-OSG: OpNHuvsVM1kWBvsi0roMFm8Wqt5D2kcg72jhsrhfemHWYcv0yj.B_sQkmWYeGWV bxsyQpXnwop7_U3tqb28bxMfhcFxU8jZZRaoC_0x8u03pjoEYJ0hAf_28CVu3JpslwbPMmgpvlf9 1ldMGqDz87WbqWDgLBJn5FgviTi_rfC6nZ9AKbiEvv2NXH2wdVhxwf5s26hLM.sn5TTebRURGBBP lVRGuWPleJaLZSSblYEigFiaJjuwRmnq0bG9XFI.linerdaPPdnDHuYGBeuhDU9HOXMLInppL04T AWZrAMxnO2yzjYyG6NEwKmWm75bkbpFlUzlAHc3jUOG_Kc4ovLPAHeFIAs5GwN1ckj35mJXzFqoC 0M0HfwJr3WWg1rPhnz3V2.genpCNBDumFRA5hY0Png8YBIfG3jN1t_VPx3ry053AM5kff0WUDMnC MPLaz8eVZ8vUdINE_rXYkn6fKBzepiZosF673.FqeK2A2V4jc8pUSCMC_5cs_RqIugnvYOFLQXvO zIdNI_K3aMC9HZ2StPj1KYk1ndn.rsGoXAaC0BtnjEYlV_D8SLYpdMbph7Bu8Db0MKWKOF5WWryJ odzSNJ83jZXi5LbMCXy5eMRp2kQE8VYEQLrsTXOMiQFgGfAB3_UnyrGDWANQbb.3VdsbPVKbatWn C0l2_MF0MHQktWwdH.xmoUgJ._BiOyVvKSS8dPtnE51o3XAGJM40Mb_zX8J3IOk1YruiExmD1dlY UJEP7p6t1VP7rmgdQ23cAW764ELjE.fgN57M0t.AHdEdfRCFIZFFgDo0_l7W7dVIuqLZkFIWWCVt 9GXbNSi8044fQHT9RraPqI4GbjBxg6tSKwwBs0FO6nLv8x9XGkxK0qHk5vdiPs.D95D.Qn6sr6U_ 2xegc1j0KPgeUoKfY3XcIWAWUTEAgAeAwZKOBHXC4W7K6iG2dXKbqPs6KT8nsToykApp6FkMhGfb itVDWDPNEZNWYJgQ7JgHQ2K6dYUeRNTJ6atsZkFeah8jiZvYvv.xK7CsVqC6sTcNIB7LcwPk.BED cCW1VWFhKamd.q3zZZbVPWF65F.3cEXbK26sIYGeXDmgI5uCNW7J6GvB.WFVWIR1iLUQ42zUJBmi EqECoaiFpgAhsNqd_EHaFJOpeT_U5KlrvkdGmAYLKFhynwu2G0Beqt._JJcVODhcL.lU3IHvUiiH Yd2l.0Xdz3Ht1UH8ylEO9enY5_HL48WvNPgLXpI9qa_ux8c6tRKjdN385AhNLAjaBOCuTmNPnTPs I.LrKYh9u59ORP1n.33RNizayXE1wKnPQOPnyYoPLW8kxjLMejp8arY0A61IwugT05nSH9AiWHZy 5MQ-- Received: from sonic.gate.mail.ne1.yahoo.com by sonic311.consmr.mail.bf2.yahoo.com with HTTP; Thu, 29 Aug 2019 23:30:02 +0000 Received: by smtp408.mail.bf1.yahoo.com (Oath Hermes SMTP Server) with ESMTPA ID 2d799b9799b92d4529ae6bd2b9fbf9fb; Thu, 29 Aug 2019 23:29:59 +0000 (UTC) From: Casey Schaufler To: casey.schaufler@intel.com, jmorris@namei.org, linux-security-module@vger.kernel.org, selinux@vger.kernel.org Cc: casey@schaufler-ca.com, keescook@chromium.org, john.johansen@canonical.com, penguin-kernel@i-love.sakura.ne.jp, paul@paul-moore.com, sds@tycho.nsa.gov Subject: [PATCH v8 15/28] LSM: Specify which LSM to display Date: Thu, 29 Aug 2019 16:29:22 -0700 Message-Id: <20190829232935.7099-16-casey@schaufler-ca.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20190829232935.7099-1-casey@schaufler-ca.com> References: <20190829232935.7099-1-casey@schaufler-ca.com> MIME-Version: 1.0 Sender: selinux-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: selinux@vger.kernel.org Create a new entry "display" in /proc/.../attr for controlling which LSM security information is displayed for a process. The name of an active LSM that supplies hooks for human readable data may be written to "display" to set the value. This requires CAP_MAC_ADMIN privilege. The name of the LSM currently in use can be read from "display". At this point there can only be one LSM capable of display active. A helper function lsm_task_display() is provided to get the display slot for a task_struct. Note that procfs restricts writes in the attr directory to 'self'. Signed-off-by: Casey Schaufler --- fs/proc/base.c | 1 + include/linux/lsm_hooks.h | 15 ++++ security/security.c | 148 +++++++++++++++++++++++++++++++++----- 3 files changed, 147 insertions(+), 17 deletions(-) diff --git a/fs/proc/base.c b/fs/proc/base.c index ddef482f1334..7bf70e041315 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -2618,6 +2618,7 @@ static const struct pid_entry attr_dir_stuff[] = { ATTR(NULL, "fscreate", 0666), ATTR(NULL, "keycreate", 0666), ATTR(NULL, "sockcreate", 0666), + ATTR(NULL, "display", 0666), #ifdef CONFIG_SECURITY_SMACK DIR("smack", 0555, proc_smack_attr_dir_inode_ops, proc_smack_attr_dir_ops), diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h index fe1fb7a69ee5..33e5ab4af9f8 100644 --- a/include/linux/lsm_hooks.h +++ b/include/linux/lsm_hooks.h @@ -2134,4 +2134,19 @@ static inline void security_delete_hooks(struct security_hook_list *hooks, extern int lsm_inode_alloc(struct inode *inode); +/** + * lsm_task_display - the "display LSM for this task + * @task: The task to report on + * + * Returns the task's display LSM slot. + */ +static inline int lsm_task_display(struct task_struct *task) +{ + int *display = task->security; + + if (display) + return *display; + return LSMBLOB_INVALID; +} + #endif /* ! __LINUX_LSM_HOOKS_H */ diff --git a/security/security.c b/security/security.c index 8927508b2142..c3c9bd88b7af 100644 --- a/security/security.c +++ b/security/security.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -46,7 +47,14 @@ static struct kmem_cache *lsm_file_cache; static struct kmem_cache *lsm_inode_cache; char *lsm_names; -static struct lsm_blob_sizes blob_sizes __lsm_ro_after_init; + +/* + * The task blob includes the "display" slot used for + * chosing which module presents contexts. + */ +static struct lsm_blob_sizes blob_sizes __lsm_ro_after_init = { + .lbs_task = sizeof(int), +}; /* Boot-time LSM user choice */ static __initdata const char *chosen_lsm_order; @@ -423,8 +431,10 @@ static int lsm_append(const char *new, char **result) /* * Current index to use while initializing the lsmblob secid list. + * Pointers to the LSM id structures for local use. */ static int lsm_slot __lsm_ro_after_init; +static struct lsm_id *lsm_slotlist[LSMBLOB_ENTRIES]; /** * security_add_hooks - Add a modules hooks to the hook lists. @@ -444,6 +454,7 @@ void __init security_add_hooks(struct security_hook_list *hooks, int count, if (lsmid->slot == LSMBLOB_NEEDED) { if (lsm_slot >= LSMBLOB_ENTRIES) panic("%s Too many LSMs registered.\n", __func__); + lsm_slotlist[lsm_slot] = lsmid; lsmid->slot = lsm_slot++; init_debug("%s assigned lsmblob slot %d\n", lsmid->lsm, lsmid->slot); @@ -564,6 +575,8 @@ int lsm_inode_alloc(struct inode *inode) */ static int lsm_task_alloc(struct task_struct *task) { + int *display; + if (blob_sizes.lbs_task == 0) { task->security = NULL; return 0; @@ -572,6 +585,15 @@ static int lsm_task_alloc(struct task_struct *task) task->security = kzalloc(blob_sizes.lbs_task, GFP_KERNEL); if (task->security == NULL) return -ENOMEM; + + /* + * The start of the task blob contains the "display" LSM slot number. + * Start with it set to the invalid slot number, indicating that the + * default first registered LSM be displayed. + */ + display = task->security; + *display = LSMBLOB_INVALID; + return 0; } @@ -1563,14 +1585,26 @@ int security_file_open(struct file *file) int security_task_alloc(struct task_struct *task, unsigned long clone_flags) { + int *odisplay = current->security; + int *ndisplay; int rc = lsm_task_alloc(task); - if (rc) + if (unlikely(rc)) return rc; + rc = call_int_hook(task_alloc, 0, task, clone_flags); - if (unlikely(rc)) + if (unlikely(rc)) { security_task_free(task); - return rc; + return rc; + } + + if (odisplay) { + ndisplay = task->security; + if (ndisplay) + *ndisplay = *odisplay; + } + + return 0; } void security_task_free(struct task_struct *task) @@ -1967,23 +2001,89 @@ int security_getprocattr(struct task_struct *p, const char *lsm, char *name, char **value) { struct security_hook_list *hp; + int display = lsm_task_display(current); + int slot = 0; + + if (!strcmp(name, "display")) { + /* + * lsm_slot will be 0 if there are no displaying modules. + */ + if (lsm_slot == 0) + return -EINVAL; + if (display != LSMBLOB_INVALID) + slot = display; + *value = kstrdup(lsm_slotlist[slot]->lsm, GFP_KERNEL); + if (*value) + return strlen(*value); + return -ENOMEM; + } hlist_for_each_entry(hp, &security_hook_heads.getprocattr, list) { if (lsm != NULL && strcmp(lsm, hp->lsmid->lsm)) continue; + if (lsm == NULL && display != LSMBLOB_INVALID && + display != hp->lsmid->slot) + continue; return hp->hook.getprocattr(p, name, value); } return -EINVAL; } +/** + * security_setprocattr - Set process attributes via /proc + * @lsm: name of module involved, or NULL + * @name: name of the attribute + * @value: value to set the attribute to + * @size: size of the value + * + * Set the process attribute for the specified security module + * to the specified value. Note that this can only be used to set + * the process attributes for the current, or "self" process. + * The /proc code has already done this check. + * + * Returns 0 on success, an appropriate code otherwise. + */ int security_setprocattr(const char *lsm, const char *name, void *value, size_t size) { struct security_hook_list *hp; + char *term; + char *cp; + int *display = current->security; + int rc = -EINVAL; + int slot = 0; + + if (!strcmp(name, "display")) { + if (!capable(CAP_MAC_ADMIN)) + return -EPERM; + /* + * lsm_slot will be 0 if there are no displaying modules. + */ + if (lsm_slot == 0 || size == 0) + return -EINVAL; + term = kmemdup_nul(value, size, GFP_KERNEL); + if (term == NULL) + return -ENOMEM; + + cp = strsep(&term, " \n"); + + for (slot = 0; slot < lsm_slot; slot++) + if (!strcmp(cp, lsm_slotlist[slot]->lsm)) { + *display = lsm_slotlist[slot]->slot; + rc = size; + break; + } + + kfree(cp); + return rc; + } hlist_for_each_entry(hp, &security_hook_heads.setprocattr, list) { if (lsm != NULL && strcmp(lsm, hp->lsmid->lsm)) continue; + if (lsm == NULL && *display != LSMBLOB_INVALID && + *display != hp->lsmid->slot) + continue; return hp->hook.setprocattr(name, value, size); } return -EINVAL; @@ -2003,15 +2103,15 @@ EXPORT_SYMBOL(security_ismaclabel); int security_secid_to_secctx(struct lsmblob *blob, char **secdata, u32 *seclen) { struct security_hook_list *hp; - int rc; + int display = lsm_task_display(current); hlist_for_each_entry(hp, &security_hook_heads.secid_to_secctx, list) { if (WARN_ON(hp->lsmid->slot < 0 || hp->lsmid->slot >= lsm_slot)) continue; - rc = hp->hook.secid_to_secctx(blob->secid[hp->lsmid->slot], - secdata, seclen); - if (rc != 0) - return rc; + if (display == LSMBLOB_INVALID || display == hp->lsmid->slot) + return hp->hook.secid_to_secctx( + blob->secid[hp->lsmid->slot], + secdata, seclen); } return 0; } @@ -2021,16 +2121,15 @@ int security_secctx_to_secid(const char *secdata, u32 seclen, struct lsmblob *blob) { struct security_hook_list *hp; - int rc; + int display = lsm_task_display(current); lsmblob_init(blob, 0); hlist_for_each_entry(hp, &security_hook_heads.secctx_to_secid, list) { if (WARN_ON(hp->lsmid->slot < 0 || hp->lsmid->slot >= lsm_slot)) continue; - rc = hp->hook.secctx_to_secid(secdata, seclen, - &blob->secid[hp->lsmid->slot]); - if (rc != 0) - return rc; + if (display == LSMBLOB_INVALID || display == hp->lsmid->slot) + return hp->hook.secctx_to_secid(secdata, seclen, + &blob->secid[hp->lsmid->slot]); } return 0; } @@ -2038,7 +2137,15 @@ EXPORT_SYMBOL(security_secctx_to_secid); void security_release_secctx(char *secdata, u32 seclen) { - call_void_hook(release_secctx, secdata, seclen); + struct security_hook_list *hp; + int *display = current->security; + + hlist_for_each_entry(hp, &security_hook_heads.release_secctx, list) + if (display == NULL || *display == LSMBLOB_INVALID || + *display == hp->lsmid->slot) { + hp->hook.release_secctx(secdata, seclen); + return; + } } EXPORT_SYMBOL(security_release_secctx); @@ -2163,8 +2270,15 @@ EXPORT_SYMBOL(security_sock_rcv_skb); int security_socket_getpeersec_stream(struct socket *sock, char __user *optval, int __user *optlen, unsigned len) { - return call_int_hook(socket_getpeersec_stream, -ENOPROTOOPT, sock, - optval, optlen, len); + int display = lsm_task_display(current); + struct security_hook_list *hp; + + hlist_for_each_entry(hp, &security_hook_heads.socket_getpeersec_stream, + list) + if (display == LSMBLOB_INVALID || display == hp->lsmid->slot) + return hp->hook.socket_getpeersec_stream(sock, optval, + optlen, len); + return -ENOPROTOOPT; } int security_socket_getpeersec_dgram(struct socket *sock, struct sk_buff *skb,