Commit 729cdb3e authored by Ingo Klöcker's avatar Ingo Klöcker
Browse files

Resolve groups after overrides but before keys

This allows proper handling of groups in the different modes of the
resolver.

GnuPG-bug-id: 5283
parent 4f0aadb3
Pipeline #59934 passed with stage
in 7 minutes and 40 seconds
......@@ -641,12 +641,123 @@ private Q_SLOTS:
QVERIFY(result.flags & KeyResolverCore::Error);
}
void test_groups__group_with_one_openpgp_key__mixed_mode()
void test_groups__openpgp_only_mode__ignores_non_openpgp_only_groups()
{
const std::vector<KeyGroup> groups = {
createGroup("group@example.net", {
testKey("sender-openpgp@example.net", OpenPGP),
testKey("sender-smime@example.net", CMS)
}),
createGroup("group@example.net", {
testKey("prefer-smime@example.net", CMS)
}),
createGroup("group@example.net", {
testKey("prefer-openpgp@example.net", OpenPGP),
}),
};
KeyCache::mutableInstance()->setGroups(groups);
KeyResolverCore resolver(/*encrypt=*/ true, /*sign=*/ false, OpenPGP);
resolver.setRecipients({"group@example.net"});
const auto result = resolver.resolve();
QCOMPARE(result.flags & KeyResolverCore::ResolvedMask, KeyResolverCore::AllResolved);
QCOMPARE(result.flags & KeyResolverCore::ProtocolsMask, KeyResolverCore::OpenPGPOnly);
QCOMPARE(result.solution.protocol, OpenPGP);
QCOMPARE(result.solution.encryptionKeys.value("group@example.net").size(), 1);
QCOMPARE(result.solution.encryptionKeys.value("group@example.net")[0].primaryFingerprint(),
testKey("prefer-openpgp@example.net", OpenPGP).primaryFingerprint());
}
void test_groups__smime_only_mode__ignores_non_smime_only_groups()
{
const std::vector<KeyGroup> groups = {
createGroup("group@example.net", {
testKey("sender-openpgp@example.net", OpenPGP),
testKey("sender-smime@example.net", CMS)
}),
createGroup("group@example.net", {
testKey("prefer-smime@example.net", CMS)
}),
createGroup("group@example.net", {
testKey("prefer-openpgp@example.net", OpenPGP),
}),
};
KeyCache::mutableInstance()->setGroups(groups);
KeyResolverCore resolver(/*encrypt=*/ true, /*sign=*/ false, CMS);
resolver.setRecipients({"group@example.net"});
const auto result = resolver.resolve();
QCOMPARE(result.flags & KeyResolverCore::ResolvedMask, KeyResolverCore::AllResolved);
QCOMPARE(result.flags & KeyResolverCore::ProtocolsMask, KeyResolverCore::CMSOnly);
QCOMPARE(result.solution.protocol, CMS);
QCOMPARE(result.solution.encryptionKeys.value("group@example.net").size(), 1);
QCOMPARE(result.solution.encryptionKeys.value("group@example.net")[0].primaryFingerprint(),
testKey("prefer-smime@example.net", CMS).primaryFingerprint());
}
void test_groups__single_protocol_mode__ignores_mixed_protocol_groups()
{
const std::vector<KeyGroup> groups = {
createGroup("sender-mixed@example.net", {
testKey("sender-openpgp@example.net", OpenPGP),
testKey("sender-smime@example.net", CMS)
}),
};
KeyCache::mutableInstance()->setGroups(groups);
KeyResolverCore resolver(/*encrypt=*/ true, /*sign=*/ false);
resolver.setAllowMixedProtocols(false);
resolver.setRecipients({"sender-mixed@example.net"});
const auto result = resolver.resolve();
QCOMPARE(result.flags & KeyResolverCore::ResolvedMask, KeyResolverCore::AllResolved);
QCOMPARE(result.flags & KeyResolverCore::ProtocolsMask, KeyResolverCore::OpenPGPOnly);
QCOMPARE(result.solution.protocol, OpenPGP);
QCOMPARE(result.solution.encryptionKeys.value("sender-mixed@example.net").size(), 1);
QCOMPARE(result.solution.encryptionKeys.value("sender-mixed@example.net")[0].primaryFingerprint(),
testKey("sender-mixed@example.net", OpenPGP).primaryFingerprint());
}
void test_groups__mixed_mode__single_protocol_groups_are_preferred_over_mixed_protocol_groups()
{
const std::vector<KeyGroup> groups = {
createGroup("group@example.net", {
testKey("sender-openpgp@example.net", OpenPGP),
testKey("sender-smime@example.net", CMS)
}),
createGroup("group@example.net", {
testKey("prefer-smime@example.net", CMS)
}),
createGroup("group@example.net", {
testKey("prefer-openpgp@example.net", OpenPGP),
}),
};
KeyCache::mutableInstance()->setGroups(groups);
KeyResolverCore resolver(/*encrypt=*/ true, /*sign=*/ false);
resolver.setRecipients({"group@example.net"});
const auto result = resolver.resolve();
QCOMPARE(result.flags & KeyResolverCore::ResolvedMask, KeyResolverCore::AllResolved);
QCOMPARE(result.flags & KeyResolverCore::ProtocolsMask, KeyResolverCore::OpenPGPOnly);
QCOMPARE(result.solution.protocol, OpenPGP);
QCOMPARE(result.solution.encryptionKeys.value("group@example.net").size(), 1);
QCOMPARE(result.solution.encryptionKeys.value("group@example.net")[0].primaryFingerprint(),
testKey("prefer-openpgp@example.net", OpenPGP).primaryFingerprint());
}
void test_groups__mixed_mode__openpgp_only_group_preferred_over_mixed_protocol_group()
{
const std::vector<KeyGroup> groups = {
createGroup("group@example.net", {
testKey("sender-openpgp@example.net", OpenPGP),
testKey("sender-smime@example.net", CMS)
}),
createGroup("group@example.net", {
testKey("sender-openpgp@example.net", OpenPGP)
})
}),
};
KeyCache::mutableInstance()->setGroups(groups);
KeyResolverCore resolver(/*encrypt=*/ true, /*sign=*/ false);
......@@ -661,6 +772,50 @@ private Q_SLOTS:
testKey("sender-openpgp@example.net", OpenPGP).primaryFingerprint());
}
void test_groups__mixed_mode__smime_only_group_preferred_over_mixed_protocol_group()
{
const std::vector<KeyGroup> groups = {
createGroup("group@example.net", {
testKey("sender-openpgp@example.net", OpenPGP),
testKey("sender-smime@example.net", CMS)
}),
createGroup("group@example.net", {
testKey("sender-smime@example.net", CMS)
}),
};
KeyCache::mutableInstance()->setGroups(groups);
KeyResolverCore resolver(/*encrypt=*/ true, /*sign=*/ false);
resolver.setRecipients({"group@example.net"});
const auto result = resolver.resolve();
QCOMPARE(result.flags & KeyResolverCore::ResolvedMask, KeyResolverCore::AllResolved);
QCOMPARE(result.flags & KeyResolverCore::ProtocolsMask, KeyResolverCore::CMSOnly);
QCOMPARE(result.solution.encryptionKeys.value("group@example.net").size(), 1);
QCOMPARE(result.solution.encryptionKeys.value("group@example.net")[0].primaryFingerprint(),
testKey("sender-smime@example.net", CMS).primaryFingerprint());
}
void test_groups__mixed_mode__mixed_protocol_groups_are_used()
{
const std::vector<KeyGroup> groups = {
createGroup("sender-mixed@example.net", {
testKey("sender-openpgp@example.net", OpenPGP),
testKey("sender-smime@example.net", CMS)
}),
};
KeyCache::mutableInstance()->setGroups(groups);
KeyResolverCore resolver(/*encrypt=*/ true, /*sign=*/ false);
resolver.setRecipients({"sender-mixed@example.net"});
const auto result = resolver.resolve();
QCOMPARE(result.flags & KeyResolverCore::ResolvedMask, KeyResolverCore::AllResolved);
QCOMPARE(result.flags & KeyResolverCore::ProtocolsMask, KeyResolverCore::MixedProtocols);
QCOMPARE(result.solution.protocol, UnknownProtocol);
QCOMPARE(result.solution.encryptionKeys.value("sender-mixed@example.net").size(), 2);
}
private:
Key testKey(const char *email, Protocol protocol = UnknownProtocol)
{
......
......@@ -104,6 +104,8 @@ public:
void addRecipients(const QStringList &addresses);
void setOverrideKeys(const QMap<Protocol, QMap<QString, QStringList>> &overrides);
void resolveOverrides();
std::vector<Key> resolveRecipientWithGroup(const QString &address, Protocol protocol);
void resolveEncryptionGroups();
void resolveSign(Protocol proto);
void setSigningKeys(const QStringList &fingerprints);
std::vector<Key> resolveRecipient(const QString &address, Protocol protocol);
......@@ -324,32 +326,79 @@ void KeyResolverCore::Private::setSigningKeys(const QStringList &fingerprints)
}
}
std::vector<Key> KeyResolverCore::Private::resolveRecipient(const QString &address, Protocol protocol)
std::vector<Key> KeyResolverCore::Private::resolveRecipientWithGroup(const QString &address, Protocol protocol)
{
const auto group = mCache->findGroup(address, protocol, KeyUsage::Encrypt);
if (!group.isNull() && group.keys().size() > 0) {
// If we have one unacceptable group key we reject the
// whole group to avoid the situation where one key is
// skipped or the operation fails.
//
// We are in Autoresolve land here. In the GUI we
// will also show unacceptable group keys so that the
// user can see which key is not acceptable.
const auto &keys = group.keys();
const bool allKeysAreAcceptable =
std::all_of(std::begin(keys), std::end(keys), [this] (const auto &key) { return isAcceptableEncryptionKey(key); });
if (!allKeysAreAcceptable) {
qCDebug(LIBKLEO_LOG) << "group" << group.name() << "has at least one unacceptable key";
return {};
if (group.isNull()) {
return {};
}
// If we have one unacceptable group key we reject the
// whole group to avoid the situation where one key is
// skipped or the operation fails.
//
// We are in Autoresolve land here. In the GUI we
// will also show unacceptable group keys so that the
// user can see which key is not acceptable.
const auto &keys = group.keys();
const bool allKeysAreAcceptable =
std::all_of(std::begin(keys), std::end(keys), [this] (const auto &key) { return isAcceptableEncryptionKey(key); });
if (!allKeysAreAcceptable) {
qCDebug(LIBKLEO_LOG) << "group" << group.name() << "has at least one unacceptable key";
return {};
}
for (const auto &k: keys) {
qCDebug(LIBKLEO_LOG) << "Resolved encrypt to" << address << "with key" << k.primaryFingerprint();
}
std::vector<Key> result;
std::copy(std::begin(keys), std::end(keys), std::back_inserter(result));
return result;
}
void KeyResolverCore::Private::resolveEncryptionGroups()
{
for (auto it = mEncKeys.begin(); it != mEncKeys.end(); ++it) {
const QString &address = it.key();
auto &protocolKeysMap = it.value();
if (!protocolKeysMap[UnknownProtocol].empty()) {
// already resolved by common override
continue;
}
for (const auto &k: keys) {
qCDebug(LIBKLEO_LOG) << "Resolved encrypt to" << address << "with key" << k.primaryFingerprint();
if (mFormat == OpenPGP) {
if (!protocolKeysMap[OpenPGP].empty()) {
// already resolved by override
continue;
}
protocolKeysMap[OpenPGP] = resolveRecipientWithGroup(address, OpenPGP);
} else if (mFormat == CMS) {
if (!protocolKeysMap[CMS].empty()) {
// already resolved by override
continue;
}
protocolKeysMap[CMS] = resolveRecipientWithGroup(address, CMS);
} else {
// prefer single-protocol groups over mixed-protocol groups
const auto openPGPGroupKeys = resolveRecipientWithGroup(address, OpenPGP);
const auto smimeGroupKeys = resolveRecipientWithGroup(address, CMS);
if (!openPGPGroupKeys.empty() && !smimeGroupKeys.empty()) {
protocolKeysMap[OpenPGP] = openPGPGroupKeys;
protocolKeysMap[CMS] = smimeGroupKeys;
} else if (openPGPGroupKeys.empty() && smimeGroupKeys.empty()) {
// no single-protocol groups found;
// if mixed protocols are allowed, then look for any group with encryption keys
if (mAllowMixed) {
protocolKeysMap[UnknownProtocol] = resolveRecipientWithGroup(address, UnknownProtocol);
}
} else {
// there is a single-protocol group only for one protocol; use this group for all protocols
protocolKeysMap[UnknownProtocol] = !openPGPGroupKeys.empty() ? openPGPGroupKeys : smimeGroupKeys;
}
}
std::vector<Key> result;
std::copy(std::begin(keys), std::end(keys), std::back_inserter(result));
return result;
}
}
std::vector<Key> KeyResolverCore::Private::resolveRecipient(const QString &address, Protocol protocol)
{
const auto key = mCache->findBestByMailBox(address.toUtf8().constData(), protocol, KeyUsage::Encrypt);
if (key.isNull()) {
qCDebug(LIBKLEO_LOG) << "Failed to find any" << Formatting::displayName(protocol) << "key for:" << address;
......@@ -371,17 +420,17 @@ void KeyResolverCore::Private::resolveEnc(Protocol proto)
const QString &address = it.key();
auto &protocolKeysMap = it.value();
if (!protocolKeysMap[proto].empty()) {
// already resolved for current protocol (by override)
// already resolved for current protocol (by override or group)
continue;
}
const std::vector<Key> &commonOverride = protocolKeysMap[UnknownProtocol];
if (!commonOverride.empty()) {
// there is a common override; use it for current protocol if possible
if (allKeysHaveProtocol(commonOverride, proto)) {
protocolKeysMap[proto] = commonOverride;
const std::vector<Key> &commonOverrideOrGroup = protocolKeysMap[UnknownProtocol];
if (!commonOverrideOrGroup.empty()) {
// there is a common override or group; use it for current protocol if possible
if (allKeysHaveProtocol(commonOverrideOrGroup, proto)) {
protocolKeysMap[proto] = commonOverrideOrGroup;
continue;
} else {
qCDebug(LIBKLEO_LOG) << "Common override for" << address << "is unusable for" << Formatting::displayName(proto);
qCDebug(LIBKLEO_LOG) << "Common override/group for" << address << "is unusable for" << Formatting::displayName(proto);
continue;
}
}
......@@ -498,6 +547,9 @@ KeyResolverCore::Result KeyResolverCore::Private::resolve()
return {Error, {}, {}};
}
// Next look for matching groups of keys
resolveEncryptionGroups();
// Then look for signing / encryption keys
if (mFormat == OpenPGP || mFormat == UnknownProtocol) {
resolveSign(OpenPGP);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment