Imported Upstream version 17.14.0
[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 first signature on the first uid
251       if(rawData->uids && rawData->uids->signatures)
252         data->_created = zypp::Date(rawData->uids->signatures->timestamp);
253       else
254         data->_created = zypp::Date(sKey->timestamp);
255
256       data->_expires = zypp::Date(sKey->expires);
257       data->_fingerprint = str::asString(sKey->fpr);
258       data->_algoName = keyAlgoName( sKey );
259       data->_id = str::asString(sKey->keyid);
260
261       //get the primary user ID
262       if (rawData->uids) {
263         data->_name = str::asString(rawData->uids->uid);
264       }
265
266       //the rest of the keys
267       sKey = sKey->next;
268       while (sKey) {
269         data->_subkeys.push_back( PublicSubkeyData(sKey) );
270         sKey = sKey->next;
271       }
272       return data;
273     }
274     return nullimpl();
275   }
276
277   zypp::PublicKeyData::Impl *PublicKeyData::Impl::clone() const
278   {
279     return new Impl( *this );
280   }
281
282   ///////////////////////////////////////////////////////////////////
283   /// class PublicKeyData
284   ///////////////////////////////////////////////////////////////////
285
286   PublicKeyData::PublicKeyData()
287     : _pimpl( Impl::nullimpl() )
288   {}
289
290   PublicKeyData::PublicKeyData(shared_ptr<Impl> data)
291     : _pimpl( data )
292   {}
293
294   PublicKeyData::~PublicKeyData()
295   {}
296
297   PublicKeyData PublicKeyData::fromGpgmeKey(_gpgme_key *data)
298   { return PublicKeyData(Impl::fromGpgmeKey(data)); }
299
300   PublicKeyData::operator bool() const
301   { return !_pimpl->_fingerprint.empty(); }
302
303   std::string PublicKeyData::id() const
304   { return _pimpl->_id; }
305
306   std::string PublicKeyData::name() const
307   { return _pimpl->_name; }
308
309   std::string PublicKeyData::fingerprint() const
310   { return _pimpl->_fingerprint; }
311
312   std::string PublicKeyData::algoName() const
313   {  return _pimpl->_algoName; }
314
315   Date PublicKeyData::created() const
316   { return _pimpl->_created; }
317
318   Date PublicKeyData::expires() const
319   { return _pimpl->_expires; }
320
321   bool PublicKeyData::expired() const
322   { return isExpired( _pimpl->_expires ); }
323
324   int PublicKeyData::daysToLive() const
325   { return hasDaysToLive( _pimpl->_expires ); }
326
327   std::string PublicKeyData::expiresAsString() const
328   { return expiresDetailVerbose( _pimpl->_expires ); }
329
330   std::string PublicKeyData::gpgPubkeyVersion() const
331   { return _pimpl->_id.empty() ? _pimpl->_id : str::toLower( _pimpl->_id.substr(8,8) ); }
332
333   std::string PublicKeyData::gpgPubkeyRelease() const
334   { return _pimpl->_created ? str::hexstring( _pimpl->_created ).substr(2) : std::string(); }
335
336   std::string PublicKeyData::rpmName() const
337   { return str::Format( "gpg-pubkey-%1%-%2%" ) % gpgPubkeyVersion() % gpgPubkeyRelease();  }
338
339   std::string PublicKeyData::asString() const
340   {
341     str::Str str;
342     str << "[" << _pimpl->_id << "-" << gpgPubkeyRelease();
343     for ( auto && sub : _pimpl->_subkeys )
344       str << ", " << sub.id();
345     return str << "] [" << _pimpl->_name.c_str() << "] [" << expiresDetail( _pimpl->_expires ) << "]";
346   }
347
348   bool PublicKeyData::hasSubkeys() const
349   { return !_pimpl->_subkeys.empty(); }
350
351   Iterable<PublicKeyData::SubkeyIterator> PublicKeyData::subkeys() const
352   { return makeIterable( &(*_pimpl->_subkeys.begin()), &(*_pimpl->_subkeys.end()) ); }
353
354   bool PublicKeyData::providesKey( const std::string & id_r ) const
355   {
356     if ( id_r.size() == 8 )     // as a convenience allow to test the 8byte short ID rpm uses as gpg-pubkey version
357       return str::endsWithCI( _pimpl->_id, id_r );
358     return( id_r == _pimpl->_id || _pimpl->hasSubkeyId( id_r ) );
359   }
360
361   PublicKeyData::AsciiArt PublicKeyData::asciiArt() const
362   { return AsciiArt( fingerprint(), algoName() ); }
363
364   std::ostream & dumpOn( std::ostream & str, const PublicKeyData & obj )
365   {
366     str << "[" << obj.name() << "]" << endl;
367     str << "  fpr " << obj.fingerprint() << endl;
368     str << "   id " << obj.id() << endl;
369     str << "  alg " << obj.algoName() << endl;
370     str << "  cre " << Date::ValueType(obj.created()) << ' ' << obj.created() << endl;
371     str << "  exp " << Date::ValueType(obj.expires()) << ' ' << obj.expiresAsString() << endl;
372     str << "  ttl " << obj.daysToLive() << endl;
373     for ( auto && sub : obj._pimpl->_subkeys )
374       str << "  sub " << sub << endl;
375     str << "  rpm " << obj.gpgPubkeyVersion() << "-" << obj.gpgPubkeyRelease() << endl;
376     return str;
377   }
378
379   bool operator==( const PublicKeyData & lhs, const PublicKeyData & rhs )
380   { return ( lhs.fingerprint() == rhs.fingerprint() && lhs.created() == rhs.created() ); }
381
382
383   ///////////////////////////////////////////////////////////////////
384   /// \class PublicKey::Impl
385   /// \brief  PublicKey implementation.
386   ///////////////////////////////////////////////////////////////////
387   struct PublicKey::Impl
388   {
389     Impl()
390     {}
391
392     Impl( const Pathname & keyFile_r )
393     : _dontUseThisPtrDirectly( new filesystem::TmpFile )
394     {
395       PathInfo info( keyFile_r );
396       MIL << "Taking pubkey from " << keyFile_r << " of size " << info.size() << " and sha1 " << filesystem::checksum(keyFile_r, "sha1") << endl;
397
398       if ( !info.isExist() )
399         ZYPP_THROW(Exception("Can't read public key from " + keyFile_r.asString() + ", file not found"));
400
401       if ( filesystem::hardlinkCopy( keyFile_r, path() ) != 0 )
402         ZYPP_THROW(Exception("Can't copy public key data from " + keyFile_r.asString() + " to " +  path().asString() ));
403
404       readFromFile();
405     }
406
407     Impl( const filesystem::TmpFile & sharedFile_r )
408       : _dontUseThisPtrDirectly( new filesystem::TmpFile( sharedFile_r ) )
409     { readFromFile(); }
410
411     // private from keyring
412     Impl( const filesystem::TmpFile & sharedFile_r, const PublicKeyData & keyData_r )
413       : _dontUseThisPtrDirectly( new filesystem::TmpFile( sharedFile_r ) )
414       , _keyData( keyData_r )
415     {
416       if ( ! keyData_r )
417       {
418         WAR << "Invalid PublicKeyData supplied: scanning from file" << endl;
419         readFromFile();
420       }
421     }
422
423     // private from keyring
424     Impl( const PublicKeyData & keyData_r )
425       : _keyData( keyData_r )
426     {}
427
428     public:
429       const PublicKeyData & keyData() const
430       { return _keyData; }
431
432       Pathname path() const
433       { return( /*the one and only intended use*/_dontUseThisPtrDirectly ? _dontUseThisPtrDirectly->path() : Pathname() ); }
434
435       const std::list<PublicKeyData> & hiddenKeys() const
436       { return _hiddenKeys; }
437
438     protected:
439       void readFromFile()
440       {
441         PathInfo info( path() );
442         MIL << "Reading pubkey from " << info.path() << " of size " << info.size() << " and sha1 " << filesystem::checksum(info.path(), "sha1") << endl;
443
444         std::list<PublicKeyData> keys = KeyManagerCtx::createForOpenPGP().readKeyFromFile( path() );
445         switch ( keys.size() )
446         {
447           case 0:
448             ZYPP_THROW( BadKeyException( "File " + path().asString() + " doesn't contain public key data" , path() ) );
449             break;
450
451           case 1:
452             // ok.
453             _keyData = keys.back();
454             _hiddenKeys.clear();
455             break;
456
457           default:
458             WAR << "File " << path().asString() << " contains multiple keys: " <<  keys << endl;
459             _keyData = keys.back();
460             keys.pop_back();
461             _hiddenKeys.swap( keys );
462             break;
463         }
464
465         MIL << "Read pubkey from " << info.path() << ": " << _keyData << endl;
466       }
467
468     private:
469       shared_ptr<filesystem::TmpFile> _dontUseThisPtrDirectly; // shared_ptr ok because TmpFile itself is a refernce type (no COW)
470       PublicKeyData             _keyData;
471       std::list<PublicKeyData>  _hiddenKeys;
472
473     public:
474       /** Offer default Impl. */
475       static shared_ptr<Impl> nullimpl()
476       {
477         static shared_ptr<Impl> _nullimpl( new Impl );
478         return _nullimpl;
479       }
480
481     private:
482       friend Impl * rwcowClone<Impl>( const Impl * rhs );
483       /** clone for RWCOW_pointer */
484       Impl * clone() const
485       { return new Impl( *this ); }
486   };
487   ///////////////////////////////////////////////////////////////////
488
489   ///////////////////////////////////////////////////////////////////
490   // class PublicKey
491   ///////////////////////////////////////////////////////////////////
492   PublicKey::PublicKey()
493     : _pimpl( Impl::nullimpl() )
494   {}
495
496   PublicKey::PublicKey( const Pathname & file )
497   : _pimpl( new Impl( file ) )
498   {}
499
500   PublicKey::PublicKey( const filesystem::TmpFile & sharedfile )
501   : _pimpl( new Impl( sharedfile ) )
502   {}
503
504   PublicKey::PublicKey( const filesystem::TmpFile & sharedfile, const PublicKeyData & keyData_r )
505   : _pimpl( new Impl( sharedfile, keyData_r ) )
506   {}
507
508   PublicKey::PublicKey( const PublicKeyData & keyData_r )
509   : _pimpl( new Impl( keyData_r ) )
510   {}
511
512   PublicKey::~PublicKey()
513   {}
514
515   const PublicKeyData & PublicKey::keyData() const
516   { return _pimpl->keyData(); }
517
518   Pathname PublicKey::path() const
519   { return _pimpl->path(); }
520
521   const std::list<PublicKeyData> & PublicKey::hiddenKeys() const
522   { return _pimpl->hiddenKeys(); }
523
524   std::string PublicKey::id() const
525   { return keyData().id(); }
526
527   std::string PublicKey::name() const
528   { return keyData().name(); }
529
530   std::string PublicKey::fingerprint() const
531   { return keyData().fingerprint(); }
532
533   std::string PublicKey::algoName() const
534   { return keyData().algoName(); }
535
536   Date PublicKey::created() const
537   { return keyData().created(); }
538
539   Date PublicKey::expires() const
540   { return keyData().expires(); }
541
542   bool PublicKey::expired() const
543   { return keyData().expired(); }
544
545   int PublicKey::daysToLive() const
546   { return keyData().daysToLive(); }
547
548   std::string PublicKey::expiresAsString() const
549   { return keyData().expiresAsString(); }
550
551   std::string PublicKey::gpgPubkeyVersion() const
552   { return keyData().gpgPubkeyVersion(); }
553
554   std::string PublicKey::gpgPubkeyRelease() const
555   { return keyData().gpgPubkeyRelease(); }
556
557   std::string PublicKey::asString() const
558   { return keyData().asString(); }
559
560   std::string PublicKey::rpmName() const
561   { return keyData().rpmName(); }
562
563   bool PublicKey::operator==( const PublicKey & rhs ) const
564   { return rhs.keyData() == keyData(); }
565
566   bool PublicKey::operator==( const std::string & sid ) const
567   { return sid == id(); }
568
569   std::ostream & dumpOn( std::ostream & str, const PublicKey & obj )
570   { return dumpOn( str, obj.keyData() ); }
571
572
573
574
575   /////////////////////////////////////////////////////////////////
576 } // namespace zypp
577 ///////////////////////////////////////////////////////////////////