From 5394cd363c44efc3d4834f73e93e6a3e8318be4c Mon Sep 17 00:00:00 2001 From: Marc Hartmayer Date: Tue, 15 Feb 2022 14:32:32 +0000 Subject: [PATCH] genprotimg: add PV guest dump support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sometimes dumping a virtual machine from the outside is the only way to get the data that is needed. This can be the case if a dumping mechanism like kdump hasn't been configured or data needs to be fetched at a specific point. Dumping a protected guest from the outside without help from FW/HW doesn't yield sufficient data to be useful. Hence we now introduce Protected Virtualization (PV) dump support. The PV dump support works by integrating the firmware into the dump process. New Ultravisor calls are used to initiate the dump process, dump cpu data, dump memory state and lastly complete the dump process. The guest's data is fully encrypted and can only be decrypted by the entity that owns the customer communication key for the dumped guest. Also dumping needs to be allowed via a flag in the SE header. This patch adds support for PV guest dumps to genprotimg. To prepare a PV image in order that the PV guest can later be dumped from the outside, the user has to provide a customer communication key used for the dump process and he has to set the corresponding control flag. For specifying the customer communication key a new command line option `--comm-key` is added and for enabling/disabling the control flag the command line options `--enable-dump` and `--disable-dump` are added. Signed-off-by: Marc Hartmayer Reviewed-by: Steffen Eiden Acked-by: Viktor Mihajlovski Signed-off-by: Jan Höppner --- genprotimg/man/genprotimg.8 | 40 +++++++++++++++-------- genprotimg/src/include/pv_hdr_def.h | 1 + genprotimg/src/pv/pv_args.c | 50 ++++++++++++++++++++--------- genprotimg/src/pv/pv_args.h | 1 + genprotimg/src/pv/pv_image.c | 13 ++++++-- 5 files changed, 75 insertions(+), 30 deletions(-) diff --git a/genprotimg/man/genprotimg.8 b/genprotimg/man/genprotimg.8 index 8a481c4b..81929926 100644 --- a/genprotimg/man/genprotimg.8 +++ b/genprotimg/man/genprotimg.8 @@ -94,6 +94,16 @@ this only if you trust the specified certificate. Optional. Do not require the host-key documents to be valid. For testing purposes, do not use for a production image. Optional. .TP +\fB\-\-comm\-key\fR=\fI\,FILE\/\fR +Specifies the encryption key you want to use for the PV guest dump. 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. Optional. +.TP \fB\-\-enable\-pckmo\fR Enable the support for the DEA, TDEA, AES, and ECC PCKMO key encryption functions. This is the default. Optional. @@ -105,21 +115,25 @@ functions. Optional. \fB\-v\fR, \fB\-\-version\fR Prints version information, then exits. -.SH EXAMPLE +.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 -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: -.PP -.Vb 1 -.EX -\& 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 \-o \fI\,/boot/vmlinuz.pv\/\fR -.EE -.Ve +.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 diff --git a/genprotimg/src/include/pv_hdr_def.h b/genprotimg/src/include/pv_hdr_def.h index 270de0d0..1a839a6d 100644 --- a/genprotimg/src/include/pv_hdr_def.h +++ b/genprotimg/src/include/pv_hdr_def.h @@ -28,6 +28,7 @@ #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 */ diff --git a/genprotimg/src/pv/pv_args.c b/genprotimg/src/pv/pv_args.c index 5cda3a8d..000eb9a0 100644 --- a/genprotimg/src/pv/pv_args.c +++ b/genprotimg/src/pv/pv_args.c @@ -64,13 +64,17 @@ static gint pv_args_validate_options(PvArgs *args, GError **err) { PvComponentType KERNEL = PV_COMP_TYPE_KERNEL; - if (args->pcf && args->allow_pckmo != PV_NOT_SET) { - g_set_error(err, PV_PARSE_ERROR, PV_PARSE_ERROR_SYNTAX, - _("The '--x-pcf' and '--(enable|disable)-pckmo' options are mutually" - " exclusive.\nUse 'genprotimg --help' for more information")); + /* Check for mutually exclusive arguments */ + if (args->pcf && !(args->allow_pckmo == PV_NOT_SET && + args->allow_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; } + /* Check for unused arguments */ if (args->unused_values->len > 0) { g_autofree gchar *unused = NULL; @@ -88,6 +92,14 @@ static gint pv_args_validate_options(PvArgs *args, GError **err) return -1; } + /* Check for mandatory arguments */ + if (args->allow_dump == PV_TRUE && !args->cust_comm_key_path) { + g_set_error(err, PV_PARSE_ERROR, PR_PARSE_ERROR_MISSING_ARGUMENT, + _("Option '--allow-dump' 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")); @@ -155,14 +167,14 @@ static gboolean cb_set_string_option(const gchar *option, const gchar *value, { 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-comm-key")) - args_option = &args->cust_comm_key_path; if (g_str_equal(option, "--x-header-key")) args_option = &args->cust_root_key_path; if (g_str_equal(option, "--x-pcf")) @@ -246,6 +258,7 @@ static gboolean cb_remaining_values(const gchar *option G_GNUC_UNUSED, #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) gint pv_args_parse_options(PvArgs *args, gint *argc, gchar **argv[], @@ -311,6 +324,11 @@ gint pv_args_parse_options(PvArgs *args, gint *argc, gchar **argv[], .description = _("Use the kernel parameters stored in PARMFILE\n" INDENT "(optional)."), .arg_description = _("PARMFILE") }, + MUT_EXCL_BOOL_FLAG( + dump, + _("Enable PV guest dumps (optional). This option\n" INDENT + "requires the '--comm-key' option."), + _("Disable PV guest dumps (default) (optional).")), MUT_EXCL_BOOL_FLAG( pckmo, _("Enable the support for the DEA, TDEA, AES, and\n" INDENT @@ -318,6 +336,16 @@ gint pv_args_parse_options(PvArgs *args, gint *argc, gchar **argv[], "(optional)."), _("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 key with which you encrypt\n" INDENT + "the PV guest dump (optional). Required by\n" INDENT + "the '--enable-dump' option."), + .arg_description = _("FILE") }, { .long_name = "crl", .short_name = 0, .flags = G_OPTION_FLAG_NONE, @@ -376,15 +404,6 @@ gint pv_args_parse_options(PvArgs *args, gint *argc, gchar **argv[], }; GOptionEntry x_entries[] = { - { .long_name = "x-comm-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 customer communication key.\n" INDENT - "Optional; default: auto-generated."), - .arg_description = _("FILE") }, { .long_name = "x-comp-key", .short_name = 0, .flags = G_OPTION_FLAG_FILENAME, @@ -467,6 +486,7 @@ 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->allow_dump = PV_NOT_SET; args->allow_pckmo = PV_NOT_SET; return g_steal_pointer(&args); } diff --git a/genprotimg/src/pv/pv_args.h b/genprotimg/src/pv/pv_args.h index 77f55396..ef659fd8 100644 --- a/genprotimg/src/pv/pv_args.h +++ b/genprotimg/src/pv/pv_args.h @@ -34,6 +34,7 @@ typedef struct { gboolean offline; gchar *pcf; gchar *scf; + PvTristate allow_dump; PvTristate allow_pckmo; gchar *psw_addr; /* PSW address which will be used for the start of * the actual component (e.g. Linux kernel) diff --git a/genprotimg/src/pv/pv_image.c b/genprotimg/src/pv/pv_image.c index 73592401..1ccb0fce 100644 --- a/genprotimg/src/pv/pv_image.c +++ b/genprotimg/src/pv/pv_image.c @@ -229,7 +229,9 @@ static gint pv_img_set_psw_addr(PvImage *img, const gchar *psw_addr_s, } static gint pv_img_set_control_flags(PvImage *img, const gchar *pcf_s, - const gchar *scf_s, PvTristate allow_pckmo, GError **err) + const gchar *scf_s, + PvTristate allow_dump, + PvTristate allow_pckmo, GError **err) { uint64_t flags; @@ -247,6 +249,11 @@ static gint pv_img_set_control_flags(PvImage *img, const gchar *pcf_s, img->scf = flags; } + if (allow_dump == PV_TRUE) + img->pcf |= PV_PCF_ALLOW_DUMPING; + else if (allow_dump == PV_FALSE) + img->pcf &= ~PV_PCF_ALLOW_DUMPING; + if (allow_pckmo == PV_TRUE) img->pcf |= PV_PCF_PCKM_ECC | PV_PCF_PCKMO_AES | PV_PCF_PCKMO_DEA_TDEA; else if (allow_pckmo == PV_FALSE) @@ -608,7 +615,9 @@ PvImage *pv_img_new(PvArgs *args, const gchar *stage3a_path, GError **err) return NULL; /* set the control flags: PCF and SCF */ - if (pv_img_set_control_flags(ret, args->pcf, args->scf, args->allow_pckmo, err) < 0) + if (pv_img_set_control_flags(ret, args->pcf, args->scf, + args->allow_dump, args->allow_pckmo, + err) < 0) return NULL; /* read in the keys */