libzypp  17.7.2
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 
22 #undef ZYPP_BASE_LOGGER_LOGGROUP
23 #define ZYPP_BASE_LOGGER_LOGGROUP "zypp::gpg"
24 
25 // @TODO [threading]
26 // make sure to call the init code of gpgme only once
27 // this might need to be moved to a different location when
28 // threads are introduced into libzypp
29 boost::once_flag gpgme_init_once = BOOST_ONCE_INIT;
30 using std::endl;
31 
32 //using boost::interprocess pointer because it allows a custom deleter
33 typedef boost::interprocess::scoped_ptr<gpgme_data, boost::function<void (gpgme_data_t)>> GpgmeDataPtr;
34 typedef boost::interprocess::scoped_ptr<_gpgme_key, boost::function<void (gpgme_key_t)>> GpgmeKeyPtr;
35 typedef boost::interprocess::scoped_ptr<FILE, boost::function<int (FILE *)>> FILEPtr;
36 
37 struct GpgmeErr
38 {
39  GpgmeErr( gpgme_error_t err_r = GPG_ERR_NO_ERROR )
40  : _err( err_r )
41  {}
42  operator gpgme_error_t() const { return _err; }
43 private:
44  gpgme_error_t _err;
45 };
46 std::ostream & operator<<( std::ostream & str, const GpgmeErr & obj )
47 { return str << "<" << gpgme_strsource(obj) << "> " << gpgme_strerror(obj); }
48 
49 static void initGpgme ()
50 {
51  const char *version = gpgme_check_version(NULL);
52  if ( version )
53  {
54  MIL << "Initialized libgpgme version: " << version << endl;
55  }
56  else
57  {
58  MIL << "Initialized libgpgme with unknown version" << endl;
59  }
60 }
61 
62 namespace zypp
63 {
64 
66 {
67 public:
68  Impl();
69  ~Impl();
70 
72  std::list<std::string> readSignaturesFprs( const Pathname & signature_r )
73  { return readSignaturesFprsOptVerify( signature_r ); }
74 
76  bool verifySignaturesFprs( const Pathname & file_r, const Pathname & signature_r )
77  {
78  bool verify = false;
79  readSignaturesFprsOptVerify( signature_r, file_r, &verify );
80  return verify;
81  }
82 
83  gpgme_ctx_t _ctx;
84 
85 private:
91  std::list<std::string> readSignaturesFprsOptVerify( const Pathname & signature_r, const Pathname & file_r = "/dev/null", bool * verify_r = nullptr );
92 };
93 
95 { }
96 
97 
99  : _pimpl( new Impl )
100 {
101 
102 }
103 
104 std::list<std::string> KeyManagerCtx::Impl::readSignaturesFprsOptVerify( const Pathname & signature_r, const Pathname & file_r, bool * verify_r )
105 {
106  //lets be pessimistic
107  if ( verify_r )
108  *verify_r = false;
109 
110 
111  if (!PathInfo( signature_r ).isExist())
112  return std::list<std::string>();
113 
114  FILEPtr dataFile(fopen(file_r.c_str(), "rb"), fclose);
115  if (!dataFile)
116  return std::list<std::string>();
117 
118  GpgmeDataPtr fileData(nullptr, gpgme_data_release);
119  GpgmeErr err = gpgme_data_new_from_stream (&fileData.get(), dataFile.get());
120  if (err) {
121  ERR << err << endl;
122  return std::list<std::string>();
123  }
124 
125  FILEPtr sigFile(fopen(signature_r.c_str(), "rb"), fclose);
126  if (!sigFile) {
127  ERR << "Unable to open signature file '" << signature_r << "'" <<endl;
128  return std::list<std::string>();
129  }
130 
131  GpgmeDataPtr sigData(nullptr, gpgme_data_release);
132  err = gpgme_data_new_from_stream (&sigData.get(), sigFile.get());
133  if (err) {
134  ERR << err << endl;
135  return std::list<std::string>();
136  }
137 
138  err = gpgme_op_verify(_ctx, sigData.get(), fileData.get(), NULL);
139  if (err != GPG_ERR_NO_ERROR) {
140  ERR << err << endl;
141  return std::list<std::string>();
142  }
143 
144  gpgme_verify_result_t res = gpgme_op_verify_result(_ctx);
145  if (!res || !res->signatures) {
146  ERR << "Unable to read signature fingerprints" <<endl;
147  return std::list<std::string>();
148  }
149 
150  bool foundBadSignature = false;
151  std::list<std::string> signatures;
152  for ( gpgme_signature_t sig = res->signatures; sig; sig = sig->next ) {
153 
154  if ( sig->fpr )
155  {
156  // bsc#1100427: With ibgpgme11-1.11.0 and if a recent gpg version was used
157  // to create the signature, the field may contain the full fingerprint, but
158  // we're expected to return the ID.
159  // [https://github.com/gpg/gpgme/commit/478d1650bbef84958ccce439fac982ef57b16cd0]
160  std::string id( sig->fpr );
161  if ( id.size() > 16 )
162  id = id.substr( id.size()-16 );
163  signatures.push_back( std::move(id) );
164  }
165 
166  if ( sig->status != GPG_ERR_NO_ERROR )
167  {
168  if ( gpgme_err_code(sig->status) != GPG_ERR_KEY_EXPIRED )
169  {
170  if ( !foundBadSignature )
171  foundBadSignature = true;
172  if ( verify_r )
173  WAR << "Failed signature check: " << file_r << " " << GpgmeErr(sig->status) << endl;
174  }
175  else
176  {
177  if ( verify_r )
178  WAR << "Legacy: Ignore expired key: " << file_r << " " << GpgmeErr(sig->status) << endl;
179  }
180  }
181  }
182 
183  if ( verify_r )
184  *verify_r = (!foundBadSignature);
185  return signatures;
186 }
187 
189 {
190  gpgme_release(_ctx);
191 }
192 
194 {
195  //make sure gpgpme is initialized
196  boost::call_once(gpgme_init_once, initGpgme);
197 
198  gpgme_ctx_t ctx;
199  GpgmeErr err = gpgme_new(&ctx);
200  if (err != GPG_ERR_NO_ERROR) {
201  ERR << err << endl;
202  return shared_ptr<KeyManagerCtx>();
203  }
204 
205  //use OpenPGP
206  err = gpgme_set_protocol(ctx, GPGME_PROTOCOL_OpenPGP);
207  if (err != GPG_ERR_NO_ERROR) {
208  ERR << err << endl;
209  gpgme_release(ctx);
210  return shared_ptr<KeyManagerCtx>();
211  }
212 
213  shared_ptr<KeyManagerCtx> me( new KeyManagerCtx());
214  me->_pimpl->_ctx = ctx;
215  return me;
216 }
217 
218 bool KeyManagerCtx::setHomedir(const Pathname &keyring_r)
219 {
220 
221  /* get engine information to read current state*/
222  gpgme_engine_info_t enginfo = gpgme_ctx_get_engine_info(_pimpl->_ctx);
223  if (!enginfo)
224  return false;
225 
226  GpgmeErr err = gpgme_ctx_set_engine_info(
227  _pimpl->_ctx,
228  GPGME_PROTOCOL_OpenPGP,
229  enginfo->file_name,
230  keyring_r.c_str());
231 
232  if (err != GPG_ERR_NO_ERROR) {
233  ERR << "Unable to set homedir " << err << endl;
234  return false;
235  }
236 
237  return true;
238 }
239 
241 {
242  gpgme_engine_info_t enginfo = gpgme_ctx_get_engine_info(_pimpl->_ctx);
243  if (!enginfo)
244  return Pathname();
245 
246  return Pathname(enginfo->home_dir);
247 }
248 
249 std::list<PublicKeyData> KeyManagerCtx::listKeys()
250 {
251  std::list<PublicKeyData> keys;
252  gpgme_key_t key;
253  GpgmeErr err = GPG_ERR_NO_ERROR;
254 
255  gpgme_keylist_mode_t mode = GPGME_KEYLIST_MODE_LOCAL | GPGME_KEYLIST_MODE_SIGS;
256  gpgme_set_keylist_mode (_pimpl->_ctx, mode);
257  gpgme_op_keylist_start (_pimpl->_ctx, NULL, 0);
258 
259  while (!(err = gpgme_op_keylist_next(_pimpl->_ctx, &key))) {
261  if (data) {
262  keys.push_back(data);
263  }
264  gpgme_key_release(key);
265  }
266  gpgme_op_keylist_end(_pimpl->_ctx);
267  return keys;
268 }
269 
270 std::list<PublicKeyData> KeyManagerCtx::readKeyFromFile(const Pathname &file)
271 {
272  //seems GPGME does not support reading keys from a keyfile using
273  //gpgme_data_t and gpgme_op_keylist_from_data_start, this always
274  //return unsupported errors. However importing and listing the key works.
275  zypp::Pathname realHomedir = homedir();
276 
277  zypp::filesystem::TmpDir tmpKeyring;
278  if (!setHomedir(tmpKeyring.path()))
279  return std::list<PublicKeyData>();
280 
281  if (!importKey(file)) {
282  setHomedir(realHomedir);
283  return std::list<PublicKeyData>();
284  }
285 
286  std::list<PublicKeyData> keys = listKeys();
287  setHomedir(realHomedir);
288  return keys;
289 }
290 
291 bool KeyManagerCtx::verify(const Pathname &file, const Pathname &signature)
292 {
293  if ( !PathInfo( file ).isExist() || !PathInfo( signature ).isExist() )
294  return false;
295 
296  return _pimpl->verifySignaturesFprs(file, signature);
297 }
298 
299 bool KeyManagerCtx::exportKey(const std::string &id, std::ostream &stream)
300 {
301  GpgmeErr err = GPG_ERR_NO_ERROR;
302 
303  GpgmeKeyPtr foundKey;
304 
305  //search for requested key id
306  gpgme_key_t key;
307  gpgme_op_keylist_start(_pimpl->_ctx, NULL, 0);
308  while (!(err = gpgme_op_keylist_next(_pimpl->_ctx, &key))) {
309  if (key->subkeys && id == str::asString(key->subkeys->keyid)) {
310  GpgmeKeyPtr(key, gpgme_key_release).swap(foundKey);
311  break;
312  }
313  gpgme_key_release(key);
314  }
315  gpgme_op_keylist_end(_pimpl->_ctx);
316 
317  if (!foundKey) {
318  WAR << "Key " << id << "not found" << endl;
319  return false;
320  }
321 
322  //function needs a array of keys to export
323  gpgme_key_t keyarray[2];
324  keyarray[0] = foundKey.get();
325  keyarray[1] = NULL;
326 
327  GpgmeDataPtr out(nullptr, gpgme_data_release);
328  err = gpgme_data_new (&out.get());
329  if (err) {
330  ERR << err << endl;
331  return false;
332  }
333 
334  //format as ascii armored
335  gpgme_set_armor (_pimpl->_ctx, 1);
336  err = gpgme_op_export_keys (_pimpl->_ctx, keyarray, 0, out.get());
337  if (!err) {
338  int ret = gpgme_data_seek (out.get(), 0, SEEK_SET);
339  if (ret) {
340  ERR << "Unable to seek in exported key data" << endl;
341  return false;
342  }
343 
344  const int bufsize = 512;
345  char buf[bufsize + 1];
346  while ((ret = gpgme_data_read(out.get(), buf, bufsize)) > 0) {
347  stream.write(buf, ret);
348  }
349 
350  //failed to read from buffer
351  if (ret < 0) {
352  ERR << "Unable to read exported key data" << endl;
353  return false;
354  }
355  } else {
356  ERR << "Error exporting key: "<< err << endl;
357  return false;
358  }
359 
360  //if we reach this point export was successful
361  return true;
362 }
363 
364 bool KeyManagerCtx::importKey(const Pathname &keyfile)
365 {
366  if ( !PathInfo( keyfile ).isExist() ) {
367  ERR << "Keyfile '" << keyfile << "' does not exist.";
368  return false;
369  }
370 
371  GpgmeDataPtr data(nullptr, gpgme_data_release);
372  GpgmeErr err;
373 
374  err = gpgme_data_new_from_file(&data.get(), keyfile.c_str(), 1);
375  if (err) {
376  ERR << "Error importing key: "<< err << endl;
377  return false;
378  }
379 
380  err = gpgme_op_import(_pimpl->_ctx, data.get());
381  if (err) {
382  ERR << "Error importing key: "<< err << endl;
383  }
384  return (err == GPG_ERR_NO_ERROR);
385 }
386 
387 bool KeyManagerCtx::deleteKey(const std::string &id)
388 {
389  gpgme_key_t key;
390  GpgmeErr err = GPG_ERR_NO_ERROR;
391 
392  gpgme_op_keylist_start(_pimpl->_ctx, NULL, 0);
393 
394  while (!(err = gpgme_op_keylist_next(_pimpl->_ctx, &key))) {
395  if (key->subkeys && id == str::asString(key->subkeys->keyid)) {
396  err = gpgme_op_delete(_pimpl->_ctx, key, 0);
397 
398  gpgme_key_release(key);
399  gpgme_op_keylist_end(_pimpl->_ctx);
400 
401  if (err) {
402  ERR << "Error deleting key: "<< err << endl;
403  return false;
404  }
405  return true;
406  }
407  gpgme_key_release(key);
408  }
409 
410  gpgme_op_keylist_end(_pimpl->_ctx);
411  WAR << "Key: '"<< id << "' not found." << endl;
412  return false;
413 }
414 
415 std::list<std::string> KeyManagerCtx::readSignatureFingerprints(const Pathname &signature)
416 { return _pimpl->readSignaturesFprs(signature); }
417 
418 }
static Ptr createForOpenPGP()
Creates a new KeyManagerCtx for PGP.
Definition: KeyManager.cc:193
#define MIL
Definition: Logger.h:64
std::list< PublicKeyData > readKeyFromFile(const Pathname &file)
Returns a list of all.
Definition: KeyManager.cc:270
GpgmeErr(gpgme_error_t err_r=GPG_ERR_NO_ERROR)
Definition: KeyManager.cc:39
std::list< std::string > readSignaturesFprs(const Pathname &signature_r)
Return all fingerprints found in signature_r.
Definition: KeyManager.cc:72
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:291
boost::interprocess::scoped_ptr< FILE, boost::function< int(FILE *)> > FILEPtr
Definition: KeyManager.cc:35
#define ERR
Definition: Logger.h:66
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:299
RW_pointer< Impl > _pimpl
Pointer to implementation.
Definition: KeyManager.h:69
Pathname homedir() const
Definition: KeyManager.cc:240
boost::interprocess::scoped_ptr< gpgme_data, boost::function< void(gpgme_data_t)> > GpgmeDataPtr
Definition: KeyManager.cc:33
bool importKey(const Pathname &keyfile)
Tries to import a key from keyfile, returns true on success.
Definition: KeyManager.cc:364
Provide a new empty temporary directory and recursively delete it when no longer needed.
Definition: TmpPath.h:177
bool verifySignaturesFprs(const Pathname &file_r, const Pathname &signature_r)
Tries to verify the file_r using signature_r.
Definition: KeyManager.cc:76
std::ostream & operator<<(std::ostream &str, const Arch::CompatEntry &obj)
Definition: Arch.cc:110
#define WAR
Definition: Logger.h:65
gpgme_error_t _err
Definition: KeyManager.cc:44
static void initGpgme()
Definition: KeyManager.cc:49
bool deleteKey(const std::string &id)
Tries to delete a key specified by id, returns true on success.
Definition: KeyManager.cc:387
std::list< PublicKeyData > listKeys()
Returns a list of all public keys found in the current keyring.
Definition: KeyManager.cc:249
bool setHomedir(const Pathname &keyring_r)
Changes the keyring directory.
Definition: KeyManager.cc:218
boost::interprocess::scoped_ptr< _gpgme_key, boost::function< void(gpgme_key_t)> > GpgmeKeyPtr
Definition: KeyManager.cc:34
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:415
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:104
static PublicKeyData fromGpgmeKey(_gpgme_key *data)
Definition: PublicKey.cc:286
Easy-to use interface to the ZYPP dependency resolver.
Definition: CodePitfalls.doc:1
boost::once_flag gpgme_init_once
Definition: KeyManager.cc:29