Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(96)

Side by Side Diff: net/base/x509_certificate.cc

Issue 6612013: Add X509Certificate::VerifyCertName(string) API. This will be used... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 9 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698