| 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 | |