Skip to content
Permalink
Browse files

Murmur: add support for EDH cipher suites, and for specifying Diffie-…

…Hellman parmeters.

This change allows server admins to specify Diffie-Hellman
parameters for Murmur to use. This is done using the sslDHParams
option in the config file. Diffie-Hellman parameters can also be
set on a per-server basis using the sslDHParams option.

Note: the functionality implemented in this change requires the
QSslDiffieHellmanParameters class in Qt, which has not yet landed
upstream in the Qt 5 'dev' branch. This means that the functionality
discussed in this change will, for now, only work in binaries provided
by the Mumble project, or binaries that are built using our build
environments, and not binaries that link against any released versions
of Qt at present.

This change modifies the default TLS cipher suite string to add
EDH+aRSA+AESGCM, DHE-RSA-AES256-SHA and DHE-RSA-AES128-SHA.

This yields the following ciphers, in TLS/RFC notation:

    TLS_DHE_RSA_WITH_AES_256_GCM_SHA384
    TLS_DHE_RSA_WITH_AES_128_GCM_SHA256
    TLS_DHE_RSA_WITH_AES_256_CBC_SHA
    TLS_DHE_RSA_WITH_AES_128_CBC_SHA

This change also allows Murmur servers to provide forward secrecy
to older clients, such as our own pre-built binaries before 1.2.9.

It also provides forward secrecy for users that use Mumble 1.2.x
versions on Linux distros, and other Unix-like systems. This is
because Mumble 1.2.x on Unix-like systems builds against Qt 4, which
limits the connection to TLS 1.0.

Before this change, Murmur was not able to negotiate an ephemeral
Diffie-Hellman key exchange for those clients. This is now possible.
  • Loading branch information...
mkrautz committed Sep 26, 2015
1 parent b627654 commit 8bd3f76a8ea3d8751c0305cfcaaa3592d2ffa9b2
Showing with 172 additions and 6 deletions.
  1. +6 −0 INSTALL
  2. +8 −1 scripts/murmur.ini
  3. +1 −1 src/SSL.cpp
  4. +72 −1 src/murmur/Cert.cpp
  5. +55 −3 src/murmur/Meta.cpp
  6. +1 −0 src/murmur/Meta.h
  7. +6 −0 src/murmur/Server.cpp
  8. +6 −0 src/murmur/Server.h
  9. +1 −0 src/murmur/main.cpp
  10. +16 −0 src/murmur/murmur.pro
@@ -160,3 +160,9 @@ CONFIG+=no-gkey (Mumble, Win32)
time dependencies, and requires Logitech Gaming
Software to be installed to have any effect at
runtime.

CONFIG+=no-qssldiffiehellmanparameters
Don't build in support for custom Diffie-Hellman
parameters, even if the QSslDiffieHellmanParameters
class is available in the version of Qt you are
building against.
@@ -160,6 +160,13 @@ users=100
;sslCert=
;sslKey=

; The sslDHParams option allows you to specify a PEM-encoded file with
; Diffie-Hellman parameters, which will be used as the default Diffie-
; Hellman parameters for all virtual servers.
; If a file is not specified, each Murmur virtual server will auto-generate
; its own unique set of 2048-bit Diffie-Hellman parameters on first launch.
;sslDHParams=

; The sslCiphers option chooses the cipher suites to make available for use
; in SSL/TLS. This option is server-wide, and cannot be set on a
; per-virtual-server basis.
@@ -176,7 +183,7 @@ users=100
; Note: Changing this option may impact the backwards compatibility of your
; Murmur server, and can remove the ability for older Mumble clients to be able
; to connect to it.
;sslCiphers=EECDH+AESGCM:AES256-SHA:AES128-SHA
;sslCiphers=EECDH+AESGCM:EDH+aRSA+AESGCM:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:AES256-SHA:AES128-SHA

; If Murmur is started as root, which user should it switch to?
; This option is ignored if Murmur isn't started with root privileges.
@@ -35,7 +35,7 @@
#include "Version.h"

