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