diff options
author | Mårten Nordheim <marten.nordheim@qt.io> | 2019-01-07 18:01:36 +0100 |
---|---|---|
committer | Mårten Nordheim <marten.nordheim@qt.io> | 2019-01-24 15:24:14 +0000 |
commit | 58c9c4b60991d2665aef29c5981591391524e108 (patch) | |
tree | 535365550cf5e202c460e4edbef006ce86ccaaa2 /src/network | |
parent | 589a01ff6b1eacf81e74a5fc4801572135214f43 (diff) |
Ssl: Add support for IP-address in alternate subject name
While it's not common it still occurs, perhaps especially with 127.0.0.1
Can be tested by attempting to connect to https://1.1.1.1/ using Qt.
Change-Id: Idad56476597ab570b8347236ff700fa66ab5b1f4
Fixes: QTBUG-71828
Reviewed-by: Timur Pocheptsov <timur.pocheptsov@qt.io>
Diffstat (limited to 'src/network')
-rw-r--r-- | src/network/ssl/qasn1element_p.h | 1 | ||||
-rw-r--r-- | src/network/ssl/qssl.cpp | 3 | ||||
-rw-r--r-- | src/network/ssl/qssl.h | 3 | ||||
-rw-r--r-- | src/network/ssl/qsslcertificate_openssl.cpp | 40 | ||||
-rw-r--r-- | src/network/ssl/qsslcertificate_qt.cpp | 28 | ||||
-rw-r--r-- | src/network/ssl/qsslsocket.cpp | 13 |
6 files changed, 78 insertions, 10 deletions
diff --git a/src/network/ssl/qasn1element_p.h b/src/network/ssl/qasn1element_p.h index 59d1f58482..22948e3ca5 100644 --- a/src/network/ssl/qasn1element_p.h +++ b/src/network/ssl/qasn1element_p.h @@ -138,6 +138,7 @@ public: Rfc822NameType = 0x81, DnsNameType = 0x82, UniformResourceIdentifierType = 0x86, + IpAddressType = 0x87, // context specific Context0Type = 0xA0, diff --git a/src/network/ssl/qssl.cpp b/src/network/ssl/qssl.cpp index 6b5dbdfeac..c9fa7f85d9 100644 --- a/src/network/ssl/qssl.cpp +++ b/src/network/ssl/qssl.cpp @@ -99,6 +99,9 @@ Q_LOGGING_CATEGORY(lcSsl, "qt.network.ssl"); \value DnsEntry A DNS host name entry; the entry contains a host name entry that the certificate is valid for. The entry may contain wildcards. + \value IpAddressEntry An IP address entry; the entry contains an IP address + entry that the certificate is valid for, introduced in Qt 5.13. + \note In Qt 4, this enum was called \c {AlternateNameEntryType}. That name is deprecated in Qt 5. diff --git a/src/network/ssl/qssl.h b/src/network/ssl/qssl.h index 5c25e4e105..42c7b5c56d 100644 --- a/src/network/ssl/qssl.h +++ b/src/network/ssl/qssl.h @@ -68,7 +68,8 @@ namespace QSsl { enum AlternativeNameEntryType { EmailEntry, - DnsEntry + DnsEntry, + IpAddressEntry }; #if QT_DEPRECATED_SINCE(5,0) diff --git a/src/network/ssl/qsslcertificate_openssl.cpp b/src/network/ssl/qsslcertificate_openssl.cpp index 57a4dca08f..899c8a0d2d 100644 --- a/src/network/ssl/qsslcertificate_openssl.cpp +++ b/src/network/ssl/qsslcertificate_openssl.cpp @@ -44,6 +44,8 @@ #include "qsslkey_p.h" #include "qsslcertificateextension_p.h" +#include <QtCore/qendian.h> + #if QT_CONFIG(thread) #include <QtCore/private/qmutexpool_p.h> #endif @@ -207,10 +209,14 @@ QMultiMap<QSsl::AlternativeNameEntryType, QString> QSslCertificate::subjectAlter STACK_OF(GENERAL_NAME) *altNames = (STACK_OF(GENERAL_NAME) *)q_X509_get_ext_d2i( d->x509, NID_subject_alt_name, nullptr, nullptr); + auto altName = [](ASN1_IA5STRING *ia5, int len) { + const char *altNameStr = reinterpret_cast<const char *>(q_ASN1_STRING_get0_data(ia5)); + return QString::fromLatin1(altNameStr, len); + }; if (altNames) { for (int i = 0; i < q_sk_GENERAL_NAME_num(altNames); ++i) { const GENERAL_NAME *genName = q_sk_GENERAL_NAME_value(altNames, i); - if (genName->type != GEN_DNS && genName->type != GEN_EMAIL) + if (genName->type != GEN_DNS && genName->type != GEN_EMAIL && genName->type != GEN_IPADD) continue; int len = q_ASN1_STRING_length(genName->d.ia5); @@ -219,12 +225,32 @@ QMultiMap<QSsl::AlternativeNameEntryType, QString> QSslCertificate::subjectAlter continue; } - const char *altNameStr = reinterpret_cast<const char *>(q_ASN1_STRING_get0_data(genName->d.ia5)); - const QString altName = QString::fromLatin1(altNameStr, len); - if (genName->type == GEN_DNS) - result.insert(QSsl::DnsEntry, altName); - else if (genName->type == GEN_EMAIL) - result.insert(QSsl::EmailEntry, altName); + switch (genName->type) { + case GEN_DNS: + result.insert(QSsl::DnsEntry, altName(genName->d.ia5, len)); + break; + case GEN_EMAIL: + result.insert(QSsl::EmailEntry, altName(genName->d.ia5, len)); + break; + case GEN_IPADD: { + QHostAddress ipAddress; + switch (len) { + case 4: // IPv4 + ipAddress = QHostAddress(qFromBigEndian(*reinterpret_cast<quint32 *>(genName->d.iPAddress->data))); + break; + case 16: // IPv6 + ipAddress = QHostAddress(reinterpret_cast<quint8 *>(genName->d.iPAddress->data)); + break; + default: // Unknown IP address format + break; + } + if (!ipAddress.isNull()) + result.insert(QSsl::IpAddressEntry, ipAddress.toString()); + break; + } + default: + break; + } } q_OPENSSL_sk_pop_free((OPENSSL_STACK*)altNames, reinterpret_cast<void(*)(void*)>(q_GENERAL_NAME_free)); diff --git a/src/network/ssl/qsslcertificate_qt.cpp b/src/network/ssl/qsslcertificate_qt.cpp index 2bd5b3b412..cce59b5ef3 100644 --- a/src/network/ssl/qsslcertificate_qt.cpp +++ b/src/network/ssl/qsslcertificate_qt.cpp @@ -50,6 +50,8 @@ #include "qasn1element_p.h" #include <QtCore/qdatastream.h> +#include <QtCore/qendian.h> +#include <QtNetwork/qhostaddress.h> QT_BEGIN_NAMESPACE @@ -403,10 +405,32 @@ bool QSslCertificatePrivate::parse(const QByteArray &data) QDataStream nameStream(sanElem.value()); QAsn1Element nameElem; while (nameElem.read(nameStream)) { - if (nameElem.type() == QAsn1Element::Rfc822NameType) { + switch (nameElem.type()) { + case QAsn1Element::Rfc822NameType: subjectAlternativeNames.insert(QSsl::EmailEntry, nameElem.toString()); - } else if (nameElem.type() == QAsn1Element::DnsNameType) { + break; + case QAsn1Element::DnsNameType: subjectAlternativeNames.insert(QSsl::DnsEntry, nameElem.toString()); + break; + case QAsn1Element::IpAddressType: { + QHostAddress ipAddress; + QByteArray ipAddrValue = nameElem.value(); + switch (ipAddrValue.length()) { + case 4: // IPv4 + ipAddress = QHostAddress(qFromBigEndian(*reinterpret_cast<quint32 *>(ipAddrValue.data()))); + break; + case 16: // IPv6 + ipAddress = QHostAddress(reinterpret_cast<quint8 *>(ipAddrValue.data())); + break; + default: // Unknown IP address format + break; + } + if (!ipAddress.isNull()) + subjectAlternativeNames.insert(QSsl::IpAddressEntry, ipAddress.toString()); + break; + } + default: + break; } } } diff --git a/src/network/ssl/qsslsocket.cpp b/src/network/ssl/qsslsocket.cpp index 8d3ca092ff..68de9dedaa 100644 --- a/src/network/ssl/qsslsocket.cpp +++ b/src/network/ssl/qsslsocket.cpp @@ -2882,6 +2882,19 @@ QSharedPointer<QSslContext> QSslSocketPrivate::sslContext(QSslSocket *socket) bool QSslSocketPrivate::isMatchingHostname(const QSslCertificate &cert, const QString &peerName) { + QHostAddress hostAddress(peerName); + if (!hostAddress.isNull()) { + const auto subjectAlternativeNames = cert.subjectAlternativeNames(); + const auto ipAddresses = subjectAlternativeNames.equal_range(QSsl::AlternativeNameEntryType::IpAddressEntry); + + for (auto it = ipAddresses.first; it != ipAddresses.second; it++) { + if (QHostAddress(*it).isEqual(hostAddress, QHostAddress::StrictConversion)) + return true; + } + + return false; + } + const QString lowerPeerName = QString::fromLatin1(QUrl::toAce(peerName)); const QStringList commonNames = cert.subjectInfo(QSslCertificate::CommonName); |