Imported Upstream version 17.13.0
[platform/upstream/libzypp.git] / zypp / KeyManager.cc
1 /*---------------------------------------------------------------------\
2 |                          ____ _   __ __ ___                          |
3 |                         |__  / \ / / . \ . \                         |
4 |                           / / \ V /|  _/  _/                         |
5 |                          / /__ | | | | | |                           |
6 |                         /_____||_| |_| |_|                           |
7 |                                                                      |
8 \---------------------------------------------------------------------*/
9 #include "zypp/KeyManager.h"
10 #include "zypp/KeyRing.h"
11 #include "zypp/PathInfo.h"
12 #include "zypp/base/Logger.h"
13 #include "zypp/TmpPath.h"
14 #include "zypp/base/String.h"
15 #include "zypp/AutoDispose.h"
16
17 #include <boost/thread/once.hpp>
18 #include <boost/interprocess/smart_ptr/scoped_ptr.hpp>
19 #include <gpgme.h>
20
21 #include <stdio.h>
22 using std::endl;
23
24 #undef  ZYPP_BASE_LOGGER_LOGGROUP
25 #define ZYPP_BASE_LOGGER_LOGGROUP "zypp::gpg"
26
27 ///////////////////////////////////////////////////////////////////
28 namespace zypp
29 {
30   ///////////////////////////////////////////////////////////////////
31   namespace
32   {
33     // @TODO [threading]
34     // make sure to call the init code of gpgme only once
35     // this might need to be moved to a different location when
36     // threads are introduced into libzypp
37     boost::once_flag gpgme_init_once = BOOST_ONCE_INIT;
38
39     void initGpgme ()
40     {
41       const char *version = gpgme_check_version(NULL);
42       if ( version )
43       {
44         MIL << "Initialized libgpgme version: " << version << endl;
45       }
46       else
47       {
48         MIL << "Initialized libgpgme with unknown version" << endl;
49       }
50     }
51
52     //using boost::interprocess pointer because it allows a custom deleter
53     typedef boost::interprocess::scoped_ptr<gpgme_data, boost::function<void (gpgme_data_t)>> GpgmeDataPtr;
54     typedef boost::interprocess::scoped_ptr<_gpgme_key, boost::function<void (gpgme_key_t)>>  GpgmeKeyPtr;
55     typedef boost::interprocess::scoped_ptr<FILE, boost::function<int (FILE *)>> FILEPtr;
56
57     struct GpgmeErr
58     {
59       GpgmeErr( gpgme_error_t err_r = GPG_ERR_NO_ERROR )
60       : _err( err_r )
61       {}
62       operator gpgme_error_t() const { return _err; }
63     private:
64       gpgme_error_t _err;
65     };
66
67     std::ostream & operator<<( std::ostream & str, const GpgmeErr & obj )
68     { return str << "<" << gpgme_strsource(obj) << "> " << gpgme_strerror(obj); }
69
70     /** \relates gpgme_import_result_t Stream output. */
71     std::ostream & operator<<( std::ostream & str, const _gpgme_op_import_result & obj )
72     {
73       str << "gpgme_op_import_result {" << endl;
74       str << "  "  << obj.considered            << " The total number of considered keys." << endl;
75       str << "  "  << obj.no_user_id            << " The number of keys without user ID." << endl;
76       str << "  "  << obj.imported              << " The total number of imported keys." << endl;
77       str << "  "  << obj.imported_rsa          << " imported RSA keys." << endl;
78       str << "  "  << obj.unchanged             << " unchanged keys." << endl;
79       str << "  "  << obj.new_user_ids          << " new user IDs." << endl;
80       str << "  "  << obj.new_sub_keys          << " new sub keys." << endl;
81       str << "  "  << obj.new_signatures        << " new signatures." << endl;
82       str << "  "  << obj.new_revocations       << " new revocations." << endl;
83       str << "  "  << obj.secret_read           << " secret keys read." << endl;
84       str << "  "  << obj.secret_imported       << " imported secret keys." << endl;
85       str << "  "  << obj.secret_unchanged      << " unchanged secret keys." << endl;
86       str << "  "  << obj.not_imported          << " keys not imported." << endl;
87       for ( gpgme_import_status_t p = obj.imports; p; p = p->next )
88       {
89         str << "  - "  << p->fpr << ": " << p->result << endl;
90       }
91       // In V.1.11: str << "  "  << obj.skipped_v3_keys << " skipped v3 keys." << endl;
92       return str << "}";
93     }
94   } // namespace
95   ///////////////////////////////////////////////////////////////////
96
97   struct GpgmeException : public KeyRingException
98   {
99     GpgmeException( const std::string & in_r, const GpgmeErr & err_r )
100     : KeyRingException( str::Format( "libgpgme error in '%1%': %2%" ) % in_r % err_r )
101     {}
102   };
103
104   class KeyManagerCtx::Impl
105   {
106   public:
107     Impl()
108     { boost::call_once( gpgme_init_once, initGpgme ); }
109
110     ~Impl()
111     { if ( _ctx ) gpgme_release( _ctx ); }
112
113
114     /** Return all fingerprints found in \a signature_r. */
115     std::list<std::string> readSignaturesFprs( const Pathname & signature_r )
116     { return readSignaturesFprsOptVerify( signature_r ); }
117
118     /** Tries to verify the \a file_r using \a signature_r. */
119     bool verifySignaturesFprs( const Pathname & file_r, const Pathname & signature_r )
120     {
121       bool verify = false;
122       readSignaturesFprsOptVerify( signature_r, file_r, &verify );
123       return verify;
124     }
125
126     gpgme_ctx_t _ctx { nullptr };
127     bool _volatile { false };   ///< readKeyFromFile workaround bsc#1140670
128
129   private:
130     /** Return all fingerprints found in \a signature_r and optionally verify the \a file_r on the fly.
131      *
132      * If \a verify_r is not a \c nullptr, log verification errors and return
133      * whether all signatures are good.
134      */
135     std::list<std::string> readSignaturesFprsOptVerify( const Pathname & signature_r, const Pathname & file_r = "/dev/null", bool * verify_r = nullptr );
136   };
137
138 std::list<std::string> KeyManagerCtx::Impl::readSignaturesFprsOptVerify( const Pathname & signature_r, const Pathname & file_r, bool * verify_r )
139 {
140   //lets be pessimistic
141   if ( verify_r )
142     *verify_r = false;
143
144
145   if (!PathInfo( signature_r ).isExist())
146     return std::list<std::string>();
147
148   FILEPtr dataFile(fopen(file_r.c_str(), "rb"), fclose);
149   if (!dataFile)
150     return std::list<std::string>();
151
152   GpgmeDataPtr fileData(nullptr, gpgme_data_release);
153   GpgmeErr err = gpgme_data_new_from_stream (&fileData.get(), dataFile.get());
154   if (err) {
155     ERR << err << endl;
156     return std::list<std::string>();
157   }
158
159   FILEPtr sigFile(fopen(signature_r.c_str(), "rb"), fclose);
160   if (!sigFile) {
161     ERR << "Unable to open signature file '" << signature_r << "'" <<endl;
162     return std::list<std::string>();
163   }
164
165   GpgmeDataPtr sigData(nullptr, gpgme_data_release);
166   err = gpgme_data_new_from_stream (&sigData.get(), sigFile.get());
167   if (err) {
168     ERR << err << endl;
169     return std::list<std::string>();
170   }
171
172   err = gpgme_op_verify(_ctx, sigData.get(), fileData.get(), NULL);
173   if (err != GPG_ERR_NO_ERROR) {
174     ERR << err << endl;
175     return std::list<std::string>();
176   }
177
178   gpgme_verify_result_t res = gpgme_op_verify_result(_ctx);
179   if (!res || !res->signatures) {
180     ERR << "Unable to read signature fingerprints" <<endl;
181     return std::list<std::string>();
182   }
183
184   bool foundBadSignature = false;
185   std::list<std::string> signatures;
186   for ( gpgme_signature_t sig = res->signatures; sig; sig = sig->next ) {
187
188     if ( sig->fpr )
189     {
190       // bsc#1100427: With ibgpgme11-1.11.0 and if a recent gpg version was used
191       // to create the signature, the field may contain the full fingerprint, but
192       // we're expected to return the ID.
193       // [https://github.com/gpg/gpgme/commit/478d1650bbef84958ccce439fac982ef57b16cd0]
194       std::string id( sig->fpr );
195       if ( id.size() > 16 )
196         id = id.substr( id.size()-16 );
197       signatures.push_back( std::move(id) );
198     }
199
200     if ( sig->status != GPG_ERR_NO_ERROR )
201     {
202       if ( gpgme_err_code(sig->status) != GPG_ERR_KEY_EXPIRED )
203       {
204         if ( !foundBadSignature )
205           foundBadSignature = true;
206         if ( verify_r )
207           WAR << "Failed signature check: " << file_r << " " << GpgmeErr(sig->status) << endl;
208       }
209       else
210       {
211         if ( verify_r )
212           WAR << "Legacy: Ignore expired key: " << file_r << " " << GpgmeErr(sig->status) << endl;
213       }
214     }
215   }
216
217   if ( verify_r )
218     *verify_r = (!foundBadSignature);
219   return signatures;
220 }
221
222
223 KeyManagerCtx::KeyManagerCtx()
224 : _pimpl( new Impl )
225 {}
226
227 KeyManagerCtx KeyManagerCtx::createForOpenPGP()
228 {
229   static Pathname tmppath( zypp::myTmpDir() / "PublicKey" );
230   filesystem::assert_dir( tmppath );
231
232   KeyManagerCtx ret { createForOpenPGP( tmppath ) };
233   ret._pimpl->_volatile = true; // readKeyFromFile workaround bsc#1140670
234   return ret;
235 }
236
237 KeyManagerCtx KeyManagerCtx::createForOpenPGP( const Pathname & keyring_r )
238 {
239   DBG << "createForOpenPGP(" << keyring_r << ")" << endl;
240
241   KeyManagerCtx ret;
242   gpgme_ctx_t & ctx { ret._pimpl->_ctx };
243
244   // create the context
245   GpgmeErr err = gpgme_new( &ctx );
246   if ( err != GPG_ERR_NO_ERROR )
247     ZYPP_THROW( GpgmeException( "gpgme_new", err ) );
248
249   // use OpenPGP
250   err = gpgme_set_protocol( ctx, GPGME_PROTOCOL_OpenPGP );
251   if ( err != GPG_ERR_NO_ERROR )
252     ZYPP_THROW( GpgmeException( "gpgme_set_protocol", err ) );
253
254   if ( !keyring_r.empty() ) {
255     // get engine information to read current state
256     gpgme_engine_info_t enginfo = gpgme_ctx_get_engine_info( ctx );
257     if ( !enginfo )
258       ZYPP_THROW( GpgmeException( "gpgme_ctx_get_engine_info", err ) );
259
260     err = gpgme_ctx_set_engine_info( ctx, GPGME_PROTOCOL_OpenPGP, enginfo->file_name, keyring_r.c_str() );
261     if ( err != GPG_ERR_NO_ERROR )
262       ZYPP_THROW( GpgmeException( "gpgme_ctx_set_engine_info", err ) );
263   }
264
265   return ret;
266 }
267
268 Pathname KeyManagerCtx::homedir() const
269 {
270   Pathname ret;
271   if ( gpgme_engine_info_t enginfo = gpgme_ctx_get_engine_info( _pimpl->_ctx ) )
272     ret = enginfo->home_dir;
273   return ret;
274 }
275
276 std::list<PublicKeyData> KeyManagerCtx::listKeys()
277 {
278   std::list<PublicKeyData> ret;
279   GpgmeErr err = GPG_ERR_NO_ERROR;
280
281   // Reset gpgme_keylist_mode on return!
282   AutoDispose<gpgme_keylist_mode_t> guard { gpgme_get_keylist_mode( _pimpl->_ctx ), bind( &gpgme_set_keylist_mode, _pimpl->_ctx, _1 ) };
283   // Let listed keys include signatures (required if PublicKeyData are created from the key)
284   if ( (err = gpgme_set_keylist_mode( _pimpl->_ctx, GPGME_KEYLIST_MODE_LOCAL | GPGME_KEYLIST_MODE_SIGS )) != GPG_ERR_NO_ERROR ) {
285     ERR << "gpgme_set_keylist_mode: " << err << endl;
286     return ret;
287   }
288
289   if ( (err = gpgme_op_keylist_start( _pimpl->_ctx, NULL, 0 )) != GPG_ERR_NO_ERROR ) {
290     ERR << "gpgme_op_keylist_start: " << err << endl;
291     return ret;
292   }
293   // Close list operation on return!
294   AutoDispose<gpgme_ctx_t> guard2 { _pimpl->_ctx, &gpgme_op_keylist_end };
295
296   AutoDispose<gpgme_key_t> key { nullptr, &gpgme_key_release };
297   for ( ; gpgme_op_keylist_next( _pimpl->_ctx, &(*key) ) == GPG_ERR_NO_ERROR; key.getDispose()( key ) ) {
298     PublicKeyData data { PublicKeyData::fromGpgmeKey( key ) };
299     if ( data )
300       ret.push_back( data );
301   }
302
303   return ret;
304 }
305
306 #if 1
307 std::list<PublicKeyData> KeyManagerCtx::readKeyFromFile( const Pathname & keyfile_r )
308 {
309   // bsc#1140670: GPGME does not support reading keys from a keyfile using
310   // gpgme_data_t and gpgme_op_keylist_from_data_start. Despite GPGME_KEYLIST_MODE_SIGS
311   // the signatures are missing, but we need them to create proper PublicKeyData objects.
312   // While this is not resolved, we read into a temp. keyring. Impl::_volatile helps
313   // to detect whether we can clear and import into the current context or need to
314   // create a temp. one.
315   std::list<PublicKeyData> ret;
316
317   if ( _pimpl->_volatile ) {
318     // in a volatile context we can simple clear the keyring...
319     filesystem::clean_dir( homedir() );
320     if ( importKey( keyfile_r ) )
321       ret = listKeys();
322   } else {
323     // read in a volatile context
324     ret = createForOpenPGP().readKeyFromFile( keyfile_r );
325   }
326
327   return ret;
328 }
329 #else
330 std::list<PublicKeyData> KeyManagerCtx::readKeyFromFile( const Pathname & file_r )
331 {
332   std::list<PublicKeyData> ret;
333   GpgmeErr err = GPG_ERR_NO_ERROR;
334
335   AutoDispose<gpgme_data_t> data { nullptr, &gpgme_data_release };
336   if ( (err = gpgme_data_new_from_file( &(*data), file_r.c_str(), 1 )) != GPG_ERR_NO_ERROR ) {
337     ERR << "gpgme_data_new_from_file " << file_r << ": " << err << endl;
338     return ret;
339   }
340
341   // Reset gpgme_keylist_mode on return!
342   AutoDispose<gpgme_keylist_mode_t> guard { gpgme_get_keylist_mode( _pimpl->_ctx ), bind( &gpgme_set_keylist_mode, _pimpl->_ctx, _1 ) };
343   // Let listed keys include signatures (required if PublicKeyData are created from the key)
344   if ( (err = gpgme_set_keylist_mode( _pimpl->_ctx, GPGME_KEYLIST_MODE_LOCAL | GPGME_KEYLIST_MODE_SIGS )) != GPG_ERR_NO_ERROR ) {
345     ERR << "gpgme_set_keylist_mode: " << err << endl;
346     return ret;
347   }
348
349   if ( (err = gpgme_op_keylist_from_data_start( _pimpl->_ctx, data, 0 )) != GPG_ERR_NO_ERROR ) {
350     ERR << "gpgme_op_keylist_from_data_start " << file_r << ": " << err << endl;
351     return ret;
352   }
353   // Close list operation on return!
354   AutoDispose<gpgme_ctx_t> guard2 { _pimpl->_ctx, &gpgme_op_keylist_end };
355
356   AutoDispose<gpgme_key_t> key { nullptr, &gpgme_key_release };
357   for ( ; gpgme_op_keylist_next( _pimpl->_ctx, &(*key) ) == GPG_ERR_NO_ERROR; key.getDispose()( key ) ) {
358     PublicKeyData data { PublicKeyData::fromGpgmeKey( key ) };
359     if ( data )
360       ret.push_back( data );
361   }
362
363   return ret;
364 }
365 #endif
366
367 bool KeyManagerCtx::verify(const Pathname &file, const Pathname &signature)
368 {
369   return _pimpl->verifySignaturesFprs(file, signature);
370 }
371
372 bool KeyManagerCtx::exportKey(const std::string &id, std::ostream &stream)
373 {
374   GpgmeErr err = GPG_ERR_NO_ERROR;
375
376   GpgmeKeyPtr foundKey;
377
378   //search for requested key id
379   gpgme_key_t key;
380   gpgme_op_keylist_start(_pimpl->_ctx, NULL, 0);
381   while (!(err = gpgme_op_keylist_next(_pimpl->_ctx, &key))) {
382     if (key->subkeys && id == str::asString(key->subkeys->keyid)) {
383       GpgmeKeyPtr(key, gpgme_key_release).swap(foundKey);
384       break;
385     }
386     gpgme_key_release(key);
387   }
388   gpgme_op_keylist_end(_pimpl->_ctx);
389
390   if (!foundKey) {
391     WAR << "Key " << id << "not found" << endl;
392     return false;
393   }
394
395   //function needs a array of keys to export
396   gpgme_key_t keyarray[2];
397   keyarray[0] = foundKey.get();
398   keyarray[1] = NULL;
399
400   GpgmeDataPtr out(nullptr, gpgme_data_release);
401   err = gpgme_data_new (&out.get());
402   if (err) {
403     ERR << err << endl;
404     return false;
405   }
406
407   //format as ascii armored
408   gpgme_set_armor (_pimpl->_ctx, 1);
409   err = gpgme_op_export_keys (_pimpl->_ctx, keyarray, 0, out.get());
410   if (!err) {
411     int ret = gpgme_data_seek (out.get(), 0, SEEK_SET);
412     if (ret) {
413       ERR << "Unable to seek in exported key data" << endl;
414       return false;
415     }
416
417     const int bufsize = 512;
418     char buf[bufsize + 1];
419     while ((ret = gpgme_data_read(out.get(), buf, bufsize)) > 0) {
420       stream.write(buf, ret);
421     }
422
423     //failed to read from buffer
424     if (ret < 0) {
425       ERR << "Unable to read exported key data" << endl;
426       return false;
427     }
428   } else {
429     ERR << "Error exporting key: "<< err << endl;
430     return false;
431   }
432
433   //if we reach this point export was successful
434   return true;
435 }
436
437 bool KeyManagerCtx::importKey(const Pathname &keyfile)
438 {
439   if ( !PathInfo( keyfile ).isExist() ) {
440     ERR << "Keyfile '" << keyfile << "' does not exist.";
441     return false;
442   }
443
444   GpgmeDataPtr data(nullptr, gpgme_data_release);
445   GpgmeErr err;
446
447   err = gpgme_data_new_from_file(&data.get(), keyfile.c_str(), 1);
448   if (err) {
449     ERR << "Error importing key: "<< err << endl;
450     return false;
451   }
452
453   err = gpgme_op_import(_pimpl->_ctx, data.get());
454   if (err) {
455     ERR << "Error importing key: "<< err << endl;
456     return false;
457   }
458
459   // Work around bsc#1127220 [libgpgme] no error upon incomplete import due to signal received.
460   // We need this error, otherwise RpmDb will report the missing keys as 'probably v3'.
461   if ( gpgme_import_result_t res = gpgme_op_import_result(_pimpl->_ctx) )
462   {
463     if ( ! res->considered && PathInfo(keyfile).size() )
464     {
465       DBG << *res << endl;
466       ERR << "Error importing key: No keys considered (bsc#1127220, [libgpgme] signal received?)" << endl;
467       return false;
468     }
469   }
470
471   return (err == GPG_ERR_NO_ERROR);
472 }
473
474 bool KeyManagerCtx::deleteKey(const std::string &id)
475 {
476   gpgme_key_t key;
477   GpgmeErr err = GPG_ERR_NO_ERROR;
478
479   gpgme_op_keylist_start(_pimpl->_ctx, NULL, 0);
480
481   while (!(err = gpgme_op_keylist_next(_pimpl->_ctx, &key))) {
482     if (key->subkeys && id == str::asString(key->subkeys->keyid)) {
483         err = gpgme_op_delete(_pimpl->_ctx, key, 0);
484
485         gpgme_key_release(key);
486         gpgme_op_keylist_end(_pimpl->_ctx);
487
488         if (err) {
489           ERR << "Error deleting key: "<< err << endl;
490           return false;
491         }
492         return true;
493     }
494     gpgme_key_release(key);
495   }
496
497   gpgme_op_keylist_end(_pimpl->_ctx);
498   WAR << "Key: '"<< id << "' not found." << endl;
499   return false;
500 }
501
502 std::list<std::string> KeyManagerCtx::readSignatureFingerprints(const Pathname &signature)
503 { return _pimpl->readSignaturesFprs(signature); }
504
505 } // namespace zypp
506 ///////////////////////////////////////////////////////////////////