tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

Cookie.cpp (8965B)


      1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 #include "Cookie.h"
      7 #include "CookieCommons.h"
      8 #include "CookieStorage.h"
      9 #include "mozilla/Encoding.h"
     10 #include "mozilla/dom/ToJSValue.h"
     11 #include "mozilla/glean/NetwerkMetrics.h"
     12 #include "mozilla/StaticPrefs_network.h"
     13 #include "nsIURLParser.h"
     14 #include "nsURLHelper.h"
     15 #include <cstdlib>
     16 
     17 namespace mozilla {
     18 namespace net {
     19 
     20 /******************************************************************************
     21 * Cookie:
     22 * creation helper
     23 ******************************************************************************/
     24 
     25 // This is a counter that keeps track of the last used creation time, each time
     26 // we create a new Cookie. This is nominally the time (in microseconds) the
     27 // cookie was created, but is guaranteed to be monotonically increasing for
     28 // cookies added at runtime after the database has been read in. This is
     29 // necessary to enforce ordering among cookies whose creation times would
     30 // otherwise overlap, since it's possible two cookies may be created at the
     31 // same time, or that the system clock isn't monotonic.
     32 static int64_t gLastCreationTimeInUSec;
     33 
     34 int64_t Cookie::GenerateUniqueCreationTimeInUSec(int64_t aCreationTimeInUSec) {
     35  // Check if the creation time given to us is greater than the running maximum
     36  // (it should always be monotonically increasing).
     37  if (aCreationTimeInUSec > gLastCreationTimeInUSec) {
     38    gLastCreationTimeInUSec = aCreationTimeInUSec;
     39    return aCreationTimeInUSec;
     40  }
     41 
     42  // Make up our own.
     43  return ++gLastCreationTimeInUSec;
     44 }
     45 
     46 already_AddRefed<Cookie> Cookie::Create(
     47    const CookieStruct& aCookieData,
     48    const OriginAttributes& aOriginAttributes) {
     49  RefPtr<Cookie> cookie =
     50      Cookie::FromCookieStruct(aCookieData, aOriginAttributes);
     51 
     52  // If the creationTimeInUSec given to us is higher than the running maximum,
     53  // update our maximum.
     54  if (cookie->mData.creationTimeInUSec() > gLastCreationTimeInUSec) {
     55    gLastCreationTimeInUSec = cookie->mData.creationTimeInUSec();
     56  }
     57 
     58  return cookie.forget();
     59 }
     60 
     61 already_AddRefed<Cookie> Cookie::FromCookieStruct(
     62    const CookieStruct& aCookieData,
     63    const OriginAttributes& aOriginAttributes) {
     64  RefPtr<Cookie> cookie = new Cookie(aCookieData, aOriginAttributes);
     65 
     66  // Ensure mValue contains a valid UTF-8 sequence. Otherwise XPConnect will
     67  // truncate the string after the first invalid octet.
     68  UTF_8_ENCODING->DecodeWithoutBOMHandling(aCookieData.value(),
     69                                           cookie->mData.value());
     70 
     71  // If sameSite value is not sensible reset to Default
     72  // cf. 5.4.7 in draft-ietf-httpbis-rfc6265bis-09
     73  if (cookie->mData.sameSite() != nsICookie::SAMESITE_NONE &&
     74      cookie->mData.sameSite() != nsICookie::SAMESITE_LAX &&
     75      cookie->mData.sameSite() != nsICookie::SAMESITE_STRICT &&
     76      cookie->mData.sameSite() != nsICookie::SAMESITE_UNSET) {
     77    cookie->mData.sameSite() = nsICookie::SAMESITE_UNSET;
     78  }
     79 
     80  // Enforce session cookie if the partition is session-only.
     81  if (CookieCommons::ShouldEnforceSessionForOriginAttributes(
     82          aOriginAttributes)) {
     83    cookie->mData.isSession() = true;
     84  }
     85 
     86  return cookie.forget();
     87 }
     88 
     89 already_AddRefed<Cookie> Cookie::CreateValidated(
     90    const CookieStruct& aCookieData,
     91    const OriginAttributes& aOriginAttributes) {
     92  if (!StaticPrefs::network_cookie_fixup_on_db_load()) {
     93    return Cookie::Create(aCookieData, aOriginAttributes);
     94  }
     95 
     96  RefPtr<Cookie> cookie =
     97      Cookie::FromCookieStruct(aCookieData, aOriginAttributes);
     98 
     99  int64_t currentTimeInUsec = PR_Now();
    100  // Assert that the last creation time is not higher than the current time.
    101  // The 10000 wiggle room accounts for the fact that calling
    102  // GenerateUniqueCreationTimeInUSec might go over the value of PR_Now(), but
    103  // we'd most likely not add 10000 cookies in a row.
    104  MOZ_ASSERT(gLastCreationTimeInUSec < currentTimeInUsec + 10000,
    105             "Last creation time must not be higher than NOW");
    106 
    107  // If the creationTimeInUSec given to us is higher than the current time then
    108  // update the creation time to now.
    109  if (cookie->mData.creationTimeInUSec() > currentTimeInUsec) {
    110    uint64_t diffInSeconds =
    111        (cookie->mData.creationTimeInUSec() - currentTimeInUsec) /
    112        PR_USEC_PER_SEC;
    113    mozilla::glean::networking::cookie_creation_fixup_diff
    114        .AccumulateSingleSample(diffInSeconds);
    115    glean::networking::cookie_timestamp_fixed_count.Get("creationTime"_ns)
    116        .Add(1);
    117 
    118    cookie->mData.creationTimeInUSec() =
    119        GenerateUniqueCreationTimeInUSec(currentTimeInUsec);
    120  }
    121 
    122  if (cookie->mData.lastAccessedInUSec() > currentTimeInUsec) {
    123    uint64_t diffInSeconds =
    124        (cookie->mData.lastAccessedInUSec() - currentTimeInUsec) /
    125        PR_USEC_PER_SEC;
    126    mozilla::glean::networking::cookie_access_fixup_diff.AccumulateSingleSample(
    127        diffInSeconds);
    128    glean::networking::cookie_timestamp_fixed_count.Get("lastAccessed"_ns)
    129        .Add(1);
    130 
    131    cookie->mData.lastAccessedInUSec() = currentTimeInUsec;
    132  }
    133 
    134  if (cookie->mData.updateTimeInUSec() > currentTimeInUsec) {
    135    uint64_t diffInSeconds =
    136        (cookie->mData.updateTimeInUSec() - currentTimeInUsec) /
    137        PR_USEC_PER_SEC;
    138    mozilla::glean::networking::cookie_access_fixup_diff.AccumulateSingleSample(
    139        diffInSeconds);
    140    glean::networking::cookie_timestamp_fixed_count.Get("updateTime"_ns).Add(1);
    141 
    142    cookie->mData.updateTimeInUSec() = currentTimeInUsec;
    143  }
    144 
    145  return cookie.forget();
    146 }
    147 
    148 size_t Cookie::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const {
    149  return aMallocSizeOf(this) +
    150         mData.name().SizeOfExcludingThisIfUnshared(MallocSizeOf) +
    151         mData.value().SizeOfExcludingThisIfUnshared(MallocSizeOf) +
    152         mData.host().SizeOfExcludingThisIfUnshared(MallocSizeOf) +
    153         mData.path().SizeOfExcludingThisIfUnshared(MallocSizeOf);
    154 }
    155 
    156 bool Cookie::IsStale() const {
    157  int64_t currentTimeInUsec = PR_Now();
    158 
    159  return currentTimeInUsec - LastAccessedInUSec() >
    160         StaticPrefs::network_cookie_staleThreshold() * PR_USEC_PER_SEC;
    161 }
    162 
    163 /******************************************************************************
    164 * Cookie:
    165 * xpcom impl
    166 ******************************************************************************/
    167 
    168 // xpcom getters
    169 NS_IMETHODIMP Cookie::GetName(nsACString& aName) {
    170  aName = Name();
    171  return NS_OK;
    172 }
    173 NS_IMETHODIMP Cookie::GetValue(nsACString& aValue) {
    174  aValue = Value();
    175  return NS_OK;
    176 }
    177 NS_IMETHODIMP Cookie::GetHost(nsACString& aHost) {
    178  aHost = Host();
    179  return NS_OK;
    180 }
    181 NS_IMETHODIMP Cookie::GetRawHost(nsACString& aHost) {
    182  aHost = RawHost();
    183  return NS_OK;
    184 }
    185 NS_IMETHODIMP Cookie::GetPath(nsACString& aPath) {
    186  aPath = Path();
    187  return NS_OK;
    188 }
    189 NS_IMETHODIMP Cookie::GetExpiry(int64_t* aExpiry) {
    190  *aExpiry = ExpiryInMSec();
    191  return NS_OK;
    192 }
    193 NS_IMETHODIMP Cookie::GetIsSession(bool* aIsSession) {
    194  *aIsSession = IsSession();
    195  return NS_OK;
    196 }
    197 NS_IMETHODIMP Cookie::GetIsDomain(bool* aIsDomain) {
    198  *aIsDomain = IsDomain();
    199  return NS_OK;
    200 }
    201 NS_IMETHODIMP Cookie::GetIsSecure(bool* aIsSecure) {
    202  *aIsSecure = IsSecure();
    203  return NS_OK;
    204 }
    205 NS_IMETHODIMP Cookie::GetIsHttpOnly(bool* aHttpOnly) {
    206  *aHttpOnly = IsHttpOnly();
    207  return NS_OK;
    208 }
    209 NS_IMETHODIMP Cookie::GetIsPartitioned(bool* aPartitioned) {
    210  *aPartitioned = IsPartitioned();
    211  return NS_OK;
    212 }
    213 NS_IMETHODIMP Cookie::GetCreationTime(int64_t* aCreation) {
    214  *aCreation = CreationTimeInUSec();
    215  return NS_OK;
    216 }
    217 NS_IMETHODIMP Cookie::GetLastAccessed(int64_t* aTime) {
    218  *aTime = LastAccessedInUSec();
    219  return NS_OK;
    220 }
    221 NS_IMETHODIMP Cookie::GetUpdateTime(int64_t* aTime) {
    222  *aTime = UpdateTimeInUSec();
    223  return NS_OK;
    224 }
    225 NS_IMETHODIMP Cookie::GetSameSite(int32_t* aSameSite) {
    226  *aSameSite = SameSite();
    227  return NS_OK;
    228 }
    229 NS_IMETHODIMP Cookie::GetSchemeMap(nsICookie::schemeType* aSchemeMap) {
    230  *aSchemeMap = static_cast<nsICookie::schemeType>(SchemeMap());
    231  return NS_OK;
    232 }
    233 
    234 NS_IMETHODIMP
    235 Cookie::GetOriginAttributes(JSContext* aCx, JS::MutableHandle<JS::Value> aVal) {
    236  if (NS_WARN_IF(!ToJSValue(aCx, mOriginAttributes, aVal))) {
    237    return NS_ERROR_FAILURE;
    238  }
    239  return NS_OK;
    240 }
    241 
    242 const OriginAttributes& Cookie::OriginAttributesNative() {
    243  return mOriginAttributes;
    244 }
    245 
    246 const Cookie& Cookie::AsCookie() { return *this; }
    247 
    248 // compatibility method, for use with the legacy nsICookie interface.
    249 // here, expires == 0 denotes a session cookie.
    250 NS_IMETHODIMP
    251 Cookie::GetExpires(uint64_t* aExpires) {
    252  if (IsSession()) {
    253    *aExpires = 0;
    254  } else {
    255    *aExpires = ExpiryInMSec() > 0 ? ExpiryInMSec() : 1;
    256  }
    257  return NS_OK;
    258 }
    259 
    260 already_AddRefed<Cookie> Cookie::Clone() const {
    261  return Create(mData, OriginAttributesRef());
    262 }
    263 
    264 NS_IMPL_ISUPPORTS(Cookie, nsICookie)
    265 
    266 }  // namespace net
    267 }  // namespace mozilla