pkcs7-util: add helpers for extracting signer info from PKCS7 signatures

Once we start measuring Verity volumes as we activate them we want to
include information about the signature keys used, so that we can have
distinct ones for confext and for sysext and ther purposes and thus have
a cryptograpically protected hint about the kind of image we have
activated in the event log.

Ideally we'd measure a fingerprint of the signing certificate here, but
we don't have that here typically (as PKCS7 signatures used here
typically do not embed that), hence use the next best thing: the issuer
name and the serial number.
This commit is contained in:
Lennart Poettering
2026-02-17 22:00:14 +01:00
parent 8485bba53d
commit 3f31c8ff46
5 changed files with 161 additions and 0 deletions
+1
View File
@@ -152,6 +152,7 @@ shared_sources = files(
'pcre2-util.c',
'pcrextend-util.c',
'pe-binary.c',
'pkcs7-util.c',
'pkcs11-util.c',
'plymouth-util.c',
'polkit-agent.c',
+84
View File
@@ -0,0 +1,84 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "alloc-util.h"
#include "openssl-util.h"
#include "pkcs7-util.h"
#include "log.h"
#define SIGNERS_MAX 32
static void signer_done(Signer *signer) {
assert(signer);
iovec_done(&signer->issuer);
iovec_done(&signer->serial);
}
void signer_free_many(Signer *signers, size_t n) {
assert(signers || n == 0);
FOREACH_ARRAY(i, signers, n)
signer_done(i);
free(signers);
}
int pkcs7_extract_signers(
const struct iovec *sig,
Signer **ret_signers,
size_t *ret_n_signers) {
assert(ret_signers);
assert(ret_n_signers);
if (!iovec_is_set(sig))
return -EBADMSG;
#if HAVE_OPENSSL
const unsigned char *d = sig->iov_base;
_cleanup_(PKCS7_freep) PKCS7 *p7 = NULL;
p7 = d2i_PKCS7(/* a= */ NULL, &d, (long) sig->iov_len);
if (!p7)
return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "Failed to parse PKCS7 DER signature data.");
STACK_OF(PKCS7_SIGNER_INFO) *sinfos = PKCS7_get_signer_info(p7);
if (!sinfos)
return log_debug_errno(SYNTHETIC_ERRNO(ENODATA), "No signature information in PKCS7 signature?");
int n = sk_PKCS7_SIGNER_INFO_num(sinfos);
if (n == 0)
return log_debug_errno(SYNTHETIC_ERRNO(ENODATA), "No signatures in PKCS7 signature, refusing.");
if (n > SIGNERS_MAX) /* safety net, in case people send us weirdly complex signatures */
return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "Too many signatures, refusing.");
assert(n > 0);
size_t n_signers = 0;
Signer *signers = new(Signer, n);
if (!signers)
return log_oom_debug();
CLEANUP_ARRAY(signers, n_signers, signer_free_many);
for (int i = 0; i < n; i++) {
PKCS7_SIGNER_INFO *si = sk_PKCS7_SIGNER_INFO_value(PKCS7_get_signer_info(p7), i);
if (!si)
return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to get signer information.");
_cleanup_(signer_done) Signer signer = {};
_cleanup_free_ unsigned char *p = NULL;
int len = i2d_X509_NAME(si->issuer_and_serial->issuer, &p);
signer.issuer = IOVEC_MAKE(TAKE_PTR(p), len);
len = i2d_ASN1_INTEGER(si->issuer_and_serial->serial, &p);
signer.serial = IOVEC_MAKE(TAKE_PTR(p), len);
signers[n_signers++] = TAKE_STRUCT(signer);
}
*ret_signers = TAKE_PTR(signers);
*ret_n_signers = n_signers;
return n;
#else
return -EOPNOTSUPP;
#endif
}
+16
View File
@@ -0,0 +1,16 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include <sys/uio.h>
typedef struct Signer {
struct iovec issuer;
struct iovec serial;
} Signer;
void signer_free_many(Signer *signers, size_t n);
int pkcs7_extract_signers(
const struct iovec *sig,
Signer **ret_signers,
size_t *ret_n_signers);
+5
View File
@@ -405,6 +405,11 @@ executables += [
'dependencies' : libopenssl,
'conditions' : ['HAVE_OPENSSL'],
},
test_template + {
'sources' : files('test-pkcs7-util.c'),
'dependencies' : libopenssl,
'conditions' : ['HAVE_OPENSSL'],
},
test_template + {
'sources' : files('test-parse-util.c'),
'dependencies' : libm,
+55
View File
@@ -0,0 +1,55 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "hexdecoct.h"
#include "iovec-util.h"
#include "pkcs7-util.h"
#include "tests.h"
TEST(pkcs7_extract_signers) {
const char tsig[] =
"MIIEgwYJKoZIhvcNAQcCoIIEdDCCBHACAQExDzANBglghkgBZQMEAgEFADALBgkqhkiG9w0BBwGgggLp"
"MIIC5TCCAc2gAwIBAgIURlvlj5ak0ZhvNS8hENNKwVv60x0wDQYJKoZIhvcNAQELBQAwGzEZMBcGA1UE"
"AwwQbWtvc2kgb2YgbGVubmFydDAeFw0yNTAyMDMxMTAwMjNaFw0yNzAyMDMxMTAwMjNaMBsxGTAXBgNV"
"BAMMEG1rb3NpIG9mIGxlbm5hcnQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCldQdmHVgU"
"m4saaSqEpF1EVRf9pcIxpVShROBGAbxpxs4BQ7vV7zCg5bXwEVaqaENlIyzqPAj+YQifQS3Dj6LQfH3i"
"War+ciKAv4PYcESG+pcdBb2kJOS8cVD6abZlO9rInvdhXK6PhYF7VohMwMPj/yJYdO50skwA5OQsCHO6"
"amowh0tVzNzpbaJZg6wWIyTf3+ZzZdnOl5EvBCaUqeUhbaxRV9SrAw51rzAOnndUhW1we/vVKEGAPk3c"
"SCb0LLPBG20XX2C00UXgnCWBkU5rkq6BnWSuInhFKCa48avstIet1ZFBA5T5J83ncU9UOVFksEYBXFoc"
"lzcmcbx/2/oFAgMBAAGjITAfMB0GA1UdDgQWBBQewILFCixwq2rejOvJqmZSug2BBDANBgkqhkiG9w0B"
"AQsFAAOCAQEAIvaeNPaJUoIUN5lQC/kcCiKeys96WNRGL2wbTp5PqdnRw14sbWY5iC2z13ih3dmTI9NF"
"TBa7C/ji+5BaAfiJF17LOV00Y8eP5V94fHz4isb0sv5RzLsE4h8X7QFk4JBdV5GiCDzPXjxQAx9kM2so"
"9RGtL8EhHpNygYDgyZ18YeiwcUPkCXT+xG2rM6s/Xlsji0s/18ycI4G8AC8dj5HycyS9BiZHgKrkgqTb"
"VPo4zHYzhZdh0Qrd0J4YpoaotzQ35bkH9PtIkF6C7mE1Z7uMSGFkGQASgJ0BDTpM8QPAf2HIR2xxEtJR"
"ZXkwxxdC+W9AJAzqJldmCHYGSrSR54J0rDGCAV4wggFaAgEBMDMwGzEZMBcGA1UEAwwQbWtvc2kgb2Yg"
"bGVubmFydAIURlvlj5ak0ZhvNS8hENNKwVv60x0wDQYJYIZIAWUDBAIBBQAwDQYJKoZIhvcNAQEBBQAE"
"ggEAXccqvpiEWsz/xvuLhINVZKIOznVdqjkERbZSqCBK94BYESSd+cijaB4XbYaFUZ45Bb3uUDQ56Ojq"
"WoY1elEfqPyCb4vc887QoHmxI0BtdIaHhIDfCGBxhX8fwMknxqjgFa9YvONmDtv4QG4syTw+U3SEqBaa"
"Avftqaa4v4eLk4uZ0nMIgMkx4qOlaxknpP404/nyZPANkOIwDxviNtRBCN9zSiPSqo1zre1vqzaM57Ww"
"8zJASsPEzNR7OsPoLaIZv2OHXpowsRB78TuXGkQnm74T6xdG6DNs24jTYJuCPfGuYLHbrytdhXpFBS6m"
"Orz9715jK2NU5VvGhNVXX4chcw==";
_cleanup_free_ void *sig = NULL;
size_t siglen;
ASSERT_OK(unbase64mem(tsig, &sig, &siglen));
size_t n_signers = 0;
Signer *signers = NULL;
CLEANUP_ARRAY(signers, n_signers, signer_free_many);
ASSERT_OK_EQ(pkcs7_extract_signers(&IOVEC_MAKE(sig, siglen), &signers, &n_signers), 1);
ASSERT_EQ(n_signers, 1U);
ASSERT_EQ(signers[0].issuer.iov_len, 29U);
ASSERT_EQ(signers[0].serial.iov_len, 22U);
_cleanup_free_ char *issuer = NULL;
ASSERT_OK(base64mem(signers[0].issuer.iov_base, signers[0].issuer.iov_len, &issuer));
_cleanup_free_ char *serial = NULL;
ASSERT_OK(base64mem(signers[0].serial.iov_base, signers[0].serial.iov_len, &serial));
ASSERT_STREQ(issuer, "MBsxGTAXBgNVBAMMEG1rb3NpIG9mIGxlbm5hcnQ=");
ASSERT_STREQ(serial, "AhRGW+WPlqTRmG81LyEQ00rBW/rTHQ==");
}
DEFINE_TEST_MAIN(LOG_INFO);