From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-9.7 required=3.0 tests=DKIM_SIGNED,DKIM_VALID, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_PATCH,MAILING_LIST_MULTI,SIGNED_OFF_BY, SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 853E4C33C8C for ; Wed, 13 Nov 2019 17:58:39 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 6A672206EE for ; Wed, 13 Nov 2019 17:58:39 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=yahoo.com header.i=@yahoo.com header.b="t1XpqEiK" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727112AbfKMR6i (ORCPT ); Wed, 13 Nov 2019 12:58:38 -0500 Received: from sonic313-15.consmr.mail.ne1.yahoo.com ([66.163.185.38]:33702 "EHLO sonic313-15.consmr.mail.ne1.yahoo.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728435AbfKMR6i (ORCPT ); Wed, 13 Nov 2019 12:58:38 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yahoo.com; s=s2048; t=1573667915; bh=T+7vkOglemjDcJCtI0jsgL0AaHeYGYB4YiqPLcOqBMY=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From:Subject; b=t1XpqEiKKg3Plyvh3jlWB46CZyXD1wQAKi9CdkVKkJUTu9cWieSaaRXmkIrEKvioXFiEwJ/ay0cCFdaHE73yHJrK5J7UMtG5Y26TTVZTwj29wKyK1h4SfHGvNumn7CR+aW6JQjsZgx+HMBSRqb/mZZPZCH57Zlaux1TOuJZCILRtgQQqjB4fIA6HRPw5QU82GrqwRz0b07Z/9N7JrksLxfa6PYIEWc6E34hYxp7uMe56NaXBMaKAL17Isv0UJjODQl3ZtYvLP5S62HCUwOA+QvkBFCvKqzFc+AVTY2uYTOie6x+QGczVsjgOJfY51fofdlxLMCxhsrWltjPPrFcY2Q== X-YMail-OSG: 6OymPcMVM1lLMCPhaQ9YvDcolNs5XoOhY9ifsq2E4P5f.FWgQN1nkyIMHnHdqlH 5JruLsWVZobQ3MJ5R.CuhptLgNVxuoWHGnmH62EQCGGQlsrU1w.cMnBT_rzNnMvSeTrjkC8tC0p6 bXshQdQ89O76FEDMz1yZbwpIC2kfStKYc42cUg2Lc.fXeu1RdlkEcSZAD2GqR00uqJGMth2Iqb8J 9WSdxT3STHCv6rSGfLaDGrTn9GQCLR8H5nZ3HQNLHi.n6QHSMLcK88ah61I2oHhWJAZP2wLjFdaI 7NJFxTUfOLk6m2Tby93lcMCn3SIzAKdS70DHGONUXinTpUo6H5C6g3n75xWlyfNB3YjnwlfyZnXK c_lURZ8rBI6X91eQQyXC1Wc8TqRMA2i2aGiRtc59XuiA4ZmxBsbabuH7u.tTchcthLmvQwpz0FGW qXBJX6OLBBYH4B0xIIGwR862nn4PARUwTkHsR9F3yQ8zwbonENVuDowiZlJObfddoWTgm33I8BPI CbgIW9VdoQl37jyZaAvizoB.lmA7dBaL1_Uig52lX75wVwLGeNKBE9xAFnXOU85QlKZoKWMsImyZ o.xcNSBvBQvEiJGlWrt2jiwM6T4NsluJPuEh_6Me9hgBtHn1DRyjR379PpTSUkA33PcJ3Q_wdGBG FQ8b_67NBhPdwWDDvHOs_kK7YqRPRXEhay9FlsnH_ZPxB4QBld2LlX2ECtvNob9wZ1RPFo6NfPR2 sRU4cgGK2nyyVaWODeoLo9TMXDNQ3nAc4In.elbcIxPOeHNe3gU_UjsQLGgTCpAg8nhWsMlWDnyB N4CyAEKM2m2eACh0RpB20kDnLZQnlRfzM4t7uWX.fQcBiNV7uQsFK1Bmc9h1JGY.RHSY7ck.frRn 5w4Y1ExqZK9VijWjlM1DsrpCW0TXsf9GQ6g3frgmp876g2TS.TuFp3kg.PH25tmG7RKyZMlUmo_u cUyJOS616aQNw8PwshxtjJzNklxDooVH3BpldcmUkO9OvKBhgeSxX18whW8CFa70fT.dPr_JfBxl S9nJ0vYfKoa2E5Hbns3Bv5GXrC6A29CXZ3FHPkDgDSCFqHyiQ4_rvbCcYGkPul7kOToDNqbMEiev YeZQCUCJprNwSgCKr60ms2BE9AwC1QZoF8DDpaGQiFgQkMVMpxWywHUu9BiErqf7i6hhBCdr6i8e WNpHeq41RXkFVNf6jB9PlxSh75_AAcfQGjPRUuTAsE1TP7L1U9tzg41k.Kn4jIMLwpA4HMjfhSU. v2_E61ePIM_rUwzFp5QGzS58efXAA8FwpyPXvMZZYJjfJabCN3xaojYK2DiQJyzjt0VXIw8oFIyx CIpQtKtja7NkXSOFubSLQDRPXiPafH2OcToDzmbnWfi2igkSq1SnKHXBlKQ.e5FmtqqX4h2AYPEL 5SNrxEY_MG5GAAY977rx7PibPxTG_l4l4agd5H3SObKTlUbAUPCPS_C91RXQ.YjFlU4Jo Received: from sonic.gate.mail.ne1.yahoo.com by sonic313.consmr.mail.ne1.yahoo.com with HTTP; Wed, 13 Nov 2019 17:58:35 +0000 Received: by smtp410.mail.ne1.yahoo.com (Oath Hermes SMTP Server) with ESMTPA ID 7adf454ff38eea70a1372d4a5370b9fb; Wed, 13 Nov 2019 17:58:32 +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 v11 13/25] LSM: Specify which LSM to display Date: Wed, 13 Nov 2019 09:57:09 -0800 Message-Id: <20191113175721.2317-14-casey@schaufler-ca.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20191113175721.2317-1-casey@schaufler-ca.com> References: <20191113175721.2317-1-casey@schaufler-ca.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Sender: owner-linux-security-module@vger.kernel.org Precedence: bulk List-ID: Create a new entry "display" in the procfs attr directory for controlling which LSM security information is displayed for a process. A process can only read or write its own display value. The name of an active LSM that supplies hooks for human readable data may be written to "display" to set the value. 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. Setting the "display" requires that all security modules using setprocattr hooks allow the action. Each security module is responsible for defining its policy. AppArmor hook provided by John Johansen SELinux hook provided by Stephen Smalley Signed-off-by: Casey Schaufler --- fs/proc/base.c | 1 + include/linux/lsm_hooks.h | 15 +++ security/apparmor/include/apparmor.h | 3 +- security/apparmor/lsm.c | 32 +++++ security/security.c | 169 ++++++++++++++++++++++++--- security/selinux/hooks.c | 11 ++ security/selinux/include/classmap.h | 2 +- security/smack/smack_lsm.c | 7 ++ 8 files changed, 221 insertions(+), 19 deletions(-) diff --git a/fs/proc/base.c b/fs/proc/base.c index ebea9501afb8..950c200cb9ad 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -2652,6 +2652,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 cfe5393840c7..b2ec81fcd1e2 100644 --- a/include/linux/lsm_hooks.h +++ b/include/linux/lsm_hooks.h @@ -2171,4 +2171,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/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h index 6b7e6e13176e..e4b43e5bb8f9 100644 --- a/security/apparmor/include/apparmor.h +++ b/security/apparmor/include/apparmor.h @@ -28,8 +28,9 @@ #define AA_CLASS_SIGNAL 10 #define AA_CLASS_NET 14 #define AA_CLASS_LABEL 16 +#define AA_CLASS_DISPLAY_LSM 17 -#define AA_CLASS_LAST AA_CLASS_LABEL +#define AA_CLASS_LAST AA_CLASS_DISPLAY_LSM /* Control parameters settable through module/boot flags */ extern enum audit_mode aa_g_audit; diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 11845348eefb..fefccd559541 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -599,6 +599,25 @@ static int apparmor_getprocattr(struct task_struct *task, char *name, return error; } + +static int profile_display_lsm(struct aa_profile *profile, + struct common_audit_data *sa) +{ + struct aa_perms perms = { }; + unsigned int state; + + state = PROFILE_MEDIATES(profile, AA_CLASS_DISPLAY_LSM); + if (state) { + aa_compute_perms(profile->policy.dfa, state, &perms); + aa_apply_modes_to_perms(profile, &perms); + aad(sa)->label = &profile->label; + + return aa_check_perms(profile, &perms, AA_MAY_WRITE, sa, NULL); + } + + return 0; +} + static int apparmor_setprocattr(const char *name, void *value, size_t size) { @@ -610,6 +629,19 @@ static int apparmor_setprocattr(const char *name, void *value, if (size == 0) return -EINVAL; + /* LSM infrastructure does actual setting of display if allowed */ + if (!strcmp(name, "display")) { + struct aa_profile *profile; + struct aa_label *label; + + aad(&sa)->info = "set display lsm"; + label = begin_current_label_crit_section(); + error = fn_for_each_confined(label, profile, + profile_display_lsm(profile, &sa)); + end_current_label_crit_section(label); + return error; + } + /* AppArmor requires that the buffer must be null terminated atm */ if (args[size - 1] != '\0') { /* null terminate */ diff --git a/security/security.c b/security/security.c index 3aba440624f9..c2874f6587d2 100644 --- a/security/security.c +++ b/security/security.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -43,7 +44,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; @@ -438,8 +446,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. @@ -459,6 +469,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); @@ -588,6 +599,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; @@ -596,6 +609,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; } @@ -1551,14 +1573,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) @@ -1955,23 +1989,110 @@ 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; + + /* + * Only allow getting the current process' display. + * There are too few reasons to get another process' + * display and too many LSM policy issues. + */ + if (current != p) + return -EINVAL; + + display = lsm_task_display(p); + 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")) { + /* + * Change the "display" value only if all the security + * modules that support setting a procattr allow it. + * It is assumed that all such security modules will be + * cooperative. + */ + if (size == 0) + return -EINVAL; + + hlist_for_each_entry(hp, &security_hook_heads.setprocattr, + list) { + rc = hp->hook.setprocattr(name, value, size); + if (rc < 0) + return rc; + } + + rc = -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; @@ -1991,15 +2112,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; } @@ -2009,16 +2130,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; } @@ -2026,7 +2146,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); @@ -2151,8 +2279,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, diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 5570a6ed49d5..5f50dae7c107 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -6315,6 +6315,17 @@ static int selinux_setprocattr(const char *name, void *value, size_t size) /* * Basic control over ability to set these attributes at all. */ + + /* + * For setting display, we only perform a permission check; + * the actual update to the display value is handled by the + * LSM framework. + */ + if (!strcmp(name, "display")) + return avc_has_perm(&selinux_state, + mysid, mysid, SECCLASS_PROCESS2, + PROCESS2__SETDISPLAY, NULL); + if (!strcmp(name, "exec")) error = avc_has_perm(&selinux_state, mysid, mysid, SECCLASS_PROCESS, diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h index 32e9b03be3dd..ab68612d0885 100644 --- a/security/selinux/include/classmap.h +++ b/security/selinux/include/classmap.h @@ -52,7 +52,7 @@ struct security_class_mapping secclass_map[] = { "execmem", "execstack", "execheap", "setkeycreate", "setsockcreate", "getrlimit", NULL } }, { "process2", - { "nnp_transition", "nosuid_transition", NULL } }, + { "nnp_transition", "nosuid_transition", "setdisplay", NULL } }, { "system", { "ipc_info", "syslog_read", "syslog_mod", "syslog_console", "module_request", "module_load", NULL } }, diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index e42336328446..aac8cb0de733 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -3519,6 +3519,13 @@ static int smack_setprocattr(const char *name, void *value, size_t size) struct smack_known_list_elem *sklep; int rc; + /* + * Allow the /proc/.../attr/current and SO_PEERSEC "display" + * to be reset at will. + */ + if (strcmp(name, "display") == 0) + return 0; + if (!smack_privileged(CAP_MAC_ADMIN) && list_empty(&tsp->smk_relabel)) return -EPERM; -- 2.20.1