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