QString MumbleSSL::defaultOpenSSLCipherString() {
return QLatin1String("EECDH+AESGCM:AES256-SHA:AES128-SHA");
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) {
@@ -49,6 +49,15 @@ static int add_ext(X509 * crt, int nid, char *value) {
return 1;
}

// dh_progress is a status callback for DH_generate_parameterss_ex.
// We use it to run the event loop while generating DH params, in
// order to keep the Murmur GUI on Windows responsive during the
// process.
static int dh_progress(int, int, BN_GENCB *) {
qApp->processEvents();
return 1;
}

bool Server::isKeyForCert(const QSslKey &key, const QSslCertificate &cert) {
if (key.isNull() || cert.isNull() || (key.type() != QSsl::PrivateKey))
return false;
@@ -85,11 +94,12 @@ bool Server::isKeyForCert(const QSslKey &key, const QSslCertificate &cert) {
}

void Server::initializeCert() {
QByteArray crt, key, pass;
QByteArray crt, key, pass, dhparams;

crt = getConf("certificate", QString()).toByteArray();
key = getConf("key", QString()).toByteArray();
pass = getConf("passphrase", QByteArray()).toByteArray();
dhparams = getConf("sslDHParams", Meta::mp.qbaDHParams).toByteArray();

QList<QSslCertificate> ql;

@@ -116,6 +126,21 @@ void Server::initializeCert() {
qlCA = ql;
}

#if defined(USE_QSSLDIFFIEHELLMANPARAMETERS)
if (! dhparams.isEmpty()) {
QSslDiffieHellmanParameters qdhp = QSslDiffieHellmanParameters(dhparams);
if (qdhp.isValid()) {
qsdhpDHParams = qdhp;
} else {
log(QString::fromLatin1("Unable to use specified Diffie-Hellman parameters (sslDHParams): %1").arg(qdhp.errorString()));
}
}
#else
if (! dhparams.isEmpty()) {
log("Diffie-Hellman parameters (sslDHParams) were specified, but will not be used. This version of Murmur does not support Diffie-Hellman parameters.");
}
#endif

QString issuer;
#if QT_VERSION >= 0x050000
QStringList issuerNames = qscCert.issuerInfo(QSslCertificate::CommonName);
@@ -190,6 +215,52 @@ void Server::initializeCert() {
setConf("key", qskKey.toPem());
}
}

#if defined(USE_QSSLDIFFIEHELLMANPARAMETERS)
if (qsdhpDHParams.isEmpty()) {
log("Generating new server 2048-bit Diffie-Hellman parameters. This could take a while...");

DH *dh = DH_new();
if (dh == NULL) {
qFatal("DH_new failed: unable to generate Diffie-Hellman parameters for virtual server");
}

// Generate DH params.
// We register a status callback in order to update the UI
// for Murmur on Windows. We don't show the actual status,
// but we do it to keep Murmur on Windows responsive while
// generating the parameters.
BN_GENCB cb;
memset(&cb, 0, sizeof(BN_GENCB));
BN_GENCB_set(&cb, dh_progress, NULL);
if (DH_generate_parameters_ex(dh, 2048, 2, &cb) == 0) {
qFatal("DH_generate_parameters_ex failed: unable to generate Diffie-Hellman parameters for virtual server");
}

BIO *mem = BIO_new(BIO_s_mem());
if (PEM_write_bio_DHparams(mem, dh) == 0) {
qFatal("PEM_write_bio_DHparams failed: unable to write generated Diffie-Hellman parameters to memory");
}

char *pem = NULL;
long len = BIO_get_mem_data(mem, &pem);
if (len <= 0) {
qFatal("BIO_get_mem_data returned an empty or invalid buffer");
}

QByteArray pemdh(pem, len);
QSslDiffieHellmanParameters qdhp(pemdh);
if (!qdhp.isValid()) {
qFatal("QSslDiffieHellmanParameters: unable to import generated Diffie-HellmanParameters: %s", qdhp.errorString());
}

qsdhpDHParams = qdhp;
setConf("sslDHParams", pemdh);

BIO_free(mem);
DH_free(dh);
}
#endif
}

const QString Server::getDigest() const {
@@ -40,6 +40,10 @@
#include "Version.h"
#include "SSL.h"

#if defined(USE_QSSLDIFFIEHELLMANPARAMETERS)
# include <QSslDiffieHellmanParameters>
#endif

MetaParams Meta::mp;

#ifdef Q_OS_WIN
@@ -377,6 +381,7 @@ void MetaParams::read(QString fname) {
QString qsSSLCert = qsSettings->value("sslCert").toString();
QString qsSSLKey = qsSettings->value("sslKey").toString();
QString qsSSLCA = qsSettings->value("sslCA").toString();
QString qsSSLDHParams = qsSettings->value("sslDHParams").toString();

qbaPassPhrase = qsSettings->value("sslPassPhrase").toByteArray();

@@ -396,7 +401,7 @@ void MetaParams::read(QString fname) {
}
}

QByteArray crt, key;
QByteArray crt, key, dhparams;

if (! qsSSLCert.isEmpty()) {
QFile pem(qsSSLCert);
@@ -452,6 +457,31 @@ void MetaParams::read(QString fname) {
}
}

#if defined(USE_QSSLDIFFIEHELLMANPARAMETERS)
if (! qsSSLDHParams.isEmpty()) {
QFile pem(qsSSLDHParams);
if (pem.open(QIODevice::ReadOnly)) {
dhparams = pem.readAll();
pem.close();
} else {
qCritical("Failed to read %s", qPrintable(qsSSLDHParams));
}
}

if (! dhparams.isEmpty()) {
QSslDiffieHellmanParameters qdhp = QSslDiffieHellmanParameters(dhparams);
if (qdhp.isValid()) {
qbaDHParams = dhparams;
} else {
qFatal("Unable to use specified Diffie-Hellman parameters: %s", qPrintable(qdhp.errorString()));
}
}
#else
if (! qsSSLDHParams.isEmpty()) {
qFatal("This version of Murmur does not support Diffie-Hellman parameters (sslDHParams). Murmur will not start unless you remove the option from your murmur.ini file.");
}
#endif

if (! QSslSocket::supportsSsl()) {
qFatal("Qt without SSL Support");
}
@@ -462,10 +492,31 @@ void MetaParams::read(QString fname) {
qFatal("Invalid sslCiphers option. Either the cipher string is invalid or none of the ciphers are available: \"%s\"", qPrintable(qsCiphers));
}

QSslSocket::setDefaultCiphers(ciphers);
// If the version of Qt we're building against doesn't support
// QSslDiffieHellmanParameters, then we must filter out Diffie-
// Hellman cipher suites in order to guarantee that we do not
// use Qt's default Diffie-Hellman parameters.
QList<QSslCipher> filtered;
#if !defined(USE_QSSLDIFFIEHELLMANPARAMETERS)
foreach (QSslCipher c, ciphers) {
if (c.keyExchangeMethod() == QLatin1String("DH")) {
continue;
}
filtered << c;
}
if (ciphers.size() != filtered.size()) {
qWarning("Warning: all cipher suites in sslCiphers using Diffie-Hellman key exchange "
"have been removed. Qt %s does not support custom Diffie-Hellman parameters.",
qVersion());
}
#else
filtered = ciphers;
#endif

QSslSocket::setDefaultCiphers(filtered);

QStringList pref;
foreach (QSslCipher c, ciphers) {
foreach (QSslCipher c, filtered) {
pref << c.name();
}
qWarning("Meta: TLS cipher preference is \"%s\"", qPrintable(pref.join(QLatin1String(":"))));
@@ -510,6 +561,7 @@ void MetaParams::read(QString fname) {
qmConfig.insert(QLatin1String("opusthreshold"), QString::number(iOpusThreshold));
qmConfig.insert(QLatin1String("channelnestinglimit"), QString::number(iChannelNestingLimit));
qmConfig.insert(QLatin1String("sslCiphers"), qsCiphers);
qmConfig.insert(QLatin1String("sslDHParams"), QString::fromLatin1(qbaDHParams.constData()));
}

Meta::Meta() {
@@ -114,6 +114,7 @@ class MetaParams {

QSslCertificate qscCert;
QSslKey qskKey;
QByteArray qbaDHParams;
QByteArray qbaPassPhrase;
QString qsCiphers;

@@ -1173,6 +1173,12 @@ void Server::newClient() {
sock->addCaCertificate(qscCert);
sock->addCaCertificates(qlCA);

#if defined(USE_QSSLDIFFIEHELLMANPARAMETERS)
QSslConfiguration cfg = sock->sslConfiguration();
cfg.setDiffieHellmanParameters(qsdhpDHParams);
sock->setSslConfiguration(cfg);
#endif

if (qqIds.isEmpty()) {
log(QString("Session ID pool (%1) empty, rejecting connection").arg(iMaxUsers));
sock->disconnectFromHost();
@@ -49,6 +49,9 @@
#include <QtNetwork/QSslKey>
#include <QtNetwork/QSslSocket>
#include <QtNetwork/QTcpServer>
#if defined(USE_QSSLDIFFIEHELLMANPARAMETERS)
# include <QtNetwork/QSslDiffieHellmanParameters>
#endif
#ifdef Q_OS_WIN
#include <windows.h>
#endif
@@ -168,6 +171,9 @@ class Server : public QThread {
QList<QSslCertificate> qlCA;
QSslCertificate qscCert;
QSslKey qskKey;
#if defined(USE_QSSLDIFFIEHELLMANPARAMETERS)
QSslDiffieHellmanParameters qsdhpDHParams;
#endif

Timer tUptime;

@@ -419,6 +419,7 @@ int main(int argc, char **argv) {
ServerDB::setConf(sid, "key");
ServerDB::setConf(sid, "certificate");
ServerDB::setConf(sid, "passphrase");
ServerDB::setConf(sid, "sslDHParams");
}
}

@@ -168,4 +168,20 @@ bonjour {
}
}

# Check for QSslDiffieHellmanParameters availability, and define
# USE_QSSLDIFFIEHELLMANPARAMETERS preprocessor if available.
#
# Can be disabled with no-qssldiffiehellmanparameters.
!CONFIG(no-qssldiffiehellmanparameters):exists($$[QT_INSTALL_HEADERS]/QtNetwork/QSslDiffieHellmanParameters) {
# ...but only if we're inside a Mumble build environment for now.
# If someone decides to put a Mumble snapshot into a distro, this
# could break the build in the future, with newer versions of Qt,
# if the API of QSslDiffieHellmanParameters changes when it is
# upstreamed.
MUMBLE_PREFIX=$$(MUMBLE_PREFIX)
!isEmpty(MUMBLE_PREFIX) {
DEFINES += USE_QSSLDIFFIEHELLMANPARAMETERS
}
}

include(../../symbols.pri)

0 comments on commit 8bd3f76

Please sign in to comment.
You can’t perform that action at this time.