From 4268176cce2ca6e216dd543ce3bfae7f58b15b53 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ale=C5=A1=20Mat=C4=9Bj?= <amatej@redhat.com>
Date: Tue, 30 Mar 2021 08:18:42 +0200
Subject: [PATCH] Check for specific key string when verifing signatures

Also improves handling of return values and exceptions.
The function can't return 0 when the signature is malformed.

Credit for the original patch goes to Demi Marie Obenour: @DemiMarie

= changelog =
msg: Check for specific key string when verifing signatures
type: security
resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1915990

CVE-2021-3445
RhBug:1915990

Related: CVE-2021-3421, CVE-2021-20271
---
 dnf/rpm/miscutils.py | 20 +++++++++++++-------
 1 file changed, 13 insertions(+), 7 deletions(-)

diff --git a/dnf/rpm/miscutils.py b/dnf/rpm/miscutils.py
index 9038f98..07451bb 100644
--- a/dnf/rpm/miscutils.py
+++ b/dnf/rpm/miscutils.py
@@ -18,6 +18,7 @@ from __future__ import unicode_literals
 
 import rpm
 import os
+import re
 
 from dnf.i18n import ucd
 
@@ -30,7 +31,7 @@ def checkSig(ts, package):
     return 3 if the key is not trusted
     return 4 if the pkg is not gpg or pgp signed"""
 
-    value = 0
+    value = 4
     currentflags = ts.setVSFlags(0)
     fdno = os.open(package, os.O_RDONLY)
     try:
@@ -38,10 +39,12 @@ def checkSig(ts, package):
     except rpm.error as e:
         if str(e) == "public key not available":
             value = 1
-        if str(e) == "public key not trusted":
+        elif str(e) == "public key not trusted":
             value = 3
-        if str(e) == "error reading package header":
+        elif str(e) == "error reading package header":
             value = 2
+        else:
+            raise ValueError('Unexpected error value %r from ts.hdrFromFdno when checking signature.' % str(e))
     else:
         # checks signature from an hdr
         string = '%|DSAHEADER?{%{DSAHEADER:pgpsig}}:{%|RSAHEADER?{%{RSAHEADER:pgpsig}}:' \
@@ -49,17 +52,20 @@ def checkSig(ts, package):
         try:
             siginfo = hdr.sprintf(string)
             siginfo = ucd(siginfo)
+            rpm_pgpsig_format_regex = re.compile(r'[0-9]{2}:[0-9]{2}:[0-9]{2} [0-9]{4}, Key ID [0-9a-f]{16}\Z')
+
             if siginfo == '(none)':
                 value = 4
+            elif rpm_pgpsig_format_regex.search(siginfo):
+                value = 0
+            else:
+                raise ValueError('Unexpected return value %r from hdr.sprintf when checking signature.' % siginfo)
         except UnicodeDecodeError:
             pass
 
         del hdr
 
-    try:
-        os.close(fdno)
-    except OSError as e:  # if we're not opened, don't scream about it
-        pass
+    os.close(fdno)
 
     ts.setVSFlags(currentflags)  # put things back like they were before
     return value
-- 
1.8.3.1

