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:
Коммит
73a0bff205
|
@ -25,11 +25,11 @@ Description:
|
||||||
lsm: [[subj_user=] [subj_role=] [subj_type=]
|
lsm: [[subj_user=] [subj_role=] [subj_type=]
|
||||||
[obj_user=] [obj_role=] [obj_type=]]
|
[obj_user=] [obj_role=] [obj_type=]]
|
||||||
option: [[appraise_type=]] [template=] [permit_directio]
|
option: [[appraise_type=]] [template=] [permit_directio]
|
||||||
[appraise_flag=]
|
[appraise_flag=] [keyrings=]
|
||||||
base: func:= [BPRM_CHECK][MMAP_CHECK][CREDS_CHECK][FILE_CHECK][MODULE_CHECK]
|
base: func:= [BPRM_CHECK][MMAP_CHECK][CREDS_CHECK][FILE_CHECK][MODULE_CHECK]
|
||||||
[FIRMWARE_CHECK]
|
[FIRMWARE_CHECK]
|
||||||
[KEXEC_KERNEL_CHECK] [KEXEC_INITRAMFS_CHECK]
|
[KEXEC_KERNEL_CHECK] [KEXEC_INITRAMFS_CHECK]
|
||||||
[KEXEC_CMDLINE]
|
[KEXEC_CMDLINE] [KEY_CHECK]
|
||||||
mask:= [[^]MAY_READ] [[^]MAY_WRITE] [[^]MAY_APPEND]
|
mask:= [[^]MAY_READ] [[^]MAY_WRITE] [[^]MAY_APPEND]
|
||||||
[[^]MAY_EXEC]
|
[[^]MAY_EXEC]
|
||||||
fsmagic:= hex value
|
fsmagic:= hex value
|
||||||
|
@ -42,6 +42,9 @@ Description:
|
||||||
appraise_flag:= [check_blacklist]
|
appraise_flag:= [check_blacklist]
|
||||||
Currently, blacklist check is only for files signed with appended
|
Currently, blacklist check is only for files signed with appended
|
||||||
signature.
|
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
|
template:= name of a defined IMA template type
|
||||||
(eg, ima-ng). Only valid when action is "measure".
|
(eg, ima-ng). Only valid when action is "measure".
|
||||||
pcr:= decimal value
|
pcr:= decimal value
|
||||||
|
@ -113,3 +116,12 @@ Description:
|
||||||
Example of appraise rule allowing modsig appended signatures:
|
Example of appraise rule allowing modsig appended signatures:
|
||||||
|
|
||||||
appraise func=KEXEC_KERNEL_CHECK appraise_type=imasig|modsig
|
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,
|
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 void ima_kexec_cmdline(const void *buf, int size);
|
extern void ima_kexec_cmdline(const void *buf, int size);
|
||||||
|
|
||||||
#ifdef CONFIG_IMA_KEXEC
|
#ifdef CONFIG_IMA_KEXEC
|
||||||
|
@ -91,6 +92,11 @@ static inline void ima_post_path_mknod(struct dentry *dentry)
|
||||||
return;
|
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) {}
|
static inline void ima_kexec_cmdline(const void *buf, int size) {}
|
||||||
#endif /* CONFIG_IMA */
|
#endif /* CONFIG_IMA */
|
||||||
|
|
||||||
|
@ -101,6 +107,20 @@ static inline void ima_add_kexec_buffer(struct kimage *image)
|
||||||
{}
|
{}
|
||||||
#endif
|
#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
|
#ifdef CONFIG_IMA_APPRAISE
|
||||||
extern bool is_ima_appraise_enabled(void);
|
extern bool is_ima_appraise_enabled(void);
|
||||||
extern void ima_inode_post_setattr(struct dentry *dentry);
|
extern void ima_inode_post_setattr(struct dentry *dentry);
|
||||||
|
|
|
@ -310,3 +310,15 @@ config IMA_APPRAISE_SIGNED_INIT
|
||||||
default n
|
default n
|
||||||
help
|
help
|
||||||
This option requires user-space init to be signed.
|
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_IMA_APPRAISE_MODSIG) += ima_modsig.o
|
||||||
ima-$(CONFIG_HAVE_IMA_KEXEC) += ima_kexec.o
|
ima-$(CONFIG_HAVE_IMA_KEXEC) += ima_kexec.o
|
||||||
obj-$(CONFIG_IMA_BLACKLIST_KEYRING) += ima_mok.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(KEXEC_INITRAMFS_CHECK) \
|
||||||
hook(POLICY_CHECK) \
|
hook(POLICY_CHECK) \
|
||||||
hook(KEXEC_CMDLINE) \
|
hook(KEXEC_CMDLINE) \
|
||||||
|
hook(KEY_CHECK) \
|
||||||
hook(MAX_CHECK)
|
hook(MAX_CHECK)
|
||||||
#define __ima_hook_enumify(ENUM) ENUM,
|
#define __ima_hook_enumify(ENUM) ENUM,
|
||||||
|
|
||||||
|
@ -204,10 +205,35 @@ extern const char *const func_tokens[];
|
||||||
|
|
||||||
struct modsig;
|
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 */
|
/* LIM API function definitions */
|
||||||
int ima_get_action(struct inode *inode, const struct cred *cred, u32 secid,
|
int ima_get_action(struct inode *inode, const struct cred *cred, u32 secid,
|
||||||
int mask, enum ima_hooks func, int *pcr,
|
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_must_measure(struct inode *inode, int mask, enum ima_hooks func);
|
||||||
int ima_collect_measurement(struct integrity_iint_cache *iint,
|
int ima_collect_measurement(struct integrity_iint_cache *iint,
|
||||||
struct file *file, void *buf, loff_t size,
|
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);
|
struct ima_template_desc *template_desc);
|
||||||
void process_buffer_measurement(const void *buf, int size,
|
void process_buffer_measurement(const void *buf, int size,
|
||||||
const char *eventname, enum ima_hooks func,
|
const char *eventname, enum ima_hooks func,
|
||||||
int pcr);
|
int pcr, const char *keyring);
|
||||||
void ima_audit_measurement(struct integrity_iint_cache *iint,
|
void ima_audit_measurement(struct integrity_iint_cache *iint,
|
||||||
const unsigned char *filename);
|
const unsigned char *filename);
|
||||||
int ima_alloc_init_template(struct ima_event_data *event_data,
|
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 */
|
/* IMA policy related functions */
|
||||||
int ima_match_policy(struct inode *inode, const struct cred *cred, u32 secid,
|
int ima_match_policy(struct inode *inode, const struct cred *cred, u32 secid,
|
||||||
enum ima_hooks func, int mask, int flags, int *pcr,
|
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_init_policy(void);
|
||||||
void ima_update_policy(void);
|
void ima_update_policy(void);
|
||||||
void ima_update_policy_flag(void);
|
void ima_update_policy_flag(void);
|
||||||
|
|
|
@ -169,12 +169,13 @@ err_out:
|
||||||
* @func: caller identifier
|
* @func: caller identifier
|
||||||
* @pcr: pointer filled in if matched measure policy sets pcr=
|
* @pcr: pointer filled in if matched measure policy sets pcr=
|
||||||
* @template_desc: pointer filled in if matched measure policy sets template=
|
* @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:
|
* The policy is defined in terms of keypairs:
|
||||||
* subj=, obj=, type=, func=, mask=, fsmagic=
|
* subj=, obj=, type=, func=, mask=, fsmagic=
|
||||||
* subj,obj, and type: are LSM specific.
|
* subj,obj, and type: are LSM specific.
|
||||||
* func: FILE_CHECK | BPRM_CHECK | CREDS_CHECK | MMAP_CHECK | MODULE_CHECK
|
* func: FILE_CHECK | BPRM_CHECK | CREDS_CHECK | MMAP_CHECK | MODULE_CHECK
|
||||||
* | KEXEC_CMDLINE
|
* | KEXEC_CMDLINE | KEY_CHECK
|
||||||
* mask: contains the permission mask
|
* mask: contains the permission mask
|
||||||
* fsmagic: hex value
|
* fsmagic: hex value
|
||||||
*
|
*
|
||||||
|
@ -183,14 +184,15 @@ err_out:
|
||||||
*/
|
*/
|
||||||
int ima_get_action(struct inode *inode, const struct cred *cred, u32 secid,
|
int ima_get_action(struct inode *inode, const struct cred *cred, u32 secid,
|
||||||
int mask, enum ima_hooks func, int *pcr,
|
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;
|
int flags = IMA_MEASURE | IMA_AUDIT | IMA_APPRAISE | IMA_HASH;
|
||||||
|
|
||||||
flags &= ima_policy_flag;
|
flags &= ima_policy_flag;
|
||||||
|
|
||||||
return ima_match_policy(inode, cred, secid, func, mask, flags, pcr,
|
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);
|
security_task_getsecid(current, &secid);
|
||||||
return ima_match_policy(inode, current_cred(), secid, func, mask,
|
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,
|
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))
|
if ((rc == -EPERM) && (iint->flags & IMA_MEASURE))
|
||||||
process_buffer_measurement(digest, digestsize,
|
process_buffer_measurement(digest, digestsize,
|
||||||
"blacklisted-hash", NONE,
|
"blacklisted-hash", NONE,
|
||||||
pcr);
|
pcr, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
return rc;
|
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;
|
rc = rbuf_len;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (rbuf_len == 0)
|
if (rbuf_len == 0) { /* unexpected EOF */
|
||||||
|
rc = -EINVAL;
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
offset += rbuf_len;
|
offset += rbuf_len;
|
||||||
|
|
||||||
rc = crypto_shash_update(shash, rbuf, rbuf_len);
|
rc = crypto_shash_update(shash, rbuf, rbuf_len);
|
||||||
|
|
|
@ -131,5 +131,11 @@ int __init ima_init(void)
|
||||||
|
|
||||||
ima_init_policy();
|
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.
|
* Included is the appraise submask.
|
||||||
*/
|
*/
|
||||||
action = ima_get_action(inode, cred, secid, mask, func, &pcr,
|
action = ima_get_action(inode, cred, secid, mask, func, &pcr,
|
||||||
&template_desc);
|
&template_desc, NULL);
|
||||||
violation_check = ((func == FILE_CHECK || func == MMAP_CHECK) &&
|
violation_check = ((func == FILE_CHECK || func == MMAP_CHECK) &&
|
||||||
(ima_policy_flag & IMA_MEASURE));
|
(ima_policy_flag & IMA_MEASURE));
|
||||||
if (!action && !violation_check)
|
if (!action && !violation_check)
|
||||||
|
@ -445,6 +445,55 @@ int ima_file_check(struct file *file, int mask)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(ima_file_check);
|
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
|
* ima_post_create_tmpfile - mark newly created tmpfile as new
|
||||||
* @file : newly created tmpfile
|
* @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.
|
* @eventname: event name to be used for the buffer entry.
|
||||||
* @func: IMA hook
|
* @func: IMA hook
|
||||||
* @pcr: pcr to extend the measurement
|
* @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.
|
* Based on policy, the buffer is measured into the ima log.
|
||||||
*/
|
*/
|
||||||
void process_buffer_measurement(const void *buf, int size,
|
void process_buffer_measurement(const void *buf, int size,
|
||||||
const char *eventname, enum ima_hooks func,
|
const char *eventname, enum ima_hooks func,
|
||||||
int pcr)
|
int pcr, const char *keyring)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
struct ima_template_entry *entry = NULL;
|
struct ima_template_entry *entry = NULL;
|
||||||
|
@ -655,6 +705,9 @@ void process_buffer_measurement(const void *buf, int size,
|
||||||
int action = 0;
|
int action = 0;
|
||||||
u32 secid;
|
u32 secid;
|
||||||
|
|
||||||
|
if (!ima_policy_flag)
|
||||||
|
return;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Both LSM hooks and auxilary based buffer measurements are
|
* Both LSM hooks and auxilary based buffer measurements are
|
||||||
* based on policy. To avoid code duplication, differentiate
|
* based on policy. To avoid code duplication, differentiate
|
||||||
|
@ -665,7 +718,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(NULL, current_cred(), secid, 0, func,
|
||||||
&pcr, &template);
|
&pcr, &template, keyring);
|
||||||
if (!(action & IMA_MEASURE))
|
if (!(action & IMA_MEASURE))
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -718,7 +771,7 @@ void ima_kexec_cmdline(const void *buf, int size)
|
||||||
{
|
{
|
||||||
if (buf && size != 0)
|
if (buf && size != 0)
|
||||||
process_buffer_measurement(buf, size, "kexec-cmdline",
|
process_buffer_measurement(buf, size, "kexec-cmdline",
|
||||||
KEXEC_CMDLINE, 0);
|
KEXEC_CMDLINE, 0, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __init init_ima(void)
|
static int __init init_ima(void)
|
||||||
|
|
|
@ -34,6 +34,7 @@
|
||||||
#define IMA_EUID 0x0080
|
#define IMA_EUID 0x0080
|
||||||
#define IMA_PCR 0x0100
|
#define IMA_PCR 0x0100
|
||||||
#define IMA_FSNAME 0x0200
|
#define IMA_FSNAME 0x0200
|
||||||
|
#define IMA_KEYRINGS 0x0400
|
||||||
|
|
||||||
#define UNKNOWN 0
|
#define UNKNOWN 0
|
||||||
#define MEASURE 0x0001 /* same as IMA_MEASURE */
|
#define MEASURE 0x0001 /* same as IMA_MEASURE */
|
||||||
|
@ -79,6 +80,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_template_desc *template;
|
struct ima_template_desc *template;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -206,6 +208,10 @@ static LIST_HEAD(ima_policy_rules);
|
||||||
static LIST_HEAD(ima_temp_rules);
|
static LIST_HEAD(ima_temp_rules);
|
||||||
static struct list_head *ima_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 ima_policy __initdata;
|
||||||
|
|
||||||
static int __init default_measure_policy_setup(char *str)
|
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)
|
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, result;
|
int i;
|
||||||
|
|
||||||
nentry = kmalloc(sizeof(*nentry), GFP_KERNEL);
|
nentry = kmalloc(sizeof(*nentry), GFP_KERNEL);
|
||||||
if (!nentry)
|
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));
|
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++) {
|
||||||
if (!entry->lsm[i].rule)
|
if (!entry->lsm[i].args_p)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
nentry->lsm[i].type = entry->lsm[i].type;
|
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)
|
if (!nentry->lsm[i].args_p)
|
||||||
goto out_err;
|
goto out_err;
|
||||||
|
|
||||||
result = security_filter_rule_init(nentry->lsm[i].type,
|
security_filter_rule_init(nentry->lsm[i].type,
|
||||||
Audit_equal,
|
Audit_equal,
|
||||||
nentry->lsm[i].args_p,
|
nentry->lsm[i].args_p,
|
||||||
&nentry->lsm[i].rule);
|
&nentry->lsm[i].rule);
|
||||||
if (result == -EINVAL)
|
if (!nentry->lsm[i].rule)
|
||||||
pr_warn("ima: rule for LSM \'%d\' is undefined\n",
|
pr_warn("rule for LSM \'%s\' is undefined\n",
|
||||||
entry->lsm[i].type);
|
(char *)entry->lsm[i].args_p);
|
||||||
}
|
}
|
||||||
return nentry;
|
return nentry;
|
||||||
|
|
||||||
|
@ -329,7 +335,7 @@ static void ima_lsm_update_rules(void)
|
||||||
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;
|
needs_update = 0;
|
||||||
for (i = 0; i < MAX_LSM_RULES; i++) {
|
for (i = 0; i < MAX_LSM_RULES; i++) {
|
||||||
if (entry->lsm[i].rule) {
|
if (entry->lsm[i].args_p) {
|
||||||
needs_update = 1;
|
needs_update = 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -339,8 +345,7 @@ static void ima_lsm_update_rules(void)
|
||||||
|
|
||||||
result = ima_lsm_update_rule(entry);
|
result = ima_lsm_update_rule(entry);
|
||||||
if (result) {
|
if (result) {
|
||||||
pr_err("ima: lsm rule update error %d\n",
|
pr_err("lsm rule update error %d\n", result);
|
||||||
result);
|
|
||||||
return;
|
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
|
* @rule: a pointer to a rule
|
||||||
* @inode: a pointer to an inode
|
* @inode: a pointer to an inode
|
||||||
* @cred: a pointer to a credentials structure for user validation
|
* @cred: a pointer to a credentials structure for user validation
|
||||||
* @secid: the secid of the task to be validated
|
* @secid: the secid of the task to be validated
|
||||||
* @func: LIM hook identifier
|
* @func: LIM hook identifier
|
||||||
* @mask: requested action (MAY_READ | MAY_WRITE | MAY_APPEND | MAY_EXEC)
|
* @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.
|
* Returns true on rule match, false on failure.
|
||||||
*/
|
*/
|
||||||
static bool ima_match_rules(struct ima_rule_entry *rule, struct inode *inode,
|
static bool ima_match_rules(struct ima_rule_entry *rule, struct inode *inode,
|
||||||
const struct cred *cred, u32 secid,
|
const struct cred *cred, u32 secid,
|
||||||
enum ima_hooks func, int mask)
|
enum ima_hooks func, int mask,
|
||||||
|
const char *keyring)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (func == KEXEC_CMDLINE) {
|
if ((func == KEXEC_CMDLINE) || (func == KEY_CHECK)) {
|
||||||
if ((rule->flags & IMA_FUNC) && (rule->func == func))
|
if ((rule->flags & IMA_FUNC) && (rule->func == func)) {
|
||||||
|
if (func == KEY_CHECK)
|
||||||
|
return ima_match_keyring(rule, keyring, cred);
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if ((rule->flags & IMA_FUNC) &&
|
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;
|
int rc = 0;
|
||||||
u32 osid;
|
u32 osid;
|
||||||
|
|
||||||
if (!rule->lsm[i].rule)
|
if (!rule->lsm[i].rule) {
|
||||||
continue;
|
if (!rule->lsm[i].args_p)
|
||||||
|
continue;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
switch (i) {
|
switch (i) {
|
||||||
case LSM_OBJ_USER:
|
case LSM_OBJ_USER:
|
||||||
case LSM_OBJ_ROLE:
|
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)
|
* @mask: requested action (MAY_READ | MAY_WRITE | MAY_APPEND | MAY_EXEC)
|
||||||
* @pcr: set the pcr to extend
|
* @pcr: set the pcr to extend
|
||||||
* @template_desc: the template that should be used for this rule
|
* @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)
|
* Measure decision based on func/mask/fsmagic and LSM(subj/obj/type)
|
||||||
* conditions.
|
* 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,
|
int ima_match_policy(struct inode *inode, const struct cred *cred, u32 secid,
|
||||||
enum ima_hooks func, int mask, int flags, int *pcr,
|
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;
|
struct ima_rule_entry *entry;
|
||||||
int action = 0, actmask = flags | (flags << 1);
|
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))
|
if (!(entry->action & actmask))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (!ima_match_rules(entry, inode, cred, secid, func, mask))
|
if (!ima_match_rules(entry, inode, cred, secid, func, mask,
|
||||||
|
keyring))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
action |= entry->flags & IMA_ACTION_FLAGS;
|
action |= entry->flags & IMA_ACTION_FLAGS;
|
||||||
|
@ -752,6 +809,9 @@ void ima_update_policy(void)
|
||||||
kfree(arch_policy_entry);
|
kfree(arch_policy_entry);
|
||||||
}
|
}
|
||||||
ima_update_policy_flag();
|
ima_update_policy_flag();
|
||||||
|
|
||||||
|
/* Custom IMA policy has been loaded */
|
||||||
|
ima_process_queued_keys();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Keep the enumeration in sync with the policy_tokens! */
|
/* 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_gt, Opt_euid_gt, Opt_fowner_gt,
|
||||||
Opt_uid_lt, Opt_euid_lt, Opt_fowner_lt,
|
Opt_uid_lt, Opt_euid_lt, Opt_fowner_lt,
|
||||||
Opt_appraise_type, Opt_appraise_flag,
|
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 = {
|
static const match_table_t policy_tokens = {
|
||||||
|
@ -802,6 +863,7 @@ static const match_table_t policy_tokens = {
|
||||||
{Opt_permit_directio, "permit_directio"},
|
{Opt_permit_directio, "permit_directio"},
|
||||||
{Opt_pcr, "pcr=%s"},
|
{Opt_pcr, "pcr=%s"},
|
||||||
{Opt_template, "template=%s"},
|
{Opt_template, "template=%s"},
|
||||||
|
{Opt_keyrings, "keyrings=%s"},
|
||||||
{Opt_err, NULL}
|
{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].args_p,
|
||||||
&entry->lsm[lsm_rule].rule);
|
&entry->lsm[lsm_rule].rule);
|
||||||
if (!entry->lsm[lsm_rule].rule) {
|
if (!entry->lsm[lsm_rule].rule) {
|
||||||
kfree(entry->lsm[lsm_rule].args_p);
|
pr_warn("rule for LSM \'%s\' is undefined\n",
|
||||||
return -EINVAL;
|
(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;
|
return result;
|
||||||
|
@ -889,6 +957,7 @@ 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);
|
||||||
|
@ -997,6 +1066,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)
|
||||||
|
entry->func = KEY_CHECK;
|
||||||
else
|
else
|
||||||
result = -EINVAL;
|
result = -EINVAL;
|
||||||
if (!result)
|
if (!result)
|
||||||
|
@ -1049,6 +1120,44 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
|
||||||
result = 0;
|
result = 0;
|
||||||
entry->flags |= IMA_FSNAME;
|
entry->flags |= IMA_FSNAME;
|
||||||
break;
|
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:
|
case Opt_fsuuid:
|
||||||
ima_log_string(ab, "fsuuid", args[0].from);
|
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, " ");
|
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) {
|
if (entry->flags & IMA_PCR) {
|
||||||
snprintf(tbuf, sizeof(tbuf), "%d", entry->pcr);
|
snprintf(tbuf, sizeof(tbuf), "%d", entry->pcr);
|
||||||
seq_printf(m, pt(Opt_pcr), tbuf);
|
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);
|
(char *)entry->lsm[i].args_p);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
seq_puts(m, " ");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (entry->template)
|
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/security.h>
|
||||||
#include <linux/workqueue.h>
|
#include <linux/workqueue.h>
|
||||||
#include <linux/random.h>
|
#include <linux/random.h>
|
||||||
|
#include <linux/ima.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include "internal.h"
|
#include "internal.h"
|
||||||
|
|
||||||
|
@ -936,6 +937,9 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
|
||||||
goto error_link_end;
|
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));
|
key_ref = make_key_ref(key, is_key_possessed(keyring_ref));
|
||||||
|
|
||||||
error_link_end:
|
error_link_end:
|
||||||
|
@ -965,6 +969,12 @@ error:
|
||||||
}
|
}
|
||||||
|
|
||||||
key_ref = __key_update(key_ref, &prep);
|
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;
|
goto error_free_prep;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(key_create_or_update);
|
EXPORT_SYMBOL(key_create_or_update);
|
||||||
|
|
Загрузка…
Ссылка в новой задаче