1 /*---------------------------------------------------------------------\
3 | |__ / \ / / . \ . \ |
8 \---------------------------------------------------------------------*/
9 /** \file zypp/PublicKey.cc
17 #include "zypp/base/Gettext.h"
18 #include "zypp/base/String.h"
19 #include "zypp/base/Regex.h"
20 #include "zypp/PublicKey.h"
21 #include "zypp/ExternalProgram.h"
22 #include "zypp/TmpPath.h"
23 #include "zypp/PathInfo.h"
24 #include "zypp/base/Exception.h"
25 #include "zypp/base/LogTools.h"
26 #include "zypp/Date.h"
27 #include "zypp/TmpPath.h"
31 /** \todo Fix duplicate define in PublicKey/KeyRing */
32 #define GPG_BINARY "/usr/bin/gpg2"
36 ///////////////////////////////////////////////////////////////////
38 { /////////////////////////////////////////////////////////////////
40 ///////////////////////////////////////////////////////////////////
41 /// \class PublicKeyData::Impl
42 /// \brief PublicKeyData implementation.
43 ///////////////////////////////////////////////////////////////////
44 struct PublicKeyData::Impl
48 std::string _fingerprint;
53 /** Offer default Impl. */
54 static shared_ptr<Impl> nullimpl()
56 static shared_ptr<Impl> _nullimpl( new Impl );
61 friend Impl * rwcowClone<Impl>( const Impl * rhs );
62 /** clone for RWCOW_pointer */
64 { return new Impl( *this ); }
66 ///////////////////////////////////////////////////////////////////
68 ///////////////////////////////////////////////////////////////////
69 /// class PublicKeyData
70 ///////////////////////////////////////////////////////////////////
72 PublicKeyData::PublicKeyData()
73 : _pimpl( Impl::nullimpl() )
76 PublicKeyData::~PublicKeyData()
79 PublicKeyData::operator bool() const
80 { return !_pimpl->_fingerprint.empty(); }
82 std::string PublicKeyData::id() const
83 { return _pimpl->_id; }
85 std::string PublicKeyData::name() const
86 { return _pimpl->_name; }
88 std::string PublicKeyData::fingerprint() const
89 { return _pimpl->_fingerprint; }
91 Date PublicKeyData::created() const
92 { return _pimpl->_created; }
94 Date PublicKeyData::expires() const
95 { return _pimpl->_expires; }
97 bool PublicKeyData::expired() const
98 { return( _pimpl->_expires && _pimpl->_expires < Date::now() ); }
100 int PublicKeyData::daysToLive() const
102 if ( _pimpl->_expires )
104 Date exp( _pimpl->_expires - Date::now() );
105 int ret = exp / Date::day;
106 if ( exp < 0 ) ret -= 1;
112 std::string PublicKeyData::expiresAsString() const
114 if ( !_pimpl->_expires )
115 { // translators: an annotation to a gpg keys expiry date
116 return _("(does not expire)");
118 std::string ret( _pimpl->_expires.asString() );
119 int ttl( daysToLive() );
124 { // translators: an annotation to a gpg keys expiry date
125 ret += _("(EXPIRED)");
128 { // translators: an annotation to a gpg keys expiry date
129 ret += _("(expires within 24h)");
132 { // translators: an annotation to a gpg keys expiry date
133 ret += str::form( PL_("(expires in %d day)", "(expires in %d days)", ttl ), ttl );
139 std::string PublicKeyData::gpgPubkeyVersion() const
140 { return _pimpl->_id.empty() ? _pimpl->_id : str::toLower( _pimpl->_id.substr(8,8) ); }
142 std::string PublicKeyData::gpgPubkeyRelease() const
143 { return _pimpl->_created ? str::hexstring( _pimpl->_created ).substr(2) : std::string(); }
145 std::string PublicKeyData::asString() const
147 return str::form( "[%s-%s] [%s] [%s] [TTL %d]",
149 gpgPubkeyRelease().c_str(),
150 _pimpl->_name.c_str(),
151 _pimpl->_fingerprint.c_str(),
155 std::ostream & dumpOn( std::ostream & str, const PublicKeyData & obj )
157 str << "[" << obj.name() << "]" << endl;
158 str << " fpr " << obj.fingerprint() << endl;
159 str << " id " << obj.id() << endl;
160 str << " cre " << Date::ValueType(obj.created()) << ' ' << obj.created() << endl;
161 str << " exp " << Date::ValueType(obj.expires()) << ' ' << obj.expiresAsString() << endl;
162 str << " ttl " << obj.daysToLive() << endl;
163 str << " rpm " << obj.gpgPubkeyVersion() << "-" << obj.gpgPubkeyRelease() << endl;
168 bool operator==( const PublicKeyData & lhs, const PublicKeyData & rhs )
169 { return ( lhs.fingerprint() == rhs.fingerprint() && lhs.created() == rhs.created() ); }
172 ///////////////////////////////////////////////////////////////////
173 /// \class PublicKeyScanner::Impl
174 /// \brief PublicKeyScanner implementation.
175 ///////////////////////////////////////////////////////////////////
176 struct PublicKeyScanner::Impl
178 std::vector<std::string> _words;
179 enum { pNONE, pPUB, pSIG, pFPR, pUID } _parseEntry;
180 bool _parseOff; // no 'sub:' key parsing
183 : _parseEntry( pNONE )
187 void scan( std::string & line_r, std::list<PublicKeyData> & keys_r )
189 // pub:-:1024:17:A84EDAE89C800ACA:971961473:1214043198::-:SuSE Package Signing Key <build@suse.de>:
190 // fpr:::::::::79C179B2E1C820C1890F9994A84EDAE89C800ACA:
191 // sig:::17:A84EDAE89C800ACA:1087899198:::::[selfsig]::13x:
192 // sig:::17:9E40E310000AABA4:980442706::::[User ID not found]:10x:
193 // sig:::1:77B2E6003D25D3D9:980443247::::[User ID not found]:10x:
194 // sig:::17:A84EDAE89C800ACA:1318348291:::::[selfsig]::13x:
195 // sub:-:2048:16:197448E88495160C:971961490:1214043258::: [expires: 2008-06-21]
196 // sig:::17:A84EDAE89C800ACA:1087899258:::::[keybind]::18x:
197 if ( line_r.empty() )
200 // quick check for interesting entries, no parsing in subkeys
205 if ( line_r[1] == 'u' && line_r[2] == 'b' && line_r[3] == ':' )
213 if ( line_r[1] == 'p' && line_r[2] == 'r' && line_r[3] == ':' )
218 if ( line_r[1] == 'i' && line_r[2] == 'd' && line_r[3] == ':' )
223 if ( line_r[1] == 'i' && line_r[2] == 'g' && line_r[3] == ':' )
225 else if ( line_r[1] == 'u' && line_r[2] == 'b' && line_r[3] == ':' )
232 if ( _parseOff || _parseEntry == pNONE )
235 if ( line_r[line_r.size()-1] == '\n' )
236 line_r.erase( line_r.size()-1 );
237 // DBG << line_r << endl;
240 str::splitFields( line_r, std::back_inserter(_words), ":" );
242 PublicKeyData * key( &keys_r.back() );
244 switch ( _parseEntry )
247 keys_r.push_back( PublicKeyData() ); // reset upon new key
248 key = &keys_r.back();
249 key->_pimpl->_id = _words[4];
250 key->_pimpl->_name = str::replaceAll( _words[9], "\\x3a", ":" );
251 key->_pimpl->_created = Date(str::strtonum<Date::ValueType>(_words[5]));
252 key->_pimpl->_expires = Date(str::strtonum<Date::ValueType>(_words[6]));
256 // Update creation/modification date from signatures type "13x".
257 if ( ( _words.size() > 10 && _words[10] == "13x" )
258 || ( _words.size() > 12 && _words[12] == "13x" ) )
260 Date cdate(str::strtonum<Date::ValueType>(_words[5]));
261 if ( key->_pimpl->_created < cdate )
262 key->_pimpl->_created = cdate;
267 if ( key->_pimpl->_fingerprint.empty() )
268 key->_pimpl->_fingerprint = _words[9];
272 if ( ! _words[9].empty() )
273 key->_pimpl->_name = str::replaceAll( _words[9], "\\x3a", ":" );
277 break; // intentionally no default:
281 ///////////////////////////////////////////////////////////////////
283 ///////////////////////////////////////////////////////////////////
284 // class PublicKeyScanner
285 ///////////////////////////////////////////////////////////////////
287 PublicKeyScanner::PublicKeyScanner()
291 PublicKeyScanner::~PublicKeyScanner()
294 void PublicKeyScanner::scan( std::string line_r )
295 { _pimpl->scan( line_r, _keys ); }
298 ///////////////////////////////////////////////////////////////////
299 /// \class PublicKey::Impl
300 /// \brief PublicKey implementation.
301 ///////////////////////////////////////////////////////////////////
302 struct PublicKey::Impl
307 Impl( const Pathname & keyFile_r )
309 PathInfo info( keyFile_r );
310 MIL << "Taking pubkey from " << keyFile_r << " of size " << info.size() << " and sha1 " << filesystem::checksum(keyFile_r, "sha1") << endl;
312 if ( !info.isExist() )
313 ZYPP_THROW(Exception("Can't read public key from " + keyFile_r.asString() + ", file not found"));
315 if ( filesystem::hardlinkCopy( keyFile_r, _dataFile.path() ) != 0 )
316 ZYPP_THROW(Exception("Can't copy public key data from " + keyFile_r.asString() + " to " + _dataFile.path().asString() ));
321 Impl( const filesystem::TmpFile & sharedFile_r )
322 : _dataFile( sharedFile_r )
325 Impl( const filesystem::TmpFile & sharedFile_r, const PublicKeyData & keyData_r )
326 : _dataFile( sharedFile_r )
327 , _keyData( keyData_r )
331 WAR << "Invalid PublicKeyData supplied: scanning from file" << endl;
337 const PublicKeyData & keyData() const
340 Pathname path() const
341 { return _dataFile.path(); }
343 const std::list<PublicKeyData> & hiddenKeys() const
344 { return _hiddenKeys; }
349 PathInfo info( _dataFile.path() );
350 MIL << "Reading pubkey from " << info.path() << " of size " << info.size() << " and sha1 " << filesystem::checksum(info.path(), "sha1") << endl;
352 static filesystem::TmpDir dir;
353 std::string tmppath( dir.path().asString() );
354 std::string datapath( _dataFile.path().asString() );
360 "--no-default-keyring",
362 "--with-fingerprint",
374 ExternalProgram prog( argv, ExternalProgram::Discard_Stderr, false, -1, true );
376 PublicKeyScanner scanner;
377 for ( std::string line = prog.receiveLine(); !line.empty(); line = prog.receiveLine() )
379 scanner.scan( line );
381 int ret = prog.close();
383 switch ( scanner._keys.size() )
387 ZYPP_THROW( Exception( std::string("Can't read public key data: ") + GPG_BINARY + " is not installed!" ) );
389 ZYPP_THROW( BadKeyException( "File " + _dataFile.path().asString() + " doesn't contain public key data" , _dataFile.path() ) );
394 _keyData = scanner._keys.back();
399 WAR << "File " << _dataFile.path().asString() << " contains multiple keys: " << scanner._keys << endl;
400 _keyData = scanner._keys.back();
401 scanner._keys.pop_back();
402 _hiddenKeys.swap( scanner._keys );
406 MIL << "Read pubkey from " << info.path() << ": " << _keyData << endl;
410 filesystem::TmpFile _dataFile;
411 PublicKeyData _keyData;
412 std::list<PublicKeyData> _hiddenKeys;
415 /** Offer default Impl. */
416 static shared_ptr<Impl> nullimpl()
418 static shared_ptr<Impl> _nullimpl( new Impl );
423 friend Impl * rwcowClone<Impl>( const Impl * rhs );
424 /** clone for RWCOW_pointer */
426 { return new Impl( *this ); }
428 ///////////////////////////////////////////////////////////////////
430 ///////////////////////////////////////////////////////////////////
432 ///////////////////////////////////////////////////////////////////
433 PublicKey::PublicKey()
434 : _pimpl( Impl::nullimpl() )
437 PublicKey::PublicKey( const Pathname & file )
438 : _pimpl( new Impl( file ) )
441 PublicKey::PublicKey( const filesystem::TmpFile & sharedfile )
442 : _pimpl( new Impl( sharedfile ) )
445 PublicKey::PublicKey( const filesystem::TmpFile & sharedfile, const PublicKeyData & keydata )
446 : _pimpl( new Impl( sharedfile, keydata ) )
449 PublicKey::~PublicKey()
452 const PublicKeyData & PublicKey::keyData() const
453 { return _pimpl->keyData(); }
455 Pathname PublicKey::path() const
456 { return _pimpl->path(); }
458 const std::list<PublicKeyData> & PublicKey::hiddenKeys() const
459 { return _pimpl->hiddenKeys(); }
461 std::string PublicKey::id() const
462 { return keyData().id(); }
464 std::string PublicKey::name() const
465 { return keyData().name(); }
467 std::string PublicKey::fingerprint() const
468 { return keyData().fingerprint(); }
470 Date PublicKey::created() const
471 { return keyData().created(); }
473 Date PublicKey::expires() const
474 { return keyData().expires(); }
476 bool PublicKey::expired() const
477 { return keyData().expired(); }
479 int PublicKey::daysToLive() const
480 { return keyData().daysToLive(); }
482 std::string PublicKey::expiresAsString() const
483 { return keyData().expiresAsString(); }
485 std::string PublicKey::gpgPubkeyVersion() const
486 { return keyData().gpgPubkeyVersion(); }
488 std::string PublicKey::gpgPubkeyRelease() const
489 { return keyData().gpgPubkeyRelease(); }
491 std::string PublicKey::asString() const
492 { return keyData().asString(); }
494 bool PublicKey::operator==( const PublicKey & rhs ) const
495 { return rhs.keyData() == keyData(); }
497 bool PublicKey::operator==( const std::string & sid ) const
498 { return sid == id(); }
500 std::ostream & dumpOn( std::ostream & str, const PublicKey & obj )
501 { return dumpOn( str, obj.keyData() ); }
503 /////////////////////////////////////////////////////////////////
505 ///////////////////////////////////////////////////////////////////