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