]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
pkcs7-util: add helpers for extracting signer info from PKCS7 signatures
authorLennart Poettering <lennart@amutable.com>
Wed, 11 Feb 2026 12:10:47 +0000 (13:10 +0100)
committerLennart Poettering <lennart@amutable.com>
Tue, 17 Feb 2026 21:00:14 +0000 (22:00 +0100)
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.

src/shared/meson.build
src/shared/pkcs7-util.c [new file with mode: 0644]
src/shared/pkcs7-util.h [new file with mode: 0644]
src/test/meson.build
src/test/test-pkcs7-util.c [new file with mode: 0644]

index b9630bef657875a69e0e549b5342301f07349540..8b49ad9a52d8afcc160c955b1c1e9dd04d91808c 100644 (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',
diff --git a/src/shared/pkcs7-util.c b/src/shared/pkcs7-util.c
new file mode 100644 (file)
index 0000000..0a0e102
--- /dev/null
@@ -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 (file)
index 0000000..11a93cd
--- /dev/null
@@ -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);
index cd563654dff6e64d568b7c2dbe4b069a6f5cf6f0..3ca614db36283f3fa355e572471c8bf1f511f4ea 100644 (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,
diff --git a/src/test/test-pkcs7-util.c b/src/test/test-pkcs7-util.c
new file mode 100644 (file)
index 0000000..ca1d38a
--- /dev/null
@@ -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);