1 // Copyright 2005-2019 The Mumble Developers. All rights reserved.
2 // Use of this source code is governed by a BSD-style license
3 // that can be found in the LICENSE file at the root of the
4 // Mumble source tree or at <https://www.mumble.info/LICENSE>.
5
6 #include "murmur_pch.h"
7
8 #include "Meta.h"
9
10 #include "Connection.h"
11 #include "Net.h"
12 #include "ServerDB.h"
13 #include "Server.h"
14 #include "OSInfo.h"
15 #include "Version.h"
16 #include "SSL.h"
17 #include "EnvUtils.h"
18 #include "FFDHE.h"
19
20 #if defined(USE_QSSLDIFFIEHELLMANPARAMETERS)
21 # include <QSslDiffieHellmanParameters>
22 #endif
23
24 MetaParams Meta::mp;
25
26 #ifdef Q_OS_WIN
27 HANDLE Meta::hQoS = NULL;
28 #endif
29
MetaParams()30 MetaParams::MetaParams() {
31 qsPassword = QString();
32 usPort = DEFAULT_MUMBLE_PORT;
33 iTimeout = 30;
34 iMaxBandwidth = 72000;
35 iMaxUsers = 1000;
36 iMaxUsersPerChannel = 0;
37 iMaxTextMessageLength = 5000;
38 iMaxImageMessageLength = 131072;
39 legacyPasswordHash = false;
40 kdfIterations = -1;
41 bAllowHTML = true;
42 iDefaultChan = 0;
43 bRememberChan = true;
44 qsWelcomeText = QString();
45 qsDatabase = QString();
46 iSQLiteWAL = 0;
47 iDBPort = 0;
48 qsDBusService = "net.sourceforge.mumble.murmur";
49 qsDBDriver = "QSQLITE";
50 qsLogfile = "murmur.log";
51
52 iLogDays = 31;
53
54 iObfuscate = 0;
55 bSendVersion = true;
56 bBonjour = true;
57 bAllowPing = true;
58 bCertRequired = false;
59 bForceExternalAuth = false;
60
61 iBanTries = 10;
62 iBanTimeframe = 120;
63 iBanTime = 300;
64
65 #ifdef Q_OS_UNIX
66 uiUid = uiGid = 0;
67 #endif
68
69 iOpusThreshold = 100;
70
71 iChannelNestingLimit = 10;
72 iChannelCountLimit = 1000;
73
74 qrUserName = QRegExp(QLatin1String("[-=\\w\\[\\]\\{\\}\\(\\)\\@\\|\\.]+"));
75 qrChannelName = QRegExp(QLatin1String("[ \\-=\\w\\#\\[\\]\\{\\}\\(\\)\\@\\|]+"));
76
77 iMessageLimit = 1;
78 iMessageBurst = 5;
79
80 qsCiphers = MumbleSSL::defaultOpenSSLCipherString();
81
82 qsSettings = NULL;
83 }
84
~MetaParams()85 MetaParams::~MetaParams() {
86 delete qsSettings;
87 }
88
89 /**
90 * Checks whether a qsSettings config variable named 'name' was set to a valid value for the type
91 * we want to convert it to. Otherwise a error message is logged and 'defaultValue' is returned.
92 *
93 * E.g. checkedFromSettings<QString>("blub", bla) would output an error message and leave bla unchanged
94 * if the user had set the ini parameter to "blub = has, commas, in, it" which QSettings will interpret
95 * not as a String but as a list of strings.
96 *
97 * @param T Conversion target type (type of 'defaultValue', auto inducable)
98 * @param name qsSettings variable name
99 * @param defaultValue Default value for 'name'
100 * @param settings The QSettings object to read from. If null, the Meta's qsSettings will be used.
101 * @return Setting if valid, default if not or setting not set.
102 */
103 template <class T>
typeCheckedFromSettings(const QString & name,const T & defaultValue,QSettings * settings)104 T MetaParams::typeCheckedFromSettings(const QString &name, const T &defaultValue, QSettings *settings) {
105 // Use qsSettings unless a specific QSettings instance
106 // is requested.
107 if (settings == NULL) {
108 settings = qsSettings;
109 }
110
111 QVariant cfgVariable = settings->value(name, defaultValue);
112
113 if (!cfgVariable.convert(QVariant(defaultValue).type())) { // Bit convoluted as canConvert<T>() only does a static check without considering whether say a string like "blub" is actually a valid double (which convert does).
114 qCritical() << "Configuration variable" << name << "is of invalid format. Set to default value of" << defaultValue << ".";
115 return defaultValue;
116 }
117
118 return cfgVariable.value<T>();
119 }
120
read(QString fname)121 void MetaParams::read(QString fname) {
122 qmConfig.clear();
123
124 if (fname.isEmpty()) {
125 QStringList datapaths;
126
127 #if defined(Q_OS_WIN)
128 #if QT_VERSION >= 0x050000
129 datapaths << QStandardPaths::writableLocation(QStandardPaths::DataLocation);
130 #else
131 datapaths << QDesktopServices::storageLocation(QDesktopServices::DataLocation);
132 #endif
133
134 QDir appdir = QDir(QDir::fromNativeSeparators(EnvUtils::getenv(QLatin1String("APPDATA"))));
135 datapaths << appdir.absolutePath() + QLatin1String("/Mumble");
136 #else
137 datapaths << QDir::homePath() + QLatin1String("/.murmurd");
138 datapaths << QDir::homePath() + QLatin1String("/.config/Mumble");
139 #endif
140
141 #if defined(Q_OS_MAC)
142 /* Old Mac data path. */
143 datapaths << QDir::homePath() + QLatin1String("/Library/Preferences/Mumble/");
144 #endif
145
146 datapaths << QDir::homePath();
147 datapaths << QDir::currentPath();
148 datapaths << QCoreApplication::instance()->applicationDirPath();
149
150 foreach(const QString &p, datapaths) {
151 if (! p.isEmpty()) {
152 QFileInfo fi(p, "murmur.ini");
153 if (fi.exists() && fi.isReadable()) {
154 qdBasePath = QDir(p);
155 qsAbsSettingsFilePath = fi.absoluteFilePath();
156 break;
157 }
158 }
159 }
160 if (qsAbsSettingsFilePath.isEmpty()) {
161 QDir::root().mkpath(qdBasePath.absolutePath());
162 qdBasePath = QDir(datapaths.at(0));
163 qsAbsSettingsFilePath = qdBasePath.absolutePath() + QLatin1String("/murmur.ini");
164 }
165 } else {
166 QFile f(fname);
167 if (! f.open(QIODevice::ReadOnly)) {
168 qFatal("Specified ini file %s could not be opened", qPrintable(fname));
169 }
170 qdBasePath = QFileInfo(f).absoluteDir();
171 qsAbsSettingsFilePath = QFileInfo(f).absoluteFilePath();
172 f.close();
173 }
174 QDir::setCurrent(qdBasePath.absolutePath());
175 qsSettings = new QSettings(qsAbsSettingsFilePath, QSettings::IniFormat);
176 #if QT_VERSION >= 0x040500
177 qsSettings->setIniCodec("UTF-8");
178 #endif
179 qWarning("Initializing settings from %s (basepath %s)", qPrintable(qsSettings->fileName()), qPrintable(qdBasePath.absolutePath()));
180
181 QString qsHost = qsSettings->value("host", QString()).toString();
182 if (! qsHost.isEmpty()) {
183 foreach(const QString &host, qsHost.split(QRegExp(QLatin1String("\\s+")), QString::SkipEmptyParts)) {
184 QHostAddress qhaddr;
185 if (qhaddr.setAddress(host)) {
186 qlBind << qhaddr;
187 } else {
188 bool found = false;
189 QHostInfo hi = QHostInfo::fromName(host);
190 foreach(QHostAddress qha, hi.addresses()) {
191 if ((qha.protocol() == QAbstractSocket::IPv4Protocol) || (qha.protocol() == QAbstractSocket::IPv6Protocol)) {
192 qlBind << qha;
193 found = true;
194 }
195 }
196 if (! found) {
197 qFatal("Lookup of bind hostname %s failed", qPrintable(host));
198 }
199 }
200 }
201 foreach(const QHostAddress &qha, qlBind)
202 qWarning("Binding to address %s", qPrintable(qha.toString()));
203 }
204
205 if (qlBind.isEmpty()) {
206 bool hasipv6 = false;
207 bool hasipv4 = false;
208 int nif = 0;
209
210 QList<QNetworkInterface> interfaces = QNetworkInterface::allInterfaces();
211 if (interfaces.isEmpty()) {
212 qWarning("Meta: Unable to acquire list of network interfaces.");
213 } else {
214 foreach(const QNetworkInterface &qni, interfaces) {
215 if (!(qni.flags() & QNetworkInterface::IsUp))
216 continue;
217 if (!(qni.flags() & QNetworkInterface::IsRunning))
218 continue;
219 if (qni.flags() & QNetworkInterface::IsLoopBack)
220 continue;
221
222 foreach(const QNetworkAddressEntry &qna, qni.addressEntries()) {
223 const QHostAddress &qha = qna.ip();
224 switch (qha.protocol()) {
225 case QAbstractSocket::IPv4Protocol:
226 hasipv4 = true;
227 break;
228 case QAbstractSocket::IPv6Protocol:
229 hasipv6 = true;
230 break;
231 default:
232 break;
233 }
234 }
235
236 ++nif;
237 }
238 }
239
240 if (nif == 0) {
241 qWarning("Meta: Could not determine IPv4/IPv6 support via network interfaces, assuming support for both.");
242 hasipv6 = true;
243 hasipv4 = true;
244 }
245
246 #if QT_VERSION >= 0x050000
247 if (hasipv6) {
248 if (SslServer::hasDualStackSupport() && hasipv4) {
249 qlBind << QHostAddress(QHostAddress::Any);
250 hasipv4 = false; // No need to add a separate ipv4 socket
251 } else {
252 qlBind << QHostAddress(QHostAddress::AnyIPv6);
253 }
254 }
255
256 if (hasipv4) {
257 qlBind << QHostAddress(QHostAddress::AnyIPv4);
258 }
259 #else // QT_VERSION < 0x050000
260 // For Qt 4 AnyIPv6 resulted in a dual stack socket on dual stack
261 // capable systems while Any resulted in an IPv4 only socket. For
262 // Qt 5 this has been reworked so that AnyIPv6/v4 are now exclusive
263 // IPv6/4 sockets while Any is the dual stack socket.
264
265 if (hasipv6) {
266 qlBind << QHostAddress(QHostAddress::AnyIPv6);
267 if (SslServer::hasDualStackSupport() && hasipv4) {
268 hasipv4 = false; // No need to add a separate ipv4 socket
269 }
270 }
271
272 if (hasipv4) {
273 qlBind << QHostAddress(QHostAddress::Any);
274 }
275 #endif
276 }
277
278 qsPassword = typeCheckedFromSettings("serverpassword", qsPassword);
279 usPort = static_cast<unsigned short>(typeCheckedFromSettings("port", static_cast<uint>(usPort)));
280 iTimeout = typeCheckedFromSettings("timeout", iTimeout);
281 iMaxTextMessageLength = typeCheckedFromSettings("textmessagelength", iMaxTextMessageLength);
282 iMaxImageMessageLength = typeCheckedFromSettings("imagemessagelength", iMaxImageMessageLength);
283 legacyPasswordHash = typeCheckedFromSettings("legacypasswordhash", legacyPasswordHash);
284 kdfIterations = typeCheckedFromSettings("kdfiterations", -1);
285 bAllowHTML = typeCheckedFromSettings("allowhtml", bAllowHTML);
286 iMaxBandwidth = typeCheckedFromSettings("bandwidth", iMaxBandwidth);
287 iDefaultChan = typeCheckedFromSettings("defaultchannel", iDefaultChan);
288 bRememberChan = typeCheckedFromSettings("rememberchannel", bRememberChan);
289 iMaxUsers = typeCheckedFromSettings("users", iMaxUsers);
290 iMaxUsersPerChannel = typeCheckedFromSettings("usersperchannel", iMaxUsersPerChannel);
291 qsWelcomeText = typeCheckedFromSettings("welcometext", qsWelcomeText);
292 bCertRequired = typeCheckedFromSettings("certrequired", bCertRequired);
293 bForceExternalAuth = typeCheckedFromSettings("forceExternalAuth", bForceExternalAuth);
294
295 qsDatabase = typeCheckedFromSettings("database", qsDatabase);
296 iSQLiteWAL = typeCheckedFromSettings("sqlite_wal", iSQLiteWAL);
297
298 qsDBDriver = typeCheckedFromSettings("dbDriver", qsDBDriver);
299 qsDBUserName = typeCheckedFromSettings("dbUsername", qsDBUserName);
300 qsDBPassword = typeCheckedFromSettings("dbPassword", qsDBPassword);
301 qsDBHostName = typeCheckedFromSettings("dbHost", qsDBHostName);
302 qsDBPrefix = typeCheckedFromSettings("dbPrefix", qsDBPrefix);
303 qsDBOpts = typeCheckedFromSettings("dbOpts", qsDBOpts);
304 iDBPort = typeCheckedFromSettings("dbPort", iDBPort);
305
306 qsIceEndpoint = typeCheckedFromSettings("ice", qsIceEndpoint);
307 qsIceSecretRead = typeCheckedFromSettings("icesecret", qsIceSecretRead);
308 qsIceSecretRead = typeCheckedFromSettings("icesecretread", qsIceSecretRead);
309 qsIceSecretWrite = typeCheckedFromSettings("icesecretwrite", qsIceSecretRead);
310
311 qsGRPCAddress = typeCheckedFromSettings("grpc", qsGRPCAddress);
312 qsGRPCCert = typeCheckedFromSettings("grpccert", qsGRPCCert);
313 qsGRPCKey = typeCheckedFromSettings("grpckey", qsGRPCKey);
314
315 iLogDays = typeCheckedFromSettings("logdays", iLogDays);
316
317 qsDBus = typeCheckedFromSettings("dbus", qsDBus);
318 qsDBusService = typeCheckedFromSettings("dbusservice", qsDBusService);
319 qsLogfile = typeCheckedFromSettings("logfile", qsLogfile);
320 qsPid = typeCheckedFromSettings("pidfile", qsPid);
321
322 qsRegName = typeCheckedFromSettings("registerName", qsRegName);
323 qsRegPassword = typeCheckedFromSettings("registerPassword", qsRegPassword);
324 qsRegHost = typeCheckedFromSettings("registerHostname", qsRegHost);
325 qsRegLocation = typeCheckedFromSettings("registerLocation", qsRegLocation);
326 qurlRegWeb = QUrl(typeCheckedFromSettings("registerUrl", qurlRegWeb).toString());
327 bBonjour = typeCheckedFromSettings("bonjour", bBonjour);
328
329 iBanTries = typeCheckedFromSettings("autobanAttempts", iBanTries);
330 iBanTimeframe = typeCheckedFromSettings("autobanTimeframe", iBanTimeframe);
331 iBanTime = typeCheckedFromSettings("autobanTime", iBanTime);
332
333 qvSuggestVersion = MumbleVersion::getRaw(qsSettings->value("suggestVersion").toString());
334 if (qvSuggestVersion.toUInt() == 0)
335 qvSuggestVersion = QVariant();
336
337 qvSuggestPositional = qsSettings->value("suggestPositional");
338 if (qvSuggestPositional.toString().trimmed().isEmpty())
339 qvSuggestPositional = QVariant();
340
341 qvSuggestPushToTalk = qsSettings->value("suggestPushToTalk");
342 if (qvSuggestPushToTalk.toString().trimmed().isEmpty())
343 qvSuggestPushToTalk = QVariant();
344
345 iOpusThreshold = typeCheckedFromSettings("opusthreshold", iOpusThreshold);
346
347 iChannelNestingLimit = typeCheckedFromSettings("channelnestinglimit", iChannelNestingLimit);
348 iChannelCountLimit = typeCheckedFromSettings("channelcountlimit", iChannelCountLimit);
349
350 #ifdef Q_OS_UNIX
351 qsName = qsSettings->value("uname").toString();
352 if (geteuid() == 0) {
353 // TODO: remove this silent fallback to enforce running as non-root
354 bool requested = true;
355 if (qsName.isEmpty()) {
356 // default server user name
357 qsName = "mumble-server";
358 requested = false;
359 }
360 struct passwd *pw = getpwnam(qPrintable(qsName));
361 if (pw) {
362 uiUid = pw->pw_uid;
363 uiGid = pw->pw_gid;
364 qsHome = pw->pw_dir;
365 } else if (requested) {
366 qFatal("Cannot find username %s", qPrintable(qsName));
367 }
368 endpwent();
369 }
370 #endif
371
372 qrUserName = QRegExp(typeCheckedFromSettings("username", qrUserName.pattern()));
373 qrChannelName = QRegExp(typeCheckedFromSettings("channelname", qrChannelName.pattern()));
374
375 iMessageLimit = typeCheckedFromSettings("messagelimit", 1);
376 iMessageBurst = typeCheckedFromSettings("messageburst", 5);
377
378 bool bObfuscate = typeCheckedFromSettings("obfuscate", false);
379 if (bObfuscate) {
380 qWarning("IP address obfuscation enabled.");
381 iObfuscate = qrand();
382 }
383 bSendVersion = typeCheckedFromSettings("sendversion", bSendVersion);
384 bAllowPing = typeCheckedFromSettings("allowping", bAllowPing);
385
386 if (!loadSSLSettings()) {
387 qFatal("MetaParams: Failed to load SSL settings. See previous errors.");
388 }
389
390 QStringList hosts;
391 foreach(const QHostAddress &qha, qlBind) {
392 hosts << qha.toString();
393 }
394 qmConfig.insert(QLatin1String("host"),hosts.join(" "));
395 qmConfig.insert(QLatin1String("password"),qsPassword);
396 qmConfig.insert(QLatin1String("port"),QString::number(usPort));
397 qmConfig.insert(QLatin1String("timeout"),QString::number(iTimeout));
398 qmConfig.insert(QLatin1String("textmessagelength"), QString::number(iMaxTextMessageLength));
399 qmConfig.insert(QLatin1String("legacypasswordhash"), legacyPasswordHash ? QLatin1String("true") : QLatin1String("false"));
400 qmConfig.insert(QLatin1String("kdfiterations"), QString::number(kdfIterations));
401 qmConfig.insert(QLatin1String("allowhtml"), bAllowHTML ? QLatin1String("true") : QLatin1String("false"));
402 qmConfig.insert(QLatin1String("bandwidth"),QString::number(iMaxBandwidth));
403 qmConfig.insert(QLatin1String("users"),QString::number(iMaxUsers));
404 qmConfig.insert(QLatin1String("defaultchannel"),QString::number(iDefaultChan));
405 qmConfig.insert(QLatin1String("rememberchannel"),bRememberChan ? QLatin1String("true") : QLatin1String("false"));
406 qmConfig.insert(QLatin1String("welcometext"),qsWelcomeText);
407 qmConfig.insert(QLatin1String("registername"),qsRegName);
408 qmConfig.insert(QLatin1String("registerpassword"),qsRegPassword);
409 qmConfig.insert(QLatin1String("registerhostname"),qsRegHost);
410 qmConfig.insert(QLatin1String("registerlocation"), qsRegLocation);
411 qmConfig.insert(QLatin1String("registerurl"),qurlRegWeb.toString());
412 qmConfig.insert(QLatin1String("bonjour"), bBonjour ? QLatin1String("true") : QLatin1String("false"));
413 qmConfig.insert(QLatin1String("certificate"),qscCert.toPem());
414 qmConfig.insert(QLatin1String("key"),qskKey.toPem());
415 qmConfig.insert(QLatin1String("obfuscate"),bObfuscate ? QLatin1String("true") : QLatin1String("false"));
416 qmConfig.insert(QLatin1String("username"),qrUserName.pattern());
417 qmConfig.insert(QLatin1String("channelname"),qrChannelName.pattern());
418 qmConfig.insert(QLatin1String("certrequired"), bCertRequired ? QLatin1String("true") : QLatin1String("false"));
419 qmConfig.insert(QLatin1String("forceExternalAuth"), bForceExternalAuth ? QLatin1String("true") : QLatin1String("false"));
420 qmConfig.insert(QLatin1String("suggestversion"), qvSuggestVersion.isNull() ? QString() : qvSuggestVersion.toString());
421 qmConfig.insert(QLatin1String("suggestpositional"), qvSuggestPositional.isNull() ? QString() : qvSuggestPositional.toString());
422 qmConfig.insert(QLatin1String("suggestpushtotalk"), qvSuggestPushToTalk.isNull() ? QString() : qvSuggestPushToTalk.toString());
423 qmConfig.insert(QLatin1String("opusthreshold"), QString::number(iOpusThreshold));
424 qmConfig.insert(QLatin1String("channelnestinglimit"), QString::number(iChannelNestingLimit));
425 qmConfig.insert(QLatin1String("channelcountlimit"), QString::number(iChannelCountLimit));
426 qmConfig.insert(QLatin1String("sslCiphers"), qsCiphers);
427 qmConfig.insert(QLatin1String("sslDHParams"), QString::fromLatin1(qbaDHParams.constData()));
428 }
429
loadSSLSettings()430 bool MetaParams::loadSSLSettings() {
431 QSettings updatedSettings(qsAbsSettingsFilePath, QSettings::IniFormat);
432 #if QT_VERSION >= 0x040500
433 updatedSettings.setIniCodec("UTF-8");
434 #endif
435
436 QString tmpCiphersStr = typeCheckedFromSettings("sslCiphers", qsCiphers);
437
438 QString qsSSLCert = qsSettings->value("sslCert").toString();
439 QString qsSSLKey = qsSettings->value("sslKey").toString();
440 QString qsSSLCA = qsSettings->value("sslCA").toString();
441 QString qsSSLDHParams = typeCheckedFromSettings(QLatin1String("sslDHParams"), QString(QLatin1String("@ffdhe2048")));
442
443 qbaPassPhrase = qsSettings->value("sslPassPhrase").toByteArray();
444
445 QSslCertificate tmpCert;
446 QList<QSslCertificate> tmpCA;
447 QList<QSslCertificate> tmpIntermediates;
448 QSslKey tmpKey;
449 QByteArray tmpDHParams;
450 QList<QSslCipher> tmpCiphers;
451
452
453 if (! qsSSLCA.isEmpty()) {
454 QFile pem(qsSSLCA);
455 if (pem.open(QIODevice::ReadOnly)) {
456 QByteArray qba = pem.readAll();
457 pem.close();
458 QList<QSslCertificate> ql = QSslCertificate::fromData(qba);
459 if (ql.isEmpty()) {
460 qCritical("MetaParams: Failed to parse any CA certificates from %s", qPrintable(qsSSLCA));
461 return false;
462 } else {
463 tmpCA = ql;
464 }
465 } else {
466 qCritical("MetaParams: Failed to read %s", qPrintable(qsSSLCA));
467 return false;
468 }
469 }
470
471 QByteArray crt, key;
472
473 if (! qsSSLCert.isEmpty()) {
474 QFile pem(qsSSLCert);
475 if (pem.open(QIODevice::ReadOnly)) {
476 crt = pem.readAll();
477 pem.close();
478 } else {
479 qCritical("MetaParams: Failed to read %s", qPrintable(qsSSLCert));
480 return false;
481 }
482 }
483 if (! qsSSLKey.isEmpty()) {
484 QFile pem(qsSSLKey);
485 if (pem.open(QIODevice::ReadOnly)) {
486 key = pem.readAll();
487 pem.close();
488 } else {
489 qCritical("MetaParams: Failed to read %s", qPrintable(qsSSLKey));
490 return false;
491 }
492 }
493
494 if (! key.isEmpty() || ! crt.isEmpty()) {
495 if (! key.isEmpty()) {
496 tmpKey = Server::privateKeyFromPEM(key, qbaPassPhrase);
497 }
498 if (tmpKey.isNull() && ! crt.isEmpty()) {
499 tmpKey = Server::privateKeyFromPEM(crt, qbaPassPhrase);
500 if (! tmpKey.isNull())
501 qCritical("MetaParams: Using private key found in certificate file.");
502 }
503 if (tmpKey.isNull()) {
504 qCritical("MetaParams: No private key found in certificate or key file.");
505 return false;
506 }
507
508 QList<QSslCertificate> ql = QSslCertificate::fromData(crt);
509 ql << QSslCertificate::fromData(key);
510 for (int i=0;i<ql.size(); ++i) {
511 const QSslCertificate &c = ql.at(i);
512 if (Server::isKeyForCert(tmpKey, c)) {
513 tmpCert = c;
514 ql.removeAt(i);
515 break;
516 }
517 }
518 if (tmpCert.isNull()) {
519 qCritical("MetaParams: Failed to find certificate matching private key.");
520 return false;
521 }
522 if (ql.size() > 0) {
523 tmpIntermediates = ql;
524 qCritical("MetaParams: Adding %d intermediate certificates from certificate file.", ql.size());
525 }
526 }
527
528 QByteArray dhparams;
529
530 #if defined(USE_QSSLDIFFIEHELLMANPARAMETERS)
531 if (! qsSSLDHParams.isEmpty()) {
532 if (qsSSLDHParams.startsWith(QLatin1String("@"))) {
533 QString group = qsSSLDHParams.mid(1).trimmed();
534 QByteArray pem = FFDHE::PEMForNamedGroup(group);
535 if (pem.isEmpty()) {
536 QStringList names = FFDHE::NamedGroups();
537 QStringList atNames;
538 foreach (QString name, names) {
539 atNames << QLatin1String("@") + name;
540 }
541 QString supported = atNames.join(QLatin1String(", "));
542 qFatal("MetaParms: Diffie-Hellman parameters with name '%s' is not available. (Supported: %s)", qPrintable(qsSSLDHParams), qPrintable(supported));
543 }
544 dhparams = pem;
545 } else {
546 QFile pem(qsSSLDHParams);
547 if (pem.open(QIODevice::ReadOnly)) {
548 dhparams = pem.readAll();
549 pem.close();
550 } else {
551 qFatal("MetaParams: Failed to read %s", qPrintable(qsSSLDHParams));
552 }
553 }
554 }
555
556 if (! dhparams.isEmpty()) {
557 QSslDiffieHellmanParameters qdhp = QSslDiffieHellmanParameters::fromEncoded(dhparams);
558 if (qdhp.isValid()) {
559 tmpDHParams = dhparams;
560 } else {
561 qFatal("MetaParams: Unable to use specified Diffie-Hellman parameters: %s", qPrintable(qdhp.errorString()));
562 return false;
563 }
564 }
565 #else
566 QString qsSSLDHParamsIniValue = qsSettings->value(QLatin1String("sslDHParams")).toString();
567 if (! qsSSLDHParamsIniValue.isEmpty()) {
568 qFatal("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.");
569 return false;
570 }
571 #endif
572
573 {
574 QList<QSslCipher> ciphers = MumbleSSL::ciphersFromOpenSSLCipherString(tmpCiphersStr);
575 if (ciphers.isEmpty()) {
576 qCritical("MetaParams: Invalid sslCiphers option. Either the cipher string is invalid or none of the ciphers are available: \"%s\"", qPrintable(qsCiphers));
577 return false;
578 }
579
580 #if !defined(USE_QSSLDIFFIEHELLMANPARAMETERS)
581 // If the version of Qt we're building against doesn't support
582 // QSslDiffieHellmanParameters, then we must filter out Diffie-
583 // Hellman cipher suites in order to guarantee that we do not
584 // use Qt's default Diffie-Hellman parameters.
585 {
586 QList<QSslCipher> filtered;
587 foreach (QSslCipher c, ciphers) {
588 if (c.keyExchangeMethod() == QLatin1String("DH")) {
589 continue;
590 }
591 filtered << c;
592 }
593 if (ciphers.size() != filtered.size()) {
594 qWarning("MetaParams: Warning: all cipher suites in sslCiphers using Diffie-Hellman key exchange "
595 "have been removed. Qt %s does not support custom Diffie-Hellman parameters.",
596 qVersion());
597 }
598
599 tmpCiphers = filtered;
600 }
601 #else
602 tmpCiphers = ciphers;
603 #endif
604
605 QStringList pref;
606 foreach (QSslCipher c, tmpCiphers) {
607 pref << c.name();
608 }
609 qWarning("MetaParams: TLS cipher preference is \"%s\"", qPrintable(pref.join(QLatin1String(":"))));
610 }
611
612 qscCert = tmpCert;
613 qlCA = tmpCA;
614 qlIntermediates = tmpIntermediates;
615 qskKey = tmpKey;
616 qbaDHParams = tmpDHParams;
617 qsCiphers = tmpCiphersStr;
618 qlCiphers = tmpCiphers;
619
620 qmConfig.insert(QLatin1String("certificate"), qscCert.toPem());
621 qmConfig.insert(QLatin1String("key"), qskKey.toPem());
622 qmConfig.insert(QLatin1String("sslCiphers"), qsCiphers);
623 qmConfig.insert(QLatin1String("sslDHParams"), QString::fromLatin1(qbaDHParams.constData()));
624
625 return true;
626 }
627
Meta()628 Meta::Meta() {
629 #ifdef Q_OS_WIN
630 QOS_VERSION qvVer;
631 qvVer.MajorVersion = 1;
632 qvVer.MinorVersion = 0;
633
634 hQoS = NULL;
635
636 HMODULE hLib = LoadLibrary(L"qWave.dll");
637 if (hLib == NULL) {
638 qWarning("Meta: Failed to load qWave.dll, no QoS available");
639 } else {
640 FreeLibrary(hLib);
641 if (! QOSCreateHandle(&qvVer, &hQoS))
642 qWarning("Meta: Failed to create QOS2 handle");
643 else
644 Connection::setQoS(hQoS);
645 }
646 #endif
647 }
648
~Meta()649 Meta::~Meta() {
650 #ifdef Q_OS_WIN
651 if (hQoS) {
652 QOSCloseHandle(hQoS);
653 Connection::setQoS(NULL);
654 }
655 #endif
656 }
657
reloadSSLSettings()658 bool Meta::reloadSSLSettings() {
659 // Reload SSL settings.
660 if (!Meta::mp.loadSSLSettings()) {
661 return false;
662 }
663
664 // Re-initialize certificates for all
665 // virtual servers using the Meta server's
666 // certificate and private key.
667 foreach (Server *s, qhServers) {
668 if (s->bUsingMetaCert) {
669 s->log("Reloading certificates...");
670 s->initializeCert();
671 } else {
672 s->log("Not reloading certificates; server does not use Meta certificate");
673 }
674 }
675
676 return true;
677 }
678
getOSInfo()679 void Meta::getOSInfo() {
680 qsOS = OSInfo::getOS();
681 qsOSVersion = OSInfo::getOSDisplayableVersion();
682 }
683
bootAll()684 void Meta::bootAll() {
685 QList<int> ql = ServerDB::getBootServers();
686 foreach(int snum, ql)
687 boot(snum);
688 }
689
boot(int srvnum)690 bool Meta::boot(int srvnum) {
691 if (qhServers.contains(srvnum))
692 return false;
693 if (! ServerDB::serverExists(srvnum))
694 return false;
695 Server *s = new Server(srvnum, this);
696 if (! s->bValid) {
697 delete s;
698 return false;
699 }
700 qhServers.insert(srvnum, s);
701 emit started(s);
702
703 #ifdef Q_OS_UNIX
704 unsigned int sockets = 19; // Base
705 foreach(s, qhServers) {
706 sockets += 11; // Listen sockets, signal pipes etc.
707 sockets += s->iMaxUsers; // One per user
708 }
709
710 struct rlimit r;
711 if (getrlimit(RLIMIT_NOFILE, &r) == 0) {
712 if (r.rlim_cur < sockets) {
713 r.rlim_cur = sockets;
714 if (r.rlim_max < sockets)
715 r.rlim_max = sockets;
716 if (setrlimit(RLIMIT_NOFILE, &r) != 0) {
717 getrlimit(RLIMIT_NOFILE, &r);
718 if (r.rlim_cur < r.rlim_max) {
719 r.rlim_cur = r.rlim_max;
720 setrlimit(RLIMIT_NOFILE, &r);
721 getrlimit(RLIMIT_NOFILE, &r);
722 }
723 }
724 }
725 if (r.rlim_cur < sockets)
726 qCritical("Current booted servers require minimum %d file descriptors when all slots are full, but only %lu file descriptors are allowed for this process. Your server will crash and burn; read the FAQ for details.", sockets, static_cast<unsigned long>(r.rlim_cur));
727 }
728 #endif
729
730 return true;
731 }
732
kill(int srvnum)733 void Meta::kill(int srvnum) {
734 Server *s = qhServers.take(srvnum);
735 if (!s)
736 return;
737 emit stopped(s);
738 delete s;
739 }
740
killAll()741 void Meta::killAll() {
742 foreach(Server *s, qhServers) {
743 emit stopped(s);
744 delete s;
745 }
746 qhServers.clear();
747 }
748
banCheck(const QHostAddress & addr)749 bool Meta::banCheck(const QHostAddress &addr) {
750 if ((mp.iBanTries == 0) || (mp.iBanTimeframe == 0))
751 return false;
752
753 if (qhBans.contains(addr)) {
754 Timer t = qhBans.value(addr);
755 if (t.elapsed() < (1000000ULL * mp.iBanTime))
756 return true;
757 qhBans.remove(addr);
758 }
759
760 QList<Timer> &ql = qhAttempts[addr];
761
762 ql.append(Timer());
763 while (! ql.isEmpty() && (ql.at(0).elapsed() > (1000000ULL * mp.iBanTimeframe)))
764 ql.removeFirst();
765
766 if (ql.count() > mp.iBanTries) {
767 qhBans.insert(addr, Timer());
768 return true;
769 }
770 return false;
771 }
772