Merge branch 'next-integrity' of git://git.kernel.org/pub/scm/linux/kernel/git/zohar/linux-integrity
Pull IMA updates from Mimi Zohar: "Two new features - measuring certificates and querying IMA for a file hash - and three bug fixes: - Measuring certificates is like the rest of IMA, based on policy, but requires loading a custom policy. Certificates loaded onto a keyring, for example during early boot, before a custom policy has been loaded, are queued and only processed after loading the custom policy. - IMA calculates and caches files hashes. Other kernel subsystems, and possibly kernel modules, are interested in accessing these cached file hashes. The bug fixes prevent classifying a file short read (e.g. shutdown) as an invalid file signature, add a missing blank when displaying the securityfs policy rules containing LSM labels, and, lastly, fix the handling of the IMA policy information for unknown LSM labels" * 'next-integrity' of git://git.kernel.org/pub/scm/linux/kernel/git/zohar/linux-integrity: IMA: Defined delayed workqueue to free the queued keys IMA: Call workqueue functions to measure queued keys IMA: Define workqueue for early boot key measurements IMA: pre-allocate buffer to hold keyrings string ima: ima/lsm policy rule loading logic bug fixes ima: add the ability to query the cached hash of a given file ima: Add a space after printing LSM rules for readability IMA: fix measuring asymmetric keys Kconfig IMA: Read keyrings= option from the IMA policy IMA: Add support to limit measuring keys KEYS: Call the IMA hook to measure keys IMA: Define an IMA hook to measure keys IMA: Add KEY_CHECK func to measure keys IMA: Check IMA policy flag ima: avoid appraise error for hash calc interrupt
This commit is contained in:
commit
73a0bff205
|
@ -25,11 +25,11 @@ Description:
|
|||
lsm: [[subj_user=] [subj_role=] [subj_type=]
|
||||
[obj_user=] [obj_role=] [obj_type=]]
|
||||
option: [[appraise_type=]] [template=] [permit_directio]
|
||||
[appraise_flag=]
|
||||
[appraise_flag=] [keyrings=]
|
||||
base: func:= [BPRM_CHECK][MMAP_CHECK][CREDS_CHECK][FILE_CHECK][MODULE_CHECK]
|
||||
[FIRMWARE_CHECK]
|
||||
[KEXEC_KERNEL_CHECK] [KEXEC_INITRAMFS_CHECK]
|
||||
[KEXEC_CMDLINE]
|
||||
[KEXEC_CMDLINE] [KEY_CHECK]
|
||||
mask:= [[^]MAY_READ] [[^]MAY_WRITE] [[^]MAY_APPEND]
|
||||
[[^]MAY_EXEC]
|
||||
fsmagic:= hex value
|
||||
|
@ -42,6 +42,9 @@ Description:
|
|||
appraise_flag:= [check_blacklist]
|
||||
Currently, blacklist check is only for files signed with appended
|
||||
signature.
|
||||
keyrings:= list of keyrings
|
||||
(eg, .builtin_trusted_keys|.ima). Only valid
|
||||
when action is "measure" and func is KEY_CHECK.
|
||||
template:= name of a defined IMA template type
|
||||
(eg, ima-ng). Only valid when action is "measure".
|
||||
pcr:= decimal value
|
||||
|
@ -113,3 +116,12 @@ Description:
|
|||
Example of appraise rule allowing modsig appended signatures:
|
||||
|
||||
appraise func=KEXEC_KERNEL_CHECK appraise_type=imasig|modsig
|
||||
|
||||
Example of measure rule using KEY_CHECK to measure all keys:
|
||||
|
||||
measure func=KEY_CHECK
|
||||
|
||||
Example of measure rule using KEY_CHECK to only measure
|
||||
keys added to .builtin_trusted_keys or .ima keyring:
|
||||
|
||||
measure func=KEY_CHECK keyrings=.builtin_trusted_keys|.ima
|
||||
|
|
|
@ -23,6 +23,7 @@ extern int ima_read_file(struct file *file, enum kernel_read_file_id id);
|
|||
extern int ima_post_read_file(struct file *file, void *buf, loff_t size,
|
||||
enum kernel_read_file_id id);
|
||||
extern void ima_post_path_mknod(struct dentry *dentry);
|
||||
extern int ima_file_hash(struct file *file, char *buf, size_t buf_size);
|
||||
extern void ima_kexec_cmdline(const void *buf, int size);
|
||||
|
||||
#ifdef CONFIG_IMA_KEXEC
|
||||
|
@ -91,6 +92,11 @@ static inline void ima_post_path_mknod(struct dentry *dentry)
|
|||
return;
|
||||
}
|
||||
|
||||
static inline int ima_file_hash(struct file *file, char *buf, size_t buf_size)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline void ima_kexec_cmdline(const void *buf, int size) {}
|
||||
#endif /* CONFIG_IMA */
|
||||
|
||||
|
@ -101,6 +107,20 @@ static inline void ima_add_kexec_buffer(struct kimage *image)
|
|||
{}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_IMA_MEASURE_ASYMMETRIC_KEYS
|
||||
extern void ima_post_key_create_or_update(struct key *keyring,
|
||||
struct key *key,
|
||||
const void *payload, size_t plen,
|
||||
unsigned long flags, bool create);
|
||||
#else
|
||||
static inline void ima_post_key_create_or_update(struct key *keyring,
|
||||
struct key *key,
|
||||
const void *payload,
|
||||
size_t plen,
|
||||
unsigned long flags,
|
||||
bool create) {}
|
||||
#endif /* CONFIG_IMA_MEASURE_ASYMMETRIC_KEYS */
|
||||
|
||||
#ifdef CONFIG_IMA_APPRAISE
|
||||
extern bool is_ima_appraise_enabled(void);
|
||||
extern void ima_inode_post_setattr(struct dentry *dentry);
|
||||
|
|
|
@ -310,3 +310,15 @@ config IMA_APPRAISE_SIGNED_INIT
|
|||
default n
|
||||
help
|
||||
This option requires user-space init to be signed.
|
||||
|
||||
config IMA_MEASURE_ASYMMETRIC_KEYS
|
||||
bool
|
||||
depends on IMA
|
||||
depends on ASYMMETRIC_PUBLIC_KEY_SUBTYPE=y
|
||||
default y
|
||||
|
||||
config IMA_QUEUE_EARLY_BOOT_KEYS
|
||||
bool
|
||||
depends on IMA_MEASURE_ASYMMETRIC_KEYS
|
||||
depends on SYSTEM_TRUSTED_KEYRING
|
||||
default y
|
||||
|
|
|
@ -12,3 +12,5 @@ ima-$(CONFIG_IMA_APPRAISE) += ima_appraise.o
|
|||
ima-$(CONFIG_IMA_APPRAISE_MODSIG) += ima_modsig.o
|
||||
ima-$(CONFIG_HAVE_IMA_KEXEC) += ima_kexec.o
|
||||
obj-$(CONFIG_IMA_BLACKLIST_KEYRING) += ima_mok.o
|
||||
obj-$(CONFIG_IMA_MEASURE_ASYMMETRIC_KEYS) += ima_asymmetric_keys.o
|
||||
obj-$(CONFIG_IMA_QUEUE_EARLY_BOOT_KEYS) += ima_queue_keys.o
|
||||
|
|
|
@ -193,6 +193,7 @@ static inline unsigned long ima_hash_key(u8 *digest)
|
|||
hook(KEXEC_INITRAMFS_CHECK) \
|
||||
hook(POLICY_CHECK) \
|
||||
hook(KEXEC_CMDLINE) \
|
||||
hook(KEY_CHECK) \
|
||||
hook(MAX_CHECK)
|
||||
#define __ima_hook_enumify(ENUM) ENUM,
|
||||
|
||||
|
@ -204,10 +205,35 @@ extern const char *const func_tokens[];
|
|||
|
||||
struct modsig;
|
||||
|
||||
#ifdef CONFIG_IMA_QUEUE_EARLY_BOOT_KEYS
|
||||
/*
|
||||
* To track keys that need to be measured.
|
||||
*/
|
||||
struct ima_key_entry {
|
||||
struct list_head list;
|
||||
void *payload;
|
||||
size_t payload_len;
|
||||
char *keyring_name;
|
||||
};
|
||||
void ima_init_key_queue(void);
|
||||
bool ima_should_queue_key(void);
|
||||
bool ima_queue_key(struct key *keyring, const void *payload,
|
||||
size_t payload_len);
|
||||
void ima_process_queued_keys(void);
|
||||
#else
|
||||
static inline void ima_init_key_queue(void) {}
|
||||
static inline bool ima_should_queue_key(void) { return false; }
|
||||
static inline bool ima_queue_key(struct key *keyring,
|
||||
const void *payload,
|
||||
size_t payload_len) { return false; }
|
||||
static inline void ima_process_queued_keys(void) {}
|
||||
#endif /* CONFIG_IMA_QUEUE_EARLY_BOOT_KEYS */
|
||||
|
||||
/* LIM API function definitions */
|
||||
int ima_get_action(struct inode *inode, const struct cred *cred, u32 secid,
|
||||
int mask, enum ima_hooks func, int *pcr,
|
||||
struct ima_template_desc **template_desc);
|
||||
struct ima_template_desc **template_desc,
|
||||
const char *keyring);
|
||||
int ima_must_measure(struct inode *inode, int mask, enum ima_hooks func);
|
||||
int ima_collect_measurement(struct integrity_iint_cache *iint,
|
||||
struct file *file, void *buf, loff_t size,
|
||||
|
@ -219,7 +245,7 @@ void ima_store_measurement(struct integrity_iint_cache *iint, struct file *file,
|
|||
struct ima_template_desc *template_desc);
|
||||
void process_buffer_measurement(const void *buf, int size,
|
||||
const char *eventname, enum ima_hooks func,
|
||||
int pcr);
|
||||
int pcr, const char *keyring);
|
||||
void ima_audit_measurement(struct integrity_iint_cache *iint,
|
||||
const unsigned char *filename);
|
||||
int ima_alloc_init_template(struct ima_event_data *event_data,
|
||||
|
@ -234,7 +260,8 @@ const char *ima_d_path(const struct path *path, char **pathbuf, char *filename);
|
|||
/* IMA policy related functions */
|
||||
int ima_match_policy(struct inode *inode, const struct cred *cred, u32 secid,
|
||||
enum ima_hooks func, int mask, int flags, int *pcr,
|
||||
struct ima_template_desc **template_desc);
|
||||
struct ima_template_desc **template_desc,
|
||||
const char *keyring);
|
||||
void ima_init_policy(void);
|
||||
void ima_update_policy(void);
|
||||
void ima_update_policy_flag(void);
|
||||
|
|
|
@ -169,12 +169,13 @@ err_out:
|
|||
* @func: caller identifier
|
||||
* @pcr: pointer filled in if matched measure policy sets pcr=
|
||||
* @template_desc: pointer filled in if matched measure policy sets template=
|
||||
* @keyring: keyring name used to determine the action
|
||||
*
|
||||
* The policy is defined in terms of keypairs:
|
||||
* subj=, obj=, type=, func=, mask=, fsmagic=
|
||||
* subj,obj, and type: are LSM specific.
|
||||
* func: FILE_CHECK | BPRM_CHECK | CREDS_CHECK | MMAP_CHECK | MODULE_CHECK
|
||||
* | KEXEC_CMDLINE
|
||||
* | KEXEC_CMDLINE | KEY_CHECK
|
||||
* mask: contains the permission mask
|
||||
* fsmagic: hex value
|
||||
*
|
||||
|
@ -183,14 +184,15 @@ err_out:
|
|||
*/
|
||||
int ima_get_action(struct inode *inode, const struct cred *cred, u32 secid,
|
||||
int mask, enum ima_hooks func, int *pcr,
|
||||
struct ima_template_desc **template_desc)
|
||||
struct ima_template_desc **template_desc,
|
||||
const char *keyring)
|
||||
{
|
||||
int flags = IMA_MEASURE | IMA_AUDIT | IMA_APPRAISE | IMA_HASH;
|
||||
|
||||
flags &= ima_policy_flag;
|
||||
|
||||
return ima_match_policy(inode, cred, secid, func, mask, flags, pcr,
|
||||
template_desc);
|
||||
template_desc, keyring);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -55,7 +55,7 @@ int ima_must_appraise(struct inode *inode, int mask, enum ima_hooks func)
|
|||
|
||||
security_task_getsecid(current, &secid);
|
||||
return ima_match_policy(inode, current_cred(), secid, func, mask,
|
||||
IMA_APPRAISE | IMA_HASH, NULL, NULL);
|
||||
IMA_APPRAISE | IMA_HASH, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
static int ima_fix_xattr(struct dentry *dentry,
|
||||
|
@ -330,7 +330,7 @@ int ima_check_blacklist(struct integrity_iint_cache *iint,
|
|||
if ((rc == -EPERM) && (iint->flags & IMA_MEASURE))
|
||||
process_buffer_measurement(digest, digestsize,
|
||||
"blacklisted-hash", NONE,
|
||||
pcr);
|
||||
pcr, NULL);
|
||||
}
|
||||
|
||||
return rc;
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2019 Microsoft Corporation
|
||||
*
|
||||
* Author: Lakshmi Ramasubramanian (nramas@linux.microsoft.com)
|
||||
*
|
||||
* File: ima_asymmetric_keys.c
|
||||
* Defines an IMA hook to measure asymmetric keys on key
|
||||
* create or update.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <keys/asymmetric-type.h>
|
||||
#include "ima.h"
|
||||
|
||||
/**
|
||||
* ima_post_key_create_or_update - measure asymmetric keys
|
||||
* @keyring: keyring to which the key is linked to
|
||||
* @key: created or updated key
|
||||
* @payload: The data used to instantiate or update the key.
|
||||
* @payload_len: The length of @payload.
|
||||
* @flags: key flags
|
||||
* @create: flag indicating whether the key was created or updated
|
||||
*
|
||||
* Keys can only be measured, not appraised.
|
||||
* The payload data used to instantiate or update the key is measured.
|
||||
*/
|
||||
void ima_post_key_create_or_update(struct key *keyring, struct key *key,
|
||||
const void *payload, size_t payload_len,
|
||||
unsigned long flags, bool create)
|
||||
{
|
||||
bool queued = false;
|
||||
|
||||
/* Only asymmetric keys are handled by this hook. */
|
||||
if (key->type != &key_type_asymmetric)
|
||||
return;
|
||||
|
||||
if (!payload || (payload_len == 0))
|
||||
return;
|
||||
|
||||
if (ima_should_queue_key())
|
||||
queued = ima_queue_key(keyring, payload, payload_len);
|
||||
|
||||
if (queued)
|
||||
return;
|
||||
|
||||
/*
|
||||
* keyring->description points to the name of the keyring
|
||||
* (such as ".builtin_trusted_keys", ".ima", etc.) to
|
||||
* which the given key is linked to.
|
||||
*
|
||||
* The name of the keyring is passed in the "eventname"
|
||||
* parameter to process_buffer_measurement() and is set
|
||||
* in the "eventname" field in ima_event_data for
|
||||
* the key measurement IMA event.
|
||||
*
|
||||
* The name of the keyring is also passed in the "keyring"
|
||||
* parameter to process_buffer_measurement() to check
|
||||
* if the IMA policy is configured to measure a key linked
|
||||
* to the given keyring.
|
||||
*/
|
||||
process_buffer_measurement(payload, payload_len,
|
||||
keyring->description, KEY_CHECK, 0,
|
||||
keyring->description);
|
||||
}
|
|
@ -362,8 +362,10 @@ static int ima_calc_file_hash_tfm(struct file *file,
|
|||
rc = rbuf_len;
|
||||
break;
|
||||
}
|
||||
if (rbuf_len == 0)
|
||||
if (rbuf_len == 0) { /* unexpected EOF */
|
||||
rc = -EINVAL;
|
||||
break;
|
||||
}
|
||||
offset += rbuf_len;
|
||||
|
||||
rc = crypto_shash_update(shash, rbuf, rbuf_len);
|
||||
|
|
|
@ -131,5 +131,11 @@ int __init ima_init(void)
|
|||
|
||||
ima_init_policy();
|
||||
|
||||
return ima_fs_init();
|
||||
rc = ima_fs_init();
|
||||
if (rc != 0)
|
||||
return rc;
|
||||
|
||||
ima_init_key_queue();
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -215,7 +215,7 @@ static int process_measurement(struct file *file, const struct cred *cred,
|
|||
* Included is the appraise submask.
|
||||
*/
|
||||
action = ima_get_action(inode, cred, secid, mask, func, &pcr,
|
||||
&template_desc);
|
||||
&template_desc, NULL);
|
||||
violation_check = ((func == FILE_CHECK || func == MMAP_CHECK) &&
|
||||
(ima_policy_flag & IMA_MEASURE));
|
||||
if (!action && !violation_check)
|
||||
|
@ -445,6 +445,55 @@ int ima_file_check(struct file *file, int mask)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(ima_file_check);
|
||||
|
||||
/**
|
||||
* ima_file_hash - return the stored measurement if a file has been hashed and
|
||||
* is in the iint cache.
|
||||
* @file: pointer to the file
|
||||
* @buf: buffer in which to store the hash
|
||||
* @buf_size: length of the buffer
|
||||
*
|
||||
* On success, return the hash algorithm (as defined in the enum hash_algo).
|
||||
* If buf is not NULL, this function also outputs the hash into buf.
|
||||
* If the hash is larger than buf_size, then only buf_size bytes will be copied.
|
||||
* It generally just makes sense to pass a buffer capable of holding the largest
|
||||
* possible hash: IMA_MAX_DIGEST_SIZE.
|
||||
* The file hash returned is based on the entire file, including the appended
|
||||
* signature.
|
||||
*
|
||||
* If IMA is disabled or if no measurement is available, return -EOPNOTSUPP.
|
||||
* If the parameters are incorrect, return -EINVAL.
|
||||
*/
|
||||
int ima_file_hash(struct file *file, char *buf, size_t buf_size)
|
||||
{
|
||||
struct inode *inode;
|
||||
struct integrity_iint_cache *iint;
|
||||
int hash_algo;
|
||||
|
||||
if (!file)
|
||||
return -EINVAL;
|
||||
|
||||
if (!ima_policy_flag)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
inode = file_inode(file);
|
||||
iint = integrity_iint_find(inode);
|
||||
if (!iint)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
mutex_lock(&iint->mutex);
|
||||
if (buf) {
|
||||
size_t copied_size;
|
||||
|
||||
copied_size = min_t(size_t, iint->ima_hash->length, buf_size);
|
||||
memcpy(buf, iint->ima_hash->digest, copied_size);
|
||||
}
|
||||
hash_algo = iint->ima_hash->algo;
|
||||
mutex_unlock(&iint->mutex);
|
||||
|
||||
return hash_algo;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ima_file_hash);
|
||||
|
||||
/**
|
||||
* ima_post_create_tmpfile - mark newly created tmpfile as new
|
||||
* @file : newly created tmpfile
|
||||
|
@ -632,12 +681,13 @@ int ima_load_data(enum kernel_load_data_id id)
|
|||
* @eventname: event name to be used for the buffer entry.
|
||||
* @func: IMA hook
|
||||
* @pcr: pcr to extend the measurement
|
||||
* @keyring: keyring name to determine the action to be performed
|
||||
*
|
||||
* Based on policy, the buffer is measured into the ima log.
|
||||
*/
|
||||
void process_buffer_measurement(const void *buf, int size,
|
||||
const char *eventname, enum ima_hooks func,
|
||||
int pcr)
|
||||
int pcr, const char *keyring)
|
||||
{
|
||||
int ret = 0;
|
||||
struct ima_template_entry *entry = NULL;
|
||||
|
@ -655,6 +705,9 @@ void process_buffer_measurement(const void *buf, int size,
|
|||
int action = 0;
|
||||
u32 secid;
|
||||
|
||||
if (!ima_policy_flag)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Both LSM hooks and auxilary based buffer measurements are
|
||||
* based on policy. To avoid code duplication, differentiate
|
||||
|
@ -665,7 +718,7 @@ void process_buffer_measurement(const void *buf, int size,
|
|||
if (func) {
|
||||
security_task_getsecid(current, &secid);
|
||||
action = ima_get_action(NULL, current_cred(), secid, 0, func,
|
||||
&pcr, &template);
|
||||
&pcr, &template, keyring);
|
||||
if (!(action & IMA_MEASURE))
|
||||
return;
|
||||
}
|
||||
|
@ -718,7 +771,7 @@ void ima_kexec_cmdline(const void *buf, int size)
|
|||
{
|
||||
if (buf && size != 0)
|
||||
process_buffer_measurement(buf, size, "kexec-cmdline",
|
||||
KEXEC_CMDLINE, 0);
|
||||
KEXEC_CMDLINE, 0, NULL);
|
||||
}
|
||||
|
||||
static int __init init_ima(void)
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#define IMA_EUID 0x0080
|
||||
#define IMA_PCR 0x0100
|
||||
#define IMA_FSNAME 0x0200
|
||||
#define IMA_KEYRINGS 0x0400
|
||||
|
||||
#define UNKNOWN 0
|
||||
#define MEASURE 0x0001 /* same as IMA_MEASURE */
|
||||
|
@ -79,6 +80,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_template_desc *template;
|
||||
};
|
||||
|
||||
|
@ -206,6 +208,10 @@ static LIST_HEAD(ima_policy_rules);
|
|||
static LIST_HEAD(ima_temp_rules);
|
||||
static struct list_head *ima_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)
|
||||
|
@ -263,7 +269,7 @@ static void ima_lsm_free_rule(struct ima_rule_entry *entry)
|
|||
static struct ima_rule_entry *ima_lsm_copy_rule(struct ima_rule_entry *entry)
|
||||
{
|
||||
struct ima_rule_entry *nentry;
|
||||
int i, result;
|
||||
int i;
|
||||
|
||||
nentry = kmalloc(sizeof(*nentry), GFP_KERNEL);
|
||||
if (!nentry)
|
||||
|
@ -277,7 +283,7 @@ static struct ima_rule_entry *ima_lsm_copy_rule(struct ima_rule_entry *entry)
|
|||
memset(nentry->lsm, 0, sizeof_field(struct ima_rule_entry, lsm));
|
||||
|
||||
for (i = 0; i < MAX_LSM_RULES; i++) {
|
||||
if (!entry->lsm[i].rule)
|
||||
if (!entry->lsm[i].args_p)
|
||||
continue;
|
||||
|
||||
nentry->lsm[i].type = entry->lsm[i].type;
|
||||
|
@ -286,13 +292,13 @@ static struct ima_rule_entry *ima_lsm_copy_rule(struct ima_rule_entry *entry)
|
|||
if (!nentry->lsm[i].args_p)
|
||||
goto out_err;
|
||||
|
||||
result = security_filter_rule_init(nentry->lsm[i].type,
|
||||
Audit_equal,
|
||||
nentry->lsm[i].args_p,
|
||||
&nentry->lsm[i].rule);
|
||||
if (result == -EINVAL)
|
||||
pr_warn("ima: rule for LSM \'%d\' is undefined\n",
|
||||
entry->lsm[i].type);
|
||||
security_filter_rule_init(nentry->lsm[i].type,
|
||||
Audit_equal,
|
||||
nentry->lsm[i].args_p,
|
||||
&nentry->lsm[i].rule);
|
||||
if (!nentry->lsm[i].rule)
|
||||
pr_warn("rule for LSM \'%s\' is undefined\n",
|
||||
(char *)entry->lsm[i].args_p);
|
||||
}
|
||||
return nentry;
|
||||
|
||||
|
@ -329,7 +335,7 @@ static void ima_lsm_update_rules(void)
|
|||
list_for_each_entry_safe(entry, e, &ima_policy_rules, list) {
|
||||
needs_update = 0;
|
||||
for (i = 0; i < MAX_LSM_RULES; i++) {
|
||||
if (entry->lsm[i].rule) {
|
||||
if (entry->lsm[i].args_p) {
|
||||
needs_update = 1;
|
||||
break;
|
||||
}
|
||||
|
@ -339,8 +345,7 @@ static void ima_lsm_update_rules(void)
|
|||
|
||||
result = ima_lsm_update_rule(entry);
|
||||
if (result) {
|
||||
pr_err("ima: lsm rule update error %d\n",
|
||||
result);
|
||||
pr_err("lsm rule update error %d\n", result);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -357,25 +362,70 @@ int ima_lsm_policy_change(struct notifier_block *nb, unsigned long event,
|
|||
}
|
||||
|
||||
/**
|
||||
* ima_match_rules - determine whether an inode matches the measure rule.
|
||||
* ima_match_keyring - determine whether the keyring matches the measure rule
|
||||
* @rule: a pointer to a rule
|
||||
* @keyring: name of the keyring to match against the measure rule
|
||||
* @cred: a pointer to a credentials structure for user validation
|
||||
*
|
||||
* Returns true if keyring matches one in the rule, false otherwise.
|
||||
*/
|
||||
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;
|
||||
|
||||
if ((rule->flags & IMA_UID) && !rule->uid_op(cred->uid, rule->uid))
|
||||
return false;
|
||||
|
||||
if (!rule->keyrings)
|
||||
return true;
|
||||
|
||||
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)) {
|
||||
matched = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return matched;
|
||||
}
|
||||
|
||||
/**
|
||||
* ima_match_rules - determine whether an inode matches the policy rule.
|
||||
* @rule: a pointer to a rule
|
||||
* @inode: a pointer to an inode
|
||||
* @cred: a pointer to a credentials structure for user validation
|
||||
* @secid: the secid of the task to be validated
|
||||
* @func: LIM hook identifier
|
||||
* @mask: requested action (MAY_READ | MAY_WRITE | MAY_APPEND | MAY_EXEC)
|
||||
* @keyring: keyring name to check in policy for KEY_CHECK func
|
||||
*
|
||||
* Returns true on rule match, false on failure.
|
||||
*/
|
||||
static bool ima_match_rules(struct ima_rule_entry *rule, struct inode *inode,
|
||||
const struct cred *cred, u32 secid,
|
||||
enum ima_hooks func, int mask)
|
||||
enum ima_hooks func, int mask,
|
||||
const char *keyring)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (func == KEXEC_CMDLINE) {
|
||||
if ((rule->flags & IMA_FUNC) && (rule->func == func))
|
||||
if ((func == KEXEC_CMDLINE) || (func == KEY_CHECK)) {
|
||||
if ((rule->flags & IMA_FUNC) && (rule->func == func)) {
|
||||
if (func == KEY_CHECK)
|
||||
return ima_match_keyring(rule, keyring, cred);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if ((rule->flags & IMA_FUNC) &&
|
||||
|
@ -415,9 +465,12 @@ static bool ima_match_rules(struct ima_rule_entry *rule, struct inode *inode,
|
|||
int rc = 0;
|
||||
u32 osid;
|
||||
|
||||
if (!rule->lsm[i].rule)
|
||||
continue;
|
||||
|
||||
if (!rule->lsm[i].rule) {
|
||||
if (!rule->lsm[i].args_p)
|
||||
continue;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
switch (i) {
|
||||
case LSM_OBJ_USER:
|
||||
case LSM_OBJ_ROLE:
|
||||
|
@ -479,6 +532,8 @@ static int get_subaction(struct ima_rule_entry *rule, enum ima_hooks func)
|
|||
* @mask: requested action (MAY_READ | MAY_WRITE | MAY_APPEND | MAY_EXEC)
|
||||
* @pcr: set the pcr to extend
|
||||
* @template_desc: the template that should be used for this rule
|
||||
* @keyring: the keyring name, if given, to be used to check in the policy.
|
||||
* keyring can be NULL if func is anything other than KEY_CHECK.
|
||||
*
|
||||
* Measure decision based on func/mask/fsmagic and LSM(subj/obj/type)
|
||||
* conditions.
|
||||
|
@ -489,7 +544,8 @@ static int get_subaction(struct ima_rule_entry *rule, enum ima_hooks func)
|
|||
*/
|
||||
int ima_match_policy(struct inode *inode, const struct cred *cred, u32 secid,
|
||||
enum ima_hooks func, int mask, int flags, int *pcr,
|
||||
struct ima_template_desc **template_desc)
|
||||
struct ima_template_desc **template_desc,
|
||||
const char *keyring)
|
||||
{
|
||||
struct ima_rule_entry *entry;
|
||||
int action = 0, actmask = flags | (flags << 1);
|
||||
|
@ -503,7 +559,8 @@ int ima_match_policy(struct inode *inode, const struct cred *cred, u32 secid,
|
|||
if (!(entry->action & actmask))
|
||||
continue;
|
||||
|
||||
if (!ima_match_rules(entry, inode, cred, secid, func, mask))
|
||||
if (!ima_match_rules(entry, inode, cred, secid, func, mask,
|
||||
keyring))
|
||||
continue;
|
||||
|
||||
action |= entry->flags & IMA_ACTION_FLAGS;
|
||||
|
@ -752,6 +809,9 @@ void ima_update_policy(void)
|
|||
kfree(arch_policy_entry);
|
||||
}
|
||||
ima_update_policy_flag();
|
||||
|
||||
/* Custom IMA policy has been loaded */
|
||||
ima_process_queued_keys();
|
||||
}
|
||||
|
||||
/* Keep the enumeration in sync with the policy_tokens! */
|
||||
|
@ -766,7 +826,8 @@ enum {
|
|||
Opt_uid_gt, Opt_euid_gt, Opt_fowner_gt,
|
||||
Opt_uid_lt, Opt_euid_lt, Opt_fowner_lt,
|
||||
Opt_appraise_type, Opt_appraise_flag,
|
||||
Opt_permit_directio, Opt_pcr, Opt_template, Opt_err
|
||||
Opt_permit_directio, Opt_pcr, Opt_template, Opt_keyrings,
|
||||
Opt_err
|
||||
};
|
||||
|
||||
static const match_table_t policy_tokens = {
|
||||
|
@ -802,6 +863,7 @@ static const match_table_t policy_tokens = {
|
|||
{Opt_permit_directio, "permit_directio"},
|
||||
{Opt_pcr, "pcr=%s"},
|
||||
{Opt_template, "template=%s"},
|
||||
{Opt_keyrings, "keyrings=%s"},
|
||||
{Opt_err, NULL}
|
||||
};
|
||||
|
||||
|
@ -823,8 +885,14 @@ static int ima_lsm_rule_init(struct ima_rule_entry *entry,
|
|||
entry->lsm[lsm_rule].args_p,
|
||||
&entry->lsm[lsm_rule].rule);
|
||||
if (!entry->lsm[lsm_rule].rule) {
|
||||
kfree(entry->lsm[lsm_rule].args_p);
|
||||
return -EINVAL;
|
||||
pr_warn("rule for LSM \'%s\' is undefined\n",
|
||||
(char *)entry->lsm[lsm_rule].args_p);
|
||||
|
||||
if (ima_rules == &ima_default_rules) {
|
||||
kfree(entry->lsm[lsm_rule].args_p);
|
||||
result = -EINVAL;
|
||||
} else
|
||||
result = 0;
|
||||
}
|
||||
|
||||
return result;
|
||||
|
@ -889,6 +957,7 @@ 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);
|
||||
|
@ -997,6 +1066,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)
|
||||
entry->func = KEY_CHECK;
|
||||
else
|
||||
result = -EINVAL;
|
||||
if (!result)
|
||||
|
@ -1049,6 +1120,44 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
|
|||
result = 0;
|
||||
entry->flags |= IMA_FSNAME;
|
||||
break;
|
||||
case Opt_keyrings:
|
||||
ima_log_string(ab, "keyrings", args[0].from);
|
||||
|
||||
keyrings_len = strlen(args[0].from) + 1;
|
||||
|
||||
if ((entry->keyrings) ||
|
||||
(entry->action != MEASURE) ||
|
||||
(entry->func != KEY_CHECK) ||
|
||||
(keyrings_len < 2)) {
|
||||
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;
|
||||
break;
|
||||
}
|
||||
result = 0;
|
||||
entry->flags |= IMA_KEYRINGS;
|
||||
break;
|
||||
case Opt_fsuuid:
|
||||
ima_log_string(ab, "fsuuid", args[0].from);
|
||||
|
||||
|
@ -1424,6 +1533,13 @@ int ima_policy_show(struct seq_file *m, void *v)
|
|||
seq_puts(m, " ");
|
||||
}
|
||||
|
||||
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, " ");
|
||||
}
|
||||
|
||||
if (entry->flags & IMA_PCR) {
|
||||
snprintf(tbuf, sizeof(tbuf), "%d", entry->pcr);
|
||||
seq_printf(m, pt(Opt_pcr), tbuf);
|
||||
|
@ -1496,6 +1612,7 @@ int ima_policy_show(struct seq_file *m, void *v)
|
|||
(char *)entry->lsm[i].args_p);
|
||||
break;
|
||||
}
|
||||
seq_puts(m, " ");
|
||||
}
|
||||
}
|
||||
if (entry->template)
|
||||
|
|
|
@ -0,0 +1,171 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2019 Microsoft Corporation
|
||||
*
|
||||
* Author: Lakshmi Ramasubramanian (nramas@linux.microsoft.com)
|
||||
*
|
||||
* File: ima_queue_keys.c
|
||||
* Enables deferred processing of keys
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/workqueue.h>
|
||||
#include <keys/asymmetric-type.h>
|
||||
#include "ima.h"
|
||||
|
||||
/*
|
||||
* Flag to indicate whether a key can be processed
|
||||
* right away or should be queued for processing later.
|
||||
*/
|
||||
static bool ima_process_keys;
|
||||
|
||||
/*
|
||||
* To synchronize access to the list of keys that need to be measured
|
||||
*/
|
||||
static DEFINE_MUTEX(ima_keys_lock);
|
||||
static LIST_HEAD(ima_keys);
|
||||
|
||||
/*
|
||||
* If custom IMA policy is not loaded then keys queued up
|
||||
* for measurement should be freed. This worker is used
|
||||
* for handling this scenario.
|
||||
*/
|
||||
static long ima_key_queue_timeout = 300000; /* 5 Minutes */
|
||||
static void ima_keys_handler(struct work_struct *work);
|
||||
static DECLARE_DELAYED_WORK(ima_keys_delayed_work, ima_keys_handler);
|
||||
static bool timer_expired;
|
||||
|
||||
/*
|
||||
* This worker function frees keys that may still be
|
||||
* queued up in case custom IMA policy was not loaded.
|
||||
*/
|
||||
static void ima_keys_handler(struct work_struct *work)
|
||||
{
|
||||
timer_expired = true;
|
||||
ima_process_queued_keys();
|
||||
}
|
||||
|
||||
/*
|
||||
* This function sets up a worker to free queued keys in case
|
||||
* custom IMA policy was never loaded.
|
||||
*/
|
||||
void ima_init_key_queue(void)
|
||||
{
|
||||
schedule_delayed_work(&ima_keys_delayed_work,
|
||||
msecs_to_jiffies(ima_key_queue_timeout));
|
||||
}
|
||||
|
||||
static void ima_free_key_entry(struct ima_key_entry *entry)
|
||||
{
|
||||
if (entry) {
|
||||
kfree(entry->payload);
|
||||
kfree(entry->keyring_name);
|
||||
kfree(entry);
|
||||
}
|
||||
}
|
||||
|
||||
static struct ima_key_entry *ima_alloc_key_entry(struct key *keyring,
|
||||
const void *payload,
|
||||
size_t payload_len)
|
||||
{
|
||||
int rc = 0;
|
||||
struct ima_key_entry *entry;
|
||||
|
||||
entry = kzalloc(sizeof(*entry), GFP_KERNEL);
|
||||
if (entry) {
|
||||
entry->payload = kmemdup(payload, payload_len, GFP_KERNEL);
|
||||
entry->keyring_name = kstrdup(keyring->description,
|
||||
GFP_KERNEL);
|
||||
entry->payload_len = payload_len;
|
||||
}
|
||||
|
||||
if ((entry == NULL) || (entry->payload == NULL) ||
|
||||
(entry->keyring_name == NULL)) {
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&entry->list);
|
||||
|
||||
out:
|
||||
if (rc) {
|
||||
ima_free_key_entry(entry);
|
||||
entry = NULL;
|
||||
}
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
bool ima_queue_key(struct key *keyring, const void *payload,
|
||||
size_t payload_len)
|
||||
{
|
||||
bool queued = false;
|
||||
struct ima_key_entry *entry;
|
||||
|
||||
entry = ima_alloc_key_entry(keyring, payload, payload_len);
|
||||
if (!entry)
|
||||
return false;
|
||||
|
||||
mutex_lock(&ima_keys_lock);
|
||||
if (!ima_process_keys) {
|
||||
list_add_tail(&entry->list, &ima_keys);
|
||||
queued = true;
|
||||
}
|
||||
mutex_unlock(&ima_keys_lock);
|
||||
|
||||
if (!queued)
|
||||
ima_free_key_entry(entry);
|
||||
|
||||
return queued;
|
||||
}
|
||||
|
||||
/*
|
||||
* ima_process_queued_keys() - process keys queued for measurement
|
||||
*
|
||||
* This function sets ima_process_keys to true and processes queued keys.
|
||||
* From here on keys will be processed right away (not queued).
|
||||
*/
|
||||
void ima_process_queued_keys(void)
|
||||
{
|
||||
struct ima_key_entry *entry, *tmp;
|
||||
bool process = false;
|
||||
|
||||
if (ima_process_keys)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Since ima_process_keys is set to true, any new key will be
|
||||
* processed immediately and not be queued to ima_keys list.
|
||||
* First one setting the ima_process_keys flag to true will
|
||||
* process the queued keys.
|
||||
*/
|
||||
mutex_lock(&ima_keys_lock);
|
||||
if (!ima_process_keys) {
|
||||
ima_process_keys = true;
|
||||
process = true;
|
||||
}
|
||||
mutex_unlock(&ima_keys_lock);
|
||||
|
||||
if (!process)
|
||||
return;
|
||||
|
||||
if (!timer_expired)
|
||||
cancel_delayed_work_sync(&ima_keys_delayed_work);
|
||||
|
||||
list_for_each_entry_safe(entry, tmp, &ima_keys, list) {
|
||||
if (!timer_expired)
|
||||
process_buffer_measurement(entry->payload,
|
||||
entry->payload_len,
|
||||
entry->keyring_name,
|
||||
KEY_CHECK, 0,
|
||||
entry->keyring_name);
|
||||
list_del(&entry->list);
|
||||
ima_free_key_entry(entry);
|
||||
}
|
||||
}
|
||||
|
||||
inline bool ima_should_queue_key(void)
|
||||
{
|
||||
return !ima_process_keys;
|
||||
}
|
|
@ -13,6 +13,7 @@
|
|||
#include <linux/security.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/ima.h>
|
||||
#include <linux/err.h>
|
||||
#include "internal.h"
|
||||
|
||||
|
@ -936,6 +937,9 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
|
|||
goto error_link_end;
|
||||
}
|
||||
|
||||
ima_post_key_create_or_update(keyring, key, payload, plen,
|
||||
flags, true);
|
||||
|
||||
key_ref = make_key_ref(key, is_key_possessed(keyring_ref));
|
||||
|
||||
error_link_end:
|
||||
|
@ -965,6 +969,12 @@ error:
|
|||
}
|
||||
|
||||
key_ref = __key_update(key_ref, &prep);
|
||||
|
||||
if (!IS_ERR(key_ref))
|
||||
ima_post_key_create_or_update(keyring, key,
|
||||
payload, plen,
|
||||
flags, false);
|
||||
|
||||
goto error_free_prep;
|
||||
}
|
||||
EXPORT_SYMBOL(key_create_or_update);
|
||||
|
|
Loading…
Reference in New Issue