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