| |
\---------------------------------------------------------------------*/
#include "zypp/KeyManager.h"
-#include "zypp/PublicKey.h"
+#include "zypp/KeyRing.h"
#include "zypp/PathInfo.h"
#include "zypp/base/Logger.h"
#include "zypp/TmpPath.h"
#include "zypp/base/String.h"
+#include "zypp/AutoDispose.h"
#include <boost/thread/once.hpp>
#include <boost/interprocess/smart_ptr/scoped_ptr.hpp>
// threads are introduced into libzypp
boost::once_flag gpgme_init_once = BOOST_ONCE_INIT;
+ void initGpgme ()
+ {
+ const char *version = gpgme_check_version(NULL);
+ if ( version )
+ {
+ MIL << "Initialized libgpgme version: " << version << endl;
+ }
+ else
+ {
+ MIL << "Initialized libgpgme with unknown version" << endl;
+ }
+ }
+
//using boost::interprocess pointer because it allows a custom deleter
typedef boost::interprocess::scoped_ptr<gpgme_data, boost::function<void (gpgme_data_t)>> GpgmeDataPtr;
typedef boost::interprocess::scoped_ptr<_gpgme_key, boost::function<void (gpgme_key_t)>> GpgmeKeyPtr;
std::ostream & operator<<( std::ostream & str, const GpgmeErr & obj )
{ return str << "<" << gpgme_strsource(obj) << "> " << gpgme_strerror(obj); }
- void initGpgme ()
- {
- const char *version = gpgme_check_version(NULL);
- if ( version )
- {
- MIL << "Initialized libgpgme version: " << version << endl;
- }
- else
- {
- MIL << "Initialized libgpgme with unknown version" << endl;
- }
- }
-
/** \relates gpgme_import_result_t Stream output. */
std::ostream & operator<<( std::ostream & str, const _gpgme_op_import_result & obj )
{
} // namespace
///////////////////////////////////////////////////////////////////
-class KeyManagerCtx::Impl
-{
-public:
- Impl();
- ~Impl();
-
- /** Return all fingerprints found in \a signature_r. */
- std::list<std::string> readSignaturesFprs( const Pathname & signature_r )
- { return readSignaturesFprsOptVerify( signature_r ); }
+ struct GpgmeException : public KeyRingException
+ {
+ GpgmeException( const std::string & in_r, const GpgmeErr & err_r )
+ : KeyRingException( str::Format( "libgpgme error in '%1%': %2%" ) % in_r % err_r )
+ {}
+ };
- /** Tries to verify the \a file_r using \a signature_r. */
- bool verifySignaturesFprs( const Pathname & file_r, const Pathname & signature_r )
+ class KeyManagerCtx::Impl
{
- bool verify = false;
- readSignaturesFprsOptVerify( signature_r, file_r, &verify );
- return verify;
- }
+ public:
+ Impl()
+ { boost::call_once( gpgme_init_once, initGpgme ); }
- gpgme_ctx_t _ctx;
+ ~Impl()
+ { if ( _ctx ) gpgme_release( _ctx ); }
-private:
- /** Return all fingerprints found in \a signature_r and optionally verify the \a file_r on the fly.
- *
- * If \a verify_r is not a \c nullptr, log verification errors and return
- * whether all signatures are good.
- */
- std::list<std::string> readSignaturesFprsOptVerify( const Pathname & signature_r, const Pathname & file_r = "/dev/null", bool * verify_r = nullptr );
-};
-KeyManagerCtx::Impl::Impl()
-{ }
+ /** Return all fingerprints found in \a signature_r. */
+ std::list<std::string> readSignaturesFprs( const Pathname & signature_r )
+ { return readSignaturesFprsOptVerify( signature_r ); }
+ /** Tries to verify the \a file_r using \a signature_r. */
+ bool verifySignaturesFprs( const Pathname & file_r, const Pathname & signature_r )
+ {
+ bool verify = false;
+ readSignaturesFprsOptVerify( signature_r, file_r, &verify );
+ return verify;
+ }
-KeyManagerCtx::KeyManagerCtx()
- : _pimpl( new Impl )
-{
+ gpgme_ctx_t _ctx { nullptr };
+ bool _volatile { false }; ///< readKeyFromFile workaround bsc#1140670
-}
+ private:
+ /** Return all fingerprints found in \a signature_r and optionally verify the \a file_r on the fly.
+ *
+ * If \a verify_r is not a \c nullptr, log verification errors and return
+ * whether all signatures are good.
+ */
+ std::list<std::string> readSignaturesFprsOptVerify( const Pathname & signature_r, const Pathname & file_r = "/dev/null", bool * verify_r = nullptr );
+ };
std::list<std::string> KeyManagerCtx::Impl::readSignaturesFprsOptVerify( const Pathname & signature_r, const Pathname & file_r, bool * verify_r )
{
return signatures;
}
-KeyManagerCtx::Impl::~Impl()
-{
- gpgme_release(_ctx);
-}
-KeyManagerCtx::Ptr KeyManagerCtx::createForOpenPGP()
+KeyManagerCtx::KeyManagerCtx()
+: _pimpl( new Impl )
+{}
+
+KeyManagerCtx KeyManagerCtx::createForOpenPGP()
{
- //make sure gpgpme is initialized
- boost::call_once(gpgme_init_once, initGpgme);
+ static Pathname tmppath( zypp::myTmpDir() / "PublicKey" );
+ filesystem::assert_dir( tmppath );
- gpgme_ctx_t ctx;
- GpgmeErr err = gpgme_new(&ctx);
- if (err != GPG_ERR_NO_ERROR) {
- ERR << err << endl;
- return shared_ptr<KeyManagerCtx>();
- }
+ KeyManagerCtx ret { createForOpenPGP( tmppath ) };
+ ret._pimpl->_volatile = true; // readKeyFromFile workaround bsc#1140670
+ return ret;
+}
- //use OpenPGP
- err = gpgme_set_protocol(ctx, GPGME_PROTOCOL_OpenPGP);
- if (err != GPG_ERR_NO_ERROR) {
- ERR << err << endl;
- gpgme_release(ctx);
- return shared_ptr<KeyManagerCtx>();
+KeyManagerCtx KeyManagerCtx::createForOpenPGP( const Pathname & keyring_r )
+{
+ DBG << "createForOpenPGP(" << keyring_r << ")" << endl;
+
+ KeyManagerCtx ret;
+ gpgme_ctx_t & ctx { ret._pimpl->_ctx };
+
+ // create the context
+ GpgmeErr err = gpgme_new( &ctx );
+ if ( err != GPG_ERR_NO_ERROR )
+ ZYPP_THROW( GpgmeException( "gpgme_new", err ) );
+
+ // use OpenPGP
+ err = gpgme_set_protocol( ctx, GPGME_PROTOCOL_OpenPGP );
+ if ( err != GPG_ERR_NO_ERROR )
+ ZYPP_THROW( GpgmeException( "gpgme_set_protocol", err ) );
+
+ if ( !keyring_r.empty() ) {
+ // get engine information to read current state
+ gpgme_engine_info_t enginfo = gpgme_ctx_get_engine_info( ctx );
+ if ( !enginfo )
+ ZYPP_THROW( GpgmeException( "gpgme_ctx_get_engine_info", err ) );
+
+ err = gpgme_ctx_set_engine_info( ctx, GPGME_PROTOCOL_OpenPGP, enginfo->file_name, keyring_r.c_str() );
+ if ( err != GPG_ERR_NO_ERROR )
+ ZYPP_THROW( GpgmeException( "gpgme_ctx_set_engine_info", err ) );
}
- shared_ptr<KeyManagerCtx> me( new KeyManagerCtx());
- me->_pimpl->_ctx = ctx;
- return me;
+ return ret;
}
-bool KeyManagerCtx::setHomedir(const Pathname &keyring_r)
+Pathname KeyManagerCtx::homedir() const
{
+ Pathname ret;
+ if ( gpgme_engine_info_t enginfo = gpgme_ctx_get_engine_info( _pimpl->_ctx ) )
+ ret = enginfo->home_dir;
+ return ret;
+}
- /* get engine information to read current state*/
- gpgme_engine_info_t enginfo = gpgme_ctx_get_engine_info(_pimpl->_ctx);
- if (!enginfo)
- return false;
+std::list<PublicKeyData> KeyManagerCtx::listKeys()
+{
+ std::list<PublicKeyData> ret;
+ GpgmeErr err = GPG_ERR_NO_ERROR;
- GpgmeErr err = gpgme_ctx_set_engine_info(
- _pimpl->_ctx,
- GPGME_PROTOCOL_OpenPGP,
- enginfo->file_name,
- keyring_r.c_str());
+ // Reset gpgme_keylist_mode on return!
+ AutoDispose<gpgme_keylist_mode_t> guard { gpgme_get_keylist_mode( _pimpl->_ctx ), bind( &gpgme_set_keylist_mode, _pimpl->_ctx, _1 ) };
+ // Let listed keys include signatures (required if PublicKeyData are created from the key)
+ if ( (err = gpgme_set_keylist_mode( _pimpl->_ctx, GPGME_KEYLIST_MODE_LOCAL | GPGME_KEYLIST_MODE_SIGS )) != GPG_ERR_NO_ERROR ) {
+ ERR << "gpgme_set_keylist_mode: " << err << endl;
+ return ret;
+ }
- if (err != GPG_ERR_NO_ERROR) {
- ERR << "Unable to set homedir " << err << endl;
- return false;
+ if ( (err = gpgme_op_keylist_start( _pimpl->_ctx, NULL, 0 )) != GPG_ERR_NO_ERROR ) {
+ ERR << "gpgme_op_keylist_start: " << err << endl;
+ return ret;
+ }
+ // Close list operation on return!
+ AutoDispose<gpgme_ctx_t> guard2 { _pimpl->_ctx, &gpgme_op_keylist_end };
+
+ AutoDispose<gpgme_key_t> key { nullptr, &gpgme_key_release };
+ for ( ; gpgme_op_keylist_next( _pimpl->_ctx, &(*key) ) == GPG_ERR_NO_ERROR; key.getDispose()( key ) ) {
+ PublicKeyData data { PublicKeyData::fromGpgmeKey( key ) };
+ if ( data )
+ ret.push_back( data );
}
- return true;
+ return ret;
}
-Pathname KeyManagerCtx::homedir() const
+#if 1
+std::list<PublicKeyData> KeyManagerCtx::readKeyFromFile( const Pathname & keyfile_r )
{
- gpgme_engine_info_t enginfo = gpgme_ctx_get_engine_info(_pimpl->_ctx);
- if (!enginfo)
- return Pathname();
+ // bsc#1140670: GPGME does not support reading keys from a keyfile using
+ // gpgme_data_t and gpgme_op_keylist_from_data_start. Despite GPGME_KEYLIST_MODE_SIGS
+ // the signatures are missing, but we need them to create proper PublicKeyData objects.
+ // While this is not resolved, we read into a temp. keyring. Impl::_volatile helps
+ // to detect whether we can clear and import into the current context or need to
+ // create a temp. one.
+ std::list<PublicKeyData> ret;
+
+ if ( _pimpl->_volatile ) {
+ // in a volatile context we can simple clear the keyring...
+ filesystem::clean_dir( homedir() );
+ if ( importKey( keyfile_r ) )
+ ret = listKeys();
+ } else {
+ // read in a volatile context
+ ret = createForOpenPGP().readKeyFromFile( keyfile_r );
+ }
- return Pathname(enginfo->home_dir);
+ return ret;
}
-
-std::list<PublicKeyData> KeyManagerCtx::listKeys()
+#else
+std::list<PublicKeyData> KeyManagerCtx::readKeyFromFile( const Pathname & file_r )
{
- std::list<PublicKeyData> keys;
- gpgme_key_t key;
+ std::list<PublicKeyData> ret;
GpgmeErr err = GPG_ERR_NO_ERROR;
- gpgme_keylist_mode_t mode = GPGME_KEYLIST_MODE_LOCAL | GPGME_KEYLIST_MODE_SIGS;
- gpgme_set_keylist_mode (_pimpl->_ctx, mode);
- gpgme_op_keylist_start (_pimpl->_ctx, NULL, 0);
+ AutoDispose<gpgme_data_t> data { nullptr, &gpgme_data_release };
+ if ( (err = gpgme_data_new_from_file( &(*data), file_r.c_str(), 1 )) != GPG_ERR_NO_ERROR ) {
+ ERR << "gpgme_data_new_from_file " << file_r << ": " << err << endl;
+ return ret;
+ }
- while (!(err = gpgme_op_keylist_next(_pimpl->_ctx, &key))) {
- PublicKeyData data = PublicKeyData::fromGpgmeKey(key);
- if (data) {
- keys.push_back(data);
- }
- gpgme_key_release(key);
+ // Reset gpgme_keylist_mode on return!
+ AutoDispose<gpgme_keylist_mode_t> guard { gpgme_get_keylist_mode( _pimpl->_ctx ), bind( &gpgme_set_keylist_mode, _pimpl->_ctx, _1 ) };
+ // Let listed keys include signatures (required if PublicKeyData are created from the key)
+ if ( (err = gpgme_set_keylist_mode( _pimpl->_ctx, GPGME_KEYLIST_MODE_LOCAL | GPGME_KEYLIST_MODE_SIGS )) != GPG_ERR_NO_ERROR ) {
+ ERR << "gpgme_set_keylist_mode: " << err << endl;
+ return ret;
}
- gpgme_op_keylist_end(_pimpl->_ctx);
- return keys;
-}
-std::list<PublicKeyData> KeyManagerCtx::readKeyFromFile(const Pathname &file)
-{
- //seems GPGME does not support reading keys from a keyfile using
- //gpgme_data_t and gpgme_op_keylist_from_data_start, this always
- //return unsupported errors. However importing and listing the key works.
- zypp::Pathname realHomedir = homedir();
-
- zypp::filesystem::TmpDir tmpKeyring;
- if (!setHomedir(tmpKeyring.path()))
- return std::list<PublicKeyData>();
-
- if (!importKey(file)) {
- setHomedir(realHomedir);
- return std::list<PublicKeyData>();
+ if ( (err = gpgme_op_keylist_from_data_start( _pimpl->_ctx, data, 0 )) != GPG_ERR_NO_ERROR ) {
+ ERR << "gpgme_op_keylist_from_data_start " << file_r << ": " << err << endl;
+ return ret;
+ }
+ // Close list operation on return!
+ AutoDispose<gpgme_ctx_t> guard2 { _pimpl->_ctx, &gpgme_op_keylist_end };
+
+ AutoDispose<gpgme_key_t> key { nullptr, &gpgme_key_release };
+ for ( ; gpgme_op_keylist_next( _pimpl->_ctx, &(*key) ) == GPG_ERR_NO_ERROR; key.getDispose()( key ) ) {
+ PublicKeyData data { PublicKeyData::fromGpgmeKey( key ) };
+ if ( data )
+ ret.push_back( data );
}
- std::list<PublicKeyData> keys = listKeys();
- setHomedir(realHomedir);
- return keys;
+ return ret;
}
+#endif
bool KeyManagerCtx::verify(const Pathname &file, const Pathname &signature)
{
- if ( !PathInfo( file ).isExist() || !PathInfo( signature ).isExist() )
- return false;
-
return _pimpl->verifySignaturesFprs(file, signature);
}