Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/security/manager/ssl/nsSiteSecurityService.cpp
Line
Count
Source (jump to first uncovered line)
1
/* This Source Code Form is subject to the terms of the Mozilla Public
2
 * License, v. 2.0. If a copy of the MPL was not distributed with this
3
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5
#include "nsSiteSecurityService.h"
6
7
#include "CertVerifier.h"
8
#include "PublicKeyPinningService.h"
9
#include "ScopedNSSTypes.h"
10
#include "SharedCertVerifier.h"
11
#include "mozilla/Assertions.h"
12
#include "mozilla/Attributes.h"
13
#include "mozilla/Base64.h"
14
#include "mozilla/LinkedList.h"
15
#include "mozilla/Logging.h"
16
#include "mozilla/Preferences.h"
17
#include "mozilla/Tokenizer.h"
18
#include "mozilla/dom/PContent.h"
19
#include "mozilla/dom/ToJSValue.h"
20
#include "nsArrayEnumerator.h"
21
#include "nsCOMArray.h"
22
#include "nsIScriptSecurityManager.h"
23
#include "nsISocketProvider.h"
24
#include "nsITransportSecurityInfo.h"
25
#include "nsIURI.h"
26
#include "nsIX509Cert.h"
27
#include "nsNSSComponent.h"
28
#include "nsNetUtil.h"
29
#include "nsPromiseFlatString.h"
30
#include "nsReadableUtils.h"
31
#include "nsSecurityHeaderParser.h"
32
#include "nsThreadUtils.h"
33
#include "nsVariant.h"
34
#include "nsXULAppAPI.h"
35
#include "prnetdb.h"
36
37
// A note about the preload list:
38
// When a site specifically disables HSTS by sending a header with
39
// 'max-age: 0', we keep a "knockout" value that means "we have no information
40
// regarding the HSTS state of this host" (any ancestor of "this host" can still
41
// influence its HSTS status via include subdomains, however).
42
// This prevents the preload list from overriding the site's current
43
// desired HSTS status.
44
#include "nsSTSPreloadList.h"
45
46
using namespace mozilla;
47
using namespace mozilla::psm;
48
49
static LazyLogModule gSSSLog("nsSSService");
50
51
0
#define SSSLOG(args) MOZ_LOG(gSSSLog, mozilla::LogLevel::Debug, args)
52
53
const char kHSTSKeySuffix[] = ":HSTS";
54
const char kHPKPKeySuffix[] = ":HPKP";
55
56
////////////////////////////////////////////////////////////////////////////////
57
58
NS_IMPL_ISUPPORTS(SiteHSTSState, nsISiteSecurityState, nsISiteHSTSState)
59
60
namespace {
61
62
static bool
63
0
stringIsBase64EncodingOf256bitValue(const nsCString& encodedString) {
64
0
  nsAutoCString binaryValue;
65
0
  nsresult rv = Base64Decode(encodedString, binaryValue);
66
0
  if (NS_FAILED(rv)) {
67
0
    return false;
68
0
  }
69
0
70
0
  return binaryValue.Length() == SHA256_LENGTH;
71
0
}
72
73
class SSSTokenizer final : public Tokenizer
74
{
75
public:
76
  explicit SSSTokenizer(const nsACString& source)
77
    : Tokenizer(source)
78
0
  {
79
0
  }
80
81
  MOZ_MUST_USE bool
82
  ReadBool(/*out*/ bool& value)
83
0
  {
84
0
    uint8_t rawValue;
85
0
    if (!ReadInteger(&rawValue)) {
86
0
      return false;
87
0
    }
88
0
89
0
    if (rawValue != 0 && rawValue != 1) {
90
0
      return false;
91
0
    }
92
0
93
0
    value = (rawValue == 1);
94
0
    return true;
95
0
  }
96
97
  MOZ_MUST_USE bool
98
  ReadState(/*out*/ SecurityPropertyState& state)
99
0
  {
100
0
    uint32_t rawValue;
101
0
    if (!ReadInteger(&rawValue)) {
102
0
      return false;
103
0
    }
104
0
105
0
    state = static_cast<SecurityPropertyState>(rawValue);
106
0
    switch (state) {
107
0
      case SecurityPropertyKnockout:
108
0
      case SecurityPropertyNegative:
109
0
      case SecurityPropertySet:
110
0
      case SecurityPropertyUnset:
111
0
        break;
112
0
      default:
113
0
        return false;
114
0
    }
115
0
116
0
    return true;
117
0
  }
118
119
  MOZ_MUST_USE bool
120
  ReadSource(/*out*/ SecurityPropertySource& source)
121
0
  {
122
0
    uint32_t rawValue;
123
0
    if (!ReadInteger(&rawValue)) {
124
0
      return false;
125
0
    }
126
0
127
0
    source = static_cast<SecurityPropertySource>(rawValue);
128
0
    switch (source) {
129
0
      case SourceUnknown:
130
0
      case SourcePreload:
131
0
      case SourceOrganic:
132
0
        break;
133
0
      default:
134
0
        return false;
135
0
    }
136
0
137
0
    return true;
138
0
  }
139
140
  // Note: Ideally, this method would be able to read SHA256 strings without
141
  // reading all the way to EOF. Unfortunately, if a token starts with digits
142
  // mozilla::Tokenizer will by default not consider the digits part of the
143
  // string. This can be worked around by making mozilla::Tokenizer consider
144
  // digit characters as "word" characters as well, but this can't be changed at
145
  // run time, meaning parsing digits as a number will fail.
146
  MOZ_MUST_USE bool
147
  ReadUntilEOFAsSHA256Keys(/*out*/ nsTArray<nsCString>& keys)
148
0
  {
149
0
    nsAutoCString mergedKeys;
150
0
    if (!ReadUntil(Token::EndOfFile(), mergedKeys, EXCLUDE_LAST)) {
151
0
      return false;
152
0
    }
153
0
154
0
    // This check makes sure the Substring() calls below are always valid.
155
0
    static const uint32_t SHA256Base64Len = 44;
156
0
    if (mergedKeys.Length() % SHA256Base64Len != 0) {
157
0
      return false;
158
0
    }
159
0
160
0
    for (uint32_t i = 0; i < mergedKeys.Length(); i += SHA256Base64Len) {
161
0
      nsAutoCString key(Substring(mergedKeys, i, SHA256Base64Len));
162
0
      if (!stringIsBase64EncodingOf256bitValue(key)) {
163
0
        return false;
164
0
      }
165
0
      keys.AppendElement(key);
166
0
    }
167
0
168
0
    return !keys.IsEmpty();
169
0
  }
170
};
171
172
// Parses a state string like "1500918564034,1,1" into its constituent parts.
173
bool
174
ParseHSTSState(const nsCString& stateString,
175
       /*out*/ PRTime& expireTime,
176
       /*out*/ SecurityPropertyState& state,
177
       /*out*/ bool& includeSubdomains,
178
       /*out*/ SecurityPropertySource& source)
179
0
{
180
0
  SSSTokenizer tokenizer(stateString);
181
0
  SSSLOG(("Parsing state from %s", stateString.get()));
182
0
183
0
  if (!tokenizer.ReadInteger(&expireTime)) {
184
0
    return false;
185
0
  }
186
0
187
0
  if (!tokenizer.CheckChar(',')) {
188
0
    return false;
189
0
  }
190
0
191
0
  if (!tokenizer.ReadState(state)) {
192
0
    return false;
193
0
  }
194
0
195
0
  if (!tokenizer.CheckChar(',')) {
196
0
    return false;
197
0
  }
198
0
199
0
  if (!tokenizer.ReadBool(includeSubdomains)) {
200
0
    return false;
201
0
  }
202
0
203
0
  source = SourceUnknown;
204
0
  if (tokenizer.CheckChar(',')) {
205
0
    if (!tokenizer.ReadSource(source)) {
206
0
      return false;
207
0
    }
208
0
  }
209
0
210
0
  return tokenizer.CheckEOF();
211
0
}
212
213
} // namespace
214
215
SiteHSTSState::SiteHSTSState(const nsCString& aHost,
216
                             const OriginAttributes& aOriginAttributes,
217
                             const nsCString& aStateString)
218
  : mHostname(aHost)
219
  , mOriginAttributes(aOriginAttributes)
220
  , mHSTSExpireTime(0)
221
  , mHSTSState(SecurityPropertyUnset)
222
  , mHSTSIncludeSubdomains(false)
223
  , mHSTSSource(SourceUnknown)
224
0
{
225
0
  bool valid = ParseHSTSState(aStateString, mHSTSExpireTime, mHSTSState,
226
0
                              mHSTSIncludeSubdomains, mHSTSSource);
227
0
  if (!valid) {
228
0
    SSSLOG(("%s is not a valid SiteHSTSState", aStateString.get()));
229
0
    mHSTSExpireTime = 0;
230
0
    mHSTSState = SecurityPropertyUnset;
231
0
    mHSTSIncludeSubdomains = false;
232
0
    mHSTSSource = SourceUnknown;
233
0
  }
234
0
}
235
236
SiteHSTSState::SiteHSTSState(const nsCString& aHost,
237
                             const OriginAttributes& aOriginAttributes,
238
                             PRTime aHSTSExpireTime,
239
                             SecurityPropertyState aHSTSState,
240
                             bool aHSTSIncludeSubdomains,
241
                             SecurityPropertySource aSource)
242
243
  : mHostname(aHost)
244
  , mOriginAttributes(aOriginAttributes)
245
  , mHSTSExpireTime(aHSTSExpireTime)
246
  , mHSTSState(aHSTSState)
247
  , mHSTSIncludeSubdomains(aHSTSIncludeSubdomains)
248
  , mHSTSSource(aSource)
249
0
{
250
0
}
251
252
void
253
SiteHSTSState::ToString(nsCString& aString)
254
0
{
255
0
  aString.Truncate();
256
0
  aString.AppendInt(mHSTSExpireTime);
257
0
  aString.Append(',');
258
0
  aString.AppendInt(mHSTSState);
259
0
  aString.Append(',');
260
0
  aString.AppendInt(static_cast<uint32_t>(mHSTSIncludeSubdomains));
261
0
  aString.Append(',');
262
0
  aString.AppendInt(mHSTSSource);
263
0
}
264
265
NS_IMETHODIMP
266
SiteHSTSState::GetHostname(nsACString& aHostname)
267
0
{
268
0
  aHostname = mHostname;
269
0
  return NS_OK;
270
0
}
271
272
NS_IMETHODIMP
273
SiteHSTSState::GetExpireTime(int64_t* aExpireTime)
274
0
{
275
0
  NS_ENSURE_ARG(aExpireTime);
276
0
  *aExpireTime = mHSTSExpireTime;
277
0
  return NS_OK;
278
0
}
279
280
NS_IMETHODIMP
281
SiteHSTSState::GetSecurityPropertyState(int16_t* aSecurityPropertyState)
282
0
{
283
0
  NS_ENSURE_ARG(aSecurityPropertyState);
284
0
  *aSecurityPropertyState = mHSTSState;
285
0
  return NS_OK;
286
0
}
287
288
NS_IMETHODIMP
289
SiteHSTSState::GetIncludeSubdomains(bool* aIncludeSubdomains)
290
0
{
291
0
  NS_ENSURE_ARG(aIncludeSubdomains);
292
0
  *aIncludeSubdomains = mHSTSIncludeSubdomains;
293
0
  return NS_OK;
294
0
}
295
296
NS_IMETHODIMP
297
SiteHSTSState::GetOriginAttributes(JSContext* aCx,
298
  JS::MutableHandle<JS::Value> aOriginAttributes)
