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