Commit 73a0bff2 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'next-integrity' of...

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
parents 2cf64d7c d54e17b4
...@@ -25,11 +25,11 @@ Description: ...@@ -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: ...@@ -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: ...@@ -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); ...@@ -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) ...@@ -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) ...@@ -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 ...@@ -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 ...@@ -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) ...@@ -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[]; ...@@ -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, ...@@ -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); ...@@ -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 @@ void ima_add_violation(struct file *file, const unsigned char *filename, ...@@ -169,12 +169,13 @@ void ima_add_violation(struct file *file, const unsigned char *filename,
* @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 @@ void ima_add_violation(struct file *file, const unsigned char *filename, ...@@ -183,14 +184,15 @@ void ima_add_violation(struct file *file, const unsigned char *filename,
*/ */
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) ...@@ -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, ...@@ -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;
......
// 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, ...@@ -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) ...@@ -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, ...@@ -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) ...@@ -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) ...@@ -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, ...@@ -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, ...@@ -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) ...@@ -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 @@ ...@@ -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 { ...@@ -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); ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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, ...@@ -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, ...@@ -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) {
if (!rule->lsm[i].args_p)
continue; 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) ...@@ -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) ...@@ -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, ...@@ -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) ...@@ -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 { ...@@ -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 = { ...@@ -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, ...@@ -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) {
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); kfree(entry->lsm[lsm_rule].args_p);
return -EINVAL; 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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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)
......
// 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 @@ ...@@ -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, ...@@ -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 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, ...@@ -965,6 +969,12 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
} }
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);
......
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