Merge branch 'topic/secureboot' into next

Merge the secureboot support, as well as the IMA changes needed to
support it.

From Nayna's cover letter:
  In order to verify the OS kernel on PowerNV systems, secure boot
  requires X.509 certificates trusted by the platform. These are
  stored in secure variables controlled by OPAL, called OPAL secure
  variables. In order to enable users to manage the keys, the secure
  variables need to be exposed to userspace.

  OPAL provides the runtime services for the kernel to be able to
  access the secure variables. This patchset defines the kernel
  interface for the OPAL APIs. These APIs are used by the hooks, which
  load these variables to the keyring and expose them to the userspace
  for reading/writing.

  Overall, this patchset adds the following support:
    * expose secure variables to the kernel via OPAL Runtime API interface
    * expose secure variables to the userspace via kernel sysfs interface
    * load kernel verification and revocation keys to .platform and
      .blacklist keyring respectively.

  The secure variables can be read/written using simple linux
  utilities cat/hexdump.

  For example:
  Path to the secure variables is: /sys/firmware/secvar/vars

    Each secure variable is listed as directory.
    $ ls -l
    total 0
    drwxr-xr-x. 2 root root 0 Aug 20 21:20 db
    drwxr-xr-x. 2 root root 0 Aug 20 21:20 KEK
    drwxr-xr-x. 2 root root 0 Aug 20 21:20 PK

  The attributes of each of the secure variables are (for example: PK):
    $ ls -l
    total 0
    -r--r--r--. 1 root root  4096 Oct  1 15:10 data
    -r--r--r--. 1 root root 65536 Oct  1 15:10 size
    --w-------. 1 root root  4096 Oct  1 15:12 update

  The "data" is used to read the existing variable value using
  hexdump. The data is stored in ESL format. The "update" is used to
  write a new value using cat. The update is to be submitted as AUTH
  file.
This commit is contained in:
Michael Ellerman 2019-11-13 16:52:25 +11:00
commit d34a5709be
31 changed files with 1035 additions and 96 deletions

View File

@ -25,6 +25,7 @@ Description:
lsm: [[subj_user=] [subj_role=] [subj_type=]
[obj_user=] [obj_role=] [obj_type=]]
option: [[appraise_type=]] [template=] [permit_directio]
[appraise_flag=]
base: func:= [BPRM_CHECK][MMAP_CHECK][CREDS_CHECK][FILE_CHECK][MODULE_CHECK]
[FIRMWARE_CHECK]
[KEXEC_KERNEL_CHECK] [KEXEC_INITRAMFS_CHECK]
@ -38,6 +39,9 @@ Description:
fowner:= decimal value
lsm: are LSM specific
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
(eg, ima-ng). Only valid when action is "measure".
pcr:= decimal value

View File

@ -0,0 +1,46 @@
What: /sys/firmware/secvar
Date: August 2019
Contact: Nayna Jain <nayna@linux.ibm.com>
Description: This directory is created if the POWER firmware supports OS
secureboot, thereby secure variables. It exposes interface
for reading/writing the secure variables
What: /sys/firmware/secvar/vars
Date: August 2019
Contact: Nayna Jain <nayna@linux.ibm.com>
Description: This directory lists all the secure variables that are supported
by the firmware.
What: /sys/firmware/secvar/format
Date: August 2019
Contact: Nayna Jain <nayna@linux.ibm.com>
Description: A string indicating which backend is in use by the firmware.
This determines the format of the variable and the accepted
format of variable updates.
What: /sys/firmware/secvar/vars/<variable name>
Date: August 2019
Contact: Nayna Jain <nayna@linux.ibm.com>
Description: Each secure variable is represented as a directory named as
<variable_name>. The variable name is unique and is in ASCII
representation. The data and size can be determined by reading
their respective attribute files.
What: /sys/firmware/secvar/vars/<variable_name>/size
Date: August 2019
Contact: Nayna Jain <nayna@linux.ibm.com>
Description: An integer representation of the size of the content of the
variable. In other words, it represents the size of the data.
What: /sys/firmware/secvar/vars/<variable_name>/data
Date: August 2019
Contact: Nayna Jain h<nayna@linux.ibm.com>
Description: A read-only file containing the value of the variable. The size
of the file represents the maximum size of the variable data.
What: /sys/firmware/secvar/vars/<variable_name>/update
Date: August 2019
Contact: Nayna Jain <nayna@linux.ibm.com>
Description: A write-only file that is used to submit the new value for the
variable. The size of the file represents the maximum size of
the variable data that can be written.

View File

