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