| 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..ed7ceccbb73080a7e5dfa1627083a728c3a044fe 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,83 @@ 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);
|
| -
|
| - if (base::LowerCaseEqualsASCII(equals.first, "max-age")) {
|
| - if (equals.second.empty() ||
|
| - !MaxAgeToInt(equals.second.begin(), equals.second.end(),
|
| - &max_age_candidate)) {
|
| + 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(equals.first, "pin-sha1")) {
|
| - if (!ParseAndAppendPin(equals.second, HASH_VALUE_SHA1, &pins))
|
| + } 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(equals.first, "pin-sha256")) {
|
| - if (!ParseAndAppendPin(equals.second, HASH_VALUE_SHA256, &pins))
|
| + }
|
| + } 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(equals.first, "includesubdomains")) {
|
| + }
|
| + } 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.
|
| }
|
| -
|
| - source = semicolon.second;
|
| }
|
|
|
| + if (!name_value_pairs.valid())
|
| + return false;
|
| +
|
| if (!parsed_max_age)
|
| return false;
|
|
|
| @@ -327,6 +338,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;
|
| }
|
|
|