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 ) {
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) );
166 if ( sig->status != GPG_ERR_NO_ERROR )
168 if ( gpgme_err_code(sig->status) != GPG_ERR_KEY_EXPIRED )
170 if ( !foundBadSignature )
171 foundBadSignature = true;
173 WAR << "Failed signature check: " << file_r << " " << GpgmeErr(sig->status) << endl;
178 WAR << "Legacy: Ignore expired key: " << file_r << " " << GpgmeErr(sig->status) << endl;
184 *verify_r = (!foundBadSignature);
188 KeyManagerCtx::Impl::~Impl()
193 KeyManagerCtx::Ptr KeyManagerCtx::createForOpenPGP()
195 //make sure gpgpme is initialized
196 boost::call_once(gpgme_init_once, initGpgme);
199 GpgmeErr err = gpgme_new(&ctx);
200 if (err != GPG_ERR_NO_ERROR) {
202 return shared_ptr<KeyManagerCtx>();
206 err = gpgme_set_protocol(ctx, GPGME_PROTOCOL_OpenPGP);
207 if (err != GPG_ERR_NO_ERROR) {
210 return shared_ptr<KeyManagerCtx>();
213 shared_ptr<KeyManagerCtx> me( new KeyManagerCtx());
214 me->_pimpl->_ctx = ctx;
218 bool KeyManagerCtx::setHomedir(const Pathname &keyring_r)
221 /* get engine information to read current state*/
222 gpgme_engine_info_t enginfo = gpgme_ctx_get_engine_info(_pimpl->_ctx);
226 GpgmeErr err = gpgme_ctx_set_engine_info(
228 GPGME_PROTOCOL_OpenPGP,
232 if (err != GPG_ERR_NO_ERROR) {
233 ERR << "Unable to set homedir " << err << endl;
240 Pathname KeyManagerCtx::homedir() const
242 gpgme_engine_info_t enginfo = gpgme_ctx_get_engine_info(_pimpl->_ctx);
246 return Pathname(enginfo->home_dir);
249 std::list<PublicKeyData> KeyManagerCtx::listKeys()
251 std::list<PublicKeyData> keys;
253 GpgmeErr err = GPG_ERR_NO_ERROR;
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);
259 while (!(err = gpgme_op_keylist_next(_pimpl->_ctx, &key))) {
260 PublicKeyData data = PublicKeyData::fromGpgmeKey(key);
262 keys.push_back(data);
264 gpgme_key_release(key);
266 gpgme_op_keylist_end(_pimpl->_ctx);
270 std::list<PublicKeyData> KeyManagerCtx::readKeyFromFile(const Pathname &file)
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();
277 zypp::filesystem::TmpDir tmpKeyring;
278 if (!setHomedir(tmpKeyring.path()))
279 return std::list<PublicKeyData>();
281 if (!importKey(file)) {
282 setHomedir(realHomedir);
283 return std::list<PublicKeyData>();
286 std::list<PublicKeyData> keys = listKeys();
287 setHomedir(realHomedir);
291 bool KeyManagerCtx::verify(const Pathname &file, const Pathname &signature)
293 if ( !PathInfo( file ).isExist() || !PathInfo( signature ).isExist() )
296 return _pimpl->verifySignaturesFprs(file, signature);
299 bool KeyManagerCtx::exportKey(const std::string &id, std::ostream &stream)
301 GpgmeErr err = GPG_ERR_NO_ERROR;
303 GpgmeKeyPtr foundKey;
305 //search for requested key id
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);
313 gpgme_key_release(key);
315 gpgme_op_keylist_end(_pimpl->_ctx);
318 WAR << "Key " << id << "not found" << endl;
322 //function needs a array of keys to export
323 gpgme_key_t keyarray[2];
324 keyarray[0] = foundKey.get();
327 GpgmeDataPtr out(nullptr, gpgme_data_release);
328 err = gpgme_data_new (&out.get());
334 //format as ascii armored
335 gpgme_set_armor (_pimpl->_ctx, 1);
336 err = gpgme_op_export_keys (_pimpl->_ctx, keyarray, 0, out.get());
338 int ret = gpgme_data_seek (out.get(), 0, SEEK_SET);
340 ERR << "Unable to seek in exported key data" << endl;
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);
350 //failed to read from buffer
352 ERR << "Unable to read exported key data" << endl;
356 ERR << "Error exporting key: "<< err << endl;
360 //if we reach this point export was successful
364 bool KeyManagerCtx::importKey(const Pathname &keyfile)
366 if ( !PathInfo( keyfile ).isExist() ) {
367 ERR << "Keyfile '" << keyfile << "' does not exist.";
371 GpgmeDataPtr data(nullptr, gpgme_data_release);
374 err = gpgme_data_new_from_file(&data.get(), keyfile.c_str(), 1);
376 ERR << "Error importing key: "<< err << endl;
380 err = gpgme_op_import(_pimpl->_ctx, data.get());
382 ERR << "Error importing key: "<< err << endl;
384 return (err == GPG_ERR_NO_ERROR);
387 bool KeyManagerCtx::deleteKey(const std::string &id)
390 GpgmeErr err = GPG_ERR_NO_ERROR;
392 gpgme_op_keylist_start(_pimpl->_ctx, NULL, 0);
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);
398 gpgme_key_release(key);
399 gpgme_op_keylist_end(_pimpl->_ctx);
402 ERR << "Error deleting key: "<< err << endl;
407 gpgme_key_release(key);
410 gpgme_op_keylist_end(_pimpl->_ctx);
411 WAR << "Key: '"<< id << "' not found." << endl;
415 std::list<std::string> KeyManagerCtx::readSignatureFingerprints(const Pathname &signature)
416 { return _pimpl->readSignaturesFprs(signature); }