alexk / rpms / rpm

Forked from rpms/rpm 2 years ago
Clone
3f3ddd
From 1f63621d098741158b5e1e7158cc570a415d88cd Mon Sep 17 00:00:00 2001
3f3ddd
From: Panu Matilainen <pmatilai@redhat.com>
3f3ddd
Date: Mon, 29 Nov 2021 14:01:39 +0200
3f3ddd
Subject: [PATCH] Fix IMA signature lengths assumed constant (#1833,
3f3ddd
 RhBug:2018937)
3f3ddd
3f3ddd
At least ECDSA and RSA signatures can vary in length, but the IMA code
3f3ddd
assumes constant lengths and thus may either place invalid signatures on
3f3ddd
disk from either truncating or overshooting, and segfault if the stars are
3f3ddd
just so.
3f3ddd
3f3ddd
Luckily the signatures are stored as strings so we can calculate the
3f3ddd
actual lengths at runtime and ignore the stored constant length info.
3f3ddd
Extend hex2bin() to optionally calculate the lengths and maximum,
3f3ddd
and use these for returning IMA data from the rpmfi(les) API.
3f3ddd
3f3ddd
Additionally update the signing code to store the largest IMA signature
3f3ddd
length rather than what happened to be last to be on the safe side.
3f3ddd
We can't rely on this value due to invalid packages being out there,
3f3ddd
but then we need to calculate the lengths on rpmfiles populate so there's
3f3ddd
not a lot to gain anyhow.
3f3ddd
3f3ddd
Fixes: #1833
3f3ddd
3f3ddd
Backported for 4.16.1.3 and combined with:
3f3ddd
31e9daf823f7052135d1decc0802b6fa775a88c5 (fix-up)
3f3ddd
0c1ad364d65c4144ff71c376e0b49fbc322b686d (python bindings)
3f3ddd
3f3ddd
Note that the test case has been removed due to it including a binary
3f3ddd
file (test package) for which we'd have to use -Sgit with %autopatch and
3f3ddd
thus depend on git-core at build time.  Nevertheless, we do have this BZ
3f3ddd
covered in our internal test suite, so no need for it anyway.
3f3ddd
---
3f3ddd
 lib/rpmfi.c          | 59 +++++++++++++++++++++++++++++++++-----------
3f3ddd
 python/rpmfiles-py.c | 18 ++++++++++++++
3f3ddd
 sign/rpmsignfiles.c  |  5 +++-
3f3ddd
 3 files changed, 67 insertions(+), 15 deletions(-)
3f3ddd
3f3ddd
diff --git a/lib/rpmfi.c b/lib/rpmfi.c
3f3ddd
index af428468c..ed8927fd5 100644
3f3ddd
--- a/lib/rpmfi.c
3f3ddd
+++ b/lib/rpmfi.c
3f3ddd
@@ -115,7 +115,8 @@ struct rpmfiles_s {
3f3ddd
     struct fingerPrint_s * fps;	/*!< File fingerprint(s). */
3f3ddd
 
3f3ddd
     int digestalgo;		/*!< File digest algorithm */
3f3ddd
-    int signaturelength;	/*!< File signature length */
3f3ddd
+    int *signaturelengths;	/*!< File signature lengths */
3f3ddd
+    int signaturemaxlen;	/*!< Largest file signature length */
3f3ddd
     unsigned char * digests;	/*!< File digests in binary. */
3f3ddd
     unsigned char * signatures; /*!< File signatures in binary. */
3f3ddd
 
3f3ddd
@@ -575,9 +576,9 @@ const unsigned char * rpmfilesFSignature(rpmfiles fi, int ix, size_t *len)
3f3ddd
 
3f3ddd
     if (fi != NULL && ix >= 0 && ix < rpmfilesFC(fi)) {
3f3ddd
 	if (fi->signatures != NULL)
3f3ddd
-	    signature = fi->signatures + (fi->signaturelength * ix);
3f3ddd
+	    signature = fi->signatures + (fi->signaturemaxlen * ix);
3f3ddd
 	if (len)
3f3ddd
-	    *len = fi->signaturelength;
3f3ddd
+	    *len = fi->signaturelengths ? fi->signaturelengths[ix] : 0;
3f3ddd
     }
3f3ddd
     return signature;
3f3ddd
 }
3f3ddd
@@ -1257,6 +1258,7 @@ rpmfiles rpmfilesFree(rpmfiles fi)
3f3ddd
 	fi->flangs = _free(fi->flangs);
