diff options
author | Thiago Macieira <thiago.macieira@intel.com> | 2024-04-17 11:09:58 -0700 |
---|---|---|
committer | Thiago Macieira <thiago.macieira@intel.com> | 2024-05-08 21:13:47 -0700 |
commit | 4503dabfbd11c084c2781a679c9af12d5fb8f763 (patch) | |
tree | 276b12b705d46ad6c9b3343cae16eeca0e5eed78 /src/network | |
parent | 503fd609881fb220ac5abe7da2fe367efd90ed4b (diff) |
QDnsLookup: add support for TLSA records
[ChangeLog][QtNetwork][QDnsLookup] Added support for querying records of
type TLSA, which are useful in DNS-based Authentication of Named
Entities (DANE).
Change-Id: I455fe22ef4ad4b2f9b01fffd17c723aa6ab7f278
Reviewed-by: MÃ¥rten Nordheim <marten.nordheim@qt.io>
Diffstat (limited to 'src/network')
-rw-r--r-- | src/network/kernel/qdnslookup.cpp | 234 | ||||
-rw-r--r-- | src/network/kernel/qdnslookup.h | 78 | ||||
-rw-r--r-- | src/network/kernel/qdnslookup_p.h | 10 | ||||
-rw-r--r-- | src/network/kernel/qdnslookup_unix.cpp | 17 | ||||
-rw-r--r-- | src/network/kernel/qdnslookup_win.cpp | 19 |
5 files changed, 357 insertions, 1 deletions
diff --git a/src/network/kernel/qdnslookup.cpp b/src/network/kernel/qdnslookup.cpp index a3ebbe04db..1b4db7130b 100644 --- a/src/network/kernel/qdnslookup.cpp +++ b/src/network/kernel/qdnslookup.cpp @@ -257,6 +257,8 @@ static void qt_qdnsservicerecord_sort(QList<QDnsServiceRecord> &records) \value SRV service records. + \value[since 6.8] TLSA TLS association records. + \value TXT text records. */ @@ -704,6 +706,21 @@ QList<QDnsTextRecord> QDnsLookup::textRecords() const return d_func()->reply.textRecords; } +/*! + \since 6.8 + Returns the list of TLS association records associated with this lookup. + + According to the standards relating to DNS-based Authentication of Named + Entities (DANE), this field should be ignored and must not be used for + verifying the authentity of a given server if the authenticity of the DNS + reply cannot itself be confirmed. See isAuthenticData() for more + information. + */ +QList<QDnsTlsAssociationRecord> QDnsLookup::tlsAssociationRecords() const +{ + return d_func()->reply.tlsAssociationRecords; +} + #if QT_CONFIG(ssl) /*! \since 6.8 @@ -1261,6 +1278,223 @@ QDnsTextRecord &QDnsTextRecord::operator=(const QDnsTextRecord &other) very fast and never fails. */ +/*! + \class QDnsTlsAssociationRecord + \since 6.8 + \brief The QDnsTlsAssociationRecord class stores information about a DNS TLSA record. + + \inmodule QtNetwork + \ingroup network + \ingroup shared + + When performing a text lookup, zero or more records will be returned. Each + record is represented by a QDnsTlsAssociationRecord instance. + + The meaning of the fields is defined in \l{RFC 6698}. + + \sa QDnsLookup +*/ + +QT_DEFINE_QSDP_SPECIALIZATION_DTOR(QDnsTlsAssociationRecordPrivate) + +/*! + \enum QDnsTlsAssociationRecord::CertificateUsage + + This enumeration contains valid values for the certificate usage field of + TLS Association queries. The following list is up-to-date with \l{RFC 6698} + section 2.1.1 and RFC 7218 section 2.1. Please refer to those documents for + authoritative instructions on interpreting this enumeration. + + \value CertificateAuthorityConstrait + Indicates the record includes an association to a specific Certificate + Authority that must be found in the TLS server's certificate chain and + must pass PKIX validation. + + \value ServiceCertificateConstraint + Indicates the record includes an association to a certificate that must + match the end entity certificate provided by the TLS server and must + pass PKIX validation. + + \value TrustAnchorAssertion + Indicates the record includes an association to a certificate that MUST + be used as the ultimate trust anchor to validate the TLS server's + certificate and must pass PKIX validation. + + \value DomainIssuedCertificate + Indicates the record includes an association to a certificate that must + match the end entity certificate provided by the TLS server. PKIX + validation is not tested. + + \value PrivateUse + No standard meaning applied. + + \value PKIX_TA + Alias; mnemonic for Public Key Infrastructure Trust Anchor + + \value PKIX_EE + Alias; mnemonic for Public Key Infrastructure End Entity + + \value DANE_TA + Alias; mnemonic for DNS-based Authentication of Named Entities Trust Anchor + + \value DANE_EE + Alias; mnemonic for DNS-based Authentication of Named Entities End Entity + + \value PrivCert + Alias + + Other values are currently reserved, but may be unreserved by future + standards. This enumeration can be used for those values even if no + enumerator is provided. + + \sa certificateUsage() +*/ + +/*! + \enum QDnsTlsAssociationRecord::Selector + + This enumeration contains valid values for the selector field of TLS + Association queries. The following list is up-to-date with \l{RFC 6698} + section 2.1.2 and RFC 7218 section 2.2. Please refer to those documents for + authoritative instructions on interpreting this enumeration. + + \value FullCertificate + Indicates this record refers to the full certificate in its binary + structure form. + + \value SubjectPublicKeyInfo + Indicates the record refers to the certificate's subject and public + key information, in DER-encoded binary structure form. + + \value PrivateUse + No standard meaning applied. + + \value Cert + Alias + + \value SPKI + Alias + + \value PrivSel + Alias + + Other values are currently reserved, but may be unreserved by future + standards. This enumeration can be used for those values even if no + enumerator is provided. + + \sa selector() +*/ + +/*! + \enum QDnsTlsAssociationRecord::MatchingType + + This enumeration contains valid values for the matching type field of TLS + Association queries. The following list is up-to-date with \l{RFC 6698} + section 2.1.3 and RFC 7218 section 2.3. Please refer to those documents for + authoritative instructions on interpreting this enumeration. + + \value Exact + Indicates this the certificate or SPKI data is stored verbatim in this + record. + + \value Sha256 + Indicates this a SHA-256 checksum of the the certificate or SPKI data + present in this record. + + \value Sha512 + Indicates this a SHA-512 checksum of the the certificate or SPKI data + present in this record. + + \value PrivateUse + No standard meaning applied. + + \value PrivMatch + Alias + + Other values are currently reserved, but may be unreserved by future + standards. This enumeration can be used for those values even if no + enumerator is provided. + + \sa matchingType() +*/ + +/*! + Constructs an empty TLS Association record. + */ +QDnsTlsAssociationRecord::QDnsTlsAssociationRecord() + : d(new QDnsTlsAssociationRecordPrivate) +{ +} + +/*! + Constructs a copy of \a other. + */ +QDnsTlsAssociationRecord::QDnsTlsAssociationRecord(const QDnsTlsAssociationRecord &other) = default; + +/*! + Moves the content of \a other into this object. + */ +QDnsTlsAssociationRecord & +QDnsTlsAssociationRecord::operator=(const QDnsTlsAssociationRecord &other) = default; + +/*! + Destroys this TLS Association record object. + */ +QDnsTlsAssociationRecord::~QDnsTlsAssociationRecord() = default; + +/*! + Returns the name of this record. +*/ +QString QDnsTlsAssociationRecord::name() const +{ + return d->name; +} + +/*! + Returns the duration in seconds for which this record is valid. +*/ +quint32 QDnsTlsAssociationRecord::timeToLive() const +{ + return d->timeToLive; +} + +/*! + Returns the certificate usage field for this record. + */ +QDnsTlsAssociationRecord::CertificateUsage QDnsTlsAssociationRecord::usage() const +{ + return d->usage; +} + +/*! + Returns the selector field for this record. + */ +QDnsTlsAssociationRecord::Selector QDnsTlsAssociationRecord::selector() const +{ + return d->selector; +} + +/*! + Returns the match type field for this record. + */ +QDnsTlsAssociationRecord::MatchingType QDnsTlsAssociationRecord::matchType() const +{ + return d->matchType; +} + +/*! + Returns the binary data field for this record. The interpretation of this + binary data depends on the three numeric fields provided by + certificateUsage(), selector(), and matchType(). + + Do note this is a binary field, even for the checksums, similar to what + QCyrptographicHash::result() returns. + */ +QByteArray QDnsTlsAssociationRecord::value() const +{ + return d->value; +} + static QDnsLookupRunnable::EncodedLabel encodeLabel(const QString &label) { QDnsLookupRunnable::EncodedLabel::value_type rootDomain = u'.'; diff --git a/src/network/kernel/qdnslookup.h b/src/network/kernel/qdnslookup.h index ad98f8ae5c..8d21e99c84 100644 --- a/src/network/kernel/qdnslookup.h +++ b/src/network/kernel/qdnslookup.h @@ -22,8 +22,11 @@ class QDnsHostAddressRecordPrivate; class QDnsMailExchangeRecordPrivate; class QDnsServiceRecordPrivate; class QDnsTextRecordPrivate; +class QDnsTlsAssociationRecordPrivate; class QSslConfiguration; +QT_DECLARE_QSDP_SPECIALIZATION_DTOR(QDnsTlsAssociationRecordPrivate) + class Q_NETWORK_EXPORT QDnsDomainNameRecord { public: @@ -138,6 +141,78 @@ private: Q_DECLARE_SHARED(QDnsTextRecord) +class Q_NETWORK_EXPORT QDnsTlsAssociationRecord +{ + Q_GADGET +public: + enum class CertificateUsage : quint8 { + // https://www.iana.org/assignments/dane-parameters/dane-parameters.xhtml#certificate-usages + // RFC 6698 + CertificateAuthorityConstrait = 0, + ServiceCertificateConstraint = 1, + TrustAnchorAssertion = 2, + DomainIssuedCertificate = 3, + PrivateUse = 255, + + // Aliases by RFC 7218 + PKIX_TA = 0, + PKIX_EE = 1, + DANE_TA = 2, + DANE_EE = 3, + PrivCert = 255, + }; + Q_ENUM(CertificateUsage) + + enum class Selector : quint8 { + // https://www.iana.org/assignments/dane-parameters/dane-parameters.xhtml#selectors + // RFC 6698 + FullCertificate = 0, + SubjectPublicKeyInfo = 1, + PrivateUse = 255, + + // Aliases by RFC 7218 + Cert = FullCertificate, + SPKI = SubjectPublicKeyInfo, + PrivSel = PrivateUse, + }; + Q_ENUM(Selector) + + enum class MatchingType : quint8 { + // https://www.iana.org/assignments/dane-parameters/dane-parameters.xhtml#matching-types + // RFC 6698 + Exact = 0, + Sha256 = 1, + Sha512 = 2, + PrivateUse = 255, + PrivMatch = PrivateUse, + }; + Q_ENUM(MatchingType) + + QDnsTlsAssociationRecord(); + QDnsTlsAssociationRecord(const QDnsTlsAssociationRecord &other); + QDnsTlsAssociationRecord(QDnsTlsAssociationRecord &&other) + : d(std::move(other.d)) + {} + QDnsTlsAssociationRecord &operator=(QDnsTlsAssociationRecord &&other) noexcept { swap(other); return *this; } + QDnsTlsAssociationRecord &operator=(const QDnsTlsAssociationRecord &other); + ~QDnsTlsAssociationRecord(); + + void swap(QDnsTlsAssociationRecord &other) noexcept { d.swap(other.d); } + + QString name() const; + quint32 timeToLive() const; + CertificateUsage usage() const; + Selector selector() const; + MatchingType matchType() const; + QByteArray value() const; + +private: + QSharedDataPointer<QDnsTlsAssociationRecordPrivate> d; + friend class QDnsLookupRunnable; +}; + +Q_DECLARE_SHARED(QDnsTlsAssociationRecord) + class Q_NETWORK_EXPORT QDnsLookup : public QObject { Q_OBJECT @@ -178,6 +253,7 @@ public: NS = 2, PTR = 12, SRV = 33, + TLSA = 52, TXT = 16 }; Q_ENUM(Type) @@ -230,7 +306,7 @@ public: QList<QDnsDomainNameRecord> pointerRecords() const; QList<QDnsServiceRecord> serviceRecords() const; QList<QDnsTextRecord> textRecords() const; - + QList<QDnsTlsAssociationRecord> tlsAssociationRecords() const; #if QT_CONFIG(ssl) void setSslConfiguration(const QSslConfiguration &sslConfiguration); diff --git a/src/network/kernel/qdnslookup_p.h b/src/network/kernel/qdnslookup_p.h index 259dca0b2e..811b7336a3 100644 --- a/src/network/kernel/qdnslookup_p.h +++ b/src/network/kernel/qdnslookup_p.h @@ -57,6 +57,7 @@ public: QList<QDnsDomainNameRecord> nameServerRecords; QList<QDnsDomainNameRecord> pointerRecords; QList<QDnsServiceRecord> serviceRecords; + QList<QDnsTlsAssociationRecord> tlsAssociationRecords; QList<QDnsTextRecord> textRecords; #if QT_CONFIG(ssl) @@ -296,6 +297,15 @@ public: QList<QByteArray> values; }; +class QDnsTlsAssociationRecordPrivate : public QDnsRecordPrivate +{ +public: + QDnsTlsAssociationRecord::CertificateUsage usage; + QDnsTlsAssociationRecord::Selector selector; + QDnsTlsAssociationRecord::MatchingType matchType; + QByteArray value; +}; + QT_END_NAMESPACE #endif // QDNSLOOKUP_P_H diff --git a/src/network/kernel/qdnslookup_unix.cpp b/src/network/kernel/qdnslookup_unix.cpp index 38a1dce5d9..c0c5f8b715 100644 --- a/src/network/kernel/qdnslookup_unix.cpp +++ b/src/network/kernel/qdnslookup_unix.cpp @@ -406,6 +406,23 @@ void QDnsLookupRunnable::query(QDnsLookupReply *reply) if (status < 0) return reply->makeInvalidReplyError(QDnsLookup::tr("Invalid service record")); reply->serviceRecords.append(record); + } else if (type == QDnsLookup::TLSA) { + // https://datatracker.ietf.org/doc/html/rfc6698#section-2.1 + if (size < 3) + return reply->makeInvalidReplyError(QDnsLookup::tr("Invalid TLS association record")); + + const quint8 usage = response[offset]; + const quint8 selector = response[offset + 1]; + const quint8 matchType = response[offset + 2]; + + QDnsTlsAssociationRecord record; + record.d->name = name; + record.d->timeToLive = ttl; + record.d->usage = QDnsTlsAssociationRecord::CertificateUsage(usage); + record.d->selector = QDnsTlsAssociationRecord::Selector(selector); + record.d->matchType = QDnsTlsAssociationRecord::MatchingType(matchType); + record.d->value.assign(response + offset + 3, response + offset + size); + reply->tlsAssociationRecords.append(std::move(record)); } else if (type == QDnsLookup::TXT) { QDnsTextRecord record; record.d->name = name; diff --git a/src/network/kernel/qdnslookup_win.cpp b/src/network/kernel/qdnslookup_win.cpp index 3e1b76a0b0..92dd95ab53 100644 --- a/src/network/kernel/qdnslookup_win.cpp +++ b/src/network/kernel/qdnslookup_win.cpp @@ -224,6 +224,25 @@ void QDnsLookupRunnable::query(QDnsLookupReply *reply) record.d->timeToLive = ptr->dwTtl; record.d->weight = ptr->Data.Srv.wWeight; reply->serviceRecords.append(record); + } else if (ptr->wType == QDnsLookup::TLSA) { + // Note: untested, because the DNS_RECORD reply appears to contain + // no records relating to TLSA. Maybe WinDNS filters them out of + // zones without DNSSEC. + QDnsTlsAssociationRecord record; + record.d->name = name; + record.d->timeToLive = ptr->dwTtl; + + const auto &tlsa = ptr->Data.Tlsa; + const quint8 usage = tlsa.bCertUsage; + const quint8 selector = tlsa.bSelector; + const quint8 matchType = tlsa.bMatchingType; + + record.d->usage = QDnsTlsAssociationRecord::CertificateUsage(usage); + record.d->selector = QDnsTlsAssociationRecord::Selector(selector); + record.d->matchType = QDnsTlsAssociationRecord::MatchingType(matchType); + record.d->value.assign(tlsa.bCertificateAssociationData, + tlsa.bCertificateAssociationData + tlsa.bCertificateAssociationDataLength); + reply->tlsAssociationRecords.append(std::move(record)); } else if (ptr->wType == QDnsLookup::TXT) { QDnsTextRecord record; record.d->name = name; |