ssl: Share the host name matching utilities

This moves the socket backend's host name matching functions up to
QSslSocketPrivate so that they can be shared between backends. This
works, as there is no OpenSSL-specific code here.

Change-Id: I73c2081fdc2e60a44c90e90800d1e1877391a626
Reviewed-by: Richard J. Moore <rich@kde.org>
This commit is contained in:
Andrew Knight 2014-08-27 11:52:05 +03:00
parent be4db73231
commit ecbf6dfbf5
5 changed files with 78 additions and 78 deletions

View File

@ -2513,6 +2513,65 @@ QSharedPointer<QSslContext> QSslSocketPrivate::sslContext(QSslSocket *socket)
return (socket) ? socket->d_func()->sslContextPointer : QSharedPointer<QSslContext>();
}
bool QSslSocketPrivate::isMatchingHostname(const QSslCertificate &cert, const QString &peerName)
{
QStringList commonNameList = cert.subjectInfo(QSslCertificate::CommonName);
foreach (const QString &commonName, commonNameList) {
if (isMatchingHostname(commonName.toLower(), peerName.toLower())) {
return true;
}
}
foreach (const QString &altName, cert.subjectAlternativeNames().values(QSsl::DnsEntry)) {
if (isMatchingHostname(altName.toLower(), peerName.toLower())) {
return true;
}
}
return false;
}
bool QSslSocketPrivate::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;
// Check if the hostname is an IP address, if so then wildcards are not allowed
QHostAddress addr(hostname);
if (!addr.isNull())
return false;
// Ok, I guess this was a wildcard CN and the hostname matches.
return true;
}
QT_END_NAMESPACE
#include "moc_qsslsocket.cpp"

View File

@ -1552,65 +1552,6 @@ QList<QSslCertificate> QSslSocketBackendPrivate::STACKOFX509_to_QSslCertificates
return certificates;
}
bool QSslSocketBackendPrivate::isMatchingHostname(const QSslCertificate &cert, const QString &peerName)
{
QStringList commonNameList = cert.subjectInfo(QSslCertificate::CommonName);
foreach (const QString &commonName, commonNameList) {
if (isMatchingHostname(commonName.toLower(), peerName.toLower())) {
return true;
}
}
foreach (const QString &altName, cert.subjectAlternativeNames().values(QSsl::DnsEntry)) {
if (isMatchingHostname(altName.toLower(), peerName.toLower())) {
return true;
}
}
return false;
}
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;
// Check if the hostname is an IP address, if so then wildcards are not allowed
QHostAddress addr(hostname);
if (!addr.isNull())
return false;
// Ok, I guess this was a wildcard CN and the hostname matches.
return true;
}
QList<QSslError> QSslSocketBackendPrivate::verify(QList<QSslCertificate> certificateChain, const QString &hostName)
{
QList<QSslError> errors;

View File

@ -142,8 +142,6 @@ public:
Q_AUTOTEST_EXPORT static long setupOpenSslOptions(QSsl::SslProtocol protocol, QSsl::SslOptions sslOptions);
static QSslCipher QSslCipher_from_SSL_CIPHER(SSL_CIPHER *cipher);
static QList<QSslCertificate> STACKOFX509_to_QSslCertificates(STACK_OF(X509) *x509);
static bool isMatchingHostname(const QSslCertificate &cert, const QString &peerName);
Q_AUTOTEST_EXPORT static bool isMatchingHostname(const QString &cn, const QString &hostname);
static QList<QSslError> verify(QList<QSslCertificate> certificateChain, const QString &hostName);
static QString getErrorsFromOpenSsl();
static bool importPKCS12(QIODevice *device,

View File

@ -150,6 +150,8 @@ public:
QRegExp::PatternSyntax syntax);
static void addDefaultCaCertificate(const QSslCertificate &cert);
static void addDefaultCaCertificates(const QList<QSslCertificate> &certs);
static bool isMatchingHostname(const QSslCertificate &cert, const QString &peerName);
Q_AUTOTEST_EXPORT static bool isMatchingHostname(const QString &cn, const QString &hostname);
#if defined(Q_OS_MACX)
static PtrSecCertificateCopyData ptrSecCertificateCopyData;

View File

@ -1462,25 +1462,25 @@ void tst_QSslSocket::systemCaCertificates()
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 );
QCOMPARE( QSslSocketBackendPrivate::isMatchingHostname(QString("192.168.0.0"), QString("192.168.0.0")), true );
QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("www.example.com"), QString("www.example.com")), true );
QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("*.example.com"), QString("www.example.com")), true );
QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("xxx*.example.com"), QString("xxxwww.example.com")), true );
QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("f*.example.com"), QString("foo.example.com")), true );
QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("192.168.0.0"), QString("192.168.0.0")), 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 );
QCOMPARE( QSslSocketBackendPrivate::isMatchingHostname(QString("*.168.0.0"), QString("192.168.0.0")), false );
QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("xxx.example.com"), QString("www.example.com")), false );
QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("*"), QString("www.example.com")), false );
QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("*.*.com"), QString("www.example.com")), false );
QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("*.example.com"), QString("baa.foo.example.com")), false );
QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("f*.example.com"), QString("baa.example.com")), false );
QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("*.com"), QString("example.com")), false );
QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("*fail.com"), QString("example.com")), false );
QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("*.example."), QString("www.example.")), false );
QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("*.example."), QString("www.example")), false );
QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString(""), QString("www")), false );
QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("*"), QString("www")), false );
QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("*.168.0.0"), QString("192.168.0.0")), false );
}
void tst_QSslSocket::wildcard()