From 4f1380b91cc0e5dbd1e54797f951de64bbf99eaf Mon Sep 17 00:00:00 2001 From: Michael Andres Date: Wed, 31 Jul 2013 13:54:02 +0200 Subject: [PATCH] Fix rpmdb key import/export (bnc#828672) --- zypp/target/rpm/RpmDb.cc | 319 +++++++++++++++++++++++++++++++---------------- zypp/target/rpm/RpmDb.h | 12 ++ 2 files changed, 224 insertions(+), 107 deletions(-) diff --git a/zypp/target/rpm/RpmDb.cc b/zypp/target/rpm/RpmDb.cc index 2c7f275..c5faac5 100644 --- a/zypp/target/rpm/RpmDb.cc +++ b/zypp/target/rpm/RpmDb.cc @@ -423,9 +423,7 @@ void RpmDb::initDatabase( Pathname root_r, Pathname dbPath_r, bool doRebuild_r ) } MIL << "Syncronizing keys with zypp keyring" << endl; - // we do this one by one now. - importZyppKeyRingTrustedKeys(); - exportTrustedKeysInZyppKeyRing(); + syncTrustedKeys(); // Close the database in case any write acces (create/convert) // happened during init. This should drop any lock acquired @@ -850,87 +848,179 @@ void RpmDb::doRebuildDatabase(callback::SendReport & report) } } -void RpmDb::importZyppKeyRingTrustedKeys() +/////////////////////////////////////////////////////////////////// +namespace { - MIL << "Importing zypp trusted keyring" << std::endl; - - std::list zypp_keys; - zypp_keys = getZYpp()->keyRing()->trustedPublicKeys(); - /* The pubkeys() call below is expensive. It calls gpg2 for each - gpg-pubkey in the rpm db. Useless if we don't have any keys in - zypp yet. */ - if (zypp_keys.empty()) - return; + /** \ref RpmDb::syncTrustedKeys helper + * Compute which keys need to be exprted to / imported from the zypp keyring. + * Return result via argument list. + */ + void computeKeyRingSync( std::set & rpmKeys_r, std::list & zyppKeys_r ) + { + /////////////////////////////////////////////////////////////////// + // Remember latest release and where it ocurred + struct Key + { + Key() + : _inRpmKeys( nullptr ) + , _inZyppKeys( nullptr ) + {} + + void updateIf( const Edition & rpmKey_r ) + { + std::string keyRelease( rpmKey_r.release() ); + int comp = _release.compare( keyRelease ); + if ( comp < 0 ) + { + // update to newer release + _release.swap( keyRelease ); + _inRpmKeys = &rpmKey_r; + _inZyppKeys = nullptr; + if ( !keyRelease.empty() ) + DBG << "Old key in R: gpg-pubkey-" << rpmKey_r.version() << "-" << keyRelease << endl; + } + else if ( comp == 0 ) + { + // stay with this release + if ( ! _inRpmKeys ) + _inRpmKeys = &rpmKey_r; + } + // else: this is an old release + else + DBG << "Old key in R: gpg-pubkey-" << rpmKey_r.version() << "-" << keyRelease << endl; + } + + void updateIf( const PublicKeyData & zyppKey_r ) + { + std::string keyRelease( zyppKey_r.gpgPubkeyRelease() ); + int comp = _release.compare( keyRelease ); + if ( comp < 0 ) + { + // update to newer release + _release.swap( keyRelease ); + _inRpmKeys = nullptr; + _inZyppKeys = &zyppKey_r; + if ( !keyRelease.empty() ) + DBG << "Old key in Z: gpg-pubkey-" << zyppKey_r.gpgPubkeyVersion() << "-" << keyRelease << endl; + } + else if ( comp == 0 ) + { + // stay with this release + if ( ! _inZyppKeys ) + _inZyppKeys = &zyppKey_r; + } + // else: this is an old release + else + DBG << "Old key in Z: gpg-pubkey-" << zyppKey_r.gpgPubkeyVersion() << "-" << keyRelease << endl; + } + + std::string _release; + const Edition * _inRpmKeys; + const PublicKeyData * _inZyppKeys; + }; + /////////////////////////////////////////////////////////////////// - std::list rpm_keys = pubkeys(); - for_( it, zypp_keys.begin(), zypp_keys.end() ) + // collect keys by ID(version) and latest creation(release) + std::map _keymap; + + for_( it, rpmKeys_r.begin(), rpmKeys_r.end() ) { - // we find only the left part of the long gpg key, as rpm does not support long ids - std::list::iterator ik = find( rpm_keys.begin(), rpm_keys.end(), (*it)); - if ( ik != rpm_keys.end() ) - { - MIL << "Key " << (*it).id() << " (" << (*it).name() << ") is already in rpm database." << std::endl; - } - else - { - // now import the key in rpm - try - { - importPubkey( *it ); - MIL << "Trusted key " << (*it).id() << " (" << (*it).name() << ") imported in rpm database." << std::endl; - } - catch (RpmException &e) - { - ERR << "Could not import key " << (*it).id() << " (" << (*it).name() << " from " << (*it).path() << " in rpm database" << std::endl; - } - } + _keymap[(*it).version()].updateIf( *it ); } -} -void RpmDb::exportTrustedKeysInZyppKeyRing() -{ - MIL << "Exporting rpm keyring into zypp trusted keyring" < rpm_keys( pubkeyEditions() ); - list zypp_keys( getZYpp()->keyRing()->trustedPublicKeys() ); + // compute missing keys + std::set rpmKeys; + std::list zyppKeys; + for_( it, _keymap.begin(), _keymap.end() ) + { + DBG << "gpg-pubkey-" << (*it).first << "-" << (*it).second._release << " " + << ( (*it).second._inRpmKeys ? "R" : "_" ) + << ( (*it).second._inZyppKeys ? "Z" : "_" ) << endl; + if ( ! (*it).second._inRpmKeys ) + { + zyppKeys.push_back( *(*it).second._inZyppKeys ); + } + if ( ! (*it).second._inZyppKeys ) + { + rpmKeys.insert( *(*it).second._inRpmKeys ); + } + } + rpmKeys_r.swap( rpmKeys ); + zyppKeys_r.swap( zyppKeys ); + } +} // namespace +/////////////////////////////////////////////////////////////////// - // Temporarily disconnect to prevent the attemt to re-import the exported keys. - callback::TempConnect tempDisconnect; +void RpmDb::syncTrustedKeys( SyncTrustedKeyBits mode_r ) +{ + MIL << "Going to sync trusted keys..." << endl; + std::set rpmKeys( pubkeyEditions() ); + std::list zyppKeys( getZYpp()->keyRing()->trustedPublicKeyData() ); + computeKeyRingSync( rpmKeys, zyppKeys ); + MIL << (mode_r & SYNC_TO_KEYRING ? "" : "(skip) ") << "Rpm keys to export into zypp trusted keyring: " << rpmKeys.size() << endl; + MIL << (mode_r & SYNC_FROM_KEYRING ? "" : "(skip) ") << "Zypp trusted keys to import into rpm database: " << zyppKeys.size() << endl; - TmpFile tmpfile( getZYpp()->tmpPath() ); + /////////////////////////////////////////////////////////////////// + if ( (mode_r & SYNC_TO_KEYRING) && ! rpmKeys.empty() ) { - ofstream tmpos( tmpfile.path().c_str() ); - for_( it, rpm_keys.begin(), rpm_keys.end() ) + // export to zypp keyring + MIL << "Exporting rpm keyring into zypp trusted keyring" < tempDisconnect; + librpmDb::db_const_iterator keepDbOpen; // just to keep a ref. + + TmpFile tmpfile( getZYpp()->tmpPath() ); { - // search the zypp key into the rpm keys - // long id is edition version + release - string id = str::toUpper( (*it).version() + (*it).release()); - list::iterator ik( find( zypp_keys.begin(), zypp_keys.end(), id) ); - if ( ik != zypp_keys.end() ) - { - MIL << "Key " << (*it) << " is already in zypp database." << endl; - } - else + ofstream tmpos( tmpfile.path().c_str() ); + for_( it, rpmKeys.begin(), rpmKeys.end() ) { // we export the rpm key into a file - RpmHeader::constPtr result( new RpmHeader() ); + RpmHeader::constPtr result; getData( string("gpg-pubkey"), *it, result ); - MIL << "Will export trusted key " << (*it) << " to zypp keyring." << endl; tmpos << result->tag_description() << endl; } } + try + { + getZYpp()->keyRing()->multiKeyImport( tmpfile.path(), true /*trusted*/); + } + catch (Exception &e) + { + ERR << "Could not import keys into in zypp keyring" << endl; + } } - try - { - getZYpp()->keyRing()->multiKeyImport( tmpfile.path(), true /*trusted*/); - } - catch (Exception &e) + + /////////////////////////////////////////////////////////////////// + if ( (mode_r & SYNC_FROM_KEYRING) && ! zyppKeys.empty() ) { - ERR << "Could not import keys into in zypp keyring" << endl; + // import from zypp keyring + MIL << "Importing zypp trusted keyring" << std::endl; + for_( it, zyppKeys.begin(), zyppKeys.end() ) + { + try + { + importPubkey( getZYpp()->keyRing()->exportTrustedPublicKey( *it ) ); + } + catch ( const RpmException & exp ) + { + ZYPP_CAUGHT( exp ); + } + } } + MIL << "Trusted keys synced." << endl; } +void RpmDb::importZyppKeyRingTrustedKeys() +{ syncTrustedKeys( SYNC_FROM_KEYRING ); } + +void RpmDb::exportTrustedKeysInZyppKeyRing() +{ syncTrustedKeys( SYNC_TO_KEYRING ); } + /////////////////////////////////////////////////////////////////// // // @@ -941,37 +1031,65 @@ void RpmDb::importPubkey( const PublicKey & pubkey_r ) { FAILIFNOTINITIALIZED; - // check if the key is already in the rpm database and just - // return if it does. - set rpm_keys = pubkeyEditions(); - string keyshortid = pubkey_r.id().substr(8,8); - MIL << "Comparing '" << keyshortid << "' to: "; - for ( set::const_iterator it = rpm_keys.begin(); it != rpm_keys.end(); ++it) - { - string id = str::toUpper( (*it).version() ); - MIL << ", '" << id << "'"; - if ( id == keyshortid ) - { - // they match id - // now check if timestamp is different - Date date = Date(str::strtonum("0x" + (*it).release())); - if ( date == pubkey_r.created() ) - { + // check if the key is already in the rpm database + Edition keyEd( pubkey_r.gpgPubkeyVersion(), pubkey_r.gpgPubkeyRelease() ); + set rpmKeys = pubkeyEditions(); + bool hasOldkeys = false; - MIL << endl << "Key " << pubkey_r << " is already in the rpm trusted keyring." << endl; - return; - } - else - { - MIL << endl << "Key " << pubkey_r << " has another version in keyring. ( " << date << " & " << pubkey_r.created() << ")" << endl; + for_( it, rpmKeys.begin(), rpmKeys.end() ) + { + if ( keyEd == *it ) // quick test (Edition is IdStringType!) + { + MIL << "Key " << pubkey_r << " is already in the rpm trusted keyring. (skip import)" << endl; + return; + } - } + if ( keyEd.version() != (*it).version() ) + continue; // different key ID (version) + + if ( keyEd.release() < (*it).release() ) + { + MIL << "Key " << pubkey_r << " is older than one in the rpm trusted keyring. (skip import)" << endl; + return; + } + else + { + hasOldkeys = true; + } + } + MIL << "Key " << pubkey_r << " will be imported into the rpm trusted keyring." << (hasOldkeys?"(update)":"(new)") << endl; + + if ( hasOldkeys ) + { + // We must explicitly delete old key IDs first (all releases, + // that's why we don't call removePubkey here). + std::string keyName( "gpg-pubkey-" + keyEd.version() ); + RpmArgVec opts; + opts.push_back ( "-e" ); + opts.push_back ( "--allmatches" ); + opts.push_back ( "--" ); + opts.push_back ( keyName.c_str() ); + // don't call modifyDatabase because it would remove the old + // rpm3 database, if the current database is a temporary one. + run_rpm( opts, ExternalProgram::Stderr_To_Stdout ); + string line; + while ( systemReadLine( line ) ) + { + ( str::startsWith( line, "error:" ) ? WAR : DBG ) << line << endl; + } + + if ( systemStatus() != 0 ) + { + ERR << "Failed to remove key " << pubkey_r << " from RPM trusted keyring (ignored)" << endl; + } + else + { + MIL << "Key " << pubkey_r << " has been removed from RPM trusted keyring" << endl; } } - // key does not exists, lets import it - MIL << endl; + // import the new key RpmArgVec opts; opts.push_back ( "--import" ); opts.push_back ( "--" ); @@ -984,19 +1102,10 @@ void RpmDb::importPubkey( const PublicKey & pubkey_r ) string line; while ( systemReadLine( line ) ) { - if ( line.substr( 0, 6 ) == "error:" ) - { - WAR << line << endl; - } - else - { - DBG << line << endl; - } + ( str::startsWith( line, "error:" ) ? WAR : DBG ) << line << endl; } - int rpm_status = systemStatus(); - - if ( rpm_status != 0 ) + if ( systemStatus() != 0 ) { //TranslatorExplanation first %s is file name, second is error message ZYPP_THROW(RpmSubprocessException(boost::str(boost::format( @@ -1022,16 +1131,12 @@ void RpmDb::removePubkey( const PublicKey & pubkey_r ) // check if the key is in the rpm database and just // return if it does not. set rpm_keys = pubkeyEditions(); - - // search the key set::const_iterator found_edition = rpm_keys.end(); + std::string pubkeyVersion( pubkey_r.gpgPubkeyVersion() ); - for ( set::const_iterator it = rpm_keys.begin(); it != rpm_keys.end(); ++it) + for_( it, rpm_keys.begin(), rpm_keys.end() ) { - string id = str::toUpper( (*it).version() ); - string keyshortid = pubkey_r.id().substr(8,8); - MIL << "Comparing '" << id << "' to '" << keyshortid << "'" << endl; - if ( id == keyshortid ) + if ( (*it).version() == pubkeyVersion ) { found_edition = it; break; @@ -1101,7 +1206,7 @@ list RpmDb::pubkeys() const if (edition != Edition::noedition) { // we export the rpm key into a file - RpmHeader::constPtr result = new RpmHeader(); + RpmHeader::constPtr result; getData( string("gpg-pubkey"), edition, result ); TmpFile file(getZYpp()->tmpPath()); ofstream os; diff --git a/zypp/target/rpm/RpmDb.h b/zypp/target/rpm/RpmDb.h index 23666e3..956ba25 100644 --- a/zypp/target/rpm/RpmDb.h +++ b/zypp/target/rpm/RpmDb.h @@ -323,6 +323,17 @@ public: // /////////////////////////////////////////////////////////////////// public: + /** Sync mode for \ref syncTrustedKeys */ + enum SyncTrustedKeyBits + { + SYNC_TO_KEYRING = 1<<0, //! export rpm trusted keys into zypp trusted keyring + SYNC_FROM_KEYRING = 1<<1, //! import zypp trusted keys into rpm database. + SYNC_BOTH = SYNC_TO_KEYRING | SYNC_FROM_KEYRING + }; + /** + * Sync trusted keys stored in rpm database and zypp trusted keyring. + */ + void syncTrustedKeys( SyncTrustedKeyBits mode_r = SYNC_BOTH ); /** * iterates through zypp keyring and import all non existant keys * into rpm keyring @@ -332,6 +343,7 @@ public: * insert all rpm trusted keys into zypp trusted keyring */ void exportTrustedKeysInZyppKeyRing(); + private: /** * The connection to the rpm process. -- 2.7.4