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 /** \todo Fix duplicate define in PublicKey/KeyRing */
41 #define GPG_BINARY "/usr/bin/gpg2"
43 ///////////////////////////////////////////////////////////////////
45 { /////////////////////////////////////////////////////////////////
47 IMPL_PTR_TYPE(KeyRing);
51 KeyRing::DefaultAccept _keyRingDefaultAccept( KeyRing::ACCEPT_NOTHING );
54 KeyRing::DefaultAccept KeyRing::defaultAccept()
55 { return _keyRingDefaultAccept; }
57 void KeyRing::setDefaultAccept( DefaultAccept value_r )
59 MIL << "Set new KeyRing::DefaultAccept: " << value_r << endl;
60 _keyRingDefaultAccept = value_r;
63 void KeyRingReport::infoVerify( const std::string & file_r, const PublicKeyData & keyData_r, const KeyContext & keycontext )
66 bool KeyRingReport::askUserToAcceptUnsignedFile( const std::string & file, const KeyContext & keycontext )
67 { return _keyRingDefaultAccept.testFlag( KeyRing::ACCEPT_UNSIGNED_FILE ); }
69 KeyRingReport::KeyTrust
70 KeyRingReport::askUserToAcceptKey( const PublicKey & key, const KeyContext & keycontext )
72 if ( _keyRingDefaultAccept.testFlag( KeyRing::TRUST_KEY_TEMPORARILY ) )
73 return KEY_TRUST_TEMPORARILY;
74 if ( _keyRingDefaultAccept.testFlag( KeyRing::TRUST_AND_IMPORT_KEY ) )
75 return KEY_TRUST_AND_IMPORT;
76 return KEY_DONT_TRUST;
79 bool KeyRingReport::askUserToAcceptUnknownKey( const std::string & file, const std::string & id, const KeyContext & keycontext )
80 { return _keyRingDefaultAccept.testFlag( KeyRing::ACCEPT_UNKNOWNKEY ); }
82 bool KeyRingReport::askUserToAcceptVerificationFailed( const std::string & file, const PublicKey & key, const KeyContext & keycontext )
83 { return _keyRingDefaultAccept.testFlag( KeyRing::ACCEPT_VERIFICATION_FAILED ); }
87 ///////////////////////////////////////////////////////////////////
88 /// \class CachedPublicKeyData
89 /// \brief Functor returning the keyrings data (cached).
91 /// const std::list<PublicKeyData> & cachedPublicKeyData( const Pathname & keyring );
93 ///////////////////////////////////////////////////////////////////
94 struct CachedPublicKeyData // : private base::NonCopyable - but KeyRing uses RWCOW though also NonCopyable :(
96 const std::list<PublicKeyData> & operator()( const Pathname & keyring_r ) const
97 { return getData( keyring_r ); }
102 // Empty copy ctor to allow insert into std::map as
103 // scoped_ptr is noncopyable.
105 Cache( const Cache & rhs ) {}
107 void assertCache( const Pathname & keyring_r )
109 // .kbx since gpg2-2.1
111 _keyringK.reset( new WatchFile( keyring_r/"pubring.kbx", WatchFile::NO_INIT ) );
113 _keyringP.reset( new WatchFile( keyring_r/"pubring.gpg", WatchFile::NO_INIT ) );
116 bool hasChanged() const
118 bool k = _keyringK->hasChanged(); // be sure both files are checked
119 bool p = _keyringP->hasChanged();
123 std::list<PublicKeyData> _data;
126 scoped_ptr<WatchFile> _keyringK;
127 scoped_ptr<WatchFile> _keyringP;
130 typedef std::map<Pathname,Cache> CacheMap;
132 const std::list<PublicKeyData> & getData( const Pathname & keyring_r ) const
134 Cache & cache( _cacheMap[keyring_r] );
135 // init new cache entry
136 cache.assertCache( keyring_r );
137 return getData( keyring_r, cache );
140 const std::list<PublicKeyData> & getData( const Pathname & keyring_r, Cache & cache_r ) const
142 if ( cache_r.hasChanged() )
147 "--list-public-keys",
148 "--homedir", keyring_r.c_str(),
149 "--no-default-keyring",
153 "--with-fingerprint",
162 PublicKeyScanner scanner;
163 ExternalProgram prog( argv ,ExternalProgram::Discard_Stderr, false, -1, true );
164 for( std::string line = prog.receiveLine(); !line.empty(); line = prog.receiveLine() )
166 scanner.scan( line );
170 cache_r._data.swap( scanner._keys );
171 MIL << "Found keys: " << cache_r._data << endl;
173 return cache_r._data;
176 mutable CacheMap _cacheMap;
178 ///////////////////////////////////////////////////////////////////
181 ///////////////////////////////////////////////////////////////////
183 // CLASS NAME : KeyRing::Impl
185 /** KeyRing implementation. */
188 Impl( const Pathname & baseTmpDir )
189 : _trusted_tmp_dir( baseTmpDir, "zypp-trusted-kr" )
190 , _general_tmp_dir( baseTmpDir, "zypp-general-kr" )
191 , _base_dir( baseTmpDir )
193 MIL << "Current KeyRing::DefaultAccept: " << _keyRingDefaultAccept << endl;
196 void importKey( const PublicKey & key, bool trusted = false );
197 void multiKeyImport( const Pathname & keyfile_r, bool trusted_r = false );
198 void deleteKey( const std::string & id, bool trusted );
200 std::string readSignatureKeyId( const Pathname & signature );
202 bool isKeyTrusted( const std::string & id )
203 { return bool(publicKeyExists( id, trustedKeyRing() )); }
204 bool isKeyKnown( const std::string & id )
205 { return publicKeyExists( id, trustedKeyRing() ) || publicKeyExists( id, generalKeyRing() ); }
207 std::list<PublicKey> trustedPublicKeys()
208 { return publicKeys( trustedKeyRing() ); }
209 std::list<PublicKey> publicKeys()
210 { return publicKeys( generalKeyRing() ); }
212 const std::list<PublicKeyData> & trustedPublicKeyData()
213 { return publicKeyData( trustedKeyRing() ); }
214 const std::list<PublicKeyData> & publicKeyData()
215 { return publicKeyData( generalKeyRing() ); }
217 void dumpPublicKey( const std::string & id, bool trusted, std::ostream & stream )
218 { dumpPublicKey( id, ( trusted ? trustedKeyRing() : generalKeyRing() ), stream ); }
220 PublicKey exportPublicKey( const PublicKeyData & keyData )
221 { return exportKey( keyData, generalKeyRing() ); }
222 PublicKey exportTrustedPublicKey( const PublicKeyData & keyData )
223 { return exportKey( keyData, trustedKeyRing() ); }
225 bool verifyFileSignatureWorkflow(
226 const Pathname & file,
227 const std::string & filedesc,
228 const Pathname & signature,
229 const KeyContext & keycontext = KeyContext());
231 bool verifyFileSignature( const Pathname & file, const Pathname & signature )
232 { return verifyFile( file, signature, generalKeyRing() ); }
233 bool verifyFileTrustedSignature( const Pathname & file, const Pathname & signature )
234 { return verifyFile( file, signature, trustedKeyRing() ); }
237 bool verifyFile( const Pathname & file, const Pathname & signature, const Pathname & keyring );
238 void importKey( const Pathname & keyfile, const Pathname & keyring );
240 PublicKey exportKey( const std::string & id, const Pathname & keyring );
241 PublicKey exportKey( const PublicKeyData & keyData, const Pathname & keyring );
243 void dumpPublicKey( const std::string & id, const Pathname & keyring, std::ostream & stream );
244 filesystem::TmpFile dumpPublicKeyToTmp( const std::string & id, const Pathname & keyring );
246 void deleteKey( const std::string & id, const Pathname & keyring );
248 std::list<PublicKey> publicKeys( const Pathname & keyring);
249 const std::list<PublicKeyData> & publicKeyData( const Pathname & keyring )
250 { return cachedPublicKeyData( keyring ); }
252 /** Get \ref PublicKeyData for ID (\c false if ID is not found). */
253 PublicKeyData publicKeyExists( const std::string & id, const Pathname & keyring );
255 const Pathname generalKeyRing() const
256 { return _general_tmp_dir.path(); }
257 const Pathname trustedKeyRing() const
258 { return _trusted_tmp_dir.path(); }
260 // Used for trusted and untrusted keyrings
261 filesystem::TmpDir _trusted_tmp_dir;
262 filesystem::TmpDir _general_tmp_dir;
266 /** Functor returning the keyrings data (cached).
268 * const std::list<PublicKeyData> & cachedPublicKeyData( const Pathname & keyring );
271 CachedPublicKeyData cachedPublicKeyData;
273 ///////////////////////////////////////////////////////////////////
276 void KeyRing::Impl::importKey( const PublicKey & key, bool trusted )
278 importKey( key.path(), trusted ? trustedKeyRing() : generalKeyRing() );
282 callback::SendReport<target::rpm::KeyRingSignals> rpmdbEmitSignal;
283 callback::SendReport<KeyRingSignals> emitSignal;
285 rpmdbEmitSignal->trustedKeyAdded( key );
286 emitSignal->trustedKeyAdded( key );
290 void KeyRing::Impl::multiKeyImport( const Pathname & keyfile_r, bool trusted_r )
292 importKey( keyfile_r, trusted_r ? trustedKeyRing() : generalKeyRing() );
295 void KeyRing::Impl::deleteKey( const std::string & id, bool trusted )
301 key = exportKey( id, trustedKeyRing() );
304 deleteKey( id, trusted ? trustedKeyRing() : generalKeyRing() );
308 callback::SendReport<target::rpm::KeyRingSignals> rpmdbEmitSignal;
309 callback::SendReport<KeyRingSignals> emitSignal;
311 rpmdbEmitSignal->trustedKeyRemoved( key );
312 emitSignal->trustedKeyRemoved( key );
316 PublicKeyData KeyRing::Impl::publicKeyExists( const std::string & id, const Pathname & keyring )
318 MIL << "Searching key [" << id << "] in keyring " << keyring << endl;
319 const std::list<PublicKeyData> & keys( publicKeyData( keyring ) );
320 for_( it, keys.begin(), keys.end() )
322 if ( id == (*it).id() )
327 return PublicKeyData();
330 PublicKey KeyRing::Impl::exportKey( const PublicKeyData & keyData, const Pathname & keyring )
332 return PublicKey( dumpPublicKeyToTmp( keyData.id(), keyring ), keyData );
335 PublicKey KeyRing::Impl::exportKey( const std::string & id, const Pathname & keyring )
337 PublicKeyData keyData( publicKeyExists( id, keyring ) );
339 return PublicKey( dumpPublicKeyToTmp( keyData.id(), keyring ), keyData );
341 // Here: key not found
342 WAR << "No key " << id << " to export from " << keyring << endl;
347 void KeyRing::Impl::dumpPublicKey( const std::string & id, const Pathname & keyring, std::ostream & stream )
354 "--homedir", keyring.asString().c_str(),
355 "--no-default-keyring",
359 "--no-permission-warning",
364 ExternalProgram prog( argv,ExternalProgram::Discard_Stderr, false, -1, true );
365 for ( std::string line = prog.receiveLine(); !line.empty(); line = prog.receiveLine() )
372 filesystem::TmpFile KeyRing::Impl::dumpPublicKeyToTmp( const std::string & id, const Pathname & keyring )
374 filesystem::TmpFile tmpFile( _base_dir, "pubkey-"+id+"-" );
375 MIL << "Going to export key " << id << " from " << keyring << " to " << tmpFile.path() << endl;
377 std::ofstream os( tmpFile.path().c_str() );
378 dumpPublicKey( id, keyring, os );
383 bool KeyRing::Impl::verifyFileSignatureWorkflow(
384 const Pathname & file,
385 const std::string & filedesc,
386 const Pathname & signature,
387 const KeyContext & context )
389 callback::SendReport<KeyRingReport> report;
390 MIL << "Going to verify signature for " << filedesc << " ( " << file << " ) with " << signature << endl;
392 // if signature does not exists, ask user if he wants to accept unsigned file.
393 if( signature.empty() || (!PathInfo( signature ).isExist()) )
395 bool res = report->askUserToAcceptUnsignedFile( filedesc, context );
396 MIL << "User decision on unsigned file: " << res << endl;
400 // get the id of the signature
401 std::string id = readSignatureKeyId( signature );
403 // doeskey exists in trusted keyring
404 PublicKeyData trustedKeyData( publicKeyExists( id, trustedKeyRing() ) );
405 if ( trustedKeyData )
407 MIL << "Key is trusted: " << trustedKeyData << endl;
409 // lets look if there is an updated key in the
411 PublicKeyData generalKeyData( publicKeyExists( id, generalKeyRing() ) );
412 if ( generalKeyData )
414 // bnc #393160: Comment #30: Compare at least the fingerprint
415 // in case an attacker created a key the the same id.
416 if ( trustedKeyData.fingerprint() == generalKeyData.fingerprint()
417 && trustedKeyData.created() < generalKeyData.created() )
419 MIL << "Key was updated. Saving new version into trusted keyring: " << generalKeyData << endl;
420 importKey( exportKey( generalKeyData, generalKeyRing() ), true );
421 trustedKeyData = generalKeyData = PublicKeyData(); // invalidated by import.
425 if ( ! trustedKeyData ) // invalidated by previous import
426 trustedKeyData = publicKeyExists( id, trustedKeyRing() );
427 report->infoVerify( filedesc, trustedKeyData, context );
429 // it exists, is trusted, does it validates?
430 if ( verifyFile( file, signature, trustedKeyRing() ) )
434 return report->askUserToAcceptVerificationFailed( filedesc, exportKey( trustedKeyData, trustedKeyRing() ), context );
439 PublicKeyData generalKeyData( publicKeyExists( id, generalKeyRing() ) );
440 if ( generalKeyData )
442 PublicKey key( exportKey( generalKeyData, generalKeyRing() ) );
443 MIL << "Exported key " << id << " to " << key.path() << endl;
444 MIL << "Key " << id << " " << key.name() << " is not trusted" << endl;
446 // ok the key is not trusted, ask the user to trust it or not
447 KeyRingReport::KeyTrust reply = report->askUserToAcceptKey( key, context );
448 if ( reply == KeyRingReport::KEY_TRUST_TEMPORARILY ||
449 reply == KeyRingReport::KEY_TRUST_AND_IMPORT )
451 MIL << "User wants to trust key " << id << " " << key.name() << endl;
452 //dumpFile( unKey.path() );
454 Pathname whichKeyring;
455 if ( reply == KeyRingReport::KEY_TRUST_AND_IMPORT )
457 MIL << "User wants to import key " << id << " " << key.name() << endl;
458 importKey( key, true );
459 whichKeyring = trustedKeyRing();
462 whichKeyring = generalKeyRing();
465 if ( verifyFile( file, signature, whichKeyring ) )
467 MIL << "File signature is verified" << endl;
472 MIL << "File signature check fails" << endl;
473 if ( report->askUserToAcceptVerificationFailed( filedesc, key, context ) )
475 MIL << "User continues anyway." << endl;
480 MIL << "User does not want to continue" << endl;
487 MIL << "User does not want to trust key " << id << " " << key.name() << endl;
494 MIL << "File [" << file << "] ( " << filedesc << " ) signed with unknown key [" << id << "]" << endl;
495 if ( report->askUserToAcceptUnknownKey( filedesc, id, context ) )
497 MIL << "User wants to accept unknown key " << id << endl;
502 MIL << "User does not want to accept unknown key " << id << endl;
510 std::list<PublicKey> KeyRing::Impl::publicKeys( const Pathname & keyring )
512 const std::list<PublicKeyData> & keys( publicKeyData( keyring ) );
513 std::list<PublicKey> ret;
515 for_( it, keys.begin(), keys.end() )
517 PublicKey key( exportKey( *it, keyring ) );
518 ret.push_back( key );
519 MIL << "Found key " << key << endl;
524 void KeyRing::Impl::importKey( const Pathname & keyfile, const Pathname & keyring )
526 if ( ! PathInfo( keyfile ).isExist() )
527 // TranslatorExplanation first %s is key name, second is keyring name
528 ZYPP_THROW(KeyRingException(boost::str(boost::format(
529 _("Tried to import not existent key %s into keyring %s"))
530 % keyfile.asString() % keyring.asString())));
536 "--homedir", keyring.asString().c_str(),
537 "--no-default-keyring",
541 "--no-permission-warning",
543 keyfile.asString().c_str(),
547 ExternalProgram prog( argv,ExternalProgram::Discard_Stderr, false, -1, true );
551 void KeyRing::Impl::deleteKey( const std::string & id, const Pathname & keyring )
557 "--homedir", keyring.asString().c_str(),
558 "--no-default-keyring",
568 ExternalProgram prog( argv,ExternalProgram::Discard_Stderr, false, -1, true );
570 int code = prog.close();
572 ZYPP_THROW(Exception(_("Failed to delete key.")));
574 MIL << "Deleted key " << id << " from keyring " << keyring << endl;
578 std::string KeyRing::Impl::readSignatureKeyId( const Pathname & signature )
580 if ( ! PathInfo( signature ).isFile() )
581 ZYPP_THROW(Exception(boost::str(boost::format(
582 _("Signature file %s not found"))% signature.asString())));
584 MIL << "Determining key id if signature " << signature << endl;
585 // HACK create a tmp keyring with no keys
586 filesystem::TmpDir dir( _base_dir, "fake-keyring" );
587 std::string tmppath( dir.path().asString() );
592 "--homedir", tmppath.c_str(),
593 "--no-default-keyring",
599 signature.asString().c_str(),
603 ExternalProgram prog( argv,ExternalProgram::Discard_Stderr, false, -1, true );
608 str::regex rxNoKey( "^\\[GNUPG:\\] NO_PUBKEY (.+)\n$" );
610 for( line = prog.receiveLine(), count=0; !line.empty(); line = prog.receiveLine(), count++ )
612 //MIL << "[" << line << "]" << endl;
614 if( str::regex_match( line, what, rxNoKey ) )
616 if ( what.size() >= 1 )
621 //dumpRegexpResults( what );
627 MIL << "no output" << endl;
630 MIL << "Determined key id [" << id << "] for signature " << signature << endl;
635 bool KeyRing::Impl::verifyFile( const Pathname & file, const Pathname & signature, const Pathname & keyring )
641 "--homedir", keyring.asString().c_str(),
642 "--no-default-keyring",
648 signature.asString().c_str(),
649 file.asString().c_str(),
653 // no need to parse output for now
654 // [GNUPG:] SIG_ID yCc4u223XRJnLnVAIllvYbUd8mQ 2006-03-29 1143618744
655 // [GNUPG:] GOODSIG A84EDAE89C800ACA SuSE Package Signing Key <build@suse.de>
656 // gpg: Good signature from "SuSE Package Signing Key <build@suse.de>"
657 // [GNUPG:] VALIDSIG 79C179B2E1C820C1890F9994A84EDAE89C800ACA 2006-03-29 1143618744 0 3 0 17 2 00 79C179B2E1C820C1890F9994A84EDAE89C800ACA
658 // [GNUPG:] TRUST_UNDEFINED
660 // [GNUPG:] ERRSIG A84EDAE89C800ACA 17 2 00 1143618744 9
661 // [GNUPG:] NO_PUBKEY A84EDAE89C800ACA
663 ExternalProgram prog( argv,ExternalProgram::Discard_Stderr, false, -1, true );
665 return ( prog.close() == 0 ) ? true : false;
668 ///////////////////////////////////////////////////////////////////
670 ///////////////////////////////////////////////////////////////////
672 // CLASS NAME : KeyRing
674 ///////////////////////////////////////////////////////////////////
676 KeyRing::KeyRing( const Pathname & baseTmpDir )
677 : _pimpl( new Impl( baseTmpDir ) )
684 void KeyRing::importKey( const PublicKey & key, bool trusted )
685 { _pimpl->importKey( key, trusted ); }
687 void KeyRing::multiKeyImport( const Pathname & keyfile_r, bool trusted_r )
688 { _pimpl->multiKeyImport( keyfile_r, trusted_r ); }
690 std::string KeyRing::readSignatureKeyId( const Pathname & signature )
691 { return _pimpl->readSignatureKeyId( signature ); }
693 void KeyRing::deleteKey( const std::string & id, bool trusted )
694 { _pimpl->deleteKey( id, trusted ); }
696 std::list<PublicKey> KeyRing::publicKeys()
697 { return _pimpl->publicKeys(); }
699 std:: list<PublicKey> KeyRing::trustedPublicKeys()
700 { return _pimpl->trustedPublicKeys(); }
702 std::list<PublicKeyData> KeyRing::publicKeyData()
703 { return _pimpl->publicKeyData(); }
705 std::list<PublicKeyData> KeyRing::trustedPublicKeyData()
706 { return _pimpl->trustedPublicKeyData(); }
708 bool KeyRing::verifyFileSignatureWorkflow(
709 const Pathname & file,
710 const std::string filedesc,
711 const Pathname & signature,
712 const KeyContext & keycontext )
713 { return _pimpl->verifyFileSignatureWorkflow( file, filedesc, signature, keycontext ); }
715 bool KeyRing::verifyFileSignature( const Pathname & file, const Pathname & signature )
716 { return _pimpl->verifyFileSignature( file, signature ); }
718 bool KeyRing::verifyFileTrustedSignature( const Pathname & file, const Pathname & signature )
719 { return _pimpl->verifyFileTrustedSignature( file, signature ); }
721 void KeyRing::dumpPublicKey( const std::string & id, bool trusted, std::ostream & stream )
722 { _pimpl->dumpPublicKey( id, trusted, stream ); }
724 PublicKey KeyRing::exportPublicKey( const PublicKeyData & keyData )
725 { return _pimpl->exportPublicKey( keyData ); }
727 PublicKey KeyRing::exportTrustedPublicKey( const PublicKeyData & keyData )
728 { return _pimpl->exportTrustedPublicKey( keyData ); }
730 bool KeyRing::isKeyTrusted( const std::string & id )
731 { return _pimpl->isKeyTrusted( id ); }
733 bool KeyRing::isKeyKnown( const std::string & id )
734 { return _pimpl->isKeyKnown( id ); }
736 /////////////////////////////////////////////////////////////////
738 ///////////////////////////////////////////////////////////////////