1 | /* |
2 | This file is part of the KDE project |
3 | SPDX-FileCopyrightText: 2009 Andreas Hartmetz <ahartmetz@gmail.com> |
4 | |
5 | SPDX-License-Identifier: LGPL-2.0-or-later |
6 | */ |
7 | |
8 | #include "sslui.h" |
9 | |
10 | #include <KLocalizedString> |
11 | #include <KMessageBox> |
12 | #include <ksslcertificatemanager.h> |
13 | #include <ksslerroruidata_p.h> |
14 | #include <ksslinfodialog.h> |
15 | |
16 | bool KIO::SslUi::askIgnoreSslErrors(const KSslErrorUiData &uiData, RulesStorage storedRules) |
17 | { |
18 | const KSslErrorUiData::Private *ud = KSslErrorUiData::Private::get(uiData: &uiData); |
19 | if (ud->sslErrors.isEmpty()) { |
20 | return true; |
21 | } |
22 | |
23 | const QList<QSslError> fatalErrors = KSslCertificateManager::nonIgnorableErrors(errors: ud->sslErrors); |
24 | if (!fatalErrors.isEmpty()) { |
25 | // TODO message "sorry, fatal error, you can't override it" |
26 | return false; |
27 | } |
28 | if (ud->certificateChain.isEmpty()) { |
29 | // SSL without certificates is quite useless and should never happen |
30 | KMessageBox::error(parent: nullptr, |
31 | i18n("The remote host did not send any SSL certificates.\n" |
32 | "Aborting because the identity of the host cannot be established." )); |
33 | return false; |
34 | } |
35 | |
36 | KSslCertificateManager *const cm = KSslCertificateManager::self(); |
37 | KSslCertificateRule rule(ud->certificateChain.first(), ud->host); |
38 | if (storedRules & RecallRules) { |
39 | rule = cm->rule(cert: ud->certificateChain.first(), hostName: ud->host); |
40 | // remove previously seen and acknowledged errors |
41 | const QList<QSslError> remainingErrors = rule.filterErrors(errors: ud->sslErrors); |
42 | if (remainingErrors.isEmpty()) { |
43 | // qDebug() << "Error list empty after removing errors to be ignored. Continuing."; |
44 | return true; |
45 | } |
46 | } |
47 | |
48 | // ### We don't ask to permanently reject the certificate |
49 | |
50 | QString message = i18n("The server failed the authenticity check (%1).\n\n" , ud->host); |
51 | for (const QSslError &err : std::as_const(t: ud->sslErrors)) { |
52 | message.append(s: err.errorString() + QLatin1Char('\n')); |
53 | } |
54 | message = message.trimmed(); |
55 | |
56 | int msgResult; |
57 | do { |
58 | msgResult = KMessageBox::warningTwoActionsCancel(parent: nullptr, |
59 | text: message, |
60 | i18n("Server Authentication" ), |
61 | primaryAction: KGuiItem(i18n("&Details" ), QStringLiteral("help-about" )), |
62 | secondaryAction: KGuiItem(i18n("Co&ntinue" ), QStringLiteral("arrow-right" ))); |
63 | if (msgResult == KMessageBox::PrimaryAction) { |
64 | // Details was chosen - show the certificate and error details |
65 | |
66 | QList<QList<QSslError::SslError>> meh; // parallel list to cert list :/ |
67 | |
68 | meh.reserve(asize: ud->certificateChain.size()); |
69 | for (const QSslCertificate &cert : std::as_const(t: ud->certificateChain)) { |
70 | QList<QSslError::SslError> errors; |
71 | for (const QSslError &error : std::as_const(t: ud->sslErrors)) { |
72 | if (error.certificate() == cert) { |
73 | // we keep only the error code enum here |
74 | errors.append(t: error.error()); |
75 | } |
76 | } |
77 | meh.append(t: errors); |
78 | } |
79 | |
80 | KSslInfoDialog *dialog = new KSslInfoDialog(); |
81 | dialog->setSslInfo(certificateChain: ud->certificateChain, ip: ud->ip, host: ud->host, sslProtocol: ud->sslProtocol, cipher: ud->cipher, usedBits: ud->usedBits, bits: ud->bits, validationErrors: meh); |
82 | dialog->exec(); |
83 | } else if (msgResult == KMessageBox::Cancel) { |
84 | return false; |
85 | } |
86 | // fall through on KMessageBox::SecondaryAction |
87 | } while (msgResult == KMessageBox::PrimaryAction); |
88 | |
89 | if (storedRules & StoreRules) { |
90 | // Save the user's choice to ignore the SSL errors. |
91 | |
92 | msgResult = KMessageBox::warningTwoActions(parent: nullptr, |
93 | i18n("Would you like to accept this " |
94 | "certificate forever without " |
95 | "being prompted?" ), |
96 | i18n("Server Authentication" ), |
97 | primaryAction: KGuiItem(i18n("&Forever" ), QStringLiteral("flag-green" )), |
98 | secondaryAction: KGuiItem(i18n("&Current Session only" ), QStringLiteral("chronometer" ))); |
99 | QDateTime ruleExpiry = QDateTime::currentDateTime(); |
100 | if (msgResult == KMessageBox::PrimaryAction) { |
101 | // accept forever ("for a very long time") |
102 | ruleExpiry = ruleExpiry.addYears(years: 1000); |
103 | } else { |
104 | // accept "for a short time", half an hour. |
105 | ruleExpiry = ruleExpiry.addSecs(secs: 30 * 60); |
106 | } |
107 | |
108 | // TODO special cases for wildcard domain name in the certificate! |
109 | // rule = KSslCertificateRule(d->socket.peerCertificateChain().first(), whatever); |
110 | |
111 | rule.setExpiryDateTime(ruleExpiry); |
112 | rule.setIgnoredErrors(ud->sslErrors); |
113 | cm->setRule(rule); |
114 | } |
115 | |
116 | return true; |
117 | } |
118 | |