Commit 840e5bb3 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'integrity-v5.10' of git://git.kernel.org/pub/scm/linux/kernel/git/zohar/linux-integrity

Pull integrity updates from Mimi Zohar:
 "Continuing IMA policy rule cleanup and validation in particular for
  measuring keys, adding/removing/updating informational and error
  messages (e.g. "ima_appraise" boot command line option), and other bug
  fixes (e.g. minimal data size validation before use, return code and
  NULL pointer checking)"

* tag 'integrity-v5.10' of git://git.kernel.org/pub/scm/linux/kernel/git/zohar/linux-integrity:
  ima: Fix NULL pointer dereference in ima_file_hash
  evm: Check size of security.evm before using it
  ima: Remove semicolon at the end of ima_get_binary_runtime_size()
  ima: Don't ignore errors from crypto_shash_update()
  ima: Use kmemdup rather than kmalloc+memcpy
  integrity: include keyring name for unknown key request
  ima: limit secure boot feedback scope for appraise
  integrity: invalid kernel parameters feedback
  ima: add check for enforced appraise option
  integrity: Use current_uid() in integrity_audit_message()
  ima: Fail rule parsing when asymmetric key measurement isn't supportable
  ima: Pre-parse the list of keyrings in a KEY_CHECK rule
parents fefa636d aa662fc0
...@@ -55,8 +55,14 @@ static struct key *request_asymmetric_key(struct key *keyring, uint32_t keyid) ...@@ -55,8 +55,14 @@ static struct key *request_asymmetric_key(struct key *keyring, uint32_t keyid)
} }
if (IS_ERR(key)) { if (IS_ERR(key)) {
pr_err_ratelimited("Request for unknown key '%s' err %ld\n", if (keyring)
name, PTR_ERR(key)); pr_err_ratelimited("Request for unknown key '%s' in '%s' keyring. err %ld\n",
name, keyring->description,
PTR_ERR(key));
else
pr_err_ratelimited("Request for unknown key '%s' err %ld\n",
name, PTR_ERR(key));
switch (PTR_ERR(key)) { switch (PTR_ERR(key)) {
/* Hide some search errors */ /* Hide some search errors */
case -EACCES: case -EACCES:
......
...@@ -59,6 +59,9 @@ static int __init evm_set_fixmode(char *str) ...@@ -59,6 +59,9 @@ static int __init evm_set_fixmode(char *str)
{ {
if (strncmp(str, "fix", 3) == 0) if (strncmp(str, "fix", 3) == 0)
evm_fixmode = 1; evm_fixmode = 1;
else
pr_err("invalid \"%s\" mode", str);
return 0; return 0;
} }
__setup("evm=", evm_set_fixmode); __setup("evm=", evm_set_fixmode);
...@@ -181,6 +184,12 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry, ...@@ -181,6 +184,12 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry,
break; break;
case EVM_IMA_XATTR_DIGSIG: case EVM_IMA_XATTR_DIGSIG:
case EVM_XATTR_PORTABLE_DIGSIG: case EVM_XATTR_PORTABLE_DIGSIG:
/* accept xattr with non-empty signature field */
if (xattr_len <= sizeof(struct signature_v2_hdr)) {
evm_status = INTEGRITY_FAIL;
goto out;
}
hdr = (struct signature_v2_hdr *)xattr_data; hdr = (struct signature_v2_hdr *)xattr_data;
digest.hdr.algo = hdr->hash_algo; digest.hdr.algo = hdr->hash_algo;
rc = evm_calc_hash(dentry, xattr_name, xattr_value, rc = evm_calc_hash(dentry, xattr_name, xattr_value,
......
...@@ -19,18 +19,29 @@ ...@@ -19,18 +19,29 @@
static int __init default_appraise_setup(char *str) static int __init default_appraise_setup(char *str)
{ {
#ifdef CONFIG_IMA_APPRAISE_BOOTPARAM #ifdef CONFIG_IMA_APPRAISE_BOOTPARAM
if (arch_ima_get_secureboot()) { bool sb_state = arch_ima_get_secureboot();
pr_info("Secure boot enabled: ignoring ima_appraise=%s boot parameter option", int appraisal_state = ima_appraise;
str);
return 1;
}
if (strncmp(str, "off", 3) == 0) if (strncmp(str, "off", 3) == 0)
ima_appraise = 0; appraisal_state = 0;
else if (strncmp(str, "log", 3) == 0) else if (strncmp(str, "log", 3) == 0)
ima_appraise = IMA_APPRAISE_LOG; appraisal_state = IMA_APPRAISE_LOG;
else if (strncmp(str, "fix", 3) == 0) else if (strncmp(str, "fix", 3) == 0)
ima_appraise = IMA_APPRAISE_FIX; appraisal_state = IMA_APPRAISE_FIX;
else if (strncmp(str, "enforce", 7) == 0)
appraisal_state = IMA_APPRAISE_ENFORCE;
else
pr_err("invalid \"%s\" appraise option", str);
/* If appraisal state was changed, but secure boot is enabled,
* keep its default */
if (sb_state) {
if (!(appraisal_state & IMA_APPRAISE_ENFORCE))
pr_info("Secure boot enabled: ignoring ima_appraise=%s option",
str);
} else {
ima_appraise = appraisal_state;
}
#endif #endif
return 1; return 1;
} }
......
...@@ -829,6 +829,8 @@ static int ima_calc_boot_aggregate_tfm(char *digest, u16 alg_id, ...@@ -829,6 +829,8 @@ static int ima_calc_boot_aggregate_tfm(char *digest, u16 alg_id,
/* now accumulate with current aggregate */ /* now accumulate with current aggregate */
rc = crypto_shash_update(shash, d.digest, rc = crypto_shash_update(shash, d.digest,
crypto_shash_digestsize(tfm)); crypto_shash_digestsize(tfm));
if (rc != 0)
return rc;
} }
/* /*
* Extend cumulative digest over TPM registers 8-9, which contain * Extend cumulative digest over TPM registers 8-9, which contain
......
...@@ -51,18 +51,23 @@ static int __init hash_setup(char *str) ...@@ -51,18 +51,23 @@ static int __init hash_setup(char *str)
return 1; return 1;
if (strcmp(template_desc->name, IMA_TEMPLATE_IMA_NAME) == 0) { if (strcmp(template_desc->name, IMA_TEMPLATE_IMA_NAME) == 0) {
if (strncmp(str, "sha1", 4) == 0) if (strncmp(str, "sha1", 4) == 0) {
ima_hash_algo = HASH_ALGO_SHA1; ima_hash_algo = HASH_ALGO_SHA1;
else if (strncmp(str, "md5", 3) == 0) } else if (strncmp(str, "md5", 3) == 0) {
ima_hash_algo = HASH_ALGO_MD5; ima_hash_algo = HASH_ALGO_MD5;
else } else {
pr_err("invalid hash algorithm \"%s\" for template \"%s\"",
str, IMA_TEMPLATE_IMA_NAME);
return 1; return 1;
}
goto out; goto out;
} }
i = match_string(hash_algo_name, HASH_ALGO__LAST, str); i = match_string(hash_algo_name, HASH_ALGO__LAST, str);
if (i < 0) if (i < 0) {
pr_err("invalid hash algorithm \"%s\"", str);
return 1; return 1;
}
ima_hash_algo = i; ima_hash_algo = i;
out: out:
...@@ -532,6 +537,16 @@ int ima_file_hash(struct file *file, char *buf, size_t buf_size) ...@@ -532,6 +537,16 @@ int ima_file_hash(struct file *file, char *buf, size_t buf_size)
return -EOPNOTSUPP; return -EOPNOTSUPP;
mutex_lock(&iint->mutex); mutex_lock(&iint->mutex);
/*
* ima_file_hash can be called when ima_collect_measurement has still
* not been called, we might not always have a hash.
*/
if (!iint->ima_hash) {
mutex_unlock(&iint->mutex);
return -EOPNOTSUPP;
}
if (buf) { if (buf) {
size_t copied_size; size_t copied_size;
......
...@@ -60,6 +60,11 @@ enum policy_types { ORIGINAL_TCB = 1, DEFAULT_TCB }; ...@@ -60,6 +60,11 @@ enum policy_types { ORIGINAL_TCB = 1, DEFAULT_TCB };
enum policy_rule_list { IMA_DEFAULT_POLICY = 1, IMA_CUSTOM_POLICY }; enum policy_rule_list { IMA_DEFAULT_POLICY = 1, IMA_CUSTOM_POLICY };
struct ima_rule_opt_list {
size_t count;
char *items[];
};
struct ima_rule_entry { struct ima_rule_entry {
struct list_head list; struct list_head list;
int action; int action;
...@@ -79,7 +84,7 @@ struct ima_rule_entry { ...@@ -79,7 +84,7 @@ struct ima_rule_entry {
int type; /* audit type */ int type; /* audit type */
} lsm[MAX_LSM_RULES]; } lsm[MAX_LSM_RULES];
char *fsname; char *fsname;
char *keyrings; /* Measure keys added to these keyrings */ struct ima_rule_opt_list *keyrings; /* Measure keys added to these keyrings */
struct ima_template_desc *template; struct ima_template_desc *template;
}; };
...@@ -207,10 +212,6 @@ static LIST_HEAD(ima_policy_rules); ...@@ -207,10 +212,6 @@ static LIST_HEAD(ima_policy_rules);
static LIST_HEAD(ima_temp_rules); static LIST_HEAD(ima_temp_rules);
static struct list_head *ima_rules = &ima_default_rules; static struct list_head *ima_rules = &ima_default_rules;
/* Pre-allocated buffer used for matching keyrings. */
static char *ima_keyrings;
static size_t ima_keyrings_len;
static int ima_policy __initdata; static int ima_policy __initdata;
static int __init default_measure_policy_setup(char *str) static int __init default_measure_policy_setup(char *str)
...@@ -241,6 +242,8 @@ static int __init policy_setup(char *str) ...@@ -241,6 +242,8 @@ static int __init policy_setup(char *str)
ima_use_secure_boot = true; ima_use_secure_boot = true;
else if (strcmp(p, "fail_securely") == 0) else if (strcmp(p, "fail_securely") == 0)
ima_fail_unverifiable_sigs = true; ima_fail_unverifiable_sigs = true;
else
pr_err("policy \"%s\" not found", p);
} }
return 1; return 1;
...@@ -254,6 +257,72 @@ static int __init default_appraise_policy_setup(char *str) ...@@ -254,6 +257,72 @@ static int __init default_appraise_policy_setup(char *str)
} }
__setup("ima_appraise_tcb", default_appraise_policy_setup); __setup("ima_appraise_tcb", default_appraise_policy_setup);
static struct ima_rule_opt_list *ima_alloc_rule_opt_list(const substring_t *src)
{
struct ima_rule_opt_list *opt_list;
size_t count = 0;
char *src_copy;
char *cur, *next;
size_t i;
src_copy = match_strdup(src);
if (!src_copy)
return ERR_PTR(-ENOMEM);
next = src_copy;
while ((cur = strsep(&next, "|"))) {
/* Don't accept an empty list item */
if (!(*cur)) {
kfree(src_copy);
return ERR_PTR(-EINVAL);
}
count++;
}
/* Don't accept an empty list */
if (!count) {
kfree(src_copy);
return ERR_PTR(-EINVAL);
}
opt_list = kzalloc(struct_size(opt_list, items, count), GFP_KERNEL);
if (!opt_list) {
kfree(src_copy);
return ERR_PTR(-ENOMEM);
}
/*
* strsep() has already replaced all instances of '|' with '\0',
* leaving a byte sequence of NUL-terminated strings. Reference each
* string with the array of items.
*
* IMPORTANT: Ownership of the allocated buffer is transferred from
* src_copy to the first element in the items array. To free the
* buffer, kfree() must only be called on the first element of the
* array.
*/
for (i = 0, cur = src_copy; i < count; i++) {
opt_list->items[i] = cur;
cur = strchr(cur, '\0') + 1;
}
opt_list->count = count;
return opt_list;
}
static void ima_free_rule_opt_list(struct ima_rule_opt_list *opt_list)
{
if (!opt_list)
return;
if (opt_list->count) {
kfree(opt_list->items[0]);
opt_list->count = 0;
}
kfree(opt_list);
}
static void ima_lsm_free_rule(struct ima_rule_entry *entry) static void ima_lsm_free_rule(struct ima_rule_entry *entry)
{ {
int i; int i;
...@@ -275,7 +344,7 @@ static void ima_free_rule(struct ima_rule_entry *entry) ...@@ -275,7 +344,7 @@ static void ima_free_rule(struct ima_rule_entry *entry)
* the defined_templates list and cannot be freed here * the defined_templates list and cannot be freed here
*/ */
kfree(entry->fsname); kfree(entry->fsname);
kfree(entry->keyrings); ima_free_rule_opt_list(entry->keyrings);
ima_lsm_free_rule(entry); ima_lsm_free_rule(entry);
kfree(entry); kfree(entry);
} }
...@@ -285,15 +354,14 @@ static struct ima_rule_entry *ima_lsm_copy_rule(struct ima_rule_entry *entry) ...@@ -285,15 +354,14 @@ static struct ima_rule_entry *ima_lsm_copy_rule(struct ima_rule_entry *entry)
struct ima_rule_entry *nentry; struct ima_rule_entry *nentry;
int i; int i;
nentry = kmalloc(sizeof(*nentry), GFP_KERNEL);
if (!nentry)
return NULL;
/* /*
* Immutable elements are copied over as pointers and data; only * Immutable elements are copied over as pointers and data; only
* lsm rules can change * lsm rules can change
*/ */
memcpy(nentry, entry, sizeof(*nentry)); nentry = kmemdup(entry, sizeof(*nentry), GFP_KERNEL);
if (!nentry)
return NULL;
memset(nentry->lsm, 0, sizeof_field(struct ima_rule_entry, lsm)); memset(nentry->lsm, 0, sizeof_field(struct ima_rule_entry, lsm));
for (i = 0; i < MAX_LSM_RULES; i++) { for (i = 0; i < MAX_LSM_RULES; i++) {
...@@ -395,8 +463,8 @@ int ima_lsm_policy_change(struct notifier_block *nb, unsigned long event, ...@@ -395,8 +463,8 @@ int ima_lsm_policy_change(struct notifier_block *nb, unsigned long event,
static bool ima_match_keyring(struct ima_rule_entry *rule, static bool ima_match_keyring(struct ima_rule_entry *rule,
const char *keyring, const struct cred *cred) const char *keyring, const struct cred *cred)
{ {
char *next_keyring, *keyrings_ptr;
bool matched = false; bool matched = false;
size_t i;
if ((rule->flags & IMA_UID) && !rule->uid_op(cred->uid, rule->uid)) if ((rule->flags & IMA_UID) && !rule->uid_op(cred->uid, rule->uid))
return false; return false;
...@@ -407,15 +475,8 @@ static bool ima_match_keyring(struct ima_rule_entry *rule, ...@@ -407,15 +475,8 @@ static bool ima_match_keyring(struct ima_rule_entry *rule,
if (!keyring) if (!keyring)
return false; return false;
strcpy(ima_keyrings, rule->keyrings); for (i = 0; i < rule->keyrings->count; i++) {
if (!strcmp(rule->keyrings->items[i], keyring)) {
/*
* "keyrings=" is specified in the policy in the format below:
* keyrings=.builtin_trusted_keys|.ima|.evm
*/
keyrings_ptr = ima_keyrings;
while ((next_keyring = strsep(&keyrings_ptr, "|")) != NULL) {
if (!strcmp(next_keyring, keyring)) {
matched = true; matched = true;
break; break;
} }
...@@ -1066,7 +1127,6 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) ...@@ -1066,7 +1127,6 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
bool uid_token; bool uid_token;
struct ima_template_desc *template_desc; struct ima_template_desc *template_desc;
int result = 0; int result = 0;
size_t keyrings_len;
ab = integrity_audit_log_start(audit_context(), GFP_KERNEL, ab = integrity_audit_log_start(audit_context(), GFP_KERNEL,
AUDIT_INTEGRITY_POLICY_RULE); AUDIT_INTEGRITY_POLICY_RULE);
...@@ -1175,7 +1235,8 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) ...@@ -1175,7 +1235,8 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
entry->func = POLICY_CHECK; entry->func = POLICY_CHECK;
else if (strcmp(args[0].from, "KEXEC_CMDLINE") == 0) else if (strcmp(args[0].from, "KEXEC_CMDLINE") == 0)
entry->func = KEXEC_CMDLINE; entry->func = KEXEC_CMDLINE;
else if (strcmp(args[0].from, "KEY_CHECK") == 0) else if (IS_ENABLED(CONFIG_IMA_MEASURE_ASYMMETRIC_KEYS) &&
strcmp(args[0].from, "KEY_CHECK") == 0)
entry->func = KEY_CHECK; entry->func = KEY_CHECK;
else else
result = -EINVAL; result = -EINVAL;
...@@ -1232,37 +1293,19 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) ...@@ -1232,37 +1293,19 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
case Opt_keyrings: case Opt_keyrings:
ima_log_string(ab, "keyrings", args[0].from); ima_log_string(ab, "keyrings", args[0].from);
keyrings_len = strlen(args[0].from) + 1; if (!IS_ENABLED(CONFIG_IMA_MEASURE_ASYMMETRIC_KEYS) ||
entry->keyrings) {
if ((entry->keyrings) ||
(keyrings_len < 2)) {
result = -EINVAL; result = -EINVAL;
break; break;
} }
if (keyrings_len > ima_keyrings_len) { entry->keyrings = ima_alloc_rule_opt_list(args);
char *tmpbuf; if (IS_ERR(entry->keyrings)) {
result = PTR_ERR(entry->keyrings);
tmpbuf = krealloc(ima_keyrings, keyrings_len, entry->keyrings = NULL;
GFP_KERNEL);
if (!tmpbuf) {
result = -ENOMEM;
break;
}
ima_keyrings = tmpbuf;
ima_keyrings_len = keyrings_len;
}
entry->keyrings = kstrdup(args[0].from, GFP_KERNEL);
if (!entry->keyrings) {
kfree(ima_keyrings);
ima_keyrings = NULL;
ima_keyrings_len = 0;
result = -ENOMEM;
break; break;
} }
result = 0;
entry->flags |= IMA_KEYRINGS; entry->flags |= IMA_KEYRINGS;
break; break;
case Opt_fsuuid: case Opt_fsuuid:
...@@ -1575,6 +1618,15 @@ static void policy_func_show(struct seq_file *m, enum ima_hooks func) ...@@ -1575,6 +1618,15 @@ static void policy_func_show(struct seq_file *m, enum ima_hooks func)
seq_printf(m, "func=%d ", func); seq_printf(m, "func=%d ", func);
} }
static void ima_show_rule_opt_list(struct seq_file *m,
const struct ima_rule_opt_list *opt_list)
{
size_t i;
for (i = 0; i < opt_list->count; i++)
seq_printf(m, "%s%s", i ? "|" : "", opt_list->items[i]);
}
int ima_policy_show(struct seq_file *m, void *v) int ima_policy_show(struct seq_file *m, void *v)
{ {
struct ima_rule_entry *entry = v; struct ima_rule_entry *entry = v;
...@@ -1631,9 +1683,8 @@ int ima_policy_show(struct seq_file *m, void *v) ...@@ -1631,9 +1683,8 @@ int ima_policy_show(struct seq_file *m, void *v)
} }
if (entry->flags & IMA_KEYRINGS) { if (entry->flags & IMA_KEYRINGS) {
if (entry->keyrings != NULL) seq_puts(m, "keyrings=");
snprintf(tbuf, sizeof(tbuf), "%s", entry->keyrings); ima_show_rule_opt_list(m, entry->keyrings);
seq_printf(m, pt(Opt_keyrings), tbuf);
seq_puts(m, " "); seq_puts(m, " ");
} }
......
...@@ -133,7 +133,7 @@ unsigned long ima_get_binary_runtime_size(void) ...@@ -133,7 +133,7 @@ unsigned long ima_get_binary_runtime_size(void)
return ULONG_MAX; return ULONG_MAX;
else else
return binary_runtime_size + sizeof(struct ima_kexec_hdr); return binary_runtime_size + sizeof(struct ima_kexec_hdr);
}; }
static int ima_pcr_extend(struct tpm_digest *digests_arg, int pcr) static int ima_pcr_extend(struct tpm_digest *digests_arg, int pcr)
{ {
......
...@@ -47,7 +47,7 @@ void integrity_audit_message(int audit_msgno, struct inode *inode, ...@@ -47,7 +47,7 @@ void integrity_audit_message(int audit_msgno, struct inode *inode,
ab = audit_log_start(audit_context(), GFP_KERNEL, audit_msgno); ab = audit_log_start(audit_context(), GFP_KERNEL, audit_msgno);
audit_log_format(ab, "pid=%d uid=%u auid=%u ses=%u", audit_log_format(ab, "pid=%d uid=%u auid=%u ses=%u",
task_pid_nr(current), task_pid_nr(current),
from_kuid(&init_user_ns, current_cred()->uid), from_kuid(&init_user_ns, current_uid()),
from_kuid(&init_user_ns, audit_get_loginuid(current)), from_kuid(&init_user_ns, audit_get_loginuid(current)),
audit_get_sessionid(current)); audit_get_sessionid(current));
audit_log_task_context(ab); audit_log_task_context(ab);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment