dcavalca / rpms / rpm

Forked from rpms/rpm a year ago
Clone
Richard Phibel 3de522
From 07f1d3132f0c7b7ecb69a47a9930edb534a9250e Mon Sep 17 00:00:00 2001
Richard Phibel 3de522
From: Panu Matilainen <pmatilai@redhat.com>
Richard Phibel 3de522
Date: Mon, 7 Feb 2022 13:38:48 +0200
Richard Phibel 3de522
Subject: [PATCH] Fix IMA signature fubar, take III (#1833, RhBug:2018937)
Richard Phibel 3de522
Richard Phibel 3de522
At least ECDSA and RSA signatures can vary in length, but the IMA code
Richard Phibel 3de522
assumes constant lengths and thus may either place invalid signatures on
Richard Phibel 3de522
disk from either truncating or overshooting, and segfault if the stars are
Richard Phibel 3de522
just so.
Richard Phibel 3de522
Richard Phibel 3de522
As we can't assume static lengths and attempts to use maximum length
Richard Phibel 3de522
have proven problematic for other reasons, use a data structure that
Richard Phibel 3de522
can actually handle variable length data properly: store offsets into
Richard Phibel 3de522
the decoded binary blob and use them to calculate lengths when needed,
Richard Phibel 3de522
empty data is simply consequtive identical offsets. This avoids a whole
Richard Phibel 3de522
class of silly overflow issues with multiplying, makes zero-length data
Richard Phibel 3de522
actually presentable in the data structure and saves memory too.
Richard Phibel 3de522
Richard Phibel 3de522
Add tests to show behavior with variable length signatures and missing
Richard Phibel 3de522
signatures.
Richard Phibel 3de522
Richard Phibel 3de522
Additionally update the signing code to store the largest IMA signature
Richard Phibel 3de522
length rather than what happened to be last to be on the safe side.
Richard Phibel 3de522
We can't rely on this value due to invalid packages being out there,
Richard Phibel 3de522
but then we need to calculate the lengths on rpmfiles populate so there's
Richard Phibel 3de522
not a lot to gain anyhow.
Richard Phibel 3de522
Richard Phibel 3de522
Fixes: #1833
Richard Phibel 3de522
---
Richard Phibel 3de522
 lib/rpmfi.c         | 61 +++++++++++++++++++++++++++++++++++++++------
Richard Phibel 3de522
 sign/rpmsignfiles.c |  5 +++-
Richard Phibel 3de522
 2 files changed, 58 insertions(+), 8 deletions(-)
Richard Phibel 3de522
Richard Phibel 3de522
diff --git a/lib/rpmfi.c b/lib/rpmfi.c
Richard Phibel 3de522
index 439179689..4673fbb85 100644
Richard Phibel 3de522
--- a/lib/rpmfi.c
Richard Phibel 3de522
+++ b/lib/rpmfi.c
Richard Phibel 3de522
@@ -116,7 +116,7 @@ struct rpmfiles_s {
Richard Phibel 3de522
     struct fingerPrint_s * fps;	/*!< File fingerprint(s). */
Richard Phibel 3de522
 
Richard Phibel 3de522
     int digestalgo;		/*!< File digest algorithm */
Richard Phibel 3de522
-    int signaturelength;	/*!< File signature length */
Richard Phibel 3de522
+    uint32_t *signatureoffs;	/*!< File signature offsets */
Richard Phibel 3de522
     int veritysiglength;	/*!< Verity signature length */
Richard Phibel 3de522
     uint16_t verityalgo;	/*!< Verity algorithm */
Richard Phibel 3de522
     unsigned char * digests;	/*!< File digests in binary. */
Richard Phibel 3de522
@@ -566,10 +566,15 @@ const unsigned char * rpmfilesFSignature(rpmfiles fi, int ix, size_t *len)
Richard Phibel 3de522
     const unsigned char *signature = NULL;
Richard Phibel 3de522
 
Richard Phibel 3de522
     if (fi != NULL && ix >= 0 && ix < rpmfilesFC(fi)) {
Richard Phibel 3de522
-	if (fi->signatures != NULL)
Richard Phibel 3de522
-	    signature = fi->signatures + (fi->signaturelength * ix);
Richard Phibel 3de522
+	size_t slen = 0;
Richard Phibel 3de522
+	if (fi->signatures != NULL && fi->signatureoffs != NULL) {
Richard Phibel 3de522
+	    uint32_t off = fi->signatureoffs[ix];
Richard Phibel 3de522
+	    slen = fi->signatureoffs[ix+1] - off;
Richard Phibel 3de522
+	    if (slen > 0)
Richard Phibel 3de522
+		signature = fi->signatures + off;
Richard Phibel 3de522
+	}
Richard Phibel 3de522
 	if (len)
Richard Phibel 3de522
-	    *len = fi->signaturelength;
Richard Phibel 3de522
+	    *len = slen;
Richard Phibel 3de522
     }
Richard Phibel 3de522
     return signature;
Richard Phibel 3de522
 }
Richard Phibel 3de522
@@ -1242,6 +1247,7 @@ rpmfiles rpmfilesFree(rpmfiles fi)
Richard Phibel 3de522
 	fi->flangs = _free(fi->flangs);
Richard Phibel 3de522
 	fi->digests = _free(fi->digests);
Richard Phibel 3de522
 	fi->signatures = _free(fi->signatures);
Richard Phibel 3de522
+	fi->signatureoffs = _free(fi->signatureoffs);
Richard Phibel 3de522
 	fi->veritysigs = _free(fi->veritysigs);
Richard Phibel 3de522
 	fi->fcaps = _free(fi->fcaps);
Richard Phibel 3de522
 
Richard Phibel 3de522
@@ -1471,6 +1477,48 @@ err:
Richard Phibel 3de522
     return;
Richard Phibel 3de522
 }
