1 // Copyright (c) 2011 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 "net/base/dnssec_chain_verifier.h"
6
7 #include "base/logging.h"
8 #include "base/memory/scoped_ptr.h"
9 #include "base/sha1.h"
10 #include "base/string_util.h"
11 #include "crypto/sha2.h"
12 #include "net/base/dns_util.h"
13 #include "net/base/dnssec_keyset.h"
14
15 // We don't have a location for the spec yet, so we'll include it here until it
16 // finds a better home.
17
18 /*
19 When connecting to a host www.example.com, www.example.com may present a certificate which includes a DNSSEC chain embedded in it. The aim of the embedded chain is to prove that the fingerprint of the public key is valid DNSSEC data. This is achieved by proving a CERT record for the target domain.
20
21 Initially, the target domain is constructed by prepending _ssl. For example, the initial target domain for www.example.com is _ssl.www.example.com.
22
23 A DNSSEC chain verifier can be in one of two states: entering a zone, or within a zone. Initially, the verifier is entering the root zone.
24
25 When entering a zone, the verifier reads the following structure:
26
27 uint8 entryKey
28 uint16 signature length:
29 // See RRSIG RDATA in RFC 4043 for details
30 uint8 algorithm
31 uint8 labels
32 uint32 ttl
33 uint32 expires
34 uint32 begins
35 uint16 keyid
36 []byte signature
37 uint8 numKeys
38 // for each key:
39 uint16 key length:
40 []byte DNSKEY RDATA
41
42 |entryKey| indexes the array of DNSKEYs and MUST be less than |numKeys|. The indexed DNSKEY MUST be a key that the verifier trusts, either because it's the long-term root key, or because of a previously presented DS signature.
43
44 If only a trusted key is needed within this zone, then the signature length MAY be zero. In which case, |entryKey| MUST be 0 and |numKeys| MUST be 1.
45
46 After processing this data, the verifier trusts one or more keys for this zone.
47
48 When within a zone, the verifier reads the following structure:
49
50 dnsName name
51 uint16 RRtype
52
53 |name| is in DNS format (a series of 8-bit, length prefixed strings). No DNS name compression is permitted.
54
55 |name| must be closer to the current target domain than the current zone. Here, 'closer' is defined as a greater number of matching labels when comparing right to left.
56
57 |RRtype| may be either DS, CERT or CNAME:
58
59 DS: this indicates a zone transition to a new zone named |name|. The verifier reads the following structure:
60 uint16 signature length:
61 ... (see above for the signature structure)
62 uint8 num_ds
63 // for each DS:
64 uint8 digest_type
65 uint16 length
66 []byte DS DATA
67
68 The verifier is now entering the named zone. It reads ahead and extracts the entry key from the zone entry data and synthisises a DS record for the given digest type and verifies the signature. It then enters the next zone.
69
70
71 CERT: |name| MUST match the target domain. The verifier reads the following structure:
72 uint16 signature length:
73 ... (see above for the signature structure)
74 []byte CERT RDATA
75
76 (The format of the CERT RDATA isn't specified here, but the verifier must be able to extract a public key fingerprint in order to validate the original certificate.)
77
78 This terminates the verification. There MUST NOT be any more data in the chain.
79
80
81 CNAME: |name| MUST match the target domain. The verifier reads the following structure:
82 uint16 signature length:
83 ... (see above for the signature structure)
84 []byte CNAME RDATA
85
86 This replaces the target domain with a new domain. The new domain is the target of the CNAME with _ssl prepended. The verifier is now in the zone that is the greatest common ancestor of the old and new target domains. (For example, when switching from _ssl.www.example.com to _ssl.www.example2.com, the verifier is now in com.)
87
88
89 Example for www.google.com:
90
91 The target domain is www.google.com.
92
93 The verifier enters ., it already trusts the long-term root key and both root keys are presented in order to extend the trust to the smaller root key.
94
95 A DS signature is presented for .com. The verifier is now entering .com.
96
97 All four .com keys are presented. The verifier is now in .com.
98
99 A DS signature is presented for google.com. The verifier is now entering google.com
100
101 As google.com contains only a single DNSKEY, it is included without a signature. The verifier is now in google.com.
102
103 A CNAME is presented for www.google.com pointing to www.l.google.com. The target domain is now www.l.google.com. The verifier is now in google.com.
104
105 A DS signature is presented for l.google.com. The verifier is now entering l.google.com.
106
107 As l.google.com contains only a single DNSKEY, it is included without a signature. The verifier is now in l.google.com.
108
109 A CERT record is presented for www.l.google.com. The verification is complete.
110 */
111
112 namespace {
113
114 // This is the 2048-bit DNS root key: http://www.iana.org/dnssec
115 // 19036 8 2 49AAC11D7B6F6446702E54A1607371607A1A41855200FD2CE1CDDE32F24E8FB5
116 const unsigned char kRootKey[] = {
117 0x01, 0x01, 0x03, 0x08, 0x03, 0x01, 0x00, 0x01, 0xa8, 0x00, 0x20, 0xa9, 0x55,
118 0x66, 0xba, 0x42, 0xe8, 0x86, 0xbb, 0x80, 0x4c, 0xda, 0x84, 0xe4, 0x7e, 0xf5,
119 0x6d, 0xbd, 0x7a, 0xec, 0x61, 0x26, 0x15, 0x55, 0x2c, 0xec, 0x90, 0x6d, 0x21,
120 0x16, 0xd0, 0xef, 0x20, 0x70, 0x28, 0xc5, 0x15, 0x54, 0x14, 0x4d, 0xfe, 0xaf,
121 0xe7, 0xc7, 0xcb, 0x8f, 0x00, 0x5d, 0xd1, 0x82, 0x34, 0x13, 0x3a, 0xc0, 0x71,
122 0x0a, 0x81, 0x18, 0x2c, 0xe1, 0xfd, 0x14, 0xad, 0x22, 0x83, 0xbc, 0x83, 0x43,
123 0x5f, 0x9d, 0xf2, 0xf6, 0x31, 0x32, 0x51, 0x93, 0x1a, 0x17, 0x6d, 0xf0, 0xda,
124 0x51, 0xe5, 0x4f, 0x42, 0xe6, 0x04, 0x86, 0x0d, 0xfb, 0x35, 0x95, 0x80, 0x25,
125 0x0f, 0x55, 0x9c, 0xc5, 0x43, 0xc4, 0xff, 0xd5, 0x1c, 0xbe, 0x3d, 0xe8, 0xcf,
126 0xd0, 0x67, 0x19, 0x23, 0x7f, 0x9f, 0xc4, 0x7e, 0xe7, 0x29, 0xda, 0x06, 0x83,
127 0x5f, 0xa4, 0x52, 0xe8, 0x25, 0xe9, 0xa1, 0x8e, 0xbc, 0x2e, 0xcb, 0xcf, 0x56,
128 0x34, 0x74, 0x65, 0x2c, 0x33, 0xcf, 0x56, 0xa9, 0x03, 0x3b, 0xcd, 0xf5, 0xd9,
129 0x73, 0x12, 0x17, 0x97, 0xec, 0x80, 0x89, 0x04, 0x1b, 0x6e, 0x03, 0xa1, 0xb7,
130 0x2d, 0x0a, 0x73, 0x5b, 0x98, 0x4e, 0x03, 0x68, 0x73, 0x09, 0x33, 0x23, 0x24,
131 0xf2, 0x7c, 0x2d, 0xba, 0x85, 0xe9, 0xdb, 0x15, 0xe8, 0x3a, 0x01, 0x43, 0x38,
132 0x2e, 0x97, 0x4b, 0x06, 0x21, 0xc1, 0x8e, 0x62, 0x5e, 0xce, 0xc9, 0x07, 0x57,
133 0x7d, 0x9e, 0x7b, 0xad, 0xe9, 0x52, 0x41, 0xa8, 0x1e, 0xbb, 0xe8, 0xa9, 0x01,
134 0xd4, 0xd3, 0x27, 0x6e, 0x40, 0xb1, 0x14, 0xc0, 0xa2, 0xe6, 0xfc, 0x38, 0xd1,
135 0x9c, 0x2e, 0x6a, 0xab, 0x02, 0x64, 0x4b, 0x28, 0x13, 0xf5, 0x75, 0xfc, 0x21,
136 0x60, 0x1e, 0x0d, 0xee, 0x49, 0xcd, 0x9e, 0xe9, 0x6a, 0x43, 0x10, 0x3e, 0x52,
137 0x4d, 0x62, 0x87, 0x3d,
138 };
139
140 // kRootKeyID is the key id for kRootKey
141 const uint16 kRootKeyID = 19036;
142
143 // CountLabels returns the number of DNS labels in |a|, which must be in DNS,
144 // length-prefixed form.
CountLabels(base::StringPiece a)145 unsigned CountLabels(base::StringPiece a) {
146 for (unsigned c = 0;; c++) {
147 if (!a.size())
148 return c;
149 uint8 label_len = a.data()[0];
150 a.remove_prefix(1);
151 DCHECK_GE(a.size(), label_len);
152 a.remove_prefix(label_len);
153 }
154 }
155
156 // RemoveLeadingLabel removes the first label from |a|, which must be in DNS,
157 // length-prefixed form.
RemoveLeadingLabel(base::StringPiece * a)158 void RemoveLeadingLabel(base::StringPiece* a) {
159 if (!a->size())
160 return;
161 uint8 label_len = a->data()[0];
162 a->remove_prefix(1);
163 a->remove_prefix(label_len);
164 }
165
166 } // namespace
167
168 namespace net {
169
170 struct DNSSECChainVerifier::Zone {
171 base::StringPiece name;
172 // The number of consecutive labels which |name| shares with |target_|,
173 // counting right-to-left from the root.
174 unsigned matching_labels;
175 DNSSECKeySet trusted_keys;
176 Zone* prev;
177 };
178
DNSSECChainVerifier(const std::string & target,const base::StringPiece & chain)179 DNSSECChainVerifier::DNSSECChainVerifier(const std::string& target,
180 const base::StringPiece& chain)
181 : current_zone_(NULL),
182 target_(target),
183 chain_(chain),
184 ignore_timestamps_(false),
185 valid_(false),
186 already_entered_zone_(false),
187 rrtype_(0) {
188 }
189
~DNSSECChainVerifier()190 DNSSECChainVerifier::~DNSSECChainVerifier() {
191 for (std::vector<void*>::iterator
192 i = scratch_pool_.begin(); i != scratch_pool_.end(); i++) {
193 free(*i);
194 }
195
196 Zone* next;
197 for (Zone* cur = current_zone_; cur; cur = next) {
198 next = cur->prev;
199 delete cur;
200 }
201 }
202
IgnoreTimestamps()203 void DNSSECChainVerifier::IgnoreTimestamps() {
204 ignore_timestamps_ = true;
205 }
206
Verify()207 DNSSECChainVerifier::Error DNSSECChainVerifier::Verify() {
208 Error err;
209
210 err = EnterRoot();
211 if (err != OK)
212 return err;
213
214 for (;;) {
215 base::StringPiece next_name;
216 err = LeaveZone(&next_name);
217 if (err != OK)
218 return err;
219 if (valid_) {
220 if (!chain_.empty())
221 return BAD_DATA; // no trailing data allowed.
222 break;
223 }
224
225 if (already_entered_zone_) {
226 already_entered_zone_ = false;
227 } else {
228 err = EnterZone(next_name);
229 if (err != OK)
230 return err;
231 }
232 }
233
234 return OK;
235 }
236
rrtype() const237 uint16 DNSSECChainVerifier::rrtype() const {
238 DCHECK(valid_);
239 return rrtype_;
240 }
241
rrdatas() const242 const std::vector<base::StringPiece>& DNSSECChainVerifier::rrdatas() const {
243 DCHECK(valid_);
244 return rrdatas_;
245 }
246
247 // static
248 std::map<std::string, std::string>
ParseTLSTXTRecord(base::StringPiece rrdata)249 DNSSECChainVerifier::ParseTLSTXTRecord(base::StringPiece rrdata) {
250 std::map<std::string, std::string> ret;
251
252 if (rrdata.empty())
253 return ret;
254
255 std::string txt;
256 txt.reserve(rrdata.size());
257
258 // TXT records are a series of 8-bit length prefixed substrings that we
259 // concatenate into |txt|
260 while (!rrdata.empty()) {
261 unsigned len = rrdata[0];
262 if (len == 0 || len + 1 > rrdata.size())
263 return ret;
264 txt.append(rrdata.data() + 1, len);
265 rrdata.remove_prefix(len + 1);
266 }
267
268 // We append a space to |txt| to make the parsing code, below, cleaner.
269 txt.append(" ");
270
271 // RECORD = KV (' '+ KV)*
272 // KV = KEY '=' VALUE
273 // KEY = [a-zA-Z0-9]+
274 // VALUE = [^ \0]*
275
276 enum State {
277 STATE_KEY,
278 STATE_VALUE,
279 STATE_SPACE,
280 };
281
282 State state = STATE_KEY;
283
284 std::map<std::string, std::string> m;
285
286 unsigned start = 0;
287 std::string key;
288
289 for (unsigned i = 0; i < txt.size(); i++) {
290 char c = txt[i];
291 if (c == 0)
292 return ret; // NUL values are never allowed.
293
294 switch (state) {
295 case STATE_KEY:
296 if (c == '=') {
297 if (i == start)
298 return ret; // zero length keys are not allowed.
299 key = txt.substr(start, i - start);
300 start = i + 1;
301 state = STATE_VALUE;
302 continue;
303 }
304 if (!IsAsciiAlpha(c) && !IsAsciiDigit(c))
305 return ret; // invalid key value
306 break;
307 case STATE_VALUE:
308 if (c == ' ') {
309 if (m.find(key) == m.end())
310 m.insert(make_pair(key, txt.substr(start, i - start)));
311 state = STATE_SPACE;
312 continue;
313 }
314 break;
315 case STATE_SPACE:
316 if (c != ' ') {
317 start = i;
318 i--;
319 state = STATE_KEY;
320 continue;
321 }
322 break;
323 default:
324 NOTREACHED();
325 return ret;
326 }
327 }
328
329 if (state != STATE_SPACE)
330 return ret;
331
332 ret.swap(m);
333 return ret;
334 }
335
336 // MatchingLabels returns the number of labels which |a| and |b| share,
337 // counting right-to-left from the root. |a| and |b| must be DNS,
338 // length-prefixed names. All names match at the root label, so this always
339 // returns a value >= 1.
340
341 // static
MatchingLabels(base::StringPiece a,base::StringPiece b)342 unsigned DNSSECChainVerifier::MatchingLabels(base::StringPiece a,
343 base::StringPiece b) {
344 unsigned c = 0;
345 unsigned a_labels = CountLabels(a);
346 unsigned b_labels = CountLabels(b);
347
348 while (a_labels > b_labels) {
349 RemoveLeadingLabel(&a);
350 a_labels--;
351 }
352 while (b_labels > a_labels) {
353 RemoveLeadingLabel(&b);
354 b_labels--;
355 }
356
357 for (;;) {
358 if (!a.size()) {
359 if (!b.size())
360 return c;
361 return 0;
362 }
363 if (!b.size())
364 return 0;
365 uint8 a_length = a.data()[0];
366 a.remove_prefix(1);
367 uint8 b_length = b.data()[0];
368 b.remove_prefix(1);
369 DCHECK_GE(a.size(), a_length);
370 DCHECK_GE(b.size(), b_length);
371
372 if (a_length == b_length && memcmp(a.data(), b.data(), a_length) == 0) {
373 c++;
374 } else {
375 c = 0;
376 }
377
378 a.remove_prefix(a_length);
379 b.remove_prefix(b_length);
380 }
381 }
382
383 // U8 reads, and removes, a single byte from |chain_|
U8(uint8 * v)384 bool DNSSECChainVerifier::U8(uint8* v) {
385 if (chain_.size() < 1)
386 return false;
387 *v = chain_[0];
388 chain_.remove_prefix(1);
389 return true;
390 }
391
392 // U16 reads, and removes, a big-endian uint16 from |chain_|
U16(uint16 * v)393 bool DNSSECChainVerifier::U16(uint16* v) {
394 if (chain_.size() < 2)
395 return false;
396 const uint8* data = reinterpret_cast<const uint8*>(chain_.data());
397 *v = static_cast<uint16>(data[0]) << 8 |
398 static_cast<uint16>(data[1]);
399 chain_.remove_prefix(2);
400 return true;
401 }
402
403 // VariableLength16 reads, and removes, a big-endian, uint16, length-prefixed
404 // chunk from |chain_|
VariableLength16(base::StringPiece * v)405 bool DNSSECChainVerifier::VariableLength16(base::StringPiece* v) {
406 uint16 length;
407 if (!U16(&length))
408 return false;
409 if (chain_.size() < length)
410 return false;
411 *v = chain_.substr(0, length);
412 chain_.remove_prefix(length);
413 return true;
414 }
415
416 // ReadName reads, and removes, an 8-bit length prefixed DNS name from |chain_|
ReadName(base::StringPiece * v)417 bool DNSSECChainVerifier::ReadName(base::StringPiece* v) {
418 base::StringPiece saved = chain_;
419 unsigned length = 0;
420 static const uint8 kMaxDNSLabelLen = 63;
421
422 for (;;) {
423 if (chain_.size() < 1)
424 return false;
425 uint8 label_len = chain_.data()[0];
426 chain_.remove_prefix(1);
427 if (label_len > kMaxDNSLabelLen)
428 return false;
429 length += 1 + label_len;
430
431 if (label_len == 0)
432 break;
433
434 if (chain_.size() < label_len)
435 return false;
436 chain_.remove_prefix(label_len);
437 }
438
439 *v = base::StringPiece(saved.data(), length);
440 return true;
441 }
442
443 // ReadAheadEntryKey returns the entry key when |chain_| is positioned at the
444 // start of a zone.
ReadAheadEntryKey(base::StringPiece * v)445 bool DNSSECChainVerifier::ReadAheadEntryKey(base::StringPiece* v) {
446 base::StringPiece saved = chain_;
447
448 uint8 entry_key;
449 base::StringPiece sig;
450 if (!U8(&entry_key) ||
451 !VariableLength16(&sig)) {
452 return false;
453 }
454
455 if (!ReadAheadKey(v, entry_key))
456 return false;
457 chain_ = saved;
458 return true;
459 }
460
461 // ReadAheadKey returns the entry key when |chain_| is positioned at the start
462 // of a list of keys.
ReadAheadKey(base::StringPiece * v,uint8 entry_key)463 bool DNSSECChainVerifier::ReadAheadKey(base::StringPiece* v, uint8 entry_key) {
464 base::StringPiece saved = chain_;
465
466 uint8 num_keys;
467 if (!U8(&num_keys))
468 return false;
469
470 for (unsigned i = 0; i < num_keys; i++) {
471 if (!VariableLength16(v))
472 return false;
473 if (i == entry_key) {
474 chain_ = saved;
475 return true;
476 }
477 }
478
479 return false;
480 }
481
ReadDNSKEYs(std::vector<base::StringPiece> * out,bool is_root)482 bool DNSSECChainVerifier::ReadDNSKEYs(std::vector<base::StringPiece>* out,
483 bool is_root) {
484 uint8 num_keys;
485 if (!U8(&num_keys))
486 return false;
487
488 for (unsigned i = 0; i < num_keys; i++) {
489 base::StringPiece key;
490 if (!VariableLength16(&key))
491 return false;
492 if (key.empty()) {
493 if (!is_root)
494 return false;
495 key = base::StringPiece(reinterpret_cast<const char*>(kRootKey),
496 sizeof(kRootKey));
497 }
498
499 out->push_back(key);
500 }
501
502 return true;
503 }
504
505 // DigestKey calculates a DS digest as specified in
506 // http://tools.ietf.org/html/rfc4034#section-5.1.4
507 // name: the DNS form name of the key
508 // dnskey: the DNSKEY's RRDATA
509 // digest_type: see http://tools.ietf.org/html/rfc4034#appendix-A.2
510 // keyid: the key's id
511 // algorithm: see http://tools.ietf.org/html/rfc4034#appendix-A.1
DigestKey(base::StringPiece * out,const base::StringPiece & name,const base::StringPiece & dnskey,uint8 digest_type,uint16 keyid,uint8 algorithm)512 bool DNSSECChainVerifier::DigestKey(base::StringPiece* out,
513 const base::StringPiece& name,
514 const base::StringPiece& dnskey,
515 uint8 digest_type,
516 uint16 keyid,
517 uint8 algorithm) {
518 std::string temp;
519 uint8 temp2[crypto::SHA256_LENGTH];
520 const uint8* digest;
521 unsigned digest_len;
522
523 std::string input = name.as_string() + dnskey.as_string();
524
525 if (digest_type == kDNSSEC_SHA1) {
526 temp = base::SHA1HashString(input);
527 digest = reinterpret_cast<const uint8*>(temp.data());
528 digest_len = base::SHA1_LENGTH;
529 } else if (digest_type == kDNSSEC_SHA256) {
530 crypto::SHA256HashString(input, temp2, sizeof(temp2));
531 digest = temp2;
532 digest_len = sizeof(temp2);
533 } else {
534 return false;
535 }
536
537 uint8* output = static_cast<uint8*>(malloc(4 + digest_len));
538 scratch_pool_.push_back(output);
539 output[0] = static_cast<uint8>(keyid >> 8);
540 output[1] = static_cast<uint8>(keyid);
541 output[2] = algorithm;
542 output[3] = digest_type;
543 memcpy(output + 4, digest, digest_len);
544 *out = base::StringPiece(reinterpret_cast<char*>(output), 4 + digest_len);
545 return true;
546 }
547
548 // EnterRoot enters the root zone at the beginning of the chain. This is
549 // special because no DS record lead us here: we have to validate that the
550 // entry key is the DNS root key that we already know and trust. Additionally,
551 // for the root zone only, the keyid of the entry key is prepended to the data.
EnterRoot()552 DNSSECChainVerifier::Error DNSSECChainVerifier::EnterRoot() {
553 uint16 root_keyid;
554
555 if (!U16(&root_keyid))
556 return BAD_DATA;
557
558 if (root_keyid != kRootKeyID)
559 return UNKNOWN_ROOT_KEY;
560
561 base::StringPiece root_key;
562 if (!ReadAheadEntryKey(&root_key))
563 return BAD_DATA;
564
565 // If the root key is given then it must match the expected root key exactly.
566 if (root_key.size()) {
567 if (root_key.size() != sizeof(kRootKey) ||
568 memcmp(root_key.data(), kRootKey, sizeof(kRootKey))) {
569 return UNKNOWN_ROOT_KEY;
570 }
571 }
572
573 base::StringPiece root("", 1);
574 return EnterZone(root);
575 }
576
577 // EnterZone enters a new DNS zone. On entry it's assumed that the entry key
578 // has been validated.
EnterZone(const base::StringPiece & zone)579 DNSSECChainVerifier::Error DNSSECChainVerifier::EnterZone(
580 const base::StringPiece& zone) {
581 Zone* prev = current_zone_;
582 current_zone_ = new Zone;
583 current_zone_->prev = prev;
584 current_zone_->name = zone;
585 current_zone_->matching_labels = MatchingLabels(target_, zone);
586 if (ignore_timestamps_)
587 current_zone_->trusted_keys.IgnoreTimestamps();
588
589 uint8 entry_key;
590 base::StringPiece sig;
591 if (!U8(&entry_key) ||
592 !VariableLength16(&sig)) {
593 return BAD_DATA;
594 }
595
596 base::StringPiece key;
597 if (!ReadAheadKey(&key, entry_key))
598 return BAD_DATA;
599
600 if (zone.size() == 1 && key.empty()) {
601 // If a key is omitted in the root zone then it's the root key.
602 key = base::StringPiece(reinterpret_cast<const char*>(kRootKey),
603 sizeof(kRootKey));
604 }
605 if (!current_zone_->trusted_keys.AddKey(key))
606 return BAD_DATA;
607
608 std::vector<base::StringPiece> dnskeys;
609 if (!ReadDNSKEYs(&dnskeys, zone.size() == 1))
610 return BAD_DATA;
611
612 if (sig.empty()) {
613 // An omitted signature on the keys means that only the entry key is used.
614 if (dnskeys.size() > 1 || entry_key != 0)
615 return BAD_DATA;
616 return OK;
617 }
618
619 if (!current_zone_->trusted_keys.CheckSignature(
620 zone, zone, sig, kDNS_DNSKEY, dnskeys)) {
621 return BAD_SIGNATURE;
622 }
623
624 // Add all the keys as trusted.
625 for (unsigned i = 0; i < dnskeys.size(); i++) {
626 if (i == entry_key)
627 continue;
628 current_zone_->trusted_keys.AddKey(dnskeys[i]);
629 }
630
631 return OK;
632 }
633
634 // LeaveZone transitions out of the current zone, either by following DS
635 // records to validate the entry key of the next zone, or because the final
636 // resource records are given.
LeaveZone(base::StringPiece * next_name)637 DNSSECChainVerifier::Error DNSSECChainVerifier::LeaveZone(
638 base::StringPiece* next_name) {
639 base::StringPiece sig;
640 uint16 rrtype;
641 Error err;
642
643 if (!ReadName(next_name) ||
644 !U16(&rrtype) ||
645 !VariableLength16(&sig)) {
646 return BAD_DATA;
647 }
648
649 std::vector<base::StringPiece> rrdatas;
650
651 if (rrtype == kDNS_DS) {
652 err = ReadDSSet(&rrdatas, *next_name);
653 } else if (rrtype == kDNS_CERT || rrtype == kDNS_TXT) {
654 err = ReadGenericRRs(&rrdatas);
655 } else if (rrtype == kDNS_CNAME) {
656 err = ReadCNAME(&rrdatas);
657 } else {
658 return UNKNOWN_TERMINAL_RRTYPE;
659 }
660 if (err != OK)
661 return err;
662
663 if (!current_zone_->trusted_keys.CheckSignature(
664 *next_name, current_zone_->name, sig, rrtype, rrdatas)) {
665 return BAD_SIGNATURE;
666 }
667
668 if (rrtype == kDNS_DS) {
669 // If we are transitioning to another zone then the next zone must be
670 // 'closer' to the target than the current zone.
671 if (MatchingLabels(target_, *next_name) <= current_zone_->matching_labels)
672 return OFF_COURSE;
673 } else if (rrtype == kDNS_CERT || rrtype == kDNS_TXT) {
674 // If this is the final entry in the chain then the name must match target_
675 if (next_name->size() != target_.size() ||
676 memcmp(next_name->data(), target_.data(), target_.size())) {
677 return BAD_TARGET;
678 }
679 rrdatas_ = rrdatas;
680 valid_ = true;
681 rrtype_ = rrtype;
682 } else if (rrtype == kDNS_CNAME) {
683 // A CNAME must match the current target. Then we update the current target
684 // and unwind the chain to the closest common ancestor.
685 if (next_name->size() != target_.size() ||
686 memcmp(next_name->data(), target_.data(), target_.size())) {
687 return BAD_TARGET;
688 }
689 DCHECK_EQ(1u, rrdatas.size());
690 target_ = rrdatas[0].as_string();
691 // We unwind the zones until the current zone is a (non-strict) subset of
692 // the new target.
693 while (MatchingLabels(target_, current_zone_->name) <
694 CountLabels(current_zone_->name)) {
695 Zone* prev = current_zone_->prev;
696 delete current_zone_;
697 current_zone_ = prev;
698 if (!current_zone_) {
699 NOTREACHED();
700 return BAD_DATA;
701 }
702 }
703 already_entered_zone_ = true;
704 } else {
705 NOTREACHED();
706 return UNKNOWN_TERMINAL_RRTYPE;
707 }
708
709 return OK;
710 }
711
712 // ReadDSSet reads a set of DS records from the chain. DS records which are
713 // omitted are calculated from the entry key of the next zone.
ReadDSSet(std::vector<base::StringPiece> * rrdatas,const base::StringPiece & next_name)714 DNSSECChainVerifier::Error DNSSECChainVerifier::ReadDSSet(
715 std::vector<base::StringPiece>* rrdatas,
716 const base::StringPiece& next_name) {
717 uint8 num_ds;
718 if (!U8(&num_ds))
719 return BAD_DATA;
720 scoped_array<uint8> digest_types(new uint8[num_ds]);
721 // lookahead[i] is true iff the i'th DS record was empty and needs to be
722 // computed by hashing the next entry key.
723 scoped_array<bool> lookahead(new bool[num_ds]);
724 rrdatas->resize(num_ds);
725
726 for (unsigned i = 0; i < num_ds; i++) {
727 uint8 digest_type;
728 base::StringPiece digest;
729 if (!U8(&digest_type) ||
730 !VariableLength16(&digest)) {
731 return BAD_DATA;
732 }
733
734 digest_types[i] = digest_type;
735 lookahead[i] = digest.empty();
736 if (!digest.empty())
737 (*rrdatas)[i] = digest;
738 }
739
740 base::StringPiece next_entry_key;
741 if (!ReadAheadEntryKey(&next_entry_key))
742 return BAD_DATA;
743 if (next_entry_key.size() < 4)
744 return BAD_DATA;
745 uint16 keyid = DNSSECKeySet::DNSKEYToKeyID(next_entry_key);
746 uint8 algorithm = next_entry_key[3];
747
748 bool good = false;
749 for (unsigned i = 0; i < num_ds; i++) {
750 base::StringPiece digest;
751 bool have_digest = false;
752 if (DigestKey(&digest, next_name, next_entry_key, digest_types[i],
753 keyid, algorithm)) {
754 have_digest = true;
755 }
756
757 if (lookahead[i]) {
758 // If we needed to fill in one of the DS entries, but we can't calculate
759 // that type of digest, then we can't continue.
760 if (!have_digest)
761 return UNKNOWN_DIGEST;
762 (*rrdatas)[i] = digest;
763 good = true;
764 } else {
765 const base::StringPiece& given_digest = (*rrdatas)[i];
766 if (have_digest &&
767 given_digest.size() == digest.size() &&
768 memcmp(given_digest.data(), digest.data(), digest.size()) == 0) {
769 good = true;
770 }
771 }
772 }
773
774 if (!good) {
775 // We didn't calculate or match any of the digests.
776 return NO_DS_LINK;
777 }
778
779 return OK;
780 }
781
ReadGenericRRs(std::vector<base::StringPiece> * rrdatas)782 DNSSECChainVerifier::Error DNSSECChainVerifier::ReadGenericRRs(
783 std::vector<base::StringPiece>* rrdatas) {
784 uint8 num_rrs;
785 if (!U8(&num_rrs))
786 return BAD_DATA;
787 rrdatas->resize(num_rrs);
788
789 for (unsigned i = 0; i < num_rrs; i++) {
790 base::StringPiece rrdata;
791 if (!VariableLength16(&rrdata))
792 return BAD_DATA;
793 (*rrdatas)[i] = rrdata;
794 }
795
796 return OK;
797 }
798
ReadCNAME(std::vector<base::StringPiece> * rrdatas)799 DNSSECChainVerifier::Error DNSSECChainVerifier::ReadCNAME(
800 std::vector<base::StringPiece>* rrdatas) {
801 base::StringPiece name;
802 if (!ReadName(&name))
803 return BAD_DATA;
804
805 rrdatas->resize(1);
806 (*rrdatas)[0] = name;
807 return OK;
808 }
809
810 } // namespace net
811