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