From 930f2582f91077b3f338b84cf9567559d52713de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ale=C5=A1=20Mat=C4=9Bj?= Date: Mon, 29 Mar 2021 09:22:09 +0200 Subject: [PATCH] Hardening: add signature check with rpmcliVerifySignatures 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 --- libdnf/dnf-keyring.cpp | 52 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 2 deletions(-) diff --git a/libdnf/dnf-keyring.cpp b/libdnf/dnf-keyring.cpp index eec58c69ea..62a6248cb8 100644 --- a/libdnf/dnf-keyring.cpp +++ b/libdnf/dnf-keyring.cpp @@ -34,6 +34,8 @@ #include #include #include +#include +#include #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) {