Imported Upstream version 17.6.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   } //namespace
106   ///////////////////////////////////////////////////////////////////
107
108
109   ///////////////////////////////////////////////////////////////////
110   /// \class PublicSubkeyData::Impl
111   /// \brief  PublicSubkeyData implementation.
112   ///////////////////////////////////////////////////////////////////
113
114   struct PublicSubkeyData::Impl
115   {
116     std::string _id;
117     Date        _created;
118     Date        _expires;
119
120   public:
121     /** Offer default Impl. */
122     static shared_ptr<Impl> nullimpl();
123
124   private:
125     friend Impl * rwcowClone<Impl>( const Impl * rhs );
126     /** clone for RWCOW_pointer */
127     Impl * clone() const;
128   };
129
130   shared_ptr<zypp::PublicSubkeyData::Impl> PublicSubkeyData::Impl::nullimpl()
131   {
132     static shared_ptr<Impl> _nullimpl( new Impl );
133     return _nullimpl;
134   }
135
136   zypp::PublicSubkeyData::Impl *PublicSubkeyData::Impl::clone() const
137   {
138     return new Impl( *this );
139   }
140
141   ///////////////////////////////////////////////////////////////////
142   /// class PublicSubkeyData
143   ///////////////////////////////////////////////////////////////////
144
145   PublicSubkeyData::PublicSubkeyData()
146     : _pimpl( Impl::nullimpl() )
147   {}
148
149   PublicSubkeyData::PublicSubkeyData(const _gpgme_subkey *rawSubKeyData)
150     : _pimpl (new Impl)
151   {
152     _pimpl->_created = zypp::Date(rawSubKeyData->timestamp);
153     _pimpl->_expires = zypp::Date(rawSubKeyData->expires);
154     _pimpl->_id = str::asString(rawSubKeyData->keyid);
155   }
156
157   PublicSubkeyData::~PublicSubkeyData()
158   {}
159
160   PublicSubkeyData::operator bool() const
161   { return !_pimpl->_id.empty(); }
162
163   std::string PublicSubkeyData::id() const
164   { return _pimpl->_id; }
165
166   Date PublicSubkeyData::created() const
167   { return _pimpl->_created; }
168
169   Date PublicSubkeyData::expires() const
170   { return _pimpl->_expires; }
171
172   bool PublicSubkeyData::expired() const
173   { return isExpired( _pimpl->_expires ); }
174
175   int PublicSubkeyData::daysToLive() const
176   { return hasDaysToLive( _pimpl->_expires ); }
177
178   std::string PublicSubkeyData::asString() const
179   {
180     return str::Str() << id() << " " << created().printDate() << " [" << expiresDetail( expires() ) << "]";
181   }
182
183   ///////////////////////////////////////////////////////////////////
184   /// \class PublicKeyData::Impl
185   /// \brief  PublicKeyData implementation.
186   ///////////////////////////////////////////////////////////////////
187   ///
188   struct PublicKeyData::Impl
189   {
190     std::string _id;
191     std::string _name;
192     std::string _fingerprint;
193     Date        _created;
194     Date        _expires;
195
196     std::vector<PublicSubkeyData> _subkeys;
197
198   public:
199     bool hasSubkeyId( const std::string & id_r ) const;
200
201   public:
202     /** Offer default Impl. */
203     static shared_ptr<Impl> nullimpl();
204     static shared_ptr<Impl> fromGpgmeKey(gpgme_key_t rawData);
205
206   private:
207     friend Impl * rwcowClone<Impl>( const Impl * rhs );
208     /** clone for RWCOW_pointer */
209     Impl * clone() const;
210   };
211
212   bool PublicKeyData::Impl::hasSubkeyId(const std::string &id_r) const
213   {
214     bool ret = false;
215     for ( const PublicSubkeyData & sub : _subkeys )
216     {
217       if ( sub.id() == id_r )
218       {
219         ret = true;
220         break;
221       }
222     }
223     return ret;
224   }
225
226   shared_ptr<PublicKeyData::Impl> PublicKeyData::Impl::nullimpl()
227   {
228     static shared_ptr<Impl> _nullimpl( new Impl );
229     return _nullimpl;
230   }
231
232   shared_ptr<PublicKeyData::Impl> PublicKeyData::Impl::fromGpgmeKey(gpgme_key_t rawData)
233   {
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;
238     if (sKey) {
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);
243       else
244         data->_created = zypp::Date(sKey->timestamp);
245
246       data->_expires = zypp::Date(sKey->expires);
247       data->_fingerprint = str::asString(sKey->fpr);
248       data->_id = str::asString(sKey->keyid);
249
250       //get the primary user ID
251       if (rawData->uids) {
252         data->_name = str::asString(rawData->uids->uid);
253       }
254
255       //the rest of the keys
256       sKey = sKey->next;
257       while (sKey) {
258         data->_subkeys.push_back( PublicSubkeyData(sKey) );
259         sKey = sKey->next;
260       }
261       return data;
262     }
263     return nullimpl();
264   }
265
266   zypp::PublicKeyData::Impl *PublicKeyData::Impl::clone() const
267   {
268     return new Impl( *this );
269   }
270
271   ///////////////////////////////////////////////////////////////////
272   /// class PublicKeyData
273   ///////////////////////////////////////////////////////////////////
274
275   PublicKeyData::PublicKeyData()
276     : _pimpl( Impl::nullimpl() )
277   {}
278
279   PublicKeyData::PublicKeyData(shared_ptr<Impl> data)
280     : _pimpl( data )
281   {}
282
283   PublicKeyData::~PublicKeyData()
284   {}
285
286   PublicKeyData PublicKeyData::fromGpgmeKey(_gpgme_key *data)
287   { return PublicKeyData(Impl::fromGpgmeKey(data)); }
288
289   PublicKeyData::operator bool() const
290   { return !_pimpl->_fingerprint.empty(); }
291
292   std::string PublicKeyData::id() const
293   { return _pimpl->_id; }
294
295   std::string PublicKeyData::name() const
296   { return _pimpl->_name; }
297
298   std::string PublicKeyData::fingerprint() const
299   { return _pimpl->_fingerprint; }
300
301   Date PublicKeyData::created() const
302   { return _pimpl->_created; }
303
304   Date PublicKeyData::expires() const
305   { return _pimpl->_expires; }
306
307   bool PublicKeyData::expired() const
308   { return isExpired( _pimpl->_expires ); }
309
310   int PublicKeyData::daysToLive() const
311   { return hasDaysToLive( _pimpl->_expires ); }
312
313   std::string PublicKeyData::expiresAsString() const
314   { return expiresDetailVerbose( _pimpl->_expires ); }
315
316   std::string PublicKeyData::gpgPubkeyVersion() const
317   { return _pimpl->_id.empty() ? _pimpl->_id : str::toLower( _pimpl->_id.substr(8,8) ); }
318
319   std::string PublicKeyData::gpgPubkeyRelease() const
320   { return _pimpl->_created ? str::hexstring( _pimpl->_created ).substr(2) : std::string(); }
321
322   std::string PublicKeyData::rpmName() const
323   { return str::Format( "gpg-pubkey-%1%-%2%" ) % gpgPubkeyVersion() % gpgPubkeyRelease();  }
324
325   std::string PublicKeyData::asString() const
326   {
327     str::Str str;
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 ) << "]";
332   }
333
334   bool PublicKeyData::hasSubkeys() const
335   { return !_pimpl->_subkeys.empty(); }
336
337   Iterable<PublicKeyData::SubkeyIterator> PublicKeyData::subkeys() const
338   { return makeIterable( &(*_pimpl->_subkeys.begin()), &(*_pimpl->_subkeys.end()) ); }
339
340   bool PublicKeyData::providesKey( const std::string & id_r ) const
341   { return( id_r == _pimpl->_id || _pimpl->hasSubkeyId( id_r ) ); }
342
343   PublicKeyData::AsciiArt PublicKeyData::asciiArt() const
344   { return AsciiArt( fingerprint() /* TODO: key algorithm could be added as top tile. */ ); }
345
346   std::ostream & dumpOn( std::ostream & str, const PublicKeyData & obj )
347   {
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;
357     return str;
358   }
359
360   bool operator==( const PublicKeyData & lhs, const PublicKeyData & rhs )
361   { return ( lhs.fingerprint() == rhs.fingerprint() && lhs.created() == rhs.created() ); }
362
363
364   ///////////////////////////////////////////////////////////////////
365   /// \class PublicKey::Impl
366   /// \brief  PublicKey implementation.
367   ///////////////////////////////////////////////////////////////////
368   struct PublicKey::Impl
369   {
370     Impl()
371     {}
372
373     Impl( const Pathname & keyFile_r )
374     : _dontUseThisPtrDirectly( new filesystem::TmpFile )
375     {
376       PathInfo info( keyFile_r );
377       MIL << "Taking pubkey from " << keyFile_r << " of size " << info.size() << " and sha1 " << filesystem::checksum(keyFile_r, "sha1") << endl;
378
379       if ( !info.isExist() )
380         ZYPP_THROW(Exception("Can't read public key from " + keyFile_r.asString() + ", file not found"));
381
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() ));
384
385       readFromFile();
386     }
387
388     Impl( const filesystem::TmpFile & sharedFile_r )
389       : _dontUseThisPtrDirectly( new filesystem::TmpFile( sharedFile_r ) )
390     { readFromFile(); }
391
392     Impl( const filesystem::TmpFile & sharedFile_r, const PublicKeyData & keyData_r )
393       : _dontUseThisPtrDirectly( new filesystem::TmpFile( sharedFile_r ) )
394       , _keyData( keyData_r )
395     {
396       if ( ! keyData_r )
397       {
398         WAR << "Invalid PublicKeyData supplied: scanning from file" << endl;
399         readFromFile();
400       }
401     }
402
403     Impl( const PublicKeyData & keyData_r )
404       : _keyData( keyData_r )
405     {}
406
407     public:
408       const PublicKeyData & keyData() const
409       { return _keyData; }
410
411       Pathname path() const
412       { return( /*the one and only intended use*/_dontUseThisPtrDirectly ? _dontUseThisPtrDirectly->path() : Pathname() ); }
413
414       const std::list<PublicKeyData> & hiddenKeys() const
415       { return _hiddenKeys; }
416
417     protected:
418       std::string _initHomeDir()        ///< readFromFile helper to prepare the 'gpg --homedir'
419       { Pathname ret( zypp::myTmpDir() / "PublicKey" ); filesystem::assert_dir( ret ); return ret.asString(); }
420
421       void readFromFile()
422       {
423         PathInfo info( path() );
424         MIL << "Reading pubkey from " << info.path() << " of size " << info.size() << " and sha1 " << filesystem::checksum(info.path(), "sha1") << endl;
425
426         //@TODO is this still required? KeyManagerCtx creates a homedir on the fly
427         static std::string tmppath( _initHomeDir() );
428
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!")) );
432         }
433
434         std::list<PublicKeyData> keys = ctx->readKeyFromFile(path());
435         switch ( keys.size() )
436         {
437           case 0:
438             ZYPP_THROW( BadKeyException( "File " + path().asString() + " doesn't contain public key data" , path() ) );
439             break;
440
441           case 1:
442             // ok.
443             _keyData = keys.back();
444             _hiddenKeys.clear();
445             break;
446
447           default:
448             WAR << "File " << path().asString() << " contains multiple keys: " <<  keys << endl;
449             _keyData = keys.back();
450             keys.pop_back();
451             _hiddenKeys.swap( keys );
452             break;
453         }
454
455         MIL << "Read pubkey from " << info.path() << ": " << _keyData << endl;
456       }
457
458     private:
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;
462
463     public:
464       /** Offer default Impl. */
465       static shared_ptr<Impl> nullimpl()
466       {
467         static shared_ptr<Impl> _nullimpl( new Impl );
468         return _nullimpl;
469       }
470
471     private:
472       friend Impl * rwcowClone<Impl>( const Impl * rhs );
473       /** clone for RWCOW_pointer */
474       Impl * clone() const
475       { return new Impl( *this ); }
476   };
477   ///////////////////////////////////////////////////////////////////
478
479   ///////////////////////////////////////////////////////////////////
480   // class PublicKey
481   ///////////////////////////////////////////////////////////////////
482   PublicKey::PublicKey()
483     : _pimpl( Impl::nullimpl() )
484   {}
485
486   PublicKey::PublicKey( const Pathname & file )
487   : _pimpl( new Impl( file ) )
488   {}
489
490   PublicKey::PublicKey( const filesystem::TmpFile & sharedfile )
491   : _pimpl( new Impl( sharedfile ) )
492   {}
493
494   PublicKey::PublicKey( const filesystem::TmpFile & sharedfile, const PublicKeyData & keyData_r )
495   : _pimpl( new Impl( sharedfile, keyData_r ) )
496   {}
497
498   PublicKey::PublicKey( const PublicKeyData & keyData_r )
499   : _pimpl( new Impl( keyData_r ) )
500   {}
501
502   PublicKey::~PublicKey()
503   {}
504
505   const PublicKeyData & PublicKey::keyData() const
506   { return _pimpl->keyData(); }
507
508   Pathname PublicKey::path() const
509   { return _pimpl->path(); }
510
511   const std::list<PublicKeyData> & PublicKey::hiddenKeys() const
512   { return _pimpl->hiddenKeys(); }
513
514   std::string PublicKey::id() const
515   { return keyData().id(); }
516
517   std::string PublicKey::name() const
518   { return keyData().name(); }
519
520   std::string PublicKey::fingerprint() const
521   { return keyData().fingerprint(); }
522
523   Date PublicKey::created() const
524   { return keyData().created(); }
525
526   Date PublicKey::expires() const
527   { return keyData().expires(); }
528
529   bool PublicKey::expired() const
530   { return keyData().expired(); }
531
532   int PublicKey::daysToLive() const
533   { return keyData().daysToLive(); }
534
535   std::string PublicKey::expiresAsString() const
536   { return keyData().expiresAsString(); }
537
538   std::string PublicKey::gpgPubkeyVersion() const
539   { return keyData().gpgPubkeyVersion(); }
540
541   std::string PublicKey::gpgPubkeyRelease() const
542   { return keyData().gpgPubkeyRelease(); }
543
544   std::string PublicKey::asString() const
545   { return keyData().asString(); }
546
547   std::string PublicKey::rpmName() const
548   { return keyData().rpmName(); }
549
550   bool PublicKey::operator==( const PublicKey & rhs ) const
551   { return rhs.keyData() == keyData(); }
552
553   bool PublicKey::operator==( const std::string & sid ) const
554   { return sid == id(); }
555
556   std::ostream & dumpOn( std::ostream & str, const PublicKey & obj )
557   { return dumpOn( str, obj.keyData() ); }
558
559
560
561
562   /////////////////////////////////////////////////////////////////
563 } // namespace zypp
564 ///////////////////////////////////////////////////////////////////