299
0
{
300
0
  if (!ToJSValue(aCx, mOriginAttributes, aOriginAttributes)) {
301
0
    return NS_ERROR_FAILURE;
302
0
  }
303
0
  return NS_OK;
304
0
}
305
306
////////////////////////////////////////////////////////////////////////////////
307
308
NS_IMPL_ISUPPORTS(SiteHPKPState, nsISiteSecurityState, nsISiteHPKPState)
309
310
namespace {
311
312
// Parses a state string like
313
// "1494603034103,1,1,AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" into its
314
// constituent parts.
315
bool
316
ParseHPKPState(const nsCString& stateString,
317
       /*out*/ PRTime& expireTime,
318
       /*out*/ SecurityPropertyState& state,
319
       /*out*/ bool& includeSubdomains,
320
       /*out*/ nsTArray<nsCString>& sha256keys)
321
0
{
322
0
  SSSTokenizer tokenizer(stateString);
323
0
324
0
  if (!tokenizer.ReadInteger(&expireTime)) {
325
0
    return false;
326
0
  }
327
0
328
0
  if (!tokenizer.CheckChar(',')) {
329
0
    return false;
330
0
  }
331
0
332
0
  if (!tokenizer.ReadState(state)) {
333
0
    return false;
334
0
  }
335
0
336
0
  // SecurityPropertyNegative isn't a valid state for HPKP.
337
0
  switch (state) {
338
0
    case SecurityPropertyKnockout:
339
0
    case SecurityPropertySet:
340
0
    case SecurityPropertyUnset:
341
0
      break;
342
0
    case SecurityPropertyNegative:
343
0
    default:
344
0
      return false;
345
0
  }
346
0
347
0
  if (!tokenizer.CheckChar(',')) {
348
0
    return false;
349
0
  }
350
0
351
0
  if (!tokenizer.ReadBool(includeSubdomains)) {
352
0
    return false;
353
0
  }
354
0
355
0
  if (!tokenizer.CheckChar(',')) {
356
0
    return false;
357
0
  }
358
0
359
0
  if (state == SecurityPropertySet) {
360
0
    // This reads to the end of input, so there's no need to explicitly check
361
0
    // for EOF.
362
0
    return tokenizer.ReadUntilEOFAsSHA256Keys(sha256keys);
363
0
  }
364
0
365
0
  return tokenizer.CheckEOF();
366
0
}
367
368
} // namespace
369
370
SiteHPKPState::SiteHPKPState()
371
  : mExpireTime(0)
372
  , mState(SecurityPropertyUnset)
373
  , mIncludeSubdomains(false)
374
0
{
375
0
}
376
377
SiteHPKPState::SiteHPKPState(const nsCString& aHost,
378
                             const OriginAttributes& aOriginAttributes,
379
                             const nsCString& aStateString)
380
  : mHostname(aHost)
381
  , mOriginAttributes(aOriginAttributes)
382
  , mExpireTime(0)
383
  , mState(SecurityPropertyUnset)
384
  , mIncludeSubdomains(false)
385
0
{
386
0
  bool valid = ParseHPKPState(aStateString, mExpireTime, mState,
387
0
                              mIncludeSubdomains, mSHA256keys);
388
0
  if (!valid) {
389
0
    SSSLOG(("%s is not a valid SiteHPKPState", aStateString.get()));
390
0
    mExpireTime = 0;
391
0
    mState = SecurityPropertyUnset;
392
0
    mIncludeSubdomains = false;
393
0
    if (!mSHA256keys.IsEmpty()) {
394
0
      mSHA256keys.Clear();
395
0
    }
396
0
  }
397
0
}
398
399
SiteHPKPState::SiteHPKPState(const nsCString& aHost,
400
                             const OriginAttributes& aOriginAttributes,
401
                             PRTime aExpireTime,
402
                             SecurityPropertyState aState,
403
                             bool aIncludeSubdomains,
404
                             nsTArray<nsCString>& aSHA256keys)
405
  : mHostname(aHost)
406
  , mOriginAttributes(aOriginAttributes)
407
  , mExpireTime(aExpireTime)
408
  , mState(aState)
409
  , mIncludeSubdomains(aIncludeSubdomains)
410
  , mSHA256keys(aSHA256keys)
411
0
{
412
0
}
413
414
NS_IMETHODIMP
415
SiteHPKPState::GetHostname(nsACString& aHostname)
416
0
{
417
0
  aHostname = mHostname;
418
0
  return NS_OK;
419
0
}
420
421
NS_IMETHODIMP
422
SiteHPKPState::GetExpireTime(int64_t* aExpireTime)
423
0
{
424
0
  NS_ENSURE_ARG(aExpireTime);
425
0
  *aExpireTime = mExpireTime;
426
0
  return NS_OK;
427
0
}
428
429
NS_IMETHODIMP
430
SiteHPKPState::GetSecurityPropertyState(int16_t* aSecurityPropertyState)
431
0
{
432
0
  NS_ENSURE_ARG(aSecurityPropertyState);
433
0
  *aSecurityPropertyState = mState;
434
0
  return NS_OK;
435
0
}
436
437
NS_IMETHODIMP
438
SiteHPKPState::GetIncludeSubdomains(bool* aIncludeSubdomains)
439
0
{
440
0
  NS_ENSURE_ARG(aIncludeSubdomains);
441
0
  *aIncludeSubdomains = mIncludeSubdomains;
442
0
  return NS_OK;
443
0
}
444
445
void
446
SiteHPKPState::ToString(nsCString& aString)
447
0
{
448
0
  aString.Truncate();
449
0
  aString.AppendInt(mExpireTime);
450
0
  aString.Append(',');
451
0
  aString.AppendInt(mState);
452
0
  aString.Append(',');
453
0
  aString.AppendInt(static_cast<uint32_t>(mIncludeSubdomains));
454
0
  aString.Append(',');
455
0
  for (unsigned int i = 0; i < mSHA256keys.Length(); i++) {
456
0
    aString.Append(mSHA256keys[i]);
457
0
  }
458
0
}
459
460
NS_IMETHODIMP
461
SiteHPKPState::GetSha256Keys(nsISimpleEnumerator** aSha256Keys)
462
0
{
463
0
  NS_ENSURE_ARG(aSha256Keys);
464
0
465
0
  nsCOMArray<nsIVariant> keys;
466
0
  for (const nsCString& key : mSHA256keys) {
467
0
    nsCOMPtr<nsIWritableVariant> variant = new nsVariant();
468
0
    nsresult rv = variant->SetAsAUTF8String(key);
469
0
    if (NS_FAILED(rv)) {
470
0
      return rv;
471
0
    }
472
0
    if (!keys.AppendObject(variant)) {
473
0
      return NS_ERROR_FAILURE;
474
0
    }
475
0
  }
476
0
  return NS_NewArrayEnumerator(aSha256Keys, keys, NS_GET_IID(nsIVariant));
477
0
}
478
479
NS_IMETHODIMP
480
SiteHPKPState::GetOriginAttributes(JSContext* aCx,
481
  JS::MutableHandle<JS::Value> aOriginAttributes)
482
0
{
483
0
  if (!ToJSValue(aCx, mOriginAttributes, aOriginAttributes)) {
484
0
    return NS_ERROR_FAILURE;
485
0
  }
486
0
  return NS_OK;
487
0
}
488
489
////////////////////////////////////////////////////////////////////////////////
490
491
const uint64_t kSixtyDaysInSeconds = 60 * 24 * 60 * 60;
492
493
nsSiteSecurityService::nsSiteSecurityService()
494
  : mMaxMaxAge(kSixtyDaysInSeconds)
495
  , mUsePreloadList(true)
496
  , mPreloadListTimeOffset(0)
497
  , mProcessPKPHeadersFromNonBuiltInRoots(false)
498
  , mDafsa(kDafsa)
499
0
{
500
0
}
501
502
nsSiteSecurityService::~nsSiteSecurityService()
503
0
{
504
0
}
505
506
NS_IMPL_ISUPPORTS(nsSiteSecurityService,
507
                  nsIObserver,
508
                  nsISiteSecurityService)
509
510
nsresult
511
nsSiteSecurityService::Init()
512
0
{
513
0
  // Don't access Preferences off the main thread.
514
0
  if (!NS_IsMainThread()) {
515
0
    MOZ_ASSERT_UNREACHABLE("nsSiteSecurityService initialized off main thread");
516
0
    return NS_ERROR_NOT_SAME_THREAD;
517
0
  }
518
0
519
0
  mMaxMaxAge = mozilla::Preferences::GetInt(
520
0
    "security.cert_pinning.max_max_age_seconds", kSixtyDaysInSeconds);
521
0
  mozilla::Preferences::AddStrongObserver(this,
522
0
    "security.cert_pinning.max_max_age_seconds");
523
0
  mUsePreloadList = mozilla::Preferences::GetBool(
524
0
    "network.stricttransportsecurity.preloadlist", true);
525
0
  mozilla::Preferences::AddStrongObserver(this,
526
0
    "network.stricttransportsecurity.preloadlist");
527
0
  mProcessPKPHeadersFromNonBuiltInRoots = mozilla::Preferences::GetBool(
528
0
    "security.cert_pinning.process_headers_from_non_builtin_roots", false);
529
0
  mozilla::Preferences::AddStrongObserver(this,
530
0
    "security.cert_pinning.process_headers_from_non_builtin_roots");
531
0
  mPreloadListTimeOffset = mozilla::Preferences::GetInt(
532
0
    "test.currentTimeOffsetSeconds", 0);
533
0
  mozilla::Preferences::AddStrongObserver(this,
534
0
    "test.currentTimeOffsetSeconds");
535
0
  mSiteStateStorage =
536
0
    mozilla::DataStorage::Get(DataStorageClass::SiteSecurityServiceState);
537
0
  mPreloadStateStorage =
538
0
    mozilla::DataStorage::Get(DataStorageClass::SecurityPreloadState);
539
0
  bool storageWillPersist = false;
540
0
  bool preloadStorageWillPersist = false;
541
0
  nsresult rv = mSiteStateStorage->Init(storageWillPersist);
542
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
543
0
    return rv;
544
0
  }
545
0
  rv = mPreloadStateStorage->Init(preloadStorageWillPersist);
546
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
547
0
    return rv;
548
0
  }
549
0
  // This is not fatal. There are some cases where there won't be a
550
0
  // profile directory (e.g. running xpcshell). There isn't the
551
0
  // expectation that site information will be presisted in those cases.
552
0
  if (!storageWillPersist || !preloadStorageWillPersist) {
553
0
    NS_WARNING("site security information will not be persisted");
554
0
  }
555
0
556
0
  return NS_OK;
557
0
}
558
559
nsresult
560
nsSiteSecurityService::GetHost(nsIURI* aURI, nsACString& aResult)
561
0
{
562
0
  nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(aURI);
563
0
  if (!innerURI) {
564
0
    return NS_ERROR_FAILURE;
565
0
  }
566
0
567
0
  nsAutoCString host;
568
0
  nsresult rv = innerURI->GetAsciiHost(host);
569
0
  if (NS_FAILED(rv)) {
570
0
    return rv;
571
0
  }
572
0
573
0
  aResult.Assign(PublicKeyPinningService::CanonicalizeHostname(host.get()));
574
0
  if (aResult.IsEmpty()) {
575
0
    return NS_ERROR_UNEXPECTED;
576
0
  }
577
0
578
0
  return NS_OK;
579
0
}
580
581
static void
582
SetStorageKey(const nsACString& hostname, uint32_t aType,
583
              const OriginAttributes& aOriginAttributes,
584
              /*out*/ nsAutoCString& storageKey)
