Commit 26b1959f authored by Michael Ellerman's avatar Michael Ellerman

Merge branch 'topic/ima' into topic/secureboot

From Nayna's cover letter:
  The IMA subsystem supports custom, built-in, arch-specific policies
  to define the files to be measured and appraised. These policies are
  honored based on priority, where arch-specific policy is the highest
  and custom is the lowest.

  PowerNV systems use a Linux-based bootloader to kexec the OS. The
  bootloader kernel relies on IMA for signature verification of the OS
  kernel before doing the kexec. This patchset adds support for
  powerpc arch-specific IMA policies that are conditionally defined
  based on a system's secure boot and trusted boot states. The OS
  secure boot and trusted boot states are determined via device-tree
  properties.

  The verification needs to be performed only for binaries that are
  not blacklisted. The kernel currently only checks against the
  blacklist of keys. However, doing so results in blacklisting all the
  binaries that are signed by the same key. In order to prevent just
  one particular binary from being loaded, it must be checked against
  a blacklist of binary hashes. This patchset also adds support to IMA
  for checking against a hash blacklist for files. signed by appended
  signature.
parents da0c9ea1 d72ea491
...@@ -25,6 +25,7 @@ Description: ...@@ -25,6 +25,7 @@ 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=]
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]
...@@ -38,6 +39,9 @@ Description: ...@@ -38,6 +39,9 @@ Description:
fowner:= decimal value fowner:= decimal value
lsm: are LSM specific lsm: are LSM specific
option: appraise_type:= [imasig] [imasig|modsig] option: appraise_type:= [imasig] [imasig|modsig]
appraise_flag:= [check_blacklist]
Currently, blacklist check is only for files signed with appended
signature.
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
......
...@@ -934,6 +934,17 @@ config PPC_MEM_KEYS ...@@ -934,6 +934,17 @@ config PPC_MEM_KEYS
If unsure, say y. If unsure, say y.
config PPC_SECURE_BOOT
prompt "Enable secure boot support"
bool
depends on PPC_POWERNV
depends on IMA_ARCH_POLICY
help
Systems with firmware secure boot enabled need to define security
policies to extend secure boot to the OS. This config allows a user
to enable OS secure boot on systems that have firmware support for
it. If in doubt say N.
endmenu endmenu
config ISA_DMA_API config ISA_DMA_API
......
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Secure boot definitions
*
* Copyright (C) 2019 IBM Corporation
* Author: Nayna Jain
*/
#ifndef _ASM_POWER_SECURE_BOOT_H
#define _ASM_POWER_SECURE_BOOT_H
#ifdef CONFIG_PPC_SECURE_BOOT
bool is_ppc_secureboot_enabled(void);
bool is_ppc_trustedboot_enabled(void);
#else
static inline bool is_ppc_secureboot_enabled(void)
{
return false;
}
static inline bool is_ppc_trustedboot_enabled(void)
{
return false;
}
#endif
#endif
...@@ -161,6 +161,8 @@ ifneq ($(CONFIG_PPC_POWERNV)$(CONFIG_PPC_SVM),) ...@@ -161,6 +161,8 @@ ifneq ($(CONFIG_PPC_POWERNV)$(CONFIG_PPC_SVM),)
obj-y += ucall.o obj-y += ucall.o
endif endif
obj-$(CONFIG_PPC_SECURE_BOOT) += secure_boot.o ima_arch.o
# Disable GCOV, KCOV & sanitizers in odd or sensitive code # Disable GCOV, KCOV & sanitizers in odd or sensitive code
GCOV_PROFILE_prom_init.o := n GCOV_PROFILE_prom_init.o := n
KCOV_INSTRUMENT_prom_init.o := n KCOV_INSTRUMENT_prom_init.o := n
......
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2019 IBM Corporation
* Author: Nayna Jain
*/
#include <linux/ima.h>
#include <asm/secure_boot.h>
bool arch_ima_get_secureboot(void)
{
return is_ppc_secureboot_enabled();
}
/*
* The "secure_rules" are enabled only on "secureboot" enabled systems.
* These rules verify the file signatures against known good values.
* The "appraise_type=imasig|modsig" option allows the known good signature
* to be stored as an xattr or as an appended signature.
*
* To avoid duplicate signature verification as much as possible, the IMA
* policy rule for module appraisal is added only if CONFIG_MODULE_SIG_FORCE
* is not enabled.
*/
static const char *const secure_rules[] = {
"appraise func=KEXEC_KERNEL_CHECK appraise_flag=check_blacklist appraise_type=imasig|modsig",
#ifndef CONFIG_MODULE_SIG_FORCE
"appraise func=MODULE_CHECK appraise_flag=check_blacklist appraise_type=imasig|modsig",
#endif
NULL
};
/*
* The "trusted_rules" are enabled only on "trustedboot" enabled systems.
* These rules add the kexec kernel image and kernel modules file hashes to
* the IMA measurement list.
*/
static const char *const trusted_rules[] = {
"measure func=KEXEC_KERNEL_CHECK",
"measure func=MODULE_CHECK",
NULL
};
/*
* The "secure_and_trusted_rules" contains rules for both the secure boot and
* trusted boot. The "template=ima-modsig" option includes the appended
* signature, when available, in the IMA measurement list.
*/
static const char *const secure_and_trusted_rules[] = {
"measure func=KEXEC_KERNEL_CHECK template=ima-modsig",
"measure func=MODULE_CHECK template=ima-modsig",
"appraise func=KEXEC_KERNEL_CHECK appraise_flag=check_blacklist appraise_type=imasig|modsig",
#ifndef CONFIG_MODULE_SIG_FORCE
"appraise func=MODULE_CHECK appraise_flag=check_blacklist appraise_type=imasig|modsig",
#endif
NULL
};
/*
* Returns the relevant IMA arch-specific policies based on the system secure
* boot state.
*/
const char *const *arch_get_ima_policy(void)
{
if (is_ppc_secureboot_enabled()) {
if (IS_ENABLED(CONFIG_MODULE_SIG))
set_module_sig_enforced();
if (is_ppc_trustedboot_enabled())
return secure_and_trusted_rules;
else
return secure_rules;
} else if (is_ppc_trustedboot_enabled()) {
return trusted_rules;
}
return NULL;
}
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2019 IBM Corporation
* Author: Nayna Jain
*/
#include <linux/types.h>
#include <linux/of.h>
#include <asm/secure_boot.h>
static struct device_node *get_ppc_fw_sb_node(void)
{
static const struct of_device_id ids[] = {
{ .compatible = "ibm,secureboot", },
{ .compatible = "ibm,secureboot-v1", },
{ .compatible = "ibm,secureboot-v2", },
{},
};
return of_find_matching_node(NULL, ids);
}
bool is_ppc_secureboot_enabled(void)
{
struct device_node *node;
bool enabled = false;
node = get_ppc_fw_sb_node();
enabled = of_property_read_bool(node, "os-secureboot-enforcing");
of_node_put(node);
pr_info("Secure boot mode %s\n", enabled ? "enabled" : "disabled");
return enabled;
}
bool is_ppc_trustedboot_enabled(void)
{
struct device_node *node;
bool enabled = false;
node = get_ppc_fw_sb_node();
enabled = of_property_read_bool(node, "trusted-enabled");
of_node_put(node);
pr_info("Trusted boot mode %s\n", enabled ? "enabled" : "disabled");
return enabled;
}
...@@ -135,6 +135,15 @@ int is_hash_blacklisted(const u8 *hash, size_t hash_len, const char *type) ...@@ -135,6 +135,15 @@ int is_hash_blacklisted(const u8 *hash, size_t hash_len, const char *type)
} }
EXPORT_SYMBOL_GPL(is_hash_blacklisted); EXPORT_SYMBOL_GPL(is_hash_blacklisted);
int is_binary_blacklisted(const u8 *hash, size_t hash_len)
{
if (is_hash_blacklisted(hash, hash_len, "bin") == -EKEYREJECTED)
return -EPERM;
return 0;
}
EXPORT_SYMBOL_GPL(is_binary_blacklisted);
/* /*
* Initialise the blacklist * Initialise the blacklist
*/ */
......
...@@ -35,12 +35,18 @@ extern int restrict_link_by_builtin_and_secondary_trusted( ...@@ -35,12 +35,18 @@ extern int restrict_link_by_builtin_and_secondary_trusted(
extern int mark_hash_blacklisted(const char *hash); extern int mark_hash_blacklisted(const char *hash);
extern int is_hash_blacklisted(const u8 *hash, size_t hash_len, extern int is_hash_blacklisted(const u8 *hash, size_t hash_len,
const char *type); const char *type);
extern int is_binary_blacklisted(const u8 *hash, size_t hash_len);
#else #else
static inline int is_hash_blacklisted(const u8 *hash, size_t hash_len, static inline int is_hash_blacklisted(const u8 *hash, size_t hash_len,
const char *type) const char *type)
{ {
return 0; return 0;
} }
static inline int is_binary_blacklisted(const u8 *hash, size_t hash_len)
{
return 0;
}
#endif #endif
#ifdef CONFIG_IMA_BLACKLIST_KEYRING #ifdef CONFIG_IMA_BLACKLIST_KEYRING
......
...@@ -29,7 +29,8 @@ extern void ima_kexec_cmdline(const void *buf, int size); ...@@ -29,7 +29,8 @@ extern void ima_kexec_cmdline(const void *buf, int size);
extern void ima_add_kexec_buffer(struct kimage *image); extern void ima_add_kexec_buffer(struct kimage *image);
#endif #endif
#if (defined(CONFIG_X86) && defined(CONFIG_EFI)) || defined(CONFIG_S390) #if (defined(CONFIG_X86) && defined(CONFIG_EFI)) || defined(CONFIG_S390) \
|| defined(CONFIG_PPC_SECURE_BOOT)
extern bool arch_ima_get_secureboot(void); extern bool arch_ima_get_secureboot(void);
extern const char * const *arch_get_ima_policy(void); extern const char * const *arch_get_ima_policy(void);
#else #else
......
...@@ -217,6 +217,9 @@ void ima_store_measurement(struct integrity_iint_cache *iint, struct file *file, ...@@ -217,6 +217,9 @@ void ima_store_measurement(struct integrity_iint_cache *iint, struct file *file,
struct evm_ima_xattr_data *xattr_value, struct evm_ima_xattr_data *xattr_value,
int xattr_len, const struct modsig *modsig, int pcr, int xattr_len, const struct modsig *modsig, int pcr,
struct ima_template_desc *template_desc); struct ima_template_desc *template_desc);
void process_buffer_measurement(const void *buf, int size,
const char *eventname, enum ima_hooks func,
int pcr);
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,
...@@ -253,6 +256,8 @@ int ima_policy_show(struct seq_file *m, void *v); ...@@ -253,6 +256,8 @@ int ima_policy_show(struct seq_file *m, void *v);
#define IMA_APPRAISE_KEXEC 0x40 #define IMA_APPRAISE_KEXEC 0x40
#ifdef CONFIG_IMA_APPRAISE #ifdef CONFIG_IMA_APPRAISE
int ima_check_blacklist(struct integrity_iint_cache *iint,
const struct modsig *modsig, int pcr);
int ima_appraise_measurement(enum ima_hooks func, int ima_appraise_measurement(enum ima_hooks func,
struct integrity_iint_cache *iint, struct integrity_iint_cache *iint,
struct file *file, const unsigned char *filename, struct file *file, const unsigned char *filename,
...@@ -268,6 +273,12 @@ int ima_read_xattr(struct dentry *dentry, ...@@ -268,6 +273,12 @@ int ima_read_xattr(struct dentry *dentry,
struct evm_ima_xattr_data **xattr_value); struct evm_ima_xattr_data **xattr_value);
#else #else
static inline int ima_check_blacklist(struct integrity_iint_cache *iint,
const struct modsig *modsig, int pcr)
{
return 0;
}
static inline int ima_appraise_measurement(enum ima_hooks func, static inline int ima_appraise_measurement(enum ima_hooks func,
struct integrity_iint_cache *iint, struct integrity_iint_cache *iint,
struct file *file, struct file *file,
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include <linux/magic.h> #include <linux/magic.h>
#include <linux/ima.h> #include <linux/ima.h>
#include <linux/evm.h> #include <linux/evm.h>
#include <keys/system_keyring.h>
#include "ima.h" #include "ima.h"
...@@ -303,6 +304,38 @@ static int modsig_verify(enum ima_hooks func, const struct modsig *modsig, ...@@ -303,6 +304,38 @@ static int modsig_verify(enum ima_hooks func, const struct modsig *modsig,
return rc; return rc;
} }
/*
* ima_check_blacklist - determine if the binary is blacklisted.
*
* Add the hash of the blacklisted binary to the measurement list, based
* on policy.
*
* Returns -EPERM if the hash is blacklisted.
*/
int ima_check_blacklist(struct integrity_iint_cache *iint,
const struct modsig *modsig, int pcr)
{
enum hash_algo hash_algo;
const u8 *digest = NULL;
u32 digestsize = 0;
int rc = 0;
if (!(iint->flags & IMA_CHECK_BLACKLIST))
return 0;
if (iint->flags & IMA_MODSIG_ALLOWED && modsig) {
ima_get_modsig_digest(modsig, &hash_algo, &digest, &digestsize);
rc = is_binary_blacklisted(digest, digestsize);
if ((rc == -EPERM) && (iint->flags & IMA_MEASURE))
process_buffer_measurement(digest, digestsize,
"blacklisted-hash", NONE,
pcr);
}
return rc;
}
/* /*
* ima_appraise_measurement - appraise file measurement * ima_appraise_measurement - appraise file measurement
* *
......
...@@ -335,10 +335,14 @@ static int process_measurement(struct file *file, const struct cred *cred, ...@@ -335,10 +335,14 @@ static int process_measurement(struct file *file, const struct cred *cred,
xattr_value, xattr_len, modsig, pcr, xattr_value, xattr_len, modsig, pcr,
template_desc); template_desc);
if (rc == 0 && (action & IMA_APPRAISE_SUBMASK)) { if (rc == 0 && (action & IMA_APPRAISE_SUBMASK)) {
inode_lock(inode); rc = ima_check_blacklist(iint, modsig, pcr);
rc = ima_appraise_measurement(func, iint, file, pathname, if (rc != -EPERM) {
xattr_value, xattr_len, modsig); inode_lock(inode);
inode_unlock(inode); rc = ima_appraise_measurement(func, iint, file,
pathname, xattr_value,
xattr_len, modsig);
inode_unlock(inode);
}
if (!rc) if (!rc)
rc = mmap_violation_check(func, file, &pathbuf, rc = mmap_violation_check(func, file, &pathbuf,
&pathname, filename); &pathname, filename);
...@@ -626,14 +630,14 @@ int ima_load_data(enum kernel_load_data_id id) ...@@ -626,14 +630,14 @@ int ima_load_data(enum kernel_load_data_id id)
* @buf: pointer to the buffer that needs to be added to the log. * @buf: pointer to the buffer that needs to be added to the log.
* @size: size of buffer(in bytes). * @size: size of buffer(in bytes).
* @eventname: event name to be used for the buffer entry. * @eventname: event name to be used for the buffer entry.
* @cred: a pointer to a credentials structure for user validation. * @func: IMA hook
* @secid: the secid of the task to be validated. * @pcr: pcr to extend the measurement
* *
* Based on policy, the buffer is measured into the ima log. * Based on policy, the buffer is measured into the ima log.
*/ */
static void process_buffer_measurement(const void *buf, int size, void process_buffer_measurement(const void *buf, int size,
const char *eventname, const char *eventname, enum ima_hooks func,
const struct cred *cred, u32 secid) int pcr)
{ {
int ret = 0; int ret = 0;
struct ima_template_entry *entry = NULL; struct ima_template_entry *entry = NULL;
...@@ -642,19 +646,45 @@ static void process_buffer_measurement(const void *buf, int size, ...@@ -642,19 +646,45 @@ static void process_buffer_measurement(const void *buf, int size,
.filename = eventname, .filename = eventname,
.buf = buf, .buf = buf,
.buf_len = size}; .buf_len = size};
struct ima_template_desc *template_desc = NULL; struct ima_template_desc *template = NULL;
struct { struct {
struct ima_digest_data hdr; struct ima_digest_data hdr;
char digest[IMA_MAX_DIGEST_SIZE]; char digest[IMA_MAX_DIGEST_SIZE];
} hash = {}; } hash = {};
int violation = 0; int violation = 0;
int pcr = CONFIG_IMA_MEASURE_PCR_IDX;
int action = 0; int action = 0;
u32 secid;
action = ima_get_action(NULL, cred, secid, 0, KEXEC_CMDLINE, &pcr, /*
&template_desc); * Both LSM hooks and auxilary based buffer measurements are
if (!(action & IMA_MEASURE)) * based on policy. To avoid code duplication, differentiate
return; * between the LSM hooks and auxilary buffer measurements,
* retrieving the policy rule information only for the LSM hook
* buffer measurements.
*/
if (func) {
security_task_getsecid(current, &secid);
action = ima_get_action(NULL, current_cred(), secid, 0, func,
&pcr, &template);
if (!(action & IMA_MEASURE))
return;
}
if (!pcr)
pcr = CONFIG_IMA_MEASURE_PCR_IDX;
if (!template) {
template = lookup_template_desc("ima-buf");
ret = template_desc_init_fields(template->fmt,
&(template->fields),
&(template->num_fields));
if (ret < 0) {
pr_err("template %s init failed, result: %d\n",
(strlen(template->name) ?
template->name : template->fmt), ret);
return;
}
}
iint.ima_hash = &hash.hdr; iint.ima_hash = &hash.hdr;
iint.ima_hash->algo = ima_hash_algo; iint.ima_hash->algo = ima_hash_algo;
...@@ -664,7 +694,7 @@ static void process_buffer_measurement(const void *buf, int size, ...@@ -664,7 +694,7 @@ static void process_buffer_measurement(const void *buf, int size,
if (ret < 0) if (ret < 0)
goto out; goto out;
ret = ima_alloc_init_template(&event_data, &entry, template_desc); ret = ima_alloc_init_template(&event_data, &entry, template);
if (ret < 0) if (ret < 0)
goto out; goto out;
...@@ -686,13 +716,9 @@ static void process_buffer_measurement(const void *buf, int size, ...@@ -686,13 +716,9 @@ static void process_buffer_measurement(const void *buf, int size,
*/ */
void ima_kexec_cmdline(const void *buf, int size) void ima_kexec_cmdline(const void *buf, int size)
{ {
u32 secid; if (buf && size != 0)
if (buf && size != 0) {
security_task_getsecid(current, &secid);
process_buffer_measurement(buf, size, "kexec-cmdline", process_buffer_measurement(buf, size, "kexec-cmdline",
current_cred(), secid); KEXEC_CMDLINE, 0);
}
} }
static int __init init_ima(void) static int __init init_ima(void)
......
...@@ -765,8 +765,8 @@ enum { ...@@ -765,8 +765,8 @@ enum {
Opt_fsuuid, Opt_uid_eq, Opt_euid_eq, Opt_fowner_eq, Opt_fsuuid, Opt_uid_eq, Opt_euid_eq, Opt_fowner_eq,
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_permit_directio, Opt_appraise_type, Opt_appraise_flag,
Opt_pcr, Opt_template, Opt_err Opt_permit_directio, Opt_pcr, Opt_template, Opt_err
}; };
static const match_table_t policy_tokens = { static const match_table_t policy_tokens = {
...@@ -798,6 +798,7 @@ static const match_table_t policy_tokens = { ...@@ -798,6 +798,7 @@ static const match_table_t policy_tokens = {
{Opt_euid_lt, "euid<%s"}, {Opt_euid_lt, "euid<%s"},
{Opt_fowner_lt, "fowner<%s"}, {Opt_fowner_lt, "fowner<%s"},
{Opt_appraise_type, "appraise_type=%s"}, {Opt_appraise_type, "appraise_type=%s"},
{Opt_appraise_flag, "appraise_flag=%s"},
{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"},
...@@ -1172,6 +1173,11 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) ...@@ -1172,6 +1173,11 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
else else
result = -EINVAL; result = -EINVAL;
break; break;
case Opt_appraise_flag:
ima_log_string(ab, "appraise_flag", args[0].from);
if (strstr(args[0].from, "blacklist"))
entry->flags |= IMA_CHECK_BLACKLIST;
break;
case Opt_permit_directio: case Opt_permit_directio:
entry->flags |= IMA_PERMIT_DIRECTIO; entry->flags |= IMA_PERMIT_DIRECTIO;
break; break;
...@@ -1500,6 +1506,8 @@ int ima_policy_show(struct seq_file *m, void *v) ...@@ -1500,6 +1506,8 @@ int ima_policy_show(struct seq_file *m, void *v)
else else
seq_puts(m, "appraise_type=imasig "); seq_puts(m, "appraise_type=imasig ");
} }
if (entry->flags & IMA_CHECK_BLACKLIST)
seq_puts(m, "appraise_flag=check_blacklist ");
if (entry->flags & IMA_PERMIT_DIRECTIO) if (entry->flags & IMA_PERMIT_DIRECTIO)
seq_puts(m, "permit_directio "); seq_puts(m, "permit_directio ");
rcu_read_unlock(); rcu_read_unlock();
......
...@@ -32,6 +32,7 @@ ...@@ -32,6 +32,7 @@
#define EVM_IMMUTABLE_DIGSIG 0x08000000 #define EVM_IMMUTABLE_DIGSIG 0x08000000
#define IMA_FAIL_UNVERIFIABLE_SIGS 0x10000000 #define IMA_FAIL_UNVERIFIABLE_SIGS 0x10000000
#define IMA_MODSIG_ALLOWED 0x20000000 #define IMA_MODSIG_ALLOWED 0x20000000
#define IMA_CHECK_BLACKLIST 0x40000000
#define IMA_DO_MASK (IMA_MEASURE | IMA_APPRAISE | IMA_AUDIT | \ #define IMA_DO_MASK (IMA_MEASURE | IMA_APPRAISE | IMA_AUDIT | \
IMA_HASH | IMA_APPRAISE_SUBMASK) IMA_HASH | IMA_APPRAISE_SUBMASK)
......
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