KeyRing.cc

Go to the documentation of this file.
00001 /*---------------------------------------------------------------------\
00002 |                          ____ _   __ __ ___                          |
00003 |                         |__  / \ / / . \ . \                         |
00004 |                           / / \ V /|  _/  _/                         |
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   }
00054 
00055   static void dumpFile(const Pathname &file)
00056   {
00057     std::ifstream is(file.asString().c_str());
00058     iostr::forEachLine( is, printLine);
00059   }
00060 
00061   namespace
00062   {
00063     bool _keyRingDefaultAccept( getenv("ZYPP_KEYRING_DEFAULT_ACCEPT_ALL") );
00064   }
00065 
00066   bool KeyRingReport::askUserToAcceptUnsignedFile( const std::string &file )
00067   { return _keyRingDefaultAccept; }
00068 
00069   bool KeyRingReport::askUserToAcceptUnknownKey( const std::string &file, const std::string &keyid, const std::string &keyname, const std::string &fingerprint )
00070   { return _keyRingDefaultAccept; }
00071 
00072   bool KeyRingReport::askUserToTrustKey( const std::string &keyid, const std::string &keyname, const std::string &fingerprint )
00073   { return _keyRingDefaultAccept; }
00074 
00075   bool KeyRingReport::askUserToAcceptVerificationFailed( const std::string &file, const std::string &keyid, const std::string &keyname, const std::string &fingerprint )
00076   { return _keyRingDefaultAccept; }
00077 
00079   //
00080   //    CLASS NAME : KeyRing::Impl
00081   //
00083   struct KeyRing::Impl
00084   {
00085     Impl(const Pathname &baseTmpDir)
00086     : _trusted_tmp_dir(baseTmpDir)
00087     ,  _general_tmp_dir(baseTmpDir)
00088 
00089     {
00090       _base_dir = baseTmpDir;
00091     }
00092 
00093     /*
00094     Impl( const Pathname &general_kr, const Pathname &trusted_kr )
00095     {
00096       filesystem::assert_dir(general_kr);
00097       filesystem::assert_dir(trusted_kr);
00098 
00099       generalKeyRing() = general_kr;
00100       trustedKeyRing() = trusted_kr;
00101     }
00102     */
00103 
00104     void importKey( const Pathname &keyfile, bool trusted = false);
00105     PublicKey readPublicKey( const Pathname &keyfile );
00106     std::string readSignatureKeyId( const Pathname &signature );
00107 
00108     void deleteKey( const std::string &id, bool trusted );
00109     std::list<PublicKey> trustedPublicKeys();
00110     std::list<PublicKey> publicKeys();
00111 
00112     void dumpPublicKey( const std::string &id, bool trusted, std::ostream &stream );
00113 
00114     bool verifyFileSignatureWorkflow( const Pathname &file, const std::string filedesc, const Pathname &signature);
00115 
00116     bool verifyFileSignature( const Pathname &file, const Pathname &signature);
00117     bool verifyFileTrustedSignature( const Pathname &file, const Pathname &signature);
00118   private:
00119     //mutable std::map<Locale, std::string> translations;
00120     bool verifyFile( const Pathname &file, const Pathname &signature, const Pathname &keyring);
00121     void importKey( const Pathname &keyfile, const Pathname &keyring);
00122 
00123     void exportKey( std::string id, const Pathname &keyfile, bool trusted);
00124 
00125     void deleteKey( const std::string &id, const Pathname &keyring );
00126     std::list<PublicKey> publicKeys(const Pathname &keyring);
00127 
00128     bool publicKeyExists( std::string id, const Pathname &keyring);
00129 
00130     const Pathname generalKeyRing() const;
00131     const Pathname trustedKeyRing() const;
00132 
00133     // Used for trusted and untrusted keyrings
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( Pathname("/var/tmp") ) );
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 Pathname &keyfile, bool trusted)
00164   {
00165     importKey( keyfile, trusted ? trustedKeyRing() : generalKeyRing() );
00166   }
00167 
00168   void KeyRing::Impl::deleteKey( const std::string &id, bool trusted)
00169   {
00170     deleteKey( id, trusted ? trustedKeyRing() : generalKeyRing() );
00171   }
00172 
00173   std::list<PublicKey> KeyRing::Impl::publicKeys()
00174   {
00175     return publicKeys( generalKeyRing() );
00176   }
00177 
00178   std::list<PublicKey> KeyRing::Impl::trustedPublicKeys()
00179   {
00180     return publicKeys( trustedKeyRing() );
00181   }
00182 
00183   bool KeyRing::Impl::verifyFileTrustedSignature( const Pathname &file, const Pathname &signature)
00184   {
00185     return verifyFile( file, signature, trustedKeyRing() );
00186   }
00187 
00188   bool KeyRing::Impl::verifyFileSignature( const Pathname &file, const Pathname &signature)
00189   {
00190     return verifyFile( file, signature, generalKeyRing() );
00191   }
00192 
00193   bool KeyRing::Impl::publicKeyExists( std::string id, const Pathname &keyring)
00194   {
00195     MIL << "Searching key [" << id << "] in keyring " << keyring << std::endl;
00196     std::list<PublicKey> keys = publicKeys(keyring);
00197     for (std::list<PublicKey>::const_iterator it = keys.begin(); it != keys.end(); it++)
00198     {
00199       if ( id == (*it).id )
00200         return true;
00201     }
00202     return false;
00203   }
00204 
00205   void KeyRing::Impl::exportKey( std::string id, const Pathname &keyfile, bool trusted)
00206   {
00207     try {
00208       std::ofstream os(keyfile.asString().c_str());
00209       dumpPublicKey( id, trusted, os );
00210       os.close();
00211     }
00212     catch (std::exception &e)
00213     {
00214       ERR << "Cannot export key " << id << " from " << (trusted ? "trusted" : "untrusted ") << " keyring  to file " << keyfile << std::endl;
00215     }
00216   }
00217 
00218   void KeyRing::Impl::dumpPublicKey( const std::string &id, bool trusted, std::ostream &stream )
00219   {
00220     Pathname keyring = trusted ? trustedKeyRing() : generalKeyRing();
00221     const char* argv[] =
00222     {
00223       "gpg",
00224       "--no-default-keyring",
00225       "--quiet",
00226       "--no-tty",
00227       "--no-greeting",
00228       "--no-permission-warning",
00229       "--batch",
00230       "--homedir",
00231       keyring.asString().c_str(),
00232       "-a",
00233       "--export",
00234       id.c_str(),
00235       NULL
00236     };
00237     ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
00238     std::string line;
00239     int count;
00240     for(line = prog.receiveLine(), count=0; !line.empty(); line = prog.receiveLine(), count++ )
00241     {
00242       stream << line;
00243     }
00244     prog.close();
00245   }
00246 
00247 
00248   bool KeyRing::Impl::verifyFileSignatureWorkflow( const Pathname &file, const std::string filedesc, const Pathname &signature)
00249   {
00250     callback::SendReport<KeyRingReport> report;
00251     callback::SendReport<KeyRingSignals> emitSignal;
00252     MIL << "Going to verify signature for " << file << " with " << signature << std::endl;
00253 
00254     // if signature does not exists, ask user if he wants to accept unsigned file.
00255     if( signature.empty() || (!PathInfo(signature).isExist()) )
00256     {
00257         bool res = report->askUserToAcceptUnsignedFile( filedesc );
00258         MIL << "User decision on unsigned file: " << res << endl;
00259         return res;
00260     }
00261 
00262     // get the id of the signature
00263     std::string id = readSignatureKeyId(signature);
00264 
00265     // doeskey exists in trusted keyring
00266     if ( publicKeyExists( id, trustedKeyRing() ) )
00267     {
00268       TmpFile trustedKey(_base_dir);
00269       exportKey( id, trustedKey.path(), true);
00270       PublicKey key = readPublicKey(trustedKey.path());
00271       MIL << "Key " << id << " " << key.name << " is trusted" << std::endl;
00272       // it exists, is trusted, does it validates?
00273       if ( verifyFile( file, signature, trustedKeyRing() ) )
00274         return true;
00275       else
00276         return report->askUserToAcceptVerificationFailed( filedesc, key.id, key.name, key.fingerprint );
00277     }
00278     else
00279     {
00280       if ( publicKeyExists( id, generalKeyRing() ) )
00281       {
00282         TmpFile unKey(_base_dir);
00283         exportKey( id, unKey.path(), false);
00284         MIL << "Exported key " << id << " to " << unKey << std::endl;
00285 
00286         PublicKey key = readPublicKey(unKey.path());
00287         MIL << "Key " << id << " " << key.name << " is not trusted" << std::endl;
00288         // ok the key is not trusted, ask the user to trust it or not
00289 #warning We need the key details passed to the callback
00290         if ( report->askUserToTrustKey(key.id, key.name, key.fingerprint) )
00291         {
00292           MIL << "User wants to trust key " << id << " " << key.name << std::endl;
00293           //dumpFile(unKey.path());
00294 
00295           importKey( unKey.path(), trustedKeyRing() );
00296           emitSignal->trustedKeyAdded( (const KeyRing &)(*this), id, key.name, key.fingerprint );
00297 
00298           // emit key added
00299           if ( verifyFile( file, signature, trustedKeyRing() ) )
00300           {
00301             MIL << "File signature is verified" << std::endl;
00302             return true;
00303           }
00304           else
00305           {
00306             MIL << "File signature check fails" << std::endl;
00307             if ( report->askUserToAcceptVerificationFailed( filedesc, key.id, key.name, key.fingerprint ) )
00308             {
00309               MIL << "User continues anyway." << std::endl;
00310               return true;
00311             }
00312             else
00313             {
00314               MIL << "User does not want to continue" << std::endl;
00315               return false;
00316             }
00317           }
00318         }
00319         else
00320         {
00321           MIL << "User does not want to trust key " << id << " " << key.name << std::endl;
00322           return false;
00323         }
00324       }
00325       else
00326       {
00327         // unknown key...
00328         MIL << "File [" << file << "] ( " << filedesc << " ) signed with unknown key [" << id << "]" << std::endl;
00329         if ( report->askUserToAcceptUnknownKey( filedesc, id, "", "" ) )
00330         {
00331           MIL << "User wants to accept unknown key " << id << std::endl;
00332           return true;
00333         }
00334         else
00335         {
00336           MIL << "User does not want to accept unknown key " << id << std::endl;
00337           return false;
00338         }
00339       }
00340     }
00341     return false;
00342   }
00343 
00344 
00345   PublicKey KeyRing::Impl::readPublicKey( const Pathname &keyfile )
00346   {
00347     TmpDir dir(_base_dir);
00348 
00349     const char* argv[] =
00350     {
00351       "gpg",
00352       "--no-default-keyring",
00353       "--homedir",
00354       dir.path().asString().c_str(),
00355       "--with-fingerprint",
00356       "--with-colons",
00357       "--quiet",
00358       "--no-tty",
00359       "--no-greeting",
00360       "--batch",
00361       "--status-fd",
00362       "1",
00363       keyfile.asString().c_str(),
00364       NULL
00365     };
00366 
00367     ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
00368 
00369     std::string line;
00370     int count = 0;
00371 
00372     str::regex rxColons("^([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):\n$");
00373 
00374     // pub:-:1024:17:A84EDAE89C800ACA:2000-10-19:2008-06-21::-:SuSE Package Signing Key <build@suse.de>:
00375 
00376     PublicKey key;
00377     for(line = prog.receiveLine(), count=0; !line.empty(); line = prog.receiveLine(), count++ )
00378     {
00379       //MIL << "[" << line << "]" << std::endl;
00380       str::smatch what;
00381       if(str::regex_match(line, what, rxColons, str::match_extra))
00382       {
00383         if ( what[1] == "pub" )
00384         {
00385           key.id = what[5];
00386           key.name = what[10];
00387           //return key;
00388         }
00389         else if ( what[1] == "fpr" )
00390         {
00391           key.fingerprint = what[10];
00392         }
00393         //dumpRegexpResults(what);
00394       }
00395     }
00396     prog.close();
00397     return key;
00398   }
00399 
00400   std::list<PublicKey> KeyRing::Impl::publicKeys(const Pathname &keyring)
00401   {
00402     const char* argv[] =
00403     {
00404       "gpg",
00405       "--no-default-keyring",
00406       "--quiet",
00407       "--list-public-keys",
00408       "--with-colons",
00409       "--with-fingerprint",
00410       "--no-tty",
00411       "--no-greeting",
00412       "--batch",
00413       "--status-fd",
00414       "1",
00415       "--homedir",
00416       keyring.asString().c_str(),
00417       NULL
00418     };
00419     std::list<PublicKey> keys;
00420 
00421     ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
00422     std::string line;
00423     int count = 0;
00424 
00425     str::regex rxColons("^([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):\n$");
00426     str::regex rxColonsFpr("^([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):\n$");
00427 
00428     for(line = prog.receiveLine(), count=0; !line.empty(); line = prog.receiveLine(), count++ )
00429     {
00430       //MIL << line << std::endl;
00431       str::smatch what;
00432       if(str::regex_match(line, what, rxColons, str::match_extra))
00433       {
00434         PublicKey key;
00435         if ( what[1] == "pub" )
00436         {
00437           key.id = what[5];
00438           key.name = what[10];
00439 
00440           std::string line2;
00441           for(line2 = prog.receiveLine(); !line2.empty(); line2 = prog.receiveLine(), count++ )
00442           {
00443             str::smatch what2;
00444             if (str::regex_match(line2, what2, rxColonsFpr, str::match_extra))
00445             {
00446               if ( (what2[1] == "fpr") && (what2[1] != "pub") && (what2[1] !="sub"))
00447               {
00448                 key.fingerprint = what2[10];
00449                 break;
00450               }
00451             }
00452           }
00453           keys.push_back(key);
00454           MIL << "Found key " << "[" << key.id << "]" << " [" << key.name << "]" << " [" << key.fingerprint << "]" << std::endl;
00455         }
00456         //dumpRegexpResults(what);
00457       }
00458     }
00459     prog.close();
00460     return keys;
00461   }
00462 
00463   void KeyRing::Impl::importKey( const Pathname &keyfile, const Pathname &keyring)
00464   {
00465     const char* argv[] =
00466     {
00467       "gpg",
00468       "--no-default-keyring",
00469       "--quiet",
00470       "--no-tty",
00471       "--no-greeting",
00472       "--no-permission-warning",
00473       "--status-fd",
00474       "1",
00475       "--homedir",
00476       keyring.asString().c_str(),
00477       "--import",
00478       keyfile.asString().c_str(),
00479       NULL
00480     };
00481 
00482     int code;
00483     ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
00484     code = prog.close();
00485 
00486     //if ( code != 0 )
00487     //  ZYPP_THROW(Exception("failed to import key"));
00488   }
00489 
00490   void KeyRing::Impl::deleteKey( const std::string &id, const Pathname &keyring )
00491   {
00492     const char* argv[] =
00493     {
00494       "gpg",
00495       "--no-default-keyring",
00496       "--yes",
00497       "--quiet",
00498       "--no-tty",
00499       "--batch",
00500       "--status-fd",
00501       "1",
00502       "--homedir",
00503       keyring.asString().c_str(),
00504       "--delete-keys",
00505       id.c_str(),
00506       NULL
00507     };
00508 
00509     ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
00510 
00511     int code = prog.close();
00512     if ( code )
00513       ZYPP_THROW(Exception("Failed to delete key."));
00514     else
00515       MIL << "Deleted key " << id << " from keyring " << keyring << std::endl;
00516   }
00517 
00518 
00519   std::string KeyRing::Impl::readSignatureKeyId(const Pathname &signature )
00520   {
00521     MIL << "Deetermining key id if signature " << signature << std::endl;
00522     // HACK create a tmp keyring with no keys
00523     TmpDir dir(_base_dir);
00524     TmpFile fakeData(_base_dir);
00525 
00526     const char* argv[] =
00527     {
00528       "gpg",
00529       "--no-default-keyring",
00530       "--quiet",
00531       "--no-tty",
00532       "--no-greeting",
00533       "--batch",
00534       "--status-fd",
00535       "1",
00536       "--homedir",
00537       dir.path().asString().c_str(),
00538       "--verify",
00539       signature.asString().c_str(),
00540       fakeData.path().asString().c_str(),
00541       NULL
00542     };
00543 
00544     ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
00545 
00546     std::string line;
00547     int count = 0;
00548 
00549     str::regex rxNoKey("^\\[GNUPG:\\] NO_PUBKEY (.+)\n$");
00550     std::string id;
00551     for(line = prog.receiveLine(), count=0; !line.empty(); line = prog.receiveLine(), count++ )
00552     {
00553       //MIL << "[" << line << "]" << std::endl;
00554       str::smatch what;
00555       if(str::regex_match(line, what, rxNoKey, str::match_extra))
00556       {
00557         if ( what.size() > 1 )
00558           id = what[1];
00559         //dumpRegexpResults(what);
00560       }
00561     }
00562     MIL << "Determined key id [" << id << "] for signature " << signature << std::endl;
00563     prog.close();
00564     return id;
00565   }
00566 
00567   bool KeyRing::Impl::verifyFile( const Pathname &file, const Pathname &signature, const Pathname &keyring)
00568   {
00569     const char* argv[] =
00570     {
00571       "gpg",
00572       "--no-default-keyring",
00573       "--quiet",
00574       "--no-tty",
00575       "--batch",
00576       "--no-greeting",
00577       "--status-fd",
00578       "1",
00579       "--homedir",
00580       keyring.asString().c_str(),
00581       "--verify",
00582       signature.asString().c_str(),
00583       file.asString().c_str(),
00584       NULL
00585     };
00586 
00587     // no need to parse output for now
00588     //     [GNUPG:] SIG_ID yCc4u223XRJnLnVAIllvYbUd8mQ 2006-03-29 1143618744
00589     //     [GNUPG:] GOODSIG A84EDAE89C800ACA SuSE Package Signing Key <build@suse.de>
00590     //     gpg: Good signature from "SuSE Package Signing Key <build@suse.de>"
00591     //     [GNUPG:] VALIDSIG 79C179B2E1C820C1890F9994A84EDAE89C800ACA 2006-03-29 1143618744 0 3 0 17 2 00 79C179B2E1C820C1890F9994A84EDAE89C800ACA
00592     //     [GNUPG:] TRUST_UNDEFINED
00593 
00594     //     [GNUPG:] ERRSIG A84EDAE89C800ACA 17 2 00 1143618744 9
00595     //     [GNUPG:] NO_PUBKEY A84EDAE89C800ACA
00596 
00597     ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
00598 
00599     return (prog.close() == 0) ? true : false;
00600   }
00601 
00603 
00605   //
00606   //    CLASS NAME : KeyRing
00607   //
00609 
00611   //
00612   //    METHOD NAME : KeyRing::KeyRing
00613   //    METHOD TYPE : Ctor
00614   //
00615   KeyRing::KeyRing(const Pathname &baseTmpDir)
00616   : _pimpl( new Impl(baseTmpDir) )
00617   {}
00618 
00620   //
00621   //    METHOD NAME : KeyRing::KeyRing
00622   //    METHOD TYPE : Ctor
00623   //
00624   //KeyRing::KeyRing( const Pathname &general_kr, const Pathname &trusted_kr )
00625   //: _pimpl( new Impl(general_kr, trusted_kr) )
00626   //{}
00627 
00629   //
00630   //    METHOD NAME : KeyRing::~KeyRing
00631   //    METHOD TYPE : Dtor
00632   //
00633   KeyRing::~KeyRing()
00634   {}
00635 
00637   //
00638   // Forward to implementation:
00639   //
00641 
00642   void KeyRing::importKey( const Pathname &keyfile, bool trusted)
00643   {
00644     _pimpl->importKey(keyfile, trusted);
00645   }
00646 
00647   PublicKey KeyRing::readPublicKey( const Pathname &keyfile )
00648   {
00649     return _pimpl->readPublicKey(keyfile);
00650   }
00651 
00652   std::string KeyRing::readSignatureKeyId( const Pathname &signature )
00653   {
00654     return _pimpl->readSignatureKeyId(signature);
00655   }
00656 
00657   void KeyRing::deleteKey( const std::string &id, bool trusted )
00658   {
00659     _pimpl->deleteKey(id, trusted);
00660   }
00661 
00662   std::list<PublicKey> KeyRing::publicKeys()
00663   {
00664     return _pimpl->publicKeys();
00665   }
00666 
00667   std::list<PublicKey> KeyRing::trustedPublicKeys()
00668   {
00669     return _pimpl->trustedPublicKeys();
00670   }
00671 
00672   bool KeyRing::verifyFileSignatureWorkflow( const Pathname &file, const std::string filedesc, const Pathname &signature)
00673   {
00674     return _pimpl->verifyFileSignatureWorkflow(file, filedesc, signature);
00675   }
00676 
00677   bool KeyRing::verifyFileSignature( const Pathname &file, const Pathname &signature)
00678   {
00679     return _pimpl->verifyFileSignature(file, signature);
00680   }
00681 
00682   bool KeyRing::verifyFileTrustedSignature( const Pathname &file, const Pathname &signature)
00683   {
00684     return _pimpl->verifyFileTrustedSignature(file, signature);
00685   }
00686 
00687   void KeyRing::dumpPublicKey( const std::string &id, bool trusted, std::ostream &stream )
00688   {
00689     _pimpl->dumpPublicKey( id, trusted, stream);
00690   }
00691 
00693 } // namespace zypp

Generated on Thu Jul 6 00:07:21 2006 for zypp by  doxygen 1.4.6