PublicKey.cc

Go to the documentation of this file.
00001 /*---------------------------------------------------------------------\
00002 |                          ____ _   __ __ ___                          |
00003 |                         |__  / \ / / . \ . \                         |
00004 |                           / / \ V /|  _/  _/                         |
00005 |                          / /__ | | | | | |                           |
00006 |                         /_____||_| |_| |_|                           |
00007 |                                                                      |
00008 \---------------------------------------------------------------------*/
00012 #include <climits>
00013 
00014 #include <iostream>
00015 #include <vector>
00016 
00017 //#include "zypp/base/Logger.h"
00018 
00019 #include "zypp/base/Gettext.h"
00020 #include "zypp/base/String.h"
00021 #include "zypp/base/Regex.h"
00022 #include "zypp/PublicKey.h"
00023 #include "zypp/ExternalProgram.h"
00024 #include "zypp/TmpPath.h"
00025 #include "zypp/PathInfo.h"
00026 #include "zypp/base/Exception.h"
00027 #include "zypp/base/Logger.h"
00028 #include "zypp/Date.h"
00029 #include "zypp/TmpPath.h"
00030 
00031 #include <ctime>
00032 
00033 using std::endl;
00034 
00036 namespace zypp
00037 { 
00038 
00043   struct PublicKey::Impl
00044   {
00046     struct KeyData
00047     {
00048       std::string _id;
00049       std::string _name;
00050       std::string _fingerprint;
00051       Date        _created;
00052       Date        _expires;
00053     };
00054 
00055     Impl()
00056     {}
00057 
00058     Impl( const Pathname & keyfile )
00059     {
00060       PathInfo info( keyfile );
00061       MIL << "Takeing pubkey from " << keyfile << " of size " << info.size() << " and sha1 " << filesystem::checksum(keyfile, "sha1") << endl;
00062 
00063       if ( !info.isExist() )
00064         ZYPP_THROW(Exception("Can't read public key from " + keyfile.asString() + ", file not found"));
00065 
00066       if ( copy( keyfile, _dataFile.path() ) != 0 )
00067         ZYPP_THROW(Exception("Can't copy public key data from " + keyfile.asString() + " to " +  _dataFile.path().asString() ));
00068 
00069       readFromFile();
00070     }
00071 
00072     Impl( const filesystem::TmpFile & sharedfile )
00073       : _dataFile( sharedfile )
00074     { readFromFile(); }
00075 
00076     public:
00078       static shared_ptr<Impl> nullimpl()
00079       {
00080         static shared_ptr<Impl> _nullimpl( new Impl );
00081         return _nullimpl;
00082       }
00083 
00084       std::string asString() const
00085       {
00086         return str::form( "[%s-%s] [%s] [%s] [TTL %d]",
00087                           id().c_str(), str::hexstring(created(),8).substr(2).c_str(),
00088                           name().c_str(),
00089                           fingerprint().c_str(),
00090                           daysToLive() );
00091       }
00092 
00093       std::string id() const
00094       { return _keyData._id; }
00095 
00096       std::string name() const
00097       { return _keyData._name; }
00098 
00099       std::string fingerprint() const
00100       { return _keyData._fingerprint; }
00101 
00102       std::string gpgPubkeyVersion() const
00103       { return _keyData._id.empty() ? _keyData._id : str::toLower( _keyData._id.substr(8,8) ); }
00104 
00105       std::string gpgPubkeyRelease() const
00106       { return _keyData._created ? str::hexstring( _keyData._created ).substr(2) : std::string(); }
00107 
00108       Date created() const
00109       { return _keyData._created; }
00110 
00111       Date expires() const
00112       { return _keyData._expires; }
00113 
00114       std::string expiresAsString() const
00115       {
00116         if ( !_keyData._expires )
00117         { // translators: an annotation to a gpg keys expiry date
00118           return _("(does not expire)");
00119         }
00120         std::string ret( _keyData._expires.asString() );
00121         int ttl( daysToLive() );
00122         if ( ttl <= 90 )
00123         {
00124           ret += " ";
00125           if ( ttl < 0 )
00126           { // translators: an annotation to a gpg keys expiry date
00127             ret += _("(EXPIRED)");
00128           }
00129           else if ( ttl == 0 )
00130           { // translators: an annotation to a gpg keys expiry date
00131             ret += _("(expires within 24h)");
00132           }
00133           else
00134           { // translators: an annotation to a gpg keys expiry date
00135             ret += str::form( _PL("(expires in %d day)", "(expires in %d days)", ttl ), ttl );
00136           }
00137         }
00138         return ret;
00139       }
00140 
00141       Pathname path() const
00142       { return _dataFile.path(); }
00143 
00144       bool expired() const
00145       {
00146         Date exp( expires() );
00147         return( exp && exp < Date::now() );
00148       }
00149 
00150       int daysToLive() const
00151       {
00152         Date exp( expires() );
00153         if ( ! expires() )
00154           return INT_MAX;
00155         exp -= Date::now();
00156         return exp < 0 ? exp / Date::day - 1 : exp / Date::day;
00157       }
00158 
00159     protected:
00160 
00161       void readFromFile()
00162       {
00163         PathInfo info( _dataFile.path() );
00164         MIL << "Reading pubkey from " << info.path() << " of size " << info.size() << " and sha1 " << filesystem::checksum(info.path(), "sha1") << endl;
00165 
00166         static filesystem::TmpDir dir;
00167         const char* argv[] =
00168         {
00169           "gpg",
00170           "-v",
00171           "--no-default-keyring",
00172           "--fixed-list-mode",
00173           "--with-fingerprint",
00174           "--with-colons",
00175           "--homedir",
00176           dir.path().asString().c_str(),
00177           "--quiet",
00178           "--no-tty",
00179           "--no-greeting",
00180           "--batch",
00181           "--status-fd",
00182           "1",
00183           _dataFile.path().asString().c_str(),
00184           NULL
00185         };
00186 
00187         ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
00188 
00189         // pub:-:1024:17:A84EDAE89C800ACA:971961473:1214043198::-:SuSE Package Signing Key <build@suse.de>:
00190         // fpr:::::::::79C179B2E1C820C1890F9994A84EDAE89C800ACA:
00191         // sig:::17:A84EDAE89C800ACA:1087899198:::::[selfsig]::13x:
00192         // sig:::17:9E40E310000AABA4:980442706::::[User ID not found]:10x:
00193         // sig:::1:77B2E6003D25D3D9:980443247::::[User ID not found]:10x:
00194         // sub:-:2048:16:197448E88495160C:971961490:1214043258::: [expires: 2008-06-21]
00195         // sig:::17:A84EDAE89C800ACA:1087899258:::::[keybind]::18x:
00196         KeyData keyData;
00197         std::vector<std::string> words;
00198         enum { pNONE, pPUB, pSIG, pFPR, pUID } parseEntry;
00199         bool sawSig = false;
00200         bool sawSub = false;
00201         for ( std::string line = prog.receiveLine(); !line.empty(); line = prog.receiveLine() )
00202         {
00203           if ( line.empty() )
00204             continue;
00205 
00206           if ( sawSub )
00207           {
00208             if ( line[0] == 'p' && line[1] == 'u' && line[2] == 'b' && line[3] == ':' )
00209               sawSub = false;   // parse next key
00210             else
00211               continue;         // don't parse in subkeys
00212           }
00213 
00214           // quick check for interesting entries
00215           parseEntry = pNONE;
00216           switch ( line[0] )
00217           {
00218 #define DOTEST( C1, C2, C3, E ) case C1: if ( line[1] == C2 && line[2] == C3 && line[3] == ':' ) parseEntry = E; break
00219             DOTEST( 'p', 'u', 'b', pPUB );
00220             DOTEST( 's', 'i', 'g', pSIG );
00221             DOTEST( 'f', 'p', 'r', pFPR );
00222             DOTEST( 'u', 'i', 'd', pUID );
00223 #undef DOTEST
00224           }
00225           if ( parseEntry == pNONE )
00226           {
00227             if ( line[0] == 's' && line[1] == 'u' && line[2] == 'b' && line[3] == ':' )
00228               sawSub = true;    // don't parse in subkeys
00229             continue;
00230           }
00231 
00232           if ( line[line.size()-1] == '\n' )
00233             line.erase( line.size()-1 );
00234 
00235           words.clear();
00236           str::splitFields( line, std::back_inserter(words), ":" );
00237 
00238           switch ( parseEntry )
00239           {
00240             case pPUB:
00241               keyData = KeyData();      // reset upon new key
00242               sawSig  = false;
00243               keyData._id      = words[4];
00244               keyData._name    = words[9];
00245               keyData._created = Date(str::strtonum<Date::ValueType>(words[5]));
00246               keyData._expires = Date(str::strtonum<Date::ValueType>(words[6]));
00247               break;
00248 
00249             case pSIG:
00250               if ( !sawSig && words[words.size()-2] == "13x"  )
00251               {
00252                 // update creation and expire dates from 1st signature type "13x"
00253                 if ( ! words[5].empty() )
00254                   keyData._created = Date(str::strtonum<Date::ValueType>(words[5]));
00255                 if ( ! words[6].empty() )
00256                   keyData._expires = Date(str::strtonum<Date::ValueType>(words[6]));
00257                 sawSig = true;
00258               }
00259               break;
00260 
00261             case pFPR:
00262               if ( ! words[9].empty() )
00263                 keyData._fingerprint = words[9];
00264               break;
00265 
00266             case pUID:
00267               if ( ! words[9].empty() )
00268                 keyData._name = words[9];
00269               break;
00270 
00271             case pNONE:
00272               break;
00273           }
00274         }
00275         prog.close();
00276 
00277         if ( keyData._id.empty() )
00278           ZYPP_THROW( BadKeyException( "File " + _dataFile.path().asString() + " doesn't contain public key data" , _dataFile.path() ) );
00279 
00280         //replace all escaped semicolon with real ':'
00281         str::replaceAll( keyData._name, "\\x3a", ":" );
00282 
00283         _keyData = keyData;
00284         MIL << "Read pubkey from " << info.path() << ": " << asString() << endl;
00285       }
00286 
00287     private:
00288       filesystem::TmpFile       _dataFile;
00289       KeyData                   _keyData;
00290 
00291     private:
00292       friend Impl * rwcowClone<Impl>( const Impl * rhs );
00294       Impl * clone() const
00295       { return new Impl( *this ); }
00296   };
00298 
00300   //
00301   //    class PublicKey
00302   //
00304   PublicKey::PublicKey()
00305   : _pimpl( Impl::nullimpl() )
00306   {}
00307 
00308   PublicKey::PublicKey( const Pathname & file )
00309   : _pimpl( new Impl(file) )
00310   {}
00311 
00312   PublicKey::PublicKey( const filesystem::TmpFile & sharedfile )
00313   : _pimpl( new Impl(sharedfile) )
00314   {}
00315 
00316   PublicKey::~PublicKey()
00317   {}
00318 
00319   std::string PublicKey::asString() const
00320   { return _pimpl->asString(); }
00321 
00322   std::string PublicKey::armoredData() const
00323   { return std::string(); }
00324 
00325   std::string PublicKey::id() const
00326   { return _pimpl->id(); }
00327 
00328   std::string PublicKey::name() const
00329   { return _pimpl->name(); }
00330 
00331   std::string PublicKey::fingerprint() const
00332   { return _pimpl->fingerprint(); }
00333 
00334   std::string PublicKey::gpgPubkeyVersion() const
00335   { return _pimpl->gpgPubkeyVersion(); }
00336 
00337   std::string PublicKey::gpgPubkeyRelease() const
00338   { return _pimpl->gpgPubkeyRelease(); }
00339 
00340   Date PublicKey::created() const
00341   { return _pimpl->created(); }
00342 
00343   Date PublicKey::expires() const
00344   { return _pimpl->expires(); }
00345 
00346   std::string PublicKey::expiresAsString() const
00347   { return _pimpl->expiresAsString(); }
00348 
00349   bool PublicKey::expired() const
00350   { return _pimpl->expired(); }
00351 
00352   int PublicKey::daysToLive() const
00353   { return _pimpl->daysToLive(); }
00354 
00355   Pathname PublicKey::path() const
00356   { return _pimpl->path(); }
00357 
00358   bool PublicKey::operator==( PublicKey b ) const
00359   {
00360     return (   b.id() == id()
00361             && b.fingerprint() == fingerprint()
00362             && b.created() == created() );
00363   }
00364 
00365   bool PublicKey::operator==( std::string sid ) const
00366   {
00367     return sid == id();
00368   }
00369 
00370   std::ostream & dumpOn( std::ostream & str, const PublicKey & obj )
00371   {
00372     str << "[" << obj.name() << "]" << endl;
00373     str << "  fpr " << obj.fingerprint() << endl;
00374     str << "   id " << obj.id() << endl;
00375     str << "  cre " << obj.created() << endl;
00376     str << "  exp " << obj.expiresAsString() << endl;
00377     str << "  ttl " << obj.daysToLive() << endl;
00378     str << "  rpm " << obj.gpgPubkeyVersion() << "-" << obj.gpgPubkeyRelease() << endl;
00379     str << "]";
00380     return str;
00381   }
00382 
00384 } // namespace zypp

doxygen