From: Alexander Larsson Date: Tue, 16 May 2023 14:01:33 +0000 (+0200) Subject: fsverity: Add _ostree_fsverity_sign helper X-Git-Tag: archive/raspbian/2023.7-3+rpi1~1^2~9^2~1^2~29^2~12 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=c6ed5cc7b2e09730fda8d7c0b2502774796020e8;p=ostree.git fsverity: Add _ostree_fsverity_sign helper This code signs a fsverity digest (using openssl) such that the resulting signature can be used with the FS_IOC_ENABLE_VERITY ioctl. --- diff --git a/src/libostree/ostree-repo-private.h b/src/libostree/ostree-repo-private.h index f3865854..5395de40 100644 --- a/src/libostree/ostree-repo-private.h +++ b/src/libostree/ostree-repo-private.h @@ -393,6 +393,9 @@ gboolean _ostree_tmpf_fsverity_core (GLnxTmpfile *tmpf, _OstreeFeatureSupport fs gboolean *supported, GError **error); gboolean _ostree_tmpf_fsverity (OstreeRepo *self, GLnxTmpfile *tmpf, GError **error); +gboolean _ostree_fsverity_sign (const char *certfile, const char *keyfile, + const guchar *fsverity_digest, GBytes **data_out, + GCancellable *cancellable, GError **error); gboolean _ostree_repo_verify_bindings (const char *collection_id, const char *ref_name, GVariant *commit, GError **error); diff --git a/src/libostree/ostree-repo-verity.c b/src/libostree/ostree-repo-verity.c index 8c199e85..fcbdaccd 100644 --- a/src/libostree/ostree-repo-verity.c +++ b/src/libostree/ostree-repo-verity.c @@ -29,6 +29,19 @@ #include #endif +#if defined(HAVE_OPENSSL) +#include +#include +#include +#include +#include + +G_DEFINE_AUTOPTR_CLEANUP_FUNC (X509, X509_free); +G_DEFINE_AUTOPTR_CLEANUP_FUNC (EVP_PKEY, EVP_PKEY_free); +G_DEFINE_AUTOPTR_CLEANUP_FUNC (BIO, BIO_free); +G_DEFINE_AUTOPTR_CLEANUP_FUNC (PKCS7, PKCS7_free); +#endif + gboolean _ostree_repo_parse_fsverity_config (OstreeRepo *self, GError **error) { @@ -167,3 +180,131 @@ _ostree_tmpf_fsverity (OstreeRepo *self, GLnxTmpfile *tmpf, GError **error) #endif return TRUE; } + +#if defined(HAVE_OPENSSL) +static gboolean +read_pem_x509_certificate (const char *certfile, X509 **cert_ret, GError **error) +{ + g_autoptr (BIO) bio = NULL; + X509 *cert; + + errno = 0; + bio = BIO_new_file (certfile, "r"); + if (!bio) + return glnx_throw_errno_prefix (error, "Error loading composefs certfile '%s'", certfile); + + cert = PEM_read_bio_X509 (bio, NULL, NULL, NULL); + if (!cert) + return glnx_throw (error, "Error parsing composefs certfile '%s'", certfile); + + *cert_ret = cert; + return TRUE; +} + +static gboolean +read_pem_pkcs8_private_key (const char *keyfile, EVP_PKEY **pkey_ret, GError **error) +{ + g_autoptr (BIO) bio; + EVP_PKEY *pkey; + + errno = 0; + bio = BIO_new_file (keyfile, "r"); + if (!bio) + return glnx_throw_errno_prefix (error, "Error loading composefs keyfile '%s'", keyfile); + + pkey = PEM_read_bio_PrivateKey (bio, NULL, NULL, NULL); + if (!pkey) + return glnx_throw (error, "Error parsing composefs keyfile '%s'", keyfile); + + *pkey_ret = pkey; + return TRUE; +} + +static gboolean +sign_pkcs7 (const void *data_to_sign, size_t data_size, EVP_PKEY *pkey, X509 *cert, + const EVP_MD *md, BIO **res, GError **error) +{ + int pkcs7_flags = PKCS7_BINARY | PKCS7_DETACHED | PKCS7_NOATTR | PKCS7_NOCERTS | PKCS7_PARTIAL; + g_autoptr (BIO) bio = NULL; + g_autoptr (BIO) bio_res = NULL; + g_autoptr (PKCS7) p7 = NULL; + + bio = BIO_new_mem_buf ((void *)data_to_sign, data_size); + if (!bio) + return glnx_throw (error, "Can't allocate buffer"); + + p7 = PKCS7_sign (NULL, NULL, NULL, bio, pkcs7_flags); + if (!p7) + return glnx_throw (error, "Can't initialize PKCS#7"); + + if (!PKCS7_sign_add_signer (p7, cert, pkey, md, pkcs7_flags)) + return glnx_throw (error, "Can't add signer to PKCS#7"); + + if (PKCS7_final (p7, bio, pkcs7_flags) != 1) + return glnx_throw (error, "Can't finalize PKCS#7"); + + bio_res = BIO_new (BIO_s_mem ()); + if (!bio_res) + return glnx_throw (error, "Can't allocate buffer"); + + if (i2d_PKCS7_bio (bio_res, p7) != 1) + return glnx_throw (error, "Can't DER-encode PKCS#7 signature object"); + + *res = g_steal_pointer (&bio_res); + return TRUE; +} + +gboolean +_ostree_fsverity_sign (const char *certfile, const char *keyfile, const guchar *fsverity_digest, + GBytes **data_out, GCancellable *cancellable, GError **error) +{ + g_autofree struct fsverity_formatted_digest *d = NULL; + gsize d_size; + g_autoptr (X509) cert = NULL; + g_autoptr (EVP_PKEY) pkey = NULL; + g_autoptr (BIO) bio_sig = NULL; + const EVP_MD *md; + guchar *sig; + long sig_size; + + if (certfile == NULL) + return glnx_throw (error, "certfile not specified"); + + if (keyfile == NULL) + return glnx_throw (error, "keyfile not specified"); + + if (!read_pem_x509_certificate (certfile, &cert, error)) + return FALSE; + + if (!read_pem_pkcs8_private_key (keyfile, &pkey, error)) + return FALSE; + + md = EVP_sha256 (); + if (md == NULL) + return glnx_throw (error, "No sha256 support in openssl"); + + d_size = sizeof (struct fsverity_formatted_digest) + OSTREE_SHA256_DIGEST_LEN; + d = g_malloc0 (d_size); + + memcpy (d->magic, "FSVerity", 8); + d->digest_algorithm = GUINT16_TO_LE (FS_VERITY_HASH_ALG_SHA256); + d->digest_size = GUINT16_TO_LE (OSTREE_SHA256_DIGEST_LEN); + memcpy (d->digest, fsverity_digest, OSTREE_SHA256_DIGEST_LEN); + + if (!sign_pkcs7 (d, d_size, pkey, cert, md, &bio_sig, error)) + return FALSE; + + sig_size = BIO_get_mem_data (bio_sig, &sig); + + *data_out = g_bytes_new (sig, sig_size); + + return TRUE; +} +#else +gboolean +_ostree_fsverity_sign (const char *certfile, const char *keyfile, const guchar *fsverity_digest, + GBytes **data_out, GCancellable *cancellable, GError **error) +{ + return glnx_throw (error, "fsverity signature support not built"); +} +#endif