/* * Copyright (C) by Klaas Freitag * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. */ #include "configfile.h" #include "sslerrordialog.h" #include "theme.h" #include #include #include #include "ui_sslerrordialog.h" namespace OCC { Q_LOGGING_CATEGORY(lcSslErrorDialog, "nextcloud.gui.sslerrordialog", QtInfoMsg) namespace Utility { // Used for QSSLCertificate::subjectInfo which returns a QStringList in Qt5, but a QString in Qt4 QString escape(const QStringList &l) { return escape(l.join(';')); } } bool SslDialogErrorHandler::handleErrors(QList errors, const QSslConfiguration &conf, QList *certs, AccountPtr account) { (void)conf; if (!certs) { qCCritical(lcSslErrorDialog) << "Certs parameter required but is NULL!"; return false; } SslErrorDialog dlg(account); // whether the failing certs have previously been accepted if (dlg.checkFailingCertsKnown(errors)) { *certs = dlg.unknownCerts(); return true; } // whether the user accepted the certs if (dlg.exec() == QDialog::Accepted) { if (dlg.trustConnection()) { *certs = dlg.unknownCerts(); return true; } } return false; } SslErrorDialog::SslErrorDialog(AccountPtr account, QWidget *parent) : QDialog(parent) , _allTrusted(false) , _ui(new Ui::SslErrorDialog) , _account(account) { setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); _ui->setupUi(this); setWindowTitle(tr("Untrusted Certificate")); QPushButton *okButton = _ui->_dialogButtonBox->button(QDialogButtonBox::Ok); QPushButton *cancelButton = _ui->_dialogButtonBox->button(QDialogButtonBox::Cancel); okButton->setEnabled(false); _ui->_cbTrustConnect->setEnabled(!Theme::instance()->forbidBadSSL()); connect(_ui->_cbTrustConnect, &QAbstractButton::clicked, okButton, &QWidget::setEnabled); if (okButton) { okButton->setDefault(true); connect(okButton, &QAbstractButton::clicked, this, &QDialog::accept); connect(cancelButton, &QAbstractButton::clicked, this, &QDialog::reject); } } SslErrorDialog::~SslErrorDialog() { delete _ui; } QString SslErrorDialog::styleSheet() const { const QString style = QLatin1String( "#cert {margin-left: 5px;} " "#ca_error { color:#a00011; margin-left:5px; margin-right:5px; }" "#ca_error p { margin-top: 2px; margin-bottom:2px; }" "#ccert { margin-left: 5px; }" "#issuer { margin-left: 5px; }" "tt { font-size: small; }"); return style; } #define QL(x) QLatin1String(x) bool SslErrorDialog::checkFailingCertsKnown(const QList &errors) { // check if unknown certs caused errors. _unknownCerts.clear(); QStringList errorStrings; QStringList additionalErrorStrings; QList trustedCerts = _account->approvedCerts(); for (int i = 0; i < errors.count(); ++i) { QSslError error = errors.at(i); if (trustedCerts.contains(error.certificate()) || _unknownCerts.contains(error.certificate())) { continue; } errorStrings += error.errorString(); if (!error.certificate().isNull()) { _unknownCerts.append(error.certificate()); } else { additionalErrorStrings.append(error.errorString()); } } // if there are no errors left, all Certs were known. if (errorStrings.isEmpty()) { _allTrusted = true; return true; } QString msg = QL(""); msg += QL(""); msg += QL(""); auto host = _account->url().host(); msg += QL("

") + tr("Cannot connect securely to %1:").arg(host) + QL("

"); // loop over the unknown certs and line up their errors. msg += QL("
"); foreach (const QSslCertificate &cert, _unknownCerts) { msg += QL("
"); // add the errors for this cert foreach (QSslError err, errors) { if (err.certificate() == cert) { msg += QL("

") + err.errorString() + QL("

"); } } msg += QL("
"); msg += certDiv(cert); if (_unknownCerts.count() > 1) { msg += QL("
"); } } if (!additionalErrorStrings.isEmpty()) { msg += QL("

") + tr("Additional errors:") + QL("

"); for (const auto &errorString : additionalErrorStrings) { msg += QL("
"); msg += QL("

") + errorString + QL("

"); msg += QL("
"); } } msg += QL("
"); auto *doc = new QTextDocument(nullptr); QString style = styleSheet(); doc->addResource(QTextDocument::StyleSheetResource, QUrl(QL("format.css")), style); doc->setHtml(msg); _ui->_tbErrors->setDocument(doc); _ui->_tbErrors->show(); return false; } QString SslErrorDialog::certDiv(QSslCertificate cert) const { QString msg; msg += QL("
"); msg += QL("

") + tr("with Certificate %1").arg(Utility::escape(cert.subjectInfo(QSslCertificate::CommonName))) + QL("

"); msg += QL("
"); QStringList li; QString org = Utility::escape(cert.subjectInfo(QSslCertificate::Organization)); QString unit = Utility::escape(cert.subjectInfo(QSslCertificate::OrganizationalUnitName)); QString country = Utility::escape(cert.subjectInfo(QSslCertificate::CountryName)); if (unit.isEmpty()) unit = tr("<not specified>"); if (org.isEmpty()) org = tr("<not specified>"); if (country.isEmpty()) country = tr("<not specified>"); li << tr("Organization: %1").arg(org); li << tr("Unit: %1").arg(unit); li << tr("Country: %1").arg(country); msg += QL("

") + li.join(QL("
")) + QL("

"); msg += QL("

"); if (cert.effectiveDate() < QDateTime(QDate(2016, 1, 1), QTime(), Qt::UTC)) { QString sha1sum = Utility::formatFingerprint(cert.digest(QCryptographicHash::Sha1).toHex()); msg += tr("Fingerprint (SHA1): %1").arg(sha1sum) + QL("
"); } QString sha256sum = Utility::formatFingerprint(cert.digest(QCryptographicHash::Sha256).toHex()); QString sha512sum = Utility::formatFingerprint(cert.digest(QCryptographicHash::Sha512).toHex()); msg += tr("Fingerprint (SHA-256): %1").arg(sha256sum) + QL("
"); msg += tr("Fingerprint (SHA-512): %1").arg(sha512sum) + QL("
"); msg += QL("
"); msg += tr("Effective Date: %1").arg(cert.effectiveDate().toString()) + QL("
"); msg += tr("Expiration Date: %1").arg(cert.expiryDate().toString()) + QL("

"); msg += QL("
"); msg += QL("

") + tr("Issuer: %1").arg(Utility::escape(cert.issuerInfo(QSslCertificate::CommonName))) + QL("

"); msg += QL("
"); li.clear(); li << tr("Organization: %1").arg(Utility::escape(cert.issuerInfo(QSslCertificate::Organization))); li << tr("Unit: %1").arg(Utility::escape(cert.issuerInfo(QSslCertificate::OrganizationalUnitName))); li << tr("Country: %1").arg(Utility::escape(cert.issuerInfo(QSslCertificate::CountryName))); msg += QL("

") + li.join(QL("
")) + QL("

"); msg += QL("
"); msg += QL("
"); return msg; } bool SslErrorDialog::trustConnection() { if (_allTrusted) return true; bool stat = (_ui->_cbTrustConnect->checkState() == Qt::Checked); qCInfo(lcSslErrorDialog) << "SSL-Connection is trusted: " << stat; return stat; } } // end namespace