if unknown key, pass empty strings insteads for description
[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 <sys/file.h>
15 #include <cstdio>
16 #include <unistd.h>
17
18 #include "zypp/ZYppFactory.h"
19 #include "zypp/ZYpp.h"
20
21 #include "zypp/base/Logger.h"
22 #include "zypp/base/IOStream.h"
23 #include "zypp/base/String.h"
24 #include "zypp/Pathname.h"
25 #include "zypp/KeyRing.h"
26 #include "zypp/ExternalProgram.h"
27 #include "zypp/TmpPath.h"
28
29 using std::endl;
30 using namespace zypp::filesystem;
31 using namespace std;
32
33 #undef  ZYPP_BASE_LOGGER_LOGGROUP
34 #define ZYPP_BASE_LOGGER_LOGGROUP "zypp::KeyRing"
35
36 ///////////////////////////////////////////////////////////////////
37 namespace zypp
38 { /////////////////////////////////////////////////////////////////
39
40   IMPL_PTR_TYPE(KeyRing);
41
42   static void dumpRegexpResults( const str::smatch &what )
43   {
44     for ( unsigned int k=0; k < what.size(); k++)
45     {
46       XXX << "[match "<< k << "] [" << what[k] << "]" << std::endl;
47     }
48   }
49
50   static bool printLine( const std::string &line )
51   {
52     MIL <<  line << std::endl;
53   }
54
55   static void dumpFile(const Pathname &file)
56   {
57     std::ifstream is(file.asString().c_str());
58     iostr::forEachLine( is, printLine);
59   }
60
61   namespace
62   {
63     bool _keyRingDefaultAccept( getenv("ZYPP_KEYRING_DEFAULT_ACCEPT_ALL") );
64   }
65
66   bool KeyRingReport::askUserToAcceptUnsignedFile( const std::string &file )
67   { return _keyRingDefaultAccept; }
68
69   bool KeyRingReport::askUserToAcceptUnknownKey( const std::string &file, const std::string &keyid, const std::string &keyname, const std::string &fingerprint )
70   { return _keyRingDefaultAccept; }
71
72   bool KeyRingReport::askUserToTrustKey( const std::string &keyid, const std::string &keyname, const std::string &fingerprint )
73   { return _keyRingDefaultAccept; }
74
75   bool KeyRingReport::askUserToAcceptVerificationFailed( const std::string &file, const std::string &keyid, const std::string &keyname, const std::string &fingerprint )
76   { return _keyRingDefaultAccept; }
77
78   ///////////////////////////////////////////////////////////////////
79   //
80   //    CLASS NAME : KeyRing::Impl
81   //
82   /** KeyRing implementation. */
83   struct KeyRing::Impl
84   {
85     Impl()
86     {
87       _general_kr = _general_tmp_dir.path();
88       _trusted_kr = _trusted_tmp_dir.path();
89     }
90
91     /*
92     Impl( const Pathname &general_kr, const Pathname &trusted_kr )
93     {
94       filesystem::assert_dir(general_kr);
95       filesystem::assert_dir(trusted_kr);
96
97       _general_kr = general_kr;
98       _trusted_kr = trusted_kr;
99     }
100     */
101
102     void importKey( const Pathname &keyfile, bool trusted = false);
103     PublicKey readPublicKey( const Pathname &keyfile );
104     std::string readSignatureKeyId(  const Pathname &data, const Pathname &keyfile );
105
106     void deleteKey( const std::string &id, bool trusted );
107     std::list<PublicKey> trustedPublicKeys();
108     std::list<PublicKey> publicKeys();
109
110     void dumpPublicKey( const std::string &id, bool trusted, std::ostream &stream );
111
112     bool verifyFileSignatureWorkflow( const Pathname &file, const std::string filedesc, const Pathname &signature);
113
114     bool verifyFileSignature( const Pathname &file, const Pathname &signature);
115     bool verifyFileTrustedSignature( const Pathname &file, const Pathname &signature);
116   private:
117     //mutable std::map<Locale, std::string> translations;
118     bool verifyFile( const Pathname &file, const Pathname &signature, const Pathname &keyring);
119     void importKey( const Pathname &keyfile, const Pathname &keyring);
120
121     void exportKey( std::string id, const Pathname &keyfile, bool trusted);
122
123     void deleteKey( const std::string &id, const Pathname &keyring );
124     std::list<PublicKey> publicKeys(const Pathname &keyring);
125
126     bool publicKeyExists( std::string id, const Pathname &keyring);
127
128     Pathname _general_kr;
129     Pathname _trusted_kr;
130
131     // Used for trusted and untrusted keyrings
132     TmpDir _trusted_tmp_dir;
133     TmpDir _general_tmp_dir;
134   public:
135     /** Offer default Impl. */
136     static shared_ptr<Impl> nullimpl()
137     {
138       static shared_ptr<Impl> _nullimpl( new Impl );
139       return _nullimpl;
140     }
141
142   private:
143     friend Impl * rwcowClone<Impl>( const Impl * rhs );
144     /** clone for RWCOW_pointer */
145     Impl * clone() const
146     { return new Impl( *this ); }
147   };
148
149   void KeyRing::Impl::importKey( const Pathname &keyfile, bool trusted)
150   {
151     importKey( keyfile, trusted ? _trusted_kr : _general_kr );
152   }
153
154   void KeyRing::Impl::deleteKey( const std::string &id, bool trusted)
155   {
156     deleteKey( id, trusted ? _trusted_kr : _general_kr );
157   }
158
159   std::list<PublicKey> KeyRing::Impl::publicKeys()
160   {
161     return publicKeys( _general_kr );
162   }
163
164   std::list<PublicKey> KeyRing::Impl::trustedPublicKeys()
165   {
166     return publicKeys( _trusted_kr );
167   }
168
169   bool KeyRing::Impl::verifyFileTrustedSignature( const Pathname &file, const Pathname &signature)
170   {
171     return verifyFile( file, signature, _trusted_kr );
172   }
173
174   bool KeyRing::Impl::verifyFileSignature( const Pathname &file, const Pathname &signature)
175   {
176     return verifyFile( file, signature, _general_kr );
177   }
178
179   bool KeyRing::Impl::publicKeyExists( std::string id, const Pathname &keyring)
180   {
181     std::list<PublicKey> keys = publicKeys(keyring);
182     for (std::list<PublicKey>::const_iterator it = keys.begin(); it != keys.end(); it++)
183     {
184       if ( id == (*it).id )
185         return true;
186     }
187     return false;
188   }
189
190   void KeyRing::Impl::exportKey( std::string id, const Pathname &keyfile, bool trusted)
191   {
192     try {
193       std::ofstream os(keyfile.asString().c_str());
194       dumpPublicKey( id, trusted, os );
195       os.close();
196     }
197     catch (std::exception &e)
198     {
199       ERR << "Cannot export key " << id << " from " << (trusted ? "trusted" : "untrusted ") << " keyring  to file " << keyfile << std::endl;
200     }
201   }
202
203   void KeyRing::Impl::dumpPublicKey( const std::string &id, bool trusted, std::ostream &stream )
204   {
205     Pathname keyring = trusted ? _trusted_kr : _general_kr;
206     const char* argv[] =
207     {
208       "gpg",
209       "--quiet",
210       "--no-tty",
211       "--no-greeting",
212       "--no-permission-warning",
213       "--batch",
214       "--homedir",
215       keyring.asString().c_str(),
216       "-a",
217       "--export",
218       id.c_str(),
219       NULL
220     };
221     ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
222     std::string line;
223     int count;
224     for(line = prog.receiveLine(), count=0; !line.empty(); line = prog.receiveLine(), count++ )
225     {
226       stream << line;
227     }
228     prog.close();
229   }
230
231
232   bool KeyRing::Impl::verifyFileSignatureWorkflow( const Pathname &file, const std::string filedesc, const Pathname &signature)
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( filedesc );
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( filedesc, key.id, key.name, key.fingerprint );
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, key.fingerprint) )
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, key.fingerprint );
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( filedesc, key.id, key.name, key.fingerprint ) )
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( filedesc, id, "", "" ) )
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     str::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       str::smatch what;
359       if(str::regex_match(line, what, rxColons, str::match_extra))
360       {
361         if ( what[1] == "pub" )
362         {
363           key.id = what[5];
364           key.name = what[10];
365           //return key;
366         }
367         else if ( what[1] == "fpr" )
368         {
369           key.fingerprint = what[10];
370         }
371         //dumpRegexpResults(what);
372       }
373     }
374     prog.close();
375     return key;
376   }
377
378   std::list<PublicKey> KeyRing::Impl::publicKeys(const Pathname &keyring)
379   {
380     const char* argv[] =
381     {
382       "gpg",
383       "--quiet",
384       "--list-public-keys",
385       "--with-colons",
386       "--with-fingerprint",
387       "--no-tty",
388       "--no-greeting",
389       "--batch",
390       "--status-fd",
391       "1",
392       "--homedir",
393       keyring.asString().c_str(),
394       NULL
395     };
396     std::list<PublicKey> keys;
397
398     ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
399     std::string line;
400     int count = 0;
401
402     str::regex rxColons("^([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):\n$");
403     str::regex rxColonsFpr("^([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):\n$");
404
405     for(line = prog.receiveLine(), count=0; !line.empty(); line = prog.receiveLine(), count++ )
406     {
407       //MIL << line << std::endl;
408       str::smatch what;
409       if(str::regex_match(line, what, rxColons, str::match_extra))
410       {
411         PublicKey key;
412         if ( what[1] == "pub" )
413         {
414           key.id = what[5];
415           key.name = what[10];
416
417           std::string line2;
418           for(line2 = prog.receiveLine(); !line2.empty(); line2 = prog.receiveLine(), count++ )
419           {
420             str::smatch what2;
421             if (str::regex_match(line2, what2, rxColonsFpr, str::match_extra))
422             {
423               if ( (what2[1] == "fpr") && (what2[1] != "pub") && (what2[1] !="sub"))
424               {
425                 key.fingerprint = what2[10];
426                 break;
427               }
428             }
429           }
430           keys.push_back(key);
431           MIL << "Found key " << "[" << key.id << "]" << " [" << key.name << "]" << " [" << key.fingerprint << "]" << std::endl;
432         }
433         //dumpRegexpResults(what);
434       }
435     }
436     prog.close();
437     return keys;
438   }
439
440   void KeyRing::Impl::importKey( const Pathname &keyfile, const Pathname &keyring)
441   {
442     const char* argv[] =
443     {
444       "gpg",
445       "--quiet",
446       "--no-tty",
447       "--no-greeting",
448       "--no-permission-warning",
449       "--status-fd",
450       "1",
451       "--homedir",
452       keyring.asString().c_str(),
453       "--import",
454       keyfile.asString().c_str(),
455       NULL
456     };
457
458     int code;
459     ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
460     code = prog.close();
461
462     //if ( code != 0 )
463     //  ZYPP_THROW(Exception("failed to import key"));
464   }
465
466   void KeyRing::Impl::deleteKey( const std::string &id, const Pathname &keyring )
467   {
468     const char* argv[] =
469     {
470       "gpg",
471       "--yes",
472       "--quiet",
473       "--no-tty",
474       "--batch",
475       "--status-fd",
476       "1",
477       "--homedir",
478       keyring.asString().c_str(),
479       "--delete-keys",
480       id.c_str(),
481       NULL
482     };
483
484     ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
485
486     int code = prog.close();
487     if ( code )
488       ZYPP_THROW(Exception("Failed to delete key."));
489     else
490       MIL << "Deleted key " << id << " from keyring " << keyring << std::endl;
491   }
492
493
494   std::string KeyRing::Impl::readSignatureKeyId( const Pathname &data, const Pathname &keyfile )
495   {
496     // HACK create a tmp keyring with no keys
497     TmpDir dir;
498
499     const char* argv[] =
500     {
501       "gpg",
502       "--quiet",
503       "--no-tty",
504       "--no-greeting",
505       "--batch",
506       "--status-fd",
507       "1",
508       "--homedir",
509       dir.path().asString().c_str(),
510       "--verify",
511       keyfile.asString().c_str(),
512       data.asString().c_str(),
513       NULL
514     };
515
516     ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
517
518     std::string line;
519     int count = 0;
520
521     str::regex rxNoKey("^\\[GNUPG:\\] NO_PUBKEY (.+)\n$");
522     std::string id;
523     for(line = prog.receiveLine(), count=0; !line.empty(); line = prog.receiveLine(), count++ )
524     {
525       //MIL << "[" << line << "]" << std::endl;
526       str::smatch what;
527       if(str::regex_match(line, what, rxNoKey, str::match_extra))
528       {
529         if ( what.size() > 1 )
530           id = what[1];
531         //dumpRegexpResults(what);
532       }
533     }
534     prog.close();
535     return id;
536   }
537
538   bool KeyRing::Impl::verifyFile( const Pathname &file, const Pathname &signature, const Pathname &keyring)
539   {
540     const char* argv[] =
541     {
542       "gpg",
543       "--quiet",
544       "--no-tty",
545       "--batch",
546       "--no-greeting",
547       "--status-fd",
548       "1",
549       "--homedir",
550       keyring.asString().c_str(),
551       "--verify",
552       signature.asString().c_str(),
553       file.asString().c_str(),
554       NULL
555     };
556
557     // no need to parse output for now
558     //     [GNUPG:] SIG_ID yCc4u223XRJnLnVAIllvYbUd8mQ 2006-03-29 1143618744
559     //     [GNUPG:] GOODSIG A84EDAE89C800ACA SuSE Package Signing Key <build@suse.de>
560     //     gpg: Good signature from "SuSE Package Signing Key <build@suse.de>"
561     //     [GNUPG:] VALIDSIG 79C179B2E1C820C1890F9994A84EDAE89C800ACA 2006-03-29 1143618744 0 3 0 17 2 00 79C179B2E1C820C1890F9994A84EDAE89C800ACA
562     //     [GNUPG:] TRUST_UNDEFINED
563
564     //     [GNUPG:] ERRSIG A84EDAE89C800ACA 17 2 00 1143618744 9
565     //     [GNUPG:] NO_PUBKEY A84EDAE89C800ACA
566
567     ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
568
569     return (prog.close() == 0) ? true : false;
570   }
571
572   ///////////////////////////////////////////////////////////////////
573
574   ///////////////////////////////////////////////////////////////////
575   //
576   //    CLASS NAME : KeyRing
577   //
578   ///////////////////////////////////////////////////////////////////
579
580   ///////////////////////////////////////////////////////////////////
581   //
582   //    METHOD NAME : KeyRing::KeyRing
583   //    METHOD TYPE : Ctor
584   //
585   KeyRing::KeyRing()
586   : _pimpl( new Impl() )
587   {}
588
589   ///////////////////////////////////////////////////////////////////
590   //
591   //    METHOD NAME : KeyRing::KeyRing
592   //    METHOD TYPE : Ctor
593   //
594   //KeyRing::KeyRing( const Pathname &general_kr, const Pathname &trusted_kr )
595   //: _pimpl( new Impl(general_kr, trusted_kr) )
596   //{}
597
598   ///////////////////////////////////////////////////////////////////
599   //
600   //    METHOD NAME : KeyRing::~KeyRing
601   //    METHOD TYPE : Dtor
602   //
603   KeyRing::~KeyRing()
604   {}
605
606   ///////////////////////////////////////////////////////////////////
607   //
608   // Forward to implementation:
609   //
610   ///////////////////////////////////////////////////////////////////
611
612   void KeyRing::importKey( const Pathname &keyfile, bool trusted)
613   {
614     _pimpl->importKey(keyfile, trusted);
615   }
616
617   PublicKey KeyRing::readPublicKey( const Pathname &keyfile )
618   {
619     return _pimpl->readPublicKey(keyfile);
620   }
621
622   std::string KeyRing::readSignatureKeyId(  const Pathname &data, const Pathname &keyfile )
623   {
624     return _pimpl->readSignatureKeyId(data, keyfile);
625   }
626
627   void KeyRing::deleteKey( const std::string &id, bool trusted )
628   {
629     _pimpl->deleteKey(id, trusted);
630   }
631
632   std::list<PublicKey> KeyRing::publicKeys()
633   {
634     return _pimpl->publicKeys();
635   }
636
637   std::list<PublicKey> KeyRing::trustedPublicKeys()
638   {
639     return _pimpl->trustedPublicKeys();
640   }
641
642   bool KeyRing::verifyFileSignatureWorkflow( const Pathname &file, const std::string filedesc, const Pathname &signature)
643   {
644     return _pimpl->verifyFileSignatureWorkflow(file, filedesc, signature);
645   }
646
647   bool KeyRing::verifyFileSignature( const Pathname &file, const Pathname &signature)
648   {
649     return _pimpl->verifyFileSignature(file, signature);
650   }
651
652   bool KeyRing::verifyFileTrustedSignature( const Pathname &file, const Pathname &signature)
653   {
654     return _pimpl->verifyFileTrustedSignature(file, signature);
655   }
656
657   void KeyRing::dumpPublicKey( const std::string &id, bool trusted, std::ostream &stream )
658   {
659     _pimpl->dumpPublicKey( id, trusted, stream);
660   }
661
662   /////////////////////////////////////////////////////////////////
663 } // namespace zypp
664 ///////////////////////////////////////////////////////////////////