]> git.dujemihanovic.xyz Git - linux.git/commitdiff
s390/ipl: read IPL report at early boot
authorMartin Schwidefsky <schwidefsky@de.ibm.com>
Thu, 21 Feb 2019 13:23:04 +0000 (14:23 +0100)
committerMartin Schwidefsky <schwidefsky@de.ibm.com>
Fri, 26 Apr 2019 10:34:05 +0000 (12:34 +0200)
Read the IPL Report block provided by secure-boot, add the entries
of the certificate list to the system key ring and print the list
of components.

PR: Adjust to Vasilys bootdata_preserved patch set. Preserve ipl_cert_list
for later use in kexec_file.

Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Signed-off-by: Philipp Rudo <prudo@linux.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
arch/s390/boot/Makefile
arch/s390/boot/boot.h
arch/s390/boot/ipl_report.c [new file with mode: 0644]
arch/s390/boot/startup.c
arch/s390/include/asm/boot_data.h
arch/s390/kernel/ipl.c
arch/s390/kernel/setup.c
security/integrity/Kconfig
security/integrity/Makefile
security/integrity/platform_certs/load_ipl_s390.c [new file with mode: 0644]

index a5ae68b2aa844f3063c7347d00c1cba7c1ef3780..1f8fd68beae382278ef5d884ca0e0e6f6ad9f81f 100644 (file)
@@ -28,8 +28,9 @@ endif
 
 CFLAGS_sclp_early_core.o += -I$(srctree)/drivers/s390/char
 
-obj-y  := head.o als.o startup.o mem_detect.o ipl_parm.o string.o ebcdic.o
-obj-y  += sclp_early_core.o mem.o ipl_vmparm.o cmdline.o ctype.o
+obj-y  := head.o als.o startup.o mem_detect.o ipl_parm.o ipl_report.o
+obj-y  += string.o ebcdic.o sclp_early_core.o mem.o ipl_vmparm.o cmdline.o
+obj-y  += ctype.o
 obj-$(CONFIG_PROTECTED_VIRTUALIZATION_GUEST)   += uv.o
 targets        := bzImage startup.a section_cmp.boot.data section_cmp.boot.preserved.data $(obj-y)
 subdir-        := compressed
index 82bc06346e05846b2c6e2c01f06a1b20ecb1771c..ca395fcff15efb00a9f251635093a60a529122cd 100644 (file)
@@ -10,4 +10,6 @@ void parse_boot_command_line(void);
 void setup_memory_end(void);
 void print_missing_facilities(void);
 
+unsigned long read_ipl_report(unsigned long safe_offset);
+
 #endif /* BOOT_BOOT_H */
