mumble-voip_mumble/src/SSL.cpp

183 lines
4.9 KiB
C++

// Copyright 2009-2023 The Mumble Developers. All rights reserved.
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file at the root of the
// Mumble source tree or at <https://www.mumble.info/LICENSE>.
#include "SSL.h"
#include "SSLLocks.h"
#include "Version.h"
#include <QtNetwork/QSslConfiguration>
#include <QtNetwork/QSslSocket>
#include <openssl/ssl.h>
void MumbleSSL::initialize() {
// Let Qt initialize its copy of OpenSSL, if it's different than
// Mumble's. (Initialization is a side-effect of calling
// QSslSocket::supportsSsl()).
QSslSocket::supportsSsl();
// Initialize our copy of OpenSSL.
SSL_library_init(); // Safe to discard return value, per OpenSSL man pages.
SSL_load_error_strings();
// Determine if a locking callback has not been set.
// This should be the case if there are multiple copies
// of OpensSSL in the address space. This is mostly due
// to Qt dynamically loading OpenSSL when it is not
// configured with -openssl-linked.
//
// If we detect that no locking callback is configured, we
// have to set it up ourselves to allow multi-threaded use
// of OpenSSL.
if (!CRYPTO_get_locking_callback()) {
SSLLocks::initialize();
}
}
void MumbleSSL::destroy() {
SSLLocks::destroy();
}
QString MumbleSSL::defaultOpenSSLCipherString() {
return QLatin1String("EECDH+AESGCM:EDH+aRSA+AESGCM:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:AES256-SHA:AES128-SHA");
}
QList< QSslCipher > MumbleSSL::ciphersFromOpenSSLCipherString(QString cipherString) {
QList< QSslCipher > chosenCiphers;
SSL_CTX *ctx = nullptr;
SSL *ssl = nullptr;
const SSL_METHOD *meth = nullptr;
int i = 0;
QByteArray csbuf = cipherString.toLatin1();
const char *ciphers = csbuf.constData();
meth = SSLv23_server_method();
if (!meth) {
qWarning("MumbleSSL: unable to get SSL method");
goto out;
}
// We use const_cast to be compatible with OpenSSL 0.9.8.
ctx = SSL_CTX_new(const_cast< SSL_METHOD * >(meth));
if (!ctx) {
qWarning("MumbleSSL: unable to allocate SSL_CTX");
goto out;
}
if (!SSL_CTX_set_cipher_list(ctx, ciphers)) {
qWarning("MumbleSSL: error parsing OpenSSL cipher string in ciphersFromOpenSSLCipherString");
goto out;
}
ssl = SSL_new(ctx);
if (!ssl) {
qWarning("MumbleSSL: unable to create SSL object in ciphersFromOpenSSLCipherString");
goto out;
}
while (1) {
const char *name = SSL_get_cipher_list(ssl, i);
if (!name) {
break;
}
#if QT_VERSION >= 0x050300
QSslCipher c = QSslCipher(QString::fromLatin1(name));
if (!c.isNull()) {
chosenCiphers << c;
}
#else
foreach (const QSslCipher &c, QSslSocket::supportedCiphers()) {
if (c.name() == QString::fromLatin1(name)) {
chosenCiphers << c;
}
}
#endif
++i;
}
out:
SSL_CTX_free(ctx);
SSL_free(ssl);
return chosenCiphers;
}
void MumbleSSL::addSystemCA() {
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
// Qt 5.15 introduced adding certificates to the QSslConfiguration and deprecated doing so on QSslSocket
auto config = QSslConfiguration::defaultConfiguration();
config.addCaCertificates(QSslConfiguration::systemCaCertificates());
QSslConfiguration::setDefaultConfiguration(config);
#else
QSslSocket::addDefaultCaCertificates(QSslConfiguration::systemCaCertificates());
#endif
#ifdef Q_OS_WIN
// Work around issue #1271.
// Skype's click-to-call feature creates an enormous
// amount of certificates in the Root CA store.
{
QSslConfiguration sslCfg = QSslConfiguration::defaultConfiguration();
QList< QSslCertificate > caList = sslCfg.caCertificates();
QList< QSslCertificate > filteredCaList;
foreach (QSslCertificate cert, caList) {
QStringList orgs = cert.subjectInfo(QSslCertificate::Organization);
bool skip = false;
foreach (QString ou, orgs) {
if (ou.contains(QLatin1String("Skype"), Qt::CaseInsensitive)) {
skip = true;
break;
}
}
if (skip) {
continue;
}
filteredCaList.append(cert);
}
sslCfg.setCaCertificates(filteredCaList);
QSslConfiguration::setDefaultConfiguration(sslCfg);
qWarning("SSL: CA certificate filter applied. Filtered size: %i, original size: %i", filteredCaList.size(),
caList.size());
}
#endif
}
QString MumbleSSL::protocolToString(QSsl::SslProtocol protocol) {
switch (protocol) {
case QSsl::SslV3:
return QLatin1String("SSL 3");
case QSsl::SslV2:
return QLatin1String("SSL 2");
case QSsl::TlsV1_0:
return QLatin1String("TLS 1.0");
case QSsl::TlsV1_1:
return QLatin1String("TLS 1.1");
case QSsl::TlsV1_2:
return QLatin1String("TLS 1.2");
#if QT_VERSION >= 0x050C00
case QSsl::TlsV1_3:
return QLatin1String("TLS 1.3");
#endif
case QSsl::AnyProtocol:
return QLatin1String("AnyProtocol");
case QSsl::TlsV1SslV3:
return QLatin1String("TlsV1SslV3");
case QSsl::SecureProtocols:
return QLatin1String("SecureProtocols");
default:
case QSsl::UnknownProtocol:
return QLatin1String("UnknownProtocol");
}
}