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 |