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 d95e5878d7c4ce10e2e393d875fb254257d5b6de..85768138680ebef707ee723516fce5ac05c61775 100644 |
| --- a/net/http/http_security_headers.cc |
| +++ b/net/http/http_security_headers.cc |
| @@ -5,10 +5,12 @@ |
| #include "base/base64.h" |
| #include "base/basictypes.h" |
| #include "base/strings/string_number_conversions.h" |
| +#include "base/strings/string_piece.h" |
| #include "base/strings/string_tokenizer.h" |
| #include "base/strings/string_util.h" |
| #include "net/http/http_security_headers.h" |
| #include "net/http/http_util.h" |
| +#include "url/gurl.h" |
| namespace net { |
| @@ -23,7 +25,10 @@ static_assert(kMaxHSTSAgeSecs <= kuint32max, "kMaxHSTSAgeSecs too large"); |
| bool MaxAgeToInt(std::string::const_iterator begin, |
| std::string::const_iterator end, |
| uint32* result) { |
| - const std::string s(begin, end); |
| + const base::StringPiece s(begin, end); |
| + if (s.empty()) |
| + return false; |
| + |
| int64 i = 0; |
| // Return false on any StringToInt64 parse errors *except* for |
| @@ -92,42 +97,16 @@ bool IsPinListValid(const HashValueVector& pins, |
| HashesIntersect(pins, from_cert_chain); |
| } |
| -std::string Strip(const std::string& source) { |
| - if (source.empty()) |
| - return source; |
| - |
| - std::string::const_iterator start = source.begin(); |
| - std::string::const_iterator end = source.end(); |
| - HttpUtil::TrimLWS(&start, &end); |
| - return std::string(start, end); |
| -} |
| - |
| -typedef std::pair<std::string, std::string> StringPair; |
| - |
| -StringPair Split(const std::string& source, char delimiter) { |
| - StringPair pair; |
| - size_t point = source.find(delimiter); |
| - |
| - pair.first = source.substr(0, point); |
| - if (std::string::npos != point) |
| - pair.second = source.substr(point + 1); |
| - |
| - return pair; |
| -} |
| - |
| -bool ParseAndAppendPin(const std::string& value, |
| +bool ParseAndAppendPin(std::string::const_iterator begin, |
| + std::string::const_iterator end, |
| HashValueTag tag, |
| HashValueVector* hashes) { |
| - // Pins are always quoted. |
| - if (value.empty() || !HttpUtil::IsQuote(value[0])) |
| + const base::StringPiece value(begin, end); |
| + if (value.empty()) |
| return false; |
| - std::string unquoted = HttpUtil::Unquote(value); |
| - if (unquoted.empty()) |
| - return false; |
| - |
| std::string decoded; |
| - if (!base::Base64Decode(unquoted, &decoded)) |
| + if (!base::Base64Decode(value, &decoded)) |
| return false; |
| HashValue hash(tag); |
| @@ -273,51 +252,72 @@ bool ParseHSTSHeader(const std::string& value, |
| } |
| } |
| -// "Public-Key-Pins" ":" |
| +// "Public-Key-Pins[-Report-Only]" ":" |
| // "max-age" "=" delta-seconds ";" |
| // "pin-" algo "=" base64 [ ";" ... ] |
| +// [ ";" "includeSubdomains" ] |
| +// [ ";" "report-uri" "=" uri-reference ] |
| bool ParseHPKPHeader(const std::string& value, |
| const HashValueVector& chain_hashes, |
| base::TimeDelta* max_age, |
| bool* include_subdomains, |
| - HashValueVector* hashes) { |
| + 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; |
| - std::string source = value; |
| - |
| - while (!source.empty()) { |
| - StringPair semicolon = Split(source, ';'); |
| - semicolon.first = Strip(semicolon.first); |
| - semicolon.second = Strip(semicolon.second); |
| - StringPair equals = Split(semicolon.first, '='); |
| - equals.first = Strip(equals.first); |
| - equals.second = Strip(equals.second); |
| + HttpUtil::NameValuePairsIterator name_value_pairs( |
| + value.begin(), value.end(), ';', |
| + HttpUtil::NameValuePairsIterator::VALUES_OPTIONAL); |
| - if (base::LowerCaseEqualsASCII(equals.first, "max-age")) { |
| - if (equals.second.empty() || |
| - !MaxAgeToInt(equals.second.begin(), equals.second.end(), |
| - &max_age_candidate)) { |
| + while (name_value_pairs.GetNext()) { |
| + if (base::LowerCaseEqualsASCII(name_value_pairs.name(), "max-age")) { |
|
Ryan Sleevi
2015/07/16 05:07:51
World's cruelest reviewer nit:
Each of these can
estark
2015/07/16 05:18:48
Done.
|
| + 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(equals.first, "pin-sha1")) { |
| - if (!ParseAndAppendPin(equals.second, HASH_VALUE_SHA1, &pins)) |
| + } else if (base::LowerCaseEqualsASCII(name_value_pairs.name(), |
| + "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(equals.first, "pin-sha256")) { |
| - if (!ParseAndAppendPin(equals.second, HASH_VALUE_SHA256, &pins)) |
| + } |
| + } else if (base::LowerCaseEqualsASCII(name_value_pairs.name(), |
| + "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(equals.first, "includesubdomains")) { |
| + } |
| + } else if (base::LowerCaseEqualsASCII(name_value_pairs.name(), |
| + "includesubdomains")) { |
| include_subdomains_candidate = true; |
| + } else if (base::LowerCaseEqualsASCII(name_value_pairs.name(), |
| + "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. |
| } |
| - |
| - source = semicolon.second; |
| } |
| + if (!name_value_pairs.valid()) |
| + return false; |
| + |
| if (!parsed_max_age) |
| return false; |
| @@ -327,6 +327,7 @@ bool ParseHPKPHeader(const std::string& value, |
| *max_age = base::TimeDelta::FromSeconds(max_age_candidate); |
| *include_subdomains = include_subdomains_candidate; |
| hashes->swap(pins); |
| + *report_uri = parsed_report_uri; |
| return true; |
| } |