Libkleo

expirychecker.cpp
1/*
2 This file is part of libkleopatra, the KDE keymanagement library
3 SPDX-FileCopyrightText: 2004 Klarälvdalens Datakonsult AB
4 SPDX-FileCopyrightText: 2021 Sandro Knauß <sknauss@kde.org>
5 SPDX-FileCopyrightText: 2023 g10 Code GmbH
6 SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
7
8 Based on kpgp.h
9 Copyright (C) 2001,2002 the KPGP authors
10 See file libkdenetwork/AUTHORS.kpgp for details
11
12 SPDX-License-Identifier: LGPL-2.0-or-later
13*/
14
15#include "expirychecker.h"
16
17#include "debug.h"
18#include "dn.h"
19#include "expirycheckersettings.h"
20
21#include <libkleo/algorithm.h>
22#include <libkleo/keycache.h>
23#include <libkleo_debug.h>
24
25#include <KLocalizedString>
26
27#include <QGpgME/KeyListJob>
28#include <QGpgME/Protocol>
29
30#include <QTimeZone>
31
32#include <gpgme++/keylistresult.h>
33
34#include <set>
35
36#include <cmath>
37#include <ctime>
38
39using namespace Kleo;
40
41class Kleo::ExpiryCheckerPrivate
42{
43 Kleo::ExpiryChecker *q;
44
45public:
46 ExpiryCheckerPrivate(ExpiryChecker *qq, const ExpiryCheckerSettings &settings_)
47 : q{qq}
48 , settings{settings_}
49 {
50 }
51
52 ExpiryChecker::Expiration calculateExpiration(const GpgME::Subkey &subkey) const;
53 ExpiryChecker::Expiration checkForExpiration(const GpgME::Key &key, Kleo::chrono::days threshold, ExpiryChecker::CheckFlags flags) const;
54
55 ExpiryChecker::Result checkKeyNearExpiry(const GpgME::Key &key, ExpiryChecker::CheckFlags flags);
56
57 ExpiryCheckerSettings settings;
58 std::set<QByteArray> alreadyWarnedFingerprints;
59 std::shared_ptr<TimeProvider> timeProvider;
60};
61
62ExpiryChecker::ExpiryChecker(const ExpiryCheckerSettings &settings, QObject *parent)
63 : QObject{parent}
64 , d{new ExpiryCheckerPrivate{this, settings}}
65{
66}
67
68ExpiryChecker::~ExpiryChecker() = default;
69
70ExpiryCheckerSettings ExpiryChecker::settings() const
71{
72 return d->settings;
73}
74
75QString formatOpenPGPMessage(ExpiryChecker::Expiration expiration, ExpiryChecker::CheckFlags flags)
76{
77 const GpgME::Key key = expiration.certificate;
78 const bool isOwnKey = flags & ExpiryChecker::OwnKey;
79 const bool isSigningKey = flags & ExpiryChecker::SigningKey;
80 const auto keyInfo = ki18nc("<b>User ID of key</b> (Key ID key ID of key in hex notation)", "<b>%1</b> (Key ID 0x%2)")
81 .subs(QString::fromUtf8(key.userID(0).id()))
82 .subs(QString::fromLatin1(key.keyID()));
83 if (expiration.status == ExpiryChecker::Expired) {
84 qCDebug(LIBKLEO_LOG) << "Key" << key << "expired" << expiration.duration.count() << "days ago";
85 if (expiration.duration.count() == 0) {
87 if (isSigningKey) {
88 msg = ki18n("<p>Your OpenPGP signing key</p><p align=center>%1</p><p>expired less than a day ago.</p>");
89 } else if (isOwnKey) {
90 msg = ki18n("<p>Your OpenPGP encryption key</p><p align=center>%1</p><p>expired less than a day ago.</p>");
91 } else {
92 msg = ki18n("<p>The OpenPGP key for</p><p align=center>%1</p><p>expired less than a day ago.</p>");
93 }
94 return msg.subs(keyInfo).toString();
95 }
97 if (isSigningKey) {
98 msg = ki18np("<p>Your OpenPGP signing key</p><p align=center>%2</p><p>expired yesterday.</p>",
99 "<p>Your OpenPGP signing key</p><p align=center>%2</p><p>expired %1 days ago.</p>");
100 } else if (isOwnKey) {
101 msg = ki18np("<p>Your OpenPGP encryption key</p><p align=center>%2</p><p>expired yesterday.</p>",
102 "<p>Your OpenPGP encryption key</p><p align=center>%2</p><p>expired %1 days ago.</p>");
103 } else {
104 msg = ki18np("<p>The OpenPGP key for</p><p align=center>%2</p><p>expired yesterday.</p>",
105 "<p>The OpenPGP key for</p><p align=center>%2</p><p>expired %1 days ago.</p>");
106 }
107 return msg.subs(expiration.duration.count()).subs(keyInfo).toString();
108 }
109 qCDebug(LIBKLEO_LOG) << "Key" << key << "expires in" << expiration.duration.count() << "days";
110 if (expiration.duration.count() == 0) {
112 if (isSigningKey) {
113 msg = ki18n("<p>Your OpenPGP signing key</p><p align=center>%1</p><p>expires today.</p>");
114 } else if (isOwnKey) {
115 msg = ki18n("<p>Your OpenPGP encryption key</p><p align=center>%1</p><p>expires today.</p>");
116 } else {
117 msg = ki18n("<p>The OpenPGP key for</p><p align=center>%1</p><p>expires today.</p>");
118 }
119 return msg.subs(keyInfo).toString();
120 }
122 if (isSigningKey) {
123 msg = ki18np("<p>Your OpenPGP signing key</p><p align=center>%2</p><p>expires tomorrow.</p>",
124 "<p>Your OpenPGP signing key</p><p align=center>%2</p><p>expires in %1 days.</p>");
125 } else if (isOwnKey) {
126 msg = ki18np("<p>Your OpenPGP encryption key</p><p align=center>%2</p><p>expires tomorrow.</p>",
127 "<p>Your OpenPGP encryption key</p><p align=center>%2</p><p>expires in %1 days.</p>");
128 } else {
129 msg = ki18np("<p>The OpenPGP key for</p><p align=center>%2</p><p>expires tomorrow.</p>",
130 "<p>The OpenPGP key for</p><p align=center>%2</p><p>expires in %1 days.</p>");
131 }
132 return msg.subs(expiration.duration.count()).subs(keyInfo).toString();
133}
134
135QString formatSMIMEMessage(const GpgME::Key &orig_key, ExpiryChecker::Expiration expiration, ExpiryChecker::CheckFlags flags, bool ca)
136{
137 const GpgME::Key key = expiration.certificate;
138 const bool isOwnKey = flags & ExpiryChecker::OwnKey;
139 const bool isSigningKey = flags & ExpiryChecker::SigningKey;
140 const auto userCert = orig_key.isNull() ? key : orig_key;
141 const auto userCertInfo = ki18nc("<b>User ID of certificate</b> (serial number serial no. of certificate)", "<b>%1</b> (serial number %2)")
142 .subs(Kleo::DN(userCert.userID(0).id()).prettyDN())
143 .subs(QString::fromLatin1(userCert.issuerSerial()));
144 if (expiration.status == ExpiryChecker::Expired) {
145 qCDebug(LIBKLEO_LOG) << "Certificate" << key << "expired" << expiration.duration.count() << "days ago";
146 if (ca) {
147 if (key.isRoot()) {
148 if (expiration.duration.count() == 0) {
150 if (isSigningKey) {
151 msg = ki18n(
152 "<p>The root certificate</p><p align=center><b>%2</b></p>"
153 "<p>for your S/MIME signing certificate</p><p align=center>%1</p>"
154 "<p>expired less than a day ago.</p>");
155 } else if (isOwnKey) {
156 msg = ki18n(
157 "<p>The root certificate</p><p align=center><b>%2</b></p>"
158 "<p>for your S/MIME encryption certificate</p><p align=center>%1</p>"
159 "<p>expired less than a day ago.</p>");
160 } else {
161 msg = ki18n(
162 "<p>The root certificate</p><p align=center><b>%2</b></p>"
163 "<p>for S/MIME certificate</p><p align=center>%1</p>"
164 "<p>expired less than a day ago.</p>");
165 }
166 return msg.subs(userCertInfo).subs(Kleo::DN(key.userID(0).id()).prettyDN()).toString();
167 }
169 if (isSigningKey) {
170 msg = ki18np(
171 "<p>The root certificate</p><p align=center><b>%3</b></p>"
172 "<p>for your S/MIME signing certificate</p><p align=center>%2</p>"
173 "<p>expired yesterday.</p>",
174 "<p>The root certificate</p><p align=center><b>%3</b></p>"
175 "<p>for your S/MIME signing certificate</p><p align=center>%2</p>"
176 "<p>expired %1 days ago.</p>");
177 } else if (isOwnKey) {
178 msg = ki18np(
179 "<p>The root certificate</p><p align=center><b>%3</b></p>"
180 "<p>for your S/MIME encryption certificate</p><p align=center>%2</p>"
181 "<p>expired yesterday.</p>",
182 "<p>The root certificate</p><p align=center><b>%3</b></p>"
183 "<p>for your S/MIME encryption certificate</p><p align=center>%2</p>"
184 "<p>expired %1 days ago.</p>");
185 } else {
186 msg = ki18np(
187 "<p>The root certificate</p><p align=center><b>%3</b></p>"
188 "<p>for S/MIME certificate</p><p align=center>%2</p>"
189 "<p>expired yesterday.</p>",
190 "<p>The root certificate</p><p align=center><b>%3</b></p>"
191 "<p>for S/MIME certificate</p><p align=center>%2</p>"
192 "<p>expired %1 days ago.</p>");
193 }
194 return msg.subs(expiration.duration.count()).subs(userCertInfo).subs(Kleo::DN(key.userID(0).id()).prettyDN()).toString();
195 } else {
196 if (expiration.duration.count() == 0) {
198 if (isSigningKey) {
199 msg = ki18n(
200 "<p>The intermediate CA certificate</p><p align=center><b>%2</b></p>"
201 "<p>for your S/MIME signing certificate</p><p align=center>%1</p>"
202 "<p>expired less than a day ago.</p>");
203 } else if (isOwnKey) {
204 msg = ki18n(
205 "<p>The intermediate CA certificate</p><p align=center><b>%2</b></p>"
206 "<p>for your S/MIME encryption certificate</p><p align=center>%1</p>"
207 "<p>expired less than a day ago.</p>");
208 } else {
209 msg = ki18n(
210 "<p>The intermediate CA certificate</p><p align=center><b>%2</b></p>"
211 "<p>for S/MIME certificate</p><p align=center>%1</p>"
212 "<p>expired less than a day ago.</p>");
213 }
214 return msg.subs(userCertInfo).subs(Kleo::DN(key.userID(0).id()).prettyDN()).toString();
215 }
217 if (isSigningKey) {
218 msg = ki18np(
219 "<p>The intermediate CA certificate</p><p align=center><b>%3</b></p>"
220 "<p>for your S/MIME signing certificate</p><p align=center>%2</p>"
221 "<p>expired yesterday.</p>",
222 "<p>The intermediate CA certificate</p><p align=center><b>%3</b></p>"
223 "<p>for your S/MIME signing certificate</p><p align=center>%2</p>"
224 "<p>expired %1 days ago.</p>");
225 } else if (isOwnKey) {
226 msg = ki18np(
227 "<p>The intermediate CA certificate</p><p align=center><b>%3</b></p>"
228 "<p>for your S/MIME encryption certificate</p><p align=center>%2</p>"
229 "<p>expired yesterday.</p>",
230 "<p>The intermediate CA certificate</p><p align=center><b>%3</b></p>"
231 "<p>for your S/MIME encryption certificate</p><p align=center>%2</p>"
232 "<p>expired %1 days ago.</p>");
233 } else {
234 msg = ki18np(
235 "<p>The intermediate CA certificate</p><p align=center><b>%3</b></p>"
236 "<p>for S/MIME certificate</p><p align=center>%2</p>"
237 "<p>expired yesterday.</p>",
238 "<p>The intermediate CA certificate</p><p align=center><b>%3</b></p>"
239 "<p>for S/MIME certificate</p><p align=center>%2</p>"
240 "<p>expired %1 days ago.</p>");
241 }
242 return msg.subs(expiration.duration.count()).subs(userCertInfo).subs(Kleo::DN(key.userID(0).id()).prettyDN()).toString();
243 }
244 } else {
245 if (expiration.duration.count() == 0) {
247 if (isSigningKey) {
248 msg = ki18n("<p>Your S/MIME signing certificate</p><p align=center>%1</p><p>expired less than a day ago.</p>");
249 } else if (isOwnKey) {
250 msg = ki18n("<p>Your S/MIME encryption certificate</p><p align=center>%1</p><p>expired less than a day ago.</p>");
251 } else {
252 msg = ki18n("<p>The S/MIME certificate for</p><p align=center>%1</p><p>expired less than a day ago.</p>");
253 }
254 return msg.subs(userCertInfo).toString();
255 }
257 if (isSigningKey) {
258 msg = ki18np("<p>Your S/MIME signing certificate</p><p align=center>%2</p><p>expired yesterday.</p>",
259 "<p>Your S/MIME signing certificate</p><p align=center>%2</p><p>expired %1 days ago.</p>");
260 } else if (isOwnKey) {
261 msg = ki18np("<p>Your S/MIME encryption certificate</p><p align=center>%2</p><p>expired yesterday.</p>",
262 "<p>Your S/MIME encryption certificate</p><p align=center>%2</p><p>expired %1 days ago.</p>");
263 } else {
264 msg = ki18np("<p>The S/MIME certificate for</p><p align=center>%2</p><p>expired yesterday.</p>",
265 "<p>The S/MIME certificate for</p><p align=center>%2</p><p>expired %1 days ago.</p>");
266 }
267 return msg.subs(expiration.duration.count()).subs(userCertInfo).toString();
268 }
269 }
270 qCDebug(LIBKLEO_LOG) << "Certificate" << key << "expires in" << expiration.duration.count() << "days";
271 if (ca) {
272 if (key.isRoot()) {
273 if (expiration.duration.count() == 0) {
275 if (isSigningKey) {
276 msg = ki18n(
277 "<p>The root certificate</p><p align=center><b>%3</b></p>"
278 "<p>for your S/MIME signing certificate</p><p align=center>%2</p>"
279 "<p>expires today.</p>");
280 } else if (isOwnKey) {
281 msg = ki18n(
282 "<p>The root certificate</p><p align=center><b>%3</b></p>"
283 "<p>for your S/MIME encryption certificate</p><p align=center>%2</p>"
284 "<p>expires today.</p>");
285 } else {
286 msg = ki18n(
287 "<p>The root certificate</p><p align=center><b>%3</b></p>"
288 "<p>for S/MIME certificate</p><p align=center>%2</p>"
289 "<p>expires today.</p>");
290 }
291 return msg.subs(userCertInfo).subs(Kleo::DN(key.userID(0).id()).prettyDN()).toString();
292 }
294 if (isSigningKey) {
295 msg = ki18np(
296 "<p>The root certificate</p><p align=center><b>%3</b></p>"
297 "<p>for your S/MIME signing certificate</p><p align=center>%2</p>"
298 "<p>expires tomorrow.</p>",
299 "<p>The root certificate</p><p align=center><b>%3</b></p>"
300 "<p>for your S/MIME signing certificate</p><p align=center>%2</p>"
301 "<p>expires in %1 days.</p>");
302 } else if (isOwnKey) {
303 msg = ki18np(
304 "<p>The root certificate</p><p align=center><b>%3</b></p>"
305 "<p>for your S/MIME encryption certificate</p><p align=center>%2</p>"
306 "<p>expires tomorrow.</p>",
307 "<p>The root certificate</p><p align=center><b>%3</b></p>"
308 "<p>for your S/MIME encryption certificate</p><p align=center>%2</p>"
309 "<p>expires in %1 days.</p>");
310 } else {
311 msg = ki18np(
312 "<p>The root certificate</p><p align=center><b>%3</b></p>"
313 "<p>for S/MIME certificate</p><p align=center>%2</p>"
314 "<p>expires tomorrow.</p>",
315 "<p>The root certificate</p><p align=center><b>%3</b></p>"
316 "<p>for S/MIME certificate</p><p align=center>%2</p>"
317 "<p>expires in %1 days.</p>");
318 }
319 return msg.subs(expiration.duration.count()).subs(userCertInfo).subs(Kleo::DN(key.userID(0).id()).prettyDN()).toString();
320 }
321 if (expiration.duration.count() == 0) {
323 if (isSigningKey) {
324 msg = ki18n(
325 "<p>The intermediate CA certificate</p><p align=center><b>%3</b></p>"
326 "<p>for your S/MIME signing certificate</p><p align=center>%2</p>"
327 "<p>expires today.</p>");
328 } else if (isOwnKey) {
329 msg = ki18n(
330 "<p>The intermediate CA certificate</p><p align=center><b>%3</b></p>"
331 "<p>for your S/MIME encryption certificate</p><p align=center>%2</p>"
332 "<p>expires today.</p>");
333 } else {
334 msg = ki18n(
335 "<p>The intermediate CA certificate</p><p align=center><b>%3</b></p>"
336 "<p>for S/MIME certificate</p><p align=center>%2</p>"
337 "<p>expires today.</p>");
338 }
339 }
341 if (isSigningKey) {
342 msg = ki18np(
343 "<p>The intermediate CA certificate</p><p align=center><b>%3</b></p>"
344 "<p>for your S/MIME signing certificate</p><p align=center>%2</p>"
345 "<p>expires tomorrow.</p>",
346 "<p>The intermediate CA certificate</p><p align=center><b>%3</b></p>"
347 "<p>for your S/MIME signing certificate</p><p align=center>%2</p>"
348 "<p>expires in %1 days.</p>");
349 } else if (isOwnKey) {
350 msg = ki18np(
351 "<p>The intermediate CA certificate</p><p align=center><b>%3</b></p>"
352 "<p>for your S/MIME encryption certificate</p><p align=center>%2</p>"
353 "<p>expires tomorrow.</p>",
354 "<p>The intermediate CA certificate</p><p align=center><b>%3</b></p>"
355 "<p>for your S/MIME encryption certificate</p><p align=center>%2</p>"
356 "<p>expires in %1 days.</p>");
357 } else {
358 msg = ki18np(
359 "<p>The intermediate CA certificate</p><p align=center><b>%3</b></p>"
360 "<p>for S/MIME certificate</p><p align=center>%2</p>"
361 "<p>expires tomorrow.</p>",
362 "<p>The intermediate CA certificate</p><p align=center><b>%3</b></p>"
363 "<p>for S/MIME certificate</p><p align=center>%2</p>"
364 "<p>expires in %1 days.</p>");
365 }
366 return msg.subs(expiration.duration.count()).subs(userCertInfo).subs(Kleo::DN(key.userID(0).id()).prettyDN()).toString();
367 }
368 if (expiration.duration.count() == 0) {
370 if (isSigningKey) {
371 msg = ki18n("<p>Your S/MIME signing certificate</p><p align=center>%2</p><p>expires today.</p>");
372 } else if (isOwnKey) {
373 msg = ki18n("<p>Your S/MIME encryption certificate</p><p align=center>%2</p><p>expires today.</p>");
374 } else {
375 msg = ki18n("<p>The S/MIME certificate for</p><p align=center>%2</p><p>expires today.</p>");
376 }
377 return msg.subs(userCertInfo).toString();
378 }
380 if (isSigningKey) {
381 msg = ki18np(
382 "<p>Your S/MIME signing certificate</p><p align=center>%2</p>"
383 "<p>expires tomorrow.</p>",
384 "<p>Your S/MIME signing certificate</p><p align=center>%2</p>"
385 "<p>expires in %1 days.</p>");
386 } else if (isOwnKey) {
387 msg = ki18np(
388 "<p>Your S/MIME encryption certificate</p><p align=center>%2</p>"
389 "<p>expires tomorrow.</p>",
390 "<p>Your S/MIME encryption certificate</p><p align=center>%2</p>"
391 "<p>expires in %1 days.</p>");
392 } else {
393 msg = ki18np(
394 "<p>The S/MIME certificate for</p><p align=center>%2</p>"
395 "<p>expires tomorrow.</p>",
396 "<p>The S/MIME certificate for</p><p align=center>%2</p>"
397 "<p>expires in %1 days.</p>");
398 }
399 return msg.subs(expiration.duration.count()).subs(userCertInfo).toString();
400}
401
402static GpgME::Subkey findBestSubkey(const GpgME::Key &key, ExpiryChecker::CheckFlags usageFlags)
403{
404 // find the subkey with the latest expiration date for the given usage flags
405 if (!(usageFlags & ExpiryChecker::UsageMask)) {
406 // return primary key if no specific usage is specified (as for chain certificates)
407 return key.subkey(0);
408 }
409 GpgME::Subkey result;
410 for (unsigned int i = 0; i < key.numSubkeys(); ++i) {
411 const auto subkey = key.subkey(i);
412 if (subkey.isRevoked() || subkey.isInvalid() || subkey.isDisabled()) {
413 // unusable subkey
414 continue;
415 }
416 if (((usageFlags & ExpiryChecker::EncryptionKey) && !subkey.canEncrypt()) //
417 || ((usageFlags & ExpiryChecker::SigningKey) && !subkey.canSign()) //
418 || ((usageFlags & ExpiryChecker::CertificationKey) && !subkey.canCertify())) {
419 // unsuitable subkey for requested usage
420 continue;
421 }
422 if (subkey.neverExpires()) {
423 // stop looking for the best subkey if we found a suitable subkey that doesn't expire;
424 // return the primary key because a non-expiring subkey inherits the primary key's expiration
425 return key.subkey(0);
426 }
427 if (quint32(subkey.expirationTime()) > quint32(result.expirationTime())) {
428 result = subkey;
429 }
430 }
431 return result;
432}
433
434ExpiryChecker::Expiration ExpiryCheckerPrivate::calculateExpiration(const GpgME::Subkey &subkey) const
435{
436 if (subkey.neverExpires()) {
437 return {subkey.parent(), ExpiryChecker::NotNearExpiry, Kleo::chrono::days::zero()};
438 }
439 const qint64 currentTime = timeProvider ? timeProvider->currentTime() : QDateTime::currentSecsSinceEpoch();
440 const auto currentDate = timeProvider ? timeProvider->currentDate() : QDate::currentDate();
441 const auto timeZone = timeProvider ? timeProvider->timeZone() : QTimeZone{QTimeZone::LocalTime};
442 // interpret the expiration time as unsigned 32-bit value if it's negative; gpg also uses uint32 internally
443 const qint64 expirationTime = qint64(subkey.expirationTime() < 0 ? quint32(subkey.expirationTime()) : subkey.expirationTime());
444 const auto expirationDate = QDateTime::fromSecsSinceEpoch(expirationTime, timeZone).date();
445 if (expirationTime <= currentTime) {
446 return {subkey.parent(), ExpiryChecker::Expired, Kleo::chrono::days{expirationDate.daysTo(currentDate)}};
447 } else {
448 return {subkey.parent(), ExpiryChecker::ExpiresSoon, Kleo::chrono::days{currentDate.daysTo(expirationDate)}};
449 }
450}
451
452ExpiryChecker::Expiration ExpiryCheckerPrivate::checkForExpiration(const GpgME::Key &key, //
453 Kleo::chrono::days threshold,
454 ExpiryChecker::CheckFlags usageFlags) const
455{
456 const auto subkey = findBestSubkey(key, usageFlags);
457 if (subkey.isNull()) {
458 return {key, ExpiryChecker::NoSuitableSubkey, {}};
459 }
460 ExpiryChecker::Expiration expiration = calculateExpiration(subkey);
461 if ((expiration.status == ExpiryChecker::ExpiresSoon) && (expiration.duration > threshold)) {
462 // key expires, but not too soon
463 expiration.status = ExpiryChecker::NotNearExpiry;
464 }
465 return expiration;
466}
467
468ExpiryChecker::Result ExpiryCheckerPrivate::checkKeyNearExpiry(const GpgME::Key &orig_key, ExpiryChecker::CheckFlags flags)
469{
470 static const int maximumCertificateChainLength = 100;
471 const bool isOwnKey = flags & ExpiryChecker::OwnKey;
472
473 ExpiryChecker::Result result;
474 result.checkFlags = flags;
475 result.expiration.certificate = orig_key;
476
477 // use vector instead of set because certificate chains are usually very short
478 std::vector<std::string> checkedCertificates;
479 auto key = orig_key;
480 for (int chainCount = 0; chainCount < maximumCertificateChainLength; ++chainCount) {
481 checkedCertificates.push_back(key.primaryFingerprint());
482
483 const GpgME::Subkey subkey = key.subkey(0);
484
485 const bool newMessage = !alreadyWarnedFingerprints.count(subkey.fingerprint());
486
487 const auto threshold = chainCount > 0 //
488 ? (key.isRoot() ? settings.rootCertThreshold() : settings.chainCertThreshold()) //
489 : (isOwnKey ? settings.ownKeyThreshold() : settings.otherKeyThreshold());
490 const auto usageFlags = (chainCount == 0) ? (flags & ExpiryChecker::UsageMask) : ExpiryChecker::CheckFlags{};
491 const auto expiration = checkForExpiration(key, threshold, usageFlags);
492 if (chainCount == 0) {
493 result.expiration = expiration;
494 } else if (expiration.status != ExpiryChecker::NotNearExpiry) {
495 result.chainExpiration.push_back(expiration);
496 }
497 if (expiration.status == ExpiryChecker::Expired) {
498 const QString msg = key.protocol() == GpgME::OpenPGP //
499 ? formatOpenPGPMessage(expiration, flags)
500 : formatSMIMEMessage(orig_key, expiration, flags, chainCount > 0);
501 alreadyWarnedFingerprints.insert(subkey.fingerprint());
502 Q_EMIT q->expiryMessage(key, msg, isOwnKey ? ExpiryChecker::OwnKeyExpired : ExpiryChecker::OtherKeyExpired, newMessage);
503 } else if (expiration.status == ExpiryChecker::ExpiresSoon) {
504 const QString msg = key.protocol() == GpgME::OpenPGP //
505 ? formatOpenPGPMessage(expiration, flags)
506 : formatSMIMEMessage(orig_key, expiration, flags, chainCount > 0);
507 alreadyWarnedFingerprints.insert(subkey.fingerprint());
508 Q_EMIT q->expiryMessage(key, msg, isOwnKey ? ExpiryChecker::OwnKeyNearExpiry : ExpiryChecker::OtherKeyNearExpiry, newMessage);
509 } else if (expiration.status == ExpiryChecker::NoSuitableSubkey) {
510 break;
511 }
512 if (!(flags & ExpiryChecker::CheckChain) || key.isRoot() || (key.protocol() != GpgME::CMS)) {
513 break;
514 }
515 const auto keys = KeyCache::instance()->findIssuers(key, KeyCache::NoOption);
516 if (keys.empty()) {
517 break;
518 }
519 key = keys.front();
520 if (Kleo::contains(checkedCertificates, key.primaryFingerprint())) {
521 break; // this certificate was already checked (looks like a circle in the chain)
522 }
523 }
524 return result;
525}
526
527ExpiryChecker::Result ExpiryChecker::checkKey(const GpgME::Key &key, CheckFlags flags) const
528{
529 if (key.isNull()) {
530 qWarning(LIBKLEO_LOG) << __func__ << "called with null key";
531 return {flags, {key, InvalidKey, {}}, {}};
532 }
533 if (!(flags & UsageMask)) {
534 qWarning(LIBKLEO_LOG) << __func__ << "called with invalid flags:" << flags;
535 return {flags, {key, InvalidCheckFlags, {}}, {}};
536 }
537 return d->checkKeyNearExpiry(key, flags);
538}
539
540void ExpiryChecker::setTimeProviderForTest(const std::shared_ptr<TimeProvider> &timeProvider)
541{
542 d->timeProvider = timeProvider;
543}
544
545#include "moc_expirychecker.cpp"
QString toString() const
KLocalizedString subs(const KLocalizedString &a, int fieldWidth=0, QChar fillChar=QLatin1Char(' ')) const
DN parser and reorderer.
Definition dn.h:27
QString prettyDN() const
Definition dn.cpp:445
KLocalizedString KI18N_EXPORT ki18np(const char *singular, const char *plural)
KLocalizedString KI18N_EXPORT ki18n(const char *text)
KLocalizedString KI18N_EXPORT ki18nc(const char *context, const char *text)
QDate currentDate()
qint64 daysTo(QDate d) const const
qint64 currentSecsSinceEpoch()
QDate date() const const
QDateTime fromSecsSinceEpoch(qint64 secs)
QString fromLatin1(QByteArrayView str)
QString fromUtf8(QByteArrayView str)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Sat Dec 21 2024 16:56:14 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.