| 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 // Portions of this code based on Mozilla: | 5 // Portions of this code based on Mozilla: |
| 6 // (netwerk/cookie/src/nsCookieService.cpp) | 6 // (netwerk/cookie/src/nsCookieService.cpp) |
| 7 /* ***** BEGIN LICENSE BLOCK ***** | 7 /* ***** BEGIN LICENSE BLOCK ***** |
| 8 * Version: MPL 1.1/GPL 2.0/LGPL 2.1 | 8 * Version: MPL 1.1/GPL 2.0/LGPL 2.1 |
| 9 * | 9 * |
| 10 * The contents of this file are subject to the Mozilla Public License Version | 10 * The contents of this file are subject to the Mozilla Public License Version |
| (...skipping 27 matching lines...) Expand all Loading... |
| 38 * decision by deleting the provisions above and replace them with the notice | 38 * decision by deleting the provisions above and replace them with the notice |
| 39 * and other provisions required by the GPL or the LGPL. If you do not delete | 39 * and other provisions required by the GPL or the LGPL. If you do not delete |
| 40 * the provisions above, a recipient may use your version of this file under | 40 * the provisions above, a recipient may use your version of this file under |
| 41 * the terms of any one of the MPL, the GPL or the LGPL. | 41 * the terms of any one of the MPL, the GPL or the LGPL. |
| 42 * | 42 * |
| 43 * ***** END LICENSE BLOCK ***** */ | 43 * ***** END LICENSE BLOCK ***** */ |
| 44 | 44 |
| 45 #include "net/cookies/parsed_cookie.h" | 45 #include "net/cookies/parsed_cookie.h" |
| 46 | 46 |
| 47 #include "base/logging.h" | 47 #include "base/logging.h" |
| 48 #include "base/metrics/histogram.h" | |
| 49 #include "base/strings/string_util.h" | 48 #include "base/strings/string_util.h" |
| 50 | 49 |
| 51 // TODO(jww): We are collecting several UMA statistics in this file, and they | |
| 52 // relate to http://crbug.com/238041. We are measuring stats related to control | |
| 53 // characters in cookies because, currently, we allow control characters in a | |
| 54 // variety of scenarios where various RFCs theoretically disallow them. These | |
| 55 // control characters have the potential to cause problems with certain web | |
| 56 // servers that reject HTTP requests that contain cookies with control | |
| 57 // characters. We are measuring whether disallowing such cookies would have a | |
| 58 // notable impact on our users. We want to collect these stats through 1 stable | |
| 59 // release, so these UMA stats should remain at least through the M29 | |
| 60 // branch-point. | |
| 61 | |
| 62 namespace { | 50 namespace { |
| 63 | 51 |
| 64 const char kPathTokenName[] = "path"; | 52 const char kPathTokenName[] = "path"; |
| 65 const char kDomainTokenName[] = "domain"; | 53 const char kDomainTokenName[] = "domain"; |
| 66 const char kExpiresTokenName[] = "expires"; | 54 const char kExpiresTokenName[] = "expires"; |
| 67 const char kMaxAgeTokenName[] = "max-age"; | 55 const char kMaxAgeTokenName[] = "max-age"; |
| 68 const char kSecureTokenName[] = "secure"; | 56 const char kSecureTokenName[] = "secure"; |
| 69 const char kHttpOnlyTokenName[] = "httponly"; | 57 const char kHttpOnlyTokenName[] = "httponly"; |
| 70 const char kPriorityTokenName[] = "priority"; | 58 const char kPriorityTokenName[] = "priority"; |
| 71 | 59 |
| (...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 191 bool ParsedCookie::IsValid() const { | 179 bool ParsedCookie::IsValid() const { |
| 192 return !pairs_.empty(); | 180 return !pairs_.empty(); |
| 193 } | 181 } |
| 194 | 182 |
| 195 CookiePriority ParsedCookie::Priority() const { | 183 CookiePriority ParsedCookie::Priority() const { |
| 196 return (priority_index_ == 0) ? COOKIE_PRIORITY_DEFAULT : | 184 return (priority_index_ == 0) ? COOKIE_PRIORITY_DEFAULT : |
| 197 StringToCookiePriority(pairs_[priority_index_].second); | 185 StringToCookiePriority(pairs_[priority_index_].second); |
| 198 } | 186 } |
| 199 | 187 |
| 200 bool ParsedCookie::SetName(const std::string& name) { | 188 bool ParsedCookie::SetName(const std::string& name) { |
| 201 bool valid_token = IsValidToken(name); | 189 if (!IsValidToken(name)) |
| 202 UMA_HISTOGRAM_BOOLEAN("Cookie.SetNameVaildity", valid_token); | |
| 203 if (!valid_token) | |
| 204 return false; | 190 return false; |
| 205 if (pairs_.empty()) | 191 if (pairs_.empty()) |
| 206 pairs_.push_back(std::make_pair("", "")); | 192 pairs_.push_back(std::make_pair("", "")); |
| 207 pairs_[0].first = name; | 193 pairs_[0].first = name; |
| 208 return true; | 194 return true; |
| 209 } | 195 } |
| 210 | 196 |
| 211 bool ParsedCookie::SetValue(const std::string& value) { | 197 bool ParsedCookie::SetValue(const std::string& value) { |
| 212 bool valid_cookie_value = IsValidCookieValue(value); | 198 if (!IsValidCookieValue(value)) |
| 213 UMA_HISTOGRAM_BOOLEAN("Cookie.SetValueCookieValueValidity", | |
| 214 valid_cookie_value); | |
| 215 if (!valid_cookie_value) | |
| 216 return false; | 199 return false; |
| 217 if (pairs_.empty()) | 200 if (pairs_.empty()) |
| 218 pairs_.push_back(std::make_pair("", "")); | 201 pairs_.push_back(std::make_pair("", "")); |
| 219 pairs_[0].second = value; | 202 pairs_[0].second = value; |
| 220 return true; | 203 return true; |
| 221 } | 204 } |
| 222 | 205 |
| 223 bool ParsedCookie::SetPath(const std::string& path) { | 206 bool ParsedCookie::SetPath(const std::string& path) { |
| 224 return SetString(&path_index_, kPathTokenName, path); | 207 return SetString(&path_index_, kPathTokenName, path); |
| 225 } | 208 } |
| (...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 351 std::string::const_iterator it = value.begin(); | 334 std::string::const_iterator it = value.begin(); |
| 352 std::string::const_iterator end = FindFirstTerminator(value); | 335 std::string::const_iterator end = FindFirstTerminator(value); |
| 353 | 336 |
| 354 std::string::const_iterator value_start, value_end; | 337 std::string::const_iterator value_start, value_end; |
| 355 ParseValue(&it, end, &value_start, &value_end); | 338 ParseValue(&it, end, &value_start, &value_end); |
| 356 return std::string(value_start, value_end); | 339 return std::string(value_start, value_end); |
| 357 } | 340 } |
| 358 | 341 |
| 359 // Parse all token/value pairs and populate pairs_. | 342 // Parse all token/value pairs and populate pairs_. |
| 360 void ParsedCookie::ParseTokenValuePairs(const std::string& cookie_line) { | 343 void ParsedCookie::ParseTokenValuePairs(const std::string& cookie_line) { |
| 361 enum ParsedCookieStatus { | |
| 362 PARSED_COOKIE_STATUS_NOTHING = 0x0, | |
| 363 PARSED_COOKIE_STATUS_CONTROL_CHAR = 0x1, | |
| 364 PARSED_COOKIE_STATUS_INVALID = 0x2, | |
| 365 PARSED_COOKIE_STATUS_BOTH = | |
| 366 PARSED_COOKIE_STATUS_CONTROL_CHAR | PARSED_COOKIE_STATUS_INVALID | |
| 367 }; | |
| 368 int parsed_cookie_status = PARSED_COOKIE_STATUS_NOTHING; | |
| 369 | |
| 370 pairs_.clear(); | 344 pairs_.clear(); |
| 371 | 345 |
| 372 // Ok, here we go. We should be expecting to be starting somewhere | 346 // Ok, here we go. We should be expecting to be starting somewhere |
| 373 // before the cookie line, not including any header name... | 347 // before the cookie line, not including any header name... |
| 374 std::string::const_iterator start = cookie_line.begin(); | 348 std::string::const_iterator start = cookie_line.begin(); |
| 375 std::string::const_iterator it = start; | 349 std::string::const_iterator it = start; |
| 376 | 350 |
| 377 // TODO(erikwright): Make sure we're stripping \r\n in the network code. | 351 // TODO(erikwright): Make sure we're stripping \r\n in the network code. |
| 378 // Then we can log any unexpected terminators. | 352 // Then we can log any unexpected terminators. |
| 379 std::string::const_iterator end = FindFirstTerminator(cookie_line); | 353 std::string::const_iterator end = FindFirstTerminator(cookie_line); |
| (...skipping 28 matching lines...) Expand all Loading... |
| 408 ++it; // Skip past the '='. | 382 ++it; // Skip past the '='. |
| 409 } | 383 } |
| 410 | 384 |
| 411 // OK, now try to parse a value. | 385 // OK, now try to parse a value. |
| 412 std::string::const_iterator value_start, value_end; | 386 std::string::const_iterator value_start, value_end; |
| 413 ParseValue(&it, end, &value_start, &value_end); | 387 ParseValue(&it, end, &value_start, &value_end); |
| 414 | 388 |
| 415 // OK, we're finished with a Token/Value. | 389 // OK, we're finished with a Token/Value. |
| 416 pair.second = std::string(value_start, value_end); | 390 pair.second = std::string(value_start, value_end); |
| 417 | 391 |
| 418 if (!IsValidCookieAttributeValue(pair.second)) | |
| 419 parsed_cookie_status |= PARSED_COOKIE_STATUS_CONTROL_CHAR; | |
| 420 if (!IsValidToken(pair.second)) | |
| 421 parsed_cookie_status |= PARSED_COOKIE_STATUS_INVALID; | |
| 422 | |
| 423 // From RFC2109: "Attributes (names) (attr) are case-insensitive." | 392 // From RFC2109: "Attributes (names) (attr) are case-insensitive." |
| 424 if (pair_num != 0) | 393 if (pair_num != 0) |
| 425 StringToLowerASCII(&pair.first); | 394 StringToLowerASCII(&pair.first); |
| 426 // Ignore Set-Cookie directives contaning control characters. See | 395 // Ignore Set-Cookie directives contaning control characters. See |
| 427 // http://crbug.com/238041. | 396 // http://crbug.com/238041. |
| 428 if (!IsValidCookieAttributeValue(pair.first) || | 397 if (!IsValidCookieAttributeValue(pair.first) || |
| 429 !IsValidCookieAttributeValue(pair.second)) { | 398 !IsValidCookieAttributeValue(pair.second)) { |
| 430 pairs_.clear(); | 399 pairs_.clear(); |
| 431 break; | 400 break; |
| 432 } | 401 } |
| 433 | 402 |
| 434 pairs_.push_back(pair); | 403 pairs_.push_back(pair); |
| 435 | 404 |
| 436 // We've processed a token/value pair, we're either at the end of | 405 // We've processed a token/value pair, we're either at the end of |
| 437 // the string or a ValueSeparator like ';', which we want to skip. | 406 // the string or a ValueSeparator like ';', which we want to skip. |
| 438 if (it != end) | 407 if (it != end) |
| 439 ++it; | 408 ++it; |
| 440 } | 409 } |
| 441 | |
| 442 UMA_HISTOGRAM_ENUMERATION("Cookie.ParsedCookieStatus", parsed_cookie_status, | |
| 443 PARSED_COOKIE_STATUS_BOTH + 1); | |
| 444 } | 410 } |
| 445 | 411 |
| 446 void ParsedCookie::SetupAttributes() { | 412 void ParsedCookie::SetupAttributes() { |
| 447 // We skip over the first token/value, the user supplied one. | 413 // We skip over the first token/value, the user supplied one. |
| 448 for (size_t i = 1; i < pairs_.size(); ++i) { | 414 for (size_t i = 1; i < pairs_.size(); ++i) { |
| 449 if (pairs_[i].first == kPathTokenName) { | 415 if (pairs_[i].first == kPathTokenName) { |
| 450 path_index_ = i; | 416 path_index_ = i; |
| 451 } else if (pairs_[i].first == kDomainTokenName) { | 417 } else if (pairs_[i].first == kDomainTokenName) { |
| 452 domain_index_ = i; | 418 domain_index_ = i; |
| 453 } else if (pairs_[i].first == kExpiresTokenName) { | 419 } else if (pairs_[i].first == kExpiresTokenName) { |
| (...skipping 30 matching lines...) Expand all Loading... |
| 484 ClearAttributePair(*index); | 450 ClearAttributePair(*index); |
| 485 return true; | 451 return true; |
| 486 } else { | 452 } else { |
| 487 return SetAttributePair(index, key, std::string()); | 453 return SetAttributePair(index, key, std::string()); |
| 488 } | 454 } |
| 489 } | 455 } |
| 490 | 456 |
| 491 bool ParsedCookie::SetAttributePair(size_t* index, | 457 bool ParsedCookie::SetAttributePair(size_t* index, |
| 492 const std::string& key, | 458 const std::string& key, |
| 493 const std::string& value) { | 459 const std::string& value) { |
| 494 bool valid_attribute_pair = IsValidToken(key) && | 460 if (!(IsValidToken(key) && IsValidCookieAttributeValue(value))) |
| 495 IsValidCookieAttributeValue(value); | |
| 496 UMA_HISTOGRAM_BOOLEAN("Cookie.SetAttributePairCharsValidity", | |
| 497 valid_attribute_pair); | |
| 498 if (!valid_attribute_pair) | |
| 499 return false; | 461 return false; |
| 500 if (!IsValid()) | 462 if (!IsValid()) |
| 501 return false; | 463 return false; |
| 502 if (*index) { | 464 if (*index) { |
| 503 pairs_[*index].second = value; | 465 pairs_[*index].second = value; |
| 504 } else { | 466 } else { |
| 505 pairs_.push_back(std::make_pair(key, value)); | 467 pairs_.push_back(std::make_pair(key, value)); |
| 506 *index = pairs_.size() - 1; | 468 *index = pairs_.size() - 1; |
| 507 } | 469 } |
| 508 return true; | 470 return true; |
| (...skipping 12 matching lines...) Expand all Loading... |
| 521 for (size_t i = 0; i < arraysize(indexes); ++i) { | 483 for (size_t i = 0; i < arraysize(indexes); ++i) { |
| 522 if (*indexes[i] == index) | 484 if (*indexes[i] == index) |
| 523 *indexes[i] = 0; | 485 *indexes[i] = 0; |
| 524 else if (*indexes[i] > index) | 486 else if (*indexes[i] > index) |
| 525 --*indexes[i]; | 487 --*indexes[i]; |
| 526 } | 488 } |
| 527 pairs_.erase(pairs_.begin() + index); | 489 pairs_.erase(pairs_.begin() + index); |
| 528 } | 490 } |
| 529 | 491 |
| 530 } // namespace | 492 } // namespace |
| OLD | NEW |