diff options
author | Richard J. Moore <rich@kde.org> | 2010-06-06 21:10:08 (GMT) |
---|---|---|
committer | Jason McDonald <jason.mcdonald@nokia.com> | 2010-07-16 07:05:10 (GMT) |
commit | 5842c00976e70d190f5604ee06e839bbb9a9853a (patch) | |
tree | 1352ca6749e10047e0e28c10f7468a70578d5e71 | |
parent | c7c91f5a2aca4e46d1d2ec605e93861f94af5d82 (diff) | |
download | Qt-5842c00976e70d190f5604ee06e839bbb9a9853a.zip Qt-5842c00976e70d190f5604ee06e839bbb9a9853a.tar.gz Qt-5842c00976e70d190f5604ee06e839bbb9a9853a.tar.bz2 |
Fix handling of SSL certificates with wildcard domain names
Merge-request: 731
Task-number: QTBUG-4455
Reviewed-by: Peter Hartmann
(cherry picked from commit 5f6018564668d368f75e431c4cdac88d7421cff0)
-rw-r--r-- | src/network/ssl/qsslsocket_openssl.cpp | 42 | ||||
-rw-r--r-- | src/network/ssl/qsslsocket_openssl_p.h | 1 | ||||
-rw-r--r-- | tests/auto/qsslsocket/tst_qsslsocket.cpp | 24 |
3 files changed, 63 insertions, 4 deletions
diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp index d7088ee..c6e340f 100644 --- a/src/network/ssl/qsslsocket_openssl.cpp +++ b/src/network/ssl/qsslsocket_openssl.cpp @@ -1100,17 +1100,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.lower(), peerName.lower())) { bool matched = false; foreach (const QString &altName, configuration.peerCertificate .alternateSubjectNames().values(QSsl::DnsEntry)) { - regexp.setPattern(altName); - if (regexp.exactMatch(peerName)) { + if (isMatchingHostname(altName.lower(), peerName.lower())) { matched = true; break; } } + if (!matched) { // No matches in common names or alternate names. QSslError error(QSslError::HostNameMismatch, configuration.peerCertificate); @@ -1240,4 +1239,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 e41320d..fd02838 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) diff --git a/tests/auto/qsslsocket/tst_qsslsocket.cpp b/tests/auto/qsslsocket/tst_qsslsocket.cpp index 0cf638b..0c12974 100644 --- a/tests/auto/qsslsocket/tst_qsslsocket.cpp +++ b/tests/auto/qsslsocket/tst_qsslsocket.cpp @@ -55,6 +55,7 @@ #include <QAuthenticator> #include "private/qhostinfo_p.h" +#include "private/qsslsocket_openssl_p.h" #include "../network-settings.h" @@ -163,6 +164,7 @@ private slots: void setDefaultCiphers(); void supportedCiphers(); void systemCaCertificates(); + void wildcardCertificateNames(); void wildcard(); void setEmptyKey(); void spontaneousWrite(); @@ -1063,6 +1065,28 @@ void tst_QSslSocket::systemCaCertificates() QCOMPARE(certs, QSslSocket::defaultCaCertificates()); } +void tst_QSslSocket::wildcardCertificateNames() +{ + // Passing CN matches + QCOMPARE( QSslSocketBackendPrivate::isMatchingHostname(QString("www.example.com"), QString("www.example.com")), true ); + QCOMPARE( QSslSocketBackendPrivate::isMatchingHostname(QString("*.example.com"), QString("www.example.com")), true ); + QCOMPARE( QSslSocketBackendPrivate::isMatchingHostname(QString("xxx*.example.com"), QString("xxxwww.example.com")), true ); + QCOMPARE( QSslSocketBackendPrivate::isMatchingHostname(QString("f*.example.com"), QString("foo.example.com")), true ); + + // Failing CN matches + QCOMPARE( QSslSocketBackendPrivate::isMatchingHostname(QString("xxx.example.com"), QString("www.example.com")), false ); + QCOMPARE( QSslSocketBackendPrivate::isMatchingHostname(QString("*"), QString("www.example.com")), false ); + QCOMPARE( QSslSocketBackendPrivate::isMatchingHostname(QString("*.*.com"), QString("www.example.com")), false ); + QCOMPARE( QSslSocketBackendPrivate::isMatchingHostname(QString("*.example.com"), QString("baa.foo.example.com")), false ); + QCOMPARE( QSslSocketBackendPrivate::isMatchingHostname(QString("f*.example.com"), QString("baa.example.com")), false ); + QCOMPARE( QSslSocketBackendPrivate::isMatchingHostname(QString("*.com"), QString("example.com")), false ); + QCOMPARE( QSslSocketBackendPrivate::isMatchingHostname(QString("*fail.com"), QString("example.com")), false ); + QCOMPARE( QSslSocketBackendPrivate::isMatchingHostname(QString("*.example."), QString("www.example.")), false ); + QCOMPARE( QSslSocketBackendPrivate::isMatchingHostname(QString("*.example."), QString("www.example")), false ); + QCOMPARE( QSslSocketBackendPrivate::isMatchingHostname(QString(""), QString("www")), false ); + QCOMPARE( QSslSocketBackendPrivate::isMatchingHostname(QString("*"), QString("www")), false ); +} + void tst_QSslSocket::wildcard() { QSKIP("TODO: solve wildcard problem", SkipAll); |