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 importKey( key.path(), trusted ? trustedKeyRing() : generalKeyRing() );
00172 if ( trusted )
00173 {
00174 callback::SendReport<KeyRingSignals>()->trustedKeyAdded( trustedKeyRing(), key );
00175 }
00176 }
00177
00178 void KeyRing::Impl::deleteKey( const std::string &id, bool trusted)
00179 {
00180 deleteKey( id, trusted ? trustedKeyRing() : generalKeyRing() );
00181 }
00182
00183 std::list<PublicKey> KeyRing::Impl::publicKeys()
00184 {
00185 return publicKeys( generalKeyRing() );
00186 }
00187
00188 std::list<PublicKey> KeyRing::Impl::trustedPublicKeys()
00189 {
00190 return publicKeys( trustedKeyRing() );
00191 }
00192
00193 bool KeyRing::Impl::verifyFileTrustedSignature( const Pathname &file, const Pathname &signature)
00194 {
00195 return verifyFile( file, signature, trustedKeyRing() );
00196 }
00197
00198 bool KeyRing::Impl::verifyFileSignature( const Pathname &file, const Pathname &signature)
00199 {
00200 return verifyFile( file, signature, generalKeyRing() );
00201 }
00202
00203 bool KeyRing::Impl::isKeyTrusted( const std::string &id)
00204 {
00205 return publicKeyExists( id, trustedKeyRing() );
00206 }
00207
00208 bool KeyRing::Impl::isKeyKnown( const std::string &id )
00209 {
00210 if ( publicKeyExists( id, trustedKeyRing() ) )
00211 return true;
00212 else
00213 return publicKeyExists( id, generalKeyRing() );
00214 }
00215
00216 bool KeyRing::Impl::publicKeyExists( std::string id, const Pathname &keyring)
00217 {
00218 MIL << "Searching key [" << id << "] in keyring " << keyring << std::endl;
00219 std::list<PublicKey> keys = publicKeys(keyring);
00220 for (std::list<PublicKey>::const_iterator it = keys.begin(); it != keys.end(); it++)
00221 {
00222 if ( id == (*it).id() )
00223 return true;
00224 }
00225 return false;
00226 }
00227
00228 PublicKey KeyRing::Impl::exportKey( std::string id, const Pathname &keyring)
00229 {
00230 TmpFile tmp_file( _base_dir, "pubkey-"+id+"-" );
00231 Pathname keyfile = tmp_file.path();
00232 MIL << "Going to export key " << id << " from " << keyring << " to " << keyfile << endl;
00233
00234 try {
00235 std::ofstream os(keyfile.asString().c_str());
00236 dumpPublicKey( id, keyring, os );
00237 os.close();
00238 PublicKey key(keyfile);
00239 return key;
00240 }
00241 catch (BadKeyException &e)
00242 {
00243 ERR << "Cannot create public key " << id << " from " << keyring << " keyring to file " << e.keyFile() << std::endl;
00244 ZYPP_THROW(Exception("Cannot create public key " + id + " from " + keyring.asString() + " keyring to file " + e.keyFile().asString() ) );
00245 }
00246 catch (std::exception &e)
00247 {
00248 ERR << "Cannot export key " << id << " from " << keyring << " keyring to file " << keyfile << std::endl;
00249 }
00250 return PublicKey();
00251 }
00252
00253 void KeyRing::Impl::dumpPublicKey( const std::string &id, bool trusted, std::ostream &stream )
00254 {
00255 dumpPublicKey( id, ( trusted ? trustedKeyRing() : generalKeyRing() ), stream );
00256 }
00257
00258 void KeyRing::Impl::dumpPublicKey( const std::string &id, const Pathname &keyring, std::ostream &stream )
00259 {
00260 const char* argv[] =
00261 {
00262 "gpg",
00263 "--no-default-keyring",
00264 "--quiet",
00265 "--no-tty",
00266 "--no-greeting",
00267 "--no-permission-warning",
00268 "--batch",
00269 "--homedir",
00270 keyring.asString().c_str(),
00271 "-a",
00272 "--export",
00273 id.c_str(),
00274 NULL
00275 };
00276 ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
00277 std::string line;
00278 int count;
00279 for(line = prog.receiveLine(), count=0; !line.empty(); line = prog.receiveLine(), count++ )
00280 {
00281 stream << line;
00282 }
00283 prog.close();
00284 }
00285
00286
00287 bool KeyRing::Impl::verifyFileSignatureWorkflow( const Pathname &file, const std::string filedesc, const Pathname &signature)
00288 {
00289 callback::SendReport<KeyRingReport> report;
00290 callback::SendReport<KeyRingSignals> emitSignal;
00291 MIL << "Going to verify signature for " << file << " with " << signature << std::endl;
00292
00293
00294 if( signature.empty() || (!PathInfo(signature).isExist()) )
00295 {
00296 bool res = report->askUserToAcceptUnsignedFile( filedesc );
00297 MIL << "User decision on unsigned file: " << res << endl;
00298 return res;
00299 }
00300
00301
00302 std::string id = readSignatureKeyId(signature);
00303
00304
00305 if ( publicKeyExists( id, trustedKeyRing() ) )
00306 {
00307 PublicKey key = exportKey( id, trustedKeyRing() );
00308
00309
00310
00311 if ( publicKeyExists( id, generalKeyRing() ) )
00312 {
00313 PublicKey untkey = exportKey( id, generalKeyRing() );
00314
00315
00316 if ( untkey.fingerprint() == key.fingerprint()
00317 && untkey.created() > key.created() )
00318 {
00319 MIL << "Key " << key << " was updated. Saving new version into trusted keyring." << endl;
00320 importKey( untkey, true );
00321 key = untkey;
00322 }
00323 }
00324
00325 MIL << "Key " << id << " " << key.name() << " is trusted" << std::endl;
00326
00327 if ( verifyFile( file, signature, trustedKeyRing() ) )
00328 return true;
00329 else
00330 return report->askUserToAcceptVerificationFailed( filedesc, key );
00331 }
00332 else
00333 {
00334 if ( publicKeyExists( id, generalKeyRing() ) )
00335 {
00336 PublicKey key = exportKey( id, generalKeyRing());
00337 MIL << "Exported key " << id << " to " << key.path() << std::endl;
00338 MIL << "Key " << id << " " << key.name() << " is not trusted" << std::endl;
00339
00340 #warning We need the key details passed to the callback
00341 if ( report->askUserToTrustKey( key ) )
00342 {
00343 MIL << "User wants to trust key " << id << " " << key.name() << std::endl;
00344
00345
00346 Pathname which_keyring;
00347 if ( report->askUserToImportKey( key ) )
00348 {
00349 MIL << "User wants to import key " << id << " " << key.name() << std::endl;
00350 importKey( key.path(), trustedKeyRing() );
00351 emitSignal->trustedKeyAdded( (const KeyRing &)(*this), key );
00352 which_keyring = trustedKeyRing();
00353 }
00354 else
00355 {
00356 which_keyring = generalKeyRing();
00357 }
00358
00359
00360 if ( verifyFile( file, signature, which_keyring ) )
00361 {
00362 MIL << "File signature is verified" << std::endl;
00363 return true;
00364 }
00365 else
00366 {
00367 MIL << "File signature check fails" << std::endl;
00368 if ( report->askUserToAcceptVerificationFailed( filedesc, key ) )
00369 {
00370 MIL << "User continues anyway." << std::endl;
00371 return true;
00372 }
00373 else
00374 {
00375 MIL << "User does not want to continue" << std::endl;
00376 return false;
00377 }
00378 }
00379 }
00380 else
00381 {
00382 MIL << "User does not want to trust key " << id << " " << key.name() << std::endl;
00383 return false;
00384 }
00385 }
00386 else
00387 {
00388
00389 MIL << "File [" << file << "] ( " << filedesc << " ) signed with unknown key [" << id << "]" << std::endl;
00390 if ( report->askUserToAcceptUnknownKey( filedesc, id ) )
00391 {
00392 MIL << "User wants to accept unknown key " << id << std::endl;
00393 return true;
00394 }
00395 else
00396 {
00397 MIL << "User does not want to accept unknown key " << id << std::endl;
00398 return false;
00399 }
00400 }
00401 }
00402 return false;
00403 }
00404
00405 std::list<PublicKey> KeyRing::Impl::publicKeys(const Pathname &keyring)
00406 {
00407 const char* argv[] =
00408 {
00409 "gpg",
00410 "--no-default-keyring",
00411 "--quiet",
00412 "--list-public-keys",
00413 "--with-colons",
00414 "--with-fingerprint",
00415 "--no-tty",
00416 "--no-greeting",
00417 "--batch",
00418 "--status-fd",
00419 "1",
00420 "--homedir",
00421 keyring.asString().c_str(),
00422 NULL
00423 };
00424 std::list<PublicKey> keys;
00425
00426 ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
00427 std::string line;
00428 int count = 0;
00429
00430 str::regex rxColons("^([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):\n$");
00431 str::regex rxColonsFpr("^([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):\n$");
00432
00433 for(line = prog.receiveLine(), count=0; !line.empty(); line = prog.receiveLine(), count++ )
00434 {
00435
00436 str::smatch what;
00437 if(str::regex_match(line, what, rxColons, str::match_extra))
00438 {
00439 string id;
00440 if ( what[1] == "pub" )
00441 {
00442 id = what[5];
00443
00444 std::string line2;
00445 for( line2 = prog.receiveLine(); !line2.empty(); line2 = prog.receiveLine(), count++ )
00446 {
00447 str::smatch what2;
00448 if (str::regex_match(line2, what2, rxColonsFpr, str::match_extra))
00449 {
00450 if ( (what2[1] == "fpr") && (what2[1] != "pub") && (what2[1] !="sub"))
00451 {
00452
00453 break;
00454 }
00455 }
00456 }
00457 PublicKey key(exportKey( id, keyring ));
00458 keys.push_back(key);
00459 MIL << "Found key " << "[" << key.id() << "]" << " [" << key.name() << "]" << " [" << key.fingerprint() << "]" << std::endl;
00460 }
00461
00462 }
00463 }
00464 prog.close();
00465 return keys;
00466 }
00467
00468 void KeyRing::Impl::importKey( const Pathname &keyfile, const Pathname &keyring)
00469 {
00470 if ( ! PathInfo(keyfile).isExist() )
00471 ZYPP_THROW(KeyRingException("Tried to import not existant key " + keyfile.asString() + " into keyring " + keyring.asString()));
00472
00473 const char* argv[] =
00474 {
00475 "gpg",
00476 "--no-default-keyring",
00477 "--quiet",
00478 "--no-tty",
00479 "--no-greeting",
00480 "--no-permission-warning",
00481 "--status-fd",
00482 "1",
00483 "--homedir",
00484 keyring.asString().c_str(),
00485 "--import",
00486 keyfile.asString().c_str(),
00487 NULL
00488 };
00489
00490 int code;
00491 ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
00492 code = prog.close();
00493
00494
00495
00496 }
00497
00498 void KeyRing::Impl::deleteKey( const std::string &id, const Pathname &keyring )
00499 {
00500 const char* argv[] =
00501 {
00502 "gpg",
00503 "--no-default-keyring",
00504 "--yes",
00505 "--quiet",
00506 "--no-tty",
00507 "--batch",
00508 "--status-fd",
00509 "1",
00510 "--homedir",
00511 keyring.asString().c_str(),
00512 "--delete-keys",
00513 id.c_str(),
00514 NULL
00515 };
00516
00517 ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
00518
00519 int code = prog.close();
00520 if ( code )
00521 ZYPP_THROW(Exception("Failed to delete key."));
00522 else
00523 MIL << "Deleted key " << id << " from keyring " << keyring << std::endl;
00524 }
00525
00526
00527 std::string KeyRing::Impl::readSignatureKeyId(const Pathname &signature )
00528 {
00529 MIL << "Deetermining key id if signature " << signature << std::endl;
00530
00531 TmpDir dir(_base_dir, "fake-keyring");
00532 TmpFile fakeData(_base_dir, "fake-data");
00533
00534 const char* argv[] =
00535 {
00536 "gpg",
00537 "--no-default-keyring",
00538 "--quiet",
00539 "--no-tty",
00540 "--no-greeting",
00541 "--batch",
00542 "--status-fd",
00543 "1",
00544 "--homedir",
00545 dir.path().asString().c_str(),
00546 "--verify",
00547 signature.asString().c_str(),
00548 fakeData.path().asString().c_str(),
00549 NULL
00550 };
00551
00552 ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
00553
00554 std::string line;
00555 int count = 0;
00556
00557 str::regex rxNoKey("^\\[GNUPG:\\] NO_PUBKEY (.+)\n$");
00558 std::string id;
00559 for(line = prog.receiveLine(), count=0; !line.empty(); line = prog.receiveLine(), count++ )
00560 {
00561
00562 str::smatch what;
00563 if(str::regex_match(line, what, rxNoKey, str::match_extra))
00564 {
00565 if ( what.size() > 1 )
00566 id = what[1];
00567
00568 }
00569 }
00570 MIL << "Determined key id [" << id << "] for signature " << signature << std::endl;
00571 prog.close();
00572 return id;
00573 }
00574
00575 bool KeyRing::Impl::verifyFile( const Pathname &file, const Pathname &signature, const Pathname &keyring)
00576 {
00577 const char* argv[] =
00578 {
00579 "gpg",
00580 "--no-default-keyring",
00581 "--quiet",
00582 "--no-tty",
00583 "--batch",
00584 "--no-greeting",
00585 "--status-fd",
00586 "1",
00587 "--homedir",
00588 keyring.asString().c_str(),
00589 "--verify",
00590 signature.asString().c_str(),
00591 file.asString().c_str(),
00592 NULL
00593 };
00594
00595
00596
00597
00598
00599
00600
00601
00602
00603
00604
00605 ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
00606
00607 return (prog.close() == 0) ? true : false;
00608 }
00609
00611
00613
00614
00615
00617
00619
00620
00621
00622
00623 KeyRing::KeyRing(const Pathname &baseTmpDir)
00624 : _pimpl( new Impl(baseTmpDir) )
00625 {}
00626
00628
00629
00630
00631
00632
00633
00634
00635
00637
00638
00639
00640
00641 KeyRing::~KeyRing()
00642 {}
00643
00645
00646
00647
00649
00650
00651 void KeyRing::importKey( const PublicKey &key, bool trusted )
00652 {
00653 _pimpl->importKey( key, trusted );
00654 }
00655
00656 std::string KeyRing::readSignatureKeyId( const Pathname &signature )
00657 {
00658 return _pimpl->readSignatureKeyId(signature);
00659 }
00660
00661 void KeyRing::deleteKey( const std::string &id, bool trusted )
00662 {
00663 _pimpl->deleteKey(id, trusted);
00664 }
00665
00666 std::list<PublicKey> KeyRing::publicKeys()
00667 {
00668 return _pimpl->publicKeys();
00669 }
00670
00671 std::list<PublicKey> KeyRing::trustedPublicKeys()
00672 {
00673 return _pimpl->trustedPublicKeys();
00674 }
00675
00676 bool KeyRing::verifyFileSignatureWorkflow( const Pathname &file, const std::string filedesc, const Pathname &signature)
00677 {
00678 return _pimpl->verifyFileSignatureWorkflow(file, filedesc, signature);
00679 }
00680
00681 bool KeyRing::verifyFileSignature( const Pathname &file, const Pathname &signature)
00682 {
00683 return _pimpl->verifyFileSignature(file, signature);
00684 }
00685
00686 bool KeyRing::verifyFileTrustedSignature( const Pathname &file, const Pathname &signature)
00687 {
00688 return _pimpl->verifyFileTrustedSignature(file, signature);
00689 }
00690
00691 void KeyRing::dumpPublicKey( const std::string &id, bool trusted, std::ostream &stream )
00692 {
00693 _pimpl->dumpPublicKey( id, trusted, stream);
00694 }
00695
00696 bool KeyRing::isKeyTrusted( const std::string &id )
00697 {
00698 return _pimpl->isKeyTrusted(id);
00699 }
00700
00701 bool KeyRing::isKeyKnown( const std::string &id )
00702 {
00703 return _pimpl->isKeyTrusted(id);
00704 }
00705
00707 }