00001
00002
00003
00004
00005
00006
00007
00008
00012 #include <iostream>
00013 #include <fstream>
00014 #include <sys/file.h>
00015 #include <cstdio>
00016 #include <unistd.h>
00017
00018 #include "zypp/TmpPath.h"
00019 #include "zypp/ZYppFactory.h"
00020 #include "zypp/ZYpp.h"
00021
00022 #include "zypp/base/Logger.h"
00023 #include "zypp/base/IOStream.h"
00024 #include "zypp/base/String.h"
00025 #include "zypp/base/Regex.h"
00026 #include "zypp/PathInfo.h"
00027 #include "zypp/KeyRing.h"
00028 #include "zypp/ExternalProgram.h"
00029 #include "zypp/TmpPath.h"
00030
00031 using namespace std;
00032 using namespace zypp::filesystem;
00033
00034 #undef ZYPP_BASE_LOGGER_LOGGROUP
00035 #define ZYPP_BASE_LOGGER_LOGGROUP "zypp::KeyRing"
00036
00037 #define GPG_BINARY "/usr/bin/gpg2"
00038
00040 namespace zypp
00041 {
00042
00043 IMPL_PTR_TYPE(KeyRing);
00044
00045 static bool printLine( const string &line )
00046 {
00047 MIL << line << endl;
00048 return true;
00049 }
00050
00051 namespace
00052 {
00053 bool _keyRingDefaultAccept( getenv("ZYPP_KEYRING_DEFAULT_ACCEPT_ALL") );
00054 }
00055
00056 bool KeyRingReport::askUserToAcceptUnsignedFile( const string &file )
00057 { return _keyRingDefaultAccept; }
00058
00059 bool KeyRingReport::askUserToAcceptUnknownKey( const string &file, const string &id )
00060 { return _keyRingDefaultAccept; }
00061
00062 bool KeyRingReport::askUserToTrustKey( const PublicKey &key )
00063 { return _keyRingDefaultAccept; }
00064
00065 bool KeyRingReport::askUserToImportKey( const PublicKey &key)
00066 { return _keyRingDefaultAccept; }
00067
00068 bool KeyRingReport::askUserToAcceptVerificationFailed( const string &file, const PublicKey &key )
00069 { return _keyRingDefaultAccept; }
00070
00072
00073
00074
00076 struct KeyRing::Impl
00077 {
00078 Impl(const Pathname &baseTmpDir)
00079 : _trusted_tmp_dir(baseTmpDir, "zypp-trusted-kr")
00080 , _general_tmp_dir(baseTmpDir, "zypp-general-kr")
00081 , _base_dir( baseTmpDir )
00082
00083 {
00084 }
00085
00086
00087
00088
00089
00090
00091
00092
00093
00094
00095
00096
00097 void importKey( const PublicKey &key, bool trusted = false);
00098 void deleteKey( const string &id, bool trusted );
00099
00100 string readSignatureKeyId( const Pathname &signature );
00101
00102 bool isKeyTrusted( const string &id);
00103 bool isKeyKnown( const string &id );
00104
00105 list<PublicKey> trustedPublicKeys();
00106 list<PublicKey> publicKeys();
00107
00108 list<string> trustedPublicKeyIds();
00109 list<string> publicKeyIds();
00110
00111 void dumpPublicKey( const string &id, bool trusted, ostream &stream );
00112
00113 bool verifyFileSignatureWorkflow( const Pathname &file, const string filedesc, const Pathname &signature);
00114
00115 bool verifyFileSignature( const Pathname &file, const Pathname &signature);
00116 bool verifyFileTrustedSignature( const Pathname &file, const Pathname &signature);
00117 private:
00118
00119 bool verifyFile( const Pathname &file, const Pathname &signature, const Pathname &keyring);
00120 void importKey( const Pathname &keyfile, const Pathname &keyring);
00121 PublicKey exportKey( string id, const Pathname &keyring);
00122 void dumpPublicKey( const string &id, const Pathname &keyring, ostream &stream );
00123 void deleteKey( const string &id, const Pathname &keyring );
00124
00125 list<PublicKey> publicKeys(const Pathname &keyring);
00126 list<string> publicKeyIds(const Pathname &keyring);
00127
00128 bool publicKeyExists( string id, const Pathname &keyring);
00129
00130 const Pathname generalKeyRing() const;
00131 const Pathname trustedKeyRing() const;
00132
00133
00134 TmpDir _trusted_tmp_dir;
00135 TmpDir _general_tmp_dir;
00136 Pathname _base_dir;
00137 public:
00139 static shared_ptr<Impl> nullimpl()
00140 {
00141 static shared_ptr<Impl> _nullimpl( new Impl( TmpPath::defaultLocation() ) );
00142 return _nullimpl;
00143 }
00144
00145 private:
00146 friend Impl * rwcowClone<Impl>( const Impl * rhs );
00148 Impl * clone() const
00149 { return new Impl( *this ); }
00150 };
00151
00152
00153 const Pathname KeyRing::Impl::generalKeyRing() const
00154 {
00155 return _general_tmp_dir.path();
00156 }
00157
00158 const Pathname KeyRing::Impl::trustedKeyRing() const
00159 {
00160 return _trusted_tmp_dir.path();
00161 }
00162
00163 void KeyRing::Impl::importKey( const PublicKey &key, bool trusted)
00164 {
00165 callback::SendReport<target::rpm::KeyRingSignals> rpmdbEmitSignal;
00166 callback::SendReport<KeyRingSignals> emitSignal;
00167
00168 importKey( key.path(), trusted ? trustedKeyRing() : generalKeyRing() );
00169
00170 if ( trusted )
00171 {
00172 rpmdbEmitSignal->trustedKeyAdded( key );
00173 emitSignal->trustedKeyAdded( key );
00174 }
00175 }
00176
00177 void KeyRing::Impl::deleteKey( const string &id, bool trusted)
00178 {
00179 deleteKey( id, trusted ? trustedKeyRing() : generalKeyRing() );
00180 }
00181
00182 list<PublicKey> KeyRing::Impl::publicKeys()
00183 {
00184 return publicKeys( generalKeyRing() );
00185 }
00186
00187 list<PublicKey> KeyRing::Impl::trustedPublicKeys()
00188 {
00189 return publicKeys( trustedKeyRing() );
00190 }
00191
00192 list<string> KeyRing::Impl::publicKeyIds()
00193 {
00194 return publicKeyIds( generalKeyRing() );
00195 }
00196
00197 list<string> KeyRing::Impl::trustedPublicKeyIds()
00198 {
00199 return publicKeyIds( trustedKeyRing() );
00200 }
00201
00202 bool KeyRing::Impl::verifyFileTrustedSignature( const Pathname &file, const Pathname &signature)
00203 {
00204 return verifyFile( file, signature, trustedKeyRing() );
00205 }
00206
00207 bool KeyRing::Impl::verifyFileSignature( const Pathname &file, const Pathname &signature)
00208 {
00209 return verifyFile( file, signature, generalKeyRing() );
00210 }
00211
00212 bool KeyRing::Impl::isKeyTrusted( const string &id)
00213 {
00214 return publicKeyExists( id, trustedKeyRing() );
00215 }
00216
00217 bool KeyRing::Impl::isKeyKnown( const string &id )
00218 {
00219 MIL << endl;
00220 if ( publicKeyExists( id, trustedKeyRing() ) )
00221 return true;
00222 else
00223 return publicKeyExists( id, generalKeyRing() );
00224 }
00225
00226 bool KeyRing::Impl::publicKeyExists( string id, const Pathname &keyring)
00227 {
00228 MIL << "Searching key [" << id << "] in keyring " << keyring << endl;
00229 list<PublicKey> keys = publicKeys(keyring);
00230 for (list<PublicKey>::const_iterator it = keys.begin(); it != keys.end(); it++)
00231 {
00232 if ( id == (*it).id() )
00233 return true;
00234 }
00235 return false;
00236 }
00237
00238 PublicKey KeyRing::Impl::exportKey( string id, const Pathname &keyring)
00239 {
00240 TmpFile tmp_file( _base_dir, "pubkey-"+id+"-" );
00241 Pathname keyfile = tmp_file.path();
00242 MIL << "Going to export key " << id << " from " << keyring << " to " << keyfile << endl;
00243
00244 try {
00245 ofstream os(keyfile.asString().c_str());
00246 dumpPublicKey( id, keyring, os );
00247 os.close();
00248 PublicKey key(keyfile);
00249 return key;
00250 }
00251 catch (BadKeyException &e)
00252 {
00253 ERR << "Cannot create public key " << id << " from " << keyring << " keyring to file " << e.keyFile() << endl;
00254 ZYPP_THROW(Exception("Cannot create public key " + id + " from " + keyring.asString() + " keyring to file " + e.keyFile().asString() ) );
00255 }
00256 catch (exception &e)
00257 {
00258 ERR << "Cannot export key " << id << " from " << keyring << " keyring to file " << keyfile << endl;
00259 }
00260 return PublicKey();
00261 }
00262
00263 void KeyRing::Impl::dumpPublicKey( const string &id, bool trusted, ostream &stream )
00264 {
00265 dumpPublicKey( id, ( trusted ? trustedKeyRing() : generalKeyRing() ), stream );
00266 }
00267
00268 void KeyRing::Impl::dumpPublicKey( const string &id, const Pathname &keyring, ostream &stream )
00269 {
00270 const char* argv[] =
00271 {
00272 GPG_BINARY,
00273 "--no-default-keyring",
00274 "--quiet",
00275 "--no-tty",
00276 "--no-greeting",
00277 "--no-permission-warning",
00278 "--batch",
00279 "--homedir",
00280 keyring.asString().c_str(),
00281 "-a",
00282 "--export",
00283 id.c_str(),
00284 NULL
00285 };
00286 ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
00287 string line;
00288 int count;
00289 for(line = prog.receiveLine(), count=0; !line.empty(); line = prog.receiveLine(), count++ )
00290 {
00291 stream << line;
00292 }
00293 prog.close();
00294 }
00295
00296
00297 bool KeyRing::Impl::verifyFileSignatureWorkflow( const Pathname &file, const string filedesc, const Pathname &signature)
00298 {
00299 callback::SendReport<KeyRingReport> report;
00300
00301 MIL << "Going to verify signature for " << file << " with " << signature << endl;
00302
00303
00304 if( signature.empty() || (!PathInfo(signature).isExist()) )
00305 {
00306 bool res = report->askUserToAcceptUnsignedFile( filedesc );
00307 MIL << "User decision on unsigned file: " << res << endl;
00308 return res;
00309 }
00310
00311
00312 string id = readSignatureKeyId(signature);
00313
00314
00315 if ( publicKeyExists( id, trustedKeyRing() ) )
00316 {
00317 PublicKey key = exportKey( id, trustedKeyRing() );
00318
00319 MIL << "Key " << id << " " << key.name() << " is trusted" << endl;
00320
00321 if ( verifyFile( file, signature, trustedKeyRing() ) )
00322 return true;
00323 else
00324 return report->askUserToAcceptVerificationFailed( filedesc, key );
00325 }
00326 else
00327 {
00328 if ( publicKeyExists( id, generalKeyRing() ) )
00329 {
00330 PublicKey key = exportKey( id, generalKeyRing());
00331 MIL << "Exported key " << id << " to " << key.path() << endl;
00332 MIL << "Key " << id << " " << key.name() << " is not trusted" << endl;
00333
00334 #warning We need the key details passed to the callback
00335 if ( report->askUserToTrustKey( key ) )
00336 {
00337 MIL << "User wants to trust key " << id << " " << key.name() << endl;
00338
00339
00340 Pathname which_keyring;
00341 if ( report->askUserToImportKey( key ) )
00342 {
00343 MIL << "User wants to import key " << id << " " << key.name() << endl;
00344 importKey( key, true );
00345 which_keyring = trustedKeyRing();
00346 }
00347 else
00348 {
00349 which_keyring = generalKeyRing();
00350 }
00351
00352
00353 if ( verifyFile( file, signature, which_keyring ) )
00354 {
00355 MIL << "File signature is verified" << endl;
00356 return true;
00357 }
00358 else
00359 {
00360 MIL << "File signature check fails" << endl;
00361 if ( report->askUserToAcceptVerificationFailed( filedesc, key ) )
00362 {
00363 MIL << "User continues anyway." << endl;
00364 return true;
00365 }
00366 else
00367 {
00368 MIL << "User does not want to continue" << endl;
00369 return false;
00370 }
00371 }
00372 }
00373 else
00374 {
00375 MIL << "User does not want to trust key " << id << " " << key.name() << endl;
00376 return false;
00377 }
00378 }
00379 else
00380 {
00381
00382 MIL << "File [" << file << "] ( " << filedesc << " ) signed with unknown key [" << id << "]" << endl;
00383 if ( report->askUserToAcceptUnknownKey( filedesc, id ) )
00384 {
00385 MIL << "User wants to accept unknown key " << id << endl;
00386 return true;
00387 }
00388 else
00389 {
00390 MIL << "User does not want to accept unknown key " << id << endl;
00391 return false;
00392 }
00393 }
00394 }
00395 return false;
00396 }
00397
00398 list<string> KeyRing::Impl::publicKeyIds(const Pathname &keyring)
00399 {
00400 static str::regex rxColons("^([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):\n$");
00401 static str::regex rxColonsFpr("^([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):\n$");
00402
00403 list<string> ids;
00404
00405 const char* argv[] =
00406 {
00407 GPG_BINARY,
00408 "--no-default-keyring",
00409 "--quiet",
00410 "--list-public-keys",
00411 "--with-colons",
00412 "--with-fingerprint",
00413 "--no-tty",
00414 "--no-greeting",
00415 "--batch",
00416 "--status-fd",
00417 "1",
00418 "--homedir",
00419 keyring.asString().c_str(),
00420 NULL
00421 };
00422
00423 ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
00424 string line;
00425 int count = 0;
00426
00427 for(line = prog.receiveLine(), count=0; !line.empty(); line = prog.receiveLine(), count++ )
00428 {
00429
00430 str::smatch what;
00431 if(str::regex_match(line, what, rxColons))
00432 {
00433 string id;
00434 string fingerprint;
00435 if ( what[1] == "pub" )
00436 {
00437 id = what[5];
00438
00439 string line2;
00440 for(line2 = prog.receiveLine(); !line2.empty(); line2 = prog.receiveLine(), count++ )
00441 {
00442 str::smatch what2;
00443 if (str::regex_match(line2, what2, rxColonsFpr))
00444 {
00445 if ( (what2[1] == "fpr") && (what2[1] != "pub") && (what2[1] !="sub"))
00446 {
00447 fingerprint = what2[10];
00448 break;
00449 }
00450 }
00451 }
00452
00453 ids.push_back(id);
00454 MIL << "Found key " << "[" << id << "]" << endl;
00455 }
00456
00457 }
00458 }
00459 prog.close();
00460 return ids;
00461 }
00462
00463 list<PublicKey> KeyRing::Impl::publicKeys(const Pathname &keyring)
00464 {
00465
00466 list<PublicKey> keys;
00467
00468 list<string> ids = publicKeyIds(keyring);
00469
00470 for ( list<string>::const_iterator it = ids.begin(); it != ids.end(); ++it )
00471 {
00472 PublicKey key(exportKey( *it, keyring ));
00473 keys.push_back(key);
00474 MIL << "Found key " << "[" << key.id() << "]" << " [" << key.name() << "]" << " [" << key.fingerprint() << "]" << endl;
00475 }
00476 return keys;
00477 }
00478
00479 void KeyRing::Impl::importKey( const Pathname &keyfile, const Pathname &keyring)
00480 {
00481 if ( ! PathInfo(keyfile).isExist() )
00482 ZYPP_THROW(KeyRingException("Tried to import not existant key " + keyfile.asString() + " into keyring " + keyring.asString()));
00483
00484 const char* argv[] =
00485 {
00486 GPG_BINARY,
00487 "--no-default-keyring",
00488 "--quiet",
00489 "--no-tty",
00490 "--no-greeting",
00491 "--no-permission-warning",
00492 "--status-fd",
00493 "1",
00494 "--homedir",
00495 keyring.asString().c_str(),
00496 "--import",
00497 keyfile.asString().c_str(),
00498 NULL
00499 };
00500
00501 int code;
00502 ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
00503 code = prog.close();
00504
00505
00506
00507 }
00508
00509 void KeyRing::Impl::deleteKey( const string &id, const Pathname &keyring )
00510 {
00511 const char* argv[] =
00512 {
00513 GPG_BINARY,
00514 "--no-default-keyring",
00515 "--yes",
00516 "--quiet",
00517 "--no-tty",
00518 "--batch",
00519 "--status-fd",
00520 "1",
00521 "--homedir",
00522 keyring.asString().c_str(),
00523 "--delete-keys",
00524 id.c_str(),
00525 NULL
00526 };
00527
00528 ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
00529
00530 int code = prog.close();
00531 if ( code )
00532 ZYPP_THROW(Exception("Failed to delete key."));
00533 else
00534 MIL << "Deleted key " << id << " from keyring " << keyring << endl;
00535 }
00536
00537
00538 string KeyRing::Impl::readSignatureKeyId(const Pathname &signature )
00539 {
00540 if ( ! PathInfo(signature).isFile() )
00541 ZYPP_THROW(Exception("Signature file " + signature.asString() + " not found"));
00542
00543 MIL << "Determining key id if signature " << signature << endl;
00544
00545 TmpDir dir(_base_dir, "fake-keyring");
00546
00547 const char* argv[] =
00548 {
00549 GPG_BINARY,
00550 "--no-default-keyring",
00551 "--quiet",
00552 "--no-tty",
00553 "--no-greeting",
00554 "--batch",
00555 "--status-fd",
00556 "1",
00557 "--homedir",
00558 dir.path().asString().c_str(),
00559 signature.asString().c_str(),
00560 NULL
00561 };
00562
00563 ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
00564
00565 string line;
00566 int count = 0;
00567
00568 str::regex rxNoKey("^\\[GNUPG:\\] NO_PUBKEY (.+)\n$");
00569 string id;
00570 for(line = prog.receiveLine(), count=0; !line.empty(); line = prog.receiveLine(), count++ )
00571 {
00572
00573 str::smatch what;
00574 if(str::regex_match(line, what, rxNoKey))
00575 {
00576 if ( what.size() >= 1 )
00577 id = what[1];
00578
00579 }
00580 else
00581 {
00582 MIL << "'" << line << "'" << endl;
00583 }
00584 }
00585
00586 if ( count == 0 )
00587 {
00588 MIL << "no output" << endl;
00589 }
00590
00591 MIL << "Determined key id [" << id << "] for signature " << signature << endl;
00592 prog.close();
00593 return id;
00594 }
00595
00596 bool KeyRing::Impl::verifyFile( const Pathname &file, const Pathname &signature, const Pathname &keyring)
00597 {
00598 const char* argv[] =
00599 {
00600 GPG_BINARY,
00601 "--no-default-keyring",
00602 "--quiet",
00603 "--no-tty",
00604 "--batch",
00605 "--no-greeting",
00606 "--status-fd",
00607 "1",
00608 "--homedir",
00609 keyring.asString().c_str(),
00610 "--verify",
00611 signature.asString().c_str(),
00612 file.asString().c_str(),
00613 NULL
00614 };
00615
00616
00617
00618
00619
00620
00621
00622
00623
00624
00625
00626 ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
00627
00628 return (prog.close() == 0) ? true : false;
00629 }
00630
00632
00634
00635
00636
00638
00640
00641
00642
00643
00644 KeyRing::KeyRing(const Pathname &baseTmpDir)
00645 : _pimpl( new Impl(baseTmpDir) )
00646 {}
00647
00649
00650
00651
00652
00653
00654
00655
00656
00658
00659
00660
00661
00662 KeyRing::~KeyRing()
00663 {}
00664
00666
00667
00668
00670
00671
00672 void KeyRing::importKey( const PublicKey &key, bool trusted )
00673 {
00674 _pimpl->importKey( key.path(), trusted );
00675 }
00676
00677 string KeyRing::readSignatureKeyId( const Pathname &signature )
00678 {
00679 return _pimpl->readSignatureKeyId(signature);
00680 }
00681
00682 void KeyRing::deleteKey( const string &id, bool trusted )
00683 {
00684 _pimpl->deleteKey(id, trusted);
00685 }
00686
00687 list<PublicKey> KeyRing::publicKeys()
00688 {
00689 return _pimpl->publicKeys();
00690 }
00691
00692 list<PublicKey> KeyRing::trustedPublicKeys()
00693 {
00694 return _pimpl->trustedPublicKeys();
00695 }
00696
00697 list<string> KeyRing::publicKeyIds()
00698 {
00699 return _pimpl->publicKeyIds();
00700 }
00701
00702 list<string> KeyRing::trustedPublicKeyIds()
00703 {
00704 return _pimpl->trustedPublicKeyIds();
00705 }
00706
00707 bool KeyRing::verifyFileSignatureWorkflow( const Pathname &file, const string filedesc, const Pathname &signature)
00708 {
00709 return _pimpl->verifyFileSignatureWorkflow(file, filedesc, signature);
00710 }
00711
00712 bool KeyRing::verifyFileSignature( const Pathname &file, const Pathname &signature)
00713 {
00714 return _pimpl->verifyFileSignature(file, signature);
00715 }
00716
00717 bool KeyRing::verifyFileTrustedSignature( const Pathname &file, const Pathname &signature)
00718 {
00719 return _pimpl->verifyFileTrustedSignature(file, signature);
00720 }
00721
00722 void KeyRing::dumpPublicKey( const string &id, bool trusted, ostream &stream )
00723 {
00724 _pimpl->dumpPublicKey( id, trusted, stream);
00725 }
00726
00727 bool KeyRing::isKeyTrusted( const string &id )
00728 {
00729 return _pimpl->isKeyTrusted(id);
00730 }
00731
00732 bool KeyRing::isKeyKnown( const string &id )
00733 {
00734 return _pimpl->isKeyKnown(id);
00735 }
00736
00738 }