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