File indexing completed on 2024-04-28 15:27:32

0001 /*
0002     This file is part of the KDE project
0003     SPDX-FileCopyrightText: 2009 Andreas Hartmetz <ahartmetz@gmail.com>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "sslui.h"
0009 
0010 #include <KLocalizedString>
0011 #include <KMessageBox>
0012 #include <ksslcertificatemanager.h>
0013 #include <ksslerroruidata_p.h>
0014 #include <ksslinfodialog.h>
0015 
0016 #if KIOWIDGETS_BUILD_DEPRECATED_SINCE(5, 64)
0017 bool KIO::SslUi::askIgnoreSslErrors(const KTcpSocket *socket, RulesStorage storedRules)
0018 {
0019     KSslErrorUiData uiData(socket);
0020     return askIgnoreSslErrors(uiData, storedRules);
0021 }
0022 #endif
0023 
0024 bool KIO::SslUi::askIgnoreSslErrors(const KSslErrorUiData &uiData, RulesStorage storedRules)
0025 {
0026     const KSslErrorUiData::Private *ud = KSslErrorUiData::Private::get(&uiData);
0027     if (ud->sslErrors.isEmpty()) {
0028         return true;
0029     }
0030 
0031     const QList<QSslError> fatalErrors = KSslCertificateManager::nonIgnorableErrors(ud->sslErrors);
0032     if (!fatalErrors.isEmpty()) {
0033         // TODO message "sorry, fatal error, you can't override it"
0034         return false;
0035     }
0036     if (ud->certificateChain.isEmpty()) {
0037         // SSL without certificates is quite useless and should never happen
0038         KMessageBox::error(nullptr,
0039                            i18n("The remote host did not send any SSL certificates.\n"
0040                                 "Aborting because the identity of the host cannot be established."));
0041         return false;
0042     }
0043 
0044     KSslCertificateManager *const cm = KSslCertificateManager::self();
0045     KSslCertificateRule rule(ud->certificateChain.first(), ud->host);
0046     if (storedRules & RecallRules) {
0047         rule = cm->rule(ud->certificateChain.first(), ud->host);
0048         // remove previously seen and acknowledged errors
0049         const QList<QSslError> remainingErrors = rule.filterErrors(ud->sslErrors);
0050         if (remainingErrors.isEmpty()) {
0051             // qDebug() << "Error list empty after removing errors to be ignored. Continuing.";
0052             return true;
0053         }
0054     }
0055 
0056     // ### We don't ask to permanently reject the certificate
0057 
0058     QString message = i18n("The server failed the authenticity check (%1).\n\n", ud->host);
0059     for (const QSslError &err : std::as_const(ud->sslErrors)) {
0060         message.append(err.errorString() + QLatin1Char('\n'));
0061     }
0062     message = message.trimmed();
0063 
0064     int msgResult;
0065     do {
0066         msgResult = KMessageBox::warningTwoActionsCancel(nullptr,
0067                                                          message,
0068                                                          i18n("Server Authentication"),
0069                                                          KGuiItem(i18n("&Details"), QStringLiteral("help-about")),
0070                                                          KGuiItem(i18n("Co&ntinue"), QStringLiteral("arrow-right")));
0071         if (msgResult == KMessageBox::PrimaryAction) {
0072             // Details was chosen - show the certificate and error details
0073 
0074             QList<QList<QSslError::SslError>> meh; // parallel list to cert list :/
0075 
0076             meh.reserve(ud->certificateChain.size());
0077             for (const QSslCertificate &cert : std::as_const(ud->certificateChain)) {
0078                 QList<QSslError::SslError> errors;
0079                 for (const QSslError &error : std::as_const(ud->sslErrors)) {
0080                     if (error.certificate() == cert) {
0081                         // we keep only the error code enum here
0082                         errors.append(error.error());
0083                     }
0084                 }
0085                 meh.append(errors);
0086             }
0087 
0088             KSslInfoDialog *dialog = new KSslInfoDialog();
0089             dialog->setSslInfo(ud->certificateChain, ud->ip, ud->host, ud->sslProtocol, ud->cipher, ud->usedBits, ud->bits, meh);
0090             dialog->exec();
0091         } else if (msgResult == KMessageBox::Cancel) {
0092             return false;
0093         }
0094         // fall through on KMessageBox::SecondaryAction
0095     } while (msgResult == KMessageBox::PrimaryAction);
0096 
0097     if (storedRules & StoreRules) {
0098         // Save the user's choice to ignore the SSL errors.
0099 
0100         msgResult = KMessageBox::warningTwoActions(nullptr,
0101                                                    i18n("Would you like to accept this "
0102                                                         "certificate forever without "
0103                                                         "being prompted?"),
0104                                                    i18n("Server Authentication"),
0105                                                    KGuiItem(i18n("&Forever"), QStringLiteral("flag-green")),
0106                                                    KGuiItem(i18n("&Current Session only"), QStringLiteral("chronometer")));
0107         QDateTime ruleExpiry = QDateTime::currentDateTime();
0108         if (msgResult == KMessageBox::PrimaryAction) {
0109             // accept forever ("for a very long time")
0110             ruleExpiry = ruleExpiry.addYears(1000);
0111         } else {
0112             // accept "for a short time", half an hour.
0113             ruleExpiry = ruleExpiry.addSecs(30 * 60);
0114         }
0115 
0116         // TODO special cases for wildcard domain name in the certificate!
0117         // rule = KSslCertificateRule(d->socket.peerCertificateChain().first(), whatever);
0118 
0119         rule.setExpiryDateTime(ruleExpiry);
0120         rule.setIgnoredErrors(ud->sslErrors);
0121         cm->setRule(rule);
0122     }
0123 
0124     return true;
0125 }