@ -952,6 +952,28 @@ config PPC_MEM_KEYS
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.
config PPC_SECVAR_SYSFS
bool "Enable sysfs interface for POWER secure variables"
default y
depends on PPC_SECURE_BOOT
depends on SYSFS
help
POWER secure variables are managed and controlled by firmware.
These variables are exposed to userspace via sysfs to enable
read/write operations on these variables. Say Y if you have
secure boot enabled and want to expose variables to userspace.
endmenu
config ISA_DMA_API

View File

@ -211,7 +211,10 @@
#define OPAL_MPIPL_UPDATE 173
#define OPAL_MPIPL_REGISTER_TAG 174
#define OPAL_MPIPL_QUERY_TAG 175
#define OPAL_LAST 175
#define OPAL_SECVAR_GET 176
#define OPAL_SECVAR_GET_NEXT 177
#define OPAL_SECVAR_ENQUEUE_UPDATE 178
#define OPAL_LAST 178
#define QUIESCE_HOLD 1 /* Spin all calls at entry */
#define QUIESCE_REJECT 2 /* Fail all calls with OPAL_BUSY */

View File

@ -298,6 +298,13 @@ int opal_sensor_group_clear(u32 group_hndl, int token);
int opal_sensor_group_enable(u32 group_hndl, int token, bool enable);
int opal_nx_coproc_init(uint32_t chip_id, uint32_t ct);
int opal_secvar_get(const char *key, uint64_t key_len, u8 *data,
uint64_t *data_size);
int opal_secvar_get_next(const char *key, uint64_t *key_len,
uint64_t key_buf_size);
int opal_secvar_enqueue_update(const char *key, uint64_t key_len, u8 *data,
uint64_t data_size);
s64 opal_mpipl_update(enum opal_mpipl_ops op, u64 src, u64 dest, u64 size);
s64 opal_mpipl_register_tag(enum opal_mpipl_tags tag, u64 addr);
s64 opal_mpipl_query_tag(enum opal_mpipl_tags tag, u64 *addr);

View File

@ -0,0 +1,29 @@
/* 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

View File

@ -0,0 +1,35 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2019 IBM Corporation
* Author: Nayna Jain
*
* PowerPC secure variable operations.
*/
#ifndef SECVAR_OPS_H
#define SECVAR_OPS_H
#include <linux/types.h>
#include <linux/errno.h>
extern const struct secvar_operations *secvar_ops;
struct secvar_operations {
int (*get)(const char *key, uint64_t key_len, u8 *data,
uint64_t *data_size);
int (*get_next)(const char *key, uint64_t *key_len,
uint64_t keybufsize);
int (*set)(const char *key, uint64_t key_len, u8 *data,
uint64_t data_size);
};
#ifdef CONFIG_PPC_SECURE_BOOT
extern void set_secvar_ops(const struct secvar_operations *ops);
#else
static inline void set_secvar_ops(const struct secvar_operations *ops) { }
#endif
#endif

View File

@ -160,6 +160,9 @@ ifneq ($(CONFIG_PPC_POWERNV)$(CONFIG_PPC_SVM),)
obj-y += ucall.o
endif
obj-$(CONFIG_PPC_SECURE_BOOT) += secure_boot.o ima_arch.o secvar-ops.o
obj-$(CONFIG_PPC_SECVAR_SYSFS) += secvar-sysfs.o
# Disable GCOV, KCOV & sanitizers in odd or sensitive code
GCOV_PROFILE_prom_init.o := n
KCOV_INSTRUMENT_prom_init.o := n

View File

@ -0,0 +1,78 @@
// 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;
}

View File

@ -0,0 +1,50 @@
// 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;
}

View File

@ -0,0 +1,17 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2019 IBM Corporation
* Author: Nayna Jain
*
* This file initializes secvar operations for PowerPC Secureboot
*/
#include <linux/cache.h>
#include <asm/secvar.h>
const struct secvar_operations *secvar_ops __ro_after_init;
void set_secvar_ops(const struct secvar_operations *ops)
{
secvar_ops = ops;
}

View File

