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