tor-browser

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

nsHttpAuthCache.cpp (13585B)


      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 // HttpLog.h should generally be included first
      7 #include "HttpLog.h"
      8 
      9 #include "nsHttpAuthCache.h"
     10 
     11 #include <algorithm>
     12 
     13 #include "nsString.h"
     14 #include "nsCRT.h"
     15 #include "nsIObserverService.h"
     16 #include "mozilla/Services.h"
     17 #include "mozilla/DebugOnly.h"
     18 #include "nsNetUtil.h"
     19 
     20 namespace mozilla {
     21 namespace net {
     22 
     23 static inline void GetAuthKey(const nsACString& scheme, const nsACString& host,
     24                              int32_t port, nsACString const& originSuffix,
     25                              nsCString& key) {
     26  key.Truncate();
     27  key.Append(originSuffix);
     28  key.Append(':');
     29  key.Append(scheme);
     30  key.AppendLiteral("://");
     31  key.Append(host);
     32  key.Append(':');
     33  key.AppendInt(port);
     34 }
     35 
     36 //-----------------------------------------------------------------------------
     37 // nsHttpAuthCache <public>
     38 //-----------------------------------------------------------------------------
     39 NS_IMPL_ISUPPORTS(nsHttpAuthCache, nsIHttpAuthCache)
     40 
     41 NS_IMETHODIMP
     42 nsHttpAuthCache::GetEntries(nsTArray<RefPtr<nsIHttpAuthEntry>>& aEntries) {
     43  for (auto iter = mDB.Iter(); !iter.Done(); iter.Next()) {
     44    nsHttpAuthNode* node = iter.Data().get();
     45    for (auto& entry : node->mList) {
     46      auto* tmp = entry.get();
     47      aEntries.AppendElement(tmp);
     48    }
     49  }
     50 
     51  return NS_OK;
     52 }
     53 
     54 NS_IMETHODIMP
     55 nsHttpAuthCache::ClearEntry(nsIHttpAuthEntry* aEntry) {
     56  NS_ENSURE_ARG_POINTER(aEntry);
     57 
     58  for (auto iter = mDB.Iter(); !iter.Done(); iter.Next()) {
     59    nsHttpAuthNode* node = iter.Data().get();
     60 
     61    for (auto& entry : node->mList) {
     62      if (entry.get() == aEntry) {
     63        node->mList.RemoveElement(entry);
     64        if (node->EntryCount() == 0) {
     65          iter.Remove();
     66        }
     67        return NS_OK;
     68      }
     69    }
     70  }
     71  return NS_ERROR_NOT_AVAILABLE;
     72 }
     73 
     74 nsHttpAuthCache::nsHttpAuthCache() : mDB(128) {
     75  LOG(("nsHttpAuthCache::nsHttpAuthCache %p", this));
     76 
     77  nsCOMPtr<nsIObserverService> obsSvc = services::GetObserverService();
     78  if (obsSvc) {
     79    obsSvc->AddObserver(this, "clear-origin-attributes-data", true);
     80  }
     81 }
     82 
     83 nsHttpAuthCache::~nsHttpAuthCache() {
     84  LOG(("nsHttpAuthCache::~nsHttpAuthCache %p", this));
     85 
     86  ClearAll();
     87  nsCOMPtr<nsIObserverService> obsSvc = services::GetObserverService();
     88  if (obsSvc) {
     89    obsSvc->RemoveObserver(this, "clear-origin-attributes-data");
     90  }
     91 }
     92 
     93 nsresult nsHttpAuthCache::GetAuthEntryForPath(const nsACString& scheme,
     94                                              const nsACString& host,
     95                                              int32_t port,
     96                                              const nsACString& path,
     97                                              nsACString const& originSuffix,
     98                                              RefPtr<nsHttpAuthEntry>& entry) {
     99  LOG(("nsHttpAuthCache::GetAuthEntryForPath %p [path=%s]\n", this,
    100       path.BeginReading()));
    101 
    102  nsAutoCString key;
    103  nsHttpAuthNode* node = LookupAuthNode(scheme, host, port, originSuffix, key);
    104  if (!node) return NS_ERROR_NOT_AVAILABLE;
    105 
    106  entry = node->LookupEntryByPath(path);
    107  LOG(("  returning %p", entry.get()));
    108  return entry ? NS_OK : NS_ERROR_NOT_AVAILABLE;
    109 }
    110 
    111 nsresult nsHttpAuthCache::GetAuthEntryForDomain(const nsACString& scheme,
    112                                                const nsACString& host,
    113                                                int32_t port,
    114                                                const nsACString& realm,
    115                                                nsACString const& originSuffix,
    116                                                RefPtr<nsHttpAuthEntry>& entry)
    117 
    118 {
    119  LOG(("nsHttpAuthCache::GetAuthEntryForDomain %p [realm=%s]\n", this,
    120       realm.BeginReading()));
    121 
    122  nsAutoCString key;
    123  nsHttpAuthNode* node = LookupAuthNode(scheme, host, port, originSuffix, key);
    124  if (!node) return NS_ERROR_NOT_AVAILABLE;
    125 
    126  entry = node->LookupEntryByRealm(realm);
    127  LOG(("  returning %p", entry.get()));
    128  return entry ? NS_OK : NS_ERROR_NOT_AVAILABLE;
    129 }
    130 
    131 nsresult nsHttpAuthCache::SetAuthEntry(
    132    const nsACString& scheme, const nsACString& host, int32_t port,
    133    const nsACString& path, const nsACString& realm, const nsACString& creds,
    134    const nsACString& challenge, nsACString const& originSuffix,
    135    const nsHttpAuthIdentity* ident, nsISupports* metadata) {
    136  nsresult rv;
    137 
    138  LOG(("nsHttpAuthCache::SetAuthEntry %p [realm=%s path=%s metadata=%p]\n",
    139       this, realm.BeginReading(), path.BeginReading(), metadata));
    140 
    141  nsAutoCString key;
    142  nsHttpAuthNode* node = LookupAuthNode(scheme, host, port, originSuffix, key);
    143 
    144  if (!node) {
    145    // create a new entry node and set the given entry
    146    auto node = UniquePtr<nsHttpAuthNode>(new nsHttpAuthNode);
    147    LOG(("  new nsHttpAuthNode %p for key='%s'", node.get(), key.get()));
    148    rv = node->SetAuthEntry(path, realm, creds, challenge, ident, metadata);
    149    if (NS_FAILED(rv)) {
    150      return rv;
    151    }
    152 
    153    mDB.InsertOrUpdate(key, std::move(node));
    154    return NS_OK;
    155  }
    156 
    157  return node->SetAuthEntry(path, realm, creds, challenge, ident, metadata);
    158 }
    159 
    160 void nsHttpAuthCache::ClearAuthEntry(const nsACString& scheme,
    161                                     const nsACString& host, int32_t port,
    162                                     const nsACString& realm,
    163                                     nsACString const& originSuffix) {
    164  nsAutoCString key;
    165  GetAuthKey(scheme, host, port, originSuffix, key);
    166  LOG(("nsHttpAuthCache::ClearAuthEntry %p key='%s'\n", this, key.get()));
    167  mDB.Remove(key);
    168 }
    169 
    170 void nsHttpAuthCache::ClearAll() {
    171  LOG(("nsHttpAuthCache::ClearAll %p\n", this));
    172  mDB.Clear();
    173 }
    174 
    175 //-----------------------------------------------------------------------------
    176 // nsHttpAuthCache <private>
    177 //-----------------------------------------------------------------------------
    178 
    179 nsHttpAuthNode* nsHttpAuthCache::LookupAuthNode(const nsACString& scheme,
    180                                                const nsACString& host,
    181                                                int32_t port,
    182                                                nsACString const& originSuffix,
    183                                                nsCString& key) {
    184  GetAuthKey(scheme, host, port, originSuffix, key);
    185  nsHttpAuthNode* result = mDB.Get(key);
    186 
    187  LOG(("nsHttpAuthCache::LookupAuthNode %p key='%s' found node=%p", this,
    188       key.get(), result));
    189  return result;
    190 }
    191 
    192 NS_IMETHODIMP
    193 nsHttpAuthCache::Observe(nsISupports* subject, const char* topic,
    194                         const char16_t* data_unicode) {
    195  OriginAttributesPattern pattern;
    196  if (!pattern.Init(nsDependentString(data_unicode))) {
    197    NS_ERROR("Cannot parse origin attributes pattern");
    198    return NS_ERROR_FAILURE;
    199  }
    200 
    201  ClearOriginData(pattern);
    202  return NS_OK;
    203 }
    204 
    205 void nsHttpAuthCache::ClearOriginData(OriginAttributesPattern const& pattern) {
    206  LOG(("nsHttpAuthCache::ClearOriginData %p", this));
    207 
    208  for (auto iter = mDB.Iter(); !iter.Done(); iter.Next()) {
    209    const nsACString& key = iter.Key();
    210 
    211    // Extract the origin attributes suffix from the key.
    212    int32_t colon = key.FindChar(':');
    213    MOZ_ASSERT(colon != kNotFound);
    214    nsDependentCSubstring oaSuffix = StringHead(key, colon);
    215 
    216    // Build the OriginAttributes object of it...
    217    OriginAttributes oa;
    218    DebugOnly<bool> rv = oa.PopulateFromSuffix(oaSuffix);
    219    MOZ_ASSERT(rv);
    220 
    221    // ...and match it against the given pattern.
    222    if (pattern.Matches(oa)) {
    223      iter.Remove();
    224    }
    225  }
    226 }
    227 
    228 void nsHttpAuthCache::CollectKeys(nsTArray<nsCString>& aValue) {
    229  AppendToArray(aValue, mDB.Keys());
    230 }
    231 
    232 //-----------------------------------------------------------------------------
    233 // nsHttpAuthIdentity
    234 //-----------------------------------------------------------------------------
    235 
    236 void nsHttpAuthIdentity::Clear() {
    237  mUser.Truncate();
    238  mPass.Truncate();
    239  mDomain.Truncate();
    240 }
    241 
    242 bool nsHttpAuthIdentity::Equals(const nsHttpAuthIdentity& ident) const {
    243  // we could probably optimize this with a single loop, but why bother?
    244  return mUser == ident.mUser && mPass == ident.mPass &&
    245         mDomain == ident.mDomain;
    246 }
    247 
    248 NS_IMPL_ISUPPORTS(AuthIdentity, nsIHttpAuthIdentity)
    249 
    250 NS_IMETHODIMP
    251 AuthIdentity::GetDomain(nsAString& aDomain) {
    252  aDomain = mIdent.Domain();
    253  return NS_OK;
    254 }
    255 
    256 NS_IMETHODIMP
    257 AuthIdentity::GetUser(nsAString& aUser) {
    258  aUser = mIdent.User();
    259  return NS_OK;
    260 }
    261 
    262 NS_IMETHODIMP
    263 AuthIdentity::GetPassword(nsAString& aPassword) {
    264  aPassword = mIdent.Password();
    265  return NS_OK;
    266 }
    267 
    268 //-----------------------------------------------------------------------------
    269 // nsHttpAuthEntry
    270 //-----------------------------------------------------------------------------
    271 NS_IMPL_ISUPPORTS(nsHttpAuthEntry, nsIHttpAuthEntry)
    272 
    273 NS_IMETHODIMP
    274 nsHttpAuthEntry::GetRealm(nsACString& aRealm) {
    275  aRealm = mRealm;
    276  return NS_OK;
    277 }
    278 
    279 NS_IMETHODIMP
    280 nsHttpAuthEntry::GetCreds(nsACString& aCreds) {
    281  aCreds = mCreds;
    282  return NS_OK;
    283 }
    284 
    285 NS_IMETHODIMP
    286 nsHttpAuthEntry::GetChallenge(nsACString& aChallenge) {
    287  aChallenge = mChallenge;
    288  return NS_OK;
    289 }
    290 
    291 NS_IMETHODIMP
    292 nsHttpAuthEntry::GetDomain(nsAString& aDomain) {
    293  aDomain = mIdent.Domain();
    294  return NS_OK;
    295 }
    296 
    297 NS_IMETHODIMP
    298 nsHttpAuthEntry::GetUser(nsAString& aUser) {
    299  aUser = mIdent.User();
    300  return NS_OK;
    301 }
    302 
    303 NS_IMETHODIMP
    304 nsHttpAuthEntry::GetPassword(nsAString& aPass) {
    305  aPass = mIdent.Password();
    306  return NS_OK;
    307 }
    308 
    309 NS_IMETHODIMP
    310 nsHttpAuthEntry::GetIdentity(nsIHttpAuthIdentity** aIdentity) {
    311  NS_ENSURE_ARG_POINTER(aIdentity);
    312  RefPtr<nsIHttpAuthIdentity> ident = new AuthIdentity(mIdent);
    313  ident.forget(aIdentity);
    314  return NS_OK;
    315 }
    316 
    317 nsresult nsHttpAuthEntry::AddPath(const nsACString& aPath) {
    318  for (const auto& p : mPaths) {
    319    if (StringBeginsWith(aPath, p)) {
    320      return NS_OK;  // subpath already exists in the list
    321    }
    322  }
    323 
    324  mPaths.AppendElement(aPath);
    325  return NS_OK;
    326 }
    327 
    328 nsresult nsHttpAuthEntry::Set(const nsACString& path, const nsACString& realm,
    329                              const nsACString& creds, const nsACString& chall,
    330                              const nsHttpAuthIdentity* ident,
    331                              nsISupports* metadata) {
    332  if (ident) {
    333    mIdent = *ident;
    334  } else if (mIdent.IsEmpty()) {
    335    // If we are not given an identity and our cached identity has not been
    336    // initialized yet (so is currently empty), initialize it now by
    337    // filling it with nulls.  We need to do that because consumers expect
    338    // that mIdent is initialized after this function returns.
    339    mIdent.Clear();
    340  }
    341 
    342  nsresult rv = AddPath(path);
    343  if (NS_FAILED(rv)) {
    344    return rv;
    345  }
    346 
    347  mRealm = realm;
    348  mCreds = creds;
    349  mChallenge = chall;
    350  mMetaData = metadata;
    351 
    352  return NS_OK;
    353 }
    354 
    355 //-----------------------------------------------------------------------------
    356 // nsHttpAuthNode
    357 //-----------------------------------------------------------------------------
    358 
    359 nsHttpAuthNode::nsHttpAuthNode() {
    360  LOG(("Creating nsHttpAuthNode @%p\n", this));
    361 }
    362 
    363 nsHttpAuthNode::~nsHttpAuthNode() {
    364  LOG(("Destroying nsHttpAuthNode @%p\n", this));
    365 
    366  mList.Clear();
    367 }
    368 
    369 nsHttpAuthEntry* nsHttpAuthNode::LookupEntryByPath(const nsACString& aPath) {
    370  // look for an entry that either matches or contains this directory.
    371  // ie. we'll give out credentials if the given directory is a sub-
    372  // directory of an existing entry.
    373  for (uint32_t i = 0; i < mList.Length(); ++i) {
    374    const auto& entry = mList[i];
    375 
    376    for (const auto& entryPath : entry->mPaths) {
    377      // proxy auth entries have no path, so require exact match on
    378      // empty path string.
    379      if (entryPath.IsEmpty()) {
    380        if (aPath.IsEmpty()) {
    381          return entry.get();
    382        }
    383      } else if (StringBeginsWith(aPath, entryPath)) {
    384        return entry.get();
    385      }
    386    }
    387  }
    388  return nullptr;
    389 }
    390 
    391 nsHttpAuthNode::EntryList::const_iterator nsHttpAuthNode::LookupEntryItrByRealm(
    392    const nsACString& realm) const {
    393  return std::find_if(mList.cbegin(), mList.cend(), [&realm](const auto& val) {
    394    return realm.Equals(val->Realm());
    395  });
    396 }
    397 
    398 nsHttpAuthEntry* nsHttpAuthNode::LookupEntryByRealm(const nsACString& realm) {
    399  auto itr = LookupEntryItrByRealm(realm);
    400  if (itr != mList.cend()) {
    401    return itr->get();
    402  }
    403 
    404  return nullptr;
    405 }
    406 
    407 nsresult nsHttpAuthNode::SetAuthEntry(const nsACString& path,
    408                                      const nsACString& realm,
    409                                      const nsACString& creds,
    410                                      const nsACString& challenge,
    411                                      const nsHttpAuthIdentity* ident,
    412                                      nsISupports* metadata) {
    413  // look for an entry with a matching realm
    414  RefPtr<nsHttpAuthEntry> entry = LookupEntryByRealm(realm);
    415  if (!entry) {
    416    // We want the latest identity be at the begining of the list so that
    417    // the newest working credentials are sent first on new requests.
    418    // Changing a realm is sometimes used to "timeout" authrozization.
    419    entry = new nsHttpAuthEntry(path, realm, creds, challenge, ident, metadata);
    420    mList.InsertElementAt(0, entry);
    421  } else {
    422    // update the entry...
    423    nsresult rv = entry->Set(path, realm, creds, challenge, ident, metadata);
    424    NS_ENSURE_SUCCESS(rv, rv);
    425  }
    426 
    427  return NS_OK;
    428 }
    429 
    430 void nsHttpAuthNode::ClearAuthEntry(const nsACString& realm) {
    431  auto idx = LookupEntryItrByRealm(realm);
    432  if (idx != mList.cend()) {
    433    mList.RemoveElementAt(idx);
    434  }
    435 }
    436 
    437 }  // namespace net
    438 }  // namespace mozilla