585
0
{
586
0
  storageKey = hostname;
587
0
588
0
  // Don't isolate by userContextId.
589
0
  OriginAttributes originAttributesNoUserContext = aOriginAttributes;
590
0
  originAttributesNoUserContext.mUserContextId =
591
0
    nsIScriptSecurityManager::DEFAULT_USER_CONTEXT_ID;
592
0
  nsAutoCString originAttributesSuffix;
593
0
  originAttributesNoUserContext.CreateSuffix(originAttributesSuffix);
594
0
  storageKey.Append(originAttributesSuffix);
595
0
  switch (aType) {
596
0
    case nsISiteSecurityService::HEADER_HSTS:
597
0
      storageKey.AppendASCII(kHSTSKeySuffix);
598
0
      break;
599
0
    case nsISiteSecurityService::HEADER_HPKP:
600
0
      storageKey.AppendASCII(kHPKPKeySuffix);
601
0
      break;
602
0
    default:
603
0
      MOZ_ASSERT_UNREACHABLE("SSS:SetStorageKey got invalid type");
604
0
  }
605
0
}
606
607
// Expire times are in millis.  Since Headers max-age is in seconds, and
608
// PR_Now() is in micros, normalize the units at milliseconds.
609
static int64_t
610
ExpireTimeFromMaxAge(uint64_t maxAge)
611
0
{
612
0
  return (PR_Now() / PR_USEC_PER_MSEC) + ((int64_t)maxAge * PR_MSEC_PER_SEC);
613
0
}
614
615
nsresult
616
nsSiteSecurityService::SetHSTSState(uint32_t aType,
617
                                    const char* aHost,
618
                                    int64_t maxage,
619
                                    bool includeSubdomains,
620
                                    uint32_t flags,
621
                                    SecurityPropertyState aHSTSState,
622
                                    SecurityPropertySource aSource,
623
                                    const OriginAttributes& aOriginAttributes)
624
0
{
625
0
  nsAutoCString hostname(aHost);
626
0
  bool isPreload = (aSource == SourcePreload);
627
0
  // If max-age is zero, that's an indication to immediately remove the
628
0
  // security state, so here's a shortcut.
629
0
  if (!maxage) {
630
0
    return RemoveStateInternal(aType, hostname, flags, isPreload,
631
0
                               aOriginAttributes);
632
0
  }
633
0
634
0
  MOZ_ASSERT((aHSTSState == SecurityPropertySet ||
635
0
              aHSTSState == SecurityPropertyNegative),
636
0
      "HSTS State must be SecurityPropertySet or SecurityPropertyNegative");
637
0
  if (isPreload && aOriginAttributes != OriginAttributes()) {
638
0
    return NS_ERROR_INVALID_ARG;
639
0
  }
640
0
641
0
  int64_t expiretime = ExpireTimeFromMaxAge(maxage);
642
0
  RefPtr<SiteHSTSState> siteState = new SiteHSTSState(
643
0
    hostname, aOriginAttributes, expiretime, aHSTSState, includeSubdomains,
644
0
    aSource);
645
0
  nsAutoCString stateString;
646
0
  siteState->ToString(stateString);
647
0
  SSSLOG(("SSS: setting state for %s", hostname.get()));
648
0
  bool isPrivate = flags & nsISocketProvider::NO_PERMANENT_STORAGE;
649
0
  mozilla::DataStorageType storageType = isPrivate
650
0
                                         ? mozilla::DataStorage_Private
651
0
                                         : mozilla::DataStorage_Persistent;
652
0
  nsAutoCString storageKey;
653
0
  SetStorageKey(hostname, aType, aOriginAttributes, storageKey);
654
0
  nsresult rv;
655
0
  if (isPreload) {
656
0
    SSSLOG(("SSS: storing entry for %s in dynamic preloads", hostname.get()));
657
0
    rv = mPreloadStateStorage->Put(storageKey, stateString,
658
0
                                   mozilla::DataStorage_Persistent);
659
0
  } else {
660
0
    SSSLOG(("SSS: storing HSTS site entry for %s", hostname.get()));
661
0
    nsCString value = mSiteStateStorage->Get(storageKey, storageType);
662
0
    RefPtr<SiteHSTSState> curSiteState =
663
0
      new SiteHSTSState(hostname, aOriginAttributes, value);
664
0
    if (curSiteState->mHSTSState != SecurityPropertyUnset &&
665
0
        curSiteState->mHSTSSource != SourceUnknown) {
666
0
      // don't override the source
667
0
      siteState->mHSTSSource = curSiteState->mHSTSSource;
668
0
      siteState->ToString(stateString);
669
0
    }
670
0
    rv = mSiteStateStorage->Put(storageKey, stateString, storageType);
671
0
  }
672
0
  NS_ENSURE_SUCCESS(rv, rv);
673
0
674
0
  return NS_OK;
675
0
}
676
677
nsresult
678
nsSiteSecurityService::RemoveStateInternal(
679
  uint32_t aType, nsIURI* aURI, uint32_t aFlags,
680
  const OriginAttributes& aOriginAttributes)
681
0
{
682
0
  nsAutoCString hostname;
683
0
  GetHost(aURI, hostname);
684
0
  return RemoveStateInternal(aType, hostname, aFlags, false, aOriginAttributes);
685
0
}
686
687
nsresult
688
nsSiteSecurityService::RemoveStateInternal(
689
  uint32_t aType,
690
  const nsAutoCString& aHost,
691
  uint32_t aFlags, bool aIsPreload,
692
  const OriginAttributes& aOriginAttributes)
693
0
{
694
0
   // Child processes are not allowed direct access to this.
695
0
   if (!XRE_IsParentProcess()) {
696
0
     MOZ_CRASH("Child process: no direct access to nsISiteSecurityService::RemoveStateInternal");
697
0
   }
698
0
699
0
  // Only HSTS is supported at the moment.
700
0
  NS_ENSURE_TRUE(aType == nsISiteSecurityService::HEADER_HSTS ||
701
0
                 aType == nsISiteSecurityService::HEADER_HPKP,
702
0
                 NS_ERROR_NOT_IMPLEMENTED);
703
0
  if (aIsPreload && aOriginAttributes != OriginAttributes()) {
704
0
    return NS_ERROR_INVALID_ARG;
705
0
  }
706
0
707
0
  bool isPrivate = aFlags & nsISocketProvider::NO_PERMANENT_STORAGE;
708
0
  mozilla::DataStorageType storageType = isPrivate
709
0
                                         ? mozilla::DataStorage_Private
710
0
                                         : mozilla::DataStorage_Persistent;
711
0
  // If this host is in the preload list, we have to store a knockout entry.
712
0
  nsAutoCString storageKey;
713
0
  SetStorageKey(aHost, aType, aOriginAttributes, storageKey);
714
0
715
0
  nsCString value = mPreloadStateStorage->Get(storageKey,
716
0
                                              mozilla::DataStorage_Persistent);
717
0
  RefPtr<SiteHSTSState> dynamicState =
718
0
    new SiteHSTSState(aHost, aOriginAttributes, value);
719
0
  if (GetPreloadStatus(aHost) ||
720
0
      dynamicState->mHSTSState != SecurityPropertyUnset) {
721
0
    SSSLOG(("SSS: storing knockout entry for %s", aHost.get()));
722
0
    RefPtr<SiteHSTSState> siteState = new SiteHSTSState(
723
0
      aHost, aOriginAttributes, 0, SecurityPropertyKnockout, false,
724
0
      SourceUnknown);
725
0
    nsAutoCString stateString;
726
0
    siteState->ToString(stateString);
727
0
    nsresult rv;
728
0
    if (aIsPreload) {
729
0
      rv = mPreloadStateStorage->Put(storageKey, stateString,
730
0
                                     mozilla::DataStorage_Persistent);
731
0
    } else {
732
0
      rv = mSiteStateStorage->Put(storageKey, stateString, storageType);
733
0
    }
734
0
    NS_ENSURE_SUCCESS(rv, rv);
735
0
  } else {
736
0
    SSSLOG(("SSS: removing entry for %s", aHost.get()));
737
0
    if (aIsPreload) {
738
0
      mPreloadStateStorage->Remove(storageKey, mozilla::DataStorage_Persistent);
739
0
    } else {
740
0
      mSiteStateStorage->Remove(storageKey, storageType);
741
0
    }
742
0
  }
743
0
744
0
  return NS_OK;
745
0
}
746
747
NS_IMETHODIMP
748
nsSiteSecurityService::RemoveState(uint32_t aType, nsIURI* aURI,
749
                                   uint32_t aFlags,
750
                                   JS::HandleValue aOriginAttributes,
751
                                   JSContext* aCx, uint8_t aArgc)
752
0
{
753
0
  OriginAttributes originAttributes;
754
0
  if (aArgc > 0) {
755
0
    // OriginAttributes were passed in.
756
0
    if (!aOriginAttributes.isObject() ||
757
0
        !originAttributes.Init(aCx, aOriginAttributes)) {
758
0
      return NS_ERROR_INVALID_ARG;
759
0
    }
760
0
  }
761
0
  return RemoveStateInternal(aType, aURI, aFlags, originAttributes);
762
0
}
763
764
static bool
765
HostIsIPAddress(const nsCString& hostname)
766
0
{
767
0
  PRNetAddr hostAddr;
768
0
  PRErrorCode prv = PR_StringToNetAddr(hostname.get(), &hostAddr);
769
0
  return (prv == PR_SUCCESS);
770
0
}
771
772
NS_IMETHODIMP
773
nsSiteSecurityService::ProcessHeaderScriptable(
774
  uint32_t aType,
775
  nsIURI* aSourceURI,
776
  const nsACString& aHeader,
777
  nsITransportSecurityInfo* aSecInfo,
778
  uint32_t aFlags,
779
  uint32_t aSource,
780
  JS::HandleValue aOriginAttributes,
781
  uint64_t* aMaxAge,
782
  bool* aIncludeSubdomains,
783
  uint32_t* aFailureResult,
784
  JSContext* aCx,
785
  uint8_t aArgc)
786
0
{
787
0
  OriginAttributes originAttributes;
788
0
  if (aArgc > 0) {
789
0
    if (!aOriginAttributes.isObject() ||
790
0
        !originAttributes.Init(aCx, aOriginAttributes)) {
791
0
      return NS_ERROR_INVALID_ARG;
792
0
    }
793
0
  }
794
0
  return ProcessHeader(aType, aSourceURI, aHeader, aSecInfo, aFlags,
795
0
                       aSource, originAttributes, aMaxAge, aIncludeSubdomains,
796
0
                       aFailureResult);
797
0
}
798
799
NS_IMETHODIMP
800
nsSiteSecurityService::ProcessHeader(uint32_t aType,
801
                                     nsIURI* aSourceURI,
802
                                     const nsACString& aHeader,
803
                                     nsITransportSecurityInfo* aSecInfo,
804
                                     uint32_t aFlags,
805
                                     uint32_t aHeaderSource,
806
                                     const OriginAttributes& aOriginAttributes,
807
                                     uint64_t* aMaxAge,
808
                                     bool* aIncludeSubdomains,
809
                                     uint32_t* aFailureResult)
