222 lines
7.2 KiB
Diff
222 lines
7.2 KiB
Diff
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
|
|
|