libzypp  17.3.1
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  gpgme_check_version(NULL);
52 
53 }
54 
55 namespace zypp
56 {
57 
59 {
60 public:
61  Impl();
62  ~Impl();
63 
64  std::list<std::string> verifyAndReadSignaturesFprs(const Pathname & file, const Pathname & signature, bool &verifed);
65 
66  gpgme_ctx_t _ctx;
67 };
68 
70 { }
71 
72 
74  : _pimpl( new Impl )
75 {
76 
77 }
78 
79 /*
80  * \brief KeyManagerCtx::Impl::verifyAndReadSignaturesFprs
81  * Tries to verify the \a file using \a signature , will return all
82  * signatures and sets \a verified to true if all signatures are good
83  * \internal
84  */
85 std::list<std::string> KeyManagerCtx::Impl::verifyAndReadSignaturesFprs(const Pathname &file, const Pathname &signature, bool &verifed)
86 {
87  //lets be pessimistic
88  verifed = false;
89 
90  if (!PathInfo( signature ).isExist())
91  return std::list<std::string>();
92 
93  FILEPtr dataFile(fopen(file.c_str(), "rb"), fclose);
94  if (!dataFile)
95  return std::list<std::string>();
96 
97  GpgmeDataPtr fileData(nullptr, gpgme_data_release);
98  GpgmeErr err = gpgme_data_new_from_stream (&fileData.get(), dataFile.get());
99  if (err) {
100  ERR << err << endl;
101  return std::list<std::string>();
102  }
103 
104  FILEPtr sigFile(fopen(signature.c_str(), "rb"), fclose);
105  if (!sigFile) {
106  ERR << "Unable to open signature file '" << signature << "'" <<endl;
107  return std::list<std::string>();
108  }
109 
110  GpgmeDataPtr sigData(nullptr, gpgme_data_release);
111  err = gpgme_data_new_from_stream (&sigData.get(), sigFile.get());
112  if (err) {
113  ERR << err << endl;
114  return std::list<std::string>();
115  }
116 
117  err = gpgme_op_verify(_ctx, sigData.get(), fileData.get(), NULL);
118  if (err != GPG_ERR_NO_ERROR) {
119  ERR << err << endl;
120  return std::list<std::string>();
121  }
122 
123  gpgme_verify_result_t res = gpgme_op_verify_result(_ctx);
124  if (!res || !res->signatures) {
125  ERR << "Unable to read signature fingerprints" <<endl;
126  return std::list<std::string>();
127  }
128 
129  bool foundBadSignature = false;
130  std::list<std::string> signatures;
131  gpgme_signature_t sig = res->signatures;
132  while (sig) {
133 
134  if (!foundBadSignature)
135  foundBadSignature = (sig->status != GPG_ERR_NO_ERROR);
136 
137  if (!sig->fpr)
138  continue;
139 
140  signatures.push_back(str::asString(sig->fpr));
141  sig = sig->next;
142  }
143 
144  verifed = (!foundBadSignature);
145  return signatures;
146 }
147 
149 {
150  gpgme_release(_ctx);
151 }
152 
154 {
155  //make sure gpgpme is initialized
156  boost::call_once(gpgme_init_once, initGpgme);
157 
158  gpgme_ctx_t ctx;
159  GpgmeErr err = gpgme_new(&ctx);
160  if (err != GPG_ERR_NO_ERROR) {
161  ERR << err << endl;
162  return shared_ptr<KeyManagerCtx>();
163  }
164 
165  //use OpenPGP
166  err = gpgme_set_protocol(ctx, GPGME_PROTOCOL_OpenPGP);
167  if (err != GPG_ERR_NO_ERROR) {
168  ERR << err << endl;
169  gpgme_release(ctx);
170  return shared_ptr<KeyManagerCtx>();
171  }
172 
173  shared_ptr<KeyManagerCtx> me( new KeyManagerCtx());
174  me->_pimpl->_ctx = ctx;
175  return me;
176 }
177 
178 bool KeyManagerCtx::setHomedir(const Pathname &keyring_r)
179 {
180 
181  /* get engine information to read current state*/
182  gpgme_engine_info_t enginfo = gpgme_ctx_get_engine_info(_pimpl->_ctx);
183  if (!enginfo)
184  return false;
185 
186  GpgmeErr err = gpgme_ctx_set_engine_info(
187  _pimpl->_ctx,
188  GPGME_PROTOCOL_OpenPGP,
189  enginfo->file_name,
190  keyring_r.c_str());
191 
192  if (err != GPG_ERR_NO_ERROR) {
193  ERR << "Unable to set homedir " << err << endl;
194  return false;
195  }
196 
197  return true;
198 }
199 
201 {
202  gpgme_engine_info_t enginfo = gpgme_ctx_get_engine_info(_pimpl->_ctx);
203  if (!enginfo)
204  return Pathname();
205 
206  return Pathname(enginfo->home_dir);
207 }
208 
209 std::list<PublicKeyData> KeyManagerCtx::listKeys()
210 {
211  std::list<PublicKeyData> keys;
212  gpgme_key_t key;
213  GpgmeErr err = GPG_ERR_NO_ERROR;
214 
215  gpgme_keylist_mode_t mode = GPGME_KEYLIST_MODE_LOCAL | GPGME_KEYLIST_MODE_SIGS;
216  gpgme_set_keylist_mode (_pimpl->_ctx, mode);
217  gpgme_op_keylist_start (_pimpl->_ctx, NULL, 0);
218 
219  while (!(err = gpgme_op_keylist_next(_pimpl->_ctx, &key))) {
221  if (data) {
222  keys.push_back(data);
223  }
224  gpgme_key_release(key);
225  }
226  gpgme_op_keylist_end(_pimpl->_ctx);
227  return keys;
228 }
229 
230 std::list<PublicKeyData> KeyManagerCtx::readKeyFromFile(const Pathname &file)
231 {
232  //seems GPGME does not support reading keys from a keyfile using
233  //gpgme_data_t and gpgme_op_keylist_from_data_start, this always
234  //return unsupported errors. However importing and listing the key works.
235  zypp::Pathname realHomedir = homedir();
236 
237  zypp::filesystem::TmpDir tmpKeyring;
238  if (!setHomedir(tmpKeyring.path()))
239  return std::list<PublicKeyData>();
240 
241  if (!importKey(file)) {
242  setHomedir(realHomedir);
243  return std::list<PublicKeyData>();
244  }
245 
246  std::list<PublicKeyData> keys = listKeys();
247  setHomedir(realHomedir);
248  return keys;
249 }
250 
251 bool KeyManagerCtx::verify(const Pathname &file, const Pathname &signature)
252 {
253  if ( !PathInfo( file ).isExist() || !PathInfo( signature ).isExist() )
254  return false;
255 
256  bool verified = false;
257  _pimpl->verifyAndReadSignaturesFprs(file, signature, verified);
258  return verified;
259 }
260 
261 bool KeyManagerCtx::exportKey(const std::string &id, std::ostream &stream)
262 {
263  GpgmeErr err = GPG_ERR_NO_ERROR;
264 
265  GpgmeKeyPtr foundKey;
266 
267  //search for requested key id
268  gpgme_key_t key;
269  gpgme_op_keylist_start(_pimpl->_ctx, NULL, 0);
270  while (!(err = gpgme_op_keylist_next(_pimpl->_ctx, &key))) {
271  if (key->subkeys && id == str::asString(key->subkeys->keyid)) {
272  GpgmeKeyPtr(key, gpgme_key_release).swap(foundKey);
273  break;
274  }
275  gpgme_key_release(key);
276  }
277  gpgme_op_keylist_end(_pimpl->_ctx);
278 
279  if (!foundKey) {
280  WAR << "Key " << id << "not found" << endl;
281  return false;
282  }
283 
284  //function needs a array of keys to export
285  gpgme_key_t keyarray[2];
286  keyarray[0] = foundKey.get();
287  keyarray[1] = NULL;
288 
289  GpgmeDataPtr out(nullptr, gpgme_data_release);
290  err = gpgme_data_new (&out.get());
291  if (err) {
292  ERR << err << endl;
293  return false;
294  }
295 
296  //format as ascii armored
297  gpgme_set_armor (_pimpl->_ctx, 1);
298  err = gpgme_op_export_keys (_pimpl->_ctx, keyarray, 0, out.get());
299  if (!err) {
300  int ret = gpgme_data_seek (out.get(), 0, SEEK_SET);
301  if (ret) {
302  ERR << "Unable to seek in exported key data" << endl;
303  return false;
304  }
305 
306  const int bufsize = 512;
307  char buf[bufsize + 1];
308  while ((ret = gpgme_data_read(out.get(), buf, bufsize)) > 0) {
309  stream.write(buf, ret);
310  }
311 
312  //failed to read from buffer
313  if (ret < 0) {
314  ERR << "Unable to read exported key data" << endl;
315  return false;
316  }
317  } else {
318  ERR << "Error exporting key: "<< err << endl;
319  return false;
320  }
321 
322  //if we reach this point export was successful
323  return true;
324 }
325 
326 bool KeyManagerCtx::importKey(const Pathname &keyfile)
327 {
328  if ( !PathInfo( keyfile ).isExist() ) {
329  ERR << "Keyfile '" << keyfile << "' does not exist.";
330  return false;
331  }
332 
333  GpgmeDataPtr data(nullptr, gpgme_data_release);
334  GpgmeErr err;
335 
336  err = gpgme_data_new_from_file(&data.get(), keyfile.c_str(), 1);
337  if (err) {
338  ERR << "Error importing key: "<< err << endl;
339  return false;
340  }
341 
342  err = gpgme_op_import(_pimpl->_ctx, data.get());
343  if (err) {
344  ERR << "Error importing key: "<< err << endl;
345  }
346  return (err == GPG_ERR_NO_ERROR);
347 }
348 
349 bool KeyManagerCtx::deleteKey(const std::string &id)
350 {
351  gpgme_key_t key;
352  GpgmeErr err = GPG_ERR_NO_ERROR;
353 
354  gpgme_op_keylist_start(_pimpl->_ctx, NULL, 0);
355 
356  while (!(err = gpgme_op_keylist_next(_pimpl->_ctx, &key))) {
357  if (key->subkeys && id == str::asString(key->subkeys->keyid)) {
358  err = gpgme_op_delete(_pimpl->_ctx, key, 0);
359 
360  gpgme_key_release(key);
361  gpgme_op_keylist_end(_pimpl->_ctx);
362 
363  if (err) {
364  ERR << "Error deleting key: "<< err << endl;
365  return false;
366  }
367  return true;
368  }
369  gpgme_key_release(key);
370  }
371 
372  gpgme_op_keylist_end(_pimpl->_ctx);
373  WAR << "Key: '"<< id << "' not found." << endl;
374  return false;
375 }
376 
377 std::list<std::string> KeyManagerCtx::readSignatureFingerprints(const Pathname &signature)
378 {
379  //gpgme needs a dummy file to read signatures from a detached sig file
380  //verification will fail but we get all signatures
381  zypp::filesystem::TmpFile dummyFile;
382 
383  bool verified = false;
384  return _pimpl->verifyAndReadSignaturesFprs(dummyFile.path(), signature, verified);
385 }
386 
387 }
static Ptr createForOpenPGP()
Creates a new KeyManagerCtx for PGP.
Definition: KeyManager.cc:153
std::list< PublicKeyData > readKeyFromFile(const Pathname &file)
Returns a list of all.
Definition: KeyManager.cc:230
GpgmeErr(gpgme_error_t err_r=GPG_ERR_NO_ERROR)
Definition: KeyManager.cc:39
std::list< std::string > verifyAndReadSignaturesFprs(const Pathname &file, const Pathname &signature, bool &verifed)
Definition: KeyManager.cc:85
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:251
Provide a new empty temporary file and delete it when no longer needed.
Definition: TmpPath.h:127
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:261
RW_pointer< Impl > _pimpl
Pointer to implementation.
Definition: KeyManager.h:69
Pathname homedir() const
Definition: KeyManager.cc:200
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:326
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 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:349
std::list< PublicKeyData > listKeys()
Returns a list of all public keys found in the current keyring.
Definition: KeyManager.cc:209
bool setHomedir(const Pathname &keyring_r)
Changes the keyring directory.
Definition: KeyManager.cc:178
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:377
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