From: Lennart Poettering Date: Wed, 11 Feb 2026 12:10:47 +0000 (+0100) Subject: pkcs7-util: add helpers for extracting signer info from PKCS7 signatures X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=3f31c8ff46537b5193a7289d1b40406263260806;p=thirdparty%2Fsystemd.git 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. --- diff --git a/src/shared/meson.build b/src/shared/meson.build index b9630bef657..8b49ad9a52d 100644 --- a/src/shared/meson.build +++ b/src/shared/meson.build @@ -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', diff --git a/src/shared/pkcs7-util.c b/src/shared/pkcs7-util.c new file mode 100644 index 00000000000..0a0e102adf7 --- /dev/null +++ b/src/shared/pkcs7-util.c @@ -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 +} diff --git a/src/shared/pkcs7-util.h b/src/shared/pkcs7-util.h new file mode 100644 index 00000000000..11a93cd8577 --- /dev/null +++ b/src/shared/pkcs7-util.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include + +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); diff --git a/src/test/meson.build b/src/test/meson.build index cd563654dff..3ca614db362 100644 --- a/src/test/meson.build +++ b/src/test/meson.build @@ -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, diff --git a/src/test/test-pkcs7-util.c b/src/test/test-pkcs7-util.c new file mode 100644 index 00000000000..ca1d38a4dec --- /dev/null +++ b/src/test/test-pkcs7-util.c @@ -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);