From 4747e369c1b5b406688ff0be5447ebd0c29575a6 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:19:37 +0200
Subject: [PATCH] Use rpmkeys to verify package signature with _pkgverify_level

rpm doesn't provide any API how to check package signature with
_pkgverify_level signature therefore we call rpmkeys binary.

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

= changelog =
msg: Use rpmkeys binary to verify package signature
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 | 32 +++++++++++++++++++++++++++++++-
 1 file changed, 31 insertions(+), 1 deletion(-)

diff --git a/dnf/rpm/miscutils.py b/dnf/rpm/miscutils.py
index 07451bb..49d3717 100644
--- a/dnf/rpm/miscutils.py
+++ b/dnf/rpm/miscutils.py
@@ -19,10 +19,40 @@ from __future__ import unicode_literals
 import rpm
 import os
 import re
+import subprocess
+import logging
 
 from dnf.i18n import ucd
+from shutil import which
 
 
+logger = logging.getLogger('dnf')
+
+
+def _verifyPkgUsingRpmkeys(package, installroot):
+    rpmkeys_binary = '/usr/bin/rpmkeys'
+    if not rpmkeys_binary:
+        rpmkeys_binary = which("rpmkeys")
+        logger.info(_('Using rpmkeys executable from {path} to verify signature for package: {package}.').format(
+            path=rpmkeys_binary, package=package))
+
+    if not rpmkeys_binary:
+        logger.critical(_('Cannot find rpmkeys executable to verify signatures.'))
+        return 0
+
+    args = ('rpmkeys', '--checksig', '--root', installroot, '--define', '_pkgverify_level all', '--', package)
+    with subprocess.Popen(
+            args=args,
+            executable=rpmkeys_binary,
+            env={'LC_ALL': 'C'},
+            stdout=subprocess.PIPE,
+            cwd='/') as p:
+        data, _ = p.communicate()
+    if p.returncode != 0 or data != (package.encode('ascii', 'strict') + b': digests signatures OK\n'):
+        return 0
+    else:
+        return 1
+
 def checkSig(ts, package):
     """Takes a transaction set and a package, check it's sigs,
     return 0 if they are all fine
@@ -56,7 +86,7 @@ def checkSig(ts, package):
 
             if siginfo == '(none)':
                 value = 4
-            elif rpm_pgpsig_format_regex.search(siginfo):
+            elif rpm_pgpsig_format_regex.search(siginfo) and _verifyPkgUsingRpmkeys(package, ts.ts.rootDir):
                 value = 0
             else:
                 raise ValueError('Unexpected return value %r from hdr.sprintf when checking signature.' % siginfo)
-- 
1.8.3.1

