lkml.org 
[lkml]   [2022]   [Jun]   [9]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
From
Subject[PATCH v36 05/33] IMA: avoid label collisions with stacked LSMs
Date
Integrity measurement may filter on security module information
and needs to be clear in the case of multiple active security
modules which applies. Provide a boot option ima_rules_lsm= to
allow the user to specify an active security module to apply
filters to. If not specified, use the first registered module
that supports the audit_rule_match() LSM hook. Allow the user
to specify in the IMA policy an lsm= option to specify the
security module to use for a particular rule.

Signed-off-by: Casey Schaufler <casey@schaufler-ca.com>
To: Mimi Zohar <zohar@linux.ibm.com>
To: linux-integrity@vger.kernel.org
---
Documentation/ABI/testing/ima_policy | 8 +++-
include/linux/security.h | 14 +++---
security/integrity/ima/ima_policy.c | 70 +++++++++++++++++++++++++---
security/security.c | 35 +++++++++++---
4 files changed, 108 insertions(+), 19 deletions(-)

diff --git a/Documentation/ABI/testing/ima_policy b/Documentation/ABI/testing/ima_policy
index db17fc8a0c9f..bac75acc24ba 100644
--- a/Documentation/ABI/testing/ima_policy
+++ b/Documentation/ABI/testing/ima_policy
@@ -26,7 +26,7 @@ Description:
[uid=] [euid=] [gid=] [egid=]
[fowner=] [fgroup=]]
lsm: [[subj_user=] [subj_role=] [subj_type=]
- [obj_user=] [obj_role=] [obj_type=]]
+ [obj_user=] [obj_role=] [obj_type=] [lsm=]]
option: [digest_type=] [template=] [permit_directio]
[appraise_type=] [appraise_flag=]
[appraise_algos=] [keyrings=]
@@ -138,6 +138,12 @@ Description:

measure subj_user=_ func=FILE_CHECK mask=MAY_READ

+ It is possible to explicitly specify which security
+ module a rule applies to using lsm=. If the security
+ module specified is not active on the system the rule
+ will be rejected. If lsm= is not specified the first
+ security module registered on the system will be assumed.
+
Example of measure rules using alternate PCRs::

measure func=KEXEC_KERNEL_CHECK pcr=4
diff --git a/include/linux/security.h b/include/linux/security.h
index 5b7a21237fea..5b0b2a596cee 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -1987,25 +1987,27 @@ static inline void security_audit_rule_free(struct audit_lsm_rules *lsmrules)
#endif /* CONFIG_AUDIT */

#if defined(CONFIG_IMA_LSM_RULES) && defined(CONFIG_SECURITY)
-int ima_filter_rule_init(u32 field, u32 op, char *rulestr, void **lsmrule);
-int ima_filter_rule_match(u32 secid, u32 field, u32 op, void *lsmrule);
-void ima_filter_rule_free(void *lsmrule);
+int ima_filter_rule_init(u32 field, u32 op, char *rulestr, void **lsmrule,
+ int lsmslot);
+int ima_filter_rule_match(u32 secid, u32 field, u32 op, void *lsmrule,
+ int lsmslot);
+void ima_filter_rule_free(void *lsmrule, int lsmslot);

#else

static inline int ima_filter_rule_init(u32 field, u32 op, char *rulestr,
- void **lsmrule)
+ void **lsmrule, int lsmslot)
{
return 0;
}

static inline int ima_filter_rule_match(u32 secid, u32 field, u32 op,
- void *lsmrule)
+ void *lsmrule, int lsmslot)
{
return 0;
}

-static inline void ima_filter_rule_free(void *lsmrule)
+static inline void ima_filter_rule_free(void *lsmrule, int lsmslot)
{ }

#endif /* defined(CONFIG_IMA_LSM_RULES) && defined(CONFIG_SECURITY) */
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
index 73917413365b..b04733a5d066 100644
--- a/security/integrity/ima/ima_policy.c
+++ b/security/integrity/ima/ima_policy.c
@@ -93,6 +93,8 @@ struct ima_rule_entry {
void *rule; /* LSM file metadata specific */
char *args_p; /* audit value */
int type; /* audit type */
+ int rules_lsm; /* which LSM rule applies to */
+ bool lsm_specific; /* true if lsm is specified */
} lsm[MAX_LSM_RULES];
char *fsname;
struct ima_rule_opt_list *keyrings; /* Measure keys added to these keyrings */
@@ -285,6 +287,30 @@ static int __init default_appraise_policy_setup(char *str)
}
__setup("ima_appraise_tcb", default_appraise_policy_setup);