Richard Phibel 3de522
 
Richard Phibel 3de522
+/*
Richard Phibel 3de522
+ * Convert a tag of variable len hex strings to binary presentation,
Richard Phibel 3de522
+ * accessed via offsets to a contiguous binary blob. Empty values
Richard Phibel 3de522
+ * are represented by identical consequtive offsets. The offsets array
Richard Phibel 3de522
+ * always has one extra element to allow calculating the size of the
Richard Phibel 3de522
+ * last element.
Richard Phibel 3de522
+ */
Richard Phibel 3de522
+static uint8_t *hex2binv(Header h, rpmTagVal tag, rpm_count_t num,
Richard Phibel 3de522
+			uint32_t **offsetp)
Richard Phibel 3de522
+{
Richard Phibel 3de522
+    struct rpmtd_s td;
Richard Phibel 3de522
+    uint8_t *bin = NULL;
Richard Phibel 3de522
+    uint32_t *offs = NULL;
Richard Phibel 3de522
+
Richard Phibel 3de522
+    if (headerGet(h, tag, &td, HEADERGET_MINMEM) && rpmtdCount(&td) == num) {
Richard Phibel 3de522
+	const char *s;
Richard Phibel 3de522
+	int i = 0;
Richard Phibel 3de522
+	uint8_t *t = bin = xmalloc(((rpmtdSize(&td) / 2) + 1));
Richard Phibel 3de522
+	offs = xmalloc((num + 1) * sizeof(*offs));
Richard Phibel 3de522
+
Richard Phibel 3de522
+	while ((s = rpmtdNextString(&td))) {
Richard Phibel 3de522
+	    uint32_t slen = strlen(s);
Richard Phibel 3de522
+	    uint32_t len = slen / 2;
Richard Phibel 3de522
+	    if (slen % 2) {
Richard Phibel 3de522
+		bin = rfree(bin);
Richard Phibel 3de522
+		offs = rfree(offs);
Richard Phibel 3de522
+		goto exit;
Richard Phibel 3de522
+	    }
Richard Phibel 3de522
+	    offs[i] = t - bin;
Richard Phibel 3de522
+	    for (int j = 0; j < len; j++, t++, s += 2)
Richard Phibel 3de522
+		*t = (rnibble(s[0]) << 4) | rnibble(s[1]);
Richard Phibel 3de522
+	    i++;
Richard Phibel 3de522
+	}
Richard Phibel 3de522
+	offs[i] = t - bin;
Richard Phibel 3de522
+	*offsetp = offs;
Richard Phibel 3de522
+    }
Richard Phibel 3de522
+
Richard Phibel 3de522
+exit:
Richard Phibel 3de522
+    rpmtdFreeData(&td);
Richard Phibel 3de522
+    return bin;
Richard Phibel 3de522
+}
Richard Phibel 3de522
+
Richard Phibel 3de522
 /* Convert a tag of hex strings to binary presentation */
