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"
28 /** \todo Fix duplicate define in PublicKey/KeyRing */
29 #define GPG_BINARY "/usr/bin/gpg2"
33 ///////////////////////////////////////////////////////////////////
36 ///////////////////////////////////////////////////////////////////
39 inline bool isExpired( const Date & expires_r )
40 { return( expires_r && expires_r < Date::now() ); }
42 inline int hasDaysToLive( const Date & expires_r )
46 Date exp( expires_r - Date::now() );
47 int ret = exp / Date::day;
48 if ( exp < 0 ) ret -= 1;
54 inline std::string expiresDetail( const Date & expires_r )
59 // translators: an annotation to a gpg keys expiry date
60 str << _("does not expire");
62 else if ( isExpired( expires_r ) )
64 // translators: an annotation to a gpg keys expiry date: "expired: 1999-04-12"
65 str << ( str::Format(_("expired: %1%") ) % expires_r.printDate() );
69 // translators: an annotation to a gpg keys expiry date: "expires: 2111-04-12"
70 str << ( str::Format(_("expires: %1%") ) % expires_r.printDate() );
75 inline std::string expiresDetailVerbose( const Date & expires_r )
78 { // translators: an annotation to a gpg keys expiry date
79 return _("(does not expire)");
81 std::string ret( expires_r.asString() );
82 int ttl( hasDaysToLive( expires_r ) );
87 { // translators: an annotation to a gpg keys expiry date
88 ret += _("(EXPIRED)");
91 { // translators: an annotation to a gpg keys expiry date
92 ret += _("(expires within 24h)");
95 { // translators: an annotation to a gpg keys expiry date
96 ret += str::form( PL_("(expires in %d day)", "(expires in %d days)", ttl ), ttl );
103 ///////////////////////////////////////////////////////////////////
105 ///////////////////////////////////////////////////////////////////
106 /// \class PublicSubkeyData::Impl
107 /// \brief PublicSubkeyData implementation.
108 ///////////////////////////////////////////////////////////////////
109 struct PublicSubkeyData::Impl
116 /** Offer default Impl. */
117 static shared_ptr<Impl> nullimpl()
119 static shared_ptr<Impl> _nullimpl( new Impl );
124 friend Impl * rwcowClone<Impl>( const Impl * rhs );
125 /** clone for RWCOW_pointer */
127 { return new Impl( *this ); }
130 ///////////////////////////////////////////////////////////////////
131 /// class PublicSubkeyData
132 ///////////////////////////////////////////////////////////////////
134 PublicSubkeyData::PublicSubkeyData()
135 : _pimpl( Impl::nullimpl() )
138 PublicSubkeyData::~PublicSubkeyData()
141 PublicSubkeyData::operator bool() const
142 { return !_pimpl->_id.empty(); }
144 std::string PublicSubkeyData::id() const
145 { return _pimpl->_id; }
147 Date PublicSubkeyData::created() const
148 { return _pimpl->_created; }
150 Date PublicSubkeyData::expires() const
151 { return _pimpl->_expires; }
153 bool PublicSubkeyData::expired() const
154 { return isExpired( _pimpl->_expires ); }
156 int PublicSubkeyData::daysToLive() const
157 { return hasDaysToLive( _pimpl->_expires ); }
159 std::string PublicSubkeyData::asString() const
161 return str::Str() << id() << " " << created().printDate() << " [" << expiresDetail( expires() ) << "]";
164 ///////////////////////////////////////////////////////////////////
165 /// \class PublicKeyData::Impl
166 /// \brief PublicKeyData implementation.
167 ///////////////////////////////////////////////////////////////////
168 struct PublicKeyData::Impl
172 std::string _fingerprint;
176 std::vector<PublicSubkeyData> _subkeys;
179 bool hasSubkeyId( const std::string & id_r ) const
182 for ( const PublicSubkeyData & sub : _subkeys )
184 if ( sub.id() == id_r )
194 /** Offer default Impl. */
195 static shared_ptr<Impl> nullimpl()
197 static shared_ptr<Impl> _nullimpl( new Impl );
202 friend Impl * rwcowClone<Impl>( const Impl * rhs );
203 /** clone for RWCOW_pointer */
205 { return new Impl( *this ); }
208 ///////////////////////////////////////////////////////////////////
209 /// class PublicKeyData
210 ///////////////////////////////////////////////////////////////////
212 PublicKeyData::PublicKeyData()
213 : _pimpl( Impl::nullimpl() )
216 PublicKeyData::~PublicKeyData()
219 PublicKeyData::operator bool() const
220 { return !_pimpl->_fingerprint.empty(); }
222 std::string PublicKeyData::id() const
223 { return _pimpl->_id; }
225 std::string PublicKeyData::name() const
226 { return _pimpl->_name; }
228 std::string PublicKeyData::fingerprint() const
229 { return _pimpl->_fingerprint; }
231 Date PublicKeyData::created() const
232 { return _pimpl->_created; }
234 Date PublicKeyData::expires() const
235 { return _pimpl->_expires; }
237 bool PublicKeyData::expired() const
238 { return isExpired( _pimpl->_expires ); }
240 int PublicKeyData::daysToLive() const
241 { return hasDaysToLive( _pimpl->_expires ); }
243 std::string PublicKeyData::expiresAsString() const
244 { return expiresDetailVerbose( _pimpl->_expires ); }
246 std::string PublicKeyData::gpgPubkeyVersion() const
247 { return _pimpl->_id.empty() ? _pimpl->_id : str::toLower( _pimpl->_id.substr(8,8) ); }
249 std::string PublicKeyData::gpgPubkeyRelease() const
250 { return _pimpl->_created ? str::hexstring( _pimpl->_created ).substr(2) : std::string(); }
252 std::string PublicKeyData::asString() const
255 str << "[" << _pimpl->_id << "-" << gpgPubkeyRelease();
256 for ( auto && sub : _pimpl->_subkeys )
257 str << ", " << sub.id();
258 return str << "] [" << _pimpl->_name.c_str() << "] [" << expiresDetail( _pimpl->_expires ) << "]";
261 bool PublicKeyData::hasSubkeys() const
262 { return !_pimpl->_subkeys.empty(); }
264 Iterable<PublicKeyData::SubkeyIterator> PublicKeyData::subkeys() const
265 { return makeIterable( &(*_pimpl->_subkeys.begin()), &(*_pimpl->_subkeys.end()) ); }
267 bool PublicKeyData::providesKey( const std::string & id_r ) const
268 { return( id_r == _pimpl->_id || _pimpl->hasSubkeyId( id_r ) ); }
270 std::ostream & dumpOn( std::ostream & str, const PublicKeyData & obj )
272 str << "[" << obj.name() << "]" << endl;
273 str << " fpr " << obj.fingerprint() << endl;
274 str << " id " << obj.id() << endl;
275 str << " cre " << Date::ValueType(obj.created()) << ' ' << obj.created() << endl;
276 str << " exp " << Date::ValueType(obj.expires()) << ' ' << obj.expiresAsString() << endl;
277 str << " ttl " << obj.daysToLive() << endl;
278 for ( auto && sub : obj._pimpl->_subkeys )
279 str << " sub " << sub << endl;
280 str << " rpm " << obj.gpgPubkeyVersion() << "-" << obj.gpgPubkeyRelease() << endl;
284 bool operator==( const PublicKeyData & lhs, const PublicKeyData & rhs )
285 { return ( lhs.fingerprint() == rhs.fingerprint() && lhs.created() == rhs.created() ); }
288 ///////////////////////////////////////////////////////////////////
289 /// \class PublicKeyScanner::Impl
290 /// \brief PublicKeyScanner implementation.
291 ///////////////////////////////////////////////////////////////////
292 struct PublicKeyScanner::Impl
294 enum { pNONE, pPUB, pSIG, pFPR, pUID, pSUB } _parseEntry;
295 std::vector<std::string> _words;
296 PublicKeyData::Impl * _keyDataPtr;
299 : _parseEntry( pNONE )
300 , _keyDataPtr( nullptr )
303 void scan( std::string & line_r, std::list<PublicKeyData> & keys_r )
305 // pub:-:1024:17:A84EDAE89C800ACA:971961473:1214043198::-:SuSE Package Signing Key <build@suse.de>:
306 // fpr:::::::::79C179B2E1C820C1890F9994A84EDAE89C800ACA:
307 // sig:::17:A84EDAE89C800ACA:1087899198:::::[selfsig]::13x:
308 // sig:::17:9E40E310000AABA4:980442706::::[User ID not found]:10x:
309 // sig:::1:77B2E6003D25D3D9:980443247::::[User ID not found]:10x:
310 // sig:::17:A84EDAE89C800ACA:1318348291:::::[selfsig]::13x:
311 // sub:-:2048:16:197448E88495160C:971961490:1214043258::: [expires: 2008-06-21]
312 // sig:::17:A84EDAE89C800ACA:1087899258:::::[keybind]::18x:
313 if ( line_r.empty() )
316 // quick check for interesting entries, no parsing in subkeys
321 if ( line_r[1] == 'u' && line_r[2] == 'b' && line_r[3] == ':' )
324 keys_r.push_back( PublicKeyData() ); // reset upon new key
325 _keyDataPtr = keys_r.back()._pimpl.get();
330 if ( line_r[1] == 'p' && line_r[2] == 'r' && line_r[3] == ':' )
335 if ( line_r[1] == 'i' && line_r[2] == 'd' && line_r[3] == ':' )
340 if ( line_r[1] == 'i' && line_r[2] == 'g' && line_r[3] == ':' )
342 else if ( line_r[1] == 'u' && line_r[2] == 'b' && line_r[3] == ':' )
349 if ( _parseEntry == pNONE )
351 if ( ! ( _keyDataPtr->_subkeys.empty() || _parseEntry == pSUB ) )
352 return; // collecting subkeys only
354 if ( line_r[line_r.size()-1] == '\n' )
355 line_r.erase( line_r.size()-1 );
356 //DBG << line_r << endl;
359 str::splitFields( line_r, std::back_inserter(_words), ":" );
361 switch ( _parseEntry )
364 _keyDataPtr->_id = _words[4];
365 _keyDataPtr->_name = str::replaceAll( _words[9], "\\x3a", ":" );
366 _keyDataPtr->_created = Date(str::strtonum<Date::ValueType>(_words[5]));
367 _keyDataPtr->_expires = Date(str::strtonum<Date::ValueType>(_words[6]));
371 // Update creation/modification date from signatures type "13x".
372 if ( ( _words.size() > 10 && _words[10] == "13x" && !_words[9].empty() && _words[9] != "[User ID not found]" )
373 || ( _words.size() > 12 && _words[12] == "13x" /* [selfsig] */) )
375 Date cdate(str::strtonum<Date::ValueType>(_words[5]));
376 if ( _keyDataPtr->_created < cdate )
377 _keyDataPtr->_created = cdate;
382 if ( _keyDataPtr->_fingerprint.empty() )
383 _keyDataPtr->_fingerprint = _words[9];
387 if ( ! _words[9].empty() && _words[9] != "[User ID not found]" )
388 _keyDataPtr->_name = str::replaceAll( _words[9], "\\x3a", ":" );
392 _keyDataPtr->_subkeys.push_back( PublicSubkeyData() );
394 PublicSubkeyData::Impl * subPtr = _keyDataPtr->_subkeys.back()._pimpl.get();
395 subPtr->_id = _words[4];
396 subPtr->_created = Date(str::strtonum<Date::ValueType>(_words[5]));
397 subPtr->_expires = Date(str::strtonum<Date::ValueType>(_words[6]));
402 break; // intentionally no default:
406 ///////////////////////////////////////////////////////////////////
408 ///////////////////////////////////////////////////////////////////
409 // class PublicKeyScanner
410 ///////////////////////////////////////////////////////////////////
412 PublicKeyScanner::PublicKeyScanner()
416 PublicKeyScanner::~PublicKeyScanner()
419 void PublicKeyScanner::scan( std::string line_r )
420 { _pimpl->scan( line_r, _keys ); }
423 ///////////////////////////////////////////////////////////////////
424 /// \class PublicKey::Impl
425 /// \brief PublicKey implementation.
426 ///////////////////////////////////////////////////////////////////
427 struct PublicKey::Impl
432 Impl( const Pathname & keyFile_r )
433 : _dontUseThisPtrDirectly( new filesystem::TmpFile )
435 PathInfo info( keyFile_r );
436 MIL << "Taking pubkey from " << keyFile_r << " of size " << info.size() << " and sha1 " << filesystem::checksum(keyFile_r, "sha1") << endl;
438 if ( !info.isExist() )
439 ZYPP_THROW(Exception("Can't read public key from " + keyFile_r.asString() + ", file not found"));
441 if ( filesystem::hardlinkCopy( keyFile_r, path() ) != 0 )
442 ZYPP_THROW(Exception("Can't copy public key data from " + keyFile_r.asString() + " to " + path().asString() ));
447 Impl( const filesystem::TmpFile & sharedFile_r )
448 : _dontUseThisPtrDirectly( new filesystem::TmpFile( sharedFile_r ) )
451 Impl( const filesystem::TmpFile & sharedFile_r, const PublicKeyData & keyData_r )
452 : _dontUseThisPtrDirectly( new filesystem::TmpFile( sharedFile_r ) )
453 , _keyData( keyData_r )
457 WAR << "Invalid PublicKeyData supplied: scanning from file" << endl;
462 Impl( const PublicKeyData & keyData_r )
463 : _keyData( keyData_r )
467 const PublicKeyData & keyData() const
470 Pathname path() const
471 { return( /*the one and only intended use*/_dontUseThisPtrDirectly ? _dontUseThisPtrDirectly->path() : Pathname() ); }
473 const std::list<PublicKeyData> & hiddenKeys() const
474 { return _hiddenKeys; }
477 std::string _initHomeDir() ///< readFromFile helper to prepare the 'gpg --homedir'
478 { Pathname ret( zypp::myTmpDir() / "PublicKey" ); filesystem::assert_dir( ret ); return ret.asString(); }
482 PathInfo info( path() );
483 MIL << "Reading pubkey from " << info.path() << " of size " << info.size() << " and sha1 " << filesystem::checksum(info.path(), "sha1") << endl;
485 static std::string tmppath( _initHomeDir() );
486 std::string datapath( path().asString() );
492 "--no-default-keyring",
494 "--with-fingerprint",
506 ExternalProgram prog( argv, ExternalProgram::Discard_Stderr, false, -1, true );
508 PublicKeyScanner scanner;
509 for ( std::string line = prog.receiveLine(); !line.empty(); line = prog.receiveLine() )
511 scanner.scan( line );
513 int ret = prog.close();
515 switch ( scanner._keys.size() )
519 ZYPP_THROW( Exception( std::string("Can't read public key data: ") + GPG_BINARY + " is not installed!" ) );
521 ZYPP_THROW( BadKeyException( "File " + path().asString() + " doesn't contain public key data" , path() ) );
526 _keyData = scanner._keys.back();
531 WAR << "File " << path().asString() << " contains multiple keys: " << scanner._keys << endl;
532 _keyData = scanner._keys.back();
533 scanner._keys.pop_back();
534 _hiddenKeys.swap( scanner._keys );
538 MIL << "Read pubkey from " << info.path() << ": " << _keyData << endl;
542 shared_ptr<filesystem::TmpFile> _dontUseThisPtrDirectly; // shared_ptr ok because TmpFile itself is a refernce type (no COW)
543 PublicKeyData _keyData;
544 std::list<PublicKeyData> _hiddenKeys;
547 /** Offer default Impl. */
548 static shared_ptr<Impl> nullimpl()
550 static shared_ptr<Impl> _nullimpl( new Impl );
555 friend Impl * rwcowClone<Impl>( const Impl * rhs );
556 /** clone for RWCOW_pointer */
558 { return new Impl( *this ); }
560 ///////////////////////////////////////////////////////////////////
562 ///////////////////////////////////////////////////////////////////
564 ///////////////////////////////////////////////////////////////////
565 PublicKey::PublicKey()
566 : _pimpl( Impl::nullimpl() )
569 PublicKey::PublicKey( const Pathname & file )
570 : _pimpl( new Impl( file ) )
573 PublicKey::PublicKey( const filesystem::TmpFile & sharedfile )
574 : _pimpl( new Impl( sharedfile ) )
577 PublicKey::PublicKey( const filesystem::TmpFile & sharedfile, const PublicKeyData & keyData_r )
578 : _pimpl( new Impl( sharedfile, keyData_r ) )
581 PublicKey::PublicKey( const PublicKeyData & keyData_r )
582 : _pimpl( new Impl( keyData_r ) )
585 PublicKey::~PublicKey()
588 const PublicKeyData & PublicKey::keyData() const
589 { return _pimpl->keyData(); }
591 Pathname PublicKey::path() const
592 { return _pimpl->path(); }
594 const std::list<PublicKeyData> & PublicKey::hiddenKeys() const
595 { return _pimpl->hiddenKeys(); }
597 std::string PublicKey::id() const
598 { return keyData().id(); }
600 std::string PublicKey::name() const
601 { return keyData().name(); }
603 std::string PublicKey::fingerprint() const
604 { return keyData().fingerprint(); }
606 Date PublicKey::created() const
607 { return keyData().created(); }
609 Date PublicKey::expires() const
610 { return keyData().expires(); }
612 bool PublicKey::expired() const
613 { return keyData().expired(); }
615 int PublicKey::daysToLive() const
616 { return keyData().daysToLive(); }
618 std::string PublicKey::expiresAsString() const
619 { return keyData().expiresAsString(); }
621 std::string PublicKey::gpgPubkeyVersion() const
622 { return keyData().gpgPubkeyVersion(); }
624 std::string PublicKey::gpgPubkeyRelease() const
625 { return keyData().gpgPubkeyRelease(); }
627 std::string PublicKey::asString() const
628 { return keyData().asString(); }
630 bool PublicKey::operator==( const PublicKey & rhs ) const
631 { return rhs.keyData() == keyData(); }
633 bool PublicKey::operator==( const std::string & sid ) const
634 { return sid == id(); }
636 std::ostream & dumpOn( std::ostream & str, const PublicKey & obj )
637 { return dumpOn( str, obj.keyData() ); }
639 /////////////////////////////////////////////////////////////////
641 ///////////////////////////////////////////////////////////////////