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:
parent
be4db73231
commit
ecbf6dfbf5
@ -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"
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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()
|
||||
|
Loading…
Reference in New Issue
Block a user