mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
pvattest is a tool to attest an IBM Secure Execution guest. In a trusted environment, one can create a request using `pvattest create`. To get a measurement of an untrusted IBM Secure Execution guest call 'pvattest perform'. Again in a trusted environment, call 'pvattest verify' to verify that the measurement is the expected one. The tool runs on s390 and x86. It has the same requirements like libpv and therefore requires openssl v1.1.1+, glib2.56+, and libcurl. Additionally, to measure, the linux kernel must provide the Ultravisor userspace interface `uvdevice` at /dev/uv and must be executed on an IBM Secure Execution guest on hardware with Ultravisor attestation support, like IBM z16 or later. Signed-off-by: Steffen Eiden <seiden@linux.ibm.com> Reviewed-by: Marc Hartmayer <mhartmay@linux.ibm.com> Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
650 lines
26 KiB
C
650 lines
26 KiB
C
/*
|
|
* Definitions used for parsing arguments.
|
|
*
|
|
* Copyright IBM Corp. 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.
|
|
*/
|
|
/* Must be included before any other header */
|
|
#include "config.h"
|
|
|
|
#include <stdio.h>
|
|
#include <fcntl.h>
|
|
#include <errno.h>
|
|
#include <unistd.h>
|
|
|
|
#include "argparse.h"
|
|
#include "log.h"
|
|
#include "common.h"
|
|
|
|
#define DEFAULT_OUTPUT_FILE_NAME "attest.bin"
|
|
#define DEFAULT_OPTION_PHKH_IMG FALSE
|
|
#define DEFAULT_OPTION_PHKH_ATT FALSE
|
|
#define DEFAULT_OPTION_NO_VERIFY FALSE
|
|
#define DEFAULT_OPTION_ONLINE TRUE
|
|
#define DEFAULT_OPTION_NONCE TRUE
|
|
|
|
static pvattest_config_t pvattest_config = {
|
|
.general = {
|
|
.log_level = PVATTEST_LOG_LVL_DEFAULT,
|
|
},
|
|
.create = {
|
|
.output_path = NULL,
|
|
.host_key_document_paths = NULL,
|
|
.crl_paths = NULL,
|
|
.root_ca_path = NULL,
|
|
.certificate_paths = NULL,
|
|
.arp_key_out_path = NULL,
|
|
.phkh_img = DEFAULT_OPTION_PHKH_IMG,
|
|
.phkh_att = DEFAULT_OPTION_PHKH_ATT,
|
|
.online = DEFAULT_OPTION_ONLINE,
|
|
.use_nonce = DEFAULT_OPTION_NONCE,
|
|
.paf = 0,
|
|
.x_aad_size = -1,
|
|
},
|
|
.perform = {
|
|
.output_path = NULL,
|
|
.input_path = NULL,
|
|
},
|
|
.verify = {
|
|
.input_path = NULL,
|
|
.hdr_path = NULL,
|
|
.arp_key_in_path = NULL,
|
|
},
|
|
};
|
|
typedef gboolean (*verify_options_fn_t)(GError **);
|
|
|
|
static gboolean check_for_non_null(const void *ptr, const char *msg, GError **error)
|
|
{
|
|
if (!ptr) {
|
|
g_set_error(error, PVATTEST_ERROR, PVATTEST_ERR_INV_ARG, "%s", msg);
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean _check_for_invalid_path(const char *path, gboolean must_exist, GError **error)
|
|
{
|
|
int cached_errno = 0;
|
|
|
|
g_assert(path);
|
|
|
|
if (must_exist) {
|
|
if (access(path, F_OK | R_OK) != 0)
|
|
cached_errno = errno;
|
|
}
|
|
if (cached_errno) {
|
|
g_set_error(error, PVATTEST_ERROR, PVATTEST_ERR_INV_ARG, "Cannot access '%s': %s",
|
|
path, g_strerror(cached_errno));
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean check_for_optional_invalid_path(const char *path, gboolean must_exist,
|
|
GError **error)
|
|
{
|
|
if (!path)
|
|
return TRUE;
|
|
return _check_for_invalid_path(path, must_exist, error);
|
|
}
|
|
|
|
static gboolean check_for_invalid_path(const char *path, gboolean must_exist, const char *null_msg,
|
|
GError **error)
|
|
{
|
|
if (!check_for_non_null(path, null_msg, error))
|
|
return FALSE;
|
|
return _check_for_invalid_path(path, must_exist, error);
|
|
}
|
|
|
|
static gboolean _check_file_list(char **path_list, gboolean must_exist, GError **error)
|
|
{
|
|
char *path = NULL;
|
|
for (char **path_it = path_list; path_it != NULL && *path_it != NULL; path_it++) {
|
|
path = *path_it;
|
|
if (!_check_for_invalid_path(path, must_exist, error))
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean check_optional_file_list(char **path_list, gboolean must_exist, GError **error)
|
|
{
|
|
if (!path_list)
|
|
return TRUE;
|
|
return _check_file_list(path_list, must_exist, error);
|
|
}
|
|
|
|
static gboolean check_file_list(char **path_list, gboolean must_exist, const char *null_msg,
|
|
GError **error)
|
|
{
|
|
if (!check_for_non_null(path_list, null_msg, error))
|
|
return FALSE;
|
|
return _check_file_list(path_list, must_exist, error);
|
|
}
|
|
|
|
static gboolean hex_str_toull(const char *nptr, uint64_t *dst, GError **error)
|
|
{
|
|
uint64_t value;
|
|
gchar *end;
|
|
|
|
g_assert(dst);
|
|
|
|
if (!g_str_is_ascii(nptr)) {
|
|
g_set_error(
|
|
error, PVATTEST_ERROR, PVATTEST_ERR_INV_ARG,
|
|
_("Invalid value: '%s'. A hexadecimal value is required, for example '0xcfe'"),
|
|
nptr);
|
|
return FALSE;
|
|
}
|
|
|
|
value = g_ascii_strtoull(nptr, &end, 16);
|
|
if ((value == G_MAXUINT64 && errno == ERANGE) || (end && *end != '\0')) {
|
|
g_set_error(
|
|
error, PVATTEST_ERROR, PVATTEST_ERR_INV_ARG,
|
|
_("Invalid value: '%s'. A hexadecimal value is required, for example '0xcfe'"),
|
|
nptr);
|
|
return FALSE;
|
|
}
|
|
*dst = value;
|
|
return TRUE;
|
|
}
|
|
|
|
#pragma GCC diagnostic push
|
|
#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
|
|
|
|
/************************* SHARED OPTIONS *************************************/
|
|
/* NOTE REQUIRED */
|
|
#define _entry_host_key_document(__arg_data, __indent) \
|
|
{ \
|
|
.long_name = "host-key-document", .short_name = 'k', .flags = G_OPTION_FLAG_NONE, \
|
|
.arg = G_OPTION_ARG_FILENAME_ARRAY, .arg_data = __arg_data, \
|
|
.description = "Specify one or more host key documents.\n", \
|
|
.arg_description = "FILE", \
|
|
}
|
|
|
|
/* NOTE REQUIRED */
|
|
#define _entry_certs(__arg_data, __indent) \
|
|
{ \
|
|
.long_name = "cert", .short_name = 'C', .flags = G_OPTION_FLAG_NONE, \
|
|
.arg = G_OPTION_ARG_FILENAME_ARRAY, .arg_data = __arg_data, \
|
|
.description = \
|
|
"Specifies the certificate that is used to establish a chain\n" __indent \
|
|
"of trust for the verification of the host-key documents. Specify\n" __indent \
|
|
"this option twice to specify the IBM Z signing key and the\n" __indent \
|
|
"intermediate CA certificate (signed by the root CA). Required.\n" __indent \
|
|
"Ignored when --no-verify is specified.\n", \
|
|
.arg_description = "FILE", \
|
|
}
|
|
|
|
/* NOTE REQUIRED */
|
|
#define _entry_crls(__arg_data, __indent) \
|
|
{ \
|
|
.long_name = "crl", .short_name = 0, .flags = G_OPTION_FLAG_NONE, \
|
|
.arg = G_OPTION_ARG_FILENAME_ARRAY, .arg_data = __arg_data, \
|
|
.description = "Specify FILE to be a certificate revocation list\n" __indent \
|
|
"(optional).", \
|
|
.arg_description = "FILE", \
|
|
}
|
|
|
|
/* NOTE REQUIRED */
|
|
#define _entry_root_ca(__arg_data, __indent) \
|
|
{ \
|
|
.long_name = "root-ca", .short_name = 0, .flags = G_OPTION_FLAG_NONE, \
|
|
.arg = G_OPTION_ARG_FILENAME_ARRAY, .arg_data = __arg_data, \
|
|
.description = "Use FILE as the trusted root CA instead the\n" __indent \
|
|
"root CAs that are installed on the system (optional).", \
|
|
.arg_description = "FILE", \
|
|
}
|
|
|
|
/* NOTE REQUIRED */
|
|
#define _entry_guest_hdr(__arg_data, __indent) \
|
|
{ \
|
|
.long_name = "hdr", .short_name = 0, .flags = G_OPTION_FLAG_NONE, \
|
|
.arg = G_OPTION_ARG_FILENAME, .arg_data = __arg_data, \
|
|
.description = \
|
|
"Specify the header of the guest image. Exactly one is required.\n", \
|
|
.arg_description = "FILE", \
|
|
}
|
|
|
|
/* NOTE REQUIRED */
|
|
#define _entry_input(__arg_data, __additional_text, __indent) \
|
|
{ \
|
|
.long_name = "input", .short_name = 'i', .flags = G_OPTION_FLAG_NONE, \
|
|
.arg = G_OPTION_ARG_FILENAME, .arg_data = __arg_data, \
|
|
.description = "FILE specifies the " __additional_text "\n" __indent \
|
|
" as input.\n", \
|
|
.arg_description = "FILE", \
|
|
}
|
|
|
|
/* NOTE REQUIRED */
|
|
#define _entry_output(__arg_data, __additional_text, __indent) \
|
|
{ \
|
|
.long_name = "output", .short_name = 'o', .flags = G_OPTION_FLAG_NONE, \
|
|
.arg = G_OPTION_ARG_FILENAME, .arg_data = __arg_data, \
|
|
.description = "FILE specifies the output for the\n" __indent __additional_text \
|
|
".\n", \
|
|
.arg_description = "FILE", \
|
|
}
|
|
|
|
/* NOTE REQUIRED */
|
|
#define _entry_att_prot_key_save(__arg_data, __indent) \
|
|
{ \
|
|
.long_name = "arpk", .short_name = 'a', .flags = G_OPTION_FLAG_NONE, \
|
|
.arg = G_OPTION_ARG_FILENAME, .arg_data = __arg_data, \
|
|
.description = \
|
|
"Save the protection key as GCM-AES256 key in FILE\n" __indent \
|
|
"Do not publish this key, otherwise your attestation is compromised.\n", \
|
|
.arg_description = "FILE", \
|
|
}
|
|
|
|
/* NOTE REQUIRED */
|
|
#define _entry_att_prot_key_load(__arg_data, __indent) \
|
|
{ \
|
|
.long_name = "arpk", .short_name = 'a', .flags = G_OPTION_FLAG_NONE, \
|
|
.arg = G_OPTION_ARG_FILENAME, .arg_data = __arg_data, \
|
|
.description = "Use FILE to specify the GCM-AES256 key to decrypt\n" __indent \
|
|
"the attestation request.\n" __indent \
|
|
"Delete this key after verification.\n", \
|
|
.arg_description = "FILE", \
|
|
}
|
|
|
|
#define _entry_phkh_img(__arg_data, __indent) \
|
|
{ \
|
|
.long_name = "x-phkh-img", .short_name = 0, .flags = G_OPTION_FLAG_NONE, \
|
|
.arg = G_OPTION_ARG_NONE, .arg_data = __arg_data, \
|
|
.description = "add the public host key hash of the\n" __indent \
|
|
"image header used to decrypt\n" __indent \
|
|
"the secure guest to the measurement. (optional)\n" \
|
|
}
|
|
|
|
#define _entry_phkh_att(__arg_data, __indent) \
|
|
{ \
|
|
.long_name = "x-phkh-att", .short_name = 0, .flags = G_OPTION_FLAG_NONE, \
|
|
.arg = G_OPTION_ARG_NONE, .arg_data = __arg_data, \
|
|
.description = "add the public host key hash of the\n" __indent \
|
|
"attestation header used to decrypt\n" __indent \
|
|
"the attestation request to the measurement. (optional)\n" \
|
|
}
|
|
|
|
#define _entry_no_verify(__arg_data, __indent) \
|
|
{ \
|
|
.long_name = "no-verify", .short_name = 0, .flags = G_OPTION_FLAG_NONE, \
|
|
.arg = G_OPTION_ARG_NONE, .arg_data = __arg_data, \
|
|
.description = \
|
|
"Disable the host-key-document verification.\n" __indent \
|
|
"Does not require the host-key documents to be valid.\n" __indent \
|
|
"For testing purposes, do not use for a production image.\n" __indent \
|
|
"(optional)\n", \
|
|
}
|
|
|
|
#define _entry_offline_maps_to_online(__arg_data, __indent) \
|
|
{ \
|
|
.long_name = "offline", .short_name = 0, .flags = G_OPTION_FLAG_REVERSE, \
|
|
.arg = G_OPTION_ARG_NONE, .arg_data = __arg_data, \
|
|
.description = "Don't download CRLs (optional).\n", \
|
|
}
|
|
|
|
#define _entry_verbose(__indent) \
|
|
{ \
|
|
.long_name = "verbose", .short_name = 'V', .flags = G_OPTION_FLAG_NO_ARG, \
|
|
.arg = G_OPTION_ARG_CALLBACK, .arg_data = &increase_log_lvl, \
|
|
.description = "Provide more detailed output (optional)\n", \
|
|
.arg_description = NULL, \
|
|
}
|
|
|
|
#define _entry_x_paf(__arg_data, __indent) \
|
|
{ \
|
|
.long_name = "x-paf", .short_name = 0, .flags = G_OPTION_FLAG_NONE, \
|
|
.arg = G_OPTION_ARG_CALLBACK, .arg_data = __arg_data, \
|
|
.description = "Specify the Plain text Attestation Flags\n" __indent \
|
|
"as a hexadecimal value. Flags that change\n" __indent \
|
|
"the paf (--phkh-*) take precedence over\n" __indent \
|
|
"this flag.\n" __indent \
|
|
"Setting the nonce paf is not allowed here.\n" __indent \
|
|
"(optional, default 0x0)\n", \
|
|
.arg_description = "HEX", \
|
|
}
|
|
|
|
#define _entry_x_no_nonce(__arg_data, __indent) \
|
|
{ \
|
|
.long_name = "x-no-nonce", .short_name = 0, .flags = G_OPTION_FLAG_REVERSE, \
|
|
.arg = G_OPTION_ARG_NONE, .arg_data = __arg_data, \
|
|
.description = "Do not use a nonce in the request.\n" __indent \
|
|
"(optional, not recommended)\n" \
|
|
}
|
|
|
|
#define _entry_x_aad_size(__arg_data, __indent) \
|
|
{ \
|
|
.long_name = "x-add-size", .short_name = 0, .flags = G_OPTION_FLAG_NONE, \
|
|
.arg = G_OPTION_ARG_INT, .arg_data = __arg_data, \
|
|
.description = "Specify the size of the additional area\n" __indent \
|
|
"Overwrite every flag that changes\n" __indent \
|
|
"this size implicitly. No verification is performed!\n" __indent \
|
|
"Ignored if negative.\n" __indent "(optional, default ignored)\n", \
|
|
.arg_description = "INT" \
|
|
}
|
|
|
|
#define _entry_x_user_data(__arg_data, __indent) \
|
|
{ \
|
|
.long_name = "x-user-data", .short_name = 0, .flags = G_OPTION_FLAG_NONE, \
|
|
.arg = G_OPTION_ARG_FILENAME, .arg_data = __arg_data, \
|
|
.description = "Use FILE to specify the user data.\n", .arg_description = "FILE", \
|
|
}
|
|
|
|
static gboolean increase_log_lvl(G_GNUC_UNUSED const char *option_name,
|
|
G_GNUC_UNUSED const char *value, G_GNUC_UNUSED void *data,
|
|
G_GNUC_UNUSED GError **error)
|
|
{
|
|
pvattest_log_increase_log_lvl(&pvattest_config.general.log_level);
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean create_set_paf(G_GNUC_UNUSED const char *option_name, const char *value,
|
|
G_GNUC_UNUSED void *data, GError **error)
|
|
{
|
|
return hex_str_toull(value, &pvattest_config.create.paf, error);
|
|
}
|
|
|
|
/***************************** GENERAL OPTIONS ********************************/
|
|
static gboolean print_version = FALSE;
|
|
|
|
static GOptionEntry general_options[] = {
|
|
{
|
|
.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.\n",
|
|
.arg_description = NULL,
|
|
},
|
|
_entry_verbose(""),
|
|
{ NULL },
|
|
};
|
|
|
|
/************************* CREATE ATTESTATION OPTIONS *************************/
|
|
#define create_indent " "
|
|
|
|
static GOptionEntry create_options[] = {
|
|
_entry_host_key_document(&pvattest_config.create.host_key_document_paths, create_indent),
|
|
_entry_certs(&pvattest_config.create.certificate_paths, create_indent),
|
|
_entry_crls(&pvattest_config.create.crl_paths, create_indent),
|
|
_entry_root_ca(&pvattest_config.create.root_ca_path, create_indent),
|
|
_entry_output(&pvattest_config.create.output_path, "attestation request", create_indent),
|
|
_entry_att_prot_key_save(&pvattest_config.create.arp_key_out_path, create_indent),
|
|
|
|
_entry_no_verify(&pvattest_config.create.no_verify, create_indent),
|
|
_entry_offline_maps_to_online(&pvattest_config.create.online, create_indent),
|
|
_entry_verbose(create_indent),
|
|
{ NULL }
|
|
};
|
|
|
|
static GOptionEntry experimental_create_options[] = {
|
|
_entry_x_no_nonce(&pvattest_config.create.use_nonce, create_indent),
|
|
_entry_x_paf(&create_set_paf, create_indent),
|
|
_entry_x_aad_size(&pvattest_config.create.x_aad_size, create_indent),
|
|
_entry_phkh_img(&pvattest_config.create.phkh_img, create_indent),
|
|
_entry_phkh_att(&pvattest_config.create.phkh_att, create_indent),
|
|
{ NULL }
|
|
};
|
|
|
|
static gboolean verify_create(GError **error)
|
|
{
|
|
if (!check_file_list(pvattest_config.create.host_key_document_paths, TRUE,
|
|
_("Specify --host-key-document at least once."), error))
|
|
return FALSE;
|
|
if (!pvattest_config.create.no_verify) {
|
|
if (!check_file_list(
|
|
pvattest_config.create.certificate_paths, TRUE,
|
|
_("Either specify the IBM Z signing key and"
|
|
" intermediate CA certificate\nby using the '--cert' option, or"
|
|
" use the '--no-verify' flag to disable the\nhost-key document"
|
|
" verification completely (at your own risk).\n"
|
|
"Only use this option in test environments or if"
|
|
" you trust the unverified document."),
|
|
error))
|
|
return FALSE;
|
|
}
|
|
if (!check_for_invalid_path(pvattest_config.create.arp_key_out_path, FALSE,
|
|
_("Missing argument for --arpk."), error))
|
|
return FALSE;
|
|
if (!check_for_invalid_path(pvattest_config.create.output_path, FALSE,
|
|
_("Missing argument for --output."), error))
|
|
return FALSE;
|
|
if (!check_optional_file_list(pvattest_config.create.crl_paths, TRUE, error))
|
|
return FALSE;
|
|
if (!check_for_optional_invalid_path(pvattest_config.create.root_ca_path, TRUE, error))
|
|
return FALSE;
|
|
return TRUE;
|
|
};
|
|
|
|
/************************* MEASUREMENT OPTIONS ********************************/
|
|
#define perform_indent " "
|
|
|
|
static GOptionEntry perform_options[] = {
|
|
_entry_input(&pvattest_config.perform.input_path, "attestation request", perform_indent),
|
|
_entry_output(&pvattest_config.perform.output_path, "attestation result", perform_indent),
|
|
_entry_verbose(perform_indent),
|
|
{ NULL },
|
|
};
|
|
|
|
static GOptionEntry experimental_perform_options[] = {
|
|
_entry_x_user_data(&pvattest_config.perform.user_data_path, perform_indent),
|
|
{ NULL },
|
|
};
|
|
|
|
static gboolean verify_perform(GError **error)
|
|
{
|
|
if (!check_for_invalid_path(pvattest_config.perform.input_path, TRUE,
|
|
_("Missing argument for --input."), error))
|
|
return FALSE;
|
|
if (!check_for_invalid_path(pvattest_config.perform.output_path, FALSE,
|
|
_("Missing argument for --output."), error))
|
|
return FALSE;
|
|
if (!check_for_optional_invalid_path(pvattest_config.perform.user_data_path, TRUE, error))
|
|
return FALSE;
|
|
return TRUE;
|
|
}
|
|
|
|
/************************* VERIFY OPTIONS ************************************/
|
|
#define verify_indent " "
|
|
|
|
static GOptionEntry verify_options[] = {
|
|
_entry_input(&pvattest_config.verify.input_path, "attestation result", verify_indent),
|
|
_entry_guest_hdr(&pvattest_config.verify.hdr_path, verify_indent),
|
|
_entry_att_prot_key_load(&pvattest_config.verify.arp_key_in_path, verify_indent),
|
|
_entry_verbose(verify_indent),
|
|
{ NULL },
|
|
};
|
|
|
|
static gboolean verify_verify(GError **error)
|
|
{
|
|
if (!check_for_invalid_path(pvattest_config.verify.input_path, TRUE,
|
|
_("Missing argument for --input."), error))
|
|
return FALSE;
|
|
if (!check_for_invalid_path(pvattest_config.verify.hdr_path, TRUE,
|
|
_("Missing argument for --hdr."), error))
|
|
return FALSE;
|
|
if (!check_for_invalid_path(pvattest_config.verify.arp_key_in_path, TRUE,
|
|
_("Missing argument for --arpk."), error))
|
|
return FALSE;
|
|
return TRUE;
|
|
}
|
|
|
|
/************************** OPTIONS END ***************************************/
|
|
|
|
#pragma GCC diagnostic pop
|
|
|
|
static char summary[] =
|
|
"\n"
|
|
"Create, perform, and verify attestation measurements for IBM Secure Execution guest"
|
|
" systems.\n"
|
|
"\n"
|
|
"COMMANDS\n"
|
|
" create On a trusted system, creates an attestation request.\n"
|
|
" perform On the SE-guest to be attested, sends the attestation request\n"
|
|
" to the Ultravisor and receives the answer.\n"
|
|
#ifndef PVATTEST_COMPILE_PERFORM
|
|
" (not supported on this platform)\n"
|
|
#endif /* PVATTEST_COMPILE_PERFORM */
|
|
|
|
" verify On a trusted system, compares the one from your trusted system.\n"
|
|
" If they differ, the Secure Execution guest might not be compromised\n"
|
|
"\n"
|
|
"Use '" GETTEXT_PACKAGE " [COMMAND] -h' to get detailed help\n";
|
|
static char create_summary[] =
|
|
"Create attestation measurement requests to attest an\n"
|
|
"IBM Secure Execution guest. Only build attestation requests in a trusted\n"
|
|
"environment such as your Workstation.\n"
|
|
"To avoid compromising the attestation do not publish the\n"
|
|
"protection key and delete it after verification.\n"
|
|
"Every 'create' will generate a new, random protection key.\n";
|
|
static char perform_summary[] =
|
|
#ifndef PVATTEST_COMPILE_PERFORM
|
|
"This system does NOT support 'perform'.\n"
|
|
#endif /* PVATTEST_COMPILE_PERFORM */
|
|
"Perform a measurement of this IBM Secure Execution guest using '/dev/uv'.\n";
|
|
static char verify_summary[] =
|
|
"Verify that a previously generated attestation measurement of an\n"
|
|
"IBM Secure Execution guest yielded the expected results.\n"
|
|
"Verify attestation requests only in a trusted environment, such as your workstation.";
|
|
|
|
static void print_version_and_exit(void)
|
|
{
|
|
printf("%s version %s\n", GETTEXT_PACKAGE, RELEASE_STRING);
|
|
printf("%s\n", COPYRIGHT_NOTICE);
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
|
|
static GOptionContext *create_ctx(GOptionEntry *options, GOptionEntry *experimental_options,
|
|
const char *param_name, const char *opt_summary)
|
|
{
|
|
GOptionContext *ret = g_option_context_new(param_name);
|
|
GOptionGroup *x_group = NULL;
|
|
g_option_context_add_main_entries(ret, options, NULL);
|
|
g_option_context_set_summary(ret, opt_summary);
|
|
if (experimental_options) {
|
|
x_group = g_option_group_new(
|
|
"experimental",
|
|
"Experimental Options; Do not use in a production environment",
|
|
"Show experimental options", NULL, NULL);
|
|
g_option_group_add_entries(x_group, experimental_options);
|
|
g_option_context_add_group(ret, x_group);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
enum pvattest_command pvattest_parse(int *argc, char **argvp[], pvattest_config_t **config,
|
|
GError **error)
|
|
{
|
|
g_autoptr(GOptionContext) main_context = NULL, subc_context = NULL;
|
|
char **argv = *argvp;
|
|
enum pvattest_command subc = PVATTEST_SUBC_INVALID;
|
|
verify_options_fn_t verify_options_fn = NULL;
|
|
|
|
pv_wrapped_g_assert(argc);
|
|
pv_wrapped_g_assert(argvp);
|
|
pv_wrapped_g_assert(config);
|
|
|
|
/*
|
|
* First parse until the first non dash argument. This must be one of the commands.
|
|
* (strict POSIX parsing)
|
|
*/
|
|
main_context = g_option_context_new(
|
|
"COMMAND [OPTIONS] - create, perform, and verify attestation measurements");
|
|
g_option_context_set_strict_posix(main_context, TRUE);
|
|
g_option_context_add_main_entries(main_context, general_options, NULL);
|
|
g_option_context_set_summary(main_context, summary);
|
|
|
|
if (!g_option_context_parse(main_context, argc, argvp, error))
|
|
return PVATTEST_SUBC_INVALID;
|
|
if (print_version)
|
|
print_version_and_exit();
|
|
|
|
/*
|
|
* Parse depending on the specified command
|
|
*/
|
|
else if (g_strcmp0(argv[1], PVATTEST_SUBC_STR_CREATE) == 0) {
|
|
subc_context =
|
|
create_ctx(create_options, experimental_create_options,
|
|
"create [OPTIONS] - create an attestation measurement request",
|
|
create_summary);
|
|
subc = PVATTEST_SUBC_CREATE;
|
|
verify_options_fn = &verify_create;
|
|
} else if (g_strcmp0(argv[1], PVATTEST_SUBC_STR_PERFORM) == 0) {
|
|
subc_context =
|
|
create_ctx(perform_options, experimental_perform_options,
|
|
"perform [OPTIONS] - perform an attestation measurement request",
|
|
perform_summary);
|
|
subc = PVATTEST_SUBC_PERFORM;
|
|
verify_options_fn = &verify_perform;
|
|
#ifndef PVATTEST_COMPILE_PERFORM
|
|
g_set_error(error, PVATTEST_ERROR, PVATTEST_ERR_INV_ARG,
|
|
_("This system does not support the 'perform' command."));
|
|
return PVATTEST_SUBC_INVALID;
|
|
#endif /* PVATTEST_COMPILE_PERFORM */
|
|
} else if (g_strcmp0(argv[1], PVATTEST_SUBC_STR_VERIFY) == 0) {
|
|
subc_context = create_ctx(verify_options, NULL,
|
|
"verify [OPTIONS] - verify an attestation measurement",
|
|
verify_summary);
|
|
subc = PVATTEST_SUBC_VERIFY;
|
|
verify_options_fn = &verify_verify;
|
|
} else {
|
|
if (argv[1])
|
|
g_set_error(error, PVATTEST_ERROR, PVATTEST_ERR_INV_ARGV,
|
|
_("Invalid command specified: %s."), argv[1]);
|
|
else
|
|
g_set_error(error, PVATTEST_ERROR, PVATTEST_ERR_INV_ARGV,
|
|
_("No command specified."));
|
|
return PVATTEST_SUBC_INVALID;
|
|
}
|
|
g_assert(verify_options_fn);
|
|
|
|
if (!g_option_context_parse(subc_context, argc, argvp, error))
|
|
return PVATTEST_SUBC_INVALID;
|
|
|
|
if (!verify_options_fn(error))
|
|
return PVATTEST_SUBC_INVALID;
|
|
|
|
*config = &pvattest_config;
|
|
return subc;
|
|
}
|
|
|
|
static void pvattest_parse_clear_create_config(pvattest_create_config_t *config)
|
|
{
|
|
if (!config)
|
|
return;
|
|
g_strfreev(config->host_key_document_paths);
|
|
g_strfreev(config->certificate_paths);
|
|
g_free(config->arp_key_out_path);
|
|
g_free(config->output_path);
|
|
}
|
|
|
|
static void pvattest_parse_clear_perform_config(pvattest_perform_config_t *config)
|
|
{
|
|
if (!config)
|
|
return;
|
|
g_free(config->input_path);
|
|
g_free(config->output_path);
|
|
}
|
|
|
|
static void pvattest_parse_clear_verify_config(pvattest_verify_config_t *config)
|
|
{
|
|
if (!config)
|
|
return;
|
|
g_free(config->input_path);
|
|
g_free(config->hdr_path);
|
|
g_free(config->arp_key_in_path);
|
|
}
|
|
|
|
void pvattest_parse_clear_config(pvattest_config_t *config)
|
|
{
|
|
if (!config)
|
|
return;
|
|
pvattest_parse_clear_create_config(&config->create);
|
|
pvattest_parse_clear_perform_config(&config->perform);
|
|
pvattest_parse_clear_verify_config(&config->verify);
|
|
}
|