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 );
106 ///////////////////////////////////////////////////////////////////
109 ///////////////////////////////////////////////////////////////////
110 /// \class PublicSubkeyData::Impl
111 /// \brief PublicSubkeyData implementation.
112 ///////////////////////////////////////////////////////////////////
114 struct PublicSubkeyData::Impl
121 /** Offer default Impl. */
122 static shared_ptr<Impl> nullimpl();
125 friend Impl * rwcowClone<Impl>( const Impl * rhs );
126 /** clone for RWCOW_pointer */
127 Impl * clone() const;
130 shared_ptr<zypp::PublicSubkeyData::Impl> PublicSubkeyData::Impl::nullimpl()
132 static shared_ptr<Impl> _nullimpl( new Impl );
136 zypp::PublicSubkeyData::Impl *PublicSubkeyData::Impl::clone() const
138 return new Impl( *this );
141 ///////////////////////////////////////////////////////////////////
142 /// class PublicSubkeyData
143 ///////////////////////////////////////////////////////////////////
145 PublicSubkeyData::PublicSubkeyData()
146 : _pimpl( Impl::nullimpl() )
149 PublicSubkeyData::PublicSubkeyData(const _gpgme_subkey *rawSubKeyData)
152 _pimpl->_created = zypp::Date(rawSubKeyData->timestamp);
153 _pimpl->_expires = zypp::Date(rawSubKeyData->expires);
154 _pimpl->_id = str::asString(rawSubKeyData->keyid);
157 PublicSubkeyData::~PublicSubkeyData()
160 PublicSubkeyData::operator bool() const
161 { return !_pimpl->_id.empty(); }
163 std::string PublicSubkeyData::id() const
164 { return _pimpl->_id; }
166 Date PublicSubkeyData::created() const
167 { return _pimpl->_created; }
169 Date PublicSubkeyData::expires() const
170 { return _pimpl->_expires; }
172 bool PublicSubkeyData::expired() const
173 { return isExpired( _pimpl->_expires ); }
175 int PublicSubkeyData::daysToLive() const
176 { return hasDaysToLive( _pimpl->_expires ); }
178 std::string PublicSubkeyData::asString() const
180 return str::Str() << id() << " " << created().printDate() << " [" << expiresDetail( expires() ) << "]";
183 ///////////////////////////////////////////////////////////////////
184 /// \class PublicKeyData::Impl
185 /// \brief PublicKeyData implementation.
186 ///////////////////////////////////////////////////////////////////
188 struct PublicKeyData::Impl
192 std::string _fingerprint;
196 std::vector<PublicSubkeyData> _subkeys;
199 bool hasSubkeyId( const std::string & id_r ) const;
202 /** Offer default Impl. */
203 static shared_ptr<Impl> nullimpl();
204 static shared_ptr<Impl> fromGpgmeKey(gpgme_key_t rawData);
207 friend Impl * rwcowClone<Impl>( const Impl * rhs );
208 /** clone for RWCOW_pointer */
209 Impl * clone() const;
212 bool PublicKeyData::Impl::hasSubkeyId(const std::string &id_r) const
215 for ( const PublicSubkeyData & sub : _subkeys )
217 if ( sub.id() == id_r )
226 shared_ptr<PublicKeyData::Impl> PublicKeyData::Impl::nullimpl()
228 static shared_ptr<Impl> _nullimpl( new Impl );
232 shared_ptr<PublicKeyData::Impl> PublicKeyData::Impl::fromGpgmeKey(gpgme_key_t rawData)
234 //gpgpme stores almost nothing in the top level key
235 //the information we look for is stored in the subkey, where subkey[0]
236 //is always the primary key
237 gpgme_subkey_t sKey = rawData->subkeys;
239 shared_ptr<PublicKeyData::Impl> data(new Impl);
240 //libzypp expects the date of the first signature on the first uid
241 if(rawData->uids && rawData->uids->signatures)
242 data->_created = zypp::Date(rawData->uids->signatures->timestamp);
244 data->_created = zypp::Date(sKey->timestamp);
246 data->_expires = zypp::Date(sKey->expires);
247 data->_fingerprint = str::asString(sKey->fpr);
248 data->_id = str::asString(sKey->keyid);
250 //get the primary user ID
252 data->_name = str::asString(rawData->uids->uid);
255 //the rest of the keys
258 data->_subkeys.push_back( PublicSubkeyData(sKey) );
266 zypp::PublicKeyData::Impl *PublicKeyData::Impl::clone() const
268 return new Impl( *this );
271 ///////////////////////////////////////////////////////////////////
272 /// class PublicKeyData
273 ///////////////////////////////////////////////////////////////////
275 PublicKeyData::PublicKeyData()
276 : _pimpl( Impl::nullimpl() )
279 PublicKeyData::PublicKeyData(shared_ptr<Impl> data)
283 PublicKeyData::~PublicKeyData()
286 PublicKeyData PublicKeyData::fromGpgmeKey(_gpgme_key *data)
287 { return PublicKeyData(Impl::fromGpgmeKey(data)); }
289 PublicKeyData::operator bool() const
290 { return !_pimpl->_fingerprint.empty(); }
292 std::string PublicKeyData::id() const
293 { return _pimpl->_id; }
295 std::string PublicKeyData::name() const
296 { return _pimpl->_name; }
298 std::string PublicKeyData::fingerprint() const
299 { return _pimpl->_fingerprint; }
301 Date PublicKeyData::created() const
302 { return _pimpl->_created; }
304 Date PublicKeyData::expires() const
305 { return _pimpl->_expires; }
307 bool PublicKeyData::expired() const
308 { return isExpired( _pimpl->_expires ); }
310 int PublicKeyData::daysToLive() const
311 { return hasDaysToLive( _pimpl->_expires ); }
313 std::string PublicKeyData::expiresAsString() const
314 { return expiresDetailVerbose( _pimpl->_expires ); }
316 std::string PublicKeyData::gpgPubkeyVersion() const
317 { return _pimpl->_id.empty() ? _pimpl->_id : str::toLower( _pimpl->_id.substr(8,8) ); }
319 std::string PublicKeyData::gpgPubkeyRelease() const
320 { return _pimpl->_created ? str::hexstring( _pimpl->_created ).substr(2) : std::string(); }
322 std::string PublicKeyData::rpmName() const
323 { return str::Format( "gpg-pubkey-%1%-%2%" ) % gpgPubkeyVersion() % gpgPubkeyRelease(); }
325 std::string PublicKeyData::asString() const
328 str << "[" << _pimpl->_id << "-" << gpgPubkeyRelease();
329 for ( auto && sub : _pimpl->_subkeys )
330 str << ", " << sub.id();
331 return str << "] [" << _pimpl->_name.c_str() << "] [" << expiresDetail( _pimpl->_expires ) << "]";
334 bool PublicKeyData::hasSubkeys() const
335 { return !_pimpl->_subkeys.empty(); }
337 Iterable<PublicKeyData::SubkeyIterator> PublicKeyData::subkeys() const
338 { return makeIterable( &(*_pimpl->_subkeys.begin()), &(*_pimpl->_subkeys.end()) ); }
340 bool PublicKeyData::providesKey( const std::string & id_r ) const
341 { return( id_r == _pimpl->_id || _pimpl->hasSubkeyId( id_r ) ); }
343 PublicKeyData::AsciiArt PublicKeyData::asciiArt() const
344 { return AsciiArt( fingerprint() /* TODO: key algorithm could be added as top tile. */ ); }
346 std::ostream & dumpOn( std::ostream & str, const PublicKeyData & obj )
348 str << "[" << obj.name() << "]" << endl;
349 str << " fpr " << obj.fingerprint() << endl;
350 str << " id " << obj.id() << endl;
351 str << " cre " << Date::ValueType(obj.created()) << ' ' << obj.created() << endl;
352 str << " exp " << Date::ValueType(obj.expires()) << ' ' << obj.expiresAsString() << endl;
353 str << " ttl " << obj.daysToLive() << endl;
354 for ( auto && sub : obj._pimpl->_subkeys )
355 str << " sub " << sub << endl;
356 str << " rpm " << obj.gpgPubkeyVersion() << "-" << obj.gpgPubkeyRelease() << endl;
360 bool operator==( const PublicKeyData & lhs, const PublicKeyData & rhs )
361 { return ( lhs.fingerprint() == rhs.fingerprint() && lhs.created() == rhs.created() ); }
364 ///////////////////////////////////////////////////////////////////
365 /// \class PublicKey::Impl
366 /// \brief PublicKey implementation.
367 ///////////////////////////////////////////////////////////////////
368 struct PublicKey::Impl
373 Impl( const Pathname & keyFile_r )
374 : _dontUseThisPtrDirectly( new filesystem::TmpFile )
376 PathInfo info( keyFile_r );
377 MIL << "Taking pubkey from " << keyFile_r << " of size " << info.size() << " and sha1 " << filesystem::checksum(keyFile_r, "sha1") << endl;
379 if ( !info.isExist() )
380 ZYPP_THROW(Exception("Can't read public key from " + keyFile_r.asString() + ", file not found"));
382 if ( filesystem::hardlinkCopy( keyFile_r, path() ) != 0 )
383 ZYPP_THROW(Exception("Can't copy public key data from " + keyFile_r.asString() + " to " + path().asString() ));
388 Impl( const filesystem::TmpFile & sharedFile_r )
389 : _dontUseThisPtrDirectly( new filesystem::TmpFile( sharedFile_r ) )
392 Impl( const filesystem::TmpFile & sharedFile_r, const PublicKeyData & keyData_r )
393 : _dontUseThisPtrDirectly( new filesystem::TmpFile( sharedFile_r ) )
394 , _keyData( keyData_r )
398 WAR << "Invalid PublicKeyData supplied: scanning from file" << endl;
403 Impl( const PublicKeyData & keyData_r )
404 : _keyData( keyData_r )
408 const PublicKeyData & keyData() const
411 Pathname path() const
412 { return( /*the one and only intended use*/_dontUseThisPtrDirectly ? _dontUseThisPtrDirectly->path() : Pathname() ); }
414 const std::list<PublicKeyData> & hiddenKeys() const
415 { return _hiddenKeys; }
418 std::string _initHomeDir() ///< readFromFile helper to prepare the 'gpg --homedir'
419 { Pathname ret( zypp::myTmpDir() / "PublicKey" ); filesystem::assert_dir( ret ); return ret.asString(); }
423 PathInfo info( path() );
424 MIL << "Reading pubkey from " << info.path() << " of size " << info.size() << " and sha1 " << filesystem::checksum(info.path(), "sha1") << endl;
426 //@TODO is this still required? KeyManagerCtx creates a homedir on the fly
427 static std::string tmppath( _initHomeDir() );
429 KeyManagerCtx::Ptr ctx = KeyManagerCtx::createForOpenPGP();
430 if (!ctx || !ctx->setHomedir(tmppath)) {
431 ZYPP_THROW( Exception( std::string("Can't read public key data: Setting the keyring path failed!")) );
434 std::list<PublicKeyData> keys = ctx->readKeyFromFile(path());
435 switch ( keys.size() )
438 ZYPP_THROW( BadKeyException( "File " + path().asString() + " doesn't contain public key data" , path() ) );
443 _keyData = keys.back();
448 WAR << "File " << path().asString() << " contains multiple keys: " << keys << endl;
449 _keyData = keys.back();
451 _hiddenKeys.swap( keys );
455 MIL << "Read pubkey from " << info.path() << ": " << _keyData << endl;
459 shared_ptr<filesystem::TmpFile> _dontUseThisPtrDirectly; // shared_ptr ok because TmpFile itself is a refernce type (no COW)
460 PublicKeyData _keyData;
461 std::list<PublicKeyData> _hiddenKeys;
464 /** Offer default Impl. */
465 static shared_ptr<Impl> nullimpl()
467 static shared_ptr<Impl> _nullimpl( new Impl );
472 friend Impl * rwcowClone<Impl>( const Impl * rhs );
473 /** clone for RWCOW_pointer */
475 { return new Impl( *this ); }
477 ///////////////////////////////////////////////////////////////////
479 ///////////////////////////////////////////////////////////////////
481 ///////////////////////////////////////////////////////////////////
482 PublicKey::PublicKey()
483 : _pimpl( Impl::nullimpl() )
486 PublicKey::PublicKey( const Pathname & file )
487 : _pimpl( new Impl( file ) )
490 PublicKey::PublicKey( const filesystem::TmpFile & sharedfile )
491 : _pimpl( new Impl( sharedfile ) )
494 PublicKey::PublicKey( const filesystem::TmpFile & sharedfile, const PublicKeyData & keyData_r )
495 : _pimpl( new Impl( sharedfile, keyData_r ) )
498 PublicKey::PublicKey( const PublicKeyData & keyData_r )
499 : _pimpl( new Impl( keyData_r ) )
502 PublicKey::~PublicKey()
505 const PublicKeyData & PublicKey::keyData() const
506 { return _pimpl->keyData(); }
508 Pathname PublicKey::path() const
509 { return _pimpl->path(); }
511 const std::list<PublicKeyData> & PublicKey::hiddenKeys() const
512 { return _pimpl->hiddenKeys(); }
514 std::string PublicKey::id() const
515 { return keyData().id(); }
517 std::string PublicKey::name() const
518 { return keyData().name(); }
520 std::string PublicKey::fingerprint() const
521 { return keyData().fingerprint(); }
523 Date PublicKey::created() const
524 { return keyData().created(); }
526 Date PublicKey::expires() const
527 { return keyData().expires(); }
529 bool PublicKey::expired() const
530 { return keyData().expired(); }
532 int PublicKey::daysToLive() const
533 { return keyData().daysToLive(); }
535 std::string PublicKey::expiresAsString() const
536 { return keyData().expiresAsString(); }
538 std::string PublicKey::gpgPubkeyVersion() const
539 { return keyData().gpgPubkeyVersion(); }
541 std::string PublicKey::gpgPubkeyRelease() const
542 { return keyData().gpgPubkeyRelease(); }
544 std::string PublicKey::asString() const
545 { return keyData().asString(); }
547 std::string PublicKey::rpmName() const
548 { return keyData().rpmName(); }
550 bool PublicKey::operator==( const PublicKey & rhs ) const
551 { return rhs.keyData() == keyData(); }
553 bool PublicKey::operator==( const std::string & sid ) const
554 { return sid == id(); }
556 std::ostream & dumpOn( std::ostream & str, const PublicKey & obj )
557 { return dumpOn( str, obj.keyData() ); }
562 /////////////////////////////////////////////////////////////////
564 ///////////////////////////////////////////////////////////////////