diff --git a/src/keycache.cpp b/src/keycache.cpp
index 69171c6..e1bb17f 100644
--- a/src/keycache.cpp
+++ b/src/keycache.cpp
@@ -1,1272 +1,1279 @@
/* @file keycache.cpp
* @brief Internal keycache
*
* Copyright (C) 2018 Intevation GmbH
*
* This file is part of GpgOL.
*
* GpgOL is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* GpgOL is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, see .
*/
#include "keycache.h"
#include "common.h"
#include "cpphelp.h"
#include "mail.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
GPGRT_LOCK_DEFINE (keycache_lock);
GPGRT_LOCK_DEFINE (fpr_map_lock);
GPGRT_LOCK_DEFINE (update_lock);
GPGRT_LOCK_DEFINE (import_lock);
static KeyCache* singleton = nullptr;
/** At some point we need to set a limit. There
seems to be no limit on how many recipients a mail
can have in outlook.
We would run out of resources or block.
50 Threads already seems a bit excessive but
it should really cover most legit use cases.
*/
#define MAX_LOCATOR_THREADS 50
static int s_thread_cnt;
namespace
{
class LocateArgs
{
public:
LocateArgs (const std::string& mbox, Mail *mail = nullptr):
m_mbox (mbox),
m_mail (mail)
{
TSTART;
s_thread_cnt++;
Mail::lockDelete ();
if (Mail::isValidPtr (m_mail))
{
m_mail->incrementLocateCount ();
}
Mail::unlockDelete ();
TRETURN;
};
~LocateArgs()
{
TSTART;
s_thread_cnt--;
Mail::lockDelete ();
if (Mail::isValidPtr (m_mail))
{
m_mail->decrementLocateCount ();
}
Mail::unlockDelete ();
TRETURN;
}
std::string m_mbox;
Mail *m_mail;
};
} // namespace
typedef std::pair update_arg_t;
typedef std::pair, std::string> import_arg_t;
static DWORD WINAPI
do_update (LPVOID arg)
{
TSTART;
auto args = std::unique_ptr ((update_arg_t*) arg);
log_debug ("%s:%s updating: \"%s\" with protocol %s",
SRCNAME, __func__, anonstr (args->first.c_str ()),
to_cstr (args->second));
auto ctx = std::unique_ptr (GpgME::Context::createForProtocol
(args->second));
if (!ctx)
{
TRACEPOINT;
KeyCache::instance ()->onUpdateJobDone (args->first.c_str(),
GpgME::Key ());
TRETURN 0;
}
ctx->setKeyListMode (GpgME::KeyListMode::Local |
GpgME::KeyListMode::Signatures |
GpgME::KeyListMode::Validate |
GpgME::KeyListMode::WithTofu);
GpgME::Error err;
const auto newKey = ctx->key (args->first.c_str (), err, false);
TRACEPOINT;
if (newKey.isNull())
{
log_debug ("%s:%s Failed to find key for %s",
SRCNAME, __func__, anonstr (args->first.c_str ()));
}
if (err)
{
log_debug ("%s:%s Failed to find key for %s err: %s",
SRCNAME, __func__, anonstr (args->first.c_str()),
err.asString ());
}
KeyCache::instance ()->onUpdateJobDone (args->first.c_str(),
newKey);
log_debug ("%s:%s Update job done",
SRCNAME, __func__);
TRETURN 0;
}
static DWORD WINAPI
do_import (LPVOID arg)
{
TSTART;
auto args = std::unique_ptr ((import_arg_t*) arg);
const std::string mbox = args->first->m_mbox;
log_debug ("%s:%s importing for: \"%s\" with data \n%s",
SRCNAME, __func__, anonstr (mbox.c_str ()),
anonstr (args->second.c_str ()));
auto ctx = std::unique_ptr (GpgME::Context::createForProtocol
(GpgME::OpenPGP));
if (!ctx)
{
TRACEPOINT;
TRETURN 0;
}
// We want to avoid unneccessary copies. The c_str will be valid
// until args goes out of scope.
const char *keyStr = args->second.c_str ();
GpgME::Data data (keyStr, strlen (keyStr), /* copy */ false);
if (data.type () != GpgME::Data::PGPKey)
{
log_debug ("%s:%s Data for: %s is not a PGP Key",
SRCNAME, __func__, anonstr (mbox.c_str ()));
TRETURN 0;
}
data.rewind ();
const auto result = ctx->importKeys (data);
std::vector fingerprints;
for (const auto import: result.imports())
{
if (import.error())
{
log_debug ("%s:%s Error importing: %s",
SRCNAME, __func__, import.error().asString());
continue;
}
const char *fpr = import.fingerprint ();
if (!fpr)
{
TRACEPOINT;
continue;
}
update_arg_t * update_args = new update_arg_t;
update_args->first = std::string (fpr);
update_args->second = GpgME::OpenPGP;
// We do it blocking to be sure that when all imports
// are done they are also part of the keycache.
do_update ((LPVOID) update_args);
fingerprints.push_back (fpr);
log_debug ("%s:%s Imported: %s from addressbook.",
SRCNAME, __func__, anonstr (fpr));
}
KeyCache::instance ()->onAddrBookImportJobDone (mbox, fingerprints);
log_debug ("%s:%s Import job done for: %s",
SRCNAME, __func__, anonstr (mbox.c_str ()));
TRETURN 0;
}
class KeyCache::Private
{
public:
Private()
{
}
void setPgpKey(const std::string &mbox, const GpgME::Key &key)
{
TSTART;
gpgol_lock (&keycache_lock);
auto it = m_pgp_key_map.find (mbox);
if (it == m_pgp_key_map.end ())
{
m_pgp_key_map.insert (std::pair (mbox, key));
}
else
{
it->second = key;
}
insertOrUpdateInFprMap (key);
gpgol_unlock (&keycache_lock);
TRETURN;
}
void setSmimeKey(const std::string &mbox, const GpgME::Key &key)
{
TSTART;
gpgol_lock (&keycache_lock);
auto it = m_smime_key_map.find (mbox);
if (it == m_smime_key_map.end ())
{
m_smime_key_map.insert (std::pair (mbox, key));
}
else
{
it->second = key;
}
insertOrUpdateInFprMap (key);
gpgol_unlock (&keycache_lock);
TRETURN;
}
void setPgpKeySecret(const std::string &mbox, const GpgME::Key &key)
{
TSTART;
gpgol_lock (&keycache_lock);
auto it = m_pgp_skey_map.find (mbox);
if (it == m_pgp_skey_map.end ())
{
m_pgp_skey_map.insert (std::pair (mbox, key));
}
else
{
it->second = key;
}
insertOrUpdateInFprMap (key);
gpgol_unlock (&keycache_lock);
TRETURN;
}
void setSmimeKeySecret(const std::string &mbox, const GpgME::Key &key)
{
TSTART;
gpgol_lock (&keycache_lock);
auto it = m_smime_skey_map.find (mbox);
if (it == m_smime_skey_map.end ())
{
m_smime_skey_map.insert (std::pair (mbox, key));
}
else
{
it->second = key;
}
insertOrUpdateInFprMap (key);
gpgol_unlock (&keycache_lock);
TRETURN;
}
std::vector getPGPOverrides (const char *addr)
{
TSTART;
std::vector ret;
if (!addr)
{
TRETURN ret;
}
auto mbox = GpgME::UserID::addrSpecFromString (addr);
gpgol_lock (&keycache_lock);
const auto it = m_addr_book_overrides.find (mbox);
if (it == m_addr_book_overrides.end ())
{
gpgol_unlock (&keycache_lock);
TRETURN ret;
}
for (const auto fpr: it->second)
{
const auto key = getByFpr (fpr.c_str (), false);
if (key.isNull())
{
log_debug ("%s:%s: No key for %s in the cache?!",
SRCNAME, __func__, anonstr (fpr.c_str()));
continue;
}
ret.push_back (key);
}
gpgol_unlock (&keycache_lock);
TRETURN ret;
}
GpgME::Key getKey (const char *addr, GpgME::Protocol proto)
{
TSTART;
if (!addr)
{
TRETURN GpgME::Key();
}
auto mbox = GpgME::UserID::addrSpecFromString (addr);
if (proto == GpgME::OpenPGP)
{
gpgol_lock (&keycache_lock);
const auto it = m_pgp_key_map.find (mbox);
if (it == m_pgp_key_map.end ())
{
gpgol_unlock (&keycache_lock);
TRETURN GpgME::Key();
}
const auto ret = it->second;
gpgol_unlock (&keycache_lock);
TRETURN ret;
}
gpgol_lock (&keycache_lock);
const auto it = m_smime_key_map.find (mbox);
if (it == m_smime_key_map.end ())
{
gpgol_unlock (&keycache_lock);
TRETURN GpgME::Key();
}
const auto ret = it->second;
gpgol_unlock (&keycache_lock);
TRETURN ret;
}
GpgME::Key getSKey (const char *addr, GpgME::Protocol proto)
{
TSTART;
if (!addr)
{
TRETURN GpgME::Key();
}
auto mbox = GpgME::UserID::addrSpecFromString (addr);
if (proto == GpgME::OpenPGP)
{
gpgol_lock (&keycache_lock);
const auto it = m_pgp_skey_map.find (mbox);
if (it == m_pgp_skey_map.end ())
{
gpgol_unlock (&keycache_lock);
TRETURN GpgME::Key();
}
const auto ret = it->second;
gpgol_unlock (&keycache_lock);
TRETURN ret;
}
gpgol_lock (&keycache_lock);
const auto it = m_smime_skey_map.find (mbox);
if (it == m_smime_skey_map.end ())
{
gpgol_unlock (&keycache_lock);
TRETURN GpgME::Key();
}
const auto ret = it->second;
gpgol_unlock (&keycache_lock);
TRETURN ret;
}
GpgME::Key getSigningKey (const char *addr, GpgME::Protocol proto)
{
TSTART;
const auto key = getSKey (addr, proto);
if (key.isNull())
{
log_debug ("%s:%s: secret key for %s is null",
SRCNAME, __func__, anonstr (addr));
TRETURN key;
}
if (!key.canReallySign())
{
log_debug ("%s:%s: Discarding key for %s because it can't sign",
SRCNAME, __func__, anonstr (addr));
TRETURN GpgME::Key();
}
if (!key.hasSecret())
{
log_debug ("%s:%s: Discarding key for %s because it has no secret",
SRCNAME, __func__, anonstr (addr));
TRETURN GpgME::Key();
}
if (in_de_vs_mode () && !key.isDeVs())
{
log_debug ("%s:%s: signing key for %s is not deVS",
SRCNAME, __func__, anonstr (addr));
TRETURN GpgME::Key();
}
TRETURN key;
}
std::vector getEncryptionKeys (const std::vector
&recipients,
GpgME::Protocol proto)
{
TSTART;
std::vector ret;
if (recipients.empty ())
{
TRACEPOINT;
TRETURN ret;
}
for (const auto &recip: recipients)
{
if (proto == GpgME::OpenPGP)
{
const auto overrides = getPGPOverrides (recip.c_str ());
if (!overrides.empty())
{
ret.insert (ret.end (), overrides.begin (), overrides.end ());
log_debug ("%s:%s: Using overides for %s",
SRCNAME, __func__, anonstr (recip.c_str ()));
continue;
}
}
const auto key = getKey (recip.c_str (), proto);
if (key.isNull())
{
log_debug ("%s:%s: No key for %s. no internal encryption",
SRCNAME, __func__, anonstr (recip.c_str ()));
TRETURN std::vector();
}
if (!key.canEncrypt() || key.isRevoked() ||
key.isExpired() || key.isDisabled() || key.isInvalid())
{
log_data ("%s:%s: Invalid key for %s. no internal encryption",
SRCNAME, __func__, anonstr (recip.c_str ()));
TRETURN std::vector();
}
if (in_de_vs_mode () && !key.isDeVs ())
{
log_data ("%s:%s: key for %s is not deVS",
SRCNAME, __func__, anonstr (recip.c_str ()));
TRETURN std::vector();
}
bool validEnough = false;
/* Here we do the check if the key is valid for this recipient */
const auto addrSpec = GpgME::UserID::addrSpecFromString (recip.c_str ());
for (const auto &uid: key.userIDs ())
{
if (addrSpec != uid.addrSpec())
{
// Ignore unmatching addr specs
continue;
}
if (uid.validity() >= GpgME::UserID::Marginal ||
uid.origin() == GpgME::Key::OriginWKD)
{
validEnough = true;
break;
}
}
if (!validEnough)
{
log_debug ("%s:%s: UID for %s does not have at least marginal trust",
SRCNAME, __func__, anonstr (recip.c_str ()));
TRETURN std::vector();
}
// Accepting key
ret.push_back (key);
}
TRETURN ret;
}
void insertOrUpdateInFprMap (const GpgME::Key &key)
{
TSTART;
if (key.isNull() || !key.primaryFingerprint())
{
TRACEPOINT;
TRETURN;
}
gpgol_lock (&fpr_map_lock);
/* First ensure that we have the subkeys mapped to the primary
fpr */
const char *primaryFpr = key.primaryFingerprint ();
for (const auto &sub: key.subkeys())
{
const char *subFpr = sub.fingerprint();
auto it = m_sub_fpr_map.find (subFpr);
if (it == m_sub_fpr_map.end ())
{
m_sub_fpr_map.insert (std::make_pair(
std::string (subFpr),
std::string (primaryFpr)));
}
}
auto it = m_fpr_map.find (primaryFpr);
log_debug ("%s:%s \"%s\" updated.",
SRCNAME, __func__, anonstr (primaryFpr));
if (it == m_fpr_map.end ())
{
m_fpr_map.insert (std::make_pair (primaryFpr, key));
gpgol_unlock (&fpr_map_lock);
TRETURN;
}
if (it->second.hasSecret () && !key.hasSecret())
{
log_debug ("%s:%s Lost secret info on update. Merging.",
SRCNAME, __func__);
auto merged = key;
merged.mergeWith (it->second);
it->second = merged;
}
else
{
it->second = key;
}
gpgol_unlock (&fpr_map_lock);
TRETURN;
}
GpgME::Key getFromMap (const char *fpr) const
{
TSTART;
if (!fpr)
{
TRACEPOINT;
TRETURN GpgME::Key();
}
gpgol_lock (&fpr_map_lock);
std::string primaryFpr;
const auto it = m_sub_fpr_map.find (fpr);
if (it != m_sub_fpr_map.end ())
{
log_debug ("%s:%s using \"%s\" for \"%s\"",
SRCNAME, __func__, anonstr (it->second.c_str()),
anonstr (fpr));
primaryFpr = it->second;
}
else
{
primaryFpr = fpr;
}
const auto keyIt = m_fpr_map.find (primaryFpr);
if (keyIt != m_fpr_map.end ())
{
const auto ret = keyIt->second;
gpgol_unlock (&fpr_map_lock);
TRETURN ret;
}
gpgol_unlock (&fpr_map_lock);
TRETURN GpgME::Key();
}
GpgME::Key getByFpr (const char *fpr, bool block) const
{
TSTART;
if (!fpr)
{
TRACEPOINT;
TRETURN GpgME::Key ();
}
TRACEPOINT;
const auto ret = getFromMap (fpr);
if (ret.isNull())
{
// If the key was not found we need to check if there is
// an update running.
if (block)
{
const std::string sFpr (fpr);
int i = 0;
gpgol_lock (&update_lock);
while (m_update_jobs.find(sFpr) != m_update_jobs.end ())
{
i++;
if (i % 100 == 0)
{
log_debug ("%s:%s Waiting on update for \"%s\"",
SRCNAME, __func__, anonstr (fpr));
}
gpgol_unlock (&update_lock);
Sleep (10);
gpgol_lock (&update_lock);
if (i == 3000)
{
/* Just to be on the save side */
log_error ("%s:%s Waiting on update for \"%s\" "
"failed! Bug!",
SRCNAME, __func__, anonstr (fpr));
break;
}
}
gpgol_unlock (&update_lock);
TRACEPOINT;
const auto ret2 = getFromMap (fpr);
if (ret2.isNull ())
{
log_debug ("%s:%s Cache miss after blocking check %s.",
SRCNAME, __func__, anonstr (fpr));
}
else
{
log_debug ("%s:%s Cache hit after wait for %s.",
SRCNAME, __func__, anonstr (fpr));
TRETURN ret2;
}
}
log_debug ("%s:%s Cache miss for %s.",
SRCNAME, __func__, anonstr (fpr));
TRETURN GpgME::Key();
}
log_debug ("%s:%s Cache hit for %s.",
SRCNAME, __func__, anonstr (fpr));
TRETURN ret;
}
void update (const char *fpr, GpgME::Protocol proto)
{
TSTART;
if (!fpr)
{
TRETURN;
}
const std::string sFpr (fpr);
gpgol_lock (&update_lock);
if (m_update_jobs.find(sFpr) != m_update_jobs.end ())
{
log_debug ("%s:%s Update for \"%s\" already in progress.",
SRCNAME, __func__, anonstr (fpr));
gpgol_unlock (&update_lock);
}
m_update_jobs.insert (sFpr);
gpgol_unlock (&update_lock);
update_arg_t * args = new update_arg_t;
args->first = sFpr;
args->second = proto;
CloseHandle (CreateThread (NULL, 0, do_update,
(LPVOID) args, 0,
NULL));
TRETURN;
}
void onUpdateJobDone (const char *fpr, const GpgME::Key &key)
{
TSTART;
if (!fpr)
{
TRETURN;
}
TRACEPOINT;
insertOrUpdateInFprMap (key);
gpgol_lock (&update_lock);
const auto it = m_update_jobs.find(fpr);
if (it == m_update_jobs.end())
{
log_debug ("%s:%s Update for \"%s\" already finished.",
SRCNAME, __func__, anonstr (fpr));
gpgol_unlock (&update_lock);
TRETURN;
}
m_update_jobs.erase (it);
gpgol_unlock (&update_lock);
TRACEPOINT;
TRETURN;
}
void importFromAddrBook (const std::string &mbox, const char *data,
Mail *mail)
{
TSTART;
if (!data || mbox.empty() || !mail)
{
TRACEPOINT;
TRETURN;
}
- gpgol_lock (&import_lock);
- if (m_import_jobs.find (mbox) != m_import_jobs.end ())
- {
- log_debug ("%s:%s import for \"%s\" already in progress.",
- SRCNAME, __func__, anonstr (mbox.c_str ()));
- gpgol_unlock (&import_lock);
- }
- m_import_jobs.insert (mbox);
- gpgol_unlock (&import_lock);
- import_arg_t * args = new import_arg_t;
- args->first = std::unique_ptr (new LocateArgs (mbox, mail));
- args->second = std::string (data);
- CloseHandle (CreateThread (NULL, 0, do_import,
- (LPVOID) args, 0,
- NULL));
+ std::string sdata (data);
+ trim (sdata);
+ if (sdata.empty())
+ {
+ TRETURN;
+ }
+ gpgol_lock (&import_lock);
+ if (m_import_jobs.find (mbox) != m_import_jobs.end ())
+ {
+ log_debug ("%s:%s import for \"%s\" already in progress.",
+ SRCNAME, __func__, anonstr (mbox.c_str ()));
+ gpgol_unlock (&import_lock);
+ }
+ m_import_jobs.insert (mbox);
+ gpgol_unlock (&import_lock);
+
+ import_arg_t * args = new import_arg_t;
+ args->first = std::unique_ptr (new LocateArgs (mbox, mail));
+ args->second = sdata;
+ CloseHandle (CreateThread (NULL, 0, do_import,
+ (LPVOID) args, 0,
+ NULL));
TRETURN;
}
void onAddrBookImportJobDone (const std::string &mbox,
const std::vector &result_fprs)
{
TSTART;
gpgol_lock (&keycache_lock);
auto it = m_addr_book_overrides.find (mbox);
if (it != m_addr_book_overrides.end ())
{
it->second = result_fprs;
}
else
{
m_addr_book_overrides.insert (
std::make_pair (mbox, result_fprs));
}
gpgol_unlock (&keycache_lock);
gpgol_lock (&import_lock);
const auto job_it = m_import_jobs.find(mbox);
if (job_it == m_import_jobs.end())
{
log_error ("%s:%s import for \"%s\" already finished.",
SRCNAME, __func__, anonstr (mbox.c_str ()));
gpgol_unlock (&import_lock);
TRETURN;
}
m_import_jobs.erase (job_it);
gpgol_unlock (&import_lock);
TRETURN;
}
std::unordered_map m_pgp_key_map;
std::unordered_map m_smime_key_map;
std::unordered_map m_pgp_skey_map;
std::unordered_map m_smime_skey_map;
std::unordered_map m_fpr_map;
std::unordered_map m_sub_fpr_map;
std::unordered_map >
m_addr_book_overrides;
std::set m_update_jobs;
std::set m_import_jobs;
};
KeyCache::KeyCache():
d(new Private)
{
}
KeyCache *
KeyCache::instance ()
{
if (!singleton)
{
singleton = new KeyCache();
}
return singleton;
}
GpgME::Key
KeyCache::getSigningKey (const char *addr, GpgME::Protocol proto) const
{
return d->getSigningKey (addr, proto);
}
std::vector
KeyCache::getEncryptionKeys (const std::vector &recipients, GpgME::Protocol proto) const
{
return d->getEncryptionKeys (recipients, proto);
}
static GpgME::Key
get_most_valid_key_simple (const std::vector &keys)
{
GpgME::Key candidate;
for (const auto &key: keys)
{
if (key.isRevoked() || key.isExpired() ||
key.isDisabled() || key.isInvalid())
{
log_debug ("%s:%s: Skipping invalid S/MIME key",
SRCNAME, __func__);
continue;
}
if (candidate.isNull() || !candidate.numUserIDs())
{
if (key.numUserIDs() &&
candidate.userID(0).validity() <= key.userID(0).validity())
{
candidate = key;
}
}
}
return candidate;
}
static std::vector
get_local_smime_keys (const std::string &addr)
{
TSTART;
std::vector keys;
auto ctx = std::unique_ptr (
GpgME::Context::createForProtocol (GpgME::CMS));
if (!ctx)
{
TRACEPOINT;
TRETURN keys;
}
// We need to validate here to fetch CRL's
ctx->setKeyListMode (GpgME::KeyListMode::Local |
GpgME::KeyListMode::Validate |
GpgME::KeyListMode::Signatures);
GpgME::Error e = ctx->startKeyListing (addr.c_str());
if (e)
{
TRACEPOINT;
TRETURN keys;
}
GpgME::Error err;
do {
keys.push_back(ctx->nextKey(err));
} while (!err);
keys.pop_back();
TRETURN keys;
}
static std::vector
get_extern_smime_keys (const std::string &addr, bool import)
{
TSTART;
std::vector keys;
auto ctx = std::unique_ptr (
GpgME::Context::createForProtocol (GpgME::CMS));
if (!ctx)
{
TRACEPOINT;
TRETURN keys;
}
// We need to validate here to fetch CRL's
ctx->setKeyListMode (GpgME::KeyListMode::Extern);
GpgME::Error e = ctx->startKeyListing (addr.c_str());
if (e)
{
TRACEPOINT;
TRETURN keys;
}
GpgME::Error err;
do
{
const auto key = ctx->nextKey (err);
if (!err && !key.isNull())
{
keys.push_back (key);
log_debug ("%s:%s: Found extern S/MIME key for %s with fpr: %s",
SRCNAME, __func__, anonstr (addr.c_str()),
anonstr (key.primaryFingerprint()));
}
} while (!err);
if (import && keys.size ())
{
const GpgME::ImportResult res = ctx->importKeys(keys);
log_debug ("%s:%s: Import result for %s: err: %s",
SRCNAME, __func__, anonstr (addr.c_str()),
res.error ().asString ());
}
TRETURN keys;
}
static DWORD WINAPI
do_locate (LPVOID arg)
{
TSTART;
if (!arg)
{
TRETURN 0;
}
auto args = std::unique_ptr ((LocateArgs *) arg);
const auto addr = args->m_mbox;
log_debug ("%s:%s searching key for addr: \"%s\"",
SRCNAME, __func__, anonstr (addr.c_str()));
const auto k = GpgME::Key::locate (addr.c_str());
if (!k.isNull ())
{
log_debug ("%s:%s found key for addr: \"%s\":%s",
SRCNAME, __func__, anonstr (addr.c_str()),
anonstr (k.primaryFingerprint()));
KeyCache::instance ()->setPgpKey (addr, k);
}
log_debug ("%s:%s pgp locate done",
SRCNAME, __func__);
if (opt.enable_smime)
{
GpgME::Key candidate = get_most_valid_key_simple (
get_local_smime_keys (addr));
if (!candidate.isNull())
{
log_debug ("%s:%s found SMIME key for addr: \"%s\":%s",
SRCNAME, __func__, anonstr (addr.c_str()),
anonstr (candidate.primaryFingerprint()));
KeyCache::instance()->setSmimeKey (addr, candidate);
TRETURN 0;
}
if (!opt.search_smime_servers || (!k.isNull() && !opt.prefer_smime))
{
log_debug ("%s:%s Found no S/MIME key locally and external "
"search is disabled.", SRCNAME, __func__);
TRETURN 0;
}
/* Search for extern keys and import them */
const auto externs = get_extern_smime_keys (addr, true);
if (externs.empty())
{
TRETURN 0;
}
/* We found and imported external keys. We need to get them
locally now to ensure that they are valid etc. */
candidate = get_most_valid_key_simple (
get_local_smime_keys (addr));
if (!candidate.isNull())
{
log_debug ("%s:%s found ext. SMIME key for addr: \"%s\":%s",
SRCNAME, __func__, anonstr (addr.c_str()),
anonstr (candidate.primaryFingerprint()));
KeyCache::instance()->setSmimeKey (addr, candidate);
TRETURN 0;
}
else
{
log_debug ("%s:%s: Found no valid key in extern S/MIME certs",
SRCNAME, __func__);
}
}
TRETURN 0;
}
static void
locate_secret (const char *addr, GpgME::Protocol proto)
{
TSTART;
auto ctx = std::unique_ptr (
GpgME::Context::createForProtocol (proto));
if (!ctx)
{
TRACEPOINT;
TRETURN;
}
if (!addr)
{
TRACEPOINT;
TRETURN;
}
const auto mbox = GpgME::UserID::addrSpecFromString (addr);
if (mbox.empty())
{
log_debug ("%s:%s: Empty mbox for addr %s",
SRCNAME, __func__, anonstr (addr));
TRETURN;
}
// We need to validate here to fetch CRL's
ctx->setKeyListMode (GpgME::KeyListMode::Local |
GpgME::KeyListMode::Validate);
GpgME::Error e = ctx->startKeyListing (mbox.c_str(), true);
if (e)
{
TRACEPOINT;
TRETURN;
}
std::vector keys;
GpgME::Error err;
do
{
const auto key = ctx->nextKey(err);
if (key.isNull())
{
continue;
}
if (key.isRevoked() || key.isExpired() ||
key.isDisabled() || key.isInvalid())
{
if ((opt.enable_debug & DBG_DATA))
{
std::stringstream ss;
ss << key;
log_data ("%s:%s: Skipping invalid secret key %s",
SRCNAME, __func__, ss.str().c_str());
}
continue;
}
if (proto == GpgME::OpenPGP)
{
log_debug ("%s:%s found pgp skey for addr: \"%s\":%s",
SRCNAME, __func__, anonstr (mbox.c_str()),
anonstr (key.primaryFingerprint()));
KeyCache::instance()->setPgpKeySecret (mbox, key);
TRETURN;
}
if (proto == GpgME::CMS)
{
log_debug ("%s:%s found cms skey for addr: \"%s\":%s",
SRCNAME, __func__, anonstr (mbox.c_str ()),
anonstr (key.primaryFingerprint()));
KeyCache::instance()->setSmimeKeySecret (mbox, key);
TRETURN;
}
} while (!err);
TRETURN;
}
static DWORD WINAPI
do_locate_secret (LPVOID arg)
{
TSTART;
auto args = std::unique_ptr ((LocateArgs *) arg);
log_debug ("%s:%s searching secret key for addr: \"%s\"",
SRCNAME, __func__, anonstr (args->m_mbox.c_str ()));
locate_secret (args->m_mbox.c_str(), GpgME::OpenPGP);
if (opt.enable_smime)
{
locate_secret (args->m_mbox.c_str(), GpgME::CMS);
}
log_debug ("%s:%s locator sthread thread done",
SRCNAME, __func__);
TRETURN 0;
}
void
KeyCache::startLocate (const std::vector &addrs, Mail *mail) const
{
for (const auto &addr: addrs)
{
startLocate (addr.c_str(), mail);
}
}
void
KeyCache::startLocate (const char *addr, Mail *mail) const
{
TSTART;
if (!addr)
{
TRACEPOINT;
TRETURN;
}
std::string recp = GpgME::UserID::addrSpecFromString (addr);
if (recp.empty ())
{
TRETURN;
}
gpgol_lock (&keycache_lock);
if (d->m_pgp_key_map.find (recp) == d->m_pgp_key_map.end ())
{
// It's enough to look at the PGP Key map. We marked
// searched keys there.
d->m_pgp_key_map.insert (std::pair (recp, GpgME::Key()));
log_debug ("%s:%s Creating a locator thread",
SRCNAME, __func__);
const auto args = new LocateArgs(recp, mail);
HANDLE thread = CreateThread (NULL, 0, do_locate,
args, 0,
NULL);
CloseHandle (thread);
}
gpgol_unlock (&keycache_lock);
TRETURN;
}
void
KeyCache::startLocateSecret (const char *addr, Mail *mail) const
{
TSTART;
if (!addr)
{
TRACEPOINT;
TRETURN;
}
std::string recp = GpgME::UserID::addrSpecFromString (addr);
if (recp.empty ())
{
TRETURN;
}
gpgol_lock (&keycache_lock);
if (d->m_pgp_skey_map.find (recp) == d->m_pgp_skey_map.end ())
{
// It's enough to look at the PGP Key map. We marked
// searched keys there.
d->m_pgp_skey_map.insert (std::pair (recp, GpgME::Key()));
log_debug ("%s:%s Creating a locator thread",
SRCNAME, __func__);
const auto args = new LocateArgs(recp, mail);
HANDLE thread = CreateThread (NULL, 0, do_locate_secret,
(LPVOID) args, 0,
NULL);
CloseHandle (thread);
}
gpgol_unlock (&keycache_lock);
TRETURN;
}
void
KeyCache::setSmimeKey(const std::string &mbox, const GpgME::Key &key)
{
d->setSmimeKey(mbox, key);
}
void
KeyCache::setPgpKey(const std::string &mbox, const GpgME::Key &key)
{
d->setPgpKey(mbox, key);
}
void
KeyCache::setSmimeKeySecret(const std::string &mbox, const GpgME::Key &key)
{
d->setSmimeKeySecret(mbox, key);
}
void
KeyCache::setPgpKeySecret(const std::string &mbox, const GpgME::Key &key)
{
d->setPgpKeySecret(mbox, key);
}
bool
KeyCache::isMailResolvable(Mail *mail)
{
TSTART;
/* Get the data from the mail. */
const auto sender = mail->getSender ();
auto recps = mail->getCachedRecipients ();
if (sender.empty() || recps.empty())
{
log_debug ("%s:%s: Mail has no sender or no recipients.",
SRCNAME, __func__);
TRETURN false;
}
std::vector encKeys = getEncryptionKeys (recps,
GpgME::OpenPGP);
if (!encKeys.empty())
{
TRETURN true;
}
if (!opt.enable_smime)
{
TRETURN false;
}
/* Check S/MIME instead here we need to include the sender
as we can't just generate a key. */
recps.push_back (sender);
GpgME::Key sigKey= getSigningKey (sender.c_str(), GpgME::CMS);
encKeys = getEncryptionKeys (recps, GpgME::CMS);
TRETURN !encKeys.empty() && !sigKey.isNull();
}
void
KeyCache::update (const char *fpr, GpgME::Protocol proto)
{
d->update (fpr, proto);
}
GpgME::Key
KeyCache::getByFpr (const char *fpr, bool block) const
{
return d->getByFpr (fpr, block);
}
void
KeyCache::onUpdateJobDone (const char *fpr, const GpgME::Key &key)
{
return d->onUpdateJobDone (fpr, key);
}
void
KeyCache::importFromAddrBook (const std::string &mbox, const char *key_data,
Mail *mail) const
{
return d->importFromAddrBook (mbox, key_data, mail);
}
void
KeyCache::onAddrBookImportJobDone (const std::string &mbox,
const std::vector &result_fprs)
{
return d->onAddrBookImportJobDone (mbox, result_fprs);
}
std::vector
KeyCache::getOverrides (const std::string &mbox)
{
return d->getPGPOverrides (mbox.c_str ());
}