1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2017 The Qt Company Ltd. |
4 | ** Copyright (C) 2014 BlackBerry Limited. All rights reserved. |
5 | ** Copyright (C) 2014 Governikus GmbH & Co. KG. |
6 | ** Copyright (C) 2016 Richard J. Moore <rich@kde.org> |
7 | ** Contact: https://www.qt.io/licensing/ |
8 | ** |
9 | ** This file is part of the QtNetwork module of the Qt Toolkit. |
10 | ** |
11 | ** $QT_BEGIN_LICENSE:LGPL$ |
12 | ** Commercial License Usage |
13 | ** Licensees holding valid commercial Qt licenses may use this file in |
14 | ** accordance with the commercial license agreement provided with the |
15 | ** Software or, alternatively, in accordance with the terms contained in |
16 | ** a written agreement between you and The Qt Company. For licensing terms |
17 | ** and conditions see https://www.qt.io/terms-conditions. For further |
18 | ** information use the contact form at https://www.qt.io/contact-us. |
19 | ** |
20 | ** GNU Lesser General Public License Usage |
21 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
22 | ** General Public License version 3 as published by the Free Software |
23 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
24 | ** packaging of this file. Please review the following information to |
25 | ** ensure the GNU Lesser General Public License version 3 requirements |
26 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
27 | ** |
28 | ** GNU General Public License Usage |
29 | ** Alternatively, this file may be used under the terms of the GNU |
30 | ** General Public License version 2.0 or (at your option) the GNU General |
31 | ** Public license version 3 or any later version approved by the KDE Free |
32 | ** Qt Foundation. The licenses are as published by the Free Software |
33 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
34 | ** included in the packaging of this file. Please review the following |
35 | ** information to ensure the GNU General Public License requirements will |
36 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
37 | ** https://www.gnu.org/licenses/gpl-3.0.html. |
38 | ** |
39 | ** $QT_END_LICENSE$ |
40 | ** |
41 | ****************************************************************************/ |
42 | |
43 | #include <QtNetwork/qsslsocket.h> |
44 | #include <QtNetwork/qssldiffiehellmanparameters.h> |
45 | |
46 | #include "private/qssl_p.h" |
47 | #include "private/qsslsocket_p.h" |
48 | #include "private/qsslcontext_openssl_p.h" |
49 | #include "private/qsslsocket_openssl_p.h" |
50 | #include "private/qsslsocket_openssl_symbols_p.h" |
51 | #include "private/qssldiffiehellmanparameters_p.h" |
52 | |
53 | #include <vector> |
54 | |
55 | QT_BEGIN_NAMESPACE |
56 | |
57 | Q_GLOBAL_STATIC(bool, forceSecurityLevel) |
58 | |
59 | Q_NETWORK_EXPORT void qt_ForceTlsSecurityLevel() |
60 | { |
61 | *forceSecurityLevel() = true; |
62 | } |
63 | |
64 | // defined in qsslsocket_openssl.cpp: |
65 | extern int q_X509Callback(int ok, X509_STORE_CTX *ctx); |
66 | extern QString getErrorsFromOpenSsl(); |
67 | |
68 | #if QT_CONFIG(dtls) |
69 | // defined in qdtls_openssl.cpp: |
70 | namespace dtlscallbacks |
71 | { |
72 | extern "C" int q_X509DtlsCallback(int ok, X509_STORE_CTX *ctx); |
73 | extern "C" int q_generate_cookie_callback(SSL *ssl, unsigned char *dst, |
74 | unsigned *cookieLength); |
75 | extern "C" int q_verify_cookie_callback(SSL *ssl, const unsigned char *cookie, |
76 | unsigned cookieLength); |
77 | } |
78 | #endif // dtls |
79 | |
80 | #ifdef TLS1_3_VERSION |
81 | extern "C" int q_ssl_sess_set_new_cb(SSL *context, SSL_SESSION *session); |
82 | #endif // TLS1_3_VERSION |
83 | |
84 | // Defined in qsslsocket.cpp |
85 | QList<QSslCipher> q_getDefaultDtlsCiphers(); |
86 | |
87 | static inline QString msgErrorSettingBackendConfig(const QString &why) |
88 | { |
89 | return QSslSocket::tr(s: "Error when setting the OpenSSL configuration (%1)" ).arg(a: why); |
90 | } |
91 | |
92 | static inline QString msgErrorSettingEllipticCurves(const QString &why) |
93 | { |
94 | return QSslSocket::tr(s: "Error when setting the elliptic curves (%1)" ).arg(a: why); |
95 | } |
96 | |
97 | QSslContext::QSslContext() |
98 | : ctx(nullptr), |
99 | pkey(nullptr), |
100 | session(nullptr), |
101 | m_sessionTicketLifeTimeHint(-1) |
102 | { |
103 | } |
104 | |
105 | QSslContext::~QSslContext() |
106 | { |
107 | if (ctx) |
108 | // This will decrement the reference count by 1 and free the context eventually when possible |
109 | q_SSL_CTX_free(a: ctx); |
110 | |
111 | if (pkey) |
112 | q_EVP_PKEY_free(a: pkey); |
113 | |
114 | if (session) |
115 | q_SSL_SESSION_free(ses: session); |
116 | } |
117 | |
118 | QSslContext* QSslContext::fromConfiguration(QSslSocket::SslMode mode, const QSslConfiguration &configuration, bool allowRootCertOnDemandLoading) |
119 | { |
120 | QSslContext *sslContext = new QSslContext(); |
121 | initSslContext(sslContext, mode, configuration, allowRootCertOnDemandLoading); |
122 | return sslContext; |
123 | } |
124 | |
125 | QSharedPointer<QSslContext> QSslContext::sharedFromConfiguration(QSslSocket::SslMode mode, const QSslConfiguration &configuration, bool allowRootCertOnDemandLoading) |
126 | { |
127 | QSharedPointer<QSslContext> sslContext = QSharedPointer<QSslContext>::create(); |
128 | initSslContext(sslContext: sslContext.data(), mode, configuration, allowRootCertOnDemandLoading); |
129 | return sslContext; |
130 | } |
131 | |
132 | #ifndef OPENSSL_NO_NEXTPROTONEG |
133 | |
134 | static int next_proto_cb(SSL *, unsigned char **out, unsigned char *outlen, |
135 | const unsigned char *in, unsigned int inlen, void *arg) |
136 | { |
137 | QSslContext::NPNContext *ctx = reinterpret_cast<QSslContext::NPNContext *>(arg); |
138 | |
139 | // comment out to debug: |
140 | // QList<QByteArray> supportedVersions; |
141 | // for (unsigned int i = 0; i < inlen; ) { |
142 | // QByteArray version(reinterpret_cast<const char *>(&in[i+1]), in[i]); |
143 | // supportedVersions << version; |
144 | // i += in[i] + 1; |
145 | // } |
146 | |
147 | int proto = q_SSL_select_next_proto(out, outlen, in, inlen, client: ctx->data, client_len: ctx->len); |
148 | switch (proto) { |
149 | case OPENSSL_NPN_UNSUPPORTED: |
150 | ctx->status = QSslConfiguration::NextProtocolNegotiationNone; |
151 | break; |
152 | case OPENSSL_NPN_NEGOTIATED: |
153 | ctx->status = QSslConfiguration::NextProtocolNegotiationNegotiated; |
154 | break; |
155 | case OPENSSL_NPN_NO_OVERLAP: |
156 | ctx->status = QSslConfiguration::NextProtocolNegotiationUnsupported; |
157 | break; |
158 | default: |
159 | qCWarning(lcSsl, "OpenSSL sent unknown NPN status" ); |
160 | } |
161 | |
162 | return SSL_TLSEXT_ERR_OK; |
163 | } |
164 | |
165 | QSslContext::NPNContext QSslContext::npnContext() const |
166 | { |
167 | return m_npnContext; |
168 | } |
169 | #endif // !OPENSSL_NO_NEXTPROTONEG |
170 | |
171 | |
172 | |
173 | // Needs to be deleted by caller |
174 | SSL* QSslContext::createSsl() |
175 | { |
176 | SSL* ssl = q_SSL_new(a: ctx); |
177 | q_SSL_clear(a: ssl); |
178 | |
179 | if (!session && !sessionASN1().isEmpty() |
180 | && !sslConfiguration.testSslOption(option: QSsl::SslOptionDisableSessionPersistence)) { |
181 | const unsigned char *data = reinterpret_cast<const unsigned char *>(m_sessionASN1.constData()); |
182 | session = q_d2i_SSL_SESSION(a: nullptr, pp: &data, length: m_sessionASN1.size()); |
183 | // 'session' has refcount 1 already, set by the function above |
184 | } |
185 | |
186 | if (session) { |
187 | // Try to resume the last session we cached |
188 | if (!q_SSL_set_session(to: ssl, session)) { |
189 | qCWarning(lcSsl, "could not set SSL session" ); |
190 | q_SSL_SESSION_free(ses: session); |
191 | session = nullptr; |
192 | } |
193 | } |
194 | |
195 | #ifndef OPENSSL_NO_NEXTPROTONEG |
196 | QList<QByteArray> protocols = sslConfiguration.d->nextAllowedProtocols; |
197 | if (!protocols.isEmpty()) { |
198 | m_supportedNPNVersions.clear(); |
199 | for (int a = 0; a < protocols.count(); ++a) { |
200 | if (protocols.at(i: a).size() > 255) { |
201 | qCWarning(lcSsl) << "TLS NPN extension" << protocols.at(i: a) |
202 | << "is too long and will be ignored." ; |
203 | continue; |
204 | } else if (protocols.at(i: a).isEmpty()) { |
205 | continue; |
206 | } |
207 | m_supportedNPNVersions.append(c: protocols.at(i: a).size()).append(a: protocols.at(i: a)); |
208 | } |
209 | if (m_supportedNPNVersions.size()) { |
210 | m_npnContext.data = reinterpret_cast<unsigned char *>(m_supportedNPNVersions.data()); |
211 | m_npnContext.len = m_supportedNPNVersions.count(); |
212 | m_npnContext.status = QSslConfiguration::NextProtocolNegotiationNone; |
213 | // Callback's type has a parameter 'const unsigned char ** out' |
214 | // since it was introduced in 1.0.2. Internally, OpenSSL's own code |
215 | // (tests/examples) cast it to unsigned char * (since it's 'out'). |
216 | // We just re-use our NPN callback and cast here: |
217 | typedef int (*alpn_callback_t) (SSL *, const unsigned char **, unsigned char *, |
218 | const unsigned char *, unsigned int, void *); |
219 | // With ALPN callback is for a server side only, for a client m_npnContext.status |
220 | // will stay in NextProtocolNegotiationNone. |
221 | q_SSL_CTX_set_alpn_select_cb(ctx, cb: alpn_callback_t(next_proto_cb), arg: &m_npnContext); |
222 | // Client: |
223 | q_SSL_set_alpn_protos(ssl, protos: m_npnContext.data, protos_len: m_npnContext.len); |
224 | // And in case our peer does not support ALPN, but supports NPN: |
225 | q_SSL_CTX_set_next_proto_select_cb(s: ctx, cb: next_proto_cb, arg: &m_npnContext); |
226 | } |
227 | } |
228 | #endif // !OPENSSL_NO_NEXTPROTONEG |
229 | |
230 | return ssl; |
231 | } |
232 | |
233 | // We cache exactly one session here |
234 | bool QSslContext::cacheSession(SSL* ssl) |
235 | { |
236 | // don't cache the same session again |
237 | if (session && session == q_SSL_get_session(ssl)) |
238 | return true; |
239 | |
240 | // decrease refcount of currently stored session |
241 | // (this might happen if there are several concurrent handshakes in flight) |
242 | if (session) |
243 | q_SSL_SESSION_free(ses: session); |
244 | |
245 | // cache the session the caller gave us and increase reference count |
246 | session = q_SSL_get1_session(ssl); |
247 | |
248 | if (session && !sslConfiguration.testSslOption(option: QSsl::SslOptionDisableSessionPersistence)) { |
249 | int sessionSize = q_i2d_SSL_SESSION(in: session, pp: nullptr); |
250 | if (sessionSize > 0) { |
251 | m_sessionASN1.resize(size: sessionSize); |
252 | unsigned char *data = reinterpret_cast<unsigned char *>(m_sessionASN1.data()); |
253 | if (!q_i2d_SSL_SESSION(in: session, pp: &data)) |
254 | qCWarning(lcSsl, "could not store persistent version of SSL session" ); |
255 | m_sessionTicketLifeTimeHint = q_SSL_SESSION_get_ticket_lifetime_hint(session); |
256 | } |
257 | } |
258 | |
259 | return (session != nullptr); |
260 | } |
261 | |
262 | QByteArray QSslContext::sessionASN1() const |
263 | { |
264 | return m_sessionASN1; |
265 | } |
266 | |
267 | void QSslContext::setSessionASN1(const QByteArray &session) |
268 | { |
269 | m_sessionASN1 = session; |
270 | } |
271 | |
272 | int QSslContext::sessionTicketLifeTimeHint() const |
273 | { |
274 | return m_sessionTicketLifeTimeHint; |
275 | } |
276 | |
277 | QSslError::SslError QSslContext::error() const |
278 | { |
279 | return errorCode; |
280 | } |
281 | |
282 | QString QSslContext::errorString() const |
283 | { |
284 | return errorStr; |
285 | } |
286 | |
287 | void QSslContext::initSslContext(QSslContext *sslContext, QSslSocket::SslMode mode, |
288 | const QSslConfiguration &configuration, |
289 | bool allowRootCertOnDemandLoading) |
290 | { |
291 | sslContext->sslConfiguration = configuration; |
292 | sslContext->errorCode = QSslError::NoError; |
293 | |
294 | bool client = (mode == QSslSocket::SslClientMode); |
295 | |
296 | bool reinitialized = false; |
297 | bool unsupportedProtocol = false; |
298 | bool isDtls = false; |
299 | init_context: |
300 | if (sslContext->sslConfiguration.protocol() == QSsl::SslV2) { |
301 | // SSL 2 is no longer supported, but chosen deliberately -> error |
302 | sslContext->ctx = nullptr; |
303 | unsupportedProtocol = true; |
304 | } else if (sslContext->sslConfiguration.protocol() == QSsl::SslV3) { |
305 | // SSL 3 is no longer supported, but chosen deliberately -> error |
306 | sslContext->ctx = nullptr; |
307 | unsupportedProtocol = true; |
308 | } else { |
309 | switch (sslContext->sslConfiguration.protocol()) { |
310 | case QSsl::DtlsV1_0: |
311 | case QSsl::DtlsV1_0OrLater: |
312 | case QSsl::DtlsV1_2: |
313 | case QSsl::DtlsV1_2OrLater: |
314 | #if QT_CONFIG(dtls) |
315 | isDtls = true; |
316 | sslContext->ctx = q_SSL_CTX_new(a: client ? q_DTLS_client_method() : q_DTLS_server_method()); |
317 | #else // dtls |
318 | sslContext->ctx = nullptr; |
319 | unsupportedProtocol = true; |
320 | qCWarning(lcSsl, "DTLS protocol requested, but feature 'dtls' is disabled" ); |
321 | |
322 | #endif // dtls |
323 | break; |
324 | case QSsl::TlsV1_3: |
325 | case QSsl::TlsV1_3OrLater: |
326 | #if !defined(TLS1_3_VERSION) |
327 | qCWarning(lcSsl, "TLS 1.3 is not supported" ); |
328 | sslContext->ctx = nullptr; |
329 | unsupportedProtocol = true; |
330 | break; |
331 | #endif // TLS1_3_VERSION |
332 | default: |
333 | // The ssl options will actually control the supported methods |
334 | sslContext->ctx = q_SSL_CTX_new(a: client ? q_TLS_client_method() : q_TLS_server_method()); |
335 | } |
336 | } |
337 | |
338 | if (!sslContext->ctx) { |
339 | // After stopping Flash 10 the SSL library loses its ciphers. Try re-adding them |
340 | // by re-initializing the library. |
341 | if (!reinitialized) { |
342 | reinitialized = true; |
343 | if (q_OPENSSL_init_ssl(opts: 0, settings: nullptr) == 1) |
344 | goto init_context; |
345 | } |
346 | |
347 | sslContext->errorStr = QSslSocket::tr(s: "Error creating SSL context (%1)" ).arg( |
348 | a: unsupportedProtocol ? QSslSocket::tr(s: "unsupported protocol" ) : QSslSocketBackendPrivate::getErrorsFromOpenSsl() |
349 | ); |
350 | sslContext->errorCode = QSslError::UnspecifiedError; |
351 | return; |
352 | } |
353 | |
354 | // A nasty hacked OpenSSL using a level that will make our auto-tests fail: |
355 | if (q_SSL_CTX_get_security_level(ctx: sslContext->ctx) > 1 && *forceSecurityLevel()) |
356 | q_SSL_CTX_set_security_level(ctx: sslContext->ctx, level: 1); |
357 | |
358 | const long anyVersion = |
359 | #if QT_CONFIG(dtls) |
360 | isDtls ? DTLS_ANY_VERSION : TLS_ANY_VERSION; |
361 | #else |
362 | TLS_ANY_VERSION; |
363 | #endif // dtls |
364 | long minVersion = anyVersion; |
365 | long maxVersion = anyVersion; |
366 | |
367 | switch (sslContext->sslConfiguration.protocol()) { |
368 | case QSsl::TlsV1_0: |
369 | minVersion = TLS1_VERSION; |
370 | maxVersion = TLS1_VERSION; |
371 | break; |
372 | case QSsl::TlsV1_1: |
373 | minVersion = TLS1_1_VERSION; |
374 | maxVersion = TLS1_1_VERSION; |
375 | break; |
376 | case QSsl::TlsV1_2: |
377 | minVersion = TLS1_2_VERSION; |
378 | maxVersion = TLS1_2_VERSION; |
379 | break; |
380 | case QSsl::TlsV1_3: |
381 | #ifdef TLS1_3_VERSION |
382 | minVersion = TLS1_3_VERSION; |
383 | maxVersion = TLS1_3_VERSION; |
384 | #else |
385 | // This protocol is not supported by OpenSSL 1.1 and we handle |
386 | // it as an error (see the code above). |
387 | Q_UNREACHABLE(); |
388 | #endif // TLS1_3_VERSION |
389 | break; |
390 | // Ranges: |
391 | case QSsl::TlsV1SslV3: |
392 | case QSsl::AnyProtocol: |
393 | case QSsl::SecureProtocols: |
394 | case QSsl::TlsV1_0OrLater: |
395 | minVersion = TLS1_VERSION; |
396 | maxVersion = 0; |
397 | break; |
398 | case QSsl::TlsV1_1OrLater: |
399 | minVersion = TLS1_1_VERSION; |
400 | maxVersion = 0; |
401 | break; |
402 | case QSsl::TlsV1_2OrLater: |
403 | minVersion = TLS1_2_VERSION; |
404 | maxVersion = 0; |
405 | break; |
406 | case QSsl::DtlsV1_0: |
407 | minVersion = DTLS1_VERSION; |
408 | maxVersion = DTLS1_VERSION; |
409 | break; |
410 | case QSsl::DtlsV1_0OrLater: |
411 | minVersion = DTLS1_VERSION; |
412 | maxVersion = DTLS_MAX_VERSION; |
413 | break; |
414 | case QSsl::DtlsV1_2: |
415 | minVersion = DTLS1_2_VERSION; |
416 | maxVersion = DTLS1_2_VERSION; |
417 | break; |
418 | case QSsl::DtlsV1_2OrLater: |
419 | minVersion = DTLS1_2_VERSION; |
420 | maxVersion = DTLS_MAX_VERSION; |
421 | break; |
422 | case QSsl::TlsV1_3OrLater: |
423 | #ifdef TLS1_3_VERSION |
424 | minVersion = TLS1_3_VERSION; |
425 | maxVersion = 0; |
426 | break; |
427 | #else |
428 | // This protocol is not supported by OpenSSL 1.1 and we handle |
429 | // it as an error (see the code above). |
430 | Q_UNREACHABLE(); |
431 | break; |
432 | #endif // TLS1_3_VERSION |
433 | case QSsl::SslV2: |
434 | case QSsl::SslV3: |
435 | // These protocols are not supported, and we handle |
436 | // them as an error (see the code above). |
437 | Q_UNREACHABLE(); |
438 | break; |
439 | case QSsl::UnknownProtocol: |
440 | break; |
441 | } |
442 | |
443 | if (minVersion != anyVersion |
444 | && !q_SSL_CTX_set_min_proto_version(sslContext->ctx, minVersion)) { |
445 | sslContext->errorStr = QSslSocket::tr(s: "Error while setting the minimal protocol version" ); |
446 | sslContext->errorCode = QSslError::UnspecifiedError; |
447 | return; |
448 | } |
449 | |
450 | if (maxVersion != anyVersion |
451 | && !q_SSL_CTX_set_max_proto_version(sslContext->ctx, maxVersion)) { |
452 | sslContext->errorStr = QSslSocket::tr(s: "Error while setting the maximum protocol version" ); |
453 | sslContext->errorCode = QSslError::UnspecifiedError; |
454 | return; |
455 | } |
456 | |
457 | // Enable bug workarounds. |
458 | long options = QSslSocketBackendPrivate::setupOpenSslOptions(protocol: configuration.protocol(), sslOptions: configuration.d->sslOptions); |
459 | q_SSL_CTX_set_options(ctx: sslContext->ctx, op: options); |
460 | |
461 | // Tell OpenSSL to release memory early |
462 | // http://www.openssl.org/docs/ssl/SSL_CTX_set_mode.html |
463 | q_SSL_CTX_set_mode(sslContext->ctx, SSL_MODE_RELEASE_BUFFERS); |
464 | |
465 | auto filterCiphers = [](const QList<QSslCipher> &ciphers, bool selectTls13) |
466 | { |
467 | QByteArray cipherString; |
468 | bool first = true; |
469 | |
470 | for (const QSslCipher &cipher : qAsConst(t: ciphers)) { |
471 | const bool isTls13Cipher = cipher.protocol() == QSsl::TlsV1_3 || cipher.protocol() == QSsl::TlsV1_3OrLater; |
472 | if (selectTls13 != isTls13Cipher) |
473 | continue; |
474 | |
475 | if (first) |
476 | first = false; |
477 | else |
478 | cipherString.append(c: ':'); |
479 | cipherString.append(a: cipher.name().toLatin1()); |
480 | } |
481 | return cipherString; |
482 | }; |
483 | |
484 | // Initialize ciphers |
485 | QList<QSslCipher> ciphers = sslContext->sslConfiguration.ciphers(); |
486 | if (ciphers.isEmpty()) |
487 | ciphers = isDtls ? q_getDefaultDtlsCiphers() : QSslSocketPrivate::defaultCiphers(); |
488 | |
489 | const QByteArray preTls13Ciphers = filterCiphers(ciphers, false); |
490 | |
491 | if (preTls13Ciphers.size()) { |
492 | if (!q_SSL_CTX_set_cipher_list(a: sslContext->ctx, b: preTls13Ciphers.data())) { |
493 | sslContext->errorStr = QSslSocket::tr(s: "Invalid or empty cipher list (%1)" ).arg(a: QSslSocketBackendPrivate::getErrorsFromOpenSsl()); |
494 | sslContext->errorCode = QSslError::UnspecifiedError; |
495 | return; |
496 | } |
497 | } |
498 | |
499 | const QByteArray tls13Ciphers = filterCiphers(ciphers, true); |
500 | #ifdef TLS1_3_VERSION |
501 | if (tls13Ciphers.size()) { |
502 | if (!q_SSL_CTX_set_ciphersuites(ctx: sslContext->ctx, str: tls13Ciphers.data())) { |
503 | sslContext->errorStr = QSslSocket::tr(s: "Invalid or empty cipher list (%1)" ).arg(a: QSslSocketBackendPrivate::getErrorsFromOpenSsl()); |
504 | sslContext->errorCode = QSslError::UnspecifiedError; |
505 | return; |
506 | } |
507 | } |
508 | #endif // TLS1_3_VERSION |
509 | if (!preTls13Ciphers.size() && !tls13Ciphers.size()) { |
510 | sslContext->errorStr = QSslSocket::tr(s: "Invalid or empty cipher list (%1)" ).arg(QStringLiteral("" )); |
511 | sslContext->errorCode = QSslError::UnspecifiedError; |
512 | return; |
513 | } |
514 | |
515 | const QDateTime now = QDateTime::currentDateTimeUtc(); |
516 | |
517 | // Add all our CAs to this store. |
518 | const auto caCertificates = sslContext->sslConfiguration.caCertificates(); |
519 | for (const QSslCertificate &caCertificate : caCertificates) { |
520 | // From https://www.openssl.org/docs/ssl/SSL_CTX_load_verify_locations.html: |
521 | // |
522 | // If several CA certificates matching the name, key identifier, and |
523 | // serial number condition are available, only the first one will be |
524 | // examined. This may lead to unexpected results if the same CA |
525 | // certificate is available with different expiration dates. If a |
526 | // ``certificate expired'' verification error occurs, no other |
527 | // certificate will be searched. Make sure to not have expired |
528 | // certificates mixed with valid ones. |
529 | // |
530 | // See also: QSslSocketBackendPrivate::verify() |
531 | if (caCertificate.expiryDate() >= now) { |
532 | q_X509_STORE_add_cert(ctx: q_SSL_CTX_get_cert_store(a: sslContext->ctx), x: (X509 *)caCertificate.handle()); |
533 | } |
534 | } |
535 | |
536 | if (QSslSocketPrivate::s_loadRootCertsOnDemand && allowRootCertOnDemandLoading) { |
537 | // tell OpenSSL the directories where to look up the root certs on demand |
538 | const QList<QByteArray> unixDirs = QSslSocketPrivate::unixRootCertDirectories(); |
539 | int success = 1; |
540 | #if OPENSSL_VERSION_MAJOR < 3 |
541 | for (const QByteArray &unixDir : unixDirs) { |
542 | if ((success = q_SSL_CTX_load_verify_locations(sslContext->ctx, nullptr, unixDir.constData())) != 1) |
543 | break; |
544 | } |
545 | #else |
546 | for (const QByteArray &unixDir : unixDirs) { |
547 | if ((success = q_SSL_CTX_load_verify_dir(ctx: sslContext->ctx, CApath: unixDir.constData())) != 1) |
548 | break; |
549 | } |
550 | #endif // OPENSSL_VERSION_MAJOR |
551 | if (success != 1) { |
552 | const auto qtErrors = QSslSocketBackendPrivate::getErrorsFromOpenSsl(); |
553 | qCWarning(lcSsl) << "An error encountered while to set root certificates location:" |
554 | << qtErrors; |
555 | } |
556 | } |
557 | |
558 | if (!sslContext->sslConfiguration.localCertificate().isNull()) { |
559 | // Require a private key as well. |
560 | if (sslContext->sslConfiguration.privateKey().isNull()) { |
561 | sslContext->errorStr = QSslSocket::tr(s: "Cannot provide a certificate with no key, %1" ).arg(a: QSslSocketBackendPrivate::getErrorsFromOpenSsl()); |
562 | sslContext->errorCode = QSslError::UnspecifiedError; |
563 | return; |
564 | } |
565 | |
566 | // Load certificate |
567 | if (!q_SSL_CTX_use_certificate(a: sslContext->ctx, b: (X509 *)sslContext->sslConfiguration.localCertificate().handle())) { |
568 | sslContext->errorStr = QSslSocket::tr(s: "Error loading local certificate, %1" ).arg(a: QSslSocketBackendPrivate::getErrorsFromOpenSsl()); |
569 | sslContext->errorCode = QSslError::UnspecifiedError; |
570 | return; |
571 | } |
572 | |
573 | if (configuration.d->privateKey.algorithm() == QSsl::Opaque) { |
574 | sslContext->pkey = reinterpret_cast<EVP_PKEY *>(configuration.d->privateKey.handle()); |
575 | } else { |
576 | // Load private key |
577 | sslContext->pkey = q_EVP_PKEY_new(); |
578 | // before we were using EVP_PKEY_assign_R* functions and did not use EVP_PKEY_free. |
579 | // this lead to a memory leak. Now we use the *_set1_* functions which do not |
580 | // take ownership of the RSA/DSA key instance because the QSslKey already has ownership. |
581 | if (configuration.d->privateKey.algorithm() == QSsl::Rsa) |
582 | q_EVP_PKEY_set1_RSA(a: sslContext->pkey, b: reinterpret_cast<RSA *>(configuration.d->privateKey.handle())); |
583 | else if (configuration.d->privateKey.algorithm() == QSsl::Dsa) |
584 | q_EVP_PKEY_set1_DSA(a: sslContext->pkey, b: reinterpret_cast<DSA *>(configuration.d->privateKey.handle())); |
585 | #ifndef OPENSSL_NO_EC |
586 | else if (configuration.d->privateKey.algorithm() == QSsl::Ec) |
587 | q_EVP_PKEY_set1_EC_KEY(a: sslContext->pkey, b: reinterpret_cast<EC_KEY *>(configuration.d->privateKey.handle())); |
588 | #endif |
589 | } |
590 | |
591 | if (!q_SSL_CTX_use_PrivateKey(a: sslContext->ctx, b: sslContext->pkey)) { |
592 | sslContext->errorStr = QSslSocket::tr(s: "Error loading private key, %1" ).arg(a: QSslSocketBackendPrivate::getErrorsFromOpenSsl()); |
593 | sslContext->errorCode = QSslError::UnspecifiedError; |
594 | return; |
595 | } |
596 | if (configuration.d->privateKey.algorithm() == QSsl::Opaque) |
597 | sslContext->pkey = nullptr; // Don't free the private key, it belongs to QSslKey |
598 | |
599 | // Check if the certificate matches the private key. |
600 | if (!q_SSL_CTX_check_private_key(a: sslContext->ctx)) { |
601 | sslContext->errorStr = QSslSocket::tr(s: "Private key does not certify public key, %1" ).arg(a: QSslSocketBackendPrivate::getErrorsFromOpenSsl()); |
602 | sslContext->errorCode = QSslError::UnspecifiedError; |
603 | return; |
604 | } |
605 | |
606 | // If we have any intermediate certificates then we need to add them to our chain |
607 | bool first = true; |
608 | for (const QSslCertificate &cert : qAsConst(t: configuration.d->localCertificateChain)) { |
609 | if (first) { |
610 | first = false; |
611 | continue; |
612 | } |
613 | q_SSL_CTX_ctrl(a: sslContext->ctx, SSL_CTRL_EXTRA_CHAIN_CERT, c: 0, |
614 | d: q_X509_dup(a: reinterpret_cast<X509 *>(cert.handle()))); |
615 | } |
616 | } |
617 | |
618 | // Initialize peer verification, different callbacks, TLS/DTLS verification first |
619 | // (note, all these set_some_callback do not have return value): |
620 | if (sslContext->sslConfiguration.peerVerifyMode() == QSslSocket::VerifyNone) { |
621 | q_SSL_CTX_set_verify(a: sslContext->ctx, SSL_VERIFY_NONE, c: nullptr); |
622 | } else { |
623 | q_SSL_CTX_set_verify(a: sslContext->ctx, SSL_VERIFY_PEER, |
624 | #if QT_CONFIG(dtls) |
625 | c: isDtls ? dtlscallbacks::q_X509DtlsCallback : |
626 | #endif // dtls |
627 | q_X509Callback); |
628 | } |
629 | |
630 | #ifdef TLS1_3_VERSION |
631 | // NewSessionTicket callback: |
632 | if (mode == QSslSocket::SslClientMode && !isDtls) { |
633 | q_SSL_CTX_sess_set_new_cb(ctx: sslContext->ctx, cb: q_ssl_sess_set_new_cb); |
634 | q_SSL_CTX_set_session_cache_mode(sslContext->ctx, SSL_SESS_CACHE_CLIENT); |
635 | } |
636 | |
637 | #endif // TLS1_3_VERSION |
638 | |
639 | #if QT_CONFIG(dtls) |
640 | // DTLS cookies: |
641 | if (mode == QSslSocket::SslServerMode && isDtls && configuration.dtlsCookieVerificationEnabled()) { |
642 | q_SSL_CTX_set_cookie_generate_cb(ctx: sslContext->ctx, cb: dtlscallbacks::q_generate_cookie_callback); |
643 | q_SSL_CTX_set_cookie_verify_cb(ctx: sslContext->ctx, cb: dtlscallbacks::q_verify_cookie_callback); |
644 | } |
645 | #endif // dtls |
646 | |
647 | // Set verification depth. |
648 | if (sslContext->sslConfiguration.peerVerifyDepth() != 0) |
649 | q_SSL_CTX_set_verify_depth(a: sslContext->ctx, b: sslContext->sslConfiguration.peerVerifyDepth()); |
650 | |
651 | // set persisted session if the user set it |
652 | if (!configuration.sessionTicket().isEmpty()) |
653 | sslContext->setSessionASN1(configuration.sessionTicket()); |
654 | |
655 | // Set temp DH params |
656 | QSslDiffieHellmanParameters dhparams = configuration.diffieHellmanParameters(); |
657 | |
658 | if (!dhparams.isValid()) { |
659 | sslContext->errorStr = QSslSocket::tr(s: "Diffie-Hellman parameters are not valid" ); |
660 | sslContext->errorCode = QSslError::UnspecifiedError; |
661 | return; |
662 | } |
663 | |
664 | if (!dhparams.isEmpty()) { |
665 | const QByteArray ¶ms = dhparams.d->derData; |
666 | const char *ptr = params.constData(); |
667 | DH *dh = q_d2i_DHparams(a: nullptr, pp: reinterpret_cast<const unsigned char **>(&ptr), |
668 | length: params.length()); |
669 | if (dh == nullptr) |
670 | qFatal(msg: "q_d2i_DHparams failed to convert QSslDiffieHellmanParameters to DER form" ); |
671 | q_SSL_CTX_set_tmp_dh(sslContext->ctx, dh); |
672 | q_DH_free(dh); |
673 | } |
674 | |
675 | #ifndef OPENSSL_NO_PSK |
676 | if (!client) |
677 | q_SSL_CTX_use_psk_identity_hint(ctx: sslContext->ctx, hint: sslContext->sslConfiguration.preSharedKeyIdentityHint().constData()); |
678 | #endif // !OPENSSL_NO_PSK |
679 | |
680 | const QVector<QSslEllipticCurve> qcurves = sslContext->sslConfiguration.ellipticCurves(); |
681 | if (!qcurves.isEmpty()) { |
682 | #ifdef OPENSSL_NO_EC |
683 | sslContext->errorStr = msgErrorSettingEllipticCurves(QSslSocket::tr("OpenSSL version with disabled elliptic curves" )); |
684 | sslContext->errorCode = QSslError::UnspecifiedError; |
685 | return; |
686 | #else |
687 | // Set the curves to be used. |
688 | std::vector<int> curves; |
689 | curves.reserve(n: qcurves.size()); |
690 | for (const auto &sslCurve : qcurves) |
691 | curves.push_back(x: sslCurve.id); |
692 | if (!q_SSL_CTX_ctrl(a: sslContext->ctx, SSL_CTRL_SET_CURVES, c: long(curves.size()), d: &curves[0])) { |
693 | sslContext->errorStr = msgErrorSettingEllipticCurves(why: QSslSocketBackendPrivate::getErrorsFromOpenSsl()); |
694 | sslContext->errorCode = QSslError::UnspecifiedError; |
695 | return; |
696 | } |
697 | #endif |
698 | } |
699 | |
700 | applyBackendConfig(sslContext); |
701 | } |
702 | |
703 | #if QT_CONFIG(ocsp) |
704 | extern "C" int qt_OCSP_status_server_callback(SSL *ssl, void *); // Defined in qsslsocket_openssl.cpp. |
705 | #endif // ocsp |
706 | // static |
707 | void QSslContext::applyBackendConfig(QSslContext *sslContext) |
708 | { |
709 | const QMap<QByteArray, QVariant> &conf = sslContext->sslConfiguration.backendConfiguration(); |
710 | if (conf.isEmpty()) |
711 | return; |
712 | |
713 | #if QT_CONFIG(ocsp) |
714 | auto ocspResponsePos = conf.find(akey: "Qt-OCSP-response" ); |
715 | if (ocspResponsePos != conf.end()) { |
716 | // This is our private, undocumented configuration option, existing only for |
717 | // the purpose of testing OCSP status responses. We don't even check this |
718 | // callback was set. If no - the test must fail. |
719 | q_SSL_CTX_set_tlsext_status_cb(sslContext->ctx, qt_OCSP_status_server_callback); |
720 | if (conf.size() == 1) |
721 | return; |
722 | } |
723 | #endif // ocsp |
724 | |
725 | QSharedPointer<SSL_CONF_CTX> cctx(q_SSL_CONF_CTX_new(), &q_SSL_CONF_CTX_free); |
726 | if (cctx) { |
727 | q_SSL_CONF_CTX_set_ssl_ctx(a: cctx.data(), b: sslContext->ctx); |
728 | q_SSL_CONF_CTX_set_flags(a: cctx.data(), SSL_CONF_FLAG_FILE); |
729 | |
730 | for (auto i = conf.constBegin(); i != conf.constEnd(); ++i) { |
731 | if (i.key() == "Qt-OCSP-response" ) // This never goes to SSL_CONF_cmd(). |
732 | continue; |
733 | |
734 | if (!i.value().canConvert(targetTypeId: QMetaType::QByteArray)) { |
735 | sslContext->errorCode = QSslError::UnspecifiedError; |
736 | sslContext->errorStr = msgErrorSettingBackendConfig( |
737 | why: QSslSocket::tr(s: "Expecting QByteArray for %1" ).arg( |
738 | a: QString::fromUtf8(str: i.key()))); |
739 | return; |
740 | } |
741 | |
742 | const QByteArray &value = i.value().toByteArray(); |
743 | const int result = q_SSL_CONF_cmd(a: cctx.data(), b: i.key().constData(), c: value.constData()); |
744 | if (result == 2) |
745 | continue; |
746 | |
747 | sslContext->errorCode = QSslError::UnspecifiedError; |
748 | switch (result) { |
749 | case 0: |
750 | sslContext->errorStr = msgErrorSettingBackendConfig( |
751 | why: QSslSocket::tr(s: "An error occurred attempting to set %1 to %2" ).arg( |
752 | args: QString::fromUtf8(str: i.key()), args: QString::fromUtf8(str: value))); |
753 | return; |
754 | case 1: |
755 | sslContext->errorStr = msgErrorSettingBackendConfig( |
756 | why: QSslSocket::tr(s: "Wrong value for %1 (%2)" ).arg( |
757 | args: QString::fromUtf8(str: i.key()), args: QString::fromUtf8(str: value))); |
758 | return; |
759 | default: |
760 | sslContext->errorStr = msgErrorSettingBackendConfig( |
761 | why: QSslSocket::tr(s: "Unrecognized command %1 = %2" ).arg( |
762 | args: QString::fromUtf8(str: i.key()), args: QString::fromUtf8(str: value))); |
763 | return; |
764 | } |
765 | } |
766 | |
767 | if (q_SSL_CONF_CTX_finish(a: cctx.data()) == 0) { |
768 | sslContext->errorStr = msgErrorSettingBackendConfig(why: QSslSocket::tr(s: "SSL_CONF_finish() failed" )); |
769 | sslContext->errorCode = QSslError::UnspecifiedError; |
770 | } |
771 | } else { |
772 | sslContext->errorStr = msgErrorSettingBackendConfig(why: QSslSocket::tr(s: "SSL_CONF_CTX_new() failed" )); |
773 | sslContext->errorCode = QSslError::UnspecifiedError; |
774 | } |
775 | } |
776 | |
777 | QT_END_NAMESPACE |
778 | |