23#include <boost/thread/once.hpp>
24#include <boost/interprocess/smart_ptr/scoped_ptr.hpp>
30#undef ZYPP_BASE_LOGGER_LOGGROUP
31#define ZYPP_BASE_LOGGER_LOGGROUP "zypp::gpg"
40 static const std::string gpgconfName {
"gpg.conf" };
46 boost::once_flag gpgme_init_once = BOOST_ONCE_INIT;
50 const char *version = gpgme_check_version(NULL);
53 MIL <<
"Initialized libgpgme version: " << version << endl;
57 MIL <<
"Initialized libgpgme with unknown version" << endl;
62 using GpgmeDataPtr = boost::interprocess::scoped_ptr<gpgme_data, boost::function<void (gpgme_data_t)>>;
63 using GpgmeKeyPtr = boost::interprocess::scoped_ptr<_gpgme_key, boost::function<void (gpgme_key_t)>>;
64 using FILEPtr = boost::interprocess::scoped_ptr<FILE, boost::function<int (FILE *)>>;
68 GpgmeErr( gpgme_error_t err_r = GPG_ERR_NO_ERROR )
71 operator gpgme_error_t()
const {
return _err; }
76 std::ostream &
operator<<( std::ostream &
str,
const GpgmeErr & obj )
77 {
return str <<
"<" << gpgme_strsource(obj) <<
"> " << gpgme_strerror(obj); }
79 bool findKeyById( gpgme_ctx_t ctx,
const std::string &
id, GpgmeKeyPtr & foundKey )
81 GpgmeErr err = GPG_ERR_NO_ERROR;
83 gpgme_key_t key =
nullptr;
84 gpgme_op_keylist_start( ctx, NULL, 0 );
85 while ( !( err = gpgme_op_keylist_next( ctx, &key ) ) ) {
86 if ( key->subkeys &&
id ==
str::asString( key->subkeys->keyid ) ) {
87 GpgmeKeyPtr( key, gpgme_key_release ).swap( foundKey );
90 gpgme_key_release( key );
92 gpgme_op_keylist_end( ctx );
94 return foundKey.get() !=
nullptr;
97 bool exportKeyData( gpgme_ctx_t ctx,
const std::string &
id, ByteArray & keydata )
99 GpgmeKeyPtr foundKey(
nullptr, gpgme_key_release );
100 if ( ! findKeyById( ctx,
id, foundKey ) ) {
101 WAR <<
"Key " <<
id <<
"not found" << endl;
105 gpgme_key_t keyarray[2];
106 keyarray[0] = foundKey.get();
109 GpgmeDataPtr out(
nullptr, gpgme_data_release );
110 GpgmeErr err = gpgme_data_new( &out.get() );
120 gpgme_set_armor( ctx, 1 );
121 err = gpgme_op_export_keys( ctx, keyarray, GPGME_EXPORT_MODE_MINIMAL, out.get() );
123 ERR <<
"Error exporting key: "<< err << endl;
127 int ret = gpgme_data_seek( out.get(), 0, SEEK_SET );
129 ERR <<
"Unable to seek in exported key data" << endl;
134 const int bufsize = 512;
136 while ( ( ret = gpgme_data_read( out.get(), buf, bufsize ) ) > 0 ) {
137 keydata.insert( keydata.end(), buf, buf + ret );
141 ERR <<
"Unable to read exported key data" << endl;
149 [[maybe_unused]] std::ostream &
operator<<( std::ostream &
str,
const _gpgme_op_import_result & obj )
151 str <<
"gpgme_op_import_result {" << endl;
152 str <<
" " << obj.considered <<
" The total number of considered keys." << endl;
153 str <<
" " << obj.no_user_id <<
" The number of keys without user ID." << endl;
154 str <<
" " << obj.imported <<
" The total number of imported keys." << endl;
155 str <<
" " << obj.imported_rsa <<
" imported RSA keys." << endl;
156 str <<
" " << obj.unchanged <<
" unchanged keys." << endl;
157 str <<
" " << obj.new_user_ids <<
" new user IDs." << endl;
158 str <<
" " << obj.new_sub_keys <<
" new sub keys." << endl;
159 str <<
" " << obj.new_signatures <<
" new signatures." << endl;
160 str <<
" " << obj.new_revocations <<
" new revocations." << endl;
161 str <<
" " << obj.secret_read <<
" secret keys read." << endl;
162 str <<
" " << obj.secret_imported <<
" imported secret keys." << endl;
163 str <<
" " << obj.secret_unchanged <<
" unchanged secret keys." << endl;
164 str <<
" " << obj.not_imported <<
" keys not imported." << endl;
165 for ( gpgme_import_status_t p = obj.imports; p; p = p->next )
167 str <<
" - " << p->fpr <<
": " << p->result << endl;
173 [[maybe_unused]] std::ostream &
operator<<( std::ostream &
str,
const gpgme_sigsum_t & obj )
175 str << ((int)obj&(int)0xffff) <<
":";
176#define OSC(V) if ( V & (unsigned)obj ) str << " " << #V;
177 OSC(GPGME_SIGSUM_VALID );
178 OSC(GPGME_SIGSUM_GREEN );
179 OSC(GPGME_SIGSUM_RED );
180 OSC(GPGME_SIGSUM_KEY_REVOKED );
181 OSC(GPGME_SIGSUM_KEY_EXPIRED );
182 OSC(GPGME_SIGSUM_SIG_EXPIRED );
183 OSC(GPGME_SIGSUM_KEY_MISSING );
184 OSC(GPGME_SIGSUM_CRL_MISSING );
185 OSC(GPGME_SIGSUM_CRL_TOO_OLD );
186 OSC(GPGME_SIGSUM_BAD_POLICY );
187 OSC(GPGME_SIGSUM_SYS_ERROR );
188 OSC(GPGME_SIGSUM_TOFU_CONFLICT);
193 [[maybe_unused]] std::ostream &
operator<<( std::ostream &
str,
const gpgme_signature_t & obj )
195 str <<
"gpgme_signature_t " << (
void *)obj <<
" {" << endl;
196 str <<
" next: " << (
void *)obj->next << endl;
197 str <<
" summary: " << obj->summary << endl;
198 str <<
" fpr: " << obj->fpr << endl;
199 str <<
" status: " << obj->status <<
" " << GpgmeErr(obj->status) << endl;
200 str <<
" timestamp: " << obj->timestamp << endl;
201 str <<
" exp_timestamp: " << obj->exp_timestamp << endl;
202 str <<
" wrong_key_usage: " << obj->wrong_key_usage << endl;
203 str <<
" pka_trust: " << obj->pka_trust << endl;
204 str <<
" chain_model: " << obj->chain_model << endl;
205 str <<
" is_de_vs: " << obj->is_de_vs << endl;
206 str <<
" validity: " << obj->validity << endl;
207 str <<
" validity_reason: " << obj->validity_reason <<
" " << GpgmeErr(obj->validity_reason) << endl;
208 str <<
" pubkey_algo: " << obj->pubkey_algo << endl;
209 str <<
" hash_algo: " << obj->hash_algo << endl;
210 str <<
" pka_address: " << (obj->pka_address ? obj->pka_address :
"") << endl;
228 { boost::call_once( gpgme_init_once, initGpgme ); }
256 template<
typename Callback >
257 bool importKey(GpgmeDataPtr &data, Callback &&calcDataSize );
260 {
return _tmpDir.has_value(); }
282 if (!
PathInfo( signature_r ).isExist())
283 return std::list<std::string>();
285 FILEPtr sigFile(fopen(signature_r.
c_str(),
"rb"), fclose);
287 ERR <<
"Unable to open signature file '" << signature_r <<
"'" <<endl;
288 return std::list<std::string>();
291 GpgmeDataPtr sigData(
nullptr, gpgme_data_release);
292 GpgmeErr err = gpgme_data_new_from_stream (&sigData.get(), sigFile.get());
295 return std::list<std::string>();
307 GpgmeDataPtr sigData(
nullptr, gpgme_data_release);
308 GpgmeErr err = gpgme_data_new_from_mem(&sigData.get(), keyData_r.data(), keyData_r.size(), 1 );
311 return std::list<std::string>();
323 FILEPtr dataFile(fopen(file_r.
c_str(),
"rb"), fclose);
325 return std::list<std::string>();
327 GpgmeDataPtr fileData(
nullptr, gpgme_data_release);
328 GpgmeErr err = gpgme_data_new_from_stream (&fileData.get(), dataFile.get());
331 return std::list<std::string>();
334 err = gpgme_op_verify(
_ctx, sigData.get(), fileData.get(), NULL);
335 if (err != GPG_ERR_NO_ERROR) {
337 return std::list<std::string>();
340 gpgme_verify_result_t res = gpgme_op_verify_result(
_ctx);
341 if (!res || !res->signatures) {
342 ERR <<
"Unable to read signature fingerprints" <<endl;
343 return std::list<std::string>();
346 bool foundBadSignature =
false;
347 bool foundGoodSignature =
false;
348 std::list<std::string> signatures;
349 for ( gpgme_signature_t sig = res->signatures; sig; sig = sig->next ) {
357 std::string id( sig->fpr );
358 if (
id.size() > 16 )
359 id =
id.substr(
id.size()-16 );
361 DBG <<
"Found signature with ID: " <<
id <<
" in " << file_r << std::endl;
362 signatures.push_back( std::move(
id) );
365 if ( verify_r && sig->status != GPG_ERR_NO_ERROR ) {
366 const auto status = gpgme_err_code(sig->status);
373 case GPG_ERR_KEY_EXPIRED:
375 foundGoodSignature =
true;
376 WAR <<
"Accept good signature from expired key: " << file_r <<
" " << GpgmeErr(sig->status) << endl;
379 case GPG_ERR_NO_PUBKEY:
380 WAR <<
"Legacy: Ignore unknown key: " << file_r <<
" " << GpgmeErr(sig->status) << endl;
384 WAR <<
"Failed signature check: " << file_r <<
" " << GpgmeErr(sig->status) << endl;
385 if ( !foundBadSignature )
386 foundBadSignature =
true;
390 foundGoodSignature =
true;
395 *verify_r = (!foundBadSignature) && foundGoodSignature;
422 GpgmeErr err = gpgme_new( &ctx );
423 if ( err != GPG_ERR_NO_ERROR )
427 err = gpgme_set_protocol( ctx, GPGME_PROTOCOL_OpenPGP );
428 if ( err != GPG_ERR_NO_ERROR )
431 if ( !keyring_r.
empty() ) {
434 PathInfo pi { keyring_r / gpgconfName };
436 std::ofstream file { pi.
path().
c_str() };
437 file <<
"no-autostart" << std::endl;
441 gpgme_engine_info_t enginfo = gpgme_ctx_get_engine_info( ctx );
445 err = gpgme_ctx_set_engine_info( ctx, GPGME_PROTOCOL_OpenPGP, enginfo->file_name, keyring_r.
c_str() );
446 if ( err != GPG_ERR_NO_ERROR )
450 DBG <<
"createForOpenPGP {" << endl;
451 for (
const auto & key : ret.
listKeys() ) {
452 DBG <<
" " << key << endl;
462 if ( gpgme_engine_info_t enginfo = gpgme_ctx_get_engine_info(
_pimpl->
_ctx ) )
463 ret = enginfo->home_dir;
469 std::list<PublicKeyData> ret;
470 GpgmeErr err = GPG_ERR_NO_ERROR;
475 if ( (err = gpgme_set_keylist_mode(
_pimpl->
_ctx, GPGME_KEYLIST_MODE_LOCAL | GPGME_KEYLIST_MODE_SIGS )) != GPG_ERR_NO_ERROR ) {
476 ERR <<
"gpgme_set_keylist_mode: " << err << endl;
480 if ( (err = gpgme_op_keylist_start(
_pimpl->
_ctx, NULL, 0 )) != GPG_ERR_NO_ERROR ) {
481 ERR <<
"gpgme_op_keylist_start: " << err << endl;
488 for ( ; gpgme_op_keylist_next(
_pimpl->
_ctx, &(*key) ) == GPG_ERR_NO_ERROR; key.
getDispose()( key ) ) {
491 ret.push_back( data );
505 std::list<PublicKeyData> ret;
511 if ( name != gpgconfName ) {
537 stream.write( keydata.data(), keydata.size() );
538 return bool( stream );
543 return exportKeyData(
_pimpl->
_ctx,
id, keydata );
548 if ( !
PathInfo( keyfile ).isExist() ) {
549 ERR <<
"Keyfile '" << keyfile <<
"' does not exist.";
553 GpgmeDataPtr data(
nullptr, gpgme_data_release);
556 err = gpgme_data_new_from_file(&data.get(), keyfile.
c_str(), 1);
558 ERR <<
"Error importing key: "<< err << endl;
569 constexpr size_t bufSize = 4096;
571 while ( stream.read( buf,
sizeof(buf) ) || stream.gcount() ) {
572 keydata.insert( keydata.end(), buf, buf + stream.gcount() );
575 if ( stream.bad() ) {
576 ERR <<
"Error importing key: failed to read key stream" << endl;
585 GpgmeDataPtr data(
nullptr, gpgme_data_release);
588 err = gpgme_data_new_from_mem( &data.get(), keydata.data(), keydata.size(), 1);
590 ERR <<
"Error importing key: "<< err << endl;
597template<
typename Callback>
601 err = gpgme_op_import(
_ctx, data.get() );
603 ERR <<
"Error importing key: "<< err << endl;
609 if ( gpgme_import_result_t res = gpgme_op_import_result(
_ctx) )
611 if ( ! res->considered && std::forward<Callback>(calcDataSize)() )
614 ERR <<
"Error importing key: No keys considered (bsc#1127220, [libgpgme] signal received?)" << endl;
619 return (err == GPG_ERR_NO_ERROR);
624 gpgme_key_t key =
nullptr;
625 GpgmeErr err = GPG_ERR_NO_ERROR;
627 gpgme_op_keylist_start(
_pimpl->
_ctx, NULL, 0);
629 while (!(err = gpgme_op_keylist_next(
_pimpl->
_ctx, &key))) {
630 if (key->subkeys &&
id ==
str::asString(key->subkeys->keyid)) {
633 gpgme_key_release(key);
637 ERR <<
"Error deleting key: "<< err << endl;
642 gpgme_key_release(key);
646 WAR <<
"Key: '"<<
id <<
"' not found." << endl;
#define ZYPP_THROW(EXCPT)
Drops a logline and throws the Exception.
std::ostream & operator<<(std::ostream &str, const zypp::sat::detail::CDataiterator *obj)
relates: zypp::sat::LookupAttr::iterator Stream output of the underlying iterator for debug.
Reference counted access to a Tp object calling a custom Dispose function when the last AutoDispose h...
const Dispose & getDispose() const
Return the current dispose function.
Impl & operator=(const Impl &)=delete
std::list< std::string > readSignaturesFprs(const Pathname &signature_r)
Return all fingerprints found in signature_r.
Impl(const Impl &)=delete
std::optional< filesystem::TmpDir > _tmpDir
volatile contexts own their private temp homedir
Impl & operator=(Impl &&)=delete
std::list< std::string > readSignaturesFprs(const ByteArray &signature_r)
Return all fingerprints found in signature_r.
std::list< std::string > readSignaturesFprsOptVerify(const Pathname &signature_r, const Pathname &file_r="/dev/null", bool *verify_r=nullptr)
Return all fingerprints found in signature_r and optionally verify the file_r on the fly.
bool verifySignaturesFprs(const Pathname &file_r, const Pathname &signature_r)
Tries to verify the file_r using signature_r.
bool importKey(GpgmeDataPtr &data, Callback &&calcDataSize)
bool exportKey(const std::string &id, std::ostream &stream)
Exports the key with id into the given stream, returns true on success.
std::list< PublicKeyData > listKeys()
Returns a list of all public keys found in the current keyring.
bool verify(const Pathname &file, const Pathname &signature)
Tries to verify file using signature, returns true on success.
static KeyManagerCtx createForOpenPGP()
Creates a new KeyManagerCtx for PGP using a volatile temp.
std::list< std::string > readSignatureFingerprints(const Pathname &signature)
Reads all fingerprints from the signature file , returns a list of all found fingerprints.
std::list< PublicKeyData > readKeyFromFile(const Pathname &file)
Returns a list of all PublicKeyData found in file.
RW_pointer< Impl > _pimpl
Pointer to implementation.
bool deleteKey(const std::string &id)
Tries to delete a key specified by id, returns true on success.
Pathname homedir() const
Return the homedir/keyring.
bool importKey(const Pathname &keyfile)
Tries to import a key from keyfile, returns true on success.
KeyRingException()
Ctor taking message.
Class representing one GPG Public Keys data.
static PublicKeyData fromGpgmeKey(_gpgme_key *data)
Wrapper class for stat/lstat.
const Pathname & path() const
Return current Pathname.
bool isExist() const
Return whether valid stat info exists.
const char * c_str() const
String representation.
bool empty() const
Test for an empty path.
Provide a new empty temporary directory and recursively delete it when no longer needed.
String related utilities and Regular expression matching.
int unlink(const Pathname &path)
Like 'unlink'.
int dirForEach(const Pathname &dir_r, const StrMatcher &matcher_r, function< bool(const Pathname &, const char *const)> fnc_r)
const std::string & asString(const std::string &t)
Global asString() that works with std::string too.
Easy-to use interface to the ZYPP dependency resolver.
Pathname myTmpDir()
Global access to the zypp.TMPDIR (created on demand, deleted when libzypp is unloaded).
GpgmeException(const std::string &in_r, const GpgmeErr &err_r)
Exchange LineWriter for the lifetime of this object.