@ -0,0 +1,248 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2019 IBM Corporation <nayna@linux.ibm.com>
*
* This code exposes secure variables to user via sysfs
*/
#define pr_fmt(fmt) "secvar-sysfs: "fmt
#include <linux/slab.h>
#include <linux/compat.h>
#include <linux/string.h>
#include <linux/of.h>
#include <asm/secvar.h>
#define NAME_MAX_SIZE 1024
static struct kobject *secvar_kobj;
static struct kset *secvar_kset;
static ssize_t format_show(struct kobject *kobj, struct kobj_attribute *attr,
char *buf)
{
ssize_t rc = 0;
struct device_node *node;
const char *format;
node = of_find_compatible_node(NULL, NULL, "ibm,secvar-backend");
if (!of_device_is_available(node))
return -ENODEV;
rc = of_property_read_string(node, "format", &format);
if (rc)
return rc;
rc = sprintf(buf, "%s\n", format);
of_node_put(node);
return rc;
}
static ssize_t size_show(struct kobject *kobj, struct kobj_attribute *attr,
char *buf)
{
uint64_t dsize;
int rc;
rc = secvar_ops->get(kobj->name, strlen(kobj->name) + 1, NULL, &dsize);
if (rc) {
pr_err("Error retrieving %s variable size %d\n", kobj->name,
rc);
return rc;
}
return sprintf(buf, "%llu\n", dsize);
}
static ssize_t data_read(struct file *filep, struct kobject *kobj,
struct bin_attribute *attr, char *buf, loff_t off,
size_t count)
{
uint64_t dsize;
char *data;
int rc;
rc = secvar_ops->get(kobj->name, strlen(kobj->name) + 1, NULL, &dsize);
if (rc) {
pr_err("Error getting %s variable size %d\n", kobj->name, rc);
return rc;
}
pr_debug("dsize is %llu\n", dsize);
data = kzalloc(dsize, GFP_KERNEL);
if (!data)
return -ENOMEM;
rc = secvar_ops->get(kobj->name, strlen(kobj->name) + 1, data, &dsize);
if (rc) {
pr_err("Error getting %s variable %d\n", kobj->name, rc);
goto data_fail;
}
rc = memory_read_from_buffer(buf, count, &off, data, dsize);
data_fail:
kfree(data);
return rc;
}
static ssize_t update_write(struct file *filep, struct kobject *kobj,
struct bin_attribute *attr, char *buf, loff_t off,
size_t count)
{
int rc;
pr_debug("count is %ld\n", count);
rc = secvar_ops->set(kobj->name, strlen(kobj->name) + 1, buf, count);
if (rc) {
pr_err("Error setting the %s variable %d\n", kobj->name, rc);
return rc;
}
return count;
}
static struct kobj_attribute format_attr = __ATTR_RO(format);
static struct kobj_attribute size_attr = __ATTR_RO(size);
static struct bin_attribute data_attr = __BIN_ATTR_RO(data, 0);
static struct bin_attribute update_attr = __BIN_ATTR_WO(update, 0);
static struct bin_attribute *secvar_bin_attrs[] = {
&data_attr,
&update_attr,
NULL,
};
static struct attribute *secvar_attrs[] = {
&size_attr.attr,
NULL,
};
static const struct attribute_group secvar_attr_group = {
.attrs = secvar_attrs,
.bin_attrs = secvar_bin_attrs,
};
__ATTRIBUTE_GROUPS(secvar_attr);
static struct kobj_type secvar_ktype = {
.sysfs_ops = &kobj_sysfs_ops,
.default_groups = secvar_attr_groups,
};
static int update_kobj_size(void)
{
struct device_node *node;
u64 varsize;
int rc = 0;
node = of_find_compatible_node(NULL, NULL, "ibm,secvar-backend");
if (!of_device_is_available(node)) {
rc = -ENODEV;
goto out;
}
rc = of_property_read_u64(node, "max-var-size", &varsize);
if (rc)
goto out;
data_attr.size = varsize;
update_attr.size = varsize;
out:
of_node_put(node);
return rc;
}
static int secvar_sysfs_load(void)
{
char *name;
uint64_t namesize = 0;
struct kobject *kobj;
int rc;
name = kzalloc(NAME_MAX_SIZE, GFP_KERNEL);
if (!name)
return -ENOMEM;
do {
rc = secvar_ops->get_next(name, &namesize, NAME_MAX_SIZE);
if (rc) {
if (rc != -ENOENT)
pr_err("error getting secvar from firmware %d\n",
rc);
break;
}
kobj = kzalloc(sizeof(*kobj), GFP_KERNEL);
if (!kobj) {
rc = -ENOMEM;
break;
}
kobject_init(kobj, &secvar_ktype);
rc = kobject_add(kobj, &secvar_kset->kobj, "%s", name);
if (rc) {
pr_warn("kobject_add error %d for attribute: %s\n", rc,
name);
kobject_put(kobj);
kobj = NULL;
}
if (kobj)
kobject_uevent(kobj, KOBJ_ADD);
} while (!rc);
kfree(name);
return rc;
}
static int secvar_sysfs_init(void)
{
int rc;
if (!secvar_ops) {
pr_warn("secvar: failed to retrieve secvar operations.\n");
return -ENODEV;
}
secvar_kobj = kobject_create_and_add("secvar", firmware_kobj);
if (!secvar_kobj) {
pr_err("secvar: Failed to create firmware kobj\n");
return -ENOMEM;
}
rc = sysfs_create_file(secvar_kobj, &format_attr.attr);
if (rc) {
kobject_put(secvar_kobj);
return -ENOMEM;
}
secvar_kset = kset_create_and_add("vars", NULL, secvar_kobj);
if (!secvar_kset) {
pr_err("secvar: sysfs kobject registration failed.\n");
kobject_put(secvar_kobj);
return -ENOMEM;
}
rc = update_kobj_size();
if (rc) {
pr_err("Cannot read the size of the attribute\n");
return rc;
}
secvar_sysfs_load();
return 0;
}
late_initcall(secvar_sysfs_init);

