diff --git a/src/kleo/defaultkeyfilter.cpp b/src/kleo/defaultkeyfilter.cpp index 53aa867dc..ee6c9c284 100644 --- a/src/kleo/defaultkeyfilter.cpp +++ b/src/kleo/defaultkeyfilter.cpp @@ -1,590 +1,589 @@ /* defaultkeyfilter.cpp This file is part of libkleopatra, the KDE keymanagement library SPDX-FileCopyrightText: 2004 Klarälvdalens Datakonsult AB SPDX-FileCopyrightText: 2016 Bundesamt für Sicherheit in der Informationstechnik SPDX-FileContributor: Intevation GmbH SPDX-License-Identifier: GPL-2.0-or-later */ #include #include "defaultkeyfilter.h" #if GPGMEPP_KEY_HAS_HASCERTIFY_SIGN_ENCRYPT_AUTHENTICATE #else #include #endif #include #include #include #include -#include using namespace GpgME; using namespace Kleo; static bool is_card_key(const Key &key) { const std::vector sks = key.subkeys(); return std::find_if(sks.begin(), sks.end(), std::mem_fn(&Subkey::isCardKey)) != sks.end(); } class DefaultKeyFilter::Private { public: Private() { } QColor mFgColor; QColor mBgColor; QString mName; QString mIcon; QString mId; MatchContexts mMatchContexts = AnyMatchContext; unsigned int mSpecificity = 0; bool mItalic = false; bool mBold = false; bool mStrikeOut = false; bool mUseFullFont = false; QFont mFont; TriState mRevoked = DoesNotMatter; TriState mExpired = DoesNotMatter; TriState mInvalid = DoesNotMatter; TriState mDisabled = DoesNotMatter; TriState mRoot = DoesNotMatter; TriState mCanEncrypt = DoesNotMatter; TriState mCanSign = DoesNotMatter; TriState mCanCertify = DoesNotMatter; TriState mCanAuthenticate = DoesNotMatter; TriState mHasEncrypt = DoesNotMatter; TriState mHasSign = DoesNotMatter; TriState mHasCertify = DoesNotMatter; TriState mHasAuthenticate = DoesNotMatter; TriState mQualified = DoesNotMatter; TriState mCardKey = DoesNotMatter; TriState mHasSecret = DoesNotMatter; TriState mIsOpenPGP = DoesNotMatter; TriState mWasValidated = DoesNotMatter; TriState mIsDeVs = DoesNotMatter; TriState mBad = DoesNotMatter; TriState mValidIfSMIME = DoesNotMatter; LevelState mOwnerTrust = LevelDoesNotMatter; GpgME::Key::OwnerTrust mOwnerTrustReferenceLevel = Key::OwnerTrust::Unknown; LevelState mValidity = LevelDoesNotMatter; GpgME::UserID::Validity mValidityReferenceLevel = UserID::Validity::Unknown; }; DefaultKeyFilter::DefaultKeyFilter() : KeyFilter{} , d{new Private} { } DefaultKeyFilter::~DefaultKeyFilter() = default; bool DefaultKeyFilter::matches(const Key &key, MatchContexts contexts) const { if (!(d->mMatchContexts & contexts)) { return false; } #ifdef MATCH #undef MATCH #endif #define MATCH(member, method) \ do { \ if (member != DoesNotMatter && key.method() != bool(member == Set)) { \ return false; \ } \ } while (false) #define IS_MATCH(what) MATCH(d->m##what, is##what) #define CAN_MATCH(what) MATCH(d->mCan##what, can##what) #if GPGMEPP_KEY_HAS_HASCERTIFY_SIGN_ENCRYPT_AUTHENTICATE #define HAS_MATCH(what) MATCH(d->mHas##what, has##what) #else #define HAS_MATCH(what) \ do { \ if (d->mHas##what != DoesNotMatter && Kleo::keyHas##what(key) != bool(d->mHas##what == Set)) { \ return false; \ } \ } while (false) #endif IS_MATCH(Revoked); IS_MATCH(Expired); IS_MATCH(Invalid); IS_MATCH(Disabled); IS_MATCH(Root); CAN_MATCH(Encrypt); CAN_MATCH(Sign); CAN_MATCH(Certify); CAN_MATCH(Authenticate); HAS_MATCH(Encrypt); HAS_MATCH(Sign); HAS_MATCH(Certify); HAS_MATCH(Authenticate); IS_MATCH(Qualified); if (d->mCardKey != DoesNotMatter) { if ((d->mCardKey == Set && !is_card_key(key)) || (d->mCardKey == NotSet && is_card_key(key))) { return false; } } MATCH(d->mHasSecret, hasSecret); #undef MATCH if (d->mIsOpenPGP != DoesNotMatter && bool(key.protocol() == GpgME::OpenPGP) != bool(d->mIsOpenPGP == Set)) { return false; } if (d->mWasValidated != DoesNotMatter && bool(key.keyListMode() & GpgME::Validate) != bool(d->mWasValidated == Set)) { return false; } if (d->mIsDeVs != DoesNotMatter && bool(DeVSCompliance::keyIsCompliant(key)) != bool(d->mIsDeVs == Set)) { return false; } if (d->mBad != DoesNotMatter && /* This is similar to GPGME::Key::isBad which was introduced in GPGME 1.13.0 */ bool(key.isNull() || key.isRevoked() || key.isExpired() || key.isDisabled() || key.isInvalid()) != bool(d->mBad == Set)) { return false; } const UserID uid = key.userID(0); if ((key.protocol() == GpgME::CMS) // && (d->mValidIfSMIME != DoesNotMatter) // && (bool(uid.validity() >= UserID::Full) != bool(d->mValidIfSMIME == Set))) { return false; } switch (d->mOwnerTrust) { default: case LevelDoesNotMatter: break; case Is: if (key.ownerTrust() != d->mOwnerTrustReferenceLevel) { return false; } break; case IsNot: if (key.ownerTrust() == d->mOwnerTrustReferenceLevel) { return false; } break; case IsAtLeast: if (static_cast(key.ownerTrust()) < static_cast(d->mOwnerTrustReferenceLevel)) { return false; } break; case IsAtMost: if (static_cast(key.ownerTrust()) > static_cast(d->mOwnerTrustReferenceLevel)) { return false; } break; } switch (d->mValidity) { default: case LevelDoesNotMatter: break; case Is: if (uid.validity() != d->mValidityReferenceLevel) { return false; } break; case IsNot: if (uid.validity() == d->mValidityReferenceLevel) { return false; } break; case IsAtLeast: if (static_cast(uid.validity()) < static_cast(d->mValidityReferenceLevel)) { return false; } break; case IsAtMost: if (static_cast(uid.validity()) > static_cast(d->mValidityReferenceLevel)) { return false; } break; } return true; } KeyFilter::FontDescription DefaultKeyFilter::fontDescription() const { if (d->mUseFullFont) { return FontDescription::create(font(), bold(), italic(), strikeOut()); } else { return FontDescription::create(bold(), italic(), strikeOut()); } } void DefaultKeyFilter::setFgColor(const QColor &value) { d->mFgColor = value; } void DefaultKeyFilter::setBgColor(const QColor &value) { d->mBgColor = value; } void DefaultKeyFilter::setName(const QString &value) { d->mName = value; } void DefaultKeyFilter::setIcon(const QString &value) { d->mIcon = value; } void DefaultKeyFilter::setId(const QString &value) { d->mId = value; } void DefaultKeyFilter::setMatchContexts(MatchContexts value) { d->mMatchContexts = value; } void DefaultKeyFilter::setSpecificity(unsigned int value) { d->mSpecificity = value; } void DefaultKeyFilter::setItalic(bool value) { d->mItalic = value; } void DefaultKeyFilter::setBold(bool value) { d->mBold = value; } void DefaultKeyFilter::setStrikeOut(bool value) { d->mStrikeOut = value; } void DefaultKeyFilter::setUseFullFont(bool value) { d->mUseFullFont = value; } void DefaultKeyFilter::setFont(const QFont &value) { d->mFont = value; } void DefaultKeyFilter::setRevoked(DefaultKeyFilter::TriState value) { d->mRevoked = value; } void DefaultKeyFilter::setExpired(DefaultKeyFilter::TriState value) { d->mExpired = value; } void DefaultKeyFilter::setInvalid(DefaultKeyFilter::TriState value) { d->mInvalid = value; } void DefaultKeyFilter::setDisabled(DefaultKeyFilter::TriState value) { d->mDisabled = value; } void DefaultKeyFilter::setRoot(DefaultKeyFilter::TriState value) { d->mRoot = value; } void DefaultKeyFilter::setCanEncrypt(DefaultKeyFilter::TriState value) { d->mCanEncrypt = value; } void DefaultKeyFilter::setCanSign(DefaultKeyFilter::TriState value) { d->mCanSign = value; } void DefaultKeyFilter::setCanCertify(DefaultKeyFilter::TriState value) { d->mCanCertify = value; } void DefaultKeyFilter::setCanAuthenticate(DefaultKeyFilter::TriState value) { d->mCanAuthenticate = value; } void DefaultKeyFilter::setHasEncrypt(DefaultKeyFilter::TriState value) { d->mHasEncrypt = value; } void DefaultKeyFilter::setHasSign(DefaultKeyFilter::TriState value) { d->mHasSign = value; } void DefaultKeyFilter::setHasCertify(DefaultKeyFilter::TriState value) { d->mHasCertify = value; } void DefaultKeyFilter::setHasAuthenticate(DefaultKeyFilter::TriState value) { d->mHasAuthenticate = value; } void DefaultKeyFilter::setQualified(DefaultKeyFilter::TriState value) { d->mQualified = value; } void DefaultKeyFilter::setCardKey(DefaultKeyFilter::TriState value) { d->mCardKey = value; } void DefaultKeyFilter::setHasSecret(DefaultKeyFilter::TriState value) { d->mHasSecret = value; } void DefaultKeyFilter::setIsOpenPGP(DefaultKeyFilter::TriState value) { d->mIsOpenPGP = value; } void DefaultKeyFilter::setWasValidated(DefaultKeyFilter::TriState value) { d->mWasValidated = value; } void DefaultKeyFilter::setOwnerTrust(DefaultKeyFilter::LevelState value) { d->mOwnerTrust = value; } void DefaultKeyFilter::setOwnerTrustReferenceLevel(GpgME::Key::OwnerTrust value) { d->mOwnerTrustReferenceLevel = value; } void DefaultKeyFilter::setValidity(DefaultKeyFilter::LevelState value) { d->mValidity = value; } void DefaultKeyFilter::setValidityReferenceLevel(GpgME::UserID::Validity value) { d->mValidityReferenceLevel = value; } void DefaultKeyFilter::setIsDeVs(DefaultKeyFilter::TriState value) { d->mIsDeVs = value; } void DefaultKeyFilter::setIsBad(DefaultKeyFilter::TriState value) { d->mBad = value; } void DefaultKeyFilter::setValidIfSMIME(DefaultKeyFilter::TriState value) { d->mValidIfSMIME = value; } QColor DefaultKeyFilter::fgColor() const { return d->mFgColor; } QColor DefaultKeyFilter::bgColor() const { return d->mBgColor; } QString DefaultKeyFilter::name() const { return d->mName; } QString DefaultKeyFilter::icon() const { return d->mIcon; } QString DefaultKeyFilter::id() const { return d->mId; } QFont DefaultKeyFilter::font() const { return d->mFont; } KeyFilter::MatchContexts DefaultKeyFilter::availableMatchContexts() const { return d->mMatchContexts; } unsigned int DefaultKeyFilter::specificity() const { return d->mSpecificity; } bool DefaultKeyFilter::italic() const { return d->mItalic; } bool DefaultKeyFilter::bold() const { return d->mBold; } bool DefaultKeyFilter::strikeOut() const { return d->mStrikeOut; } bool DefaultKeyFilter::useFullFont() const { return d->mUseFullFont; } DefaultKeyFilter::TriState DefaultKeyFilter::revoked() const { return d->mRevoked; } DefaultKeyFilter::TriState DefaultKeyFilter::expired() const { return d->mExpired; } DefaultKeyFilter::TriState DefaultKeyFilter::invalid() const { return d->mInvalid; } DefaultKeyFilter::TriState DefaultKeyFilter::disabled() const { return d->mDisabled; } DefaultKeyFilter::TriState DefaultKeyFilter::root() const { return d->mRoot; } DefaultKeyFilter::TriState DefaultKeyFilter::canEncrypt() const { return d->mCanEncrypt; } DefaultKeyFilter::TriState DefaultKeyFilter::canSign() const { return d->mCanSign; } DefaultKeyFilter::TriState DefaultKeyFilter::canCertify() const { return d->mCanCertify; } DefaultKeyFilter::TriState DefaultKeyFilter::canAuthenticate() const { return d->mCanAuthenticate; } DefaultKeyFilter::TriState DefaultKeyFilter::hasEncrypt() const { return d->mHasEncrypt; } DefaultKeyFilter::TriState DefaultKeyFilter::hasSign() const { return d->mHasSign; } DefaultKeyFilter::TriState DefaultKeyFilter::hasCertify() const { return d->mHasCertify; } DefaultKeyFilter::TriState DefaultKeyFilter::hasAuthenticate() const { return d->mHasAuthenticate; } DefaultKeyFilter::TriState DefaultKeyFilter::qualified() const { return d->mQualified; } DefaultKeyFilter::TriState DefaultKeyFilter::cardKey() const { return d->mCardKey; } DefaultKeyFilter::TriState DefaultKeyFilter::hasSecret() const { return d->mHasSecret; } DefaultKeyFilter::TriState DefaultKeyFilter::isOpenPGP() const { return d->mIsOpenPGP; } DefaultKeyFilter::TriState DefaultKeyFilter::wasValidated() const { return d->mWasValidated; } DefaultKeyFilter::LevelState DefaultKeyFilter::ownerTrust() const { return d->mOwnerTrust; } GpgME::Key::OwnerTrust DefaultKeyFilter::ownerTrustReferenceLevel() const { return d->mOwnerTrustReferenceLevel; } DefaultKeyFilter::LevelState DefaultKeyFilter::validity() const { return d->mValidity; } GpgME::UserID::Validity DefaultKeyFilter::validityReferenceLevel() const { return d->mValidityReferenceLevel; } DefaultKeyFilter::TriState DefaultKeyFilter::isDeVS() const { return d->mIsDeVs; } DefaultKeyFilter::TriState DefaultKeyFilter::isBad() const { return d->mBad; } DefaultKeyFilter::TriState DefaultKeyFilter::validIfSMIME() const { return d->mValidIfSMIME; } diff --git a/src/utils/keyhelpers.cpp b/src/utils/keyhelpers.cpp index 00937f053..7df4ba545 100644 --- a/src/utils/keyhelpers.cpp +++ b/src/utils/keyhelpers.cpp @@ -1,295 +1,294 @@ /* utils/keyhelpers.cpp This file is part of libkleopatra, the KDE keymanagement library SPDX-FileCopyrightText: 2022 g10 Code GmbH SPDX-FileContributor: Ingo Klöcker SPDX-License-Identifier: GPL-2.0-or-later */ #include #include "keyhelpers.h" #include #include #include #include #include // needed for GPGME_VERSION_NUMBER #include -#include #include using namespace Kleo; using namespace GpgME; namespace { bool havePublicKeyForSignature(const GpgME::UserID::Signature &signature) { // GnuPG returns status "NoPublicKey" for missing signing keys, but also // for expired or revoked signing keys. return (signature.status() != GpgME::UserID::Signature::NoPublicKey) // || !KeyCache::instance()->findByKeyIDOrFingerprint(signature.signerKeyID()).isNull(); } auto _getMissingSignerKeyIds(const std::vector &signatures) { return std::accumulate(std::begin(signatures), std::end(signatures), std::set{}, [](auto &keyIds, const auto &signature) { if (!havePublicKeyForSignature(signature)) { keyIds.insert(QLatin1String{signature.signerKeyID()}); } return keyIds; }); } } std::set Kleo::getMissingSignerKeyIds(const std::vector &userIds) { return std::accumulate(std::begin(userIds), std::end(userIds), std::set{}, [](auto &keyIds, const auto &userID) { if (!userID.isBad()) { const auto newKeyIds = _getMissingSignerKeyIds(userID.signatures()); std::copy(std::begin(newKeyIds), std::end(newKeyIds), std::inserter(keyIds, std::end(keyIds))); } return keyIds; }); } std::set Kleo::getMissingSignerKeyIds(const std::vector &keys) { return std::accumulate(std::begin(keys), std::end(keys), std::set{}, [](auto &keyIds, const auto &key) { if (!key.isBad()) { const auto newKeyIds = getMissingSignerKeyIds(key.userIDs()); std::copy(std::begin(newKeyIds), std::end(newKeyIds), std::inserter(keyIds, std::end(keyIds))); } return keyIds; }); } bool Kleo::isRemoteKey(const GpgME::Key &key) { // a remote key looked up via WKD has key list mode Local; therefore we also look for the key in the local key ring return (key.keyListMode() == GpgME::Extern) || KeyCache::instance()->findByFingerprint(key.primaryFingerprint()).isNull(); } GpgME::UserID::Validity Kleo::minimalValidityOfNotRevokedUserIDs(const Key &key) { const std::vector userIDs = key.userIDs(); const int minValidity = std::accumulate(userIDs.begin(), userIDs.end(), UserID::Ultimate + 1, [](int validity, const UserID &userID) { return userID.isRevoked() ? validity : std::min(validity, static_cast(userID.validity())); }); return minValidity <= UserID::Ultimate ? static_cast(minValidity) : UserID::Unknown; } GpgME::UserID::Validity Kleo::maximalValidityOfUserIDs(const Key &key) { const auto userIDs = key.userIDs(); const int maxValidity = std::accumulate(userIDs.begin(), userIDs.end(), 0, [](int validity, const UserID &userID) { return std::max(validity, static_cast(userID.validity())); }); return static_cast(maxValidity); } bool Kleo::allUserIDsHaveFullValidity(const GpgME::Key &key) { return minimalValidityOfNotRevokedUserIDs(key) >= UserID::Full; } namespace { bool isLastValidUserID(const GpgME::UserID &userId) { if (Kleo::isRevokedOrExpired(userId)) { return false; } const auto userIds = userId.parent().userIDs(); const int numberOfValidUserIds = std::count_if(std::begin(userIds), std::end(userIds), [](const auto &u) { return !Kleo::isRevokedOrExpired(u); }); return numberOfValidUserIds == 1; } bool hasValidUserID(const GpgME::Key &key) { return Kleo::any_of(key.userIDs(), [](const auto &u) { return !Kleo::isRevokedOrExpired(u); }); } } bool Kleo::isSelfSignature(const GpgME::UserID::Signature &signature) { return !qstrcmp(signature.parent().parent().keyID(), signature.signerKeyID()); } bool Kleo::isRevokedOrExpired(const GpgME::UserID &userId) { if (userId.isRevoked() || userId.parent().isExpired()) { return true; } const auto sigs = userId.signatures(); std::vector selfSigs; std::copy_if(std::begin(sigs), std::end(sigs), std::back_inserter(selfSigs), &Kleo::isSelfSignature); std::sort(std::begin(selfSigs), std::end(selfSigs)); // check the most recent signature const auto sig = !selfSigs.empty() ? selfSigs.back() : GpgME::UserID::Signature{}; return !sig.isNull() && (sig.isRevokation() || sig.isExpired()); } bool Kleo::canCreateCertifications(const GpgME::Key &key) { return Kleo::keyHasCertify(key) && canBeUsedForSecretKeyOperations(key); } bool Kleo::canBeCertified(const GpgME::Key &key) { return key.protocol() == GpgME::OpenPGP // && !key.isBad() // && hasValidUserID(key); } namespace { static inline bool subkeyHasSecret(const GpgME::Subkey &subkey) { #if GPGME_VERSION_NUMBER >= 0x011102 // 1.17.2 // we need to check the primary subkey because Key::hasSecret() is also true if just the secret key stub of an offline key is available return subkey.isSecret(); #else // older versions of GpgME did not always set the secret flag for card keys return subkey.isSecret() || subkey.isCardKey(); #endif } } bool Kleo::canBeUsedForEncryption(const GpgME::Key &key) { return !key.isBad() && Kleo::any_of(key.subkeys(), [](const auto &subkey) { return subkey.canEncrypt() && !subkey.isBad(); }); } bool Kleo::canBeUsedForSigning(const GpgME::Key &key) { return !key.isBad() && Kleo::any_of(key.subkeys(), [](const auto &subkey) { return subkey.canSign() && !subkey.isBad() && subkeyHasSecret(subkey); }); } bool Kleo::canBeUsedForSecretKeyOperations(const GpgME::Key &key) { return subkeyHasSecret(key.subkey(0)); } bool Kleo::canRevokeUserID(const GpgME::UserID &userId) { return (!userId.isNull() // && userId.parent().protocol() == GpgME::OpenPGP // && !isLastValidUserID(userId)); } bool Kleo::isSecretKeyStoredInKeyRing(const GpgME::Key &key) { return key.subkey(0).isSecret() && !key.subkey(0).isCardKey(); } bool Kleo::userHasCertificationKey() { const auto secretKeys = KeyCache::instance()->secretKeys(); return Kleo::any_of(secretKeys, [](const auto &k) { return (k.protocol() == GpgME::OpenPGP) && canCreateCertifications(k); }); } Kleo::CertificationRevocationFeasibility Kleo::userCanRevokeCertification(const GpgME::UserID::Signature &certification) { const auto certificationKey = KeyCache::instance()->findByKeyIDOrFingerprint(certification.signerKeyID()); const bool isSelfSignature = qstrcmp(certification.parent().parent().keyID(), certification.signerKeyID()) == 0; if (!certificationKey.hasSecret()) { return CertificationNotMadeWithOwnKey; } else if (isSelfSignature) { return CertificationIsSelfSignature; } else if (certification.isRevokation()) { return CertificationIsRevocation; } else if (certification.isExpired()) { return CertificationIsExpired; } else if (certification.isInvalid()) { return CertificationIsInvalid; } else if (!canCreateCertifications(certificationKey)) { return CertificationKeyNotAvailable; } return CertificationCanBeRevoked; } bool Kleo::userCanRevokeCertifications(const GpgME::UserID &userId) { if (userId.numSignatures() == 0) { qCWarning(LIBKLEO_LOG) << __func__ << "- Error: Signatures of user ID" << QString::fromUtf8(userId.id()) << "not available"; } return Kleo::any_of(userId.signatures(), [](const auto &certification) { return userCanRevokeCertification(certification) == CertificationCanBeRevoked; }); } bool Kleo::userIDBelongsToKey(const GpgME::UserID &userID, const GpgME::Key &key) { return !qstricmp(userID.parent().primaryFingerprint(), key.primaryFingerprint()); } static time_t creationDate(const GpgME::UserID &uid) { // returns the date of the first self-signature for (unsigned int i = 0, numSignatures = uid.numSignatures(); i < numSignatures; ++i) { const auto sig = uid.signature(i); if (Kleo::isSelfSignature(sig)) { return sig.creationTime(); } } return 0; } bool Kleo::userIDsAreEqual(const GpgME::UserID &lhs, const GpgME::UserID &rhs) { return (qstrcmp(lhs.parent().primaryFingerprint(), rhs.parent().primaryFingerprint()) == 0 // && qstrcmp(lhs.id(), rhs.id()) == 0 // && creationDate(lhs) == creationDate(rhs)); } static inline bool isOpenPGPCertification(const GpgME::UserID::Signature &sig) { // certification class is 0x10, ..., 0x13 return (sig.certClass() & ~0x03) == 0x10; } static bool isOpenPGPCertificationByUser(const GpgME::UserID::Signature &sig) { if (!isOpenPGPCertification(sig)) { return false; } const auto certificationKey = KeyCache::instance()->findByKeyIDOrFingerprint(sig.signerKeyID()); return certificationKey.ownerTrust() == Key::Ultimate; } bool Kleo::userIDIsCertifiedByUser(const GpgME::UserID &userId) { if (userId.parent().protocol() != GpgME::OpenPGP) { qCWarning(LIBKLEO_LOG) << __func__ << "not called with OpenPGP key"; return false; } if (userId.numSignatures() == 0) { qCWarning(LIBKLEO_LOG) << __func__ << "- Error: Signatures of user ID" << QString::fromUtf8(userId.id()) << "not available"; } for (unsigned int i = 0, numSignatures = userId.numSignatures(); i < numSignatures; ++i) { const auto sig = userId.signature(i); if ((sig.status() == UserID::Signature::NoError) && !sig.isBad() && sig.isExportable() && isOpenPGPCertificationByUser(sig)) { return true; } } return false; }