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)
}
if (IS_ERR(key)) {
pr_err_ratelimited("Request for unknown key '%s' err %ld\n",
name, PTR_ERR(key));
if (keyring)
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)) {
/* Hide some search errors */
case -EACCES:
......
......@@ -59,6 +59,9 @@ static int __init evm_set_fixmode(char *str)
{
if (strncmp(str, "fix", 3) == 0)
evm_fixmode = 1;
else
pr_err("invalid \"%s\" mode", str);
return 0;
}
__setup("evm=", evm_set_fixmode);
......@@ -181,6 +184,12 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry,
break;
case EVM_IMA_XATTR_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;
digest.hdr.algo = hdr->hash_algo;
rc = evm_calc_hash(dentry, xattr_name, xattr_value,
......
......@@ -19,18 +19,29 @@
static int __init default_appraise_setup(char *str)
{
#ifdef CONFIG_IMA_APPRAISE_BOOTPARAM
if (arch_ima_get_secureboot()) {
pr_info("Secure boot enabled: ignoring ima_appraise=%s boot parameter option",
str);
return 1;
}
bool sb_state = arch_ima_get_secureboot();
int appraisal_state = ima_appraise;
if (strncmp(str, "off", 3) == 0)
ima_appraise = 0;
appraisal_state = 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)
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
return 1;
}
......
......@@ -829,6 +829,8 @@ static int ima_calc_boot_aggregate_tfm(char *digest, u16 alg_id,
/* now accumulate with current aggregate */
rc = crypto_shash_update(shash, d.digest,
crypto_shash_digestsize(tfm));
if (rc != 0)
return rc;
}
/*
* Extend cumulative digest over TPM registers 8-9, which contain
......
......@@ -51,18 +51,23 @@ static int __init hash_setup(char *str)
return 1;
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;
else if (strncmp(str, "md5", 3) == 0)
} else if (strncmp(str, "md5", 3) == 0) {
ima_hash_algo = HASH_ALGO_MD5;
else
} else {
pr_err("invalid hash algorithm \"%s\" for template \"%s\"",
str, IMA_TEMPLATE_IMA_NAME);
return 1;
}
goto out;
}
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;
}
ima_hash_algo = i;
out:
......@@ -532,6 +537,16 @@ int ima_file_hash(struct file *file, char *buf, size_t buf_size)
return -EOPNOTSUPP;
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) {
size_t copied_size;
......
......@@ -60,6 +60,11 @@ enum policy_types { ORIGINAL_TCB = 1, DEFAULT_TCB };
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 list_head list;
int action;
......@@ -79,7 +84,7 @@ struct ima_rule_entry {
int type; /* audit type */
} lsm[MAX_LSM_RULES];
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;
};
......@@ -207,10 +212,6 @@ static LIST_HEAD(ima_policy_rules);
static LIST_HEAD(ima_temp_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 __init default_measure_policy_setup(char *str)
......@@ -241,6 +242,8 @@ static int __init policy_setup(char *str)
ima_use_secure_boot = true;
else if (strcmp(p, "fail_securely") == 0)
ima_fail_unverifiable_sigs = true;
else
pr_err("policy \"%s\" not found", p);
}
return 1;
......@@ -254,6 +257,72 @@ static int __init default_appraise_policy_setup(char *str)
}
__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)
{
int i;
......@@ -275,7 +344,7 @@ static void ima_free_rule(struct ima_rule_entry *entry)
* the defined_templates list and cannot be freed here
*/
kfree(entry->fsname);
kfree(entry->keyrings);
ima_free_rule_opt_list(entry->keyrings);
ima_lsm_free_rule(entry);
kfree(entry);
}
......@@ -285,15 +354,14 @@ static struct ima_rule_entry *ima_lsm_copy_rule(struct ima_rule_entry *entry)
struct ima_rule_entry *nentry;
int i;
nentry = kmalloc(sizeof(*nentry), GFP_KERNEL);
if (!nentry)
return NULL;
/*
* Immutable elements are copied over as pointers and data; only
* 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));
for (i = 0; i < MAX_LSM_RULES; i++) {
......@@ -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,
const char *keyring, const struct cred *cred)
{
char *next_keyring, *keyrings_ptr;
bool matched = false;
size_t i;
if ((rule->flags & IMA_UID) && !rule->uid_op(cred->uid, rule->uid))
return false;
......@@ -407,15 +475,8 @@ static bool ima_match_keyring(struct ima_rule_entry *rule,
if (!keyring)
return false;
strcpy(ima_keyrings, rule->keyrings);
/*
* "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)) {
for (i = 0; i < rule->keyrings->count; i++) {
if (!strcmp(rule->keyrings->items[i], keyring)) {
matched = true;
break;
}
......@@ -1066,7 +1127,6 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
bool uid_token;
struct ima_template_desc *template_desc;
int result = 0;
size_t keyrings_len;
ab = integrity_audit_log_start(audit_context(), GFP_KERNEL,
AUDIT_INTEGRITY_POLICY_RULE);
......@@ -1175,7 +1235,8 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
entry->func = POLICY_CHECK;
else if (strcmp(args[0].from, "KEXEC_CMDLINE") == 0)
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;
else
result = -EINVAL;
......@@ -1232,37 +1293,19 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
case Opt_keyrings:
ima_log_string(ab, "keyrings", args[0].from);
keyrings_len = strlen(args[0].from) + 1;
if ((entry->keyrings) ||
(keyrings_len < 2)) {
if (!IS_ENABLED(CONFIG_IMA_MEASURE_ASYMMETRIC_KEYS) ||
entry->keyrings) {
result = -EINVAL;
break;
}
if (keyrings_len > ima_keyrings_len) {
char *tmpbuf;
tmpbuf = krealloc(ima_keyrings, keyrings_len,
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;
entry->keyrings = ima_alloc_rule_opt_list(args);
if (IS_ERR(entry->keyrings)) {
result = PTR_ERR(entry->keyrings);
entry->keyrings = NULL;
break;
}
result = 0;
entry->flags |= IMA_KEYRINGS;
break;
case Opt_fsuuid:
......@@ -1575,6 +1618,15 @@ static void policy_func_show(struct seq_file *m, enum ima_hooks 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)
{
struct ima_rule_entry *entry = v;
......@@ -1631,9 +1683,8 @@ int ima_policy_show(struct seq_file *m, void *v)
}
if (entry->flags & IMA_KEYRINGS) {
if (entry->keyrings != NULL)
snprintf(tbuf, sizeof(tbuf), "%s", entry->keyrings);
seq_printf(m, pt(Opt_keyrings), tbuf);
seq_puts(m, "keyrings=");
ima_show_rule_opt_list(m, entry->keyrings);
seq_puts(m, " ");
}
......
......@@ -133,7 +133,7 @@ unsigned long ima_get_binary_runtime_size(void)
return ULONG_MAX;
else
return binary_runtime_size + sizeof(struct ima_kexec_hdr);
};
}
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,
ab = audit_log_start(audit_context(), GFP_KERNEL, audit_msgno);
audit_log_format(ab, "pid=%d uid=%u auid=%u ses=%u",
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)),
audit_get_sessionid(current));
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