From patchwork Sat Aug 13 20:36:49 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Casey Schaufler X-Patchwork-Id: 9279351 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 026A160780 for ; Sun, 14 Aug 2016 11:52:15 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id E69AF28A06 for ; Sun, 14 Aug 2016 11:52:14 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id DB3A628A57; Sun, 14 Aug 2016 11:52:14 +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 BE53B28A06 for ; Sun, 14 Aug 2016 11:52:13 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S933151AbcHNLwM (ORCPT ); Sun, 14 Aug 2016 07:52:12 -0400 Received: from nm47-vm1.bullet.mail.bf1.yahoo.com ([216.109.115.124]:53686 "EHLO nm47-vm1.bullet.mail.bf1.yahoo.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S932763AbcHNLwL (ORCPT ); Sun, 14 Aug 2016 07:52:11 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yahoo.com; s=s2048; t=1471120612; bh=kltbeTOJpxt3ATUuZioTe5zzKECeW+PHOtLG7C2NUUA=; h=Subject:To:References:Cc:From:Date:In-Reply-To:From:Subject; b=OwhBYnmqmhss0AkO1wpRHZ9hePNE7RBv8R0nD53nsyqLMu/2rGhoJEcgowaP+U+CFeGVsZFiA3XHZhiwlhGmvPzTjqtVe/NLQPEUrPwGFD/TRfrMqjvk9MWEj1c25nM7fJBNSsN6sArJxC4h3MJNyhO9ttwVIKDKkNayiaXATT/eawbu65LgoY1A3rTMQT5yi4tzsnNbrQryzAZZKLkZKxQLrkJIwGacDUAl5874zzpbmSUlCc/JUj0d6RghQTgKQJpWIvmoaCbx2XCdvwuj00jP7+gYPvvrOuAS+3OfnCxzyzn7gxMlrhxXGuNOnuKs6wPDFMR9p0Rxy730C8p10Q== Received: from [98.139.170.181] by nm47.bullet.mail.bf1.yahoo.com with NNFMP; 13 Aug 2016 20:36:52 -0000 Received: from [98.139.211.162] by tm24.bullet.mail.bf1.yahoo.com with NNFMP; 13 Aug 2016 20:36:52 -0000 Received: from [127.0.0.1] by smtp219.mail.bf1.yahoo.com with NNFMP; 13 Aug 2016 20:36:52 -0000 X-Yahoo-Newman-Id: 227851.87473.bm@smtp219.mail.bf1.yahoo.com X-Yahoo-Newman-Property: ymail-3 X-YMail-OSG: 8EwFrL0VM1nrN7rG6BuDNmW3nzQdvSXInx04P6vHZcv89TQ uxEcVvzznHeCh.CBR0UH8hjcGNBdcRafsLu4DAfmc6y2jjz4pxlNCPc42DHZ DUbHiO2yzzAZ0dlmEXW50njj68rfD31jmZy0qz8bdy3CAh4d5RqlxUKG3l1y BEnZYd_587JxQ75.WaWjhOYq9flDhw8rJfxeCF6S8G3VVX0PvbRExrR685V0 jl.WJmSNOdjmLzAgNl16CVpa0f4KfgRfgZPRthZmbVrFYi.q5H0bH5_PIn1g ujKxjKMGhzp4AjBS4v2IEhZLwW86_518.0AkWKQTPHfVWZ46wH6LafeMJBiv HzlwpDCDV5_TztJausi_7nMme0je0eZbpjl86uLl9Nv5AkzkCLyDYS2qHA6J vOQiP76.kv2grk3mgBjQh39IcKhWTimsQ1GiePEqDj.2_dG2T50Nedmk3PWK ff0kDJ0YRcKJ8iW.2t_CMnLKUifCIcGlHItrczkz2fbSTetRFUyV7oJT5hX8 s8qJfolkChXei1PKXjKG6oX5f8pXADaj1idtYSXNdVCSlBvTHdMu5NBzXKTz ekv1vRuwnbwFz X-Yahoo-SMTP: OIJXglSswBDfgLtXluJ6wiAYv6_cnw-- Subject: [PATCH 14/25] SELinux: Maintain secid to secctx mapping To: LSM , James Morris References: <801ef9a9-e594-387c-f285-8d90879ee2bf@schaufler-ca.com> Cc: John Johansen , Tetsuo Handa , Paul Moore , Stephen Smalley From: Casey Schaufler Message-ID: <05c7cbda-4dc0-0eb0-02bf-5a93d4a4ecb4@schaufler-ca.com> Date: Sat, 13 Aug 2016 13:36:49 -0700 User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:45.0) Gecko/20100101 Thunderbird/45.2.0 MIME-Version: 1.0 In-Reply-To: <801ef9a9-e594-387c-f285-8d90879ee2bf@schaufler-ca.com> Sender: owner-linux-security-module@vger.kernel.org Precedence: bulk List-ID: X-Virus-Scanned: ClamAV using ClamSMTP Subject: [PATCH 14/25] SELinux: Maintain secid to secctx mapping Maintain the mapping from secid to secctx in context structures that are on the sidtab. This has two important impacts: - Because the secctx is kept with the secid it is not necessary to regenerate the context when looking for the secctx by secid or the secid by secctx. - With the secctx being kept around the interfaces that use it don't have to make copies. The minor increase in memory usage (a secctx string with each secid) is a small price to pay for the elimination of countless alloc/free pairs in the networking and audit code. A new field has been added to the context structure to contain the context. The existing "str" field is not overloaded as there is some complexity in the way it is managed. Signed-off-by: Casey Schaufler --- security/selinux/avc.c | 8 ++----- security/selinux/hooks.c | 49 +++++++++++++++++++----------------------- security/selinux/selinuxfs.c | 7 ------ security/selinux/ss/context.h | 2 ++ security/selinux/ss/services.c | 28 ++++++++++++++---------- security/selinux/ss/services.h | 4 ++++ security/selinux/ss/sidtab.c | 38 ++++++++++++++++++++++++++++++++ security/selinux/ss/sidtab.h | 1 + security/selinux/xfrm.c | 8 ++----- 9 files changed, 88 insertions(+), 57 deletions(-) diff --git a/security/selinux/avc.c b/security/selinux/avc.c index e60c79d..09977a9 100644 --- a/security/selinux/avc.c +++ b/security/selinux/avc.c @@ -152,18 +152,14 @@ static void avc_dump_query(struct audit_buffer *ab, u32 ssid, u32 tsid, u16 tcla rc = security_sid_to_context(ssid, &scontext, &scontext_len); if (rc) audit_log_format(ab, "ssid=%d", ssid); - else { + else audit_log_format(ab, "scontext=%s", scontext); - kfree(scontext); - } rc = security_sid_to_context(tsid, &scontext, &scontext_len); if (rc) audit_log_format(ab, " tsid=%d", tsid); - else { + else audit_log_format(ab, " tcontext=%s", scontext); - kfree(scontext); - } BUG_ON(!tclass || tclass >= ARRAY_SIZE(secclass_map)); audit_log_format(ab, " tclass=%s", secclass_map[tclass-1].name); diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 03717c2..e8c6c5d 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -2872,7 +2872,9 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir, rc = security_sid_to_context_force(newsid, &context, &clen); if (rc) return rc; - *value = context; + *value = kstrdup(context, GFP_KERNEL); + if (*value == NULL) + return -ENOMEM; *len = clen; } @@ -3231,12 +3233,13 @@ static int selinux_inode_getsecurity(struct inode *inode, const char *name, void if (error) return error; error = size; - if (alloc) { + + /* + * context will not be freed because selinux_release_context + * does not free anything. + */ + if (alloc) *buffer = context; - goto out_nofree; - } - kfree(context); -out_nofree: return error; } @@ -4621,7 +4624,6 @@ static int selinux_socket_getpeersec_stream(struct socket *sock, char __user *op out_len: if (put_user(scontext_len, optlen)) err = -EFAULT; - kfree(scontext); return err; } @@ -5674,6 +5676,7 @@ static int selinux_getprocattr(struct task_struct *p, u32 sid; int error; unsigned len; + char *vp; if (current != p) { error = current_has_perm(p, PROCESS__GETATTR); @@ -5705,21 +5708,17 @@ static int selinux_getprocattr(struct task_struct *p, if (!sid) return 0; - if (strcmp(name, "context")) { - error = security_sid_to_context(sid, value, &len); - } else { - char *vp; - - error = security_sid_to_context(sid, &vp, &len); - if (!error) { - *value = kasprintf(GFP_KERNEL, "selinux='%s'", vp); - if (*value == NULL) - error = -ENOMEM; - } - } - + error = security_sid_to_context(sid, &vp, &len); if (error) return error; + + if (strcmp(name, "context")) + *value = kstrdup(vp, GFP_KERNEL); + else + *value = kasprintf(GFP_KERNEL, "selinux='%s'", vp); + + if (*value == NULL) + return -ENOMEM; return len; invalid: @@ -5876,11 +5875,6 @@ static int selinux_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid) return security_context_to_sid(secdata, seclen, secid, GFP_KERNEL); } -static void selinux_release_secctx(char *secdata, u32 seclen) -{ - kfree(secdata); -} - static void selinux_inode_invalidate_secctx(struct inode *inode) { struct inode_security_struct *isec = inode->i_security; @@ -5978,7 +5972,9 @@ static int selinux_key_getsecurity(struct key *key, char **_buffer) rc = security_sid_to_context(ksec->sid, &context, &len); if (!rc) rc = len; - *_buffer = context; + *_buffer = kstrdup(context, GFP_KERNEL); + if (*_buffer == NULL) + rc = -ENOMEM; return rc; } @@ -6123,7 +6119,6 @@ static struct security_hook_list selinux_hooks[] = { LSM_HOOK_INIT(ismaclabel, selinux_ismaclabel), LSM_HOOK_INIT(secid_to_secctx, selinux_secid_to_secctx), LSM_HOOK_INIT(secctx_to_secid, selinux_secctx_to_secid), - LSM_HOOK_INIT(release_secctx, selinux_release_secctx), LSM_HOOK_INIT(inode_invalidate_secctx, selinux_inode_invalidate_secctx), LSM_HOOK_INIT(inode_notifysecctx, selinux_inode_notifysecctx), LSM_HOOK_INIT(inode_setsecctx, selinux_inode_setsecctx), diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index 2519e26..96674d8 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -582,7 +582,6 @@ static ssize_t sel_write_context(struct file *file, char *buf, size_t size) memcpy(buf, canon, len); length = len; out: - kfree(canon); return length; } @@ -902,7 +901,6 @@ static ssize_t sel_write_create(struct file *file, char *buf, size_t size) memcpy(buf, newcon, len); length = len; out: - kfree(newcon); kfree(namebuf); kfree(tcon); kfree(scon); @@ -959,7 +957,6 @@ static ssize_t sel_write_relabel(struct file *file, char *buf, size_t size) memcpy(buf, newcon, len); length = len; out: - kfree(newcon); kfree(tcon); kfree(scon); return length; @@ -1009,12 +1006,10 @@ static ssize_t sel_write_user(struct file *file, char *buf, size_t size) goto out; } if ((length + len) >= SIMPLE_TRANSACTION_LIMIT) { - kfree(newcon); length = -ERANGE; goto out; } memcpy(ptr, newcon, len); - kfree(newcon); ptr += len; length += len; } @@ -1078,7 +1073,6 @@ static ssize_t sel_write_member(struct file *file, char *buf, size_t size) memcpy(buf, newcon, len); length = len; out: - kfree(newcon); kfree(tcon); kfree(scon); return length; @@ -1521,7 +1515,6 @@ static ssize_t sel_read_initcon(struct file *file, char __user *buf, return ret; ret = simple_read_from_buffer(buf, count, ppos, con, len); - kfree(con); return ret; } diff --git a/security/selinux/ss/context.h b/security/selinux/ss/context.h index 212e347..730c886 100644 --- a/security/selinux/ss/context.h +++ b/security/selinux/ss/context.h @@ -30,6 +30,8 @@ struct context { u32 len; /* length of string in bytes */ struct mls_range range; char *str; /* string representation if context cannot be mapped. */ + u32 seclen; + char *secctx; /* string representation if context can be mapped. */ }; static inline void mls_context_init(struct context *c) diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index 082b20c..feab3d5 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -89,8 +89,6 @@ int ss_initialized; static u32 latest_granting; /* Forward declaration. */ -static int context_struct_to_string(struct context *context, char **scontext, - u32 *scontext_len); static void context_struct_compute_av(struct context *scontext, struct context *tcontext, @@ -1176,7 +1174,8 @@ allow: * to point to this string and set `*scontext_len' to * the length of the string. */ -static int context_struct_to_string(struct context *context, char **scontext, u32 *scontext_len) +int context_struct_to_string(struct context *context, char **scontext, + u32 *scontext_len) { char *scontextp; @@ -1236,6 +1235,7 @@ const char *security_get_initial_sid_context(u32 sid) static int security_sid_to_context_core(u32 sid, char **scontext, u32 *scontext_len, int force) { + struct sidtab_node *snode; struct context *context; int rc = 0; @@ -1245,18 +1245,11 @@ static int security_sid_to_context_core(u32 sid, char **scontext, if (!ss_initialized) { if (sid <= SECINITSID_NUM) { - char *scontextp; *scontext_len = strlen(initial_sid_to_string[sid]) + 1; if (!scontext) goto out; - scontextp = kmemdup(initial_sid_to_string[sid], - *scontext_len, GFP_ATOMIC); - if (!scontextp) { - rc = -ENOMEM; - goto out; - } - *scontext = scontextp; + *scontext = (char *)initial_sid_to_string[sid]; goto out; } printk(KERN_ERR "SELinux: %s: called before initial " @@ -1265,6 +1258,19 @@ static int security_sid_to_context_core(u32 sid, char **scontext, goto out; } read_lock(&policy_rwlock); + snode = sidtab_find(&sidtab, sid, force); + if (!snode) { + printk(KERN_ERR "SELinux: %s: unrecognized SID tab %d\n", + __func__, sid); + } else if (snode->context.secctx == NULL || + snode->context.seclen == 0) { + printk(KERN_ERR "SELinux: %s: unrecognized SID secctx %d\n", + __func__, sid); + } else { + *scontext = snode->context.secctx; + *scontext_len = snode->context.seclen; + goto out_unlock; + } if (force) context = sidtab_search_force(&sidtab, sid); else diff --git a/security/selinux/ss/services.h b/security/selinux/ss/services.h index 6abcd87..dd9dffd 100644 --- a/security/selinux/ss/services.h +++ b/security/selinux/ss/services.h @@ -17,5 +17,9 @@ void services_compute_xperms_drivers(struct extended_perms *xperms, void services_compute_xperms_decision(struct extended_perms_decision *xpermd, struct avtab_node *node); +int context_struct_to_string(struct context *context, char **scontext, + u32 *scontext_len); + + #endif /* _SS_SERVICES_H_ */ diff --git a/security/selinux/ss/sidtab.c b/security/selinux/ss/sidtab.c index 5840a35..e18c38f 100644 --- a/security/selinux/ss/sidtab.c +++ b/security/selinux/ss/sidtab.c @@ -9,6 +9,7 @@ #include #include "flask.h" #include "security.h" +#include "services.h" #include "sidtab.h" #define SIDTAB_HASH(sid) \ @@ -64,6 +65,13 @@ int sidtab_insert(struct sidtab *s, u32 sid, struct context *context) rc = -ENOMEM; goto out; } + if (context_struct_to_string(context, &newnode->context.secctx, + &newnode->context.seclen) < 0) { + context_destroy(&newnode->context); + kfree(newnode); + rc = -ENOMEM; + goto out; + } if (prev) { newnode->next = prev->next; @@ -82,6 +90,36 @@ out: return rc; } +struct sidtab_node *sidtab_find(struct sidtab *s, u32 sid, int force) +{ + int hvalue; + struct sidtab_node *cur; + + if (!s) + return NULL; + + hvalue = SIDTAB_HASH(sid); + cur = s->htable[hvalue]; + while (cur && sid > cur->sid) + cur = cur->next; + + if (force && cur && sid == cur->sid && cur->context.len) + return cur; + + if (cur == NULL || sid != cur->sid || cur->context.len) { + /* Remap invalid SIDs to the unlabeled SID. */ + sid = SECINITSID_UNLABELED; + hvalue = SIDTAB_HASH(sid); + cur = s->htable[hvalue]; + while (cur && sid > cur->sid) + cur = cur->next; + if (!cur || sid != cur->sid) + return NULL; + } + + return cur; +} + static struct context *sidtab_search_core(struct sidtab *s, u32 sid, int force) { int hvalue; diff --git a/security/selinux/ss/sidtab.h b/security/selinux/ss/sidtab.h index 84dc154..0d60b380 100644 --- a/security/selinux/ss/sidtab.h +++ b/security/selinux/ss/sidtab.h @@ -35,6 +35,7 @@ int sidtab_init(struct sidtab *s); int sidtab_insert(struct sidtab *s, u32 sid, struct context *context); struct context *sidtab_search(struct sidtab *s, u32 sid); struct context *sidtab_search_force(struct sidtab *s, u32 sid); +struct sidtab_node *sidtab_find(struct sidtab *s, u32 sid, int force); int sidtab_map(struct sidtab *s, int (*apply) (u32 sid, diff --git a/security/selinux/xfrm.c b/security/selinux/xfrm.c index 789d07b..ca165d3 100644 --- a/security/selinux/xfrm.c +++ b/security/selinux/xfrm.c @@ -357,10 +357,8 @@ int selinux_xfrm_state_alloc_acquire(struct xfrm_state *x, return rc; ctx = kmalloc(sizeof(*ctx) + str_len, GFP_ATOMIC); - if (!ctx) { - rc = -ENOMEM; - goto out; - } + if (!ctx) + return -ENOMEM; ctx->ctx_doi = XFRM_SC_DOI_LSM; ctx->ctx_alg = XFRM_SC_ALG_SELINUX; @@ -370,8 +368,6 @@ int selinux_xfrm_state_alloc_acquire(struct xfrm_state *x, x->security = ctx; atomic_inc(&selinux_xfrm_refcount); -out: - kfree(ctx_str); return rc; }