+static int default_rules_lsm __ro_after_init = LSMBLOB_INVALID;
+
+static int __init ima_rules_lsm_init(char *str)
+{
+ const char *oldstr;
+ int newdrl;
+
+ newdrl = lsm_name_to_slot(str);
+ if (newdrl >= 0) {
+ default_rules_lsm = newdrl;
+ return 1;
+ }
+
+ oldstr = lsm_slot_to_name(default_rules_lsm);
+ if (oldstr) {
+ pr_err("default ima rule lsm \"%s\" not registered, continue using \"%s\"",
+ str, oldstr);
+ return 1;
+ }
+
+ return 1;
+}
+__setup("ima_rules_lsm=", ima_rules_lsm_init);
+
static struct ima_rule_opt_list *ima_alloc_rule_opt_list(const substring_t *src)
{
struct ima_rule_opt_list *opt_list;
@@ -356,7 +382,8 @@ static void ima_lsm_free_rule(struct ima_rule_entry *entry)
int i;

for (i = 0; i < MAX_LSM_RULES; i++) {
- ima_filter_rule_free(entry->lsm[i].rule);
+ ima_filter_rule_free(entry->lsm[i].rule,
+ entry->lsm[i].rules_lsm);
kfree(entry->lsm[i].args_p);
}
}
@@ -407,7 +434,8 @@ static struct ima_rule_entry *ima_lsm_copy_rule(struct ima_rule_entry *entry)

ima_filter_rule_init(nentry->lsm[i].type, Audit_equal,
nentry->lsm[i].args_p,
- &nentry->lsm[i].rule);
+ &nentry->lsm[i].rule,
+ entry->lsm[i].rules_lsm);
if (!nentry->lsm[i].rule)
pr_warn("rule for LSM \'%s\' is undefined\n",
nentry->lsm[i].args_p);
@@ -623,14 +651,16 @@ static bool ima_match_rules(struct ima_rule_entry *rule,
security_inode_getsecid(inode, &osid);
rc = ima_filter_rule_match(osid, rule->lsm[i].type,
Audit_equal,
- rule->lsm[i].rule);
+ rule->lsm[i].rule,
+ rule->lsm[i].rules_lsm);
break;
case LSM_SUBJ_USER:
case LSM_SUBJ_ROLE:
case LSM_SUBJ_TYPE:
rc = ima_filter_rule_match(secid, rule->lsm[i].type,
Audit_equal,
- rule->lsm[i].rule);
+ rule->lsm[i].rule,
+ rule->lsm[i].rules_lsm);
break;
default:
break;
@@ -1026,7 +1056,7 @@ enum policy_opt {
Opt_digest_type,
Opt_appraise_type, Opt_appraise_flag, Opt_appraise_algos,
Opt_permit_directio, Opt_pcr, Opt_template, Opt_keyrings,
- Opt_label, Opt_err
+ Opt_lsm, Opt_label, Opt_err
};

