Imported Upstream version 17.25.4
[platform/upstream/libzypp.git] / zypp / PublicKey.cc
1 /*---------------------------------------------------------------------\
2 |                          ____ _   __ __ ___                          |
3 |                         |__  / \ / / . \ . \                         |
4 |                           / / \ V /|  _/  _/                         |
5 |                          / /__ | | | | | |                           |
6 |                         /_____||_| |_| |_|                           |
7 |                                                                      |
8 \---------------------------------------------------------------------*/
9 /** \file       zypp/PublicKey.cc
10  *
11 */
12 #include <climits>
13
14 #include <iostream>
15 #include <vector>
16
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>
28
29 #include <gpgme.h>
30
31 using std::endl;
32
33 #undef  ZYPP_BASE_LOGGER_LOGGROUP
34 #define ZYPP_BASE_LOGGER_LOGGROUP "zypp::gpg"
35
36 ///////////////////////////////////////////////////////////////////
37 namespace zypp
38 {
39   ///////////////////////////////////////////////////////////////////
40   namespace
41   {
42     inline bool isExpired( const Date & expires_r )
43     { return( expires_r && expires_r < Date::now() ); }
44
45     inline int hasDaysToLive( const Date & expires_r )
46     {
47       if ( expires_r )
48       {
49         Date exp( expires_r - Date::now() );
50         int ret = exp / Date::day;
51         if ( exp < 0 ) ret -= 1;
52         return ret;
53       }
54       return INT_MAX;
55     }
56
57     inline std::string expiresDetail( const Date & expires_r )
58     {
59       str::Str str;
60       if ( ! expires_r )
61       {
62         // translators: an annotation to a gpg keys expiry date
63         str << _("does not expire");
64       }
65       else if ( isExpired( expires_r ) )
66       {
67         // translators: an annotation to a gpg keys expiry date: "expired: 1999-04-12"
68         str << ( str::Format(_("expired: %1%") ) % expires_r.printDate() );
69       }
70       else
71       {
72         // translators: an annotation to a gpg keys expiry date: "expires: 2111-04-12"
73         str << ( str::Format(_("expires: %1%") ) % expires_r.printDate() );
74       }
75       return str;
76     }
77
78     inline std::string expiresDetailVerbose( const Date & expires_r )
79     {
80       if ( !expires_r )
81       { // translators: an annotation to a gpg keys expiry date
82         return _("(does not expire)");
83       }
84       std::string ret( expires_r.asString() );
85       int ttl( hasDaysToLive( expires_r ) );
86       if ( ttl <= 90 )
87       {
88         ret += " ";
89         if ( ttl < 0 )
90         { // translators: an annotation to a gpg keys expiry date
91           ret += _("(EXPIRED)");
92         }
93         else if ( ttl == 0 )
94         { // translators: an annotation to a gpg keys expiry date
95           ret += _("(expires within 24h)");
96         }
97         else
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 );
100         }
101       }
102       return ret;
103     }
104
105     std::string keyAlgoName( const gpgme_subkey_t & key_r )
106     {
107       std::string ret;
108       if ( const char * n = ::gpgme_pubkey_algo_name( key_r->pubkey_algo ) )
109         ret = str::Str() << n << ' ' << key_r->length;
110       else
111         ret = "?";
112       return ret;
113     }
114   } //namespace
115   ///////////////////////////////////////////////////////////////////
116
117
118   ///////////////////////////////////////////////////////////////////
119   /// \class PublicSubkeyData::Impl
120   /// \brief  PublicSubkeyData implementation.
121   ///////////////////////////////////////////////////////////////////
122
123   struct PublicSubkeyData::Impl
124   {
125     std::string _id;
126     Date        _created;
127     Date        _expires;
128
129   public:
130     /** Offer default Impl. */
131     static shared_ptr<Impl> nullimpl();
132
133   private:
134     friend Impl * rwcowClone<Impl>( const Impl * rhs );
135     /** clone for RWCOW_pointer */
136     Impl * clone() const;
137   };
138
139   shared_ptr<zypp::PublicSubkeyData::Impl> PublicSubkeyData::Impl::nullimpl()
140   {
141     static shared_ptr<Impl> _nullimpl( new Impl );
142     return _nullimpl;
143   }
144
145   zypp::PublicSubkeyData::Impl *PublicSubkeyData::Impl::clone() const
146   {
147     return new Impl( *this );
148   }
149
150   ///////////////////////////////////////////////////////////////////
151   /// class PublicSubkeyData
152   ///////////////////////////////////////////////////////////////////
153
154   PublicSubkeyData::PublicSubkeyData()
155     : _pimpl( Impl::nullimpl() )
156   {}
157
158   PublicSubkeyData::PublicSubkeyData(const _gpgme_subkey *rawSubKeyData)
159     : _pimpl (new Impl)
160   {
161     _pimpl->_created = zypp::Date(rawSubKeyData->timestamp);
162     _pimpl->_expires = zypp::Date(rawSubKeyData->expires);
163     _pimpl->_id = str::asString(rawSubKeyData->keyid);
164   }
165
166   PublicSubkeyData::~PublicSubkeyData()
167   {}
168
169   PublicSubkeyData::operator bool() const
170   { return !_pimpl->_id.empty(); }
171
172   std::string PublicSubkeyData::id() const
173   { return _pimpl->_id; }
174
175   Date PublicSubkeyData::created() const
176   { return _pimpl->_created; }
177
178   Date PublicSubkeyData::expires() const
179   { return _pimpl->_expires; }
180
181   bool PublicSubkeyData::expired() const
182   { return isExpired( _pimpl->_expires ); }
183
184   int PublicSubkeyData::daysToLive() const
185   { return hasDaysToLive( _pimpl->_expires ); }
186
187   std::string PublicSubkeyData::asString() const
188   {
189     return str::Str() << id() << " " << created().printDate() << " [" << expiresDetail( expires() ) << "]";
190   }
191
192   ///////////////////////////////////////////////////////////////////
193   /// \class PublicKeyData::Impl
194   /// \brief  PublicKeyData implementation.
195   ///////////////////////////////////////////////////////////////////
196   ///
197   struct PublicKeyData::Impl
198   {
199     std::string _id;
200     std::string _name;
201     std::string _fingerprint;
202     std::string _algoName;
203     Date        _created;
204     Date        _expires;
205
206     std::vector<PublicSubkeyData> _subkeys;
207
208   public:
209     bool hasSubkeyId( const std::string & id_r ) const;
210
211   public:
212     /** Offer default Impl. */
213     static shared_ptr<Impl> nullimpl();
214     static shared_ptr<Impl> fromGpgmeKey(gpgme_key_t rawData);
215
216   private:
217     friend Impl * rwcowClone<Impl>( const Impl * rhs );
218     /** clone for RWCOW_pointer */
219     Impl * clone() const;
220   };
221
222   bool PublicKeyData::Impl::hasSubkeyId(const std::string &id_r) const
223   {
224     bool ret = false;
225     for ( const PublicSubkeyData & sub : _subkeys )
226     {
227       if ( sub.id() == id_r )
228       {
229         ret = true;
230         break;
231       }
232     }
233     return ret;
234   }
235
236   shared_ptr<PublicKeyData::Impl> PublicKeyData::Impl::nullimpl()
237   {
238     static shared_ptr<Impl> _nullimpl( new Impl );
239     return _nullimpl;
240   }
241
242   shared_ptr<PublicKeyData::Impl> PublicKeyData::Impl::fromGpgmeKey(gpgme_key_t rawData)
243   {
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;
248     if (sKey) {
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;
259         }
260       }
261       else
262         data->_created = zypp::Date(sKey->timestamp);
263
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);
268
269       //get the primary user ID
270       if (rawData->uids) {
271         data->_name = str::asString(rawData->uids->uid);
272       }
273
274       //the rest of the keys
275       sKey = sKey->next;
276       while (sKey) {
277         data->_subkeys.push_back( PublicSubkeyData(sKey) );
278         sKey = sKey->next;
279       }
280       return data;
281     }
282     return nullimpl();
283   }
284
285   zypp::PublicKeyData::Impl *PublicKeyData::Impl::clone() const
286   {
287     return new Impl( *this );
288   }
289
290   ///////////////////////////////////////////////////////////////////
291   /// class PublicKeyData
292   ///////////////////////////////////////////////////////////////////
293
294   PublicKeyData::PublicKeyData()
295     : _pimpl( Impl::nullimpl() )
296   {}
297
298   PublicKeyData::PublicKeyData(shared_ptr<Impl> data)
299     : _pimpl( data )
300   {}
301
302   PublicKeyData::~PublicKeyData()
303   {}
304
305   PublicKeyData PublicKeyData::fromGpgmeKey(_gpgme_key *data)
306   { return PublicKeyData(Impl::fromGpgmeKey(data)); }
307
308   PublicKeyData::operator bool() const
309   { return !_pimpl->_fingerprint.empty(); }
310
311   std::string PublicKeyData::id() const
312   { return _pimpl->_id; }
313
314   std::string PublicKeyData::name() const
315   { return _pimpl->_name; }
316
317   std::string PublicKeyData::fingerprint() const
318   { return _pimpl->_fingerprint; }
319
320   std::string PublicKeyData::algoName() const
321   {  return _pimpl->_algoName; }
322
323   Date PublicKeyData::created() const
324   { return _pimpl->_created; }
325
326   Date PublicKeyData::expires() const
327   { return _pimpl->_expires; }
328
329   bool PublicKeyData::expired() const
330   { return isExpired( _pimpl->_expires ); }
331
332   int PublicKeyData::daysToLive() const
333   { return hasDaysToLive( _pimpl->_expires ); }
334
335   std::string PublicKeyData::expiresAsString() const
336   { return expiresDetailVerbose( _pimpl->_expires ); }
337
338   std::string PublicKeyData::gpgPubkeyVersion() const
339   { return _pimpl->_id.empty() ? _pimpl->_id : str::toLower( _pimpl->_id.substr(8,8) ); }
340
341   std::string PublicKeyData::gpgPubkeyRelease() const
342   { return _pimpl->_created ? str::hexstring( _pimpl->_created ).substr(2) : std::string(); }
343
344   std::string PublicKeyData::rpmName() const
345   { return str::Format( "gpg-pubkey-%1%-%2%" ) % gpgPubkeyVersion() % gpgPubkeyRelease();  }
346
347   std::string PublicKeyData::asString() const
348   {
349     str::Str str;
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 ) << "]";
354   }
355
356   bool PublicKeyData::hasSubkeys() const
357   { return !_pimpl->_subkeys.empty(); }
358
359   Iterable<PublicKeyData::SubkeyIterator> PublicKeyData::subkeys() const
360   { return makeIterable( &(*_pimpl->_subkeys.begin()), &(*_pimpl->_subkeys.end()) ); }
361
362   bool PublicKeyData::providesKey( const std::string & id_r ) const
363   {
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 ) );
367   }
368
369   PublicKeyData::AsciiArt PublicKeyData::asciiArt() const
370   { return AsciiArt( fingerprint(), algoName() ); }
371
372   std::ostream & dumpOn( std::ostream & str, const PublicKeyData & obj )
373   {
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;
384     return str;
385   }
386
387   bool operator==( const PublicKeyData & lhs, const PublicKeyData & rhs )
388   { return ( lhs.fingerprint() == rhs.fingerprint() && lhs.created() == rhs.created() ); }
389
390
391   ///////////////////////////////////////////////////////////////////
392   /// \class PublicKey::Impl
393   /// \brief  PublicKey implementation.
394   ///////////////////////////////////////////////////////////////////
395   struct PublicKey::Impl
396   {
397     Impl()
398     {}
399
400     Impl( const Pathname & keyFile_r )
401     : _dontUseThisPtrDirectly( new filesystem::TmpFile )
402     {
403       PathInfo info( keyFile_r );
404       MIL << "Taking pubkey from " << keyFile_r << " of size " << info.size() << " and sha1 " << filesystem::checksum(keyFile_r, "sha1") << endl;
405
406       if ( !info.isExist() )
407         ZYPP_THROW(Exception("Can't read public key from " + keyFile_r.asString() + ", file not found"));
408
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() ));
411
412       readFromFile();
413     }
414
415     Impl( const filesystem::TmpFile & sharedFile_r )
416       : _dontUseThisPtrDirectly( new filesystem::TmpFile( sharedFile_r ) )
417     { readFromFile(); }
418
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 )
423     {
424       if ( ! keyData_r )
425       {
426         WAR << "Invalid PublicKeyData supplied: scanning from file" << endl;
427         readFromFile();
428       }
429     }
430
431     // private from keyring
432     Impl( const PublicKeyData & keyData_r )
433       : _keyData( keyData_r )
434     {}
435
436     public:
437       const PublicKeyData & keyData() const
438       { return _keyData; }
439
440       Pathname path() const
441       { return( /*the one and only intended use*/_dontUseThisPtrDirectly ? _dontUseThisPtrDirectly->path() : Pathname() ); }
442
443       const std::list<PublicKeyData> & hiddenKeys() const
444       { return _hiddenKeys; }
445
446     protected:
447       void readFromFile()
448       {
449         PathInfo info( path() );
450         MIL << "Reading pubkey from " << info.path() << " of size " << info.size() << " and sha1 " << filesystem::checksum(info.path(), "sha1") << endl;
451
452         std::list<PublicKeyData> keys = KeyManagerCtx::createForOpenPGP().readKeyFromFile( path() );
453         switch ( keys.size() )
454         {
455           case 0:
456             ZYPP_THROW( BadKeyException( "File " + path().asString() + " doesn't contain public key data" , path() ) );
457             break;
458
459           case 1:
460             // ok.
461             _keyData = keys.back();
462             _hiddenKeys.clear();
463             break;
464
465           default:
466             WAR << "File " << path().asString() << " contains multiple keys: " <<  keys << endl;
467             _keyData = keys.back();
468             keys.pop_back();
469             _hiddenKeys.swap( keys );
470             break;
471         }
472
473         MIL << "Read pubkey from " << info.path() << ": " << _keyData << endl;
474       }
475
476     private:
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;
480
481     public:
482       /** Offer default Impl. */
483       static shared_ptr<Impl> nullimpl()
484       {
485         static shared_ptr<Impl> _nullimpl( new Impl );
486         return _nullimpl;
487       }
488
489     private:
490       friend Impl * rwcowClone<Impl>( const Impl * rhs );
491       /** clone for RWCOW_pointer */
492       Impl * clone() const
493       { return new Impl( *this ); }
494   };
495   ///////////////////////////////////////////////////////////////////
496
497   ///////////////////////////////////////////////////////////////////
498   // class PublicKey
499   ///////////////////////////////////////////////////////////////////
500   PublicKey::PublicKey()
501     : _pimpl( Impl::nullimpl() )
502   {}
503
504   PublicKey::PublicKey( const Pathname & file )
505   : _pimpl( new Impl( file ) )
506   {}
507
508   PublicKey::PublicKey( const filesystem::TmpFile & sharedfile )
509   : _pimpl( new Impl( sharedfile ) )
510   {}
511
512   PublicKey::PublicKey( const filesystem::TmpFile & sharedfile, const PublicKeyData & keyData_r )
513   : _pimpl( new Impl( sharedfile, keyData_r ) )
514   {}
515
516   PublicKey::PublicKey( const PublicKeyData & keyData_r )
517   : _pimpl( new Impl( keyData_r ) )
518   {}
519
520   PublicKey::~PublicKey()
521   {}
522
523   const PublicKeyData & PublicKey::keyData() const
524   { return _pimpl->keyData(); }
525
526   Pathname PublicKey::path() const
527   { return _pimpl->path(); }
528
529   const std::list<PublicKeyData> & PublicKey::hiddenKeys() const
530   { return _pimpl->hiddenKeys(); }
531
532   std::string PublicKey::id() const
533   { return keyData().id(); }
534
535   std::string PublicKey::name() const
536   { return keyData().name(); }
537
538   std::string PublicKey::fingerprint() const
539   { return keyData().fingerprint(); }
540
541   std::string PublicKey::algoName() const
542   { return keyData().algoName(); }
543
544   Date PublicKey::created() const
545   { return keyData().created(); }
546
547   Date PublicKey::expires() const
548   { return keyData().expires(); }
549
550   bool PublicKey::expired() const
551   { return keyData().expired(); }
552
553   int PublicKey::daysToLive() const
554   { return keyData().daysToLive(); }
555
556   std::string PublicKey::expiresAsString() const
557   { return keyData().expiresAsString(); }
558
559   std::string PublicKey::gpgPubkeyVersion() const
560   { return keyData().gpgPubkeyVersion(); }
561
562   std::string PublicKey::gpgPubkeyRelease() const
563   { return keyData().gpgPubkeyRelease(); }
564
565   std::string PublicKey::asString() const
566   { return keyData().asString(); }
567
568   std::string PublicKey::rpmName() const
569   { return keyData().rpmName(); }
570
571   bool PublicKey::operator==( const PublicKey & rhs ) const
572   { return rhs.keyData() == keyData(); }
573
574   bool PublicKey::operator==( const std::string & sid ) const
575   { return sid == id(); }
576
577   std::ostream & dumpOn( std::ostream & str, const PublicKey & obj )
578   { return dumpOn( str, obj.keyData() ); }
579
580
581
582
583   /////////////////////////////////////////////////////////////////
584 } // namespace zypp
585 ///////////////////////////////////////////////////////////////////