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:
commit
d34a5709be
@ -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
|
||||
|
46
Documentation/ABI/testing/sysfs-secvar
Normal file
46
Documentation/ABI/testing/sysfs-secvar
Normal 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.
|
@ -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
|
||||
|
@ -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 */
|
||||
|
@ -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);
|
||||
|
29
arch/powerpc/include/asm/secure_boot.h
Normal file
29
arch/powerpc/include/asm/secure_boot.h
Normal 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
|
35
arch/powerpc/include/asm/secvar.h
Normal file
35
arch/powerpc/include/asm/secvar.h
Normal 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
|
@ -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
|
||||
|
78
arch/powerpc/kernel/ima_arch.c
Normal file
78
arch/powerpc/kernel/ima_arch.c
Normal 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;
|
||||
}
|
50
arch/powerpc/kernel/secure_boot.c
Normal file
50
arch/powerpc/kernel/secure_boot.c
Normal 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;
|
||||
}
|
17
arch/powerpc/kernel/secvar-ops.c
Normal file
17
arch/powerpc/kernel/secvar-ops.c
Normal 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;
|
||||
}
|
248
arch/powerpc/kernel/secvar-sysfs.c
Normal file
248
arch/powerpc/kernel/secvar-sysfs.c
Normal 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);
|
@ -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
|
||||
|
@ -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);
|
||||
|
140
arch/powerpc/platforms/powernv/opal-secvar.c
Normal file
140
arch/powerpc/platforms/powernv/opal-secvar.c
Normal 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);
|
@ -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);
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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, \
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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/
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
*
|
||||
|
@ -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 @@ out:
|
||||
*/
|
||||
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)
|
||||
|
@ -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();
|
||||
|
@ -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)
|
||||
|
80
security/integrity/platform_certs/keyring_handler.c
Normal file
80
security/integrity/platform_certs/keyring_handler.c
Normal 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;
|
||||
}
|
32
security/integrity/platform_certs/keyring_handler.h
Normal file
32
security/integrity/platform_certs/keyring_handler.h
Normal 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
|
96
security/integrity/platform_certs/load_powerpc.c
Normal file
96
security/integrity/platform_certs/load_powerpc.c
Normal 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);
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user