810
0
{
811
0
  // Child processes are not allowed direct access to this.
812
0
  if (!XRE_IsParentProcess()) {
813
0
    MOZ_CRASH("Child process: no direct access to "
814
0
              "nsISiteSecurityService::ProcessHeader");
815
0
  }
816
0
817
0
  if (aFailureResult) {
818
0
    *aFailureResult = nsISiteSecurityService::ERROR_UNKNOWN;
819
0
  }
820
0
  NS_ENSURE_TRUE(aType == nsISiteSecurityService::HEADER_HSTS ||
821
0
                 aType == nsISiteSecurityService::HEADER_HPKP,
822
0
                 NS_ERROR_NOT_IMPLEMENTED);
823
0
  SecurityPropertySource source = static_cast<SecurityPropertySource>(aHeaderSource);
824
0
  switch (source) {
825
0
    case SourceUnknown:
826
0
    case SourcePreload:
827
0
    case SourceOrganic:
828
0
      break;
829
0
    default:
830
0
      return NS_ERROR_INVALID_ARG;
831
0
  }
832
0
833
0
  NS_ENSURE_ARG(aSecInfo);
834
0
  return ProcessHeaderInternal(aType, aSourceURI, PromiseFlatCString(aHeader),
835
0
                               aSecInfo, aFlags, source, aOriginAttributes,
836
0
                               aMaxAge, aIncludeSubdomains, aFailureResult);
837
0
}
838
839
nsresult
840
nsSiteSecurityService::ProcessHeaderInternal(
841
  uint32_t aType,
842
  nsIURI* aSourceURI,
843
  const nsCString& aHeader,
844
  nsITransportSecurityInfo* aSecInfo,
845
  uint32_t aFlags,
846
  SecurityPropertySource aSource,
847
  const OriginAttributes& aOriginAttributes,
848
  uint64_t* aMaxAge,
849
  bool* aIncludeSubdomains,
850
  uint32_t* aFailureResult)
851
0
{
852
0
  if (aFailureResult) {
853
0
    *aFailureResult = nsISiteSecurityService::ERROR_UNKNOWN;
854
0
  }
855
0
  // Only HSTS and HPKP are supported at the moment.
856
0
  NS_ENSURE_TRUE(aType == nsISiteSecurityService::HEADER_HSTS ||
857
0
                 aType == nsISiteSecurityService::HEADER_HPKP,
858
0
                 NS_ERROR_NOT_IMPLEMENTED);
859
0
860
0
  if (aMaxAge != nullptr) {
861
0
    *aMaxAge = 0;
862
0
  }
863
0
864
0
  if (aIncludeSubdomains != nullptr) {
865
0
    *aIncludeSubdomains = false;
866
0
  }
867
0
868
0
  if (aSecInfo) {
869
0
    bool tlsIsBroken = false;
870
0
    bool trustcheck;
871
0
    nsresult rv;
872
0
    rv = aSecInfo->GetIsDomainMismatch(&trustcheck);
873
0
    NS_ENSURE_SUCCESS(rv, rv);
874
0
    tlsIsBroken = tlsIsBroken || trustcheck;
875
0
876
0
    rv = aSecInfo->GetIsNotValidAtThisTime(&trustcheck);
877
0
    NS_ENSURE_SUCCESS(rv, rv);
878
0
    tlsIsBroken = tlsIsBroken || trustcheck;
879
0
880
0
    rv = aSecInfo->GetIsUntrusted(&trustcheck);
881
0
    NS_ENSURE_SUCCESS(rv, rv);
882
0
    tlsIsBroken = tlsIsBroken || trustcheck;
883
0
    if (tlsIsBroken) {
884
0
       SSSLOG(("SSS: discarding header from untrustworthy connection"));
885
0
       if (aFailureResult) {
886
0
         *aFailureResult = nsISiteSecurityService::ERROR_UNTRUSTWORTHY_CONNECTION;
887
0
       }
888
0
      return NS_ERROR_FAILURE;
889
0
    }
890
0
  }
891
0
892
0
  nsAutoCString host;
893
0
  nsresult rv = GetHost(aSourceURI, host);
894
0
  NS_ENSURE_SUCCESS(rv, rv);
895
0
  if (HostIsIPAddress(host)) {
896
0
    /* Don't process headers if a site is accessed by IP address. */
897
0
    return NS_OK;
898
0
  }
899
0
900
0
  switch (aType) {
901
0
    case nsISiteSecurityService::HEADER_HSTS:
902
0
      rv = ProcessSTSHeader(aSourceURI, aHeader, aFlags, aSource,
903
0
                            aOriginAttributes, aMaxAge, aIncludeSubdomains,
904
0
                            aFailureResult);
905
0
      break;
906
0
    case nsISiteSecurityService::HEADER_HPKP:
907
0
      rv = ProcessPKPHeader(aSourceURI, aHeader, aSecInfo, aFlags,
908
0
                            aOriginAttributes, aMaxAge, aIncludeSubdomains,
909
0
                            aFailureResult);
910
0
      break;
911
0
    default:
912
0
      MOZ_CRASH("unexpected header type");
913
0
  }
914
0
  return rv;
915
0
}
916
917
static uint32_t
918
ParseSSSHeaders(uint32_t aType,
919
                const nsCString& aHeader,
920
                bool& foundIncludeSubdomains,
921
                bool& foundMaxAge,
922
                bool& foundUnrecognizedDirective,
923
                uint64_t& maxAge,
924
                nsTArray<nsCString>& sha256keys)
925
0
{
926
0
  // Strict transport security and Public Key Pinning have very similar
927
0
  // Header formats.
928
0
929
0
  // "Strict-Transport-Security" ":" OWS
930
0
  //      STS-d  *( OWS ";" OWS STS-d  OWS)
931
0
  //
932
0
  //  ; STS directive
933
0
  //  STS-d      = maxAge / includeSubDomains
934
0
  //
935
0
  //  maxAge     = "max-age" "=" delta-seconds v-ext
936
0
  //
937
0
  //  includeSubDomains = [ "includeSubDomains" ]
938
0
  //
939
0
940
0
  // "Public-Key-Pins ":" OWS
941
0
  //      PKP-d  *( OWS ";" OWS PKP-d  OWS)
942
0
  //
943
0
  //  ; PKP directive
944
0
  //  PKP-d      = maxAge / includeSubDomains / reportUri / pin-directive
945
0
  //
946
0
  //  maxAge     = "max-age" "=" delta-seconds v-ext
947
0
  //
948
0
  //  includeSubDomains = [ "includeSubDomains" ]
949
0
  //
950
0
  //  reportURi  = "report-uri" "=" quoted-string
951
0
  //
952
0
  //  pin-directive = "pin-" token "=" quoted-string
953
0
  //
954
0
  //  the only valid token currently specified is sha256
955
0
  //  the quoted string for a pin directive is the base64 encoding
956
0
  //  of the hash of the public key of the fingerprint
957
0
  //
958
0
959
0
  //  The order of the directives is not significant.
960
0
  //  All directives must appear only once.
961
0
  //  Directive names are case-insensitive.
962
0
  //  The entire header is invalid if a directive not conforming to the
963
0
  //  syntax is encountered.
964
0
  //  Unrecognized directives (that are otherwise syntactically valid) are
965
0
  //  ignored, and the rest of the header is parsed as normal.
966
0
967
0
  bool foundReportURI = false;
968
0
969
0
  NS_NAMED_LITERAL_CSTRING(max_age_var, "max-age");
970
0
  NS_NAMED_LITERAL_CSTRING(include_subd_var, "includesubdomains");
971
0
  NS_NAMED_LITERAL_CSTRING(pin_sha256_var, "pin-sha256");
972
0
  NS_NAMED_LITERAL_CSTRING(report_uri_var, "report-uri");
973
0
974
0
  nsSecurityHeaderParser parser(aHeader);
975
0
  nsresult rv = parser.Parse();
976
0
  if (NS_FAILED(rv)) {
977
0
    SSSLOG(("SSS: could not parse header"));
978
0
    return nsISiteSecurityService::ERROR_COULD_NOT_PARSE_HEADER;
979
0
  }
980
0
  mozilla::LinkedList<nsSecurityHeaderDirective>* directives = parser.GetDirectives();
981
0
982
0
  for (nsSecurityHeaderDirective* directive = directives->getFirst();
983
0
       directive != nullptr; directive = directive->getNext()) {
984
0
    SSSLOG(("SSS: found directive %s\n", directive->mName.get()));
985
0
    if (directive->mName.Length() == max_age_var.Length() &&
986
0
        directive->mName.EqualsIgnoreCase(max_age_var.get(),
987
0
                                          max_age_var.Length())) {
988
0
      if (foundMaxAge) {
989
0
        SSSLOG(("SSS: found two max-age directives"));
990
0
        return nsISiteSecurityService::ERROR_MULTIPLE_MAX_AGES;
991
0
      }
992
0
993
0
      SSSLOG(("SSS: found max-age directive"));
994
0
      foundMaxAge = true;
995
0
996
0
      Tokenizer tokenizer(directive->mValue);
997
0
      if (!tokenizer.ReadInteger(&maxAge)) {
998
0
        SSSLOG(("SSS: could not parse delta-seconds"));
999
0
        return nsISiteSecurityService::ERROR_INVALID_MAX_AGE;
1000
0
      }
1001
0
1002
0
      if (!tokenizer.CheckEOF()) {
1003
0
        SSSLOG(("SSS: invalid value for max-age directive"));
1004
0
        return nsISiteSecurityService::ERROR_INVALID_MAX_AGE;
1005
0
      }
1006
0
1007
0
      SSSLOG(("SSS: parsed delta-seconds: %" PRIu64, maxAge));
1008
0
    } else if (directive->mName.Length() == include_subd_var.Length() &&
1009
0
               directive->mName.EqualsIgnoreCase(include_subd_var.get(),
1010
0
                                                 include_subd_var.Length())) {
1011
0
      if (foundIncludeSubdomains) {
1012
0
        SSSLOG(("SSS: found two includeSubdomains directives"));
1013
0
        return nsISiteSecurityService::ERROR_MULTIPLE_INCLUDE_SUBDOMAINS;
1014
0
      }
1015
0
1016
0
      SSSLOG(("SSS: found includeSubdomains directive"));
1017
0
      foundIncludeSubdomains = true;
1018
0
1019
0
      if (directive->mValue.Length() != 0) {
1020
0
        SSSLOG(("SSS: includeSubdomains directive unexpectedly had value '%s'",
1021
0
                directive->mValue.get()));
1022
0
        return nsISiteSecurityService::ERROR_INVALID_INCLUDE_SUBDOMAINS;
1023
0
      }
1024
0
    } else if (aType == nsISiteSecurityService::HEADER_HPKP &&
1025
0
               directive->mName.Length() == pin_sha256_var.Length() &&
1026
0
               directive->mName.EqualsIgnoreCase(pin_sha256_var.get(),
1027
0
                                                 pin_sha256_var.Length())) {
1028
0
       SSSLOG(("SSS: found pinning entry '%s' length=%d",
1029
0
               directive->mValue.get(), directive->mValue.Length()));
1030
0
       if (!stringIsBase64EncodingOf256bitValue(directive->mValue)) {
1031
0
         return nsISiteSecurityService::ERROR_INVALID_PIN;
1032
0
       }
1033
0
       sha256keys.AppendElement(directive->mValue);
1034
0
   } else if (aType == nsISiteSecurityService::HEADER_HPKP &&
1035
0
              directive->mName.Length() == report_uri_var.Length() &&
1036
0
              directive->mName.EqualsIgnoreCase(report_uri_var.get(),
1037
0
                                                report_uri_var.Length())) {
1038
0
       // We don't support the report-uri yet, but to avoid unrecognized
1039
0
       // directive warnings, we still have to handle its presence
1040
0
      if (foundReportURI) {
1041
0
        SSSLOG(("SSS: found two report-uri directives"));
1042
0
        return nsISiteSecurityService::ERROR_MULTIPLE_REPORT_URIS;
1043
0
      }
1044
0
      SSSLOG(("SSS: found report-uri directive"));
1045
0
      foundReportURI = true;
1046
0
   } else {
1047
0
      SSSLOG(("SSS: ignoring unrecognized directive '%s'",
1048
0
              directive->mName.get()));
1049
0
      foundUnrecognizedDirective = true;
1050
0
    }
1051
0
  }
1052
0
  return nsISiteSecurityService::Success;
1053
0
}
1054
1055
nsresult
1056
nsSiteSecurityService::ProcessPKPHeader(
1057
  nsIURI* aSourceURI,
1058
  const nsCString& aHeader,
1059
  nsITransportSecurityInfo* aSecInfo,
1060
  uint32_t aFlags,
1061
  const OriginAttributes& aOriginAttributes,
1062
  uint64_t* aMaxAge,
1063
  bool* aIncludeSubdomains,
1064
  uint32_t* aFailureResult)
