From patchwork Fri Jun 10 21:25:53 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Casey Schaufler X-Patchwork-Id: 9170467 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 EB7E56048F for ; Fri, 10 Jun 2016 21:26:26 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id DDA532766D for ; Fri, 10 Jun 2016 21:26:26 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id D267828356; Fri, 10 Jun 2016 21:26:26 +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=unavailable 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 24CC52766D for ; Fri, 10 Jun 2016 21:26:26 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932284AbcFJV0F (ORCPT ); Fri, 10 Jun 2016 17:26:05 -0400 Received: from nm32-vm1.bullet.mail.bf1.yahoo.com ([72.30.239.137]:39806 "EHLO nm32-vm1.bullet.mail.bf1.yahoo.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S932276AbcFJV0E (ORCPT ); Fri, 10 Jun 2016 17:26:04 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yahoo.com; s=s2048; t=1465593957; bh=ZgdB8Rf1FkCjUqBrxWzBYMWY/XzwiXz8vgyUw49pnYc=; h=Subject:To:References:Cc:From:Date:In-Reply-To:From:Subject; b=kS5KazTgG3UQIOUM3dBCInx8A606tJ2MejfaEfoKy9lBsmj2MH0LgwnUMvsRD95Od3FuEbo2Q7o3NKacmgf0FVvrv1YCmIkxqMgAf6nKGHlsWlD/9yxqudA8pDNTCirujUsdmV4aSj3nWTt2NCGqYsdQu9EvV24dx5ev1nWKvL2fqYJMvitSlijxgCQkevXvFWb0JwswmOczoJq2OmO1rIUp3JkgZ8U1Mufa3FwvbBV9XNTBLH9iAsIZNpvt2j+ZtJEZTRqaKp05aE0+NxoyMT9ap8qQdzepaJHrqgyys120xvVBY0fl2/s1q9n1XXTTPU+ZHN03CCWOcD26h/LEIA== Received: from [98.139.215.143] by nm32.bullet.mail.bf1.yahoo.com with NNFMP; 10 Jun 2016 21:25:57 -0000 Received: from [98.139.213.10] by tm14.bullet.mail.bf1.yahoo.com with NNFMP; 10 Jun 2016 21:25:57 -0000 Received: from [127.0.0.1] by smtp110.mail.bf1.yahoo.com with NNFMP; 10 Jun 2016 21:25:57 -0000 X-Yahoo-Newman-Id: 181542.73093.bm@smtp110.mail.bf1.yahoo.com X-Yahoo-Newman-Property: ymail-3 X-YMail-OSG: qoSDPQUVM1njmVh5SfZj0uc6PbcXH7LxAmJW687NI5JyX8r RqGL619rBvBYQY0LVM2FFnKiIuI8Xwu0bStJo6SgzX44jTsJ2VQHU0rNwJhx FPI0T4M41Xs95JoJG6pNFoeZfaCgjtc8lcyzq89UZfBtaXmiX_Aa1PkeWLgC h3NJeISQ2mwSgL1jAGY8hQTlTKSDnzknNFg1wlG77V90pPiZEgMzagKrreVX CSu0n6odKsNYWf7Hz2.LRZUb.TT.6ki3GL0KjYEFjhyO57ZpYyUg7jgInMfh XIMXZJBGHUZRZhlQH3HhmX13FZuB6U5GT8RhlMxuJfFIhcwrYRiLqdPZvh.n XGfZfoSGVD6E8n4AXkTJq1.DH3ZSL0Fsvzz2YJuLNwRnyV1VbBS1mgk7iSd_ ovz9rYFBdYdOezEpQ2tGlsiSAf2ZkHUEyo9xX5Y3iA6r6X51V43LnyuXNwNn 65Ig0OCKPkLyhVfMPeCtIG_Bp1BIkKYl1xPoqng55Qgbo8GxCYcDtw71AD4O 80sqrKmIL3ZJYbN7aQbq6s90L2V6hnXJul5faqY.ClwNcZg0T_7jcSGzaF0w - X-Yahoo-SMTP: OIJXglSswBDfgLtXluJ6wiAYv6_cnw-- Subject: [PATCH v3 3/3] LSM: Add context interface for proc attr To: LSM , James Morris References: Cc: John Johansen , Stephen Smalley , Paul Moore , Tetsuo Handa , Kees Cook , LKLM , James Morris From: Casey Schaufler Message-ID: <08700e20-93c7-1d5c-5215-01cd0c0c7190@schaufler-ca.com> Date: Fri, 10 Jun 2016 14:25:53 -0700 User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:45.0) Gecko/20100101 Thunderbird/45.1.1 MIME-Version: 1.0 In-Reply-To: Sender: owner-linux-security-module@vger.kernel.org Precedence: bulk List-ID: X-Virus-Scanned: ClamAV using ClamSMTP Subject: [PATCH v3 3/3] LSM: Add context interface for proc attrs The /proc/.../attr/current interface is used by all three Linux security modules (SELinux, Smack and AppArmor) to report and modify the process security attribute. This is all fine when there is exactly one of these modules active and the userspace code knows which it module it is. It would require a major change to the "current" interface to provide information about more than one set of process security attributes. Instead, a "context" attribute is added, which identifies the security module that the information applies to. The format is: lsmname='context-value' When multiple concurrent modules are supported the /proc/.../attr/context interface will include the data for all of the active modules. lsmname1='context-value1'lsmname2='context-value2' The module specific subdirectories under attr contain context entries that report the information for that specific module in the same format. Signed-off-by: Casey Schaufler --- fs/proc/base.c | 4 ++ security/apparmor/lsm.c | 34 ++++++++++++++-- security/security.c | 97 ++++++++++++++++++++++++++++++++++++++++++++++ security/selinux/hooks.c | 22 ++++++++++- security/smack/smack_lsm.c | 21 ++++++---- 5 files changed, 164 insertions(+), 14 deletions(-) -- To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html diff --git a/fs/proc/base.c b/fs/proc/base.c index 182bc28..df94f26 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -2532,6 +2532,7 @@ static const struct pid_entry selinux_attr_dir_stuff[] = { ATTR("selinux", "fscreate", S_IRUGO|S_IWUGO), ATTR("selinux", "keycreate", S_IRUGO|S_IWUGO), ATTR("selinux", "sockcreate", S_IRUGO|S_IWUGO), + ATTR("selinux", "context", S_IRUGO|S_IWUGO), }; LSM_DIR_OPS(selinux); #endif @@ -2539,6 +2540,7 @@ LSM_DIR_OPS(selinux); #ifdef CONFIG_SECURITY_SMACK static const struct pid_entry smack_attr_dir_stuff[] = { ATTR("smack", "current", S_IRUGO|S_IWUGO), + ATTR("smack", "context", S_IRUGO|S_IWUGO), }; LSM_DIR_OPS(smack); #endif @@ -2548,6 +2550,7 @@ static const struct pid_entry apparmor_attr_dir_stuff[] = { ATTR("apparmor", "current", S_IRUGO|S_IWUGO), ATTR("apparmor", "prev", S_IRUGO), ATTR("apparmor", "exec", S_IRUGO|S_IWUGO), + ATTR("apparmor", "context", S_IRUGO|S_IWUGO), }; LSM_DIR_OPS(apparmor); #endif @@ -2559,6 +2562,7 @@ static const struct pid_entry attr_dir_stuff[] = { ATTR(NULL, "fscreate", S_IRUGO|S_IWUGO), ATTR(NULL, "keycreate", S_IRUGO|S_IWUGO), ATTR(NULL, "sockcreate", S_IRUGO|S_IWUGO), + ATTR(NULL, "context", S_IRUGO|S_IWUGO), #ifdef CONFIG_SECURITY_SELINUX DIR("selinux", S_IRUGO|S_IXUGO, proc_selinux_attr_dir_inode_ops, proc_selinux_attr_dir_ops), diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index fb0fb03..3790a7d 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -479,6 +479,8 @@ static int apparmor_getprocattr(struct task_struct *task, char *name, if (strcmp(name, "current") == 0) profile = aa_get_newest_profile(cxt->profile); + else if (strcmp(name, "context") == 0) + profile = aa_get_newest_profile(cxt->profile); else if (strcmp(name, "prev") == 0 && cxt->previous) profile = aa_get_newest_profile(cxt->previous); else if (strcmp(name, "exec") == 0 && cxt->onexec) @@ -486,8 +488,29 @@ static int apparmor_getprocattr(struct task_struct *task, char *name, else error = -EINVAL; - if (profile) - error = aa_getprocattr(profile, value); + if (profile) { + if (strcmp(name, "context") == 0) { + char *vp; + char *np; + + error = aa_getprocattr(profile, &vp); + if (error > 0) { + error += 12; + *value = kzalloc(error, GFP_KERNEL); + if (*value == NULL) + error = -ENOMEM; + else { + sprintf(*value, "apparmor='%s'", vp); + np = strchr(*value, '\n'); + if (np != NULL) { + np[0] = '\''; + np[1] = '\0'; + } + } + } + } else + error = aa_getprocattr(profile, value); + } aa_put_profile(profile); put_cred(cred); @@ -530,7 +553,7 @@ static int apparmor_setprocattr(struct task_struct *task, char *name, return -EINVAL; arg_size = size - (args - (char *) value); - if (strcmp(name, "current") == 0) { + if (strcmp(name, "current") == 0 || strcmp(name, "context") == 0) { if (strcmp(command, "changehat") == 0) { error = aa_setprocattr_changehat(args, arg_size, !AA_DO_TEST); @@ -552,7 +575,10 @@ static int apparmor_setprocattr(struct task_struct *task, char *name, else goto fail; } else - /* only support the "current" and "exec" process attributes */ + /* + * only support the "current", context and "exec" + * process attributes + */ return -EINVAL; if (!error) diff --git a/security/security.c b/security/security.c index 41ac80d..6f7ad19 100644 --- a/security/security.c +++ b/security/security.c @@ -1187,8 +1187,49 @@ int security_getprocattr(struct task_struct *p, const char *lsm, char *name, char **value) { struct security_hook_list *hp; + char *vp; + char *cp = NULL; int rc = -EINVAL; + int trc; + /* + * "context" requires work here in addition to what + * the modules provide. + */ + if (strcmp(name, "context") == 0) { + *value = NULL; + list_for_each_entry(hp, + &security_hook_heads.getprocattr, list) { + if (lsm != NULL && strcmp(lsm, hp->lsm)) + continue; + trc = hp->hook.getprocattr(p, "context", &vp); + if (trc == -ENOENT) + continue; + if (trc <= 0) { + kfree(*value); + return trc; + } + rc = trc; + if (*value == NULL) { + *value = vp; + } else { + cp = kzalloc(strlen(*value) + strlen(vp) + 1, + GFP_KERNEL); + if (cp == NULL) { + kfree(*value); + kfree(vp); + return -ENOMEM; + } + sprintf(cp, "%s%s", *value, vp); + kfree(*value); + kfree(vp); + *value = cp; + } + } + if (rc > 0) + return strlen(*value); + return rc; + } list_for_each_entry(hp, &security_hook_heads.getprocattr, list) { if (lsm != NULL && strcmp(lsm, hp->lsm)) @@ -1205,7 +1246,63 @@ int security_setprocattr(struct task_struct *p, const char *lsm, char *name, { struct security_hook_list *hp; int rc = -EINVAL; + char *local; + char *cp; + int slen; + int failed = 0; + /* + * "context" is handled directly here. + */ + if (strcmp(name, "context") == 0) { + /* + * First verify that the input is acceptable. + * lsm1='v1'lsm2='v2'lsm3='v3' + * + * A note on the use of strncmp() below. + * The check is for the substring at the beginning of cp. + */ + local = kzalloc(size + 1, GFP_KERNEL); + memcpy(local, value, size); + cp = local; + list_for_each_entry(hp, &security_hook_heads.setprocattr, + list) { + if (lsm != NULL && strcmp(lsm, hp->lsm)) + continue; + slen = strlen(hp->lsm); + if (strncmp(cp, hp->lsm, slen)) + goto free_out; + cp += slen; + if (cp[0] != '=' || cp[1] != '\'' || cp[2] == '\'') + goto free_out; + for (cp += 2; cp[0] != '\''; cp++) + if (cp[0] == '\0') + goto free_out; + cp++; + } + cp = local; + list_for_each_entry(hp, &security_hook_heads.setprocattr, + list) { + if (lsm != NULL && strcmp(lsm, hp->lsm)) + continue; + cp += strlen(hp->lsm) + 2; + for (slen = 0; cp[slen] != '\''; slen++) + ; + cp[slen] = '\0'; + + rc = hp->hook.setprocattr(p, "context", cp, slen); + if (rc < 0) + failed = rc; + cp += slen + 1; + } + if (failed != 0) + rc = failed; + else + rc = size; +free_out: + kfree(local); + return rc; + } list_for_each_entry(hp, &security_hook_heads.setprocattr, list) { if (lsm != NULL && strcmp(lsm, hp->lsm)) continue; diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index ed3a757..3a21c2b 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -5711,6 +5711,8 @@ static int selinux_getprocattr(struct task_struct *p, if (!strcmp(name, "current")) sid = __tsec->sid; + else if (!strcmp(name, "context")) + sid = __tsec->sid; else if (!strcmp(name, "prev")) sid = __tsec->osid; else if (!strcmp(name, "exec")) @@ -5728,7 +5730,21 @@ static int selinux_getprocattr(struct task_struct *p, if (!sid) return 0; - error = security_sid_to_context(sid, value, &len); + 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 = kzalloc(len + 10, GFP_KERNEL); + if (*value == NULL) + error = -ENOMEM; + else + sprintf(*value, "selinux='%s'", vp); + } + } + if (error) return error; return len; @@ -5768,6 +5784,8 @@ static int selinux_setprocattr(struct task_struct *p, error = current_has_perm(p, PROCESS__SETSOCKCREATE); else if (!strcmp(name, "current")) error = current_has_perm(p, PROCESS__SETCURRENT); + else if (!strcmp(name, "context")) + error = current_has_perm(p, PROCESS__SETCURRENT); else error = -EINVAL; if (error) @@ -5827,7 +5845,7 @@ static int selinux_setprocattr(struct task_struct *p, tsec->keycreate_sid = sid; } else if (!strcmp(name, "sockcreate")) { tsec->sockcreate_sid = sid; - } else if (!strcmp(name, "current")) { + } else if (!strcmp(name, "current") || !strcmp(name, "context")) { error = -EINVAL; if (sid == 0) goto abort_change; diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 3577009..d2d8624 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -3576,16 +3576,21 @@ static int smack_getprocattr(struct task_struct *p, char *name, char **value) char *cp; int slen; - if (strcmp(name, "current") != 0) + if (strcmp(name, "current") == 0) { + cp = kstrdup(skp->smk_known, GFP_KERNEL); + if (cp == NULL) + return -ENOMEM; + } else if (strcmp(name, "context") == 0) { + slen = strlen(skp->smk_known) + 9; + cp = kzalloc(slen, GFP_KERNEL); + if (cp == NULL) + return -ENOMEM; + sprintf(cp, "smack='%s'", skp->smk_known); + } else return -EINVAL; - cp = kstrdup(skp->smk_known, GFP_KERNEL); - if (cp == NULL) - return -ENOMEM; - - slen = strlen(cp); *value = cp; - return slen; + return strlen(cp); } /** @@ -3622,7 +3627,7 @@ static int smack_setprocattr(struct task_struct *p, char *name, if (value == NULL || size == 0 || size >= SMK_LONGLABEL) return -EINVAL; - if (strcmp(name, "current") != 0) + if (strcmp(name, "current") != 0 && strcmp(name, "context") != 0) return -EINVAL; skp = smk_import_entry(value, size);