Index: net/socket/ssl_client_socket_nss.cc |
diff --git a/net/socket/ssl_client_socket_nss.cc b/net/socket/ssl_client_socket_nss.cc |
index dd3b13a16905338f08c50ec2005fa15f27bac941..acbb875b5af48fac374d8124e21cd09fc0b97396 100644 |
--- a/net/socket/ssl_client_socket_nss.cc |
+++ b/net/socket/ssl_client_socket_nss.cc |
@@ -278,6 +278,7 @@ enum DNSValidationResult { |
// server_cert_nss: the server's leaf certificate. |
// rrdatas: the CAA records for the current domain. |
// port: the TCP port number that we connected to. |
+// TODO(agl): remove once DANE support has been released. |
DNSValidationResult VerifyCAARecords( |
CERTCertificate* server_cert_nss, |
const std::vector<base::StringPiece>& rrdatas, |
@@ -323,6 +324,55 @@ DNSValidationResult VerifyCAARecords( |
return DNSVR_FAILURE; |
} |
+// VerifyTLSARecords processes DNSSEC validated RRDATA for a number of DNS TLSA |
+// records and checks them against the given chain. |
+// server_cert_nss: the server's leaf certificate. |
+// rrdatas: the TLSA records for the current domain. |
+DNSValidationResult VerifyTLSARecords( |
+ CERTCertificate* server_cert_nss, |
+ const std::vector<base::StringPiece>& rrdatas) { |
+ std::vector<DnsTLSARecord::Match> matches; |
+ DnsTLSARecord::Parse(rrdatas, &matches); |
Ryan Sleevi
2012/10/18 23:21:13
Should a certificate with invalid record types be
agl
2012/10/29 15:41:56
Invalid record types are future record types that
|
+ |
+ for (std::vector<DnsTLSARecord::Match>::const_iterator |
+ i = matches.begin(); i != matches.end(); i++) { |
Ryan Sleevi
2012/10/18 23:21:13
style nit: pre-increment
http://google-styleguide
agl
2012/10/29 15:41:56
Done.
|
+ SECItem matched_data; |
+ switch (i->target) { |
+ case DnsTLSARecord::Match::CERTIFICATE: |
+ matched_data = server_cert_nss->derCert; |
+ break; |
+ case DnsTLSARecord::Match::SUBJECT_PUBLIC_KEY_INFO: |
+ matched_data = server_cert_nss->derPublicKey; |
+ break; |
+ default: |
+ continue; |
+ } |
Ryan Sleevi
2012/10/18 23:21:13
style nit: indentation of cases
agl
2012/10/29 15:41:56
Done.
|
+ |
+ uint8 calculated_hash[SHA512_LENGTH]; // SHA512 is the largest. |
Ryan Sleevi
2012/10/18 23:21:13
nit: HASH_LENGTH_MAX (comes from hasht.h, the same
agl
2012/10/29 15:41:56
Done.
|
+ SECItem processed_data; |
+ if (i->algorithm == HASH_AlgNULL) { |
+ processed_data = matched_data; |
+ } else { |
+ SECStatus rv = HASH_HashBuf( |
+ static_cast<HASH_HashType>(i->algorithm), |
Ryan Sleevi
2012/10/18 23:21:13
DESIGN: You seem to have tightly coupled the HASH_
agl
2012/10/29 15:41:56
The alternative would be Yet Another Hash Type enu
|
+ calculated_hash, |
+ matched_data.data, |
+ matched_data.len); |
+ DCHECK(rv == SECSuccess); |
Ryan Sleevi
2012/10/18 23:21:13
nit: DCHECK_EQ(SECSuccess, rv)
|
+ processed_data.data = calculated_hash; |
+ processed_data.len = i->data.size(); |
+ } |
+ |
+ if (processed_data.len == i->data.size() && |
+ memcmp(processed_data.data, i->data.data(), i->data.size()) == 0) { |
+ return DNSVR_SUCCESS; |
+ } |
+ } |
+ |
+ // No TLSA records matched so we reject the certificate. |
+ return DNSVR_FAILURE; |
+} |
+ |
// CheckDNSSECChain tries to validate a DNSSEC chain embedded in |
// |server_cert_nss|. It returns true iff a chain is found that proves the |
// value of a CAA record that contains a valid public key fingerprint. |
@@ -363,27 +413,50 @@ DNSValidationResult CheckDNSSECChain( |
if (rv != SECSuccess) |
return DNSVR_CONTINUE; |
- base::StringPiece chain( |
+ std::string chain( |
reinterpret_cast<char*>(dnssec_embedded_chain.data), |
dnssec_embedded_chain.len); |
+ SECITEM_FreeItem(&dnssec_embedded_chain, PR_FALSE); |
std::string dns_hostname; |
if (!DNSDomainFromDot(hostname, &dns_hostname)) |
return DNSVR_CONTINUE; |
+ |
DNSSECChainVerifier verifier(dns_hostname, chain); |
DNSSECChainVerifier::Error err = verifier.Verify(); |
+ bool is_tlsa = false; |
+ if (err == DNSSECChainVerifier::BAD_TARGET) { |
+ // We might have failed because this is a DANE chain, not a CAA chain. |
+ // DANE stores records in a subdomain of the name that includes the port |
+ // and protocol, i.e. _443._tcp.www.example.com. We have to construct |
+ // such a name for |hostname|. |
+ const std::string port_str = base::UintToString(port); |
+ char port_label_len = 1 + port_str.size(); |
Ryan Sleevi
2012/10/18 23:21:13
nit: I would expect a warning here from at least o
agl
2012/10/29 15:41:56
Added static_cast.
|
+ const std::string tlsa_domain = |
+ std::string(&port_label_len, 1) + "_" + port_str + |
+ "\x04_tcp" + |
+ dns_hostname; |
Ryan Sleevi
2012/10/18 23:21:13
world's tiniest nit: StringPrintf / StringAppend,
agl
2012/10/29 15:41:56
While I agree in general, since |dns_hostname| has
|
+ |
+ verifier = DNSSECChainVerifier(tlsa_domain, chain); |
+ err = verifier.Verify(); |
+ is_tlsa = true; |
+ } |
+ |
if (err != DNSSECChainVerifier::OK) { |
LOG(ERROR) << "DNSSEC chain verification failed: " << err; |
return DNSVR_CONTINUE; |
} |
+ if (is_tlsa) { |
+ if (verifier.rrtype() != kDNS_TLSA) |
+ return DNSVR_CONTINUE; |
+ |
+ return VerifyTLSARecords(server_cert_nss, verifier.rrdatas()); |
+ } |
+ |
if (verifier.rrtype() != kDNS_CAA) |
return DNSVR_CONTINUE; |
- DNSValidationResult r = VerifyCAARecords( |
- server_cert_nss, verifier.rrdatas(), port); |
- SECITEM_FreeItem(&dnssec_embedded_chain, PR_FALSE); |
- |
- return r; |
+ return VerifyCAARecords(server_cert_nss, verifier.rrdatas(), port); |
} |
void DestroyCertificates(CERTCertificate** certs, size_t len) { |