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