- fix https://bugzilla.novell.com/show_bug.cgi?id=162984 , gpg hangs
[platform/upstream/libzypp.git] / zypp / KeyRing.cc
1 /*---------------------------------------------------------------------\
2 |                          ____ _   __ __ ___                          |
3 |                         |__  / \ / / . \ . \                         |
4 |                           / / \ V /|  _/  _/                         |
5 |                          / /__ | | | | | |                           |
6 |                         /_____||_| |_| |_|                           |
7 |                                                                      |
8 \---------------------------------------------------------------------*/
9 /** \file       zypp/KeyRing.cc
10  *
11 */
12 #include <iostream>
13 #include <fstream>
14 //#include "zypp/base/Logger.h"
15 #include <sys/file.h>
16 #include <cstdio>
17 #include <unistd.h>
18
19 #include "zypp/ZYppFactory.h"
20 #include "zypp/ZYpp.h"
21
22 #include <boost/regex.hpp>
23
24 #include "zypp/base/String.h"
25 #include "zypp/base/IOStream.h"
26 #include "zypp/KeyRing.h"
27 #include "zypp/ExternalProgram.h"
28 #include "zypp/TmpPath.h"
29
30 using std::endl;
31 using namespace boost;
32 using namespace zypp::filesystem;
33 using namespace std;
34
35 ///////////////////////////////////////////////////////////////////
36 namespace zypp
37 { /////////////////////////////////////////////////////////////////
38
39   IMPL_PTR_TYPE(KeyRing);
40   
41   static void dumpRegexpResults( const boost::smatch &what )
42   {
43     for ( unsigned int k=0; k < what.size(); k++)
44     {
45       XXX << "[match "<< k << "] [" << what[k] << "]" << std::endl;
46     }
47   }
48   
49   static bool printLine( const std::string &line )
50   {
51     MIL <<  line << std::endl;
52   }
53   
54   static void dumpFile(const Pathname &file)
55   {
56     std::ifstream is(file.asString().c_str());
57     iostr::forEachLine( is, printLine);    
58   }
59   
60   ///////////////////////////////////////////////////////////////////
61   //
62   //    CLASS NAME : KeyRing::Impl
63   //
64   /** KeyRing implementation. */
65   struct KeyRing::Impl
66   {
67     Impl()
68     {}
69
70     Impl( const Pathname &general_kr, const Pathname &trusted_kr )
71     {
72       filesystem::assert_dir(general_kr);
73       filesystem::assert_dir(trusted_kr);
74       
75       _general_kr = general_kr;
76       _trusted_kr = trusted_kr;
77     }
78
79     void importKey( const Pathname &keyfile, bool trusted = false);
80     PublicKey readPublicKey( const Pathname &keyfile );
81     std::string readSignatureKeyId(  const Pathname &data, const Pathname &keyfile );
82     
83     void deleteKey( const std::string &id, bool trusted );
84     std::list<PublicKey> trustedPublicKeys();
85     std::list<PublicKey> publicKeys();
86     
87     void dumpPublicKey( const std::string &id, bool trusted, std::ostream &stream );
88     
89     bool verifyFileSignatureWorkflow( const Pathname &file, const Pathname &signature);
90     
91     bool verifyFileSignature( const Pathname &file, const Pathname &signature);
92     bool verifyFileTrustedSignature( const Pathname &file, const Pathname &signature);
93   private:
94     //mutable std::map<Locale, std::string> translations;
95     bool verifyFile( const Pathname &file, const Pathname &signature, const Pathname &keyring);
96     void importKey( const Pathname &keyfile, const Pathname &keyring);
97     
98     void exportKey( std::string id, const Pathname &keyfile, bool trusted);
99     
100     void deleteKey( const std::string &id, const Pathname &keyring );
101     std::list<PublicKey> publicKeys(const Pathname &keyring);
102     
103     bool publicKeyExists( std::string id, const Pathname &keyring);
104     
105     Pathname _general_kr;
106     Pathname _trusted_kr;
107   public:
108     /** Offer default Impl. */
109     static shared_ptr<Impl> nullimpl()
110     {
111       static shared_ptr<Impl> _nullimpl( new Impl );
112       return _nullimpl;
113     }
114
115   private:
116     friend Impl * rwcowClone<Impl>( const Impl * rhs );
117     /** clone for RWCOW_pointer */
118     Impl * clone() const
119     { return new Impl( *this ); }
120   };
121   
122   void KeyRing::Impl::importKey( const Pathname &keyfile, bool trusted)
123   {
124     importKey( keyfile, trusted ? _trusted_kr : _general_kr );
125   }
126   
127   void KeyRing::Impl::deleteKey( const std::string &id, bool trusted)
128   {
129     deleteKey( id, trusted ? _trusted_kr : _general_kr );
130   }
131   
132   std::list<PublicKey> KeyRing::Impl::publicKeys()
133   {
134     return publicKeys( _general_kr );
135   }
136   
137   std::list<PublicKey> KeyRing::Impl::trustedPublicKeys()
138   {
139     return publicKeys( _trusted_kr );
140   }
141   
142   bool KeyRing::Impl::verifyFileTrustedSignature( const Pathname &file, const Pathname &signature)
143   {
144     return verifyFile( file, signature, _trusted_kr );
145   }
146   
147   bool KeyRing::Impl::verifyFileSignature( const Pathname &file, const Pathname &signature)
148   {
149     return verifyFile( file, signature, _general_kr );
150   }
151   
152   bool KeyRing::Impl::publicKeyExists( std::string id, const Pathname &keyring)
153   {
154     std::list<PublicKey> keys = publicKeys(keyring);
155     for (std::list<PublicKey>::const_iterator it = keys.begin(); it != keys.end(); it++)
156     {
157       if ( id == (*it).id )
158         return true;
159     }
160     return false;
161   }
162   
163   void KeyRing::Impl::exportKey( std::string id, const Pathname &keyfile, bool trusted)
164   { 
165     try {   
166       std::ofstream os(keyfile.asString().c_str());
167       dumpPublicKey( id, trusted, os );
168       os.close();
169     }
170     catch (std::exception &e)
171     {
172       ERR << "Cannot export key " << id << " from " << (trusted ? "trusted" : "untrusted ") << " keyring  to file " << keyfile << std::endl;
173     }
174   }
175   
176   void KeyRing::Impl::dumpPublicKey( const std::string &id, bool trusted, std::ostream &stream )
177   {
178     Pathname keyring = trusted ? _trusted_kr : _general_kr;
179     const char* argv[] =
180     {
181       "gpg",
182       "--quiet",
183       "--no-tty",
184       "--no-greeting",
185       "--no-permission-warning",
186       "--batch",
187       "--homedir",
188       keyring.asString().c_str(),
189       "-a",
190       "--export",
191       id.c_str(),
192       NULL
193     };
194     ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
195     std::string line;
196     int count;
197     for(line = prog.receiveLine(), count=0; !line.empty(); line = prog.receiveLine(), count++ )
198     {
199       stream << line;
200     }
201     prog.close();
202   }
203   
204   
205   bool KeyRing::Impl::verifyFileSignatureWorkflow( const Pathname &file, const Pathname &signature)
206   {
207     callback::SendReport<KeyRingReport> report;
208     callback::SendReport<KeyRingSignals> emitSignal;
209     MIL << "Going to verify signature for " << file << " with " << signature << std::endl; 
210
211     // get the id of the signature
212     std::string id = readSignatureKeyId(file, signature);
213     
214     // doeskey exists in trusted keyring
215     if ( publicKeyExists( id, _trusted_kr ) )
216     {
217       TmpFile trustedKey;
218       exportKey( id, trustedKey.path(), true);
219       PublicKey key = readPublicKey(trustedKey.path());
220       MIL << "Key " << id << " " << key.name << " is trusted" << std::endl;
221       // it exists, is trusted, does it validates?
222       if ( verifyFile( file, signature, _trusted_kr ) )
223         return true;  
224       else
225         return report->askUserToAcceptVerificationFailed( file, key.id, key.name );
226     }
227     else
228     {
229       if ( publicKeyExists( id, _general_kr ) )
230       {
231         TmpFile unKey;
232         exportKey( id, unKey.path(), false);
233         MIL << "Exported key " << id << " to " << unKey << std::endl;
234         
235         PublicKey key = readPublicKey(unKey.path());
236         MIL << "Key " << id << " " << key.name << " is not trusted" << std::endl;
237         // ok the key is not trusted, ask the user to trust it or not
238         if ( report->askUserToTrustKey(key.id, key.name) )
239         {
240           MIL << "User wants to trust key " << id << " " << key.name << std::endl;
241           //dumpFile(unKey.path());
242           
243           importKey( unKey.path(), _trusted_kr );
244           emitSignal->trustedKeyAdded( (const KeyRing &)(*this), id, key.name );
245           
246           // emit key added
247           if ( verifyFile( file, signature, _trusted_kr ) )
248           {
249             MIL << "File signature is verified" << std::endl;
250             return true;  
251           }
252           else
253           {
254             MIL << "File signature check fails" << std::endl;
255             if ( report->askUserToAcceptVerificationFailed( file, key.id, key.name ) )
256             {
257               MIL << "User continues anyway." << std::endl;
258               return true;
259             }
260             else
261             {
262               MIL << "User does not want to continue" << std::endl;
263               return false;
264             }
265           }
266         }
267         else
268         {
269           MIL << "User does not want to trust key " << id << " " << key.name << std::endl;
270           return false;
271         }
272       }
273       else
274       {
275         // unknown key...
276         if ( report->askUserToAcceptUnknownKey( id, "Unkown Key" ) )
277         {
278           MIL << "User wants to accept unknown key " << id << std::endl;
279           return true;
280         }
281         else
282         {
283           MIL << "User does not want to accept unknown key " << id << std::endl;
284           return false;
285         }
286       }
287     }
288     return false;
289   }
290   
291   
292   PublicKey KeyRing::Impl::readPublicKey( const Pathname &keyfile )
293   {  
294     const char* argv[] =
295     {
296       "gpg",
297       "--with-fingerprint",
298       "--with-colons",
299       "--quiet",
300       "--no-tty",
301       "--no-greeting",
302       "--batch",
303       "--status-fd",
304       "1",  
305       keyfile.asString().c_str(),
306       NULL
307     };
308     
309     ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
310     
311     std::string line;
312     int count = 0;
313     
314     boost::regex rxColons("^([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):\n$");
315     
316     // pub:-:1024:17:A84EDAE89C800ACA:2000-10-19:2008-06-21::-:SuSE Package Signing Key <build@suse.de>:
317     
318     PublicKey key;
319     for(line = prog.receiveLine(), count=0; !line.empty(); line = prog.receiveLine(), count++ )
320     {
321       //MIL << "[" << line << "]" << std::endl;
322       boost::smatch what;
323       if(boost::regex_match(line, what, rxColons, boost::match_extra))
324       {
325         if ( what[1] == "pub" )
326         {
327           key.id = what[5];
328           key.name = what[10];
329           return key;
330         }
331         //dumpRegexpResults(what);
332       }
333     }
334     prog.close();
335     return key;
336   }
337   
338   std::list<PublicKey> KeyRing::Impl::publicKeys(const Pathname &keyring)
339   {
340     const char* argv[] =
341     {
342       "gpg",
343       "--quiet",
344       "--list-public-keys",
345       "--with-colons",
346       "--with-fingerprint",
347       "--homedir",
348       keyring.asString().c_str(),
349       NULL
350     };
351     std::list<PublicKey> keys;
352     
353     ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
354     std::string line;
355     int count = 0;
356     
357     boost::regex rxColons("^([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):\n$");
358     for(line = prog.receiveLine(), count=0; !line.empty(); line = prog.receiveLine(), count++ )
359     {
360       //MIL << line << std::endl;
361       boost::smatch what;
362       if(boost::regex_match(line, what, rxColons, boost::match_extra))
363       {
364         if ( what[1] == "pub" )
365         {
366           PublicKey key;
367           key.id = what[5];
368           key.name = what[10];
369           MIL << "Found key " << key.id << " [" << key.name << "]" << std::endl;
370           keys.push_back(key);
371         }
372         dumpRegexpResults(what);
373       }
374     }
375     prog.close();
376     return keys;
377   }
378   
379   void KeyRing::Impl::importKey( const Pathname &keyfile, const Pathname &keyring)
380   {
381     const char* argv[] =
382     {
383       "gpg",
384       "--quiet",
385       "--no-tty",
386       "--no-greeting",
387       "--no-permission-warning",
388       "--status-fd",
389       "1",  
390       "--homedir",
391       keyring.asString().c_str(),
392       "--import",
393       keyfile.asString().c_str(),
394       NULL
395     };
396     
397     int code;
398     ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
399     code = prog.close();
400     
401     //if ( code != 0 )
402     //  ZYPP_THROW(Exception("failed to import key"));
403   }
404   
405   void KeyRing::Impl::deleteKey( const std::string &id, const Pathname &keyring )
406   {
407     const char* argv[] =
408     {
409       "gpg",
410       "--yes",
411       "--quiet",
412       "--no-tty",
413       "--batch",
414       "--status-fd",
415       "1",
416       "--homedir",
417       keyring.asString().c_str(),
418       "--delete-keys",
419       id.c_str(),
420       NULL
421     };
422     
423     ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
424     
425     int code = prog.close();
426     if ( code )
427       ZYPP_THROW(Exception("Failed to delete key."));
428     else    
429       MIL << "Deleted key " << id << " from keyring " << keyring << std::endl;
430   }    
431   
432   
433   std::string KeyRing::Impl::readSignatureKeyId( const Pathname &data, const Pathname &keyfile )
434   {  
435     // HACK create a tmp keyring with no keys
436     TmpDir dir;
437     
438     const char* argv[] =
439     {
440       "gpg",
441       "--quiet",
442       "--no-tty",
443       "--no-greeting",
444       "--batch",
445       "--status-fd",
446       "1",
447       "--homedir",
448       dir.path().asString().c_str(),
449       "--verify",
450       keyfile.asString().c_str(),
451       data.asString().c_str(),
452       NULL
453     };
454     
455     ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
456     
457     std::string line;
458     int count = 0;
459     
460     boost::regex rxNoKey("^\\[GNUPG:\\] NO_PUBKEY (.+)\n$");
461     std::string id;
462     for(line = prog.receiveLine(), count=0; !line.empty(); line = prog.receiveLine(), count++ )
463     {
464       //MIL << "[" << line << "]" << std::endl;
465       boost::smatch what;
466       if(boost::regex_match(line, what, rxNoKey, boost::match_extra))
467       {
468         if ( what.size() > 1 )
469           id = what[1];
470         //dumpRegexpResults(what);
471       }
472     }
473     prog.close();
474     return id;
475   }    
476       
477   bool KeyRing::Impl::verifyFile( const Pathname &file, const Pathname &signature, const Pathname &keyring)
478   {
479     const char* argv[] =
480     {
481       "gpg",
482       "--quiet",
483       "--no-tty",
484       "--batch",
485       "--no-greeting",
486       "--status-fd",
487       "1",
488       "--homedir",
489       keyring.asString().c_str(),
490       "--verify",
491       signature.asString().c_str(),
492       file.asString().c_str(),
493       NULL
494     };
495     
496     // no need to parse output for now
497     //     [GNUPG:] SIG_ID yCc4u223XRJnLnVAIllvYbUd8mQ 2006-03-29 1143618744
498     //     [GNUPG:] GOODSIG A84EDAE89C800ACA SuSE Package Signing Key <build@suse.de>
499     //     gpg: Good signature from "SuSE Package Signing Key <build@suse.de>"
500     //     [GNUPG:] VALIDSIG 79C179B2E1C820C1890F9994A84EDAE89C800ACA 2006-03-29 1143618744 0 3 0 17 2 00 79C179B2E1C820C1890F9994A84EDAE89C800ACA
501     //     [GNUPG:] TRUST_UNDEFINED
502     
503     //     [GNUPG:] ERRSIG A84EDAE89C800ACA 17 2 00 1143618744 9
504     //     [GNUPG:] NO_PUBKEY A84EDAE89C800ACA
505
506     ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
507      
508     return (prog.close() == 0) ? true : false;
509   }
510   
511   ///////////////////////////////////////////////////////////////////
512
513   ///////////////////////////////////////////////////////////////////
514   //
515   //    CLASS NAME : KeyRing
516   //
517   ///////////////////////////////////////////////////////////////////
518
519   ///////////////////////////////////////////////////////////////////
520   //
521   //    METHOD NAME : KeyRing::KeyRing
522   //    METHOD TYPE : Ctor
523   //
524   KeyRing::KeyRing()
525   : _pimpl( Impl::nullimpl() )
526   {}
527
528   ///////////////////////////////////////////////////////////////////
529   //
530   //    METHOD NAME : KeyRing::KeyRing
531   //    METHOD TYPE : Ctor
532   //
533   KeyRing::KeyRing( const Pathname &general_kr, const Pathname &trusted_kr )
534   : _pimpl( new Impl(general_kr, trusted_kr) )
535   {}
536
537   ///////////////////////////////////////////////////////////////////
538   //
539   //    METHOD NAME : KeyRing::~KeyRing
540   //    METHOD TYPE : Dtor
541   //
542   KeyRing::~KeyRing()
543   {}
544
545   ///////////////////////////////////////////////////////////////////
546   //
547   // Forward to implementation:
548   //
549   ///////////////////////////////////////////////////////////////////
550
551   void KeyRing::importKey( const Pathname &keyfile, bool trusted)
552   {
553     _pimpl->importKey(keyfile, trusted);
554   }
555   
556   PublicKey KeyRing::readPublicKey( const Pathname &keyfile )
557   {
558     return _pimpl->readPublicKey(keyfile);
559   }
560   
561   std::string KeyRing::readSignatureKeyId(  const Pathname &data, const Pathname &keyfile )
562   {
563     return _pimpl->readSignatureKeyId(data, keyfile);
564   }
565   
566   void KeyRing::deleteKey( const std::string &id, bool trusted )
567   {
568     _pimpl->deleteKey(id, trusted);
569   }
570   
571   std::list<PublicKey> KeyRing::publicKeys()
572   {
573     return _pimpl->publicKeys();
574   }
575   
576   std::list<PublicKey> KeyRing::trustedPublicKeys()
577   {
578     return _pimpl->trustedPublicKeys();
579   }
580   
581   bool KeyRing::verifyFileSignatureWorkflow( const Pathname &file, const Pathname &signature)
582   {
583     return _pimpl->verifyFileSignatureWorkflow(file, signature);
584   }
585   
586   bool KeyRing::verifyFileSignature( const Pathname &file, const Pathname &signature)
587   {
588     return _pimpl->verifyFileSignature(file, signature);
589   }
590   
591   bool KeyRing::verifyFileTrustedSignature( const Pathname &file, const Pathname &signature)
592   {
593     return _pimpl->verifyFileTrustedSignature(file, signature);
594   }
595   
596   void KeyRing::dumpPublicKey( const std::string &id, bool trusted, std::ostream &stream )
597   {
598     _pimpl->dumpPublicKey( id, trusted, stream);
599   }
600   
601   /////////////////////////////////////////////////////////////////
602 } // namespace zypp
603 ///////////////////////////////////////////////////////////////////