diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2020-01-28 18:52:09 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2020-01-28 18:52:09 -0800 |
commit | 73a0bff2058f2403c604371c325fec737ac2ac61 (patch) | |
tree | b862ab7ec0b6bf155cc491ec34343ac1df25bfa9 | |
parent | 2cf64d7cb20b04cf25e4ebffc37833298f1d4bde (diff) | |
parent | d54e17b4066612d88c4ef3e5fb3115f12733763d (diff) |
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
-rw-r--r-- | Documentation/ABI/testing/ima_policy | 16 | ||||
-rw-r--r-- | include/linux/ima.h | 20 | ||||
-rw-r--r-- | security/integrity/ima/Kconfig | 12 | ||||
-rw-r--r-- | security/integrity/ima/Makefile | 2 | ||||
-rw-r--r-- | security/integrity/ima/ima.h | 33 | ||||
-rw-r--r-- | security/integrity/ima/ima_api.c | 8 | ||||
-rw-r--r-- | security/integrity/ima/ima_appraise.c | 4 | ||||
-rw-r--r-- | security/integrity/ima/ima_asymmetric_keys.c | 66 | ||||
-rw-r--r-- | security/integrity/ima/ima_crypto.c | 4 | ||||
-rw-r--r-- | security/integrity/ima/ima_init.c | 8 | ||||
-rw-r--r-- | security/integrity/ima/ima_main.c | 61 | ||||
-rw-r--r-- | security/integrity/ima/ima_policy.c | 165 | ||||
-rw-r--r-- | security/integrity/ima/ima_queue_keys.c | 171 | ||||
-rw-r--r-- | security/keys/key.c | 10 |
14 files changed, 540 insertions, 40 deletions
diff --git a/Documentation/ABI/testing/ima_policy b/Documentation/ABI/testing/ima_policy index 29aaedf33246..cd572912c593 100644 --- a/Documentation/ABI/testing/ima_policy +++ b/Documentation/ABI/testing/ima_policy @@ -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 diff --git a/include/linux/ima.h b/include/linux/ima.h index 6d904754d858..1659217e9b60 100644 --- a/include/linux/ima.h +++ b/include/linux/ima.h @@ -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); diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig index 838476d780e5..711ff10fa36e 100644 --- a/security/integrity/ima/Kconfig +++ b/security/integrity/ima/Kconfig @@ -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 diff --git a/security/integrity/ima/Makefile b/security/integrity/ima/Makefile index 31d57cdf2421..064a256f8725 100644 --- a/security/integrity/ima/Makefile +++ b/security/integrity/ima/Makefile @@ -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 diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index df4ca482fb53..64317d95363e 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -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); diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c index 610759fe63b8..f6bc00914aa5 100644 --- a/security/integrity/ima/ima_api.c +++ b/security/integrity/ima/ima_api.c @@ -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); } /* diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index 300c8d2943c5..a9649b04b9f1 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -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; diff --git a/security/integrity/ima/ima_asymmetric_keys.c b/security/integrity/ima/ima_asymmetric_keys.c new file mode 100644 index 000000000000..7678f0e3e84d --- /dev/null +++ b/security/integrity/ima/ima_asymmetric_keys.c @@ -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); +} diff --git a/security/integrity/ima/ima_crypto.c b/security/integrity/ima/ima_crypto.c index 73044fc6a952..7967a6904851 100644 --- a/security/integrity/ima/ima_crypto.c +++ b/security/integrity/ima/ima_crypto.c @@ -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); diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c index 5d55ade5f3b9..195cb4079b2b 100644 --- a/security/integrity/ima/ima_init.c +++ b/security/integrity/ima/ima_init.c @@ -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; } diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index d7e987baf127..9fe949c6a530 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -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) @@ -446,6 +446,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) diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index ef8dfd47c7e3..453427048999 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -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) diff --git a/security/integrity/ima/ima_queue_keys.c b/security/integrity/ima/ima_queue_keys.c new file mode 100644 index 000000000000..c87c72299191 --- /dev/null +++ b/security/integrity/ima/ima_queue_keys.c @@ -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; +} diff --git a/security/keys/key.c b/security/keys/key.c index 764f4c57913e..718bf7217420 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -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); |