618 lines
24 KiB
Diff
618 lines
24 KiB
Diff
From 37c4cd7014f4b3c5db64a0a900761e53caf645be Mon Sep 17 00:00:00 2001
|
|
From: =?UTF-8?q?Petr=20P=C3=ADsa=C5=99?= <ppisar@redhat.com>
|
|
Date: Tue, 25 Mar 2025 12:34:34 +0100
|
|
Subject: [PATCH] multisig: A new plugin for verifying extraordinary RPM
|
|
signatures
|
|
MIME-Version: 1.0
|
|
Content-Type: text/plain; charset=UTF-8
|
|
Content-Transfer-Encoding: 8bit
|
|
|
|
This plugin executes a dedicated rpmkeys(8) tool of pqrpm for
|
|
verifying RPM packages bound into a transaction.
|
|
|
|
The dedicated tool is supposed to understand signatures in RPM version
|
|
6 format. One feature of that format is that you can have multiple
|
|
signatures on a single RPM package.
|
|
|
|
This plugin takes public keys from a default key store of pqrpm, which
|
|
is separate from the native RPM database. A reason is that the native
|
|
database might reject storing OpenPGP keys with an unsupported key
|
|
schema. A downside is that "dnf --installroot" places the keyring to
|
|
a directory which is ununowned in the installroot.
|
|
|
|
XXX: This plugin does not report which keys required for the
|
|
verification are missing (NOKEY case in _process_rpm_output()).
|
|
Implementing that would require changing too many function interfaces.
|
|
Classical single-signature verification does not do that either.
|
|
|
|
XXX: The plugin is packaged into a package which requires pqrpm which
|
|
will only be available in RHEL 9. Next RHEL version will support the
|
|
extraordinary signatures natively. Thus this patch only makes sense in
|
|
RHEL 9.
|
|
|
|
Signed-off-by: Petr Písař <ppisar@redhat.com>
|
|
---
|
|
dnf-plugins-core.spec | 19 ++
|
|
doc/CMakeLists.txt | 5 +
|
|
doc/conf.py | 3 +
|
|
doc/index.rst | 1 +
|
|
doc/multisig.rst | 62 +++++++
|
|
plugins/CMakeLists.txt | 3 +
|
|
plugins/multisig.py | 402 +++++++++++++++++++++++++++++++++++++++++
|
|
7 files changed, 495 insertions(+)
|
|
create mode 100644 doc/multisig.rst
|
|
create mode 100644 plugins/multisig.py
|
|
|
|
diff --git a/dnf-plugins-core.spec b/dnf-plugins-core.spec
|
|
index f2d1bc4..cb3b1b8 100644
|
|
--- a/dnf-plugins-core.spec
|
|
+++ b/dnf-plugins-core.spec
|
|
@@ -301,6 +301,18 @@ Obsoletes: python-dnf-plugins-extras-migrate < %{dnf_plugins_extra}
|
|
Migrate Plugin for DNF, Python 2 version. Migrates history, group and yumdb data from yum to dnf.
|
|
%endif
|
|
|
|
+%if %{with python3}
|
|
+%package -n python3-dnf-plugin-multisig
|
|
+Summary: Multisig Plugin for DNF
|
|
+Requires: pqrpm
|
|
+Requires: python3-%{name} = %{version}-%{release}
|
|
+Provides: dnf-plugin-multisig = %{version}-%{release}
|
|
+
|
|
+%description -n python3-dnf-plugin-multisig
|
|
+Multisig Plugin for DNF, Python 3 version. The plugin verifies multiple RPMv6
|
|
+signatures on RPMv4 packages by using an external rpmkeys program.
|
|
+%endif
|
|
+
|
|
%if %{with python2}
|
|
%package -n python2-dnf-plugin-post-transaction-actions
|
|
Summary: Post transaction actions Plugin for DNF
|
|
@@ -716,6 +728,13 @@ ln -sf %{_mandir}/man1/%{yum_utils_subpackage_name}.1.gz %{buildroot}%{_mandir}/
|
|
%exclude %{_mandir}/man8/dnf-migrate.*
|
|
%endif
|
|
|
|
+%if %{with python3}
|
|
+%files -n python3-dnf-plugin-multisig
|
|
+%{python3_sitelib}/dnf-plugins/multisig.*
|
|
+%{python3_sitelib}/dnf-plugins/__pycache__/multisig.*
|
|
+%{_mandir}/man8/dnf*-multisig.*
|
|
+%endif
|
|
+
|
|
%if %{with python2}
|
|
%files -n python2-dnf-plugin-post-transaction-actions
|
|
%config(noreplace) %{_sysconfdir}/dnf/plugins/post-transaction-actions.conf
|
|
diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt
|
|
index 79472a5..297506a 100644
|
|
--- a/doc/CMakeLists.txt
|
|
+++ b/doc/CMakeLists.txt
|
|
@@ -47,6 +47,11 @@ INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/dnf-migrate.8
|
|
DESTINATION share/man/man8)
|
|
endif()
|
|
|
|
+if (${PYTHON_VERSION_MAJOR} STREQUAL "3")
|
|
+INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/dnf4-multisig.8
|
|
+ DESTINATION share/man/man8)
|
|
+endif()
|
|
+
|
|
if (${WITHOUT_LOCAL} STREQUAL "0")
|
|
INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/dnf-local.8
|
|
DESTINATION share/man/man8)
|
|
diff --git a/doc/conf.py b/doc/conf.py
|
|
index 327ac07..2845d18 100644
|
|
--- a/doc/conf.py
|
|
+++ b/doc/conf.py
|
|
@@ -300,6 +300,9 @@ man_pages = [
|
|
if sys.version_info[0] < 3:
|
|
man_pages.append(('migrate', 'dnf-migrate', u'DNF migrate Plugin', AUTHORS, 8))
|
|
|
|
+if sys.version_info[0] == 3:
|
|
+ man_pages.append(('multisig', 'dnf4-multisig', u'DNF multisig Plugin', AUTHORS, 8))
|
|
+
|
|
# If true, show URL addresses after external links.
|
|
#man_show_urls = False
|
|
|
|
diff --git a/doc/index.rst b/doc/index.rst
|
|
index 251a24e..32984a5 100644
|
|
--- a/doc/index.rst
|
|
+++ b/doc/index.rst
|
|
@@ -37,6 +37,7 @@ This documents core plugins of DNF:
|
|
leaves
|
|
local
|
|
migrate
|
|
+ multisig
|
|
modulesync
|
|
needs_restarting
|
|
post-transaction-actions
|
|
diff --git a/doc/multisig.rst b/doc/multisig.rst
|
|
new file mode 100644
|
|
index 0000000..21b9436
|
|
--- /dev/null
|
|
+++ b/doc/multisig.rst
|
|
@@ -0,0 +1,62 @@
|
|
+===================
|
|
+DNF multisig Plugin
|
|
+===================
|
|
+
|
|
+-----------
|
|
+Description
|
|
+-----------
|
|
+
|
|
+This plugin verifies extraordinary RPMv6 signatures when installing,
|
|
+reinstalling, upgrading, or downgrading packages from a repository. If the
|
|
+verification fails, the RPM operation will be aborted.
|
|
+
|
|
+The verification is achieved by executing a dedicated rpmkeys(8) tool which is
|
|
+supposed to understand package signatures in RPM version 6 format. One feature
|
|
+of that format is that you can have multiple signatures on a single RPM
|
|
+package. If the package has no RPMv6 signature a signature in version 4 format
|
|
+will be verified instead. If there is no signature, the verification will be
|
|
+handled as failed.
|
|
+
|
|
+The dedicated rpmkeys(8) tool trusts public keys in a key store separate from
|
|
+the native RPM database because the native database might reject storing
|
|
+OpenPGP keys with an unsupported key schema which is foreseen to be used in
|
|
+RPMv6 signatures.
|
|
+
|
|
+Public keys missing from the separate key store are attempted to be imported
|
|
+from URLs listed in ``gpgkey`` configuration field of a repository the package
|
|
+belongs to. Before importing, a user is asked for a confirmation with the
|
|
+import, unless DNF was invoked with ``--assumeyes`` or ``--assumeno`` options.
|
|
+
|
|
+The key store can be inspected with ``/usr/lib/pqrpm/bin/rpmkeys -D --list``
|
|
+command. A key can be deleted from the key store with
|
|
+``/usr/lib/pqrpm/bin/rpmkeys -D --erase KEY_ID`` command. The ``KEY_ID`` is
|
|
+the first word in an output of the ``--list`` command.
|
|
+
|
|
+Users who do not wish to verify the extraordinary RPMv6 signatures should
|
|
+uninstall this plugin.
|
|
+
|
|
+-------------
|
|
+Configuration
|
|
+-------------
|
|
+
|
|
+Hard-coded path to the rpmkeys(8) tool is ``/usr/lib/pqrpm/bin/rpmkeys``.
|
|
+
|
|
+This plugin respects ``gpgkey`` and ``gpgcheck`` fields in a repository
|
|
+configuration. See dnf.conf(5) for more details.
|
|
+
|
|
+-----
|
|
+Files
|
|
+-----
|
|
+
|
|
+``/usr/lib/pqrpm/lib/sysimage/rpm``
|
|
+ A location of the key store defined by ``/usr/lib/pqrpm/bin/rpmkeys``
|
|
+ tool.
|
|
+
|
|
+--------
|
|
+See Also
|
|
+--------
|
|
+
|
|
+* :manpage:`dnf.conf(5)`
|
|
+* :manpage:`dnf(8)`
|
|
+* :manpage:`rpmkeys(8)`
|
|
+
|
|
diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt
|
|
index d004e5e..a84a786 100644
|
|
--- a/plugins/CMakeLists.txt
|
|
+++ b/plugins/CMakeLists.txt
|
|
@@ -14,6 +14,9 @@ endif()
|
|
if (${PYTHON_VERSION_MAJOR} STREQUAL "2")
|
|
INSTALL (FILES migrate.py DESTINATION ${PYTHON_INSTALL_DIR}/dnf-plugins)
|
|
endif()
|
|
+if (${PYTHON_VERSION_MAJOR} STREQUAL "3")
|
|
+INSTALL (FILES multisig.py DESTINATION ${PYTHON_INSTALL_DIR}/dnf-plugins)
|
|
+endif()
|
|
INSTALL (FILES needs_restarting.py DESTINATION ${PYTHON_INSTALL_DIR}/dnf-plugins)
|
|
INSTALL (FILES post-transaction-actions.py DESTINATION ${PYTHON_INSTALL_DIR}/dnf-plugins)
|
|
INSTALL (FILES repoclosure.py DESTINATION ${PYTHON_INSTALL_DIR}/dnf-plugins)
|
|
diff --git a/plugins/multisig.py b/plugins/multisig.py
|
|
new file mode 100644
|
|
index 0000000..8735a26
|
|
--- /dev/null
|
|
+++ b/plugins/multisig.py
|
|
@@ -0,0 +1,402 @@
|
|
+from __future__ import print_function, absolute_import, unicode_literals
|
|
+import dnf
|
|
+import dnf.crypto
|
|
+import dnf.dnssec
|
|
+import dnf.exceptions
|
|
+from dnf.i18n import ucd
|
|
+import dnf.rpm.transaction
|
|
+import dnf.transaction
|
|
+from dnfpluginscore import _, logger
|
|
+import os
|
|
+import subprocess
|
|
+import sys
|
|
+
|
|
+class MultiSig(dnf.Plugin):
|
|
+ """
|
|
+ This plugin verifies signatures of RPM packages by executing an
|
|
+ extraordinary "rpmkeys" tool. That tool can, for example, support multiple
|
|
+ RPM v6 signatures, or signature schemata uknown to the ordinary,
|
|
+ system-wide rpmkeys tool.
|
|
+
|
|
+ This verification is perfmored in addition to the standard verification
|
|
+ performed by DNF.
|
|
+ """
|
|
+
|
|
+ name = "multisig"
|
|
+
|
|
+ def __init__(self, base, cli):
|
|
+ super(MultiSig, self).__init__(base, cli)
|
|
+ # Path to the rpmkeys executable
|
|
+ self.rpmkeys_executable = "/usr/lib/pqrpm/bin/rpmkeys"
|
|
+ # List of repositories whose keys we have tried importing so far
|
|
+ # during a run of this plugin.
|
|
+ self._repo_set_imported_gpg_keys = [];
|
|
+
|
|
+ def pre_transaction(self):
|
|
+ inbound_packages = []
|
|
+ for ts_item in self.base.transaction:
|
|
+ if ts_item.action in dnf.transaction.FORWARD_ACTIONS:
|
|
+ inbound_packages.append(ts_item.pkg);
|
|
+ self.gpgsigcheck(inbound_packages)
|
|
+
|
|
+ def _process_rpm_output(self, data):
|
|
+ # No signatures or digests = corrupt package.
|
|
+ # There is at least one line for -: and another (empty) entry after the
|
|
+ # last newline.
|
|
+ if len(data) < 3 or data[0] != b'-:' or data[-1]:
|
|
+ return 2
|
|
+ seen_sig, missing_key, not_trusted, not_signed = False, False, False, False
|
|
+ for i in data[1:-1]:
|
|
+ if b': BAD' in i:
|
|
+ return 2
|
|
+ elif i.endswith(b': NOKEY'):
|
|
+ missing_key = True
|
|
+ elif i.endswith(b': NOTTRUSTED'):
|
|
+ not_trusted = True
|
|
+ elif i.endswith(b': NOTFOUND'):
|
|
+ not_signed = True
|
|
+ elif not i.endswith(b': OK'):
|
|
+ return 2
|
|
+ if not_trusted:
|
|
+ return 3
|
|
+ elif missing_key:
|
|
+ return 1
|
|
+ elif not_signed:
|
|
+ return 4
|
|
+ # we still check return code, so this is safe
|
|
+ return 0
|
|
+
|
|
+ def _verifyPackageUsingRpmkeys(self, package, installroot):
|
|
+ # "--define=_pkgverify_level signature" enforces signature checking;
|
|
+ # "--define=_pkgverify_flags 0x0" ensures that all signatures are checked.
|
|
+ args = (self.rpmkeys_executable,
|
|
+ '--checksig', '--root', installroot, '--verbose',
|
|
+ '--define=_pkgverify_level signature', '--define=_pkgverify_flags 0x0',
|
|
+ '-')
|
|
+ env = dict(os.environ)
|
|
+ env['LC_ALL'] = 'C'
|
|
+ with subprocess.Popen(
|
|
+ args=args,
|
|
+ executable=self.rpmkeys_executable,
|
|
+ env=env,
|
|
+ stdout=subprocess.PIPE,
|
|
+ cwd='/',
|
|
+ stdin=package) as p:
|
|
+ data = p.communicate()[0]
|
|
+ returncode = p.returncode
|
|
+ if type(returncode) is not int:
|
|
+ raise AssertionError('Popen set return code to non-int')
|
|
+ # rpmkeys can return something other than 0 or 1 in the case of a
|
|
+ # fatal error (OOM, abort() called, SIGSEGV, etc)
|
|
+ if returncode >= 2 or returncode < 0:
|
|
+ return 2
|
|
+ ret = self._process_rpm_output(data.split(b'\n'))
|
|
+ if ret:
|
|
+ return ret
|
|
+ return 2 if returncode else 0
|
|
+
|
|
+ def _checkSig(self, installroot, package):
|
|
+ """Takes a transaction set and a package, check it's sigs,
|
|
+ return 0 if they are all fine
|
|
+ return 1 if the gpg key can't be found
|
|
+ return 2 if the header is in someway damaged
|
|
+ return 3 if the key is not trusted
|
|
+ return 4 if the pkg is not gpg or pgp signed"""
|
|
+
|
|
+ fdno = os.open(package, os.O_RDONLY|os.O_NOCTTY|os.O_CLOEXEC)
|
|
+ try:
|
|
+ value = self._verifyPackageUsingRpmkeys(fdno, installroot)
|
|
+ finally:
|
|
+ os.close(fdno)
|
|
+ return value
|
|
+
|
|
+ def _sig_check_pkg(self, po):
|
|
+ """Verify the GPG signature of the given package object.
|
|
+
|
|
+ :param po: the package object to verify the signature of
|
|
+ :return: (result, error_string)
|
|
+ where result is::
|
|
+
|
|
+ 0 = GPG signature verifies ok or verification is not required.
|
|
+ 1 = GPG verification failed but installation of the right GPG key
|
|
+ might help.
|
|
+ 2 = Fatal GPG verification error, give up.
|
|
+ """
|
|
+ if po._from_cmdline:
|
|
+ check = self.base.conf.localpkg_gpgcheck
|
|
+ hasgpgkey = 0
|
|
+ else:
|
|
+ repo = self.base.repos[po.repoid]
|
|
+ check = repo.gpgcheck
|
|
+ hasgpgkey = not not repo.gpgkey
|
|
+
|
|
+ localfn = os.path.basename(po.localPkg())
|
|
+ if check:
|
|
+ logger.debug(_("Multisig: verifying: {}").format(po.localPkg()))
|
|
+ sigresult = self._checkSig(self.base.conf.installroot, po.localPkg())
|
|
+ if sigresult == 0:
|
|
+ result = 0
|
|
+ msg = _('All signatures for %s successfully verified') % localfn
|
|
+
|
|
+ elif sigresult == 1:
|
|
+ if hasgpgkey:
|
|
+ result = 1
|
|
+ else:
|
|
+ result = 2
|
|
+ msg = _('Public key for %s is not installed') % localfn
|
|
+
|
|
+ elif sigresult == 2:
|
|
+ result = 2
|
|
+ msg = _('Problem opening package %s') % localfn
|
|
+
|
|
+ elif sigresult == 3:
|
|
+ if hasgpgkey:
|
|
+ result = 1
|
|
+ else:
|
|
+ result = 2
|
|
+ result = 1
|
|
+ msg = _('Public key for %s is not trusted') % localfn
|
|
+
|
|
+ elif sigresult == 4:
|
|
+ result = 2
|
|
+ msg = _('Package %s is not signed') % localfn
|
|
+
|
|
+ else:
|
|
+ result = 0
|
|
+ msg = _('Signature verification for %s is disabled') % localfn
|
|
+
|
|
+ logger.debug(_("Multisig: verification result: {} (code={})").format(msg, result))
|
|
+ return result, msg
|
|
+
|
|
+ def keyInstalled(self, fingerprint):
|
|
+ '''
|
|
+ Return if the GPG key described by the given fingerprint is installed
|
|
+ in the multisig keyring.
|
|
+
|
|
+ Return values:
|
|
+ - True key is installed
|
|
+ - False otherwise
|
|
+ Trows: If rpmkeys program could not been executed.
|
|
+
|
|
+ No effort is made to handle duplicates.
|
|
+ '''
|
|
+ # XXX: rpmkeys expects lowercase
|
|
+ # <https://github.com/rpm-software-management/rpm/issues/3721>
|
|
+ logger.debug(_("Multisig: Checking a presence of key={}").format(fingerprint))
|
|
+ args = (self.rpmkeys_executable,
|
|
+ '--root', self.base.conf.installroot,
|
|
+ '--list', fingerprint.lower())
|
|
+ p = subprocess.run(
|
|
+ args=args,
|
|
+ executable=self.rpmkeys_executable,
|
|
+ cwd='/',
|
|
+ stdin=subprocess.DEVNULL,
|
|
+ stdout=subprocess.DEVNULL,
|
|
+ stderr=subprocess.DEVNULL)
|
|
+ return p.returncode == 0
|
|
+
|
|
+ def importKey(self, key):
|
|
+ '''
|
|
+ Import given Key object into the multisig keyring.
|
|
+
|
|
+ Return values:
|
|
+ - True key imported successfully
|
|
+ - False otherwise
|
|
+ Trows: If rpmkeys program could not been executed.
|
|
+
|
|
+ What happens if a key's raw_string contains multiple public key
|
|
+ packets, or if the key was already in the keyring is unspecified and
|
|
+ it depends on rpmkeys behavior. Current rpmkeys implementation
|
|
+ gracefully ignores (or updates?) existing keys.
|
|
+ '''
|
|
+ args = (self.rpmkeys_executable,
|
|
+ '--root', self.base.conf.installroot,
|
|
+ '--import', '-')
|
|
+ env = dict(os.environ)
|
|
+ env['LC_ALL'] = 'C'
|
|
+ with subprocess.Popen(
|
|
+ executable=self.rpmkeys_executable,
|
|
+ args=args,
|
|
+ env=env,
|
|
+ cwd='/',
|
|
+ # XXX: rpmkeys used to fail reading from a pipe. Fix at
|
|
+ # <https://github.com/rpm-software-management/rpm/pull/3706>.
|
|
+ stdin=subprocess.PIPE,
|
|
+ stdout=subprocess.PIPE,
|
|
+ stderr=subprocess.PIPE) as p:
|
|
+ stdout, stderr = p.communicate(input=key.raw_key)
|
|
+ returncode = p.returncode
|
|
+ if type(returncode) is not int:
|
|
+ raise AssertionError('Popen set return code to non-int')
|
|
+ logger.debug(_("Multisig: Key import result: exitcode={}, stdout={}, stderr={}").format(
|
|
+ returncode, stdout, stderr))
|
|
+ return returncode == 0
|
|
+
|
|
+ def _get_key_for_package(self, po, askcb=None, fullaskcb=None):
|
|
+ """Retrieve a key for a package. If needed, use the given
|
|
+ callback to prompt whether the key should be imported.
|
|
+
|
|
+ :param po: the package object to retrieve the key of
|
|
+ :param askcb: Callback function to use to ask permission to
|
|
+ import a key. The arguments *askcb* should take are the
|
|
+ package object, the userid of the key, and the keyid
|
|
+ :param fullaskcb: Callback function to use to ask permission to
|
|
+ import a key. This differs from *askcb* in that it gets
|
|
+ passed a dictionary so that we can expand the values passed.
|
|
+ :raises: :class:`dnf.exceptions.Error` if there are errors
|
|
+ retrieving the keys
|
|
+ """
|
|
+ if po._from_cmdline:
|
|
+ # raise an exception, because po.repoid is not in self.repos
|
|
+ msg = _('Unable to retrieve a key for a commandline package: %s')
|
|
+ raise ValueError(msg % po)
|
|
+
|
|
+ repo = self.base.repos[po.repoid]
|
|
+ key_installed = repo.id in self._repo_set_imported_gpg_keys
|
|
+ keyurls = [] if key_installed else repo.gpgkey
|
|
+
|
|
+ def _prov_key_data(msg):
|
|
+ msg += _('. Failing package is: %s') % (po) + '\n '
|
|
+ msg += _('GPG Keys are configured as: %s') % \
|
|
+ (', '.join(repo.gpgkey))
|
|
+ return msg
|
|
+
|
|
+ user_cb_fail = False
|
|
+ self._repo_set_imported_gpg_keys.append(repo.id)
|
|
+ for keyurl in keyurls:
|
|
+ keys = dnf.crypto.retrieve(keyurl, repo)
|
|
+
|
|
+ for info in keys:
|
|
+ # Check if key is already installed
|
|
+ if self.keyInstalled(info.fingerprint):
|
|
+ msg = _('GPG key at %s (0x%s) is already installed')
|
|
+ logger.info(msg, keyurl, info.short_id)
|
|
+ continue
|
|
+
|
|
+ # DNS Extension: create a key object, pass it to the verification class
|
|
+ # and print its result as an advice to the user.
|
|
+ if self.base.conf.gpgkey_dns_verification:
|
|
+ dns_input_key = dnf.dnssec.KeyInfo.from_rpm_key_object(info.userid,
|
|
+ info.raw_key)
|
|
+ dns_result = dnf.dnssec.DNSSECKeyVerification.verify(dns_input_key)
|
|
+ logger.info(dnf.dnssec.nice_user_msg(dns_input_key, dns_result))
|
|
+
|
|
+ # Try installing/updating GPG key
|
|
+ info.url = keyurl
|
|
+ if self.base.conf.gpgkey_dns_verification:
|
|
+ dnf.crypto.log_dns_key_import(info, dns_result)
|
|
+ else:
|
|
+ dnf.crypto.log_key_import(info)
|
|
+ rc = False
|
|
+ if self.base.conf.assumeno:
|
|
+ rc = False
|
|
+ elif self.base.conf.assumeyes:
|
|
+ # DNS Extension: We assume, that the key is trusted in case it is valid,
|
|
+ # its existence is explicitly denied or in case the domain is not signed
|
|
+ # and therefore there is no way to know for sure (this is mainly for
|
|
+ # backward compatibility)
|
|
+ # FAQ:
|
|
+ # * What is PROVEN_NONEXISTENCE?
|
|
+ # In DNSSEC, your domain does not need to be signed, but this state
|
|
+ # (not signed) has to be proven by the upper domain. e.g. when example.com.
|
|
+ # is not signed, com. servers have to sign the message, that example.com.
|
|
+ # does not have any signing key (KSK to be more precise).
|
|
+ if self.base.conf.gpgkey_dns_verification:
|
|
+ if dns_result in (dnf.dnssec.Validity.VALID,
|
|
+ dnf.dnssec.Validity.PROVEN_NONEXISTENCE):
|
|
+ rc = True
|
|
+ logger.info(dnf.dnssec.any_msg(_("The key has been approved.")))
|
|
+ else:
|
|
+ rc = False
|
|
+ logger.info(dnf.dnssec.any_msg(_("The key has been rejected.")))
|
|
+ else:
|
|
+ rc = True
|
|
+
|
|
+ # grab the .sig/.asc for the keyurl, if it exists if it
|
|
+ # does check the signature on the key if it is signed by
|
|
+ # one of our ca-keys for this repo or the global one then
|
|
+ # rc = True else ask as normal.
|
|
+
|
|
+ elif fullaskcb:
|
|
+ rc = fullaskcb({"po": po, "userid": info.userid,
|
|
+ "hexkeyid": info.short_id,
|
|
+ "keyurl": keyurl,
|
|
+ "fingerprint": info.fingerprint,
|
|
+ "timestamp": info.timestamp})
|
|
+ elif askcb:
|
|
+ rc = askcb(po, info.userid, info.short_id)
|
|
+
|
|
+ if not rc:
|
|
+ user_cb_fail = True
|
|
+ continue
|
|
+
|
|
+ # Import the key
|
|
+ # XXX: raw_key of second info erroneously contains first and
|
|
+ # second key. Probably a bug in key parser.
|
|
+ #logger.debug(_("Multisig: Importing a key: {}").format(info.raw_key))
|
|
+ result = self.importKey(info)
|
|
+ if result == False:
|
|
+ msg = _('Key import failed')
|
|
+ raise dnf.exceptions.Error(_prov_key_data(msg))
|
|
+ logger.info(_('Key imported successfully'))
|
|
+ key_installed = True
|
|
+
|
|
+ if not key_installed and user_cb_fail:
|
|
+ raise dnf.exceptions.Error(_("Didn't install any keys"))
|
|
+
|
|
+ if not key_installed:
|
|
+ msg = _('The GPG keys listed for the "%s" repository are '
|
|
+ 'already installed but they are not correct for this '
|
|
+ 'package.\n'
|
|
+ 'Check that the correct key URLs are configured for '
|
|
+ 'this repository.') % repo.name
|
|
+ raise dnf.exceptions.Error(_prov_key_data(msg))
|
|
+
|
|
+ # Check if the newly installed keys helped
|
|
+ result, errmsg = self._sig_check_pkg(po)
|
|
+ if result != 0:
|
|
+ if keyurls:
|
|
+ msg = _("Import of key(s) didn't help, wrong key(s)?")
|
|
+ logger.info(msg)
|
|
+ errmsg = ucd(errmsg)
|
|
+ raise dnf.exceptions.Error(_prov_key_data(errmsg))
|
|
+
|
|
+ def gpgsigcheck(self, pkgs):
|
|
+ """Perform GPG signature verification on the given packages,
|
|
+ installing keys if possible.
|
|
+
|
|
+ :param pkgs: a list of package objects to verify the GPG
|
|
+ signatures of
|
|
+ :raises: Will raise :class:`Error` if there's a problem
|
|
+ """
|
|
+ error_messages = []
|
|
+ for po in pkgs:
|
|
+ result, errmsg = self._sig_check_pkg(po)
|
|
+
|
|
+ if result == 0:
|
|
+ # Verified ok, or verify not req'd
|
|
+ continue
|
|
+
|
|
+ elif result == 1:
|
|
+ ay = self.base.conf.assumeyes and not self.base.conf.assumeno
|
|
+ if (not sys.stdin or not sys.stdin.isatty()) and not ay:
|
|
+ raise dnf.exceptions.Error(_('Refusing to automatically import keys when running ' \
|
|
+ 'unattended.\nUse "-y" to override.'))
|
|
+
|
|
+ # the callback here expects to be able to take options which
|
|
+ # userconfirm really doesn't... so fake it
|
|
+ fn = lambda x, y, z: self.base.output.userconfirm()
|
|
+ try:
|
|
+ self._get_key_for_package(po, fn)
|
|
+ except (dnf.exceptions.Error, ValueError) as e:
|
|
+ error_messages.append(str(e))
|
|
+
|
|
+ else:
|
|
+ # Fatal error
|
|
+ error_messages.append(errmsg)
|
|
+
|
|
+ if error_messages:
|
|
+ for msg in error_messages:
|
|
+ logger.critical(msg)
|
|
+ raise dnf.exceptions.Error(_("GPG check FAILED"))
|
|
+
|
|
--
|
|
2.50.1
|
|
|