View File

@ -20,3 +20,4 @@ obj-$(CONFIG_PPC_MEMTRACE) += memtrace.o
obj-$(CONFIG_PPC_VAS) += vas.o vas-window.o vas-debug.o
obj-$(CONFIG_OCXL_BASE) += ocxl.o
obj-$(CONFIG_SCOM_DEBUGFS) += opal-xscom.o
obj-$(CONFIG_PPC_SECURE_BOOT) += opal-secvar.o

View File

@ -290,3 +290,6 @@ OPAL_CALL(opal_nx_coproc_init, OPAL_NX_COPROC_INIT);
OPAL_CALL(opal_mpipl_update, OPAL_MPIPL_UPDATE);
OPAL_CALL(opal_mpipl_register_tag, OPAL_MPIPL_REGISTER_TAG);
OPAL_CALL(opal_mpipl_query_tag, OPAL_MPIPL_QUERY_TAG);
OPAL_CALL(opal_secvar_get, OPAL_SECVAR_GET);
OPAL_CALL(opal_secvar_get_next, OPAL_SECVAR_GET_NEXT);
OPAL_CALL(opal_secvar_enqueue_update, OPAL_SECVAR_ENQUEUE_UPDATE);

View File

@ -0,0 +1,140 @@
// SPDX-License-Identifier: GPL-2.0
/*
* PowerNV code for secure variables
*
* Copyright (C) 2019 IBM Corporation
* Author: Claudio Carvalho
* Nayna Jain
*
* APIs to access secure variables managed by OPAL.
*/
#define pr_fmt(fmt) "secvar: "fmt
#include <linux/types.h>
#include <linux/platform_device.h>
#include <linux/of_platform.h>
#include <asm/opal.h>
#include <asm/secvar.h>
#include <asm/secure_boot.h>
static int opal_status_to_err(int rc)
{
int err;
switch (rc) {
case OPAL_SUCCESS:
err = 0;
break;
case OPAL_UNSUPPORTED:
err = -ENXIO;
break;
case OPAL_PARAMETER:
err = -EINVAL;
break;
case OPAL_RESOURCE:
err = -ENOSPC;
break;
case OPAL_HARDWARE:
err = -EIO;
break;
case OPAL_NO_MEM:
err = -ENOMEM;
break;
case OPAL_EMPTY:
err = -ENOENT;
break;
case OPAL_PARTIAL:
err = -EFBIG;
break;
default:
err = -EINVAL;
}
return err;
}
static int opal_get_variable(const char *key, uint64_t ksize,
u8 *data, uint64_t *dsize)
{
int rc;
if (!key || !dsize)
return -EINVAL;
*dsize = cpu_to_be64(*dsize);
rc = opal_secvar_get(key, ksize, data, dsize);
*dsize = be64_to_cpu(*dsize);
return opal_status_to_err(rc);
}
static int opal_get_next_variable(const char *key, uint64_t *keylen,
uint64_t keybufsize)
{
int rc;
if (!key || !keylen)
return -EINVAL;
*keylen = cpu_to_be64(*keylen);
rc = opal_secvar_get_next(key, keylen, keybufsize);
*keylen = be64_to_cpu(*keylen);
return opal_status_to_err(rc);
}
static int opal_set_variable(const char *key, uint64_t ksize, u8 *data,
uint64_t dsize)
{
int rc;
if (!key || !data)
return -EINVAL;
rc = opal_secvar_enqueue_update(key, ksize, data, dsize);
return opal_status_to_err(rc);
}
static const struct secvar_operations opal_secvar_ops = {
.get = opal_get_variable,
.get_next = opal_get_next_variable,
.set = opal_set_variable,
};
static int opal_secvar_probe(struct platform_device *pdev)
{
if (!opal_check_token(OPAL_SECVAR_GET)
|| !opal_check_token(OPAL_SECVAR_GET_NEXT)
|| !opal_check_token(OPAL_SECVAR_ENQUEUE_UPDATE)) {
pr_err("OPAL doesn't support secure variables\n");
return -ENODEV;
}
set_secvar_ops(&opal_secvar_ops);
return 0;
}
static const struct of_device_id opal_secvar_match[] = {
{ .compatible = "ibm,secvar-backend",},
{},
};
static struct platform_driver opal_secvar_driver = {
.driver = {
.name = "secvar",
.of_match_table = opal_secvar_match,
},
};
static int __init opal_secvar_init(void)
{
return platform_driver_probe(&opal_secvar_driver, opal_secvar_probe);
}
device_initcall(opal_secvar_init);

