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/Logger.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/PathInfo.h"
30 #include "zypp/KeyRing.h"
31 #include "zypp/ExternalProgram.h"
32 #include "zypp/TmpPath.h"
35 using namespace zypp::filesystem;
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);
48 static bool printLine( const string &line )
54 ///////////////////////////////////////////////////////////////////
58 KeyRingReport::DefaultAccept _keyRingDefaultAccept( KeyRingReport::ACCEPT_NOTHING );
61 KeyRingReport::DefaultAccept KeyRingReport::defaultAccept()
62 { return _keyRingDefaultAccept; }
64 void KeyRingReport::setDefaultAccept( DefaultAccept value_r )
66 MIL << "Set new default accept: " << value_r << endl;
67 _keyRingDefaultAccept = value_r;
70 ///////////////////////////////////////////////////////////////////
72 bool KeyRingReport::askUserToAcceptUnsignedFile( const string &file )
73 { return _keyRingDefaultAccept.testFlag( ACCEPT_UNSIGNED_FILE ); }
75 bool KeyRingReport::askUserToAcceptUnknownKey( const string &file, const string &id )
76 { return _keyRingDefaultAccept.testFlag( ACCEPT_UNKNOWNKEY ); }
78 bool KeyRingReport::askUserToTrustKey( const PublicKey &key )
79 { return _keyRingDefaultAccept.testFlag( TRUST_KEY ); }
81 bool KeyRingReport::askUserToImportKey( const PublicKey &key)
82 { return _keyRingDefaultAccept.testFlag( IMPORT_KEY ); }
84 bool KeyRingReport::askUserToAcceptVerificationFailed( const string &file, const PublicKey &key )
85 { return _keyRingDefaultAccept.testFlag( ACCEPT_VERIFICATION_FAILED ); }
87 ///////////////////////////////////////////////////////////////////
89 // CLASS NAME : KeyRing::Impl
91 /** KeyRing implementation. */
94 Impl(const Pathname &baseTmpDir)
95 : _trusted_tmp_dir(baseTmpDir, "zypp-trusted-kr")
96 , _general_tmp_dir(baseTmpDir, "zypp-general-kr")
97 , _base_dir( baseTmpDir )
103 Impl( const Pathname &general_kr, const Pathname &trusted_kr )
105 filesystem::assert_dir(general_kr);
106 filesystem::assert_dir(trusted_kr);
108 generalKeyRing() = general_kr;
109 trustedKeyRing() = trusted_kr;
113 void importKey( const PublicKey &key, bool trusted = false);
114 void deleteKey( const string &id, bool trusted );
116 string readSignatureKeyId( const Pathname &signature );
118 bool isKeyTrusted( const string &id);
119 bool isKeyKnown( const string &id );
121 list<PublicKey> trustedPublicKeys();
122 list<PublicKey> publicKeys();
124 list<string> trustedPublicKeyIds();
125 list<string> publicKeyIds();
127 void dumpPublicKey( const string &id, bool trusted, ostream &stream );
129 bool verifyFileSignatureWorkflow( const Pathname &file, const string filedesc, const Pathname &signature);
131 bool verifyFileSignature( const Pathname &file, const Pathname &signature);
132 bool verifyFileTrustedSignature( const Pathname &file, const Pathname &signature);
134 //mutable map<Locale, string> translations;
135 bool verifyFile( const Pathname &file, const Pathname &signature, const Pathname &keyring);
136 void importKey( const Pathname &keyfile, const Pathname &keyring);
137 PublicKey exportKey( string id, const Pathname &keyring);
138 void dumpPublicKey( const string &id, const Pathname &keyring, ostream &stream );
139 void deleteKey( const string &id, const Pathname &keyring );
141 list<PublicKey> publicKeys(const Pathname &keyring);
142 list<string> publicKeyIds(const Pathname &keyring);
144 bool publicKeyExists( string id, const Pathname &keyring);
146 const Pathname generalKeyRing() const;
147 const Pathname trustedKeyRing() const;
149 // Used for trusted and untrusted keyrings
150 TmpDir _trusted_tmp_dir;
151 TmpDir _general_tmp_dir;
154 /** Offer default Impl. */
155 static shared_ptr<Impl> nullimpl()
157 static shared_ptr<Impl> _nullimpl( new Impl( TmpPath::defaultLocation() ) );
162 friend Impl * rwcowClone<Impl>( const Impl * rhs );
163 /** clone for RWCOW_pointer */
165 { return new Impl( *this ); }
169 const Pathname KeyRing::Impl::generalKeyRing() const
171 return _general_tmp_dir.path();
174 const Pathname KeyRing::Impl::trustedKeyRing() const
176 return _trusted_tmp_dir.path();
179 void KeyRing::Impl::importKey( const PublicKey &key, bool trusted)
181 callback::SendReport<target::rpm::KeyRingSignals> rpmdbEmitSignal;
182 callback::SendReport<KeyRingSignals> emitSignal;
184 importKey( key.path(), trusted ? trustedKeyRing() : generalKeyRing() );
188 rpmdbEmitSignal->trustedKeyAdded( key );
189 emitSignal->trustedKeyAdded( key );
193 void KeyRing::Impl::deleteKey( const string &id, bool trusted)
199 key = exportKey(id, trustedKeyRing());
202 deleteKey( id, trusted ? trustedKeyRing() : generalKeyRing() );
206 callback::SendReport<target::rpm::KeyRingSignals> rpmdbEmitSignal;
207 callback::SendReport<KeyRingSignals> emitSignal;
209 rpmdbEmitSignal->trustedKeyRemoved( key );
210 emitSignal->trustedKeyRemoved( key );
214 list<PublicKey> KeyRing::Impl::publicKeys()
216 return publicKeys( generalKeyRing() );
219 list<PublicKey> KeyRing::Impl::trustedPublicKeys()
221 return publicKeys( trustedKeyRing() );
224 list<string> KeyRing::Impl::publicKeyIds()
226 return publicKeyIds( generalKeyRing() );
229 list<string> KeyRing::Impl::trustedPublicKeyIds()
231 return publicKeyIds( trustedKeyRing() );
234 bool KeyRing::Impl::verifyFileTrustedSignature( const Pathname &file, const Pathname &signature)
236 return verifyFile( file, signature, trustedKeyRing() );
239 bool KeyRing::Impl::verifyFileSignature( const Pathname &file, const Pathname &signature)
241 return verifyFile( file, signature, generalKeyRing() );
244 bool KeyRing::Impl::isKeyTrusted( const string &id)
246 return publicKeyExists( id, trustedKeyRing() );
249 bool KeyRing::Impl::isKeyKnown( const string &id )
252 if ( publicKeyExists( id, trustedKeyRing() ) )
255 return publicKeyExists( id, generalKeyRing() );
258 bool KeyRing::Impl::publicKeyExists( string id, const Pathname &keyring)
260 MIL << "Searching key [" << id << "] in keyring " << keyring << endl;
261 list<PublicKey> keys = publicKeys(keyring);
262 for (list<PublicKey>::const_iterator it = keys.begin(); it != keys.end(); it++)
264 if ( id == (*it).id() )
271 PublicKey KeyRing::Impl::exportKey( string id, const Pathname &keyring)
273 TmpFile tmp_file( _base_dir, "pubkey-"+id+"-" );
274 Pathname keyfile = tmp_file.path();
275 MIL << "Going to export key " << id << " from " << keyring << " to " << keyfile << endl;
278 ofstream os(keyfile.asString().c_str());
279 dumpPublicKey( id, keyring, os );
281 PublicKey key(keyfile);
284 catch (BadKeyException &e)
286 ERR << "Cannot create public key " << id << " from " << keyring << " keyring to file " << e.keyFile() << endl;
287 // TranslatorExplanation first %s is key name, second is keyring name
288 // and third is keyfile name
289 ZYPP_THROW(Exception(boost::str(boost::format(
290 _("Cannot create public key %s from %s keyring to file %s"))
291 % id % keyring.asString() % e.keyFile().asString())));
295 ERR << "Cannot export key " << id << " from " << keyring << " keyring to file " << keyfile << endl;
300 void KeyRing::Impl::dumpPublicKey( const string &id, bool trusted, ostream &stream )
302 dumpPublicKey( id, ( trusted ? trustedKeyRing() : generalKeyRing() ), stream );
305 void KeyRing::Impl::dumpPublicKey( const string &id, const Pathname &keyring, ostream &stream )
310 "--no-default-keyring",
314 "--no-permission-warning",
317 keyring.asString().c_str(),
323 ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
326 for(line = prog.receiveLine(), count=0; !line.empty(); line = prog.receiveLine(), count++ )
334 bool KeyRing::Impl::verifyFileSignatureWorkflow( const Pathname &file, const string filedesc, const Pathname &signature)
336 callback::SendReport<KeyRingReport> report;
337 //callback::SendReport<KeyRingSignals> emitSignal;
338 MIL << "Going to verify signature for " << file << " with " << signature << endl;
340 // if signature does not exists, ask user if he wants to accept unsigned file.
341 if( signature.empty() || (!PathInfo(signature).isExist()) )
343 bool res = report->askUserToAcceptUnsignedFile( filedesc );
344 MIL << "User decision on unsigned file: " << res << endl;
348 // get the id of the signature
349 string id = readSignatureKeyId(signature);
351 // doeskey exists in trusted keyring
352 if ( publicKeyExists( id, trustedKeyRing() ) )
354 PublicKey key = exportKey( id, trustedKeyRing() );
356 // lets look if there is an updated key in the
358 if ( publicKeyExists( id, generalKeyRing() ) )
360 // bnc #393160: Comment #30: Compare at least the fingerprint
361 // in case an attacker created a key the the same id.
362 PublicKey untkey = exportKey( id, generalKeyRing() );
363 if ( untkey.fingerprint() == key.fingerprint()
364 && untkey.created() > key.created() )
366 MIL << "Key " << key << " was updated. Saving new version into trusted keyring." << endl;
367 importKey( untkey, true );
372 MIL << "Key " << id << " " << key.name() << " is trusted" << endl;
373 // it exists, is trusted, does it validates?
374 if ( verifyFile( file, signature, trustedKeyRing() ) )
377 return report->askUserToAcceptVerificationFailed( filedesc, key );
381 if ( publicKeyExists( id, generalKeyRing() ) )
383 PublicKey key = exportKey( id, generalKeyRing());
384 MIL << "Exported key " << id << " to " << key.path() << endl;
385 MIL << "Key " << id << " " << key.name() << " is not trusted" << endl;
386 // ok the key is not trusted, ask the user to trust it or not
387 #warning We need the key details passed to the callback
388 if ( report->askUserToTrustKey( key ) )
390 MIL << "User wants to trust key " << id << " " << key.name() << endl;
391 //dumpFile(unKey.path());
393 Pathname which_keyring;
394 if ( report->askUserToImportKey( key ) )
396 MIL << "User wants to import key " << id << " " << key.name() << endl;
397 importKey( key, true );
398 which_keyring = trustedKeyRing();
402 which_keyring = generalKeyRing();
406 if ( verifyFile( file, signature, which_keyring ) )
408 MIL << "File signature is verified" << endl;
413 MIL << "File signature check fails" << endl;
414 if ( report->askUserToAcceptVerificationFailed( filedesc, key ) )
416 MIL << "User continues anyway." << endl;
421 MIL << "User does not want to continue" << endl;
428 MIL << "User does not want to trust key " << id << " " << key.name() << endl;
435 MIL << "File [" << file << "] ( " << filedesc << " ) signed with unknown key [" << id << "]" << endl;
436 if ( report->askUserToAcceptUnknownKey( filedesc, id ) )
438 MIL << "User wants to accept unknown key " << id << endl;
443 MIL << "User does not want to accept unknown key " << id << endl;
451 list<string> KeyRing::Impl::publicKeyIds(const Pathname &keyring)
453 static str::regex rxColons("^([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):\n$");
454 static str::regex rxColonsFpr("^([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):\n$");
461 "--no-default-keyring",
463 "--list-public-keys",
465 "--with-fingerprint",
472 keyring.asString().c_str(),
476 ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
480 for(line = prog.receiveLine(), count=0; !line.empty(); line = prog.receiveLine(), count++ )
482 //MIL << line << endl;
484 if(str::regex_match(line, what, rxColons))
488 if ( what[1] == "pub" )
493 for(line2 = prog.receiveLine(); !line2.empty(); line2 = prog.receiveLine(), count++ )
496 if (str::regex_match(line2, what2, rxColonsFpr))
498 if ( (what2[1] == "fpr") && (what2[1] != "pub") && (what2[1] !="sub"))
500 fingerprint = what2[10];
507 MIL << "Found key " << "[" << id << "]" << endl;
509 //dumpRegexpResults(what);
516 list<PublicKey> KeyRing::Impl::publicKeys(const Pathname &keyring)
519 list<PublicKey> keys;
521 list<string> ids = publicKeyIds(keyring);
523 for ( list<string>::const_iterator it = ids.begin(); it != ids.end(); ++it )
525 PublicKey key(exportKey( *it, keyring ));
527 MIL << "Found key " << "[" << key.id() << "]" << " [" << key.name() << "]" << " [" << key.fingerprint() << "]" << endl;
532 void KeyRing::Impl::importKey( const Pathname &keyfile, const Pathname &keyring)
534 if ( ! PathInfo(keyfile).isExist() )
535 // TranslatorExplanation first %s is key name, second is keyring name
536 ZYPP_THROW(KeyRingException(boost::str(boost::format(
537 _("Tried to import not existant key %s into keyring %s"))
538 % keyfile.asString() % keyring.asString())));
543 "--no-default-keyring",
547 "--no-permission-warning",
551 keyring.asString().c_str(),
553 keyfile.asString().c_str(),
558 ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
562 // ZYPP_THROW(Exception("failed to import key"));
565 void KeyRing::Impl::deleteKey( const string &id, const Pathname &keyring )
570 "--no-default-keyring",
578 keyring.asString().c_str(),
584 ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
586 int code = prog.close();
588 ZYPP_THROW(Exception(_("Failed to delete key.")));
590 MIL << "Deleted key " << id << " from keyring " << keyring << endl;
594 string KeyRing::Impl::readSignatureKeyId(const Pathname &signature )
596 if ( ! PathInfo(signature).isFile() )
597 ZYPP_THROW(Exception(boost::str(boost::format(
598 _("Signature file %s not found"))% signature.asString())));
600 MIL << "Determining key id if signature " << signature << endl;
601 // HACK create a tmp keyring with no keys
602 TmpDir dir(_base_dir, "fake-keyring");
607 "--no-default-keyring",
615 dir.path().asString().c_str(),
616 signature.asString().c_str(),
620 ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
625 str::regex rxNoKey("^\\[GNUPG:\\] NO_PUBKEY (.+)\n$");
627 for(line = prog.receiveLine(), count=0; !line.empty(); line = prog.receiveLine(), count++ )
629 //MIL << "[" << line << "]" << endl;
631 if(str::regex_match(line, what, rxNoKey))
633 if ( what.size() >= 1 )
635 //dumpRegexpResults(what);
639 MIL << "'" << line << "'" << endl;
645 MIL << "no output" << endl;
648 MIL << "Determined key id [" << id << "] for signature " << signature << endl;
653 bool KeyRing::Impl::verifyFile( const Pathname &file, const Pathname &signature, const Pathname &keyring)
658 "--no-default-keyring",
666 keyring.asString().c_str(),
668 signature.asString().c_str(),
669 file.asString().c_str(),
673 // no need to parse output for now
674 // [GNUPG:] SIG_ID yCc4u223XRJnLnVAIllvYbUd8mQ 2006-03-29 1143618744
675 // [GNUPG:] GOODSIG A84EDAE89C800ACA SuSE Package Signing Key <build@suse.de>
676 // gpg: Good signature from "SuSE Package Signing Key <build@suse.de>"
677 // [GNUPG:] VALIDSIG 79C179B2E1C820C1890F9994A84EDAE89C800ACA 2006-03-29 1143618744 0 3 0 17 2 00 79C179B2E1C820C1890F9994A84EDAE89C800ACA
678 // [GNUPG:] TRUST_UNDEFINED
680 // [GNUPG:] ERRSIG A84EDAE89C800ACA 17 2 00 1143618744 9
681 // [GNUPG:] NO_PUBKEY A84EDAE89C800ACA
683 ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
685 return (prog.close() == 0) ? true : false;
688 ///////////////////////////////////////////////////////////////////
690 ///////////////////////////////////////////////////////////////////
692 // CLASS NAME : KeyRing
694 ///////////////////////////////////////////////////////////////////
696 ///////////////////////////////////////////////////////////////////
698 // METHOD NAME : KeyRing::KeyRing
699 // METHOD TYPE : Ctor
701 KeyRing::KeyRing(const Pathname &baseTmpDir)
702 : _pimpl( new Impl(baseTmpDir) )
705 ///////////////////////////////////////////////////////////////////
707 // METHOD NAME : KeyRing::KeyRing
708 // METHOD TYPE : Ctor
710 //KeyRing::KeyRing( const Pathname &general_kr, const Pathname &trusted_kr )
711 //: _pimpl( new Impl(general_kr, trusted_kr) )
714 ///////////////////////////////////////////////////////////////////
716 // METHOD NAME : KeyRing::~KeyRing
717 // METHOD TYPE : Dtor
722 ///////////////////////////////////////////////////////////////////
724 // Forward to implementation:
726 ///////////////////////////////////////////////////////////////////
729 void KeyRing::importKey( const PublicKey &key, bool trusted )
731 _pimpl->importKey( key, trusted );
734 string KeyRing::readSignatureKeyId( const Pathname &signature )
736 return _pimpl->readSignatureKeyId(signature);
739 void KeyRing::deleteKey( const string &id, bool trusted )
741 _pimpl->deleteKey(id, trusted);
744 list<PublicKey> KeyRing::publicKeys()
746 return _pimpl->publicKeys();
749 list<PublicKey> KeyRing::trustedPublicKeys()
751 return _pimpl->trustedPublicKeys();
754 list<string> KeyRing::publicKeyIds()
756 return _pimpl->publicKeyIds();
759 list<string> KeyRing::trustedPublicKeyIds()
761 return _pimpl->trustedPublicKeyIds();
764 bool KeyRing::verifyFileSignatureWorkflow( const Pathname &file, const string filedesc, const Pathname &signature)
766 return _pimpl->verifyFileSignatureWorkflow(file, filedesc, signature);
769 bool KeyRing::verifyFileSignature( const Pathname &file, const Pathname &signature)
771 return _pimpl->verifyFileSignature(file, signature);
774 bool KeyRing::verifyFileTrustedSignature( const Pathname &file, const Pathname &signature)
776 return _pimpl->verifyFileTrustedSignature(file, signature);
779 void KeyRing::dumpPublicKey( const string &id, bool trusted, ostream &stream )
781 _pimpl->dumpPublicKey( id, trusted, stream);
784 bool KeyRing::isKeyTrusted( const string &id )
786 return _pimpl->isKeyTrusted(id);
789 bool KeyRing::isKeyKnown( const string &id )
791 return _pimpl->isKeyKnown(id);
794 /////////////////////////////////////////////////////////////////
796 ///////////////////////////////////////////////////////////////////