| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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 <limits> | 5 #include <limits> |
| 6 | 6 |
| 7 #include "base/base64.h" | 7 #include "base/base64.h" |
| 8 #include "base/strings/string_number_conversions.h" | 8 #include "base/strings/string_number_conversions.h" |
| 9 #include "base/strings/string_piece.h" | 9 #include "base/strings/string_piece.h" |
| 10 #include "base/strings/string_tokenizer.h" | 10 #include "base/strings/string_tokenizer.h" |
| 11 #include "base/strings/string_util.h" | 11 #include "base/strings/string_util.h" |
| 12 #include "net/http/http_security_headers.h" | 12 #include "net/http/http_security_headers.h" |
| 13 #include "net/http/http_util.h" | 13 #include "net/http/http_util.h" |
| 14 #include "url/gurl.h" | 14 #include "url/gurl.h" |
| 15 | 15 |
| 16 namespace net { | 16 namespace net { |
| 17 | 17 |
| 18 namespace { | 18 namespace { |
| 19 | 19 |
| 20 enum MaxAgeParsing { REQUIRE_MAX_AGE, DO_NOT_REQUIRE_MAX_AGE }; | 20 enum MaxAgeParsing { REQUIRE_MAX_AGE, DO_NOT_REQUIRE_MAX_AGE }; |
| 21 | 21 |
| 22 static_assert(kMaxHSTSAgeSecs <= UINT32_MAX, "kMaxHSTSAgeSecs too large"); | 22 static_assert(kMaxHSTSAgeSecs <= UINT32_MAX, "kMaxHSTSAgeSecs too large"); |
| 23 static_assert(kMaxHPKPAgeSecs <= UINT32_MAX, "kMaxHPKPAgeSecs too large"); |
| 23 | 24 |
| 24 // MaxAgeToInt converts a string representation of a "whole number" of | 25 // MaxAgeToLimitedInt converts a string representation of a "whole number" of |
| 25 // seconds into a uint32_t. The string may contain an arbitrarily large number, | 26 // seconds into a uint32_t. The string may contain an arbitrarily large number, |
| 26 // which will be clipped to kMaxHSTSAgeSecs and which is guaranteed to fit | 27 // which will be clipped to a supplied limit and which is guaranteed to fit |
| 27 // within a 32-bit unsigned integer. False is returned on any parse error. | 28 // within a 32-bit unsigned integer. False is returned on any parse error. |
| 28 bool MaxAgeToInt(std::string::const_iterator begin, | 29 bool MaxAgeToLimitedInt(std::string::const_iterator begin, |
| 29 std::string::const_iterator end, | 30 std::string::const_iterator end, |
| 30 uint32_t* result) { | 31 uint32_t limit, |
| 32 uint32_t* result) { |
| 31 const base::StringPiece s(begin, end); | 33 const base::StringPiece s(begin, end); |
| 32 if (s.empty()) | 34 if (s.empty()) |
| 33 return false; | 35 return false; |
| 34 | 36 |
| 35 int64_t i = 0; | 37 int64_t i = 0; |
| 36 | 38 |
| 37 // Return false on any StringToInt64 parse errors *except* for int64_t | 39 // Return false on any StringToInt64 parse errors *except* for int64_t |
| 38 // overflow. StringToInt64 is used, rather than StringToUint64, in order to | 40 // overflow. StringToInt64 is used, rather than StringToUint64, in order to |
| 39 // properly handle and reject negative numbers (StringToUint64 does not return | 41 // properly handle and reject negative numbers (StringToUint64 does not return |
| 40 // false on negative numbers). For values too large to be stored in an | 42 // false on negative numbers). For values too large to be stored in an |
| 41 // int64_t, StringToInt64 will return false with i set to | 43 // int64_t, StringToInt64 will return false with i set to |
| 42 // std::numeric_limits<int64_t>::max(), so this case is detected by the | 44 // std::numeric_limits<int64_t>::max(), so this case is allowed to fall |
| 43 // immediately following if-statement and allowed to fall through so that i | 45 // through so that i gets clipped to limit. |
| 44 // gets clipped to kMaxHSTSAgeSecs. | |
| 45 if (!base::StringToInt64(s, &i) && i != std::numeric_limits<int64_t>::max()) | 46 if (!base::StringToInt64(s, &i) && i != std::numeric_limits<int64_t>::max()) |
| 46 return false; | 47 return false; |
| 47 if (i < 0) | 48 if (i < 0) |
| 48 return false; | 49 return false; |
| 49 if (i > kMaxHSTSAgeSecs) | 50 if (i > limit) |
| 50 i = kMaxHSTSAgeSecs; | 51 i = limit; |
| 51 *result = (uint32_t)i; | 52 *result = (uint32_t)i; |
| 52 return true; | 53 return true; |
| 53 } | 54 } |
| 54 | 55 |
| 55 // Returns true iff there is an item in |pins| which is not present in | 56 // Returns true iff there is an item in |pins| which is not present in |
| 56 // |from_cert_chain|. Such an SPKI hash is called a "backup pin". | 57 // |from_cert_chain|. Such an SPKI hash is called a "backup pin". |
| 57 bool IsBackupPinPresent(const HashValueVector& pins, | 58 bool IsBackupPinPresent(const HashValueVector& pins, |
| 58 const HashValueVector& from_cert_chain) { | 59 const HashValueVector& from_cert_chain) { |
| 59 for (HashValueVector::const_iterator i = pins.begin(); i != pins.end(); | 60 for (HashValueVector::const_iterator i = pins.begin(); i != pins.end(); |
| 60 ++i) { | 61 ++i) { |
| (...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 136 | 137 |
| 137 HttpUtil::NameValuePairsIterator name_value_pairs( | 138 HttpUtil::NameValuePairsIterator name_value_pairs( |
| 138 value.begin(), value.end(), ';', | 139 value.begin(), value.end(), ';', |
| 139 HttpUtil::NameValuePairsIterator::VALUES_OPTIONAL); | 140 HttpUtil::NameValuePairsIterator::VALUES_OPTIONAL); |
| 140 | 141 |
| 141 while (name_value_pairs.GetNext()) { | 142 while (name_value_pairs.GetNext()) { |
| 142 if (base::LowerCaseEqualsASCII( | 143 if (base::LowerCaseEqualsASCII( |
| 143 base::StringPiece(name_value_pairs.name_begin(), | 144 base::StringPiece(name_value_pairs.name_begin(), |
| 144 name_value_pairs.name_end()), | 145 name_value_pairs.name_end()), |
| 145 "max-age")) { | 146 "max-age")) { |
| 146 if (!MaxAgeToInt(name_value_pairs.value_begin(), | 147 if (!MaxAgeToLimitedInt(name_value_pairs.value_begin(), |
| 147 name_value_pairs.value_end(), &max_age_candidate)) { | 148 name_value_pairs.value_end(), kMaxHPKPAgeSecs, |
| 149 &max_age_candidate)) { |
| 148 return false; | 150 return false; |
| 149 } | 151 } |
| 150 parsed_max_age = true; | 152 parsed_max_age = true; |
| 151 } else if (base::LowerCaseEqualsASCII( | 153 } else if (base::LowerCaseEqualsASCII( |
| 152 base::StringPiece(name_value_pairs.name_begin(), | 154 base::StringPiece(name_value_pairs.name_begin(), |
| 153 name_value_pairs.name_end()), | 155 name_value_pairs.name_end()), |
| 154 "pin-sha256")) { | 156 "pin-sha256")) { |
| 155 // Pins are always quoted. | 157 // Pins are always quoted. |
| 156 if (!name_value_pairs.value_is_quoted() || | 158 if (!name_value_pairs.value_is_quoted() || |
| 157 !ParseAndAppendPin(name_value_pairs.value_begin(), | 159 !ParseAndAppendPin(name_value_pairs.value_begin(), |
| (...skipping 115 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 273 if (*tokenizer.token_begin() != '=') | 275 if (*tokenizer.token_begin() != '=') |
| 274 return false; | 276 return false; |
| 275 DCHECK_EQ(tokenizer.token().length(), 1U); | 277 DCHECK_EQ(tokenizer.token().length(), 1U); |
| 276 state = AFTER_MAX_AGE_EQUALS; | 278 state = AFTER_MAX_AGE_EQUALS; |
| 277 break; | 279 break; |
| 278 | 280 |
| 279 case AFTER_MAX_AGE_EQUALS: | 281 case AFTER_MAX_AGE_EQUALS: |
| 280 if (base::IsAsciiWhitespace(*tokenizer.token_begin())) | 282 if (base::IsAsciiWhitespace(*tokenizer.token_begin())) |
| 281 continue; | 283 continue; |
| 282 unquoted = HttpUtil::Unquote(tokenizer.token()); | 284 unquoted = HttpUtil::Unquote(tokenizer.token()); |
| 283 if (!MaxAgeToInt(unquoted.begin(), unquoted.end(), &max_age_candidate)) | 285 if (!MaxAgeToLimitedInt(unquoted.begin(), unquoted.end(), |
| 286 kMaxHSTSAgeSecs, &max_age_candidate)) |
| 284 return false; | 287 return false; |
| 285 state = AFTER_MAX_AGE; | 288 state = AFTER_MAX_AGE; |
| 286 break; | 289 break; |
| 287 | 290 |
| 288 case AFTER_MAX_AGE: | 291 case AFTER_MAX_AGE: |
| 289 case AFTER_INCLUDE_SUBDOMAINS: | 292 case AFTER_INCLUDE_SUBDOMAINS: |
| 290 if (base::IsAsciiWhitespace(*tokenizer.token_begin())) | 293 if (base::IsAsciiWhitespace(*tokenizer.token_begin())) |
| 291 continue; | 294 continue; |
| 292 else if (*tokenizer.token_begin() == ';') | 295 else if (*tokenizer.token_begin() == ';') |
| 293 state = DIRECTIVE_END; | 296 state = DIRECTIVE_END; |
| 294 else | 297 else |
| 295 return false; | 298 return false; |
| 296 break; | 299 break; |
| 297 | 300 |
| 298 case AFTER_UNKNOWN_LABEL: | 301 case AFTER_UNKNOWN_LABEL: |
| 299 // Consume and ignore the post-label contents (if any). | 302 // Consume and ignore the post-label contents (if any). |
| 300 if (*tokenizer.token_begin() != ';') | 303 if (*tokenizer.token_begin() != ';') |
| 301 continue; | 304 continue; |
| 302 state = DIRECTIVE_END; | 305 state = DIRECTIVE_END; |
| 303 break; | 306 break; |
| 304 } | 307 } |
| 305 } | 308 } |
| 306 | 309 |
| 307 // We've consumed all the input. Let's see what state we ended up in. | 310 // We've consumed all the input. Let's see what state we ended up in. |
| 308 if (max_age_observed != 1 || | 311 if (max_age_observed != 1 || |
| 309 (include_subdomains_observed != 0 && include_subdomains_observed != 1)) { | 312 (include_subdomains_observed != 0 && include_subdomains_observed != 1)) { |
| 310 return false; | 313 return false; |
| 311 } | 314 } |
| 312 | 315 |
| 313 switch (state) { | 316 switch (state) { |
| 314 case DIRECTIVE_END: | 317 case DIRECTIVE_END: |
| 315 case AFTER_MAX_AGE: | 318 case AFTER_MAX_AGE: |
| 316 case AFTER_INCLUDE_SUBDOMAINS: | 319 case AFTER_INCLUDE_SUBDOMAINS: |
| 317 case AFTER_UNKNOWN_LABEL: | 320 case AFTER_UNKNOWN_LABEL: |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 369 bool* include_subdomains, | 372 bool* include_subdomains, |
| 370 HashValueVector* hashes, | 373 HashValueVector* hashes, |
| 371 GURL* report_uri) { | 374 GURL* report_uri) { |
| 372 // max-age is irrelevant for Report-Only headers. | 375 // max-age is irrelevant for Report-Only headers. |
| 373 base::TimeDelta unused_max_age; | 376 base::TimeDelta unused_max_age; |
| 374 return ParseHPKPHeaderImpl(value, DO_NOT_REQUIRE_MAX_AGE, &unused_max_age, | 377 return ParseHPKPHeaderImpl(value, DO_NOT_REQUIRE_MAX_AGE, &unused_max_age, |
| 375 include_subdomains, hashes, report_uri); | 378 include_subdomains, hashes, report_uri); |
| 376 } | 379 } |
| 377 | 380 |
| 378 } // namespace net | 381 } // namespace net |
| OLD | NEW |