View File

@ -1084,6 +1084,9 @@ static int __init opal_init(void)
/* Initialise OPAL Power control interface */
opal_power_control_init();
/* Initialize OPAL secure variables */
opal_pdev_init("ibm,secvar-backend");
return 0;
}
machine_subsys_initcall(powernv, opal_init);

View File

@ -135,6 +135,15 @@ int is_hash_blacklisted(const u8 *hash, size_t hash_len, const char *type)
}
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
*/

View File

@ -35,12 +35,18 @@ extern int restrict_link_by_builtin_and_secondary_trusted(
extern int mark_hash_blacklisted(const char *hash);
extern int is_hash_blacklisted(const u8 *hash, size_t hash_len,
const char *type);
extern int is_binary_blacklisted(const u8 *hash, size_t hash_len);
#else
static inline int is_hash_blacklisted(const u8 *hash, size_t hash_len,
const char *type)
{
return 0;
}
static inline int is_binary_blacklisted(const u8 *hash, size_t hash_len)
{
return 0;
}
#endif
#ifdef CONFIG_IMA_BLACKLIST_KEYRING

View File

@ -29,7 +29,8 @@ extern void ima_kexec_cmdline(const void *buf, int size);
extern void ima_add_kexec_buffer(struct kimage *image);
#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 const char * const *arch_get_ima_policy(void);
#else

View File

@ -196,9 +196,9 @@ struct bin_attribute {
.size = _size, \
}
#define __BIN_ATTR_WO(_name) { \
#define __BIN_ATTR_WO(_name, _size) { \
.attr = { .name = __stringify(_name), .mode = 0200 }, \
.store = _name##_store, \
.write = _name##_write, \
.size = _size, \
}

View File

@ -72,6 +72,15 @@ config LOAD_IPL_KEYS
depends on S390
def_bool y
config LOAD_PPC_KEYS
bool "Enable loading of platform and blacklisted keys for POWER"
depends on INTEGRITY_PLATFORM_KEYRING
depends on PPC_SECURE_BOOT
default y
help
Enable loading of keys to the .platform keyring and blacklisted
hashes to the .blacklist keyring for powerpc based platforms.
config INTEGRITY_AUDIT
bool "Enables integrity auditing support "
depends on AUDIT

View File

@ -11,8 +11,11 @@ integrity-$(CONFIG_INTEGRITY_SIGNATURE) += digsig.o
integrity-$(CONFIG_INTEGRITY_ASYMMETRIC_KEYS) += digsig_asymmetric.o
integrity-$(CONFIG_INTEGRITY_PLATFORM_KEYRING) += platform_certs/platform_keyring.o
integrity-$(CONFIG_LOAD_UEFI_KEYS) += platform_certs/efi_parser.o \
platform_certs/load_uefi.o
platform_certs/load_uefi.o \
platform_certs/keyring_handler.o
integrity-$(CONFIG_LOAD_IPL_KEYS) += platform_certs/load_ipl_s390.o
integrity-$(CONFIG_LOAD_PPC_KEYS) += platform_certs/efi_parser.o \
platform_certs/load_powerpc.o \
platform_certs/keyring_handler.o
obj-$(CONFIG_IMA) += ima/
obj-$(CONFIG_EVM) += evm/

View File

