Load WebUI certificate & key from file path

This allow users to update certificate & key more easily, i.e. without the need to import them
into qbt.

Closes #6675, #7547, #8315, #8564.
This commit is contained in:
Chocobo1 2019-01-17 09:42:01 +08:00
parent 48d358872f
commit 5cdb3b6a2d
No known key found for this signature in database
GPG key ID: 210D9C873253A68C
12 changed files with 212 additions and 277 deletions

View file

@ -30,55 +30,69 @@
#include "server.h"
#include <algorithm>
#include <QMutableListIterator>
#include <QNetworkProxy>
#include <QSslCipher>
#include <QSslSocket>
#include <QStringList>
#include <QTimer>
#ifndef QT_NO_OPENSSL
#include <QSslSocket>
#else
#include <QTcpSocket>
#endif
#include "base/utils/net.h"
#include "connection.h"
static const int KEEP_ALIVE_DURATION = 7 * 1000; // milliseconds
static const int CONNECTIONS_LIMIT = 500;
static const int CONNECTIONS_SCAN_INTERVAL = 2; // seconds
namespace
{
const int KEEP_ALIVE_DURATION = 7 * 1000; // milliseconds
const int CONNECTIONS_LIMIT = 500;
const int CONNECTIONS_SCAN_INTERVAL = 2; // seconds
QList<QSslCipher> safeCipherList()
{
const QStringList badCiphers = {"idea", "rc4"};
const QList<QSslCipher> allCiphers = QSslSocket::supportedCiphers();
QList<QSslCipher> safeCiphers;
for (const QSslCipher &cipher : allCiphers) {
bool isSafe = true;
for (const QString &badCipher : badCiphers) {
if (cipher.name().contains(badCipher, Qt::CaseInsensitive)) {
isSafe = false;
break;
}
}
if (isSafe)
safeCiphers += cipher;
}
return safeCiphers;
}
}
using namespace Http;
Server::Server(IRequestHandler *requestHandler, QObject *parent)
: QTcpServer(parent)
, m_requestHandler(requestHandler)
#ifndef QT_NO_OPENSSL
, m_https(false)
#endif
{
setProxy(QNetworkProxy::NoProxy);
#ifndef QT_NO_OPENSSL
QSslSocket::setDefaultCiphers(safeCipherList());
#endif
QTimer *dropConnectionTimer = new QTimer(this);
connect(dropConnectionTimer, &QTimer::timeout, this, &Server::dropTimedOutConnection);
dropConnectionTimer->start(CONNECTIONS_SCAN_INTERVAL * 1000);
}
Server::~Server()
{
}
void Server::incomingConnection(qintptr socketDescriptor)
{
if (m_connections.size() >= CONNECTIONS_LIMIT) return;
QTcpSocket *serverSocket;
#ifndef QT_NO_OPENSSL
if (m_https)
serverSocket = new QSslSocket(this);
else
#endif
serverSocket = new QTcpSocket(this);
if (!serverSocket->setSocketDescriptor(socketDescriptor)) {
@ -86,7 +100,6 @@ void Server::incomingConnection(qintptr socketDescriptor)
return;
}
#ifndef QT_NO_OPENSSL
if (m_https) {
static_cast<QSslSocket *>(serverSocket)->setProtocol(QSsl::SecureProtocols);
static_cast<QSslSocket *>(serverSocket)->setPrivateKey(m_key);
@ -94,7 +107,6 @@ void Server::incomingConnection(qintptr socketDescriptor)
static_cast<QSslSocket *>(serverSocket)->setPeerVerifyMode(QSslSocket::VerifyNone);
static_cast<QSslSocket *>(serverSocket)->startServerEncryption();
}
#endif
Connection *c = new Connection(serverSocket, m_requestHandler, this);
m_connections.append(c);
@ -112,26 +124,20 @@ void Server::dropTimedOutConnection()
}
}
#ifndef QT_NO_OPENSSL
bool Server::setupHttps(const QByteArray &certificates, const QByteArray &key)
bool Server::setupHttps(const QByteArray &certificates, const QByteArray &privateKey)
{
QSslKey sslKey(key, QSsl::Rsa);
if (sslKey.isNull())
sslKey = QSslKey(key, QSsl::Ec);
const QList<QSslCertificate> certs {Utils::Net::loadSSLCertificate(certificates)};
const QSslKey key {Utils::Net::loadSSLKey(privateKey)};
const QList<QSslCertificate> certs = QSslCertificate::fromData(certificates);
const bool areCertsValid = !certs.empty() && std::all_of(certs.begin(), certs.end(), [](const QSslCertificate &c) { return !c.isNull(); });
if (!sslKey.isNull() && areCertsValid) {
m_key = sslKey;
m_certificates = certs;
m_https = true;
return true;
}
else {
if (certs.isEmpty() || key.isNull()) {
disableHttps();
return false;
}
m_key = key;
m_certificates = certs;
m_https = true;
return true;
}
void Server::disableHttps()
@ -140,25 +146,3 @@ void Server::disableHttps()
m_certificates.clear();
m_key.clear();
}
QList<QSslCipher> Server::safeCipherList() const
{
const QStringList badCiphers = {"idea", "rc4"};
const QList<QSslCipher> allCiphers = QSslSocket::supportedCiphers();
QList<QSslCipher> safeCiphers;
for (const QSslCipher &cipher : allCiphers) {
bool isSafe = true;
for (const QString &badCipher : badCiphers) {
if (cipher.name().contains(badCipher, Qt::CaseInsensitive)) {
isSafe = false;
break;
}
}
if (isSafe)
safeCiphers += cipher;
}
return safeCiphers;
}
#endif // QT_NO_OPENSSL