1 /*---------------------------------------------------------------------\
3 | |__ / \ / / . \ . \ |
8 \---------------------------------------------------------------------*/
9 /** \file zypp/KeyRing.cc
18 #include <boost/format.hpp>
20 #include "zypp/TmpPath.h"
21 #include "zypp/ZYppFactory.h"
22 #include "zypp/ZYpp.h"
24 #include "zypp/base/LogTools.h"
25 #include "zypp/base/IOStream.h"
26 #include "zypp/base/String.h"
27 #include "zypp/base/Regex.h"
28 #include "zypp/base/Gettext.h"
29 #include "zypp/base/WatchFile.h"
30 #include "zypp/PathInfo.h"
31 #include "zypp/KeyRing.h"
32 #include "zypp/ExternalProgram.h"
33 #include "zypp/TmpPath.h"
37 #undef ZYPP_BASE_LOGGER_LOGGROUP
38 #define ZYPP_BASE_LOGGER_LOGGROUP "zypp::KeyRing"
40 #define GPG_BINARY "/usr/bin/gpg2"
42 ///////////////////////////////////////////////////////////////////
44 { /////////////////////////////////////////////////////////////////
46 IMPL_PTR_TYPE(KeyRing);
50 KeyRing::DefaultAccept _keyRingDefaultAccept( KeyRing::ACCEPT_NOTHING );
53 KeyRing::DefaultAccept KeyRing::defaultAccept()
54 { return _keyRingDefaultAccept; }
56 void KeyRing::setDefaultAccept( DefaultAccept value_r )
58 MIL << "Set new KeyRing::DefaultAccept: " << value_r << endl;
59 _keyRingDefaultAccept = value_r;
62 bool KeyRingReport::askUserToAcceptUnsignedFile( const std::string & file, const KeyContext & keycontext )
63 { return _keyRingDefaultAccept.testFlag( KeyRing::ACCEPT_UNSIGNED_FILE ); }
65 KeyRingReport::KeyTrust
66 KeyRingReport::askUserToAcceptKey( const PublicKey & key, const KeyContext & keycontext )
68 if ( _keyRingDefaultAccept.testFlag( KeyRing::TRUST_KEY_TEMPORARILY ) )
69 return KEY_TRUST_TEMPORARILY;
70 if ( _keyRingDefaultAccept.testFlag( KeyRing::TRUST_AND_IMPORT_KEY ) )
71 return KEY_TRUST_AND_IMPORT;
72 return KEY_DONT_TRUST;
75 bool KeyRingReport::askUserToAcceptUnknownKey( const std::string & file, const std::string & id, const KeyContext & keycontext )
76 { return _keyRingDefaultAccept.testFlag( KeyRing::ACCEPT_UNKNOWNKEY ); }
78 bool KeyRingReport::askUserToAcceptVerificationFailed( const std::string & file, const PublicKey & key, const KeyContext & keycontext )
79 { return _keyRingDefaultAccept.testFlag( KeyRing::ACCEPT_VERIFICATION_FAILED ); }
83 ///////////////////////////////////////////////////////////////////
84 /// \class CachedPublicKeyData
85 /// \brief Functor returning the keyrings data (cached).
87 /// const std::list<PublicKeyData> & cachedPublicKeyData( const Pathname & keyring );
89 ///////////////////////////////////////////////////////////////////
90 struct CachedPublicKeyData // : private base::NonCopyable - but KeyRing uses RWCOW though also NonCopyable :(
92 const std::list<PublicKeyData> & operator()( const Pathname & keyring_r ) const
93 { return getData( keyring_r ); }
98 scoped_ptr<WatchFile> _keyringP;
99 std::list<PublicKeyData> _data;
101 // Empty copy ctor to allow insert into std::map as
102 // scoped_ptr is noncopyable.
104 Cache( const Cache & rhs ) {}
107 typedef std::map<Pathname,Cache> CacheMap;
109 const std::list<PublicKeyData> & getData( const Pathname & keyring_r ) const
111 Cache & cache( _cacheMap[keyring_r] );
112 if ( ! cache._keyringP )
114 // init new cache entry
115 cache._keyringP.reset( new WatchFile( keyring_r/"pubring.gpg", WatchFile::NO_INIT ) );
117 return getData( keyring_r, cache );
120 const std::list<PublicKeyData> & getData( const Pathname & keyring_r, Cache & cache_r ) const
122 if ( cache_r._keyringP->hasChanged() )
127 "--list-public-keys",
128 "--homedir", keyring_r.c_str(),
129 "--no-default-keyring",
133 "--with-fingerprint",
142 PublicKeyScanner scanner;
143 ExternalProgram prog( argv ,ExternalProgram::Discard_Stderr, false, -1, true );
144 for( std::string line = prog.receiveLine(); !line.empty(); line = prog.receiveLine() )
146 scanner.scan( line );
150 cache_r._data.swap( scanner._keys );
151 MIL << "Found keys: " << cache_r._data << endl;
153 return cache_r._data;
156 mutable CacheMap _cacheMap;
158 ///////////////////////////////////////////////////////////////////
161 ///////////////////////////////////////////////////////////////////
163 // CLASS NAME : KeyRing::Impl
165 /** KeyRing implementation. */
168 Impl( const Pathname & baseTmpDir )
169 : _trusted_tmp_dir( baseTmpDir, "zypp-trusted-kr" )
170 , _general_tmp_dir( baseTmpDir, "zypp-general-kr" )
171 , _base_dir( baseTmpDir )
173 MIL << "Current KeyRing::DefaultAccept: " << _keyRingDefaultAccept << endl;
176 void importKey( const PublicKey & key, bool trusted = false );
177 void multiKeyImport( const Pathname & keyfile_r, bool trusted_r = false );
178 void deleteKey( const std::string & id, bool trusted );
180 std::string readSignatureKeyId( const Pathname & signature );
182 bool isKeyTrusted( const std::string & id )
183 { return bool(publicKeyExists( id, trustedKeyRing() )); }
184 bool isKeyKnown( const std::string & id )
185 { return publicKeyExists( id, trustedKeyRing() ) || publicKeyExists( id, generalKeyRing() ); }
187 std::list<PublicKey> trustedPublicKeys()
188 { return publicKeys( trustedKeyRing() ); }
189 std::list<PublicKey> publicKeys()
190 { return publicKeys( generalKeyRing() ); }
192 const std::list<PublicKeyData> & trustedPublicKeyData()
193 { return publicKeyData( trustedKeyRing() ); }
194 const std::list<PublicKeyData> & publicKeyData()
195 { return publicKeyData( generalKeyRing() ); }
197 void dumpPublicKey( const std::string & id, bool trusted, std::ostream & stream )
198 { dumpPublicKey( id, ( trusted ? trustedKeyRing() : generalKeyRing() ), stream ); }
200 PublicKey exportPublicKey( const PublicKeyData & keyData )
201 { return exportKey( keyData, generalKeyRing() ); }
202 PublicKey exportTrustedPublicKey( const PublicKeyData & keyData )
203 { return exportKey( keyData, trustedKeyRing() ); }
205 bool verifyFileSignatureWorkflow(
206 const Pathname & file,
207 const std::string & filedesc,
208 const Pathname & signature,
209 const KeyContext & keycontext = KeyContext());
211 bool verifyFileSignature( const Pathname & file, const Pathname & signature )
212 { return verifyFile( file, signature, generalKeyRing() ); }
213 bool verifyFileTrustedSignature( const Pathname & file, const Pathname & signature )
214 { return verifyFile( file, signature, trustedKeyRing() ); }
217 bool verifyFile( const Pathname & file, const Pathname & signature, const Pathname & keyring );
218 void importKey( const Pathname & keyfile, const Pathname & keyring );
220 PublicKey exportKey( const std::string & id, const Pathname & keyring );
221 PublicKey exportKey( const PublicKeyData & keyData, const Pathname & keyring );
223 void dumpPublicKey( const std::string & id, const Pathname & keyring, std::ostream & stream );
224 filesystem::TmpFile dumpPublicKeyToTmp( const std::string & id, const Pathname & keyring );
226 void deleteKey( const std::string & id, const Pathname & keyring );
228 std::list<PublicKey> publicKeys( const Pathname & keyring);
229 const std::list<PublicKeyData> & publicKeyData( const Pathname & keyring )
230 { return cachedPublicKeyData( keyring ); }
232 /** Get \ref PublicKeyData for ID (\c false if ID is not found). */
233 PublicKeyData publicKeyExists( const std::string & id, const Pathname & keyring );
235 const Pathname generalKeyRing() const
236 { return _general_tmp_dir.path(); }
237 const Pathname trustedKeyRing() const
238 { return _trusted_tmp_dir.path(); }
240 // Used for trusted and untrusted keyrings
241 filesystem::TmpDir _trusted_tmp_dir;
242 filesystem::TmpDir _general_tmp_dir;
246 /** Functor returning the keyrings data (cached).
248 * const std::list<PublicKeyData> & cachedPublicKeyData( const Pathname & keyring );
251 CachedPublicKeyData cachedPublicKeyData;
254 /** Offer default Impl. */
255 static shared_ptr<Impl> nullimpl()
257 static shared_ptr<Impl> _nullimpl( new Impl( filesystem::TmpPath::defaultLocation() ) );
262 friend Impl * rwcowClone<Impl>( const Impl * rhs );
263 /** clone for RWCOW_pointer */
265 { return new Impl( *this ); }
267 ///////////////////////////////////////////////////////////////////
270 void KeyRing::Impl::importKey( const PublicKey & key, bool trusted )
272 importKey( key.path(), trusted ? trustedKeyRing() : generalKeyRing() );
276 callback::SendReport<target::rpm::KeyRingSignals> rpmdbEmitSignal;
277 callback::SendReport<KeyRingSignals> emitSignal;
279 rpmdbEmitSignal->trustedKeyAdded( key );
280 emitSignal->trustedKeyAdded( key );
284 void KeyRing::Impl::multiKeyImport( const Pathname & keyfile_r, bool trusted_r )
286 importKey( keyfile_r, trusted_r ? trustedKeyRing() : generalKeyRing() );
289 void KeyRing::Impl::deleteKey( const std::string & id, bool trusted )
295 key = exportKey( id, trustedKeyRing() );
298 deleteKey( id, trusted ? trustedKeyRing() : generalKeyRing() );
302 callback::SendReport<target::rpm::KeyRingSignals> rpmdbEmitSignal;
303 callback::SendReport<KeyRingSignals> emitSignal;
305 rpmdbEmitSignal->trustedKeyRemoved( key );
306 emitSignal->trustedKeyRemoved( key );
310 PublicKeyData KeyRing::Impl::publicKeyExists( const std::string & id, const Pathname & keyring )
312 MIL << "Searching key [" << id << "] in keyring " << keyring << endl;
313 const std::list<PublicKeyData> & keys( publicKeyData( keyring ) );
314 for_( it, keys.begin(), keys.end() )
316 if ( id == (*it).id() )
321 return PublicKeyData();
324 PublicKey KeyRing::Impl::exportKey( const PublicKeyData & keyData, const Pathname & keyring )
326 return PublicKey( dumpPublicKeyToTmp( keyData.id(), keyring ), keyData );
329 PublicKey KeyRing::Impl::exportKey( const std::string & id, const Pathname & keyring )
331 PublicKeyData keyData( publicKeyExists( id, keyring ) );
333 return PublicKey( dumpPublicKeyToTmp( keyData.id(), keyring ), keyData );
335 // Here: key not found
336 WAR << "No key " << id << " to export from " << keyring << endl;
341 void KeyRing::Impl::dumpPublicKey( const std::string & id, const Pathname & keyring, std::ostream & stream )
348 "--homedir", keyring.asString().c_str(),
349 "--no-default-keyring",
353 "--no-permission-warning",
358 ExternalProgram prog( argv,ExternalProgram::Discard_Stderr, false, -1, true );
359 for ( std::string line = prog.receiveLine(); !line.empty(); line = prog.receiveLine() )
366 filesystem::TmpFile KeyRing::Impl::dumpPublicKeyToTmp( const std::string & id, const Pathname & keyring )
368 filesystem::TmpFile tmpFile( _base_dir, "pubkey-"+id+"-" );
369 MIL << "Going to export key " << id << " from " << keyring << " to " << tmpFile.path() << endl;
371 std::ofstream os( tmpFile.path().c_str() );
372 dumpPublicKey( id, keyring, os );
377 bool KeyRing::Impl::verifyFileSignatureWorkflow(
378 const Pathname & file,
379 const std::string & filedesc,
380 const Pathname & signature,
381 const KeyContext & context )
383 callback::SendReport<KeyRingReport> report;
384 MIL << "Going to verify signature for " << filedesc << " ( " << file << " ) with " << signature << endl;
386 // if signature does not exists, ask user if he wants to accept unsigned file.
387 if( signature.empty() || (!PathInfo( signature ).isExist()) )
389 bool res = report->askUserToAcceptUnsignedFile( filedesc, context );
390 MIL << "User decision on unsigned file: " << res << endl;
394 // get the id of the signature
395 std::string id = readSignatureKeyId( signature );
397 // doeskey exists in trusted keyring
398 PublicKeyData trustedKeyData( publicKeyExists( id, trustedKeyRing() ) );
399 if ( trustedKeyData )
401 MIL << "Key is trusted: " << trustedKeyData << endl;
403 // lets look if there is an updated key in the
405 PublicKeyData generalKeyData( publicKeyExists( id, generalKeyRing() ) );
406 if ( generalKeyData )
408 // bnc #393160: Comment #30: Compare at least the fingerprint
409 // in case an attacker created a key the the same id.
410 if ( trustedKeyData.fingerprint() == generalKeyData.fingerprint()
411 && trustedKeyData.created() < generalKeyData.created() )
413 MIL << "Key was updated. Saving new version into trusted keyring: " << generalKeyData << endl;
414 importKey( exportKey( generalKeyData, generalKeyRing() ), true );
415 trustedKeyData = generalKeyData = PublicKeyData(); // invalidated by import.
419 // it exists, is trusted, does it validates?
420 if ( verifyFile( file, signature, trustedKeyRing() ) )
424 if ( ! trustedKeyData ) // invalidated by previous import
425 trustedKeyData = publicKeyExists( id, trustedKeyRing() );
426 return report->askUserToAcceptVerificationFailed( filedesc, exportKey( trustedKeyData, trustedKeyRing() ), context );
431 PublicKeyData generalKeyData( publicKeyExists( id, generalKeyRing() ) );
432 if ( generalKeyData )
434 PublicKey key( exportKey( generalKeyData, generalKeyRing() ) );
435 MIL << "Exported key " << id << " to " << key.path() << endl;
436 MIL << "Key " << id << " " << key.name() << " is not trusted" << endl;
438 // ok the key is not trusted, ask the user to trust it or not
439 KeyRingReport::KeyTrust reply = report->askUserToAcceptKey( key, context );
440 if ( reply == KeyRingReport::KEY_TRUST_TEMPORARILY ||
441 reply == KeyRingReport::KEY_TRUST_AND_IMPORT )
443 MIL << "User wants to trust key " << id << " " << key.name() << endl;
444 //dumpFile( unKey.path() );
446 Pathname whichKeyring;
447 if ( reply == KeyRingReport::KEY_TRUST_AND_IMPORT )
449 MIL << "User wants to import key " << id << " " << key.name() << endl;
450 importKey( key, true );
451 whichKeyring = trustedKeyRing();
454 whichKeyring = generalKeyRing();
457 if ( verifyFile( file, signature, whichKeyring ) )
459 MIL << "File signature is verified" << endl;
464 MIL << "File signature check fails" << endl;
465 if ( report->askUserToAcceptVerificationFailed( filedesc, key, context ) )
467 MIL << "User continues anyway." << endl;
472 MIL << "User does not want to continue" << endl;
479 MIL << "User does not want to trust key " << id << " " << key.name() << endl;
486 MIL << "File [" << file << "] ( " << filedesc << " ) signed with unknown key [" << id << "]" << endl;
487 if ( report->askUserToAcceptUnknownKey( filedesc, id, context ) )
489 MIL << "User wants to accept unknown key " << id << endl;
494 MIL << "User does not want to accept unknown key " << id << endl;
502 std::list<PublicKey> KeyRing::Impl::publicKeys( const Pathname & keyring )
504 const std::list<PublicKeyData> & keys( publicKeyData( keyring ) );
505 std::list<PublicKey> ret;
507 for_( it, keys.begin(), keys.end() )
509 PublicKey key( exportKey( *it, keyring ) );
510 ret.push_back( key );
511 MIL << "Found key " << key << endl;
516 void KeyRing::Impl::importKey( const Pathname & keyfile, const Pathname & keyring )
518 if ( ! PathInfo( keyfile ).isExist() )
519 // TranslatorExplanation first %s is key name, second is keyring name
520 ZYPP_THROW(KeyRingException(boost::str(boost::format(
521 _("Tried to import not existent key %s into keyring %s"))
522 % keyfile.asString() % keyring.asString())));
528 "--homedir", keyring.asString().c_str(),
529 "--no-default-keyring",
533 "--no-permission-warning",
535 keyfile.asString().c_str(),
539 ExternalProgram prog( argv,ExternalProgram::Discard_Stderr, false, -1, true );
543 void KeyRing::Impl::deleteKey( const std::string & id, const Pathname & keyring )
549 "--homedir", keyring.asString().c_str(),
550 "--no-default-keyring",
560 ExternalProgram prog( argv,ExternalProgram::Discard_Stderr, false, -1, true );
562 int code = prog.close();
564 ZYPP_THROW(Exception(_("Failed to delete key.")));
566 MIL << "Deleted key " << id << " from keyring " << keyring << endl;
570 std::string KeyRing::Impl::readSignatureKeyId( const Pathname & signature )
572 if ( ! PathInfo( signature ).isFile() )
573 ZYPP_THROW(Exception(boost::str(boost::format(
574 _("Signature file %s not found"))% signature.asString())));
576 MIL << "Determining key id if signature " << signature << endl;
577 // HACK create a tmp keyring with no keys
578 filesystem::TmpDir dir( _base_dir, "fake-keyring" );
583 "--homedir", dir.path().asString().c_str(),
584 "--no-default-keyring",
590 signature.asString().c_str(),
594 ExternalProgram prog( argv,ExternalProgram::Discard_Stderr, false, -1, true );
599 str::regex rxNoKey( "^\\[GNUPG:\\] NO_PUBKEY (.+)\n$" );
601 for( line = prog.receiveLine(), count=0; !line.empty(); line = prog.receiveLine(), count++ )
603 //MIL << "[" << line << "]" << endl;
605 if( str::regex_match( line, what, rxNoKey ) )
607 if ( what.size() >= 1 )
612 //dumpRegexpResults( what );
618 MIL << "no output" << endl;
621 MIL << "Determined key id [" << id << "] for signature " << signature << endl;
626 bool KeyRing::Impl::verifyFile( const Pathname & file, const Pathname & signature, const Pathname & keyring )
632 "--homedir", keyring.asString().c_str(),
633 "--no-default-keyring",
639 signature.asString().c_str(),
640 file.asString().c_str(),
644 // no need to parse output for now
645 // [GNUPG:] SIG_ID yCc4u223XRJnLnVAIllvYbUd8mQ 2006-03-29 1143618744
646 // [GNUPG:] GOODSIG A84EDAE89C800ACA SuSE Package Signing Key <build@suse.de>
647 // gpg: Good signature from "SuSE Package Signing Key <build@suse.de>"
648 // [GNUPG:] VALIDSIG 79C179B2E1C820C1890F9994A84EDAE89C800ACA 2006-03-29 1143618744 0 3 0 17 2 00 79C179B2E1C820C1890F9994A84EDAE89C800ACA
649 // [GNUPG:] TRUST_UNDEFINED
651 // [GNUPG:] ERRSIG A84EDAE89C800ACA 17 2 00 1143618744 9
652 // [GNUPG:] NO_PUBKEY A84EDAE89C800ACA
654 ExternalProgram prog( argv,ExternalProgram::Discard_Stderr, false, -1, true );
656 return ( prog.close() == 0 ) ? true : false;
659 ///////////////////////////////////////////////////////////////////
661 ///////////////////////////////////////////////////////////////////
663 // CLASS NAME : KeyRing
665 ///////////////////////////////////////////////////////////////////
667 KeyRing::KeyRing( const Pathname & baseTmpDir )
668 : _pimpl( new Impl( baseTmpDir ) )
675 void KeyRing::importKey( const PublicKey & key, bool trusted )
676 { _pimpl->importKey( key, trusted ); }
678 void KeyRing::multiKeyImport( const Pathname & keyfile_r, bool trusted_r )
679 { _pimpl->multiKeyImport( keyfile_r, trusted_r ); }
681 std::string KeyRing::readSignatureKeyId( const Pathname & signature )
682 { return _pimpl->readSignatureKeyId( signature ); }
684 void KeyRing::deleteKey( const std::string & id, bool trusted )
685 { _pimpl->deleteKey( id, trusted ); }
687 std::list<PublicKey> KeyRing::publicKeys()
688 { return _pimpl->publicKeys(); }
690 std:: list<PublicKey> KeyRing::trustedPublicKeys()
691 { return _pimpl->trustedPublicKeys(); }
693 std::list<PublicKeyData> KeyRing::publicKeyData()
694 { return _pimpl->publicKeyData(); }
696 std::list<PublicKeyData> KeyRing::trustedPublicKeyData()
697 { return _pimpl->trustedPublicKeyData(); }
699 std::list<std::string> KeyRing::publicKeyIds() // deprecated: use publicKeyData
701 const std::list<PublicKeyData> & keys( publicKeyData() );
702 std::list<std::string> ret;
703 for_( it, keys.begin(), keys.end() )
705 ret.push_back( (*it).id() );
710 std::list<std::string> KeyRing::trustedPublicKeyIds() // deprecated: use trustedPublicKeyData
712 const std::list<PublicKeyData> & keys( trustedPublicKeyData() );
713 std::list<std::string> ret;
714 for_( it, keys.begin(), keys.end() )
716 ret.push_back( (*it).id() );
721 bool KeyRing::verifyFileSignatureWorkflow(
722 const Pathname & file,
723 const std::string filedesc,
724 const Pathname & signature,
725 const KeyContext & keycontext )
726 { return _pimpl->verifyFileSignatureWorkflow( file, filedesc, signature, keycontext ); }
728 bool KeyRing::verifyFileSignature( const Pathname & file, const Pathname & signature )
729 { return _pimpl->verifyFileSignature( file, signature ); }
731 bool KeyRing::verifyFileTrustedSignature( const Pathname & file, const Pathname & signature )
732 { return _pimpl->verifyFileTrustedSignature( file, signature ); }
734 void KeyRing::dumpPublicKey( const std::string & id, bool trusted, std::ostream & stream )
735 { _pimpl->dumpPublicKey( id, trusted, stream ); }
737 PublicKey KeyRing::exportPublicKey( const PublicKeyData & keyData )
738 { return _pimpl->exportPublicKey( keyData ); }
740 PublicKey KeyRing::exportTrustedPublicKey( const PublicKeyData & keyData )
741 { return _pimpl->exportTrustedPublicKey( keyData ); }
743 bool KeyRing::isKeyTrusted( const std::string & id )
744 { return _pimpl->isKeyTrusted( id ); }
746 bool KeyRing::isKeyKnown( const std::string & id )
747 { return _pimpl->isKeyKnown( id ); }
749 /////////////////////////////////////////////////////////////////
751 ///////////////////////////////////////////////////////////////////