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