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/KeyManager.h>
33 #undef ZYPP_BASE_LOGGER_LOGGROUP
34 #define ZYPP_BASE_LOGGER_LOGGROUP "zypp::gpg"
36 ///////////////////////////////////////////////////////////////////
39 ///////////////////////////////////////////////////////////////////
42 inline bool isExpired( const Date & expires_r )
43 { return( expires_r && expires_r < Date::now() ); }
45 inline int hasDaysToLive( const Date & expires_r )
49 Date exp( expires_r - Date::now() );
50 int ret = exp / Date::day;
51 if ( exp < 0 ) ret -= 1;
57 inline std::string expiresDetail( const Date & expires_r )
62 // translators: an annotation to a gpg keys expiry date
63 str << _("does not expire");
65 else if ( isExpired( expires_r ) )
67 // translators: an annotation to a gpg keys expiry date: "expired: 1999-04-12"
68 str << ( str::Format(_("expired: %1%") ) % expires_r.printDate() );
72 // translators: an annotation to a gpg keys expiry date: "expires: 2111-04-12"
73 str << ( str::Format(_("expires: %1%") ) % expires_r.printDate() );
78 inline std::string expiresDetailVerbose( const Date & expires_r )
81 { // translators: an annotation to a gpg keys expiry date
82 return _("(does not expire)");
84 std::string ret( expires_r.asString() );
85 int ttl( hasDaysToLive( expires_r ) );
90 { // translators: an annotation to a gpg keys expiry date
91 ret += _("(EXPIRED)");
94 { // translators: an annotation to a gpg keys expiry date
95 ret += _("(expires within 24h)");
98 { // translators: an annotation to a gpg keys expiry date
99 ret += str::form( PL_("(expires in %d day)", "(expires in %d days)", ttl ), ttl );
105 std::string keyAlgoName( const gpgme_subkey_t & key_r )
108 if ( const char * n = ::gpgme_pubkey_algo_name( key_r->pubkey_algo ) )
109 ret = str::Str() << n << ' ' << key_r->length;
115 ///////////////////////////////////////////////////////////////////
118 ///////////////////////////////////////////////////////////////////
119 /// \class PublicSubkeyData::Impl
120 /// \brief PublicSubkeyData implementation.
121 ///////////////////////////////////////////////////////////////////
123 struct PublicSubkeyData::Impl
130 /** Offer default Impl. */
131 static shared_ptr<Impl> nullimpl();
134 friend Impl * rwcowClone<Impl>( const Impl * rhs );
135 /** clone for RWCOW_pointer */
136 Impl * clone() const;
139 shared_ptr<zypp::PublicSubkeyData::Impl> PublicSubkeyData::Impl::nullimpl()
141 static shared_ptr<Impl> _nullimpl( new Impl );
145 zypp::PublicSubkeyData::Impl *PublicSubkeyData::Impl::clone() const
147 return new Impl( *this );
150 ///////////////////////////////////////////////////////////////////
151 /// class PublicSubkeyData
152 ///////////////////////////////////////////////////////////////////
154 PublicSubkeyData::PublicSubkeyData()
155 : _pimpl( Impl::nullimpl() )
158 PublicSubkeyData::PublicSubkeyData(const _gpgme_subkey *rawSubKeyData)
161 _pimpl->_created = zypp::Date(rawSubKeyData->timestamp);
162 _pimpl->_expires = zypp::Date(rawSubKeyData->expires);
163 _pimpl->_id = str::asString(rawSubKeyData->keyid);
166 PublicSubkeyData::~PublicSubkeyData()
169 PublicSubkeyData::operator bool() const
170 { return !_pimpl->_id.empty(); }
172 std::string PublicSubkeyData::id() const
173 { return _pimpl->_id; }
175 Date PublicSubkeyData::created() const
176 { return _pimpl->_created; }
178 Date PublicSubkeyData::expires() const
179 { return _pimpl->_expires; }
181 bool PublicSubkeyData::expired() const
182 { return isExpired( _pimpl->_expires ); }
184 int PublicSubkeyData::daysToLive() const
185 { return hasDaysToLive( _pimpl->_expires ); }
187 std::string PublicSubkeyData::asString() const
189 return str::Str() << id() << " " << created().printDate() << " [" << expiresDetail( expires() ) << "]";
192 ///////////////////////////////////////////////////////////////////
193 /// \class PublicKeyData::Impl
194 /// \brief PublicKeyData implementation.
195 ///////////////////////////////////////////////////////////////////
197 struct PublicKeyData::Impl
201 std::string _fingerprint;
202 std::string _algoName;
206 std::vector<PublicSubkeyData> _subkeys;
209 bool hasSubkeyId( const std::string & id_r ) const;
212 /** Offer default Impl. */
213 static shared_ptr<Impl> nullimpl();
214 static shared_ptr<Impl> fromGpgmeKey(gpgme_key_t rawData);
217 friend Impl * rwcowClone<Impl>( const Impl * rhs );
218 /** clone for RWCOW_pointer */
219 Impl * clone() const;
222 bool PublicKeyData::Impl::hasSubkeyId(const std::string &id_r) const
225 for ( const PublicSubkeyData & sub : _subkeys )
227 if ( sub.id() == id_r )
236 shared_ptr<PublicKeyData::Impl> PublicKeyData::Impl::nullimpl()
238 static shared_ptr<Impl> _nullimpl( new Impl );
242 shared_ptr<PublicKeyData::Impl> PublicKeyData::Impl::fromGpgmeKey(gpgme_key_t rawData)
244 //gpgpme stores almost nothing in the top level key
245 //the information we look for is stored in the subkey, where subkey[0]
246 //is always the primary key
247 gpgme_subkey_t sKey = rawData->subkeys;
249 shared_ptr<PublicKeyData::Impl> data(new Impl);
250 //libzypp expects the date of the latest signature on the first uid
251 if ( rawData->uids && rawData->uids->signatures ) {
252 data->_created = zypp::Date(rawData->uids->signatures->timestamp);
253 // bsc#1179222: The keyring does not order the signatures when multiple
254 // versions of the same key are imported. We take the last signature here,
255 // the one GPGME_EXPORT_MODE_MINIMAL will later use in export.
256 for ( auto t = rawData->uids->signatures->next; t; t = t->next ) {
257 if ( t->timestamp > data->_created )
258 data->_created = t->timestamp;
262 data->_created = zypp::Date(sKey->timestamp);
264 data->_expires = zypp::Date(sKey->expires);
265 data->_fingerprint = str::asString(sKey->fpr);
266 data->_algoName = keyAlgoName( sKey );
267 data->_id = str::asString(sKey->keyid);
269 //get the primary user ID
271 data->_name = str::asString(rawData->uids->uid);
274 //the rest of the keys
277 data->_subkeys.push_back( PublicSubkeyData(sKey) );
285 zypp::PublicKeyData::Impl *PublicKeyData::Impl::clone() const
287 return new Impl( *this );
290 ///////////////////////////////////////////////////////////////////
291 /// class PublicKeyData
292 ///////////////////////////////////////////////////////////////////
294 PublicKeyData::PublicKeyData()
295 : _pimpl( Impl::nullimpl() )
298 PublicKeyData::PublicKeyData(shared_ptr<Impl> data)
302 PublicKeyData::~PublicKeyData()
305 PublicKeyData PublicKeyData::fromGpgmeKey(_gpgme_key *data)
306 { return PublicKeyData(Impl::fromGpgmeKey(data)); }
308 PublicKeyData::operator bool() const
309 { return !_pimpl->_fingerprint.empty(); }
311 std::string PublicKeyData::id() const
312 { return _pimpl->_id; }
314 std::string PublicKeyData::name() const
315 { return _pimpl->_name; }
317 std::string PublicKeyData::fingerprint() const
318 { return _pimpl->_fingerprint; }
320 std::string PublicKeyData::algoName() const
321 { return _pimpl->_algoName; }
323 Date PublicKeyData::created() const
324 { return _pimpl->_created; }
326 Date PublicKeyData::expires() const
327 { return _pimpl->_expires; }
329 bool PublicKeyData::expired() const
330 { return isExpired( _pimpl->_expires ); }
332 int PublicKeyData::daysToLive() const
333 { return hasDaysToLive( _pimpl->_expires ); }
335 std::string PublicKeyData::expiresAsString() const
336 { return expiresDetailVerbose( _pimpl->_expires ); }
338 std::string PublicKeyData::gpgPubkeyVersion() const
339 { return _pimpl->_id.empty() ? _pimpl->_id : str::toLower( _pimpl->_id.substr(8,8) ); }
341 std::string PublicKeyData::gpgPubkeyRelease() const
342 { return _pimpl->_created ? str::hexstring( _pimpl->_created ).substr(2) : std::string(); }
344 std::string PublicKeyData::rpmName() const
345 { return str::Format( "gpg-pubkey-%1%-%2%" ) % gpgPubkeyVersion() % gpgPubkeyRelease(); }
347 std::string PublicKeyData::asString() const
350 str << "[" << _pimpl->_id << "-" << gpgPubkeyRelease();
351 for ( auto && sub : _pimpl->_subkeys )
352 str << ", " << sub.id();
353 return str << "] [" << _pimpl->_name.c_str() << "] [" << expiresDetail( _pimpl->_expires ) << "]";
356 bool PublicKeyData::hasSubkeys() const
357 { return !_pimpl->_subkeys.empty(); }
359 Iterable<PublicKeyData::SubkeyIterator> PublicKeyData::subkeys() const
360 { return makeIterable( &(*_pimpl->_subkeys.begin()), &(*_pimpl->_subkeys.end()) ); }
362 bool PublicKeyData::providesKey( const std::string & id_r ) const
364 if ( id_r.size() == 8 ) // as a convenience allow to test the 8byte short ID rpm uses as gpg-pubkey version
365 return str::endsWithCI( _pimpl->_id, id_r );
366 return( id_r == _pimpl->_id || _pimpl->hasSubkeyId( id_r ) );
369 PublicKeyData::AsciiArt PublicKeyData::asciiArt() const
370 { return AsciiArt( fingerprint(), algoName() ); }
372 std::ostream & dumpOn( std::ostream & str, const PublicKeyData & obj )
374 str << "[" << obj.name() << "]" << endl;
375 str << " fpr " << obj.fingerprint() << endl;
376 str << " id " << obj.id() << endl;
377 str << " alg " << obj.algoName() << endl;
378 str << " cre " << Date::ValueType(obj.created()) << ' ' << obj.created() << endl;
379 str << " exp " << Date::ValueType(obj.expires()) << ' ' << obj.expiresAsString() << endl;
380 str << " ttl " << obj.daysToLive() << endl;
381 for ( auto && sub : obj._pimpl->_subkeys )
382 str << " sub " << sub << endl;
383 str << " rpm " << obj.gpgPubkeyVersion() << "-" << obj.gpgPubkeyRelease() << endl;
387 bool operator==( const PublicKeyData & lhs, const PublicKeyData & rhs )
388 { return ( lhs.fingerprint() == rhs.fingerprint() && lhs.created() == rhs.created() ); }
391 ///////////////////////////////////////////////////////////////////
392 /// \class PublicKey::Impl
393 /// \brief PublicKey implementation.
394 ///////////////////////////////////////////////////////////////////
395 struct PublicKey::Impl
400 Impl( const Pathname & keyFile_r )
401 : _dontUseThisPtrDirectly( new filesystem::TmpFile )
403 PathInfo info( keyFile_r );
404 MIL << "Taking pubkey from " << keyFile_r << " of size " << info.size() << " and sha1 " << filesystem::checksum(keyFile_r, "sha1") << endl;
406 if ( !info.isExist() )
407 ZYPP_THROW(Exception("Can't read public key from " + keyFile_r.asString() + ", file not found"));
409 if ( filesystem::hardlinkCopy( keyFile_r, path() ) != 0 )
410 ZYPP_THROW(Exception("Can't copy public key data from " + keyFile_r.asString() + " to " + path().asString() ));
415 Impl( const filesystem::TmpFile & sharedFile_r )
416 : _dontUseThisPtrDirectly( new filesystem::TmpFile( sharedFile_r ) )
419 // private from keyring
420 Impl( const filesystem::TmpFile & sharedFile_r, const PublicKeyData & keyData_r )
421 : _dontUseThisPtrDirectly( new filesystem::TmpFile( sharedFile_r ) )
422 , _keyData( keyData_r )
426 WAR << "Invalid PublicKeyData supplied: scanning from file" << endl;
431 // private from keyring
432 Impl( const PublicKeyData & keyData_r )
433 : _keyData( keyData_r )
437 const PublicKeyData & keyData() const
440 Pathname path() const
441 { return( /*the one and only intended use*/_dontUseThisPtrDirectly ? _dontUseThisPtrDirectly->path() : Pathname() ); }
443 const std::list<PublicKeyData> & hiddenKeys() const
444 { return _hiddenKeys; }
449 PathInfo info( path() );
450 MIL << "Reading pubkey from " << info.path() << " of size " << info.size() << " and sha1 " << filesystem::checksum(info.path(), "sha1") << endl;
452 std::list<PublicKeyData> keys = KeyManagerCtx::createForOpenPGP().readKeyFromFile( path() );
453 switch ( keys.size() )
456 ZYPP_THROW( BadKeyException( "File " + path().asString() + " doesn't contain public key data" , path() ) );
461 _keyData = keys.back();
466 WAR << "File " << path().asString() << " contains multiple keys: " << keys << endl;
467 _keyData = keys.back();
469 _hiddenKeys.swap( keys );
473 MIL << "Read pubkey from " << info.path() << ": " << _keyData << endl;
477 shared_ptr<filesystem::TmpFile> _dontUseThisPtrDirectly; // shared_ptr ok because TmpFile itself is a refernce type (no COW)
478 PublicKeyData _keyData;
479 std::list<PublicKeyData> _hiddenKeys;
482 /** Offer default Impl. */
483 static shared_ptr<Impl> nullimpl()
485 static shared_ptr<Impl> _nullimpl( new Impl );
490 friend Impl * rwcowClone<Impl>( const Impl * rhs );
491 /** clone for RWCOW_pointer */
493 { return new Impl( *this ); }
495 ///////////////////////////////////////////////////////////////////
497 ///////////////////////////////////////////////////////////////////
499 ///////////////////////////////////////////////////////////////////
500 PublicKey::PublicKey()
501 : _pimpl( Impl::nullimpl() )
504 PublicKey::PublicKey( const Pathname & file )
505 : _pimpl( new Impl( file ) )
508 PublicKey::PublicKey( const filesystem::TmpFile & sharedfile )
509 : _pimpl( new Impl( sharedfile ) )
512 PublicKey::PublicKey( const filesystem::TmpFile & sharedfile, const PublicKeyData & keyData_r )
513 : _pimpl( new Impl( sharedfile, keyData_r ) )
516 PublicKey::PublicKey( const PublicKeyData & keyData_r )
517 : _pimpl( new Impl( keyData_r ) )
520 PublicKey::~PublicKey()
523 const PublicKeyData & PublicKey::keyData() const
524 { return _pimpl->keyData(); }
526 Pathname PublicKey::path() const
527 { return _pimpl->path(); }
529 const std::list<PublicKeyData> & PublicKey::hiddenKeys() const
530 { return _pimpl->hiddenKeys(); }
532 std::string PublicKey::id() const
533 { return keyData().id(); }
535 std::string PublicKey::name() const
536 { return keyData().name(); }
538 std::string PublicKey::fingerprint() const
539 { return keyData().fingerprint(); }
541 std::string PublicKey::algoName() const
542 { return keyData().algoName(); }
544 Date PublicKey::created() const
545 { return keyData().created(); }
547 Date PublicKey::expires() const
548 { return keyData().expires(); }
550 bool PublicKey::expired() const
551 { return keyData().expired(); }
553 int PublicKey::daysToLive() const
554 { return keyData().daysToLive(); }
556 std::string PublicKey::expiresAsString() const
557 { return keyData().expiresAsString(); }
559 std::string PublicKey::gpgPubkeyVersion() const
560 { return keyData().gpgPubkeyVersion(); }
562 std::string PublicKey::gpgPubkeyRelease() const
563 { return keyData().gpgPubkeyRelease(); }
565 std::string PublicKey::asString() const
566 { return keyData().asString(); }
568 std::string PublicKey::rpmName() const
569 { return keyData().rpmName(); }
571 bool PublicKey::operator==( const PublicKey & rhs ) const
572 { return rhs.keyData() == keyData(); }
574 bool PublicKey::operator==( const std::string & sid ) const
575 { return sid == id(); }
577 std::ostream & dumpOn( std::ostream & str, const PublicKey & obj )
578 { return dumpOn( str, obj.keyData() ); }
583 /////////////////////////////////////////////////////////////////
585 ///////////////////////////////////////////////////////////////////