1 /*---------------------------------------------------------------------\
3 | |__ / \ / / . \ . \ |
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"
16 #include <boost/thread/once.hpp>
17 #include <boost/interprocess/smart_ptr/scoped_ptr.hpp>
22 #undef ZYPP_BASE_LOGGER_LOGGROUP
23 #define ZYPP_BASE_LOGGER_LOGGROUP "zypp::gpg"
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;
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;
39 GpgmeErr( gpgme_error_t err_r = GPG_ERR_NO_ERROR )
42 operator gpgme_error_t() const { return _err; }
46 std::ostream & operator<<( std::ostream & str, const GpgmeErr & obj )
47 { return str << "<" << gpgme_strsource(obj) << "> " << gpgme_strerror(obj); }
49 static void initGpgme ()
51 gpgme_check_version(NULL);
58 class KeyManagerCtx::Impl
64 std::list<std::string> verifyAndReadSignaturesFprs(const Pathname & file, const Pathname & signature, bool &verifed);
69 KeyManagerCtx::Impl::Impl()
73 KeyManagerCtx::KeyManagerCtx()
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
85 std::list<std::string> KeyManagerCtx::Impl::verifyAndReadSignaturesFprs(const Pathname &file, const Pathname &signature, bool &verifed)
90 if (!PathInfo( signature ).isExist())
91 return std::list<std::string>();
93 FILEPtr dataFile(fopen(file.c_str(), "rb"), fclose);
95 return std::list<std::string>();
97 GpgmeDataPtr fileData(nullptr, gpgme_data_release);
98 GpgmeErr err = gpgme_data_new_from_stream (&fileData.get(), dataFile.get());
101 return std::list<std::string>();
104 FILEPtr sigFile(fopen(signature.c_str(), "rb"), fclose);
106 ERR << "Unable to open signature file '" << signature << "'" <<endl;
107 return std::list<std::string>();
110 GpgmeDataPtr sigData(nullptr, gpgme_data_release);
111 err = gpgme_data_new_from_stream (&sigData.get(), sigFile.get());
114 return std::list<std::string>();
117 err = gpgme_op_verify(_ctx, sigData.get(), fileData.get(), NULL);
118 if (err != GPG_ERR_NO_ERROR) {
120 return std::list<std::string>();
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>();
129 bool foundBadSignature = false;
130 std::list<std::string> signatures;
131 gpgme_signature_t sig = res->signatures;
134 if (!foundBadSignature)
135 foundBadSignature = (sig->status != GPG_ERR_NO_ERROR);
140 signatures.push_back(str::asString(sig->fpr));
144 verifed = (!foundBadSignature);
148 KeyManagerCtx::Impl::~Impl()
153 KeyManagerCtx::Ptr KeyManagerCtx::createForOpenPGP()
155 //make sure gpgpme is initialized
156 boost::call_once(gpgme_init_once, initGpgme);
159 GpgmeErr err = gpgme_new(&ctx);
160 if (err != GPG_ERR_NO_ERROR) {
162 return shared_ptr<KeyManagerCtx>();
166 err = gpgme_set_protocol(ctx, GPGME_PROTOCOL_OpenPGP);
167 if (err != GPG_ERR_NO_ERROR) {
170 return shared_ptr<KeyManagerCtx>();
173 shared_ptr<KeyManagerCtx> me( new KeyManagerCtx());
174 me->_pimpl->_ctx = ctx;
178 bool KeyManagerCtx::setHomedir(const Pathname &keyring_r)
181 /* get engine information to read current state*/
182 gpgme_engine_info_t enginfo = gpgme_ctx_get_engine_info(_pimpl->_ctx);
186 GpgmeErr err = gpgme_ctx_set_engine_info(
188 GPGME_PROTOCOL_OpenPGP,
192 if (err != GPG_ERR_NO_ERROR) {
193 ERR << "Unable to set homedir " << err << endl;
200 Pathname KeyManagerCtx::homedir() const
202 gpgme_engine_info_t enginfo = gpgme_ctx_get_engine_info(_pimpl->_ctx);
206 return Pathname(enginfo->home_dir);
209 std::list<PublicKeyData> KeyManagerCtx::listKeys()
211 std::list<PublicKeyData> keys;
213 GpgmeErr err = GPG_ERR_NO_ERROR;
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);
219 while (!(err = gpgme_op_keylist_next(_pimpl->_ctx, &key))) {
220 PublicKeyData data = PublicKeyData::fromGpgmeKey(key);
222 keys.push_back(data);
224 gpgme_key_release(key);
226 gpgme_op_keylist_end(_pimpl->_ctx);
230 std::list<PublicKeyData> KeyManagerCtx::readKeyFromFile(const Pathname &file)
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();
237 zypp::filesystem::TmpDir tmpKeyring;
238 if (!setHomedir(tmpKeyring.path()))
239 return std::list<PublicKeyData>();
241 if (!importKey(file)) {
242 setHomedir(realHomedir);
243 return std::list<PublicKeyData>();
246 std::list<PublicKeyData> keys = listKeys();
247 setHomedir(realHomedir);
251 bool KeyManagerCtx::verify(const Pathname &file, const Pathname &signature)
253 if ( !PathInfo( file ).isExist() || !PathInfo( signature ).isExist() )
256 bool verified = false;
257 _pimpl->verifyAndReadSignaturesFprs(file, signature, verified);
261 bool KeyManagerCtx::exportKey(const std::string &id, std::ostream &stream)
263 GpgmeErr err = GPG_ERR_NO_ERROR;
265 GpgmeKeyPtr foundKey;
267 //search for requested key id
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);
275 gpgme_key_release(key);
277 gpgme_op_keylist_end(_pimpl->_ctx);
280 WAR << "Key " << id << "not found" << endl;
284 //function needs a array of keys to export
285 gpgme_key_t keyarray[2];
286 keyarray[0] = foundKey.get();
289 GpgmeDataPtr out(nullptr, gpgme_data_release);
290 err = gpgme_data_new (&out.get());
296 //format as ascii armored
297 gpgme_set_armor (_pimpl->_ctx, 1);
298 err = gpgme_op_export_keys (_pimpl->_ctx, keyarray, 0, out.get());
300 int ret = gpgme_data_seek (out.get(), 0, SEEK_SET);
302 ERR << "Unable to seek in exported key data" << endl;
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);
312 //failed to read from buffer
314 ERR << "Unable to read exported key data" << endl;
318 ERR << "Error exporting key: "<< err << endl;
322 //if we reach this point export was successful
326 bool KeyManagerCtx::importKey(const Pathname &keyfile)
328 if ( !PathInfo( keyfile ).isExist() ) {
329 ERR << "Keyfile '" << keyfile << "' does not exist.";
333 GpgmeDataPtr data(nullptr, gpgme_data_release);
336 err = gpgme_data_new_from_file(&data.get(), keyfile.c_str(), 1);
338 ERR << "Error importing key: "<< err << endl;
342 err = gpgme_op_import(_pimpl->_ctx, data.get());
344 ERR << "Error importing key: "<< err << endl;
346 return (err == GPG_ERR_NO_ERROR);
349 bool KeyManagerCtx::deleteKey(const std::string &id)
352 GpgmeErr err = GPG_ERR_NO_ERROR;
354 gpgme_op_keylist_start(_pimpl->_ctx, NULL, 0);
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);
360 gpgme_key_release(key);
361 gpgme_op_keylist_end(_pimpl->_ctx);
364 ERR << "Error deleting key: "<< err << endl;
369 gpgme_key_release(key);
372 gpgme_op_keylist_end(_pimpl->_ctx);
373 WAR << "Key: '"<< id << "' not found." << endl;
377 std::list<std::string> KeyManagerCtx::readSignatureFingerprints(const Pathname &signature)
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;
383 bool verified = false;
384 return _pimpl->verifyAndReadSignaturesFprs(dummyFile.path(), signature, verified);