Commit 4cec9293 authored by Linus Torvalds's avatar Linus Torvalds

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

Pull integrity updates from Mimi Zohar:
 "The nicest change is the IMA policy rule checking. The other changes
  include allowing the kexec boot cmdline line measure policy rules to
  be defined in terms of the inode associated with the kexec kernel
  image, making the IMA_APPRAISE_BOOTPARAM, which governs the IMA
  appraise mode (log, fix, enforce), a runtime decision based on the
  secure boot mode of the system, and including errno in the audit log"

* tag 'integrity-v5.9' of git://git.kernel.org/pub/scm/linux/kernel/git/zohar/linux-integrity:
  integrity: remove redundant initialization of variable ret
  ima: move APPRAISE_BOOTPARAM dependency on ARCH_POLICY to runtime
  ima: AppArmor satisfies the audit rule requirements
  ima: Rename internal filter rule functions
  ima: Support additional conditionals in the KEXEC_CMDLINE hook function
  ima: Use the common function to detect LSM conditionals in a rule
  ima: Move comprehensive rule validation checks out of the token parser
  ima: Use correct type for the args_p member of ima_rule_entry.lsm elements
  ima: Shallow copy the args_p member of ima_rule_entry.lsm elements
  ima: Fail rule parsing when appraise_flag=blacklist is unsupportable
  ima: Fail rule parsing when the KEY_CHECK hook is combined with an invalid cond
  ima: Fail rule parsing when the KEXEC_CMDLINE hook is combined with an invalid cond
  ima: Fail rule parsing when buffer hook functions have an invalid action
  ima: Free the entire rule if it fails to parse
  ima: Free the entire rule when deleting a list of rules
  ima: Have the LSM free its audit rule
  IMA: Add audit log for failure conditions
  integrity: Add errno field in audit message
