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