From 0f89c912f5176e60c0e0152c5f64c1b7045231e0 Mon Sep 17 00:00:00 2001 From: Jaroslav Rohel Date: Tue, 22 Aug 2023 14:35:26 +0200 Subject: [PATCH] crypto: Use libdnf crypto API instead of using GnuPG/GpgME - It is compatible with the new librepo. The new librepo may use a different backend than GpgME internally. - Removes dependency on GnuPG/gpgme. --- VERSION.cmake | 2 +- dnf.spec | 6 +-- dnf/crypto.py | 118 ++++++++++++------------------------------------ dnf/yum/misc.py | 43 +++++++----------- 4 files changed, 47 insertions(+), 122 deletions(-) diff --git a/VERSION.cmake b/VERSION.cmake index 0dd83fff96..a2f5d509f4 100644 --- a/VERSION.cmake +++ b/VERSION.cmake @@ -1,4 +1,4 @@ -set (DEFAULT_DNF_VERSION "4.16.2") +set (DEFAULT_DNF_VERSION "4.17.0") if(DEFINED DNF_VERSION) if(NOT ${DEFAULT_DNF_VERSION} STREQUAL ${DNF_VERSION}) diff --git a/dnf.spec b/dnf.spec index d7925b31de..ed1941ed0e 100644 --- a/dnf.spec +++ b/dnf.spec @@ -2,7 +2,7 @@ %define __cmake_in_source_build 1 # default dependencies -%global hawkey_version 0.66.0 +%global hawkey_version 0.71.0 %global libcomps_version 0.1.8 %global libmodulemd_version 2.9.3 %global rpm_version 4.14.0 @@ -67,7 +67,7 @@ It supports RPMs, modules and comps groups & environments. Name: dnf -Version: 4.16.2 +Version: 4.17.0 Release: 1%{?dist} Summary: %{pkg_summary} # For a breakdown of the licensing, see PACKAGE-LICENSING @@ -130,8 +130,6 @@ BuildRequires: python3-libcomps >= %{libcomps_version} BuildRequires: python3-libdnf BuildRequires: libmodulemd >= %{libmodulemd_version} Requires: libmodulemd >= %{libmodulemd_version} -BuildRequires: python3-gpg -Requires: python3-gpg Requires: %{name}-data = %{version}-%{release} %if 0%{?fedora} Recommends: deltarpm diff --git a/dnf/crypto.py b/dnf/crypto.py index 9eee6b269b..63b814cad5 100644 --- a/dnf/crypto.py +++ b/dnf/crypto.py @@ -22,74 +22,20 @@ from __future__ import absolute_import from __future__ import unicode_literals from dnf.i18n import _ +import libdnf.repo import contextlib import dnf.pycomp import dnf.util import dnf.yum.misc -import io import logging import os -import tempfile - -try: - from gpg import Context - from gpg import Data -except ImportError: - import gpgme - - - class Context(object): - def __init__(self): - self.__dict__["ctx"] = gpgme.Context() - - def __enter__(self): - return self - - def __exit__(self, type, value, tb): - pass - - @property - def armor(self): - return self.ctx.armor - - @armor.setter - def armor(self, value): - self.ctx.armor = value - - def op_import(self, key_fo): - if isinstance(key_fo, basestring): - key_fo = io.BytesIO(key_fo) - self.ctx.import_(key_fo) - - def op_export(self, pattern, mode, keydata): - self.ctx.export(pattern, keydata) - - def __getattr__(self, name): - return getattr(self.ctx, name) - - - class Data(object): - def __init__(self): - self.__dict__["buf"] = io.BytesIO() - - def __enter__(self): - return self - - def __exit__(self, type, value, tb): - pass - - def read(self): - return self.buf.getvalue() - - def __getattr__(self, name): - return getattr(self.buf, name) - GPG_HOME_ENV = 'GNUPGHOME' logger = logging.getLogger('dnf') - def _extract_signing_subkey(key): + # :deprecated, undocumented + """ Deprecated feature. It was used internally. It is no longer used. """ return dnf.util.first(subkey for subkey in key.subkeys if subkey.can_sign) @@ -99,6 +45,11 @@ def _printable_fingerprint(fpr_hex): def import_repo_keys(repo): + # :deprecated, undocumented + """ Deprecated function. Please do not use. + It was used internally. In 2018, the code was rewritten into libdnf. This code is no longer used. + It was broken in 2018 - the `repo._key_import._confirm` method now needs 5 arguments. + It is now fixed and marked as deprecated. Please do not use. """ gpgdir = repo._pubring_dir known_keys = keyids_from_pubring(gpgdir) for keyurl in repo.gpgkey: @@ -107,7 +58,8 @@ def import_repo_keys(repo): if keyid in known_keys: logger.debug(_('repo %s: 0x%s already imported'), repo.id, keyid) continue - if not repo._key_import._confirm(keyinfo): + if not repo._key_import._confirm( + keyid, keyinfo.userid, keyinfo.fingerprint, keyinfo.url, keyinfo.timestamp): continue dnf.yum.misc.import_key_to_pubring( keyinfo.raw_key, keyinfo.short_id, gpgdir=gpgdir, @@ -116,16 +68,12 @@ def import_repo_keys(repo): def keyids_from_pubring(gpgdir): - if not os.path.exists(gpgdir): - return [] - - with pubring_dir(gpgdir), Context() as ctx: - keyids = [] - for k in ctx.keylist(): - subkey = _extract_signing_subkey(k) - if subkey is not None: - keyids.append(subkey.keyid) - return keyids + # :deprecated, undocumented + """ Used internally by deprecated function `import_repo_keys` """ + keyids = [] + for keyid in libdnf.repo.keyidsFromPubring(gpgdir): + keyids.append(keyid) + return keyids def log_key_import(keyinfo): @@ -148,6 +96,8 @@ def log_dns_key_import(keyinfo, dns_result): @contextlib.contextmanager def pubring_dir(pubring_dir): + # :deprecated, undocumented + """ Deprecated feature. It was used internally. It is no longer used. """ orig = os.environ.get(GPG_HOME_ENV, None) os.environ[GPG_HOME_ENV] = pubring_dir try: @@ -160,22 +110,10 @@ def pubring_dir(pubring_dir): def rawkey2infos(key_fo): - pb_dir = tempfile.mkdtemp() keyinfos = [] - with pubring_dir(pb_dir), Context() as ctx: - ctx.op_import(key_fo) - for key in ctx.keylist(): - subkey = _extract_signing_subkey(key) - if subkey is None: - continue - keyinfos.append(Key(key, subkey)) - ctx.armor = True - for info in keyinfos: - with Data() as sink: - ctx.op_export(info.id_, 0, sink) - sink.seek(0, os.SEEK_SET) - info.raw_key = sink.read() - dnf.util.rm_rf(pb_dir) + keys = libdnf.repo.Key.keysFromFd(key_fo.fileno()) + for key in keys: + keyinfos.append(Key(key)) return keyinfos @@ -190,13 +128,13 @@ def retrieve(keyurl, repo=None): class Key(object): - def __init__(self, key, subkey): - self.id_ = subkey.keyid - self.fingerprint = subkey.fpr - self.raw_key = None - self.timestamp = subkey.timestamp - self.url = None - self.userid = key.uids[0].uid + def __init__(self, repokey): + self.id_ = repokey.getId() + self.fingerprint = repokey.getFingerprint() + self.raw_key = bytes(repokey.getAsciiArmoredKey(), 'utf-8') + self.timestamp = repokey.getTimestamp() + self.url = repokey.getUrl() + self.userid = repokey.getUserId() @property def short_id(self): diff --git a/dnf/yum/misc.py b/dnf/yum/misc.py index ed48627d72..0c151470b6 100644 --- a/dnf/yum/misc.py +++ b/dnf/yum/misc.py @@ -24,6 +24,7 @@ from __future__ import unicode_literals from dnf.pycomp import base64_decodebytes, basestring, unicode from stat import * +import libdnf.repo import libdnf.utils import dnf.const import dnf.crypto @@ -159,38 +160,26 @@ def keyInstalled(ts, keyid, timestamp): def import_key_to_pubring(rawkey, keyid, gpgdir=None, make_ro_copy=True): + # :deprecated, undocumented + """ Used internally by deprecated function `import_repo_keys` """ + if not os.path.exists(gpgdir): os.makedirs(gpgdir) - with dnf.crypto.pubring_dir(gpgdir), dnf.crypto.Context() as ctx: - # import the key - with open(os.path.join(gpgdir, 'gpg.conf'), 'wb') as fp: - fp.write(b'') - ctx.op_import(rawkey) - - if make_ro_copy: - - rodir = gpgdir + '-ro' - if not os.path.exists(rodir): - os.makedirs(rodir, mode=0o755) - for f in glob.glob(gpgdir + '/*'): - basename = os.path.basename(f) - ro_f = rodir + '/' + basename - shutil.copy(f, ro_f) - os.chmod(ro_f, 0o755) - # yes it is this stupid, why do you ask? - opts = """lock-never - no-auto-check-trustdb - trust-model direct - no-expensive-trust-checks - no-permission-warning - preserve-permissions - """ - with open(os.path.join(rodir, 'gpg.conf'), 'w', 0o755) as fp: - fp.write(opts) + # import the key + libdnf.repo.importKeyToPubring(str(rawkey, 'utf-8'), gpgdir) + if make_ro_copy: + rodir = gpgdir + '-ro' + if not os.path.exists(rodir): + os.makedirs(rodir, mode=0o755) + for f in glob.glob(gpgdir + '/*'): + basename = os.path.basename(f) + ro_f = rodir + '/' + basename + shutil.copy(f, ro_f) + os.chmod(ro_f, 0o755) - return True + return True def getCacheDir():