OLD | NEW |
(Empty) | |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "components/certificate_transparency/log_dns_client.h" |
| 6 |
| 7 #include "base/bind.h" |
| 8 #include "base/location.h" |
| 9 #include "base/logging.h" |
| 10 #include "base/strings/string_number_conversions.h" |
| 11 #include "base/strings/string_util.h" |
| 12 #include "base/threading/thread_task_runner_handle.h" |
| 13 #include "components/base32/base32.h" |
| 14 #include "crypto/sha2.h" |
| 15 #include "net/base/net_errors.h" |
| 16 #include "net/cert/merkle_audit_proof.h" |
| 17 #include "net/dns/dns_client.h" |
| 18 #include "net/dns/dns_protocol.h" |
| 19 #include "net/dns/dns_response.h" |
| 20 #include "net/dns/dns_transaction.h" |
| 21 #include "net/dns/record_parsed.h" |
| 22 #include "net/dns/record_rdata.h" |
| 23 |
| 24 namespace certificate_transparency { |
| 25 |
| 26 LogDnsClient::LogDnsClient(std::unique_ptr<net::DnsClient> dns_client, |
| 27 const net::BoundNetLog& net_log, |
| 28 base::Clock* clock) |
| 29 : dns_client_(std::move(dns_client)), |
| 30 net_log_(net_log), |
| 31 clock_(clock), |
| 32 weak_ptr_factory_(this) { |
| 33 CHECK(dns_client_); |
| 34 CHECK(clock_); |
| 35 } |
| 36 |
| 37 LogDnsClient::~LogDnsClient() {} |
| 38 |
| 39 void LogDnsClient::QueryLeafIndex(base::StringPiece domain_for_log, |
| 40 base::StringPiece leaf_hash, |
| 41 const LeafIndexCallback& callback) { |
| 42 if (leaf_hash.size() != crypto::kSHA256Length) { |
| 43 base::ThreadTaskRunnerHandle::Get()->PostTask( |
| 44 FROM_HERE, base::Bind(callback, net::Error::ERR_INVALID_ARGUMENT, 0)); |
| 45 return; |
| 46 } |
| 47 |
| 48 std::string encoded_leaf_hash = |
| 49 base32::Base32Encode(leaf_hash, base32::Base32EncodePolicy::OMIT_PADDING); |
| 50 DCHECK_EQ(encoded_leaf_hash.size(), 52u); |
| 51 std::string qname = encoded_leaf_hash; |
| 52 qname += ".hash."; |
| 53 domain_for_log.AppendToString(&qname); |
| 54 qname += "."; |
| 55 |
| 56 net::DnsTransactionFactory* factory = dns_client_->GetTransactionFactory(); |
| 57 if (factory == nullptr) { |
| 58 base::ThreadTaskRunnerHandle::Get()->PostTask( |
| 59 FROM_HERE, |
| 60 base::Bind(callback, net::Error::ERR_NAME_RESOLUTION_FAILED, 0)); |
| 61 return; |
| 62 } |
| 63 |
| 64 net::DnsTransactionFactory::CallbackType transaction_callback = base::Bind( |
| 65 &LogDnsClient::QueryLeafIndexComplete, weak_ptr_factory_.GetWeakPtr()); |
| 66 |
| 67 std::unique_ptr<net::DnsTransaction> dns_transaction = |
| 68 factory->CreateTransaction(qname, net::dns_protocol::kTypeTXT, |
| 69 transaction_callback, net_log_); |
| 70 |
| 71 dns_transaction->Start(); |
| 72 leaf_index_queries_.push_back({std::move(dns_transaction), callback}); |
| 73 } |
| 74 |
| 75 void LogDnsClient::QueryAuditProof(base::StringPiece domain_for_log, |
| 76 uint64_t leaf_index, |
| 77 uint64_t tree_size, |
| 78 const AuditProofCallback& callback) { |
| 79 if (leaf_index >= tree_size) { |
| 80 base::ThreadTaskRunnerHandle::Get()->PostTask( |
| 81 FROM_HERE, |
| 82 base::Bind(callback, net::Error::ERR_INVALID_ARGUMENT, nullptr)); |
| 83 return; |
| 84 } |
| 85 |
| 86 std::unique_ptr<net::ct::MerkleAuditProof> proof( |
| 87 new net::ct::MerkleAuditProof); |
| 88 proof->leaf_index = leaf_index; |
| 89 |
| 90 QueryAuditProofNodes(std::move(proof), domain_for_log, leaf_index, tree_size, |
| 91 0, callback); |
| 92 } |
| 93 |
| 94 void LogDnsClient::QueryLeafIndexComplete(net::DnsTransaction* transaction, |
| 95 int net_error, |
| 96 const net::DnsResponse* response) { |
| 97 auto query_iterator = |
| 98 std::find_if(leaf_index_queries_.begin(), leaf_index_queries_.end(), |
| 99 [transaction](const Query<LeafIndexCallback>& query) { |
| 100 return query.transaction.get() == transaction; |
| 101 }); |
| 102 if (query_iterator == leaf_index_queries_.end()) { |
| 103 NOTREACHED(); |
| 104 return; |
| 105 } |
| 106 const Query<LeafIndexCallback> query = std::move(*query_iterator); |
| 107 leaf_index_queries_.erase(query_iterator); |
| 108 |
| 109 if (net_error != net::OK) { |
| 110 base::ThreadTaskRunnerHandle::Get()->PostTask( |
| 111 FROM_HERE, base::Bind(query.callback, net_error, 0)); |
| 112 return; |
| 113 } |
| 114 |
| 115 if (response == nullptr) { |
| 116 base::ThreadTaskRunnerHandle::Get()->PostTask( |
| 117 FROM_HERE, base::Bind(query.callback, net::ERR_INVALID_RESPONSE, 0)); |
| 118 return; |
| 119 } |
| 120 |
| 121 uint64_t leaf_index; |
| 122 if (!ParseLeafIndex(*response, &leaf_index)) { |
| 123 base::ThreadTaskRunnerHandle::Get()->PostTask( |
| 124 FROM_HERE, |
| 125 base::Bind(query.callback, net::ERR_DNS_MALFORMED_RESPONSE, 0)); |
| 126 return; |
| 127 } |
| 128 |
| 129 base::ThreadTaskRunnerHandle::Get()->PostTask( |
| 130 FROM_HERE, base::Bind(query.callback, net::OK, leaf_index)); |
| 131 } |
| 132 |
| 133 void LogDnsClient::QueryAuditProofNodes( |
| 134 std::unique_ptr<net::ct::MerkleAuditProof> proof, |
| 135 base::StringPiece domain_for_log, |
| 136 uint64_t leaf_index, |
| 137 uint64_t tree_size, |
| 138 uint64_t node_index, |
| 139 const AuditProofCallback& callback) { |
| 140 CHECK(proof); |
| 141 CHECK_LT(leaf_index, tree_size); |
| 142 CHECK_LT(node_index, |
| 143 net::ct::CalculateAuditPathLength(leaf_index, tree_size)); |
| 144 |
| 145 std::string qname = base::Uint64ToString(node_index); |
| 146 qname += "."; |
| 147 qname += base::Uint64ToString(leaf_index); |
| 148 qname += "."; |
| 149 qname += base::Uint64ToString(tree_size); |
| 150 qname += ".tree."; |
| 151 domain_for_log.AppendToString(&qname); |
| 152 qname += "."; |
| 153 |
| 154 net::DnsTransactionFactory* factory = dns_client_->GetTransactionFactory(); |
| 155 if (factory == nullptr) { |
| 156 base::ThreadTaskRunnerHandle::Get()->PostTask( |
| 157 FROM_HERE, |
| 158 base::Bind(callback, net::Error::ERR_NAME_RESOLUTION_FAILED, nullptr)); |
| 159 return; |
| 160 } |
| 161 |
| 162 net::DnsTransactionFactory::CallbackType transaction_callback = |
| 163 base::Bind(&LogDnsClient::QueryAuditProofNodesComplete, |
| 164 weak_ptr_factory_.GetWeakPtr(), base::Passed(std::move(proof)), |
| 165 domain_for_log, leaf_index, tree_size); |
| 166 |
| 167 std::unique_ptr<net::DnsTransaction> dns_transaction = |
| 168 factory->CreateTransaction(qname, net::dns_protocol::kTypeTXT, |
| 169 transaction_callback, net_log_); |
| 170 dns_transaction->Start(); |
| 171 audit_proof_queries_.push_back({std::move(dns_transaction), callback}); |
| 172 } |
| 173 |
| 174 void LogDnsClient::QueryAuditProofNodesComplete( |
| 175 std::unique_ptr<net::ct::MerkleAuditProof> proof, |
| 176 base::StringPiece domain_for_log, |
| 177 uint64_t leaf_index, |
| 178 uint64_t tree_size, |
| 179 net::DnsTransaction* transaction, |
| 180 int net_error, |
| 181 const net::DnsResponse* response) { |
| 182 auto query_iterator = |
| 183 std::find_if(audit_proof_queries_.begin(), audit_proof_queries_.end(), |
| 184 [transaction](const Query<AuditProofCallback>& query) { |
| 185 return query.transaction.get() == transaction; |
| 186 }); |
| 187 |
| 188 if (query_iterator == audit_proof_queries_.end()) { |
| 189 NOTREACHED(); |
| 190 return; |
| 191 } |
| 192 const Query<AuditProofCallback> query = std::move(*query_iterator); |
| 193 audit_proof_queries_.erase(query_iterator); |
| 194 |
| 195 if (net_error != net::OK) { |
| 196 base::ThreadTaskRunnerHandle::Get()->PostTask( |
| 197 FROM_HERE, base::Bind(query.callback, net_error, nullptr)); |
| 198 return; |
| 199 } |
| 200 |
| 201 if (response == nullptr) { |
| 202 base::ThreadTaskRunnerHandle::Get()->PostTask( |
| 203 FROM_HERE, |
| 204 base::Bind(query.callback, net::ERR_INVALID_RESPONSE, nullptr)); |
| 205 return; |
| 206 } |
| 207 |
| 208 if (!ParseAuditPath(*response, proof.get())) { |
| 209 base::ThreadTaskRunnerHandle::Get()->PostTask( |
| 210 FROM_HERE, |
| 211 base::Bind(query.callback, net::ERR_DNS_MALFORMED_RESPONSE, nullptr)); |
| 212 return; |
| 213 } |
| 214 |
| 215 const uint64_t audit_path_nodes_received = proof->nodes.size(); |
| 216 const uint64_t audit_path_length = |
| 217 net::ct::CalculateAuditPathLength(leaf_index, tree_size); |
| 218 if (audit_path_nodes_received < audit_path_length) { |
| 219 QueryAuditProofNodes(std::move(proof), domain_for_log, leaf_index, |
| 220 tree_size, audit_path_nodes_received, query.callback); |
| 221 return; |
| 222 } |
| 223 |
| 224 base::ThreadTaskRunnerHandle::Get()->PostTask( |
| 225 FROM_HERE, |
| 226 base::Bind(query.callback, net::OK, base::Passed(std::move(proof)))); |
| 227 } |
| 228 |
| 229 bool LogDnsClient::ParseTxtResponse(const net::DnsResponse& response, |
| 230 std::string* txt) { |
| 231 DCHECK(txt); |
| 232 |
| 233 net::DnsRecordParser parser = response.Parser(); |
| 234 auto parsed_record = net::RecordParsed::CreateFrom(&parser, clock_->Now()); |
| 235 if (parsed_record == nullptr) |
| 236 return false; |
| 237 |
| 238 auto txt_record = parsed_record->rdata<net::TxtRecordRdata>(); |
| 239 if (txt_record == nullptr) |
| 240 return false; |
| 241 |
| 242 *txt = base::JoinString(txt_record->texts(), ""); |
| 243 return true; |
| 244 } |
| 245 |
| 246 bool LogDnsClient::ParseLeafIndex(const net::DnsResponse& response, |
| 247 uint64_t* index) { |
| 248 std::string index_str; |
| 249 if (!ParseTxtResponse(response, &index_str)) |
| 250 return false; |
| 251 |
| 252 return base::StringToUint64(index_str, index); |
| 253 } |
| 254 |
| 255 bool LogDnsClient::ParseAuditPath(const net::DnsResponse& response, |
| 256 net::ct::MerkleAuditProof* proof) { |
| 257 std::string audit_path; |
| 258 if (!ParseTxtResponse(response, &audit_path)) |
| 259 return false; |
| 260 |
| 261 for (size_t i = 0; i < audit_path.size(); i += crypto::kSHA256Length) { |
| 262 std::string node = audit_path.substr(i, crypto::kSHA256Length); |
| 263 |
| 264 if (node.size() == crypto::kSHA256Length) |
| 265 proof->nodes.push_back(node); |
| 266 else |
| 267 return false; |
| 268 } |
| 269 |
| 270 return true; |
| 271 } |
| 272 |
| 273 } // namespace certificate_transparency |
OLD | NEW |