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 #ifdef MUMBLE
7 	#include "mumble_pch.hpp"
8 #else
9 	#include "murmur_pch.h"
10 #endif
11 
12 #include "SSL.h"
13 #include "SSLLocks.h"
14 
15 #include "Version.h"
16 
initialize()17 void MumbleSSL::initialize() {
18 	// Let Qt initialize its copy of OpenSSL, if it's different than
19 	// Mumble's. (Initialization is a side-effect of calling
20 	// QSslSocket::supportsSsl()).
21 	QSslSocket::supportsSsl();
22 
23 	// Initialize our copy of OpenSSL.
24 	SSL_library_init(); // Safe to discard return value, per OpenSSL man pages.
25 	SSL_load_error_strings();
26 
27 	// Determine if a locking callback has not been set.
28 	// This should be the case if there are multiple copies
29 	// of OpensSSL in the address space. This is mostly due
30 	// to Qt dynamically loading OpenSSL when it is not
31 	// configured with -openssl-linked.
32 	//
33 	// If we detect that no locking callback is configured, we
34 	// have to set it up ourselves to allow multi-threaded use
35 	// of OpenSSL.
36 	if (CRYPTO_get_locking_callback() == NULL) {
37 		SSLLocks::initialize();
38 	}
39 }
40 
destroy()41 void MumbleSSL::destroy() {
42 	SSLLocks::destroy();
43 }
44 
defaultOpenSSLCipherString()45 QString MumbleSSL::defaultOpenSSLCipherString() {
46 	return QLatin1String("EECDH+AESGCM:EDH+aRSA+AESGCM:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:AES256-SHA:AES128-SHA");
47 }
48 
ciphersFromOpenSSLCipherString(QString cipherString)49 QList<QSslCipher> MumbleSSL::ciphersFromOpenSSLCipherString(QString cipherString) {
50 	QList<QSslCipher> chosenCiphers;
51 
52 	SSL_CTX *ctx = NULL;
53 	SSL *ssl = NULL;
54 	const SSL_METHOD *meth = NULL;
55 	int i = 0;
56 
57 	QByteArray csbuf = cipherString.toLatin1();
58 	const char *ciphers = csbuf.constData();
59 
60 	meth = SSLv23_server_method();
61 	if (meth == NULL) {
62 		qWarning("MumbleSSL: unable to get SSL method");
63 		goto out;
64 	}
65 
66 	// We use const_cast to be compatible with OpenSSL 0.9.8.
67 	ctx = SSL_CTX_new(const_cast<SSL_METHOD *>(meth));
68 	if (ctx == NULL) {
69 		qWarning("MumbleSSL: unable to allocate SSL_CTX");
70 		goto out;
71 	}
72 
73 	if (!SSL_CTX_set_cipher_list(ctx, ciphers)) {
74 		qWarning("MumbleSSL: error parsing OpenSSL cipher string in ciphersFromOpenSSLCipherString");
75 		goto out;
76 	}
77 
78 	ssl = SSL_new(ctx);
79 	if (ssl == NULL) {
80 		qWarning("MumbleSSL: unable to create SSL object in ciphersFromOpenSSLCipherString");
81 		goto out;
82 	}
83 
84 	while (1) {
85 		const char *name = SSL_get_cipher_list(ssl, i);
86 		if (name == NULL) {
87 			break;
88 		}
89 #if QT_VERSION >= 0x050300
90 		QSslCipher c = QSslCipher(QString::fromLatin1(name));
91 		if (!c.isNull()) {
92 			chosenCiphers << c;
93 		}
94 #else
95 		foreach (const QSslCipher &c, QSslSocket::supportedCiphers()) {
96 			if (c.name() == QString::fromLatin1(name)) {
97 				chosenCiphers << c;
98 			}
99 		}
100 #endif
101 		++i;
102 	}
103 
104 out:
105 	SSL_CTX_free(ctx);
106 	SSL_free(ssl);
107 	return chosenCiphers;
108 }
109 
addSystemCA()110 void MumbleSSL::addSystemCA() {
111 #if QT_VERSION < 0x040700 && !defined(NO_SYSTEM_CA_OVERRIDE)
112 #if defined(Q_OS_WIN)
113 	QStringList qsl;
114 	qsl << QLatin1String("Ca");
115 	qsl << QLatin1String("Root");
116 	qsl << QLatin1String("AuthRoot");
117 	foreach(const QString &store, qsl) {
118 		HCERTSTORE hCertStore;
119 		PCCERT_CONTEXT pCertContext = NULL;
120 
121 		bool found = false;
122 
123 		hCertStore = CertOpenSystemStore(NULL, store.utf16());
124 		if (! hCertStore) {
125 			qWarning("SSL: Failed to open CA store %s", qPrintable(store));
126 			continue;
127 		}
128 
129 		while (pCertContext = CertEnumCertificatesInStore(hCertStore, pCertContext)) {
130 			QByteArray qba(reinterpret_cast<const char *>(pCertContext->pbCertEncoded), pCertContext->cbCertEncoded);
131 
132 			QList<QSslCertificate> ql = QSslCertificate::fromData(qba, QSsl::Pem);
133 			ql += QSslCertificate::fromData(qba, QSsl::Der);
134 			if (! ql.isEmpty()) {
135 				found = true;
136 				QSslSocket::addDefaultCaCertificates(ql);
137 			}
138 		}
139 		if (found)
140 			qWarning("SSL: Added CA certificates from system store '%s'", qPrintable(store));
141 
142 		CertCloseStore(hCertStore, 0);
143 	}
144 
145 #elif defined(Q_OS_MAC)
146 	CFArrayRef certs = NULL;
147 	bool found = false;
148 
149 	if (SecTrustCopyAnchorCertificates(&certs) == noErr) {
150 		int ncerts = CFArrayGetCount(certs);
151 		for (int i = 0; i < ncerts; i++) {
152 			CFDataRef data = NULL;
153 
154 			SecCertificateRef cert = reinterpret_cast<SecCertificateRef>(const_cast<void *>(CFArrayGetValueAtIndex(certs, i)));
155 			if (! cert)
156 				continue;
157 
158 			if (SecKeychainItemExport(cert, kSecFormatX509Cert, kSecItemPemArmour, NULL, &data) == noErr) {
159 				const char *ptr = reinterpret_cast<const char *>(CFDataGetBytePtr(data));
160 				int len = CFDataGetLength(data);
161 				QByteArray qba(ptr, len);
162 
163 				QList<QSslCertificate> ql = QSslCertificate::fromData(qba, QSsl::Pem);
164 				if (! ql.isEmpty()) {
165 					found = true;
166 					QSslSocket::addDefaultCaCertificates(ql);
167 				}
168 			}
169 		}
170 
171 		CFRelease(certs);
172 
173 		if (found)
174 			qWarning("SSL: Added CA certificates from 'System Roots' store.");
175 	}
176 #elif defined(Q_OS_UNIX)
177 	QStringList qsl;
178 
179 #ifdef SYSTEM_CA_DIR
180 	QSslSocket::addDefaultCaCertificates(QLatin1String(MUMTEXT(SYSTEM_CA_DIR)));
181 #else
182 #ifdef SYSTEM_CA_BUNDLE
183 	qsl << QLatin1String(MUMTEXT(SYSTEM_CA_BUNDLE));
184 #else
185 #ifdef __FreeBSD__
186 	qsl << QLatin1String("/usr/local/share/certs/ca-root-nss.crt");
187 #else
188 	qsl << QLatin1String("/etc/pki/tls/certs/ca-bundle.crt");
189 	qsl << QLatin1String("/etc/ssl/certs/ca-certificates.crt");
190 #endif
191 #endif
192 
193 	foreach(const QString &filename, qsl) {
194 		QFile f(filename);
195 		if (f.exists() && f.open(QIODevice::ReadOnly)) {
196 			QList<QSslCertificate> ql = QSslCertificate::fromDevice(&f, QSsl::Pem);
197 			ql += QSslCertificate::fromDevice(&f, QSsl::Der);
198 			if (! ql.isEmpty()) {
199 				qWarning("SSL: Added CA certificates from '%s'", qPrintable(filename));
200 				QSslSocket::addDefaultCaCertificates(ql);
201 			}
202 		}
203 	}
204 #endif // SYSTEM_CA_DIR
205 #endif // Q_OS_UNIX
206 
207 	QSet<QByteArray> digests;
208 	QList<QSslCertificate> ql;
209 	foreach(const QSslCertificate &crt, QSslSocket::defaultCaCertificates()) {
210 		QByteArray digest = crt.digest(QCryptographicHash::Sha1);
211 		if (! digests.contains(digest) && crt.isValid()) {
212 			ql << crt;
213 			digests.insert(digest);
214 		}
215 	}
216 	QSslSocket::setDefaultCaCertificates(ql);
217 #endif // NO_SYSTEM_CA_OVERRIDE
218 
219 	// Don't perform on-demand loading of root certificates
220 #if QT_VERSION >= 0x050500
221 	QSslSocket::addDefaultCaCertificates(QSslConfiguration::systemCaCertificates());
222 #elif QT_VERSION >= 0x040800
223 	QSslSocket::addDefaultCaCertificates(QSslSocket::systemCaCertificates());
224 #endif
225 
226 #ifdef Q_OS_WIN
227 	// Work around issue #1271.
228 	// Skype's click-to-call feature creates an enormous
229 	// amount of certificates in the Root CA store.
230 	{
231 		QSslConfiguration sslCfg = QSslConfiguration::defaultConfiguration();
232 		QList<QSslCertificate> caList = sslCfg.caCertificates();
233 
234 		QList<QSslCertificate> filteredCaList;
235 		foreach (QSslCertificate cert, caList) {
236 #if QT_VERSION >= 0x050000
237 			QStringList orgs = cert.subjectInfo(QSslCertificate::Organization);
238 			bool skip = false;
239 			foreach (QString ou, orgs) {
240 				if (ou.contains(QLatin1String("Skype"), Qt::CaseInsensitive)) {
241 					skip = true;
242 					break;
243 				}
244 			}
245 			if (skip) {
246 				continue;
247 			}
248 #else
249 			QString ou = cert.subjectInfo(QSslCertificate::Organization);
250 			if (ou.contains(QLatin1String("Skype"), Qt::CaseInsensitive)) {
251 				continue;
252 			}
253 #endif
254 			filteredCaList.append(cert);
255 		}
256 
257 		sslCfg.setCaCertificates(filteredCaList);
258 		QSslConfiguration::setDefaultConfiguration(sslCfg);
259 
260 		qWarning("SSL: CA certificate filter applied. Filtered size: %i, original size: %i", filteredCaList.size(), caList.size());
261 	}
262 #endif
263 }
264 
protocolToString(QSsl::SslProtocol protocol)265 QString MumbleSSL::protocolToString(QSsl::SslProtocol protocol) {
266 	switch(protocol) {
267 		case QSsl::SslV3: return QLatin1String("SSL 3");
268 		case QSsl::SslV2: return QLatin1String("SSL 2");
269 #if QT_VERSION >= 0x050000
270 		case QSsl::TlsV1_0: return QLatin1String("TLS 1.0");
271 		case QSsl::TlsV1_1: return QLatin1String("TLS 1.1");
272 		case QSsl::TlsV1_2: return QLatin1String("TLS 1.2");
273 #if QT_VERSION >= 0x050C00
274 		case QSsl::TlsV1_3: return QLatin1String("TLS 1.3");
275 #endif
276 #else
277 		case QSsl::TlsV1: return  QLatin1String("TLS 1.0");
278 #endif
279 		case QSsl::AnyProtocol: return QLatin1String("AnyProtocol");
280 #if QT_VERSION >= 0x040800
281 		case QSsl::TlsV1SslV3: return QLatin1String("TlsV1SslV3");
282 		case QSsl::SecureProtocols: return QLatin1String("SecureProtocols");
283 #endif
284 		default:
285 		case QSsl::UnknownProtocol: return QLatin1String("UnknownProtocol");
286 	}
287 }
288