1065
0
{
1066
0
  if (aFailureResult) {
1067
0
    *aFailureResult = nsISiteSecurityService::ERROR_UNKNOWN;
1068
0
  }
1069
0
  SSSLOG(("SSS: processing HPKP header '%s'", aHeader.get()));
1070
0
  NS_ENSURE_ARG(aSecInfo);
1071
0
1072
0
  const uint32_t aType = nsISiteSecurityService::HEADER_HPKP;
1073
0
  bool foundMaxAge = false;
1074
0
  bool foundIncludeSubdomains = false;
1075
0
  bool foundUnrecognizedDirective = false;
1076
0
  uint64_t maxAge = 0;
1077
0
  nsTArray<nsCString> sha256keys;
1078
0
  uint32_t sssrv = ParseSSSHeaders(aType, aHeader, foundIncludeSubdomains,
1079
0
                                   foundMaxAge, foundUnrecognizedDirective,
1080
0
                                   maxAge, sha256keys);
1081
0
  if (sssrv != nsISiteSecurityService::Success) {
1082
0
    if (aFailureResult) {
1083
0
      *aFailureResult = sssrv;
1084
0
    }
1085
0
    return NS_ERROR_FAILURE;
1086
0
  }
1087
0
1088
0
  // after processing all the directives, make sure we came across max-age
1089
0
  // somewhere.
1090
0
  if (!foundMaxAge) {
1091
0
    SSSLOG(("SSS: did not encounter required max-age directive"));
1092
0
    if (aFailureResult) {
1093
0
      *aFailureResult = nsISiteSecurityService::ERROR_NO_MAX_AGE;
1094
0
    }
1095
0
    return NS_ERROR_FAILURE;
1096
0
  }
1097
0
1098
0
  // before we add the pin we need to ensure it will not break the site as
1099
0
  // currently visited so:
1100
0
  // 1. recompute a valid chain (no external ocsp)
1101
0
  // 2. use this chain to check if things would have broken!
1102
0
  nsAutoCString host;
1103
0
  nsresult rv = GetHost(aSourceURI, host);
1104
0
  NS_ENSURE_SUCCESS(rv, rv);
1105
0
  nsCOMPtr<nsIX509Cert> cert;
1106
0
  rv = aSecInfo->GetServerCert(getter_AddRefs(cert));
1107
0
  NS_ENSURE_SUCCESS(rv, rv);
1108
0
  NS_ENSURE_TRUE(cert, NS_ERROR_FAILURE);
1109
0
  UniqueCERTCertificate nssCert(cert->GetCert());
1110
0
  NS_ENSURE_TRUE(nssCert, NS_ERROR_FAILURE);
1111
0
1112
0
  // This use of VerifySSLServerCert should be able to be removed in Bug #1406854
1113
0
  mozilla::pkix::Time now(mozilla::pkix::Now());
1114
0
  UniqueCERTCertList certList;
1115
0
  RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
1116
0
  NS_ENSURE_TRUE(certVerifier, NS_ERROR_UNEXPECTED);
1117
0
  // We don't want this verification to cause any network traffic that would
1118
0
  // block execution. Also, since we don't have access to the original stapled
1119
0
  // OCSP response, we can't enforce this aspect of the TLS Feature extension.
1120
0
  // This is ok, because it will have been enforced when we originally connected
1121
0
  // to the site (or it's disabled, in which case we wouldn't want to enforce it
1122
0
  // anyway).
1123
0
  CertVerifier::Flags flags = CertVerifier::FLAG_LOCAL_ONLY |
1124
0
                              CertVerifier::FLAG_TLS_IGNORE_STATUS_REQUEST;
1125
0
  if (certVerifier->VerifySSLServerCert(nssCert,
1126
0
                                        nullptr, // stapledOCSPResponse
1127
0
                                        nullptr, // sctsFromTLSExtension
1128
0
                                        now, nullptr, // pinarg
1129
0
                                        host, // hostname
1130
0
                                        certList,
1131
0
                                        false, // don't store intermediates
1132
0
                                        flags,
1133
0
                                        aOriginAttributes)
1134
0
        != mozilla::pkix::Success) {
1135
0
    return NS_ERROR_FAILURE;
1136
0
  }
1137
0
1138
0
  // This copy to produce an nsNSSCertList should also be removed in Bug #1406854
1139
0
  nsCOMPtr<nsIX509CertList> x509CertList = new nsNSSCertList(std::move(certList));
1140
0
  if (!x509CertList) {
1141
0
    return rv;
1142
0
  }
1143
0
1144
0
  RefPtr<nsNSSCertList> nssCertList = x509CertList->GetCertList();
1145
0
  nsCOMPtr<nsIX509Cert> rootCert;
1146
0
  rv = nssCertList->GetRootCertificate(rootCert);
1147
0
  if (NS_FAILED(rv)) {
1148
0
    return rv;
1149
0
  }
1150
0
1151
0
  bool isBuiltIn = false;
1152
0
  rv = rootCert->GetIsBuiltInRoot(&isBuiltIn);
1153
0
  if (NS_FAILED(rv)) {
1154
0
    return NS_ERROR_FAILURE;
1155
0
  }
1156
0
1157
0
  if (!isBuiltIn && !mProcessPKPHeadersFromNonBuiltInRoots) {
1158
0
    if (aFailureResult) {
1159
0
      *aFailureResult = nsISiteSecurityService::ERROR_ROOT_NOT_BUILT_IN;
1160
0
    }
1161
0
    return NS_ERROR_FAILURE;
1162
0
  }
1163
0
1164
0
  // if maxAge == 0 we must delete all state, for now no hole-punching
1165
0
  if (maxAge == 0) {
1166
0
    return RemoveStateInternal(aType, aSourceURI, aFlags, aOriginAttributes);
1167
0
  }
1168
0
1169
0
  // clamp maxAge to the maximum set by pref
1170
0
  if (maxAge > mMaxMaxAge) {
1171
0
    maxAge = mMaxMaxAge;
1172
0
  }
1173
0
1174
0
  bool chainMatchesPinset;
1175
0
  rv = PublicKeyPinningService::ChainMatchesPinset(nssCertList, sha256keys,
1176
0
                                                   chainMatchesPinset);
1177
0
  if (NS_FAILED(rv)) {
1178
0
    return rv;
1179
0
  }
1180
0
  if (!chainMatchesPinset) {
1181
0
    // is invalid
1182
0
    SSSLOG(("SSS: Pins provided by %s are invalid no match with certList\n", host.get()));
1183
0
    if (aFailureResult) {
1184
0
      *aFailureResult = nsISiteSecurityService::ERROR_PINSET_DOES_NOT_MATCH_CHAIN;
1185
0
    }
1186
0
    return NS_ERROR_FAILURE;
1187
0
  }
1188
0
1189
0
  // finally we need to ensure that there is a "backup pin" ie. There must be
1190
0
  // at least one fingerprint hash that does NOT validate against the verified
1191
0
  // chain (Section 2.5 of the spec)
1192
0
  bool hasBackupPin = false;
1193
0
  for (uint32_t i = 0; i < sha256keys.Length(); i++) {
1194
0
    nsTArray<nsCString> singlePin;
1195
0
    singlePin.AppendElement(sha256keys[i]);
1196
0
    rv = PublicKeyPinningService::ChainMatchesPinset(nssCertList, singlePin,
1197
0
                                                     chainMatchesPinset);
1198
0
    if (NS_FAILED(rv)) {
1199
0
      return rv;
1200
0
    }
1201
0
    if (!chainMatchesPinset) {
1202
0
      hasBackupPin = true;
1203
0
    }
1204
0
  }
1205
0
  if (!hasBackupPin) {
1206
0
     // is invalid
1207
0
    SSSLOG(("SSS: Pins provided by %s are invalid no backupPin\n", host.get()));
1208
0
    if (aFailureResult) {
1209
0
      *aFailureResult = nsISiteSecurityService::ERROR_NO_BACKUP_PIN;
1210
0
    }
1211
0
    return NS_ERROR_FAILURE;
1212
0
  }
1213
0
1214
0
  int64_t expireTime = ExpireTimeFromMaxAge(maxAge);
1215
0
  RefPtr<SiteHPKPState> dynamicEntry =
1216
0
    new SiteHPKPState(host, aOriginAttributes, expireTime, SecurityPropertySet,
1217
0
                      foundIncludeSubdomains, sha256keys);
1218
0
  SSSLOG(("SSS: about to set pins for  %s, expires=%" PRId64 " now=%" PRId64 " maxAge=%" PRIu64 "\n",
1219
0
           host.get(), expireTime, PR_Now() / PR_USEC_PER_MSEC, maxAge));
1220
0
1221
0
  rv = SetHPKPState(host.get(), *dynamicEntry, aFlags, false, aOriginAttributes);
1222
0
  if (NS_FAILED(rv)) {
1223
0
    SSSLOG(("SSS: failed to set pins for %s\n", host.get()));
1224
0
    if (aFailureResult) {
1225
0
      *aFailureResult = nsISiteSecurityService::ERROR_COULD_NOT_SAVE_STATE;
1226
0
    }
1227
0
    return rv;
1228
0
  }
1229
0
1230
0
  if (aMaxAge != nullptr) {
1231
0
    *aMaxAge = maxAge;
1232
0
  }
1233
0
1234
0
  if (aIncludeSubdomains != nullptr) {
1235
0
    *aIncludeSubdomains = foundIncludeSubdomains;
1236
0
  }
1237
0
1238
0
  return foundUnrecognizedDirective
1239
0
           ? NS_SUCCESS_LOSS_OF_INSIGNIFICANT_DATA
1240
0
           : NS_OK;
1241
0
}
1242
1243
nsresult
1244
nsSiteSecurityService::ProcessSTSHeader(
1245
  nsIURI* aSourceURI,
1246
  const nsCString& aHeader,
1247
  uint32_t aFlags,
1248
  SecurityPropertySource aSource,
1249
  const OriginAttributes& aOriginAttributes,
1250
  uint64_t* aMaxAge,
1251
  bool* aIncludeSubdomains,
1252
  uint32_t* aFailureResult)
