diff options
Diffstat (limited to 'src/network/ssl')
-rw-r--r-- | src/network/ssl/qsslsocket.cpp | 4 | ||||
-rw-r--r-- | src/network/ssl/qsslsocket_openssl.cpp | 89 | ||||
-rw-r--r-- | src/network/ssl/qsslsocket_openssl_p.h | 1 |
3 files changed, 71 insertions, 23 deletions
diff --git a/src/network/ssl/qsslsocket.cpp b/src/network/ssl/qsslsocket.cpp index f85fa84..f73068e 100644 --- a/src/network/ssl/qsslsocket.cpp +++ b/src/network/ssl/qsslsocket.cpp @@ -574,7 +574,7 @@ void QSslSocket::setProtocol(QSsl::SslProtocol protocol) certificate is valid. The default mode is AutoVerifyPeer, which tells QSslSocket to use - VerifyPeer for clients, QueryPeer for clients. + VerifyPeer for clients and QueryPeer for servers. \sa setPeerVerifyMode(), peerVerifyDepth(), mode() */ @@ -594,7 +594,7 @@ QSslSocket::PeerVerifyMode QSslSocket::peerVerifyMode() const certificate is valid. The default mode is AutoVerifyPeer, which tells QSslSocket to use - VerifyPeer for clients, QueryPeer for clients. + VerifyPeer for clients and QueryPeer for servers. Setting this mode after encryption has started has no effect on the current connection. diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp index 1d794ae..c297eea 100644 --- a/src/network/ssl/qsslsocket_openssl.cpp +++ b/src/network/ssl/qsslsocket_openssl.cpp @@ -475,27 +475,12 @@ bool QSslSocketPrivate::ensureLibraryLoaded() void QSslSocketPrivate::ensureCiphersAndCertsLoaded() { + QMutexLocker locker(openssl_locks()->initLock()); if (s_loadedCiphersAndCerts) return; s_loadedCiphersAndCerts = true; resetDefaultCiphers(); - setDefaultCaCertificates(systemCaCertificates()); -} - -/*! - \internal - - Declared static in QSslSocketPrivate, makes sure the SSL libraries have - been initialized. -*/ - -void QSslSocketPrivate::ensureInitialized() -{ - if (!supportsSsl()) - return; - - ensureCiphersAndCertsLoaded(); //load symbols needed to receive certificates from system store #if defined(Q_OS_MAC) @@ -532,6 +517,22 @@ void QSslSocketPrivate::ensureInitialized() qWarning("could not load crypt32 library"); // should never happen } #endif + setDefaultCaCertificates(systemCaCertificates()); +} + +/*! + \internal + + Declared static in QSslSocketPrivate, makes sure the SSL libraries have + been initialized. +*/ + +void QSslSocketPrivate::ensureInitialized() +{ + if (!supportsSsl()) + return; + + ensureCiphersAndCertsLoaded(); } /*! @@ -970,8 +971,20 @@ void QSslSocketBackendPrivate::transmit() #endif plainSocket->disconnectFromHost(); break; + case SSL_ERROR_SYSCALL: // some IO error + case SSL_ERROR_SSL: // error in the SSL library + // we do not know exactly what the error is, nor whether we can recover from it, + // so just return to prevent an endless loop in the outer "while" statement + q->setErrorString(QSslSocket::tr("Error while reading: %1").arg(SSL_ERRORSTR())); + q->setSocketError(QAbstractSocket::UnknownSocketError); + emit q->error(QAbstractSocket::UnknownSocketError); + return; default: - // ### Handle errors better. + // SSL_ERROR_WANT_CONNECT, SSL_ERROR_WANT_ACCEPT: can only happen with a + // BIO_s_connect() or BIO_s_accept(), which we do not call. + // SSL_ERROR_WANT_X509_LOOKUP: can only happen with a + // SSL_CTX_set_client_cert_cb(), which we do not call. + // So this default case should never be triggered. q->setErrorString(QSslSocket::tr("Error while reading: %1").arg(SSL_ERRORSTR())); q->setSocketError(QAbstractSocket::UnknownSocketError); emit q->error(QAbstractSocket::UnknownSocketError); @@ -1105,17 +1118,16 @@ bool QSslSocketBackendPrivate::startHandshake() QString peerName = (verificationPeerName.isEmpty () ? q->peerName() : verificationPeerName); QString commonName = configuration.peerCertificate.subjectInfo(QSslCertificate::CommonName); - QRegExp regexp(commonName, Qt::CaseInsensitive, QRegExp::Wildcard); - if (!regexp.exactMatch(peerName)) { + if (!isMatchingHostname(commonName.toLower(), peerName.toLower())) { bool matched = false; foreach (const QString &altName, configuration.peerCertificate .alternateSubjectNames().values(QSsl::DnsEntry)) { - regexp.setPattern(altName); - if (regexp.exactMatch(peerName)) { + if (isMatchingHostname(altName.toLower(), peerName.toLower())) { matched = true; break; } } + if (!matched) { // No matches in common names or alternate names. QSslError error(QSslError::HostNameMismatch, configuration.peerCertificate); @@ -1245,4 +1257,39 @@ QList<QSslCertificate> QSslSocketBackendPrivate::STACKOFX509_to_QSslCertificates return certificates; } +bool QSslSocketBackendPrivate::isMatchingHostname(const QString &cn, const QString &hostname) +{ + int wildcard = cn.indexOf(QLatin1Char('*')); + + // Check this is a wildcard cert, if not then just compare the strings + if (wildcard < 0) + return cn == hostname; + + int firstCnDot = cn.indexOf(QLatin1Char('.')); + int secondCnDot = cn.indexOf(QLatin1Char('.'), firstCnDot+1); + + // Check at least 3 components + if ((-1 == secondCnDot) || (secondCnDot+1 >= cn.length())) + return false; + + // Check * is last character of 1st component (ie. there's a following .) + if (wildcard+1 != firstCnDot) + return false; + + // Check only one star + if (cn.lastIndexOf(QLatin1Char('*')) != wildcard) + return false; + + // Check characters preceding * (if any) match + if (wildcard && (hostname.leftRef(wildcard) != cn.leftRef(wildcard))) + return false; + + // Check characters following first . match + if (hostname.midRef(hostname.indexOf(QLatin1Char('.'))) != cn.midRef(firstCnDot)) + return false; + + // Ok, I guess this was a wildcard CN and the hostname matches. + return true; +} + QT_END_NAMESPACE diff --git a/src/network/ssl/qsslsocket_openssl_p.h b/src/network/ssl/qsslsocket_openssl_p.h index 987dfae..7a4e6b6 100644 --- a/src/network/ssl/qsslsocket_openssl_p.h +++ b/src/network/ssl/qsslsocket_openssl_p.h @@ -116,6 +116,7 @@ public: static QSslCipher QSslCipher_from_SSL_CIPHER(SSL_CIPHER *cipher); static QList<QSslCertificate> STACKOFX509_to_QSslCertificates(STACK_OF(X509) *x509); + Q_AUTOTEST_EXPORT static bool isMatchingHostname(const QString &cn, const QString &hostname); }; #if defined(Q_OS_SYMBIAN) |