rpm/0002-Fix-IMA-signature-fubar-take-III-1833-RhBug-2018937.patch

222 lines
7.2 KiB
Diff
Raw Permalink Normal View History

From eb3ee2ab221f12937fb35d304ba96d1f626aee4b Mon Sep 17 00:00:00 2001
From: Panu Matilainen <pmatilai@redhat.com>
Date: Tue, 10 Oct 2017 15:04:38 +0300
Subject: [PATCH 2/5] Fix IMA signature fubar, take III (#1833, RhBug:2018937)
At least ECDSA and RSA signatures can vary in length, but the IMA code
assumes constant lengths and thus may either place invalid signatures on
disk from either truncating or overshooting, and segfault if the stars are
just so.
As we can't assume static lengths and attempts to use maximum length
have proven problematic for other reasons, use a data structure that
can actually handle variable length data properly: store offsets into
the decoded binary blob and use them to calculate lengths when needed,
empty data is simply consequtive identical offsets. This avoids a whole
class of silly overflow issues with multiplying, makes zero-length data
actually presentable in the data structure and saves memory too.
Add tests to show behavior with variable length signatures and missing
signatures.
Additionally update the signing code to store the largest IMA signature
length rather than what happened to be last to be on the safe side.
We can't rely on this value due to invalid packages being out there,
but then we need to calculate the lengths on rpmfiles populate so there's
not a lot to gain anyhow.
Backported from commits:
5af8ab60c652cda0bffcd4d65130bb57b5666ff0
07f1d3132f0c7b7ecb69a47a9930edb534a9250e
Tests are excluded from this backport since they would need significant
rework, the use case will be covered by Beaker.
Fixes: RHEL-39896
---
lib/rpmfi.c | 61 +++++++++++++++++++++++++++++++++++++++------
sign/rpmsignfiles.c | 30 ++++++++--------------
2 files changed, 64 insertions(+), 27 deletions(-)
diff --git a/lib/rpmfi.c b/lib/rpmfi.c
index 6c631fdb5..0aacd9f85 100644
--- a/lib/rpmfi.c
+++ b/lib/rpmfi.c
@@ -117,7 +117,7 @@ struct rpmfiles_s {
struct fingerPrint_s * fps; /*!< File fingerprint(s). */
int digestalgo; /*!< File digest algorithm */
- int signaturelength; /*!< File signature length */
+ uint32_t *signatureoffs; /*!< File signature offsets */
unsigned char * digests; /*!< File digests in binary. */
unsigned char * signatures; /*!< File signatures in binary. */
@@ -589,10 +589,15 @@ const unsigned char * rpmfilesFSignature(rpmfiles fi, int ix, size_t *len)
const unsigned char *signature = NULL;
if (fi != NULL && ix >= 0 && ix < rpmfilesFC(fi)) {
- if (fi->signatures != NULL)
- signature = fi->signatures + (fi->signaturelength * ix);
+ size_t slen = 0;
+ if (fi->signatures != NULL && fi->signatureoffs != NULL) {
+ uint32_t off = fi->signatureoffs[ix];
+ slen = fi->signatureoffs[ix+1] - off;
+ if (slen > 0)
+ signature = fi->signatures + off;
+ }
if (len)
- *len = fi->signaturelength;
+ *len = slen;
}
return signature;
}
@@ -1276,6 +1281,7 @@ rpmfiles rpmfilesFree(rpmfiles fi)
fi->flangs = _free(fi->flangs);
fi->digests = _free(fi->digests);
fi->signatures = _free(fi->signatures);
+ fi->signatureoffs = _free(fi->signatureoffs);
fi->fcaps = _free(fi->fcaps);
fi->cdict = _free(fi->cdict);
@@ -1504,6 +1510,48 @@ err:
return;
}
+/*
+ * Convert a tag of variable len hex strings to binary presentation,
+ * accessed via offsets to a contiguous binary blob. Empty values
+ * are represented by identical consequtive offsets. The offsets array
+ * always has one extra element to allow calculating the size of the
+ * last element.
+ */
+static uint8_t *hex2binv(Header h, rpmTagVal tag, rpm_count_t num,
+ uint32_t **offsetp)
+{
+ struct rpmtd_s td;
+ uint8_t *bin = NULL;
+ uint32_t *offs = NULL;
+
+ if (headerGet(h, tag, &td, HEADERGET_MINMEM) && rpmtdCount(&td) == num) {
+ const char *s;
+ int i = 0;
+ uint8_t *t = bin = xmalloc(((rpmtdSize(&td) / 2) + 1));
+ offs = xmalloc((num + 1) * sizeof(*offs));
+
+ while ((s = rpmtdNextString(&td))) {
+ uint32_t slen = strlen(s);
+ uint32_t len = slen / 2;
+ if (slen % 2) {
+ bin = rfree(bin);
+ offs = rfree(offs);
+ goto exit;
+ }
+ offs[i] = t - bin;
+ for (int j = 0; j < len; j++, t++, s += 2)
+ *t = (rnibble(s[0]) << 4) | rnibble(s[1]);
+ i++;
+ }
+ offs[i] = t - bin;
+ *offsetp = offs;
+ }
+
+exit:
+ rpmtdFreeData(&td);
+ return bin;
+}
+
/* Convert a tag of hex strings to binary presentation */
static uint8_t *hex2bin(Header h, rpmTagVal tag, rpm_count_t num, size_t len)
{
@@ -1595,9 +1643,8 @@ static int rpmfilesPopulate(rpmfiles fi, Header h, rpmfiFlags flags)
fi->signatures = NULL;
/* grab hex signatures from header and store in binary format */
if (!(flags & RPMFI_NOFILESIGNATURES)) {
- fi->signaturelength = headerGetNumber(h, RPMTAG_FILESIGNATURELENGTH);
- fi->signatures = hex2bin(h, RPMTAG_FILESIGNATURES,
- totalfc, fi->signaturelength);
+ fi->signatures = hex2binv(h, RPMTAG_FILESIGNATURES,
+ totalfc, &fi->signatureoffs);
}
/* XXX TR_REMOVED doesn;t need fmtimes, frdevs, finodes */
diff --git a/sign/rpmsignfiles.c b/sign/rpmsignfiles.c
index 61b73bd40..3b87ae875 100644
--- a/sign/rpmsignfiles.c
+++ b/sign/rpmsignfiles.c
@@ -33,7 +33,7 @@ static const char *hash_algo_name[] = {
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
static char *signFile(const char *algo, const char *fdigest, int diglen,
-const char *key, char *keypass)
+const char *key, char *keypass, uint32_t *siglenp)
{
char *fsignature;
unsigned char digest[diglen];
@@ -60,32 +60,18 @@ const char *key, char *keypass)
return NULL;
}
+ *siglenp = siglen + 1;
/* convert file signature binary to hex */
fsignature = pgpHexStr(signature, siglen+1);
return fsignature;
}
-static uint32_t signatureLength(const char *algo, int diglen, const char *key,
-char *keypass)
-{
- unsigned char digest[diglen];
- unsigned char signature[MAX_SIGNATURE_LENGTH];
-
- memset(digest, 0, diglen);
- memset(signature, 0, MAX_SIGNATURE_LENGTH);
- signature[0] = '\x03';
-
- uint32_t siglen = sign_hash(algo, digest, diglen, key, keypass,
- signature+1);
- return siglen + 1;
-}
-
rpmRC rpmSignFiles(Header h, const char *key, char *keypass)
{
struct rpmtd_s digests;
int algo;
int diglen;
- uint32_t siglen;
+ uint32_t siglen = 0;
const char *algoname;
const char *digest;
char *signature;
@@ -109,12 +95,11 @@ rpmRC rpmSignFiles(Header h, const char *key, char *keypass)
headerDel(h, RPMTAG_FILESIGNATURELENGTH);
headerDel(h, RPMTAG_FILESIGNATURES);
- siglen = signatureLength(algoname, diglen, key, keypass);
- headerPutUint32(h, RPMTAG_FILESIGNATURELENGTH, &siglen, 1);
headerGet(h, RPMTAG_FILEDIGESTS, &digests, HEADERGET_MINMEM);
while ((digest = rpmtdNextString(&digests))) {
- signature = signFile(algoname, digest, diglen, key, keypass);
+ uint32_t slen = 0;
+ signature = signFile(algoname, digest, diglen, key, keypass, &slen);
if (!signature) {
rpmlog(RPMLOG_ERR, _("signFile failed\n"));
rc = RPMRC_FAIL;
@@ -127,8 +112,13 @@ rpmRC rpmSignFiles(Header h, const char *key, char *keypass)
goto exit;
}
free(signature);
+ if (slen > siglen)
+ siglen = slen;
}
+ if (siglen > 0)
+ headerPutUint32(h, RPMTAG_FILESIGNATURELENGTH, &siglen, 1);
+
exit:
rpmtdFreeData(&digests);
return rc;
--
2.47.0