1
1
mirror of https://github.com/systemd/systemd-stable.git synced 2024-12-23 17:34:00 +03:00

Merge pull request #24635 from DaanDeMeyer/repart-verity-sig

repart: Add support for generating verity sig partitions
This commit is contained in:
Daan De Meyer 2022-09-23 18:53:04 +02:00 committed by GitHub
commit 354dc913c5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 403 additions and 41 deletions

View File

@ -280,8 +280,8 @@ size. Currently three fields are defined for the JSON object:
in `rootHash` above.
3. The (optional) `certificateFingerprint` field should be a string containing
a SHA256 fingerprint of the X.509 certificate for the key that signed the
root hash, formatted as series of (lowercase) hex characters (no `:`
a SHA256 fingerprint of the X.509 certificate in DER format for the key that
signed the root hash, formatted as series of (lowercase) hex characters (no `:`
separators or such).
More fields might be added in later revisions of this specification.

View File

@ -583,17 +583,21 @@
<varlistentry>
<term><varname>Verity=</varname></term>
<listitem><para>Takes one of <literal>off</literal>, <literal>data</literal> or
<literal>hash</literal>. Defaults to <literal>off</literal>. If set to <literal>off</literal> or
<literal>data</literal>, the partition is populated with content as specified by
<varname>CopyBlocks=</varname> or <varname>CopyFiles=</varname>. If set to <literal>hash</literal>,
the partition will be populated with verity hashes from a matching verity data partition. A matching
data partition is a partition with <varname>Verity=</varname> set to <literal>data</literal> and the
same verity match key (as configured with <varname>VerityMatchKey=</varname>). If not explicitly
configured, the data partition's UUID will be set to the first 128 bits of the verity root hash.
Similarly, if not configured, the hash partition's UUID will be set to the final 128 bits of the
verity root hash. The verity root hash itself will be included in the output of
<command>systemd-repart</command>.</para>
<listitem><para>Takes one of <literal>off</literal>, <literal>data</literal>,
<literal>hash</literal> or <literal>signature</literal>. Defaults to <literal>off</literal>. If set
to <literal>off</literal> or <literal>data</literal>, the partition is populated with content as
specified by <varname>CopyBlocks=</varname> or <varname>CopyFiles=</varname>. If set to
<literal>hash</literal>, the partition will be populated with verity hashes from the matching verity
data partition. If set to <literal>signature</literal>, The partition will be populated with a JSON
object containing a signature of the verity root hash of the matching verity hash partition.</para>
<para>A matching verity partition is a partition with the same verity match key (as configured with
<varname>VerityMatchKey=</varname>).</para>
<para>If not explicitly configured, the data partition's UUID will be set to the first 128
bits of the verity root hash. Similarly, if not configured, the hash partition's UUID will be set to
the final 128 bits of the verity root hash. The verity root hash itself will be included in the
output of <command>systemd-repart</command>.</para>
<para>This option has no effect if the partition already exists.</para>

View File

@ -3682,7 +3682,8 @@ if conf.get('ENABLE_REPART') == 1
link_with : [libshared],
dependencies : [threads,
libblkid,
libfdisk],
libfdisk,
libopenssl],
install_rpath : rootpkglibdir,
install : true,
install_dir : rootbindir)

View File

@ -521,6 +521,19 @@ char* strshorten(char *s, size_t l) {
return s;
}
int strgrowpad0(char **s, size_t l) {
assert(s);
char *q = realloc(*s, l);
if (!q)
return -ENOMEM;
*s = q;
size_t sz = strlen(*s);
memzero(*s + sz, l - sz);
return 0;
}
char *strreplace(const char *text, const char *old_string, const char *new_string) {
size_t l, old_len, new_len;
char *t, *ret = NULL;

View File

@ -152,6 +152,8 @@ char *cellescape(char *buf, size_t len, const char *s);
char* strshorten(char *s, size_t l);
int strgrowpad0(char **s, size_t l);
char *strreplace(const char *text, const char *old_string, const char *new_string);
char *strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]);

View File

