From d447376aa2bf66a5d5b6a928fb0c6e65189910ba Mon Sep 17 00:00:00 2001 From: Jes Sorensen Date: Fri, 3 Apr 2020 16:38:08 -0400 Subject: [PATCH 19/33] Implement rpmSignVerity() This generates the root Merkle tree hash and signs it using the specified key and certificate. Signed-off-by: Jes Sorensen --- sign/rpmgensig.c | 36 +++++++++++++ sign/rpmsignverity.c | 121 +++++++++++++++++++++++++++++++++++++++++++ sign/rpmsignverity.h | 29 +++++++++++ 3 files changed, 186 insertions(+) create mode 100644 sign/rpmsignverity.c create mode 100644 sign/rpmsignverity.h diff --git a/sign/rpmgensig.c b/sign/rpmgensig.c index a6e37e71b..8d5c5858f 100644 --- a/sign/rpmgensig.c +++ b/sign/rpmgensig.c @@ -22,6 +22,7 @@ #include "lib/signature.h" #include "lib/rpmvs.h" #include "sign/rpmsignfiles.h" +#include "sign/rpmsignverity.h" #include "debug.h" @@ -446,6 +447,36 @@ static rpmRC includeFileSignatures(Header *sigp, Header *hdrp) #endif } +static rpmRC includeVeritySignatures(FD_t fd, Header *sigp, Header *hdrp) +{ +#ifdef WITH_FSVERITY + rpmRC rc; + char *key = rpmExpand("%{?_file_signing_key}", NULL); + char *keypass = rpmExpand("%{?_file_signing_key_password}", NULL); + char *cert = rpmExpand("%{?_file_signing_cert}", NULL); + + if (rstreq(keypass, "")) { + free(keypass); + keypass = NULL; + } + + if (key && cert) { + rc = rpmSignVerity(fd, *sigp, *hdrp, key, keypass, cert); + } else { + rpmlog(RPMLOG_ERR, _("fsverity signatures requires a key and a cert\n")); + rc = RPMRC_FAIL; + } + + free(keypass); + free(key); + free(cert); + return rc; +#else + rpmlog(RPMLOG_ERR, _("fsverity signing support not built in\n")); + return RPMRC_FAIL; +#endif +} + static int msgCb(struct rpmsinfo_s *sinfo, void *cbdata) { char **msg = cbdata; @@ -544,6 +575,11 @@ static int rpmSign(const char *rpm, int deleting, int flags) goto exit; } + if (flags & RPMSIGN_FLAG_FSVERITY) { + if (includeVeritySignatures(fd, &sigh, &h)) + goto exit; + } + if (deleting) { /* Nuke all the signature tags. */ deleteSigs(sigh); } else { diff --git a/sign/rpmsignverity.c b/sign/rpmsignverity.c new file mode 100644 index 000000000..5346c3bc8 --- /dev/null +++ b/sign/rpmsignverity.c @@ -0,0 +1,121 @@ +/** + * Copyright (C) 2020 Facebook + * + * Author: Jes Sorensen + */ + +#include "system.h" + +#include /* RPMSIGTAG & related */ +#include /* rpmlog */ +#include +#include /* rpmDigestLength */ +#include "lib/header.h" /* HEADERGET_MINMEM */ +#include "lib/header_internal.h" +#include "lib/rpmtypes.h" /* rpmRC */ +#include +#include "rpmio/rpmio_internal.h" +#include "lib/rpmvs.h" + +#include "sign/rpmsignverity.h" + +#define MAX_SIGNATURE_LENGTH 1024 + +static int rpmVerityRead(void *opaque, void *buf, size_t size) +{ + int retval; + rpmfi fi = (rpmfi)opaque; + + retval = rpmfiArchiveRead(fi, buf, size); + + if (retval > 0) + retval = 0; + return retval; +} + +rpmRC rpmSignVerity(FD_t fd, Header sigh, Header h, char *key, + char *keypass, char *cert) +{ + int rc, status; + FD_t gzdi; + rpmfiles files = NULL; + rpmfi fi = NULL; + rpmts ts = rpmtsCreate(); + struct libfsverity_digest *digest = NULL; + struct libfsverity_merkle_tree_params params; + struct libfsverity_signature_params sig_params; + rpm_loff_t file_size; + off_t offset = Ftell(fd); + const char *compr; + char *rpmio_flags = NULL; + char *digest_hex; + uint8_t *sig; + size_t sig_size; + + Fseek(fd, 0, SEEK_SET); + rpmtsSetVSFlags(ts, RPMVSF_MASK_NODIGESTS | RPMVSF_MASK_NOSIGNATURES | + RPMVSF_NOHDRCHK); + rc = rpmReadPackageFile(ts, fd, "fsverity", &h); + if (rc != RPMRC_OK) { + rpmlog(RPMLOG_DEBUG, _("%s: rpmReadPackageFile returned %i\n"), + __func__, rc); + goto out; + } + + rpmlog(RPMLOG_DEBUG, _("key: %s\n"), key); + rpmlog(RPMLOG_DEBUG, _("cert: %s\n"), cert); + + compr = headerGetString(h, RPMTAG_PAYLOADCOMPRESSOR); + rpmio_flags = rstrscat(NULL, "r.", compr ? compr : "gzip", NULL); + + gzdi = Fdopen(fdDup(Fileno(fd)), rpmio_flags); + free(rpmio_flags); + + files = rpmfilesNew(NULL, h, RPMTAG_BASENAMES, RPMFI_FLAGS_QUERY); + fi = rpmfiNewArchiveReader(gzdi, files, + RPMFI_ITER_READ_ARCHIVE_OMIT_HARDLINKS); + + while (rpmfiNext(fi) >= 0) { + if (!S_ISREG(rpmfiFMode(fi))) + continue; + file_size = rpmfiFSize(fi); + rpmlog(RPMLOG_DEBUG, _("file: %s, (size %li)\n"), + rpmfiFN(fi), file_size); + + memset(¶ms, 0, sizeof(struct libfsverity_merkle_tree_params)); + params.version = 1; + params.hash_algorithm = FS_VERITY_HASH_ALG_SHA256; + params.block_size = sysconf(_SC_PAGESIZE); + params.salt_size = 0 /* salt_size */; + params.salt = NULL /* salt */; + params.file_size = file_size; + status = libfsverity_compute_digest(fi, rpmVerityRead, + ¶ms, &digest); + if (!status) { + digest_hex = pgpHexStr(digest->digest, digest->digest_size); + rpmlog(RPMLOG_DEBUG, _("digest(%i): %s\n"), + digest->digest_size, digest_hex); + free(digest_hex); + } + memset(&sig_params, 0, sizeof(struct libfsverity_signature_params)); + sig_params.keyfile = key; + sig_params.certfile = cert; + if (libfsverity_sign_digest(digest, &sig_params, &sig, &sig_size)) { + rpmlog(RPMLOG_DEBUG, _("failed to sign digest\n")); + rc = RPMRC_FAIL; + goto out; + } + rpmlog(RPMLOG_DEBUG, _("digest signing success\n")); + + free(digest); + free(sig); + } + +out: + Fseek(fd, offset, SEEK_SET); + + rpmfilesFree(files); + rpmfiFree(fi); + rpmtsFree(ts); + return rc; +} diff --git a/sign/rpmsignverity.h b/sign/rpmsignverity.h new file mode 100644 index 000000000..f3ad3bb18 --- /dev/null +++ b/sign/rpmsignverity.h @@ -0,0 +1,29 @@ +#ifndef H_RPMSIGNVERITY +#define H_RPMSIGNVERITY + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Sign file digests in header into signature header + * @param fd file descriptor of RPM + * @param sigh package signature header + * @param h package header + * @param key signing key + * @param keypass signing key password + * @param cert signing cert + * @return RPMRC_OK on success + */ +RPM_GNUC_INTERNAL +rpmRC rpmSignVerity(FD_t fd, Header sigh, Header h, char *key, + char *keypass, char *cert); + +#ifdef _cplusplus +} +#endif + +#endif /* H_RPMSIGNVERITY */ -- 2.27.0