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