Imported Upstream version 17.0.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
28 /** \todo Fix duplicate define in PublicKey/KeyRing */
29 #define GPG_BINARY "/usr/bin/gpg2"
30
31 using std::endl;
32
33 ///////////////////////////////////////////////////////////////////
34 namespace zypp
35 {
36   ///////////////////////////////////////////////////////////////////
37   namespace
38   {
39     inline bool isExpired( const Date & expires_r )
40     { return( expires_r && expires_r < Date::now() ); }
41
42     inline int hasDaysToLive( const Date & expires_r )
43     {
44       if ( expires_r )
45       {
46         Date exp( expires_r - Date::now() );
47         int ret = exp / Date::day;
48         if ( exp < 0 ) ret -= 1;
49         return ret;
50       }
51       return INT_MAX;
52     }
53
54     inline std::string expiresDetail( const Date & expires_r )
55     {
56       str::Str str;
57       if ( ! expires_r )
58       {
59         // translators: an annotation to a gpg keys expiry date
60         str << _("does not expire");
61       }
62       else if ( isExpired( expires_r ) )
63       {
64         // translators: an annotation to a gpg keys expiry date: "expired: 1999-04-12"
65         str << ( str::Format(_("expired: %1%") ) % expires_r.printDate() );
66       }
67       else
68       {
69         // translators: an annotation to a gpg keys expiry date: "expires: 2111-04-12"
70         str << ( str::Format(_("expires: %1%") ) % expires_r.printDate() );
71       }
72       return str;
73     }
74
75     inline std::string expiresDetailVerbose( const Date & expires_r )
76     {
77       if ( !expires_r )
78       { // translators: an annotation to a gpg keys expiry date
79         return _("(does not expire)");
80       }
81       std::string ret( expires_r.asString() );
82       int ttl( hasDaysToLive( expires_r ) );
83       if ( ttl <= 90 )
84       {
85         ret += " ";
86         if ( ttl < 0 )
87         { // translators: an annotation to a gpg keys expiry date
88           ret += _("(EXPIRED)");
89         }
90         else if ( ttl == 0 )
91         { // translators: an annotation to a gpg keys expiry date
92           ret += _("(expires within 24h)");
93         }
94         else
95         { // translators: an annotation to a gpg keys expiry date
96           ret += str::form( PL_("(expires in %d day)", "(expires in %d days)", ttl ), ttl );
97         }
98       }
99       return ret;
100     }
101
102   } //namespace
103   ///////////////////////////////////////////////////////////////////
104
105   ///////////////////////////////////////////////////////////////////
106   /// \class PublicSubkeyData::Impl
107   /// \brief  PublicSubkeyData implementation.
108   ///////////////////////////////////////////////////////////////////
109   struct PublicSubkeyData::Impl
110   {
111     std::string _id;
112     Date        _created;
113     Date        _expires;
114
115   public:
116     /** Offer default Impl. */
117     static shared_ptr<Impl> nullimpl()
118     {
119       static shared_ptr<Impl> _nullimpl( new Impl );
120       return _nullimpl;
121     }
122
123   private:
124     friend Impl * rwcowClone<Impl>( const Impl * rhs );
125     /** clone for RWCOW_pointer */
126     Impl * clone() const
127     { return new Impl( *this ); }
128   };
129
130   ///////////////////////////////////////////////////////////////////
131   /// class PublicSubkeyData
132   ///////////////////////////////////////////////////////////////////
133
134   PublicSubkeyData::PublicSubkeyData()
135     : _pimpl( Impl::nullimpl() )
136   {}
137
138   PublicSubkeyData::~PublicSubkeyData()
139   {}
140
141   PublicSubkeyData::operator bool() const
142   { return !_pimpl->_id.empty(); }
143
144   std::string PublicSubkeyData::id() const
145   { return _pimpl->_id; }
146
147   Date PublicSubkeyData::created() const
148   { return _pimpl->_created; }
149
150   Date PublicSubkeyData::expires() const
151   { return _pimpl->_expires; }
152
153   bool PublicSubkeyData::expired() const
154   { return isExpired( _pimpl->_expires ); }
155
156   int PublicSubkeyData::daysToLive() const
157   { return hasDaysToLive( _pimpl->_expires ); }
158
159   std::string PublicSubkeyData::asString() const
160   {
161     return str::Str() << id() << " " << created().printDate() << " [" << expiresDetail( expires() ) << "]";
162   }
163
164   ///////////////////////////////////////////////////////////////////
165   /// \class PublicKeyData::Impl
166   /// \brief  PublicKeyData implementation.
167   ///////////////////////////////////////////////////////////////////
168   struct PublicKeyData::Impl
169   {
170     std::string _id;
171     std::string _name;
172     std::string _fingerprint;
173     Date        _created;
174     Date        _expires;
175
176     std::vector<PublicSubkeyData> _subkeys;
177
178   public:
179     bool hasSubkeyId( const std::string & id_r ) const
180     {
181       bool ret = false;
182       for ( const PublicSubkeyData & sub : _subkeys )
183       {
184         if ( sub.id() == id_r )
185         {
186           ret = true;
187           break;
188         }
189       }
190       return ret;
191     }
192
193   public:
194     /** Offer default Impl. */
195     static shared_ptr<Impl> nullimpl()
196     {
197       static shared_ptr<Impl> _nullimpl( new Impl );
198       return _nullimpl;
199     }
200
201   private:
202     friend Impl * rwcowClone<Impl>( const Impl * rhs );
203     /** clone for RWCOW_pointer */
204     Impl * clone() const
205     { return new Impl( *this ); }
206   };
207
208   ///////////////////////////////////////////////////////////////////
209   /// class PublicKeyData
210   ///////////////////////////////////////////////////////////////////
211
212   PublicKeyData::PublicKeyData()
213     : _pimpl( Impl::nullimpl() )
214   {}
215
216   PublicKeyData::~PublicKeyData()
217   {}
218
219   PublicKeyData::operator bool() const
220   { return !_pimpl->_fingerprint.empty(); }
221
222   std::string PublicKeyData::id() const
223   { return _pimpl->_id; }
224
225   std::string PublicKeyData::name() const
226   { return _pimpl->_name; }
227
228   std::string PublicKeyData::fingerprint() const
229   { return _pimpl->_fingerprint; }
230
231   Date PublicKeyData::created() const
232   { return _pimpl->_created; }
233
234   Date PublicKeyData::expires() const
235   { return _pimpl->_expires; }
236
237   bool PublicKeyData::expired() const
238   { return isExpired( _pimpl->_expires ); }
239
240   int PublicKeyData::daysToLive() const
241   { return hasDaysToLive( _pimpl->_expires ); }
242
243   std::string PublicKeyData::expiresAsString() const
244   { return expiresDetailVerbose( _pimpl->_expires ); }
245
246   std::string PublicKeyData::gpgPubkeyVersion() const
247   { return _pimpl->_id.empty() ? _pimpl->_id : str::toLower( _pimpl->_id.substr(8,8) ); }
248
249   std::string PublicKeyData::gpgPubkeyRelease() const
250   { return _pimpl->_created ? str::hexstring( _pimpl->_created ).substr(2) : std::string(); }
251
252   std::string PublicKeyData::asString() const
253   {
254     str::Str str;
255     str << "[" << _pimpl->_id << "-" << gpgPubkeyRelease();
256     for ( auto && sub : _pimpl->_subkeys )
257       str << ", " << sub.id();
258     return str << "] [" << _pimpl->_name.c_str() << "] [" << expiresDetail( _pimpl->_expires ) << "]";
259   }
260
261   bool PublicKeyData::hasSubkeys() const
262   { return !_pimpl->_subkeys.empty(); }
263
264   Iterable<PublicKeyData::SubkeyIterator> PublicKeyData::subkeys() const
265   { return makeIterable( &(*_pimpl->_subkeys.begin()), &(*_pimpl->_subkeys.end()) ); }
266
267   bool PublicKeyData::providesKey( const std::string & id_r ) const
268   { return( id_r == _pimpl->_id || _pimpl->hasSubkeyId( id_r ) ); }
269
270   std::ostream & dumpOn( std::ostream & str, const PublicKeyData & obj )
271   {
272     str << "[" << obj.name() << "]" << endl;
273     str << "  fpr " << obj.fingerprint() << endl;
274     str << "   id " << obj.id() << endl;
275     str << "  cre " << Date::ValueType(obj.created()) << ' ' << obj.created() << endl;
276     str << "  exp " << Date::ValueType(obj.expires()) << ' ' << obj.expiresAsString() << endl;
277     str << "  ttl " << obj.daysToLive() << endl;
278     for ( auto && sub : obj._pimpl->_subkeys )
279       str << "  sub " << sub << endl;
280     str << "  rpm " << obj.gpgPubkeyVersion() << "-" << obj.gpgPubkeyRelease() << endl;
281     return str;
282   }
283
284   bool operator==( const PublicKeyData & lhs, const PublicKeyData & rhs )
285   { return ( lhs.fingerprint() == rhs.fingerprint() && lhs.created() == rhs.created() ); }
286
287
288   ///////////////////////////////////////////////////////////////////
289   /// \class PublicKeyScanner::Impl
290   /// \brief  PublicKeyScanner implementation.
291   ///////////////////////////////////////////////////////////////////
292   struct PublicKeyScanner::Impl
293   {
294     enum { pNONE, pPUB, pSIG, pFPR, pUID, pSUB } _parseEntry;
295     std::vector<std::string> _words;
296     PublicKeyData::Impl * _keyDataPtr;
297
298    Impl()
299       : _parseEntry( pNONE )
300       , _keyDataPtr( nullptr )
301     {}
302
303     void scan( std::string & line_r, std::list<PublicKeyData> & keys_r )
304     {
305       // pub:-:1024:17:A84EDAE89C800ACA:971961473:1214043198::-:SuSE Package Signing Key <build@suse.de>:
306       // fpr:::::::::79C179B2E1C820C1890F9994A84EDAE89C800ACA:
307       // sig:::17:A84EDAE89C800ACA:1087899198:::::[selfsig]::13x:
308       // sig:::17:9E40E310000AABA4:980442706::::[User ID not found]:10x:
309       // sig:::1:77B2E6003D25D3D9:980443247::::[User ID not found]:10x:
310       // sig:::17:A84EDAE89C800ACA:1318348291:::::[selfsig]::13x:
311       // sub:-:2048:16:197448E88495160C:971961490:1214043258::: [expires: 2008-06-21]
312       // sig:::17:A84EDAE89C800ACA:1087899258:::::[keybind]::18x:
313       if ( line_r.empty() )
314         return;
315
316       // quick check for interesting entries, no parsing in subkeys
317       _parseEntry = pNONE;
318       switch ( line_r[0] )
319       {
320         case 'p':
321           if ( line_r[1] == 'u' && line_r[2] == 'b' && line_r[3] == ':' )
322           {
323             _parseEntry = pPUB;
324             keys_r.push_back( PublicKeyData() );        // reset upon new key
325             _keyDataPtr = keys_r.back()._pimpl.get();
326           }
327           break;
328
329         case 'f':
330           if ( line_r[1] == 'p' && line_r[2] == 'r' && line_r[3] == ':' )
331             _parseEntry = pFPR;
332           break;
333
334         case 'u':
335           if ( line_r[1] == 'i' && line_r[2] == 'd' && line_r[3] == ':' )
336             _parseEntry = pUID;
337           break;
338
339         case 's':
340           if ( line_r[1] == 'i' && line_r[2] == 'g' && line_r[3] == ':' )
341             _parseEntry = pSIG;
342           else if ( line_r[1] == 'u' && line_r[2] == 'b' && line_r[3] == ':' )
343             _parseEntry = pSUB;
344           break;
345
346         default:
347           return;
348       }
349       if ( _parseEntry == pNONE )
350         return;
351       if ( ! ( _keyDataPtr->_subkeys.empty() || _parseEntry == pSUB ) )
352         return; // collecting subkeys only
353
354       if ( line_r[line_r.size()-1] == '\n' )
355         line_r.erase( line_r.size()-1 );
356       //DBG << line_r << endl;
357
358       _words.clear();
359       str::splitFields( line_r, std::back_inserter(_words), ":" );
360
361       switch ( _parseEntry )
362       {
363         case pPUB:
364           _keyDataPtr->_id      = _words[4];
365           _keyDataPtr->_name    = str::replaceAll( _words[9], "\\x3a", ":" );
366           _keyDataPtr->_created = Date(str::strtonum<Date::ValueType>(_words[5]));
367           _keyDataPtr->_expires = Date(str::strtonum<Date::ValueType>(_words[6]));
368           break;
369
370         case pSIG:
371           // Update creation/modification date from signatures type "13x".
372           if ( ( _words.size() > 10 && _words[10] == "13x" && !_words[9].empty() && _words[9] != "[User ID not found]" )
373             || ( _words.size() > 12 && _words[12] == "13x" /* [selfsig] */) )
374           {
375             Date cdate(str::strtonum<Date::ValueType>(_words[5]));
376             if ( _keyDataPtr->_created < cdate )
377               _keyDataPtr->_created = cdate;
378           }
379           break;
380
381         case pFPR:
382           if ( _keyDataPtr->_fingerprint.empty() )
383             _keyDataPtr->_fingerprint = _words[9];
384           break;
385
386         case pUID:
387           if ( ! _words[9].empty() && _words[9] != "[User ID not found]" )
388             _keyDataPtr->_name = str::replaceAll( _words[9], "\\x3a", ":" );
389           break;
390
391         case pSUB:
392           _keyDataPtr->_subkeys.push_back( PublicSubkeyData() );
393           {
394             PublicSubkeyData::Impl * subPtr = _keyDataPtr->_subkeys.back()._pimpl.get();
395             subPtr->_id      = _words[4];
396             subPtr->_created = Date(str::strtonum<Date::ValueType>(_words[5]));
397             subPtr->_expires = Date(str::strtonum<Date::ValueType>(_words[6]));
398           }
399           break;
400
401         case pNONE:
402           break;        // intentionally no default:
403       }
404     }
405   };
406   ///////////////////////////////////////////////////////////////////
407
408   ///////////////////////////////////////////////////////////////////
409   // class PublicKeyScanner
410   ///////////////////////////////////////////////////////////////////
411
412   PublicKeyScanner::PublicKeyScanner()
413     : _pimpl( new Impl )
414   {}
415
416   PublicKeyScanner::~PublicKeyScanner()
417   {}
418
419   void PublicKeyScanner::scan( std::string line_r )
420   { _pimpl->scan( line_r, _keys ); }
421
422
423   ///////////////////////////////////////////////////////////////////
424   /// \class PublicKey::Impl
425   /// \brief  PublicKey implementation.
426   ///////////////////////////////////////////////////////////////////
427   struct PublicKey::Impl
428   {
429     Impl()
430     {}
431
432     Impl( const Pathname & keyFile_r )
433     : _dontUseThisPtrDirectly( new filesystem::TmpFile )
434     {
435       PathInfo info( keyFile_r );
436       MIL << "Taking pubkey from " << keyFile_r << " of size " << info.size() << " and sha1 " << filesystem::checksum(keyFile_r, "sha1") << endl;
437
438       if ( !info.isExist() )
439         ZYPP_THROW(Exception("Can't read public key from " + keyFile_r.asString() + ", file not found"));
440
441       if ( filesystem::hardlinkCopy( keyFile_r, path() ) != 0 )
442         ZYPP_THROW(Exception("Can't copy public key data from " + keyFile_r.asString() + " to " +  path().asString() ));
443
444       readFromFile();
445     }
446
447     Impl( const filesystem::TmpFile & sharedFile_r )
448       : _dontUseThisPtrDirectly( new filesystem::TmpFile( sharedFile_r ) )
449     { readFromFile(); }
450
451     Impl( const filesystem::TmpFile & sharedFile_r, const PublicKeyData & keyData_r )
452       : _dontUseThisPtrDirectly( new filesystem::TmpFile( sharedFile_r ) )
453       , _keyData( keyData_r )
454     {
455       if ( ! keyData_r )
456       {
457         WAR << "Invalid PublicKeyData supplied: scanning from file" << endl;
458         readFromFile();
459       }
460     }
461
462     Impl( const PublicKeyData & keyData_r )
463       : _keyData( keyData_r )
464     {}
465
466     public:
467       const PublicKeyData & keyData() const
468       { return _keyData; }
469
470       Pathname path() const
471       { return( /*the one and only intended use*/_dontUseThisPtrDirectly ? _dontUseThisPtrDirectly->path() : Pathname() ); }
472
473       const std::list<PublicKeyData> & hiddenKeys() const
474       { return _hiddenKeys; }
475
476     protected:
477       std::string _initHomeDir()        ///< readFromFile helper to prepare the 'gpg --homedir'
478       { Pathname ret( zypp::myTmpDir() / "PublicKey" ); filesystem::assert_dir( ret ); return ret.asString(); }
479
480       void readFromFile()
481       {
482         PathInfo info( path() );
483         MIL << "Reading pubkey from " << info.path() << " of size " << info.size() << " and sha1 " << filesystem::checksum(info.path(), "sha1") << endl;
484
485         static std::string tmppath( _initHomeDir() );
486         std::string datapath( path().asString() );
487
488         const char* argv[] =
489         {
490           GPG_BINARY,
491           "-v",
492           "--no-default-keyring",
493           "--fixed-list-mode",
494           "--with-fingerprint",
495           "--with-colons",
496           "--homedir",
497           tmppath.c_str(),
498           "--quiet",
499           "--no-tty",
500           "--no-greeting",
501           "--batch",
502           "--status-fd", "1",
503           datapath.c_str(),
504           NULL
505         };
506         ExternalProgram prog( argv, ExternalProgram::Discard_Stderr, false, -1, true );
507
508         PublicKeyScanner scanner;
509         for ( std::string line = prog.receiveLine(); !line.empty(); line = prog.receiveLine() )
510         {
511           scanner.scan( line );
512         }
513         int ret = prog.close();
514
515         switch ( scanner._keys.size() )
516         {
517           case 0:
518             if ( ret == 129 )
519               ZYPP_THROW( Exception( std::string("Can't read public key data: ") + GPG_BINARY + " is not installed!" ) );
520             else
521               ZYPP_THROW( BadKeyException( "File " + path().asString() + " doesn't contain public key data" , path() ) );
522             break;
523
524           case 1:
525             // ok.
526             _keyData = scanner._keys.back();
527             _hiddenKeys.clear();
528             break;
529
530           default:
531             WAR << "File " << path().asString() << " contains multiple keys: " <<  scanner._keys << endl;
532             _keyData = scanner._keys.back();
533             scanner._keys.pop_back();
534             _hiddenKeys.swap( scanner._keys );
535             break;
536         }
537
538         MIL << "Read pubkey from " << info.path() << ": " << _keyData << endl;
539       }
540
541     private:
542       shared_ptr<filesystem::TmpFile> _dontUseThisPtrDirectly; // shared_ptr ok because TmpFile itself is a refernce type (no COW)
543       PublicKeyData             _keyData;
544       std::list<PublicKeyData>  _hiddenKeys;
545
546     public:
547       /** Offer default Impl. */
548       static shared_ptr<Impl> nullimpl()
549       {
550         static shared_ptr<Impl> _nullimpl( new Impl );
551         return _nullimpl;
552       }
553
554     private:
555       friend Impl * rwcowClone<Impl>( const Impl * rhs );
556       /** clone for RWCOW_pointer */
557       Impl * clone() const
558       { return new Impl( *this ); }
559   };
560   ///////////////////////////////////////////////////////////////////
561
562   ///////////////////////////////////////////////////////////////////
563   // class PublicKey
564   ///////////////////////////////////////////////////////////////////
565   PublicKey::PublicKey()
566   : _pimpl( Impl::nullimpl() )
567   {}
568
569   PublicKey::PublicKey( const Pathname & file )
570   : _pimpl( new Impl( file ) )
571   {}
572
573   PublicKey::PublicKey( const filesystem::TmpFile & sharedfile )
574   : _pimpl( new Impl( sharedfile ) )
575   {}
576
577   PublicKey::PublicKey( const filesystem::TmpFile & sharedfile, const PublicKeyData & keyData_r )
578   : _pimpl( new Impl( sharedfile, keyData_r ) )
579   {}
580
581   PublicKey::PublicKey( const PublicKeyData & keyData_r )
582   : _pimpl( new Impl( keyData_r ) )
583   {}
584
585   PublicKey::~PublicKey()
586   {}
587
588   const PublicKeyData & PublicKey::keyData() const
589   { return _pimpl->keyData(); }
590
591   Pathname PublicKey::path() const
592   { return _pimpl->path(); }
593
594   const std::list<PublicKeyData> & PublicKey::hiddenKeys() const
595   { return _pimpl->hiddenKeys(); }
596
597   std::string PublicKey::id() const
598   { return keyData().id(); }
599
600   std::string PublicKey::name() const
601   { return keyData().name(); }
602
603   std::string PublicKey::fingerprint() const
604   { return keyData().fingerprint(); }
605
606   Date PublicKey::created() const
607   { return keyData().created(); }
608
609   Date PublicKey::expires() const
610   { return keyData().expires(); }
611
612   bool PublicKey::expired() const
613   { return keyData().expired(); }
614
615   int PublicKey::daysToLive() const
616   { return keyData().daysToLive(); }
617
618   std::string PublicKey::expiresAsString() const
619   { return keyData().expiresAsString(); }
620
621   std::string PublicKey::gpgPubkeyVersion() const
622   { return keyData().gpgPubkeyVersion(); }
623
624   std::string PublicKey::gpgPubkeyRelease() const
625   { return keyData().gpgPubkeyRelease(); }
626
627   std::string PublicKey::asString() const
628   { return keyData().asString(); }
629
630   bool PublicKey::operator==( const PublicKey & rhs ) const
631   { return rhs.keyData() == keyData(); }
632
633   bool PublicKey::operator==( const std::string & sid ) const
634   { return sid == id(); }
635
636   std::ostream & dumpOn( std::ostream & str, const PublicKey & obj )
637   { return dumpOn( str, obj.keyData() ); }
638
639   /////////////////////////////////////////////////////////////////
640 } // namespace zypp
641 ///////////////////////////////////////////////////////////////////