3f3ddd
 	fi->digests = _free(fi->digests);
3f3ddd
 	fi->signatures = _free(fi->signatures);
3f3ddd
+	fi->signaturelengths = _free(fi->signaturelengths);
3f3ddd
 	fi->fcaps = _free(fi->fcaps);
3f3ddd
 
3f3ddd
 	fi->cdict = _free(fi->cdict);
3f3ddd
@@ -1486,23 +1488,52 @@ err:
3f3ddd
 }
3f3ddd
 
3f3ddd
 /* Convert a tag of hex strings to binary presentation */
3f3ddd
-static uint8_t *hex2bin(Header h, rpmTagVal tag, rpm_count_t num, size_t len)
3f3ddd
+/* If lengths is non-NULL, assume variable length strings */
3f3ddd
+static uint8_t *hex2bin(Header h, rpmTagVal tag, rpm_count_t num, size_t len,
3f3ddd
+			int **lengths, int *maxlen)
3f3ddd
 {
3f3ddd
     struct rpmtd_s td;
3f3ddd
     uint8_t *bin = NULL;
3f3ddd
 
3f3ddd
     if (headerGet(h, tag, &td, HEADERGET_MINMEM) && rpmtdCount(&td) == num) {
3f3ddd
-	uint8_t *t = bin = xmalloc(num * len);
3f3ddd
 	const char *s;
3f3ddd
+	int maxl = 0;
3f3ddd
+	int *lens = NULL;
3f3ddd
+
3f3ddd
+	/* Figure string sizes + max length for allocation purposes */
3f3ddd
+	if (lengths) {
3f3ddd
+	    int i = 0;
3f3ddd
+	    lens = xmalloc(num * sizeof(*lens));
3f3ddd
+
3f3ddd
+	    while ((s = rpmtdNextString(&td))) {
3f3ddd
+		lens[i] = strlen(s) / 2;
3f3ddd
+		if (lens[i] > maxl)
3f3ddd
+		    maxl = lens[i];
3f3ddd
+		i++;
3f3ddd
+	    }
3f3ddd
+
3f3ddd
+	    *lengths = lens;
3f3ddd
+	    *maxlen = maxl;
3f3ddd
+
3f3ddd
+	    /* Reinitialize iterator for next round */
3f3ddd
+	    rpmtdInit(&td);
3f3ddd
+	} else {
3f3ddd
+	    maxl = len;
3f3ddd
+	}
3f3ddd
 
3f3ddd
+	uint8_t *t = bin = xmalloc(num * maxl);
3f3ddd
+	int i = 0;
3f3ddd
 	while ((s = rpmtdNextString(&td))) {
3f3ddd
 	    if (*s == '\0') {
3f3ddd
-		memset(t, 0, len);
3f3ddd
-		t += len;
3f3ddd
-		continue;
3f3ddd
+		memset(t, 0, maxl);
3f3ddd
+	    } else {
3f3ddd
+		if (lens)
3f3ddd
+		    len = lens[i];
3f3ddd
+		for (int j = 0; j < len; j++, s += 2)
3f3ddd
+		    t[j] = (rnibble(s[0]) << 4) | rnibble(s[1]);
3f3ddd
 	    }
3f3ddd
-	    for (int j = 0; j < len; j++, t++, s += 2)
3f3ddd
-		*t = (rnibble(s[0]) << 4) | rnibble(s[1]);
3f3ddd
+	    t += maxl;
3f3ddd
+	    i++;
3f3ddd
 	}
3f3ddd
     }
3f3ddd
     rpmtdFreeData(&td);
3f3ddd
@@ -1570,15 +1601,15 @@ static int rpmfilesPopulate(rpmfiles fi, Header h, rpmfiFlags flags)
3f3ddd
     /* grab hex digests from header and store in binary format */
3f3ddd
     if (!(flags & RPMFI_NOFILEDIGESTS)) {
3f3ddd
 	size_t diglen = rpmDigestLength(fi->digestalgo);
3f3ddd
-	fi->digests = hex2bin(h, RPMTAG_FILEDIGESTS, totalfc, diglen);
3f3ddd
+	fi->digests = hex2bin(h, RPMTAG_FILEDIGESTS, totalfc, diglen,
3f3ddd
+				NULL, NULL);
3f3ddd
     }
3f3ddd
 
3f3ddd
     fi->signatures = NULL;
3f3ddd
     /* grab hex signatures from header and store in binary format */
3f3ddd
     if (!(flags & RPMFI_NOFILESIGNATURES)) {
3f3ddd
-	fi->signaturelength = headerGetNumber(h, RPMTAG_FILESIGNATURELENGTH);
3f3ddd
-	fi->signatures = hex2bin(h, RPMTAG_FILESIGNATURES,
3f3ddd
-				 totalfc, fi->signaturelength);
3f3ddd
+	fi->signatures = hex2bin(h, RPMTAG_FILESIGNATURES, totalfc, 0,
3f3ddd
+				&fi->signaturelengths, &fi->signaturemaxlen);
3f3ddd
     }
3f3ddd
 
3f3ddd
     /* XXX TR_REMOVED doesn;t need fmtimes, frdevs, finodes */
3f3ddd
diff --git a/python/rpmfiles-py.c b/python/rpmfiles-py.c
3f3ddd
index 27666021d..48189a0ac 100644
3f3ddd
--- a/python/rpmfiles-py.c
3f3ddd
+++ b/python/rpmfiles-py.c
3f3ddd
@@ -152,6 +152,22 @@ static PyObject *rpmfile_digest(rpmfileObject *s)
3f3ddd
     Py_RETURN_NONE;
3f3ddd
 }