diff --git a/arch/s390/boot/ipl_report.c b/arch/s390/boot/ipl_report.c
new file mode 100644 (file)
index 0000000..0b49655
--- /dev/null
@@ -0,0 +1,165 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/init.h>
+#include <linux/ctype.h>
+#include <asm/ebcdic.h>
+#include <asm/sclp.h>
+#include <asm/sections.h>
+#include <asm/boot_data.h>
+#include <uapi/asm/ipl.h>
+#include "boot.h"
+
+int __bootdata_preserved(ipl_secure_flag);
+
+unsigned long __bootdata_preserved(ipl_cert_list_addr);
+unsigned long __bootdata_preserved(ipl_cert_list_size);
+
+unsigned long __bootdata(early_ipl_comp_list_addr);
+unsigned long __bootdata(early_ipl_comp_list_size);
+
+#define for_each_rb_entry(entry, rb) \
+       for (entry = rb->entries; \
+            (void *) entry + sizeof(*entry) <= (void *) rb + rb->len; \
+            entry++)
+
+static inline bool intersects(unsigned long addr0, unsigned long size0,
+                             unsigned long addr1, unsigned long size1)
+{
+       return addr0 + size0 > addr1 && addr1 + size1 > addr0;
+}
+
+static unsigned long find_bootdata_space(struct ipl_rb_components *comps,
+                                        struct ipl_rb_certificates *certs,
+                                        unsigned long safe_addr)
+{
+       struct ipl_rb_certificate_entry *cert;
+       struct ipl_rb_component_entry *comp;
+       size_t size;
+
+       /*
+        * Find the length for the IPL report boot data
+        */
+       early_ipl_comp_list_size = 0;
+       for_each_rb_entry(comp, comps)
+               early_ipl_comp_list_size += sizeof(*comp);
+       ipl_cert_list_size = 0;
+       for_each_rb_entry(cert, certs)
+               ipl_cert_list_size += sizeof(unsigned int) + cert->len;
+       size = ipl_cert_list_size + early_ipl_comp_list_size;
+
+       /*
+        * Start from safe_addr to find a free memory area large
+        * enough for the IPL report boot data. This area is used
+        * for ipl_cert_list_addr/ipl_cert_list_size and
+        * early_ipl_comp_list_addr/early_ipl_comp_list_size. It must
+        * not overlap with any component or any certificate.
+        */
+repeat:
+       if (IS_ENABLED(CONFIG_BLK_DEV_INITRD) && INITRD_START && INITRD_SIZE &&
+           intersects(INITRD_START, INITRD_SIZE, safe_addr, size))
+               safe_addr = INITRD_START + INITRD_SIZE;
+       for_each_rb_entry(comp, comps)
+               if (intersects(safe_addr, size, comp->addr, comp->len)) {
+                       safe_addr = comp->addr + comp->len;
+                       goto repeat;
+               }
+       for_each_rb_entry(cert, certs)
+               if (intersects(safe_addr, size, cert->addr, cert->len)) {
+                       safe_addr = cert->addr + cert->len;
+                       goto repeat;
+               }
+       early_ipl_comp_list_addr = safe_addr;
+       ipl_cert_list_addr = safe_addr + early_ipl_comp_list_size;
+
+       return safe_addr + size;
+}
+
+static void copy_components_bootdata(struct ipl_rb_components *comps)
+{
+       struct ipl_rb_component_entry *comp, *ptr;
+
+       ptr = (struct ipl_rb_component_entry *) early_ipl_comp_list_addr;
+       for_each_rb_entry(comp, comps)
+               memcpy(ptr++, comp, sizeof(*ptr));
+}
+
+static void copy_certificates_bootdata(struct ipl_rb_certificates *certs)
+{
+       struct ipl_rb_certificate_entry *cert;
+       void *ptr;
+
+       ptr = (void *) ipl_cert_list_addr;
+       for_each_rb_entry(cert, certs) {
+               *(unsigned int *) ptr = cert->len;
+               ptr += sizeof(unsigned int);
+               memcpy(ptr, (void *) cert->addr, cert->len);
+               ptr += cert->len;
+       }
+}
+
+unsigned long read_ipl_report(unsigned long safe_addr)
+{
+       struct ipl_rb_certificates *certs;
+       struct ipl_rb_components *comps;
+       struct ipl_pl_hdr *pl_hdr;
+       struct ipl_rl_hdr *rl_hdr;
+       struct ipl_rb_hdr *rb_hdr;
+       unsigned long tmp;
+       void *rl_end;
+
+       /*
+        * Check if there is a IPL report by looking at the copy
+        * of the IPL parameter information block.
+        */
+       if (!ipl_block_valid ||
+           !(ipl_block.hdr.flags & IPL_PL_FLAG_IPLSR))
+               return safe_addr;
+       ipl_secure_flag = !!(ipl_block.hdr.flags & IPL_PL_FLAG_SIPL);
+       /*
+        * There is an IPL report, to find it load the pointer to the
+        * IPL parameter information block from lowcore and skip past
+        * the IPL parameter list, then align the address to a double
+        * word boundary.
+        */
+       tmp = (unsigned long) S390_lowcore.ipl_parmblock_ptr;
+       pl_hdr = (struct ipl_pl_hdr *) tmp;
+       tmp = (tmp + pl_hdr->len + 7) & -8UL;
+       rl_hdr = (struct ipl_rl_hdr *) tmp;
+       /* Walk through the IPL report blocks in the IPL Report list */
+       certs = NULL;
+       comps = NULL;
+       rl_end = (void *) rl_hdr + rl_hdr->len;
+       rb_hdr = (void *) rl_hdr + sizeof(*rl_hdr);
+       while ((void *) rb_hdr + sizeof(*rb_hdr) < rl_end &&
+              (void *) rb_hdr + rb_hdr->len <= rl_end) {
+
+               switch (rb_hdr->rbt) {
+               case IPL_RBT_CERTIFICATES:
+                       certs = (struct ipl_rb_certificates *) rb_hdr;
+                       break;
+               case IPL_RBT_COMPONENTS:
+                       comps = (struct ipl_rb_components *) rb_hdr;
+                       break;
+               default:
+                       break;
+               }
+
+               rb_hdr = (void *) rb_hdr + rb_hdr->len;
+       }
+
+       /*
+        * With either the component list or the certificate list
+        * missing the kernel will stay ignorant of secure IPL.
+        */
+       if (!comps || !certs)
+               return safe_addr;
+
+       /*
+        * Copy component and certificate list to a safe area
+        * where the decompressed kernel can find them.
+        */
+       safe_addr = find_bootdata_space(comps, certs, safe_addr);
+       copy_components_bootdata(comps);
+       copy_certificates_bootdata(certs);
+
+       return safe_addr;
+}
index 2bd4a62d436c19b82a871c1e06581d85db7268ee..90898976a9410bd99afb9d9452e9052b5f92c64d 100644 (file)
@@ -25,19 +25,16 @@ unsigned long mem_safe_offset(void)
 }
 #endif
 
