Chromium Code Reviews| Index: net/http/http_security_headers.cc |
| diff --git a/net/http/http_security_headers.cc b/net/http/http_security_headers.cc |
| index ed7ceccbb73080a7e5dfa1627083a728c3a044fe..9e368accb7686d0b2b328f6b30f4fa7de5562aff 100644 |
| --- a/net/http/http_security_headers.cc |
| +++ b/net/http/http_security_headers.cc |
| @@ -16,6 +16,8 @@ namespace net { |
| namespace { |
| +enum MaxAgeParsing { REQUIRE_MAX_AGE, DO_NOT_REQUIRE_MAX_AGE }; |
| + |
| static_assert(kMaxHSTSAgeSecs <= kuint32max, "kMaxHSTSAgeSecs too large"); |
| // MaxAgeToInt converts a string representation of a "whole number" of |
| @@ -118,6 +120,90 @@ bool ParseAndAppendPin(std::string::const_iterator begin, |
| return true; |
| } |
| +bool ParseHPKPHeaderImpl(const std::string& value, |
| + MaxAgeParsing max_age_status, |
| + base::TimeDelta* max_age, |
| + bool* include_subdomains, |
| + HashValueVector* hashes, |
| + GURL* report_uri) { |
| + bool parsed_max_age = false; |
| + bool include_subdomains_candidate = false; |
| + uint32 max_age_candidate = 0; |
| + GURL parsed_report_uri; |
| + HashValueVector pins; |
| + bool require_max_age = max_age_status == REQUIRE_MAX_AGE; |
| + |
| + HttpUtil::NameValuePairsIterator name_value_pairs( |
| + value.begin(), value.end(), ';', |
| + HttpUtil::NameValuePairsIterator::VALUES_OPTIONAL); |
| + |
| + while (name_value_pairs.GetNext()) { |
| + if (base::LowerCaseEqualsASCII( |
| + base::StringPiece(name_value_pairs.name_begin(), |
| + name_value_pairs.name_end()), |
| + "max-age")) { |
| + if (!MaxAgeToInt(name_value_pairs.value_begin(), |
| + name_value_pairs.value_end(), &max_age_candidate)) { |
| + return false; |
| + } |
| + parsed_max_age = true; |
| + } else if (base::LowerCaseEqualsASCII( |
| + base::StringPiece(name_value_pairs.name_begin(), |
| + name_value_pairs.name_end()), |
| + "pin-sha1")) { |
| + // Pins are always quoted. |
| + if (!name_value_pairs.value_is_quoted() || |
| + !ParseAndAppendPin(name_value_pairs.value_begin(), |
| + name_value_pairs.value_end(), HASH_VALUE_SHA1, |
| + &pins)) { |
| + return false; |
| + } |
| + } else if (base::LowerCaseEqualsASCII( |
| + base::StringPiece(name_value_pairs.name_begin(), |
| + name_value_pairs.name_end()), |
| + "pin-sha256")) { |
| + // Pins are always quoted. |
| + if (!name_value_pairs.value_is_quoted() || |
| + !ParseAndAppendPin(name_value_pairs.value_begin(), |
| + name_value_pairs.value_end(), HASH_VALUE_SHA256, |
| + &pins)) { |
| + return false; |
| + } |
| + } else if (base::LowerCaseEqualsASCII( |
| + base::StringPiece(name_value_pairs.name_begin(), |
| + name_value_pairs.name_end()), |
| + "includesubdomains")) { |
| + include_subdomains_candidate = true; |
| + } else if (base::LowerCaseEqualsASCII( |
| + base::StringPiece(name_value_pairs.name_begin(), |
| + name_value_pairs.name_end()), |
| + "report-uri")) { |
| + // report-uris are always quoted. |
| + if (!name_value_pairs.value_is_quoted()) |
| + return false; |
| + |
| + parsed_report_uri = GURL(name_value_pairs.value()); |
| + if (parsed_report_uri.is_empty() || !parsed_report_uri.is_valid()) |
| + return false; |
| + } else { |
| + // Silently ignore unknown directives for forward compatibility. |
| + } |
| + } |
| + |
| + if (!name_value_pairs.valid()) |
| + return false; |
| + |
| + if (!parsed_max_age && require_max_age) |
| + return false; |
| + |
| + *max_age = base::TimeDelta::FromSeconds(max_age_candidate); |
| + *include_subdomains = include_subdomains_candidate; |
| + hashes->swap(pins); |
| + *report_uri = parsed_report_uri; |
| + |
| + return true; |
| +} |
| + |
| } // namespace |
| // Parse the Strict-Transport-Security header, as currently defined in |
| @@ -252,7 +338,7 @@ bool ParseHSTSHeader(const std::string& value, |
| } |
| } |
| -// "Public-Key-Pins[-Report-Only]" ":" |
| +// "Public-Key-Pins" ":" |
| // "max-age" "=" delta-seconds ";" |
| // "pin-" algo "=" base64 [ ";" ... ] |
| // [ ";" "includeSubdomains" ] |
| @@ -263,84 +349,42 @@ bool ParseHPKPHeader(const std::string& value, |
| bool* include_subdomains, |
| HashValueVector* hashes, |
| GURL* report_uri) { |
| - bool parsed_max_age = false; |
| - bool include_subdomains_candidate = false; |
| - uint32 max_age_candidate = 0; |
| - GURL parsed_report_uri; |
| - HashValueVector pins; |
| - |
| - HttpUtil::NameValuePairsIterator name_value_pairs( |
| - value.begin(), value.end(), ';', |
| - HttpUtil::NameValuePairsIterator::VALUES_OPTIONAL); |
| - |
| - while (name_value_pairs.GetNext()) { |
| - if (base::LowerCaseEqualsASCII( |
| - base::StringPiece(name_value_pairs.name_begin(), |
| - name_value_pairs.name_end()), |
| - "max-age")) { |
| - if (!MaxAgeToInt(name_value_pairs.value_begin(), |
| - name_value_pairs.value_end(), &max_age_candidate)) { |
| - return false; |
| - } |
| - parsed_max_age = true; |
| - } else if (base::LowerCaseEqualsASCII( |
| - base::StringPiece(name_value_pairs.name_begin(), |
| - name_value_pairs.name_end()), |
| - "pin-sha1")) { |
| - // Pins are always quoted. |
| - if (!name_value_pairs.value_is_quoted() || |
| - !ParseAndAppendPin(name_value_pairs.value_begin(), |
| - name_value_pairs.value_end(), HASH_VALUE_SHA1, |
| - &pins)) { |
| - return false; |
| - } |
| - } else if (base::LowerCaseEqualsASCII( |
| - base::StringPiece(name_value_pairs.name_begin(), |
| - name_value_pairs.name_end()), |
| - "pin-sha256")) { |
| - // Pins are always quoted. |
| - if (!name_value_pairs.value_is_quoted() || |
| - !ParseAndAppendPin(name_value_pairs.value_begin(), |
| - name_value_pairs.value_end(), HASH_VALUE_SHA256, |
| - &pins)) { |
| - return false; |
| - } |
| - } else if (base::LowerCaseEqualsASCII( |
| - base::StringPiece(name_value_pairs.name_begin(), |
| - name_value_pairs.name_end()), |
| - "includesubdomains")) { |
| - include_subdomains_candidate = true; |
| - } else if (base::LowerCaseEqualsASCII( |
| - base::StringPiece(name_value_pairs.name_begin(), |
| - name_value_pairs.name_end()), |
| - "report-uri")) { |
| - // report-uris are always quoted. |
| - if (!name_value_pairs.value_is_quoted()) |
| - return false; |
| - |
| - parsed_report_uri = GURL(name_value_pairs.value()); |
| - if (parsed_report_uri.is_empty() || !parsed_report_uri.is_valid()) |
| - return false; |
| - } else { |
| - // Silently ignore unknown directives for forward compatibility. |
| - } |
| - } |
| - |
| - if (!name_value_pairs.valid()) |
| - return false; |
| - |
| - if (!parsed_max_age) |
| + base::TimeDelta candidate_max_age; |
| + bool candidate_include_subdomains; |
| + HashValueVector candidate_hashes; |
| + GURL candidate_report_uri; |
|
davidben
2015/07/31 19:53:23
[Incidentally, this "candidate" silliness is why I
|
| + |
| + if (!ParseHPKPHeaderImpl(value, REQUIRE_MAX_AGE, &candidate_max_age, |
| + &candidate_include_subdomains, &candidate_hashes, |
| + &candidate_report_uri)) { |
| return false; |
| + } |
| - if (!IsPinListValid(pins, chain_hashes)) |
| + if (!IsPinListValid(candidate_hashes, chain_hashes)) |
| return false; |
| - *max_age = base::TimeDelta::FromSeconds(max_age_candidate); |
| - *include_subdomains = include_subdomains_candidate; |
| - hashes->swap(pins); |
| - *report_uri = parsed_report_uri; |
| - |
| + *max_age = candidate_max_age; |
| + *include_subdomains = candidate_include_subdomains; |
| + hashes->swap(candidate_hashes); |
| + *report_uri = candidate_report_uri; |
| return true; |
| } |
| +// "Public-Key-Pins-Report-Only" ":" |
| +// [ "max-age" "=" delta-seconds ";" ] |
| +// "pin-" algo "=" base64 [ ";" ... ] |
| +// [ ";" "includeSubdomains" ] |
| +// [ ";" "report-uri" "=" uri-reference ] |
| +bool ParseHPKPReportOnlyHeader(const std::string& value, |
| + bool* include_subdomains, |
| + HashValueVector* hashes, |
| + GURL* report_uri) { |
| + // max-age and includeSubdomains are irrelevant for Report-Only |
| + // headers. |
|
davidben
2015/07/31 19:53:23
and includeSubdomains are -> is?
(Hrm. Looks like
estark
2015/07/31 20:29:47
Yeah, just fixed it here too.
|
| + base::TimeDelta unused_max_age; |
| + |
| + return ParseHPKPHeaderImpl(value, DO_NOT_REQUIRE_MAX_AGE, &unused_max_age, |
| + include_subdomains, hashes, report_uri); |
| +} |
| + |
| } // namespace net |