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