Imported Upstream version 17.2.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/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
58 class KeyManagerCtx::Impl
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
69 KeyManagerCtx::Impl::Impl()
70 { }
71
72
73 KeyManagerCtx::KeyManagerCtx()
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
148 KeyManagerCtx::Impl::~Impl()
149 {
150   gpgme_release(_ctx);
151 }
152
153 KeyManagerCtx::Ptr KeyManagerCtx::createForOpenPGP()
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
200 Pathname KeyManagerCtx::homedir() const
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))) {
220     PublicKeyData data = PublicKeyData::fromGpgmeKey(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 }