OLD | NEW |
1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "net/base/x509_certificate.h" | 5 #include "net/base/x509_certificate.h" |
6 | 6 |
7 #include <map> | 7 #include <map> |
| 8 #include <string> |
| 9 #include <vector> |
8 | 10 |
9 #include "base/lazy_instance.h" | 11 #include "base/lazy_instance.h" |
10 #include "base/logging.h" | 12 #include "base/logging.h" |
11 #include "base/metrics/histogram.h" | 13 #include "base/metrics/histogram.h" |
12 #include "base/singleton.h" | 14 #include "base/singleton.h" |
13 #include "base/string_piece.h" | 15 #include "base/string_piece.h" |
| 16 #include "base/string_util.h" |
14 #include "base/time.h" | 17 #include "base/time.h" |
15 #include "net/base/pem_tokenizer.h" | 18 #include "net/base/pem_tokenizer.h" |
16 | 19 |
17 namespace net { | 20 namespace net { |
18 | 21 |
19 namespace { | 22 namespace { |
20 | 23 |
21 // Returns true if this cert fingerprint is the null (all zero) fingerprint. | 24 // Returns true if this cert fingerprint is the null (all zero) fingerprint. |
22 // We use this as a bogus fingerprint value. | 25 // We use this as a bogus fingerprint value. |
23 bool IsNullFingerprint(const SHA1Fingerprint& fingerprint) { | 26 bool IsNullFingerprint(const SHA1Fingerprint& fingerprint) { |
(...skipping 293 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
317 } | 320 } |
318 | 321 |
319 bool X509Certificate::HasIntermediateCertificates(const OSCertHandles& certs) { | 322 bool X509Certificate::HasIntermediateCertificates(const OSCertHandles& certs) { |
320 for (size_t i = 0; i < certs.size(); ++i) { | 323 for (size_t i = 0; i < certs.size(); ++i) { |
321 if (!HasIntermediateCertificate(certs[i])) | 324 if (!HasIntermediateCertificate(certs[i])) |
322 return false; | 325 return false; |
323 } | 326 } |
324 return true; | 327 return true; |
325 } | 328 } |
326 | 329 |
| 330 // static |
| 331 bool X509Certificate::VerifyHostname( |
| 332 const std::string& hostname, |
| 333 const std::vector<std::string>& cert_names) { |
| 334 DCHECK(!hostname.empty()); |
| 335 |
| 336 // Simple host name validation. A valid domain name must only contain |
| 337 // alpha, digits, hyphens, and dots. An IP address may have digits and dots, |
| 338 // and also square braces and colons for IPv6 addresses. |
| 339 std::string reference_name; |
| 340 reference_name.reserve(hostname.length()); |
| 341 |
| 342 bool found_alpha = false; |
| 343 bool found_ip6_chars = false; |
| 344 bool found_hyphen = false; |
| 345 int dot_count = 0; |
| 346 |
| 347 size_t first_dot_index = std::string::npos; |
| 348 for (std::string::const_iterator it = hostname.begin(); |
| 349 it != hostname.end(); ++it) { |
| 350 char c = *it; |
| 351 if (IsAsciiAlpha(c)) { |
| 352 found_alpha = true; |
| 353 c = base::ToLowerASCII(c); |
| 354 } else if (c == '.') { |
| 355 ++dot_count; |
| 356 if (first_dot_index == std::string::npos) |
| 357 first_dot_index = reference_name.length(); |
| 358 } else if (c == ':') { |
| 359 found_ip6_chars = true; |
| 360 } else if (c == '-') { |
| 361 found_hyphen = true; |
| 362 } else if (!IsAsciiDigit(c)) { |
| 363 LOG(WARNING) << "Invalid char " << c << " in hostname " << hostname; |
| 364 return false; |
| 365 } |
| 366 reference_name.push_back(c); |
| 367 } |
| 368 DCHECK(!reference_name.empty()); |
| 369 |
| 370 if (found_ip6_chars || !found_alpha) { |
| 371 // For now we just do simple localhost IP address support, primarily as |
| 372 // it's needed by the test server. TODO(joth): Replace this with full IP |
| 373 // address support. See http://crbug.com/62973 |
| 374 if (hostname == "127.0.0.1") { |
| 375 for (size_t index = 0; index < cert_names.size(); ++index) { |
| 376 if (cert_names[index] == hostname) { |
| 377 DVLOG(1) << "Allowing localhost IP certificate: " << hostname; |
| 378 return true; |
| 379 } |
| 380 } |
| 381 } |
| 382 NOTIMPLEMENTED() << hostname; // See comment above. |
| 383 return false; |
| 384 } |
| 385 |
| 386 // |wildcard_domain| is the remainder of |host| after the leading host |
| 387 // component is stripped off, but includes the leading dot e.g. |
| 388 // "www.f.com" -> ".f.com". |
| 389 // If there is no meaningful domain part to |host| (e.g. it is an IP address |
| 390 // or contains no dots) then |wildcard_domain| will be empty. |
| 391 // We required at least 3 components (i.e. 2 dots) as a basic protection |
| 392 // against too-broad wild-carding. |
| 393 base::StringPiece wildcard_domain; |
| 394 if (found_alpha && !found_ip6_chars && dot_count >= 2) { |
| 395 DCHECK(first_dot_index != std::string::npos); |
| 396 wildcard_domain = reference_name; |
| 397 wildcard_domain.remove_prefix(first_dot_index); |
| 398 DCHECK(wildcard_domain.starts_with(".")); |
| 399 } |
| 400 |
| 401 for (std::vector<std::string>::const_iterator it = cert_names.begin(); |
| 402 it != cert_names.end(); ++it) { |
| 403 // Catch badly corrupt cert names up front. |
| 404 if (it->empty() || it->find('\0') != std::string::npos) { |
| 405 LOG(WARNING) << "Bad name in cert: " << *it; |
| 406 continue; |
| 407 } |
| 408 const std::string cert_name_string(StringToLowerASCII(*it)); |
| 409 base::StringPiece cert_match(cert_name_string); |
| 410 |
| 411 // Remove trailing dot, if any. |
| 412 if (cert_match.ends_with(".")) |
| 413 cert_match.remove_suffix(1); |
| 414 |
| 415 // The hostname must be at least as long as the cert name it is matching, |
| 416 // as we require the wildcard (if present) to match at least one character. |
| 417 if (cert_match.length() > reference_name.length()) |
| 418 continue; |
| 419 |
| 420 if (cert_match == reference_name) |
| 421 return true; |
| 422 |
| 423 // Next see if this cert name starts with a wildcard, so long as the |
| 424 // hostname we're matching against has a valid 'domain' part to match. |
| 425 // Note the "-10" version of draft-saintandre-tls-server-id-check allows |
| 426 // the wildcard to appear anywhere in the leftmost label, rather than |
| 427 // requiring it to be the only character. See also http://crbug.com/60719 |
| 428 if (wildcard_domain.empty() || !cert_match.starts_with("*")) |
| 429 continue; |
| 430 |
| 431 // Erase the * but not the . from the domain, as we need to include the dot |
| 432 // in the comparison. |
| 433 cert_match.remove_prefix(1); |
| 434 |
| 435 // Do character by character comparison on the remainder to see |
| 436 // if we have a wildcard match. This intentionally does no special handling |
| 437 // for any other wildcard characters in |domain|; alternatively it could |
| 438 // detect these and skip those candidate cert names. |
| 439 if (cert_match == wildcard_domain) |
| 440 return true; |
| 441 } |
| 442 DVLOG(1) << "Could not find any match for " << hostname |
| 443 << " (canonicalized as " << reference_name |
| 444 << ") in cert names " << JoinString(cert_names, '|'); |
| 445 return false; |
| 446 } |
| 447 |
| 448 #if !defined(USE_NSS) |
| 449 bool X509Certificate::VerifyNameMatch(const std::string& hostname) const { |
| 450 std::vector<std::string> dns_names; |
| 451 GetDNSNames(&dns_names); |
| 452 return VerifyHostname(hostname, dns_names); |
| 453 } |
| 454 #endif |
| 455 |
327 X509Certificate::X509Certificate(OSCertHandle cert_handle, | 456 X509Certificate::X509Certificate(OSCertHandle cert_handle, |
328 Source source, | 457 Source source, |
329 const OSCertHandles& intermediates) | 458 const OSCertHandles& intermediates) |
330 : cert_handle_(DupOSCertHandle(cert_handle)), | 459 : cert_handle_(DupOSCertHandle(cert_handle)), |
331 source_(source) { | 460 source_(source) { |
332 // Copy/retain the intermediate cert handles. | 461 // Copy/retain the intermediate cert handles. |
333 for (size_t i = 0; i < intermediates.size(); ++i) | 462 for (size_t i = 0; i < intermediates.size(); ++i) |
334 intermediate_ca_certs_.push_back(DupOSCertHandle(intermediates[i])); | 463 intermediate_ca_certs_.push_back(DupOSCertHandle(intermediates[i])); |
335 // Platform-specific initialization. | 464 // Platform-specific initialization. |
336 Initialize(); | 465 Initialize(); |
337 } | 466 } |
338 | 467 |
339 X509Certificate::~X509Certificate() { | 468 X509Certificate::~X509Certificate() { |
340 // We might not be in the cache, but it is safe to remove ourselves anyway. | 469 // We might not be in the cache, but it is safe to remove ourselves anyway. |
341 g_x509_certificate_cache.Get().Remove(this); | 470 g_x509_certificate_cache.Get().Remove(this); |
342 if (cert_handle_) | 471 if (cert_handle_) |
343 FreeOSCertHandle(cert_handle_); | 472 FreeOSCertHandle(cert_handle_); |
344 for (size_t i = 0; i < intermediate_ca_certs_.size(); ++i) | 473 for (size_t i = 0; i < intermediate_ca_certs_.size(); ++i) |
345 FreeOSCertHandle(intermediate_ca_certs_[i]); | 474 FreeOSCertHandle(intermediate_ca_certs_[i]); |
346 } | 475 } |
347 | 476 |
348 } // namespace net | 477 } // namespace net |
OLD | NEW |