[PATCH] Add GlowSlayer Explicit Access Control from Cory Craig <gs.cory.craig at gmail.com>
Casey Schaufler
casey at schaufler-ca.com
Thu Dec 2 01:39:20 UTC 2021
On 12/1/2021 4:45 PM, Cory Craig wrote:
> Add GlowSlayer Security System with Tinfoil Pre-Init check
> Add GlowSlayer Security System with Slowboot Post-Init check
> Add GlowSlayer Resilient Return Value Holder struct pbit
> Add GlowSlayer gs_tinfoil_slowboot shared code
> Add Makefiles/KConfigs
> Updated security/{Kconfig, Makefile} to allow for enabling
> GlowSlayer Security System
> Add conditional compilation in init/main.c for calling tinfoil_verify
>
> Signed-off-by: Cory Craig <gs.cory.craig at gmail.com>
You *must*:
- Adhere to the Linux kernel coding conventions
- Explain what GlowSlayer does, and why it is relevant
You really should:
- Break this into a set of smaller patches for review
- Refactor it as an LSM
> ---
> This could prevent a physical access backdoor from being trivially
> exploited via a poisoned init program placed by the attacker.
> This also helps prevent a successful hack of a computer while
> running from putting in a poisoned init causing LUKS encryption
> password from being disclosed to the adversary since the only
> component trusted is the kernel and a reboot into the UEFI console
> is required to add additional MOK certificates. This is also resistant
> to tampering of the files being validated since it doesn't depend on
> atributes set on the file. The administration is also extremely simple.
> This would prove very helpful in areas of the world that are less than
> free where state level actors may target journalists via the purchase
> or development of zero day attacks because it accounts for the scenario
> where uncontained root is obtained and the attacker may add any keys
> or run any commands. Ideally this will be combined with an external
> booloader carried with the user, removed from the computer when in use
> that validates the kernel in a zero trust manner with an unencrypted
> boot partition. This would serve as a honeypot for attackers that obtain
> physical access to the system so the user might know if someone has
> attempted to compromise their system and has the resources to alter
> the TPM in some way.
> ---
> MAINTAINERS | 4 +
> include/linux/gs_pbit.h | 46 ++
> include/linux/gs_tinfoil.h | 5 +
> include/linux/gs_tinfoil_slowboot.h | 159 ++++
> init/main.c | 7 +
> security/Kconfig | 1 +
> security/Makefile | 1 +
> security/gs/Kconfig | 139 ++++
> security/gs/Makefile | 13 +
> security/gs/gs_pbit.c | 137 +++
> security/gs/gs_slowboot.c | 82 ++
> security/gs/gs_tinfoil.c | 72 ++
> security/gs/gs_tinfoil_slowboot.c | 1193 +++++++++++++++++++++++++++
> 13 files changed, 1859 insertions(+)
> create mode 100644 include/linux/gs_pbit.h
> create mode 100644 include/linux/gs_tinfoil.h
> create mode 100644 include/linux/gs_tinfoil_slowboot.h
> create mode 100644 security/gs/Kconfig
> create mode 100644 security/gs/Makefile
> create mode 100644 security/gs/gs_pbit.c
> create mode 100644 security/gs/gs_slowboot.c
> create mode 100644 security/gs/gs_tinfoil.c
> create mode 100644 security/gs/gs_tinfoil_slowboot.c
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 7a2345c..76a78bb 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -8046,6 +8046,10 @@ F: Documentation/filesystems/gfs2*
> F: fs/gfs2/
> F: include/uapi/linux/gfs2_ondisk.h
>
> +GLOWSLAYER SECURITY SYSTEM
> +M: Cory Craig <gs.cory.craig at gmail.com>
> +S: Maintained
> +
> GIGABYTE WMI DRIVER
> M: Thomas Weißschuh <thomas at weissschuh.net>
> L: platform-driver-x86 at vger.kernel.org
> diff --git a/include/linux/gs_pbit.h b/include/linux/gs_pbit.h
> new file mode 100644
> index 0000000..599a96b
> --- /dev/null
> +++ b/include/linux/gs_pbit.h
> @@ -0,0 +1,46 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +#ifndef _GS_PBIT_H
> +#define _GS_PBIT_H
> +
> +#include <linux/random.h>
> +/*
> + * Tamper Resistant "Paranoid Bit"
> + */
> +
> +struct pbit {
> + int rs;
> + int ev1;
> + int status;
> + int ev2;
> + int ms;
> + int ev3;
> + int dead;
> + int ls;
> +};
> +
> +#define PBIT_DST 0xBAAAAAAA
> +#define PBIT_DED 0x5555555D
> +#define PBIT_YES 0x55552AAA
> +#define PBIT_NO 0x81083C1
> +#define PBIT_ERR 0xFFFFFFFF
> +#define PBIT_MGK 0xCF0850F1
> +
> +void pbit_check_no(struct pbit *pc, int ev);
> +void pbit_check_setup(struct pbit *pc, int ev);
> +void pbit_check_yes(struct pbit *pc, int ev, const int *rv);
> +int pbit_check(struct pbit *pc);
> +int pbit_infer(struct pbit *pc);
> +
> +#define PBIT_OK(pc) (pbit_check(&(pc)) == PBIT_YES ? 1 : 0)
> +#define PBIT_FAIL(pc) (pbit_check(&(pc)) == PBIT_NO ? 1 : 0)
> +#define PBIT_DEAD(pc) (pbit_check(&(pc)) == PBIT_ERR ? 1 : 0)
> +#define PBIT_GET(pc) (pbit_infer(&(pc)))
> +#define PBIT_RET(pc) (pbit_infer(&(pc)))
> +#define PBIT_Y(pc, x) do {\
> + int __PBIT_RV_VAL;\
> + get_random_bytes(&__PBIT_RV_VAL, sizeof(int));\
> + pbit_check_yes(&(pc), (x), &__PBIT_RV_VAL);\
> +} while (0)
> +#define PBIT_N(pc, x) pbit_check_no(&(pc), (x))
> +#define PBIT_RECOVER(pc) pbit_check_recover(&(pc))
> +#endif
> diff --git a/include/linux/gs_tinfoil.h b/include/linux/gs_tinfoil.h
> new file mode 100644
> index 0000000..bf9e44f
> --- /dev/null
> +++ b/include/linux/gs_tinfoil.h
> @@ -0,0 +1,5 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +#ifndef _X_GS_TINFOIL_H
> +#define _X_GS_TINFOIL_H
> +void tinfoil_verify(void);
> +#endif
> diff --git a/include/linux/gs_tinfoil_slowboot.h b/include/linux/gs_tinfoil_slowboot.h
> new file mode 100644
> index 0000000..7ee4634
> --- /dev/null
> +++ b/include/linux/gs_tinfoil_slowboot.h
> @@ -0,0 +1,159 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +#ifndef _X_GS_TINFOIL_SLOWBOOT_H
> +#define _X_GS_TINFOIL_SLOWBOOT_H
> +
> +#include <linux/init.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <crypto/aead.h>
> +#include <crypto/hash.h>
> +#include <crypto/skcipher.h>
> +#include <crypto/akcipher.h>
> +#include <crypto/public_key.h>
> +#include <linux/err.h>
> +#include <linux/fips.h>
> +#include <linux/gfp.h>
> +#include <linux/scatterlist.h>
> +#include <linux/string.h>
> +#include <linux/moduleparam.h>
> +#include <linux/jiffies.h>
> +#include <linux/timex.h>
> +#include <linux/interrupt.h>
> +#include <linux/limits.h>
> +#include <linux/fs.h>
> +#include <linux/uaccess.h>
> +#include <linux/slab.h>
> +#include <linux/stat.h>
> +#include <linux/random.h>
> +#include <linux/gs_pbit.h>
> +
> +#define GS_STRING_BASE 4096
> +
> +/* File Validation item */
> +struct slowboot_validation_item {
> + char hash[GS_STRING_BASE+2];
> + u8 b_hash[GS_STRING_BASE+1];
> + char path[PATH_MAX+1];
> + struct pbit is_ok;
> + char *buf;
> + size_t buf_len;
> + struct file *fp;
> + long long pos;
> +};
> +
> +/* Container Struct for the entire process */
> +struct slowboot_tinfoil {
> + struct kstat *st;
> + struct slowboot_validation_item *validation_items;
> + int failures;
> + int initialized;
> + int slwbt_ct;
> + char config_file[PATH_MAX];
> + char config_file_signature[PATH_MAX];
> + char config_pkey[GS_STRING_BASE+1];
> + int error_code;
> + struct pbit error;
> +};
> +
> +/* shash container struct */
> +struct sdesc {
> + struct shash_desc shash;
> + char ctx[];
> +};
> +
> +/* Container for a single item check */
> +struct tinfoil_check {
> + struct slowboot_validation_item *item;
> + struct crypto_shash *alg;
> + struct sdesc *sd;
> + unsigned char *digest;
> +};
> +
> +/* Initialization Container Holding initial signature verification items */
> +struct slowboot_init_container {
> + struct file *fp;
> + struct file *sfp;
> + struct crypto_shash *halg;
> + struct sdesc *hsd;
> + size_t file_size;
> + size_t sfp_file_size;
> + loff_t pos;
> + loff_t remaining;
> + loff_t sfp_pos;
> + int num_read;
> + int sfp_num_read;
> + long num_items;
> + char *buf;
> + char *sfp_buf;
> + unsigned char *kernel_key;
> + unsigned char *digest;
> + struct slowboot_validation_item *items;
> + struct slowboot_validation_item *c_item;
> + int kernel_key_len;
> + struct public_key_signature sig;
> + struct public_key rsa_pub_key;
> +};
> +
> +/* Signature Verification Container */
> +struct sig_verify {
> + struct crypto_wait cwait;
> + struct crypto_akcipher *tfm;
> + struct akcipher_request *req;
> + struct scatterlist src_tab[3];
> + const char *alg_name;
> + void *output;
> + unsigned int outlen;
> + char alg_name_buf[CRYPTO_MAX_ALG_NAME];
> +};
> +
> +char *__gs_read_file_to_memory(struct file *fp,
> + size_t file_size,
> + loff_t *pos,
> + int ignore_size);
> +size_t __gs_get_file_size(struct file *fp);
> +int __gs_memmem_sp(const char *s1, size_t s1_len,
> + const char *s2, size_t s2_len);
> +struct sdesc *__gs_init_sdesc(struct crypto_shash *alg);
> +int __gs_pk_sig_verify_init(struct sig_verify *sv,
> + const struct public_key *pkey,
> + const struct public_key_signature *sig,
> + const char *pkalgopd);
> +/*
> + * Perform entire test
> + * @config_tinfoil_cf: path for the configuration file
> + * @config_tinfoil_cfs: path for the configuration file checksum file
> + * @config_tinfoil_pk: correctly (DER for RSA) encoded public key in HEX
> + * @config_tinfoil_pklen: strlen of @tinfoil_pk
> + * @config_tinfoil_dglen: number of bytes in digest 64 for sha512
> + * @config_tinfoil_hslen: strlen of hex representation of digest, 128 for sha512
> + * @tinfoil_pkalgo: algorithm used, likely "rsa"
> + * @tinfoil_pkalgopd: algorithm padding, likely "pkcs1pad(rsa,sha512)" can be ""
> + * @tinfoil_hsalgo: digest used, likely "sha512"
> + * @config_tinfoil_idtype: public_key.id_type likely "X509"
> + * @gs_irq_killer: Nullable spinlock_t to block IRQ during test
> + * @config_tinfoil_new_line: char for new line '\n'
> + * @config_tinfoil_override: magic cmdline value to bypass test
> + * @config_tinfoil_version: logic version to use likely 1
> + * @config_tinfoil_reserved: future use
> + * @config_tinfoil_unused: future use
> + * @config_bug_on_fail: BUG(); if errors occur
> + */
> +int __gs_tfsb_go(const char *config_tinfoil_cf,
> + const char *config_tinfoil_cfs,
> + const char *config_tinfoil_pk,
> + int config_tinfoil_pklen,
> + int config_tinfoil_dglen,
> + int config_tinfoil_hslen,
> + const char *config_tinfoil_pkalgo,
> + const char *config_tinfoil_pkalgopd,
> + const char *config_tinfoil_hsalgo,
> + const char *config_tinfoil_idtype,
> + spinlock_t *gs_irq_killer,
> + char config_tinfoil_new_line,
> + const char *config_tinfoil_override,
> + int config_tinfoil_version,
> + const char *config_tinfoil_reserved,
> + const void *config_tinfoil_unused,
> + int config_bug_on_fail);
> +
> +#endif
> diff --git a/init/main.c b/init/main.c
> index bb984ed..a2bd8e7 100644
> --- a/init/main.c
> +++ b/init/main.c
> @@ -99,6 +99,9 @@
> #include <linux/kcsan.h>
> #include <linux/init_syscalls.h>
> #include <linux/stackdepot.h>
> +#ifdef CONFIG_TINFOIL
> +#include <linux/gs_tinfoil.h>
> +#endif
>
> #include <asm/io.h>
> #include <asm/bugs.h>
> @@ -1521,6 +1524,10 @@ static int __ref kernel_init(void *unused)
>
> do_sysctl_args();
>
> +#ifdef CONFIG_TINFOIL
> + tinfoil_verify();
> +#endif
> +
> if (ramdisk_execute_command) {
> ret = run_init_process(ramdisk_execute_command);
> if (!ret)
> diff --git a/security/Kconfig b/security/Kconfig
> index 0b847f4..3508094 100644
> --- a/security/Kconfig
> +++ b/security/Kconfig
> @@ -228,6 +228,7 @@ source "security/yama/Kconfig"
> source "security/safesetid/Kconfig"
> source "security/lockdown/Kconfig"
> source "security/landlock/Kconfig"
> +source "security/gs/Kconfig"
>
> source "security/integrity/Kconfig"
>
> diff --git a/security/Makefile b/security/Makefile
> index 18121f8..b79402b 100644
> --- a/security/Makefile
> +++ b/security/Makefile
> @@ -24,6 +24,7 @@ obj-$(CONFIG_SECURITY_LOCKDOWN_LSM) += lockdown/
> obj-$(CONFIG_CGROUPS) += device_cgroup.o
> obj-$(CONFIG_BPF_LSM) += bpf/
> obj-$(CONFIG_SECURITY_LANDLOCK) += landlock/
> +obj-$(CONFIG_GS_BASE) += gs/
>
> # Object integrity file lists
> obj-$(CONFIG_INTEGRITY) += integrity/
> diff --git a/security/gs/Kconfig b/security/gs/Kconfig
> new file mode 100644
> index 0000000..b6dc784
> --- /dev/null
> +++ b/security/gs/Kconfig
> @@ -0,0 +1,139 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +config GS_BASE
> + prompt "GlowSlayer Base Code"
> + bool
> + default n
> + help
> + This enables GS Security Enhancements for cryptographic checks of files
> + at times when the check is useful to create a form of explicit access
> + control. Tinfoil is Pre-Init. Slowboot is Post-Init and controlled by
> + the init process
> +menu "Tinfoil Cryptographic Init Verification"
> + config TINFOIL
> + prompt "Enable Tinfoil Init Verification"
> + bool
> + default n
> + depends on GS_BASE
> + config TINFOIL_CF
> + prompt "Tinfoil Config File(must be visible at boot)"
> + string
> + default "/etc/gs/tinfoil" if TINFOIL
> + depends on TINFOIL
> + config TINFOIL_CFS
> + prompt "Tinfoil Config File Signature File (must be visible at boot)"
> + string
> + default "/etc/gs/tinfoil.sig" if TINFOIL
> + depends on TINFOIL
> + config TINFOIL_PK
> + prompt "Tinfoil Public Key (Hex String Literal)"
> + string
> + default "" if TINFOIL
> + depends on TINFOIL
> + config TINFOIL_PKLEN
> + prompt "Tinfoil Public Key Length (Of Hex, maybe <1052>)"
> + int
> + default 1052 if TINFOIL
> + depends on TINFOIL
> + config TINFOIL_DGLEN
> + prompt "Digest Length for validation (sha512 is <64>)"
> + int
> + default 64 if TINFOIL
> + depends on TINFOIL
> + config TINFOIL_HSLEN
> + prompt "Digest Has Length in Hex (sha512 is <128>)"
> + int
> + default 128 if TINFOIL
> + depends on TINFOIL
> + config TINFOIL_PKALGO
> + prompt "Algorithm used for verification <rsa>"
> + string
> + default "rsa" if TINFOIL
> + depends on TINFOIL
> + config TINFOIL_PKALGOPD
> + prompt "Algorithm with padding <pkcs1pad(rsa,sha512)>"
> + string
> + default "pkcs1pad(rsa,sha512)" if TINFOIL
> + depends on TINFOIL
> + config TINFOIL_HSALGO
> + prompt "Hash Algorithm <sha512>"
> + string
> + default "sha512" if TINFOIL
> + depends on TINFOIL
> + config TINFOIL_IDTYPE
> + prompt "Signature ID Type <X509>"
> + string
> + default "X509" if TINFOIL
> + depends on TINFOIL
> + config TINFOIL_BUG
> + prompt "Bug on Fail"
> + int
> + default 0 if TINFOIL
> + depends on TINFOIL
> + config TINFOIL_OVERRIDE
> + prompt "Override Parameter"
> + string
> + default "tinfoil_override" if TINFOIL
> + depends on TINFOIL
> +endmenu
> +menu "Slowboot Cryptographic PostInit Verification"
> + config SLOWBOOT
> + prompt "Enable Slowboot PostInit Verification"
> + bool
> + default n
> + depends on GS_BASE
> + config SLOWBOOT_CF
> + prompt "Slowboot Config File(must be visible at boot)"
> + string
> + default "/etc/gs/slowboot" if SLOWBOOT
> + depends on SLOWBOOT
> + config SLOWBOOT_CFS
> + prompt "Slowboot Config File Signature File (must be visible at boot)"
> + string
> + default "/etc/gs/slowboot.sig" if SLOWBOOT
> + depends on SLOWBOOT
> + config SLOWBOOT_PK
> + prompt "Slowboot Public Key (Hex String Literal)"
> + string
> + default "" if SLOWBOOT
> + depends on SLOWBOOT
> + config SLOWBOOT_PKLEN
> + prompt "Slowboot Public Key Length (Of Hex, maybe <1052>)"
> + int
> + default 1052 if SLOWBOOT
> + depends on SLOWBOOT
> + config SLOWBOOT_DGLEN
> + prompt "Digest Length for validation (sha512 is <64>)"
> + int
> + default 64 if SLOWBOOT
> + depends on SLOWBOOT
> + config SLOWBOOT_HSLEN
> + prompt "Digest Has Length in Hex (sha512 is <128>)"
> + int
> + default 128 if SLOWBOOT
> + depends on SLOWBOOT
> + config SLOWBOOT_PKALGO
> + prompt "Algorithm used for verification <rsa>"
> + string
> + default "rsa" if SLOWBOOT
> + depends on SLOWBOOT
> + config SLOWBOOT_PKALGOPD
> + prompt "Algorithm with padding <pkcs1pad(rsa,sha512)>"
> + string
> + default "pkcs1pad(rsa,sha512)" if SLOWBOOT
> + depends on SLOWBOOT
> + config SLOWBOOT_HSALGO
> + prompt "Hash Algorithm <sha512>"
> + string
> + default "sha512" if SLOWBOOT
> + depends on SLOWBOOT
> + config SLOWBOOT_IDTYPE
> + prompt "Signature ID Type <X509>"
> + string
> + default "X509" if SLOWBOOT
> + depends on SLOWBOOT
> + config SLOWBOOT_BUG
> + prompt "Bug on Fail"
> + int
> + default 0 if SLOWBOOT
> + depends on SLOWBOOT
> +endmenu
> diff --git a/security/gs/Makefile b/security/gs/Makefile
> new file mode 100644
> index 0000000..db59bdc
> --- /dev/null
> +++ b/security/gs/Makefile
> @@ -0,0 +1,13 @@
> +# SPDX-License-Identifier: GPL-2.0
> +#
> +# Makefile for building the GS Security Items
> +#
> +obj-$(CONFIG_GS_BASE) += gs_pbit.o gs_tinfoil_slowboot.o
> +
> +ifdef CONFIG_TINFOIL
> +obj-y += gs_tinfoil.o
> +endif
> +
> +ifdef CONFIG_SLOWBOOT
> +obj-m += gs_slowboot.o
> +endif
> diff --git a/security/gs/gs_pbit.c b/security/gs/gs_pbit.c
> new file mode 100644
> index 0000000..d4eca49
> --- /dev/null
> +++ b/security/gs/gs_pbit.c
> @@ -0,0 +1,137 @@
> +// SPDX-License-Identifier: GPL-2.0
> +#include <linux/gs_pbit.h>
> +
> +/*
> + * Copyright Cory Craig <gs.cory.craig at gmail.com> 2021
> + *
> + * 'Paranoid Bit'
> + *
> + * The pbit data type should provide a mitigation for rowhammer as well
> + * as other situations where the processor/memory cannot be trusted completely
> + * such as unintentional or intentional radiation energizing a bit(s)
> + * This can be useful when you set a status for a permission check and then
> + * have cleanup to perform and an attacker my try to flip a status variable
> + */
> +
> +/*
> + * Set the status to NO
> + * @pc: paranoid bit
> + */
> +void pbit_check_no(struct pbit *pc, int ev)
> +{
> + if (!pc)
> + return;
> + pc->status = PBIT_DST;
> + pc->dead = PBIT_DED;
> + pc->ls = PBIT_NO;
> + pc->ms = PBIT_NO;
> + pc->rs = PBIT_NO;
> + pc->ev1 = ev;
> + pc->ev2 = ev;
> + pc->ev3 = ev;
> +}
> +/*
> + * Initialize the state to ERR
> + * @pc: paranoid bit
> + */
> +void pbit_check_setup(struct pbit *pc, int ev)
> +{
> + if (!pc)
> + return;
> + pc->status = PBIT_DST;
> + pc->dead = PBIT_DED;
> + pc->ls = PBIT_YES;
> + pc->ms = PBIT_ERR;
> + pc->rs = PBIT_NO;
> + pc->ev1 = ev;
> + pc->ev2 = ev;
> + pc->ev3 = ev;
> +}
> +
> +/*
> + * Set the status to YES
> + * @pc: paranoid bit
> + */
> +void pbit_check_yes(struct pbit *pc, int ev, const int *rv)
> +{
> + if (!pc)
> + return;
> + if (rv)
> + pc->dead = *rv;
> + else
> + pc->dead = PBIT_MGK;
> + pc->status = pc->dead;
> + pc->ls = PBIT_YES;
> + pc->ms = PBIT_YES;
> + pc->rs = PBIT_YES;
> + pc->ev1 = ev;
> + pc->ev2 = ev;
> + pc->ev3 = ev;
> +}
> +
> +/*
> + * Check current status of paranoid bit, any alterations since a set status
> + * should return a PBIT_ERR
> + * @pc: paranoid bit
> + */
> +int pbit_check(struct pbit *pc)
> +{
> + struct pbit pc_copy;
> +
> + if (!pc)
> + return PBIT_ERR;
> + pc_copy = *pc;
> + if (pc_copy.status == pc_copy.dead && pc_copy.ls == PBIT_YES
> + && pc_copy.ms == PBIT_YES && pc_copy.rs == PBIT_YES
> + && pc_copy.status != PBIT_ERR && pc_copy.ev1 == pc_copy.ev2
> + && pc_copy.ev1 == pc_copy.ev3) {
> + *pc = pc_copy;
> + return PBIT_YES;
> + } else if (pc_copy.status == PBIT_DST && pc_copy.dead == PBIT_DED
> + && pc_copy.ls == PBIT_NO && pc_copy.ms == PBIT_NO
> + && pc_copy.rs == PBIT_NO && pc_copy.ev1 == pc_copy.ev2
> + && pc_copy.ev1 == pc_copy.ev3) {
> + *pc = pc_copy;
> + return PBIT_NO;
> + }
> + *pc = pc_copy;
> + return PBIT_ERR;
> +}
> +
> +/*
> + * Infer the value out of the pbit, failure is always -EINVAL
> + * @pc: paranoid bit
> + */
> +int pbit_infer(struct pbit *pc)
> +{
> + struct pbit pc_copy;
> +
> + pc_copy = *pc;
> + switch (pbit_check(&pc_copy)) {
> + case PBIT_ERR:
> + return PBIT_ERR;
> + default:
> + *pc = pc_copy;
> + return pc_copy.ev2;
> + }
> +}
> +
> +/*
> + * Attempt to recover the value, sets pbit value to PBIT_ERR
> + * @pc: paranoid bit
> + */
> +void pbit_check_recover(struct pbit *pc)
> +{
> + struct pbit pc_copy;
> +
> + pc_copy = *pc;
> + if (pc_copy.ev1 == pc_copy.ev2 && pc_copy.ev3 == pc_copy.ev1)
> + pbit_check_setup(&pc_copy, pc_copy.ev1);
> + else if (pc_copy.ev1 == pc_copy.ev2 || pc_copy.ev1 == pc_copy.ev3)
> + pbit_check_setup(&pc_copy, pc_copy.ev1);
> + else if (pc_copy.ev2 == pc_copy.ev3)
> + pbit_check_setup(&pc_copy, pc_copy.ev3);
> + else
> + pbit_check_setup(&pc_copy, PBIT_ERR);
> + *pc = pc_copy;
> +}
> diff --git a/security/gs/gs_slowboot.c b/security/gs/gs_slowboot.c
> new file mode 100644
> index 0000000..e0dd1f2
> --- /dev/null
> +++ b/security/gs/gs_slowboot.c
> @@ -0,0 +1,82 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * GS Slowboot PostInit Integrity Check
> + * Copyright (C) 2021 Cory Craig
> + */
> +#include <linux/init.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <crypto/aead.h>
> +#include <crypto/hash.h>
> +#include <crypto/skcipher.h>
> +#include <crypto/akcipher.h>
> +#include <crypto/public_key.h>
> +#include <linux/err.h>
> +#include <linux/fips.h>
> +#include <linux/gfp.h>
> +#include <linux/scatterlist.h>
> +#include <linux/string.h>
> +#include <linux/moduleparam.h>
> +#include <linux/jiffies.h>
> +#include <linux/timex.h>
> +#include <linux/interrupt.h>
> +#include <linux/limits.h>
> +#include <linux/fs.h>
> +#include <linux/uaccess.h>
> +#include <linux/slab.h>
> +#include <linux/stat.h>
> +#include <linux/random.h>
> +#include <linux/gs_pbit.h>
> +#include <linux/gs_tinfoil_slowboot.h>
> +
> +DEFINE_SPINLOCK(gs_s_irq_killer);
> +
> +#ifndef CONFIG_SLOWBOOT_NEW_LINE
> +#define CONFIG_SLOWBOOT_NEW_LINE '\n'
> +#endif
> +
> +#ifndef CONFIG_SLOWBOOT_OVERRIDE
> +#define CONFIG_SLOWBOOT_OVERRIDE "not_applicable_no_gssb_override"
> +#endif
> +
> +#ifndef CONFIG_SLOWBOOT_VERSION
> +#define CONFIG_SLOWBOOT_VERSION 1
> +#endif
> +
> +#ifndef CONFIG_SLOWBOOT_BUG
> +#define CONFIG_SLOWBOOT_BUG 0
> +#endif
> +
> +/*
> + * Main function for validation
> + */
> +static int __init slowboot_mod_init(void)
> +{
> + return __gs_tfsb_go(CONFIG_SLOWBOOT_CF,
> + CONFIG_SLOWBOOT_CFS,
> + CONFIG_SLOWBOOT_PK,
> + CONFIG_SLOWBOOT_PKLEN,
> + CONFIG_SLOWBOOT_DGLEN,
> + CONFIG_SLOWBOOT_HSLEN,
> + CONFIG_SLOWBOOT_PKALGO,
> + CONFIG_SLOWBOOT_PKALGOPD,
> + CONFIG_SLOWBOOT_HSALGO,
> + CONFIG_SLOWBOOT_IDTYPE,
> + &gs_s_irq_killer,
> + CONFIG_SLOWBOOT_NEW_LINE,
> + CONFIG_SLOWBOOT_OVERRIDE,
> + CONFIG_SLOWBOOT_VERSION,
> + NULL,
> + NULL,
> + CONFIG_SLOWBOOT_BUG);
> +}
> +
> +static void __exit slowboot_mod_exit(void) { }
> +
> +module_init(slowboot_mod_init);
> +module_exit(slowboot_mod_exit);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_DESCRIPTION("GS Slowboot");
> +MODULE_AUTHOR("Cory Craig <gs.cory.craig at gmail.com>");
> +MODULE_VERSION("1.0");
> diff --git a/security/gs/gs_tinfoil.c b/security/gs/gs_tinfoil.c
> new file mode 100644
> index 0000000..917aa1f
> --- /dev/null
> +++ b/security/gs/gs_tinfoil.c
> @@ -0,0 +1,72 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * GS Tinfoil Pre Init Integrity Check
> + * Copyright (C) 2021 Cory Craig
> + */
> +#include <linux/init.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <crypto/aead.h>
> +#include <crypto/hash.h>
> +#include <crypto/skcipher.h>
> +#include <crypto/akcipher.h>
> +#include <crypto/public_key.h>
> +#include <linux/err.h>
> +#include <linux/fips.h>
> +#include <linux/gfp.h>
> +#include <linux/scatterlist.h>
> +#include <linux/string.h>
> +#include <linux/moduleparam.h>
> +#include <linux/jiffies.h>
> +#include <linux/timex.h>
> +#include <linux/interrupt.h>
> +#include <linux/limits.h>
> +#include <linux/fs.h>
> +#include <linux/uaccess.h>
> +#include <linux/slab.h>
> +#include <linux/stat.h>
> +#include <linux/random.h>
> +#include <linux/gs_pbit.h>
> +#include <linux/gs_tinfoil_slowboot.h>
> +#include <linux/gs_tinfoil.h>
> +
> +#ifndef CONFIG_TINFOIL_NEW_LINE
> +#define CONFIG_TINFOIL_NEW_LINE '\n'
> +#endif
> +
> +#ifndef CONFIG_TINFOIL_VERSION
> +#define CONFIG_TINFOIL_VERSION 1
> +#endif
> +
> +#ifndef CONFIG_TINFOIL_BUG
> +#define CONFIG_TINFOIL_BUG 0
> +#endif
> +
> +DEFINE_SPINLOCK(gs_irq_killer);
> +
> +/*
> + * Verify boot files chaining off a trusted kernel
> + * There should be an LSM hook for this to avoid
> + * conditional compilation
> + */
> +void tinfoil_verify(void)
> +{
> + pr_err("GS TFSB tinfoil verify finished with status: %d\n",
> + __gs_tfsb_go(CONFIG_TINFOIL_CF,
> + CONFIG_TINFOIL_CFS,
> + CONFIG_TINFOIL_PK,
> + CONFIG_TINFOIL_PKLEN,
> + CONFIG_TINFOIL_DGLEN,
> + CONFIG_TINFOIL_HSLEN,
> + CONFIG_TINFOIL_PKALGO,
> + CONFIG_TINFOIL_PKALGOPD,
> + CONFIG_TINFOIL_HSALGO,
> + CONFIG_TINFOIL_IDTYPE,
> + &gs_irq_killer,
> + CONFIG_TINFOIL_NEW_LINE,
> + CONFIG_TINFOIL_OVERRIDE,
> + CONFIG_TINFOIL_VERSION,
> + NULL,
> + NULL,
> + CONFIG_TINFOIL_BUG));
> +}
> diff --git a/security/gs/gs_tinfoil_slowboot.c b/security/gs/gs_tinfoil_slowboot.c
> new file mode 100644
> index 0000000..8b605f9
> --- /dev/null
> +++ b/security/gs/gs_tinfoil_slowboot.c
> @@ -0,0 +1,1193 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * GlowSlayer Tinfoil/Slowboot Shared
> + * Copyright (C) 2021 Cory Craig
> + */
> +#include <linux/init.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <crypto/aead.h>
> +#include <crypto/hash.h>
> +#include <crypto/skcipher.h>
> +#include <crypto/akcipher.h>
> +#include <crypto/public_key.h>
> +#include <linux/err.h>
> +#include <linux/fips.h>
> +#include <linux/gfp.h>
> +#include <linux/scatterlist.h>
> +#include <linux/string.h>
> +#include <linux/moduleparam.h>
> +#include <linux/jiffies.h>
> +#include <linux/timex.h>
> +#include <linux/interrupt.h>
> +#include <linux/limits.h>
> +#include <linux/fs.h>
> +#include <linux/uaccess.h>
> +#include <linux/slab.h>
> +#include <linux/stat.h>
> +#include <linux/random.h>
> +#include <linux/gs_pbit.h>
> +#include <linux/gs_tinfoil_slowboot.h>
> +/*******************************************************************************
> + * ___ _ ___ _ *
> + * / __| | | ___ __ __ __ / __| | | __ _ _ _ ___ _ _ *
> + * | (_-\ | | / _ \ \ V V / \__ \ | | / _` | | || | / -_) | '_| *
> + * \___| |_| \___/ \_/\_/ |___/ |_| \__,_| \_, | \___| |_| *
> + * |__/ *
> + * Dedicated to Terry A. Davis *
> + ******************************************************************************/
> +#define GLOW(code, spot, FUNC) pr_err("GS TFSB Fail ErrorCode: %d @ %s.%s\n",\
> + code, spot, FUNC)
> +////////////////////////////////////////////////////////////////////////////////
> +
> +/*
> + * Failure Option to simply alert
> + * @tf: slowboot_tinfoil struct
> + */
> +static void __gs_tinfoil_fail_alert(struct slowboot_tinfoil **tf, int is_bug)
> +{
> + pr_err("GS TFSB FAIL\n");
> + if (is_bug) {
> + if (*tf != NULL)
> + kfree(*tf);
> + *tf = NULL;
> + BUG_ON(is_bug);
> + }
> +}
> +
> +/*
> + * Allocate data for public key signature validation
> + * @sv: sig verify container
> + * @pk: public key
> + */
> +static int pk_sig_verify_alloc(struct sig_verify *sv,
> + const struct public_key *pkey)
> +{
> + struct pbit pc;
> +
> + sv->tfm = crypto_alloc_akcipher(sv->alg_name, 0, 0);
> + if (IS_ERR(sv->tfm)) {
> + PBIT_Y(pc, (int)(long)sv->tfm);
> + sv->tfm = NULL;
> + GLOW(PBIT_GET(pc), __func__, "crypto_alloc_akcipher");
> + return PBIT_RET(pc);
> + }
> +
> + sv->req = akcipher_request_alloc(sv->tfm, GFP_KERNEL);
> + if (!sv->req) {
> + GLOW(-ENOMEM, __func__, "akcipher_request_alloc");
> + return -ENOMEM;
> + }
> +
> + if (crypto_akcipher_set_pub_key(sv->tfm, pkey->key, pkey->keylen)) {
> + GLOW(-ENOMEM,
> + __func__, "crypto_akcipher_set_pub_key");
> + return -EINVAL;
> + }
> +
> + sv->outlen = crypto_akcipher_maxsize(sv->tfm);
> + sv->output = kmalloc(sv->outlen, GFP_KERNEL);
> + if (!sv->output) {
> + GLOW(-ENOMEM, __func__, "kmalloc(sv->output)");
> + return -ENOMEM;
> + }
> +
> + return 0;
> +}
> +
> +/*
> + * Perform signature verification of the config file
> + * @sv: sig verify container
> + * @sig: public key signature
> + */
> +static int pk_sig_verify_validate(struct sig_verify *sv,
> + const struct public_key_signature *sig)
> +{
> + akcipher_request_set_crypt(sv->req, sv->src_tab, NULL, sig->s_size,
> + sig->digest_size);
> +
> + crypto_init_wait(&sv->cwait);
> + akcipher_request_set_callback(sv->req,
> + CRYPTO_TFM_REQ_MAY_BACKLOG|CRYPTO_TFM_REQ_MAY_SLEEP,
> + crypto_req_done, &sv->cwait);
> +
> + return crypto_wait_req(crypto_akcipher_verify(sv->req), &sv->cwait);
> +}
> +
> +/*
> + * Deallocate signature verification data
> + * @sv: sig verify container
> + */
> +static void pk_sig_verify_free(struct sig_verify *sv)
> +{
> + if (sv->output != NULL)
> + kfree(sv->output);
> + if (sv->req != NULL)
> + akcipher_request_free(sv->req);
> + if (sv->tfm != NULL)
> + crypto_free_akcipher(sv->tfm);
> +
> + sv->output = NULL;
> + sv->req = NULL;
> + sv->tfm = NULL;
> +}
> +
> +/*
> + * Perform signature verification
> + * @pkey: public key struct
> + * @sig: public key signature struct
> + */
> +int local_public_key_verify_signature(const struct public_key *pkey,
> + const struct public_key_signature *sig,
> + const char *XCFG_TINFOIL_PKALGOPD)
> +{
> + struct sig_verify sv;
> + struct pbit pc;
> +
> + if (!pkey || !sig || !sig->s || !sig->digest)
> + return -ENOPKG;
> +
> + PBIT_N(pc, -EINVAL);
> +
> + if (__gs_pk_sig_verify_init(&sv, pkey, sig, XCFG_TINFOIL_PKALGOPD)) {
> + GLOW(-EINVAL,
> + __func__, "__gs_pk_sig_verify_init");
> + goto err;
> + }
> +
> + if (pk_sig_verify_alloc(&sv, pkey)) {
> + GLOW(-EINVAL,
> + __func__, "pk_sig_verify_alloc");
> + goto err;
> + }
> +
> + if (pk_sig_verify_validate(&sv, sig) == 0) {
> + PBIT_Y(pc, 0);
> + goto out;
> + }
> +
> +err:
> + PBIT_N(pc, -EINVAL);
> +out:
> + pk_sig_verify_free(&sv);
> + return PBIT_RET(pc);
> +}
> +
> +/*
> + * Open file related to current item
> + * @item: slow boot validation item
> + */
> +static int tinfoil_open(struct slowboot_validation_item *item)
> +{
> + struct pbit pc;
> +
> + item->fp = filp_open(item->path, O_RDONLY, 0);
> + if (IS_ERR(item->fp) || item->fp == NULL) {
> + PBIT_N(pc, (int)(long)item->fp);
> + item->fp = NULL;
> + pr_err("GS TFSB Fail:%s:%s:%d @ %s.filp_open\n",
> + item->hash,
> + item->path,
> + PBIT_OK(item->is_ok),
> + __func__);
> + return PBIT_RET(pc);
> + }
> + item->pos = 0;
> + return 0;
> +}
> +
> +/*
> + * Stat file to get size
> + * @item: slow boot validation item
> + */
> +static int tinfoil_stat_alloc(struct slowboot_tinfoil *tinfoil,
> + struct slowboot_validation_item *item)
> +{
> + if (
> + vfs_getattr(&(item->fp->f_path),
> + tinfoil->st,
> + STATX_SIZE,
> + AT_STATX_SYNC_AS_STAT
> + ) != 0) {
> + pr_err("GS TFSB Fail: Cannot Stat:%s @ %s.vfs_getattr\n",
> + item->path,
> + __func__);
> + return -EINVAL;
> + }
> +
> + item->buf_len = tinfoil->st->size;
> +
> + return 0;
> +}
> +
> +/*
> + * Close file
> + * @item: slowboot validation item
> + */
> +static void tinfoil_close(struct slowboot_validation_item *item)
> +{
> + if (item->fp != NULL)
> + filp_close(item->fp, NULL);
> +}
> +
> +/*
> + * read file into buffer
> + * @item: slowboot validation item
> + */
> +static int tinfoil_read(struct slowboot_tinfoil *tinfoil,
> + struct slowboot_validation_item *item)
> +{
> + struct pbit pc;
> + size_t number_read;
> +
> + number_read = 0;
> + PBIT_N(pc, -EINVAL);
> +
> + if (item->fp == NULL)
> + goto fail;
> +
> + item->buf = vmalloc(item->buf_len+1);
> + if (!item->buf) {
> + PBIT_N(pc, -ENOMEM);
> + goto fail;
> + }
> + memset(item->buf, 0, item->buf_len+1);
> +
> + item->pos = 0;
> + number_read = kernel_read(item->fp,
> + item->buf,
> + tinfoil->st->size,
> + &(item->pos));
> +
> + if (number_read != item->buf_len)
> + goto fail;
> +
> + if (hex2bin(item->b_hash, item->hash, 64) != 0) {
> + pr_err("GS TFSB Fail: StoredHashFail:%s @ %s.hex2bin\n",
> + item->path, __func__);
> + goto fail;
> + }
> +
> + PBIT_Y(pc, 0);
> + goto out;
> +fail:
> + if (item->buf != NULL) {
> + vfree(item->buf);
> + item->buf = NULL;
> + }
> +out:
> + return PBIT_RET(pc);
> +}
> +
> +/*
> + * Zero tinfoil_check and set the item
> + * @c: struct tinfoil check
> + * @item: slowboot validation item
> + */
> +static int tinfoil_check_init(struct tinfoil_check *c,
> + struct slowboot_validation_item *item)
> +{
> + memset(c, 0, sizeof(struct tinfoil_check));
> +
> + if (item == NULL || item->buf == NULL || item->buf_len == 0)
> + return -EINVAL;
> +
> + c->item = item;
> +
> + return 0;
> +}
> +
> +/*
> + * Allocate everyting needed to check one item
> + * @c: tinfoil check
> + */
> +static int tinfoil_check_allocate(struct tinfoil_check *c,
> + const char *XCFG_TINFOIL_HSALGO,
> + int XCFG_TINFOIL_DGLEN)
> +{
> + struct pbit pc;
> +
> + c->alg = crypto_alloc_shash(XCFG_TINFOIL_HSALGO, 0, 0);
> + if (IS_ERR(c->alg)) {
> + PBIT_N(pc, (int)(long)c->alg);
> + c->alg = NULL;
> + GLOW(PBIT_GET(pc), __func__, "crypto_alloc_shash");
> + return PBIT_RET(pc);
> + }
> +
> + c->digest = kmalloc(XCFG_TINFOIL_DGLEN+1, GFP_KERNEL);
> + if (!c->digest) {
> + c->digest = NULL;
> + GLOW(PBIT_GET(pc), __func__, "kmalloc");
> + return -ENOMEM;
> + }
> +
> + memset(c->digest, 0, XCFG_TINFOIL_DGLEN+1);
> +
> + c->sd = __gs_init_sdesc(c->alg);
> + if (!c->sd) {
> + c->sd = NULL;
> + GLOW(-EINVAL, __func__, "__gs_init_sdesc");
> + return -EINVAL;
> + }
> + return 0;
> +}
> +
> +/*
> + * Hash and validate the hash
> + * @c: tinfoil check
> + */
> +static void tinfoil_check_validate(struct tinfoil_check *c,
> + int XCFG_TINFOIL_DGLEN)
> +{
> + int i;
> +
> + crypto_shash_digest(&(c->sd->shash), c->item->buf, c->item->buf_len,
> + c->digest);
> +
> + PBIT_Y(c->item->is_ok, 0);
> + for (i = 0; i < XCFG_TINFOIL_DGLEN; i++) {
> + if (c->item->b_hash[i] != c->digest[i]) {
> + PBIT_N(c->item->is_ok, 0);
> + return;
> + }
> + }
> +}
> +
> +/*
> + * Free Items realted to a tinfoil check
> + * @c: tinfoil check
> + */
> +static void tinfoil_check_free(struct tinfoil_check *c)
> +{
> + if (c->item != NULL && c->item->buf != NULL) {
> + vfree(c->item->buf);
> + c->item->buf = NULL;
> + }
> + if (c->sd != NULL)
> + kfree(c->sd);
> + if (c->digest != NULL)
> + kfree(c->digest);
> + if (c->alg != NULL)
> + crypto_free_shash(c->alg);
> +}
> +
> +/*
> + * Check a single item for validity
> + * @item: slowboot validation item
> + * consumes item->buf
> + */
> +static void tinfoil_check(struct slowboot_validation_item *item,
> + const char *XCFG_TINFOIL_HSALGO,
> + int XCFG_TINFOIL_DGLEN)
> +{
> +
> + struct tinfoil_check check;
> +
> + if (tinfoil_check_init(&check, item)) {
> + GLOW(-EINVAL, __func__, "tinfoil_check_init");
> + goto err;
> + }
> +
> + if (tinfoil_check_allocate(&check,
> + XCFG_TINFOIL_HSALGO,
> + XCFG_TINFOIL_DGLEN)) {
> + GLOW(-EINVAL, __func__, "tinfoil_check_allocate");
> + goto err;
> + }
> +
> + tinfoil_check_validate(&check, XCFG_TINFOIL_DGLEN);
> + goto std_return;
> +err:
> + PBIT_N(item->is_ok, 0);
> +std_return:
> + tinfoil_check_free(&check);
> +}
> +
> +/*
> + * Validate an item (file against it's hash)
> + * The functions will log the failure
> + * This must return 0 or 1 because it adds to a failure count
> + * @item: slowboot validation item
> + */
> +static int tinfoil_unwrap(struct slowboot_tinfoil *tinfoil,
> + struct slowboot_validation_item *item,
> + const char *XCFG_TINFOIL_HSALGO,
> + int XCFG_TINFOIL_DGLEN)
> +{
> + if (tinfoil_open(item) != 0) {
> + GLOW(1, __func__, "tinfoil_open");
> + return 1;
> + }
> +
> + if (tinfoil_stat_alloc(tinfoil, item) != 0) {
> + GLOW(1, __func__, "tinfoil_close");
> + tinfoil_close(item);
> + return 1;
> + }
> +
> + // Do not access item->buf after this
> + if (tinfoil_read(tinfoil, item) != 0) {
> + GLOW(1, __func__, "tinfoil_read");
> + tinfoil_close(item);
> + return 1;
> + }
> +
> + tinfoil_check(item, XCFG_TINFOIL_HSALGO, XCFG_TINFOIL_DGLEN);
> + if (!PBIT_OK(item->is_ok)) {
> + pr_err("GS TFSB Fail:%s:%s @ %s.tinfoil_check\n",
> + item->path,
> + "Fail",
> + __func__);
> + }
> + tinfoil_close(item);
> + if (PBIT_OK(item->is_ok))
> + return 0;
> + else
> + return 1;
> +}
> +
> +/*
> + * Parse one line, fill in the item
> + * @item: slowboot validation item
> + * @line: start of current line
> + * @remaining: remaining bytes
> + */
> +static loff_t fill_in_item(struct slowboot_validation_item *item,
> + char *line, loff_t *remaining,
> + const char XCFG_TINFOIL_NEW_LINE,
> + int XCFG_TINFOIL_HSLEN)
> +{
> + loff_t pos, off, rem;
> +
> + if (line == NULL) {
> + if (remaining != NULL)
> + *remaining = 0;
> + return 0;
> + }
> +
> + pos = 0;
> + off = 0;
> + rem = *remaining;
> +
> + while (rem > 0) {
> + if (line[pos] == ' ' && off == 0 && rem > 1)
> + off = pos+1;
> +
> + if (line[pos] == XCFG_TINFOIL_NEW_LINE)
> + break;
> +
> + pos++;
> + rem--;
> + }
> +
> + if (item->path != NULL && item->hash != NULL) {
> + memset(item->path, 0, PATH_MAX+1);
> + memset(item->hash, 0, XCFG_TINFOIL_HSLEN+2);
> +
> + // Make sure we have a good item
> + // This should not happen because who
> + // would sign something malicous?
> + if (pos > (XCFG_TINFOIL_HSLEN+5) && (pos-off-1) > 0) {
> + memcpy(item->hash, line, XCFG_TINFOIL_HSLEN);
> + memcpy(item->path, line+off, pos-off);
> + }
> + }
> +
> + if (rem > 0) {
> + pos++;
> + rem--;
> + }
> + *remaining = rem;
> + return pos;
> +}
> +
> +/*
> + * initialize slowboot init container items
> + * @sic: slowboot init container
> + */
> +static void slowboot_init_setup(struct slowboot_init_container *sic,
> + const char *XCFG_TINFOIL_PKALGO,
> + const char *XCFG_TINFOIL_IDTYPE,
> + int XCFG_TINFOIL_DGLEN,
> + const char *XCFG_TINFOIL_HSALGO,
> + int XCFG_TINFOIL_PKLEN)
> +{
> + memset(sic, 0, sizeof(struct slowboot_init_container));
> +
> + sic->rsa_pub_key.pkey_algo = XCFG_TINFOIL_PKALGO;
> + sic->rsa_pub_key.id_type = XCFG_TINFOIL_IDTYPE;
> + sic->rsa_pub_key.keylen = -1;
> + sic->sig.digest_size = XCFG_TINFOIL_DGLEN;
> + sic->sig.pkey_algo = XCFG_TINFOIL_PKALGO;
> + sic->sig.hash_algo = XCFG_TINFOIL_HSALGO;
> + sic->kernel_key_len = XCFG_TINFOIL_PKLEN/2; // Hex/2
> +}
> +
> +/*
> + * Set up keys
> + * @sic: slowboot init container
> + * @config_pkey: hex representation of DER encoded public key
> + */
> +static int slowboot_init_setup_keys(struct slowboot_init_container *sic,
> + const char *config_pkey)
> +{
> +
> + if (sic->kernel_key_len <= 0 || config_pkey == NULL)
> + return -EINVAL;
> +
> + sic->kernel_key = (unsigned char *)
> + kmalloc(sic->kernel_key_len+1, GFP_KERNEL);
> + if (!sic->kernel_key)
> + return -ENOMEM;
> +
> + if (hex2bin(sic->kernel_key, config_pkey, sic->kernel_key_len) == 0)
> + sic->kernel_key[sic->kernel_key_len] = '\0';
> + else
> + return -EINVAL;
> +
> + sic->rsa_pub_key.key = sic->kernel_key;
> + sic->rsa_pub_key.keylen = sic->kernel_key_len;
> +
> + return 0;
> +}
> +
> +/*
> + * Open config files, read them into memory
> + * @sic: slowboot init container
> + * @config_file: hash path\n format config file
> + * @config_file_signature: raw binary checksum file of @config_file
> + */
> +static int slowboot_init_open_files(struct slowboot_init_container *sic,
> + const char *config_file,
> + const char *config_file_signature)
> +{
> + struct pbit pc;
> +
> + if (config_file == NULL || config_file_signature == NULL)
> + return -EINVAL;
> +
> + sic->fp = filp_open(config_file, O_RDONLY, 0);
> + if (IS_ERR(sic->fp)) {
> + PBIT_N(pc, (int)(long)sic->fp);
> + sic->fp = NULL;
> + GLOW(PBIT_GET(pc), __func__, "config_file");
> + return PBIT_RET(pc);
> + }
> +
> + sic->sfp = filp_open(config_file_signature, O_RDONLY, 0);
> + if (IS_ERR(sic->sfp)) {
> + PBIT_N(pc, (int)(long)sic->sfp);
> + sic->sfp = NULL;
> + GLOW(PBIT_GET(pc), __func__, "config_file_signature");
> + return PBIT_RET(pc);
> + }
> +
> + sic->file_size = __gs_get_file_size(sic->fp);
> + sic->sfp_file_size = __gs_get_file_size(sic->sfp);
> +
> + if (sic->file_size <= 0 || sic->sfp_file_size <= 0) {
> + GLOW(-EINVAL, __func__, "invalid file size");
> + return -EINVAL;
> + }
> +
> + sic->pos = 0;
> + sic->buf = __gs_read_file_to_memory(sic->fp, sic->file_size,
> + &sic->pos, 0);
> + if (!sic->buf) {
> + pr_err("GS TFSB File Read Error:%s @ %s.config_file\n",
> + config_file,
> + __func__);
> + return -EINVAL;
> + }
> +
> + sic->sfp_pos = 0;
> + sic->sfp_buf = __gs_read_file_to_memory(sic->sfp, sic->sfp_file_size,
> + &sic->sfp_pos, 0);
> + if (!sic->sfp_buf) {
> + pr_err("GS TFSB File Read Error:%s @ %s.config_file_signature\n",
> + config_file_signature,
> + __func__);
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +/*
> + * Intiatilize and perform hash digest of the config file
> + * @sic: slowboot init container
> + */
> +static int slowboot_init_digest(struct slowboot_init_container *sic,
> + int XCFG_TINFOIL_DGLEN,
> + const char *XCFG_TINFOIL_HSALGO)
> +{
> + struct pbit pc;
> +
> + sic->halg = crypto_alloc_shash(XCFG_TINFOIL_HSALGO, 0, 0);
> + if (IS_ERR(sic->halg)) {
> + PBIT_N(pc, (int)(long)sic->halg);
> + GLOW(PBIT_GET(pc), __func__, "crypto_alloc_shash");
> + sic->halg = NULL;
> + return PBIT_RET(pc);
> + }
> +
> + sic->digest = kmalloc(XCFG_TINFOIL_DGLEN+1, GFP_KERNEL);
> + if (!sic->digest) {
> + GLOW(-ENOMEM, __func__, "kmalloc~digest");
> + return -ENOMEM;
> + }
> +
> + memset(sic->digest, 0, XCFG_TINFOIL_DGLEN+1);
> +
> + sic->hsd = __gs_init_sdesc(sic->halg);
> + if (!sic->hsd) {
> + GLOW(-EINVAL, __func__, "__gs_init_sdesc");
> + return -EINVAL;
> + }
> +
> + if (sic->buf == NULL || sic->file_size <= 0) {
> + GLOW(-EINVAL, __func__, "~invalid buffer or file_size");
> + return -EINVAL;
> + }
> +
> + crypto_shash_digest(&(sic->hsd->shash), sic->buf, sic->file_size,
> + sic->digest);
> +
> + sic->sig.s = sic->sfp_buf; // Raw signature file data
> + sic->sig.s_size = sic->sfp_file_size; // Length of Signature File
> + sic->sig.digest = sic->digest; // Hash of the config file
> +
> + return 0;
> +}
> +
> +/*
> + * Free slowboot init container items
> + * @sic: slowboot init container
> + */
> +static void slowboot_init_free(struct slowboot_init_container *sic)
> +{
> + if (sic->fp != NULL)
> + filp_close(sic->fp, NULL);
> + if (sic->sfp != NULL)
> + filp_close(sic->sfp, NULL);
> + if (sic->halg != NULL)
> + kfree(sic->halg);
> + if (sic->buf != NULL)
> + vfree(sic->buf);
> + if (sic->sfp_buf != NULL)
> + vfree(sic->sfp_buf);
> + if (sic->kernel_key != NULL)
> + kfree(sic->kernel_key);
> + if (sic->digest != NULL)
> + kfree(sic->digest);
> + sic->c_item = NULL;
> +}
> +
> +/*
> + * Determine the number of lines, allocate enough space for all items
> + * parse all the lines, update values to reflect
> + * @sic: slowboot init container
> + * @item_ref: ** to array of items, set to not null by function on success
> + * @item-ct: * to item count, updated by function
> + */
> +static int slowboot_init_process(struct slowboot_init_container *sic,
> + struct slowboot_validation_item **item_ref,
> + int *item_ct,
> + const char XCFG_TINFOIL_NEW_LINE,
> + int XCFG_TINFOIL_HSLEN)
> +{
> +
> + if (sic->file_size <= 0) {
> + GLOW(-EINVAL, __func__, "~invalid file size");
> + return -EINVAL;
> + }
> +
> + for (sic->pos = 0; sic->pos < sic->file_size; sic->pos++) {
> + if (sic->buf[sic->pos] == XCFG_TINFOIL_NEW_LINE)
> + sic->num_items++;
> + }
> +
> + if (sic->num_items == 0) {
> + GLOW(-EINVAL, __func__, "~no items");
> + return -EINVAL;
> + }
> +
> + sic->c_item = sic->items = (struct slowboot_validation_item *)
> + vmalloc(sizeof(struct slowboot_validation_item)
> + *sic->num_items);
> +
> + if (!sic->c_item) {
> + GLOW(-ENOMEM, __func__, "vmalloc~c_item");
> + return -ENOMEM;
> + }
> +
> + sic->pos = 0; // reusing
> + sic->remaining = sic->file_size;
> + while (sic->remaining) {
> + sic->pos += fill_in_item(sic->c_item, &sic->buf[sic->pos],
> + &sic->remaining,
> + XCFG_TINFOIL_NEW_LINE,
> + XCFG_TINFOIL_HSLEN);
> + sic->c_item++;
> + }
> +
> + *item_ref = sic->items;
> + *item_ct = sic->num_items;
> + return 0;
> +}
> +
> +/*
> + * Signature check the config file and initialize all the data
> + * The functions called will log the error so no need to store/check
> + * @tinfoil: slowboot tinfoil
> + */
> +static int slowboot_init(struct slowboot_tinfoil *tinfoil,
> + const char *XCFG_TINFOIL_PKALGOPD,
> + const char *XCFG_TINFOIL_PKALGO,
> + const char *XCFG_TINFOIL_IDTYPE,
> + int XCFG_TINFOIL_DGLEN,
> + const char *XCFG_TINFOIL_HSALGO,
> + int XCFG_TINFOIL_PKLEN,
> + const char XCFG_TINFOIL_NEW_LINE,
> + int XCFG_TINFOIL_HSLEN)
> +{
> + struct slowboot_init_container sic;
> + struct pbit pc;
> +
> + PBIT_N(pc, -EINVAL);
> +
> + slowboot_init_setup(&sic,
> + XCFG_TINFOIL_PKALGO,
> + XCFG_TINFOIL_IDTYPE,
> + XCFG_TINFOIL_DGLEN,
> + XCFG_TINFOIL_HSALGO,
> + XCFG_TINFOIL_PKLEN);
> +
> + if (slowboot_init_setup_keys(&sic, tinfoil->config_pkey))
> + goto fail;
> +
> + if (slowboot_init_open_files(&sic, tinfoil->config_file,
> + tinfoil->config_file_signature))
> + goto fail;
> +
> + if (slowboot_init_digest(&sic,
> + XCFG_TINFOIL_DGLEN,
> + XCFG_TINFOIL_HSALGO))
> + goto fail;
> +
> + if (local_public_key_verify_signature(&sic.rsa_pub_key,
> + &sic.sig,
> + XCFG_TINFOIL_PKALGOPD))
> + goto fail;
> +
> + if (slowboot_init_process(&sic, &tinfoil->validation_items,
> + &tinfoil->slwbt_ct,
> + XCFG_TINFOIL_NEW_LINE,
> + XCFG_TINFOIL_HSLEN))
> + goto fail;
> +
> + PBIT_Y(pc, 0);
> + goto out;
> +
> +fail:
> + PBIT_N(pc, -EINVAL);
> + GLOW(PBIT_GET(pc), __func__, "~^^^^^^///////////>");
> + tinfoil->slwbt_ct = 0;
> + if (!sic.items)
> + vfree(sic.items);
> +
> + tinfoil->validation_items = NULL;
> +out:
> + slowboot_init_free(&sic);
> + return PBIT_RET(pc);
> +}
> +
> +/*
> + * Check for /proc/cmdlin override
> + */
> +static int slowboot_enabled(const char *XCFG_TINFOIL_OVERRIDE)
> +{
> + struct file *fp;
> + size_t file_size;
> + char *buf;
> + loff_t pos;
> + struct pbit pc;
> +
> + fp = NULL;
> + file_size = 0;
> + buf = NULL;
> + pos = 0;
> +
> + PBIT_N(pc, 0);
> +
> + fp = filp_open("/proc/cmdline", O_RDONLY, 0);
> + if (IS_ERR(fp)) {
> + fp = NULL;
> + PBIT_N(pc, (int)(long)fp);
> + goto out;
> + }
> +
> + file_size = PAGE_SIZE;
> + buf = __gs_read_file_to_memory(fp, file_size, &pos, 1);
> +
> + if (!buf) {
> + PBIT_N(pc, -ENOMEM);
> + goto out;
> + }
> +
> + if (__gs_memmem_sp(buf, file_size,
> + XCFG_TINFOIL_OVERRIDE,
> + strlen(XCFG_TINFOIL_OVERRIDE)) == 0)
> + PBIT_Y(pc, 0);
> +
> +out:
> + if (fp != NULL)
> + filp_close(fp, NULL);
> + if (buf != NULL)
> + vfree(buf);
> +
> + if (!PBIT_FAIL(pc) || PBIT_GET(pc) != 0)
> + GLOW(PBIT_GET(pc), __func__, "~???");
> +
> + if (PBIT_OK(pc) && PBIT_GET(pc) == 0)
> + return 0;
> + else
> + return 1;
> +
> +}
> +
> +/*
> + * Run validation test
> + * @tinfoil: slowboot tinfoil struct
> + */
> +static void slowboot_run_test(struct slowboot_tinfoil *tinfoil,
> + const char *XCFG_TINFOIL_PKALGOPD,
> + const char *XCFG_TINFOIL_PKALGO,
> + const char *XCFG_TINFOIL_IDTYPE,
> + int XCFG_TINFOIL_DGLEN,
> + const char *XCFG_TINFOIL_HSALGO,
> + int XCFG_TINFOIL_PKLEN,
> + const char XCFG_TINFOIL_NEW_LINE,
> + int XCFG_TINFOIL_HSLEN,
> + spinlock_t *gs_irq_killer)
> +{
> + int j;
> + unsigned long flags;
> + struct pbit hard_fail;
> +
> + WARN_ON(!tinfoil);
> + if (!tinfoil)
> + return;
> +
> + PBIT_Y(hard_fail, 0);
> + PBIT_N(tinfoil->error, -EINVAL);
> +
> + spin_lock_irqsave(gs_irq_killer, flags); // Occupy all threads?
> + if (tinfoil->initialized != 0) {
> + tinfoil->initialized = 0;
> + tinfoil->validation_items = NULL;
> + if (slowboot_init(tinfoil,
> + XCFG_TINFOIL_PKALGOPD,
> + XCFG_TINFOIL_PKALGO,
> + XCFG_TINFOIL_IDTYPE,
> + XCFG_TINFOIL_DGLEN,
> + XCFG_TINFOIL_HSALGO,
> + XCFG_TINFOIL_PKLEN,
> + XCFG_TINFOIL_NEW_LINE,
> + XCFG_TINFOIL_HSLEN) != 0) {
> + PBIT_N(hard_fail, 0);
> + goto out;
> + }
> + }
> +
> + for (j = 0; j < tinfoil->slwbt_ct; j++) {
> + tinfoil->failures += tinfoil_unwrap(tinfoil,
> + &(tinfoil->validation_items[j]),
> + XCFG_TINFOIL_HSALGO,
> + XCFG_TINFOIL_DGLEN);
> + }
> +out:
> + if (tinfoil->validation_items != NULL) {
> + vfree(tinfoil->validation_items);
> + tinfoil->validation_items = NULL;
> + tinfoil->initialized = 1;
> + }
> +
> + if (tinfoil->failures != 0 || tinfoil->slwbt_ct == 0 ||
> + !PBIT_OK(hard_fail))
> + PBIT_N(tinfoil->error, -EINVAL);
> + else
> + PBIT_Y(tinfoil->error, 0);
> + spin_unlock_irqrestore(gs_irq_killer, flags);
> +}
> +
> +/*
> + * Initialize data for verification process
> + * @tinfoil: slowboot tinfoil struct
> + */
> +static int slowboot_tinfoil_init(struct slowboot_tinfoil *tinfoil,
> + const char *XCFG_TINFOIL_CF,
> + const char *XCFG_TINFOIL_CFS,
> + const char *XCFG_TINFOIL_PK,
> + int XCFG_TINFOIL_PKLEN)
> +{
> + if (tinfoil == NULL)
> + return -EINVAL;
> +
> + memset(tinfoil, 0, sizeof(struct slowboot_tinfoil));
> + strncpy(tinfoil->config_file, XCFG_TINFOIL_CF, PATH_MAX);
> + strncpy(tinfoil->config_file_signature, XCFG_TINFOIL_CFS, PATH_MAX);
> + strncpy(tinfoil->config_pkey, XCFG_TINFOIL_PK, XCFG_TINFOIL_PKLEN);
> + tinfoil->initialized = 1;
> + tinfoil->failures = 0;
> + PBIT_Y(tinfoil->error, 0);
> + tinfoil->st = kmalloc(sizeof(struct kstat), GFP_KERNEL);
> + if (!tinfoil->st) {
> + PBIT_N(tinfoil->error, -ENOMEM);
> + return 1;
> + }
> + return 0;
> +}
> +
> +/*
> + * Deallocate data
> + * @tinfoil: slowboot tinfoil struct
> + */
> +static void slowboot_tinfoil_free(struct slowboot_tinfoil *tinfoil)
> +{
> + if (tinfoil->st != NULL) {
> + kfree(tinfoil->st);
> + tinfoil->st = NULL;
> + }
> +}
> +
> +////////////////////////////////////////////////////////////////////////////////
> +/*
> + * Obtain size of file via seeking
> + * @fp: struct file
> + */
> +size_t __gs_get_file_size(struct file *fp)
> +{
> + size_t file_size;
> +
> + file_size = 0;
> + if (fp == NULL)
> + goto out;
> +
> + default_llseek(fp, 0, SEEK_END);
> + file_size = fp->f_pos;
> + default_llseek(fp, fp->f_pos * -1, SEEK_CUR);
> +
> +out:
> + return file_size;
> +}
> +
> +/*
> + * Read file into memory, check every thing
> + * @fp: file structure
> + * @file_size: stated size of file
> + * @pos: position offset return value
> + */
> +char *__gs_read_file_to_memory(struct file *fp,
> + size_t file_size,
> + loff_t *pos,
> + int ignore_size)
> +{
> + char *buf;
> + size_t num_read;
> +
> + buf = NULL;
> +
> + if (!fp || file_size < 1)
> + goto out;
> +
> + buf = vmalloc(file_size+1);
> +
> + if (!buf)
> + goto out;
> +
> + *pos = 0;
> +
> + default_llseek(fp, 0, SEEK_END);
> + default_llseek(fp, fp->f_pos * -1, SEEK_CUR);
> + num_read = kernel_read(fp, buf, file_size, pos);
> +
> + if (num_read != file_size && !ignore_size) {
> + vfree(buf);
> + buf = NULL;
> + }
> +
> +out:
> + return buf;
> +}
> +
> +/*
> + * Check string for string, 0 is true
> + * @s1: big string
> + * @s1_len: length of big_string
> + * @s2: little string
> + * @s2_len: length of little string
> + */
> +int __gs_memmem_sp(const char *s1, size_t s1_len, const char *s2, size_t s2_len)
> +{
> + while (s1_len >= s2_len) {
> + s1_len--;
> + if (!memcmp(s1, s2, s2_len))
> + return 0;
> + s1++;
> + }
> + return 1;
> +}
> +
> +/*
> + * Initialize sdesc struct for digest measuring
> + * @alg: crypto_shash structure
> + */
> +struct sdesc *__gs_init_sdesc(struct crypto_shash *alg)
> +{
> + struct sdesc *sdesc;
> + int size;
> +
> + size = sizeof(struct shash_desc) + crypto_shash_descsize(alg);
> + sdesc = kmalloc(size, GFP_KERNEL);
> + if (!sdesc)
> + return NULL;
> + sdesc->shash.tfm = alg;
> + return sdesc;
> +}
> +
> +/*
> + * Initialize public key signature verification
> + * @sv: sig verify container
> + * @pkey: public key
> + * @sig: public key signature
> + * @pkalgopd: padding string
> + */
> +int __gs_pk_sig_verify_init(struct sig_verify *sv,
> + const struct public_key *pkey,
> + const struct public_key_signature *sig,
> + const char *pkalgopd)
> +{
> + memset(sv, 0, sizeof(struct sig_verify));
> +
> + if (pkalgopd != NULL && strlen(pkalgopd) > 0) {
> + snprintf(sv->alg_name_buf, CRYPTO_MAX_ALG_NAME,
> + pkalgopd);
> + sv->alg_name = sv->alg_name_buf;
> + } else
> + sv->alg_name = sig->pkey_algo;
> +
> + sg_init_table(sv->src_tab, 3);
> + sg_set_buf(&sv->src_tab[1], sig->digest, sig->digest_size);
> + sg_set_buf(&sv->src_tab[0], sig->s, sig->s_size);
> + return 0;
> +}
> +
> +/*
> + * Perform entire test
> + * @config_tinfoil_cf: path for the configuration file
> + * @config_tinfoil_cfs: path for the configuration file checksum file
> + * @config_tinfoil_pk: correctly (DER for RSA) encoded public key in HEX
> + * @config_tinfoil_pklen: strlen of @tinfoil_pk
> + * @config_tinfoil_dglen: number of bytes in digest 64 for sha512
> + * @config_tinfoil_hslen: strlen of hex representation of digest, 128 for sha512
> + * @tinfoil_pkalgo: algorithm used, likely "rsa"
> + * @tinfoil_pkalgopd: algorithm padding, likely "pkcs1pad(rsa,sha512)" can be ""
> + * @tinfoil_hsalgo: digest used, likely "sha512"
> + * @config_tinfoil_idtype: public_key.id_type likely "X509"
> + * @gs_irq_killer: Nullable spinlock_t to block IRQ during test
> + * @config_tinfoil_new_line: char for new line '\n'
> + * @config_tinfoil_override: magic cmdline value to bypass test
> + * @config_tinfoil_version: logic version to use likely 1
> + * @config_tinfoil_reserved: future use
> + * @config_tinfoil_unused: future use
> + * @config_bug_on_fail: BUG(); if errors occur
> + */
> +int __gs_tfsb_go(const char *config_tinfoil_cf,
> + const char *config_tinfoil_cfs,
> + const char *config_tinfoil_pk,
> + int config_tinfoil_pklen,
> + int config_tinfoil_dglen,
> + int config_tinfoil_hslen,
> + const char *config_tinfoil_pkalgo,
> + const char *config_tinfoil_pkalgopd,
> + const char *config_tinfoil_hsalgo,
> + const char *config_tinfoil_idtype,
> + spinlock_t *gs_irq_killer,
> + char config_tinfoil_new_line,
> + const char *config_tinfoil_override,
> + int config_tinfoil_version,
> + const char *config_tinfoil_reserved,
> + const void *config_tinfoil_unused,
> + int config_bug_on_fail)
> +{
> + struct slowboot_tinfoil *tinfoil;
> + struct pbit pc;
> +
> + if (!slowboot_enabled(config_tinfoil_override)) {
> + pr_err("GS TFSB: disabled\n");
> + return 0;
> + }
> +
> + PBIT_N(pc, -EINVAL);
> + pr_info("GS TFSB START\n");
> +
> + tinfoil = kmalloc(sizeof(struct slowboot_tinfoil), GFP_KERNEL);
> + if (!tinfoil) {
> + PBIT_N(pc, -ENOMEM);
> + goto out;
> + }
> +
> +
> + if (slowboot_tinfoil_init(tinfoil,
> + config_tinfoil_cf,
> + config_tinfoil_cfs,
> + config_tinfoil_pk,
> + config_tinfoil_pklen)) {
> + PBIT_N(pc, -EINVAL);
> + goto out;
> + }
> +
> + slowboot_run_test(tinfoil,
> + config_tinfoil_pkalgopd,
> + config_tinfoil_pkalgo,
> + config_tinfoil_idtype,
> + config_tinfoil_dglen,
> + config_tinfoil_hsalgo,
> + config_tinfoil_pklen,
> + config_tinfoil_new_line,
> + config_tinfoil_hslen,
> + gs_irq_killer);
> +
> +out:
> + if (tinfoil) {
> + slowboot_tinfoil_free(tinfoil);
> +
> + pr_info("GS TFSB Audit: {Total:%d/Failures:%d}\n",
> + tinfoil->slwbt_ct, tinfoil->failures);
> +
> + if (!PBIT_OK(tinfoil->error) || PBIT_GET(tinfoil->error) != 0) {
> + PBIT_N(pc, PBIT_GET(tinfoil->error));
> + if (PBIT_GET(tinfoil->error) == 0)
> + PBIT_N(pc, -EINVAL);
> + } else
> + PBIT_Y(pc, 0);
> +
> + } else {
> + PBIT_N(pc, -EINVAL);
> + }
> +
> +
> + if (PBIT_GET(pc) != 0 || !PBIT_OK(pc)) {
> + __gs_tinfoil_fail_alert(&tinfoil, config_bug_on_fail);
> + if (tinfoil != NULL) {
> + kfree(tinfoil);
> + tinfoil = NULL;
> + }
> + return PBIT_RET(pc);
> + } else
> + return PBIT_RET(pc);
> +
> +}
> +EXPORT_SYMBOL_GPL(__gs_tfsb_go);
More information about the Linux-security-module-archive
mailing list