static const match_table_t policy_tokens = {
@@ -1074,6 +1104,7 @@ static const match_table_t policy_tokens = {
{Opt_pcr, "pcr=%s"},
{Opt_template, "template=%s"},
{Opt_keyrings, "keyrings=%s"},
+ {Opt_lsm, "lsm=%s"},
{Opt_label, "label=%s"},
{Opt_err, NULL}
};
@@ -1093,7 +1124,8 @@ static int ima_lsm_rule_init(struct ima_rule_entry *entry,
entry->lsm[lsm_rule].type = audit_type;
result = ima_filter_rule_init(entry->lsm[lsm_rule].type, Audit_equal,
entry->lsm[lsm_rule].args_p,
- &entry->lsm[lsm_rule].rule);
+ &entry->lsm[lsm_rule].rule,
+ entry->lsm[lsm_rule].rules_lsm);
if (!entry->lsm[lsm_rule].rule) {
pr_warn("rule for LSM \'%s\' is undefined\n",
entry->lsm[lsm_rule].args_p);
@@ -1833,6 +1865,23 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
&(template_desc->num_fields));
entry->template = template_desc;
break;
+ case Opt_lsm: {
+ int i;
+
+ result = lsm_name_to_slot(args[0].from);
+ if (result < 0) {
+ for (i = 0; i < MAX_LSM_RULES; i++)
+ entry->lsm[i].args_p = NULL;
+ result = -EINVAL;
+ break;
+ }
+ for (i = 0; i < MAX_LSM_RULES; i++) {
+ entry->lsm[i].rules_lsm = result;
+ entry->lsm[i].lsm_specific = true;
+ }
+ result = 0;
+ break;
+ }
case Opt_err:
ima_log_string(ab, "UNKNOWN", p);
result = -EINVAL;
@@ -1878,6 +1927,7 @@ ssize_t ima_parse_add_rule(char *rule)
struct ima_rule_entry *entry;
ssize_t result, len;
int audit_info = 0;
+ int i;

p = strsep(&rule, "\n");
len = strlen(p) + 1;
@@ -1895,6 +1945,11 @@ ssize_t ima_parse_add_rule(char *rule)

INIT_LIST_HEAD(&entry->list);

+ for (i = 0; i < MAX_LSM_RULES; i++) {
+ entry->lsm[i].rules_lsm = default_rules_lsm;
+ entry->lsm[i].lsm_specific = false;
+ }
+
result = ima_parse_rule(p, entry);
if (result) {
ima_free_rule(entry);
@@ -2205,6 +2260,9 @@ int ima_policy_show(struct seq_file *m, void *v)
entry->lsm[i].args_p);
break;
}
+ if (entry->lsm[i].lsm_specific)
+ seq_printf(m, pt(Opt_lsm),
+ lsm_slot_to_name(entry->lsm[i].rules_lsm));
seq_puts(m, " ");
}
}
diff --git a/security/security.c b/security/security.c
index 37c14572501e..141922732d10 100644
--- a/security/security.c
+++ b/security/security.c
@@ -2722,19 +2722,42 @@ int security_audit_rule_match(u32 secid, u32 field, u32 op,
* The integrity subsystem uses the same hooks as
* the audit subsystem.
*/
-int ima_filter_rule_init(u32 field, u32 op, char *rulestr, void **lsmrule)
+int ima_filter_rule_init(u32 field, u32 op, char *rulestr, void **lsmrule,
+ int lsmslot)
{
- return call_int_hook(audit_rule_init, 0, field, op, rulestr, lsmrule);
+ struct security_hook_list *hp;
+
+ hlist_for_each_entry(hp, &security_hook_heads.audit_rule_init, list)
+ if (hp->lsmid->slot == lsmslot)
+ return hp->hook.audit_rule_init(field, op, rulestr,
+ lsmrule);
+
+ return 0;
}

-void ima_filter_rule_free(void *lsmrule)
+void ima_filter_rule_free(void *lsmrule, int lsmslot)
{
- call_void_hook(audit_rule_free, lsmrule);
+ struct security_hook_list *hp;
+
+ hlist_for_each_entry(hp, &security_hook_heads.audit_rule_free, list) {
+ if (hp->lsmid->slot == lsmslot) {
+ hp->hook.audit_rule_free(lsmrule);
+ return;
+ }
+ }
}

-int ima_filter_rule_match(u32 secid, u32 field, u32 op, void *lsmrule)
+int ima_filter_rule_match(u32 secid, u32 field, u32 op, void *lsmrule,
+ int lsmslot)
{
- return call_int_hook(audit_rule_match, 0, secid, field, op, lsmrule);
+ struct security_hook_list *hp;
+
+ hlist_for_each_entry(hp, &security_hook_heads.audit_rule_match, list)
+ if (hp->lsmid->slot == lsmslot)
+ return hp->hook.audit_rule_match(secid, field, op,
+ lsmrule);
+
+ return 0;
}
#endif /* CONFIG_IMA_LSM_RULES */

--
2.35.1
\
 
 \ /
  Last update: 2022-06-10 01:04    [W:2.654 / U:1.620 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site