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 const char *version = gpgme_check_version(NULL);
54 MIL << "Initialized libgpgme version: " << version << endl;
58 MIL << "Initialized libgpgme with unknown version" << endl;
65 class KeyManagerCtx::Impl
71 /** Return all fingerprints found in \a signature_r. */
72 std::list<std::string> readSignaturesFprs( const Pathname & signature_r )
73 { return readSignaturesFprsOptVerify( signature_r ); }
75 /** Tries to verify the \a file_r using \a signature_r. */
76 bool verifySignaturesFprs( const Pathname & file_r, const Pathname & signature_r )
79 readSignaturesFprsOptVerify( signature_r, file_r, &verify );
86 /** Return all fingerprints found in \a signature_r and optionally verify the \a file_r on the fly.
88 * If \a verify_r is not a \c nullptr, log verification errors and return
89 * whether all signatures are good.
91 std::list<std::string> readSignaturesFprsOptVerify( const Pathname & signature_r, const Pathname & file_r = "/dev/null", bool * verify_r = nullptr );
94 KeyManagerCtx::Impl::Impl()
98 KeyManagerCtx::KeyManagerCtx()
104 std::list<std::string> KeyManagerCtx::Impl::readSignaturesFprsOptVerify( const Pathname & signature_r, const Pathname & file_r, bool * verify_r )
106 //lets be pessimistic
111 if (!PathInfo( signature_r ).isExist())
112 return std::list<std::string>();
114 FILEPtr dataFile(fopen(file_r.c_str(), "rb"), fclose);
116 return std::list<std::string>();
118 GpgmeDataPtr fileData(nullptr, gpgme_data_release);
119 GpgmeErr err = gpgme_data_new_from_stream (&fileData.get(), dataFile.get());
122 return std::list<std::string>();
125 FILEPtr sigFile(fopen(signature_r.c_str(), "rb"), fclose);
127 ERR << "Unable to open signature file '" << signature_r << "'" <<endl;
128 return std::list<std::string>();
131 GpgmeDataPtr sigData(nullptr, gpgme_data_release);
132 err = gpgme_data_new_from_stream (&sigData.get(), sigFile.get());
135 return std::list<std::string>();
138 err = gpgme_op_verify(_ctx, sigData.get(), fileData.get(), NULL);
139 if (err != GPG_ERR_NO_ERROR) {
141 return std::list<std::string>();
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>();
150 bool foundBadSignature = false;
151 std::list<std::string> signatures;
152 for ( gpgme_signature_t sig = res->signatures; sig; sig = sig->next ) {
155 signatures.push_back(str::asString(sig->fpr));
157 if ( sig->status != GPG_ERR_NO_ERROR )
159 if ( gpgme_err_code(sig->status) != GPG_ERR_KEY_EXPIRED )
161 if ( !foundBadSignature )
162 foundBadSignature = true;
164 WAR << "Failed signature check: " << file_r << " " << GpgmeErr(sig->status) << endl;
169 WAR << "Legacy: Ignore expired key: " << file_r << " " << GpgmeErr(sig->status) << endl;
175 *verify_r = (!foundBadSignature);
179 KeyManagerCtx::Impl::~Impl()
184 KeyManagerCtx::Ptr KeyManagerCtx::createForOpenPGP()
186 //make sure gpgpme is initialized
187 boost::call_once(gpgme_init_once, initGpgme);
190 GpgmeErr err = gpgme_new(&ctx);
191 if (err != GPG_ERR_NO_ERROR) {
193 return shared_ptr<KeyManagerCtx>();
197 err = gpgme_set_protocol(ctx, GPGME_PROTOCOL_OpenPGP);
198 if (err != GPG_ERR_NO_ERROR) {
201 return shared_ptr<KeyManagerCtx>();
204 shared_ptr<KeyManagerCtx> me( new KeyManagerCtx());
205 me->_pimpl->_ctx = ctx;
209 bool KeyManagerCtx::setHomedir(const Pathname &keyring_r)
212 /* get engine information to read current state*/
213 gpgme_engine_info_t enginfo = gpgme_ctx_get_engine_info(_pimpl->_ctx);
217 GpgmeErr err = gpgme_ctx_set_engine_info(
219 GPGME_PROTOCOL_OpenPGP,
223 if (err != GPG_ERR_NO_ERROR) {
224 ERR << "Unable to set homedir " << err << endl;
231 Pathname KeyManagerCtx::homedir() const
233 gpgme_engine_info_t enginfo = gpgme_ctx_get_engine_info(_pimpl->_ctx);
237 return Pathname(enginfo->home_dir);
240 std::list<PublicKeyData> KeyManagerCtx::listKeys()
242 std::list<PublicKeyData> keys;
244 GpgmeErr err = GPG_ERR_NO_ERROR;
246 gpgme_keylist_mode_t mode = GPGME_KEYLIST_MODE_LOCAL | GPGME_KEYLIST_MODE_SIGS;
247 gpgme_set_keylist_mode (_pimpl->_ctx, mode);
248 gpgme_op_keylist_start (_pimpl->_ctx, NULL, 0);
250 while (!(err = gpgme_op_keylist_next(_pimpl->_ctx, &key))) {
251 PublicKeyData data = PublicKeyData::fromGpgmeKey(key);
253 keys.push_back(data);
255 gpgme_key_release(key);
257 gpgme_op_keylist_end(_pimpl->_ctx);
261 std::list<PublicKeyData> KeyManagerCtx::readKeyFromFile(const Pathname &file)
263 //seems GPGME does not support reading keys from a keyfile using
264 //gpgme_data_t and gpgme_op_keylist_from_data_start, this always
265 //return unsupported errors. However importing and listing the key works.
266 zypp::Pathname realHomedir = homedir();
268 zypp::filesystem::TmpDir tmpKeyring;
269 if (!setHomedir(tmpKeyring.path()))
270 return std::list<PublicKeyData>();
272 if (!importKey(file)) {
273 setHomedir(realHomedir);
274 return std::list<PublicKeyData>();
277 std::list<PublicKeyData> keys = listKeys();
278 setHomedir(realHomedir);
282 bool KeyManagerCtx::verify(const Pathname &file, const Pathname &signature)
284 if ( !PathInfo( file ).isExist() || !PathInfo( signature ).isExist() )
287 return _pimpl->verifySignaturesFprs(file, signature);
290 bool KeyManagerCtx::exportKey(const std::string &id, std::ostream &stream)
292 GpgmeErr err = GPG_ERR_NO_ERROR;
294 GpgmeKeyPtr foundKey;
296 //search for requested key id
298 gpgme_op_keylist_start(_pimpl->_ctx, NULL, 0);
299 while (!(err = gpgme_op_keylist_next(_pimpl->_ctx, &key))) {
300 if (key->subkeys && id == str::asString(key->subkeys->keyid)) {
301 GpgmeKeyPtr(key, gpgme_key_release).swap(foundKey);
304 gpgme_key_release(key);
306 gpgme_op_keylist_end(_pimpl->_ctx);
309 WAR << "Key " << id << "not found" << endl;
313 //function needs a array of keys to export
314 gpgme_key_t keyarray[2];
315 keyarray[0] = foundKey.get();
318 GpgmeDataPtr out(nullptr, gpgme_data_release);
319 err = gpgme_data_new (&out.get());
325 //format as ascii armored
326 gpgme_set_armor (_pimpl->_ctx, 1);
327 err = gpgme_op_export_keys (_pimpl->_ctx, keyarray, 0, out.get());
329 int ret = gpgme_data_seek (out.get(), 0, SEEK_SET);
331 ERR << "Unable to seek in exported key data" << endl;
335 const int bufsize = 512;
336 char buf[bufsize + 1];
337 while ((ret = gpgme_data_read(out.get(), buf, bufsize)) > 0) {
338 stream.write(buf, ret);
341 //failed to read from buffer
343 ERR << "Unable to read exported key data" << endl;
347 ERR << "Error exporting key: "<< err << endl;
351 //if we reach this point export was successful
355 bool KeyManagerCtx::importKey(const Pathname &keyfile)
357 if ( !PathInfo( keyfile ).isExist() ) {
358 ERR << "Keyfile '" << keyfile << "' does not exist.";
362 GpgmeDataPtr data(nullptr, gpgme_data_release);
365 err = gpgme_data_new_from_file(&data.get(), keyfile.c_str(), 1);
367 ERR << "Error importing key: "<< err << endl;
371 err = gpgme_op_import(_pimpl->_ctx, data.get());
373 ERR << "Error importing key: "<< err << endl;
375 return (err == GPG_ERR_NO_ERROR);
378 bool KeyManagerCtx::deleteKey(const std::string &id)
381 GpgmeErr err = GPG_ERR_NO_ERROR;
383 gpgme_op_keylist_start(_pimpl->_ctx, NULL, 0);
385 while (!(err = gpgme_op_keylist_next(_pimpl->_ctx, &key))) {
386 if (key->subkeys && id == str::asString(key->subkeys->keyid)) {
387 err = gpgme_op_delete(_pimpl->_ctx, key, 0);
389 gpgme_key_release(key);
390 gpgme_op_keylist_end(_pimpl->_ctx);
393 ERR << "Error deleting key: "<< err << endl;
398 gpgme_key_release(key);
401 gpgme_op_keylist_end(_pimpl->_ctx);
402 WAR << "Key: '"<< id << "' not found." << endl;
406 std::list<std::string> KeyManagerCtx::readSignatureFingerprints(const Pathname &signature)
407 { return _pimpl->readSignaturesFprs(signature); }