diff --git a/SOURCES/rpm-4.11.x-improve-sig-header-range-checks.patch b/SOURCES/rpm-4.11.x-improve-sig-header-range-checks.patch new file mode 100644 index 0000000..c856272 --- /dev/null +++ b/SOURCES/rpm-4.11.x-improve-sig-header-range-checks.patch @@ -0,0 +1,278 @@ +commit d6a86b5e69e46cc283b1e06c92343319beb42e21 +Author: Panu Matilainen +Date: Thu Mar 4 13:21:19 2021 +0200 + + Be much more careful about copying data from the signature header + + Only look for known tags, and ensure correct type and size where known + before copying over. Bump the old arbitrary 16k count limit to 16M limit + though, it's not inconceivable that a package could have that many files. + While at it, ensure none of these tags exist in the main header, + which would confuse us greatly. + + This is optimized for backporting ease, upstream can remove redundancies + and further improve checking later. + + Reported and initial patches by Demi Marie Obenour. + + Fixes: RhBug:1935049, RhBug:1933867, RhBug:1935035, RhBug:1934125, ... + + Fixes: CVE-2021-3421, CVE-2021-20271 + + Backported into 4.11.3 and combined with upstream commits: + - f7b97593af5cf818a5c6c5b9bc55bba6d08c9cb0 + - e2f1f1931c5ccf3ecbe4e1e12cacb1e17a277776 + - 822c3dc2046c29718e34ac2da16a9757a9be11da + +diff --git a/lib/package.c b/lib/package.c +index 985ac47d3..565e4bf6d 100644 +--- a/lib/package.c ++++ b/lib/package.c +@@ -28,79 +28,75 @@ static unsigned int * keyids; + extern int auditGpgResult; + extern int auditEnabled; + ++static struct taglate_s { ++ rpmTagVal stag; ++ rpmTagVal xtag; ++ rpm_count_t count; ++ int quirk; ++} const xlateTags[] = { ++ { RPMSIGTAG_SIZE, RPMTAG_SIGSIZE, 1, 0 }, ++ { RPMSIGTAG_PGP, RPMTAG_SIGPGP, 0, 0 }, ++ { RPMSIGTAG_MD5, RPMTAG_SIGMD5, 16, 0 }, ++ { RPMSIGTAG_GPG, RPMTAG_SIGGPG, 0, 0 }, ++ /* { RPMSIGTAG_PGP5, RPMTAG_SIGPGP5, 0, 0 }, */ /* long obsolete, dont use */ ++ { RPMSIGTAG_PAYLOADSIZE, RPMTAG_ARCHIVESIZE, 1, 1 }, ++ { RPMSIGTAG_SHA1, RPMTAG_SHA1HEADER, 1, 0 }, ++ { RPMSIGTAG_DSA, RPMTAG_DSAHEADER, 0, 0 }, ++ { RPMSIGTAG_RSA, RPMTAG_RSAHEADER, 0, 0 }, ++ { RPMSIGTAG_LONGSIZE, RPMTAG_LONGSIGSIZE, 1, 0 }, ++ { RPMSIGTAG_LONGARCHIVESIZE, RPMTAG_LONGARCHIVESIZE, 1, 0 }, ++ { 0 } ++}; ++ + /** \ingroup header + * Translate and merge legacy signature tags into header. + * @param h header (dest) + * @param sigh signature header (src) ++ * @return failing tag number, 0 on success + */ +-static void headerMergeLegacySigs(Header h, Header sigh) ++static ++rpmTagVal headerMergeLegacySigs(Header h, Header sigh, char **msg) + { +- HeaderIterator hi; ++ const struct taglate_s *xl; + struct rpmtd_s td; + +- hi = headerInitIterator(sigh); +- for (; headerNext(hi, &td); rpmtdFreeData(&td)) +- { +- switch (td.tag) { +- /* XXX Translate legacy signature tag values. */ +- case RPMSIGTAG_SIZE: +- td.tag = RPMTAG_SIGSIZE; +- break; +- case RPMSIGTAG_PGP: +- td.tag = RPMTAG_SIGPGP; +- break; +- case RPMSIGTAG_MD5: +- td.tag = RPMTAG_SIGMD5; +- break; +- case RPMSIGTAG_GPG: +- td.tag = RPMTAG_SIGGPG; +- break; +- case RPMSIGTAG_PGP5: +- td.tag = RPMTAG_SIGPGP5; +- break; +- case RPMSIGTAG_PAYLOADSIZE: +- td.tag = RPMTAG_ARCHIVESIZE; +- break; +- case RPMSIGTAG_SHA1: +- case RPMSIGTAG_DSA: +- case RPMSIGTAG_RSA: +- default: +- if (!(td.tag >= HEADER_SIGBASE && td.tag < HEADER_TAGBASE)) ++ for (xl = xlateTags; xl->stag; xl++) { ++ /* There mustn't be one in the main header */ ++ if (headerIsEntry(h, xl->xtag)) { ++ /* Some tags may exist in either header, but never both */ ++ if (xl->quirk && !headerIsEntry(sigh, xl->stag)) + continue; +- break; ++ goto exit; + } +- if (td.data == NULL) continue; /* XXX can't happen */ +- if (!headerIsEntry(h, td.tag)) { +- if (hdrchkType(td.type)) +- continue; +- if (td.count < 0 || hdrchkData(td.count)) +- continue; +- switch(td.type) { +- case RPM_NULL_TYPE: +- continue; ++ } ++ ++ rpmtdReset(&td); ++ for (xl = xlateTags; xl->stag; xl++) { ++ if (headerGet(sigh, xl->stag, &td, HEADERGET_RAW|HEADERGET_MINMEM)) { ++ /* Translate legacy tags */ ++ if (xl->stag != xl->xtag) ++ td.tag = xl->xtag; ++ /* Ensure type and tag size match expectations */ ++ if (td.type != rpmTagGetTagType(td.tag)) + break; +- case RPM_CHAR_TYPE: +- case RPM_INT8_TYPE: +- case RPM_INT16_TYPE: +- case RPM_INT32_TYPE: +- case RPM_INT64_TYPE: +- if (td.count != 1) +- continue; ++ if (td.count < 1 || td.count > 16*1024*1024) + break; +- case RPM_STRING_TYPE: +- case RPM_BIN_TYPE: +- if (td.count >= 16*1024) +- continue; ++ if (xl->count && td.count != xl->count) + break; +- case RPM_STRING_ARRAY_TYPE: +- case RPM_I18NSTRING_TYPE: +- continue; ++ if (!headerPut(h, &td, HEADERPUT_DEFAULT)) + break; +- } +- (void) headerPut(h, &td, HEADERPUT_DEFAULT); ++ rpmtdFreeData(&td); + } + } +- headerFreeIterator(hi); ++ rpmtdFreeData(&td); ++ ++exit: ++ if (xl->stag) { ++ rasprintf(msg, "invalid signature tag %s (%d)", ++ rpmTagGetName(xl->xtag), xl->xtag); ++ } ++ ++ return xl->stag; + } + + /** +@@ -671,7 +667,7 @@ static rpmRC rpmpkgRead(rpmKeyring keyring, rpmVSFlags vsflags, + rpmlog(RPMLOG_ERR, "%s: %s", fn, msg); + break; + } +- free(msg); ++ msg = _free(msg); + + exit: + if (rc != RPMRC_FAIL && h != NULL && hdrp != NULL) { +@@ -701,10 +697,13 @@ exit: + headerConvert(h, HEADERCONV_COMPRESSFILELIST); + + /* Append (and remap) signature tags to the metadata. */ +- headerMergeLegacySigs(h, sigh); +- +- /* Bump reference count for return. */ +- *hdrp = headerLink(h); ++ if (headerMergeLegacySigs(h, sigh, &msg)) { ++ rpmlog(RPMLOG_ERR, "%s: %s\n", fn, msg); ++ free(msg); ++ rc = RPMRC_FAIL; ++ } else ++ /* Bump reference count for return. */ ++ *hdrp = headerLink(h); + } + rpmtdFreeData(&sigtd); + rpmDigestFinal(ctx, NULL, NULL, 0); + +commit 441a0998fca9b3d01052bc3d33f8a72f725d5865 +Author: Panu Matilainen +Date: Fri Oct 21 17:37:26 2016 +0300 + + Require exact match on header vs region sizes when reading package files + + In rpm V4 packages both the signature and mail header consist of one + contiguous immutable region when read from package files and any + disparities in index or data size means malformed package. + + Once the signature header is merged into the main header this is + no longer true of course and installation adds further tags, so for + headers from other sources (such as rpmdb), tags outside the immutable + region (known as "dribbles" in rpm lore) must be allowed, and eg + headerCheck() cannot require exact match. + + Backported into 4.11.3. Note that the upstream patch also enforces + exact_size for signature headers, but this was later reverted in commit + 34c2ba3c6a80a778cdf2e42a9193b3264e08e1b3, so we omit that part here. + +diff --git a/lib/package.c b/lib/package.c +index 08f0bc634..fea27e9ca 100644 +--- a/lib/package.c ++++ b/lib/package.c +@@ -275,7 +275,8 @@ exit: + } + + static rpmRC headerVerify(rpmKeyring keyring, rpmVSFlags vsflags, +- const void * uh, size_t uc, char ** msg) ++ const void * uh, size_t uc, int exact_size, ++ char ** msg) + { + char *buf = NULL; + int32_t * ei = (int32_t *) uh; +@@ -287,6 +288,7 @@ static rpmRC headerVerify(rpmKeyring keyring, rpmVSFlags vsflags, + struct indexEntry_s entry; + struct entryInfo_s info; + int32_t ril = 0; ++ int32_t rdl = 0; + unsigned char * regionEnd = NULL; + rpmRC rc = RPMRC_FAIL; /* assume failure */ + +@@ -337,6 +339,7 @@ static rpmRC headerVerify(rpmKeyring keyring, rpmVSFlags vsflags, + regionEnd = dataStart + entry.info.offset; + (void) memcpy(&info, regionEnd, REGION_TAG_COUNT); + regionEnd += REGION_TAG_COUNT; ++ rdl = regionEnd - dataStart; + + if (headerVerifyInfo(1, il * sizeof(*pe) + REGION_TAG_COUNT, &info, &entry.info, 1) != -1 || + !(entry.info.tag == RPMTAG_HEADERIMMUTABLE +@@ -358,9 +361,17 @@ static rpmRC headerVerify(rpmKeyring keyring, rpmVSFlags vsflags, + goto exit; + } + ++ /* In package files region size is expected to match header size. */ ++ if (exact_size && !(il == ril && dl == rdl)) { ++ rasprintf(&buf, ++ _("region %d: tag number mismatch %d ril %d dl %d rdl %d\n"), ++ entry.info.tag, il, ril, dl, rdl); ++ goto exit; ++ } ++ + /* Verify header-only digest/signature if there is one we can use. */ + rc = headerSigVerify(keyring, vsflags, +- il, dl, ril, (regionEnd - dataStart), ++ il, dl, ril, rdl, + pe, dataStart, &buf); + + exit: +@@ -394,7 +405,7 @@ rpmRC headerCheck(rpmts ts, const void * uh, size_t uc, char ** msg) + rpmKeyring keyring = rpmtsGetKeyring(ts, 1); + + rpmswEnter(rpmtsOp(ts, RPMTS_OP_DIGEST), 0); +- rc = headerVerify(keyring, vsflags, uh, uc, msg); ++ rc = headerVerify(keyring, vsflags, uh, uc, 0, msg); + rpmswExit(rpmtsOp(ts, RPMTS_OP_DIGEST), uc); + rpmKeyringFree(keyring); + +@@ -453,7 +464,7 @@ static rpmRC rpmpkgReadHeader(rpmKeyring keyring, rpmVSFlags vsflags, + } + + /* Sanity check header tags */ +- rc = headerVerify(keyring, vsflags, ei, uc, &buf); ++ rc = headerVerify(keyring, vsflags, ei, uc, 1, &buf); + if (rc != RPMRC_OK) + goto exit; + diff --git a/SPECS/rpm.spec b/SPECS/rpm.spec index dccf4bb..cadf92e 100644 --- a/SPECS/rpm.spec +++ b/SPECS/rpm.spec @@ -21,7 +21,7 @@ Summary: The RPM package management system Name: rpm Version: %{rpmver} -Release: %{?snapver:0.%{snapver}.}46%{?dist} +Release: %{?snapver:0.%{snapver}.}48%{?dist} Group: System Environment/Base Url: http://www.rpm.org/ Source0: http://rpm.org/releases/rpm-4.11.x/%{name}-%{srcver}.tar.bz2 @@ -106,6 +106,7 @@ Patch201: rpm-4.11.x-do-not-filter-ld64.patch Patch202: rpm-4.14.x-PGP-handle-no-EOL-at-EOF.patch Patch203: rpm-4.11.x-bump-up-the-limit-of-signature-header-to-64MB.patch +Patch204: rpm-4.11.x-improve-sig-header-range-checks.patch # These are not yet upstream Patch301: rpm-4.6.0-niagara.patch @@ -397,6 +398,7 @@ Requires: rpm-libs%{_isa} = %{version}-%{release} %patch202 -p1 -b .PGP-no-EOL %patch203 -p1 -b .sig-limit-bump +%patch204 -p1 -b .sig-hdr-check %patch301 -p1 -b .niagara %patch302 -p1 -b .geode @@ -659,6 +661,13 @@ exit 0 %doc COPYING doc/librpm/html/* %changelog +* Mon Nov 01 2021 Michal Domonkos - 4.11.3-48 +- Fix double-free in previously added patch (#2004228) + +* Thu Oct 21 2021 Michal Domonkos - 4.11.3-47 +- Improve range checks on signature and main header tags (#2004228) +- Fixes CVE-2021-20271 + * Fri Sep 17 2021 Michal Domonkos - 4.11.3-46 - Bump up the limit of signature header to 64MB (#1993242)