parents e3243e2a 3db0d0c2
...@@ -25,7 +25,7 @@ extern int ima_post_read_file(struct file *file, void *buf, loff_t size, ...@@ -25,7 +25,7 @@ extern int ima_post_read_file(struct file *file, void *buf, loff_t size,
enum kernel_read_file_id id); enum kernel_read_file_id id);
extern void ima_post_path_mknod(struct dentry *dentry); extern void ima_post_path_mknod(struct dentry *dentry);
extern int ima_file_hash(struct file *file, char *buf, size_t buf_size); extern int ima_file_hash(struct file *file, char *buf, size_t buf_size);
extern void ima_kexec_cmdline(const void *buf, int size); extern void ima_kexec_cmdline(int kernel_fd, const void *buf, int size);
#ifdef CONFIG_IMA_KEXEC #ifdef CONFIG_IMA_KEXEC
extern void ima_add_kexec_buffer(struct kimage *image); extern void ima_add_kexec_buffer(struct kimage *image);
...@@ -103,7 +103,7 @@ static inline int ima_file_hash(struct file *file, char *buf, size_t buf_size) ...@@ -103,7 +103,7 @@ static inline int ima_file_hash(struct file *file, char *buf, size_t buf_size)
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
static inline void ima_kexec_cmdline(const void *buf, int size) {} static inline void ima_kexec_cmdline(int kernel_fd, const void *buf, int size) {}
#endif /* CONFIG_IMA */ #endif /* CONFIG_IMA */
#ifndef CONFIG_IMA_KEXEC #ifndef CONFIG_IMA_KEXEC
......
...@@ -265,7 +265,7 @@ kimage_file_prepare_segments(struct kimage *image, int kernel_fd, int initrd_fd, ...@@ -265,7 +265,7 @@ kimage_file_prepare_segments(struct kimage *image, int kernel_fd, int initrd_fd,
goto out; goto out;
} }
ima_kexec_cmdline(image->cmdline_buf, ima_kexec_cmdline(kernel_fd, image->cmdline_buf,
image->cmdline_buf_len - 1); image->cmdline_buf_len - 1);
} }
......
...@@ -79,7 +79,7 @@ int asymmetric_verify(struct key *keyring, const char *sig, ...@@ -79,7 +79,7 @@ int asymmetric_verify(struct key *keyring, const char *sig,
struct public_key_signature pks; struct public_key_signature pks;
struct signature_v2_hdr *hdr = (struct signature_v2_hdr *)sig; struct signature_v2_hdr *hdr = (struct signature_v2_hdr *)sig;
struct key *key; struct key *key;
int ret = -ENOMEM; int ret;
if (siglen <= sizeof(*hdr)) if (siglen <= sizeof(*hdr))
return -EBADMSG; return -EBADMSG;
......
...@@ -54,7 +54,7 @@ config IMA_MEASURE_PCR_IDX ...@@ -54,7 +54,7 @@ config IMA_MEASURE_PCR_IDX
config IMA_LSM_RULES config IMA_LSM_RULES
bool bool
depends on IMA && AUDIT && (SECURITY_SELINUX || SECURITY_SMACK) depends on IMA && AUDIT && (SECURITY_SELINUX || SECURITY_SMACK || SECURITY_APPARMOR)
default y default y
help help
Disabling this option will disregard LSM based policy rules. Disabling this option will disregard LSM based policy rules.
...@@ -232,7 +232,7 @@ config IMA_APPRAISE_REQUIRE_POLICY_SIGS ...@@ -232,7 +232,7 @@ config IMA_APPRAISE_REQUIRE_POLICY_SIGS
config IMA_APPRAISE_BOOTPARAM config IMA_APPRAISE_BOOTPARAM
bool "ima_appraise boot parameter" bool "ima_appraise boot parameter"
depends on IMA_APPRAISE && !IMA_ARCH_POLICY depends on IMA_APPRAISE
default y default y
help help
This option enables the different "ima_appraise=" modes This option enables the different "ima_appraise=" modes
......
...@@ -186,27 +186,43 @@ static inline unsigned int ima_hash_key(u8 *digest) ...@@ -186,27 +186,43 @@ static inline unsigned int ima_hash_key(u8 *digest)
return (digest[0] | digest[1] << 8) % IMA_MEASURE_HTABLE_SIZE; return (digest[0] | digest[1] << 8) % IMA_MEASURE_HTABLE_SIZE;
} }
#define __ima_hooks(hook) \ #define __ima_hooks(hook) \
hook(NONE) \ hook(NONE, none) \
hook(FILE_CHECK) \ hook(FILE_CHECK, file) \
hook(MMAP_CHECK) \ hook(MMAP_CHECK, mmap) \
hook(BPRM_CHECK) \ hook(BPRM_CHECK, bprm) \
hook(CREDS_CHECK) \ hook(CREDS_CHECK, creds) \
hook(POST_SETATTR) \ hook(POST_SETATTR, post_setattr) \
hook(MODULE_CHECK) \ hook(MODULE_CHECK, module) \
hook(FIRMWARE_CHECK) \ hook(FIRMWARE_CHECK, firmware) \
hook(KEXEC_KERNEL_CHECK) \ hook(KEXEC_KERNEL_CHECK, kexec_kernel) \
hook(KEXEC_INITRAMFS_CHECK) \ hook(KEXEC_INITRAMFS_CHECK, kexec_initramfs) \
hook(POLICY_CHECK) \ hook(POLICY_CHECK, policy) \
hook(KEXEC_CMDLINE) \ hook(KEXEC_CMDLINE, kexec_cmdline) \
hook(KEY_CHECK) \ hook(KEY_CHECK, key) \
hook(MAX_CHECK) hook(MAX_CHECK, none)
#define __ima_hook_enumify(ENUM) ENUM,
#define __ima_hook_enumify(ENUM, str) ENUM,
#define __ima_stringify(arg) (#arg)
#define __ima_hook_measuring_stringify(ENUM, str) \
(__ima_stringify(measuring_ ##str)),
enum ima_hooks { enum ima_hooks {
__ima_hooks(__ima_hook_enumify) __ima_hooks(__ima_hook_enumify)
}; };
static const char * const ima_hooks_measure_str[] = {
__ima_hooks(__ima_hook_measuring_stringify)
};
static inline const char *func_measure_str(enum ima_hooks func)
{
if (func >= MAX_CHECK)
return ima_hooks_measure_str[NONE];
return ima_hooks_measure_str[func];
}
extern const char *const func_tokens[]; extern const char *const func_tokens[];
struct modsig; struct modsig;
...@@ -249,7 +265,7 @@ void ima_store_measurement(struct integrity_iint_cache *iint, struct file *file, ...@@ -249,7 +265,7 @@ void ima_store_measurement(struct integrity_iint_cache *iint, struct file *file,
struct evm_ima_xattr_data *xattr_value, struct evm_ima_xattr_data *xattr_value,
int xattr_len, const struct modsig *modsig, int pcr, int xattr_len, const struct modsig *modsig, int pcr,
struct ima_template_desc *template_desc); struct ima_template_desc *template_desc);
void process_buffer_measurement(const void *buf, int size, void process_buffer_measurement(struct inode *inode, const void *buf, int size,
const char *eventname, enum ima_hooks func, const char *eventname, enum ima_hooks func,
int pcr, const char *keyring); int pcr, const char *keyring);
void ima_audit_measurement(struct integrity_iint_cache *iint, void ima_audit_measurement(struct integrity_iint_cache *iint,
...@@ -356,7 +372,6 @@ static inline int ima_read_xattr(struct dentry *dentry, ...@@ -356,7 +372,6 @@ static inline int ima_read_xattr(struct dentry *dentry,
#endif /* CONFIG_IMA_APPRAISE */ #endif /* CONFIG_IMA_APPRAISE */
#ifdef CONFIG_IMA_APPRAISE_MODSIG #ifdef CONFIG_IMA_APPRAISE_MODSIG
bool ima_hook_supports_modsig(enum ima_hooks func);
int ima_read_modsig(enum ima_hooks func, const void *buf, loff_t buf_len, int ima_read_modsig(enum ima_hooks func, const void *buf, loff_t buf_len,
struct modsig **modsig); struct modsig **modsig);
void ima_collect_modsig(struct modsig *modsig, const void *buf, loff_t size); void ima_collect_modsig(struct modsig *modsig, const void *buf, loff_t size);
...@@ -366,11 +381,6 @@ int ima_get_raw_modsig(const struct modsig *modsig, const void **data, ...@@ -366,11 +381,6 @@ int ima_get_raw_modsig(const struct modsig *modsig, const void **data,
u32 *data_len); u32 *data_len);
void ima_free_modsig(struct modsig *modsig); void ima_free_modsig(struct modsig *modsig);
#else #else
static inline bool ima_hook_supports_modsig(enum ima_hooks func)
{
return false;
}
static inline int ima_read_modsig(enum ima_hooks func, const void *buf, static inline int ima_read_modsig(enum ima_hooks func, const void *buf,
loff_t buf_len, struct modsig **modsig) loff_t buf_len, struct modsig **modsig)
{ {
...@@ -403,19 +413,24 @@ static inline void ima_free_modsig(struct modsig *modsig) ...@@ -403,19 +413,24 @@ static inline void ima_free_modsig(struct modsig *modsig)
/* LSM based policy rules require audit */ /* LSM based policy rules require audit */
#ifdef CONFIG_IMA_LSM_RULES #ifdef CONFIG_IMA_LSM_RULES
#define security_filter_rule_init security_audit_rule_init #define ima_filter_rule_init security_audit_rule_init
#define security_filter_rule_match security_audit_rule_match #define ima_filter_rule_free security_audit_rule_free
#define ima_filter_rule_match security_audit_rule_match
#else #else
static inline int security_filter_rule_init(u32 field, u32 op, char *rulestr, static inline int ima_filter_rule_init(u32 field, u32 op, char *rulestr,
void **lsmrule) void **lsmrule)
{ {
return -EINVAL; return -EINVAL;
} }
static inline int security_filter_rule_match(u32 secid, u32 field, u32 op, static inline void ima_filter_rule_free(void *lsmrule)
void *lsmrule) {
}
static inline int ima_filter_rule_match(u32 secid, u32 field, u32 op,
void *lsmrule)
{ {
return -EINVAL; return -EINVAL;
} }
......
...@@ -162,7 +162,7 @@ void ima_add_violation(struct file *file, const unsigned char *filename, ...@@ -162,7 +162,7 @@ void ima_add_violation(struct file *file, const unsigned char *filename,
/** /**
* ima_get_action - appraise & measure decision based on policy. * ima_get_action - appraise & measure decision based on policy.
* @inode: pointer to inode to measure * @inode: pointer to the inode associated with the object being validated
* @cred: pointer to credentials structure to validate * @cred: pointer to credentials structure to validate
* @secid: secid of the task being validated * @secid: secid of the task being validated
* @mask: contains the permission mask (MAY_READ, MAY_WRITE, MAY_EXEC, * @mask: contains the permission mask (MAY_READ, MAY_WRITE, MAY_EXEC,
......
...@@ -19,6 +19,12 @@ ...@@ -19,6 +19,12 @@
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()) {
pr_info("Secure boot enabled: ignoring ima_appraise=%s boot parameter option",
str);
return 1;
}
if (strncmp(str, "off", 3) == 0) if (strncmp(str, "off", 3) == 0)
ima_appraise = 0; ima_appraise = 0;
else if (strncmp(str, "log", 3) == 0) else if (strncmp(str, "log", 3) == 0)
...@@ -328,7 +334,7 @@ int ima_check_blacklist(struct integrity_iint_cache *iint, ...@@ -328,7 +334,7 @@ int ima_check_blacklist(struct integrity_iint_cache *iint,
rc = is_binary_blacklisted(digest, digestsize); rc = is_binary_blacklisted(digest, digestsize);
if ((rc == -EPERM) && (iint->flags & IMA_MEASURE)) if ((rc == -EPERM) && (iint->flags & IMA_MEASURE))
process_buffer_measurement(digest, digestsize, process_buffer_measurement(NULL, digest, digestsize,
"blacklisted-hash", NONE, "blacklisted-hash", NONE,
pcr, NULL); pcr, NULL);
} }
......
...@@ -58,7 +58,7 @@ void ima_post_key_create_or_update(struct key *keyring, struct key *key, ...@@ -58,7 +58,7 @@ void ima_post_key_create_or_update(struct key *keyring, struct key *key,
* if the IMA policy is configured to measure a key linked * if the IMA policy is configured to measure a key linked
* to the given keyring. * to the given keyring.
*/ */
process_buffer_measurement(payload, payload_len, process_buffer_measurement(NULL, payload, payload_len,
keyring->description, KEY_CHECK, 0, keyring->description, KEY_CHECK, 0,
keyring->description); keyring->description);
} }
...@@ -726,6 +726,7 @@ int ima_load_data(enum kernel_load_data_id id) ...@@ -726,6 +726,7 @@ int ima_load_data(enum kernel_load_data_id id)
/* /*
* process_buffer_measurement - Measure the buffer to ima log. * process_buffer_measurement - Measure the buffer to ima log.
* @inode: inode associated with the object being measured (NULL for KEY_CHECK)
* @buf: pointer to the buffer that needs to be added to the log. * @buf: pointer to the buffer that needs to be added to the log.
* @size: size of buffer(in bytes). * @size: size of buffer(in bytes).
* @eventname: event name to be used for the buffer entry. * @eventname: event name to be used for the buffer entry.
...@@ -735,11 +736,12 @@ int ima_load_data(enum kernel_load_data_id id) ...@@ -735,11 +736,12 @@ int ima_load_data(enum kernel_load_data_id id)
* *
* Based on policy, the buffer is measured into the ima log. * Based on policy, the buffer is measured into the ima log.
*/ */
void process_buffer_measurement(const void *buf, int size, void process_buffer_measurement(struct inode *inode, const void *buf, int size,
const char *eventname, enum ima_hooks func, const char *eventname, enum ima_hooks func,
int pcr, const char *keyring) int pcr, const char *keyring)
{ {
int ret = 0; int ret = 0;
const char *audit_cause = "ENOMEM";
struct ima_template_entry *entry = NULL; struct ima_template_entry *entry = NULL;
struct integrity_iint_cache iint = {}; struct integrity_iint_cache iint = {};
struct ima_event_data event_data = {.iint = &iint, struct ima_event_data event_data = {.iint = &iint,
...@@ -767,7 +769,7 @@ void process_buffer_measurement(const void *buf, int size, ...@@ -767,7 +769,7 @@ void process_buffer_measurement(const void *buf, int size,
*/ */
if (func) { if (func) {
security_task_getsecid(current, &secid); security_task_getsecid(current, &secid);
action = ima_get_action(NULL, current_cred(), secid, 0, func, action = ima_get_action(inode, current_cred(), secid, 0, func,
&pcr, &template, keyring); &pcr, &template, keyring);
if (!(action & IMA_MEASURE)) if (!(action & IMA_MEASURE))
return; return;
...@@ -794,37 +796,54 @@ void process_buffer_measurement(const void *buf, int size, ...@@ -794,37 +796,54 @@ void process_buffer_measurement(const void *buf, int size,
iint.ima_hash->length = hash_digest_size[ima_hash_algo]; iint.ima_hash->length = hash_digest_size[ima_hash_algo];
ret = ima_calc_buffer_hash(buf, size, iint.ima_hash); ret = ima_calc_buffer_hash(buf, size, iint.ima_hash);
if (ret < 0) if (ret < 0) {
audit_cause = "hashing_error";
goto out; goto out;
}
ret = ima_alloc_init_template(&event_data, &entry, template); ret = ima_alloc_init_template(&event_data, &entry, template);
if (ret < 0) if (ret < 0) {
audit_cause = "alloc_entry";
goto out; goto out;
}
ret = ima_store_template(entry, violation, NULL, buf, pcr); ret = ima_store_template(entry, violation, NULL, buf, pcr);
if (ret < 0) {
if (ret < 0) audit_cause = "store_entry";
ima_free_template_entry(entry); ima_free_template_entry(entry);
}
out: out:
if (ret < 0) if (ret < 0)
pr_devel("%s: failed, result: %d\n", __func__, ret); integrity_audit_message(AUDIT_INTEGRITY_PCR, NULL, eventname,
func_measure_str(func),
audit_cause, ret, 0, ret);
return; return;
} }
/** /**
* ima_kexec_cmdline - measure kexec cmdline boot args * ima_kexec_cmdline - measure kexec cmdline boot args
* @kernel_fd: file descriptor of the kexec kernel being loaded
* @buf: pointer to buffer * @buf: pointer to buffer
* @size: size of buffer * @size: size of buffer
* *
* Buffers can only be measured, not appraised. * Buffers can only be measured, not appraised.
*/ */
void ima_kexec_cmdline(const void *buf, int size) void ima_kexec_cmdline(int kernel_fd, const void *buf, int size)
{ {
if (buf && size != 0) struct fd f;
process_buffer_measurement(buf, size, "kexec-cmdline",
KEXEC_CMDLINE, 0, NULL); if (!buf || !size)
return;
f = fdget(kernel_fd);
if (!f.file)
return;
process_buffer_measurement(file_inode(f.file), buf, size,
"kexec-cmdline", KEXEC_CMDLINE, 0, NULL);
fdput(f);
} }
static int __init init_ima(void) static int __init init_ima(void)
......
...@@ -32,26 +32,6 @@ struct modsig { ...@@ -32,26 +32,6 @@ struct modsig {
u8 raw_pkcs7[]; u8 raw_pkcs7[];
}; };
/**
* ima_hook_supports_modsig - can the policy allow modsig for this hook?
*
* modsig is only supported by hooks using ima_post_read_file(), because only
* they preload the contents of the file in a buffer. FILE_CHECK does that in
* some cases, but not when reached from vfs_open(). POLICY_CHECK can support
* it, but it's not useful in practice because it's a text file so deny.
*/
bool ima_hook_supports_modsig(enum ima_hooks func)
{
switch (func) {
case KEXEC_KERNEL_CHECK:
case KEXEC_INITRAMFS_CHECK:
case MODULE_CHECK:
return true;
default:
return false;
}
}
/* /*
* ima_read_modsig - Read modsig from buf. * ima_read_modsig - Read modsig from buf.
* *
......
...@@ -74,7 +74,7 @@ struct ima_rule_entry { ...@@ -74,7 +74,7 @@ struct ima_rule_entry {
int pcr; int pcr;
struct { struct {
void *rule; /* LSM file metadata specific */ void *rule; /* LSM file metadata specific */
void *args_p; /* audit value */ char *args_p; /* audit value */
int type; /* audit type */ int type; /* audit type */
} lsm[MAX_LSM_RULES]; } lsm[MAX_LSM_RULES];
char *fsname; char *fsname;
...@@ -258,9 +258,24 @@ static void ima_lsm_free_rule(struct ima_rule_entry *entry) ...@@ -258,9 +258,24 @@ static void ima_lsm_free_rule(struct ima_rule_entry *entry)
int i; int i;
for (i = 0; i < MAX_LSM_RULES; i++) { for (i = 0; i < MAX_LSM_RULES; i++) {
kfree(entry->lsm[i].rule); ima_filter_rule_free(entry->lsm[i].rule);
kfree(entry->lsm[i].args_p); kfree(entry->lsm[i].args_p);
} }
}
static void ima_free_rule(struct ima_rule_entry *entry)
{
if (!entry)
return;
/*
* entry->template->fields may be allocated in ima_parse_rule() but that
* reference is owned by the corresponding ima_template_desc element in
* the defined_templates list and cannot be freed here
*/
kfree(entry->fsname);
kfree(entry->keyrings);
ima_lsm_free_rule(entry);
kfree(entry); kfree(entry);
} }
...@@ -285,24 +300,22 @@ static struct ima_rule_entry *ima_lsm_copy_rule(struct ima_rule_entry *entry) ...@@ -285,24 +300,22 @@ static struct ima_rule_entry *ima_lsm_copy_rule(struct ima_rule_entry *entry)
continue; continue;
nentry->lsm[i].type = entry->lsm[i].type; nentry->lsm[i].type = entry->lsm[i].type;
nentry->lsm[i].args_p = kstrdup(entry->lsm[i].args_p, nentry->lsm[i].args_p = entry->lsm[i].args_p;
GFP_KERNEL); /*
if (!nentry->lsm[i].args_p) * Remove the reference from entry so that the associated
goto out_err; * memory will not be freed during a later call to
* ima_lsm_free_rule(entry).
security_filter_rule_init(nentry->lsm[i].type, */
Audit_equal, entry->lsm[i].args_p = NULL;
nentry->lsm[i].args_p,
&nentry->lsm[i].rule); ima_filter_rule_init(nentry->lsm[i].type, Audit_equal,
nentry->lsm[i].args_p,
&nentry->lsm[i].rule);
if (!nentry->lsm[i].rule) if (!nentry->lsm[i].rule)
pr_warn("rule for LSM \'%s\' is undefined\n", pr_warn("rule for LSM \'%s\' is undefined\n",
(char *)entry->lsm[i].args_p); nentry->lsm[i].args_p);
} }
return nentry; return nentry;
out_err:
ima_lsm_free_rule(nentry);
return NULL;
} }
static int ima_lsm_update_rule(struct ima_rule_entry *entry) static int ima_lsm_update_rule(struct ima_rule_entry *entry)
...@@ -315,11 +328,29 @@ static int ima_lsm_update_rule(struct ima_rule_entry *entry) ...@@ -315,11 +328,29 @@ static int ima_lsm_update_rule(struct ima_rule_entry *entry)
list_replace_rcu(&entry->list, &nentry->list); list_replace_rcu(&entry->list, &nentry->list);
synchronize_rcu(); synchronize_rcu();
/*
* ima_lsm_copy_rule() shallow copied all references, except for the
* LSM references, from entry to nentry so we only want to free the LSM
* references and the entry itself. All other memory refrences will now
* be owned by nentry.
*/
ima_lsm_free_rule(entry); ima_lsm_free_rule(entry);
kfree(entry);
return 0; return 0;
} }
static bool ima_rule_contains_lsm_cond(struct ima_rule_entry *entry)
{
int i;
for (i = 0; i < MAX_LSM_RULES; i++)
if (entry->lsm[i].args_p)
return true;
return false;
}
/* /*
* The LSM policy can be reloaded, leaving the IMA LSM based rules referring * The LSM policy can be reloaded, leaving the IMA LSM based rules referring
* to the old, stale LSM policy. Update the IMA LSM based rules to reflect * to the old, stale LSM policy. Update the IMA LSM based rules to reflect
...@@ -328,17 +359,10 @@ static int ima_lsm_update_rule(struct ima_rule_entry *entry) ...@@ -328,17 +359,10 @@ static int ima_lsm_update_rule(struct ima_rule_entry *entry)
static void ima_lsm_update_rules(void) static void ima_lsm_update_rules(void)
{ {
struct ima_rule_entry *entry, *e; struct ima_rule_entry *entry, *e;
int i, result, needs_update; int result;
list_for_each_entry_safe(entry, e, &ima_policy_rules, list) { list_for_each_entry_safe(entry, e, &ima_policy_rules, list) {
needs_update = 0; if (!ima_rule_contains_lsm_cond(entry))
for (i = 0; i < MAX_LSM_RULES; i++) {
if (entry->lsm[i].args_p) {
needs_update = 1;
break;
}
}
if (!needs_update)
continue; continue;
result = ima_lsm_update_rule(entry); result = ima_lsm_update_rule(entry);
...@@ -418,13 +442,9 @@ static bool ima_match_rules(struct ima_rule_entry *rule, struct inode *inode, ...@@ -418,13 +442,9 @@ static bool ima_match_rules(struct ima_rule_entry *rule, struct inode *inode,
{ {
int i; int i;
if ((func == KEXEC_CMDLINE) || (func == KEY_CHECK)) { if (func == KEY_CHECK) {
if ((rule->flags & IMA_FUNC) && (rule->func == func)) { return (rule->flags & IMA_FUNC) && (rule->func == func) &&
if (func == KEY_CHECK) ima_match_keyring(rule, keyring, cred);
return ima_match_keyring(rule, keyring, cred);
return true;
}
return false;
} }
if ((rule->flags & IMA_FUNC) && if ((rule->flags & IMA_FUNC) &&
(rule->func != func && func != POST_SETATTR)) (rule->func != func && func != POST_SETATTR))
...@@ -474,18 +494,16 @@ static bool ima_match_rules(struct ima_rule_entry *rule, struct inode *inode, ...@@ -474,18 +494,16 @@ static bool ima_match_rules(struct ima_rule_entry *rule, struct inode *inode,
case LSM_OBJ_ROLE: case LSM_OBJ_ROLE:
case LSM_OBJ_TYPE: case LSM_OBJ_TYPE:
security_inode_getsecid(inode, &osid); security_inode_getsecid(inode, &osid);
rc = security_filter_rule_match(osid, rc = ima_filter_rule_match(osid, rule->lsm[i].type,
rule->lsm[i].type, Audit_equal,
Audit_equal, rule->lsm[i].rule);
rule->lsm[i].rule);
break; break;
case LSM_SUBJ_USER: case LSM_SUBJ_USER:
case LSM_SUBJ_ROLE: case LSM_SUBJ_ROLE:
case LSM_SUBJ_TYPE: case LSM_SUBJ_TYPE:
rc = security_filter_rule_match(secid, rc = ima_filter_rule_match(secid, rule->lsm[i].type,
rule->lsm[i].type, Audit_equal,
Audit_equal, rule->lsm[i].rule);
rule->lsm[i].rule);
default: default:
break; break;
} }
...@@ -880,16 +898,16 @@ static int ima_lsm_rule_init(struct ima_rule_entry *entry, ...@@ -880,16 +898,16 @@ static int ima_lsm_rule_init(struct ima_rule_entry *entry,
return -ENOMEM; return -ENOMEM;
entry->lsm[lsm_rule].type = audit_type; entry->lsm[lsm_rule].type = audit_type;
result = security_filter_rule_init(entry->lsm[lsm_rule].type, result = ima_filter_rule_init(entry->lsm[lsm_rule].type, Audit_equal,
Audit_equal, entry->lsm[lsm_rule].args_p,
entry->lsm[lsm_rule].args_p, &entry->lsm[lsm_rule].rule);
&entry->lsm[lsm_rule].rule);
if (!entry->lsm[lsm_rule].rule) { if (!entry->lsm[lsm_rule].rule) {
pr_warn("rule for LSM \'%s\' is undefined\n", pr_warn("rule for LSM \'%s\' is undefined\n",
(char *)entry->lsm[lsm_rule].args_p); entry->lsm[lsm_rule].args_p);
if (ima_rules == &ima_default_rules) { if (ima_rules == &ima_default_rules) {
kfree(entry->lsm[lsm_rule].args_p); kfree(entry->lsm[lsm_rule].args_p);
entry->lsm[lsm_rule].args_p = NULL;
result = -EINVAL; result = -EINVAL;
} else } else
result = 0; result = 0;
...@@ -949,6 +967,96 @@ static void check_template_modsig(const struct ima_template_desc *template) ...@@ -949,6 +967,96 @@ static void check_template_modsig(const struct ima_template_desc *template)
#undef MSG #undef MSG
} }
static bool ima_validate_rule(struct ima_rule_entry *entry)
{
/* Ensure that the action is set and is compatible with the flags */
if (entry->action == UNKNOWN)
return false;
if (entry->action != MEASURE && entry->flags & IMA_PCR)
return false;
if (entry->action != APPRAISE &&
entry->flags & (IMA_DIGSIG_REQUIRED | IMA_MODSIG_ALLOWED | IMA_CHECK_BLACKLIST))
return false;
/*
* The IMA_FUNC bit must be set if and only if there's a valid hook
* function specified, and vice versa. Enforcing this property allows
* for the NONE case below to validate a rule without an explicit hook
* function.
*/
if (((entry->flags & IMA_FUNC) && entry->func == NONE) ||
(!(entry->flags & IMA_FUNC) && entry->func != NONE))
return false;
/*
* Ensure that the hook function is compatible with the other
* components of the rule
*/
switch (entry->func) {
case NONE:
case FILE_CHECK:
case MMAP_CHECK:
case BPRM_CHECK:
case CREDS_CHECK:
case POST_SETATTR:
case FIRMWARE_CHECK:
case POLICY_CHECK:
if (entry->flags & ~(IMA_FUNC | IMA_MASK | IMA_FSMAGIC |
IMA_UID | IMA_FOWNER | IMA_FSUUID |
IMA_INMASK | IMA_EUID | IMA_PCR |
IMA_FSNAME | IMA_DIGSIG_REQUIRED |
IMA_PERMIT_DIRECTIO))
return false;
break;
case MODULE_CHECK:
case KEXEC_KERNEL_CHECK:
case KEXEC_INITRAMFS_CHECK:
if (entry->flags & ~(IMA_FUNC | IMA_MASK | IMA_FSMAGIC |
IMA_UID | IMA_FOWNER | IMA_FSUUID |
IMA_INMASK | IMA_EUID | IMA_PCR |
IMA_FSNAME | IMA_DIGSIG_REQUIRED |
IMA_PERMIT_DIRECTIO | IMA_MODSIG_ALLOWED |
IMA_CHECK_BLACKLIST))
return false;
break;
case KEXEC_CMDLINE:
if (entry->action & ~(MEASURE | DONT_MEASURE))
return false;
if (entry->flags & ~(IMA_FUNC | IMA_FSMAGIC | IMA_UID |
IMA_FOWNER | IMA_FSUUID | IMA_EUID |
IMA_PCR | IMA_FSNAME))
return false;
break;
case KEY_CHECK:
if (entry->action & ~(MEASURE | DONT_MEASURE))
return false;
if (entry->flags & ~(IMA_FUNC | IMA_UID | IMA_PCR |
IMA_KEYRINGS))
return false;
if (ima_rule_contains_lsm_cond(entry))
return false;
break;
default:
return false;
}
/* Ensure that combinations of flags are compatible with each other */
if (entry->flags & IMA_CHECK_BLACKLIST &&
!(entry->flags & IMA_MODSIG_ALLOWED))
return false;
return true;
}
static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
{ {
struct audit_buffer *ab; struct audit_buffer *ab;
...@@ -1126,8 +1234,6 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) ...@@ -1126,8 +1234,6 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
keyrings_len = strlen(args[0].from) + 1; keyrings_len = strlen(args[0].from) + 1;
if ((entry->keyrings) || if ((entry->keyrings) ||
(entry->action != MEASURE) ||
(entry->func != KEY_CHECK) ||
(keyrings_len < 2)) { (keyrings_len < 2)) {
result = -EINVAL; result = -EINVAL;
break; break;
...@@ -1267,15 +1373,10 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) ...@@ -1267,15 +1373,10 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
AUDIT_SUBJ_TYPE); AUDIT_SUBJ_TYPE);
break; break;
case Opt_appraise_type: case Opt_appraise_type:
if (entry->action != APPRAISE) {
result = -EINVAL;
break;
}
ima_log_string(ab, "appraise_type", args[0].from); ima_log_string(ab, "appraise_type", args[0].from);
if ((strcmp(args[0].from, "imasig")) == 0) if ((strcmp(args[0].from, "imasig")) == 0)
entry->flags |= IMA_DIGSIG_REQUIRED; entry->flags |= IMA_DIGSIG_REQUIRED;
else if (ima_hook_supports_modsig(entry->func) && else if (IS_ENABLED(CONFIG_IMA_APPRAISE_MODSIG) &&
strcmp(args[0].from, "imasig|modsig") == 0) strcmp(args[0].from, "imasig|modsig") == 0)
entry->flags |= IMA_DIGSIG_REQUIRED | entry->flags |= IMA_DIGSIG_REQUIRED |
IMA_MODSIG_ALLOWED; IMA_MODSIG_ALLOWED;
...@@ -1284,17 +1385,16 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) ...@@ -1284,17 +1385,16 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
break; break;
case Opt_appraise_flag: case Opt_appraise_flag:
ima_log_string(ab, "appraise_flag", args[0].from); ima_log_string(ab, "appraise_flag", args[0].from);
if (strstr(args[0].from, "blacklist")) if (IS_ENABLED(CONFIG_IMA_APPRAISE_MODSIG) &&
strstr(args[0].from, "blacklist"))
entry->flags |= IMA_CHECK_BLACKLIST; entry->flags |= IMA_CHECK_BLACKLIST;
else
result = -EINVAL;
break; break;
case Opt_permit_directio: case Opt_permit_directio:
entry->flags |= IMA_PERMIT_DIRECTIO; entry->flags |= IMA_PERMIT_DIRECTIO;
break; break;
case Opt_pcr: case Opt_pcr:
if (entry->action != MEASURE) {
result = -EINVAL;
break;
}
ima_log_string(ab, "pcr", args[0].from); ima_log_string(ab, "pcr", args[0].from);
result = kstrtoint(args[0].from, 10, &entry->pcr); result = kstrtoint(args[0].from, 10, &entry->pcr);
...@@ -1332,7 +1432,7 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) ...@@ -1332,7 +1432,7 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
break; break;
} }
} }
if (!result && (entry->action == UNKNOWN)) if (!result && !ima_validate_rule(entry))
result = -EINVAL; result = -EINVAL;
else if (entry->action == APPRAISE) else if (entry->action == APPRAISE)
temp_ima_appraise |= ima_appraise_flag(entry->func); temp_ima_appraise |= ima_appraise_flag(entry->func);
...@@ -1381,7 +1481,7 @@ ssize_t ima_parse_add_rule(char *rule) ...@@ -1381,7 +1481,7 @@ ssize_t ima_parse_add_rule(char *rule)
result = ima_parse_rule(p, entry); result = ima_parse_rule(p, entry);
if (result) { if (result) {
kfree(entry); ima_free_rule(entry);
integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL,
NULL, op, "invalid-policy", result, NULL, op, "invalid-policy", result,
audit_info); audit_info);
...@@ -1402,19 +1502,15 @@ ssize_t ima_parse_add_rule(char *rule) ...@@ -1402,19 +1502,15 @@ ssize_t ima_parse_add_rule(char *rule)
void ima_delete_rules(void) void ima_delete_rules(void)
{ {
struct ima_rule_entry *entry, *tmp; struct ima_rule_entry *entry, *tmp;
int i;
temp_ima_appraise = 0; temp_ima_appraise = 0;
list_for_each_entry_safe(entry, tmp, &ima_temp_rules, list) { list_for_each_entry_safe(entry, tmp, &ima_temp_rules, list) {
for (i = 0; i < MAX_LSM_RULES; i++)
kfree(entry->lsm[i].args_p);
list_del(&entry->list); list_del(&entry->list);
kfree(entry); ima_free_rule(entry);
} }
} }
#define __ima_hook_stringify(str) (#str), #define __ima_hook_stringify(func, str) (#func),
const char *const func_tokens[] = { const char *const func_tokens[] = {
__ima_hooks(__ima_hook_stringify) __ima_hooks(__ima_hook_stringify)
...@@ -1589,27 +1685,27 @@ int ima_policy_show(struct seq_file *m, void *v) ...@@ -1589,27 +1685,27 @@ int ima_policy_show(struct seq_file *m, void *v)
switch (i) { switch (i) {
case LSM_OBJ_USER: case LSM_OBJ_USER:
seq_printf(m, pt(Opt_obj_user), seq_printf(m, pt(Opt_obj_user),
(char *)entry->lsm[i].args_p); entry->lsm[i].args_p);
break; break;
case LSM_OBJ_ROLE: case LSM_OBJ_ROLE:
seq_printf(m, pt(Opt_obj_role), seq_printf(m, pt(Opt_obj_role),
(char *)entry->lsm[i].args_p); entry->lsm[i].args_p);
break; break;
case LSM_OBJ_TYPE: case LSM_OBJ_TYPE:
seq_printf(m, pt(Opt_obj_type), seq_printf(m, pt(Opt_obj_type),
(char *)entry->lsm[i].args_p); entry->lsm[i].args_p);
break; break;
case LSM_SUBJ_USER: case LSM_SUBJ_USER:
seq_printf(m, pt(Opt_subj_user), seq_printf(m, pt(Opt_subj_user),
(char *)entry->lsm[i].args_p); entry->lsm[i].args_p);
break; break;
case LSM_SUBJ_ROLE: case LSM_SUBJ_ROLE:
seq_printf(m, pt(Opt_subj_role), seq_printf(m, pt(Opt_subj_role),
(char *)entry->lsm[i].args_p); entry->lsm[i].args_p);
break; break;
case LSM_SUBJ_TYPE: case LSM_SUBJ_TYPE:
seq_printf(m, pt(Opt_subj_type), seq_printf(m, pt(Opt_subj_type),
(char *)entry->lsm[i].args_p); entry->lsm[i].args_p);
break; break;
} }
seq_puts(m, " "); seq_puts(m, " ");
......
...@@ -68,6 +68,7 @@ static struct ima_key_entry *ima_alloc_key_entry(struct key *keyring, ...@@ -68,6 +68,7 @@ static struct ima_key_entry *ima_alloc_key_entry(struct key *keyring,
size_t payload_len) size_t payload_len)
{ {
int rc = 0; int rc = 0;
const char *audit_cause = "ENOMEM";
struct ima_key_entry *entry; struct ima_key_entry *entry;
entry = kzalloc(sizeof(*entry), GFP_KERNEL); entry = kzalloc(sizeof(*entry), GFP_KERNEL);
...@@ -88,6 +89,10 @@ static struct ima_key_entry *ima_alloc_key_entry(struct key *keyring, ...@@ -88,6 +89,10 @@ static struct ima_key_entry *ima_alloc_key_entry(struct key *keyring,
out: out:
if (rc) { if (rc) {
integrity_audit_message(AUDIT_INTEGRITY_PCR, NULL,
keyring->description,
func_measure_str(KEY_CHECK),
audit_cause, rc, 0, rc);
ima_free_key_entry(entry); ima_free_key_entry(entry);
entry = NULL; entry = NULL;
} }
...@@ -153,7 +158,7 @@ void ima_process_queued_keys(void) ...@@ -153,7 +158,7 @@ void ima_process_queued_keys(void)
list_for_each_entry_safe(entry, tmp, &ima_keys, list) { list_for_each_entry_safe(entry, tmp, &ima_keys, list) {
if (!timer_expired) if (!timer_expired)
process_buffer_measurement(entry->payload, process_buffer_measurement(NULL, entry->payload,
entry->payload_len, entry->payload_len,
entry->keyring_name, entry->keyring_name,
KEY_CHECK, 0, KEY_CHECK, 0,
......
...@@ -239,6 +239,11 @@ void integrity_audit_msg(int audit_msgno, struct inode *inode, ...@@ -239,6 +239,11 @@ void integrity_audit_msg(int audit_msgno, struct inode *inode,
const unsigned char *fname, const char *op, const unsigned char *fname, const char *op,
const char *cause, int result, int info); const char *cause, int result, int info);
void integrity_audit_message(int audit_msgno, struct inode *inode,
const unsigned char *fname, const char *op,
const char *cause, int result, int info,
int errno);
static inline struct audit_buffer * static inline struct audit_buffer *
integrity_audit_log_start(struct audit_context *ctx, gfp_t gfp_mask, int type) integrity_audit_log_start(struct audit_context *ctx, gfp_t gfp_mask, int type)
{ {
...@@ -253,6 +258,14 @@ static inline void integrity_audit_msg(int audit_msgno, struct inode *inode, ...@@ -253,6 +258,14 @@ static inline void integrity_audit_msg(int audit_msgno, struct inode *inode,
{ {
} }
static inline void integrity_audit_message(int audit_msgno,
struct inode *inode,
const unsigned char *fname,
const char *op, const char *cause,
int result, int info, int errno)
{
}
static inline struct audit_buffer * static inline struct audit_buffer *
integrity_audit_log_start(struct audit_context *ctx, gfp_t gfp_mask, int type) integrity_audit_log_start(struct audit_context *ctx, gfp_t gfp_mask, int type)
{ {
......
...@@ -28,6 +28,15 @@ __setup("integrity_audit=", integrity_audit_setup); ...@@ -28,6 +28,15 @@ __setup("integrity_audit=", integrity_audit_setup);
void integrity_audit_msg(int audit_msgno, struct inode *inode, void integrity_audit_msg(int audit_msgno, struct inode *inode,
const unsigned char *fname, const char *op, const unsigned char *fname, const char *op,
const char *cause, int result, int audit_info) const char *cause, int result, int audit_info)
{
integrity_audit_message(audit_msgno, inode, fname, op, cause,
result, audit_info, 0);
}
void integrity_audit_message(int audit_msgno, struct inode *inode,
const unsigned char *fname, const char *op,
const char *cause, int result, int audit_info,
int errno)
{ {
struct audit_buffer *ab; struct audit_buffer *ab;
char name[TASK_COMM_LEN]; char name[TASK_COMM_LEN];
...@@ -53,6 +62,6 @@ void integrity_audit_msg(int audit_msgno, struct inode *inode, ...@@ -53,6 +62,6 @@ void integrity_audit_msg(int audit_msgno, struct inode *inode,
audit_log_untrustedstring(ab, inode->i_sb->s_id); audit_log_untrustedstring(ab, inode->i_sb->s_id);
audit_log_format(ab, " ino=%lu", inode->i_ino); audit_log_format(ab, " ino=%lu", inode->i_ino);
} }
audit_log_format(ab, " res=%d", !result); audit_log_format(ab, " res=%d errno=%d", !result, errno);
audit_log_end(ab); audit_log_end(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