File indexing completed on 2024-11-17 04:50:26
0001 /* 0002 This file is part of libkleopatra, the KDE keymanagement library 0003 SPDX-FileCopyrightText: 2004 Klarälvdalens Datakonsult AB 0004 SPDX-FileCopyrightText: 2021 Sandro Knauß <sknauss@kde.org> 0005 SPDX-FileCopyrightText: 2023 g10 Code GmbH 0006 SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de> 0007 0008 Based on kpgp.h 0009 Copyright (C) 2001,2002 the KPGP authors 0010 See file libkdenetwork/AUTHORS.kpgp for details 0011 0012 SPDX-License-Identifier: LGPL-2.0-or-later 0013 */ 0014 0015 #include "expirychecker.h" 0016 0017 #include "debug.h" 0018 #include "dn.h" 0019 #include "expirycheckersettings.h" 0020 0021 #include <libkleo/algorithm.h> 0022 #include <libkleo/keycache.h> 0023 #include <libkleo_debug.h> 0024 0025 #include <KLocalizedString> 0026 0027 #include <QGpgME/KeyListJob> 0028 #include <QGpgME/Protocol> 0029 0030 #include <gpgme++/keylistresult.h> 0031 0032 #include <set> 0033 0034 #include <cmath> 0035 #include <ctime> 0036 0037 using namespace Kleo; 0038 0039 class Kleo::ExpiryCheckerPrivate 0040 { 0041 Kleo::ExpiryChecker *q; 0042 0043 public: 0044 ExpiryCheckerPrivate(ExpiryChecker *qq, const ExpiryCheckerSettings &settings_) 0045 : q{qq} 0046 , settings{settings_} 0047 { 0048 } 0049 0050 ExpiryChecker::Expiration calculateExpiration(const GpgME::Subkey &subkey) const; 0051 ExpiryChecker::Expiration checkForExpiration(const GpgME::Key &key, Kleo::chrono::days threshold, ExpiryChecker::CheckFlags flags) const; 0052 0053 ExpiryChecker::Result checkKeyNearExpiry(const GpgME::Key &key, ExpiryChecker::CheckFlags flags); 0054 0055 ExpiryCheckerSettings settings; 0056 std::set<QByteArray> alreadyWarnedFingerprints; 0057 std::shared_ptr<TimeProvider> timeProvider; 0058 }; 0059 0060 ExpiryChecker::ExpiryChecker(const ExpiryCheckerSettings &settings, QObject *parent) 0061 : QObject{parent} 0062 , d{new ExpiryCheckerPrivate{this, settings}} 0063 { 0064 } 0065 0066 ExpiryChecker::~ExpiryChecker() = default; 0067 0068 ExpiryCheckerSettings ExpiryChecker::settings() const 0069 { 0070 return d->settings; 0071 } 0072 0073 QString formatOpenPGPMessage(ExpiryChecker::Expiration expiration, ExpiryChecker::CheckFlags flags) 0074 { 0075 const GpgME::Key key = expiration.certificate; 0076 const bool isOwnKey = flags & ExpiryChecker::OwnKey; 0077 const bool isSigningKey = flags & ExpiryChecker::SigningKey; 0078 const auto keyInfo = ki18nc("<b>User ID of key</b> (KeyID key ID of key in hex notation)", "<b>%1</b> (KeyID 0x%2)") 0079 .subs(QString::fromUtf8(key.userID(0).id())) 0080 .subs(QString::fromLatin1(key.keyID())); 0081 if (expiration.status == ExpiryChecker::Expired) { 0082 qCDebug(LIBKLEO_LOG) << "Key" << key << "expired" << expiration.duration.count() << "days ago"; 0083 if (expiration.duration.count() == 0) { 0084 KLocalizedString msg; 0085 if (isSigningKey) { 0086 msg = ki18n("<p>Your OpenPGP signing key</p><p align=center>%1</p><p>expired less than a day ago.</p>"); 0087 } else if (isOwnKey) { 0088 msg = ki18n("<p>Your OpenPGP encryption key</p><p align=center>%1</p><p>expired less than a day ago.</p>"); 0089 } else { 0090 msg = ki18n("<p>The OpenPGP key for</p><p align=center>%1</p><p>expired less than a day ago.</p>"); 0091 } 0092 return msg.subs(keyInfo).toString(); 0093 } 0094 KLocalizedString msg; 0095 if (isSigningKey) { 0096 msg = ki18np("<p>Your OpenPGP signing key</p><p align=center>%2</p><p>expired yesterday.</p>", 0097 "<p>Your OpenPGP signing key</p><p align=center>%2</p><p>expired %1 days ago.</p>"); 0098 } else if (isOwnKey) { 0099 msg = ki18np("<p>Your OpenPGP encryption key</p><p align=center>%2</p><p>expired yesterday.</p>", 0100 "<p>Your OpenPGP encryption key</p><p align=center>%2</p><p>expired %1 days ago.</p>"); 0101 } else { 0102 msg = ki18np("<p>The OpenPGP key for</p><p align=center>%2</p><p>expired yesterday.</p>", 0103 "<p>The OpenPGP key for</p><p align=center>%2</p><p>expired %1 days ago.</p>"); 0104 } 0105 return msg.subs(expiration.duration.count()).subs(keyInfo).toString(); 0106 } 0107 qCDebug(LIBKLEO_LOG) << "Key" << key << "expires in" << expiration.duration.count() << "days"; 0108 if (expiration.duration.count() == 0) { 0109 KLocalizedString msg; 0110 if (isSigningKey) { 0111 msg = ki18n("<p>Your OpenPGP signing key</p><p align=center>%1</p><p>expires today.</p>"); 0112 } else if (isOwnKey) { 0113 msg = ki18n("<p>Your OpenPGP encryption key</p><p align=center>%1</p><p>expires today.</p>"); 0114 } else { 0115 msg = ki18n("<p>The OpenPGP key for</p><p align=center>%1</p><p>expires today.</p>"); 0116 } 0117 return msg.subs(keyInfo).toString(); 0118 } 0119 KLocalizedString msg; 0120 if (isSigningKey) { 0121 msg = ki18np("<p>Your OpenPGP signing key</p><p align=center>%2</p><p>expires tomorrow.</p>", 0122 "<p>Your OpenPGP signing key</p><p align=center>%2</p><p>expires in %1 days.</p>"); 0123 } else if (isOwnKey) { 0124 msg = ki18np("<p>Your OpenPGP encryption key</p><p align=center>%2</p><p>expires tomorrow.</p>", 0125 "<p>Your OpenPGP encryption key</p><p align=center>%2</p><p>expires in %1 days.</p>"); 0126 } else { 0127 msg = ki18np("<p>The OpenPGP key for</p><p align=center>%2</p><p>expires tomorrow.</p>", 0128 "<p>The OpenPGP key for</p><p align=center>%2</p><p>expires in %1 days.</p>"); 0129 } 0130 return msg.subs(expiration.duration.count()).subs(keyInfo).toString(); 0131 } 0132 0133 QString formatSMIMEMessage(const GpgME::Key &orig_key, ExpiryChecker::Expiration expiration, ExpiryChecker::CheckFlags flags, bool ca) 0134 { 0135 const GpgME::Key key = expiration.certificate; 0136 const bool isOwnKey = flags & ExpiryChecker::OwnKey; 0137 const bool isSigningKey = flags & ExpiryChecker::SigningKey; 0138 const auto userCert = orig_key.isNull() ? key : orig_key; 0139 const auto userCertInfo = ki18nc("<b>User ID of certificate</b> (serial number serial no. of certificate)", "<b>%1</b> (serial number %2)") 0140 .subs(Kleo::DN(userCert.userID(0).id()).prettyDN()) 0141 .subs(QString::fromLatin1(userCert.issuerSerial())); 0142 if (expiration.status == ExpiryChecker::Expired) { 0143 qCDebug(LIBKLEO_LOG) << "Certificate" << key << "expired" << expiration.duration.count() << "days ago"; 0144 if (ca) { 0145 if (key.isRoot()) { 0146 if (expiration.duration.count() == 0) { 0147 KLocalizedString msg; 0148 if (isSigningKey) { 0149 msg = ki18n( 0150 "<p>The root certificate</p><p align=center><b>%2</b></p>" 0151 "<p>for your S/MIME signing certificate</p><p align=center>%1</p>" 0152 "<p>expired less than a day ago.</p>"); 0153 } else if (isOwnKey) { 0154 msg = ki18n( 0155 "<p>The root certificate</p><p align=center><b>%2</b></p>" 0156 "<p>for your S/MIME encryption certificate</p><p align=center>%1</p>" 0157 "<p>expired less than a day ago.</p>"); 0158 } else { 0159 msg = ki18n( 0160 "<p>The root certificate</p><p align=center><b>%2</b></p>" 0161 "<p>for S/MIME certificate</p><p align=center>%1</p>" 0162 "<p>expired less than a day ago.</p>"); 0163 } 0164 return msg.subs(userCertInfo).subs(Kleo::DN(key.userID(0).id()).prettyDN()).toString(); 0165 } 0166 KLocalizedString msg; 0167 if (isSigningKey) { 0168 msg = ki18np( 0169 "<p>The root certificate</p><p align=center><b>%3</b></p>" 0170 "<p>for your S/MIME signing certificate</p><p align=center>%2</p>" 0171 "<p>expired yesterday.</p>", 0172 "<p>The root certificate</p><p align=center><b>%3</b></p>" 0173 "<p>for your S/MIME signing certificate</p><p align=center>%2</p>" 0174 "<p>expired %1 days ago.</p>"); 0175 } else if (isOwnKey) { 0176 msg = ki18np( 0177 "<p>The root certificate</p><p align=center><b>%3</b></p>" 0178 "<p>for your S/MIME encryption certificate</p><p align=center>%2</p>" 0179 "<p>expired yesterday.</p>", 0180 "<p>The root certificate</p><p align=center><b>%3</b></p>" 0181 "<p>for your S/MIME encryption certificate</p><p align=center>%2</p>" 0182 "<p>expired %1 days ago.</p>"); 0183 } else { 0184 msg = ki18np( 0185 "<p>The root certificate</p><p align=center><b>%3</b></p>" 0186 "<p>for S/MIME certificate</p><p align=center>%2</p>" 0187 "<p>expired yesterday.</p>", 0188 "<p>The root certificate</p><p align=center><b>%3</b></p>" 0189 "<p>for S/MIME certificate</p><p align=center>%2</p>" 0190 "<p>expired %1 days ago.</p>"); 0191 } 0192 return msg.subs(expiration.duration.count()).subs(userCertInfo).subs(Kleo::DN(key.userID(0).id()).prettyDN()).toString(); 0193 } else { 0194 if (expiration.duration.count() == 0) { 0195 KLocalizedString msg; 0196 if (isSigningKey) { 0197 msg = ki18n( 0198 "<p>The intermediate CA certificate</p><p align=center><b>%2</b></p>" 0199 "<p>for your S/MIME signing certificate</p><p align=center>%1</p>" 0200 "<p>expired less than a day ago.</p>"); 0201 } else if (isOwnKey) { 0202 msg = ki18n( 0203 "<p>The intermediate CA certificate</p><p align=center><b>%2</b></p>" 0204 "<p>for your S/MIME encryption certificate</p><p align=center>%1</p>" 0205 "<p>expired less than a day ago.</p>"); 0206 } else { 0207 msg = ki18n( 0208 "<p>The intermediate CA certificate</p><p align=center><b>%2</b></p>" 0209 "<p>for S/MIME certificate</p><p align=center>%1</p>" 0210 "<p>expired less than a day ago.</p>"); 0211 } 0212 return msg.subs(userCertInfo).subs(Kleo::DN(key.userID(0).id()).prettyDN()).toString(); 0213 } 0214 KLocalizedString msg; 0215 if (isSigningKey) { 0216 msg = ki18np( 0217 "<p>The intermediate CA certificate</p><p align=center><b>%3</b></p>" 0218 "<p>for your S/MIME signing certificate</p><p align=center>%2</p>" 0219 "<p>expired yesterday.</p>", 0220 "<p>The intermediate CA certificate</p><p align=center><b>%3</b></p>" 0221 "<p>for your S/MIME signing certificate</p><p align=center>%2</p>" 0222 "<p>expired %1 days ago.</p>"); 0223 } else if (isOwnKey) { 0224 msg = ki18np( 0225 "<p>The intermediate CA certificate</p><p align=center><b>%3</b></p>" 0226 "<p>for your S/MIME encryption certificate</p><p align=center>%2</p>" 0227 "<p>expired yesterday.</p>", 0228 "<p>The intermediate CA certificate</p><p align=center><b>%3</b></p>" 0229 "<p>for your S/MIME encryption certificate</p><p align=center>%2</p>" 0230 "<p>expired %1 days ago.</p>"); 0231 } else { 0232 msg = ki18np( 0233 "<p>The intermediate CA certificate</p><p align=center><b>%3</b></p>" 0234 "<p>for S/MIME certificate</p><p align=center>%2</p>" 0235 "<p>expired yesterday.</p>", 0236 "<p>The intermediate CA certificate</p><p align=center><b>%3</b></p>" 0237 "<p>for S/MIME certificate</p><p align=center>%2</p>" 0238 "<p>expired %1 days ago.</p>"); 0239 } 0240 return msg.subs(expiration.duration.count()).subs(userCertInfo).subs(Kleo::DN(key.userID(0).id()).prettyDN()).toString(); 0241 } 0242 } else { 0243 if (expiration.duration.count() == 0) { 0244 KLocalizedString msg; 0245 if (isSigningKey) { 0246 msg = ki18n("<p>Your S/MIME signing certificate</p><p align=center>%1</p><p>expired less than a day ago.</p>"); 0247 } else if (isOwnKey) { 0248 msg = ki18n("<p>Your S/MIME encryption certificate</p><p align=center>%1</p><p>expired less than a day ago.</p>"); 0249 } else { 0250 msg = ki18n("<p>The S/MIME certificate for</p><p align=center>%1</p><p>expired less than a day ago.</p>"); 0251 } 0252 return msg.subs(userCertInfo).toString(); 0253 } 0254 KLocalizedString msg; 0255 if (isSigningKey) { 0256 msg = ki18np("<p>Your S/MIME signing certificate</p><p align=center>%2</p><p>expired yesterday.</p>", 0257 "<p>Your S/MIME signing certificate</p><p align=center>%2</p><p>expired %1 days ago.</p>"); 0258 } else if (isOwnKey) { 0259 msg = ki18np("<p>Your S/MIME encryption certificate</p><p align=center>%2</p><p>expired yesterday.</p>", 0260 "<p>Your S/MIME encryption certificate</p><p align=center>%2</p><p>expired %1 days ago.</p>"); 0261 } else { 0262 msg = ki18np("<p>The S/MIME certificate for</p><p align=center>%2</p><p>expired yesterday.</p>", 0263 "<p>The S/MIME certificate for</p><p align=center>%2</p><p>expired %1 days ago.</p>"); 0264 } 0265 return msg.subs(expiration.duration.count()).subs(userCertInfo).toString(); 0266 } 0267 } 0268 qCDebug(LIBKLEO_LOG) << "Certificate" << key << "expires in" << expiration.duration.count() << "days"; 0269 if (ca) { 0270 if (key.isRoot()) { 0271 if (expiration.duration.count() == 0) { 0272 KLocalizedString msg; 0273 if (isSigningKey) { 0274 msg = ki18n( 0275 "<p>The root certificate</p><p align=center><b>%3</b></p>" 0276 "<p>for your S/MIME signing certificate</p><p align=center>%2</p>" 0277 "<p>expires today.</p>"); 0278 } else if (isOwnKey) { 0279 msg = ki18n( 0280 "<p>The root certificate</p><p align=center><b>%3</b></p>" 0281 "<p>for your S/MIME encryption certificate</p><p align=center>%2</p>" 0282 "<p>expires today.</p>"); 0283 } else { 0284 msg = ki18n( 0285 "<p>The root certificate</p><p align=center><b>%3</b></p>" 0286 "<p>for S/MIME certificate</p><p align=center>%2</p>" 0287 "<p>expires today.</p>"); 0288 } 0289 return msg.subs(userCertInfo).subs(Kleo::DN(key.userID(0).id()).prettyDN()).toString(); 0290 } 0291 KLocalizedString msg; 0292 if (isSigningKey) { 0293 msg = ki18np( 0294 "<p>The root certificate</p><p align=center><b>%3</b></p>" 0295 "<p>for your S/MIME signing certificate</p><p align=center>%2</p>" 0296 "<p>expires tomorrow.</p>", 0297 "<p>The root certificate</p><p align=center><b>%3</b></p>" 0298 "<p>for your S/MIME signing certificate</p><p align=center>%2</p>" 0299 "<p>expires in %1 days.</p>"); 0300 } else if (isOwnKey) { 0301 msg = ki18np( 0302 "<p>The root certificate</p><p align=center><b>%3</b></p>" 0303 "<p>for your S/MIME encryption certificate</p><p align=center>%2</p>" 0304 "<p>expires tomorrow.</p>", 0305 "<p>The root certificate</p><p align=center><b>%3</b></p>" 0306 "<p>for your S/MIME encryption certificate</p><p align=center>%2</p>" 0307 "<p>expires in %1 days.</p>"); 0308 } else { 0309 msg = ki18np( 0310 "<p>The root certificate</p><p align=center><b>%3</b></p>" 0311 "<p>for S/MIME certificate</p><p align=center>%2</p>" 0312 "<p>expires tomorrow.</p>", 0313 "<p>The root certificate</p><p align=center><b>%3</b></p>" 0314 "<p>for S/MIME certificate</p><p align=center>%2</p>" 0315 "<p>expires in %1 days.</p>"); 0316 } 0317 return msg.subs(expiration.duration.count()).subs(userCertInfo).subs(Kleo::DN(key.userID(0).id()).prettyDN()).toString(); 0318 } 0319 if (expiration.duration.count() == 0) { 0320 KLocalizedString msg; 0321 if (isSigningKey) { 0322 msg = ki18n( 0323 "<p>The intermediate CA certificate</p><p align=center><b>%3</b></p>" 0324 "<p>for your S/MIME signing certificate</p><p align=center>%2</p>" 0325 "<p>expires today.</p>"); 0326 } else if (isOwnKey) { 0327 msg = ki18n( 0328 "<p>The intermediate CA certificate</p><p align=center><b>%3</b></p>" 0329 "<p>for your S/MIME encryption certificate</p><p align=center>%2</p>" 0330 "<p>expires today.</p>"); 0331 } else { 0332 msg = ki18n( 0333 "<p>The intermediate CA certificate</p><p align=center><b>%3</b></p>" 0334 "<p>for S/MIME certificate</p><p align=center>%2</p>" 0335 "<p>expires today.</p>"); 0336 } 0337 } 0338 KLocalizedString msg; 0339 if (isSigningKey) { 0340 msg = ki18np( 0341 "<p>The intermediate CA certificate</p><p align=center><b>%3</b></p>" 0342 "<p>for your S/MIME signing certificate</p><p align=center>%2</p>" 0343 "<p>expires tomorrow.</p>", 0344 "<p>The intermediate CA certificate</p><p align=center><b>%3</b></p>" 0345 "<p>for your S/MIME signing certificate</p><p align=center>%2</p>" 0346 "<p>expires in %1 days.</p>"); 0347 } else if (isOwnKey) { 0348 msg = ki18np( 0349 "<p>The intermediate CA certificate</p><p align=center><b>%3</b></p>" 0350 "<p>for your S/MIME encryption certificate</p><p align=center>%2</p>" 0351 "<p>expires tomorrow.</p>", 0352 "<p>The intermediate CA certificate</p><p align=center><b>%3</b></p>" 0353 "<p>for your S/MIME encryption certificate</p><p align=center>%2</p>" 0354 "<p>expires in %1 days.</p>"); 0355 } else { 0356 msg = ki18np( 0357 "<p>The intermediate CA certificate</p><p align=center><b>%3</b></p>" 0358 "<p>for S/MIME certificate</p><p align=center>%2</p>" 0359 "<p>expires tomorrow.</p>", 0360 "<p>The intermediate CA certificate</p><p align=center><b>%3</b></p>" 0361 "<p>for S/MIME certificate</p><p align=center>%2</p>" 0362 "<p>expires in %1 days.</p>"); 0363 } 0364 return msg.subs(expiration.duration.count()).subs(userCertInfo).subs(Kleo::DN(key.userID(0).id()).prettyDN()).toString(); 0365 } 0366 if (expiration.duration.count() == 0) { 0367 KLocalizedString msg; 0368 if (isSigningKey) { 0369 msg = ki18n("<p>Your S/MIME signing certificate</p><p align=center>%2</p><p>expires today.</p>"); 0370 } else if (isOwnKey) { 0371 msg = ki18n("<p>Your S/MIME encryption certificate</p><p align=center>%2</p><p>expires today.</p>"); 0372 } else { 0373 msg = ki18n("<p>The S/MIME certificate for</p><p align=center>%2</p><p>expires today.</p>"); 0374 } 0375 return msg.subs(userCertInfo).toString(); 0376 } 0377 KLocalizedString msg; 0378 if (isSigningKey) { 0379 msg = ki18np( 0380 "<p>Your S/MIME signing certificate</p><p align=center>%2</p>" 0381 "<p>expires tomorrow.</p>", 0382 "<p>Your S/MIME signing certificate</p><p align=center>%2</p>" 0383 "<p>expires in %1 days.</p>"); 0384 } else if (isOwnKey) { 0385 msg = ki18np( 0386 "<p>Your S/MIME encryption certificate</p><p align=center>%2</p>" 0387 "<p>expires tomorrow.</p>", 0388 "<p>Your S/MIME encryption certificate</p><p align=center>%2</p>" 0389 "<p>expires in %1 days.</p>"); 0390 } else { 0391 msg = ki18np( 0392 "<p>The S/MIME certificate for</p><p align=center>%2</p>" 0393 "<p>expires tomorrow.</p>", 0394 "<p>The S/MIME certificate for</p><p align=center>%2</p>" 0395 "<p>expires in %1 days.</p>"); 0396 } 0397 return msg.subs(expiration.duration.count()).subs(userCertInfo).toString(); 0398 } 0399 0400 static GpgME::Subkey findBestSubkey(const GpgME::Key &key, ExpiryChecker::CheckFlags usageFlags) 0401 { 0402 // find the subkey with the latest expiration date for the given usage flags 0403 if (!(usageFlags & ExpiryChecker::UsageMask)) { 0404 // return primary key if no specific usage is specified (as for chain certificates) 0405 return key.subkey(0); 0406 } 0407 GpgME::Subkey result; 0408 for (unsigned int i = 0; i < key.numSubkeys(); ++i) { 0409 const auto subkey = key.subkey(i); 0410 if (subkey.isRevoked() || subkey.isInvalid() || subkey.isDisabled()) { 0411 // unusable subkey 0412 continue; 0413 } 0414 if (((usageFlags & ExpiryChecker::EncryptionKey) && !subkey.canEncrypt()) // 0415 || ((usageFlags & ExpiryChecker::SigningKey) && !subkey.canSign()) // 0416 || ((usageFlags & ExpiryChecker::CertificationKey) && !subkey.canCertify())) { 0417 // unsuitable subkey for requested usage 0418 continue; 0419 } 0420 if (subkey.neverExpires()) { 0421 // stop looking for the best subkey if we found a suitable subkey that doesn't expire; 0422 // return the primary key because a non-expiring subkey inherits the primary key's expiration 0423 return key.subkey(0); 0424 } 0425 if (quint32(subkey.expirationTime()) > quint32(result.expirationTime())) { 0426 result = subkey; 0427 } 0428 } 0429 return result; 0430 } 0431 0432 ExpiryChecker::Expiration ExpiryCheckerPrivate::calculateExpiration(const GpgME::Subkey &subkey) const 0433 { 0434 if (subkey.neverExpires()) { 0435 return {subkey.parent(), ExpiryChecker::NotNearExpiry, Kleo::chrono::days::zero()}; 0436 } 0437 const time_t currentTime = timeProvider ? timeProvider->currentTime() : std::time(nullptr); 0438 const auto currentDate = timeProvider ? timeProvider->currentDate() : QDate::currentDate(); 0439 const auto timeSpec = timeProvider ? timeProvider->timeSpec() : Qt::LocalTime; 0440 const time_t expirationTime = subkey.expirationTime(); 0441 const auto expirationDate = QDateTime::fromSecsSinceEpoch(quint32(expirationTime), timeSpec).date(); 0442 // use std::difftime to avoid problems with negative values on 32-bit systems 0443 if (std::difftime(expirationTime, currentTime) <= 0) { 0444 return {subkey.parent(), ExpiryChecker::Expired, Kleo::chrono::days{expirationDate.daysTo(currentDate)}}; 0445 } else { 0446 return {subkey.parent(), ExpiryChecker::ExpiresSoon, Kleo::chrono::days{currentDate.daysTo(expirationDate)}}; 0447 } 0448 } 0449 0450 ExpiryChecker::Expiration ExpiryCheckerPrivate::checkForExpiration(const GpgME::Key &key, // 0451 Kleo::chrono::days threshold, 0452 ExpiryChecker::CheckFlags usageFlags) const 0453 { 0454 const auto subkey = findBestSubkey(key, usageFlags); 0455 if (subkey.isNull()) { 0456 return {key, ExpiryChecker::NoSuitableSubkey, {}}; 0457 } 0458 ExpiryChecker::Expiration expiration = calculateExpiration(subkey); 0459 if ((expiration.status == ExpiryChecker::ExpiresSoon) && (expiration.duration > threshold)) { 0460 // key expires, but not too soon 0461 expiration.status = ExpiryChecker::NotNearExpiry; 0462 } 0463 return expiration; 0464 } 0465 0466 ExpiryChecker::Result ExpiryCheckerPrivate::checkKeyNearExpiry(const GpgME::Key &orig_key, ExpiryChecker::CheckFlags flags) 0467 { 0468 static const int maximumCertificateChainLength = 100; 0469 const bool isOwnKey = flags & ExpiryChecker::OwnKey; 0470 0471 ExpiryChecker::Result result; 0472 result.checkFlags = flags; 0473 result.expiration.certificate = orig_key; 0474 0475 // use vector instead of set because certificate chains are usually very short 0476 std::vector<std::string> checkedCertificates; 0477 auto key = orig_key; 0478 for (int chainCount = 0; chainCount < maximumCertificateChainLength; ++chainCount) { 0479 checkedCertificates.push_back(key.primaryFingerprint()); 0480 0481 const GpgME::Subkey subkey = key.subkey(0); 0482 0483 const bool newMessage = !alreadyWarnedFingerprints.count(subkey.fingerprint()); 0484 0485 const auto threshold = chainCount > 0 // 0486 ? (key.isRoot() ? settings.rootCertThreshold() : settings.chainCertThreshold()) // 0487 : (isOwnKey ? settings.ownKeyThreshold() : settings.otherKeyThreshold()); 0488 const auto usageFlags = (chainCount == 0) ? (flags & ExpiryChecker::UsageMask) : ExpiryChecker::CheckFlags{}; 0489 const auto expiration = checkForExpiration(key, threshold, usageFlags); 0490 if (chainCount == 0) { 0491 result.expiration = expiration; 0492 } else if (expiration.status != ExpiryChecker::NotNearExpiry) { 0493 result.chainExpiration.push_back(expiration); 0494 } 0495 if (expiration.status == ExpiryChecker::Expired) { 0496 const QString msg = key.protocol() == GpgME::OpenPGP // 0497 ? formatOpenPGPMessage(expiration, flags) 0498 : formatSMIMEMessage(orig_key, expiration, flags, chainCount > 0); 0499 alreadyWarnedFingerprints.insert(subkey.fingerprint()); 0500 Q_EMIT q->expiryMessage(key, msg, isOwnKey ? ExpiryChecker::OwnKeyExpired : ExpiryChecker::OtherKeyExpired, newMessage); 0501 } else if (expiration.status == ExpiryChecker::ExpiresSoon) { 0502 const QString msg = key.protocol() == GpgME::OpenPGP // 0503 ? formatOpenPGPMessage(expiration, flags) 0504 : formatSMIMEMessage(orig_key, expiration, flags, chainCount > 0); 0505 alreadyWarnedFingerprints.insert(subkey.fingerprint()); 0506 Q_EMIT q->expiryMessage(key, msg, isOwnKey ? ExpiryChecker::OwnKeyNearExpiry : ExpiryChecker::OtherKeyNearExpiry, newMessage); 0507 } else if (expiration.status == ExpiryChecker::NoSuitableSubkey) { 0508 break; 0509 } 0510 if (!(flags & ExpiryChecker::CheckChain) || key.isRoot() || (key.protocol() != GpgME::CMS)) { 0511 break; 0512 } 0513 const auto keys = KeyCache::instance()->findIssuers(key, KeyCache::NoOption); 0514 if (keys.empty()) { 0515 break; 0516 } 0517 key = keys.front(); 0518 if (Kleo::contains(checkedCertificates, key.primaryFingerprint())) { 0519 break; // this certificate was already checked (looks like a circle in the chain) 0520 } 0521 } 0522 return result; 0523 } 0524 0525 ExpiryChecker::Result ExpiryChecker::checkKey(const GpgME::Key &key, CheckFlags flags) const 0526 { 0527 if (key.isNull()) { 0528 qWarning(LIBKLEO_LOG) << __func__ << "called with null key"; 0529 return {flags, {key, InvalidKey, {}}, {}}; 0530 } 0531 if (!(flags & UsageMask)) { 0532 qWarning(LIBKLEO_LOG) << __func__ << "called with invalid flags:" << flags; 0533 return {flags, {key, InvalidCheckFlags, {}}, {}}; 0534 } 0535 return d->checkKeyNearExpiry(key, flags); 0536 } 0537 0538 void ExpiryChecker::setTimeProviderForTest(const std::shared_ptr<TimeProvider> &timeProvider) 0539 { 0540 d->timeProvider = timeProvider; 0541 } 0542 0543 #include "moc_expirychecker.cpp"