- Fixes for the checksum/signature subsystem (#302059)
[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     if ( ! PathInfo(signature).isFile() )
538       ZYPP_THROW(Exception("Signature file " + signature.asString() + " not found"));
539
540     MIL << "Determining key id if signature " << signature << endl;
541     // HACK create a tmp keyring with no keys
542     TmpDir dir(_base_dir, "fake-keyring");
543     TmpFile fakeData(_base_dir, "fake-data");
544
545     const char* argv[] =
546     {
547       GPG_BINARY,
548       "--no-default-keyring",
549       "--quiet",
550       "--no-tty",
551       "--no-greeting",
552       "--batch",
553       "--status-fd",
554       "1",
555       "--homedir",
556       dir.path().asString().c_str(),
557       "--verify",
558       signature.asString().c_str(),
559       fakeData.path().asString().c_str(),
560       NULL
561     };
562
563     ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
564
565     string line;
566     int count = 0;
567
568     str::regex rxNoKey("^\\[GNUPG:\\] NO_PUBKEY (.+)\n$");
569     string id;
570     for(line = prog.receiveLine(), count=0; !line.empty(); line = prog.receiveLine(), count++ )
571     {
572       //MIL << "[" << line << "]" << endl;
573       str::smatch what;
574       if(str::regex_match(line, what, rxNoKey))
575       {
576         if ( what.size() >= 1 )
577           id = what[1];
578         //dumpRegexpResults(what);
579       }
580       else
581       {
582         MIL << "'" << line << "'" << endl;
583       }
584     }
585
586     if ( count == 0 )
587     {
588       MIL << "no output" << endl;
589     }
590
591     MIL << "Determined key id [" << id << "] for signature " << signature << endl;
592     prog.close();
593     return id;
594   }
595
596   bool KeyRing::Impl::verifyFile( const Pathname &file, const Pathname &signature, const Pathname &keyring)
597   {
598     const char* argv[] =
599     {
600       GPG_BINARY,
601       "--no-default-keyring",
602       "--quiet",
603       "--no-tty",
604       "--batch",
605       "--no-greeting",
606       "--status-fd",
607       "1",
608       "--homedir",
609       keyring.asString().c_str(),
610       "--verify",
611       signature.asString().c_str(),
612       file.asString().c_str(),
613       NULL
614     };
615
616     // no need to parse output for now
617     //     [GNUPG:] SIG_ID yCc4u223XRJnLnVAIllvYbUd8mQ 2006-03-29 1143618744
618     //     [GNUPG:] GOODSIG A84EDAE89C800ACA SuSE Package Signing Key <build@suse.de>
619     //     gpg: Good signature from "SuSE Package Signing Key <build@suse.de>"
620     //     [GNUPG:] VALIDSIG 79C179B2E1C820C1890F9994A84EDAE89C800ACA 2006-03-29 1143618744 0 3 0 17 2 00 79C179B2E1C820C1890F9994A84EDAE89C800ACA
621     //     [GNUPG:] TRUST_UNDEFINED
622
623     //     [GNUPG:] ERRSIG A84EDAE89C800ACA 17 2 00 1143618744 9
624     //     [GNUPG:] NO_PUBKEY A84EDAE89C800ACA
625
626     ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
627
628     return (prog.close() == 0) ? true : false;
629   }
630
631   ///////////////////////////////////////////////////////////////////
632
633   ///////////////////////////////////////////////////////////////////
634   //
635   //    CLASS NAME : KeyRing
636   //
637   ///////////////////////////////////////////////////////////////////
638
639   ///////////////////////////////////////////////////////////////////
640   //
641   //    METHOD NAME : KeyRing::KeyRing
642   //    METHOD TYPE : Ctor
643   //
644   KeyRing::KeyRing(const Pathname &baseTmpDir)
645   : _pimpl( new Impl(baseTmpDir) )
646   {}
647
648   ///////////////////////////////////////////////////////////////////
649   //
650   //    METHOD NAME : KeyRing::KeyRing
651   //    METHOD TYPE : Ctor
652   //
653   //KeyRing::KeyRing( const Pathname &general_kr, const Pathname &trusted_kr )
654   //: _pimpl( new Impl(general_kr, trusted_kr) )
655   //{}
656
657   ///////////////////////////////////////////////////////////////////
658   //
659   //    METHOD NAME : KeyRing::~KeyRing
660   //    METHOD TYPE : Dtor
661   //
662   KeyRing::~KeyRing()
663   {}
664
665   ///////////////////////////////////////////////////////////////////
666   //
667   // Forward to implementation:
668   //
669   ///////////////////////////////////////////////////////////////////
670
671   
672   void KeyRing::importKey( const PublicKey &key, bool trusted )
673   {
674     _pimpl->importKey( key.path(), trusted );    
675   }
676   
677   string KeyRing::readSignatureKeyId( const Pathname &signature )
678   {
679     return _pimpl->readSignatureKeyId(signature);
680   }
681
682   void KeyRing::deleteKey( const string &id, bool trusted )
683   {
684     _pimpl->deleteKey(id, trusted);
685   }
686
687   list<PublicKey> KeyRing::publicKeys()
688   {
689     return _pimpl->publicKeys();
690   }
691
692   list<PublicKey> KeyRing::trustedPublicKeys()
693   {
694     return _pimpl->trustedPublicKeys();
695   }
696
697   list<string> KeyRing::publicKeyIds()
698   {
699     return _pimpl->publicKeyIds();
700   }
701
702   list<string> KeyRing::trustedPublicKeyIds()
703   {
704     return _pimpl->trustedPublicKeyIds();
705   }
706   
707   bool KeyRing::verifyFileSignatureWorkflow( const Pathname &file, const string filedesc, const Pathname &signature)
708   {
709     return _pimpl->verifyFileSignatureWorkflow(file, filedesc, signature);
710   }
711
712   bool KeyRing::verifyFileSignature( const Pathname &file, const Pathname &signature)
713   {
714     return _pimpl->verifyFileSignature(file, signature);
715   }
716
717   bool KeyRing::verifyFileTrustedSignature( const Pathname &file, const Pathname &signature)
718   {
719     return _pimpl->verifyFileTrustedSignature(file, signature);
720   }
721
722   void KeyRing::dumpPublicKey( const string &id, bool trusted, ostream &stream )
723   {
724     _pimpl->dumpPublicKey( id, trusted, stream);
725   }
726
727   bool KeyRing::isKeyTrusted( const string &id )
728   {
729     return _pimpl->isKeyTrusted(id);
730   }
731      
732   bool KeyRing::isKeyKnown( const string &id )
733   {
734     return _pimpl->isKeyKnown(id);
735   }
736   
737   /////////////////////////////////////////////////////////////////
738 } // namespace zypp
739 ///////////////////////////////////////////////////////////////////