-static void rescue_initrd(void)
+static void rescue_initrd(unsigned long addr)
 {
-       unsigned long min_initrd_addr;
-
        if (!IS_ENABLED(CONFIG_BLK_DEV_INITRD))
                return;
        if (!INITRD_START || !INITRD_SIZE)
                return;
-       min_initrd_addr = mem_safe_offset();
-       if (min_initrd_addr <= INITRD_START)
+       if (addr <= INITRD_START)
                return;
-       memmove((void *)min_initrd_addr, (void *)INITRD_START, INITRD_SIZE);
-       INITRD_START = min_initrd_addr;
+       memmove((void *)addr, (void *)INITRD_START, INITRD_SIZE);
+       INITRD_START = addr;
 }
 
 static void copy_bootdata(void)
@@ -52,12 +49,15 @@ static void copy_bootdata(void)
 
 void startup_kernel(void)
 {
+       unsigned long safe_addr;
        void *img;
 
+       store_ipl_parmblock();
+       safe_addr = mem_safe_offset();
+       safe_addr = read_ipl_report(safe_addr);
        uv_query_info();
-       rescue_initrd();
+       rescue_initrd(safe_addr);
        sclp_early_read_info();
-       store_ipl_parmblock();
        setup_boot_command_line();
        parse_boot_command_line();
        setup_memory_end();
index d794802a22917ff6fd7affb568435da8c6255cdf..f7eed27b3220fcc7e3583f2889650468c7066422 100644 (file)
@@ -7,5 +7,12 @@
 extern char early_command_line[COMMAND_LINE_SIZE];
 extern struct ipl_parameter_block ipl_block;
 extern int ipl_block_valid;
+extern int ipl_secure_flag;
+
+extern unsigned long ipl_cert_list_addr;
+extern unsigned long ipl_cert_list_size;
+
+extern unsigned long early_ipl_comp_list_addr;
+extern unsigned long early_ipl_comp_list_size;
 
 #endif /* _ASM_S390_BOOT_DATA_H */
index f9718bc67cd4edc36df86bc4185e659cf6248170..0567de4005b4290f1911ac62418f9e6c6e8a6bd9 100644 (file)
@@ -122,6 +122,13 @@ static char *dump_type_str(enum dump_type type)
 
 int __bootdata_preserved(ipl_block_valid);
 struct ipl_parameter_block __bootdata_preserved(ipl_block);
+int __bootdata_preserved(ipl_secure_flag);
+
+unsigned long __bootdata_preserved(ipl_cert_list_addr);
+unsigned long __bootdata_preserved(ipl_cert_list_size);
+
+unsigned long __bootdata(early_ipl_comp_list_addr);
+unsigned long __bootdata(early_ipl_comp_list_size);
 
 static int reipl_capabilities = IPL_TYPE_UNKNOWN;
 
@@ -267,6 +274,15 @@ static ssize_t ipl_type_show(struct kobject *kobj, struct kobj_attribute *attr,
 
 static struct kobj_attribute sys_ipl_type_attr = __ATTR_RO(ipl_type);
 
+static ssize_t ipl_secure_show(struct kobject *kobj,
+                              struct kobj_attribute *attr, char *page)
+{
+       return sprintf(page, "%i\n", !!ipl_secure_flag);
+}
+
+static struct kobj_attribute sys_ipl_secure_attr =
+       __ATTR(secure, 0444, ipl_secure_show, NULL);
+
 static ssize_t ipl_vm_parm_show(struct kobject *kobj,
                                struct kobj_attribute *attr, char *page)
 {
@@ -362,6 +378,7 @@ static struct attribute *ipl_fcp_attrs[] = {
        &sys_ipl_fcp_bootprog_attr.attr,
        &sys_ipl_fcp_br_lba_attr.attr,
        &sys_ipl_ccw_loadparm_attr.attr,
+       &sys_ipl_secure_attr.attr,
        NULL,
 };
 
@@ -377,6 +394,7 @@ static struct attribute *ipl_ccw_attrs_vm[] = {
        &sys_ipl_device_attr.attr,
        &sys_ipl_ccw_loadparm_attr.attr,
        &sys_ipl_vm_parm_attr.attr,
+       &sys_ipl_secure_attr.attr,
        NULL,
 };
 
@@ -384,6 +402,7 @@ static struct attribute *ipl_ccw_attrs_lpar[] = {
        &sys_ipl_type_attr.attr,
        &sys_ipl_device_attr.attr,
        &sys_ipl_ccw_loadparm_attr.attr,
+       &sys_ipl_secure_attr.attr,
        NULL,
 };
 
index 12d136e567c48afc2ac4cc9ac50397c6834295a8..ffc87520aca96482f444afffef68712605d90700 100644 (file)
@@ -50,6 +50,7 @@
 #include <linux/compat.h>
 #include <linux/start_kernel.h>
 
+#include <asm/boot_data.h>
 #include <asm/ipl.h>
 #include <asm/facility.h>
 #include <asm/smp.h>
@@ -741,6 +742,15 @@ static void __init reserve_initrd(void)
 #endif
 }
 
+/*
+ * Reserve the memory area used to pass the certificate lists
+ */
+static void __init reserve_certificate_list(void)
+{
+       if (ipl_cert_list_addr)
+               memblock_reserve(ipl_cert_list_addr, ipl_cert_list_size);
+}
+
 static void __init reserve_mem_detect_info(void)
 {
        unsigned long start, size;
@@ -1035,6 +1045,38 @@ static void __init setup_control_program_code(void)
        asm volatile("diag %0,0,0x318\n" : : "d" (diag318_info.val));
 }
 
+/*
+ * Print the component list from the IPL report
+ */
+static void __init log_component_list(void)
+{
+       struct ipl_rb_component_entry *ptr, *end;
+       char *str;
+
+       if (!early_ipl_comp_list_addr)
+               return;
+       if (ipl_block.hdr.flags & IPL_PL_FLAG_IPLSR)
+               pr_info("Linux is running with Secure-IPL enabled\n");
+       else
+               pr_info("Linux is running with Secure-IPL disabled\n");
+       ptr = (void *) early_ipl_comp_list_addr;
+       end = (void *) ptr + early_ipl_comp_list_size;
+       pr_info("The IPL report contains the following components:\n");
+       while (ptr < end) {
+               if (ptr->flags & IPL_RB_COMPONENT_FLAG_SIGNED) {
+                       if (ptr->flags & IPL_RB_COMPONENT_FLAG_VERIFIED)
+                               str = "signed, verified";
+                       else
+                               str = "signed, verification failed";
+               } else {
+                       str = "not signed";
+               }
+               pr_info("%016llx - %016llx (%s)\n",
+                       ptr->addr, ptr->addr + ptr->len, str);
+               ptr++;
+       }
+}
+
 /*
  * Setup function called from init/main.c just after the banner
  * was printed.
@@ -1055,6 +1097,8 @@ void __init setup_arch(char **cmdline_p)
        else
                pr_info("Linux is running as a guest in 64-bit mode\n");
 
+       log_component_list();
+
        /* Have one command line that is parsed and saved in /proc/cmdline */
        /* boot_command_line has been already set up in early.c */
        *cmdline_p = boot_command_line;
@@ -1086,6 +1130,7 @@ void __init setup_arch(char **cmdline_p)
        reserve_oldmem();
        reserve_kernel();
        reserve_initrd();
+       reserve_certificate_list();
        reserve_mem_detect_info();
        memblock_allow_resize();
 
index 2ea4ec9991d516c8d9850e0dc091afd648143af9..3ba1168b1756c97df73bd966387600425c5d3a22 100644 (file)
@@ -55,13 +55,22 @@ config INTEGRITY_PLATFORM_KEYRING
         bool "Provide keyring for platform/firmware trusted keys"
         depends on INTEGRITY_ASYMMETRIC_KEYS
         depends on SYSTEM_BLACKLIST_KEYRING
-        depends on EFI
         help
          Provide a separate, distinct keyring for platform trusted keys, which
          the kernel automatically populates during initialization from values
          provided by the platform for verifying the kexec'ed kerned image
          and, possibly, the initramfs signature.
 
+config LOAD_UEFI_KEYS
+       depends on INTEGRITY_PLATFORM_KEYRING
+       depends on EFI
+       def_bool y
+
+config LOAD_IPL_KEYS
+       depends on INTEGRITY_PLATFORM_KEYRING
+       depends on S390
+       def_bool y
+
 config INTEGRITY_AUDIT
        bool "Enables integrity auditing support "
        depends on AUDIT
index 86df9aba8c0fe88ac1f1ef462a1a2495151b0951..19faace6964416f4d87062c4b0e6d5e1c3cc4e1b 100644 (file)
@@ -9,10 +9,10 @@ integrity-y := iint.o
 integrity-$(CONFIG_INTEGRITY_AUDIT) += integrity_audit.o
 integrity-$(CONFIG_INTEGRITY_SIGNATURE) += digsig.o
 integrity-$(CONFIG_INTEGRITY_ASYMMETRIC_KEYS) += digsig_asymmetric.o
-integrity-$(CONFIG_INTEGRITY_PLATFORM_KEYRING) += platform_certs/platform_keyring.o \
-                                                 platform_certs/efi_parser.o \
-                                                 platform_certs/load_uefi.o
-obj-$(CONFIG_LOAD_UEFI_KEYS) += platform_certs/load_uefi.o
+integrity-$(CONFIG_INTEGRITY_PLATFORM_KEYRING) += platform_certs/platform_keyring.o
+integrity-$(CONFIG_LOAD_UEFI_KEYS) += platform_certs/efi_parser.o \
+                                       platform_certs/load_uefi.o
+integrity-$(CONFIG_LOAD_IPL_KEYS) += platform_certs/load_ipl_s390.o
 $(obj)/load_uefi.o: KBUILD_CFLAGS += -fshort-wchar
 
 subdir-$(CONFIG_IMA)                   += ima
diff --git a/security/integrity/platform_certs/load_ipl_s390.c b/security/integrity/platform_certs/load_ipl_s390.c
new file mode 100644 (file)
index 0000000..e769dcb
--- /dev/null
@@ -0,0 +1,36 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/cred.h>
+#include <linux/err.h>
+#include <linux/efi.h>
+#include <linux/slab.h>
+#include <keys/asymmetric-type.h>
+#include <keys/system_keyring.h>
+#include <asm/boot_data.h>
+#include "../integrity.h"
+
+/*
+ * Load the certs contained in the IPL report created by the machine loader
+ * into the platform trusted keyring.
+ */
+static int __init load_ipl_certs(void)
+{
+       void *ptr, *end;
+       unsigned int len;
+
+       if (!ipl_cert_list_addr)
+               return 0;
+       /* Copy the certificates to the system keyring */
+       ptr = (void *) ipl_cert_list_addr;
+       end = ptr + ipl_cert_list_size;
+       while ((void *) ptr < end) {
+               len = *(unsigned int *) ptr;
+               ptr += sizeof(unsigned int);
+               add_to_platform_keyring("IPL:db", ptr, len);
+               ptr += len;
+       }
+       return 0;
+}
+late_initcall(load_ipl_certs);