diff options
Diffstat (limited to 'src/network/ssl/qsslsocket_openssl.cpp')
-rw-r--r-- | src/network/ssl/qsslsocket_openssl.cpp | 186 |
1 files changed, 51 insertions, 135 deletions
diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp index 1fc7817fe8..3858b4b21f 100644 --- a/src/network/ssl/qsslsocket_openssl.cpp +++ b/src/network/ssl/qsslsocket_openssl.cpp @@ -66,6 +66,10 @@ #include "qsslpresharedkeyauthenticator.h" #include "qsslpresharedkeyauthenticator_p.h" +#ifdef Q_OS_WIN +#include "qwindowscarootfetcher_p.h" +#endif + #include <QtCore/qdatetime.h> #include <QtCore/qdebug.h> #include <QtCore/qdir.h> @@ -99,12 +103,13 @@ int QSslSocketBackendPrivate::s_indexForSSLExtraData = -1; QString QSslSocketBackendPrivate::getErrorsFromOpenSsl() { QString errorString; + char buf[256] = {}; // OpenSSL docs claim both 120 and 256; use the larger. unsigned long errNum; while ((errNum = q_ERR_get_error())) { - if (! errorString.isEmpty()) + if (!errorString.isEmpty()) errorString.append(QLatin1String(", ")); - const char *error = q_ERR_error_string(errNum, NULL); - errorString.append(QString::fromLatin1(error)); // error is ascii according to man ERR_error_string + q_ERR_error_string_n(errNum, buf, sizeof buf); + errorString.append(QString::fromLatin1(buf)); // error is ascii according to man ERR_error_string } return errorString; } @@ -188,8 +193,7 @@ QSslCipher QSslSocketBackendPrivate::QSslCipher_from_SSL_CIPHER(const SSL_CIPHER return ciph; } -// static -inline QSslErrorEntry QSslErrorEntry::fromStoreContext(X509_STORE_CTX *ctx) +QSslErrorEntry QSslErrorEntry::fromStoreContext(X509_STORE_CTX *ctx) { return { q_X509_STORE_CTX_get_error(ctx), @@ -242,6 +246,33 @@ int q_X509Callback(int ok, X509_STORE_CTX *ctx) return 1; } +static void q_loadCiphersForConnection(SSL *connection, QList<QSslCipher> &ciphers, + QList<QSslCipher> &defaultCiphers) +{ + Q_ASSERT(connection); + + STACK_OF(SSL_CIPHER) *supportedCiphers = q_SSL_get_ciphers(connection); + for (int i = 0; i < q_sk_SSL_CIPHER_num(supportedCiphers); ++i) { + if (SSL_CIPHER *cipher = q_sk_SSL_CIPHER_value(supportedCiphers, i)) { + QSslCipher ciph = QSslSocketBackendPrivate::QSslCipher_from_SSL_CIPHER(cipher); + if (!ciph.isNull()) { + // Unconditionally exclude ADH and AECDH ciphers since they offer no MITM protection + if (!ciph.name().toLower().startsWith(QLatin1String("adh")) && + !ciph.name().toLower().startsWith(QLatin1String("exp-adh")) && + !ciph.name().toLower().startsWith(QLatin1String("aecdh"))) { + ciphers << ciph; + + if (ciph.usedBits() >= 128) + defaultCiphers << ciph; + } + } + } + } +} + +// Defined in qsslsocket.cpp +void q_setDefaultDtlsCiphers(const QList<QSslCipher> &ciphers); + long QSslSocketBackendPrivate::setupOpenSslOptions(QSsl::SslProtocol protocol, QSsl::SslOptions sslOptions) { long options; @@ -447,29 +478,27 @@ void QSslSocketPrivate::resetDefaultCiphers() QList<QSslCipher> ciphers; QList<QSslCipher> defaultCiphers; - STACK_OF(SSL_CIPHER) *supportedCiphers = q_SSL_get_ciphers(mySsl); - for (int i = 0; i < q_sk_SSL_CIPHER_num(supportedCiphers); ++i) { - if (SSL_CIPHER *cipher = q_sk_SSL_CIPHER_value(supportedCiphers, i)) { - QSslCipher ciph = QSslSocketBackendPrivate::QSslCipher_from_SSL_CIPHER(cipher); - if (!ciph.isNull()) { - // Unconditionally exclude ADH and AECDH ciphers since they offer no MITM protection - if (!ciph.name().toLower().startsWith(QLatin1String("adh")) && - !ciph.name().toLower().startsWith(QLatin1String("exp-adh")) && - !ciph.name().toLower().startsWith(QLatin1String("aecdh"))) { - ciphers << ciph; - - if (ciph.usedBits() >= 128) - defaultCiphers << ciph; - } - } - } - } + q_loadCiphersForConnection(mySsl, ciphers, defaultCiphers); q_SSL_CTX_free(myCtx); q_SSL_free(mySsl); setDefaultSupportedCiphers(ciphers); setDefaultCiphers(defaultCiphers); + + ciphers.clear(); + defaultCiphers.clear(); + + myCtx = q_SSL_CTX_new(q_DTLS_client_method()); + if (myCtx) { + mySsl = q_SSL_new(myCtx); + if (mySsl) { + q_loadCiphersForConnection(mySsl, ciphers, defaultCiphers); + q_setDefaultDtlsCiphers(defaultCiphers); + q_SSL_free(mySsl); + } + q_SSL_CTX_free(myCtx); + } } void QSslSocketPrivate::resetDefaultEllipticCurves() @@ -1172,119 +1201,6 @@ void QSslSocketBackendPrivate::_q_caRootLoaded(QSslCertificate cert, QSslCertifi } } -class QWindowsCaRootFetcherThread : public QThread -{ -public: - QWindowsCaRootFetcherThread() - { - qRegisterMetaType<QSslCertificate>(); - setObjectName(QStringLiteral("QWindowsCaRootFetcher")); - start(); - } - ~QWindowsCaRootFetcherThread() - { - quit(); - wait(15500); // worst case, a running request can block for 15 seconds - } -}; - -Q_GLOBAL_STATIC(QWindowsCaRootFetcherThread, windowsCaRootFetcherThread); - -QWindowsCaRootFetcher::QWindowsCaRootFetcher(const QSslCertificate &certificate, QSslSocket::SslMode sslMode) - : cert(certificate), mode(sslMode) -{ - moveToThread(windowsCaRootFetcherThread()); -} - -QWindowsCaRootFetcher::~QWindowsCaRootFetcher() -{ -} - -void QWindowsCaRootFetcher::start() -{ - QByteArray der = cert.toDer(); - PCCERT_CONTEXT wincert = CertCreateCertificateContext(X509_ASN_ENCODING, (const BYTE *)der.constData(), der.length()); - if (!wincert) { -#ifdef QSSLSOCKET_DEBUG - qCDebug(lcSsl, "QWindowsCaRootFetcher failed to convert certificate to windows form"); -#endif - emit finished(cert, QSslCertificate()); - deleteLater(); - return; - } - - CERT_CHAIN_PARA parameters; - memset(¶meters, 0, sizeof(parameters)); - parameters.cbSize = sizeof(parameters); - // set key usage constraint - parameters.RequestedUsage.dwType = USAGE_MATCH_TYPE_AND; - parameters.RequestedUsage.Usage.cUsageIdentifier = 1; - LPSTR oid = (LPSTR)(mode == QSslSocket::SslClientMode ? szOID_PKIX_KP_SERVER_AUTH : szOID_PKIX_KP_CLIENT_AUTH); - parameters.RequestedUsage.Usage.rgpszUsageIdentifier = &oid; - -#ifdef QSSLSOCKET_DEBUG - QElapsedTimer stopwatch; - stopwatch.start(); -#endif - PCCERT_CHAIN_CONTEXT chain; - BOOL result = CertGetCertificateChain( - 0, //default engine - wincert, - 0, //current date/time - 0, //default store - ¶meters, - 0, //default dwFlags - 0, //reserved - &chain); -#ifdef QSSLSOCKET_DEBUG - qCDebug(lcSsl) << "QWindowsCaRootFetcher" << stopwatch.elapsed() << "ms to get chain"; -#endif - - QSslCertificate trustedRoot; - if (result) { -#ifdef QSSLSOCKET_DEBUG - qCDebug(lcSsl) << "QWindowsCaRootFetcher - examining windows chains"; - if (chain->TrustStatus.dwErrorStatus == CERT_TRUST_NO_ERROR) - qCDebug(lcSsl) << " - TRUSTED"; - else - qCDebug(lcSsl) << " - NOT TRUSTED" << chain->TrustStatus.dwErrorStatus; - if (chain->TrustStatus.dwInfoStatus & CERT_TRUST_IS_SELF_SIGNED) - qCDebug(lcSsl) << " - SELF SIGNED"; - qCDebug(lcSsl) << "QSslSocketBackendPrivate::fetchCaRootForCert - dumping simple chains"; - for (unsigned int i = 0; i < chain->cChain; i++) { - if (chain->rgpChain[i]->TrustStatus.dwErrorStatus == CERT_TRUST_NO_ERROR) - qCDebug(lcSsl) << " - TRUSTED SIMPLE CHAIN" << i; - else - qCDebug(lcSsl) << " - UNTRUSTED SIMPLE CHAIN" << i << "reason:" << chain->rgpChain[i]->TrustStatus.dwErrorStatus; - for (unsigned int j = 0; j < chain->rgpChain[i]->cElement; j++) { - QSslCertificate foundCert(QByteArray((const char *)chain->rgpChain[i]->rgpElement[j]->pCertContext->pbCertEncoded - , chain->rgpChain[i]->rgpElement[j]->pCertContext->cbCertEncoded), QSsl::Der); - qCDebug(lcSsl) << " - " << foundCert; - } - } - qCDebug(lcSsl) << " - and" << chain->cLowerQualityChainContext << "low quality chains"; //expect 0, we haven't asked for them -#endif - - //based on http://msdn.microsoft.com/en-us/library/windows/desktop/aa377182%28v=vs.85%29.aspx - //about the final chain rgpChain[cChain-1] which must begin with a trusted root to be valid - if (chain->TrustStatus.dwErrorStatus == CERT_TRUST_NO_ERROR - && chain->cChain > 0) { - const PCERT_SIMPLE_CHAIN finalChain = chain->rgpChain[chain->cChain - 1]; - // http://msdn.microsoft.com/en-us/library/windows/desktop/aa377544%28v=vs.85%29.aspx - // rgpElement[0] is the end certificate chain element. rgpElement[cElement-1] is the self-signed "root" certificate element. - if (finalChain->TrustStatus.dwErrorStatus == CERT_TRUST_NO_ERROR - && finalChain->cElement > 0) { - trustedRoot = QSslCertificate(QByteArray((const char *)finalChain->rgpElement[finalChain->cElement - 1]->pCertContext->pbCertEncoded - , finalChain->rgpElement[finalChain->cElement - 1]->pCertContext->cbCertEncoded), QSsl::Der); - } - } - CertFreeCertificateChain(chain); - } - CertFreeCertificateContext(wincert); - - emit finished(cert, trustedRoot); - deleteLater(); -} #endif void QSslSocketBackendPrivate::disconnectFromHost() |