2014-01-21 04:45:02 +04:00
|
|
|
/*
|
|
|
|
* Copyright (C) by Daniel Molkentin <danimo@owncloud.com>
|
|
|
|
*
|
|
|
|
* 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; version 2 of the License.
|
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
2014-07-11 02:31:24 +04:00
|
|
|
#include "sslbutton.h"
|
|
|
|
#include "account.h"
|
2014-12-17 16:09:57 +03:00
|
|
|
#include "accountstate.h"
|
2014-07-11 02:31:24 +04:00
|
|
|
#include "utility.h"
|
2015-02-16 12:24:42 +03:00
|
|
|
#include "theme.h"
|
2014-01-21 04:45:02 +04:00
|
|
|
|
|
|
|
#include <QMenu>
|
|
|
|
#include <QUrl>
|
|
|
|
#include <QtNetwork>
|
|
|
|
#include <QSslConfiguration>
|
2014-01-21 17:55:23 +04:00
|
|
|
#include <QWidgetAction>
|
|
|
|
#include <QLabel>
|
2014-01-21 04:45:02 +04:00
|
|
|
|
2014-11-10 00:34:07 +03:00
|
|
|
namespace OCC {
|
2014-01-21 04:45:02 +04:00
|
|
|
|
|
|
|
SslButton::SslButton(QWidget *parent) :
|
|
|
|
QToolButton(parent)
|
|
|
|
{
|
|
|
|
setPopupMode(QToolButton::InstantPopup);
|
2014-01-30 12:52:48 +04:00
|
|
|
setAutoRaise(true);
|
2015-03-25 17:39:53 +03:00
|
|
|
|
2015-07-02 11:49:18 +03:00
|
|
|
_menu = new QMenu(this);
|
|
|
|
QObject::connect(_menu, SIGNAL(aboutToShow()),
|
2015-03-25 17:39:53 +03:00
|
|
|
this, SLOT(slotUpdateMenu()));
|
2014-01-21 04:45:02 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
QString SslButton::protoToString(QSsl::SslProtocol proto)
|
|
|
|
{
|
|
|
|
switch(proto) {
|
|
|
|
break;
|
|
|
|
case QSsl::SslV2:
|
|
|
|
return QLatin1String("SSL v2");
|
|
|
|
case QSsl::SslV3:
|
|
|
|
return QLatin1String("SSL v3");
|
|
|
|
case QSsl::TlsV1:
|
|
|
|
return QLatin1String("TLS");
|
|
|
|
default:
|
|
|
|
return QString();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-04-09 16:30:43 +04:00
|
|
|
static QString addCertDetailsField(const QString &key, const QString &value)
|
2014-01-21 04:45:02 +04:00
|
|
|
{
|
2014-01-21 17:55:23 +04:00
|
|
|
if (value.isEmpty())
|
|
|
|
return QString();
|
|
|
|
|
2014-04-09 16:30:43 +04:00
|
|
|
return QString::fromLatin1("<tr><td style=\"vertical-align: top;\"><b>%1</b></td><td style=\"vertical-align: bottom;\">%2</td></tr>").arg(key).arg(value);
|
2014-01-21 04:45:02 +04:00
|
|
|
}
|
|
|
|
|
2014-01-31 13:35:35 +04:00
|
|
|
|
|
|
|
// necessary indication only, not sufficient for primary validation!
|
|
|
|
static bool isSelfSigned(const QSslCertificate &certificate)
|
|
|
|
{
|
|
|
|
return certificate.issuerInfo(QSslCertificate::CommonName) == certificate.subjectInfo(QSslCertificate::CommonName) &&
|
|
|
|
certificate.issuerInfo(QSslCertificate::OrganizationalUnitName) == certificate.subjectInfo(QSslCertificate::OrganizationalUnitName);
|
|
|
|
}
|
|
|
|
|
2014-01-21 04:45:02 +04:00
|
|
|
QMenu* SslButton::buildCertMenu(QMenu *parent, const QSslCertificate& cert,
|
|
|
|
const QList<QSslCertificate>& userApproved, int pos)
|
|
|
|
{
|
2014-01-21 17:55:23 +04:00
|
|
|
QString cn = QStringList(cert.subjectInfo(QSslCertificate::CommonName)).join(QChar(';'));
|
|
|
|
QString ou = QStringList(cert.subjectInfo(QSslCertificate::OrganizationalUnitName)).join(QChar(';'));
|
|
|
|
QString org = QStringList(cert.subjectInfo(QSslCertificate::Organization)).join(QChar(';'));
|
|
|
|
QString country = QStringList(cert.subjectInfo(QSslCertificate::CountryName)).join(QChar(';'));
|
|
|
|
QString state = QStringList(cert.subjectInfo(QSslCertificate::StateOrProvinceName)).join(QChar(';'));
|
|
|
|
QString issuer = QStringList(cert.issuerInfo(QSslCertificate::CommonName)).join(QChar(';'));
|
2014-02-22 02:31:29 +04:00
|
|
|
if (issuer.isEmpty())
|
|
|
|
issuer = QStringList(cert.issuerInfo(QSslCertificate::OrganizationalUnitName)).join(QChar(';'));
|
2014-04-09 16:30:43 +04:00
|
|
|
QString sha1 = Utility::formatFingerprint(cert.digest(QCryptographicHash::Sha1).toHex(), false);
|
|
|
|
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
|
|
|
|
QString md5 = Utility::formatFingerprint(cert.digest(QCryptographicHash::Md5).toHex(), false);
|
|
|
|
#else
|
|
|
|
QByteArray sha265hash = cert.digest(QCryptographicHash::Sha256).toHex();
|
|
|
|
QString sha256escaped =
|
|
|
|
Utility::escape(Utility::formatFingerprint(sha265hash.left(sha265hash.length()/2), false)) +
|
|
|
|
QLatin1String("<br/>") +
|
|
|
|
Utility::escape(Utility::formatFingerprint(sha265hash.mid(sha265hash.length()/2), false));
|
|
|
|
#endif
|
2014-05-28 23:15:03 +04:00
|
|
|
QString serial = QString::fromUtf8(cert.serialNumber());
|
2014-01-21 04:45:02 +04:00
|
|
|
QString effectiveDate = cert.effectiveDate().date().toString();
|
|
|
|
QString expiryDate = cert.expiryDate().date().toString();
|
2014-01-21 17:55:23 +04:00
|
|
|
QString sna = QStringList(cert.alternateSubjectNames().values()).join(" ");
|
|
|
|
|
|
|
|
QString details;
|
|
|
|
QTextStream stream(&details);
|
|
|
|
|
|
|
|
stream << QLatin1String("<html><body>");
|
|
|
|
|
|
|
|
stream << tr("<h3>Certificate Details</h3>");
|
|
|
|
|
|
|
|
stream << QLatin1String("<table>");
|
|
|
|
stream << addCertDetailsField(tr("Common Name (CN):"), Utility::escape(cn));
|
|
|
|
stream << addCertDetailsField(tr("Subject Alternative Names:"), Utility::escape(sna)
|
|
|
|
.replace(" ", "<br/>"));
|
|
|
|
stream << addCertDetailsField(tr("Organization (O):"), Utility::escape(org));
|
|
|
|
stream << addCertDetailsField(tr("Organizational Unit (OU):"), Utility::escape(ou));
|
2014-01-22 19:09:01 +04:00
|
|
|
stream << addCertDetailsField(tr("State/Province:"), Utility::escape(state));
|
2014-01-21 17:55:23 +04:00
|
|
|
stream << addCertDetailsField(tr("Country:"), Utility::escape(country));
|
2014-04-09 16:30:43 +04:00
|
|
|
stream << addCertDetailsField(tr("Serial:"), Utility::escape(serial));
|
2014-01-21 17:55:23 +04:00
|
|
|
stream << QLatin1String("</table>");
|
|
|
|
|
|
|
|
stream << tr("<h3>Issuer</h3>");
|
|
|
|
|
|
|
|
stream << QLatin1String("<table>");
|
|
|
|
stream << addCertDetailsField(tr("Issuer:"), Utility::escape(issuer));
|
|
|
|
stream << addCertDetailsField(tr("Issued on:"), Utility::escape(effectiveDate));
|
|
|
|
stream << addCertDetailsField(tr("Expires on:"), Utility::escape(expiryDate));
|
|
|
|
stream << QLatin1String("</table>");
|
|
|
|
|
|
|
|
stream << tr("<h3>Fingerprints</h3>");
|
|
|
|
|
|
|
|
stream << QLatin1String("<table>");
|
2014-04-09 16:30:43 +04:00
|
|
|
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
|
|
|
|
stream << addCertDetailsField(tr("MD 5:"), Utility::escape(md5));
|
|
|
|
#else
|
|
|
|
stream << addCertDetailsField(tr("SHA-256:"), sha256escaped);
|
|
|
|
#endif
|
|
|
|
stream << addCertDetailsField(tr("SHA-1:"), Utility::escape(sha1));
|
2014-01-21 17:55:23 +04:00
|
|
|
stream << QLatin1String("</table>");
|
2014-01-21 04:45:02 +04:00
|
|
|
|
|
|
|
if (userApproved.contains(cert)) {
|
2014-01-21 17:55:23 +04:00
|
|
|
stream << tr("<p><b>Note:</b> This certificate was manually approved</p>");
|
2014-01-21 04:45:02 +04:00
|
|
|
}
|
2014-01-21 17:55:23 +04:00
|
|
|
stream << QLatin1String("</body></html>");
|
2014-01-21 04:45:02 +04:00
|
|
|
|
|
|
|
QString txt;
|
|
|
|
if (pos > 0) {
|
2014-01-31 13:35:35 +04:00
|
|
|
txt += QString(2*pos, ' ');
|
2014-02-12 20:51:11 +04:00
|
|
|
if (!Utility::isWindows()) {
|
|
|
|
// doesn't seem to work reliably on Windows
|
|
|
|
txt += QChar(0x21AA); // nicer '->' symbol
|
|
|
|
txt += QChar(' ');
|
|
|
|
}
|
2014-01-21 04:45:02 +04:00
|
|
|
}
|
|
|
|
|
2014-01-31 13:35:35 +04:00
|
|
|
QString certId = cn.isEmpty() ? ou : cn;
|
|
|
|
|
2014-02-22 02:31:29 +04:00
|
|
|
if (QSslSocket::systemCaCertificates().contains(cert)) {
|
|
|
|
txt += certId;
|
2014-01-21 04:45:02 +04:00
|
|
|
} else {
|
2014-01-31 13:35:35 +04:00
|
|
|
if (isSelfSigned(cert)) {
|
|
|
|
txt += tr("%1 (self-signed)").arg(certId);
|
2014-01-21 04:45:02 +04:00
|
|
|
} else {
|
2014-01-31 13:35:35 +04:00
|
|
|
txt += tr("%1").arg(certId);
|
2014-01-21 04:45:02 +04:00
|
|
|
}
|
|
|
|
}
|
2014-01-21 17:55:23 +04:00
|
|
|
|
|
|
|
// create label first
|
|
|
|
QLabel *label = new QLabel(parent);
|
|
|
|
label->setStyleSheet(QLatin1String("QLabel { padding: 8px; background-color: #fff; }"));
|
|
|
|
label->setText(details);
|
|
|
|
// plug label into widget action
|
|
|
|
QWidgetAction *action = new QWidgetAction(parent);
|
|
|
|
action->setDefaultWidget(label);
|
|
|
|
// plug action into menu
|
|
|
|
QMenu *menu = new QMenu(parent);
|
|
|
|
menu->menuAction()->setText(txt);
|
|
|
|
menu->addAction(action);
|
|
|
|
|
|
|
|
return menu;
|
2014-01-21 04:45:02 +04:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2014-12-17 16:09:57 +03:00
|
|
|
void SslButton::updateAccountState(AccountState *accountState)
|
2014-01-21 04:45:02 +04:00
|
|
|
{
|
2014-12-17 16:09:57 +03:00
|
|
|
if (!accountState || !accountState->isConnected()) {
|
2014-01-21 04:45:02 +04:00
|
|
|
setVisible(false);
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
setVisible(true);
|
|
|
|
}
|
2015-03-25 17:39:53 +03:00
|
|
|
_accountState = accountState;
|
|
|
|
|
|
|
|
AccountPtr account = _accountState->account();
|
2014-01-21 04:45:02 +04:00
|
|
|
if (account->url().scheme() == QLatin1String("https")) {
|
2015-11-17 12:48:45 +03:00
|
|
|
setIcon(QIcon(QLatin1String(":/client/resources/lock-https.png")));
|
2015-08-11 13:18:25 +03:00
|
|
|
QSslCipher cipher = account->_sessionCipher;
|
2014-01-21 04:45:02 +04:00
|
|
|
setToolTip(tr("This connection is encrypted using %1 bit %2.\n").arg(cipher.usedBits()).arg(cipher.name()));
|
2015-07-02 11:49:18 +03:00
|
|
|
setMenu(_menu);
|
2015-03-25 17:39:53 +03:00
|
|
|
} else {
|
2015-11-17 12:48:45 +03:00
|
|
|
setIcon(QIcon(QLatin1String(":/client/resources/lock-http.png")));
|
2015-03-25 17:39:53 +03:00
|
|
|
setToolTip(tr("This connection is NOT secure as it is not encrypted.\n"));
|
2015-07-30 11:44:07 +03:00
|
|
|
setMenu(0);
|
2015-03-25 17:39:53 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void SslButton::slotUpdateMenu() {
|
2015-07-02 11:49:18 +03:00
|
|
|
_menu->clear();
|
2015-03-25 17:39:53 +03:00
|
|
|
|
|
|
|
if (!_accountState) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
AccountPtr account = _accountState->account();
|
|
|
|
|
|
|
|
if (account->url().scheme() == QLatin1String("https")) {
|
2015-08-11 13:18:25 +03:00
|
|
|
QString sslVersion = account->_sessionCipher.protocolString()
|
|
|
|
+ ", " + account->_sessionCipher.authenticationMethod()
|
|
|
|
+ ", " + account->_sessionCipher.keyExchangeMethod()
|
|
|
|
+ ", " + account->_sessionCipher.encryptionMethod();
|
2015-08-05 13:58:04 +03:00
|
|
|
_menu->addAction(sslVersion)->setEnabled(false);
|
|
|
|
|
|
|
|
#if QT_VERSION > QT_VERSION_CHECK(5, 2, 0)
|
2015-08-11 13:18:25 +03:00
|
|
|
if (account->_sessionTicket.isEmpty()) {
|
2015-08-05 13:58:04 +03:00
|
|
|
_menu->addAction(tr("No support for SSL session tickets/identifiers"))->setEnabled(false);
|
|
|
|
}
|
|
|
|
#endif
|
2015-03-25 17:39:53 +03:00
|
|
|
|
2015-08-11 13:18:25 +03:00
|
|
|
QList<QSslCertificate> chain = account->_peerCertificateChain;
|
2015-03-23 20:28:51 +03:00
|
|
|
|
|
|
|
if (chain.isEmpty()) {
|
|
|
|
qWarning() << "empty certificate chain";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-07-02 11:49:18 +03:00
|
|
|
_menu->addAction(tr("Certificate information:"))->setEnabled(false);
|
2014-01-21 04:45:02 +04:00
|
|
|
|
2014-02-22 02:31:29 +04:00
|
|
|
QList<QSslCertificate> tmpChain;
|
|
|
|
foreach(QSslCertificate cert, chain) {
|
|
|
|
tmpChain << cert;
|
|
|
|
if (QSslSocket::systemCaCertificates().contains(cert))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
chain = tmpChain;
|
|
|
|
|
2014-01-21 04:45:02 +04:00
|
|
|
// find trust anchor (informational only, verification is done by QSslSocket!)
|
2014-02-22 02:31:29 +04:00
|
|
|
foreach(QSslCertificate rootCA, QSslSocket::systemCaCertificates()) {
|
2014-01-21 18:07:57 +04:00
|
|
|
if (rootCA.issuerInfo(QSslCertificate::CommonName) == chain.last().issuerInfo(QSslCertificate::CommonName) &&
|
2014-02-22 02:31:29 +04:00
|
|
|
rootCA.issuerInfo(QSslCertificate::Organization) == chain.last().issuerInfo(QSslCertificate::Organization)) {
|
|
|
|
chain.append(rootCA);
|
2014-01-21 04:45:02 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
QListIterator<QSslCertificate> it(chain);
|
|
|
|
it.toBack();
|
|
|
|
int i = 0;
|
|
|
|
while (it.hasPrevious()) {
|
2015-07-02 11:49:18 +03:00
|
|
|
_menu->addMenu(buildCertMenu(_menu, it.previous(), account->approvedCerts(), i));
|
2014-01-21 04:45:02 +04:00
|
|
|
i++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-11-10 00:34:07 +03:00
|
|
|
} // namespace OCC
|