@ -948,7 +948,7 @@ static int run(int argc, char *argv[]) {
d->fd,
&arg_verity_settings);
if (r < 0)
return r;
return log_error_errno(r, "Failed to load verity signature partition: %m");
switch (arg_action) {

View File

@ -40,6 +40,7 @@
#include "hexdecoct.h"
#include "hmac.h"
#include "id128-util.h"
#include "io-util.h"
#include "json.h"
#include "list.h"
#include "loop-util.h"
@ -48,6 +49,7 @@
#include "mkfs-util.h"
#include "mount-util.h"
#include "mountpoint-util.h"
#include "openssl-util.h"
#include "parse-argument.h"
#include "parse-helpers.h"
#include "pretty-print.h"
@ -76,6 +78,9 @@
/* Hard lower limit for new partition sizes */
#define HARD_MIN_SIZE 4096
/* We know up front we're never going to put more than this in a verity sig partition. */
#define VERITY_SIG_SIZE (HARD_MIN_SIZE * 4)
/* libfdisk takes off slightly more than 1M of the disk size when creating a GPT disk label */
#define GPT_METADATA_SIZE (1044*1024)
@ -113,6 +118,8 @@ static PagerFlags arg_pager_flags = 0;
static bool arg_legend = true;
static void *arg_key = NULL;
static size_t arg_key_size = 0;
static EVP_PKEY *arg_private_key = NULL;
static X509 *arg_certificate = NULL;
static char *arg_tpm2_device = NULL;
static uint32_t arg_tpm2_pcr_mask = UINT32_MAX;
static char *arg_tpm2_public_key = NULL;
@ -123,6 +130,8 @@ STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
STATIC_DESTRUCTOR_REGISTER(arg_definitions, strv_freep);
STATIC_DESTRUCTOR_REGISTER(arg_key, erase_and_freep);
STATIC_DESTRUCTOR_REGISTER(arg_private_key, EVP_PKEY_freep);
STATIC_DESTRUCTOR_REGISTER(arg_certificate, X509_freep);
STATIC_DESTRUCTOR_REGISTER(arg_tpm2_device, freep);
STATIC_DESTRUCTOR_REGISTER(arg_tpm2_public_key, freep);
@ -143,6 +152,7 @@ typedef enum VerityMode {
VERITY_OFF,
VERITY_DATA,
VERITY_HASH,
VERITY_SIG,
_VERITY_MODE_MAX,
_VERITY_MODE_INVALID = -EINVAL,
} VerityMode;
@ -240,6 +250,7 @@ static const char *verity_mode_table[_VERITY_MODE_MAX] = {
[VERITY_OFF] = "off",
[VERITY_DATA] = "data",
[VERITY_HASH] = "hash",
[VERITY_SIG] = "signature",
};
#if HAVE_LIBCRYPTSETUP
@ -515,6 +526,9 @@ static uint64_t partition_min_size(const Context *context, const Partition *p) {
return p->current_size;
}
if (p->verity == VERITY_SIG)
return VERITY_SIG_SIZE;
sz = p->current_size != UINT64_MAX ? p->current_size : HARD_MIN_SIZE;
if (!PARTITION_EXISTS(p)) {
@ -556,6 +570,9 @@ static uint64_t partition_max_size(const Context *context, const Partition *p) {
return p->current_size;
}
if (p->verity == VERITY_SIG)
return VERITY_SIG_SIZE;
if (p->size_max == UINT64_MAX)
return UINT64_MAX;
@ -1548,7 +1565,8 @@ static int partition_read_definition(Partition *p, const char *path, const char
"VerityMatchKey= can only be set if Verity= is not \"%s\"",
verity_mode_to_string(p->verity));
if (p->verity == VERITY_HASH && (p->copy_files || p->copy_blocks_path || p->copy_blocks_auto || p->format || p->make_directories))
if (IN_SET(p->verity, VERITY_HASH, VERITY_SIG) &&
(p->copy_files || p->copy_blocks_path || p->copy_blocks_auto || p->format || p->make_directories))
return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
"CopyBlocks=/CopyFiles=/Format=/MakeDirectories= cannot be used with Verity=%s",
verity_mode_to_string(p->verity));
@ -1557,6 +1575,19 @@ static int partition_read_definition(Partition *p, const char *path, const char
return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
"Encrypting verity hash/data partitions is not supported");
if (p->verity == VERITY_SIG && !arg_private_key)
return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
"Verity signature partition requested but no private key provided (--private-key=)");
if (p->verity == VERITY_SIG && !arg_certificate)
return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
"Verity signature partition requested but no PEM certificate provided (--certificate-file=)");
if (p->verity == VERITY_SIG && (p->size_min != UINT64_MAX || p->size_max != UINT64_MAX))
return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
"SizeMinBytes=/SizeMaxBytes= cannot be used with Verity=%s",
verity_mode_to_string(p->verity));
/* Verity partitions are read only, let's imply the RO flag hence, unless explicitly configured otherwise. */
if ((gpt_partition_type_is_root_verity(p->type_uuid) ||
gpt_partition_type_is_usr_verity(p->type_uuid)) &&
@ -1665,7 +1696,7 @@ static int context_read_definitions(
continue;
for (VerityMode mode = VERITY_OFF + 1; mode < _VERITY_MODE_MAX; mode++) {
Partition *q;
Partition *q = NULL;
if (p->verity == mode)
continue;
@ -1674,7 +1705,7 @@ static int context_read_definitions(
continue;
r = find_verity_sibling(context, p, mode, &q);
if (r == -ENXIO)
if (mode != VERITY_SIG && r == -ENXIO)
return log_syntax(NULL, LOG_ERR, p->definition_path, 1, SYNTHETIC_ERRNO(EINVAL),
"Missing verity %s partition for verity %s partition with VerityMatchKey=%s",
verity_mode_to_string(mode), verity_mode_to_string(p->verity), p->verity_match_key);
@ -1685,12 +1716,14 @@ static int context_read_definitions(
if (r < 0)
return r;
if (q->priority != p->priority)
return log_syntax(NULL, LOG_ERR, p->definition_path, 1, SYNTHETIC_ERRNO(EINVAL),
"Priority mismatch (%i != %i) for verity sibling partitions with VerityMatchKey=%s",
p->priority, q->priority, p->verity_match_key);
if (q) {
if (q->priority != p->priority)
return log_syntax(NULL, LOG_ERR, p->definition_path, 1, SYNTHETIC_ERRNO(EINVAL),
"Priority mismatch (%i != %i) for verity sibling partitions with VerityMatchKey=%s",
p->priority, q->priority, p->verity_match_key);
p->siblings[mode] = q;
p->siblings[mode] = q;
}
}
}
@ -3577,11 +3610,11 @@ static int do_verity_format(
return 0;
#else
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "libcryptsetup is not supported, cannot setup verity: %m");
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "libcryptsetup is not supported, cannot setup verity hashes: %m");
#endif
}
static int context_verity(Context *context) {
static int context_verity_hash(Context *context) {
int fd = -1, r;
assert(context);
@ -3642,6 +3675,181 @@ static int context_verity(Context *context) {
return 0;
}
static int parse_x509_certificate(const char *certificate, size_t certificate_size, X509 **ret) {
#if HAVE_OPENSSL
_cleanup_(X509_freep) X509 *cert = NULL;
_cleanup_(BIO_freep) BIO *cb = NULL;
assert(certificate);
assert(certificate_size > 0);
assert(ret);
cb = BIO_new_mem_buf(certificate, certificate_size);
if (!cb)
return log_oom();
cert = PEM_read_bio_X509(cb, NULL, NULL, NULL);
if (!cert)
return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Failed to parse X.509 certificate: %s",
ERR_error_string(ERR_get_error(), NULL));
if (ret)
*ret = TAKE_PTR(cert);
return 0;
#else
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "openssl is not supported, cannot parse X509 certificate.");
#endif
}
static int parse_private_key(const char *key, size_t key_size, EVP_PKEY **ret) {
#if HAVE_OPENSSL
_cleanup_(BIO_freep) BIO *kb = NULL;
_cleanup_(EVP_PKEY_freep) EVP_PKEY *pk = NULL;
assert(key);
assert(key_size > 0);
assert(ret);
kb = BIO_new_mem_buf(key, key_size);
if (!kb)
return log_oom();
pk = PEM_read_bio_PrivateKey(kb, NULL, NULL, NULL);
if (!pk)
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to parse PEM private key: %s",
ERR_error_string(ERR_get_error(), NULL));
if (ret)
*ret = TAKE_PTR(pk);
return 0;
#else
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "openssl is not supported, cannot parse private key.");
#endif
}
static int sign_verity_roothash(
const uint8_t *roothash,
size_t roothash_size,
uint8_t **ret_signature,
size_t *ret_signature_size) {
#if HAVE_OPENSSL
_cleanup_(BIO_freep) BIO *rb = NULL;
_cleanup_(PKCS7_freep) PKCS7 *p7 = NULL;
_cleanup_free_ char *hex = NULL;
_cleanup_free_ uint8_t *sig = NULL;
int sigsz;
assert(roothash);
assert(roothash_size > 0);
assert(ret_signature);
assert(ret_signature_size);
hex = hexmem(roothash, roothash_size);
if (!hex)
return log_oom();
rb = BIO_new_mem_buf(hex, -1);
if (!rb)
return log_oom();
p7 = PKCS7_sign(arg_certificate, arg_private_key, NULL, rb, PKCS7_DETACHED|PKCS7_NOATTR|PKCS7_BINARY);
if (!p7)
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to calculate PKCS7 signature: %s",
ERR_error_string(ERR_get_error(), NULL));
sigsz = i2d_PKCS7(p7, &sig);
if (sigsz < 0)
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to convert PKCS7 signature to DER: %s",
ERR_error_string(ERR_get_error(), NULL));
*ret_signature = TAKE_PTR(sig);
*ret_signature_size = sigsz;
return 0;
#else
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "openssl is not supported, cannot setup verity signature: %m");
#endif
}
static int context_verity_sig(Context *context) {
int fd = -1, r;
assert(context);
LIST_FOREACH(partitions, p, context->partitions) {
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
_cleanup_free_ uint8_t *sig = NULL;
_cleanup_free_ char *text = NULL;
Partition *hp;
uint8_t fp[X509_FINGERPRINT_SIZE];
size_t sigsz, padsz;
if (p->dropped)
continue;
if (PARTITION_EXISTS(p))
continue;
if (p->verity != VERITY_SIG)
continue;
assert_se(hp = p->siblings[VERITY_HASH]);
assert(!hp->dropped);
assert(arg_certificate);
if (fd < 0)
assert_se((fd = fdisk_get_devfd(context->fdisk_context)) >= 0);
r = sign_verity_roothash(hp->roothash, hp->roothash_size, &sig, &sigsz);
if (r < 0)
return r;
r = x509_fingerprint(arg_certificate, fp);
if (r < 0)
return log_error_errno(r, "Unable to calculate X509 certificate fingerprint: %m");
r = json_build(&v,
JSON_BUILD_OBJECT(
JSON_BUILD_PAIR("rootHash", JSON_BUILD_HEX(hp->roothash, hp->roothash_size)),
JSON_BUILD_PAIR(
"certificateFingerprint",
JSON_BUILD_HEX(fp, sizeof(fp))
),
JSON_BUILD_PAIR("signature", JSON_BUILD_BASE64(sig, sigsz))
)
);
if (r < 0)
return log_error_errno(r, "Failed to build JSON object: %m");
r = json_variant_format(v, 0, &text);
if (r < 0)
return log_error_errno(r, "Failed to format JSON object: %m");
padsz = round_up_size(strlen(text), 4096);
assert_se(padsz <= p->new_size);
r = strgrowpad0(&text, padsz);
if (r < 0)
return log_error_errno(r, "Failed to pad string to %s", FORMAT_BYTES(padsz));
if (lseek(fd, p->offset, SEEK_SET) == (off_t) -1)
return log_error_errno(errno, "Failed to seek to partition offset: %m");
r = loop_write(fd, text, padsz, /*do_poll=*/ false);
if (r < 0)
return log_error_errno(r, "Failed to write verity signature to partition: %m");
if (fsync(fd) < 0)
return log_error_errno(errno, "Failed to synchronize verity signature JSON: %m");
}
return 0;
}
static int partition_acquire_uuid(Context *context, Partition *p, sd_id128_t *ret) {
struct {
sd_id128_t type_uuid;
@ -3787,7 +3995,7 @@ static int context_acquire_partition_uuids_and_labels(Context *context) {
if (!sd_id128_is_null(p->current_uuid))
p->new_uuid = p->current_uuid; /* Never change initialized UUIDs */
else if (!p->new_uuid_is_set && p->verity == VERITY_OFF) {
else if (!p->new_uuid_is_set && !IN_SET(p->verity, VERITY_DATA, VERITY_HASH)) {
/* Not explicitly set by user! */
r = partition_acquire_uuid(context, p, &p->new_uuid);
if (r < 0)
@ -4201,7 +4409,11 @@ static int context_write_partition_table(
if (r < 0)
return r;
r = context_verity(context);
r = context_verity_hash(context);
if (r < 0)
return r;
r = context_verity_sig(context);
if (r < 0)
return r;
@ -4747,6 +4959,10 @@ static int help(void) {
" --image=PATH Operate relative to image file\n"
" --definitions=DIR Find partition definitions in specified directory\n"
" --key-file=PATH Key to use when encrypting partitions\n"
" --private-key=PATH Private key to use when generating verity roothash\n"
" signatures\n"
" --certificate=PATH PEM certificate to use when generating verity\n"
" roothash signatures\n"
" --tpm2-device=PATH Path to TPM2 device node to use\n"
" --tpm2-pcrs=PCR1+PCR2+PCR3+…\n"
" TPM2 PCR indexes to use for TPM2 enrollment\n"
@ -4787,6 +5003,8 @@ static int parse_argv(int argc, char *argv[]) {
ARG_SIZE,
ARG_JSON,
ARG_KEY_FILE,
ARG_PRIVATE_KEY,
ARG_CERTIFICATE,
ARG_TPM2_DEVICE,
ARG_TPM2_PCRS,
ARG_TPM2_PUBLIC_KEY,
@ -4812,6 +5030,8 @@ static int parse_argv(int argc, char *argv[]) {
{ "size", required_argument, NULL, ARG_SIZE },
{ "json", required_argument, NULL, ARG_JSON },
{ "key-file", required_argument, NULL, ARG_KEY_FILE },
{ "private-key", required_argument, NULL, ARG_PRIVATE_KEY },
{ "certificate", required_argument, NULL, ARG_CERTIFICATE },
{ "tpm2-device", required_argument, NULL, ARG_TPM2_DEVICE },
{ "tpm2-pcrs", required_argument, NULL, ARG_TPM2_PCRS },
{ "tpm2-public-key", required_argument, NULL, ARG_TPM2_PUBLIC_KEY },
@ -4985,6 +5205,46 @@ static int parse_argv(int argc, char *argv[]) {
break;
}
case ARG_PRIVATE_KEY: {
_cleanup_(erase_and_freep) char *k = NULL;
size_t n = 0;
r = read_full_file_full(
AT_FDCWD, optarg, UINT64_MAX, SIZE_MAX,
READ_FULL_FILE_SECURE|READ_FULL_FILE_WARN_WORLD_READABLE|READ_FULL_FILE_CONNECT_SOCKET,
NULL,
&k, &n);
if (r < 0)
return log_error_errno(r, "Failed to read key file '%s': %m", optarg);
EVP_PKEY_free(arg_private_key);
arg_private_key = NULL;
r = parse_private_key(k, n, &arg_private_key);
if (r < 0)
return r;
break;
}
case ARG_CERTIFICATE: {
_cleanup_free_ char *cert = NULL;
size_t n = 0;
r = read_full_file_full(
AT_FDCWD, optarg, UINT64_MAX, SIZE_MAX,
READ_FULL_FILE_CONNECT_SOCKET,
NULL,
&cert, &n);
if (r < 0)
return log_error_errno(r, "Failed to read certificate file '%s': %m", optarg);
X509_free(arg_certificate);
arg_certificate = NULL;
r = parse_x509_certificate(cert, n, &arg_certificate);
if (r < 0)
return r;
break;
}
case ARG_TPM2_DEVICE: {
_cleanup_free_ char *device = NULL;

View File

@ -603,13 +603,10 @@ int dissect_image(
m->has_verity_sig = true;
/* If root hash is specified explicitly, then ignore any embedded signature */
if (!verity)
continue;
if (verity->designator >= 0 && verity->designator != PARTITION_ROOT)
continue;
if (verity->root_hash)
continue;
assert_se((architecture = gpt_partition_type_uuid_to_arch(type_id)) >= 0);
designator = PARTITION_VERITY_SIG_OF(PARTITION_ROOT_OF_ARCH(architecture));
@ -667,13 +664,10 @@ int dissect_image(
m->has_verity_sig = true;
/* If usr hash is specified explicitly, then ignore any embedded signature */
if (!verity)
continue;
if (verity->designator >= 0 && verity->designator != PARTITION_USR)
continue;
if (verity->root_hash)
continue;
assert_se((architecture = gpt_partition_type_uuid_to_arch(type_id)) >= 0);
designator = PARTITION_VERITY_SIG_OF(PARTITION_USR_OF_ARCH(architecture));
@ -1074,6 +1068,9 @@ int dissect_image(
if (verity->designator >= 0 && !m->partitions[verity->designator].found)
return -EADDRNOTAVAIL;
bool have_verity_sig_partition =
m->partitions[verity->designator == PARTITION_USR ? PARTITION_USR_VERITY_SIG : PARTITION_ROOT_VERITY_SIG].found;
if (verity->root_hash) {
/* If we have an explicit root hash and found the partitions for it, then we are ready to use
* Verity, set things up for it */
@ -1097,9 +1094,9 @@ int dissect_image(
}
if (m->verity_ready)
m->verity_sig_ready = verity->root_hash_sig;
m->verity_sig_ready = verity->root_hash_sig || have_verity_sig_partition;
} else if (m->partitions[verity->designator == PARTITION_USR ? PARTITION_USR_VERITY_SIG : PARTITION_ROOT_VERITY_SIG].found) {
} else if (have_verity_sig_partition) {
/* If we found an embedded signature partition, we are ready, too. */

View File

@ -195,3 +195,22 @@ int string_hashsum(
}
# endif
#endif
int x509_fingerprint(X509 *cert, uint8_t buffer[static SHA256_DIGEST_SIZE]) {
#if HAVE_OPENSSL
_cleanup_free_ uint8_t *der = NULL;
int dersz;
assert(cert);
dersz = i2d_X509(cert, &der);
if (dersz < 0)
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Unable to convert PEM certificate to DER format: %s",
ERR_error_string(ERR_get_error(), NULL));
sha256_direct(der, dersz, buffer);
return 0;
#else
return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "openssl is not supported, cannot calculate X509 fingerprint: %m");
#endif
}

View File

@ -2,6 +2,9 @@
#pragma once
#include "macro.h"
#include "sha256.h"
#define X509_FINGERPRINT_SIZE SHA256_DIGEST_SIZE
#if HAVE_OPENSSL
# include <openssl/bio.h>
@ -20,10 +23,8 @@
# include <openssl/core_names.h>
# endif
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(X509*, X509_free, NULL);
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(X509_NAME*, X509_NAME_free, NULL);
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(EVP_PKEY_CTX*, EVP_PKEY_CTX_free, NULL);
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(EVP_PKEY*, EVP_PKEY_free, NULL);
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(EVP_CIPHER_CTX*, EVP_CIPHER_CTX_free, NULL);
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(EC_POINT*, EC_POINT_free, NULL);
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(EC_GROUP*, EC_GROUP_free, NULL);
@ -50,8 +51,28 @@ int rsa_pkey_to_suitable_key_size(EVP_PKEY *pkey, size_t *ret_suitable_key_size)
int pubkey_fingerprint(EVP_PKEY *pk, const EVP_MD *md, void **ret, size_t *ret_size);
#else
typedef struct X509 X509;
typedef struct EVP_PKEY EVP_PKEY;
static inline void *X509_free(X509 *p) {
assert(p == NULL);
return NULL;
}
static inline void *EVP_PKEY_free(EVP_PKEY *p) {
assert(p == NULL);
return NULL;
}
#endif
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(X509*, X509_free, NULL);
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(EVP_PKEY*, EVP_PKEY_free, NULL);
int x509_fingerprint(X509 *cert, uint8_t buffer[static X509_FINGERPRINT_SIZE]);
#if PREFER_OPENSSL
/* The openssl definition */
typedef const EVP_MD* hash_md_t;

View File

@ -10,6 +10,9 @@ TEST_DESCRIPTION="test systemd-repart"
test_append_files() {
if ! get_bool "${TEST_NO_QEMU:=}"; then
install_dmevent
if command -v openssl >/dev/null 2>&1; then
inst_binary openssl
fi
instmods dm_verity =md
generate_module_dependencies
fi

View File

@ -1267,6 +1267,15 @@ install_missing_libraries() {
continue
fi
done
# Install extra openssl 3 stuff
path="$(pkg-config --variable=libdir libcrypto)"
inst_simple "${path}/ossl-modules/legacy.so" || true
inst_simple "${path}/ossl-modules/fips.so" || true
inst_simple "${path}/engines-3/afalg.so" || true
inst_simple "${path}/engines-3/capi.so" || true
inst_simple "${path}/engines-3/loader_attic.so" || true
inst_simple "${path}/engines-3/padlock.so" || true
}
cleanup_loopdev() {

View File

@ -214,8 +214,11 @@ losetup -d "${loop}"
ROOT_UUID="$(systemd-id128 -u show "$(head -c 32 "${image}.roothash")" -u | tail -n 1 | cut -b 6-)"
VERITY_UUID="$(systemd-id128 -u show "$(tail -c 32 "${image}.roothash")" -u | tail -n 1 | cut -b 6-)"
systemd-dissect --json=short --root-hash "${roothash}" "${image}.gpt" | grep -q '{"rw":"ro","designator":"root","partition_uuid":"'"$ROOT_UUID"'","partition_label":"Root Partition","fstype":"squashfs","architecture":"'"$architecture"'","verity":"yes",'
systemd-dissect --json=short --root-hash "${roothash}" "${image}.gpt" | grep -q '{"rw":"ro","designator":"root","partition_uuid":"'"$ROOT_UUID"'","partition_label":"Root Partition","fstype":"squashfs","architecture":"'"$architecture"'","verity":"signed",'
systemd-dissect --json=short --root-hash "${roothash}" "${image}.gpt" | grep -q '{"rw":"ro","designator":"root-verity","partition_uuid":"'"$VERITY_UUID"'","partition_label":"Verity Partition","fstype":"DM_verity_hash","architecture":"'"$architecture"'","verity":null,'
if [ "${HAVE_OPENSSL}" -eq 1 ]; then
systemd-dissect --json=short --root-hash "${roothash}" "${image}.gpt" | grep -q -E '{"rw":"ro","designator":"root-verity-sig","partition_uuid":"'".*"'","partition_label":"Signature Partition","fstype":"verity_hash_signature","architecture":"'"$architecture"'","verity":null,'
fi
systemd-dissect --root-hash "${roothash}" "${image}.gpt" | grep -q -F "MARKER=1"
systemd-dissect --root-hash "${roothash}" "${image}.gpt" | grep -q -F -f <(sed 's/"//g' "$os_release")

View File

@ -711,7 +711,7 @@ test_verity() {
# shellcheck disable=SC2064
trap "rm -rf '$defs' '$imgs'" RETURN
cat >"$defs/root.conf" <<EOF
cat >"$defs/verity-data.conf" <<EOF
[Partition]
Type=root-${architecture}
CopyFiles=${defs}
@ -719,24 +719,54 @@ Verity=data
VerityMatchKey=root
EOF
cat >"$defs/verity.conf" <<EOF
cat >"$defs/verity-hash.conf" <<EOF
[Partition]
Type=root-${architecture}-verity
Verity=hash
VerityMatchKey=root
EOF
cat >"$defs/verity-sig.conf" <<EOF
[Partition]
Type=root-${architecture}-verity-sig
Verity=signature
VerityMatchKey=root
EOF
# Unfortunately OpenSSL insists on reading some config file, hence provide one with mostly placeholder contents
cat >> "$defs/verity.openssl.cnf" <<EOF
[ req ]
prompt = no
distinguished_name = req_distinguished_name
[ req_distinguished_name ]
C = DE
ST = Test State
L = Test Locality
O = Org Name
OU = Org Unit Name
CN = Common Name
emailAddress = test@email.com
EOF
openssl req -config "$defs/verity.openssl.cnf" -new -x509 -newkey rsa:1024 -keyout "$defs/verity.key" -out "$defs/verity.crt" -days 365 -nodes
mkdir -p /run/verity.d
ln -s "$defs/verity.crt" /run/verity.d/ok.crt
output=$(systemd-repart --definitions="$defs" \
--seed="$seed" \
--dry-run=no \
--empty=create \
--size=auto \
--json=pretty \
--private-key="$defs/verity.key" \
--certificate="$defs/verity.crt" \
"$imgs/verity")
roothash=$(jq -r ".[] | select(.type == \"root-${architecture}-verity\") | .roothash" <<< "$output")
# Check that we can dissect, mount and unmount a repart verity image.
# Check that we can dissect, mount and unmount a repart verity image.
systemd-dissect "$imgs/verity" --root-hash "$roothash"
systemd-dissect "$imgs/verity" --root-hash "$roothash" -M "$imgs/mnt"