mirror of
https://github.com/mumble-voip/mumble.git
synced 2025-10-26 11:19:16 +00:00
Meta, UnixMurmur: implement live certificate reloading via the USR1 signal.
This moves the SSL loading code from MetaParams::read() into a separate method, loadSSLSettings(). It also hooks up the SIGUSR1 signal handler to reload SSL settings and apply the setings to each suitable virtual server. A follow-up commit will change MetaParams::read() to also use loadSSLSettings(), however it was left out of this commit to aid in reviewability.
This commit is contained in:
parent
9ae2a7f59e
commit
fd78d6431e
@ -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<QSslCertificate> tmpCA;
|
||||
QList<QSslCertificate> tmpIntermediates;
|
||||
QSslKey tmpKey;
|
||||
QByteArray tmpDHParams;
|
||||
QList<QSslCipher> tmpCiphers;
|
||||
|
||||
|
||||
if (! qsSSLCA.isEmpty()) {
|
||||
QFile pem(qsSSLCA);
|
||||
if (pem.open(QIODevice::ReadOnly)) {
|
||||
QByteArray qba = pem.readAll();
|
||||
pem.close();
|
||||
QList<QSslCertificate> 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<QSslCertificate> ql = QSslCertificate::fromData(crt);
|
||||
ql << QSslCertificate::fromData(key);
|
||||
for (int i=0;i<ql.size(); ++i) {
|
||||
const QSslCertificate &c = ql.at(i);
|
||||
if (Server::isKeyForCert(tmpKey, c)) {
|
||||
tmpCert = c;
|
||||
ql.removeAt(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (tmpCert.isNull()) {
|
||||
qCritical("MetaParams: Failed to find certificate matching private key.");
|
||||
return false;
|
||||
}
|
||||
if (ql.size() > 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<QSslCipher> 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<QSslCipher> 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();
|
||||
|
||||
@ -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 <class T>
|
||||
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 &);
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user