3f3ddd
 
3f3ddd
+static PyObject *bytebuf(const unsigned char *buf, size_t len)
3f3ddd
+{
3f3ddd
+    if (buf) {
3f3ddd
+	PyObject *o = PyBytes_FromStringAndSize((const char *)buf, len);
3f3ddd
+	return o;
3f3ddd
+    }
3f3ddd
+    Py_RETURN_NONE;
3f3ddd
+}
3f3ddd
+
3f3ddd
+static PyObject *rpmfile_imasig(rpmfileObject *s)
3f3ddd
+{
3f3ddd
+    size_t len = 0;
3f3ddd
+    const unsigned char *sig = rpmfilesFSignature(s->files, s->ix, &len;;
3f3ddd
+    return bytebuf(sig, len);
3f3ddd
+}
3f3ddd
+
3f3ddd
 static PyObject *rpmfile_class(rpmfileObject *s)
3f3ddd
 {
3f3ddd
     return utf8FromString(rpmfilesFClass(s->files, s->ix));
3f3ddd
@@ -278,6 +294,8 @@ static PyGetSetDef rpmfile_getseters[] = {
3f3ddd
       "language the file provides (typically for doc files)" },
3f3ddd
     { "caps",		(getter) rpmfile_caps,		NULL,
3f3ddd
       "file capabilities" },
3f3ddd
+    { "imasig",	(getter) rpmfile_imasig,	NULL,
3f3ddd
+      "IMA signature" },
3f3ddd
     { NULL, NULL, NULL, NULL }
3f3ddd
 };
3f3ddd
 
3f3ddd
diff --git a/sign/rpmsignfiles.c b/sign/rpmsignfiles.c
3f3ddd
index b143c5b9b..6f39db6be 100644
3f3ddd
--- a/sign/rpmsignfiles.c
3f3ddd
+++ b/sign/rpmsignfiles.c
3f3ddd
@@ -98,8 +98,9 @@ rpmRC rpmSignFiles(Header sigh, Header h, const char *key, char *keypass)
3f3ddd
     td.count = 1;
3f3ddd
 
3f3ddd
     while (rpmfiNext(fi) >= 0) {
3f3ddd
+	uint32_t slen;
3f3ddd
 	digest = rpmfiFDigest(fi, NULL, NULL);
3f3ddd
-	signature = signFile(algoname, digest, diglen, key, keypass, &siglen);
3f3ddd
+	signature = signFile(algoname, digest, diglen, key, keypass, &slen);
3f3ddd
 	if (!signature) {
3f3ddd
 	    rpmlog(RPMLOG_ERR, _("signFile failed\n"));
3f3ddd
 	    goto exit;
3f3ddd
@@ -110,6 +111,8 @@ rpmRC rpmSignFiles(Header sigh, Header h, const char *key, char *keypass)
3f3ddd
 	    goto exit;
3f3ddd
 	}
3f3ddd
 	signature = _free(signature);
3f3ddd
+	if (slen > siglen)
3f3ddd
+	    siglen = slen;
3f3ddd
     }
3f3ddd
 
3f3ddd
     if (siglen > 0) {
3f3ddd
-- 
3f3ddd
2.33.1
3f3ddd