00001
00002
00003
00004
00005
00006
00007
00008
00012 #include <climits>
00013
00014 #include <iostream>
00015 #include <vector>
00016
00017 #include "zypp/base/Gettext.h"
00018 #include "zypp/base/String.h"
00019 #include "zypp/base/Regex.h"
00020 #include "zypp/PublicKey.h"
00021 #include "zypp/ExternalProgram.h"
00022 #include "zypp/TmpPath.h"
00023 #include "zypp/PathInfo.h"
00024 #include "zypp/base/Exception.h"
00025 #include "zypp/base/LogTools.h"
00026 #include "zypp/Date.h"
00027 #include "zypp/TmpPath.h"
00028
00029 #include <ctime>
00030
00031 using std::endl;
00032
00034 namespace zypp
00035 {
00036
00041 struct PublicKeyData::Impl
00042 {
00043 std::string _id;
00044 std::string _name;
00045 std::string _fingerprint;
00046 Date _created;
00047 Date _expires;
00048
00049 public:
00051 static shared_ptr<Impl> nullimpl()
00052 {
00053 static shared_ptr<Impl> _nullimpl( new Impl );
00054 return _nullimpl;
00055 }
00056
00057 private:
00058 friend Impl * rwcowClone<Impl>( const Impl * rhs );
00060 Impl * clone() const
00061 { return new Impl( *this ); }
00062 };
00064
00068
00069 PublicKeyData::PublicKeyData()
00070 : _pimpl( Impl::nullimpl() )
00071 {}
00072
00073 PublicKeyData::~PublicKeyData()
00074 {}
00075
00076 bool PublicKeyData::boolTest() const
00077 { return !_pimpl->_fingerprint.empty(); }
00078
00079 std::string PublicKeyData::id() const
00080 { return _pimpl->_id; }
00081
00082 std::string PublicKeyData::name() const
00083 { return _pimpl->_name; }
00084
00085 std::string PublicKeyData::fingerprint() const
00086 { return _pimpl->_fingerprint; }
00087
00088 Date PublicKeyData::created() const
00089 { return _pimpl->_created; }
00090
00091 Date PublicKeyData::expires() const
00092 { return _pimpl->_expires; }
00093
00094 bool PublicKeyData::expired() const
00095 { return( _pimpl->_expires && _pimpl->_expires < Date::now() ); }
00096
00097 int PublicKeyData::daysToLive() const
00098 {
00099 if ( _pimpl->_expires )
00100 {
00101 Date exp( _pimpl->_expires - Date::now() );
00102 return exp < 0 ? exp / Date::day - 1 : exp / Date::day;
00103 }
00104 return INT_MAX;
00105 }
00106
00107 std::string PublicKeyData::expiresAsString() const
00108 {
00109 if ( !_pimpl->_expires )
00110 {
00111 return _("(does not expire)");
00112 }
00113 std::string ret( _pimpl->_expires.asString() );
00114 int ttl( daysToLive() );
00115 if ( ttl <= 90 )
00116 {
00117 ret += " ";
00118 if ( ttl < 0 )
00119 {
00120 ret += _("(EXPIRED)");
00121 }
00122 else if ( ttl == 0 )
00123 {
00124 ret += _("(expires within 24h)");
00125 }
00126 else
00127 {
00128 ret += str::form( _PL("(expires in %d day)", "(expires in %d days)", ttl ), ttl );
00129 }
00130 }
00131 return ret;
00132 }
00133
00134 std::string PublicKeyData::gpgPubkeyVersion() const
00135 { return _pimpl->_id.empty() ? _pimpl->_id : str::toLower( _pimpl->_id.substr(8,8) ); }
00136
00137 std::string PublicKeyData::gpgPubkeyRelease() const
00138 { return _pimpl->_created ? str::hexstring( _pimpl->_created ).substr(2) : std::string(); }
00139
00140 std::string PublicKeyData::asString() const
00141 {
00142 return str::form( "[%s-%s] [%s] [%s] [TTL %d]",
00143 _pimpl->_id.c_str(),
00144 gpgPubkeyRelease().c_str(),
00145 _pimpl->_name.c_str(),
00146 _pimpl->_fingerprint.c_str(),
00147 daysToLive() );
00148 }
00149
00150 std::ostream & dumpOn( std::ostream & str, const PublicKeyData & obj )
00151 {
00152 str << "[" << obj.name() << "]" << endl;
00153 str << " fpr " << obj.fingerprint() << endl;
00154 str << " id " << obj.id() << endl;
00155 str << " cre " << Date::ValueType(obj.created()) << ' ' << obj.created() << endl;
00156 str << " exp " << Date::ValueType(obj.expires()) << ' ' << obj.expiresAsString() << endl;
00157 str << " ttl " << obj.daysToLive() << endl;
00158 str << " rpm " << obj.gpgPubkeyVersion() << "-" << obj.gpgPubkeyRelease() << endl;
00159 str << "]";
00160 return str;
00161 }
00162
00163 bool operator==( const PublicKeyData & lhs, const PublicKeyData & rhs )
00164 { return ( lhs.fingerprint() == rhs.fingerprint() && lhs.created() == rhs.created() ); }
00165
00166
00171 struct PublicKeyScanner::Impl
00172 {
00173 std::vector<std::string> _words;
00174 enum { pNONE, pPUB, pSIG, pFPR, pUID } _parseEntry;
00175
00176 Impl()
00177 : _parseEntry( pNONE )
00178 {}
00179
00180 void scan( std::string & line_r, std::list<PublicKeyData> & keys_r )
00181 {
00182
00183
00184
00185
00186
00187
00188
00189
00190 if ( line_r.empty() )
00191 return;
00192
00193
00194 _parseEntry = pNONE;
00195 switch ( line_r[0] )
00196 {
00197 #define DOTEST( C1, C2, C3, E ) case C1: if ( line_r[1] == C2 && line_r[2] == C3 && line_r[3] == ':' ) _parseEntry = E; break
00198 DOTEST( 'p', 'u', 'b', pPUB );
00199 DOTEST( 's', 'i', 'g', pSIG );
00200 DOTEST( 'f', 'p', 'r', pFPR );
00201 DOTEST( 'u', 'i', 'd', pUID );
00202 #undef DOTEST
00203 }
00204 if ( _parseEntry == pNONE )
00205 return;
00206
00207 if ( line_r[line_r.size()-1] == '\n' )
00208 line_r.erase( line_r.size()-1 );
00209
00210
00211 _words.clear();
00212 str::splitFields( line_r, std::back_inserter(_words), ":" );
00213
00214 PublicKeyData * key( &keys_r.back() );
00215
00216 switch ( _parseEntry )
00217 {
00218 case pPUB:
00219 keys_r.push_back( PublicKeyData() );
00220 key = &keys_r.back();
00221 key->_pimpl->_id = _words[4];
00222 key->_pimpl->_name = str::replaceAll( _words[9], "\\x3a", ":" );
00223 key->_pimpl->_created = Date(str::strtonum<Date::ValueType>(_words[5]));
00224 key->_pimpl->_expires = Date(str::strtonum<Date::ValueType>(_words[6]));
00225 break;
00226
00227 case pSIG:
00228
00229 if ( _words[_words.size()-2] == "13x" )
00230 {
00231 Date cdate(str::strtonum<Date::ValueType>(_words[5]));
00232 if ( key->_pimpl->_created < cdate )
00233 key->_pimpl->_created = cdate;
00234 }
00235 break;
00236
00237 case pFPR:
00238 if ( key->_pimpl->_fingerprint.empty() )
00239 key->_pimpl->_fingerprint = _words[9];
00240 break;
00241
00242 case pUID:
00243 if ( ! _words[9].empty() )
00244 key->_pimpl->_name = str::replaceAll( _words[9], "\\x3a", ":" );
00245 break;
00246
00247 case pNONE:
00248 break;
00249 }
00250 }
00251 };
00253
00255
00257
00258 PublicKeyScanner::PublicKeyScanner()
00259 : _pimpl( new Impl )
00260 {}
00261
00262 PublicKeyScanner::~PublicKeyScanner()
00263 {}
00264
00265 void PublicKeyScanner::scan( std::string line_r )
00266 { _pimpl->scan( line_r, _keys ); }
00267
00268
00273 struct PublicKey::Impl
00274 {
00275 Impl()
00276 {}
00277
00278 Impl( const Pathname & keyFile_r )
00279 {
00280 PathInfo info( keyFile_r );
00281 MIL << "Taking pubkey from " << keyFile_r << " of size " << info.size() << " and sha1 " << filesystem::checksum(keyFile_r, "sha1") << endl;
00282
00283 if ( !info.isExist() )
00284 ZYPP_THROW(Exception("Can't read public key from " + keyFile_r.asString() + ", file not found"));
00285
00286 if ( filesystem::hardlinkCopy( keyFile_r, _dataFile.path() ) != 0 )
00287 ZYPP_THROW(Exception("Can't copy public key data from " + keyFile_r.asString() + " to " + _dataFile.path().asString() ));
00288
00289 readFromFile();
00290 }
00291
00292 Impl( const filesystem::TmpFile & sharedFile_r )
00293 : _dataFile( sharedFile_r )
00294 { readFromFile(); }
00295
00296 Impl( const filesystem::TmpFile & sharedFile_r, const PublicKeyData & keyData_r )
00297 : _dataFile( sharedFile_r )
00298 , _keyData( keyData_r )
00299 {
00300 if ( ! keyData_r )
00301 {
00302 WAR << "Invalid PublicKeyData supplied: scanning from file" << endl;
00303 readFromFile();
00304 }
00305 }
00306
00307 public:
00308 const PublicKeyData & keyData() const
00309 { return _keyData; }
00310
00311 Pathname path() const
00312 { return _dataFile.path(); }
00313
00314 const std::list<PublicKeyData> & hiddenKeys() const
00315 { return _hiddenKeys; }
00316
00317 protected:
00318 void readFromFile()
00319 {
00320 PathInfo info( _dataFile.path() );
00321 MIL << "Reading pubkey from " << info.path() << " of size " << info.size() << " and sha1 " << filesystem::checksum(info.path(), "sha1") << endl;
00322
00323 static filesystem::TmpDir dir;
00324 const char* argv[] =
00325 {
00326 "gpg",
00327 "-v",
00328 "--no-default-keyring",
00329 "--fixed-list-mode",
00330 "--with-fingerprint",
00331 "--with-colons",
00332 "--homedir",
00333 dir.path().asString().c_str(),
00334 "--quiet",
00335 "--no-tty",
00336 "--no-greeting",
00337 "--batch",
00338 "--status-fd",
00339 "1",
00340 _dataFile.path().asString().c_str(),
00341 NULL
00342 };
00343 ExternalProgram prog( argv, ExternalProgram::Discard_Stderr, false, -1, true );
00344
00345 PublicKeyScanner scanner;
00346 for ( std::string line = prog.receiveLine(); !line.empty(); line = prog.receiveLine() )
00347 {
00348 scanner.scan( line );
00349 }
00350 prog.close();
00351
00352 switch ( scanner._keys.size() )
00353 {
00354 case 0:
00355 ZYPP_THROW( BadKeyException( "File " + _dataFile.path().asString() + " doesn't contain public key data" , _dataFile.path() ) );
00356 break;
00357
00358 case 1:
00359
00360 _keyData = scanner._keys.back();
00361 _hiddenKeys.clear();
00362 break;
00363
00364 default:
00365 WAR << "File " << _dataFile.path().asString() << " contains multiple keys: " << scanner._keys << endl;
00366 _keyData = scanner._keys.back();
00367 scanner._keys.pop_back();
00368 _hiddenKeys.swap( scanner._keys );
00369 break;
00370 }
00371
00372 MIL << "Read pubkey from " << info.path() << ": " << _keyData << endl;
00373 }
00374
00375 private:
00376 filesystem::TmpFile _dataFile;
00377 PublicKeyData _keyData;
00378 std::list<PublicKeyData> _hiddenKeys;
00379
00380 public:
00382 static shared_ptr<Impl> nullimpl()
00383 {
00384 static shared_ptr<Impl> _nullimpl( new Impl );
00385 return _nullimpl;
00386 }
00387
00388 private:
00389 friend Impl * rwcowClone<Impl>( const Impl * rhs );
00391 Impl * clone() const
00392 { return new Impl( *this ); }
00393 };
00395
00397
00399 PublicKey::PublicKey()
00400 : _pimpl( Impl::nullimpl() )
00401 {}
00402
00403 PublicKey::PublicKey( const Pathname & file )
00404 : _pimpl( new Impl( file ) )
00405 {}
00406
00407 PublicKey::PublicKey( const filesystem::TmpFile & sharedfile )
00408 : _pimpl( new Impl( sharedfile ) )
00409 {}
00410
00411 PublicKey::PublicKey( const filesystem::TmpFile & sharedfile, const PublicKeyData & keydata )
00412 : _pimpl( new Impl( sharedfile, keydata ) )
00413 {}
00414
00415 PublicKey::~PublicKey()
00416 {}
00417
00418 const PublicKeyData & PublicKey::keyData() const
00419 { return _pimpl->keyData(); }
00420
00421 Pathname PublicKey::path() const
00422 { return _pimpl->path(); }
00423
00424 const std::list<PublicKeyData> & PublicKey::hiddenKeys() const
00425 { return _pimpl->hiddenKeys(); }
00426
00427 std::string PublicKey::id() const
00428 { return keyData().id(); }
00429
00430 std::string PublicKey::name() const
00431 { return keyData().name(); }
00432
00433 std::string PublicKey::fingerprint() const
00434 { return keyData().fingerprint(); }
00435
00436 Date PublicKey::created() const
00437 { return keyData().created(); }
00438
00439 Date PublicKey::expires() const
00440 { return keyData().expires(); }
00441
00442 bool PublicKey::expired() const
00443 { return keyData().expired(); }
00444
00445 int PublicKey::daysToLive() const
00446 { return keyData().daysToLive(); }
00447
00448 std::string PublicKey::expiresAsString() const
00449 { return keyData().expiresAsString(); }
00450
00451 std::string PublicKey::gpgPubkeyVersion() const
00452 { return keyData().gpgPubkeyVersion(); }
00453
00454 std::string PublicKey::gpgPubkeyRelease() const
00455 { return keyData().gpgPubkeyRelease(); }
00456
00457 std::string PublicKey::asString() const
00458 { return keyData().asString(); }
00459
00460 bool PublicKey::operator==( PublicKey rhs ) const
00461 { return rhs.keyData() == keyData(); }
00462
00463 bool PublicKey::operator==( std::string sid ) const
00464 { return sid == id(); }
00465
00466 std::ostream & dumpOn( std::ostream & str, const PublicKey & obj )
00467 { return dumpOn( str, obj.keyData() ); }
00468
00470 }