1253
0
{
1254
0
  if (aFailureResult) {
1255
0
    *aFailureResult = nsISiteSecurityService::ERROR_UNKNOWN;
1256
0
  }
1257
0
  SSSLOG(("SSS: processing HSTS header '%s'", aHeader.get()));
1258
0
1259
0
  const uint32_t aType = nsISiteSecurityService::HEADER_HSTS;
1260
0
  bool foundMaxAge = false;
1261
0
  bool foundIncludeSubdomains = false;
1262
0
  bool foundUnrecognizedDirective = false;
1263
0
  uint64_t maxAge = 0;
1264
0
  nsTArray<nsCString> unusedSHA256keys; // Required for sane internal interface
1265
0
1266
0
  uint32_t sssrv = ParseSSSHeaders(aType, aHeader, foundIncludeSubdomains,
1267
0
                                   foundMaxAge, foundUnrecognizedDirective,
1268
0
                                   maxAge, unusedSHA256keys);
1269
0
  if (sssrv != nsISiteSecurityService::Success) {
1270
0
    if (aFailureResult) {
1271
0
      *aFailureResult = sssrv;
1272
0
    }
1273
0
    return NS_ERROR_FAILURE;
1274
0
  }
1275
0
1276
0
  // after processing all the directives, make sure we came across max-age
1277
0
  // somewhere.
1278
0
  if (!foundMaxAge) {
1279
0
    SSSLOG(("SSS: did not encounter required max-age directive"));
1280
0
    if (aFailureResult) {
1281
0
      *aFailureResult = nsISiteSecurityService::ERROR_NO_MAX_AGE;
1282
0
    }
1283
0
    return NS_ERROR_FAILURE;
1284
0
  }
1285
0
1286
0
  nsAutoCString hostname;
1287
0
  nsresult rv = GetHost(aSourceURI, hostname);
1288
0
  NS_ENSURE_SUCCESS(rv, rv);
1289
0
1290
0
  // record the successfully parsed header data.
1291
0
  rv = SetHSTSState(aType, hostname.get(), maxAge, foundIncludeSubdomains,
1292
0
                    aFlags, SecurityPropertySet, aSource, aOriginAttributes);
1293
0
  if (NS_FAILED(rv)) {
1294
0
    SSSLOG(("SSS: failed to set STS state"));
1295
0
    if (aFailureResult) {
1296
0
      *aFailureResult = nsISiteSecurityService::ERROR_COULD_NOT_SAVE_STATE;
1297
0
    }
1298
0
    return rv;
1299
0
  }
1300
0
1301
0
  if (aMaxAge != nullptr) {
1302
0
    *aMaxAge = maxAge;
1303
0
  }
1304
0
1305
0
  if (aIncludeSubdomains != nullptr) {
1306
0
    *aIncludeSubdomains = foundIncludeSubdomains;
1307
0
  }
1308
0
1309
0
  return foundUnrecognizedDirective
1310
0
           ? NS_SUCCESS_LOSS_OF_INSIGNIFICANT_DATA
1311
0
           : NS_OK;
1312
0
}
1313
1314
NS_IMETHODIMP
1315
nsSiteSecurityService::IsSecureURIScriptable(uint32_t aType, nsIURI* aURI,
1316
                                             uint32_t aFlags,
1317
                                             JS::HandleValue aOriginAttributes,
1318
                                             bool* aCached,
1319
                                             uint32_t* aSource, JSContext* aCx,
1320
                                             uint8_t aArgc,  bool* aResult)
1321
0
{
1322
0
  OriginAttributes originAttributes;
1323
0
  if (aArgc > 0) {
1324
0
    if (!aOriginAttributes.isObject() ||
1325
0
        !originAttributes.Init(aCx, aOriginAttributes)) {
1326
0
      return NS_ERROR_INVALID_ARG;
1327
0
    }
1328
0
  }
1329
0
  return IsSecureURI(aType, aURI, aFlags, originAttributes, aCached, aSource, aResult);
1330
0
}
1331
1332
NS_IMETHODIMP
1333
nsSiteSecurityService::IsSecureURI(uint32_t aType, nsIURI* aURI,
1334
                                   uint32_t aFlags,
1335
                                   const OriginAttributes& aOriginAttributes,
1336
                                   bool* aCached,
1337
                                   uint32_t* aSource, bool* aResult)
1338
0
{
1339
0
   // Child processes are not allowed direct access to this.
1340
0
   if (!XRE_IsParentProcess() && aType != nsISiteSecurityService::HEADER_HSTS) {
1341
0
     MOZ_CRASH("Child process: no direct access to nsISiteSecurityService::IsSecureURI for non-HSTS entries");
1342
0
   }
1343
0
1344
0
  NS_ENSURE_ARG(aURI);
1345
0
  NS_ENSURE_ARG(aResult);
1346
0
1347
0
  // Only HSTS and HPKP are supported at the moment.
1348
0
  NS_ENSURE_TRUE(aType == nsISiteSecurityService::HEADER_HSTS ||
1349
0
                 aType == nsISiteSecurityService::HEADER_HPKP,
1350
0
                 NS_ERROR_NOT_IMPLEMENTED);
1351
0
1352
0
  nsAutoCString hostname;
1353
0
  nsresult rv = GetHost(aURI, hostname);
1354
0
  NS_ENSURE_SUCCESS(rv, rv);
1355
0
  /* An IP address never qualifies as a secure URI. */
1356
0
  if (HostIsIPAddress(hostname)) {
1357
0
    *aResult = false;
1358
0
    return NS_OK;
1359
0
  }
1360
0
1361
0
  SecurityPropertySource* source = BitwiseCast<SecurityPropertySource*>(aSource);
1362
0
1363
0
  return IsSecureHost(aType, hostname, aFlags, aOriginAttributes, aCached,
1364
0
                      source, aResult);
1365
0
}
1366
1367
// Checks if the given host is in the preload list.
1368
//
1369
// @param aHost The host to match. Only does exact host matching.
1370
// @param aIncludeSubdomains Out, optional. Indicates whether or not to include
1371
//        subdomains. Only set if the host is matched and this function returns
1372
//        true.
1373
//
1374
// @return True if the host is matched, false otherwise.
1375
bool
1376
nsSiteSecurityService::GetPreloadStatus(const nsACString& aHost,
1377
                                        bool* aIncludeSubdomains) const
1378
0
{
1379
0
  const int kIncludeSubdomains = 1;
1380
0
  bool found = false;
1381
0
1382
0
  PRTime currentTime = PR_Now() + (mPreloadListTimeOffset * PR_USEC_PER_SEC);
1383
0
  if (mUsePreloadList && currentTime < gPreloadListExpirationTime) {
1384
0
    int result = mDafsa.Lookup(aHost);
1385
0
    found = (result != mozilla::Dafsa::kKeyNotFound);
1386
0
    if (found && aIncludeSubdomains) {
1387
0
      *aIncludeSubdomains = (result == kIncludeSubdomains);
1388
0
    }
1389
0
  }
1390
0
1391
0
  return found;
1392
0
}
1393
1394
// Allows us to determine if we have an HSTS entry for a given host (and, if
1395
// so, what that state is). The return value says whether or not we know
1396
// anything about this host (true if the host has an HSTS entry). aHost is
1397
// the host which we wish to deteming HSTS information on,
1398
// aRequireIncludeSubdomains specifies whether we require includeSubdomains
1399
// to be set on the entry (with the other parameters being as per IsSecureHost).
1400
bool
1401
nsSiteSecurityService::HostHasHSTSEntry(
1402
  const nsAutoCString& aHost, bool aRequireIncludeSubdomains, uint32_t aFlags,
1403
  const OriginAttributes& aOriginAttributes, bool* aResult, bool* aCached,
1404
  SecurityPropertySource* aSource)
