File indexing completed on 2024-07-14 03:54:33

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