[PATCH] Hardening: add signature check with rpmcliVerifySignatures
authorAleš Matěj <amatej@redhat.com>
Mon, 29 Mar 2021 07:22:09 +0000 (09:22 +0200)
committerFrédéric Pierret <frederic.pierret@qubes-os.org>
Wed, 14 Apr 2021 19:26:57 +0000 (20:26 +0100)
This api is not ideal but works for now. We don't have to set
installroot for the used transaction because we set keyring which is
used to retrieve the keys.

= changelog =
msg: Hardening: add signature check with rpmcliVerifySignatures
type: security
resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1932079

CVE-2021-3445
RhBug:1932079
RhBug:1932089
RhBug:1932090

Related: CVE-2021-3421, CVE-2021-20271

Gbp-Pq: Name 0014-Hardening-add-signature-check-with-rpmcliVerifySigna.patch

libdnf/dnf-keyring.cpp

index eec58c69ea09e2a171f21fbec66d368336d7375e..62a6248cb8e1b0dc4deb53cbbb878762f2c98b0c 100644 (file)
@@ -34,6 +34,8 @@
 #include <glib.h>
 #include <rpm/rpmlib.h>
 #include <rpm/rpmts.h>
+#include <rpm/rpmlog.h>
+#include <rpm/rpmcli.h>
 
 #include "catch-error.hpp"
 #include "dnf-types.h"
@@ -216,6 +218,26 @@ dnf_keyring_add_public_keys(rpmKeyring keyring, GError **error) try
     return TRUE;
 } CATCH_TO_GERROR(FALSE)
 
+static int
+rpmcliverifysignatures_log_handler_cb(rpmlogRec rec, rpmlogCallbackData data)
+{
+    GString **string =(GString **) data;
+
+    /* create string if required */
+    if (*string == NULL)
+        *string = g_string_new("");
+
+    /* if text already exists, join them */
+    if ((*string)->len > 0)
+        g_string_append(*string, ": ");
+    g_string_append(*string, rpmlogRecMessage(rec));
+
+    /* remove the trailing /n which rpm does */
+    if ((*string)->len > 0)
+        g_string_truncate(*string,(*string)->len - 1);
+    return 0;
+}
+
 /**
  * dnf_keyring_check_untrusted_file:
  */
@@ -232,6 +254,10 @@ dnf_keyring_check_untrusted_file(rpmKeyring keyring,
     rpmtd td = NULL;
     rpmts ts = NULL;
 
+    char *path = g_strdup(filename);
+    char *path_array[2] = {path, NULL};
+    g_autoptr(GString) rpm_error = NULL;
+
     /* open the file for reading */
     fd = Fopen(filename, "r.fdio");
     if (fd == NULL) {
@@ -252,9 +278,27 @@ dnf_keyring_check_untrusted_file(rpmKeyring keyring,
         goto out;
     }
 
-    /* we don't want to abort on missing keys */
     ts = rpmtsCreate();
-    rpmtsSetVSFlags(ts, _RPMVSF_NOSIGNATURES);
+
+    if (rpmtsSetKeyring(ts, keyring) < 0) {
+        g_set_error_literal(error, DNF_ERROR, DNF_ERROR_INTERNAL_ERROR, "failed to set keyring");
+        goto out;
+    }
+    rpmtsSetVfyLevel(ts, RPMSIG_SIGNATURE_TYPE);
+    rpmlogSetCallback(rpmcliverifysignatures_log_handler_cb, &rpm_error);
+
+    // rpm doesn't provide any better API call than rpmcliVerifySignatures (which is for CLI):
+    // - use path_array as input argument
+    // - gather logs via callback because we don't want to print anything if check is successful
+    if (rpmcliVerifySignatures(ts, (char * const*) path_array)) {
+        g_set_error(error,
+                DNF_ERROR,
+                DNF_ERROR_GPG_SIGNATURE_INVALID,
+                "%s could not be verified.\n%s",
+                filename,
+                (rpm_error ? rpm_error->str : "UNKNOWN ERROR"));
+        goto out;
+    }
 
     /* read in the file */
     rc = rpmReadPackageFile(ts, fd, filename, &hdr);
@@ -318,6 +362,10 @@ dnf_keyring_check_untrusted_file(rpmKeyring keyring,
     g_debug("%s has been verified as trusted", filename);
     ret = TRUE;
 out:
+    rpmlogSetCallback(NULL, NULL);
+
+    if (path != NULL)
+        g_free(path);
     if (dig != NULL)
         pgpFreeDig(dig);
     if (td != NULL) {