1405
0
{
1406
0
  if (aSource) {
1407
0
    *aSource = SourceUnknown;
1408
0
  }
1409
0
  if (aCached) {
1410
0
    *aCached = false;
1411
0
  }
1412
0
  // First we check for an entry in site security storage. If that entry exists,
1413
0
  // we don't want to check in the preload lists. We only want to use the
1414
0
  // stored value if it is not a knockout entry, however.
1415
0
  // Additionally, if it is a knockout entry, we want to stop looking for data
1416
0
  // on the host, because the knockout entry indicates "we have no information
1417
0
  // regarding the security status of this host".
1418
0
  bool isPrivate = aFlags & nsISocketProvider::NO_PERMANENT_STORAGE;
1419
0
  mozilla::DataStorageType storageType = isPrivate
1420
0
                                         ? mozilla::DataStorage_Private
1421
0
                                         : mozilla::DataStorage_Persistent;
1422
0
  nsAutoCString storageKey;
1423
0
  SSSLOG(("Seeking HSTS entry for %s", aHost.get()));
1424
0
  SetStorageKey(aHost, nsISiteSecurityService::HEADER_HSTS, aOriginAttributes,
1425
0
                storageKey);
1426
0
  nsAutoCString preloadKey;
1427
0
  SetStorageKey(aHost, nsISiteSecurityService::HEADER_HSTS, OriginAttributes(),
1428
0
                preloadKey);
1429
0
  nsCString value = mSiteStateStorage->Get(storageKey, storageType);
1430
0
  RefPtr<SiteHSTSState> siteState =
1431
0
    new SiteHSTSState(aHost, aOriginAttributes, value);
1432
0
  if (siteState->mHSTSState != SecurityPropertyUnset) {
1433
0
    SSSLOG(("Found HSTS entry for %s", aHost.get()));
1434
0
    bool expired = siteState->IsExpired(nsISiteSecurityService::HEADER_HSTS);
1435
0
    if (!expired) {
1436
0
      SSSLOG(("Entry for %s is not expired", aHost.get()));
1437
0
      if (siteState->mHSTSState == SecurityPropertySet) {
1438
0
        *aResult = aRequireIncludeSubdomains ? siteState->mHSTSIncludeSubdomains
1439
0
                                             : true;
1440
0
        if (aCached) {
1441
0
          // Only set cached if this includes subdomains
1442
0
          *aCached = aRequireIncludeSubdomains ? siteState->mHSTSIncludeSubdomains
1443
0
                                               : true;
1444
0
        }
1445
0
        if (aSource) {
1446
0
          *aSource = siteState->mHSTSSource;
1447
0
        }
1448
0
        return true;
1449
0
      } else if (siteState->mHSTSState == SecurityPropertyNegative) {
1450
0
        *aResult = false;
1451
0
        if (aCached) {
1452
0
          // if it's negative, it is always cached
1453
0
          SSSLOG(("Marking HSTS as as cached (SecurityPropertyNegative)"));
1454
0
          *aCached = true;
1455
0
        }
1456
0
        if (aSource) {
1457
0
          *aSource = siteState->mHSTSSource;
1458
0
        }
1459
0
        return true;
1460
0
      }
1461
0
    }
1462
0
1463
0
    if (expired) {
1464
0
      SSSLOG(("Entry %s is expired - checking for preload state", aHost.get()));
1465
0
      // If the entry is expired and is not in either the static or dynamic
1466
0
      // preload lists, we can remove it.
1467
0
      // First, check the dynamic preload list.
1468
0
      value = mPreloadStateStorage->Get(preloadKey,
1469
0
                                        mozilla::DataStorage_Persistent);
1470
0
      RefPtr<SiteHSTSState> dynamicState =
1471
0
        new SiteHSTSState(aHost, aOriginAttributes, value);
1472
0
      if (dynamicState->mHSTSState == SecurityPropertyUnset) {
1473
0
        SSSLOG(("No dynamic preload - checking for static preload"));
1474
0
        // Now check the static preload list.
1475
0
        if (!GetPreloadStatus(aHost)) {
1476
0
          SSSLOG(("No static preload - removing expired entry"));
1477
0
          mSiteStateStorage->Remove(storageKey, storageType);
1478
0
        }
1479
0
      }
1480
0
    }
1481
0
    return false;
1482
0
  }
1483
0
1484
0
  // Next, look in the dynamic preload list.
1485
0
  value = mPreloadStateStorage->Get(preloadKey,
1486
0
                                    mozilla::DataStorage_Persistent);
1487
0
  RefPtr<SiteHSTSState> dynamicState =
1488
0
    new SiteHSTSState(aHost, aOriginAttributes, value);
1489
0
  if (dynamicState->mHSTSState != SecurityPropertyUnset) {
1490
0
    SSSLOG(("Found dynamic preload entry for %s", aHost.get()));
1491
0
    bool expired = dynamicState->IsExpired(nsISiteSecurityService::HEADER_HSTS);
1492
0
    if (!expired) {
1493
0
      if (dynamicState->mHSTSState == SecurityPropertySet) {
1494
0
        *aResult = aRequireIncludeSubdomains ? dynamicState->mHSTSIncludeSubdomains
1495
0
                                             : true;
1496
0
        if (aCached) {
1497
0
          // Only set cached if this includes subdomains
1498
0
          *aCached = aRequireIncludeSubdomains ? dynamicState->mHSTSIncludeSubdomains
1499
0
                                               : true;
1500
0
        }
1501
0
        if (aSource) {
1502
0
          *aSource = dynamicState->mHSTSSource;
1503
0
        }
1504
0
        return true;
1505
0
      } else if (dynamicState->mHSTSState == SecurityPropertyNegative) {
1506
0
        *aResult = false;
1507
0
        if (aCached) {
1508
0
          // if it's negative, it is always cached
1509
0
          *aCached = true;
1510
0
        }
1511
0
        if (aSource) {
1512
0
          *aSource = dynamicState->mHSTSSource;
1513
0
        }
1514
0
        return true;
1515
0
      }
1516
0
    } else {
1517
0
      // if a dynamic preload has expired and is not in the static preload
1518
0
      // list, we can remove it.
1519
0
      if (!GetPreloadStatus(aHost)) {
1520
0
        mPreloadStateStorage->Remove(preloadKey,
1521
0
                                     mozilla::DataStorage_Persistent);
1522
0
      }
1523
0
    }
1524
0
    return false;
1525
0
  }
1526
0
1527
0
  bool includeSubdomains = false;
1528
0
1529
0
  // Finally look in the static preload list.
1530
0
  if (siteState->mHSTSState == SecurityPropertyUnset &&
1531
0
      dynamicState->mHSTSState == SecurityPropertyUnset &&
1532
0
      GetPreloadStatus(aHost, &includeSubdomains)) {
1533
0
    SSSLOG(("%s is a preloaded HSTS host", aHost.get()));
1534
0
    *aResult = aRequireIncludeSubdomains ? includeSubdomains
1535
0
                                         : true;
1536
0
    if (aCached) {
1537
0
      // Only set cached if this includes subdomains
1538
0
      *aCached = aRequireIncludeSubdomains ? includeSubdomains
1539
0
                                           : true;
1540
0
    }
1541
0
    if (aSource) {
1542
0
      *aSource = SourcePreload;
1543
0
    }
1544
0
    return true;
1545
0
  }
1546
0
1547
0
  return false;
1548
0
}
1549
1550
nsresult
1551
nsSiteSecurityService::IsSecureHost(uint32_t aType, const nsACString& aHost,
1552
                                    uint32_t aFlags,
1553
                                    const OriginAttributes& aOriginAttributes,
1554
                                    bool* aCached,
1555
                                    SecurityPropertySource* aSource,
1556
                                    bool* aResult)
1557
0
{
1558
0
  // Child processes are not allowed direct access to this.
1559
0
  if (!XRE_IsParentProcess() && aType != nsISiteSecurityService::HEADER_HSTS) {
1560
0
    MOZ_CRASH("Child process: no direct access to "
1561
0
              "nsISiteSecurityService::IsSecureHost for non-HSTS entries");
1562
0
  }
1563
0
1564
0
  NS_ENSURE_ARG(aResult);
1565
0
1566
0
  // Only HSTS and HPKP are supported at the moment.
1567
0
  NS_ENSURE_TRUE(aType == nsISiteSecurityService::HEADER_HSTS ||
1568
0
                 aType == nsISiteSecurityService::HEADER_HPKP,
1569
0
                 NS_ERROR_NOT_IMPLEMENTED);
1570
0
1571
0
  // set default in case if we can't find any STS information
1572
0
  *aResult = false;
1573
0
1574
0
  /* An IP address never qualifies as a secure URI. */
1575
0
  const nsCString& flatHost = PromiseFlatCString(aHost);
1576
0
  if (HostIsIPAddress(flatHost)) {
1577
0
    return NS_OK;
1578
0
  }
1579
0
1580
0
  if (aType == nsISiteSecurityService::HEADER_HPKP) {
1581
0
    RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
1582
0
    if (!certVerifier) {
1583
0
      return NS_ERROR_FAILURE;
1584
0
    }
1585
0
    if (certVerifier->mPinningMode ==
1586
0
        CertVerifier::PinningMode::pinningDisabled) {
1587
0
      return NS_OK;
1588
0
    }
1589
0
    bool enforceTestMode = certVerifier->mPinningMode ==
1590
0
                           CertVerifier::PinningMode::pinningEnforceTestMode;
1591
0
    return PublicKeyPinningService::HostHasPins(flatHost.get(),
1592
0
                                                mozilla::pkix::Now(),
1593
0
                                                enforceTestMode, aOriginAttributes,
1594
0
                                                *aResult);
1595
0
  }
1596
0
1597
0
  // Holepunch chart.apis.google.com and subdomains.
1598
0
  nsAutoCString host(
1599
0
    PublicKeyPinningService::CanonicalizeHostname(flatHost.get()));
1600
0
  if (host.EqualsLiteral("chart.apis.google.com") ||
1601
0
      StringEndsWith(host, NS_LITERAL_CSTRING(".chart.apis.google.com"))) {
1602
0
    if (aCached) {
1603
0
      *aCached = true;
1604
0
    }
1605
0
    if (aSource) {
1606
0
      *aSource = SourcePreload;
1607
0
    }
1608
0
    return NS_OK;
1609
0
  }
1610
0
1611
0
  // First check the exact host.
1612
0
  if (HostHasHSTSEntry(host, false, aFlags, aOriginAttributes, aResult,
1613
0
                       aCached, aSource)) {
1614
0
    return NS_OK;
1615
0
  }
1616
0
1617
0
1618
0
  SSSLOG(("no HSTS data for %s found, walking up domain", host.get()));
1619
0
  const char *subdomain;
1620
0
1621
0
  uint32_t offset = 0;
1622
0
  for (offset = host.FindChar('.', offset) + 1;
1623
0
       offset > 0;
1624
0
       offset = host.FindChar('.', offset) + 1) {
1625
0
1626
0
    subdomain = host.get() + offset;
1627
0
1628
0
    // If we get an empty string, don't continue.
1629
0
    if (strlen(subdomain) < 1) {
1630
0
      break;
1631
0
    }
1632
0
1633
0
    // Do the same thing as with the exact host except now we're looking at
1634
0
    // ancestor domains of the original host and, therefore, we have to require
1635
0
    // that the entry includes subdomains.
1636
0
    nsAutoCString subdomainString(subdomain);
1637
0
1638
0
    if (HostHasHSTSEntry(subdomainString, true, aFlags, aOriginAttributes, aResult,
1639
0
                         aCached, aSource)) {
1640
0
      break;
1641
0
    }
1642
0
1643
0
    SSSLOG(("no HSTS data for %s found, walking up domain", subdomain));
1644
0
  }
1645
0
1646
0
  // Use whatever we ended up with, which defaults to false.
1647
0
  return NS_OK;
1648
0
}
1649
1650
NS_IMETHODIMP
1651
nsSiteSecurityService::ClearAll()
1652
0
{
1653
0
   // Child processes are not allowed direct access to this.
1654
0
   if (!XRE_IsParentProcess()) {
1655
0
     MOZ_CRASH("Child process: no direct access to nsISiteSecurityService::ClearAll");
1656
0
   }
1657
0
1658
0
  return mSiteStateStorage->Clear();
1659
0
}
1660
1661
NS_IMETHODIMP
1662
nsSiteSecurityService::ClearPreloads()
1663
0
{
1664
0
  // Child processes are not allowed direct access to this.
1665
0
  if (!XRE_IsParentProcess()) {
1666
0
    MOZ_CRASH("Child process: no direct access to nsISiteSecurityService::ClearPreloads");
1667
0
  }
1668
0
1669
0
  return mPreloadStateStorage->Clear();
1670
0
}
1671
1672
0
bool entryStateNotOK(SiteHPKPState& state, mozilla::pkix::Time& aEvalTime) {
1673
0
  return state.mState != SecurityPropertySet || state.IsExpired(aEvalTime) ||
1674
0
         state.mSHA256keys.Length() < 1;
1675
0
}
1676
1677
NS_IMETHODIMP
1678
nsSiteSecurityService::GetKeyPinsForHostname(
1679
  const nsACString& aHostname,
1680
  mozilla::pkix::Time& aEvalTime,
1681
  const OriginAttributes& aOriginAttributes,
1682
  /*out*/ nsTArray<nsCString>& pinArray,
1683
  /*out*/ bool* aIncludeSubdomains,
1684
  /*out*/ bool* afound)