Richard Phibel 3de522
 static uint8_t *hex2bin(Header h, rpmTagVal tag, rpm_count_t num, size_t len)
Richard Phibel 3de522
 {
Richard Phibel 3de522
@@ -1623,9 +1671,8 @@ static int rpmfilesPopulate(rpmfiles fi, Header h, rpmfiFlags flags)
Richard Phibel 3de522
     fi->signatures = NULL;
Richard Phibel 3de522
     /* grab hex signatures from header and store in binary format */
Richard Phibel 3de522
     if (!(flags & RPMFI_NOFILESIGNATURES)) {
Richard Phibel 3de522
-	fi->signaturelength = headerGetNumber(h, RPMTAG_FILESIGNATURELENGTH);
Richard Phibel 3de522
-	fi->signatures = hex2bin(h, RPMTAG_FILESIGNATURES,
Richard Phibel 3de522
-				 totalfc, fi->signaturelength);
Richard Phibel 3de522
+	fi->signatures = hex2binv(h, RPMTAG_FILESIGNATURES,
Richard Phibel 3de522
+				 totalfc, &fi->signatureoffs);
Richard Phibel 3de522
     }
Richard Phibel 3de522
 
Richard Phibel 3de522
     fi->veritysigs = NULL;
Richard Phibel 3de522
diff --git a/sign/rpmsignfiles.c b/sign/rpmsignfiles.c
Richard Phibel 3de522
index b143c5b9b..372ba634c 100644
Richard Phibel 3de522
--- a/sign/rpmsignfiles.c
Richard Phibel 3de522
+++ b/sign/rpmsignfiles.c
Richard Phibel 3de522
@@ -98,8 +98,9 @@ rpmRC rpmSignFiles(Header sigh, Header h, const char *key, char *keypass)
Richard Phibel 3de522
     td.count = 1;
Richard Phibel 3de522
 
Richard Phibel 3de522
     while (rpmfiNext(fi) >= 0) {
Richard Phibel 3de522
+	uint32_t slen = 0;
Richard Phibel 3de522
 	digest = rpmfiFDigest(fi, NULL, NULL);
Richard Phibel 3de522
-	signature = signFile(algoname, digest, diglen, key, keypass, &siglen);
Richard Phibel 3de522
+	signature = signFile(algoname, digest, diglen, key, keypass, &slen);
Richard Phibel 3de522
 	if (!signature) {
Richard Phibel 3de522
 	    rpmlog(RPMLOG_ERR, _("signFile failed\n"));
Richard Phibel 3de522
 	    goto exit;
Richard Phibel 3de522
@@ -110,6 +111,8 @@ rpmRC rpmSignFiles(Header sigh, Header h, const char *key, char *keypass)
Richard Phibel 3de522
 	    goto exit;
Richard Phibel 3de522
 	}
Richard Phibel 3de522
 	signature = _free(signature);
Richard Phibel 3de522
+	if (slen > siglen)
Richard Phibel 3de522
+	    siglen = slen;
Richard Phibel 3de522
     }
Richard Phibel 3de522
 
Richard Phibel 3de522
     if (siglen > 0) {
Richard Phibel 3de522
-- 
Richard Phibel 3de522
2.40.1
Richard Phibel 3de522