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