From 69e0407f66388b9ca2cffc4c35fa0ed1a12ce96f Mon Sep 17 00:00:00 2001 From: Daniel Wymark Date: Fri, 12 Nov 2021 22:42:14 -0800 Subject: [PATCH 1/5] Allow auto import only for specific key Implemented a new command option "--gpg-auto-import-key-id" which allows users to only auto-import a GPG key with the provided key ID. --- src/Config.cc | 13 +++++++++++++ src/Config.h | 1 + src/callbacks/keyring.h | 5 +++-- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/Config.cc b/src/Config.cc index aabaf98690..96c5391486 100644 --- a/src/Config.cc +++ b/src/Config.cc @@ -297,6 +297,7 @@ Config::Config() , reboot_req_non_interactive( false ) , no_gpg_checks( false ) , gpg_auto_import_keys( false ) + , gpg_auto_import_key_id( "" ) , machine_readable( false ) , no_refresh( false ) , no_cd( false ) @@ -570,6 +571,18 @@ std::vector Config::cliOptions() // translators: --gpg-auto-import-keys _("Automatically trust and import new repository signing keys.") }, + { "gpg-auto-import-key-id", 0, ZyppFlags::RequiredArgument, std::move( ZyppFlags::StringType( &gpg_auto_import_key_id, "", "KEY_ID"). + after( [](const ZyppFlags::CommandOption &, const boost::optional & key_id) { + std::string warn = str::form( + _("Turning on '%s' for key id '%s'. The specified signing key will be automatically imported if present!"), + "--gpg-auto-import-key-id", + key_id->c_str()); + Zypper::instance().out().warning( warn, Out::HIGH ); + MIL << "gpg-auto-import-key-id is on for " << *key_id << endl; + })), + // translators: --gpg-auto-import-key-id + _("Automatically trust and import the specified repository signing key, if it is present.") + }, { "plus-repo", 'p', ZyppFlags::Repeatable | ZyppFlags::RequiredArgument, ZyppFlags::GenericContainerType( plusRepoFromCLI, ARG_URI ), // translators: --plus-repo, -p _("Use an additional repository.") diff --git a/src/Config.h b/src/Config.h index 6696fc1e24..be167674e5 100644 --- a/src/Config.h +++ b/src/Config.h @@ -95,6 +95,7 @@ struct Config bool reboot_req_non_interactive; bool no_gpg_checks; bool gpg_auto_import_keys; + std::string gpg_auto_import_key_id; bool machine_readable; /** Whether to disable autorefresh. */ bool no_refresh; diff --git a/src/callbacks/keyring.h b/src/callbacks/keyring.h index 36157b6ab6..415c6e86df 100644 --- a/src/callbacks/keyring.h +++ b/src/callbacks/keyring.h @@ -273,8 +273,9 @@ namespace zypp // gpg key info dumpKeyInfo( s << std::endl, key_r, context_r ) << std::endl; - // if --gpg-auto-import-keys or --no-gpg-checks print info and don't ask - if (_gopts.gpg_auto_import_keys) + // if --gpg-auto-import-keys, --no-gpg-check, or --gpg-auto-import-key-id matches key, print info and don't ask. + if (_gopts.gpg_auto_import_keys + || (!_gopts.gpg_auto_import_key_id.empty() && _gopts.gpg_auto_import_key_id == key_r.id())) { MIL << "Automatically importing key " << key_r << std::endl; zypper.out().info(s.str()); From e21b6e9ac136b734a78d16413cc9a31ed204fc98 Mon Sep 17 00:00:00 2001 From: Daniel Wymark Date: Sat, 20 Nov 2021 11:58:56 -0800 Subject: [PATCH 2/5] Allow default value to be specified for GenericContainerType --- src/utils/flags/flagtypes.h | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/utils/flags/flagtypes.h b/src/utils/flags/flagtypes.h index a799fe5526..a8eaf39c9e 100644 --- a/src/utils/flags/flagtypes.h +++ b/src/utils/flags/flagtypes.h @@ -112,9 +112,16 @@ template <> int argValueConvert ( const CommandOption &, const boost::optional &in ); template class Container, typename T > -Value GenericContainerType ( Container &target_r, std::string hint = std::string(), const std::string sep = "" ) { +Value GenericContainerType ( Container &target_r, std::string hint = std::string(), const std::string &sep = "", const std::string &defaultVal = std::string() ) { + DefValueFun defValueFun; + if (!defaultVal.empty()) { + defValueFun = [defaultVal]() { return boost::optional(defaultVal); }; + } + else { + defValueFun = noDefaultValue; + } return Value ( - noDefaultValue, + std::move( defValueFun ), [ &target_r, sep ] ( const CommandOption &opt, const boost::optional &in ) { if ( !in ) ZYPP_THROW(MissingArgumentException(opt.name)); //value required From 180266c95532ac9c02a57c3a235686b06e3e1677 Mon Sep 17 00:00:00 2001 From: Daniel Wymark Date: Sat, 20 Nov 2021 12:07:43 -0800 Subject: [PATCH 3/5] Make --gpg-auto-import-key-id accept optional list of key IDs --- src/Config.cc | 53 ++++++++++++++++++++++--------------- src/Config.h | 3 +-- src/callbacks/keyring.h | 23 +++++++++++----- src/utils/flags/zyppflags.h | 2 ++ 4 files changed, 51 insertions(+), 30 deletions(-) diff --git a/src/Config.cc b/src/Config.cc index 96c5391486..8cdf4d01cb 100644 --- a/src/Config.cc +++ b/src/Config.cc @@ -10,6 +10,7 @@ extern "C" #include } #include +#include #include #include @@ -296,8 +297,6 @@ Config::Config() , non_interactive( false ) , reboot_req_non_interactive( false ) , no_gpg_checks( false ) - , gpg_auto_import_keys( false ) - , gpg_auto_import_key_id( "" ) , machine_readable( false ) , no_refresh( false ) , no_cd( false ) @@ -560,28 +559,38 @@ std::vector Config::cliOptions() // translators: --no-gpg-checks _("Ignore GPG check failures and continue.") }, - { "gpg-auto-import-keys", 0, ZyppFlags::NoArgument, std::move( ZyppFlags::BoolType( &gpg_auto_import_keys, ZyppFlags::StoreTrue ). - after( []() { - std::string warn = str::form( - _("Turning on '%s'. New repository signing keys will be automatically imported!"), - "--gpg-auto-import-keys"); - Zypper::instance().out().warning( warn, Out::HIGH ); - MIL << "gpg-auto-import-keys is on" << endl; + { "gpg-auto-import-keys", 0, ZyppFlags::OptionalArgument | ZyppFlags::Repeatable, std::move( ZyppFlags::GenericContainerType( gpg_auto_import_keys, ARG_KEY_ID, ",", "*" ). + after( [this](const ZyppFlags::CommandOption &, const boost::optional & arg) { + if (arg.has_value()) { + std::vector key_ids; + boost::split(key_ids, *arg, boost::is_any_of(",")); + for (const std::string& key_id : key_ids) { + std::string warn = str::form( + _("Turning on '%s' for key id '%s'. This signing key will be automatically imported if present!"), + "--gpg-auto-import-keys", + key_id.c_str()); + Zypper::instance().out().warning( warn, Out::HIGH ); + + if (!PublicKeyData::isSafeKeyId(key_id)) { + warn = str::form( + _("Key id '%s' is not a secure key ID."), + key_id.c_str()); + Zypper::instance().out().warning( warn, Out::NORMAL ); + } + + MIL << "gpg-auto-import-keys is on for " << key_id << endl; + } + } + else { + std::string warn = str::form( + _("Turning on '%s'. New repository signing keys will be automatically imported!"), + "--gpg-auto-import-keys"); + Zypper::instance().out().warning( warn, Out::HIGH ); + MIL << "gpg-auto-import-keys is on" << endl; + } })), // translators: --gpg-auto-import-keys - _("Automatically trust and import new repository signing keys.") - }, - { "gpg-auto-import-key-id", 0, ZyppFlags::RequiredArgument, std::move( ZyppFlags::StringType( &gpg_auto_import_key_id, "", "KEY_ID"). - after( [](const ZyppFlags::CommandOption &, const boost::optional & key_id) { - std::string warn = str::form( - _("Turning on '%s' for key id '%s'. The specified signing key will be automatically imported if present!"), - "--gpg-auto-import-key-id", - key_id->c_str()); - Zypper::instance().out().warning( warn, Out::HIGH ); - MIL << "gpg-auto-import-key-id is on for " << *key_id << endl; - })), - // translators: --gpg-auto-import-key-id - _("Automatically trust and import the specified repository signing key, if it is present.") + _("Automatically trust and import new repository signing keys. If one or more key IDs are provided, only those keys will be allowed to be trusted and imported. Note that 8-byte short IDs are considered insecure, but will be accepted for convenience.") }, { "plus-repo", 'p', ZyppFlags::Repeatable | ZyppFlags::RequiredArgument, ZyppFlags::GenericContainerType( plusRepoFromCLI, ARG_URI ), // translators: --plus-repo, -p diff --git a/src/Config.h b/src/Config.h index be167674e5..545c71e9ed 100644 --- a/src/Config.h +++ b/src/Config.h @@ -94,8 +94,7 @@ struct Config bool non_interactive; bool reboot_req_non_interactive; bool no_gpg_checks; - bool gpg_auto_import_keys; - std::string gpg_auto_import_key_id; + std::vector gpg_auto_import_keys; bool machine_readable; /** Whether to disable autorefresh. */ bool no_refresh; diff --git a/src/callbacks/keyring.h b/src/callbacks/keyring.h index 415c6e86df..8ae0c51035 100644 --- a/src/callbacks/keyring.h +++ b/src/callbacks/keyring.h @@ -261,11 +261,23 @@ namespace zypp { Zypper & zypper = Zypper::instance(); + // if --gpg-auto-import-keys, "*" is in _gopts.gpg_auto_import_keys. In this case, allow any key. + bool autoImportKey = std::find(_gopts.gpg_auto_import_keys.begin(), _gopts.gpg_auto_import_keys.end(), "*") != _gopts.gpg_auto_import_keys.end(); + + // if --gpg-auto-import-keys , acceptable key IDs are in _gopts.gpg_auto_import_keys. + if (!autoImportKey) { + autoImportKey = std::any_of(_gopts.gpg_auto_import_keys.begin(), _gopts.gpg_auto_import_keys.end(), [&key_r](const std::string& keyId) { + return key_r.providesKey(keyId); + }); + } + + bool autoTrustKey = _gopts.no_gpg_checks && canTrustTemporarily_r; + std::ostringstream s; s << std::endl; - if (_gopts.gpg_auto_import_keys) + if (autoImportKey) s << _("Automatically importing the following key:") << std::endl; - else if ( _gopts.no_gpg_checks && canTrustTemporarily_r ) + else if (autoTrustKey) s << _("Automatically trusting the following key:") << std::endl; else s << _("New repository or package signing key received:") << std::endl; @@ -273,16 +285,15 @@ namespace zypp // gpg key info dumpKeyInfo( s << std::endl, key_r, context_r ) << std::endl; - // if --gpg-auto-import-keys, --no-gpg-check, or --gpg-auto-import-key-id matches key, print info and don't ask. - if (_gopts.gpg_auto_import_keys - || (!_gopts.gpg_auto_import_key_id.empty() && _gopts.gpg_auto_import_key_id == key_r.id())) + // if --no-gpg-check enabled or --gpg-auto-import-keys enabled and applicable, print info and don't ask. + if (autoImportKey) { MIL << "Automatically importing key " << key_r << std::endl; zypper.out().info(s.str()); hintFingerprint(); return KeyRingReport::KEY_TRUST_AND_IMPORT; } - else if (_gopts.no_gpg_checks && canTrustTemporarily_r ) + else if (autoTrustKey) { MIL << "Automatically trusting key " << key_r << std::endl; zypper.out().info(s.str()); diff --git a/src/utils/flags/zyppflags.h b/src/utils/flags/zyppflags.h index b8eeb93542..f6739a03dc 100644 --- a/src/utils/flags/zyppflags.h +++ b/src/utils/flags/zyppflags.h @@ -48,6 +48,8 @@ // translator: Option argument like '--export '. Do do not translate lowercase wordparts #define ARG_URI _( "URI" ) // translator: Option argument like '--export '. Do do not translate lowercase wordparts +#define ARG_KEY_ID _( "KEY_ID" ) +// translator: Option argument like '--export '. Do do not translate lowercase wordparts #define ARG_YYYY_MM_DD _( "YYYY-MM-DD" ) // translator: Option argument like '--export '. Do do not translate lowercase wordparts #define ARG_MODE _( "MODE" ) From f06fd0aa2a0ae313b457f8931ba251c071f69c6e Mon Sep 17 00:00:00 2001 From: Daniel Wymark Date: Fri, 26 Nov 2021 19:23:11 -0800 Subject: [PATCH 4/5] Use splitRx rather than boost::split This helps to keep the code style consistent with the rest of the code base. --- src/Config.cc | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Config.cc b/src/Config.cc index 8cdf4d01cb..a2bc3db680 100644 --- a/src/Config.cc +++ b/src/Config.cc @@ -10,11 +10,11 @@ extern "C" #include } #include -#include #include #include #include +#include #include #include @@ -560,10 +560,14 @@ std::vector Config::cliOptions() _("Ignore GPG check failures and continue.") }, { "gpg-auto-import-keys", 0, ZyppFlags::OptionalArgument | ZyppFlags::Repeatable, std::move( ZyppFlags::GenericContainerType( gpg_auto_import_keys, ARG_KEY_ID, ",", "*" ). - after( [this](const ZyppFlags::CommandOption &, const boost::optional & arg) { + after( [](const ZyppFlags::CommandOption &, const boost::optional & arg) { if (arg.has_value()) { std::vector key_ids; - boost::split(key_ids, *arg, boost::is_any_of(",")); + strv::splitRx(*arg, ",", [&key_ids]( std::string_view key_id ) { + if ( ! key_id.empty() ) + key_ids.push_back( std::string(key_id) ); + }); + for (const std::string& key_id : key_ids) { std::string warn = str::form( _("Turning on '%s' for key id '%s'. This signing key will be automatically imported if present!"), From 7b387875f2c5276fd3a17fc557baed7bbdf1816c Mon Sep 17 00:00:00 2001 From: Daniel Wymark Date: Fri, 26 Nov 2021 19:30:08 -0800 Subject: [PATCH 5/5] Update man page entry for --gpg-auto-import-keys --- doc/zypper.8.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/zypper.8.txt b/doc/zypper.8.txt index 47fbd2a2b8..0bdb660122 100644 --- a/doc/zypper.8.txt +++ b/doc/zypper.8.txt @@ -1787,8 +1787,8 @@ Repository Options: :: {nop} *--no-gpg-checks*:: Ignore GPG check failures and continue. If a GPG issue occurs when using this option zypper prints and logs a warning and automatically continues without interrupting the operation. Use this option with caution, as you can easily overlook security problems by using it. (see section *GPG checks*) -*--gpg-auto-import-keys*:: - If new repository signing key is found, do not ask what to do; trust and import it automatically. This option causes that the new key is imported also in non-interactive mode, where it would otherwise got rejected. +*--gpg-auto-import-keys* _KEY_ID_:: + If the specified repository signing key is found, do not ask what to do; trust and import it automatically. This option causes that the new key is imported also in non-interactive mode, where it would otherwise got rejected. The KEY_ID argument is optional; when left unspecified, any repository signing key which is found will be trusted and imported automatically. You can specify this option multiple times. *-p*, *--plus-repo* _URI_:: Use an additional repository for this operation. The repository aliased tmp# and named by the specified URI will be added for this operation and removed at the end. You can specify this option multiple times.