From 195579cf0bcff320ed9ebe2cab6b59c9cc817f6e Mon Sep 17 00:00:00 2001 From: Marc Hartmayer Date: Tue, 26 Nov 2024 16:09:15 +0000 Subject: [PATCH] Remove genprotimg-C and switch to genprotimg-Rust implementation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Removes the C implementation of genprotimg and use the Rust implementation instead. Adapt the README.md accordingly. Reviewed-by: Steffen Eiden Signed-off-by: Marc Hartmayer Signed-off-by: Jan Höppner --- Makefile | 4 +- README.md | 23 +- genprotimg/.gitignore | 5 - genprotimg/Makefile | 25 - genprotimg/README.md | 85 - genprotimg/boot/.gitignore | 4 - genprotimg/boot/Makefile | 111 -- genprotimg/boot/common_memory_layout.h | 25 - genprotimg/boot/head.S | 36 - genprotimg/boot/stage3a.c | 62 - genprotimg/boot/stage3a.h | 34 - genprotimg/boot/stage3a.lds.S | 98 -- genprotimg/boot/stage3a_init.S | 33 - genprotimg/boot/stage3b.c | 92 -- genprotimg/boot/stage3b.h | 42 - genprotimg/boot/stage3b.lds.S | 84 - genprotimg/boot/stage3b_reloc.S | 60 - genprotimg/boot/stage3b_reloc.lds.S | 20 - genprotimg/man/Makefile | 12 - genprotimg/man/genprotimg.1 | 159 -- genprotimg/samples/check_hostkeydoc | 303 ---- genprotimg/src/Makefile | 104 -- genprotimg/src/common.h | 41 - genprotimg/src/genprotimg.c | 200 --- genprotimg/src/include/pv_crypto_def.h | 41 - genprotimg/src/include/pv_hdr_def.h | 95 -- genprotimg/src/pv/pv_args.c | 556 ------- genprotimg/src/pv/pv_args.h | 75 - genprotimg/src/pv/pv_comp.c | 432 ----- genprotimg/src/pv/pv_comp.h | 78 - genprotimg/src/pv/pv_comps.c | 252 --- genprotimg/src/pv/pv_comps.h | 42 - genprotimg/src/pv/pv_error.c | 37 - genprotimg/src/pv/pv_error.h | 89 - genprotimg/src/pv/pv_hdr.c | 293 ---- genprotimg/src/pv/pv_hdr.h | 35 - genprotimg/src/pv/pv_image.c | 1030 ------------ genprotimg/src/pv/pv_image.h | 68 - genprotimg/src/pv/pv_ipib.c | 127 -- genprotimg/src/pv/pv_ipib.h | 27 - genprotimg/src/pv/pv_opt_item.c | 26 - genprotimg/src/pv/pv_opt_item.h | 20 - genprotimg/src/pv/pv_stage3.c | 164 -- genprotimg/src/pv/pv_stage3.h | 29 - genprotimg/src/utils/align.h | 24 - genprotimg/src/utils/buffer.c | 78 - genprotimg/src/utils/buffer.h | 35 - genprotimg/src/utils/crypto.c | 2102 ------------------------ genprotimg/src/utils/crypto.h | 174 -- genprotimg/src/utils/curl.c | 121 -- genprotimg/src/utils/curl.h | 25 - genprotimg/src/utils/file_utils.c | 234 --- genprotimg/src/utils/file_utils.h | 34 - genprotimg/src/utils/openssl_compat.h | 33 - 54 files changed, 9 insertions(+), 8029 deletions(-) delete mode 100644 genprotimg/.gitignore delete mode 100644 genprotimg/Makefile delete mode 100644 genprotimg/README.md delete mode 100644 genprotimg/boot/.gitignore delete mode 100644 genprotimg/boot/Makefile delete mode 100644 genprotimg/boot/common_memory_layout.h delete mode 100644 genprotimg/boot/head.S delete mode 100644 genprotimg/boot/stage3a.c delete mode 100644 genprotimg/boot/stage3a.h delete mode 100644 genprotimg/boot/stage3a.lds.S delete mode 100644 genprotimg/boot/stage3a_init.S delete mode 100644 genprotimg/boot/stage3b.c delete mode 100644 genprotimg/boot/stage3b.h delete mode 100644 genprotimg/boot/stage3b.lds.S delete mode 100644 genprotimg/boot/stage3b_reloc.S delete mode 100644 genprotimg/boot/stage3b_reloc.lds.S delete mode 100644 genprotimg/man/Makefile delete mode 100644 genprotimg/man/genprotimg.1 delete mode 100755 genprotimg/samples/check_hostkeydoc delete mode 100644 genprotimg/src/Makefile delete mode 100644 genprotimg/src/common.h delete mode 100644 genprotimg/src/genprotimg.c delete mode 100644 genprotimg/src/include/pv_crypto_def.h delete mode 100644 genprotimg/src/include/pv_hdr_def.h delete mode 100644 genprotimg/src/pv/pv_args.c delete mode 100644 genprotimg/src/pv/pv_args.h delete mode 100644 genprotimg/src/pv/pv_comp.c delete mode 100644 genprotimg/src/pv/pv_comp.h delete mode 100644 genprotimg/src/pv/pv_comps.c delete mode 100644 genprotimg/src/pv/pv_comps.h delete mode 100644 genprotimg/src/pv/pv_error.c delete mode 100644 genprotimg/src/pv/pv_error.h delete mode 100644 genprotimg/src/pv/pv_hdr.c delete mode 100644 genprotimg/src/pv/pv_hdr.h delete mode 100644 genprotimg/src/pv/pv_image.c delete mode 100644 genprotimg/src/pv/pv_image.h delete mode 100644 genprotimg/src/pv/pv_ipib.c delete mode 100644 genprotimg/src/pv/pv_ipib.h delete mode 100644 genprotimg/src/pv/pv_opt_item.c delete mode 100644 genprotimg/src/pv/pv_opt_item.h delete mode 100644 genprotimg/src/pv/pv_stage3.c delete mode 100644 genprotimg/src/pv/pv_stage3.h delete mode 100644 genprotimg/src/utils/align.h delete mode 100644 genprotimg/src/utils/buffer.c delete mode 100644 genprotimg/src/utils/buffer.h delete mode 100644 genprotimg/src/utils/crypto.c delete mode 100644 genprotimg/src/utils/crypto.h delete mode 100644 genprotimg/src/utils/curl.c delete mode 100644 genprotimg/src/utils/curl.h delete mode 100644 genprotimg/src/utils/file_utils.c delete mode 100644 genprotimg/src/utils/file_utils.h delete mode 100644 genprotimg/src/utils/openssl_compat.h diff --git a/Makefile b/Makefile index f182a28b..caf7d6d7 100644 --- a/Makefile +++ b/Makefile @@ -15,12 +15,12 @@ TOOL_DIRS = zipl zdump fdasd dasdfmt dasdview tunedasd \ vmcp man mon_tools dasdinfo vmur cpuplugd ipl_tools \ ziomon iucvterm hyptop cmsfs-fuse qethqoat zfcpdump zdsfs cpumf \ systemd hmcdrvfs cpacfstats zdev dump2tar zkey netboot etc zpcictl \ - genprotimg lsstp hsci hsavmcore chreipl-fcp-mpath ap_tools rust + lsstp hsci hsavmcore chreipl-fcp-mpath ap_tools rust else BASELIB_DIRS = LIB_DIRS = libpv -TOOL_DIRS = genprotimg rust +TOOL_DIRS = rust endif SUB_DIRS = $(BASELIB_DIRS) $(LIB_DIRS) $(TOOL_DIRS) diff --git a/README.md b/README.md index 40d09d62..253fbf08 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,8 @@ Package contents Display unique DASD ID, either UID or volser. * genprotimg: - Create a protected virtualization image. + Create an IBM Secure Execution (protected virtualization) image. The + genprotimg command is a symbolic link to the `pvimg create` command. * udev rules: - 59-dasd.rules: rules for unique DASD device nodes created in /dev/disk/. @@ -314,17 +315,15 @@ build options: | ncurses | `HAVE_NCURSES` | hyptop | | net-snmp | `HAVE_SNMP` | osasnmpd | | glibc-static | `HAVE_LIBC_STATIC` | zfcpdump | -| openssl | `HAVE_OPENSSL` | genprotimg, zkey, libekmfweb, | -| | | libkmipclient, zgetdump, | -| | | rust/pvattest, rust/pvsecret, | -| | | rust/pvimg | +| openssl | `HAVE_OPENSSL` | zkey, libekmfweb, libkmipclient, | +| | | zgetdump, rust/pvattest, rust/pvimg, | +| | | zgetdump/pvsecret | | cryptsetup | `HAVE_CRYPTSETUP2` | zkey-cryptsetup | | json-c | `HAVE_JSONC` | zkey-cryptsetup, libekmfweb, | | | | libkmipclient | -| glib2 | `HAVE_GLIB2` | genprotimg, zgetdump | -| libcurl | `HAVE_LIBCURL` | genprotimg, libekmfweb, libkmipclient, | +| glib2 | `HAVE_GLIB2` | zgetdump | +| libcurl | `HAVE_LIBCURL` | libekmfweb, libkmipclient, rust/pvimg, | | | | rust/pvattest, rust/pvsecret, | -| | | rust/pvimg | | libxml2 | `HAVE_LIBXML2` | libkmipclient | | systemd | `HAVE_SYSTEMD` | hsavmcore | | libudev | `HAVE_LIBUDEV` | cpacfstatsd | @@ -359,14 +358,6 @@ the different tools are provided: * dbginfo.sh: The tar package is required to archive collected data. -* genprotimg: - For building genprotimg you need OpenSSL version 1.1.0 or newer - installed (openssl-devel.rpm). Also required is glib2 - (glib2-devel.rpm). Tip: you may skip the genprotimg build by adding - `HAVE_OPENSSL=0` or `HAVE_GLIB2=0`. - - The runtime requirements are: openssl-libs (>= 1.1.0) and glib2. - * rust/pvimg: For building pvimg you need OpenSSL version 1.1.1 or newer installed (openssl-devel.rpm). Also required is cargo and libcurl. diff --git a/genprotimg/.gitignore b/genprotimg/.gitignore deleted file mode 100644 index 08318df8..00000000 --- a/genprotimg/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -tags -compile_commands.json -src/.check-dep-genprotimg -src/.detect-openssl.dep.c -src/genprotimg diff --git a/genprotimg/Makefile b/genprotimg/Makefile deleted file mode 100644 index c7c6d608..00000000 --- a/genprotimg/Makefile +++ /dev/null @@ -1,25 +0,0 @@ -# Common definitions -include ../common.mak - -.DEFAULT_GOAL := all - -PKGDATADIR := $(TOOLS_DATADIR)/genprotimg -TESTS := -SUBDIRS := boot src man -RECURSIVE_TARGETS := all-recursive install-recursive clean-recursive - -all: all-recursive - -install: install-recursive - $(INSTALL) -d -m 755 $(DESTDIR)$(PKGDATADIR) - $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 samples/check_hostkeydoc $(DESTDIR)$(PKGDATADIR) - -clean: clean-recursive - -$(RECURSIVE_TARGETS): - @target=`echo $@ |sed s/-recursive//`; \ - for d in $(SUBDIRS); do \ - $(MAKE) -C $$d $$target || exit 1; \ - done - -.PHONY: all install clean $(RECURSIVE_TARGETS) diff --git a/genprotimg/README.md b/genprotimg/README.md deleted file mode 100644 index e37ac338..00000000 --- a/genprotimg/README.md +++ /dev/null @@ -1,85 +0,0 @@ -# genprotimg - -`genprotimg` takes a kernel, key files, optionally an initrd image, -optionally a file containing the kernel command line parameters, and -generates a single, bootable image file. The generated image file -consists of a concatenation of a plain text boot loader, the encrypted -components for kernel, initrd, kernel command line, and the -integrity-protected PV header, containing the metadata necessary for -running the guest in protected mode. See [Memory Layout](#memory-layout) -for details about the internal structure of the created image. - -It is possible to use the generated image as a kernel for zipl or for -a direct kernel boot using QEMU. - -## Getting started - -If all dependencies are met a simple `make` call in the source tree -should be enough for building `genprotimg`. - -## Details - -The main idea of `genprotimg` is: - -1. read in all keys, IVs, and other information needed for the - encryption of the components and the generation of the PV header -2. add stub stage3a (so we can calculate the memory addresses) -3. add components: prepare the components (alignment and encryption) - and add them to the memory layout -4. build and add stage3b: generate the stage3b and add it to the memory layout -5. generate the PV header: generate the hashes (pld, ald, and tld) of - the components and create the PV header and IPIB -6. parameterize the stub stage3a: uses the IPIB and PV header -7. write the final image to the specified output path - -### Boot Loader - -The boot loader consists of two parts: - -1. stage3a boot loader (cleartext), this loader is responsible for the - transition into the protected mode by doing diag308 subcode 8 and - 10 calls. -2. stage3b boot loader (encrypted), this loader is very similar to the - normal zipl stage3 boot loader. It will be loaded by the Ultravisor - after the successful transition into protected mode. Like the zipl - stage3 boot loader it moves the kernel and patches in the values - for initrd and parmline. - -The loaders have the following constraints: - -1. It must be possible to place stage3a and stage3b at a location - greater than 0x10000 because the zipl stage3 loader zeroes out - everything at addresses lower than 0x10000 of the image. -2. As the stage3 loader of zipl assumes that the passed kernel image - looks like a normal kernel image, the zipl stage3 loader modifies the - content at the memory area 0x10400 - 0x10800, therefore we leave this - area unused in our stage3a loader. -3. The default entry address used by the zipl stage3 loader is 0x10000 - so we add a simple branch to 0x11000 at 0x10000 so the zipl stage3 - loader can modify the area 0x10400 - 0x10800 without affecting the - stage3a loader. - -#### Detail about stage3b - -The stage3b.bin is linked at address 0x9000, therefore it will not -work at another address. The relocation support for the stage3b -loader, so that it can be placed at addresses != 0x9000, is added in -the loader with the name stage3b_reloc.bin. By default, if we're -talking about stage3b we refer to stage3b_reloc.bin. - -### Memory Layout - -The memory layout of the bootable file looks like: - -| Start | End | Use | -|------------------------|------------|-----------------------------------------------------------------------| -| 0 | 0x7 | Short PSW, starting instruction at 0x11000 | -| 0x10000 | 0x10012 | Branch to 0x11000 | -| 0x10013 | 0x10fff | Left intentionally unused | -| 0x11000 | 0x12fff | Stage3a | -| 0x13000 | 0x13fff | IPIB used as argument for the diag308 call | -| 0x14000 | 0x1[45]fff | UV header used for the diag308 call (size can be either 1 or 2 pages) | -| NEXT_PAGE_ALIGNED_ADDR | | Encrypted kernel | -| NEXT_PAGE_ALIGNED_ADDR | | Encrypted kernel parameters | -| NEXT_PAGE_ALIGNED_ADDR | | Encrypted initrd | -| NEXT_PAGE_ALIGNED_ADDR | | Encrypted stage3b_reloc | diff --git a/genprotimg/boot/.gitignore b/genprotimg/boot/.gitignore deleted file mode 100644 index 837684ed..00000000 --- a/genprotimg/boot/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -*.elf -*.lds -*.bin -*.d diff --git a/genprotimg/boot/Makefile b/genprotimg/boot/Makefile deleted file mode 100644 index 11e4def3..00000000 --- a/genprotimg/boot/Makefile +++ /dev/null @@ -1,111 +0,0 @@ -# Common definitions -include ../../common.mak - -FILES := stage3a.bin stage3b.bin stage3b_reloc.bin -DEBUG_FILES := $(addsuffix .debug,$(FILES)) - -ifeq ($(HOST_ARCH),s390x) -ZIPL_DIR := $(rootdir)/zipl -ZIPL_BOOT_DIR := $(ZIPL_DIR)/boot -PKGDATADIR := $(TOOLS_DATADIR)/genprotimg - -INCLUDE_PATHS := $(ZIPL_BOOT_DIR) $(ZIPL_DIR)/include $(rootdir)/include -INCLUDE_PARMS := $(addprefix -I,$(INCLUDE_PATHS)) - -ALL_CFLAGS := $(NO_PIE_CFLAGS) -Os -g \ - $(INCLUDE_PARMS) \ - -DENABLE_SCLP_ASCII=1 \ - -DS390_TOOLS_RELEASE=$(S390_TOOLS_RELEASE) \ - -fno-builtin -ffreestanding -fno-asynchronous-unwind-tables \ - -fno-delete-null-pointer-checks -fno-stack-protector \ - -fexec-charset=IBM1047 -m64 -mpacked-stack \ - -mstack-size=4096 -mstack-guard=128 -msoft-float \ - -Wall -Wformat-security -Wextra \ - -Wno-array-bounds - -ZIPL_SRCS_C := libc.c ebcdic.c ebcdic_conv.c sclp.c -ZIPL_SRCS_ASM := entry.S - -ZIPL_OBJS_C := $(ZIPL_SRCS_C:%.c=%.o) -ZIPL_OBJS_ASM := $(ZIPL_SRCS_ASM:%.S=%.o) -ZIPL_OBJS := $(ZIPL_OBJS_C) $(ZIPL_OBJS_ASM) - -# Prevent make from using some default rules... -%: %.S - -%.o: %.S Makefile - $(CC) $(ALL_CFLAGS) -c -o $@ $< - -%.o: %.c Makefile - $(CC) $(ALL_CFLAGS) -c -o $@ $< - - -# Dependencies for the .lds generation -sources_lds_S = $(wildcard *.lds.S) -dependencies_lds_S = $(sources_lds_S:%.lds.S=.%.lds.d) -# Include all ".lds.d" dependency files for all make targets except for "clean" -ifneq ($(MAKECMDGOALS),clean) --include $(dependencies_lds_S) -endif - -%.lds: %.lds.S Makefile - $(CPP) -Wp,-MD,.$@.d,-MT,$@ $(INCLUDE_PARMS) -P -C -o $@ $< - -# Special rules for zipl object files -$(ZIPL_OBJS_C): %.o : $(ZIPL_BOOT_DIR)/%.c - $(CC) $(ALL_CFLAGS) -c -o $@ $< - -$(ZIPL_OBJS_ASM): %.o : $(ZIPL_BOOT_DIR)/%.S - $(CC) $(ALL_CFLAGS) -c -o $@ $< - -dependencies_zipl_c := $(ZIPL_SRCS_C:%.c=.%.o.d) - -$(dependencies_zipl_c): .%.o.d : $(ZIPL_BOOT_DIR)/%.c - $(CC_SILENT) -MM $(ALL_CPPFLAGS) $(ALL_CFLAGS) $< > $@ - -ifneq ($(MAKECMDGOALS),clean) --include $(dependencies_zipl_c) -endif - -stage3b_reloc.o: stage3b.bin - -stage3a.elf: head.o stage3a_init.o $(ZIPL_OBJS) -stage3b.elf: head.o $(ZIPL_OBJS) - -.SECONDARY: $(FILES:.bin=.lds) -%.elf: %.lds %.o - $(LINK) $(NO_PIE_LDFLAGS) $(NO_WARN_RWX_SEGMENTS_LDFLAGS) -Wl,-T,$< -Wl,--build-id=none -m64 -static -nostdlib $(filter %.o, $^) -o $@ - @chmod a-x $@ - -%.bin.debug: %.elf - $(OBJCOPY) --only-keep-debug $< $@ - @chmod a-x $@ - -%.bin: %.elf - $(OBJCOPY) -O binary $< $@ - @chmod a-x $@ - -install: stage3a.bin stage3b_reloc.bin - $(INSTALL) -d -m 755 $(DESTDIR)$(PKGDATADIR) - $(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 stage3a.bin $(DESTDIR)$(PKGDATADIR) - $(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 stage3b_reloc.bin $(DESTDIR)$(PKGDATADIR) - -else -# Don't generate the dependency files (see `common.mak` for the -# `-include $(dependencies_c)` statement). -.PHONY: $(dependencies_c) - -$(FILES) $(DEBUG_FILES): - echo " SKIP $@ due to HOST_ARCH != s390x" - -install: - echo " SKIP Bootloader installation due to HOST_ARCH != s390x" -endif - -.DEFAULT_GOAL := all -all: $(FILES) $(DEBUG_FILES) - -clean: - rm -f -- *.o *.elf *.bin *.map .*.d *.lds *.debug - -.PHONY: all clean diff --git a/genprotimg/boot/common_memory_layout.h b/genprotimg/boot/common_memory_layout.h deleted file mode 100644 index 4750191c..00000000 --- a/genprotimg/boot/common_memory_layout.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Common memory layout for stage3a and stage3b bootloader. - * - * Copyright IBM Corp. 2020 - * - * s390-tools is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See LICENSE for details. - */ - -#ifndef COMMON_MEMORY_LAYOUT_H -#define COMMON_MEMORY_LAYOUT_H - -#include "boot/loaders_layout.h" - -#define STACK_ADDRESS STAGE3_STACK_ADDRESS -#define STACK_SIZE STAGE3_STACK_SIZE - -#define HEAP_ADDRESS STAGE3_HEAP_ADDRESS -#define HEAP_SIZE STAGE3_HEAP_SIZE - - -#ifndef __ASSEMBLER__ - -#endif /* __ASSEMBLER__ */ -#endif /* COMMON_MEMORY_LAYOUT_H */ diff --git a/genprotimg/boot/head.S b/genprotimg/boot/head.S deleted file mode 100644 index 2a459487..00000000 --- a/genprotimg/boot/head.S +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Entry code for stage 3a and stage 3b boot loader - * - * Copyright IBM Corp. 2020 - * - * s390-tools is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See LICENSE for details. - */ - - -#include "common_memory_layout.h" - -#include "boot/s390.h" -#include "boot/sigp.h" - -.section .text.start -.globl _start -_start: - /* Might be called after a diag308 so better set - * architecture and addressing mode - */ - lhi %r1, 1 - sigp %r1, %r0, SIGP_SET_ARCHITECTURE - sam64 - - /* Initialize stack */ - basr %r13, 0 -.Lbase: llgf %r15, .Lstack - .Lbase(%r13) - brasl %r14, initialize -.Lstack: .long STACK_ADDRESS + STACK_SIZE - STACK_FRAME_OVERHEAD -.previous - -/* The code doesn't require an executable stack */ -#if defined(__linux__) && defined(__ELF__) -.section .note.GNU-stack,"",%progbits -#endif diff --git a/genprotimg/boot/stage3a.c b/genprotimg/boot/stage3a.c deleted file mode 100644 index a4388b6f..00000000 --- a/genprotimg/boot/stage3a.c +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Main program for stage3a bootloader - * - * Copyright IBM Corp. 2020 - * - * s390-tools is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See LICENSE for details. - */ - -#include "libc.h" -#include "stage3a.h" - -#include "lib/zt_common.h" -#include "boot/error.h" -#include "boot/s390.h" -#include "boot/ipl.h" -#include "sclp.h" - - -static volatile struct stage3a_args __section(".loader_parms") loader_parms; - -void __noreturn start(void) -{ - int rc; - volatile struct stage3a_args *args = &loader_parms; - /* calculate the IPIB memory address */ - struct ipl_parameter_block *ipib = (void *)((uint64_t)args + args->ipib_offs); - - /* Calculate the PV header memory address and set it and its - * size in the IPIB. This allows the PV header to be position - * independent. - */ - ipib->pv.pv_hdr_addr = (uint64_t)args + args->hdr_offs; - ipib->pv.pv_hdr_size = args->hdr_size; - - /* set up ASCII and line-mode */ - sclp_setup(SCLP_LINE_ASCII_INIT); - - /* test if Secure Execution Unpack facility is available */ - stfle(S390_lowcore.stfle_fac_list, - ARRAY_SIZE(S390_lowcore.stfle_fac_list)); - rc = test_facility(UNPACK_FACILITY); - if (rc == 0) - panic(ENOPV, "Secure unpack facility is not available\n"); - - rc = diag308(DIAG308_SET_PV, ipib); - if (rc != DIAG308_RC_OK) - panic(EPV, "Protected boot setup has failed: 0x%x\n", rc); - - rc = diag308(DIAG308_UNPACK_PV, 0x0); - if (rc != DIAG308_RC_OK) { - sclp_setup(SCLP_LINE_ASCII_INIT); - panic(EPV, "Protected boot has failed: 0x%x\n", rc); - } - - while (1) - ; -} - -void panic_notify(unsigned long UNUSED(rc)) -{ -} diff --git a/genprotimg/boot/stage3a.h b/genprotimg/boot/stage3a.h deleted file mode 100644 index f362e866..00000000 --- a/genprotimg/boot/stage3a.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Main program for stage3a bootloader. - * - * Copyright IBM Corp. 2020 - * - * s390-tools is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See LICENSE for details. - */ - -#ifndef STAGE3A_H -#define STAGE3A_H - -#include "lib/zt_common.h" -#include "boot/loaders_layout.h" - -#define STAGE3A_INIT_ENTRY IMAGE_ENTRY -#define STAGE3A_ENTRY (STAGE3A_INIT_ENTRY + _AC(0x1000, UL)) -#define STAGE3A_LOAD_ADDRESS IMAGE_LOAD_ADDRESS - - -#ifndef __ASSEMBLER__ - -#include - -/* Must not have any padding */ -struct stage3a_args { - uint64_t hdr_offs; - uint64_t hdr_size; - uint64_t ipib_offs; -}; -STATIC_ASSERT(sizeof(struct stage3a_args) == 3 * 8) - -#endif /* __ASSEMBLER__ */ -#endif /* STAGE3A_H */ diff --git a/genprotimg/boot/stage3a.lds.S b/genprotimg/boot/stage3a.lds.S deleted file mode 100644 index cc50948d..00000000 --- a/genprotimg/boot/stage3a.lds.S +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Memory layout for stage 3a - * ========================== - * - * General memory layout - * --------------------- - * - * 0x00000 - 0x01fff Lowcore - * 0x02000 - 0x05fff Memory allocation (heap) - * 0x0f000 - 0x0ffff Stack - * 0x10000 - 0x10012 Jump to the "actual" stage3a code - * 0x11000 - 0x12fff Stage3a code + arguments (offsets and lengths to the - * actual data: IPIB and UV header) - */ - -#include "stage3a.h" -#include "common_memory_layout.h" - -OUTPUT_FORMAT("elf64-s390", "elf64-s390", "elf64-s390") -OUTPUT_ARCH(s390:64-bit) - -ENTRY(_init) - -SECTIONS -{ - . = HEAP_ADDRESS; - __heap_start = .; - .heap : { - . = . + HEAP_SIZE; - ASSERT(__heap_stop - __heap_start == HEAP_SIZE, - "Heap section doesn't conform to the described memory layout"); - } - __heap_stop = .; - - . = STACK_ADDRESS; - __stack_start = .; - .stack : { - . = . + STACK_SIZE; - ASSERT(__stack_end - __stack_start == STACK_SIZE, - "Stack section doesn't conform to the described memory layout"); - } - __stack_end = .; - - . = STAGE3A_INIT_ENTRY; - __text_init_start = .; - .text : { - *(.text.init) - __text_init_stop = ABSOLUTE(.); - /* Text size of text_init must be smaller than 'PARMAREA - IMAGE_ENTRY', - * otherwise the text data could be overwritten by the original zipl stage3 - * boot loader */ - ASSERT(__text_init_stop - __text_init_start < PARMAREA - IMAGE_ENTRY, - "Text size must be smaller than 'PARMAREA - IMAGE_ENTRY'"); - . = 0x1000; - ASSERT(ABSOLUTE(.) == STAGE3A_ENTRY, - "Text section doesn't conform to the described memory layout"); - *(.text.start) - *(.text .text.*) - } - - .ex_table ALIGN(16) : { - __ex_table_start = .; - *(.ex_table) - __ex_table_stop = .; - } - - .bss ALIGN(16) : { - __bss_start = .; - *(.bss) - __bss_stop = .; - } - - .rodata ALIGN(16) : { - *(.rodata) - *(.rodata*) - } - - .data ALIGN(16) : { - *(.data) - . = ALIGN(16); - /* The IPIB offset and the UV header offset and size will be - * saved in 'loader_parms' */ - __loader_parms_start = .; - KEEP(*(.loader_parms)); - __loader_parms_stop = .; - ASSERT(__loader_parms_stop - __loader_parms_start == 3 * 8, - "Data size must be equal to 'sizeof(struct stage3a_args)'"); - ASSERT(ABSOLUTE(.) < 0x13000, "Data section doesn't conform to the described memory layout"); - } - - /* Sections to be discarded */ - /DISCARD/ : { - *(.eh_frame) - *(.interp) - *(.note.GNU-stack) - *(.note.package) - } -} diff --git a/genprotimg/boot/stage3a_init.S b/genprotimg/boot/stage3a_init.S deleted file mode 100644 index de5ebcf1..00000000 --- a/genprotimg/boot/stage3a_init.S +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Entry code for stage 3a boot loader - * - * Copyright IBM Corp. 2020 - * - * s390-tools is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See LICENSE for details. - */ - -#include "stage3a.h" -#include "boot/sigp.h" - -.section .text.init -.globl _init -_init: - /* set architecture and switch to 64bit */ - lhi %r1, 1 - sigp %r1, %r0, SIGP_SET_ARCHITECTURE - sam64 - /* The original stage3 boot loader will try to store the - * kernel command line and the address and size of the - * ramdisk. Simply ignore this by starting at 0x11000. - */ - basr %r13, 0 -.Lbase: llgf %r1, .Lstage3a_entry - .Lbase(%r13) - br %r1 -.Lstage3a_entry: .long STAGE3A_ENTRY -.previous - -/* The code doesn't require an executable stack */ -#if defined(__linux__) && defined(__ELF__) -.section .note.GNU-stack,"",%progbits -#endif diff --git a/genprotimg/boot/stage3b.c b/genprotimg/boot/stage3b.c deleted file mode 100644 index 99bceff6..00000000 --- a/genprotimg/boot/stage3b.c +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Main program for stage3b bootloader - * - * Copyright IBM Corp. 2020 - * - * s390-tools is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See LICENSE for details. - */ - -#include "libc.h" -#include "stage3b.h" - -#include "lib/zt_common.h" -#include "boot/psw.h" -#include "boot/error.h" -#include "boot/s390.h" -#include "boot/linux_layout.h" -#include "boot/loaders_layout.h" -#include "sclp.h" - - -static volatile struct stage3b_args __section(".loader_parms") loader_parms; - -static inline void __noreturn load_psw(struct psw_t psw) -{ - asm volatile("lpswe %0" : : "Q"(psw) : "cc"); - - while (1) - ; -} - -static unsigned long get_kernel_cmdline_size(void) -{ - unsigned long size = *(volatile unsigned long *)MAX_COMMAND_LINE_SIZE; - - if (size != 0) - return size; - - return LEGACY_COMMAND_LINE_SIZE; -} - -void __noreturn start(void) -{ - volatile struct stage3b_args *args = &loader_parms; - volatile struct memblob *kernel = &args->kernel; - volatile struct memblob *cmdline = &args->cmdline; - volatile struct memblob *initrd = &args->initrd; - struct psw_t psw = args->psw; - - /* set up ASCII and line-mode */ - sclp_setup(SCLP_LINE_ASCII_INIT); - - if (kernel->size < IMAGE_LOAD_ADDRESS) - panic(EINTERNAL, "Invalid kernel\n"); - - /* move the kernel and cut the kernel header */ - memmove((void *)IMAGE_LOAD_ADDRESS, - (void *)(kernel->src + IMAGE_LOAD_ADDRESS), - kernel->size - IMAGE_LOAD_ADDRESS); - - if (cmdline->size > get_kernel_cmdline_size()) - panic(EINTERNAL, "Command line is too large\n"); - - if (cmdline->size > 0) { - /* make sure the cmdline is a null-terminated string */ - if (((char *)cmdline->src)[cmdline->size - 1] != '\0') - panic(EINTERNAL, "Command line needs to be null-terminated\n"); - - /* move the kernel cmdline */ - memmove((void *)COMMAND_LINE, (void *)cmdline->src, cmdline->size); - } - /* the initrd does not need to be moved */ - - if (initrd->size > 0) { - /* copy initrd start address and size into new kernel space */ - *(unsigned long long *)INITRD_START = initrd->src; - *(unsigned long long *)INITRD_SIZE = initrd->size; - } - - /* disable ASCII and line-mode */ - sclp_setup(SCLP_DISABLE); - - /* use lpswe instead of diag308 as a I/O subsystem reset is not - * needed as this was already done by the diag308 subcode 10 call - * in stage3a - */ - load_psw(psw); -} - -void panic_notify(unsigned long UNUSED(rc)) -{ -} diff --git a/genprotimg/boot/stage3b.h b/genprotimg/boot/stage3b.h deleted file mode 100644 index 90419ed5..00000000 --- a/genprotimg/boot/stage3b.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Main program for stage3b bootloader - * - * Copyright IBM Corp. 2020 - * - * s390-tools is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See LICENSE for details. - */ - -#ifndef STAGE3B_H -#define STAGE3B_H - -#include "lib/zt_common.h" -#include "boot/loaders_layout.h" - -#define STAGE3B_ENTRY STAGE3_ENTRY -#define STAGE3B_LOAD_ADDRESS STAGE3B_ENTRY - - -#ifndef __ASSEMBLER__ - -#include - -#include "boot/psw.h" - -/* Must not have any padding included */ -struct memblob { - uint64_t src; - uint64_t size; -}; -STATIC_ASSERT(sizeof(struct memblob) == 2 * 8) - -/* Must not have any padding included */ -struct stage3b_args { - struct memblob kernel; - struct memblob cmdline; - struct memblob initrd; - struct psw_t psw; -}; -STATIC_ASSERT(sizeof(struct stage3b_args) == 3 * sizeof(struct memblob) + 16) -#endif /* __ASSEMBLER__ */ -#endif /* STAGE3B_H */ diff --git a/genprotimg/boot/stage3b.lds.S b/genprotimg/boot/stage3b.lds.S deleted file mode 100644 index e0eb10a3..00000000 --- a/genprotimg/boot/stage3b.lds.S +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Memory layout for stage 3b - * ========================== - * - * General memory layout - * --------------------- - * - * 0x00000 - 0x01fff Lowcore - * 0x02000 - 0x05fff Memory allocation (heap) - * 0x0a000 - 0x0efff Stage3b code - * 0x0f000 - 0x0ffff Stack - */ - -#include "stage3b.h" -#include "common_memory_layout.h" - -OUTPUT_FORMAT("elf64-s390", "elf64-s390", "elf64-s390") -OUTPUT_ARCH(s390:64-bit) - -ENTRY(_start) - -SECTIONS -{ - . = HEAP_ADDRESS; - __heap_start = .; - .heap : { - . = . + HEAP_SIZE; - ASSERT(__heap_stop - __heap_start == HEAP_SIZE, - "Heap section doesn't conform to the described memory layout"); - } - __heap_stop = .; - - . = STAGE3B_ENTRY; - .text : { - *(.text.start) - *(.text .text.*) - } - - .ex_table ALIGN(16) : { - __ex_table_start = .; - *(.ex_table) - __ex_table_stop = .; - } - - .bss ALIGN(16) : { - __bss_start = .; - *(.bss) - __bss_stop = .; - } - - .rodata ALIGN(16) : { - *(.rodata) - *(.rodata*) - } - - .data ALIGN(16) : { - *(.data) - . = ALIGN(16); - __loader_parms_start = .; - KEEP(*(.loader_parms)); - __loader_parms_end = .; - ASSERT(__loader_parms_end - __loader_parms_start == 3 * 16 + 16, - "Data size must be equal to 'sizeof(struct stage3b_args)'"); - } - - . = STACK_ADDRESS; - __stack_start = .; - .stack : { - . = . + STACK_SIZE; - ASSERT(__stack_end - __stack_start == STACK_SIZE, - "Stack section doesn't conform to the described memory layout"); - } - __stack_end = .; - - ASSERT(. <= IMAGE_ENTRY, "stage3b size must be smaller than 0x10000 bytes") - - /* Sections to be discarded */ - /DISCARD/ : { - *(.eh_frame) - *(.interp) - *(.note.GNU-stack) - *(.note.package) - } -} diff --git a/genprotimg/boot/stage3b_reloc.S b/genprotimg/boot/stage3b_reloc.S deleted file mode 100644 index 1fe5d943..00000000 --- a/genprotimg/boot/stage3b_reloc.S +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Relocator code for stage 3b boot loader - * - * Copyright IBM Corp. 2020 - * - * s390-tools is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See LICENSE for details. - */ - -#include "stage3b.h" -#include "boot/sigp.h" - -.macro MEMCPY dst,src,len - lgr %r0, \dst - lgr %r1, \len - lgr %r2, \src - lgr %r3, \len - -20: mvcle %r0, %r2, 0 - jo 20b -.endm - -.org 0x0 -.section .text.start -.globl _start -_start: - /* Might be called after a diag308 so better set - * architecture and addressing mode - */ - lhi %r1, 1 - sigp %r1, %r0, SIGP_SET_ARCHITECTURE - sam64 - - /* Location of stage3b in memory */ - larl %r8, stage3b_start - - /* Destination for stage3b */ - basr %r13, 0 -.Lbase: llgf %r9, .Lstage3b_load_address - .Lbase(%r13) - - /* Size of stage3b */ - lghi %r11, stage3b_end - stage3b_start - - /* Copy the stage3b loader to address STAGE3B_LOAD_ADDRESS */ - MEMCPY %r9, %r8, %r11 - - /* Branch to STAGE3B_ENTRY */ - llgf %r9, .Lstage3b_entry - .Lbase(%r13) - br %r9 -.Lstage3b_load_address: .long STAGE3B_LOAD_ADDRESS -.Lstage3b_entry: .long STAGE3B_ENTRY -stage3b_start: - .incbin "stage3b.bin" -stage3b_end: -.previous - -/* The code doesn't require an executable stack */ -#if defined(__linux__) && defined(__ELF__) -.section .note.GNU-stack,"",%progbits -#endif diff --git a/genprotimg/boot/stage3b_reloc.lds.S b/genprotimg/boot/stage3b_reloc.lds.S deleted file mode 100644 index 52b7471a..00000000 --- a/genprotimg/boot/stage3b_reloc.lds.S +++ /dev/null @@ -1,20 +0,0 @@ -OUTPUT_FORMAT("elf64-s390", "elf64-s390", "elf64-s390") -OUTPUT_ARCH(s390:64-bit) - -ENTRY(_start) - -SECTIONS -{ - .text : { - *(.text.start) - *(.text .text.*) - } - - /* Sections to be discarded */ - /DISCARD/ : { - *(.eh_frame) - *(.interp) - *(.note.GNU-stack) - *(.note.package) - } -} diff --git a/genprotimg/man/Makefile b/genprotimg/man/Makefile deleted file mode 100644 index 0094d34c..00000000 --- a/genprotimg/man/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -# Common definitions -include ../../common.mak - -all: - -install: - $(INSTALL) -d -m 755 $(DESTDIR)$(MANDIR)/man1 - $(INSTALL) -m 644 -c genprotimg.1 $(DESTDIR)$(MANDIR)/man1 - -clean: - -.PHONY: all install clean diff --git a/genprotimg/man/genprotimg.1 b/genprotimg/man/genprotimg.1 deleted file mode 100644 index e1c5fbf9..00000000 --- a/genprotimg/man/genprotimg.1 +++ /dev/null @@ -1,159 +0,0 @@ -.\" Copyright 2020 IBM Corp. -.\" s390-tools is free software; you can redistribute it and/or modify -.\" it under the terms of the MIT license. See LICENSE for details. -.\" -.TH GENPROTIMG 1 "May 2022" "s390-tools" -.SH NAME -genprotimg \- Create a protected virtualization image - -.SH SYNOPSIS -.SY -.B genprotimg -\fB\-k\fR \fIHOST_KEY_DOCUMENT\fR... -\fB\-C\fR \fICERTIFICATE\fR... -\fB\-i\fR \fIVMLINUZ\fR -[\fB\-r\fR \fIRAMDISK\fR] -[\fB\-p\fR \fIPARMFILE\fR] -\fB\-o\fR \fIOUTFILE\fR -[\fIOPTION\fR]... -.YS - -.SH DESCRIPTION -.PP -Use \fBgenprotimg\fR to generate a single bootable image file with -encrypted and integrity-protected parts. The command requires a kernel -image, a host-key document, certificates for the host-key document -verification, and an output file name. Optionally, specify an initial -RAM filesystem, and a file containing the kernel parameters. If the -command should be run offline, use the \fB\-\-offline\fR option and -specify the certificate revocation lists (CRLs) by using the -\fB\-\-crl\fR option. Should special circumstances require it, you can -optionally specify your own keys for the encryption by using the -experimental options. For all certificates, CRLs, and host-key -documents, both the PEM and DER input formats are supported. In the -resulting image file, a plain text boot loader, the encrypted -components for kernel, initial RAM disk, kernel parameters, and the -encrypted and integrity-protected header are concatenated. The header -contains metadata necessary for running the guest in protected mode. -.PP -Use this image file as a kernel image for zipl or for a direct kernel -boot using QEMU. - -.SH OPTIONS -.TP -\fB\-h\fR, \fB\-\-help\fR -Prints usage information, then exits. -.TP -\fB\-\-help-experimental\fR -Prints experimental usage information, then exits. -.TP -\fB\-\-help-all\fR -Prints all usage information, then exits. -.TP -\fB\-V\fR, \fB\-\-verbose\fR -Provides more detailed output. -.TP -\fB\-k\fR, \fB\-\-host-key-document\fR=\fI\,HOST_KEY_DOCUMENT\/\fR -Specifies a host-key document. At least one is required. Specify this -option multiple times to enable the image to run on more than one -host. -.TP -\fB\-C\fR, \fB\-\-cert\fR=\fI\,FILE\/\fR -Specifies the certificate that is used to establish a chain of trust -for the verification of the host-key documents. Specify this option -twice to specify the IBM Z signing key and the intermediate CA -certificate (signed by the root CA). -Required. Ignored if \fB--no-verify\fP is specified. -.TP -\fB\-o\fR, \fB\-\-output\fR=\fI\,OUTPUT_FILE\/\fR -Specifies the output file. Required. -.TP -\fB\-i\fR, \fB\-\-image\fR=\fI\,VMLINUZ\/\fR -Specifies the Linux kernel image file. Required. -.TP -\fB\-r\fR, \fB\-\-ramdisk\fR=\fI\,RAMDISK\/\fR -Specifies the RAM disk image. Optional. -.TP -\fB\-p\fR, \fB\-\-parmfile\fR=\fI\,PARMFILE\/\fR -Specifies the kernel command line stored in \fI\,PARMFILE\/\fR. Optional. -.TP -\fB\-\-crl\fR=\fI\,FILE\/\fR -Specifies the revocation list that is used to check whether a -certificate of the chain of trust is revoked. Specify this option -multiple times to use multiple CRLs. Optional. -.TP -\fB\-\-offline\fR -Specifies offline mode, in which no attempt is made to download -CRLs. Optional. -.TP -\fB\-\-root\-ca\fR=\fI\,FILE\/\fR -Specifies the root CA certificate for the verification. If omitted, -the system wide root CAs installed on the system is used. Use -this only if you trust the specified certificate. Optional. -.TP -\fB\-\-no-verify\fR -Does not require the host-key documents to be valid. -Do not use for a production image unless you verified -the host-key document before. Optional. -.TP -\fB\-\-comm\-key\fR=\fI\,FILE\/\fR -Specifies the customer communication key (CCK). This key is used for the -PV guest dump encryption and to derive the CCK-derived extension secret -used for add-secret requests. Use a secure, random, plaintext AES-256 -GCM key. Optional. -.TP -\fB\-\-enable\-dump\fR -Enable PV guest dumps. Requires the \fB\-\-comm\-key\fR option. Optional. -.TP -\fB\-\-disable\-dump\fR -Disable PV guest dumps. This is the default. -.TP -\fB\-\-enable\-cck\-extension\-secret\fR -Add-secret requests must provide an extension secret that matches the -CCK-derived extension secret. Requires the \fB\-\-comm\-key\fR option. -Optional. -.TP -\fB\-\-disable\-cck\-extension\-secret\fR -Add-secret requests don't have to provide an extension secret. This is -the default. -.TP -\fB\-\-enable\-pckmo\fR -Enable the support for the DEA, TDEA, AES, and ECC PCKMO key encryption -functions. This is the default. -.TP -\fB\-\-disable\-pckmo\fR -Disable the support for the DEA, TDEA, AES, and ECC PCKMO key encryption -functions. Optional. -.TP -\fB\-v\fR, \fB\-\-version\fR -Prints version information, then exits. - -.SH EXAMPLES - -These are examples to generate a protected virtualization image in -\fI\,/boot/vmlinuz.pv\/\fR, using the kernel file \fI\,vmlinuz\/\fR, the -initrd in \fI\,initramfs\/\fR, the kernel parameters contained in -\fI\,parmfile\/\fR, the intermediate CA in \fI\,DigiCertCA.crt\/\fR, the -IBM Z signing key in \fI\,ibm-z-host-key-signing.crt\/\fR, and the -host-key document in \fI\,host_key.crt\/\fR. An AES-256 GCM key is stored in -\fI\,comm-key\/\fR, which is used for the PV guest dump support in the second -example. - -Generate a protected virtualization image: - -.PP -.B genprotimg \-i \fI\,vmlinuz\/\fR \-r \fI\,initramfs\/\fR \-p \fI\,parmfile\/\fR \-k \fI\,host_key.crt\/\fR \-C \fI\,ibm-z-host-key-signing.crt\/\fR \-C \fI\,DigiCertCA.crt\fR \-o \fI\,/boot/vmlinuz.pv\/\fR - -Generate a protected virtualization image with PV guest dump support: -.PP -.B genprotimg \-i \fI\,vmlinuz\/\fR \-r \fI\,initramfs\/\fR \-p \fI\,parmfile\/\fR \-k \fI\,host_key.crt\/\fR \-C \fI\,ibm-z-host-key-signing.crt\/\fR \-C \fI\,DigiCertCA.crt\fR \-o \fI\,/boot/vmlinuz.pv\/\fR \-\-enable\-dump \-\-comm\-key \fI\,comm-key\fR - -.SH NOTES -.IP "1." 4 -An ELF file cannot be used as a Linux kernel image. -.IP "2." 4 -Remember to re-run \fBzipl\fR after updating a protected -virtualization image. - -.SH SEE ALSO -\&\fBzipl\fR\|(5), \fBqemu\fR\|(1) diff --git a/genprotimg/samples/check_hostkeydoc b/genprotimg/samples/check_hostkeydoc deleted file mode 100755 index de8178ba..00000000 --- a/genprotimg/samples/check_hostkeydoc +++ /dev/null @@ -1,303 +0,0 @@ -#!/bin/sh -# -# check_hostkeydoc - Verify an IBM Secure Execution host key document -# -# Sample script to verify that a host key document is genuine by -# verifying the issuer, the validity date and the signature. -# Optionally verify the full trust chain using a CA certificate. -# -# Sample invocation: -# -# ./check_hostkeydoc HKD1234.crt ibm-z-host-key-signing.crt -c DigiCertCA.crt -r ibm-z-host-key.crl -# -# Copyright IBM Corp. 2020 -# -# s390-tools is free software; you can redistribute it and/or modify -# it under the terms of the MIT license. See LICENSE for details. - -# Allocate temporary files -ISSUER_PUBKEY_FILE=$(mktemp) -SIGNATURE_FILE=$(mktemp) -BODY_FILE=$(mktemp) -ISSUER_DN_FILE=$(mktemp) -SUBJECT_DN_FILE=$(mktemp) -DEF_ISSUER_ARMONK_DN_FILE=$(mktemp) -DEF_ISSUER_POUGHKEEPSIE_DN_FILE=$(mktemp) -CANONICAL_ISSUER_DN_FILE=$(mktemp) -CRL_SERIAL_FILE=$(mktemp) - -# Cleanup on exit -cleanup() -{ - rm -f "$ISSUER_PUBKEY_FILE" "$SIGNATURE_FILE" "$BODY_FILE" \ - "$ISSUER_DN_FILE" "$SUBJECT_DN_FILE" "$DEF_ISSUER_ARMONK_DN_FILE" "$DEF_ISSUER_POUGHKEEPSIE_DN_FILE" \ - "$CANONICAL_ISSUER_DN_FILE" "$CRL_SERIAL_FILE" -} -trap cleanup EXIT - -# Enhanced error checking for bash -if [ -n "${BASH}" ]; then - # shellcheck disable=SC3040 - set -o posix - # shellcheck disable=SC3040 - set -o pipefail - # shellcheck disable=SC3040 - set -o nounset -fi -set -e - -# Usage -usage() -{ - cat <<-EOF -Usage: $(basename "$1") [-d] [-c CA-cert] [-r CRL] host-key-doc signing-key-cert - -Verify an IBM Secure Execution host key document against -a signing key. - -Options: --d disable default issuer check of host-key-doc --c CA-cert trusted CA certificate --r CRL list of revoked host-key-docs - -Note that in order to have the full trust chain verified -it is necessary to provide the issuing CA's certificate. -The default issuer check may be disabled if a non-default -signing key certificate needs to be verified against the -CA certificate. - -EOF -} - -check_verify_chain() -{ - # Verify certificate chain in case a CA certificate file/bundle - # was specified on the command line. - if [ -z "$2" ]; then - cat >&2 <<-EOF -!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -No CA certificate specified! Skipping trust chain verification. -Make sure that '$1' is a valid certificate. -!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -EOF - else - openssl verify -crl_download -crl_check "$2" && - openssl verify -crl_download -crl_check -untrusted "$2" "$1" || - exit 1 - fi -} - -extract_pubkey() -{ - openssl x509 -in "$1" -pubkey -noout >"$2" -} - -extract_signature() -{ - # Assuming that the last field is the signature - SIGOFFSET=$(openssl asn1parse -in "$1" | tail -1 | cut -d : -f 1) - - openssl asn1parse -in "$1" -out "$2" -strparse "$SIGOFFSET" -noout -} - -extract_body() -{ - # Assuming that the first field is the full cert body - SIGOFFSET=$(openssl asn1parse -in "$1" | head -2 | tail -1 | cut -d : -f 1) - - openssl asn1parse -in "$1" -out "$2" -strparse "$SIGOFFSET" -noout -} - -verify_signature() -{ - # Assuming that the signature algorithm is SHA512 with RSA - openssl sha512 -verify "$1" -signature "$2" "$3" -} - -canonical_dn() -{ - OBJTYPE=$1 - OBJ=$2 - DNTYPE=$3 - OUTPUT=$4 - - openssl "$OBJTYPE" -in "$OBJ" -"$DNTYPE" -noout -nameopt multiline | - LC_ALL=C sort | grep -v "$DNTYPE"= >"$OUTPUT" -} - -default_issuer_armonk() -{ - cat <<-EOF - commonName = International Business Machines Corporation - countryName = US - localityName = Armonk - organizationName = International Business Machines Corporation - organizationalUnitName = Key Signing Service - stateOrProvinceName = New York -EOF -} - -default_issuer_pougkeepsie() -{ - cat <<-EOF - commonName = International Business Machines Corporation - countryName = US - localityName = Poughkeepsie - organizationName = International Business Machines Corporation - organizationalUnitName = Key Signing Service - stateOrProvinceName = New York -EOF -} - -# As organizationalUnitName can have an arbitrary prefix but must -# end with "Key Signing Service" let's normalize the OU name by -# stripping off the prefix -verify_default_issuer() -{ - default_issuer_pougkeepsie >"$DEF_ISSUER_POUGHKEEPSIE_DN_FILE" - default_issuer_armonk >"$DEF_ISSUER_ARMONK_DN_FILE" - - sed "s/\(^[ ]*organizationalUnitName[ ]*=[ ]*\).*\(Key Signing Service$\)/\1\2/" \ - "$ISSUER_DN_FILE" >"$CANONICAL_ISSUER_DN_FILE" - - if ! { - diff "$CANONICAL_ISSUER_DN_FILE" "$DEF_ISSUER_POUGHKEEPSIE_DN_FILE" || - diff "$CANONICAL_ISSUER_DN_FILE" "$DEF_ISSUER_ARMONK_DN_FILE" - } >/dev/null 2>&1; then - echo Incorrect default issuer >&2 && exit 1 - fi -} - -verify_issuer_files() -{ - if [ "$1" -eq 1 ]; then - verify_default_issuer - fi -} - -cert_time() -{ - DATE=$(openssl x509 -in "$1" -"$2" -noout | sed "s/^.*=//") - - date -d "$DATE" +%s -} - -crl_time() -{ - DATE=$(openssl crl -in "$1" -"$2" -noout | sed "s/^.*=//") - - date -d "$DATE" +%s -} - -verify_dates() -{ - START="$1" - END="$2" - MSG="${3:-Certificate}" - NOW=$(date +%s) - - if [ "$START" -le "$NOW" ] && [ "$NOW" -le "$END" ]; then - echo "${MSG} dates are OK" - else - echo "${MSG} date verification failed" >&2 && exit 1 - fi -} - -crl_serials() -{ - openssl crl -in "$1" -text -noout | - grep "Serial Number" >"$CRL_SERIAL_FILE" -} - -check_serial() -{ - CERT_SERIAL=$(openssl x509 -in "$1" -noout -serial | cut -d = -f 2) - - grep -q "$CERT_SERIAL" "$CRL_SERIAL_FILE" -} - -check_file() -{ - [ -e "$1" ] || - (echo "File '$1' not found" >&2 && exit 1) -} - -# check args -CRL_FILE= -CA_FILE= -CHECK_DEFAULT_ISSUER=1 - -while getopts 'dr:c:h' opt; do - case $opt in - d) CHECK_DEFAULT_ISSUER=0 ;; - r) CRL_FILE=$OPTARG ;; - c) CA_FILE=$OPTARG ;; - h) - usage "$0" - exit 0 - ;; - ?) - usage "$0" - exit 1 - ;; - esac -done -shift "$((OPTIND - 1))" - -if [ $# -ne 2 ]; then - usage "$0" >&2 - exit 1 -fi - -HKD_FILE=$1 -HKSK_FILE=$2 - -# Check whether all specified files exist -check_file "$HKD_FILE" -check_file "$HKSK_FILE" -# CA and CRL are optional arguments -[ -n "$CA_FILE" ] && check_file "$CA_FILE" -[ -n "$CRL_FILE" ] && check_file "$CRL_FILE" - -# Check trust chain -check_verify_chain "$HKSK_FILE" "$CA_FILE" - -# Verify host key document signature -printf "Checking host key document signature: " -extract_pubkey "$HKSK_FILE" "$ISSUER_PUBKEY_FILE" && - extract_signature "$HKD_FILE" "$SIGNATURE_FILE" && - extract_body "$HKD_FILE" "$BODY_FILE" && - verify_signature "$ISSUER_PUBKEY_FILE" "$SIGNATURE_FILE" "$BODY_FILE" || - exit 1 - -# Verify the issuer -canonical_dn x509 "$HKD_FILE" issuer "$ISSUER_DN_FILE" -canonical_dn x509 "$HKSK_FILE" subject "$SUBJECT_DN_FILE" -verify_issuer_files $CHECK_DEFAULT_ISSUER - -# Verify dates -verify_dates "$(cert_time "$HKD_FILE" startdate)" "$(cert_time "$HKD_FILE" enddate)" - -# Check CRL if specified -if [ -n "$CRL_FILE" ]; then - printf "Checking CRL signature: " - extract_signature "$CRL_FILE" "$SIGNATURE_FILE" && - extract_body "$CRL_FILE" "$BODY_FILE" && - verify_signature "$ISSUER_PUBKEY_FILE" "$SIGNATURE_FILE" "$BODY_FILE" || - exit 1 - - printf "CRL " - canonical_dn crl "$CRL_FILE" issuer "$ISSUER_DN_FILE" - canonical_dn x509 "$HKSK_FILE" subject "$SUBJECT_DN_FILE" - verify_issuer_files $CHECK_DEFAULT_ISSUER - - verify_dates "$(crl_time "$CRL_FILE" lastupdate)" "$(crl_time "$CRL_FILE" nextupdate)" 'CRL' - - crl_serials "$CRL_FILE" - check_serial "$HKD_FILE" && - echo "Certificate is revoked, do not use it anymore!" >&2 && - exit 1 -fi - -# We made it -echo All checks requested for \'"$HKD_FILE"\' were successful diff --git a/genprotimg/src/Makefile b/genprotimg/src/Makefile deleted file mode 100644 index cf61d7f6..00000000 --- a/genprotimg/src/Makefile +++ /dev/null @@ -1,104 +0,0 @@ -# Common definitions -include ../../common.mak - -bin_PROGRAM = genprotimg - -PKGDATADIR ?= $(TOOLS_DATADIR)/genprotimg -SRC_DIR := $(dir $(realpath $(firstword $(MAKEFILE_LIST)))) -TOP_SRCDIR := $(SRC_DIR)/../ -ROOT_DIR = $(TOP_SRC_DIR)/../../ -ZIPL_DIR = $(ROOT_DIR)/zipl -LOADER_DIR = $(TOP_SRCDIR)/boot - -INCLUDE_PATHS = $(SRC_DIR) $(TOP_SRCDIR) $(ROOTDIR)/include -INCLUDE_PARMS = $(addprefix -I,$(INCLUDE_PATHS)) - -WARNINGS := -Wall -Wextra -Wshadow \ - -Wcast-align -Wwrite-strings -Wmissing-prototypes \ - -Wmissing-declarations -Wredundant-decls -Wnested-externs \ - -Wno-long-long -Wuninitialized -Wconversion -Wstrict-prototypes \ - -Wpointer-arith -Wno-error=inline \ - $(NULL) - -$(bin_PROGRAM)_SRCS := $(bin_PROGRAM).c pv/pv_stage3.c pv/pv_image.c \ - pv/pv_comp.c pv/pv_hdr.c pv/pv_ipib.c utils/crypto.c utils/file_utils.c \ - pv/pv_args.c utils/buffer.c pv/pv_comps.c pv/pv_error.c \ - pv/pv_opt_item.c utils/curl.c \ - $(NULL) -$(bin_PROGRAM)_OBJS := $($(bin_PROGRAM)_SRCS:.c=.o) - -ALL_CFLAGS += -DPKGDATADIR=$(PKGDATADIR) \ - $(GLIB2_CFLAGS) $(LIBCRYPTO_CFLAGS) $(LIBCURL_CFLAGS) \ - -DOPENSSL_API_COMPAT=0x10100000L \ - $(WARNINGS) \ - $(NULL) -ALL_CPPFLAGS += $(INCLUDE_PARMS) -LDLIBS += $(GLIB2_LIBS) $(LIBCRYPTO_LIBS) $(LIBCURL_LIBS) - - -GLIB2_CFLAGS := $(shell $(PKG_CONFIG) --silence-errors --cflags glib-2.0) -GLIB2_LIBS := $(shell $(PKG_CONFIG) --silence-errors --libs glib-2.0) -LIBCRYPTO_CFLAGS := $(shell $(PKG_CONFIG) --silence-errors --cflags libcrypto) -LIBCRYPTO_LIBS := $(shell $(PKG_CONFIG) --silence-errors --libs libcrypto) -LIBCURL_CFLAGS := $(shell $(PKG_CONFIG) --silence-errors --cflags libcurl) -LIBCURL_LIBS := $(shell $(PKG_CONFIG) --silence-errors --libs libcurl) - -BUILD_TARGETS := skip-$(bin_PROGRAM) -INSTALL_TARGETS := skip-$(bin_PROGRAM) -ifneq (${HAVE_OPENSSL},0) -ifneq (${HAVE_GLIB2},0) -ifneq (${HAVE_LIBCURL},0) -BUILD_TARGETS := $(bin_PROGRAM) -INSTALL_TARGETS := install-$(bin_PROGRAM) -endif -endif -endif - -all: $(BUILD_TARGETS) - -install: $(INSTALL_TARGETS) - -$(bin_PROGRAM): $($(bin_PROGRAM)_OBJS) - -skip-$(bin_PROGRAM): - echo " SKIP $(bin_PROGRAM) due to unresolved dependencies" - -install-$(bin_PROGRAM): $(bin_PROGRAM) - $(INSTALL) -d -m 755 $(DESTDIR)$(USRBINDIR) - $(INSTALL) -c $^ $(DESTDIR)$(USRBINDIR) - -clean: - $(RM) -f $($(bin_PROGRAM)_OBJS) $(bin_PROGRAM) .check-dep-$(bin_PROGRAM) .detect-openssl.dep.c - -.PHONY: all install clean skip-$(bin_PROGRAM) install-$(bin_PROGRAM) - -$($(bin_PROGRAM)_OBJS): .check-dep-$(bin_PROGRAM) - -.detect-openssl.dep.c: - echo "#include " > $@ - echo "#if OPENSSL_VERSION_NUMBER < 0x10100000L" >> $@ - echo " #error openssl version 1.1.0 is required" >> $@ - echo "#endif" >> $@ - echo "static void __attribute__((unused)) test(void) {" >> $@ - echo " EVP_MD_CTX *ctx = EVP_MD_CTX_new();" >> $@ - echo " EVP_MD_CTX_free(ctx);" >> $@ - echo "}" >> $@ - -.check-dep-$(bin_PROGRAM): .detect-openssl.dep.c - $(call check_dep, \ - "$(bin_PROGRAM)", \ - "glib.h", \ - "glib2-devel / libglib2.0-dev", \ - "HAVE_GLIB2=0") - $(call check_dep, \ - "$(bin_PROGRAM)", \ - $^, \ - "openssl-devel / libssl-dev version >= 1.1.0", \ - "HAVE_OPENSSL=0", \ - "-I.") - $(call check_dep, \ - "$(bin_PROGRAM)", \ - "curl/curl.h", \ - "libcurl-devel", \ - "HAVE_LIBCURL=0") - touch $@ diff --git a/genprotimg/src/common.h b/genprotimg/src/common.h deleted file mode 100644 index 10b4f5a0..00000000 --- a/genprotimg/src/common.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright IBM Corp. 2020, 2022 - * - * s390-tools is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See LICENSE for details. - */ - -#ifndef COMMON_H -#define COMMON_H - -#define GETTEXT_PACKAGE "genprotimg" -#include -#include - -#include "boot/linux_layout.h" -#include "lib/zt_common.h" - -static const gchar tool_name[] = "genprotimg"; -static const gchar copyright_notice[] = "Copyright IBM Corp. 2020"; - -/* default values */ -#define GENPROTIMG_STAGE3A_PATH (STRINGIFY(PKGDATADIR) "/stage3a.bin") -#define GENPROTIMG_STAGE3B_PATH (STRINGIFY(PKGDATADIR) "/stage3b_reloc.bin") - -#define DEFAULT_INITIAL_PSW_ADDR IMAGE_ENTRY -#define DEFAULT_INITIAL_PSW_MASK (PSW_MASK_EA | PSW_MASK_BA) - -#define DO_PRAGMA(x) _Pragma(#x) - -# ifdef __clang__ -# define WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(...) \ - DO_PRAGMA(clang diagnostic push) \ - DO_PRAGMA(clang diagnostic ignored "-Wunused-function") \ - G_DEFINE_AUTOPTR_CLEANUP_FUNC(__VA_ARGS__) \ - DO_PRAGMA(clang diagnostic pop) -# else -# define WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(...) \ - G_DEFINE_AUTOPTR_CLEANUP_FUNC(__VA_ARGS__) -# endif - -#endif diff --git a/genprotimg/src/genprotimg.c b/genprotimg/src/genprotimg.c deleted file mode 100644 index 2041fa28..00000000 --- a/genprotimg/src/genprotimg.c +++ /dev/null @@ -1,200 +0,0 @@ -/* - * genprotimg - build relocatable secure images - * - * Copyright IBM Corp. 2020 - * - * s390-tools is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See LICENSE for details. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "common.h" -#include "pv/pv_args.h" -#include "pv/pv_image.h" -#include "utils/crypto.h" -#include "utils/curl.h" - -enum { - LOG_LEVEL_CRITICAL = 0, - LOG_LEVEL_INFO = 1, - LOG_LEVEL_DEBUG = 2, -}; - -static gint log_level = LOG_LEVEL_CRITICAL; -static gchar *tmp_dir; - -static void rmdir_recursive(gchar *dir_path, GError **err) -{ - const gchar *file = NULL; - g_autoptr(GDir) d = NULL; - - if (!dir_path) - return; - - d = g_dir_open(dir_path, 0, err); - if (!d) { - g_set_error(err, G_FILE_ERROR, - (gint)g_file_error_from_errno(errno), - _("Failed to open directory '%s': %s"), dir_path, - g_strerror(errno)); - return; - } - - while ((file = g_dir_read_name(d)) != NULL) { - g_autofree gchar *file_path = - g_build_filename(dir_path, file, NULL); - /* ignore error */ - (void)g_unlink(file_path); - } - - if (g_rmdir(dir_path) != 0) { - g_set_error(err, G_FILE_ERROR, - (gint)g_file_error_from_errno(errno), - _("Failed to remove directory '%s': %s"), dir_path, - g_strerror(errno)); - return; - } -} - -static void sig_term_handler(int signal G_GNUC_UNUSED) -{ - rmdir_recursive(tmp_dir, NULL); - exit(EXIT_FAILURE); -} - -static void log_handler_cb(const gchar *log_domain G_GNUC_UNUSED, - GLogLevelFlags level, const gchar *message, - gpointer user_data G_GNUC_UNUSED) -{ - const gchar *prefix = ""; - - /* filter out messages depending on debugging level */ - if ((level & G_LOG_LEVEL_DEBUG) && log_level < LOG_LEVEL_DEBUG) - return; - - if ((level & G_LOG_LEVEL_INFO) && log_level < LOG_LEVEL_INFO) - return; - - if (level & G_LOG_LEVEL_WARNING) - prefix = "WARNING: "; - - if (level & G_LOG_LEVEL_ERROR) - prefix = "ERROR: "; - - if (level & (G_LOG_LEVEL_WARNING | G_LOG_LEVEL_ERROR)) - g_printerr("%s%s\n", prefix, message); - else - g_print("%s%s\n", prefix, message); -} - -static void setup_prgname(const gchar *name) -{ - g_set_prgname(name); - g_set_application_name(_(name)); -} - -static void setup_handler(const gint *signals, const gsize signals_n) -{ - /* set up logging handler */ - g_log_set_handler(NULL, - G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | - G_LOG_FLAG_RECURSION, - log_handler_cb, NULL); - - /* set signal handler */ - for (gsize i = 0; i < signals_n; i++) - signal(signals[i], sig_term_handler); -} - -static void remove_signal_handler(const gint *signals, const gsize signals_n) -{ - for (gsize i = 0; i < signals_n; i++) - signal(signals[i], SIG_DFL); -} - -static void __attribute__((constructor)) __init(void); -static void __attribute__((destructor)) __cleanup(void); -gint main(gint argc, gchar *argv[]) -{ - g_autoptr(PvArgs) args = pv_args_new(); - gint signals[] = { SIGINT, SIGTERM }; - g_autoptr(PvImage) img = NULL; - gint ret = EXIT_FAILURE; - GError *err = NULL; - - setlocale(LC_CTYPE, ""); - setup_prgname(tool_name); - setup_handler(signals, G_N_ELEMENTS(signals)); - - if (pv_args_parse_options(args, &argc, &argv, &err) < 0) - goto error; - - /* set new log level */ - log_level = args->log_level; - - /* if the user has not specified a temporary directory let's - * create one - */ - if (!args->tmp_dir) { - tmp_dir = g_dir_make_tmp("genprotimg-XXXXXX", &err); - if (!tmp_dir) - goto error; - args->tmp_dir = g_strdup(tmp_dir); - } - - /* allocate and initialize ``pv_img`` data structure */ - img = pv_img_new(args, GENPROTIMG_STAGE3A_PATH, &err); - if (!img) - goto error; - - /* add user components: `args->comps` must be sorted by the - * component type => by memory address - */ - for (GSList *iterator = args->comps; iterator; iterator = iterator->next) { - const PvArg *arg = iterator->data; - - if (pv_img_add_component(img, arg, &err) < 0) - goto error; - } - - if (pv_img_finalize(img, GENPROTIMG_STAGE3B_PATH, &err) < 0) - goto error; - - if (pv_img_write(img, args->output_path, &err) < 0) - goto error; - - ret = EXIT_SUCCESS; - -error: - if (err) { - fputs(err->message, stderr); - fputc('\n', stderr); - g_clear_error(&err); - } - rmdir_recursive(tmp_dir, NULL); - remove_signal_handler(signals, G_N_ELEMENTS(signals)); - g_free(tmp_dir); - g_clear_pointer(&img, pv_img_free); - g_clear_pointer(&args, pv_args_free); - exit(ret); -} - -static void __init(void) -{ - pv_crypto_init(); - if (curl_init() != 0) - g_abort(); -} - -static void __cleanup(void) -{ - curl_cleanup(); - pv_crypto_cleanup(); -} diff --git a/genprotimg/src/include/pv_crypto_def.h b/genprotimg/src/include/pv_crypto_def.h deleted file mode 100644 index 49710dc1..00000000 --- a/genprotimg/src/include/pv_crypto_def.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * PV cryptography related definitions - * - * Copyright IBM Corp. 2020 - * - * s390-tools is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See LICENSE for details. - */ - -#ifndef PV_CRYPTO_DEF_H -#define PV_CRYPTO_DEF_H - -#include - -#include "lib/zt_common.h" - -/* IBM signing key subject */ -#define PV_IBM_Z_SUBJECT_COMMON_NAME "International Business Machines Corporation" -#define PV_IBM_Z_SUBJECT_COUNTRY_NAME "US" -#define PV_IBM_Z_SUBJECT_LOCALITY_NAME_POUGHKEEPSIE "Poughkeepsie" -#define PV_IBM_Z_SUBJECT_LOCALITY_NAME_ARMONK "Armonk" -#define PV_IBM_Z_SUBJECT_ORGANIZATIONONAL_UNIT_NAME_SUFFIX "Key Signing Service" -#define PV_IBM_Z_SUBJECT_ORGANIZATION_NAME "International Business Machines Corporation" -#define PV_IBM_Z_SUBJECT_STATE "New York" -#define PV_IMB_Z_SUBJECT_ENTRY_COUNT 6 - -/* Minimum security level for the keys/certificates used to establish a chain of - * trust (see https://www.openssl.org/docs/man1.1.1/man3/X509_VERIFY_PARAM_set_auth_level.html - * for details). - */ -#define PV_CERTS_SECURITY_LEVEL 2 - -union ecdh_pub_key { - struct { - uint8_t x[80]; - uint8_t y[80]; - }; - uint8_t data[160]; -} __packed; - -#endif diff --git a/genprotimg/src/include/pv_hdr_def.h b/genprotimg/src/include/pv_hdr_def.h deleted file mode 100644 index ecb482fd..00000000 --- a/genprotimg/src/include/pv_hdr_def.h +++ /dev/null @@ -1,95 +0,0 @@ -/* - * PV header definitions - * - * Copyright IBM Corp. 2020 - * - * s390-tools is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See LICENSE for details. - */ - -#ifndef PV_HDR_DEF_H -#define PV_HDR_DEF_H - -#include - -#include "boot/psw.h" -#include "lib/zt_common.h" -#include "utils/crypto.h" - -#include "pv_crypto_def.h" - -/* Magic number which is used to identify the file containing the PV - * header - */ -#define PV_MAGIC_NUMBER 0x49424d5365634578ULL -#define PV_VERSION_1 0x00000100U - -/* Internal helper macro */ -#define __PV_BIT(nr) (1ULL << (63 - (nr))) - -/* Plaintext control flags */ -#define PV_PCF_ALLOW_DUMPING __PV_BIT(34) /* dumping of the configuration is allowed */ -#define PV_PCF_NO_DECRYPTION __PV_BIT(35) /* prevent Ultravisor decryption during unpack operation */ -#define PV_PCF_PCKMO_DEA_TDEA __PV_BIT(56) /* PCKMO encrypt-DEA/TDEA-key functions allowed */ -#define PV_PCF_PCKMO_AES __PV_BIT(57) /* PCKMO encrypt-AES-key functions allowed */ -#define PV_PCF_PCKM_ECC __PV_BIT(58) /* PCKMO encrypt-ECC-key functions allowed */ - -/* Secret control flags */ -#define PV_SCF_CCK_EXTENSION_SECRET_ENFORCMENT \ - __PV_BIT(1) /* All add-secret requests must provide an extension secret */ - -/* maxima for the PV version 1 */ -#define PV_V1_IPIB_MAX_SIZE PAGE_SIZE -#define PV_V1_PV_HDR_MAX_SIZE (2 * PAGE_SIZE) - -typedef struct pv_hdr_key_slot { - uint8_t digest_key[SHA256_DIGEST_LENGTH]; - uint8_t wrapped_key[32]; - uint8_t tag[AES_256_GCM_TAG_SIZE]; -} __packed PvHdrKeySlot; - -typedef struct pv_hdr_opt_item { - uint32_t otype; - uint8_t ibk[32]; - uint8_t data[]; -} __packed PvHdrOptItem; - -/* integrity protected data (by GCM tag), but non-encrypted */ -struct pv_hdr_head { - uint64_t magic; - uint32_t version; - uint32_t phs; - uint8_t iv[AES_256_GCM_IV_SIZE]; - uint32_t res1; - uint64_t nks; - uint64_t sea; - uint64_t nep; - uint64_t pcf; - union ecdh_pub_key cust_pub_key; - uint8_t pld[SHA512_DIGEST_LENGTH]; - uint8_t ald[SHA512_DIGEST_LENGTH]; - uint8_t tld[SHA512_DIGEST_LENGTH]; -} __packed; - -/* Must not have any padding */ -struct pv_hdr_encrypted { - uint8_t cust_comm_key[32]; - uint8_t img_enc_key_1[AES_256_XTS_KEY_SIZE / 2]; - uint8_t img_enc_key_2[AES_256_XTS_KEY_SIZE / 2]; - struct psw_t psw; - uint64_t scf; - uint32_t noi; - uint32_t res2; -}; -STATIC_ASSERT(sizeof(struct pv_hdr_encrypted) == - 32 + 32 + 32 + sizeof(struct psw_t) + 8 + 4 + 4) - -typedef struct pv_hdr { - struct pv_hdr_head head; - struct pv_hdr_key_slot *slots; - struct pv_hdr_encrypted *encrypted; - struct pv_hdr_opt_item **optional_items; - uint8_t tag[AES_256_GCM_TAG_SIZE]; -} PvHdr; - -#endif diff --git a/genprotimg/src/pv/pv_args.c b/genprotimg/src/pv/pv_args.c deleted file mode 100644 index 67ddc398..00000000 --- a/genprotimg/src/pv/pv_args.c +++ /dev/null @@ -1,556 +0,0 @@ -/* - * PV arguments related definitions and functions - * - * Copyright IBM Corp. 2020 - * - * s390-tools is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See LICENSE for details. - */ - -#include -#include - -#include "common.h" - -#include "pv_comp.h" -#include "pv_error.h" -#include "pv_args.h" - -static gchar summary[] = - "Use genprotimg to create a protected virtualization kernel image file,\n" - "which can be loaded using zipl or QEMU. For all certificates, revocation\n" - "lists, and host-key documents, both the PEM and DER input formats are\n" - "supported."; - -static gint pv_arg_compare(gconstpointer arg_1, gconstpointer arg_2) -{ - g_assert(arg_1); - g_assert(arg_2); - - PvComponentType a = ((PvArg *)arg_1)->type; - PvComponentType b = ((PvArg *)arg_2)->type; - - if (a < b) - return -1; - if (a == b) - return 0; - return 1; -} - -static gint pv_arg_has_type(gconstpointer arg, gconstpointer type) -{ - const PvArg *c = arg; - const PvComponentType *t = type; - - g_assert(arg); - - if (c->type == *t) - return 0; - if (c->type < *t) - return -1; - return 1; -} - -static gint pv_args_set_defaults(PvArgs *args, GError **err G_GNUC_UNUSED) -{ - if (!args->psw_addr) - args->psw_addr = - g_strdup_printf("0x%lx", DEFAULT_INITIAL_PSW_ADDR); - - return 0; -} - -static gint pv_args_validate_options(PvArgs *args, GError **err) -{ - const PvControlFlagsArgs *cf_args = &args->cf_args; - PvComponentType KERNEL = PV_COMP_TYPE_KERNEL; - - /* Check for mutually exclusive arguments */ - if (cf_args->pcf && - !(cf_args->enable_pckmo == PV_NOT_SET && cf_args->enable_dump == PV_NOT_SET)) { - g_set_error( - err, PV_PARSE_ERROR, PV_PARSE_ERROR_SYNTAX, - _("The '--x-pcf' option cannot be used with the '--(enable|disable)-pckmo' or" - " '--(enable|disable)-dump' flags.\nUse 'genprotimg --help' for more information")); - return -1; - } - - if (cf_args->scf && !(cf_args->enable_cck_extension_secret_enforcement == PV_NOT_SET)) { - g_set_error( - err, PV_PARSE_ERROR, PV_PARSE_ERROR_SYNTAX, - _("The '--x-scf' option cannot be used with the '--(enable|disable)-extension-secret-required' flags.\nUse 'genprotimg --help' for more information")); - return -1; - } - - /* Check for unused arguments */ - if (args->unused_values->len > 0) { - g_autofree gchar *unused = NULL; - - for (gsize i = args->unused_values->len; i > 0; i--) { - g_autofree gchar *tmp = unused; - - unused = g_strjoin(" ", g_ptr_array_index(args->unused_values, i - 1), - tmp, - NULL); - } - - g_set_error(err, PV_PARSE_ERROR, PR_PARSE_ERROR_INVALID_ARGUMENT, - _("Unrecognized arguments: '%s'.\nUse 'genprotimg --help' for more information"), - unused); - return -1; - } - - /* Check for mandatory arguments */ - if (cf_args->enable_dump == PV_TRUE && !args->cust_comm_key_path) { - g_set_error(err, PV_PARSE_ERROR, PR_PARSE_ERROR_MISSING_ARGUMENT, - _("Option '--enable-dump' requires the '--comm-key' option.\nUse 'genprotimg " - "--help' for more information")); - return -1; - } - if (cf_args->enable_cck_extension_secret_enforcement == PV_TRUE && - !args->cust_comm_key_path) { - g_set_error( - err, PV_PARSE_ERROR, PR_PARSE_ERROR_MISSING_ARGUMENT, - _("Option '--enable-cck-extension-secret' requires the '--comm-key' option.\nUse 'genprotimg " - "--help' for more information")); - return -1; - } - - if (!args->output_path) { - g_set_error(err, PV_PARSE_ERROR, PR_PARSE_ERROR_MISSING_ARGUMENT, - _("Option '--output' is required.\nUse 'genprotimg --help' for more information")); - return -1; - } - - if (!g_slist_find_custom(args->comps, &KERNEL, pv_arg_has_type)) { - g_set_error(err, PV_PARSE_ERROR, PR_PARSE_ERROR_MISSING_ARGUMENT, - _("Option '--image' is required.\nUse 'genprotimg --help' for more information")); - return -1; - } - - if (!args->host_keys || g_strv_length(args->host_keys) == 0) { - g_set_error(err, PV_PARSE_ERROR, PR_PARSE_ERROR_MISSING_ARGUMENT, - _("Option '--host-key-document' is required.\nUse 'genprotimg --help' for more information")); - return -1; - } - - if (!args->no_verify && - (!args->untrusted_cert_paths || - g_strv_length(args->untrusted_cert_paths) == 0)) { - g_set_error( - err, PV_PARSE_ERROR, PR_PARSE_ERROR_MISSING_ARGUMENT, - _("Either specify the IBM Z signing key and intermediate CA certificate\n" - "by using the '--cert' option, or use the '--no-verify' flag to disable the\n" - "host-key document verification completely (at your own risk).")); - return -1; - } - - return 0; -} - -static gboolean cb_add_component(const gchar *option, const gchar *value, - PvArgs *args, GError **err) -{ - PvArg *comp = NULL; - gint type = -1; - - if (g_str_equal(option, "-i") || g_str_equal(option, "--image")) - type = PV_COMP_TYPE_KERNEL; - if (g_str_equal(option, "-r") || g_str_equal(option, "--ramdisk")) - type = PV_COMP_TYPE_INITRD; - if (g_str_equal(option, "-p") || g_str_equal(option, "--parmfile")) - type = PV_COMP_TYPE_CMDLINE; - - if (type < 0) { - g_set_error(err, PV_PARSE_ERROR, PV_PARSE_ERROR_SYNTAX, - _("Invalid option '%s': "), option); - return FALSE; - } - - if (g_slist_find_custom(args->comps, &type, pv_arg_has_type)) { - g_set_error(err, PV_PARSE_ERROR, PV_PARSE_ERROR_SYNTAX, - _("Multiple values for option '%s'"), option); - return FALSE; - } - - comp = pv_arg_new((PvComponentType)type, value); - args->comps = g_slist_insert_sorted(args->comps, comp, pv_arg_compare); - return TRUE; -} - -static gboolean cb_set_string_option(const gchar *option, const gchar *value, - PvArgs *args, GError **err) -{ - gchar **args_option = NULL; - - if (g_str_equal(option, "--comm-key")) - args_option = &args->cust_comm_key_path; - if (g_str_equal(option, "--root-ca")) - args_option = &args->root_ca_path; - if (g_str_equal(option, "-o") || g_str_equal(option, "--output")) - args_option = &args->output_path; - if (g_str_equal(option, "--x-comp-key")) - args_option = &args->xts_key_path; - if (g_str_equal(option, "--x-header-key")) - args_option = &args->cust_root_key_path; - if (g_str_equal(option, "--x-pcf")) - args_option = &args->cf_args.pcf; - if (g_str_equal(option, "--x-psw")) - args_option = &args->psw_addr; - if (g_str_equal(option, "--x-scf")) - args_option = &args->cf_args.scf; - - if (!args_option) { - g_set_error(err, PV_PARSE_ERROR, PV_PARSE_ERROR_SYNTAX, - _("Invalid option '%s': "), option); - return FALSE; - } - - if (*args_option) { - g_set_error(err, PV_PARSE_ERROR, PV_PARSE_ERROR_SYNTAX, - _("Multiple values for option '%s'"), option); - return FALSE; - } - - *args_option = g_strdup(value); - return TRUE; -} - -static gboolean cb_set_log_level(const gchar *option G_GNUC_UNUSED, - const gchar *value G_GNUC_UNUSED, PvArgs *args, - GError **err G_GNUC_UNUSED) -{ - args->log_level++; - return TRUE; -} - -static gboolean cb_remaining_values(const gchar *option G_GNUC_UNUSED, - const gchar *value, PvArgs *args, - GError **err G_GNUC_UNUSED) -{ - g_ptr_array_add(args->unused_values, g_strdup(value)); - return TRUE; -} - -#define MUT_EXCL_BOOL_FLAG_CB_NAME(FLAG, VALUE) (cb_##FLAG##_##VALUE) -#define DEFINE_MUT_EXCL_BOOL_FLAG_CB(FLAG, VALUE) \ - static gboolean MUT_EXCL_BOOL_FLAG_CB_NAME(FLAG, VALUE)(const gchar *option G_GNUC_UNUSED, \ - const gchar *value G_GNUC_UNUSED, \ - PvArgs *args, GError **err) \ - { \ - if (!(args->cf_args.enable_##FLAG == PV_NOT_SET || \ - args->cf_args.enable_##FLAG == VALUE)) { \ - g_set_error(err, G_OPTION_ERROR, G_OPTION_ERROR_FAILED, \ - "'--enable-" #FLAG "' and '--disable-" #FLAG \ - "' are mutually exclusive"); \ - return FALSE; \ - } \ - args->cf_args.enable_##FLAG = VALUE; \ - return TRUE; \ - } - -#define DEFINE_MUT_EXCL_BOOL_FLAG_CBS(FLAG) \ - DEFINE_MUT_EXCL_BOOL_FLAG_CB(FLAG, PV_TRUE) \ - DEFINE_MUT_EXCL_BOOL_FLAG_CB(FLAG, PV_FALSE) - -#define MUT_EXCL_BOOL_FLAG(NAME, FLAG, ENABLE_DESC, DISABLE_DESC) \ - { \ - .long_name = "enable-" #NAME, \ - .short_name = 0, \ - .flags = G_OPTION_FLAG_NO_ARG, \ - .arg = G_OPTION_ARG_CALLBACK, \ - .arg_data = MUT_EXCL_BOOL_FLAG_CB_NAME(FLAG, PV_TRUE), \ - .description = ENABLE_DESC, \ - }, \ - { \ - .long_name = "disable-" #NAME, .short_name = 0, .flags = G_OPTION_FLAG_NO_ARG, \ - .arg = G_OPTION_ARG_CALLBACK, \ - .arg_data = MUT_EXCL_BOOL_FLAG_CB_NAME(FLAG, PV_FALSE), \ - .description = DISABLE_DESC, \ - } - -#define INDENT " " - -/* Define the callbacks for mutually exclusive command line flags */ -DEFINE_MUT_EXCL_BOOL_FLAG_CBS(dump); -DEFINE_MUT_EXCL_BOOL_FLAG_CBS(pckmo); -DEFINE_MUT_EXCL_BOOL_FLAG_CBS(cck_extension_secret_enforcement); - -gint pv_args_parse_options(PvArgs *args, gint *argc, gchar **argv[], - GError **err) -{ - g_autoptr(GOptionContext) context = NULL; - gboolean print_version = FALSE; - GOptionGroup *group, *x_group; - - g_autofree gchar *psw_desc = g_strdup_printf( - _("Load from the specified hexadecimal ADDRESS.\n" INDENT - "Optional; default: '0x%lx'."), - DEFAULT_INITIAL_PSW_ADDR); - GOptionEntry entries[] = { - { .long_name = "host-key-document", - .short_name = 'k', - .flags = G_OPTION_FLAG_NONE, - .arg = G_OPTION_ARG_FILENAME_ARRAY, - .arg_data = &args->host_keys, - .description = - _("FILE specifies a host-key document. At least\n" INDENT - "one is required. Specify this option multiple times\n" INDENT - "to enable the image to run on more than one host."), - .arg_description = _("FILE") }, - { .long_name = "cert", - .short_name = 'C', - .flags = G_OPTION_FLAG_NONE, - .arg = G_OPTION_ARG_FILENAME_ARRAY, - .arg_data = &args->untrusted_cert_paths, - .description = _( - "FILE contains a certificate that is used to\n" INDENT - "establish a chain of trust for the verification\n" INDENT - "of the host-key documents. The IBM Z signing\n" INDENT - "key and intermediate CA certificate (signed\n" INDENT - "by the root CA) are required."), - .arg_description = _("FILE") }, - { .long_name = "output", - .short_name = 'o', - .flags = G_OPTION_FLAG_FILENAME, - .arg = G_OPTION_ARG_CALLBACK, - .arg_data = cb_set_string_option, - .description = _("Set FILE as the output file."), - .arg_description = _("FILE") }, - { .long_name = "image", - .short_name = 'i', - .flags = G_OPTION_FLAG_FILENAME, - .arg = G_OPTION_ARG_CALLBACK, - .arg_data = cb_add_component, - .description = _("Use IMAGE as the Linux kernel image."), - .arg_description = _("IMAGE") }, - { .long_name = "ramdisk", - .short_name = 'r', - .flags = G_OPTION_FLAG_FILENAME, - .arg = G_OPTION_ARG_CALLBACK, - .arg_data = cb_add_component, - .description = _("Use RAMDISK as the initial RAM disk\n" INDENT - "(optional)."), - .arg_description = _("RAMDISK") }, - { .long_name = "parmfile", - .short_name = 'p', - .flags = G_OPTION_FLAG_FILENAME, - .arg = G_OPTION_ARG_CALLBACK, - .arg_data = cb_add_component, - .description = _("Use the kernel parameters stored in PARMFILE\n" INDENT - "(optional)."), - .arg_description = _("PARMFILE") }, - MUT_EXCL_BOOL_FLAG(dump, dump, - _("Enable PV guest dumps (optional). This option\n" INDENT - "requires the '--comm-key' option."), - _("Disable PV guest dumps (default).")), - MUT_EXCL_BOOL_FLAG( - cck-extension-secret, cck_extension_secret_enforcement, - _("Add-secret requests must provide an extension\n" INDENT - "secret that matches the CCK-derived extension\n" INDENT - "secret (optional). This option requires the\n" INDENT - "'--comm-key' option."), - _("Add-secret requests don't have to provide\n" INDENT - "the CCK-derived extension secret (default).")), - MUT_EXCL_BOOL_FLAG(pckmo, pckmo, - _("Enable the support for the DEA, TDEA, AES, and\n" INDENT - "ECC PCKMO key encryption functions (default)."), - _("Disable the support for the DEA, TDEA, AES, and\n" INDENT - "ECC PCKMO key encryption functions (optional).")), - { .long_name = "comm-key", - .short_name = 0, - .flags = G_OPTION_FLAG_FILENAME, - .arg = G_OPTION_ARG_CALLBACK, - .arg_data = cb_set_string_option, - .description = _( - "FILE contains the customer communication key\n" INDENT - "(CCK) (optional)."), - .arg_description = _("FILE") }, - { .long_name = "crl", - .short_name = 0, - .flags = G_OPTION_FLAG_NONE, - .arg = G_OPTION_ARG_FILENAME_ARRAY, - .arg_data = &args->crl_paths, - .description = _( - "FILE contains a certificate revocation list\n" INDENT - "(optional)."), - .arg_description = _("FILE") }, - { .long_name = "offline", - .short_name = 0, - .flags = G_OPTION_FLAG_NONE, - .arg = G_OPTION_ARG_NONE, - .arg_data = &args->offline, - .description = _("Don't download CRLs (optional)."), - .arg_description = NULL }, - { .long_name = "root-ca", - .short_name = 0, - .flags = G_OPTION_FLAG_FILENAME, - .arg = G_OPTION_ARG_CALLBACK, - .arg_data = cb_set_string_option, - .description = _( - "Set FILE as the trusted root CA and don't use the\n" INDENT - "root CAs that are installed on the system (optional)."), - .arg_description = _("FILE") }, - { .long_name = "no-verify", - .short_name = 0, - .flags = G_OPTION_FLAG_NONE, - .arg = G_OPTION_ARG_NONE, - .arg_data = &args->no_verify, - .description = _("Disable the host-key document verification\n" INDENT - "(optional)."), - .arg_description = NULL }, - { .long_name = "verbose", - .short_name = 'V', - .flags = G_OPTION_FLAG_NO_ARG, - .arg = G_OPTION_ARG_CALLBACK, - .arg_data = cb_set_log_level, - .description = _("Provide more detailed output (optional)."), - .arg_description = NULL }, - { .long_name = "version", - .short_name = 'v', - .flags = G_OPTION_FLAG_NONE, - .arg = G_OPTION_ARG_NONE, - .arg_data = &print_version, - .description = _("Print the version and exit."), - .arg_description = NULL }, - { .long_name = G_OPTION_REMAINING, - .short_name = 0, - .flags = 0, - .arg = G_OPTION_ARG_CALLBACK, - .arg_data = cb_remaining_values, - .description = NULL, - .arg_description = NULL }, - { 0 }, - }; - - GOptionEntry x_entries[] = { - { .long_name = "x-comp-key", - .short_name = 0, - .flags = G_OPTION_FLAG_FILENAME, - .arg = G_OPTION_ARG_CALLBACK, - .arg_data = cb_set_string_option, - .description = _( - "Use FILE as the AES 256-bit XTS key\n" INDENT - "that is used for the component encryption.\n" INDENT - "Optional; default: auto-generated."), - .arg_description = _("FILE") }, - { .long_name = "x-header-key", - .short_name = 0, - .flags = G_OPTION_FLAG_FILENAME, - .arg = G_OPTION_ARG_CALLBACK, - .arg_data = cb_set_string_option, - .description = _( - "Use FILE as the AES 256-bit GCM header key\n" INDENT - "that protects the PV header.\n" INDENT - "Optional; default: auto-generated."), - .arg_description = _("FILE") }, - { .long_name = "x-pcf", - .short_name = 0, - .flags = G_OPTION_FLAG_NONE, - .arg = G_OPTION_ARG_CALLBACK, - .arg_data = cb_set_string_option, - .description = - _("Specify the plaintext control flags\n" INDENT - "as a hexadecimal value.\n" INDENT - "Optional; mutually exclusive with\n" INDENT - "'--(enable|disable)-pckmo'; default: '0xe0'."), - .arg_description = _("VALUE") }, - { .long_name = "x-psw", - .short_name = 0, - .flags = G_OPTION_FLAG_NONE, - .arg = G_OPTION_ARG_CALLBACK, - .arg_data = cb_set_string_option, - .description = psw_desc, - .arg_description = _("ADDRESS") }, - { .long_name = "x-scf", - .short_name = 0, - .flags = G_OPTION_FLAG_NONE, - .arg = G_OPTION_ARG_CALLBACK, - .arg_data = cb_set_string_option, - .description = _("Specify the secret control flags\n" INDENT - "as a hexadecimal value.\n" INDENT - "Optional; mutually exclusive with\n" INDENT - "'--(enable|disable)-cck-extension-secret';\n" INDENT - "Optional; default: '0x0'."), - .arg_description = _("VALUE") }, - { 0 }, - }; - - context = g_option_context_new( - _("- Create a protected virtualization image")); - g_option_context_set_summary(context, _(summary)); - group = g_option_group_new(GETTEXT_PACKAGE, _("Application Options:"), - _("Show help options"), args, NULL); - g_option_group_add_entries(group, entries); - g_option_context_set_main_group(context, group); - - x_group = g_option_group_new("experimental", _("Experimental Options:"), - _("Show experimental options"), args, NULL); - g_option_group_add_entries(x_group, x_entries); - g_option_context_add_group(context, x_group); - if (!g_option_context_parse(context, argc, argv, err)) - return -1; - - if (print_version) { - g_printf(_("%s version %s\n"), tool_name, RELEASE_STRING); - g_printf("%s\n", copyright_notice); - exit(EXIT_SUCCESS); - } - - if (pv_args_set_defaults(args, err) < 0) - return -1; - - return pv_args_validate_options(args, err); -} - -PvArgs *pv_args_new(void) -{ - g_autoptr(PvArgs) args = g_new0(PvArgs, 1); - - args->unused_values = g_ptr_array_new_with_free_func(g_free); - /* `args->cf_args` is implicitly initialized with zeros since - * `g_new0` is used. So there is no reason to explicitly - * initialize the values as PV_NOT_SET == 0. - */ - return g_steal_pointer(&args); -} - -void pv_args_free(PvArgs *args) -{ - if (!args) - return; - - g_free(args->cf_args.pcf); - g_free(args->cf_args.scf); - g_free(args->psw_addr); - g_free(args->cust_root_key_path); - g_free(args->cust_comm_key_path); - g_free(args->gcm_iv_path); - g_free(args->root_ca_path); - g_strfreev(args->crl_paths); - g_strfreev(args->untrusted_cert_paths); - g_strfreev(args->host_keys); - g_free(args->xts_key_path); - g_slist_free_full(args->comps, (GDestroyNotify)pv_arg_free); - g_ptr_array_free(args->unused_values, TRUE); - g_free(args->output_path); - g_free(args->tmp_dir); - g_free(args); -} - -void pv_arg_free(PvArg *arg) -{ - if (!arg) - return; - - g_free(arg->path); - g_free(arg); -} -PvArg *pv_arg_new(PvComponentType type, const gchar *path) -{ - g_autoptr(PvArg) ret = g_new0(struct pv_arg, 1); - - ret->type = type; - ret->path = g_strdup(path); - return g_steal_pointer(&ret); -} diff --git a/genprotimg/src/pv/pv_args.h b/genprotimg/src/pv/pv_args.h deleted file mode 100644 index 87fbe425..00000000 --- a/genprotimg/src/pv/pv_args.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * PV arguments related definitions and functions - * - * Copyright IBM Corp. 2020 - * - * s390-tools is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See LICENSE for details. - */ - -#ifndef PV_ARGS_H -#define PV_ARGS_H - -#include - -#include "pv_comp.h" - -typedef struct pv_arg { - PvComponentType type; - gchar *path; -} PvArg; - -PvArg *pv_arg_new(PvComponentType type, const gchar *path); -void pv_arg_free(PvArg *arg); - -typedef enum pv_tristate { - PV_NOT_SET = 0, - PV_TRUE, - PV_FALSE, -} PvTristate; -/* The value of PV_NOT_SET is not allowed to be changed */ -STATIC_ASSERT(PV_NOT_SET == 0) - -typedef struct { - gchar *pcf; - gchar *scf; - /* Add-secret requests do require CCK-extension secrets */ - PvTristate enable_cck_extension_secret_enforcement; - PvTristate enable_dump; - PvTristate enable_pckmo; -} PvControlFlagsArgs; - -typedef struct { - gint log_level; - gint no_verify; - gboolean offline; - PvControlFlagsArgs cf_args; - gchar *psw_addr; /* PSW address which will be used for the start of - * the actual component (e.g. Linux kernel) - */ - gchar *cust_root_key_path; - gchar *cust_comm_key_path; - gchar *gcm_iv_path; - gchar **host_keys; - gchar *root_ca_path; /* Trusted root CA used for the verification of the - * chain of trust (if specified). - */ - gchar **untrusted_cert_paths; - gchar **crl_paths; - gchar *xts_key_path; - GSList *comps; - gchar *output_path; - gchar *tmp_dir; - GPtrArray *unused_values; -} PvArgs; - -PvArgs *pv_args_new(void); -void pv_args_free(PvArgs *args); - -gint pv_args_parse_options(PvArgs *args, gint *argc, gchar **argv[], - GError **err); - -WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(PvArg, pv_arg_free) -WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(PvArgs, pv_args_free) - -#endif diff --git a/genprotimg/src/pv/pv_comp.c b/genprotimg/src/pv/pv_comp.c deleted file mode 100644 index a9c0d397..00000000 --- a/genprotimg/src/pv/pv_comp.c +++ /dev/null @@ -1,432 +0,0 @@ -/* - * PV component related definitions and functions - * - * Copyright IBM Corp. 2020 - * - * s390-tools is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See LICENSE for details. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "common.h" -#include "utils/align.h" -#include "utils/buffer.h" -#include "utils/crypto.h" -#include "utils/file_utils.h" - -#include "pv_comp.h" -#include "pv_error.h" - -static void comp_file_free(CompFile *comp) -{ - if (!comp) - return; - - g_free(comp->path); - g_free(comp); -} - -WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(CompFile, comp_file_free) - -static PvComponent *pv_component_new(PvComponentType type, gsize size, - PvComponentDataType d_type, void **data, - GError **err) -{ - g_autoptr(PvComponent) ret = g_new0(PvComponent, 1); - - g_assert(type >= 0 && type <= UINT16_MAX); - - ret->type = (int)type; - ret->d_type = (int)d_type; - ret->data = g_steal_pointer(data); - ret->orig_size = size; - - if (generate_tweak(&ret->tweak, (uint16_t)type, err) < 0) - return NULL; - - return g_steal_pointer(&ret); -} - -PvComponent *pv_component_new_file(PvComponentType type, const gchar *path, - GError **err) -{ - g_autoptr(CompFile) file = g_new0(CompFile, 1); - gsize size; - gint rc; - - g_assert(path != NULL); - - rc = file_size(path, &size, err); - if (rc < 0) - return NULL; - - file->path = g_strdup(path); - file->size = size; - return pv_component_new(type, size, DATA_FILE, (void **)&file, err); -} - -PvComponent *pv_component_new_buf(PvComponentType type, const PvBuffer *buf, - GError **err) -{ - g_assert(buf); - - g_autoptr(PvBuffer) dup_buf = pv_buffer_dup(buf, FALSE); - return pv_component_new(type, buf->size, DATA_BUFFER, (void **)&dup_buf, - err); -} - -void pv_component_free(PvComponent *component) -{ - if (!component) - return; - - switch ((PvComponentDataType)component->d_type) { - case DATA_BUFFER: - pv_buffer_clear(&component->buf); - break; - case DATA_FILE: - comp_file_free(component->file); - break; - } - - g_free(component); -} - -gint pv_component_type(const PvComponent *component) -{ - return component->type; -} - -const gchar *pv_component_name(const PvComponent *component) -{ - gint type = pv_component_type(component); - - switch ((PvComponentType)type) { - case PV_COMP_TYPE_KERNEL: - return "kernel"; - case PV_COMP_TYPE_INITRD: - return "ramdisk"; - case PV_COMP_TYPE_CMDLINE: - return "parmline"; - case PV_COMP_TYPE_STAGE3B: - return "stage3b"; - } - - g_assert_not_reached(); -} - -uint64_t pv_component_size(const PvComponent *component) -{ - switch ((PvComponentDataType)component->d_type) { - case DATA_BUFFER: - return component->buf->size; - case DATA_FILE: - return component->file->size; - } - - g_assert_not_reached(); -} - -uint64_t pv_component_get_src_addr(const PvComponent *component) -{ - return component->src_addr; -} - -uint64_t pv_component_get_orig_size(const PvComponent *component) -{ - return component->orig_size; -} - -uint64_t pv_component_get_tweak_prefix(const PvComponent *component) -{ - return GUINT64_FROM_BE(component->tweak.cmp_idx.data); -} - -gboolean pv_component_is_stage3b(const PvComponent *component) -{ - return pv_component_type(component) == PV_COMP_TYPE_STAGE3B; -} - -gint pv_component_align_and_encrypt(PvComponent *component, const gchar *tmp_path, - void *opaque, GError **err) -{ - struct cipher_parms *parms = opaque; - - switch ((PvComponentDataType)component->d_type) { - case DATA_BUFFER: { - g_autoptr(PvBuffer) enc_buf = NULL; - - if (!(IS_PAGE_ALIGNED(pv_component_size(component)))) { - g_autoptr(PvBuffer) new = NULL; - - /* create a page aligned copy */ - new = pv_buffer_dup(component->buf, TRUE); - pv_buffer_clear(&component->buf); - component->buf = g_steal_pointer(&new); - } - enc_buf = encrypt_buf(parms, component->buf, err); - if (!enc_buf) - return -1; - - pv_buffer_clear(&component->buf); - component->buf = g_steal_pointer(&enc_buf); - return 0; - } - case DATA_FILE: { - const gchar *comp_name = pv_component_name(component); - gchar *path_in = component->file->path; - g_autofree gchar *path_out = NULL; - gsize orig_size; - gsize prep_size; - - g_assert(path_in); - - path_out = g_build_filename(tmp_path, comp_name, NULL); - if (encrypt_file(parms, path_in, path_out, &orig_size, - &prep_size, err) < 0) - return -1; - - if (component->orig_size != orig_size) { - g_set_error(err, G_FILE_ERROR, PV_ERROR_INTERNAL, - _("File has changed during the preparation '%s'"), - path_out); - return -1; - } - - g_free(component->file->path); - component->file->size = prep_size; - component->file->path = g_steal_pointer(&path_out); - return 0; - } - } - - g_assert_not_reached(); -} - -/* Page align the size of the component */ -gint pv_component_align(PvComponent *component, const gchar *tmp_path, - void *opaque G_GNUC_UNUSED, GError **err) -{ - if (IS_PAGE_ALIGNED(pv_component_size(component))) - return 0; - - switch (component->d_type) { - case DATA_BUFFER: { - g_autoptr(PvBuffer) buf = NULL; - - buf = pv_buffer_dup(component->buf, TRUE); - pv_buffer_clear(&component->buf); - component->buf = g_steal_pointer(&buf); - return 0; - } break; - case DATA_FILE: { - const gchar *comp_name = pv_component_name(component); - g_autofree gchar *path_out = - g_build_filename(tmp_path, comp_name, NULL); - gchar *path_in = component->file->path; - gsize size_out; - - if (pad_file_right(path_out, path_in, &size_out, PAGE_SIZE, - err) < 0) - return -1; - - g_free(component->file->path); - component->file->path = g_steal_pointer(&path_out); - component->file->size = size_out; - return 0; - } break; - } - - g_assert_not_reached(); -} - -int64_t pv_component_update_ald(const PvComponent *comp, EVP_MD_CTX *ctx, - GError **err) -{ - uint64_t addr = pv_component_get_src_addr(comp); - uint64_t size = pv_component_size(comp); - uint64_t cur = addr; - int64_t nep = 0; - - g_assert(IS_PAGE_ALIGNED(size) && size != 0); - - do { - uint64_t cur_be = GUINT64_TO_BE(cur); - - if (EVP_DigestUpdate(ctx, &cur_be, sizeof(cur_be)) != 1) { - g_set_error(err, PV_CRYPTO_ERROR, - PV_CRYPTO_ERROR_INTERNAL, - _("EVP_DigestUpdate failed")); - return -1; - } - - cur += PAGE_SIZE; - nep++; - } while (cur < addr + size); - - return nep; -} - -int64_t pv_component_update_pld(const PvComponent *comp, EVP_MD_CTX *ctx, - GError **err) -{ - uint64_t size = pv_component_size(comp); - int64_t nep = 0; - - g_assert(IS_PAGE_ALIGNED(size) && size != 0); - - switch (comp->d_type) { - case DATA_BUFFER: { - const PvBuffer *buf = comp->buf; - - g_assert(buf->size <= INT64_MAX); - g_assert(buf->size == size); - - if (EVP_DigestUpdate(ctx, buf->data, buf->size) != 1) { - g_set_error(err, PV_CRYPTO_ERROR, - PV_CRYPTO_ERROR_INTERNAL, - _("EVP_DigestUpdate failed")); - return -1; - } - - nep = (int64_t)(buf->size / PAGE_SIZE); - break; - } - case DATA_FILE: { - const gchar *in_path = comp->file->path; - guchar in_buf[PAGE_SIZE]; - gsize num_bytes_read_total = 0; - gsize num_bytes_read = 0; - FILE *f_in; - - f_in = file_open(in_path, "rb", err); - if (!f_in) - return -1; - - do { - /* Read data in blocks. Update the digest - * context each read. - */ - if (file_read(f_in, in_buf, sizeof(*in_buf), - sizeof(in_buf), &num_bytes_read, - err) < 0) { - fclose(f_in); - return -1; - } - num_bytes_read_total += num_bytes_read; - - if (EVP_DigestUpdate(ctx, in_buf, sizeof(in_buf)) != 1) { - g_set_error(err, PV_CRYPTO_ERROR, - PV_CRYPTO_ERROR_INTERNAL, - _("EVP_DigestUpdate failed")); - fclose(f_in); - return -1; - } - - nep++; - } while (num_bytes_read_total < pv_component_size(comp) && - num_bytes_read != 0); - - if (num_bytes_read_total != pv_component_size(comp)) { - g_set_error(err, G_FILE_ERROR, PV_ERROR_INTERNAL, - _("'%s' has changed during the preparation"), - in_path); - fclose(f_in); - return -1; - } - fclose(f_in); - break; - } - default: - g_assert_not_reached(); - } - - return nep; -} - -int64_t pv_component_update_tld(const PvComponent *comp, EVP_MD_CTX *ctx, - GError **err) -{ - uint64_t size = pv_component_size(comp); - const union tweak *tweak = &comp->tweak; - g_autoptr(BIGNUM) tweak_num = NULL; - int64_t nep = 0; - - g_assert(IS_PAGE_ALIGNED(size) && size != 0); - - tweak_num = BN_bin2bn(tweak->data, sizeof(tweak->data), NULL); - if (!tweak_num) { - g_set_error(err, PV_CRYPTO_ERROR, - PV_CRYPTO_ERROR_INTERNAL, - _("BN_bin2bn failed")); - return -1; - } - - for (uint64_t cur = 0; cur < size; cur += PAGE_SIZE) { - guchar tmp[sizeof(tweak->data)] = { 0 }; - - g_assert(BN_num_bytes(tweak_num) >= 0); - g_assert(sizeof(tmp) - (guint)BN_num_bytes(tweak_num) > 0); - - if (BN_bn2binpad(tweak_num, tmp, sizeof(tmp)) < 0) { - g_set_error(err, PV_CRYPTO_ERROR, - PV_CRYPTO_ERROR_INTERNAL, - _("BN_bn2binpad failed")); - return -1; - } - - if (EVP_DigestUpdate(ctx, tmp, sizeof(tmp)) != 1) { - g_set_error(err, PV_CRYPTO_ERROR, - PV_CRYPTO_ERROR_INTERNAL, - _("EVP_DigestUpdate failed")); - return -1; - } - - /* calculate new tweak value */ - if (BN_add_word(tweak_num, PAGE_SIZE) != 1) { - g_set_error(err, PV_CRYPTO_ERROR, - PV_CRYPTO_ERROR_INTERNAL, - _("BN_add_word failed")); - return -1; - } - - nep++; - } - - return nep; -} - -gint pv_component_write(const PvComponent *component, FILE *f, GError **err) -{ - uint64_t offset = pv_component_get_src_addr(component); - - g_assert(f); - - switch (component->d_type) { - case DATA_BUFFER: { - const PvBuffer *buf = component->buf; - - if (seek_and_write_buffer(f, buf, offset, err) < 0) - return -1; - - return 0; - } - case DATA_FILE: { - const CompFile *file = component->file; - - if (seek_and_write_file(f, file, offset, err) < 0) - return -1; - - return 0; - } - } - - g_assert_not_reached(); -} diff --git a/genprotimg/src/pv/pv_comp.h b/genprotimg/src/pv/pv_comp.h deleted file mode 100644 index a4ffae81..00000000 --- a/genprotimg/src/pv/pv_comp.h +++ /dev/null @@ -1,78 +0,0 @@ -/* - * PV component related definitions and functions - * - * Copyright IBM Corp. 2020 - * - * s390-tools is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See LICENSE for details. - */ - -#ifndef PV_COMP_H -#define PV_COMP_H - -#include -#include -#include - -#include "utils/crypto.h" - -/* The order of this enum also implicitly defines the order of the - * components within the PV image! - */ -typedef enum { - PV_COMP_TYPE_KERNEL = 0, - PV_COMP_TYPE_CMDLINE = 1, - PV_COMP_TYPE_INITRD = 2, - PV_COMP_TYPE_STAGE3B = 3, -} PvComponentType; - -typedef enum { - DATA_FILE = 0, - DATA_BUFFER, -} PvComponentDataType; - -typedef struct comp_file { - gchar *path; - gsize size; -} CompFile; - -typedef struct { - gint type; /* PvComponentType */ - gint d_type; /* PvComponentDataType */ - union { - struct comp_file *file; - PvBuffer *buf; - void *data; - }; - uint64_t src_addr; - uint64_t orig_size; - union tweak tweak; /* used for the AES XTS encryption */ -} PvComponent; - -PvComponent *pv_component_new_file(PvComponentType type, const gchar *path, - GError **err); -PvComponent *pv_component_new_buf(PvComponentType type, const PvBuffer *buf, - GError **err); -void pv_component_free(PvComponent *component); -gint pv_component_type(const PvComponent *component); -const gchar *pv_component_name(const PvComponent *component); -uint64_t pv_component_size(const PvComponent *component); -uint64_t pv_component_get_src_addr(const PvComponent *component); -uint64_t pv_component_get_orig_size(const PvComponent *component); -uint64_t pv_component_get_tweak_prefix(const PvComponent *component); -gboolean pv_component_is_stage3b(const PvComponent *component); -gint pv_component_align_and_encrypt(PvComponent *component, const gchar *tmp_path, - void *opaque, GError **err); -gint pv_component_align(PvComponent *component, const gchar *tmp_path, - void *opaque G_GNUC_UNUSED, GError **err); -int64_t pv_component_update_pld(const PvComponent *comp, EVP_MD_CTX *ctx, - GError **err); -int64_t pv_component_update_ald(const PvComponent *comp, EVP_MD_CTX *ctx, - GError **err); -int64_t pv_component_update_tld(const PvComponent *comp, EVP_MD_CTX *ctx, - GError **err); -gint pv_component_write(const PvComponent *component, FILE *f, GError **err); - -WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(PvComponent, pv_component_free) - -#endif diff --git a/genprotimg/src/pv/pv_comps.c b/genprotimg/src/pv/pv_comps.c deleted file mode 100644 index 310ca4be..00000000 --- a/genprotimg/src/pv/pv_comps.c +++ /dev/null @@ -1,252 +0,0 @@ -/* - * PV components related definitions and functions - * - * Copyright IBM Corp. 2020 - * - * s390-tools is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See LICENSE for details. - */ - -#include -#include -#include -#include - -#include "boot/psw.h" -#include "boot/stage3b.h" -#include "common.h" -#include "utils/align.h" -#include "utils/crypto.h" - -#include "pv_comp.h" -#include "pv_comps.h" -#include "pv_error.h" -#include "pv_stage3.h" - -struct _pv_img_comps { - gboolean finalized; - uint64_t next_src; - uint64_t nep; - EVP_MD_CTX *ald; /* context used for the hash of the addresses */ - EVP_MD_CTX *pld; /* context used for the hash of the pages content */ - EVP_MD_CTX *tld; /* context used for the hash of the tweaks */ - GSList *comps; /* elements sorted by component type */ -}; - -void pv_img_comps_free(PvImgComps *comps) -{ - if (!comps) - return; - - EVP_MD_CTX_free(comps->ald); - EVP_MD_CTX_free(comps->pld); - EVP_MD_CTX_free(comps->tld); - g_slist_free_full(comps->comps, (GDestroyNotify)pv_component_free); - g_free(comps); -} - -PvImgComps *pv_img_comps_new(const EVP_MD *ald_md, const EVP_MD *pld_md, - const EVP_MD *tld_md, GError **err) -{ - g_autoptr(PvImgComps) ret = g_new0(PvImgComps, 1); - - ret->ald = digest_ctx_new(ald_md, err); - if (!ret->ald) - return NULL; - - ret->pld = digest_ctx_new(pld_md, err); - if (!ret->pld) - return NULL; - - ret->tld = digest_ctx_new(tld_md, err); - if (!ret->tld) - return NULL; - - return g_steal_pointer(&ret); -} - -guint pv_img_comps_length(const PvImgComps *comps) -{ - return g_slist_length(comps->comps); -} - -/* Update hashes and nep */ -/* Returns 0 in case of success and -1 in case of a failure */ -static gint pv_img_comps_hash_comp(PvImgComps *comps, const PvComponent *comp, - GError **err) -{ - int64_t nep_1 = 0; - int64_t nep_2 = 0; - int64_t nep_3 = 0; - - /* update pld */ - nep_1 = pv_component_update_pld(comp, comps->pld, err); - if (nep_1 < 0) - return -1; - - /* update ald */ - nep_2 = pv_component_update_ald(comp, comps->ald, err); - if (nep_2 < 0) - return -1; - - /* update tld */ - nep_3 = pv_component_update_tld(comp, comps->tld, err); - if (nep_3 < 0) - return -1; - - g_assert(nep_1 == nep_2); - g_assert(nep_2 == nep_3); - - /* update comps->nep */ - g_assert_true(g_uint64_checked_add(&comps->nep, comps->nep, - (uint64_t)nep_1)); - return 0; -} - -gint pv_img_comps_add_component(PvImgComps *comps, PvComponent **comp, - GError **err) -{ - g_assert(comp); - g_assert(*comp); - g_assert(comps); - g_assert(IS_PAGE_ALIGNED(comps->next_src)); - - uint64_t src_addr = comps->next_src; - uint64_t src_size = pv_component_size(*comp) - ? PAGE_ALIGN(pv_component_size(*comp)) - : PAGE_SIZE; - - if (comps->finalized) { - g_set_error(err, PV_COMPONENT_ERROR, PV_COMPONENT_ERROR_FINALIZED, - _("Failed to add component, image is already finalized")); - return -1; - } - - /* set the address of the component in the memory layout */ - (*comp)->src_addr = src_addr; - - g_info("%12s:\t0x%012lx (%12ld / %12ld Bytes)", - pv_component_name(*comp), pv_component_get_src_addr(*comp), - pv_component_size(*comp), pv_component_get_orig_size(*comp)); - - /* append the component and pass the responsibility of @comp - * to @comps - */ - comps->comps = g_slist_append(comps->comps, g_steal_pointer(comp)); - comps->next_src += src_size; - - g_assert(IS_PAGE_ALIGNED(comps->next_src)); - g_assert(!*comp); - return 0; -} - -struct stage3b_args *pv_img_comps_get_stage3b_args(const PvImgComps *comps, - struct psw_t *psw) -{ - g_autofree struct stage3b_args *ret = g_new0(struct stage3b_args, 1); - - for (GSList *iterator = comps->comps; iterator; iterator = iterator->next) { - const PvComponent *img_comp = iterator->data; - uint64_t src_addr, dst_size; - - g_assert(img_comp); - - src_addr = pv_component_get_src_addr(img_comp); - dst_size = pv_component_get_orig_size(img_comp); - - g_assert(dst_size <= pv_component_size(img_comp)); - - switch ((PvComponentType)pv_component_type(img_comp)) { - case PV_COMP_TYPE_KERNEL: - memblob_init(&ret->kernel, src_addr, dst_size); - break; - case PV_COMP_TYPE_CMDLINE: - memblob_init(&ret->cmdline, src_addr, dst_size); - break; - case PV_COMP_TYPE_INITRD: - memblob_init(&ret->initrd, src_addr, dst_size); - break; - case PV_COMP_TYPE_STAGE3B: - /* nothing needs to be done since it is the - * stage3b itself - */ - break; - default: - g_assert_not_reached(); - break; - } - } - - /* for `stage3b_args` big-endian format must be used */ - ret->psw.mask = GUINT64_TO_BE(psw->mask); - ret->psw.addr = GUINT64_TO_BE(psw->addr); - return g_steal_pointer(&ret); -} - -gint pv_img_comps_set_offset(PvImgComps *comps, gsize offset, GError **err) -{ - g_assert(IS_PAGE_ALIGNED(comps->next_src)); - - if (!IS_PAGE_ALIGNED(offset)) { - g_set_error(err, PV_IMAGE_ERROR, PV_IMAGE_ERROR_OFFSET, - _("Offset must be page aligned")); - return -1; - } - - if (pv_img_comps_length(comps) > 0) { - g_set_error(err, PV_IMAGE_ERROR, PV_IMAGE_ERROR_OFFSET, - _("Offset cannot be changed after a component was added")); - return -1; - } - - comps->next_src += offset; - - g_assert(IS_PAGE_ALIGNED(comps->next_src)); - return 0; -} - -GSList *pv_img_comps_get_comps(const PvImgComps *comps) -{ - return comps->comps; -} - -gint pv_img_comps_finalize(PvImgComps *comps, PvBuffer **pld_digest, - PvBuffer **ald_digest, PvBuffer **tld_digest, - uint64_t *nep, GError **err) -{ - g_autoptr(PvBuffer) tmp_pld_digest = NULL; - g_autoptr(PvBuffer) tmp_ald_digest = NULL; - g_autoptr(PvBuffer) tmp_tld_digest = NULL; - - comps->finalized = TRUE; - for (GSList *iterator = comps->comps; iterator; iterator = iterator->next) { - const PvComponent *comp = iterator->data; - - /* update hashes and nep */ - if (pv_img_comps_hash_comp(comps, comp, err) < 0) - return -1; - } - - tmp_pld_digest = digest_ctx_finalize(comps->pld, err); - if (!tmp_pld_digest) - return -1; - - tmp_ald_digest = digest_ctx_finalize(comps->ald, err); - if (!tmp_ald_digest) - return -1; - - tmp_tld_digest = digest_ctx_finalize(comps->tld, err); - if (!tmp_tld_digest) - return -1; - - *pld_digest = g_steal_pointer(&tmp_pld_digest); - *ald_digest = g_steal_pointer(&tmp_ald_digest); - *tld_digest = g_steal_pointer(&tmp_tld_digest); - *nep = comps->nep; - return 0; -} - -PvComponent *pv_img_comps_get_nth_comp(PvImgComps *comps, guint n) -{ - return g_slist_nth_data(comps->comps, n); -} diff --git a/genprotimg/src/pv/pv_comps.h b/genprotimg/src/pv/pv_comps.h deleted file mode 100644 index a041b6e3..00000000 --- a/genprotimg/src/pv/pv_comps.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * PV components related definitions and functions - * - * Copyright IBM Corp. 2020 - * - * s390-tools is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See LICENSE for details. - */ - -#ifndef PV_COMPS_H -#define PV_COMPS_H - -#include -#include -#include - -#include "boot/psw.h" -#include "boot/stage3b.h" -#include "utils/buffer.h" - -#include "pv_comp.h" - -typedef struct _pv_img_comps PvImgComps; - -PvImgComps *pv_img_comps_new(const EVP_MD *ald_md, const EVP_MD *pld_md, - const EVP_MD *tld_md, GError **err); -guint pv_img_comps_length(const PvImgComps *comps); -GSList *pv_img_comps_get_comps(const PvImgComps *comps); -struct stage3b_args *pv_img_comps_get_stage3b_args(const PvImgComps *comps, - struct psw_t *psw); -gint pv_img_comps_add_component(PvImgComps *comps, PvComponent **comp, - GError **err); -PvComponent *pv_img_comps_get_nth_comp(PvImgComps *comps, guint n); -gint pv_img_comps_set_offset(PvImgComps *comps, gsize offset, GError **err); -gint pv_img_comps_finalize(PvImgComps *comps, PvBuffer **pld_digest, - PvBuffer **ald_digest, PvBuffer **tld_digest, - uint64_t *nep, GError **err); -void pv_img_comps_free(PvImgComps *comps); - -WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(PvImgComps, pv_img_comps_free) - -#endif diff --git a/genprotimg/src/pv/pv_error.c b/genprotimg/src/pv/pv_error.c deleted file mode 100644 index 9d6f264c..00000000 --- a/genprotimg/src/pv/pv_error.c +++ /dev/null @@ -1,37 +0,0 @@ -/* - * PV error related functions - * - * Copyright IBM Corp. 2020 - * - * s390-tools is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See LICENSE for details. - */ - -#include - -#include "pv_error.h" - -GQuark pv_error_quark(void) -{ - return g_quark_from_static_string("pv-error-quark"); -} - -GQuark pv_crypto_error_quark(void) -{ - return g_quark_from_static_string("pv-crypto-error-quark"); -} - -GQuark pv_component_error_quark(void) -{ - return g_quark_from_static_string("pv-component-error-quark"); -} - -GQuark pv_image_error_quark(void) -{ - return g_quark_from_static_string("pv-image-error-quark"); -} - -GQuark pv_parse_error_quark(void) -{ - return g_quark_from_static_string("pv-parse-error-quark"); -} diff --git a/genprotimg/src/pv/pv_error.h b/genprotimg/src/pv/pv_error.h deleted file mode 100644 index abe47aee..00000000 --- a/genprotimg/src/pv/pv_error.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * PV error related definitions and functions - * - * Copyright IBM Corp. 2020 - * - * s390-tools is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See LICENSE for details. - */ - -#ifndef PV_ERROR_H -#define PV_ERROR_H - -#include - -GQuark pv_error_quark(void); -GQuark pv_parse_error_quark(void); -GQuark pv_component_error_quark(void); -GQuark pv_crypto_error_quark(void); -GQuark pv_image_error_quark(void); - -#define PV_ERROR pv_error_quark() -#define PV_PARSE_ERROR pv_parse_error_quark() -#define PV_CRYPTO_ERROR pv_crypto_error_quark() -#define PV_COMPONENT_ERROR pv_component_error_quark() -#define PV_IMAGE_ERROR pv_image_error_quark() - -typedef enum { - PV_ERROR_IPIB_SIZE, - PV_ERROR_PV_HDR_SIZE, - PV_ERROR_INTERNAL, - PV_ERROR_CURL_INIT_FAILED, - PV_ERROR_DOWNLOAD_FAILED, -} PvErrors; - -typedef enum { - PV_PARSE_ERROR_OK = 0, - PV_PARSE_ERROR_SYNTAX, - PR_PARSE_ERROR_INVALID_ARGUMENT, - PR_PARSE_ERROR_MISSING_ARGUMENT, -} PvParseErrors; - -typedef enum { - PV_COMPONENT_ERROR_UNALIGNED, - PV_COMPONENT_ERROR_FINALIZED, -} PvComponentErrors; - -typedef enum { - PV_IMAGE_ERROR_OFFSET, - PV_IMAGE_ERROR_FINALIZED, -} PvImageErrors; - -typedef enum { - PV_CRYPTO_ERROR_VERIFICATION, - PV_CRYPTO_ERROR_INIT, - PV_CRYPTO_ERROR_READ_CERTIFICATE, - PV_CRYPTO_ERROR_INTERNAL, - PV_CRYPTO_ERROR_DERIVE, - PV_CRYPTO_ERROR_KEYGENERATION, - PV_CRYPTO_ERROR_RANDOMIZATION, - PV_CRYPTO_ERROR_INVALID_PARM, - PV_CRYPTO_ERROR_INVALID_KEY_SIZE, - PV_CRYPTO_ERROR_INVALID_VALIDITY_PERIOD, - PV_CRYPTO_ERROR_EXPIRED, - PV_CRYPTO_ERROR_NOT_VALID_YET, - PV_CRYPTO_ERROR_LOAD_CRL, - PV_CRYPTO_ERROR_NO_PUBLIC_KEY, - PV_CRYPTO_ERROR_INVALID_SIGNATURE_ALGORITHM, - PV_CRYPTO_ERROR_SIGNATURE_ALGORITHM_MISMATCH, - PV_CRYPTO_ERROR_INVALID_URI, - PV_CRYPTO_ERROR_CRL_DOWNLOAD_FAILED, - PV_CRYPTO_ERROR_CERT_SIGNATURE_INVALID, - PV_CRYPTO_ERROR_CRL_SIGNATURE_INVALID, - PV_CRYPTO_ERROR_CERT_SUBJECT_ISSUER_MISMATCH, - PV_CRYPTO_ERROR_CRL_SUBJECT_ISSUER_MISMATCH, - PV_CRYPTO_ERROR_NO_IBM_Z_SIGNING_KEY, - PV_CRYPTO_ERROR_MALFORMED_CERTIFICATE, - PV_CRYPTO_ERROR_NO_CRL, - PV_CRYPTO_ERROR_LOAD_ROOT_CA, - PV_CRYPTO_ERROR_LOAD_DEFAULT_CA, - PV_CRYPTO_ERROR_MALFORMED_ROOT_CA, - PV_CRYPTO_ERROR_WRONG_CA_USED, - PV_CRYPTO_ERROR_SKID_AKID_MISMATCH, - PV_CRYPTO_ERROR_NO_ISSUER_IBM_Z_FOUND, - PV_CRYPTO_ERROR_FAILED_DOWNLOAD_CRL, - PV_CRYPTO_ERROR_NO_CRLDP, - PV_CRYPTO_ERROR_CERT_REVOKED, -} PvCryptoErrors; - -#endif diff --git a/genprotimg/src/pv/pv_hdr.c b/genprotimg/src/pv/pv_hdr.c deleted file mode 100644 index 713c38bc..00000000 --- a/genprotimg/src/pv/pv_hdr.c +++ /dev/null @@ -1,293 +0,0 @@ -/* - * PV header related functions - * - * Copyright IBM Corp. 2020 - * - * s390-tools is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See LICENSE for details. - */ - -#include -#include -#include -#include -#include -#include - -#include "boot/psw.h" -#include "include/pv_crypto_def.h" -#include "utils/buffer.h" -#include "utils/crypto.h" - -#include "pv_comp.h" -#include "pv_hdr.h" -#include "pv_image.h" - -void pv_hdr_free(PvHdr *hdr) -{ - if (!hdr) - return; - - g_free(hdr->optional_items); - g_free(hdr->encrypted); - g_free(hdr->slots); - g_free(hdr); -} - -uint32_t pv_hdr_size(const PvHdr *hdr) -{ - return GUINT32_FROM_BE(hdr->head.phs); -} - -gboolean pv_hdr_uses_encryption(const PvHdr *hdr) -{ - return !(GUINT64_FROM_BE(hdr->head.pcf) & PV_PCF_NO_DECRYPTION); -} - -uint64_t pv_hdr_enc_size(const PvHdr *hdr) -{ - return GUINT64_FROM_BE(hdr->head.sea); -} - -uint32_t pv_hdr_enc_size_casted(const PvHdr *hdr) -{ - uint64_t size = pv_hdr_enc_size(hdr); - - if (size > UINT32_MAX) - g_abort(); - - return (uint32_t)size; -} - -static guint pv_hdr_tag_size(const PvHdr *hdr) -{ - return sizeof(hdr->tag); -} - -uint32_t pv_hdr_aad_size(const PvHdr *hdr) -{ - return pv_hdr_size(hdr) - pv_hdr_enc_size_casted(hdr) - - pv_hdr_tag_size(hdr); -} - -uint64_t pv_hdr_get_nks(const PvHdr *hdr) -{ - return GUINT64_FROM_BE(hdr->head.nks); -} - -/* In-place modification of ``buf`` */ -static gint pv_hdr_encrypt(const PvHdr *hdr, const PvImage *img, PvBuffer *buf, - GError **err) -{ - uint32_t hdr_len = pv_hdr_size(hdr); - uint32_t aad_len = pv_hdr_aad_size(hdr); - guint tag_len = pv_hdr_tag_size(hdr); - uint32_t enc_len = pv_hdr_enc_size_casted(hdr); - const PvBuffer aad_part = { .data = buf->data, .size = aad_len }; - PvBuffer enc_part = { .data = (uint8_t *)buf->data + aad_len, - .size = enc_len }; - PvBuffer tag_part = { .data = (uint8_t *)buf->data + hdr_len - tag_len, - .size = tag_len }; - struct cipher_parms parms; - int64_t c_len; - - g_assert(aad_part.size + enc_part.size + tag_part.size == buf->size); - g_assert(img->cust_root_key->size <= INT_MAX); - g_assert(img->gcm_iv->size <= INT_MAX); - g_assert(EVP_CIPHER_key_length(img->gcm_cipher) == - (int)img->cust_root_key->size); - g_assert(EVP_CIPHER_iv_length(img->gcm_cipher) == (int)img->gcm_iv->size); - - parms.key = img->cust_root_key; - parms.iv_or_tweak = img->gcm_iv; - parms.cipher = img->gcm_cipher; - - /* in-place encryption */ - c_len = gcm_encrypt(&enc_part, &aad_part, &parms, &enc_part, &tag_part, err); - if (c_len < 0) - return -1; - - g_assert(c_len == enc_len); - return 0; -} - -/* Initializes the unencrypted, but integrity protected part of the PV - * header - */ -static gint pv_hdr_aad_init(PvHdr *hdr, const PvImage *img, GError **err) -{ - g_autofree union ecdh_pub_key *cust_pub_key = NULL; - struct pv_hdr_key_slot *hdr_slot = hdr->slots; - struct pv_hdr_head *head = &hdr->head; - g_autoptr(PvBuffer) pld = NULL; - g_autoptr(PvBuffer) ald = NULL; - g_autoptr(PvBuffer) tld = NULL; - uint64_t nep = 0; - - g_assert(sizeof(head->iv) == img->gcm_iv->size); - g_assert(sizeof(head->cust_pub_key) == sizeof(*cust_pub_key)); - - cust_pub_key = evp_pkey_to_ecdh_pub_key(img->cust_pub_priv_key, err); - if (!cust_pub_key) - return -1; - - head->magic = GUINT64_TO_BE(PV_MAGIC_NUMBER); - head->version = GUINT32_TO_BE(PV_VERSION_1); - /* ``phs`` is already set so we can skip it here */ - memcpy(head->iv, img->gcm_iv->data, sizeof(head->iv)); - /* ``nks`` is already set so we can skip it here */ - /* ``sea`` is already set so we can skip it here */ - head->pcf = GUINT64_TO_BE(img->pcf); - memcpy(head->cust_pub_key.data, cust_pub_key, - sizeof(head->cust_pub_key)); - - if (pv_img_calc_pld_ald_tld_nep(img, &pld, &ald, &tld, &nep, err) < 0) - return -1; - - g_assert(sizeof(head->pld) == pld->size); - g_assert(sizeof(head->ald) == ald->size); - g_assert(sizeof(head->tld) == tld->size); - - head->nep = GUINT64_TO_BE(nep); - memcpy(head->pld, pld->data, sizeof(head->pld)); - memcpy(head->ald, ald->data, sizeof(head->ald)); - memcpy(head->tld, tld->data, sizeof(head->tld)); - - /* set the key slots */ - for (GSList *iterator = img->key_slots; iterator; iterator = iterator->next) { - const PvHdrKeySlot *slot = iterator->data; - - g_assert(slot); - - /* the memory for the slots is pre-allocated so we - * have not to allocate and since PvHdrKeySlot is - * stored in the big-edian format we can simply use - * memcpy. - */ - memcpy(hdr_slot++, slot, sizeof(*slot)); - } - - return 0; -} - -/* Initializes the encrypted and also integrity protected part of the - * PV header - */ -static gint pv_hdr_enc_init(PvHdr *hdr, const PvImage *img, GError **err) -{ - struct pv_hdr_encrypted *enc = hdr->encrypted; - const PvComponent *stage3b; - struct psw_t psw; - - g_assert(sizeof(enc->img_enc_key_1) + sizeof(enc->img_enc_key_2) == - EVP_CIPHER_key_length(img->xts_cipher)); - g_assert(sizeof(enc->cust_comm_key) == img->cust_comm_key->size); - g_assert(img->xts_key->size == - (guint)EVP_CIPHER_key_length(img->xts_cipher)); - - stage3b = pv_img_get_stage3b_comp(img, err); - if (!stage3b) - return -1; - - memcpy(enc->cust_comm_key, img->cust_comm_key->data, - sizeof(enc->cust_comm_key)); - memcpy(enc->img_enc_key_1, img->xts_key->data, - sizeof(enc->img_enc_key_1)); - memcpy(enc->img_enc_key_2, - (uint8_t *)img->xts_key->data + sizeof(enc->img_enc_key_1), - sizeof(enc->img_enc_key_2)); - - /* Setup program check handler */ - psw.mask = GUINT64_TO_BE(DEFAULT_INITIAL_PSW_MASK); - psw.addr = GUINT64_TO_BE(pv_component_get_src_addr(stage3b)); - enc->psw = psw; - enc->scf = GUINT64_TO_BE(img->scf); - enc->noi = GUINT32_TO_BE(g_slist_length(img->optional_items)); - - /* set the optional items */ - for (GSList *iterator = img->optional_items; iterator; - iterator = iterator->next) { - const struct pv_hdr_opt_item *item = iterator->data; - - g_assert(item); - - /* not supported in the first version */ - g_assert_not_reached(); - } - - return 0; -} - -PvHdr *pv_hdr_new(const PvImage *img, GError **err) -{ - uint32_t noi = g_slist_length(img->optional_items); - uint32_t hdr_size = pv_img_get_pv_hdr_size(img); - gsize nks = g_slist_length(img->key_slots); - uint32_t sea = pv_img_get_enc_size(img); - g_autoptr(PvHdr) ret = NULL; - - g_assert(nks > 0); - /* must be a multiple of AES block size */ - g_assert(sea % AES_BLOCK_SIZE == 0); - g_assert(sea >= sizeof(struct pv_hdr_encrypted)); - - ret = g_new0(PvHdr, 1); - ret->slots = g_new0(struct pv_hdr_key_slot, nks); - ret->head.phs = GUINT32_TO_BE(hdr_size); - ret->head.nks = GUINT64_TO_BE(nks); - ret->head.sea = GUINT64_TO_BE(sea); - - ret->encrypted = g_new0(struct pv_hdr_encrypted, 1); - ret->optional_items = g_malloc0(sea - sizeof(struct pv_hdr_encrypted)); - ret->encrypted->noi = GUINT32_TO_BE(noi); - - if (pv_hdr_aad_init(ret, img, err) < 0) - return NULL; - - if (pv_hdr_enc_init(ret, img, err) < 0) - return NULL; - - return g_steal_pointer(&ret); -} - -static void pv_hdr_memcpy(const PvHdr *hdr, const PvBuffer *dst) -{ - uint64_t nks = pv_hdr_get_nks(hdr); - uint8_t *data; - - g_assert(dst->size == pv_hdr_size(hdr)); - g_assert(pv_hdr_enc_size_casted(hdr) >= sizeof(*hdr->encrypted)); - - data = memcpy(dst->data, &hdr->head, sizeof(hdr->head)); - data = memcpy(data + sizeof(hdr->head), hdr->slots, - sizeof(struct pv_hdr_key_slot) * nks); - data = memcpy(data + sizeof(struct pv_hdr_key_slot) * nks, - hdr->encrypted, sizeof(*hdr->encrypted)); - if (pv_hdr_enc_size_casted(hdr) - sizeof(*hdr->encrypted) > 0) { - (void)memcpy(data + sizeof(*hdr->encrypted), - hdr->optional_items, - pv_hdr_enc_size_casted(hdr) - sizeof(*hdr->encrypted)); - } -} - -PvBuffer *pv_hdr_serialize(const PvHdr *hdr, const PvImage *img, - enum PvCryptoMode mode, GError **err) -{ - uint32_t hdr_size = pv_hdr_size(hdr); - g_autoptr(PvBuffer) ret = NULL; - - ret = pv_buffer_alloc(hdr_size); - pv_hdr_memcpy(hdr, ret); - - if (mode == PV_ENCRYPT) { - /* The buffer @ret is modified in-place */ - if (pv_hdr_encrypt(hdr, img, ret, err) < 0) - return NULL; - } else { - /* Simply copy the tag */ - memcpy((uint8_t *)ret->data + hdr_size - pv_hdr_tag_size(hdr), - hdr->tag, pv_hdr_tag_size(hdr)); - } - - return g_steal_pointer(&ret); -} diff --git a/genprotimg/src/pv/pv_hdr.h b/genprotimg/src/pv/pv_hdr.h deleted file mode 100644 index 7287dbd4..00000000 --- a/genprotimg/src/pv/pv_hdr.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * PV header related functions - * - * Copyright IBM Corp. 2020 - * - * s390-tools is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See LICENSE for details. - */ - -#ifndef PV_HDR_H -#define PV_HDR_H - -#include -#include - -#include "include/pv_hdr_def.h" -#include "utils/crypto.h" -#include "utils/buffer.h" - -#include "pv_image.h" - -PvHdr *pv_hdr_new(const PvImage *img, GError **err); -void pv_hdr_free(PvHdr *hdr); -G_GNUC_UNUSED gboolean pv_hdr_uses_encryption(const PvHdr *hdr); -PvBuffer *pv_hdr_serialize(const PvHdr *hdr, const PvImage *img, - enum PvCryptoMode mode, GError **err); -uint32_t pv_hdr_size(const PvHdr *hdr); -uint32_t pv_hdr_aad_size(const PvHdr *hdr); -uint64_t pv_hdr_enc_size(const PvHdr *hdr); -uint32_t pv_hdr_enc_size_casted(const PvHdr *hdr); -uint64_t pv_hdr_get_nks(const PvHdr *hdr); - -WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(PvHdr, pv_hdr_free) - -#endif diff --git a/genprotimg/src/pv/pv_image.c b/genprotimg/src/pv/pv_image.c deleted file mode 100644 index 94ebb620..00000000 --- a/genprotimg/src/pv/pv_image.c +++ /dev/null @@ -1,1030 +0,0 @@ -/* - * PV image related definitions and functions - * - * Copyright IBM Corp. 2020 - * - * s390-tools is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See LICENSE for details. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "boot/psw.h" -#include "boot/stage3a.h" -#include "common.h" -#include "include/pv_crypto_def.h" -#include "include/pv_hdr_def.h" -#include "utils/align.h" -#include "utils/crypto.h" -#include "utils/file_utils.h" - -#include "pv_args.h" -#include "pv_comps.h" -#include "pv_error.h" -#include "pv_hdr.h" -#include "pv_image.h" -#include "pv_ipib.h" -#include "pv_opt_item.h" -#include "pv_stage3.h" - -const PvComponent *pv_img_get_stage3b_comp(const PvImage *img, GError **err) -{ - const PvComponent *comp; - - g_return_val_if_fail(pv_img_comps_length(img->comps) >= 1, NULL); - - comp = pv_img_comps_get_nth_comp(img->comps, - pv_img_comps_length(img->comps) - 1); - if (!pv_component_is_stage3b(comp)) { - g_set_error(err, PV_ERROR, PV_ERROR_INTERNAL, - _("Failed to get 'stage3b' component")); - return NULL; - } - return comp; -} - -typedef gint (*prepare_func)(PvComponent *obj, const gchar *tmp_path, - void *opaque, GError **err); - -static gint pv_img_prepare_component(const PvImage *img, PvComponent *comp, - GError **err) -{ - struct cipher_parms parms = { 0 }; - g_autoptr(PvBuffer) tweak = NULL; - prepare_func func = NULL; - void *opaque = NULL; - gint rc; - - if (img->pcf & PV_PCF_NO_DECRYPTION) { - /* we only need to align the components */ - func = pv_component_align; - opaque = NULL; - } else { - const EVP_CIPHER *cipher = img->xts_cipher; - - g_assert_cmpint((int)img->xts_key->size, ==, - EVP_CIPHER_key_length(cipher)); - g_assert_cmpint((int)PAGE_SIZE % EVP_CIPHER_block_size(cipher), - ==, 0); - g_assert_cmpint(sizeof(comp->tweak), ==, - EVP_CIPHER_iv_length(cipher)); - g_assert(img->xts_key->size <= UINT_MAX); - - tweak = pv_buffer_alloc(sizeof(comp->tweak.data)); - memcpy(tweak->data, comp->tweak.data, tweak->size); - func = pv_component_align_and_encrypt; - parms.cipher = cipher; - parms.key = img->xts_key; - parms.iv_or_tweak = tweak; - - opaque = &parms; - } - - rc = (*func)(comp, img->tmp_dir, opaque, err); - if (rc < 0) - return -1; - - return 0; -} - -static PvBuffer *pv_img_read_key(const gchar *path, guint key_size, - GError **err) -{ - g_autoptr(PvBuffer) tmp_ret = NULL; - PvBuffer *ret = NULL; - gsize bytes_read; - FILE *f = NULL; - gsize size; - - if (file_size(path, &size, err) != 0) - return NULL; - - if (size - key_size != 0) { - g_set_error(err, PV_ERROR, PV_CRYPTO_ERROR_INVALID_KEY_SIZE, - _("Wrong file size '%s': read %zd, expected %u"), path, size, - key_size); - return NULL; - } - - f = file_open(path, "rb", err); - if (!f) - return NULL; - - tmp_ret = pv_buffer_alloc(size); - if (file_read(f, tmp_ret->data, 1, tmp_ret->size, &bytes_read, err) < 0) - goto err; - - if (bytes_read - key_size != 0) { - g_set_error(err, PV_ERROR, PV_CRYPTO_ERROR_INVALID_KEY_SIZE, - _("Wrong file size '%s': read %zd, expected %u"), - path, bytes_read, key_size); - goto err; - } - - ret = g_steal_pointer(&tmp_ret); -err: - if (f) - fclose(f); - return ret; -} - -static EVP_PKEY *pv_img_get_cust_pub_priv_key(gint nid, GError **err) -{ - return generate_ec_key(nid, err); -} - -static HostKeyList *pv_img_get_host_keys(GSList *host_keys_with_path, gint nid, - GError **err) -{ - g_autoslist(EVP_PKEY) ret = NULL; - - for (GSList *iterator = host_keys_with_path; iterator; - iterator = iterator->next) { - x509_with_path *cert_with_path = iterator->data; - g_autoptr(EVP_PKEY) host_key = NULL; - X509 *cert = cert_with_path->cert; - - host_key = read_ec_pubkey_cert(cert, nid, err); - if (!host_key) - return NULL; - - ret = g_slist_append(ret, g_steal_pointer(&host_key)); - } - - return g_steal_pointer(&ret); -} - -static PvBuffer *pv_img_get_key(const EVP_CIPHER *cipher, const gchar *path, - GError **err) -{ - gint key_len = EVP_CIPHER_key_length(cipher); - - g_assert(key_len > 0); - - if (path) - return pv_img_read_key(path, (guint)key_len, err); - - return generate_aes_key((guint)key_len, err); -} - -static PvBuffer *pv_img_get_iv(const EVP_CIPHER *cipher, const gchar *path, - GError **err) -{ - gint iv_len = EVP_CIPHER_iv_length(cipher); - - g_assert(iv_len > 0); - - if (path) - return pv_img_read_key(path, (guint)iv_len, err); - - return generate_aes_iv((guint)iv_len, err); -} - -static int hex_str_toull(const gchar *nptr, uint64_t *dst, - GError **err) -{ - uint64_t value; - gchar *end; - - g_assert(dst); - - if (!g_str_is_ascii(nptr)) { - g_set_error(err, PV_ERROR, EINVAL, - _("Invalid value: '%s'. A hexadecimal value is required, for example '0xcfe'"), - nptr); - return -1; - } - - value = g_ascii_strtoull(nptr, &end, 16); - if ((value == G_MAXUINT64 && errno == ERANGE) || - (end && *end != '\0')) { - g_set_error(err, PV_ERROR, EINVAL, - _("Invalid value: '%s'. A hexadecimal value is required, for example '0xcfe'"), - nptr); - return -1; - } - *dst = value; - return 0; -} - -static gint pv_img_set_psw_addr(PvImage *img, const gchar *psw_addr_s, - GError **err) -{ - if (psw_addr_s) { - uint64_t psw_addr; - - if (hex_str_toull(psw_addr_s, &psw_addr, err) < 0) - return -1; - - img->initial_psw.addr = psw_addr; - } - - return 0; -} - -static void pv_img_set_control_flag(uint64_t *flags, const PvTristate option, const uint64_t flag) -{ - if (option == PV_TRUE) - *flags |= flag; - else if (option == PV_FALSE) - *flags &= ~flag; -} - -static gint pv_img_set_control_flags(PvImage *img, const PvControlFlagsArgs *cf_args, GError **err) -{ - uint64_t flags; - - /* Set plain control flags */ - if (cf_args->pcf) { - if (hex_str_toull(cf_args->pcf, &flags, err) < 0) - return -1; - - img->pcf = flags; - } - - pv_img_set_control_flag(&img->pcf, cf_args->enable_dump, PV_PCF_ALLOW_DUMPING); - pv_img_set_control_flag(&img->pcf, cf_args->enable_pckmo, - PV_PCF_PCKM_ECC | PV_PCF_PCKMO_AES | PV_PCF_PCKMO_DEA_TDEA); - - /* Set secret control flags */ - if (cf_args->scf) { - if (hex_str_toull(cf_args->scf, &flags, err) < 0) - return -1; - - img->scf = flags; - } - - pv_img_set_control_flag(&img->scf, cf_args->enable_cck_extension_secret_enforcement, - PV_SCF_CCK_EXTENSION_SECRET_ENFORCMENT); - return 0; -} - -static gint pv_img_hostkey_verify(GSList *host_key_certs, - const gchar *root_ca_path, - const gchar *const *crl_paths, - const gchar *const *untrusted_cert_paths, - gboolean offline, GError **err) -{ - g_autoslist(x509_with_path) untrusted_certs_with_path = NULL; - g_autoptr(STACK_OF_X509) ibm_signing_certs = NULL; - g_autoptr(STACK_OF_X509) untrusted_certs = NULL; - g_autoslist(x509_pair) ibm_z_pairs = NULL; - g_autoptr(X509_STORE) trusted = NULL; - gint ibm_signing_certs_count; - - /* Load trusted root CAs of the system if and only if @root_ca_path is - * NULL, otherwise use the root CA specified by @root_ca_path. - */ - trusted = store_setup(root_ca_path, crl_paths, err); - if (!trusted) - goto error; - - if (!offline) { - g_autoptr(STACK_OF_X509_CRL) downloaded_ibm_signing_crls = NULL; - - /* Set up the download routine for the lookup of CRLs. */ - store_setup_crl_download(trusted); - - /* Try to download the CRLs of the IBM Z signing certificates - * specified in the host-key documents. Ignore download errors - * as it's still possible that a CRL is specified via command - * line. - */ - downloaded_ibm_signing_crls = try_load_crls_by_certs(host_key_certs); - - /* Add the downloaded CRLs to the store so they can be used for - * the verification later. - */ - for (int i = 0; i < sk_X509_CRL_num(downloaded_ibm_signing_crls); i++) { - X509_CRL *crl = sk_X509_CRL_value(downloaded_ibm_signing_crls, i); - - if (X509_STORE_add_crl(trusted, crl) != 1) { - g_set_error(err, PV_CRYPTO_ERROR, - PV_CRYPTO_ERROR_INTERNAL, - _("failed to load CRL")); - goto error; - } - } - } - - /* Load all untrusted certificates (e.g. IBM Z signing key and - * intermediate CA) that are required to establish a chain of trust - * starting from the host-key document up to the root CA (if not - * otherwise specified that can be one of the system wide installed - * root CAs, e.g. DigiCert). - */ - untrusted_certs_with_path = load_certificates(untrusted_cert_paths, err); - if (!untrusted_certs_with_path) - goto error; - - /* Convert to STACK_OF(X509) */ - untrusted_certs = get_x509_stack(untrusted_certs_with_path); - - /* Find all IBM Z signing keys and remove them from the chain as we - * have to verify that they're valid. The last step of the chain of - * trust verification must be done manually, as the IBM Z signing keys - * are not marked as (intermediate) CA and therefore the standard - * `X509_verify_cert` function of OpenSSL cannot be used to verify the - * actual host-key documents. - */ - ibm_signing_certs = delete_ibm_signing_certs(untrusted_certs); - ibm_signing_certs_count = sk_X509_num(ibm_signing_certs); - if (ibm_signing_certs_count < 1) { - g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_NO_IBM_Z_SIGNING_KEY, - _("please specify at least one IBM Z signing key")); - goto error; - } else if (ibm_signing_certs_count > 1) { - g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_NO_IBM_Z_SIGNING_KEY, - _("please specify only one IBM Z signing key")); - goto error; - } - - if (store_set_verify_param(trusted, err) < 0) - goto error; - - /* Verify that the IBM Z signing keys are trustable. - * For this we must check: - * - * 1. Can a chain of trust be established ending in a root CA - * 2. Is the correct root CA used? It has either to be a system CA - * or the root CA specified via command line. - */ - for (gint i = 0; i < sk_X509_num(ibm_signing_certs); ++i) { - X509 *ibm_signing_cert = sk_X509_value(ibm_signing_certs, i); - g_autoptr(STACK_OF_X509_CRL) ibm_signing_crls = NULL; - g_autoptr(X509_STORE_CTX) ctx = NULL; - x509_pair *pair = NULL; - - g_assert(ibm_signing_cert); - - /* Create the verification context and set the trusted - * and chain parameters. - */ - ctx = create_store_ctx(trusted, untrusted_certs, err); - if (!ctx) - goto error; - - /* Verify the IBM Z signing key */ - if (verify_cert(ibm_signing_cert, ctx, err) < 0) - goto error; - - /* If there is a chain of trust using either the provided root - * CA on the command line or a system wide trusted root CA. - */ - if (check_chain_parameters(X509_STORE_CTX_get0_chain(ctx), - err) < 0) - goto error; - - ibm_signing_crls = store_ctx_find_valid_crls(ctx, ibm_signing_cert, err); - if (!ibm_signing_crls) { - g_prefix_error(err, _("IBM Z signing key: ")); - goto error; - } - - /* Increment reference counter of @ibm_signing_cert as the - * certificate will now also be owned by @ibm_z_pairs. - */ - if (X509_up_ref(ibm_signing_cert) != 1) - g_abort(); - - pair = x509_pair_new(&ibm_signing_cert, &ibm_signing_crls); - ibm_z_pairs = g_slist_append(ibm_z_pairs, pair); - g_assert(!ibm_signing_cert); - g_assert(!ibm_signing_crls); - } - - /* Verify host-key documents by using the IBM Z signing - * certificates and the corresponding certificate revocation - * lists. - */ - for (GSList *iterator = host_key_certs; iterator; iterator = iterator->next) { - x509_with_path *host_key_with_path = iterator->data; - const gchar *host_key_path = host_key_with_path->path; - X509 *host_key = host_key_with_path->cert; - gint flags = X509_V_FLAG_CRL_CHECK; - - if (verify_host_key(host_key, ibm_z_pairs, flags, - PV_CERTS_SECURITY_LEVEL, err) < 0) { - g_prefix_error(err, "'%s': ", host_key_path); - goto error; - } - } - - return 0; -error: - g_prefix_error(err, _("Failed to verify host-key document: ")); - return -1; -} - -/* read in the keys or auto-generate them */ -static gint pv_img_set_keys(PvImage *img, const PvArgs *args, GError **err) -{ - g_autoslist(x509_with_path) host_key_certs = NULL; - - g_assert(img->xts_cipher); - g_assert(img->cust_comm_cipher); - g_assert(img->gcm_cipher); - g_assert(img->nid); - - img->xts_key = pv_img_get_key(img->xts_cipher, args->xts_key_path, err); - if (!img->xts_key) - return -1; - - img->cust_comm_key = pv_img_get_key(img->cust_comm_cipher, - args->cust_comm_key_path, err); - if (!img->cust_comm_key) - return -1; - - img->cust_root_key = - pv_img_get_key(img->gcm_cipher, args->cust_root_key_path, err); - if (!img->cust_root_key) - return -1; - - img->gcm_iv = pv_img_get_iv(img->gcm_cipher, args->gcm_iv_path, err); - if (!img->gcm_iv) - return -1; - - img->cust_pub_priv_key = pv_img_get_cust_pub_priv_key(img->nid, err); - if (!img->cust_pub_priv_key) - return -1; - - /* Load all host-key documents specified on the command line */ - host_key_certs = load_certificates((const gchar **)args->host_keys, - err); - if (!host_key_certs) - return -1; - - if (!args->no_verify && - pv_img_hostkey_verify(host_key_certs, args->root_ca_path, - (const gchar * const *)args->crl_paths, - (const gchar * const *)args->untrusted_cert_paths, - args->offline, err) < 0) { - return -1; - } - - /* Loads the public keys stored in the host-key documents and verify - * that the correct elliptic curve is used. - */ - img->host_pub_keys = - pv_img_get_host_keys(host_key_certs, img->nid, err); - if (!img->host_pub_keys) - return -1; - - return 0; -} - -static void pv_img_add_host_slot(PvImage *img, PvHdrKeySlot *slot) -{ - img->key_slots = g_slist_append(img->key_slots, slot); -} - -static void pv_hdr_key_slot_free(PvHdrKeySlot *slot) -{ - if (!slot) - return; - - g_free(slot); -} - -WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(PvHdrKeySlot, pv_hdr_key_slot_free) - -static PvHdrKeySlot *pv_hdr_key_slot_new(const EVP_CIPHER *gcm_cipher, - const PvBuffer *cust_root_key, - EVP_PKEY *cust_key, EVP_PKEY *host_key, - GError **err) -{ - g_autoptr(PvHdrKeySlot) ret = g_new0(PvHdrKeySlot, 1); - g_autofree union ecdh_pub_key *pub = NULL; - g_autoptr(PvBuffer) exchange_key = NULL; - g_autoptr(PvBuffer) digest_key = NULL; - g_autoptr(PvBuffer) iv = NULL; - PvBuffer pub_buf; - /* No AAD data is used */ - PvBuffer aad = { .data = NULL, .size = 0 }; - /* Set the output buffers for the encrypted data and the - * generated GCM tag - */ - PvBuffer enc = { .data = ret->wrapped_key, .size = sizeof(ret->wrapped_key) }; - PvBuffer tag = { .data = ret->tag, .size = sizeof(ret->tag) }; - struct cipher_parms parms; - int64_t c_len = 0; - - g_assert(EVP_CIPHER_iv_length(gcm_cipher) >= 0); - - pub = evp_pkey_to_ecdh_pub_key(host_key, err); - if (!pub) - return NULL; - - pub_buf.data = pub->data; - pub_buf.size = sizeof(*pub); - digest_key = sha256_buffer(&pub_buf, err); - if (!digest_key) - return NULL; - - g_assert(digest_key->size == sizeof(ret->digest_key)); - /* set `digest_key` field */ - memcpy(ret->digest_key, digest_key->data, sizeof(ret->digest_key)); - - exchange_key = compute_exchange_key(cust_key, host_key, err); - if (!exchange_key) - return NULL; - - /* initialize cipher parameters */ - g_assert(exchange_key->size <= INT_MAX); - g_assert(exchange_key->size == (guint)EVP_CIPHER_key_length(gcm_cipher)); - - /* create zero IV */ - iv = pv_buffer_alloc((guint)EVP_CIPHER_iv_length(gcm_cipher)); - parms.iv_or_tweak = iv; - parms.key = exchange_key; - parms.cipher = gcm_cipher; - - /* Encrypt the customer root key that is used for the encryption - * of the PV header - */ - c_len = gcm_encrypt(cust_root_key, &aad, &parms, &enc, &tag, err); - if (c_len < 0) - return NULL; - - g_assert(c_len == (int64_t)cust_root_key->size); - return g_steal_pointer(&ret); -} - -static gint pv_img_set_host_slots(PvImage *img, GError **err) -{ - for (GSList *iterator = img->host_pub_keys; iterator; iterator = iterator->next) { - EVP_PKEY *host_key = iterator->data; - - g_assert(host_key); - - PvHdrKeySlot *slot = pv_hdr_key_slot_new(img->gcm_cipher, - img->cust_root_key, - img->cust_pub_priv_key, - host_key, err); - if (!slot) - return -1; - - pv_img_add_host_slot(img, slot); - } - - return 0; -} - -static gint pv_img_set_comps_offset(PvImage *img, uint64_t offset, GError **err) -{ - return pv_img_comps_set_offset(img->comps, offset, err); -} - -PvImage *pv_img_new(PvArgs *args, const gchar *stage3a_path, GError **err) -{ - g_autoptr(PvImage) ret = g_new0(PvImage, 1); - uint64_t offset; - - g_assert(args->tmp_dir); - g_assert(stage3a_path); - - if (args->no_verify) - g_warning(_("host-key document verification is disabled. Your workload is not secured.")); - - if (args->root_ca_path) - g_warning(_("The root CA is selected through the command line. Ensure that this root CA is trusted.")); - - ret->comps = pv_img_comps_new(EVP_sha512(), EVP_sha512(), EVP_sha512(), err); - if (!ret->comps) - return NULL; - - ret->pcf = PV_PCF_PCKMO_AES | PV_PCF_PCKMO_DEA_TDEA | PV_PCF_PCKM_ECC; - ret->cust_comm_cipher = EVP_aes_256_gcm(); - ret->gcm_cipher = EVP_aes_256_gcm(); - ret->initial_psw.addr = DEFAULT_INITIAL_PSW_ADDR; - ret->initial_psw.mask = DEFAULT_INITIAL_PSW_MASK; - ret->nid = NID_secp521r1; - ret->tmp_dir = g_strdup(args->tmp_dir); - ret->xts_cipher = EVP_aes_256_xts(); - - /* set initial PSW that will be loaded by the stage3b */ - if (pv_img_set_psw_addr(ret, args->psw_addr, err) < 0) - return NULL; - - /* set the control flags: PCF and SCF */ - if (pv_img_set_control_flags(ret, &args->cf_args, err) < 0) - return NULL; - - /* read in the keys */ - if (pv_img_set_keys(ret, args, err) < 0) - return NULL; - - if (pv_img_set_host_slots(ret, err) < 0) - return NULL; - - /* allocate enough memory for the stage3a args and load the - * stage3a template into memory and set the loader_psw - */ - if (pv_img_load_and_set_stage3a(ret, stage3a_path, err) < 0) - return NULL; - - offset = PAGE_ALIGN(STAGE3A_LOAD_ADDRESS + ret->stage3a->size); - - /* shift right all components by the size of stage3a loader */ - if (pv_img_set_comps_offset(ret, offset, err) < 0) - return NULL; - - return g_steal_pointer(&ret); -} - -void pv_img_free(PvImage *img) -{ - if (!img) - return; - - g_slist_free_full(img->optional_items, - (GDestroyNotify)pv_opt_item_free); - g_slist_free_full(img->key_slots, (GDestroyNotify)pv_hdr_key_slot_free); - g_slist_free_full(img->host_pub_keys, (GDestroyNotify)EVP_PKEY_free); - EVP_PKEY_free(img->cust_pub_priv_key); - pv_buffer_clear(&img->stage3a); - pv_img_comps_free(img->comps); - g_free(img->tmp_dir); - pv_buffer_free(img->xts_key); - pv_buffer_free(img->cust_root_key); - pv_buffer_free(img->gcm_iv); - pv_buffer_free(img->cust_comm_key); - g_free(img); -} - -static gint pv_img_prepare_and_add_component(PvImage *img, PvComponent **comp, - GError **err) -{ - g_assert(comp); - g_assert(*comp); - - /* prepares the component: does the alignment and encryption - * if required - */ - if (pv_img_prepare_component(img, *comp, err) < 0) - return -1; - - /* calculates the memory layout and adds the component to its - * internal list - */ - if (pv_img_comps_add_component(img->comps, comp, err) < 0) - return -1; - - g_assert(!*comp); - return 0; -} - -gint pv_img_add_component(PvImage *img, const PvArg *arg, GError **err) -{ - g_autoptr(PvComponent) comp = NULL; - - switch (arg->type) { - case PV_COMP_TYPE_INITRD: - case PV_COMP_TYPE_KERNEL: - case PV_COMP_TYPE_STAGE3B: - comp = pv_component_new_file(arg->type, arg->path, err); - break; - case PV_COMP_TYPE_CMDLINE: { - g_autoptr(PvBuffer) buf = NULL; - g_autofree char *data = NULL; - gsize length; - - if (!g_file_get_contents(arg->path, &data, &length, err)) - return -1; - - /* Add one for the null terminator */ - buf = pv_buffer_take(g_steal_pointer(&data), length + 1); - comp = pv_component_new_buf(arg->type, buf, err); - } break; - } - - if (!comp) - return -1; - - if (pv_img_prepare_and_add_component(img, &comp, err) < 0) - return -1; - - g_assert(!comp); - return 0; -} - -gint pv_img_calc_pld_ald_tld_nep(const PvImage *img, PvBuffer **pld, PvBuffer **ald, - PvBuffer **tld, uint64_t *nep, GError **err) -{ - return pv_img_comps_finalize(img->comps, pld, ald, tld, nep, err); -} - -static gint pv_img_build_stage3b(PvImage *img, PvBuffer *stage3b, GError **err) -{ - g_autofree struct stage3b_args *args = NULL; - - args = pv_img_comps_get_stage3b_args(img->comps, &img->initial_psw); - if (!args) { - g_set_error(err, PV_ERROR, PV_ERROR_INTERNAL, - _("Cannot generate stage3b arguments")); - return -1; - } - - build_stage3b(stage3b, args); - return 0; -} - -gint pv_img_add_stage3b_comp(PvImage *img, const gchar *path, GError **err) -{ - g_autoptr(PvComponent) comp = NULL; - g_autoptr(PvBuffer) stage3b = NULL; - - stage3b = stage3b_getblob(path, err); - if (!stage3b) - return -1; - - /* set the stage3b data */ - if (pv_img_build_stage3b(img, stage3b, err) < 0) - return -1; - - comp = pv_component_new_buf(PV_COMP_TYPE_STAGE3B, stage3b, err); - if (!comp) - return -1; - - if (pv_img_prepare_and_add_component(img, &comp, err) < 0) - return -1; - - g_assert(!comp); - return 0; -} - -static uint32_t pv_img_get_aad_size(const PvImage *img) -{ - uint32_t key_size, size = 0; - - g_assert(sizeof(struct pv_hdr_head) <= UINT32_MAX); - g_assert(sizeof(struct pv_hdr_key_slot) <= UINT32_MAX); - - g_assert_true(g_uint_checked_add(&size, size, - (uint32_t)sizeof(struct pv_hdr_head))); - g_assert_true(g_uint_checked_mul(&key_size, - (uint32_t)sizeof(struct pv_hdr_key_slot), - g_slist_length(img->key_slots))); - g_assert_true(g_uint_checked_add(&size, size, key_size)); - return size; -} - -static uint32_t pv_img_get_opt_items_size(const PvImage *img) -{ - uint32_t ret = 0; - - g_assert(img); - - for (GSList *iterator = img->optional_items; iterator; - iterator = iterator->next) { - const struct pv_hdr_opt_item *item = iterator->data; - - g_assert(item); - g_assert_true(g_uint_checked_add(&ret, ret, pv_opt_item_size(item))); - } - return ret; -} - -uint32_t pv_img_get_enc_size(const PvImage *img) -{ - uint32_t ret = 0; - - g_assert(sizeof(struct pv_hdr_encrypted) <= UINT32_MAX); - - g_assert_true(g_uint_checked_add( - &ret, ret, (uint32_t)sizeof(struct pv_hdr_encrypted))); - g_assert_true( - g_uint_checked_add(&ret, ret, pv_img_get_opt_items_size(img))); - return ret; -} - -static uint32_t pv_img_get_tag_size(const PvImage *img G_GNUC_UNUSED) -{ - g_assert(sizeof(((struct pv_hdr *)0)->tag) <= UINT32_MAX); - - return (uint32_t)sizeof(((struct pv_hdr *)0)->tag); -} - -uint32_t pv_img_get_pv_hdr_size(const PvImage *img) -{ - uint32_t size = 0; - - g_assert_true( - g_uint_checked_add(&size, size, pv_img_get_aad_size(img))); - g_assert_true( - g_uint_checked_add(&size, size, pv_img_get_enc_size(img))); - g_assert_true( - g_uint_checked_add(&size, size, pv_img_get_tag_size(img))); - return size; -} - -static gint get_stage3a_data_size(const PvImage *img, gsize *data_size, - GError **err) -{ - gsize ipib_size, hdr_size; - - g_assert(data_size); - g_assert(*data_size == 0); - - ipib_size = pv_ipib_get_size(pv_img_comps_length(img->comps)); - if (ipib_size > PV_V1_IPIB_MAX_SIZE) { - g_set_error(err, PV_ERROR, PV_ERROR_IPIB_SIZE, - _("IPIB size is too large: '%zu' > '%zu'"), - ipib_size, PV_V1_IPIB_MAX_SIZE); - return -1; - } - - hdr_size = pv_img_get_pv_hdr_size(img); - if (hdr_size > PV_V1_PV_HDR_MAX_SIZE) { - g_set_error(err, PV_ERROR, PV_ERROR_PV_HDR_SIZE, - _("PV header size is too large: '%zu' > '%zu'"), - hdr_size, PV_V1_PV_HDR_MAX_SIZE); - return -1; - } - - *data_size += PAGE_ALIGN(ipib_size); - *data_size += PAGE_ALIGN(hdr_size); - return 0; -} - -gint pv_img_load_and_set_stage3a(PvImage *img, const gchar *path, GError **err) -{ - g_autoptr(PvBuffer) stage3a = NULL; - gsize bin_size, data_size = 0; - - if (get_stage3a_data_size(img, &data_size, err) < 0) - return -1; - - stage3a = stage3a_getblob(path, &bin_size, data_size, err); - if (!stage3a) - return -1; - - img->stage3a_psw.addr = STAGE3A_ENTRY; - img->stage3a_psw.mask = DEFAULT_INITIAL_PSW_MASK; - - /* set addresses and size */ - img->stage3a = g_steal_pointer(&stage3a); - img->stage3a_bin_size = bin_size; - return 0; -} - -/* Creates the PV IPIB and sets the stage3a arguments */ -static gint pv_img_build_stage3a(PvBuffer *stage3a, gsize stage3a_bin_size, - GSList *comps, const PvBuffer *hdr, GError **err) -{ - g_autofree struct ipl_parameter_block *ipib = NULL; - - g_assert(stage3a); - g_assert(hdr); - - ipib = pv_ipib_new(comps, hdr, err); - if (!ipib) - return -1; - - if (build_stage3a(stage3a, stage3a_bin_size, hdr, ipib, err) < 0) - return -1; - - g_info("%12s:\t0x%012lx (%12ld / %12ld Bytes)", "stage3a", - STAGE3A_LOAD_ADDRESS, stage3a->size, stage3a->size); - return 0; -} - -/* Creates the actual PV header (serialized and AES-GCM encrypted) */ -static PvBuffer *pv_img_create_pv_hdr(PvImage *img, GError **err) -{ - g_autoptr(PvBuffer) hdr_buf = NULL; - g_autoptr(PvHdr) hdr = NULL; - - hdr = pv_hdr_new(img, err); - if (!hdr) - return NULL; - - hdr_buf = pv_hdr_serialize(hdr, img, PV_ENCRYPT, err); - if (!hdr_buf) - return NULL; - - return g_steal_pointer(&hdr_buf); -} - -/* No changes to the components are allowed after calling this - * function - */ -gint pv_img_finalize(PvImage *pv, const gchar *stage3b_path, GError **err) -{ - g_autoptr(PvBuffer) hdr = NULL; - - /* load stage3b template into memory and add it to the list of - * components. This must be done before calling - * `pv_img_load_and_set_stage3a`. - */ - if (pv_img_add_stage3b_comp(pv, stage3b_path, err) < 0) - return -1; - - /* create the PV header */ - hdr = pv_img_create_pv_hdr(pv, err); - if (!hdr) - return -1; - - /* generate stage3a. At this point in time the PV header and - * the stage3b must be generated and encrypted - */ - if (pv_img_build_stage3a(pv->stage3a, pv->stage3a_bin_size, - pv_img_comps_get_comps(pv->comps), hdr, err) < 0) - return -1; - - return 0; -} - -static gint convert_psw_to_short_psw(const struct psw_t *psw, uint64_t *dst, - GError **err) -{ - g_assert(psw); - g_assert(dst); - - uint64_t psw_addr = psw->addr; - uint64_t psw_mask = psw->mask; - - /* test if PSW mask can be converted */ - if (psw_mask & PSW32_ADDR_MASK) { - g_set_error(err, PV_ERROR, PV_ERROR_INTERNAL, - _("Failed to convert PSW to short PSW")); - return -1; - } - - /* test for bit 12 */ - if (psw_mask & PSW_MASK_BIT_12) { - g_set_error(err, PV_ERROR, PV_ERROR_INTERNAL, - _("Failed to convert PSW to short PSW")); - return -1; - } - - /* test if PSW addr can be converted */ - if (psw_addr & ~PSW32_ADDR_MASK) { - g_set_error(err, PV_ERROR, PV_ERROR_INTERNAL, - _("Failed to convert PSW to short PSW")); - return -1; - } - - *dst = psw_mask; - /* set bit 12 to 1 */ - *dst |= PSW_MASK_BIT_12; - *dst |= psw_addr; - return 0; -} - -static gint write_short_psw(FILE *f, struct psw_t *psw, GError **err) -{ - uint64_t short_psw, short_psw_be; - - if (convert_psw_to_short_psw(psw, &short_psw, err) < 0) - return -1; - - short_psw_be = GUINT64_TO_BE(short_psw); - return file_write(f, &short_psw_be, 1, sizeof(short_psw_be), NULL, err); -} - -gint pv_img_write(PvImage *img, const gchar *path, GError **err) -{ - gint ret = -1; - FILE *f = file_open(path, "wb", err); - - if (!f) - return -1; - - if (write_short_psw(f, &img->stage3a_psw, err) < 0) { - g_prefix_error(err, _("Failed to write image '%s': "), path); - goto err; - } - - if (seek_and_write_buffer(f, img->stage3a, STAGE3A_LOAD_ADDRESS, err) < - 0) { - g_prefix_error(err, _("Failed to write image '%s': "), path); - goto err; - } - - /* list is sorted by component type => by address */ - for (GSList *iterator = pv_img_comps_get_comps(img->comps); iterator; - iterator = iterator->next) { - gint rc; - const PvComponent *comp = iterator->data; - - rc = pv_component_write(comp, f, err); - if (rc < 0) { - g_prefix_error(err, _("Failed to write image '%s': "), - path); - goto err; - } - } - - ret = 0; -err: - if (f) - fclose(f); - return ret; -} diff --git a/genprotimg/src/pv/pv_image.h b/genprotimg/src/pv/pv_image.h deleted file mode 100644 index 10182028..00000000 --- a/genprotimg/src/pv/pv_image.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * PV image related definitions and functions - * - * Copyright IBM Corp. 2020 - * - * s390-tools is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See LICENSE for details. - */ - -#ifndef PV_IMAGE_H -#define PV_IMAGE_H - -#include -#include -#include -#include - -#include "boot/psw.h" -#include "utils/buffer.h" - -#include "pv_args.h" -#include "pv_comp.h" -#include "pv_comps.h" -#include "pv_stage3.h" - -typedef struct { - gchar *tmp_dir; /* directory used for temporary files */ - PvBuffer *stage3a; /* stage3a containing IPIB and PV header */ - gsize stage3a_bin_size; /* size of stage3a.bin */ - struct psw_t stage3a_psw; /* (short) PSW that is written to - * location 0 of the created image - */ - struct psw_t initial_psw; /* PSW loaded by stage3b */ - EVP_PKEY *cust_pub_priv_key; /* customer private/public key */ - GSList *host_pub_keys; /* public host keys */ - gint nid; /* Elliptic Curve used for the key derivation */ - /* keys and cipher used for the AES-GCM encryption */ - PvBuffer *cust_root_key; - PvBuffer *gcm_iv; - const EVP_CIPHER *gcm_cipher; - /* Information for the IPIB and PV header */ - uint64_t pcf; - uint64_t scf; - PvBuffer *cust_comm_key; - const EVP_CIPHER *cust_comm_cipher; - PvBuffer *xts_key; - const EVP_CIPHER *xts_cipher; - GSList *key_slots; - GSList *optional_items; - PvImgComps *comps; -} PvImage; - -PvImage *pv_img_new(PvArgs *args, const gchar *stage3a_path, GError **err); -void pv_img_free(PvImage *img); -gint pv_img_add_component(PvImage *img, const PvArg *arg, GError **err); -gint pv_img_finalize(PvImage *img, const gchar *stage3b_path, GError **err); -gint pv_img_calc_pld_ald_tld_nep(const PvImage *img, PvBuffer **pld, PvBuffer **ald, - PvBuffer **tld, uint64_t *nep, GError **err); -gint pv_img_load_and_set_stage3a(PvImage *img, const gchar *path, GError **err); -const PvComponent *pv_img_get_stage3b_comp(const PvImage *img, GError **err); -gint pv_img_add_stage3b_comp(PvImage *img, const gchar *path, GError **err); -uint32_t pv_img_get_enc_size(const PvImage *img); -uint32_t pv_img_get_pv_hdr_size(const PvImage *img); -gint pv_img_write(PvImage *img, const gchar *path, GError **err); - -G_DEFINE_AUTOPTR_CLEANUP_FUNC(PvImage, pv_img_free) - -#endif diff --git a/genprotimg/src/pv/pv_ipib.c b/genprotimg/src/pv/pv_ipib.c deleted file mode 100644 index 60072941..00000000 --- a/genprotimg/src/pv/pv_ipib.c +++ /dev/null @@ -1,127 +0,0 @@ -/* - * PV IPIB related definitions and functions - * - * Copyright IBM Corp. 2020 - * - * s390-tools is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See LICENSE for details. - */ - -#include -#include -#include -#include - -#include "boot/ipl.h" -#include "common.h" -#include "include/pv_hdr_def.h" -#include "lib/zt_common.h" -#include "utils/align.h" -#include "utils/buffer.h" - -#include "pv_comp.h" -#include "pv_error.h" -#include "pv_ipib.h" - -uint64_t pv_ipib_get_size(uint32_t num_comp) -{ - gsize ipib_size = sizeof(struct ipl_pl_hdr) + - sizeof(struct ipl_pb0_pv) + - num_comp * sizeof(struct ipl_pb0_pv_comp); - - /* the minimal size is one page */ - return MAX(ipib_size, PAGE_SIZE); -} - -static gint pv_ipib_init(IplParameterBlock *ipib, GSList *comps, - const PvBuffer *hdr) -{ - g_assert(sizeof(struct ipl_pl_hdr) <= UINT32_MAX); - g_assert(sizeof(struct ipl_pb0_pv_comp) <= UINT32_MAX); - g_assert(sizeof(struct ipl_pb0_pv) <= UINT32_MAX); - g_assert(ipib); - - guint comps_length = g_slist_length(comps); - uint32_t ipl_pl_hdr_size = (uint32_t)sizeof(struct ipl_pl_hdr); - struct ipl_pb0_pv *pv = &ipib->pv; - uint32_t ipib_comps_size; - uint32_t blk0_len; - uint32_t ipib_size; - gsize i; - - g_assert_true( - g_uint_checked_mul(&ipib_comps_size, comps_length, - (uint32_t)sizeof(struct ipl_pb0_pv_comp))); - g_assert_true(g_uint_checked_add(&blk0_len, (uint32_t)sizeof(*pv), - ipib_comps_size)); - g_assert(ipl_pl_hdr_size + blk0_len <= PAGE_SIZE); - - ipib_size = MAX(ipl_pl_hdr_size + blk0_len, (uint32_t)PAGE_SIZE); - g_assert(pv_ipib_get_size(comps_length) == ipib_size); - - pv->pbt = IPL_PBT_PV; - pv->len = GUINT32_TO_BE(blk0_len); - pv->num_comp = GUINT32_TO_BE(comps_length); - /* both values will be overwritten during the IPL process by - * the stage3a loader - */ - pv->pv_hdr_addr = GUINT64_TO_BE(0x0); - pv->pv_hdr_size = GUINT64_TO_BE(hdr->size); - - ipib->hdr.len = GUINT32_TO_BE(ipib_size); - ipib->hdr.version = IPL_PARM_BLOCK_VERSION; - - i = 0; - for (GSList *iterator = comps; iterator; iterator = iterator->next, i++) { - const PvComponent *comp = iterator->data; - uint64_t comp_addr, comp_size; - - g_assert(comp); - - comp_addr = pv_component_get_src_addr(comp); - comp_size = pv_component_size(comp); - - g_assert(IS_PAGE_ALIGNED(comp_size)); - - pv->components[i].addr = GUINT64_TO_BE(comp_addr); - pv->components[i].len = GUINT64_TO_BE(comp_size); - pv->components[i].tweak_pref = - GUINT64_TO_BE(pv_component_get_tweak_prefix(comp)); - if (i > 0) { - /* tweak prefixes of the components must grow - * strictly monotonous - */ - g_assert(GUINT64_FROM_BE(pv->components[i].tweak_pref) > - GUINT64_FROM_BE(pv->components[i - 1].tweak_pref)); - } - } - - return 0; -} - -IplParameterBlock *pv_ipib_new(GSList *comps, const PvBuffer *hdr, GError **err) -{ - uint64_t ipib_size = pv_ipib_get_size(g_slist_length(comps)); - g_autoptr(IplParameterBlock) ret = NULL; - - if (ipib_size > PV_V1_IPIB_MAX_SIZE) { - g_set_error(err, PV_ERROR, PV_ERROR_IPIB_SIZE, - _("IPIB size is too large: %lu < %lu"), ipib_size, - PAGE_SIZE); - return NULL; - } - - ret = g_malloc0(ipib_size); - if (pv_ipib_init(ret, comps, hdr) < 0) - return NULL; - - return g_steal_pointer(&ret); -} - -void pv_ipib_free(IplParameterBlock *ipib) -{ - if (!ipib) - return; - - g_free(ipib); -} diff --git a/genprotimg/src/pv/pv_ipib.h b/genprotimg/src/pv/pv_ipib.h deleted file mode 100644 index 4b66643d..00000000 --- a/genprotimg/src/pv/pv_ipib.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * PV IPIB related definitions and functions - * - * Copyright IBM Corp. 2020 - * - * s390-tools is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See LICENSE for details. - */ - -#ifndef PV_IPIB_H -#define PV_IPIB_H - -#include -#include - -#include "boot/ipl.h" -#include "utils/buffer.h" - -typedef struct ipl_parameter_block IplParameterBlock; - -uint64_t pv_ipib_get_size(uint32_t num_comp); -IplParameterBlock *pv_ipib_new(GSList *comps, const PvBuffer *hdr, GError **err); -void pv_ipib_free(IplParameterBlock *ipib); - -WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(IplParameterBlock, pv_ipib_free) - -#endif diff --git a/genprotimg/src/pv/pv_opt_item.c b/genprotimg/src/pv/pv_opt_item.c deleted file mode 100644 index 39b9a8da..00000000 --- a/genprotimg/src/pv/pv_opt_item.c +++ /dev/null @@ -1,26 +0,0 @@ -/* - * PV optional item related definitions and functions - * - * Copyright IBM Corp. 2020 - * - * s390-tools is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See LICENSE for details. - */ - -#include - -#include "pv_opt_item.h" - -uint32_t pv_opt_item_size(const struct pv_hdr_opt_item *item G_GNUC_UNUSED) -{ - /* not implemented yet */ - g_assert_not_reached(); -} - -void pv_opt_item_free(struct pv_hdr_opt_item *item) -{ - if (!item) - return; - - g_free(item); -} diff --git a/genprotimg/src/pv/pv_opt_item.h b/genprotimg/src/pv/pv_opt_item.h deleted file mode 100644 index 34bb0a97..00000000 --- a/genprotimg/src/pv/pv_opt_item.h +++ /dev/null @@ -1,20 +0,0 @@ -/* - * PV optional item related definitions and functions - * - * Copyright IBM Corp. 2020 - * - * s390-tools is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See LICENSE for details. - */ - -#ifndef PV_OPT_ITEM_H -#define PV_OPT_ITEM_H - -#include - -#include "include/pv_hdr_def.h" - -uint32_t pv_opt_item_size(const struct pv_hdr_opt_item *item); -void pv_opt_item_free(struct pv_hdr_opt_item *item); - -#endif diff --git a/genprotimg/src/pv/pv_stage3.c b/genprotimg/src/pv/pv_stage3.c deleted file mode 100644 index bff9db7d..00000000 --- a/genprotimg/src/pv/pv_stage3.c +++ /dev/null @@ -1,164 +0,0 @@ -/* - * PV stage3 loader related definitions and functions - * - * Copyright IBM Corp. 2020 - * - * s390-tools is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See LICENSE for details. - */ - -#include -#include -#include - -#include "boot/ipl.h" -#include "boot/stage3a.h" -#include "boot/stage3b.h" -#include "common.h" -#include "utils/align.h" - -#include "pv_error.h" -#include "pv_stage3.h" - -#define STAGE3A_ARGS(data_ptr, loader_size) \ - ((struct stage3a_args *)((uint64_t)data_ptr + loader_size - \ - sizeof(struct stage3a_args))) - -static PvBuffer *loader_getblob(const gchar *filename, gsize *loader_size, - gsize args_size, gsize data_size, - gboolean data_aligned, GError **err) -{ - g_autoptr(GMappedFile) mapped_file = NULL; - g_autoptr(PvBuffer) ret = NULL; - gsize size, tmp_loader_size; - gchar *loader_data; - - g_assert(loader_size); - - mapped_file = g_mapped_file_new(filename, FALSE, err); - if (!mapped_file) - return NULL; - - loader_data = g_mapped_file_get_contents(mapped_file); - if (!loader_data) { - g_set_error(err, G_FILE_ERROR, G_FILE_ERROR_BADF, - _("File '%s' is empty"), filename); - return NULL; - } - tmp_loader_size = g_mapped_file_get_length(mapped_file); - - if (tmp_loader_size < args_size) { - g_set_error(err, G_FILE_ERROR, G_FILE_ERROR_BADF, - _("File size less than expected: %lu < %ln"), - tmp_loader_size, loader_size); - return NULL; - } - - /* For example, the PV header and IPIB data must be page - * aligned. - */ - size = (data_aligned ? PAGE_ALIGN(tmp_loader_size) : tmp_loader_size) + - data_size; - - ret = pv_buffer_alloc(size); - - /* copy the loader "template" */ - memcpy(ret->data, loader_data, tmp_loader_size); - /* reset our dummy data (offsets and length) to zeros */ - memset((uint8_t *)ret->data + tmp_loader_size - args_size, 0, - args_size); - *loader_size = tmp_loader_size; - return g_steal_pointer(&ret); -} - -PvBuffer *stage3a_getblob(const gchar *filename, gsize *loader_size, - gsize data_size, GError **err) -{ - return loader_getblob(filename, loader_size, - sizeof(struct stage3a_args), data_size, TRUE, - err); -} - -/* For the memory layout see stage3a.lds */ -/* Set the right offsets and sizes in the stage3a template + add - * the IPIB block with the PV header - */ -static gint stage3a_set_data(PvBuffer *loader, gsize loader_size, - const PvBuffer *hdr, struct ipl_parameter_block *ipib, - GError **err) -{ - uint32_t ipib_size = GUINT32_FROM_BE(ipib->hdr.len); - gsize args_size = sizeof(struct stage3a_args); - uint32_t hdr_size = (uint32_t)hdr->size; - uint64_t args_addr, next_data_addr; - - if (hdr->size > UINT32_MAX) { - g_set_error(err, PV_ERROR, PV_ERROR_INTERNAL, - _("Invalid header size: %zu"), hdr->size); - return -1; - } - - /* we assume here that the loader ``stage3a`` is loaded page - * aligned in the guest - */ - args_addr = (uint64_t)loader->data + loader_size - args_size; - - /* therefore `next_data_addr` is also page aligned */ - next_data_addr = (uint64_t)loader->data + PAGE_ALIGN(loader_size); - - /* copy IPIB data */ - memcpy((void *)next_data_addr, ipib, ipib_size); - - /* set IPIB offset in relation to the stage3a arguments */ - STAGE3A_ARGS(loader->data, loader_size)->ipib_offs = - GUINT64_TO_BE(next_data_addr - args_addr); - - next_data_addr = next_data_addr + PAGE_ALIGN(ipib_size); - /* copy PV header */ - memcpy((void *)next_data_addr, hdr->data, hdr_size); - /* set PV header size and offset in relation to the stage3a - * arguments - */ - STAGE3A_ARGS(loader->data, loader_size)->hdr_offs = - GUINT64_TO_BE(next_data_addr - args_addr); - STAGE3A_ARGS(loader->data, loader_size)->hdr_size = GUINT64_TO_BE(hdr_size); - - return 0; -} - -gint build_stage3a(PvBuffer *loader, gsize loader_size, const PvBuffer *hdr, - struct ipl_parameter_block *ipib, GError **err) -{ - return stage3a_set_data(loader, loader_size, hdr, ipib, err); -} - -PvBuffer *stage3b_getblob(const gchar *filename, GError **err) -{ - g_autoptr(PvBuffer) ret = NULL; - gsize rb_size; - - ret = loader_getblob(filename, &rb_size, sizeof(struct stage3b_args), 0, - FALSE, err); - if (!ret) - return NULL; - - g_assert(ret->size == rb_size); - return g_steal_pointer(&ret); -} - -void build_stage3b(PvBuffer *stage3b, const struct stage3b_args *args) -{ - g_assert(stage3b->size > sizeof(*args)); - - /* at the end of the stage3b there are the stage3b args - * positioned - */ - memcpy((uint8_t *)stage3b->data + stage3b->size - sizeof(*args), args, - sizeof(*args)); -} - -void memblob_init(struct memblob *arg, uint64_t src, uint64_t size) -{ - arg->src = GUINT64_TO_BE(src); - arg->size = GUINT64_TO_BE(size); -} diff --git a/genprotimg/src/pv/pv_stage3.h b/genprotimg/src/pv/pv_stage3.h deleted file mode 100644 index 7b3e8d07..00000000 --- a/genprotimg/src/pv/pv_stage3.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * PV stage3 loader related definitions and functions - * - * Copyright IBM Corp. 2020 - * - * s390-tools is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See LICENSE for details. - */ - -#ifndef PV_STAGE3_H -#define PV_STAGE3_H - -#include -#include -#include - -#include "boot/ipl.h" -#include "boot/stage3b.h" -#include "utils/buffer.h" - -PvBuffer *stage3a_getblob(const gchar *filename, gsize *loader_size, - gsize data_size, GError **err); -gint build_stage3a(PvBuffer *dc, gsize dc_size, const PvBuffer *hdr, - struct ipl_parameter_block *ipib, GError **err); -PvBuffer *stage3b_getblob(const gchar *filename, GError **err); -void build_stage3b(PvBuffer *stage3b, const struct stage3b_args *args); -void memblob_init(struct memblob *arg, uint64_t src, uint64_t size); - -#endif diff --git a/genprotimg/src/utils/align.h b/genprotimg/src/utils/align.h deleted file mode 100644 index 57f940fd..00000000 --- a/genprotimg/src/utils/align.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Alignment utils - * - * Copyright IBM Corp. 2020 - * - * s390-tools is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See LICENSE for details. - */ - -#ifndef PV_UTILS_ALIGN_H -#define PV_UTILS_ALIGN_H - -#include "lib/zt_common.h" -#include "boot/page.h" - -#define IS_ALIGNED(addr, size) (!(addr & (size - 1))) - -/* align addr to the next page boundary */ -#define PAGE_ALIGN(addr) ALIGN((unsigned long)addr, PAGE_SIZE) - -/* test whether an address is aligned to PAGE_SIZE or not */ -#define IS_PAGE_ALIGNED(addr) IS_ALIGNED((unsigned long)(addr), PAGE_SIZE) - -#endif diff --git a/genprotimg/src/utils/buffer.c b/genprotimg/src/utils/buffer.c deleted file mode 100644 index d7a10844..00000000 --- a/genprotimg/src/utils/buffer.c +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Buffer functions - * - * Copyright IBM Corp. 2020 - * - * s390-tools is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See LICENSE for details. - */ - -#include -#include -#include -#include - -#include "align.h" -#include "buffer.h" -#include "common.h" -#include "file_utils.h" - -PvBuffer *pv_buffer_alloc(gsize size) -{ - PvBuffer *ret = g_new0(PvBuffer, 1); - - ret->data = g_malloc0(size); - ret->size = size; - return ret; -} - -PvBuffer *pv_buffer_take(char *data, gsize size) -{ - PvBuffer *ret = g_new0(PvBuffer, 1); - - ret->data = data; - ret->size = size; - return ret; -} - -PvBuffer *pv_buffer_dup(const PvBuffer *buf, gboolean page_aligned) -{ - PvBuffer *ret; - gsize size; - - if (!buf) - return NULL; - - size = buf->size; - if (page_aligned) - size = PAGE_ALIGN(size); - - ret = pv_buffer_alloc(size); - - /* content will be 0-right-padded */ - memcpy(ret->data, buf->data, buf->size); - return ret; -} - -gint pv_buffer_write(const PvBuffer *buf, FILE *file, GError **err) -{ - return file_write(file, buf->data, buf->size, 1, NULL, err); -} - -void pv_buffer_free(PvBuffer *buf) -{ - if (!buf) - return; - - g_free(buf->data); - g_free(buf); -} - -void pv_buffer_clear(PvBuffer **buf) -{ - if (!buf || !*buf) - return; - - pv_buffer_free(*buf); - *buf = NULL; -} diff --git a/genprotimg/src/utils/buffer.h b/genprotimg/src/utils/buffer.h deleted file mode 100644 index 3fb870a0..00000000 --- a/genprotimg/src/utils/buffer.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Buffer definition and functions - * - * Copyright IBM Corp. 2020 - * - * s390-tools is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See LICENSE for details. - */ - -#ifndef PV_UTILS_BUFFER_H -#define PV_UTILS_BUFFER_H - -#include -#include - -#include "common.h" - -typedef struct PvBuffer { - void *data; - gsize size; /* in bytes */ -} PvBuffer; - -PvBuffer *pv_buffer_alloc(gsize size); -/* After this call @data belongs to the PvBuffer and must no longer be modified - * by the caller. - */ -PvBuffer *pv_buffer_take(char *data, gsize size); -void pv_buffer_free(PvBuffer *buf); -void pv_buffer_clear(PvBuffer **buf); -gint pv_buffer_write(const PvBuffer *buf, FILE *file, GError **err); -PvBuffer *pv_buffer_dup(const PvBuffer *buf, gboolean page_aligned); - -WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(PvBuffer, pv_buffer_free) - -#endif diff --git a/genprotimg/src/utils/crypto.c b/genprotimg/src/utils/crypto.c deleted file mode 100644 index 22868df1..00000000 --- a/genprotimg/src/utils/crypto.c +++ /dev/null @@ -1,2102 +0,0 @@ -/* - * General cryptography helper functions - * - * Copyright IBM Corp. 2020 - * - * s390-tools is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See LICENSE for details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "boot/page.h" -#include "common.h" -#include "include/pv_crypto_def.h" -#include "pv/pv_error.h" - -#include "buffer.h" -#include "curl.h" -#include "openssl_compat.h" -#include "crypto.h" - -#define DEFINE_GSLIST_MAP(t2, t1) \ - typedef t1 *(*g_slist_map_func_##t2##_##t1)(const t2 *x, \ - GError **err); \ - G_GNUC_UNUSED static GSList *g_slist_map_##t2##_##t1(const GSList *list, \ - g_slist_map_func_##t2##_##t1 func, \ - GError **err) \ - { \ - g_autoslist(t1) ret = NULL; \ - for (const GSList *iterator = list; iterator; \ - iterator = iterator->next) { \ - const t2 *value = iterator->data; \ - t1 *new_value = NULL; \ - g_assert(value); \ - new_value = func(value, err); \ - if (!new_value) \ - return NULL; \ - ret = g_slist_append(ret, g_steal_pointer(&new_value)); \ - } \ - return g_steal_pointer(&ret); \ - } - -#define DEFINE_GSLIST_TO_STACK(t1) \ - G_GNUC_UNUSED static STACK_OF(t1) *g_slist_to_stack_of_##t1(GSList **list) \ - { \ - g_assert(list); \ - g_autoptr(STACK_OF_##t1) ret = sk_##t1##_new_null(); \ - if (!ret) \ - g_abort(); \ - for (GSList *iterator = *list; iterator; \ - iterator = iterator->next) { \ - if (sk_##t1##_push(ret, g_steal_pointer(&iterator->data)) == 0) \ - g_abort(); \ - } \ - g_clear_pointer(list, g_slist_free); \ - return g_steal_pointer(&ret); \ - } - -DEFINE_GSLIST_MAP(x509_with_path, X509) -DEFINE_GSLIST_TO_STACK(X509) - -EVP_MD_CTX *digest_ctx_new(const EVP_MD *md, GError **err) -{ - g_autoptr(EVP_MD_CTX) ctx = EVP_MD_CTX_new(); - - if (!ctx) - g_abort(); - - if (EVP_DigestInit_ex(ctx, md, NULL) != 1) { - g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, - _("EVP_DigestInit_ex failed")); - return NULL; - } - - return g_steal_pointer(&ctx); -} - -PvBuffer *digest_ctx_finalize(EVP_MD_CTX *ctx, GError **err) -{ - gint md_size = EVP_MD_size(EVP_MD_CTX_md(ctx)); - g_autoptr(PvBuffer) ret = NULL; - guint digest_size; - - g_assert(md_size > 0); - - ret = pv_buffer_alloc((guint)md_size); - if (EVP_DigestFinal_ex(ctx, ret->data, &digest_size) != 1) { - g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, - _("EVP_DigestFinal_ex failed")); - return NULL; - } - - g_assert(digest_size == (guint)md_size); - g_assert(digest_size == ret->size); - return g_steal_pointer(&ret); -} - -/* Returns the digest of @buf using the hash algorithm @md */ -static PvBuffer *digest_buffer(const EVP_MD *md, const PvBuffer *buf, GError **err) -{ - g_autoptr(EVP_MD_CTX) md_ctx = NULL; - g_autoptr(PvBuffer) ret = NULL; - g_assert(buf); - - md_ctx = digest_ctx_new(md, err); - if (!md_ctx) - return NULL; - - if (EVP_DigestUpdate(md_ctx, buf->data, buf->size) != 1) { - g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, - _("EVP_DigestUpdate failed")); - return NULL; - } - - ret = digest_ctx_finalize(md_ctx, err); - if (!ret) - return NULL; - - return g_steal_pointer(&ret); -} - -/* Returns the SHA256 digest of @buf */ -PvBuffer *sha256_buffer(const PvBuffer *buf, GError **err) -{ - g_autoptr(PvBuffer) ret = NULL; - - ret = digest_buffer(EVP_sha256(), buf, err); - if (!ret) - return NULL; - - g_assert(ret->size == SHA256_DIGEST_LENGTH); - return g_steal_pointer(&ret); -} - -/* Convert a EVP_PKEY to the key format used in the PV header */ -union ecdh_pub_key *evp_pkey_to_ecdh_pub_key(EVP_PKEY *key, GError **err) -{ - g_autofree union ecdh_pub_key *ret = g_new0(union ecdh_pub_key, 1); - g_autoptr(BIGNUM) pub_x_big = NULL; - g_autoptr(BIGNUM) pub_y_big = NULL; - g_autoptr(EC_KEY) ec_key = NULL; - const EC_POINT *pub_key; - const EC_GROUP *grp; - - ec_key = EVP_PKEY_get1_EC_KEY(key); - if (!ec_key) { - g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, - _("Key has the wrong type")); - return NULL; - } - - pub_key = EC_KEY_get0_public_key(ec_key); - if (!pub_key) { - g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, - _("Failed to get public key")); - return NULL; - } - - grp = EC_KEY_get0_group(ec_key); - if (!grp) { - g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, - _("Failed to get EC group")); - return NULL; - } - - pub_x_big = BN_new(); - if (!pub_x_big) - g_abort(); - - pub_y_big = BN_new(); - if (!pub_y_big) - g_abort(); - - if (EC_POINT_get_affine_coordinates_GFp(grp, pub_key, pub_x_big, - pub_y_big, NULL) != 1) { - g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, - _("Cannot convert key to internal format")); - return NULL; - } - - if (BN_bn2binpad(pub_x_big, ret->x, sizeof(ret->x)) < 0) { - g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, - _("Cannot convert key to internal format")); - return NULL; - } - - if (BN_bn2binpad(pub_y_big, ret->y, sizeof(ret->y)) < 0) { - g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, - _("Cannot convert key to internal format")); - return NULL; - } - - return g_steal_pointer(&ret); -} - -static PvBuffer *derive_key(EVP_PKEY *cust, EVP_PKEY *host, GError **err) -{ - g_autoptr(EVP_PKEY_CTX) ctx = NULL; - g_autoptr(PvBuffer) ret = NULL; - gsize key_size; - - ctx = EVP_PKEY_CTX_new(cust, NULL); - if (!ctx) - g_abort(); - - if (EVP_PKEY_derive_init(ctx) != 1) { - g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, - _("Key derivation failed")); - return NULL; - } - - if (EVP_PKEY_derive_set_peer(ctx, host) != 1) { - g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, - _("Key derivation failed")); - return NULL; - } - - /* Determine buffer length */ - if (EVP_PKEY_derive(ctx, NULL, &key_size) != 1) { - g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_DERIVE, - _("Key derivation failed")); - return NULL; - } - - ret = pv_buffer_alloc(key_size); - if (EVP_PKEY_derive(ctx, ret->data, &key_size) != 1) { - g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_DERIVE, - _("Key derivation failed")); - return NULL; - } - - g_assert(ret->size == key_size); - return g_steal_pointer(&ret); -} - -PvBuffer *compute_exchange_key(EVP_PKEY *cust, EVP_PKEY *host, GError **err) -{ - g_autoptr(PvBuffer) raw = pv_buffer_alloc(70); - g_autoptr(PvBuffer) ret = NULL; - g_autoptr(PvBuffer) key = NULL; - guchar *data; - - key = derive_key(cust, host, err); - if (!key) - return NULL; - - g_assert(key->size == 66); - g_assert(key->size < raw->size); - - /* ANSI X.9.63-2011: 66 bytes x with leading 7 bits and - * concatenate 32 bit int '1' - */ - memcpy(raw->data, key->data, key->size); - data = raw->data; - data[66] = 0x00; - data[67] = 0x00; - data[68] = 0x00; - data[69] = 0x01; - - ret = sha256_buffer(raw, err); - if (!ret) - return NULL; - - return g_steal_pointer(&ret); -} - -gint generate_tweak(union tweak *tweak, uint16_t i, GError **err) -{ - tweak->cmp_idx.idx = GUINT16_TO_BE(i); - if (RAND_bytes(tweak->cmp_idx.rand, sizeof(tweak->cmp_idx.rand)) != 1) { - g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_RANDOMIZATION, - _("Generating a tweak failed because the required amount of random data is not available")); - return -1; - } - - return 0; -} - -static PvBuffer *generate_rand_data(guint size, const gchar *err_msg, - GError **err) -{ - g_autoptr(PvBuffer) buf = pv_buffer_alloc(size); - - g_assert(size <= INT_MAX); - - if (RAND_bytes(buf->data, (int)size) != 1) { - g_set_error_literal(err, PV_CRYPTO_ERROR, - PV_CRYPTO_ERROR_RANDOMIZATION, - err_msg); - return NULL; - } - - return g_steal_pointer(&buf); -} - -PvBuffer *generate_aes_iv(guint size, GError **err) -{ - return generate_rand_data(size, - _("Generating a IV failed because the required amount of random data is not available"), - err); -} - -PvBuffer *generate_aes_key(guint size, GError **err) -{ - return generate_rand_data(size, - _("Generating a key failed because the required amount of random data is not available"), - err); -} - -EVP_PKEY *generate_ec_key(gint nid, GError **err) -{ - g_autoptr(EVP_PKEY_CTX) ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL); - g_autoptr(EVP_PKEY) ret = NULL; - - if (!ctx) - g_abort(); - - if (EVP_PKEY_keygen_init(ctx) != 1) { - g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_KEYGENERATION, - _("EC key could not be auto-generated")); - return NULL; - } - - if (EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx, nid) != 1) { - g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_KEYGENERATION, - _("EC key could not be auto-generated")); - return NULL; - } - - if (EVP_PKEY_keygen(ctx, &ret) != 1) { - g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_KEYGENERATION, - _("EC key could not be auto-generated")); - return NULL; - } - - return g_steal_pointer(&ret); -} - -static gboolean certificate_uses_correct_curve(EVP_PKEY *key, gint nid, - GError **err) -{ - g_autoptr(EC_KEY) ec = NULL; - gint rc; - - g_assert(key); - - if (EVP_PKEY_id(key) != EVP_PKEY_EC) { - g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INVALID_PARM, - _("No EC key found")); - return FALSE; - } - - ec = EVP_PKEY_get1_EC_KEY(key); - if (!ec) { - g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INVALID_PARM, - _("No EC key found")); - return FALSE; - } - - if (EC_KEY_check_key(ec) != 1) { - g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INVALID_PARM, - _("Invalid EC key")); - return FALSE; - } - - rc = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec)); - if (rc != nid) { - /* maybe the NID is unset */ - if (rc == 0) { - g_autoptr(EC_GROUP) grp = EC_GROUP_new_by_curve_name(nid); - const EC_POINT *pub = EC_KEY_get0_public_key(ec); - g_autoptr(BN_CTX) ctx = BN_CTX_new(); - - if (EC_POINT_is_on_curve(grp, pub, ctx) != 1) { - g_set_error_literal(err, PV_CRYPTO_ERROR, - PV_CRYPTO_ERROR_INVALID_PARM, - _("Invalid EC curve")); - return FALSE; - } - } else { - /* NID was set but doesn't match with the expected NID - */ - g_set_error(err, PV_CRYPTO_ERROR, - PV_CRYPTO_ERROR_INVALID_PARM, - _("Wrong NID used: '%d'"), - EC_GROUP_get_curve_name(EC_KEY_get0_group(ec))); - return FALSE; - } - } - - return TRUE; -} - -/* Verify that the used public key algorithm matches the subject signature - * algorithm - */ -static int check_signature_algo_match(const EVP_PKEY *pkey, const X509 *subject, - GError **err) -{ - gint pkey_nid; - - if (!pkey) { - g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_NO_PUBLIC_KEY, - _("no public key")); - return -1; - } - - if (OBJ_find_sigid_algs(X509_get_signature_nid(subject), NULL, - &pkey_nid) != 1) { - g_set_error(err, PV_CRYPTO_ERROR, - PV_CRYPTO_ERROR_INVALID_SIGNATURE_ALGORITHM, - _("unsupported signature algorithm")); - return -1; - } - - if (EVP_PKEY_type(pkey_nid) != EVP_PKEY_base_id(pkey)) { - g_set_error(err, PV_CRYPTO_ERROR, - PV_CRYPTO_ERROR_SIGNATURE_ALGORITHM_MISMATCH, - _("signature algorithm mismatch")); - return -1; - } - - return 0; -} - -static X509_CRL *load_crl_from_bio(BIO *bio) -{ - g_autoptr(X509_CRL) crl = PEM_read_bio_X509_CRL(bio, NULL, NULL, NULL); - gint rc; - - if (crl) - return g_steal_pointer(&crl); - ERR_clear_error(); - rc = BIO_reset(bio); - if (rc != 1 && !(rc == 0 && BIO_method_type(bio) == BIO_TYPE_FILE)) - return NULL; - - /* maybe the CRL is stored in DER format */ - crl = d2i_X509_CRL_bio(bio, NULL); - if (crl) - return g_steal_pointer(&crl); - return NULL; -} - -static X509_CRL *GByteArray_to_X509_CRL(const GByteArray *data) -{ - g_autoptr(X509_CRL) ret = NULL; - g_autoptr(BIO) bio = NULL; - - g_assert(data); - - if (data->len > INT_MAX) - return NULL; - - bio = BIO_new_mem_buf(data->data, (int)data->len); - if (!bio) - g_abort(); - - ret = load_crl_from_bio(bio); - if (!ret) - return NULL; - - return g_steal_pointer(&ret); -} - -static gint load_crl_from_web(const gchar *url, X509_CRL **crl, GError **err) -{ - g_autoptr(X509_CRL) tmp_crl = NULL; - g_autoptr(GByteArray) data = NULL; - g_assert(crl); - - data = curl_download(url, CRL_DOWNLOAD_TIMEOUT_MS, - CRL_DOWNLOAD_MAX_SIZE, err); - if (!data) { - g_prefix_error(err, _("unable to download CRL: ")); - return -1; - } - tmp_crl = GByteArray_to_X509_CRL(data); - if (!tmp_crl) { - g_set_error(err, PV_CRYPTO_ERROR, - PV_CRYPTO_ERROR_CRL_DOWNLOAD_FAILED, - _("unable to load CRL from '%s'"), url); - return -1; - } - *crl = g_steal_pointer(&tmp_crl); - return 0; -} - -static BIO *bio_read_from_file(const char *path) -{ - g_autoptr(BIO) bio = BIO_new_file(path, "r"); - - if (!bio) - return NULL; - - return g_steal_pointer(&bio); -} - -/* This function reads in only the first certificate and ignores all other. This - * is only relevant for the PEM file format. For the host-key document and the - * root CA this behavior is expected. - */ -X509 *load_cert_from_file(const char *path, GError **err) -{ - g_autoptr(BIO) bio = bio_read_from_file(path); - g_autoptr(X509) cert = NULL; - gint rc; - - if (!bio) { - g_set_error(err, PV_CRYPTO_ERROR, - PV_CRYPTO_ERROR_READ_CERTIFICATE, - _("unable to read certificate: '%s'"), path); - return NULL; - } - - cert = PEM_read_bio_X509(bio, NULL, NULL, NULL); - if (cert) - return g_steal_pointer(&cert); - ERR_clear_error(); - rc = BIO_reset(bio); - if (rc != 1 && !(rc == 0 && BIO_method_type(bio) == BIO_TYPE_FILE)) { - g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_READ_CERTIFICATE, - _("unable to load certificate: '%s'"), path); - return NULL; - } - - /* maybe the certificate is stored in DER format */ - cert = d2i_X509_bio(bio, NULL); - if (cert) - return g_steal_pointer(&cert); - - g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_READ_CERTIFICATE, - _("unable to load certificate: '%s'"), path); - return NULL; -} - -/* @crl_paths is allowed to be NULL */ -static int load_crls_to_store(X509_STORE *store, const gchar *const *crl_paths, - gboolean err_out_empty_crls, GError **err) -{ - for (const gchar *const *iterator = crl_paths; - iterator != NULL && *iterator != NULL; iterator++) { - const gchar *crl_path = *iterator; - X509_LOOKUP *lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file()); - int count; - - g_assert(crl_path); - - if (!lookup) { - g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, - _("X509 store initialization failed")); - return -1; - } - - /* support *.pem files containing multiple CRLs */ - count = X509_load_crl_file(lookup, crl_path, X509_FILETYPE_PEM); - if (count > 0) - continue; - - count = X509_load_crl_file(lookup, crl_path, X509_FILETYPE_ASN1); - if (count == 1) - continue; - - if (err_out_empty_crls) { - g_set_error(err, PV_CRYPTO_ERROR, - PV_CRYPTO_ERROR_LOAD_CRL, - _("unable to load CRL from: '%s'"), crl_path); - return -1; - } - } - - return 0; -} - -/* returns - * 0 when the certificate is valid, - * -1 when not yet valid, - * 1 when expired - */ -static int check_validity_period(const ASN1_TIME *not_before, const ASN1_TIME *not_after) -{ - if (X509_cmp_current_time(not_before) != -1) - return -1; - - if (X509_cmp_current_time(not_after) != 1) - return 1; - - return 0; -} - -static gint x509_name_entry_get_data0(X509_NAME_ENTRY *entry, const guchar **data, - gsize *data_len) -{ - const ASN1_STRING *asn1_str; - gint tmp_data_len; - - g_assert(data); - g_assert(data_len); - - asn1_str = X509_NAME_ENTRY_get_data(entry); - if (!asn1_str) - return -1; - - tmp_data_len = ASN1_STRING_length(asn1_str); - if (tmp_data_len < 0) - return -1; - - *data = ASN1_STRING_get0_data(asn1_str); - *data_len = (gsize)tmp_data_len; - return 0; -} - -/* The caller must not free *data! */ -static gint x509_name_get_data0_by_NID(X509_NAME *name, gint nid, - const guchar **data, gsize *data_len) -{ - - X509_NAME_ENTRY *entry = NULL; - gint lastpos = -1; - - lastpos = X509_NAME_get_index_by_NID(name, nid, lastpos); - if (lastpos == -1) - return -1; - - entry = X509_NAME_get_entry(name, lastpos); - if (!entry) - return -1; - - if (x509_name_entry_get_data0(entry, data, data_len) < 0) - return -1; - - return 0; -} - -/* @y must be a NULL-terminated string */ -static gboolean x509_name_data_by_nid_equal(X509_NAME *name, gint nid, - const gchar *y) -{ - const guchar *data = NULL; - gsize y_len = strlen(y); - gsize data_len; - - if (x509_name_get_data0_by_NID(name, nid, &data, &data_len) < 0) - return FALSE; - - if (data_len != y_len) - return FALSE; - - return memcmp(data, y, data_len) == 0; -} - -/* Checks whether the subject of @cert is a IBM signing key subject. For this we - * must check that the subject is equal to: 'C = US, ST = New York, L = - * Poughkeepsie or Armonk, O = International Business Machines Corporation, CN = - * International Business Machines Corporation' and the organization unit (OUT) - * must end with the suffix ' Key Signing Service'. - */ -static gboolean has_ibm_signing_subject(X509 *cert) -{ - X509_NAME *subject = X509_get_subject_name(cert); - /* X509_NAME_entry_count is safe to be used with NULL */ - gint entry_count = X509_NAME_entry_count(subject); - g_autofree gchar *data_str = NULL; - const guchar *data; - gsize data_len; - - if (entry_count != PV_IMB_Z_SUBJECT_ENTRY_COUNT) - return FALSE; - - if (!x509_name_data_by_nid_equal(subject, NID_countryName, - PV_IBM_Z_SUBJECT_COUNTRY_NAME)) - return FALSE; - - if (!x509_name_data_by_nid_equal(subject, NID_stateOrProvinceName, - PV_IBM_Z_SUBJECT_STATE)) - return FALSE; - - if (!(x509_name_data_by_nid_equal(subject, NID_localityName, - PV_IBM_Z_SUBJECT_LOCALITY_NAME_POUGHKEEPSIE) || - x509_name_data_by_nid_equal(subject, NID_localityName, - PV_IBM_Z_SUBJECT_LOCALITY_NAME_ARMONK))) - return FALSE; - - if (!x509_name_data_by_nid_equal(subject, NID_organizationName, - PV_IBM_Z_SUBJECT_ORGANIZATION_NAME)) - return FALSE; - - if (!x509_name_data_by_nid_equal(subject, NID_commonName, - PV_IBM_Z_SUBJECT_COMMON_NAME)) - return FALSE; - - if (x509_name_get_data0_by_NID(subject, NID_organizationalUnitName, - &data, &data_len) < 0) - return FALSE; - - /* Make sure that data_str is null-terminated as in general it cannot be - * assumed that @data is null-terminated. - */ - data_str = g_strndup((const gchar *)data, data_len); - if (!g_str_has_suffix(data_str, - PV_IBM_Z_SUBJECT_ORGANIZATIONONAL_UNIT_NAME_SUFFIX)) - return FALSE; - - return TRUE; -} - -static X509_NAME *x509_name_reorder_attributes(const X509_NAME *name, const gint nids[], - gsize nids_len) -{ - gint entry_count = X509_NAME_entry_count(name); - g_autoptr(X509_NAME) ret = NULL; - - if (entry_count < 0) - return NULL; - - if (nids_len != (gsize) entry_count) - return NULL; - - ret = X509_NAME_new(); - if (!ret) - g_abort(); - - for (gsize i = 0; i < nids_len; i++) { - const X509_NAME_ENTRY *entry = NULL; - gint nid = nids[i]; - gint lastpos = -1; - - lastpos = X509_NAME_get_index_by_NID((X509_NAME *)name, nid, lastpos); - if (lastpos == -1) - return NULL; - - entry = X509_NAME_get_entry(name, lastpos); - if (!entry) - return NULL; - - if (X509_NAME_add_entry(ret, entry, -1, 0) != 1) - return NULL; - } - - return g_steal_pointer(&ret); -} - -/** Replace locality 'Armonk' with 'Pougkeepsie'. If Armonk was not set return - * `NULL`. - */ -static X509_NAME *x509_armonk_locality_fixup(const X509_NAME *name) -{ - g_autoptr(X509_NAME) ret = NULL; - int pos; - - /* Check if ``L=Armonk`` */ - if (!x509_name_data_by_nid_equal((X509_NAME *)name, NID_localityName, - PV_IBM_Z_SUBJECT_LOCALITY_NAME_ARMONK)) - return NULL; - - ret = X509_NAME_dup((X509_NAME *)name); - if (!ret) - g_abort(); - - pos = X509_NAME_get_index_by_NID(ret, NID_localityName, -1); - if (pos == -1) - return NULL; - - X509_NAME_ENTRY_free(X509_NAME_delete_entry(ret, pos)); - - /* Create a new name entry at the same position as before */ - if (X509_NAME_add_entry_by_NID( - ret, NID_localityName, MBSTRING_UTF8, - (const unsigned char *)&PV_IBM_Z_SUBJECT_LOCALITY_NAME_POUGHKEEPSIE, - sizeof(PV_IBM_Z_SUBJECT_LOCALITY_NAME_POUGHKEEPSIE) - 1, pos, 0) != 1) - return NULL; - - return g_steal_pointer(&ret); -} - -/* In RFC 5280 the attributes of a (subject/issuer) name is not mandatory - * ordered. The problem is that our certificates are not consistent in the order - * (see https://tools.ietf.org/html/rfc5280#section-4.1.2.4 for details). - * - * This function converts a correct X509_NAME into the broken one. The caller is - * responsible to free the returned value. - */ -X509_NAME *c2b_name(const X509_NAME *name) -{ - gint nids[] = { NID_countryName, NID_organizationName, NID_organizationalUnitName, - NID_localityName, NID_stateOrProvinceName, NID_commonName }; - g_autoptr(X509_NAME) broken_name = NULL; - - g_assert(name); - - /* Try to reorder the attributes */ - broken_name = x509_name_reorder_attributes(name, nids, G_N_ELEMENTS(nids)); - if (broken_name) - return g_steal_pointer(&broken_name); - return X509_NAME_dup((X509_NAME *)name); -} - -/* Verify that SKID(issuer) == AKID(crl) if available */ -static gint check_crl_issuer(X509_CRL *crl, X509 *issuer, GError **err) -{ - g_autoptr(AUTHORITY_KEYID) akid = NULL; - - /* If AKID(@crl) is specified it must match with SKID(@issuer) */ - akid = X509_CRL_get_ext_d2i(crl, NID_authority_key_identifier, NULL, NULL); - if (akid && X509_check_akid(issuer, akid) != X509_V_OK) { - g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_SKID_AKID_MISMATCH, - _("AKID mismatch")); - return -1; - } - - return 0; -} - -/* Verify whether a revocation list @crl is valid and is issued by @cert. For - * this multiple steps must be done: - * - * 1. verify issuer of the CRL matches with the suject name of @cert - * 2. verify the validity period of the CRL - * 3. verify the signature of the CRL - * - * Important: This function doesn't verify whether @cert is allowed to issue a - * CRL. Returns 0 if @crl is valid and issued by @cert, otherwise -1. - */ -gint check_crl_valid_for_cert(X509_CRL *crl, X509 *cert, - gint verify_flags, GError **err) -{ - EVP_PKEY *pkey = X509_get0_pubkey(cert); - - g_assert(crl); - - if (!pkey) { - g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, - _("failed to retrieve public key from the certificate")); - return -1; - } - - if (check_crl_issuer(crl, cert, err) < 0) - return -1; - - /* verify the validity period of the CRL */ - if (!(verify_flags & X509_V_FLAG_NO_CHECK_TIME)) { - const ASN1_TIME *last = X509_CRL_get0_lastUpdate(crl); - const ASN1_TIME *next = X509_CRL_get0_nextUpdate(crl); - - if (!last || !next || check_validity_period(last, next)) { - g_set_error(err, PV_CRYPTO_ERROR, - PV_CRYPTO_ERROR_INVALID_VALIDITY_PERIOD, - _("validity period is not valid")); - return -1; - } - } else { - verify_flags &= ~X509_V_FLAG_NO_CHECK_TIME; - } - - /* verify the signature */ - if (X509_CRL_verify(crl, pkey) != 1) { - g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_CRL_SIGNATURE_INVALID, - _("signature is not valid")); - return -1; - } - g_assert(verify_flags == 0); - return 0; -} - -/* This function contains work-arounds for some known subject(CRT)<->issuer(CRL) - * issues. - */ -static STACK_OF_X509_CRL *quirk_X509_STORE_ctx_get1_crls(X509_STORE_CTX *ctx, - const X509_NAME *subject, GError **err) -{ - g_autoptr(X509_NAME) fixed_subject = NULL; - g_autoptr(STACK_OF_X509_CRL) ret = NULL; - - ret = Pv_X509_STORE_CTX_get1_crls(ctx, subject); - if (ret && sk_X509_CRL_num(ret) > 0) - return g_steal_pointer(&ret); - - /* Workaround to fix the mismatch between issuer name of the * IBM - * signing CRLs and the IBM signing key subject name. Locality name has - * changed from Poughkeepsie to Armonk. - */ - fixed_subject = x509_armonk_locality_fixup(subject); - /* Was the locality replaced? */ - if (fixed_subject) { - X509_NAME *tmp; - - sk_X509_CRL_free(ret); - ret = Pv_X509_STORE_CTX_get1_crls(ctx, fixed_subject); - if (ret && sk_X509_CRL_num(ret) > 0) - return g_steal_pointer(&ret); - - /* Workaround to fix the ordering mismatch between issuer name - * of the IBM signing CRLs and the IBM signing key subject name. - */ - tmp = fixed_subject; - fixed_subject = c2b_name(fixed_subject); - X509_NAME_free(tmp); - sk_X509_CRL_free(ret); - ret = Pv_X509_STORE_CTX_get1_crls(ctx, fixed_subject); - if (ret && sk_X509_CRL_num(ret) > 0) - return g_steal_pointer(&ret); - X509_NAME_free(fixed_subject); - fixed_subject = NULL; - } - - /* Workaround to fix the ordering mismatch between issuer name of the - * IBM signing CRLs and the IBM signing key subject name. - */ - fixed_subject = c2b_name(subject); - sk_X509_CRL_free(ret); - ret = Pv_X509_STORE_CTX_get1_crls(ctx, fixed_subject); - if (ret && sk_X509_CRL_num(ret) > 0) - return g_steal_pointer(&ret); - - g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_NO_CRL, _("no CRL found")); - return NULL; -} - -/* Given a certificate @cert try to find valid revocation lists in @ctx. If no - * valid CRL was found NULL is returned. - */ -STACK_OF_X509_CRL *store_ctx_find_valid_crls(X509_STORE_CTX *ctx, X509 *cert, - GError **err) -{ - g_autoptr(STACK_OF_X509_CRL) ret = NULL; - const gint verify_flags = 0; - X509_NAME *subject = NULL; - - subject = X509_get_subject_name(cert); - if (!subject) { - g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_MALFORMED_CERTIFICATE, - _("certificate is malformed")); - return NULL; - } - - ret = quirk_X509_STORE_ctx_get1_crls(ctx, subject, err); - if (!ret) - return NULL; - - /* Filter out non-valid CRLs for @cert */ - for (gint i = 0; i < sk_X509_CRL_num(ret); i++) { - X509_CRL *crl = sk_X509_CRL_value(ret, i); - - g_assert(crl); - - /* If @crl is not valid remove it from the array and log a - * warning. - */ - if (check_crl_valid_for_cert(crl, cert, verify_flags, err) < 0) { - g_assert(err); - g_warning(_("CRL is not valid: %s"), (*err)->message); - g_clear_error(err); - - /* Remove this certificate from the list and change i-- as the - * array has changed - this is not beautfiul, but right now the - * easiest solution I came up with - */ - if (sk_X509_CRL_delete(ret, i--) != crl) - g_abort(); - - g_clear_pointer(&crl, X509_CRL_free); - } - } - - if (sk_X509_CRL_num(ret) < 1) { - g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_NO_CRL, - _("no valid CRL found")); - return NULL; - } - return g_steal_pointer(&ret); -} - -/* Return a list of all IBM Z signing key certificates in @certs and remove them - * from the chain. Return empty stack if no IBM Z signing key is found. - */ -STACK_OF_X509 *delete_ibm_signing_certs(STACK_OF_X509 *certs) -{ - g_autoptr(STACK_OF_X509) ret = sk_X509_new_null(); - - for (gint i = 0; i < sk_X509_num(certs); i++) { - X509 *cert = sk_X509_value(certs, i); - - g_assert(cert); - - if (!has_ibm_signing_subject(cert)) - continue; - - /* Remove this certificate from the list and change i-- as the - * array has changed - this is not beautfiul, but right now the - * easiest solution I came up with. - */ - if (sk_X509_delete(certs, i--) != cert) - g_abort(); - - if (sk_X509_push(ret, g_steal_pointer(&cert)) == 0) - g_abort(); - } - - return g_steal_pointer(&ret); -} - -X509_STORE *store_setup(const gchar *root_ca_path, const gchar * const *crl_paths, - GError **err) -{ - g_autoptr(X509_STORE) store = X509_STORE_new(); - - g_assert(store); - - /* if @root_ca_path != NULL use the specified root CA only, otherwise use the - * default root CAs found on the system - */ - if (root_ca_path) { - X509_LOOKUP *lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file()); - int count; - - if (!lookup) { - g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, - _("X509 store initialization failed")); - return NULL; - } - - count = X509_load_cert_file(lookup, root_ca_path, X509_FILETYPE_PEM); - if (count > 1) { - g_set_error(err, PV_CRYPTO_ERROR, - PV_CRYPTO_ERROR_LOAD_ROOT_CA, - _("multiple certificates in one PEM file is not supported: '%s'"), - root_ca_path); - return NULL; - } else if (count < 1) { - count = X509_load_cert_file(lookup, root_ca_path, - X509_FILETYPE_ASN1); - if (count != 1) { - g_set_error(err, PV_CRYPTO_ERROR, - PV_CRYPTO_ERROR_LOAD_ROOT_CA, - _("failed to load root certificate from '%s'"), - root_ca_path); - return NULL; - } - } - } else { - /* Load certificates into @store from the hardcoded OpenSSL - * default paths - */ - if (X509_STORE_set_default_paths(store) != 1) { - g_set_error(err, PV_CRYPTO_ERROR, - PV_CRYPTO_ERROR_LOAD_DEFAULT_CA, - _("failed to load system root certificates")); - return NULL; - } - } - - /* Error out if a CRL file was provided that has not at least one CRL*/ - if (load_crls_to_store(store, crl_paths, TRUE, err) < 0) - return NULL; - - return g_steal_pointer(&store); -} - -int store_set_verify_param(X509_STORE *store, GError **err) -{ - g_autoptr(X509_VERIFY_PARAM) param = NULL; - unsigned long flags = X509_V_FLAG_CRL_CHECK | - X509_V_FLAG_CRL_CHECK_ALL | - X509_V_FLAG_TRUSTED_FIRST | - X509_V_FLAG_CHECK_SS_SIGNATURE | - X509_V_FLAG_X509_STRICT | - X509_V_FLAG_POLICY_CHECK; - - /* Create a X509_VERIFY_PARAM structure, which specifies which checks - * should be done by the certificate verification operation - */ - param = X509_VERIFY_PARAM_new(); - if (!param) - g_abort(); - - /* The maximum depth level of the chain of trust for the verification of - * the IBM Z signing key is 2, i.e. IBM Z signing key -> intermediate CA - * -> root CA - */ - X509_VERIFY_PARAM_set_depth(param, 2); - - /* Set minimum allowed security level to at least 112 bits. */ - X509_VERIFY_PARAM_set_auth_level(param, PV_CERTS_SECURITY_LEVEL); - - /* Set verification purpose to 'Any Purpose' and specify that the - * associated trust setting of the default purpose should be used. - */ - if (X509_VERIFY_PARAM_set_purpose(param, - X509_PURPOSE_ANY | X509_TRUST_DEFAULT) != 1) - goto error; - - /* Each certificate from the chain of trust must be checked against a - * CRL to see if it has been revoked. In addition, use trusted - * certificates first mode, check signature of the last certificate, - * strict mode, and verify the policies. - */ - if (X509_VERIFY_PARAM_set_flags(param, flags) != 1) - goto error; - - if (X509_STORE_set1_param(store, param) != 1) - goto error; - - return 0; - -error: - g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, - _("X509 store initialization failed")); - return -1; -} - -/* @cert_paths must contain at least one element, otherwise an error is - * reported. - */ -GSList *load_certificates(const gchar *const *cert_paths, GError **err) -{ - g_autoslist(x509_with_path) ret = NULL; - - for (const gchar *const *iterator = cert_paths; - iterator != NULL && *iterator != NULL; iterator++) { - const gchar *cert_path = *iterator; - g_autoptr(X509) cert = NULL; - - g_assert(cert_path); - - cert = load_cert_from_file(cert_path, err); - if (!cert) - return NULL; - - ret = g_slist_append(ret, x509_with_path_new(cert, cert_path)); - } - if (!ret) { - g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_READ_CERTIFICATE, - _("no certificates specified")); - return NULL; - } - - return g_steal_pointer(&ret); -} - -static X509 *get_cert(const x509_with_path *cert_with_path, G_GNUC_UNUSED GError **err) -{ - g_autoptr(X509) cert = NULL; - - g_assert(cert_with_path && cert_with_path->cert); - - cert = cert_with_path->cert; - if (X509_up_ref(cert) != 1) - g_abort(); - return g_steal_pointer(&cert); -} - -STACK_OF_X509 *get_x509_stack(const GSList *x509_with_path_list) -{ - g_autoslist(X509) certs = NULL; - g_autoptr(GError) err = NULL; - - certs = g_slist_map_x509_with_path_X509(x509_with_path_list, - get_cert, &err); - g_assert_null(err); - return g_slist_to_stack_of_X509(&certs); -} - -x509_with_path *x509_with_path_new(X509 *cert, const gchar *path) -{ - g_autoptr(x509_with_path) ret = g_new(x509_with_path, 1); - - g_assert(cert && path); - - if (X509_up_ref(cert) != 1) - g_abort(); - ret->cert = cert; - ret->path = g_strdup(path); - return g_steal_pointer(&ret); -} - -void x509_with_path_free(x509_with_path *cert) -{ - if (!cert) - return; - - X509_free(cert->cert); - g_free((gchar *)cert->path); - g_free(cert); -} - -x509_pair *x509_pair_new(X509 **cert, STACK_OF_X509_CRL **crls) -{ - g_autoptr(x509_pair) ret = g_new0(x509_pair, 1); - - g_assert(cert); - g_assert(crls); - - ret->cert = g_steal_pointer(cert); - ret->crls = g_steal_pointer(crls); - return g_steal_pointer(&ret); -} - -void x509_pair_free(x509_pair *pair) -{ - if (!pair) - return; - - sk_X509_CRL_pop_free(pair->crls, X509_CRL_free); - X509_free(pair->cert); - g_free(pair); -} - -X509_STORE_CTX *create_store_ctx(X509_STORE *trusted, STACK_OF_X509 *chain, - GError **err) -{ - g_autoptr(X509_STORE_CTX) ctx = X509_STORE_CTX_new(); - - if (!ctx || !X509_STORE_CTX_init(ctx, trusted, NULL, chain)) { - g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, - _("X509 store initialization failed: %s"), - X509_verify_cert_error_string(X509_STORE_CTX_get_error(ctx))); - return NULL; - } - - return g_steal_pointer(&ctx); -} - -gint verify_cert(X509 *cert, X509_STORE_CTX *ctx, GError **err) -{ - gint rc; - - X509_STORE_CTX_set_cert(ctx, cert); - rc = X509_verify_cert(ctx); - if (rc != 1) { - X509 *tmp_cert = NULL; - - tmp_cert = X509_STORE_CTX_get_current_cert(ctx); - if (tmp_cert) { - g_autofree char *subj_name = X509_NAME_oneline( - X509_get_subject_name(tmp_cert), NULL, 0); - g_set_error(err, PV_CRYPTO_ERROR, - PV_CRYPTO_ERROR_INTERNAL, - _("failed to verify certificate '%s': %s"), - subj_name, - X509_verify_cert_error_string( - X509_STORE_CTX_get_error(ctx))); - } else { - g_set_error(err, PV_CRYPTO_ERROR, - PV_CRYPTO_ERROR_INTERNAL, - _("failed to verify certificate: %s"), - X509_verify_cert_error_string( - X509_STORE_CTX_get_error(ctx))); - } - - return -1; - } - - return 0; -} - -static int security_level_to_bits(int level) -{ - static int security_bits[] = { 0, 80, 112, 128, 192, 256 }; - - g_assert(level > 0 && level < (int)G_N_ELEMENTS(security_bits)); - - return security_bits[level]; -} - -/* Used for the caching of the downloaded CRLs */ -static GHashTable *cached_crls; - -void pv_crypto_init(void) -{ - if (cached_crls) - return; - cached_crls = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, - (GDestroyNotify)X509_CRL_free); -} - -void pv_crypto_cleanup(void) -{ - if (!cached_crls) - return; - g_clear_pointer(&cached_crls, g_hash_table_destroy); -} - -gint check_chain_parameters(const STACK_OF_X509 *chain, - GError **err) -{ - const X509_NAME *ca_x509_subject = NULL; - g_autofree gchar *ca_subject = NULL; - gint len = sk_X509_num(chain); - X509 *ca = NULL; - - /* at least one root and one leaf certificate must be defined */ - if (len < 2) { - g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, - _("there must be at least on root and one leaf certificate in the chain of trust")); - return -1; - } - - /* get the root certificate of the chain of trust */ - ca = sk_X509_value(chain, len - 1); - if (!ca) { - g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, - _("no root certificate found")); - return -1; - } - - ca_x509_subject = X509_get_subject_name(ca); - if (!ca_x509_subject) { - g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, - _("subject of the root CA cannot be retrieved")); - return -1; - } - - ca_subject = X509_NAME_oneline(ca_x509_subject, NULL, 0); - if (!ca_subject) { - g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, - _("subject name of the root CA cannot be retrieved")); - return -1; - } - - g_info("Root CA used: '%s'", ca_subject); - return 0; -} - -/* It's almost the same as X509_check_issed from OpenSSL does except that we - * don't check the key usage of the potential issuer. This means we check: - * 1. Check whether the akid(cert) (if available) matches the issuer skid - * 2. Check that the cert algrithm matches the subject algorithm - * 3. Verify the signature of certificate @cert is using the public key of - * @issuer. - */ -static gint check_host_key_issued(X509 *cert, X509 *issuer, GError **err) -{ - g_autoptr(AUTHORITY_KEYID) akid = NULL; - - akid = X509_get_ext_d2i(cert, NID_authority_key_identifier, NULL, NULL); - if (akid && X509_check_akid(issuer, akid) != X509_V_OK) { - g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_SKID_AKID_MISMATCH, - _("AKID mismatch")); - return -1; - } - - if (check_signature_algo_match(X509_get0_pubkey(issuer), cert, err) < 0) - return -1; - - if (X509_verify(cert, X509_get0_pubkey(issuer)) != 1) { - g_set_error(err, PV_CRYPTO_ERROR, - PV_CRYPTO_ERROR_CERT_SIGNATURE_INVALID, - _("Signature verification failed")); - return -1; - } - - return 0; -} - -static gboolean is_cert_revoked(X509 *cert, X509_CRL *crl) -{ - X509_REVOKED *revoked = NULL; - gint rc; - - if (!cert || !crl) - g_abort(); - - rc = X509_CRL_get0_by_serial(crl, &revoked, - (ASN1_INTEGER *)X509_get0_serialNumber(cert)); - if (rc == 0) - return FALSE; - - if (revoked) - return TRUE; - - return FALSE; -} - -/* Get the first http[s] URL from a DIST_POINT */ -static const char *get_first_dp_url(DIST_POINT *dp) -{ - GENERAL_NAMES *general_names; - - g_assert(dp); - - if (!dp->distpoint || dp->distpoint->type != 0) - return NULL; - - general_names = dp->distpoint->name.fullname; - for (gint i = 0; i < sk_GENERAL_NAME_num(general_names); i++) { - GENERAL_NAME *name = sk_GENERAL_NAME_value(general_names, i); - g_autofree const gchar *uri_str = NULL; - ASN1_STRING *uri_asn1; - const gchar *uri_data; - gint uri_data_len; - gint type; - - uri_asn1 = GENERAL_NAME_get0_value(name, &type); - if (type != GEN_URI) - continue; - uri_data_len = ASN1_STRING_length(uri_asn1); - if (uri_data_len < 0) - continue; - uri_data = (const gchar *)ASN1_STRING_get0_data(uri_asn1); - /* Make sure that uri_str is null-terminated as in general it - * cannot be assumed that @uri_data is null-terminated. - */ - uri_str = g_strndup(uri_data, - (gsize)uri_data_len); - if (g_str_has_prefix(uri_str, "http://")) - return uri_data; - if (g_str_has_prefix(uri_str, "https://")) - return uri_data; - } - return NULL; -} - -static gboolean insert_crl(const X509_NAME *name, X509_CRL *crl) -{ - g_autofree gchar *key = NULL; - - g_assert(name); - - key = X509_NAME_oneline(name, NULL, 0); - if (!key) - g_abort(); - if (X509_CRL_up_ref(crl) != 1) - g_abort(); - return g_hash_table_insert(cached_crls, g_steal_pointer(&key), crl); -} - -/* Caller is responsible for free'ing */ -static X509_CRL *lookup_crl(const X509_NAME *name) -{ - g_autoptr(X509_CRL) crl = NULL; - g_autofree gchar *key = NULL; - - g_assert(name); - - key = X509_NAME_oneline(name, NULL, 0); - if (!key) - g_abort(); - crl = g_hash_table_lookup(cached_crls, key); - if (crl) { - if (X509_CRL_up_ref(crl) != 1) - g_abort(); - return g_steal_pointer(&crl); - } - return NULL; -} - -/* Returns empty stack if no CRL downloaded. */ -static STACK_OF_X509_CRL *crls_download_cb(const X509_STORE_CTX *ctx, const X509_NAME *nm) -{ - g_autoptr(STACK_OF_X509_CRL) crls = NULL; - g_autoptr(X509_CRL) crl = NULL; - /* must not be free'd */ - X509 *cert = NULL; - - crls = sk_X509_CRL_new_null(); - if (!crls) - g_abort(); - cert = Pv_X509_STORE_CTX_get_current_cert(ctx); - if (!cert) - return g_steal_pointer(&crls); - g_assert(X509_NAME_cmp(X509_get_issuer_name(cert), nm) == 0); - crl = lookup_crl(nm); - if (!crl) { - /* ignore error */ - crl = load_crl_by_cert(cert, NULL); - if (!crl) - return g_steal_pointer(&crls); - g_assert_true(insert_crl(nm, crl)); - } - if (sk_X509_CRL_push(crls, g_steal_pointer(&crl)) == 0) - g_abort(); - return g_steal_pointer(&crls); -} - -void STACK_OF_DIST_POINT_free(STACK_OF_DIST_POINT *stack) -{ - if (!stack) - return; - - sk_DIST_POINT_pop_free(stack, DIST_POINT_free); -} - -void STACK_OF_X509_free(STACK_OF_X509 *stack) -{ - if (!stack) - return; - - sk_X509_pop_free(stack, X509_free); -} - -void STACK_OF_X509_CRL_free(STACK_OF_X509_CRL *stack) -{ - if (!stack) - return; - - sk_X509_CRL_pop_free(stack, X509_CRL_free); -} - -/* Downloaded CRLs have a higher precedence than the CRLs specified on the - * command line. - */ -static STACK_OF_X509_CRL *crls_cb(const X509_STORE_CTX *ctx, const X509_NAME *nm) -{ - g_autoptr(STACK_OF_X509_CRL) crls = crls_download_cb(ctx, nm); - - if (sk_X509_CRL_num(crls) > 0) - return g_steal_pointer(&crls); - return Pv_X509_STORE_CTX_get1_crls(ctx, nm); -} - -/* Set up CRL lookup with download support */ -void store_setup_crl_download(X509_STORE *st) -{ - Pv_X509_STORE_set_lookup_crls(st, crls_cb); -} - -/* Download a CRL using the URI specified in the distribution @crldp */ -static X509_CRL *load_crl_by_dist_point(DIST_POINT *crldp, GError **err) -{ - const gchar *uri = get_first_dp_url(crldp); - g_autoptr(X509_CRL) crl = NULL; - - if (!uri) { - g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, - _("no valid URL specified in distribution point")); - return NULL; - } - - if (load_crl_from_web(uri, &crl, err) < 0) - return NULL; - - return g_steal_pointer(&crl); -} - -/* This function returns the first X509_CRL found from the CRL distribution - * points specified in @cert. This function could be optimized by filtering - * duplicate certificates and/or filtering duplicated URIs. - */ -X509_CRL *load_crl_by_cert(X509 *cert, GError **err) -{ - g_autoptr(STACK_OF_DIST_POINT) crldps = NULL; - g_autoptr(X509_CRL) ret = NULL; - - g_assert(cert); - - crldps = X509_get_ext_d2i(cert, NID_crl_distribution_points, NULL, NULL); - if (!crldps || sk_DIST_POINT_num(crldps) == 0) { - g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_NO_CRLDP, - _("no distribution point found")); - return NULL; - } - - for (int i = 0; i < sk_DIST_POINT_num(crldps); i++) { - DIST_POINT *crldp = sk_DIST_POINT_value(crldps, i); - - g_assert(crldp); - - /* ignore error */ - ret = load_crl_by_dist_point(crldp, NULL); - if (ret) - return g_steal_pointer(&ret); - } - - g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_FAILED_DOWNLOAD_CRL, - _("failed to download CRL")); - return NULL; -} - -STACK_OF_X509_CRL *try_load_crls_by_certs(GSList *certs_with_path) -{ - g_autoptr(STACK_OF_X509_CRL) ret = sk_X509_CRL_new_null(); - if (!ret) - g_abort(); - - for (GSList *iterator = certs_with_path; iterator; - iterator = iterator->next) { - x509_with_path *cert_with_path = iterator->data; - X509 *cert = cert_with_path->cert; - g_autoptr(X509_CRL) crl = NULL; - - g_assert(cert); - - /* ignore error */ - crl = load_crl_by_cert(cert, NULL); - if (!crl) - continue; - - if (sk_X509_CRL_push(ret, g_steal_pointer(&crl)) == 0) - g_abort(); - } - - return g_steal_pointer(&ret); -} - -/* Assumptions are that the issuer_crt and issuer_crl is a trusted IBM Z - * signing certificate/revocation list. This function verifies a host-key - * document. To do so multiple steps are required: - * - * 1. issuer(host_key) == subject(issuer_crt) - * 2. Signature verification - * 3. @host_key must not be expired - * 4. @host_key must not be revoked - */ -gint verify_host_key(X509 *host_key, GSList *issuer_pairs, - gint verify_flags, int level, GError **err) -{ - g_assert(host_key); - - const gint exp_security_bits = security_level_to_bits(level); - EVP_PKEY *pkey = X509_get0_pubkey(host_key); - gboolean successfully_checked = FALSE; - gint pkey_security_bits; - - if (!pkey) { - g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, - _("failed to retrieve public key")); - return -1; - } - - /* check key level, if necessary */ - pkey_security_bits = EVP_PKEY_security_bits(pkey); - if (exp_security_bits > 0 && pkey_security_bits < exp_security_bits) { - g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_VERIFICATION, - _("not enough bits of security (%d, %d expected)"), - pkey_security_bits, exp_security_bits); - return -1; - } - - if (!(verify_flags & X509_V_FLAG_NO_CHECK_TIME)) { - const ASN1_TIME *last = X509_get0_notBefore(host_key); - const ASN1_TIME *next = X509_get0_notAfter(host_key); - - if (!last || !next || check_validity_period(last, next)) { - g_set_error(err, PV_CRYPTO_ERROR, - PV_CRYPTO_ERROR_INVALID_VALIDITY_PERIOD, - _("validity period is not valid")); - return -1; - } - } else { - verify_flags &= ~X509_V_FLAG_NO_CHECK_TIME; - } - - /* Verify that the host_key was issued by a certificate and that it - * wasn't revoked. - */ - for (GSList *iterator = issuer_pairs; iterator; - iterator = iterator->next) { - const x509_pair *pair = iterator->data; - STACK_OF_X509_CRL *issuer_crls = NULL; - X509 *issuer_cert = NULL; - - g_assert(pair); - - issuer_cert = pair->cert; - issuer_crls = pair->crls; - - g_assert(issuer_cert); - - /* Verify that the issuer(host_key) == subject(issuer_cert) and - * that the signature is valid - */ - if (check_host_key_issued(host_key, issuer_cert, NULL) < 0) - continue; - - /* Check against CRL */ - if (verify_flags & X509_V_FLAG_CRL_CHECK) { - gboolean crl_checked = FALSE; - - verify_flags &= ~X509_V_FLAG_CRL_CHECK; - for (gint i = 0; i < sk_X509_CRL_num(issuer_crls); i++) { - X509_CRL *issuer_crl = - sk_X509_CRL_value(issuer_crls, i); - - g_assert(issuer_crl); - - if (is_cert_revoked(host_key, issuer_crl)) { - g_set_error(err, PV_CRYPTO_ERROR, - PV_CRYPTO_ERROR_CERT_REVOKED, - _("certificate revoked")); - return -1; - } - - crl_checked = TRUE; - } - - if (!crl_checked) { - g_set_error(err, PV_CRYPTO_ERROR, - PV_CRYPTO_ERROR_INTERNAL, - _("no valid CRL found")); - return -1; - } - successfully_checked = TRUE; - break; - } - } - - if (!successfully_checked) { - g_set_error(err, PV_CRYPTO_ERROR, - PV_CRYPTO_ERROR_NO_ISSUER_IBM_Z_FOUND, - _("no IBM Z signing key that issued this host-key document found")); - return -1; - } - - /* were some unsupported flags specified? */ - g_assert(verify_flags == 0); - return 0; -} - -EVP_PKEY *read_ec_pubkey_cert(X509 *cert, gint nid, - GError **err) -{ - g_autoptr(EVP_PKEY) ret = NULL; - - ret = X509_get_pubkey(cert); - if (!ret) { - g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INVALID_PARM, - _("Failed to get public key from host-key document")); - return NULL; - } - - if (!certificate_uses_correct_curve(ret, nid, err)) { - g_prefix_error(err, - _("Host-key document doesn\'t use correct EC curve")); - return NULL; - } - - return g_steal_pointer(&ret); -} - -static gint __encrypt_decrypt_bio(const struct cipher_parms *parms, BIO *b_in, - BIO *b_out, gsize *size_in, gsize *size_out, - gboolean encrypt, GError **err) -{ - gint num_bytes_read, num_bytes_written; - g_autoptr(EVP_CIPHER_CTX) ctx = NULL; - g_autoptr(BIGNUM) tweak_num = NULL; - const EVP_CIPHER *cipher = parms->cipher; - gint cipher_block_size = EVP_CIPHER_block_size(cipher); - guchar in_buf[PAGE_SIZE], - out_buf[PAGE_SIZE + (guint)cipher_block_size]; - const PvBuffer *key = parms->key; - const PvBuffer *tweak = parms->iv_or_tweak; - g_autofree guchar *tmp_tweak = NULL; - gint out_len, tweak_size; - gsize tmp_size_in = 0, tmp_size_out = 0; - - g_assert(cipher_block_size > 0); - g_assert(key); - g_assert(tweak); - g_assert(tweak->size <= INT_MAX); - - /* copy the value for leaving the original value untouched */ - tmp_tweak = g_malloc0(tweak->size); - memcpy(tmp_tweak, tweak->data, tweak->size); - tweak_size = (int)tweak->size; - tweak_num = BN_bin2bn(tmp_tweak, tweak_size, NULL); - if (!tweak_num) { - g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, - _("BN_bin2bn failed")); - return -1; - } - - ctx = EVP_CIPHER_CTX_new(); - if (!ctx) - g_abort(); - - /* don't set the key or tweak right away as we want to check - * lengths before - */ - if (EVP_CipherInit_ex(ctx, cipher, NULL, NULL, NULL, encrypt) != 1) { - g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, - _("EVP_CipherInit_ex failed")); - return -1; - } - - /* Now we can set the key and tweak */ - if (EVP_CipherInit_ex(ctx, NULL, NULL, key->data, tmp_tweak, encrypt) != - 1) { - g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, - _("EVP_CipherInit_ex failed")); - return -1; - } - - do { - memset(in_buf, 0, sizeof(in_buf)); - /* Read in data in 4096 bytes blocks. Update the ciphering - * with each read. - */ - num_bytes_read = BIO_read(b_in, in_buf, (int)PAGE_SIZE); - if (num_bytes_read < 0) { - g_set_error(err, PV_CRYPTO_ERROR, - PV_CRYPTO_ERROR_INTERNAL, - _("Failed to read")); - return -1; - } - tmp_size_in += (guint)num_bytes_read; - - /* in case we reached the end and it's not the special - * case of an empty component we can break here - */ - if (num_bytes_read == 0 && tmp_size_in != 0) - break; - - if (EVP_CipherUpdate(ctx, out_buf, &out_len, in_buf, - sizeof(in_buf)) != 1) { - g_set_error(err, PV_CRYPTO_ERROR, - PV_CRYPTO_ERROR_INTERNAL, - _("EVP_CipherUpdate failed")); - return -1; - } - g_assert(out_len >= 0); - - num_bytes_written = BIO_write(b_out, out_buf, out_len); - if (num_bytes_written != out_len) { - g_set_error(err, PV_CRYPTO_ERROR, - PV_CRYPTO_ERROR_INTERNAL, - _("Failed to write")); - return -1; - } - tmp_size_out += (guint)num_bytes_written; - - /* Set new tweak value. Please keep in mind that the - * tweaks are stored in big-endian form. Therefore we - * must use the correct OpenSSL functions - */ - if (BN_add_word(tweak_num, PAGE_SIZE) != 1) { - g_set_error(err, PV_CRYPTO_ERROR, - PV_CRYPTO_ERROR_INTERNAL, - _("BN_add_word failed")); - return -1; - } - g_assert(BN_num_bytes(tweak_num) > 0); - g_assert(BN_num_bytes(tweak_num) <= tweak_size); - - if (BN_bn2binpad(tweak_num, tmp_tweak, tweak_size) < 0) { - g_set_error(err, PV_CRYPTO_ERROR, - PV_CRYPTO_ERROR_INTERNAL, - _("BN_bn2binpad failed")); - return -1; - }; - - /* set new tweak */ - if (EVP_CipherInit_ex(ctx, NULL, NULL, NULL, tmp_tweak, - encrypt) != 1) { - g_set_error(err, PV_CRYPTO_ERROR, - PV_CRYPTO_ERROR_INTERNAL, - _("EVP_CipherInit_ex failed")); - return -1; - } - } while (num_bytes_read == PAGE_SIZE); - - /* Now cipher the final block and write it out to file */ - if (EVP_CipherFinal_ex(ctx, out_buf, &out_len) != 1) { - g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, - _("EVP_CipherFinal_ex failed")); - return -1; - } - g_assert(out_len >= 0); - - num_bytes_written = BIO_write(b_out, out_buf, out_len); - if (num_bytes_written < 0) { - g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, - _("Failed to write")); - return -1; - } - g_assert(out_len == num_bytes_written); - tmp_size_out += (guint)out_len; - - if (BIO_flush(b_out) != 1) { - g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, - _("Failed to flush")); - return -1; - } - - *size_in = tmp_size_in; - *size_out = tmp_size_out; - return 0; -} - -static PvBuffer *__encrypt_decrypt_buffer(const struct cipher_parms *parms, - const PvBuffer *in, gboolean encrypt, - GError **err) -{ - g_autoptr(PvBuffer) ret = NULL; - g_autoptr(BIO) b_out = NULL; - g_autoptr(BIO) b_in = NULL; - gsize in_size, out_size; - gchar *data = NULL; - long data_size; - - g_assert(in->size <= INT_MAX); - - b_in = BIO_new_mem_buf(in->data, (int)in->size); - if (!b_in) - g_abort(); - - b_out = BIO_new(BIO_s_mem()); - if (!b_out) - g_abort(); - - if (__encrypt_decrypt_bio(parms, b_in, b_out, &in_size, &out_size, - encrypt, err) < 0) - return NULL; - - data_size = BIO_get_mem_data(b_out, &data); - if (data_size < 0) { - g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, - _("Could not read buffer")); - return NULL; - } - - ret = pv_buffer_alloc((unsigned long)data_size); - memcpy(ret->data, data, ret->size); - return g_steal_pointer(&ret); -} - -PvBuffer *encrypt_buf(const struct cipher_parms *parms, const PvBuffer *in, - GError **err) -{ - return __encrypt_decrypt_buffer(parms, in, TRUE, err); -} - -PvBuffer *decrypt_buf(const struct cipher_parms *parms, const PvBuffer *in, - GError **err) -{ - return __encrypt_decrypt_buffer(parms, in, FALSE, err); -} - -static gint __encrypt_decrypt_file(const struct cipher_parms *parms, - const gchar *path_in, const gchar *path_out, - gsize *size_in, gsize *size_out, gboolean encrypt, - GError **err) -{ - g_autoptr(BIO) b_out = NULL; - g_autoptr(BIO) b_in = NULL; - - b_in = BIO_new_file(path_in, "rb"); - if (!b_in) { - g_set_error(err, PV_CRYPTO_ERROR, - PV_CRYPTO_ERROR_READ_CERTIFICATE, - _("Failed to read file '%s'"), path_in); - return -1; - } - - b_out = BIO_new_file(path_out, "wb"); - if (!b_out) { - g_set_error(err, PV_CRYPTO_ERROR, - PV_CRYPTO_ERROR_READ_CERTIFICATE, - _("Failed to write file '%s'"), path_out); - return -1; - } - - if (__encrypt_decrypt_bio(parms, b_in, b_out, size_in, size_out, - encrypt, err) < 0) - return -1; - - return 0; -} - -gint encrypt_file(const struct cipher_parms *parms, const gchar *path_in, - const gchar *path_out, gsize *in_size, gsize *out_size, - GError **err) -{ - return __encrypt_decrypt_file(parms, path_in, path_out, in_size, - out_size, TRUE, err); -} - -G_GNUC_UNUSED static gint decrypt_file(const struct cipher_parms *parms, - const gchar *path_in, const gchar *path_out, - gsize *in_size, gsize *out_size, - GError **err) -{ - return __encrypt_decrypt_file(parms, path_in, path_out, in_size, - out_size, FALSE, err); -} - -/* GCM mode uses (zero-)padding */ -static int64_t gcm_encrypt_decrypt(const PvBuffer *in, const PvBuffer *aad, - const struct cipher_parms *parms, - PvBuffer *out, PvBuffer *tag, - enum PvCryptoMode mode, GError **err) -{ - g_autoptr(EVP_CIPHER_CTX) ctx = NULL; - const EVP_CIPHER *cipher = parms->cipher; - const PvBuffer *iv = parms->iv_or_tweak; - gboolean encrypt = mode == PV_ENCRYPT; - const PvBuffer *key = parms->key; - int64_t ret = -1; - gint len = -1; - - g_assert(cipher); - g_assert(key); - g_assert(iv); - /* Checks for later casts */ - g_assert(aad->size <= INT_MAX); - g_assert(in->size <= INT_MAX); - g_assert(tag->size <= INT_MAX); - g_assert(iv->size <= INT_MAX); - g_assert(out->size == in->size); - - ctx = EVP_CIPHER_CTX_new(); - if (!ctx) - g_abort(); - - /* First, set the cipher algorithm so we can verify our key/IV lengths - */ - if (EVP_CipherInit_ex(ctx, cipher, NULL, NULL, NULL, encrypt) != 1) { - g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, - _("EVP_CIPHER_CTX_new failed")); - return -1; - } - - /* Set IV length */ - if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, (int)iv->size, NULL) != 1) { - g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, - _("EVP_CIPHER_CTX_ex failed")); - return -1; - } - - /* Initialise key and IV */ - if (EVP_CipherInit_ex(ctx, NULL, NULL, key->data, iv->data, encrypt) != - 1) { - g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, - _("EVP_CipherInit_ex failed")); - return -1; - } - - if (aad->size > 0) { - /* Provide any AAD data */ - if (EVP_CipherUpdate(ctx, NULL, &len, aad->data, - (int)aad->size) != 1) { - g_set_error(err, PV_CRYPTO_ERROR, - PV_CRYPTO_ERROR_INTERNAL, - _("EVP_CipherUpdate failed")); - return -1; - } - g_assert(len == (int)aad->size); - } - - /* Provide data to be en/decrypted */ - if (EVP_CipherUpdate(ctx, out->data, &len, in->data, (int)in->size) != 1) { - g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, - _("EVP_CipherUpdate failed")); - return -1; - } - ret = len; - - if (!encrypt) { - /* Set expected tag value */ - if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, - (int)tag->size, tag->data) != 1) { - g_set_error(err, PV_CRYPTO_ERROR, - PV_CRYPTO_ERROR_INTERNAL, - _("Setting the GCM tag failed")); - return -1; - } - } - - /* Finalize the en/decryption */ - if (EVP_CipherFinal_ex(ctx, (guchar *)out->data + len, &len) != 1) { - g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, - _("EVP_CipherFinal_ex failed")); - return -1; - } - ret += len; - - if (encrypt) { - /* Get the tag */ - if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, - (int)tag->size, tag->data) != 1) { - g_set_error(err, PV_CRYPTO_ERROR, - PV_CRYPTO_ERROR_INTERNAL, - _("Getting the GCM tag failed")); - return -1; - } - } - - g_assert(ret == (int)in->size); - return ret; -} - -int64_t gcm_encrypt(const PvBuffer *in, const PvBuffer *aad, - const struct cipher_parms *parms, PvBuffer *out, PvBuffer *tag, - GError **err) -{ - return gcm_encrypt_decrypt(in, aad, parms, out, tag, PV_ENCRYPT, err); -} diff --git a/genprotimg/src/utils/crypto.h b/genprotimg/src/utils/crypto.h deleted file mode 100644 index e45e57df..00000000 --- a/genprotimg/src/utils/crypto.h +++ /dev/null @@ -1,174 +0,0 @@ -/* - * General cryptography helper functions and definitions - * - * Copyright IBM Corp. 2020 - * - * s390-tools is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See LICENSE for details. - */ - -#ifndef PV_UTILS_CRYPTO_H -#define PV_UTILS_CRYPTO_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "common.h" -#include "include/pv_crypto_def.h" -#include "lib/zt_common.h" - -#include "buffer.h" - -#define AES_256_GCM_IV_SIZE 12 -#define AES_256_GCM_TAG_SIZE 16 - -#define AES_256_XTS_TWEAK_SIZE 16 -#define AES_256_XTS_KEY_SIZE 64 - -#define CRL_DOWNLOAD_TIMEOUT_MS 3000 -#define CRL_DOWNLOAD_MAX_SIZE (1024 * 1024) /* in bytes */ - -enum PvCryptoMode { - PV_ENCRYPT, - PV_DECRYPT, -}; - -typedef GSList HostKeyList; - -/* play nice with g_autoptr */ -typedef STACK_OF(DIST_POINT) STACK_OF_DIST_POINT; -typedef STACK_OF(X509) STACK_OF_X509; -typedef STACK_OF(X509_CRL) STACK_OF_X509_CRL; - -void STACK_OF_DIST_POINT_free(STACK_OF_DIST_POINT *stack); -void STACK_OF_X509_free(STACK_OF_X509 *stack); -void STACK_OF_X509_CRL_free(STACK_OF_X509_CRL *stack); - -typedef struct { - X509 *cert; - const gchar *path; -} x509_with_path; - -x509_with_path *x509_with_path_new(X509 *cert, const gchar *path); -void x509_with_path_free(x509_with_path *cert); - -typedef struct { - X509 *cert; - STACK_OF_X509_CRL *crls; -} x509_pair; - -x509_pair *x509_pair_new(X509 **cert, STACK_OF_X509_CRL **crls); -void x509_pair_free(x509_pair *pair); - -/* Register auto cleanup functions */ -WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(ASN1_INTEGER, ASN1_INTEGER_free) -WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(ASN1_OCTET_STRING, ASN1_OCTET_STRING_free) -WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(AUTHORITY_KEYID, AUTHORITY_KEYID_free) -WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(BIGNUM, BN_free) -WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(BIO, BIO_free_all) -WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(BN_CTX, BN_CTX_free) -WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(EC_GROUP, EC_GROUP_free) -WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(EC_KEY, EC_KEY_free) -WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(EC_POINT, EC_POINT_free) -WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(EVP_CIPHER_CTX, EVP_CIPHER_CTX_free) -WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(EVP_MD_CTX, EVP_MD_CTX_free) -WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(EVP_PKEY, EVP_PKEY_free) -WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(EVP_PKEY_CTX, EVP_PKEY_CTX_free) -WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(STACK_OF_DIST_POINT, STACK_OF_DIST_POINT_free); -WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(STACK_OF_X509, STACK_OF_X509_free); -WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(STACK_OF_X509_CRL, STACK_OF_X509_CRL_free); -WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(X509, X509_free) -WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(X509_CRL, X509_CRL_free) -WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(X509_LOOKUP, X509_LOOKUP_free) -WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(X509_NAME, X509_NAME_free) -WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(x509_pair, x509_pair_free) -WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(X509_STORE, X509_STORE_free) -WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(X509_STORE_CTX, X509_STORE_CTX_free) -WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(X509_VERIFY_PARAM, X509_VERIFY_PARAM_free) -WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(x509_with_path, x509_with_path_free) - -union cmp_index { - struct { - uint16_t idx; - guchar rand[6]; - } __packed; - uint64_t data; -}; - -/* The tweak is always stored in big endian format */ -union tweak { - struct { - union cmp_index cmp_idx; - uint64_t page_idx; /* page index */ - } __packed; - uint8_t data[AES_256_XTS_TWEAK_SIZE]; -}; - -struct cipher_parms { - const EVP_CIPHER *cipher; - const PvBuffer *key; - const PvBuffer *iv_or_tweak; -}; - -int check_crl_valid_for_cert(X509_CRL *crl, X509 *cert, - gint verify_flags, GError **err); -void pv_crypto_init(void); -void pv_crypto_cleanup(void); -gint verify_host_key(X509 *host_key, GSList *issuer_pairs, - gint verify_flags, int level, GError **err); -X509 *load_cert_from_file(const char *path, GError **err); -X509_CRL *load_crl_from_file(const gchar *path, GError **err); -GSList *load_certificates(const gchar *const *cert_paths, GError **err); -STACK_OF_X509 *get_x509_stack(const GSList *x509_with_path_list); -X509_STORE *store_setup(const gchar *root_ca_path, - const gchar * const *crl_paths, - GError **err); -int store_set_verify_param(X509_STORE *store, GError **err); -X509_CRL *load_crl_by_cert(X509 *cert, GError **err); -STACK_OF_X509_CRL *try_load_crls_by_certs(GSList *certs_with_path); -gint check_chain_parameters(const STACK_OF_X509 *chain, GError **err); -X509_NAME *c2b_name(const X509_NAME *name); - -STACK_OF_X509 *delete_ibm_signing_certs(STACK_OF_X509 *certs); -STACK_OF_X509_CRL *store_ctx_find_valid_crls(X509_STORE_CTX *ctx, X509 *cert, - GError **err); -X509_STORE_CTX *create_store_ctx(X509_STORE *trusted, STACK_OF_X509 *chain, - GError **err); -gint verify_cert(X509 *cert, X509_STORE_CTX *ctx, GError **err); -X509_CRL *get_first_valid_crl(X509_STORE_CTX *ctx, X509 *cert, GError **err); -void store_setup_crl_download(X509_STORE *st); -EVP_PKEY *read_ec_pubkey_cert(X509 *cert, gint nid, GError **err); - -PvBuffer *compute_exchange_key(EVP_PKEY *cust, EVP_PKEY *host, GError **err); -PvBuffer *generate_aes_key(guint size, GError **err); -PvBuffer *generate_aes_iv(guint size, GError **err); -EVP_PKEY *generate_ec_key(gint nid, GError **err); -gint generate_tweak(union tweak *tweak, uint16_t i, GError **err); -union ecdh_pub_key *evp_pkey_to_ecdh_pub_key(EVP_PKEY *key, GError **err); -EVP_MD_CTX *digest_ctx_new(const EVP_MD *md, GError **err); -PvBuffer *digest_ctx_finalize(EVP_MD_CTX *ctx, GError **err); -PvBuffer *sha256_buffer(const PvBuffer *buf, GError **err); -int64_t gcm_encrypt(const PvBuffer *in, const PvBuffer *aad, - const struct cipher_parms *parms, PvBuffer *out, - PvBuffer *tag, GError **err); -gint encrypt_file(const struct cipher_parms *parms, const gchar *in_path, - const gchar *path_out, gsize *in_size, gsize *out_size, - GError **err); -PvBuffer *encrypt_buf(const struct cipher_parms *parms, const PvBuffer *in, - GError **err); -G_GNUC_UNUSED PvBuffer *decrypt_buf(const struct cipher_parms *parms, - const PvBuffer *in, GError **err); - -#endif diff --git a/genprotimg/src/utils/curl.c b/genprotimg/src/utils/curl.c deleted file mode 100644 index a8ef3f6c..00000000 --- a/genprotimg/src/utils/curl.c +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Libcurl utils - * - * Copyright IBM Corp. 2020 - * - * s390-tools is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See LICENSE for details. - */ - -#include -#include -#include -#include - -#include "lib/zt_common.h" -#include "pv/pv_error.h" - -#include "curl.h" - -struct UserData { - GByteArray *buffer; - guint max_size; -}; - -static size_t write_callback(char *ptr, size_t size, size_t nmemb, void *userdata) -{ - g_assert(userdata); - struct UserData *data = (struct UserData *)userdata; - GByteArray *buffer = data->buffer; - guint64 actual_size; - size_t err; - - g_assert(buffer); - - if (!g_uint64_checked_mul(&actual_size, size, nmemb)) - g_abort(); - - /* Signal an error condition by returning a amount that differs - * from the amount passed to the callback. This results in a - * CURLE_WRITE_ERROR. - */ - err = actual_size + 1; - - if (actual_size > G_MAXUINT) - return err; - - data->buffer = g_byte_array_append(buffer, (guchar *)ptr, (guint)actual_size); - if (data->buffer->len > data->max_size) - return err; - - return actual_size; -} - -gint curl_init(void) -{ - if (curl_global_init(CURL_GLOBAL_ALL) != 0) - return -1; - return 0; -} - -void curl_cleanup(void) -{ - curl_global_cleanup(); -} - -GByteArray *curl_download(const gchar *url, long timeout_ms, guint max_size, - GError **err) -{ - g_autoptr(GByteArray) ret = NULL; - g_autoptr(CURL) handle = NULL; - g_autofree gchar *agent = NULL; - struct UserData userdata; - CURLcode rc; - - /* set up curl session */ - handle = curl_easy_init(); - if (!handle) - g_abort(); - - /* follow redirection */ - rc = curl_easy_setopt(handle, CURLOPT_FOLLOWLOCATION, 1l); - if (rc != CURLE_OK) - goto curl_err; - rc = curl_easy_setopt(handle, CURLOPT_TIMEOUT_MS, timeout_ms); - if (rc != CURLE_OK) - goto curl_err; - rc = curl_easy_setopt(handle, CURLOPT_NOSIGNAL, 1l); - if (rc != CURLE_OK) - goto curl_err; - agent = g_strdup_printf("%s/%s", tool_name, RELEASE_STRING); - rc = curl_easy_setopt(handle, CURLOPT_USERAGENT, agent); - if (rc != CURLE_OK) - goto curl_err; - rc = curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, write_callback); - if (rc != CURLE_OK) - goto curl_err; - ret = g_byte_array_new(); - userdata.buffer = ret; - userdata.max_size = max_size; - rc = curl_easy_setopt(handle, CURLOPT_WRITEDATA, (void *)&userdata); - if (rc != CURLE_OK) - goto curl_err; - rc = curl_easy_setopt(handle, CURLOPT_URL, url); - if (rc != CURLE_OK) - goto curl_err; - - rc = curl_easy_perform(handle); - if (rc != CURLE_OK) { - g_set_error(err, PV_ERROR, PV_ERROR_DOWNLOAD_FAILED, - _("download failed: %s"), curl_easy_strerror(rc)); - return NULL; - } - - return g_steal_pointer(&ret); -curl_err: - g_set_error(err, PV_ERROR, - PV_ERROR_CURL_INIT_FAILED, - _("cURL initialization failed: %s"), - curl_easy_strerror(rc)); - return NULL; -} diff --git a/genprotimg/src/utils/curl.h b/genprotimg/src/utils/curl.h deleted file mode 100644 index 4ec1c113..00000000 --- a/genprotimg/src/utils/curl.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Libcurl utils - * - * Copyright IBM Corp. 2020 - * - * s390-tools is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See LICENSE for details. - */ - -#ifndef PV_UTILS_LIBCURL_H -#define PV_UTILS_LIBCURL_H - -#include -#include - -#include "common.h" - -WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(CURL, curl_easy_cleanup) - -GByteArray *curl_download(const gchar *url, long timeout_ms, guint max_size, - GError **err); -gint curl_init(void); -void curl_cleanup(void); - -#endif /* PV_UTILS_LIBCURL_H */ diff --git a/genprotimg/src/utils/file_utils.c b/genprotimg/src/utils/file_utils.c deleted file mode 100644 index ba33400f..00000000 --- a/genprotimg/src/utils/file_utils.c +++ /dev/null @@ -1,234 +0,0 @@ -/* - * General file utils - * - * Copyright IBM Corp. 2020 - * - * s390-tools is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See LICENSE for details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "pv/pv_error.h" - -#include "align.h" -#include "buffer.h" -#include "common.h" -#include "file_utils.h" - -FILE *file_open(const gchar *filename, const gchar *mode, GError **err) -{ - FILE *f = fopen(filename, mode); - - if (!f) { - g_set_error(err, G_FILE_ERROR, - (gint)g_file_error_from_errno(errno), - _("Failed to open file '%s': %s"), filename, - g_strerror(errno)); - return NULL; - } - - return f; -} - -gint file_size(const gchar *filename, gsize *size, GError **err) -{ - GStatBuf st_buf; - - g_assert(size); - - if (g_stat(filename, &st_buf) != 0) { - g_set_error(err, G_FILE_ERROR, - (gint)g_file_error_from_errno(errno), - _("Failed to get file status '%s': %s"), filename, - g_strerror(errno)); - return -1; - } - - if (!S_ISREG(st_buf.st_mode)) { - g_set_error(err, G_FILE_ERROR, PV_ERROR_INTERNAL, - _("File '%s' is not a regular file"), filename); - return -1; - } - - if (st_buf.st_size < 0) { - g_set_error(err, G_FILE_ERROR, PV_ERROR_INTERNAL, - _("Invalid file size for '%s': %zu"), filename, - st_buf.st_size); - return -1; - } - - *size = (gsize)st_buf.st_size; - return 0; -} - -/* Returns 0 on success, otherwise -1. Stores the total number of - * elements successfully read in @count_read - */ -gint file_read(FILE *in, void *ptr, gsize size, gsize count, - gsize *count_read, GError **err) -{ - gsize tmp_count_read; - - tmp_count_read = fread(ptr, size, count, in); - if (count_read) - *count_read = tmp_count_read; - - if (ferror(in)) { - g_set_error(err, G_FILE_ERROR, 0, _("Failed to read file")); - return -1; - } - - return 0; -} - -gint file_write(FILE *out, const void *ptr, gsize size, gsize count, - gsize *count_written, GError **err) -{ - gsize tmp_count_written; - - tmp_count_written = fwrite(ptr, size, count, out); - if (count_written) - *count_written = tmp_count_written; - - if (tmp_count_written != count || ferror(out)) { - g_set_error(err, G_FILE_ERROR, 0, _("Failed to write file")); - return -1; - } - - return 0; -} - -static gint file_seek(FILE *f, uint64_t offset, GError **err) -{ - gint rc; - - if (offset > LONG_MAX) { - g_set_error(err, PV_ERROR, 0, _("Offset is too large")); - return -1; - } - - rc = fseek(f, (long)offset, SEEK_SET); - if (rc != 0) { - g_set_error(err, G_FILE_ERROR, - (gint)g_file_error_from_errno(errno), - _("Failed to seek: '%s'"), g_strerror(errno)); - return -1; - } - - return 0; -} - -gint seek_and_write_file(FILE *o, const CompFile *ifile, uint64_t offset, - GError **err) -{ - gsize bytes_read, bytes_written; - gsize total_bytes_read = 0; - FILE *i = NULL; - gchar buf[4096]; - gint ret = -1; - - if (file_seek(o, offset, err) < 0) - return -1; - - i = file_open(ifile->path, "rb", err); - if (!i) - return -1; - - do { - if (file_read(i, buf, 1, sizeof(buf), &bytes_read, err) < 0) { - g_prefix_error(err, _("Failed to read file '%s': "), - ifile->path); - goto err; - } - - if (bytes_read == 0) - break; - - total_bytes_read += bytes_read; - - if (file_write(o, buf, bytes_read, 1, &bytes_written, err) < 0) - goto err; - } while (bytes_written != 0); - - if (ifile->size != total_bytes_read) { - g_set_error(err, PV_ERROR, PV_ERROR_INTERNAL, - _("'%s' has changed during the preparation"), - ifile->path); - goto err; - } - - ret = 0; -err: - fclose(i); - return ret; -} - -gint seek_and_write_buffer(FILE *o, const PvBuffer *buf, uint64_t offset, - GError **err) -{ - if (file_seek(o, offset, err) < 0) - return -1; - - if (pv_buffer_write(buf, o, err) < 0) - return -1; - - return 0; -} - -gint pad_file_right(const gchar *path_out, const gchar *path_in, gsize *size_out, - guint padding, GError **err) -{ - FILE *f_in, *f_out = NULL; - guchar buf[padding]; - gsize num_bytes_written; - gsize num_bytes_read; - uint64_t size_in = 0; - gint ret = -1; - - *size_out = 0; - f_in = file_open(path_in, "rb", err); - if (!f_in) - goto err; - - f_out = file_open(path_out, "wb", err); - if (!f_out) - goto err; - - do { - memset(buf, 0, sizeof(buf)); - - if (file_read(f_in, buf, 1, sizeof(buf), &num_bytes_read, err) < 0) { - g_prefix_error(err, _("Failed to read file '%s': "), - path_in); - goto err; - } - - size_in += num_bytes_read; - - if (file_write(f_out, buf, 1, sizeof(buf), &num_bytes_written, err)) { - g_prefix_error(err, _("Failed to write file '%s': "), - path_out); - goto err; - } - - *size_out += num_bytes_written; - } while (num_bytes_read == padding); - - g_assert(num_bytes_written == ALIGN(num_bytes_read, padding)); - - ret = 0; -err: - if (f_out) - fclose(f_out); - if (f_in) - fclose(f_in); - return ret; -} diff --git a/genprotimg/src/utils/file_utils.h b/genprotimg/src/utils/file_utils.h deleted file mode 100644 index 456e7aca..00000000 --- a/genprotimg/src/utils/file_utils.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * General file utils - * - * Copyright IBM Corp. 2020 - * - * s390-tools is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See LICENSE for details. - */ - -#ifndef PV_FILE_UTILS_H -#define PV_FILE_UTILS_H - -#include -#include -#include - -#include "pv/pv_comp.h" - -#include "buffer.h" - -FILE *file_open(const gchar *filename, const gchar *mode, GError **err); -gint file_size(const gchar *filename, gsize *size, GError **err); -gint file_read(FILE *in, void *ptr, gsize size, gsize count, - gsize *count_read, GError **err); -gint file_write(FILE *out, const void *ptr, gsize size, gsize count, - gsize *count_written, GError **err); -gint pad_file_right(const gchar *path_out, const gchar *path_in, - gsize *size_out, guint padding, GError **err); -gint seek_and_write_buffer(FILE *out, const PvBuffer *buf, uint64_t offset, - GError **err); -gint seek_and_write_file(FILE *o, const CompFile *ifile, uint64_t offset, - GError **err); - -#endif diff --git a/genprotimg/src/utils/openssl_compat.h b/genprotimg/src/utils/openssl_compat.h deleted file mode 100644 index 791c31fc..00000000 --- a/genprotimg/src/utils/openssl_compat.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * OpenSSL compatibility utils - * - * Copyright IBM Corp. 2021 - * - * s390-tools is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See LICENSE for details. - */ - -#ifndef PV_UTILS_OPENSSL_COMPAT_H -#define PV_UTILS_OPENSSL_COMPAT_H - -#include -#include -#include - -#if OPENSSL_VERSION_NUMBER < 0x30000000L -#define Pv_X509_STORE_CTX_get_current_cert(ctx) \ - X509_STORE_CTX_get_current_cert((X509_STORE_CTX *)(ctx)) -#define Pv_X509_STORE_CTX_get1_crls(ctx, nm) \ - X509_STORE_CTX_get1_crls((X509_STORE_CTX *)(ctx), (X509_NAME *)(nm)) -#define Pv_X509_STORE_set_lookup_crls(st, cb) \ - X509_STORE_set_lookup_crls(st, (X509_STORE_CTX_lookup_crls_fn)(cb)) -#else -#define Pv_X509_STORE_CTX_get_current_cert(ctx) \ - X509_STORE_CTX_get_current_cert(ctx) -#define Pv_X509_STORE_CTX_get1_crls(ctx, nm) \ - X509_STORE_CTX_get1_crls(ctx, nm) -#define Pv_X509_STORE_set_lookup_crls(st, cb) \ - X509_STORE_set_lookup_crls(st, cb) -#endif - -#endif