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