diff options
Diffstat (limited to 'src/boot/measure.c')
-rw-r--r-- | src/boot/measure.c | 165 |
1 files changed, 126 insertions, 39 deletions
diff --git a/src/boot/measure.c b/src/boot/measure.c index 5c5071e..a6d27a7 100644 --- a/src/boot/measure.c +++ b/src/boot/measure.c @@ -30,7 +30,10 @@ static char *arg_sections[_UNIFIED_SECTION_MAX] = {}; static char **arg_banks = NULL; static char *arg_tpm2_device = NULL; static char *arg_private_key = NULL; +static KeySourceType arg_private_key_source_type = OPENSSL_KEY_SOURCE_FILE; +static char *arg_private_key_source = NULL; static char *arg_public_key = NULL; +static char *arg_certificate = NULL; static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_PRETTY_AUTO|JSON_FORMAT_COLOR_AUTO|JSON_FORMAT_OFF; static PagerFlags arg_pager_flags = 0; static bool arg_current = false; @@ -40,7 +43,9 @@ static char *arg_append = NULL; STATIC_DESTRUCTOR_REGISTER(arg_banks, strv_freep); STATIC_DESTRUCTOR_REGISTER(arg_tpm2_device, freep); STATIC_DESTRUCTOR_REGISTER(arg_private_key, freep); +STATIC_DESTRUCTOR_REGISTER(arg_private_key_source, freep); STATIC_DESTRUCTOR_REGISTER(arg_public_key, freep); +STATIC_DESTRUCTOR_REGISTER(arg_certificate, freep); STATIC_DESTRUCTOR_REGISTER(arg_phase, strv_freep); STATIC_DESTRUCTOR_REGISTER(arg_append, freep); @@ -74,7 +79,11 @@ static int help(int argc, char *argv[], void *userdata) { " --bank=DIGEST Select TPM bank (SHA1, SHA256, SHA384, SHA512)\n" " --tpm2-device=PATH Use specified TPM2 device\n" " --private-key=KEY Private key (PEM) to sign with\n" + " --private-key-source=file|provider:PROVIDER|engine:ENGINE\n" + " Specify how to use KEY for --private-key=. Allows\n" + " an OpenSSL engine/provider to be used for signing\n" " --public-key=KEY Public key (PEM) to validate against\n" + " --certificate=PATH PEM certificate to use when signing with a URI\n" " --json=MODE Output as JSON\n" " -j Same as --json=pretty on tty, --json=short otherwise\n" " --append=PATH Load specified JSON signature, and append new signature to it\n" @@ -83,6 +92,7 @@ static int help(int argc, char *argv[], void *userdata) { " --osrel=PATH Path to os-release file %7$s .osrel\n" " --cmdline=PATH Path to file with kernel command line %7$s .cmdline\n" " --initrd=PATH Path to initrd image file %7$s .initrd\n" + " --ucode=PATH Path to microcode image file %7$s .ucode\n" " --splash=PATH Path to splash bitmap file %7$s .splash\n" " --dtb=PATH Path to Devicetree file %7$s .dtb\n" " --uname=PATH Path to 'uname -r' file %7$s .uname\n" @@ -124,6 +134,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_OSREL, ARG_CMDLINE, ARG_INITRD, + ARG_UCODE, ARG_SPLASH, ARG_DTB, ARG_UNAME, @@ -133,7 +144,9 @@ static int parse_argv(int argc, char *argv[]) { ARG_PCRPKEY = _ARG_SECTION_LAST, ARG_BANK, ARG_PRIVATE_KEY, + ARG_PRIVATE_KEY_SOURCE, ARG_PUBLIC_KEY, + ARG_CERTIFICATE, ARG_TPM2_DEVICE, ARG_JSON, ARG_PHASE, @@ -141,26 +154,29 @@ static int parse_argv(int argc, char *argv[]) { }; static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "no-pager", no_argument, NULL, ARG_NO_PAGER }, - { "version", no_argument, NULL, ARG_VERSION }, - { "linux", required_argument, NULL, ARG_LINUX }, - { "osrel", required_argument, NULL, ARG_OSREL }, - { "cmdline", required_argument, NULL, ARG_CMDLINE }, - { "initrd", required_argument, NULL, ARG_INITRD }, - { "splash", required_argument, NULL, ARG_SPLASH }, - { "dtb", required_argument, NULL, ARG_DTB }, - { "uname", required_argument, NULL, ARG_UNAME }, - { "sbat", required_argument, NULL, ARG_SBAT }, - { "pcrpkey", required_argument, NULL, ARG_PCRPKEY }, - { "current", no_argument, NULL, 'c' }, - { "bank", required_argument, NULL, ARG_BANK }, - { "tpm2-device", required_argument, NULL, ARG_TPM2_DEVICE }, - { "private-key", required_argument, NULL, ARG_PRIVATE_KEY }, - { "public-key", required_argument, NULL, ARG_PUBLIC_KEY }, - { "json", required_argument, NULL, ARG_JSON }, - { "phase", required_argument, NULL, ARG_PHASE }, - { "append", required_argument, NULL, ARG_APPEND }, + { "help", no_argument, NULL, 'h' }, + { "no-pager", no_argument, NULL, ARG_NO_PAGER }, + { "version", no_argument, NULL, ARG_VERSION }, + { "linux", required_argument, NULL, ARG_LINUX }, + { "osrel", required_argument, NULL, ARG_OSREL }, + { "cmdline", required_argument, NULL, ARG_CMDLINE }, + { "initrd", required_argument, NULL, ARG_INITRD }, + { "ucode", required_argument, NULL, ARG_UCODE }, + { "splash", required_argument, NULL, ARG_SPLASH }, + { "dtb", required_argument, NULL, ARG_DTB }, + { "uname", required_argument, NULL, ARG_UNAME }, + { "sbat", required_argument, NULL, ARG_SBAT }, + { "pcrpkey", required_argument, NULL, ARG_PCRPKEY }, + { "current", no_argument, NULL, 'c' }, + { "bank", required_argument, NULL, ARG_BANK }, + { "tpm2-device", required_argument, NULL, ARG_TPM2_DEVICE }, + { "private-key", required_argument, NULL, ARG_PRIVATE_KEY }, + { "private-key-source", required_argument, NULL, ARG_PRIVATE_KEY_SOURCE }, + { "public-key", required_argument, NULL, ARG_PUBLIC_KEY }, + { "certificate", required_argument, NULL, ARG_CERTIFICATE }, + { "json", required_argument, NULL, ARG_JSON }, + { "phase", required_argument, NULL, ARG_PHASE }, + { "append", required_argument, NULL, ARG_APPEND }, {} }; @@ -213,7 +229,17 @@ static int parse_argv(int argc, char *argv[]) { } case ARG_PRIVATE_KEY: - r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_private_key); + r = free_and_strdup_warn(&arg_private_key, optarg); + if (r < 0) + return r; + + break; + + case ARG_PRIVATE_KEY_SOURCE: + r = parse_openssl_key_source_argument( + optarg, + &arg_private_key_source, + &arg_private_key_source_type); if (r < 0) return r; @@ -226,6 +252,13 @@ static int parse_argv(int argc, char *argv[]) { break; + case ARG_CERTIFICATE: + r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_certificate); + if (r < 0) + return r; + + break; + case ARG_TPM2_DEVICE: { _cleanup_free_ char *device = NULL; @@ -281,6 +314,12 @@ static int parse_argv(int argc, char *argv[]) { assert_not_reached(); } + if (arg_public_key && arg_certificate) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Both --public-key= and --certificate= specified, refusing."); + + if (arg_private_key_source && !arg_certificate) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "When using --private-key-source=, --certificate= must be specified."); + if (strv_isempty(arg_banks)) { /* If no banks are specifically selected, pick all known banks */ arg_banks = strv_new("SHA1", "SHA256", "SHA384", "SHA512"); @@ -300,12 +339,11 @@ static int parse_argv(int argc, char *argv[]) { if (strv_isempty(arg_phase)) { /* If no phases are specifically selected, pick everything from the beginning of the initrd * to the beginning of shutdown. */ - if (strv_extend_strv(&arg_phase, - STRV_MAKE("enter-initrd", - "enter-initrd:leave-initrd", - "enter-initrd:leave-initrd:sysinit", - "enter-initrd:leave-initrd:sysinit:ready"), - /* filter_duplicates= */ false) < 0) + if (strv_extend_many(&arg_phase, + "enter-initrd", + "enter-initrd:leave-initrd", + "enter-initrd:leave-initrd:sysinit", + "enter-initrd:leave-initrd:sysinit:ready") < 0) return log_oom(); } else { strv_sort(arg_phase); @@ -419,7 +457,7 @@ static int measure_kernel(PcrState *pcr_states, size_t n) { if (r < 0) return log_error_errno(r, "Failed to read '%s': %m", p); - r = unhexmem(strstrip(s), SIZE_MAX, &v, &sz); + r = unhexmem(strstrip(s), &v, &sz); if (r < 0) return log_error_errno(r, "Failed to decode PCR value '%s': %m", s); @@ -732,7 +770,7 @@ static int verb_sign(int argc, char *argv[], void *userdata) { _cleanup_(json_variant_unrefp) JsonVariant *v = NULL; _cleanup_(pcr_state_free_all) PcrState *pcr_states = NULL; _cleanup_(EVP_PKEY_freep) EVP_PKEY *privkey = NULL, *pubkey = NULL; - _cleanup_fclose_ FILE *privkeyf = NULL; + _cleanup_(X509_freep) X509 *certificate = NULL; size_t n; int r; @@ -760,13 +798,57 @@ static int verb_sign(int argc, char *argv[], void *userdata) { /* When signing we only support JSON output */ arg_json_format_flags &= ~JSON_FORMAT_OFF; - privkeyf = fopen(arg_private_key, "re"); - if (!privkeyf) - return log_error_errno(errno, "Failed to open private key file '%s': %m", arg_private_key); + /* This must be done before openssl_load_key_from_token() otherwise it will get stuck */ + if (arg_certificate) { + _cleanup_(BIO_freep) BIO *cb = NULL; + _cleanup_free_ char *crt = NULL; - privkey = PEM_read_PrivateKey(privkeyf, NULL, NULL, NULL); - if (!privkey) - return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to parse private key '%s'.", arg_private_key); + r = read_full_file_full( + AT_FDCWD, arg_certificate, UINT64_MAX, SIZE_MAX, + READ_FULL_FILE_CONNECT_SOCKET, + /* bind_name= */ NULL, + &crt, &n); + if (r < 0) + return log_error_errno(r, "Failed to read certificate file '%s': %m", arg_certificate); + + cb = BIO_new_mem_buf(crt, n); + if (!cb) + return log_oom(); + + certificate = PEM_read_bio_X509(cb, NULL, NULL, NULL); + if (!certificate) + return log_error_errno( + SYNTHETIC_ERRNO(EBADMSG), + "Failed to parse X.509 certificate: %s", + ERR_error_string(ERR_get_error(), NULL)); + } + + if (arg_private_key_source_type == OPENSSL_KEY_SOURCE_FILE) { + _cleanup_fclose_ FILE *privkeyf = NULL; + _cleanup_free_ char *resolved_pkey = NULL; + + r = parse_path_argument(arg_private_key, /* suppress_root= */ false, &resolved_pkey); + if (r < 0) + return log_error_errno(r, "Failed to parse private key path %s: %m", arg_private_key); + + privkeyf = fopen(resolved_pkey, "re"); + if (!privkeyf) + return log_error_errno(errno, "Failed to open private key file '%s': %m", resolved_pkey); + + privkey = PEM_read_PrivateKey(privkeyf, NULL, NULL, NULL); + if (!privkey) + return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to parse private key '%s'.", resolved_pkey); + } else if (arg_private_key_source && + IN_SET(arg_private_key_source_type, OPENSSL_KEY_SOURCE_ENGINE, OPENSSL_KEY_SOURCE_PROVIDER)) { + r = openssl_load_key_from_token( + arg_private_key_source_type, arg_private_key_source, arg_private_key, &privkey); + if (r < 0) + return log_error_errno( + r, + "Failed to load key '%s' from OpenSSL key source %s: %m", + arg_private_key, + arg_private_key_source); + } if (arg_public_key) { _cleanup_fclose_ FILE *pubkeyf = NULL; @@ -778,6 +860,13 @@ static int verb_sign(int argc, char *argv[], void *userdata) { pubkey = PEM_read_PUBKEY(pubkeyf, NULL, NULL, NULL); if (!pubkey) return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to parse public key '%s'.", arg_public_key); + } else if (certificate) { + pubkey = X509_get_pubkey(certificate); + if (!pubkey) + return log_error_errno( + SYNTHETIC_ERRNO(EIO), + "Failed to extract public key from certificate %s.", + arg_certificate); } else { _cleanup_(memstream_done) MemStream m = {}; FILE *tf; @@ -995,7 +1084,7 @@ static int verb_status(int argc, char *argv[], void *userdata) { if (r < 0) return log_error_errno(r, "Failed to read '%s': %m", p); - r = unhexmem(strstrip(s), SIZE_MAX, &h, &l); + r = unhexmem(strstrip(s), &h, &l); if (r < 0) return log_error_errno(r, "Failed to decode PCR value '%s': %m", s); @@ -1071,9 +1160,7 @@ static int measure_main(int argc, char *argv[]) { static int run(int argc, char *argv[]) { int r; - log_show_color(true); - log_parse_environment(); - log_open(); + log_setup(); r = parse_argv(argc, argv); if (r <= 0) |