summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRichard J. Moore <rich@kde.org>2010-06-06 21:10:08 (GMT)
committerJason McDonald <jason.mcdonald@nokia.com>2010-07-16 07:05:10 (GMT)
commit5842c00976e70d190f5604ee06e839bbb9a9853a (patch)
tree1352ca6749e10047e0e28c10f7468a70578d5e71
parentc7c91f5a2aca4e46d1d2ec605e93861f94af5d82 (diff)
downloadQt-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.cpp42
-rw-r--r--src/network/ssl/qsslsocket_openssl_p.h1
-rw-r--r--tests/auto/qsslsocket/tst_qsslsocket.cpp24
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);