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

KDE's Doxygen guidelines are available online.