diff --git a/src/murmur/Meta.cpp b/src/murmur/Meta.cpp index f21be0415..1da7d3997 100644 --- a/src/murmur/Meta.cpp +++ b/src/murmur/Meta.cpp @@ -544,6 +544,188 @@ void MetaParams::read(QString fname) { qmConfig.insert(QLatin1String("sslDHParams"), QString::fromLatin1(qbaDHParams.constData())); } +bool MetaParams::loadSSLSettings() { + QSettings updatedSettings(qsAbsSettingsFilePath, QSettings::IniFormat); +#if QT_VERSION >= 0x040500 + updatedSettings.setIniCodec("UTF-8"); +#endif + + QString tmpCiphersStr = typeCheckedFromSettings("sslCiphers", qsCiphers); + + 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(); + + QSslCertificate tmpCert; + QList tmpCA; + QList tmpIntermediates; + QSslKey tmpKey; + QByteArray tmpDHParams; + QList tmpCiphers; + + + if (! qsSSLCA.isEmpty()) { + QFile pem(qsSSLCA); + if (pem.open(QIODevice::ReadOnly)) { + QByteArray qba = pem.readAll(); + pem.close(); + QList ql = QSslCertificate::fromData(qba); + if (ql.isEmpty()) { + qCritical("MetaParams: Failed to parse any CA certificates from %s", qPrintable(qsSSLCA)); + return false; + } else { + tmpCA = ql; + } + } else { + qCritical("MetaParams: Failed to read %s", qPrintable(qsSSLCA)); + return false; + } + } + + QByteArray crt, key; + + if (! qsSSLCert.isEmpty()) { + QFile pem(qsSSLCert); + if (pem.open(QIODevice::ReadOnly)) { + crt = pem.readAll(); + pem.close(); + } else { + qCritical("MetaParams: Failed to read %s", qPrintable(qsSSLCert)); + return false; + } + } + if (! qsSSLKey.isEmpty()) { + QFile pem(qsSSLKey); + if (pem.open(QIODevice::ReadOnly)) { + key = pem.readAll(); + pem.close(); + } else { + qCritical("MetaParams: Failed to read %s", qPrintable(qsSSLKey)); + return false; + } + } + + if (! key.isEmpty() || ! crt.isEmpty()) { + if (! key.isEmpty()) { + tmpKey = Server::privateKeyFromPEM(key, qbaPassPhrase); + } + if (tmpKey.isNull() && ! crt.isEmpty()) { + tmpKey = Server::privateKeyFromPEM(crt, qbaPassPhrase); + if (! tmpKey.isNull()) + qCritical("MetaParams: Using private key found in certificate file."); + } + if (tmpKey.isNull()) { + qCritical("MetaParams: No private key found in certificate or key file."); + return false; + } + + QList ql = QSslCertificate::fromData(crt); + ql << QSslCertificate::fromData(key); + for (int i=0;i 0) { + tmpIntermediates = ql; + qCritical("MetaParams: Adding %d intermediate certificates from certificate file.", ql.size()); + } + } + + QByteArray dhparams; + +#if defined(USE_QSSLDIFFIEHELLMANPARAMETERS) + if (! qsSSLDHParams.isEmpty()) { + QFile pem(qsSSLDHParams); + if (pem.open(QIODevice::ReadOnly)) { + dhparams = pem.readAll(); + pem.close(); + } else { + qCritical("MetaParams: Failed to read %s", qPrintable(qsSSLDHParams)); + } + } + + if (! dhparams.isEmpty()) { + QSslDiffieHellmanParameters qdhp = QSslDiffieHellmanParameters::fromEncoded(dhparams); + if (qdhp.isValid()) { + tmpDHParams = dhparams; + } else { + qCritical("MetaParams: Unable to use specified Diffie-Hellman parameters: %s", qPrintable(qdhp.errorString())); + return false; + } + } +#else + if (! qsSSLDHParams.isEmpty()) { + qCritical("MetaParams: 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."); + return false; + } +#endif + + { + QList ciphers = MumbleSSL::ciphersFromOpenSSLCipherString(tmpCiphersStr); + if (ciphers.isEmpty()) { + qCritical("MetaParams: Invalid sslCiphers option. Either the cipher string is invalid or none of the ciphers are available: \"%s\"", qPrintable(qsCiphers)); + return false; + } + +#if !defined(USE_QSSLDIFFIEHELLMANPARAMETERS) + // 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 filtered; + foreach (QSslCipher c, ciphers) { + if (c.keyExchangeMethod() == QLatin1String("DH")) { + continue; + } + filtered << c; + } + if (ciphers.size() != filtered.size()) { + qWarning("MetaParams: Warning: all cipher suites in sslCiphers using Diffie-Hellman key exchange " + "have been removed. Qt %s does not support custom Diffie-Hellman parameters.", + qVersion()); + } + + tmpCiphers = filtered; + } +#else + tmpCiphers = ciphers; +#endif + + QStringList pref; + foreach (QSslCipher c, tmpCiphers) { + pref << c.name(); + } + qWarning("MetaParams: TLS cipher preference is \"%s\"", qPrintable(pref.join(QLatin1String(":")))); + } + + qscCert = tmpCert; + qlCA = tmpCA; + qlIntermediates = tmpIntermediates; + qskKey = tmpKey; + qbaDHParams = tmpDHParams; + qsCiphers = tmpCiphersStr; + qlCiphers = tmpCiphers; + + qmConfig.insert(QLatin1String("certificate"), qscCert.toPem()); + qmConfig.insert(QLatin1String("key"), qskKey.toPem()); + qmConfig.insert(QLatin1String("sslCiphers"), qsCiphers); + qmConfig.insert(QLatin1String("sslDHParams"), QString::fromLatin1(qbaDHParams.constData())); + + return true; +} + Meta::Meta() { #ifdef Q_OS_WIN QOS_VERSION qvVer; @@ -574,6 +756,27 @@ Meta::~Meta() { #endif } +bool Meta::reloadSSLSettings() { + // Reload SSL settings. + if (!Meta::mp.loadSSLSettings()) { + return false; + } + + // Re-initialize certificates for all + // virtual servers using the Meta server's + // certificate and private key. + foreach (Server *s, qhServers) { + if (s->bUsingMetaCert) { + s->log("Reloading certificates..."); + s->initializeCert(); + } else { + s->log("Not reloading certificates; server does not use Meta certificate"); + } + } + + return true; +} + void Meta::getOSInfo() { qsOS = OSInfo::getOS(); qsOSVersion = OSInfo::getOSDisplayableVersion(); diff --git a/src/murmur/Meta.h b/src/murmur/Meta.h index b5466ff27..db3406383 100644 --- a/src/murmur/Meta.h +++ b/src/murmur/Meta.h @@ -138,6 +138,12 @@ public: ~MetaParams(); void read(QString fname = QString("murmur.ini")); + /// Attempt to load SSL settings from murmur.ini. + /// Returns true if successful. Returns false if + /// the operation failed. On failure, the MetaParams + /// object is left 100% intact. + bool loadSSLSettings(); + private: template T typeCheckedFromSettings(const QString &name, const T &variable, QSettings *settings = NULL); @@ -161,6 +167,13 @@ class Meta : public QObject { Meta(); ~Meta(); + + /// reloadSSLSettings reloads Murmur's MetaParams's + /// SSL settings, and updates the certificate and + /// private key for all virtual servers that use the + /// Meta server's certificate and private key. + bool reloadSSLSettings(); + void bootAll(); bool boot(int); bool banCheck(const QHostAddress &); diff --git a/src/murmur/UnixMurmur.cpp b/src/murmur/UnixMurmur.cpp index 1e3e45efa..a3429503f 100644 --- a/src/murmur/UnixMurmur.cpp +++ b/src/murmur/UnixMurmur.cpp @@ -228,7 +228,15 @@ void UnixMurmur::handleSigUsr1() { ssize_t len = ::read(iUsr1Fd[1], &tmp, sizeof(tmp)); Q_UNUSED(len); - qWarning("Received USR1 signal... Ignoring..."); + if (meta) { + qWarning("UnixMurmur: Trying to reload SSL settings..."); + bool ok = meta->reloadSSLSettings(); + if (ok) { + qWarning("UnixMurmur: Done reloading SSL settings."); + } else { + qWarning("UnixMurmur: Failed to reload SSL settings. Server state is intact and fully operational. No configuration changes were made."); + } + } qsnUsr1->setEnabled(true); }