diff --git a/doc/zypper.8.txt b/doc/zypper.8.txt index 772f9308d7..0723c5ffed 100644 --- a/doc/zypper.8.txt +++ b/doc/zypper.8.txt @@ -1423,7 +1423,7 @@ Package locks serve the purpose of preventing changes to the set of installed pa Key Management ~~~~~~~~~~~~~~ -The *keys* command lists keys from the internal trust database. +The *keys*, *addkey*, and *removekey* commands serve for manipulating the trusted key database. A key is specified by its ID, which is the last 16 characters of its fingerprint. *keys* (*lk*) ['options'] ['key-id'] ['key-filename'] ...:: List all trusted keys or show detailed information about those specified as arguments, supports also keyfiles as argument. @@ -1433,6 +1433,13 @@ The *keys* command lists keys from the internal trust database. Shows the keys in a more detailed multiline output, also shows subkey information -- +*addkey* (*ak*) ['options'] 'url'...:: + Imports a new key into the trusted database, the file can be specified as a URL. + +*removekey* (*rk*) ['options'] 'key-id' :: + Remove specified key from the trusted database. + + Other Commands ~~~~~~~~~~~~~~ *versioncmp* (*vcmp*) 'version1' 'version2':: diff --git a/src/Command.cc b/src/Command.cc index c5093c4b89..f5215b2825 100644 --- a/src/Command.cc +++ b/src/Command.cc @@ -104,7 +104,9 @@ namespace _t( RUG_PATCH_SEARCH_e ) | "patch-search" | "pse"; _t( RUG_PING_e ) | "ping"; - _t( KEYS_e ) | "keys" | "lk"; + _t( KEYS_e ) | "keys" | "lk"; + _t( ADDKEY_e ) | "addkey" | "ak"; + _t( REMOVEKEY_e ) | "removekey" | "rk"; #undef _t } return _table; diff --git a/src/Command.h b/src/Command.h index 3c11774de8..e9b40a64de 100644 --- a/src/Command.h +++ b/src/Command.h @@ -173,6 +173,8 @@ struct ZypperCommand RUG_PING_e, KEYS_e, + ADDKEY_e, + REMOVEKEY_e }; ZypperCommand( Command command ) : _command(command) {} diff --git a/src/Zypper.cc b/src/Zypper.cc index ac68b80f98..a71d49d5d3 100644 --- a/src/Zypper.cc +++ b/src/Zypper.cc @@ -791,8 +791,8 @@ void print_main_help( Zypper & zypper ) static std::string help_keys_commands = _(" Key Management:\n" "\tkeys, lk\t\tList all keys.\n" - //"\taddkey, as\t\tAdd a new key.\n" - //"\tremovekey, rs\tRemove a key from trust.\n" + "\taddkey, as\t\tAdd a new key to trust.\n" + "\tremovekey, rs\tRemove a key from trust.\n" ); zypper.out().info( help_usage, Out::QUIET ); @@ -3494,6 +3494,43 @@ void Zypper::processCommandOptions() break; } + case ZypperCommand::ADDKEY_e: + { + static struct option options[] = { + {"help", no_argument, 0, 'h'}, + {0, 0, 0, 0} + }; + specific_options = options; + + _command_help = CommandHelpFormater() + .synopsis( // translators: command synopsis; do not translate lowercase words + _("addkey ") + ) + .description(// translators: command description + _("Import all keys from a keyfile specified by url.") + ) + ; + break; + } + + case ZypperCommand::REMOVEKEY_e: + { + static struct option options[] = { + {"help", no_argument, 0, 'h'}, + {0, 0, 0, 0} + }; + specific_options = options; + + _command_help = CommandHelpFormater() + .synopsis( // translators: command synopsis; do not translate lowercase words + _("removekey ") + ) + .description(// translators: command description + _("Remove key specified by ID from trust.") + ); + break; + } + default: { if ( runningHelp() ) @@ -5585,6 +5622,28 @@ void Zypper::doCommand() break; } + case ZypperCommand::ADDKEY_e: + { + // load system data... + init_target( *this ); + if ( exitCode() != ZYPPER_EXIT_OK ) + return; + + importKey( *this, arguments().front() ); + break; + } + + case ZypperCommand::REMOVEKEY_e: + { + // load system data... + init_target( *this ); + if ( exitCode() != ZYPPER_EXIT_OK ) + return; + + removeKey( *this, arguments().front() ); + break; + } + case ZypperCommand::SUBCOMMAND_e: // subcommands are not expected to be executed here! default: // if the program reaches this line, something went wrong diff --git a/src/keys.cc b/src/keys.cc index ac26ca99e9..f07768b674 100644 --- a/src/keys.cc +++ b/src/keys.cc @@ -6,13 +6,15 @@ \*---------------------------------------------------------------------------*/ #include "Zypper.h" -#include "Table.h" +#include "utils/prompt.h" #include "utils/misc.h" #include #include #include +#include +#include extern ZYpp::Ptr God; @@ -101,19 +103,121 @@ void listTrustedKeys ( Zypper &zypp_r ) } } -#if 0 -void removeKey(Zypper &zypp_r ) +void removeKey ( Zypper &zypp_r, const std::string &id ) { + if ( !God || !God->keyRing() ) + return; -} + KeyRing_Ptr keyRing = God->keyRing(); -void exportKey( Zypper &zypp_r ) -{ + bool found = false; + for ( const PublicKeyData &key : keyRing->trustedPublicKeyData() ) + { + if ( key.providesKey(id) ) + { + found = true; + + std::stringstream keyInfo; + dumpKeyInfo ( keyInfo, key ); + std::string question = str::Format( + "About to delete key: \n" + "%1%\n" + "Do you want to continue?\n" + ) % keyInfo.str(); + + if ( read_bool_answer(PROMPT_YN_GPG_UNKNOWN_KEY_ACCEPT, question, false) ) + { + try + { + keyRing->deleteKey( key.id(), true ); + } catch ( const Exception & e ) { + ZYPP_CAUGHT( e ); + zypp_r.out().error( e, str::Format(_("Failed to delete key: %1%")) % id ); + zypp_r.setExitCode( ZYPPER_EXIT_ERR_ZYPP ); + return; + } + } + break; + } + } + if ( !found ) + { + zypp_r.out().warning( str::Format(_("Key %1% is not known")) % id ); + zypp_r.setExitCode( ZYPPER_EXIT_ERR_INVALID_ARGS ); + return; + } } -void checkKey( Zypper &zypp_r ) +void importKey ( Zypper &zypp_r, const std::string &url_r ) { + if ( !God || !God->keyRing() ) + return; + Url url = make_url( url_r ); + if ( !url.isValid() ) + { + zypp_r.setExitCode( ZYPPER_EXIT_ERR_INVALID_ARGS ); + return; + } + + //try to get a file from the URL + Pathname local; + try + { local = MediaSetAccess::provideFileFromUrl(url); } + catch ( const media::MediaException & e ) + { + ZYPP_CAUGHT( e ); + zypp_r.out().error( e, str::Format(_("Problem accessing the file at the specified URI: %1%")) % url, + _("Please check if the URI is valid and accessible.") ); + zypp_r.setExitCode( ZYPPER_EXIT_ERR_ZYPP ); + return; + } + catch ( const Exception & e ) + { + ZYPP_CAUGHT( e ); + zypp_r.out().error( e, str::Format(_("Problem encountered while trying to read the file at the specified URI %1%")) % url ); + zypp_r.setExitCode( ZYPPER_EXIT_ERR_ZYPP ); + return; + } + + PublicKey pKey; + try + { + pKey = PublicKey(local); + } catch ( const Exception & e ) { + ZYPP_CAUGHT( e ); + zypp_r.out().error( e, str::Format(_("Problem encountered while parsing the file at the specified URI %1%")) % url ); + zypp_r.setExitCode( ZYPPER_EXIT_ERR_ZYPP ); + return; + } + + //first import all the keys in keyfile into the general keyring, we do not trust yet + KeyRing_Ptr keyRing = God->keyRing(); + keyRing->importKey( pKey, false); + + //now lets go over all imported keys and ask explicitely if the user wants to trust them + for ( const PublicKey &key : keyRing->publicKeys() ) + { + std::stringstream keyInfo; + dumpKeyInfo ( keyInfo, key ); + std::string question = str::Format( + "About to import key: \n" + "%1%\n" + "Do you want to continue?\n" + ) % keyInfo.str(); + + if ( read_bool_answer(PROMPT_YN_GPG_UNKNOWN_KEY_ACCEPT, question, false) ) + { + try + { + keyRing->importKey( keyRing->exportPublicKey(key.keyData()), true ); + } catch ( const Exception & e ) { + ZYPP_CAUGHT( e ); + zypp_r.out().error( e, str::Format(_("Failed to import key from URL: %1%")) % url ); + zypp_r.setExitCode( ZYPPER_EXIT_ERR_ZYPP ); + return; + } + } + } } -#endif diff --git a/src/keys.h b/src/keys.h index efec2e2b1a..7938a65c97 100644 --- a/src/keys.h +++ b/src/keys.h @@ -13,11 +13,7 @@ class Zypper; void listTrustedKeys ( Zypper &zypp_r ); -#if 0 -void removeKey ( Zypper &zypp_r ); -void exportKey ( Zypper &zypp_r ); -void checkKey ( Zypper &zypp_r ); -#endif - +void importKey ( Zypper &zypp_r, const std::string &url); +void removeKey ( Zypper &zypp_r, const std::string &id ); #endif diff --git a/src/utils/misc.cc b/src/utils/misc.cc index 07b458d53e..2da5a51a9e 100644 --- a/src/utils/misc.cc +++ b/src/utils/misc.cc @@ -778,6 +778,7 @@ std::ostream &dumpKeyInfo(std::ostream &str, const PublicKeyData &key, const Key { dumpAsXmlOn( *parent, context.repoInfo().asUserString(), "repository" ); } + dumpAsXmlOn( *parent, key.id(), "key-id" ); dumpAsXmlOn( *parent, key.name(), "key-name" ); dumpAsXmlOn( *parent, key.fingerprint(), "key-fingerprint" ); dumpAsXmlOn( *parent, key.created(), "key-created" ); @@ -793,7 +794,8 @@ std::ostream &dumpKeyInfo(std::ostream &str, const PublicKeyData &key, const Key { t << ( TableRow() << "" << _("Repository:") << context.repoInfo().asUserString() ); } - t << ( TableRow() << "" << _("Key Name:") << key.name() ) + t << ( TableRow() << "" << _("Key ID:") << key.id() ) + << ( TableRow() << "" << _("Key Name:") << key.name() ) << ( TableRow() << "" << _("Key Fingerprint:") << str::gapify( key.fingerprint(), 8 ) ) << ( TableRow() << "" << _("Key Created:") << key.created() ) << ( TableRow() << "" << _("Key Expires:") << key.expiresAsString() );