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
00320
00321 if ( publicKeyExists( id, generalKeyRing() ) )
00322 {
00323 PublicKey untkey = exportKey( id, generalKeyRing() );
00324
00325
00326 if ( untkey.fingerprint() == key.fingerprint()
00327 && untkey.created() > key.created() )
00328 {
00329 MIL << "Key " << key << " was updated. Saving new version into trusted keyring." << endl;
00330 importKey( untkey, true );
00331 key = untkey;
00332 }
00333 }
00334
00335 MIL << "Key " << id << " " << key.name() << " is trusted" << endl;
00336
00337 if ( verifyFile( file, signature, trustedKeyRing() ) )
00338 return true;
00339 else
00340 return report->askUserToAcceptVerificationFailed( filedesc, key );
00341 }
00342 else
00343 {
00344 if ( publicKeyExists( id, generalKeyRing() ) )
00345 {
00346 PublicKey key = exportKey( id, generalKeyRing());
00347 MIL << "Exported key " << id << " to " << key.path() << endl;
00348 MIL << "Key " << id << " " << key.name() << " is not trusted" << endl;
00349
00350 #warning We need the key details passed to the callback
00351 if ( report->askUserToTrustKey( key ) )
00352 {
00353 MIL << "User wants to trust key " << id << " " << key.name() << endl;
00354
00355
00356 Pathname which_keyring;
00357 if ( report->askUserToImportKey( key ) )
00358 {
00359 MIL << "User wants to import key " << id << " " << key.name() << endl;
00360 importKey( key, true );
00361 which_keyring = trustedKeyRing();
00362 }
00363 else
00364 {
00365 which_keyring = generalKeyRing();
00366 }
00367
00368
00369 if ( verifyFile( file, signature, which_keyring ) )
00370 {
00371 MIL << "File signature is verified" << endl;
00372 return true;
00373 }
00374 else
00375 {
00376 MIL << "File signature check fails" << endl;
00377 if ( report->askUserToAcceptVerificationFailed( filedesc, key ) )
00378 {
00379 MIL << "User continues anyway." << endl;
00380 return true;
00381 }
00382 else
00383 {
00384 MIL << "User does not want to continue" << endl;
00385 return false;
00386 }
00387 }
00388 }
00389 else
00390 {
00391 MIL << "User does not want to trust key " << id << " " << key.name() << endl;
00392 return false;
00393 }
00394 }
00395 else
00396 {
00397
00398 MIL << "File [" << file << "] ( " << filedesc << " ) signed with unknown key [" << id << "]" << endl;
00399 if ( report->askUserToAcceptUnknownKey( filedesc, id ) )
00400 {
00401 MIL << "User wants to accept unknown key " << id << endl;
00402 return true;
00403 }
00404 else
00405 {
00406 MIL << "User does not want to accept unknown key " << id << endl;
00407 return false;
00408 }
00409 }
00410 }
00411 return false;
00412 }
00413
00414 list<string> KeyRing::Impl::publicKeyIds(const Pathname &keyring)
00415 {
00416 static str::regex rxColons("^([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):\n$");
00417 static str::regex rxColonsFpr("^([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):\n$");
00418
00419 list<string> ids;
00420
00421 const char* argv[] =
00422 {
00423 GPG_BINARY,
00424 "--no-default-keyring",
00425 "--quiet",
00426 "--list-public-keys",
00427 "--with-colons",
00428 "--with-fingerprint",
00429 "--no-tty",
00430 "--no-greeting",
00431 "--batch",
00432 "--status-fd",
00433 "1",
00434 "--homedir",
00435 keyring.asString().c_str(),
00436 NULL
00437 };
00438
00439 ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
00440 string line;
00441 int count = 0;
00442
00443 for(line = prog.receiveLine(), count=0; !line.empty(); line = prog.receiveLine(), count++ )
00444 {
00445
00446 str::smatch what;
00447 if(str::regex_match(line, what, rxColons))
00448 {
00449 string id;
00450 string fingerprint;
00451 if ( what[1] == "pub" )
00452 {
00453 id = what[5];
00454
00455 string line2;
00456 for(line2 = prog.receiveLine(); !line2.empty(); line2 = prog.receiveLine(), count++ )
00457 {
00458 str::smatch what2;
00459 if (str::regex_match(line2, what2, rxColonsFpr))
00460 {
00461 if ( (what2[1] == "fpr") && (what2[1] != "pub") && (what2[1] !="sub"))
00462 {
00463 fingerprint = what2[10];
00464 break;
00465 }
00466 }
00467 }
00468
00469 ids.push_back(id);
00470 MIL << "Found key " << "[" << id << "]" << endl;
00471 }
00472
00473 }
00474 }
00475 prog.close();
00476 return ids;
00477 }
00478
00479 list<PublicKey> KeyRing::Impl::publicKeys(const Pathname &keyring)
00480 {
00481
00482 list<PublicKey> keys;
00483
00484 list<string> ids = publicKeyIds(keyring);
00485
00486 for ( list<string>::const_iterator it = ids.begin(); it != ids.end(); ++it )
00487 {
00488 PublicKey key(exportKey( *it, keyring ));
00489 keys.push_back(key);
00490 MIL << "Found key " << "[" << key.id() << "]" << " [" << key.name() << "]" << " [" << key.fingerprint() << "]" << endl;
00491 }
00492 return keys;
00493 }
00494
00495 void KeyRing::Impl::importKey( const Pathname &keyfile, const Pathname &keyring)
00496 {
00497 if ( ! PathInfo(keyfile).isExist() )
00498 ZYPP_THROW(KeyRingException("Tried to import not existant key " + keyfile.asString() + " into keyring " + keyring.asString()));
00499
00500 const char* argv[] =
00501 {
00502 GPG_BINARY,
00503 "--no-default-keyring",
00504 "--quiet",
00505 "--no-tty",
00506 "--no-greeting",
00507 "--no-permission-warning",
00508 "--status-fd",
00509 "1",
00510 "--homedir",
00511 keyring.asString().c_str(),
00512 "--import",
00513 keyfile.asString().c_str(),
00514 NULL
00515 };
00516
00517 int code;
00518 ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
00519 code = prog.close();
00520
00521
00522
00523 }
00524
00525 void KeyRing::Impl::deleteKey( const string &id, const Pathname &keyring )
00526 {
00527 const char* argv[] =
00528 {
00529 GPG_BINARY,
00530 "--no-default-keyring",
00531 "--yes",
00532 "--quiet",
00533 "--no-tty",
00534 "--batch",
00535 "--status-fd",
00536 "1",
00537 "--homedir",
00538 keyring.asString().c_str(),
00539 "--delete-keys",
00540 id.c_str(),
00541 NULL
00542 };
00543
00544 ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
00545
00546 int code = prog.close();
00547 if ( code )
00548 ZYPP_THROW(Exception("Failed to delete key."));
00549 else
00550 MIL << "Deleted key " << id << " from keyring " << keyring << endl;
00551 }
00552
00553
00554 string KeyRing::Impl::readSignatureKeyId(const Pathname &signature )
00555 {
00556 if ( ! PathInfo(signature).isFile() )
00557 ZYPP_THROW(Exception("Signature file " + signature.asString() + " not found"));
00558
00559 MIL << "Determining key id if signature " << signature << endl;
00560
00561 TmpDir dir(_base_dir, "fake-keyring");
00562
00563 const char* argv[] =
00564 {
00565 GPG_BINARY,
00566 "--no-default-keyring",
00567 "--quiet",
00568 "--no-tty",
00569 "--no-greeting",
00570 "--batch",
00571 "--status-fd",
00572 "1",
00573 "--homedir",
00574 dir.path().asString().c_str(),
00575 signature.asString().c_str(),
00576 NULL
00577 };
00578
00579 ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
00580
00581 string line;
00582 int count = 0;
00583
00584 str::regex rxNoKey("^\\[GNUPG:\\] NO_PUBKEY (.+)\n$");
00585 string id;
00586 for(line = prog.receiveLine(), count=0; !line.empty(); line = prog.receiveLine(), count++ )
00587 {
00588
00589 str::smatch what;
00590 if(str::regex_match(line, what, rxNoKey))
00591 {
00592 if ( what.size() >= 1 )
00593 id = what[1];
00594
00595 }
00596 else
00597 {
00598 MIL << "'" << line << "'" << endl;
00599 }
00600 }
00601
00602 if ( count == 0 )
00603 {
00604 MIL << "no output" << endl;
00605 }
00606
00607 MIL << "Determined key id [" << id << "] for signature " << signature << endl;
00608 prog.close();
00609 return id;
00610 }
00611
00612 bool KeyRing::Impl::verifyFile( const Pathname &file, const Pathname &signature, const Pathname &keyring)
00613 {
00614 const char* argv[] =
00615 {
00616 GPG_BINARY,
00617 "--no-default-keyring",
00618 "--quiet",
00619 "--no-tty",
00620 "--batch",
00621 "--no-greeting",
00622 "--status-fd",
00623 "1",
00624 "--homedir",
00625 keyring.asString().c_str(),
00626 "--verify",
00627 signature.asString().c_str(),
00628 file.asString().c_str(),
00629 NULL
00630 };
00631
00632
00633
00634
00635
00636
00637
00638
00639
00640
00641
00642 ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
00643
00644 return (prog.close() == 0) ? true : false;
00645 }
00646
00648
00650
00651
00652
00654
00656
00657
00658
00659
00660 KeyRing::KeyRing(const Pathname &baseTmpDir)
00661 : _pimpl( new Impl(baseTmpDir) )
00662 {}
00663
00665
00666
00667
00668
00669
00670
00671
00672
00674
00675
00676
00677
00678 KeyRing::~KeyRing()
00679 {}
00680
00682
00683
00684
00686
00687
00688 void KeyRing::importKey( const PublicKey &key, bool trusted )
00689 {
00690 _pimpl->importKey( key.path(), trusted );
00691 }
00692
00693 string KeyRing::readSignatureKeyId( const Pathname &signature )
00694 {
00695 return _pimpl->readSignatureKeyId(signature);
00696 }
00697
00698 void KeyRing::deleteKey( const string &id, bool trusted )
00699 {
00700 _pimpl->deleteKey(id, trusted);
00701 }
00702
00703 list<PublicKey> KeyRing::publicKeys()
00704 {
00705 return _pimpl->publicKeys();
00706 }
00707
00708 list<PublicKey> KeyRing::trustedPublicKeys()
00709 {
00710 return _pimpl->trustedPublicKeys();
00711 }
00712
00713 list<string> KeyRing::publicKeyIds()
00714 {
00715 return _pimpl->publicKeyIds();
00716 }
00717
00718 list<string> KeyRing::trustedPublicKeyIds()
00719 {
00720 return _pimpl->trustedPublicKeyIds();
00721 }
00722
00723 bool KeyRing::verifyFileSignatureWorkflow( const Pathname &file, const string filedesc, const Pathname &signature)
00724 {
00725 return _pimpl->verifyFileSignatureWorkflow(file, filedesc, signature);
00726 }
00727
00728 bool KeyRing::verifyFileSignature( const Pathname &file, const Pathname &signature)
00729 {
00730 return _pimpl->verifyFileSignature(file, signature);
00731 }
00732
00733 bool KeyRing::verifyFileTrustedSignature( const Pathname &file, const Pathname &signature)
00734 {
00735 return _pimpl->verifyFileTrustedSignature(file, signature);
00736 }
00737
00738 void KeyRing::dumpPublicKey( const string &id, bool trusted, ostream &stream )
00739 {
00740 _pimpl->dumpPublicKey( id, trusted, stream);
00741 }
00742
00743 bool KeyRing::isKeyTrusted( const string &id )
00744 {
00745 return _pimpl->isKeyTrusted(id);
00746 }
00747
00748 bool KeyRing::isKeyKnown( const string &id )
00749 {
00750 return _pimpl->isKeyKnown(id);
00751 }
00752
00754 }