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