Coverage Report

Created: 2018-09-25 14:53

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