@ -217,6 +217,9 @@ void ima_store_measurement(struct integrity_iint_cache *iint, struct file *file,
struct evm_ima_xattr_data *xattr_value,
int xattr_len, const struct modsig *modsig, int pcr,
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,
const unsigned char *filename);
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);
#define IMA_APPRAISE_KEXEC 0x40
#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,
struct integrity_iint_cache *iint,
struct file *file, const unsigned char *filename,
@ -268,6 +273,12 @@ int ima_read_xattr(struct dentry *dentry,
struct evm_ima_xattr_data **xattr_value);
#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,
struct integrity_iint_cache *iint,
struct file *file,

View File

@ -12,6 +12,7 @@
#include <linux/magic.h>
#include <linux/ima.h>
#include <linux/evm.h>
#include <keys/system_keyring.h>
#include "ima.h"
@ -303,6 +304,38 @@ static int modsig_verify(enum ima_hooks func, const struct modsig *modsig,
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
*

View File

@ -335,10 +335,14 @@ static int process_measurement(struct file *file, const struct cred *cred,
xattr_value, xattr_len, modsig, pcr,
template_desc);
if (rc == 0 && (action & IMA_APPRAISE_SUBMASK)) {
inode_lock(inode);
rc = ima_appraise_measurement(func, iint, file, pathname,
xattr_value, xattr_len, modsig);
inode_unlock(inode);
rc = ima_check_blacklist(iint, modsig, pcr);
if (rc != -EPERM) {
inode_lock(inode);
rc = ima_appraise_measurement(func, iint, file,
pathname, xattr_value,
xattr_len, modsig);
inode_unlock(inode);
}
if (!rc)
rc = mmap_violation_check(func, file, &pathbuf,
&pathname, filename);
@ -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.
* @size: size of buffer(in bytes).
* @eventname: event name to be used for the buffer entry.
* @cred: a pointer to a credentials structure for user validation.
* @secid: the secid of the task to be validated.
* @func: IMA hook
* @pcr: pcr to extend the measurement
*
* Based on policy, the buffer is measured into the ima log.
*/
static void process_buffer_measurement(const void *buf, int size,
const char *eventname,
const struct cred *cred, u32 secid)
void process_buffer_measurement(const void *buf, int size,
const char *eventname, enum ima_hooks func,
int pcr)
{
int ret = 0;
struct ima_template_entry *entry = NULL;
@ -642,19 +646,45 @@ static void process_buffer_measurement(const void *buf, int size,
.filename = eventname,
.buf = buf,
.buf_len = size};
struct ima_template_desc *template_desc = NULL;
struct ima_template_desc *template = NULL;
struct {
struct ima_digest_data hdr;
char digest[IMA_MAX_DIGEST_SIZE];
} hash = {};
int violation = 0;
int pcr = CONFIG_IMA_MEASURE_PCR_IDX;
int action = 0;
u32 secid;
action = ima_get_action(NULL, cred, secid, 0, KEXEC_CMDLINE, &pcr,
&template_desc);
if (!(action & IMA_MEASURE))
return;
/*
* Both LSM hooks and auxilary based buffer measurements are
* based on policy. To avoid code duplication, differentiate
* 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->algo = ima_hash_algo;
@ -664,7 +694,7 @@ static void process_buffer_measurement(const void *buf, int size,
if (ret < 0)
goto out;
ret = ima_alloc_init_template(&event_data, &entry, template_desc);
ret = ima_alloc_init_template(&event_data, &entry, template);
if (ret < 0)
goto out;
@ -686,13 +716,9 @@ static void process_buffer_measurement(const void *buf, int size,
*/
void ima_kexec_cmdline(const void *buf, int size)
{
u32 secid;
if (buf && size != 0) {
security_task_getsecid(current, &secid);
if (buf && size != 0)
process_buffer_measurement(buf, size, "kexec-cmdline",
current_cred(), secid);
}
KEXEC_CMDLINE, 0);
}
static int __init init_ima(void)

View File

@ -765,8 +765,8 @@ enum {
Opt_fsuuid, Opt_uid_eq, Opt_euid_eq, Opt_fowner_eq,
Opt_uid_gt, Opt_euid_gt, Opt_fowner_gt,
Opt_uid_lt, Opt_euid_lt, Opt_fowner_lt,
Opt_appraise_type, Opt_permit_directio,
Opt_pcr, Opt_template, Opt_err
Opt_appraise_type, Opt_appraise_flag,
Opt_permit_directio, Opt_pcr, Opt_template, Opt_err
};
static const match_table_t policy_tokens = {
@ -798,6 +798,7 @@ static const match_table_t policy_tokens = {
{Opt_euid_lt, "euid<%s"},
{Opt_fowner_lt, "fowner<%s"},
{Opt_appraise_type, "appraise_type=%s"},
{Opt_appraise_flag, "appraise_flag=%s"},
{Opt_permit_directio, "permit_directio"},
{Opt_pcr, "pcr=%s"},
{Opt_template, "template=%s"},
@ -1172,6 +1173,11 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
else
result = -EINVAL;
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:
entry->flags |= IMA_PERMIT_DIRECTIO;
break;
@ -1500,6 +1506,8 @@ int ima_policy_show(struct seq_file *m, void *v)
else
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)
seq_puts(m, "permit_directio ");
rcu_read_unlock();

View File

@ -32,6 +32,7 @@
#define EVM_IMMUTABLE_DIGSIG 0x08000000
#define IMA_FAIL_UNVERIFIABLE_SIGS 0x10000000
#define IMA_MODSIG_ALLOWED 0x20000000
#define IMA_CHECK_BLACKLIST 0x40000000
#define IMA_DO_MASK (IMA_MEASURE | IMA_APPRAISE | IMA_AUDIT | \
IMA_HASH | IMA_APPRAISE_SUBMASK)

View File

@ -0,0 +1,80 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/cred.h>
#include <linux/err.h>
#include <linux/efi.h>
#include <linux/slab.h>
#include <keys/asymmetric-type.h>
#include <keys/system_keyring.h>
#include "../integrity.h"
static efi_guid_t efi_cert_x509_guid __initdata = EFI_CERT_X509_GUID;
static efi_guid_t efi_cert_x509_sha256_guid __initdata =
EFI_CERT_X509_SHA256_GUID;
static efi_guid_t efi_cert_sha256_guid __initdata = EFI_CERT_SHA256_GUID;
/*
* Blacklist a hash.
*/
static __init void uefi_blacklist_hash(const char *source, const void *data,
size_t len, const char *type,
size_t type_len)
{
char *hash, *p;
hash = kmalloc(type_len + len * 2 + 1, GFP_KERNEL);
if (!hash)
return;
p = memcpy(hash, type, type_len);
p += type_len;
bin2hex(p, data, len);
p += len * 2;
*p = 0;
mark_hash_blacklisted(hash);
kfree(hash);
}
/*
* Blacklist an X509 TBS hash.
*/
static __init void uefi_blacklist_x509_tbs(const char *source,
const void *data, size_t len)
{
uefi_blacklist_hash(source, data, len, "tbs:", 4);
}
/*
* Blacklist the hash of an executable.
*/
static __init void uefi_blacklist_binary(const char *source,
const void *data, size_t len)
{
uefi_blacklist_hash(source, data, len, "bin:", 4);
}
/*
* Return the appropriate handler for particular signature list types found in
* the UEFI db and MokListRT tables.
*/
__init efi_element_handler_t get_handler_for_db(const efi_guid_t *sig_type)
{
if (efi_guidcmp(*sig_type, efi_cert_x509_guid) == 0)
return add_to_platform_keyring;
return 0;
}
/*
* Return the appropriate handler for particular signature list types found in
* the UEFI dbx and MokListXRT tables.
*/
__init efi_element_handler_t get_handler_for_dbx(const efi_guid_t *sig_type)
{
if (efi_guidcmp(*sig_type, efi_cert_x509_sha256_guid) == 0)
return uefi_blacklist_x509_tbs;
if (efi_guidcmp(*sig_type, efi_cert_sha256_guid) == 0)
return uefi_blacklist_binary;
return 0;
}

View File

@ -0,0 +1,32 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef PLATFORM_CERTS_INTERNAL_H
#define PLATFORM_CERTS_INTERNAL_H
#include <linux/efi.h>
void blacklist_hash(const char *source, const void *data,
size_t len, const char *type,
size_t type_len);
/*
* Blacklist an X509 TBS hash.
*/
void blacklist_x509_tbs(const char *source, const void *data, size_t len);
/*
* Blacklist the hash of an executable.
*/
void blacklist_binary(const char *source, const void *data, size_t len);
/*
* Return the handler for particular signature list types found in the db.
*/
efi_element_handler_t get_handler_for_db(const efi_guid_t *sig_type);
/*
* Return the handler for particular signature list types found in the dbx.
*/
efi_element_handler_t get_handler_for_dbx(const efi_guid_t *sig_type);
#endif

View File

@ -0,0 +1,96 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2019 IBM Corporation
* Author: Nayna Jain
*
* - loads keys and hashes stored and controlled by the firmware.
*/
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/cred.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/of.h>
#include <asm/secure_boot.h>
#include <asm/secvar.h>
#include "keyring_handler.h"
/*
* Get a certificate list blob from the named secure variable.
*/
static __init void *get_cert_list(u8 *key, unsigned long keylen, uint64_t *size)
{
int rc;
void *db;
rc = secvar_ops->get(key, keylen, NULL, size);
if (rc) {
pr_err("Couldn't get size: %d\n", rc);
return NULL;
}
db = kmalloc(*size, GFP_KERNEL);
if (!db)
return NULL;
rc = secvar_ops->get(key, keylen, db, size);
if (rc) {
kfree(db);
pr_err("Error reading %s var: %d\n", key, rc);
return NULL;
}
return db;
}
/*
* Load the certs contained in the keys databases into the platform trusted
* keyring and the blacklisted X.509 cert SHA256 hashes into the blacklist
* keyring.
*/
static int __init load_powerpc_certs(void)
{
void *db = NULL, *dbx = NULL;
uint64_t dbsize = 0, dbxsize = 0;
int rc = 0;
struct device_node *node;
if (!secvar_ops)
return -ENODEV;
/* The following only applies for the edk2-compat backend. */
node = of_find_compatible_node(NULL, NULL, "ibm,edk2-compat-v1");
if (!node)
return -ENODEV;
/*
* Get db, and dbx. They might not exist, so it isn't an error if we
* can't get them.
*/
db = get_cert_list("db", 3, &dbsize);
if (!db) {
pr_err("Couldn't get db list from firmware\n");
} else {
rc = parse_efi_signature_list("powerpc:db", db, dbsize,
get_handler_for_db);
if (rc)
pr_err("Couldn't parse db signatures: %d\n", rc);
kfree(db);
}
dbx = get_cert_list("dbx", 4, &dbxsize);
if (!dbx) {
pr_info("Couldn't get dbx list from firmware\n");
} else {
rc = parse_efi_signature_list("powerpc:dbx", dbx, dbxsize,
get_handler_for_dbx);
if (rc)
pr_err("Couldn't parse dbx signatures: %d\n", rc);
kfree(dbx);
}
of_node_put(node);
return rc;
}
late_initcall(load_powerpc_certs);

View File

@ -9,6 +9,7 @@
#include <keys/asymmetric-type.h>
#include <keys/system_keyring.h>
#include "../integrity.h"
#include "keyring_handler.h"
static efi_guid_t efi_cert_x509_guid __initdata = EFI_CERT_X509_GUID;
static efi_guid_t efi_cert_x509_sha256_guid __initdata =
@ -67,72 +68,6 @@ static __init void *get_cert_list(efi_char16_t *name, efi_guid_t *guid,
return db;
}
/*
* Blacklist a hash.
*/
static __init void uefi_blacklist_hash(const char *source, const void *data,
size_t len, const char *type,
size_t type_len)
{
char *hash, *p;
hash = kmalloc(type_len + len * 2 + 1, GFP_KERNEL);
if (!hash)
return;
p = memcpy(hash, type, type_len);
p += type_len;
bin2hex(p, data, len);
p += len * 2;
*p = 0;
mark_hash_blacklisted(hash);
kfree(hash);
}
/*
* Blacklist an X509 TBS hash.
*/
static __init void uefi_blacklist_x509_tbs(const char *source,
const void *data, size_t len)
{
uefi_blacklist_hash(source, data, len, "tbs:", 4);
}
/*
* Blacklist the hash of an executable.
*/
static __init void uefi_blacklist_binary(const char *source,
const void *data, size_t len)
{
uefi_blacklist_hash(source, data, len, "bin:", 4);
}
/*
* Return the appropriate handler for particular signature list types found in
* the UEFI db and MokListRT tables.
*/
static __init efi_element_handler_t get_handler_for_db(const efi_guid_t *
sig_type)
{
if (efi_guidcmp(*sig_type, efi_cert_x509_guid) == 0)
return add_to_platform_keyring;
return 0;
}
/*
* Return the appropriate handler for particular signature list types found in
* the UEFI dbx and MokListXRT tables.
*/
static __init efi_element_handler_t get_handler_for_dbx(const efi_guid_t *
sig_type)
{
if (efi_guidcmp(*sig_type, efi_cert_x509_sha256_guid) == 0)
return uefi_blacklist_x509_tbs;
if (efi_guidcmp(*sig_type, efi_cert_sha256_guid) == 0)
return uefi_blacklist_binary;
return 0;
}
/*
* Load the certs contained in the UEFI databases into the platform trusted
* keyring and the UEFI blacklisted X.509 cert SHA256 hashes into the blacklist