1685
0
{
1686
0
  // Child processes are not allowed direct access to this.
1687
0
  if (!XRE_IsParentProcess()) {
1688
0
    MOZ_CRASH("Child process: no direct access to "
1689
0
              "nsISiteSecurityService::GetKeyPinsForHostname");
1690
0
  }
1691
0
1692
0
  NS_ENSURE_ARG(afound);
1693
0
1694
0
  const nsCString& flatHostname = PromiseFlatCString(aHostname);
1695
0
  SSSLOG(("Top of GetKeyPinsForHostname for %s", flatHostname.get()));
1696
0
  *afound = false;
1697
0
  *aIncludeSubdomains = false;
1698
0
  pinArray.Clear();
1699
0
1700
0
  nsAutoCString host(
1701
0
    PublicKeyPinningService::CanonicalizeHostname(flatHostname.get()));
1702
0
  nsAutoCString storageKey;
1703
0
  SetStorageKey(host, nsISiteSecurityService::HEADER_HPKP, aOriginAttributes,
1704
0
                storageKey);
1705
0
1706
0
  SSSLOG(("storagekey '%s'\n", storageKey.get()));
1707
0
  mozilla::DataStorageType storageType = mozilla::DataStorage_Persistent;
1708
0
  nsCString value = mSiteStateStorage->Get(storageKey, storageType);
1709
0
1710
0
  // decode now
1711
0
  RefPtr<SiteHPKPState> foundEntry =
1712
0
    new SiteHPKPState(host, aOriginAttributes, value);
1713
0
  if (entryStateNotOK(*foundEntry, aEvalTime)) {
1714
0
    // not in permanent storage, try now private
1715
0
    value = mSiteStateStorage->Get(storageKey, mozilla::DataStorage_Private);
1716
0
    RefPtr<SiteHPKPState> privateEntry =
1717
0
      new SiteHPKPState(host, aOriginAttributes, value);
1718
0
    if (entryStateNotOK(*privateEntry, aEvalTime)) {
1719
0
      // not in private storage, try dynamic preload
1720
0
      nsAutoCString preloadKey;
1721
0
      SetStorageKey(host, nsISiteSecurityService::HEADER_HPKP,
1722
0
                    OriginAttributes(), preloadKey);
1723
0
      value = mPreloadStateStorage->Get(preloadKey,
1724
0
                                        mozilla::DataStorage_Persistent);
1725
0
      RefPtr<SiteHPKPState> preloadEntry =
1726
0
        new SiteHPKPState(host, aOriginAttributes, value);
1727
0
      if (entryStateNotOK(*preloadEntry, aEvalTime)) {
1728
0
        return NS_OK;
1729
0
      }
1730
0
      foundEntry = preloadEntry;
1731
0
    } else {
1732
0
      foundEntry = privateEntry;
1733
0
    }
1734
0
  }
1735
0
  pinArray = foundEntry->mSHA256keys;
1736
0
  *aIncludeSubdomains = foundEntry->mIncludeSubdomains;
1737
0
  *afound = true;
1738
0
  return NS_OK;
1739
0
}
1740
1741
NS_IMETHODIMP
1742
nsSiteSecurityService::SetKeyPins(const nsACString& aHost,
1743
                                  bool aIncludeSubdomains,
1744
                                  int64_t aExpires, uint32_t aPinCount,
1745
                                  const char** aSha256Pins,
1746
                                  bool aIsPreload,
1747
                                  JS::HandleValue aOriginAttributes,
1748
                                  JSContext* aCx,
1749
                                  uint8_t aArgc,
1750
                                  /*out*/ bool* aResult)
1751
0
{
1752
0
  // Child processes are not allowed direct access to this.
1753
0
  if (!XRE_IsParentProcess()) {
1754
0
    MOZ_CRASH("Child process: no direct access to "
1755
0
              "nsISiteSecurityService::SetKeyPins");
1756
0
  }
1757
0
1758
0
  NS_ENSURE_ARG_POINTER(aResult);
1759
0
  NS_ENSURE_ARG_POINTER(aSha256Pins);
1760
0
  OriginAttributes originAttributes;
1761
0
  if (aArgc > 1) {
1762
0
    // OriginAttributes were passed in.
1763
0
    if (!aOriginAttributes.isObject() ||
1764
0
        !originAttributes.Init(aCx, aOriginAttributes)) {
1765
0
      return NS_ERROR_INVALID_ARG;
1766
0
    }
1767
0
  }
1768
0
  if (aIsPreload && originAttributes != OriginAttributes()) {
1769
0
    return NS_ERROR_INVALID_ARG;
1770
0
  }
1771
0
1772
0
  SSSLOG(("Top of SetKeyPins"));
1773
0
1774
0
  nsTArray<nsCString> sha256keys;
1775
0
  for (unsigned int i = 0; i < aPinCount; i++) {
1776
0
    nsAutoCString pin(aSha256Pins[i]);
1777
0
    SSSLOG(("SetPins pin=%s\n", pin.get()));
1778
0
    if (!stringIsBase64EncodingOf256bitValue(pin)) {
1779
0
      return NS_ERROR_INVALID_ARG;
1780
0
    }
1781
0
    sha256keys.AppendElement(pin);
1782
0
  }
1783
0
  // we always store data in permanent storage (ie no flags)
1784
0
  const nsCString& flatHost = PromiseFlatCString(aHost);
1785
0
  nsAutoCString host(
1786
0
    PublicKeyPinningService::CanonicalizeHostname(flatHost.get()));
1787
0
  RefPtr<SiteHPKPState> dynamicEntry = new SiteHPKPState(host, originAttributes,
1788
0
    aExpires, SecurityPropertySet, aIncludeSubdomains, sha256keys);
1789
0
  return SetHPKPState(host.get(), *dynamicEntry, 0, aIsPreload, originAttributes);
1790
0
}
1791
1792
NS_IMETHODIMP
1793
nsSiteSecurityService::SetHSTSPreload(const nsACString& aHost,
1794
                                      bool aIncludeSubdomains,
1795
                                      int64_t aExpires,
1796
                              /*out*/ bool* aResult)
1797
0
{
1798
0
  // Child processes are not allowed direct access to this.
1799
0
  if (!XRE_IsParentProcess()) {
1800
0
    MOZ_CRASH("Child process: no direct access to "
1801
0
              "nsISiteSecurityService::SetHSTSPreload");
1802
0
  }
1803
0
1804
0
  NS_ENSURE_ARG_POINTER(aResult);
1805
0
1806
0
  SSSLOG(("Top of SetHSTSPreload"));
1807
0
1808
0
  const nsCString& flatHost = PromiseFlatCString(aHost);
1809
0
  nsAutoCString host(
1810
0
    PublicKeyPinningService::CanonicalizeHostname(flatHost.get()));
1811
0
  return SetHSTSState(nsISiteSecurityService::HEADER_HSTS, host.get(), aExpires,
1812
0
                      aIncludeSubdomains, 0, SecurityPropertySet,
1813
0
                      SourcePreload, OriginAttributes());
1814
0
}
1815
1816
nsresult
1817
nsSiteSecurityService::SetHPKPState(const char* aHost, SiteHPKPState& entry,
1818
                                    uint32_t aFlags, bool aIsPreload,
1819
                                    const OriginAttributes& aOriginAttributes)
1820
0
{
1821
0
  if (aIsPreload && aOriginAttributes != OriginAttributes()) {
1822
0
    return NS_ERROR_INVALID_ARG;
1823
0
  }
1824
0
  SSSLOG(("Top of SetPKPState"));
1825
0
  nsAutoCString host(aHost);
1826
0
  nsAutoCString storageKey;
1827
0
  SetStorageKey(host, nsISiteSecurityService::HEADER_HPKP, aOriginAttributes,
1828
0
                storageKey);
1829
0
  bool isPrivate = aFlags & nsISocketProvider::NO_PERMANENT_STORAGE;
1830
0
  mozilla::DataStorageType storageType = isPrivate
1831
0
                                         ? mozilla::DataStorage_Private
1832
0
                                         : mozilla::DataStorage_Persistent;
1833
0
  nsAutoCString stateString;
1834
0
  entry.ToString(stateString);
1835
0
1836
0
  nsresult rv;
1837
0
  if (aIsPreload) {
1838
0
    rv = mPreloadStateStorage->Put(storageKey, stateString,
1839
0
                                   mozilla::DataStorage_Persistent);
1840
0
  } else {
1841
0
    rv = mSiteStateStorage->Put(storageKey, stateString, storageType);
1842
0
  }
1843
0
  NS_ENSURE_SUCCESS(rv, rv);
1844
0
  return NS_OK;
1845
0
}
1846
1847
NS_IMETHODIMP
1848
nsSiteSecurityService::Enumerate(uint32_t aType,
1849
                                 nsISimpleEnumerator** aEnumerator)
1850
0
{
1851
0
  NS_ENSURE_ARG(aEnumerator);
1852
0
1853
0
  nsAutoCString keySuffix;
1854
0
  switch (aType) {
1855
0
    case nsISiteSecurityService::HEADER_HSTS:
1856
0
      keySuffix.AssignASCII(kHSTSKeySuffix);
1857
0
      break;
1858
0
    case nsISiteSecurityService::HEADER_HPKP:
1859
0
      keySuffix.AssignASCII(kHPKPKeySuffix);
1860
0
      break;
1861
0
    default:
1862
0
      return NS_ERROR_INVALID_ARG;
1863
0
  }
1864
0
1865
0
  InfallibleTArray<mozilla::dom::DataStorageItem> items;
1866
0
  mSiteStateStorage->GetAll(&items);
1867
0
1868
0
  nsCOMArray<nsISiteSecurityState> states;
1869
0
  for (const mozilla::dom::DataStorageItem& item : items) {
1870
0
    if (!StringEndsWith(item.key(), keySuffix)) {
1871
0
      // The key does not end with correct suffix, so is not the type we want.
1872
0
      continue;
1873
0
    }
1874
0
1875
0
    nsCString origin(
1876
0
      StringHead(item.key(), item.key().Length() - keySuffix.Length()));
1877
0
    nsAutoCString hostname;
1878
0
    OriginAttributes originAttributes;
1879
0
    if (!originAttributes.PopulateFromOrigin(origin, hostname)) {
1880
0
      return NS_ERROR_FAILURE;
1881
0
    }
1882
0
1883
0
    nsCOMPtr<nsISiteSecurityState> state;
1884
0
    switch(aType) {
1885
0
      case nsISiteSecurityService::HEADER_HSTS:
1886
0
        state = new SiteHSTSState(hostname, originAttributes, item.value());
1887
0
        break;
1888
0
      case nsISiteSecurityService::HEADER_HPKP:
1889
0
        state = new SiteHPKPState(hostname, originAttributes, item.value());
1890
0
        break;
1891
0
      default:
1892
0
        MOZ_ASSERT_UNREACHABLE("SSS:Enumerate got invalid type");
1893
0
    }
1894
0
1895
0
    states.AppendObject(state);
1896
0
  }
1897
0
1898
0
  NS_NewArrayEnumerator(aEnumerator, states, NS_GET_IID(nsISiteSecurityState));
1899
0
  return NS_OK;
1900
0
}
1901
1902
//------------------------------------------------------------
1903
// nsSiteSecurityService::nsIObserver
1904
//------------------------------------------------------------
1905
1906
NS_IMETHODIMP
1907
nsSiteSecurityService::Observe(nsISupports* /*subject*/, const char* topic,
1908
                               const char16_t* /*data*/)
1909
0
{
1910
0
  // Don't access Preferences off the main thread.
1911
0
  if (!NS_IsMainThread()) {
1912
0
    MOZ_ASSERT_UNREACHABLE("Preferences accessed off main thread");
1913
0
    return NS_ERROR_NOT_SAME_THREAD;
1914
0
  }
1915
0
1916
0
  if (strcmp(topic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0) {
1917
0
    mUsePreloadList = mozilla::Preferences::GetBool(
1918
0
      "network.stricttransportsecurity.preloadlist", true);
1919
0
    mPreloadListTimeOffset =
1920
0
      mozilla::Preferences::GetInt("test.currentTimeOffsetSeconds", 0);
1921
0
    mProcessPKPHeadersFromNonBuiltInRoots = mozilla::Preferences::GetBool(
1922
0
      "security.cert_pinning.process_headers_from_non_builtin_roots", false);
1923
0
    mMaxMaxAge = mozilla::Preferences::GetInt(
1924
0
      "security.cert_pinning.max_max_age_seconds", kSixtyDaysInSeconds);
1925
0
  }
1926
0
1927
0
  return NS_OK;
1928
0
}