[PATCH] Add GlowSlayer Explicit Access Control from Cory Craig <gs.cory.craig at gmail.com>
Cory Craig
gs.cory.craig at gmail.com
Thu Dec 2 00:45:05 UTC 2021
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>
---
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);
--
2.33.1
More information about the Linux-security-module-archive
mailing list