nsHttpAuthCache.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608
  1. /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
  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. // HttpLog.h should generally be included first
  6. #include "HttpLog.h"
  7. #include "nsHttpAuthCache.h"
  8. #include <stdlib.h>
  9. #include "mozilla/Attributes.h"
  10. #include "nsString.h"
  11. #include "nsCRT.h"
  12. #include "mozIApplicationClearPrivateDataParams.h"
  13. #include "nsIObserverService.h"
  14. #include "mozilla/Services.h"
  15. #include "mozilla/DebugOnly.h"
  16. #include "nsNetUtil.h"
  17. namespace mozilla {
  18. namespace net {
  19. static inline void
  20. GetAuthKey(const char *scheme, const char *host, int32_t port, nsACString const &originSuffix, nsCString &key)
  21. {
  22. key.Truncate();
  23. key.Append(originSuffix);
  24. key.Append(':');
  25. key.Append(scheme);
  26. key.AppendLiteral("://");
  27. key.Append(host);
  28. key.Append(':');
  29. key.AppendInt(port);
  30. }
  31. // return true if the two strings are equal or both empty. an empty string
  32. // is either null or zero length.
  33. static bool
  34. StrEquivalent(const char16_t *a, const char16_t *b)
  35. {
  36. static const char16_t emptyStr[] = {0};
  37. if (!a)
  38. a = emptyStr;
  39. if (!b)
  40. b = emptyStr;
  41. return nsCRT::strcmp(a, b) == 0;
  42. }
  43. //-----------------------------------------------------------------------------
  44. // nsHttpAuthCache <public>
  45. //-----------------------------------------------------------------------------
  46. nsHttpAuthCache::nsHttpAuthCache()
  47. : mDB(nullptr)
  48. , mObserver(new OriginClearObserver(this))
  49. {
  50. nsCOMPtr<nsIObserverService> obsSvc = services::GetObserverService();
  51. if (obsSvc) {
  52. obsSvc->AddObserver(mObserver, "clear-origin-attributes-data", false);
  53. }
  54. }
  55. nsHttpAuthCache::~nsHttpAuthCache()
  56. {
  57. if (mDB)
  58. ClearAll();
  59. nsCOMPtr<nsIObserverService> obsSvc = services::GetObserverService();
  60. if (obsSvc) {
  61. obsSvc->RemoveObserver(mObserver, "clear-origin-attributes-data");
  62. mObserver->mOwner = nullptr;
  63. }
  64. }
  65. nsresult
  66. nsHttpAuthCache::Init()
  67. {
  68. NS_ENSURE_TRUE(!mDB, NS_ERROR_ALREADY_INITIALIZED);
  69. LOG(("nsHttpAuthCache::Init\n"));
  70. mDB = PL_NewHashTable(128, (PLHashFunction) PL_HashString,
  71. (PLHashComparator) PL_CompareStrings,
  72. (PLHashComparator) 0, &gHashAllocOps, this);
  73. if (!mDB)
  74. return NS_ERROR_OUT_OF_MEMORY;
  75. return NS_OK;
  76. }
  77. nsresult
  78. nsHttpAuthCache::GetAuthEntryForPath(const char *scheme,
  79. const char *host,
  80. int32_t port,
  81. const char *path,
  82. nsACString const &originSuffix,
  83. nsHttpAuthEntry **entry)
  84. {
  85. LOG(("nsHttpAuthCache::GetAuthEntryForPath [key=%s://%s:%d path=%s]\n",
  86. scheme, host, port, path));
  87. nsAutoCString key;
  88. nsHttpAuthNode *node = LookupAuthNode(scheme, host, port, originSuffix, key);
  89. if (!node)
  90. return NS_ERROR_NOT_AVAILABLE;
  91. *entry = node->LookupEntryByPath(path);
  92. return *entry ? NS_OK : NS_ERROR_NOT_AVAILABLE;
  93. }
  94. nsresult
  95. nsHttpAuthCache::GetAuthEntryForDomain(const char *scheme,
  96. const char *host,
  97. int32_t port,
  98. const char *realm,
  99. nsACString const &originSuffix,
  100. nsHttpAuthEntry **entry)
  101. {
  102. LOG(("nsHttpAuthCache::GetAuthEntryForDomain [key=%s://%s:%d realm=%s]\n",
  103. scheme, host, port, realm));
  104. nsAutoCString key;
  105. nsHttpAuthNode *node = LookupAuthNode(scheme, host, port, originSuffix, key);
  106. if (!node)
  107. return NS_ERROR_NOT_AVAILABLE;
  108. *entry = node->LookupEntryByRealm(realm);
  109. return *entry ? NS_OK : NS_ERROR_NOT_AVAILABLE;
  110. }
  111. nsresult
  112. nsHttpAuthCache::SetAuthEntry(const char *scheme,
  113. const char *host,
  114. int32_t port,
  115. const char *path,
  116. const char *realm,
  117. const char *creds,
  118. const char *challenge,
  119. nsACString const &originSuffix,
  120. const nsHttpAuthIdentity *ident,
  121. nsISupports *metadata)
  122. {
  123. nsresult rv;
  124. LOG(("nsHttpAuthCache::SetAuthEntry [key=%s://%s:%d realm=%s path=%s metadata=%x]\n",
  125. scheme, host, port, realm, path, metadata));
  126. if (!mDB) {
  127. rv = Init();
  128. if (NS_FAILED(rv)) return rv;
  129. }
  130. nsAutoCString key;
  131. nsHttpAuthNode *node = LookupAuthNode(scheme, host, port, originSuffix, key);
  132. if (!node) {
  133. // create a new entry node and set the given entry
  134. node = new nsHttpAuthNode();
  135. if (!node)
  136. return NS_ERROR_OUT_OF_MEMORY;
  137. rv = node->SetAuthEntry(path, realm, creds, challenge, ident, metadata);
  138. if (NS_FAILED(rv))
  139. delete node;
  140. else
  141. PL_HashTableAdd(mDB, strdup(key.get()), node);
  142. return rv;
  143. }
  144. return node->SetAuthEntry(path, realm, creds, challenge, ident, metadata);
  145. }
  146. void
  147. nsHttpAuthCache::ClearAuthEntry(const char *scheme,
  148. const char *host,
  149. int32_t port,
  150. const char *realm,
  151. nsACString const &originSuffix)
  152. {
  153. if (!mDB)
  154. return;
  155. nsAutoCString key;
  156. GetAuthKey(scheme, host, port, originSuffix, key);
  157. PL_HashTableRemove(mDB, key.get());
  158. }
  159. nsresult
  160. nsHttpAuthCache::ClearAll()
  161. {
  162. LOG(("nsHttpAuthCache::ClearAll\n"));
  163. if (mDB) {
  164. PL_HashTableDestroy(mDB);
  165. mDB = 0;
  166. }
  167. return NS_OK;
  168. }
  169. //-----------------------------------------------------------------------------
  170. // nsHttpAuthCache <private>
  171. //-----------------------------------------------------------------------------
  172. nsHttpAuthNode *
  173. nsHttpAuthCache::LookupAuthNode(const char *scheme,
  174. const char *host,
  175. int32_t port,
  176. nsACString const &originSuffix,
  177. nsCString &key)
  178. {
  179. if (!mDB)
  180. return nullptr;
  181. GetAuthKey(scheme, host, port, originSuffix, key);
  182. return (nsHttpAuthNode *) PL_HashTableLookup(mDB, key.get());
  183. }
  184. void *
  185. nsHttpAuthCache::AllocTable(void *self, size_t size)
  186. {
  187. return malloc(size);
  188. }
  189. void
  190. nsHttpAuthCache::FreeTable(void *self, void *item)
  191. {
  192. free(item);
  193. }
  194. PLHashEntry *
  195. nsHttpAuthCache::AllocEntry(void *self, const void *key)
  196. {
  197. return (PLHashEntry *) malloc(sizeof(PLHashEntry));
  198. }
  199. void
  200. nsHttpAuthCache::FreeEntry(void *self, PLHashEntry *he, unsigned flag)
  201. {
  202. if (flag == HT_FREE_VALUE) {
  203. // this would only happen if PL_HashTableAdd were to replace an
  204. // existing entry in the hash table, but we _always_ do a lookup
  205. // before adding a new entry to avoid this case.
  206. NS_NOTREACHED("should never happen");
  207. }
  208. else if (flag == HT_FREE_ENTRY) {
  209. // three wonderful flavors of freeing memory ;-)
  210. delete (nsHttpAuthNode *) he->value;
  211. free((char *) he->key);
  212. free(he);
  213. }
  214. }
  215. PLHashAllocOps nsHttpAuthCache::gHashAllocOps =
  216. {
  217. nsHttpAuthCache::AllocTable,
  218. nsHttpAuthCache::FreeTable,
  219. nsHttpAuthCache::AllocEntry,
  220. nsHttpAuthCache::FreeEntry
  221. };
  222. NS_IMPL_ISUPPORTS(nsHttpAuthCache::OriginClearObserver, nsIObserver)
  223. NS_IMETHODIMP
  224. nsHttpAuthCache::OriginClearObserver::Observe(nsISupports *subject,
  225. const char * topic,
  226. const char16_t * data_unicode)
  227. {
  228. NS_ENSURE_TRUE(mOwner, NS_ERROR_NOT_AVAILABLE);
  229. OriginAttributesPattern pattern;
  230. if (!pattern.Init(nsDependentString(data_unicode))) {
  231. NS_ERROR("Cannot parse origin attributes pattern");
  232. return NS_ERROR_FAILURE;
  233. }
  234. mOwner->ClearOriginData(pattern);
  235. return NS_OK;
  236. }
  237. static int
  238. RemoveEntriesForPattern(PLHashEntry *entry, int32_t number, void *arg)
  239. {
  240. nsDependentCString key(static_cast<const char*>(entry->key));
  241. // Extract the origin attributes suffix from the key.
  242. int32_t colon = key.Find(NS_LITERAL_CSTRING(":"));
  243. MOZ_ASSERT(colon != kNotFound);
  244. nsDependentCSubstring oaSuffix;
  245. oaSuffix.Rebind(key.BeginReading(), colon);
  246. // Build the NeckoOriginAttributes object of it...
  247. NeckoOriginAttributes oa;
  248. DebugOnly<bool> rv = oa.PopulateFromSuffix(oaSuffix);
  249. MOZ_ASSERT(rv);
  250. // ...and match it against the given pattern.
  251. OriginAttributesPattern const *pattern = static_cast<OriginAttributesPattern const*>(arg);
  252. if (pattern->Matches(oa)) {
  253. return HT_ENUMERATE_NEXT | HT_ENUMERATE_REMOVE;
  254. }
  255. return HT_ENUMERATE_NEXT;
  256. }
  257. void
  258. nsHttpAuthCache::ClearOriginData(OriginAttributesPattern const &pattern)
  259. {
  260. if (!mDB) {
  261. return;
  262. }
  263. PL_HashTableEnumerateEntries(mDB, RemoveEntriesForPattern, (void*)&pattern);
  264. }
  265. //-----------------------------------------------------------------------------
  266. // nsHttpAuthIdentity
  267. //-----------------------------------------------------------------------------
  268. nsresult
  269. nsHttpAuthIdentity::Set(const char16_t *domain,
  270. const char16_t *user,
  271. const char16_t *pass)
  272. {
  273. char16_t *newUser, *newPass, *newDomain;
  274. int domainLen = domain ? NS_strlen(domain) : 0;
  275. int userLen = user ? NS_strlen(user) : 0;
  276. int passLen = pass ? NS_strlen(pass) : 0;
  277. int len = userLen + 1 + passLen + 1 + domainLen + 1;
  278. newUser = (char16_t *) malloc(len * sizeof(char16_t));
  279. if (!newUser)
  280. return NS_ERROR_OUT_OF_MEMORY;
  281. if (user)
  282. memcpy(newUser, user, userLen * sizeof(char16_t));
  283. newUser[userLen] = 0;
  284. newPass = &newUser[userLen + 1];
  285. if (pass)
  286. memcpy(newPass, pass, passLen * sizeof(char16_t));
  287. newPass[passLen] = 0;
  288. newDomain = &newPass[passLen + 1];
  289. if (domain)
  290. memcpy(newDomain, domain, domainLen * sizeof(char16_t));
  291. newDomain[domainLen] = 0;
  292. // wait until the end to clear member vars in case input params
  293. // reference our members!
  294. if (mUser)
  295. free(mUser);
  296. mUser = newUser;
  297. mPass = newPass;
  298. mDomain = newDomain;
  299. return NS_OK;
  300. }
  301. void
  302. nsHttpAuthIdentity::Clear()
  303. {
  304. if (mUser) {
  305. free(mUser);
  306. mUser = nullptr;
  307. mPass = nullptr;
  308. mDomain = nullptr;
  309. }
  310. }
  311. bool
  312. nsHttpAuthIdentity::Equals(const nsHttpAuthIdentity &ident) const
  313. {
  314. // we could probably optimize this with a single loop, but why bother?
  315. return StrEquivalent(mUser, ident.mUser) &&
  316. StrEquivalent(mPass, ident.mPass) &&
  317. StrEquivalent(mDomain, ident.mDomain);
  318. }
  319. //-----------------------------------------------------------------------------
  320. // nsHttpAuthEntry
  321. //-----------------------------------------------------------------------------
  322. nsHttpAuthEntry::~nsHttpAuthEntry()
  323. {
  324. if (mRealm)
  325. free(mRealm);
  326. while (mRoot) {
  327. nsHttpAuthPath *ap = mRoot;
  328. mRoot = mRoot->mNext;
  329. free(ap);
  330. }
  331. }
  332. nsresult
  333. nsHttpAuthEntry::AddPath(const char *aPath)
  334. {
  335. // null path matches empty path
  336. if (!aPath)
  337. aPath = "";
  338. nsHttpAuthPath *tempPtr = mRoot;
  339. while (tempPtr) {
  340. const char *curpath = tempPtr->mPath;
  341. if (strncmp(aPath, curpath, strlen(curpath)) == 0)
  342. return NS_OK; // subpath already exists in the list
  343. tempPtr = tempPtr->mNext;
  344. }
  345. //Append the aPath
  346. nsHttpAuthPath *newAuthPath;
  347. int newpathLen = strlen(aPath);
  348. newAuthPath = (nsHttpAuthPath *) malloc(sizeof(nsHttpAuthPath) + newpathLen);
  349. if (!newAuthPath)
  350. return NS_ERROR_OUT_OF_MEMORY;
  351. memcpy(newAuthPath->mPath, aPath, newpathLen+1);
  352. newAuthPath->mNext = nullptr;
  353. if (!mRoot)
  354. mRoot = newAuthPath; //first entry
  355. else
  356. mTail->mNext = newAuthPath; // Append newAuthPath
  357. //update the tail pointer.
  358. mTail = newAuthPath;
  359. return NS_OK;
  360. }
  361. nsresult
  362. nsHttpAuthEntry::Set(const char *path,
  363. const char *realm,
  364. const char *creds,
  365. const char *chall,
  366. const nsHttpAuthIdentity *ident,
  367. nsISupports *metadata)
  368. {
  369. char *newRealm, *newCreds, *newChall;
  370. int realmLen = realm ? strlen(realm) : 0;
  371. int credsLen = creds ? strlen(creds) : 0;
  372. int challLen = chall ? strlen(chall) : 0;
  373. int len = realmLen + 1 + credsLen + 1 + challLen + 1;
  374. newRealm = (char *) malloc(len);
  375. if (!newRealm)
  376. return NS_ERROR_OUT_OF_MEMORY;
  377. if (realm)
  378. memcpy(newRealm, realm, realmLen);
  379. newRealm[realmLen] = 0;
  380. newCreds = &newRealm[realmLen + 1];
  381. if (creds)
  382. memcpy(newCreds, creds, credsLen);
  383. newCreds[credsLen] = 0;
  384. newChall = &newCreds[credsLen + 1];
  385. if (chall)
  386. memcpy(newChall, chall, challLen);
  387. newChall[challLen] = 0;
  388. nsresult rv = NS_OK;
  389. if (ident) {
  390. rv = mIdent.Set(*ident);
  391. }
  392. else if (mIdent.IsEmpty()) {
  393. // If we are not given an identity and our cached identity has not been
  394. // initialized yet (so is currently empty), initialize it now by
  395. // filling it with nulls. We need to do that because consumers expect
  396. // that mIdent is initialized after this function returns.
  397. rv = mIdent.Set(nullptr, nullptr, nullptr);
  398. }
  399. if (NS_FAILED(rv)) {
  400. free(newRealm);
  401. return rv;
  402. }
  403. rv = AddPath(path);
  404. if (NS_FAILED(rv)) {
  405. free(newRealm);
  406. return rv;
  407. }
  408. // wait until the end to clear member vars in case input params
  409. // reference our members!
  410. if (mRealm)
  411. free(mRealm);
  412. mRealm = newRealm;
  413. mCreds = newCreds;
  414. mChallenge = newChall;
  415. mMetaData = metadata;
  416. return NS_OK;
  417. }
  418. //-----------------------------------------------------------------------------
  419. // nsHttpAuthNode
  420. //-----------------------------------------------------------------------------
  421. nsHttpAuthNode::nsHttpAuthNode()
  422. {
  423. LOG(("Creating nsHttpAuthNode @%x\n", this));
  424. }
  425. nsHttpAuthNode::~nsHttpAuthNode()
  426. {
  427. LOG(("Destroying nsHttpAuthNode @%x\n", this));
  428. mList.Clear();
  429. }
  430. nsHttpAuthEntry *
  431. nsHttpAuthNode::LookupEntryByPath(const char *path)
  432. {
  433. nsHttpAuthEntry *entry;
  434. // null path matches empty path
  435. if (!path)
  436. path = "";
  437. // look for an entry that either matches or contains this directory.
  438. // ie. we'll give out credentials if the given directory is a sub-
  439. // directory of an existing entry.
  440. for (uint32_t i=0; i<mList.Length(); ++i) {
  441. entry = mList[i];
  442. nsHttpAuthPath *authPath = entry->RootPath();
  443. while (authPath) {
  444. const char *entryPath = authPath->mPath;
  445. // proxy auth entries have no path, so require exact match on
  446. // empty path string.
  447. if (entryPath[0] == '\0') {
  448. if (path[0] == '\0')
  449. return entry;
  450. }
  451. else if (strncmp(path, entryPath, strlen(entryPath)) == 0)
  452. return entry;
  453. authPath = authPath->mNext;
  454. }
  455. }
  456. return nullptr;
  457. }
  458. nsHttpAuthEntry *
  459. nsHttpAuthNode::LookupEntryByRealm(const char *realm)
  460. {
  461. nsHttpAuthEntry *entry;
  462. // null realm matches empty realm
  463. if (!realm)
  464. realm = "";
  465. // look for an entry that matches this realm
  466. uint32_t i;
  467. for (i=0; i<mList.Length(); ++i) {
  468. entry = mList[i];
  469. if (strcmp(realm, entry->Realm()) == 0)
  470. return entry;
  471. }
  472. return nullptr;
  473. }
  474. nsresult
  475. nsHttpAuthNode::SetAuthEntry(const char *path,
  476. const char *realm,
  477. const char *creds,
  478. const char *challenge,
  479. const nsHttpAuthIdentity *ident,
  480. nsISupports *metadata)
  481. {
  482. // look for an entry with a matching realm
  483. nsHttpAuthEntry *entry = LookupEntryByRealm(realm);
  484. if (!entry) {
  485. entry = new nsHttpAuthEntry(path, realm, creds, challenge, ident, metadata);
  486. if (!entry)
  487. return NS_ERROR_OUT_OF_MEMORY;
  488. // We want the latest identity be at the begining of the list so that
  489. // the newest working credentials are sent first on new requests.
  490. // Changing a realm is sometimes used to "timeout" authrozization.
  491. mList.InsertElementAt(0, entry);
  492. }
  493. else {
  494. // update the entry...
  495. entry->Set(path, realm, creds, challenge, ident, metadata);
  496. }
  497. return NS_OK;
  498. }
  499. void
  500. nsHttpAuthNode::ClearAuthEntry(const char *realm)
  501. {
  502. nsHttpAuthEntry *entry = LookupEntryByRealm(realm);
  503. if (entry) {
  504. mList.RemoveElement(entry); // double search OK
  505. }
  506. }
  507. } // namespace net
  508. } // namespace mozilla