mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-05 23:07:31 +09:00
import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo
This commit is contained in:
commit
dcd9973243
150858 changed files with 23884658 additions and 0 deletions
231
security/manager/ssl/CSTrustDomain.cpp
Normal file
231
security/manager/ssl/CSTrustDomain.cpp
Normal file
|
|
@ -0,0 +1,231 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "CSTrustDomain.h"
|
||||
#include "mozilla/Base64.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "nsNSSCertificate.h"
|
||||
#include "nsNSSComponent.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "pkix/pkixnss.h"
|
||||
|
||||
using namespace mozilla::pkix;
|
||||
|
||||
namespace mozilla { namespace psm {
|
||||
|
||||
static LazyLogModule gTrustDomainPRLog("CSTrustDomain");
|
||||
#define CSTrust_LOG(args) MOZ_LOG(gTrustDomainPRLog, LogLevel::Debug, args)
|
||||
|
||||
CSTrustDomain::CSTrustDomain(UniqueCERTCertList& certChain)
|
||||
: mCertChain(certChain)
|
||||
, mCertBlocklist(do_GetService(NS_CERTBLOCKLIST_CONTRACTID))
|
||||
{
|
||||
}
|
||||
|
||||
Result
|
||||
CSTrustDomain::GetCertTrust(EndEntityOrCA endEntityOrCA,
|
||||
const CertPolicyId& policy, Input candidateCertDER,
|
||||
/*out*/ TrustLevel& trustLevel)
|
||||
{
|
||||
MOZ_ASSERT(policy.IsAnyPolicy());
|
||||
if (!policy.IsAnyPolicy()) {
|
||||
return Result::FATAL_ERROR_INVALID_ARGS;
|
||||
}
|
||||
|
||||
SECItem candidateCertDERSECItem = UnsafeMapInputToSECItem(candidateCertDER);
|
||||
UniqueCERTCertificate candidateCert(
|
||||
CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &candidateCertDERSECItem,
|
||||
nullptr, false, true));
|
||||
if (!candidateCert) {
|
||||
return MapPRErrorCodeToResult(PR_GetError());
|
||||
}
|
||||
|
||||
bool isCertRevoked;
|
||||
nsresult nsrv = mCertBlocklist->IsCertRevoked(
|
||||
candidateCert->derIssuer.data,
|
||||
candidateCert->derIssuer.len,
|
||||
candidateCert->serialNumber.data,
|
||||
candidateCert->serialNumber.len,
|
||||
candidateCert->derSubject.data,
|
||||
candidateCert->derSubject.len,
|
||||
candidateCert->derPublicKey.data,
|
||||
candidateCert->derPublicKey.len,
|
||||
&isCertRevoked);
|
||||
if (NS_FAILED(nsrv)) {
|
||||
return Result::FATAL_ERROR_LIBRARY_FAILURE;
|
||||
}
|
||||
|
||||
if (isCertRevoked) {
|
||||
CSTrust_LOG(("CSTrustDomain: certificate is revoked\n"));
|
||||
return Result::ERROR_REVOKED_CERTIFICATE;
|
||||
}
|
||||
|
||||
// Is this cert our built-in content signing root?
|
||||
bool isRoot = false;
|
||||
nsCOMPtr<nsINSSComponent> component(do_GetService(PSM_COMPONENT_CONTRACTID));
|
||||
if (!component) {
|
||||
return Result::FATAL_ERROR_LIBRARY_FAILURE;
|
||||
}
|
||||
nsrv = component->IsCertContentSigningRoot(candidateCert.get(), isRoot);
|
||||
if (NS_FAILED(nsrv)) {
|
||||
return Result::FATAL_ERROR_LIBRARY_FAILURE;
|
||||
}
|
||||
if (isRoot) {
|
||||
CSTrust_LOG(("CSTrustDomain: certificate is a trust anchor\n"));
|
||||
trustLevel = TrustLevel::TrustAnchor;
|
||||
return Success;
|
||||
}
|
||||
CSTrust_LOG(("CSTrustDomain: certificate is *not* a trust anchor\n"));
|
||||
|
||||
trustLevel = TrustLevel::InheritsTrust;
|
||||
return Success;
|
||||
}
|
||||
|
||||
Result
|
||||
CSTrustDomain::FindIssuer(Input encodedIssuerName, IssuerChecker& checker,
|
||||
Time time)
|
||||
{
|
||||
// Loop over the chain, look for a matching subject
|
||||
for (CERTCertListNode* n = CERT_LIST_HEAD(mCertChain);
|
||||
!CERT_LIST_END(n, mCertChain); n = CERT_LIST_NEXT(n)) {
|
||||
Input certDER;
|
||||
Result rv = certDER.Init(n->cert->derCert.data, n->cert->derCert.len);
|
||||
if (rv != Success) {
|
||||
continue; // probably too big
|
||||
}
|
||||
|
||||
// if the subject does not match, try the next certificate
|
||||
Input subjectDER;
|
||||
rv = subjectDER.Init(n->cert->derSubject.data, n->cert->derSubject.len);
|
||||
if (rv != Success) {
|
||||
continue; // just try the next one
|
||||
}
|
||||
if (!InputsAreEqual(subjectDER, encodedIssuerName)) {
|
||||
CSTrust_LOG(("CSTrustDomain: subjects don't match\n"));
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the subject does match, try the next step
|
||||
bool keepGoing;
|
||||
rv = checker.Check(certDER, nullptr/*additionalNameConstraints*/,
|
||||
keepGoing);
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
if (!keepGoing) {
|
||||
CSTrust_LOG(("CSTrustDomain: don't keep going\n"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return Success;
|
||||
}
|
||||
|
||||
Result
|
||||
CSTrustDomain::CheckRevocation(EndEntityOrCA endEntityOrCA,
|
||||
const CertID& certID, Time time,
|
||||
Duration validityDuration,
|
||||
/*optional*/ const Input* stapledOCSPresponse,
|
||||
/*optional*/ const Input* aiaExtension)
|
||||
{
|
||||
// We're relying solely on the CertBlocklist for revocation - and we're
|
||||
// performing checks on this in GetCertTrust (as per nsNSSCertDBTrustDomain)
|
||||
return Success;
|
||||
}
|
||||
|
||||
Result
|
||||
CSTrustDomain::IsChainValid(const DERArray& certChain, Time time)
|
||||
{
|
||||
// Check that our chain is not empty
|
||||
if (certChain.GetLength() == 0) {
|
||||
return Result::FATAL_ERROR_LIBRARY_FAILURE;
|
||||
}
|
||||
|
||||
return Success;
|
||||
}
|
||||
|
||||
Result
|
||||
CSTrustDomain::CheckSignatureDigestAlgorithm(DigestAlgorithm digestAlg,
|
||||
EndEntityOrCA endEntityOrCA,
|
||||
Time notBefore)
|
||||
{
|
||||
if (digestAlg == DigestAlgorithm::sha1) {
|
||||
return Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED;
|
||||
}
|
||||
return Success;
|
||||
}
|
||||
|
||||
Result
|
||||
CSTrustDomain::CheckRSAPublicKeyModulusSizeInBits(
|
||||
EndEntityOrCA endEntityOrCA, unsigned int modulusSizeInBits)
|
||||
{
|
||||
if (modulusSizeInBits < 2048) {
|
||||
return Result::ERROR_INADEQUATE_KEY_SIZE;
|
||||
}
|
||||
return Success;
|
||||
}
|
||||
|
||||
Result
|
||||
CSTrustDomain::VerifyRSAPKCS1SignedDigest(const SignedDigest& signedDigest,
|
||||
Input subjectPublicKeyInfo)
|
||||
{
|
||||
return VerifyRSAPKCS1SignedDigestNSS(signedDigest, subjectPublicKeyInfo,
|
||||
nullptr);
|
||||
}
|
||||
|
||||
Result
|
||||
CSTrustDomain::CheckECDSACurveIsAcceptable(EndEntityOrCA endEntityOrCA,
|
||||
NamedCurve curve)
|
||||
{
|
||||
switch (curve) {
|
||||
case NamedCurve::secp256r1: // fall through
|
||||
case NamedCurve::secp384r1: // fall through
|
||||
case NamedCurve::secp521r1:
|
||||
return Success;
|
||||
}
|
||||
|
||||
return Result::ERROR_UNSUPPORTED_ELLIPTIC_CURVE;
|
||||
}
|
||||
|
||||
Result
|
||||
CSTrustDomain::VerifyECDSASignedDigest(const SignedDigest& signedDigest,
|
||||
Input subjectPublicKeyInfo)
|
||||
{
|
||||
return VerifyECDSASignedDigestNSS(signedDigest, subjectPublicKeyInfo,
|
||||
nullptr);
|
||||
}
|
||||
|
||||
Result
|
||||
CSTrustDomain::CheckValidityIsAcceptable(Time notBefore, Time notAfter,
|
||||
EndEntityOrCA endEntityOrCA,
|
||||
KeyPurposeId keyPurpose)
|
||||
{
|
||||
return Success;
|
||||
}
|
||||
|
||||
Result
|
||||
CSTrustDomain::NetscapeStepUpMatchesServerAuth(Time notBefore,
|
||||
/*out*/ bool& matches)
|
||||
{
|
||||
matches = false;
|
||||
return Success;
|
||||
}
|
||||
|
||||
void
|
||||
CSTrustDomain::NoteAuxiliaryExtension(AuxiliaryExtension /*extension*/,
|
||||
Input /*extensionData*/)
|
||||
{
|
||||
}
|
||||
|
||||
Result
|
||||
CSTrustDomain::DigestBuf(Input item, DigestAlgorithm digestAlg,
|
||||
/*out*/ uint8_t* digestBuf, size_t digestBufLen)
|
||||
{
|
||||
return DigestBufNSS(item, digestAlg, digestBuf, digestBufLen);
|
||||
}
|
||||
|
||||
} } // end namespace mozilla::psm
|
||||
80
security/manager/ssl/CSTrustDomain.h
Normal file
80
security/manager/ssl/CSTrustDomain.h
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef CSTrustDomain_h
|
||||
#define CSTrustDomain_h
|
||||
|
||||
#include "pkix/pkixtypes.h"
|
||||
#include "mozilla/StaticMutex.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "nsDebug.h"
|
||||
#include "nsICertBlocklist.h"
|
||||
#include "nsIX509CertDB.h"
|
||||
#include "ScopedNSSTypes.h"
|
||||
|
||||
namespace mozilla { namespace psm {
|
||||
|
||||
class CSTrustDomain final : public mozilla::pkix::TrustDomain
|
||||
{
|
||||
public:
|
||||
typedef mozilla::pkix::Result Result;
|
||||
|
||||
explicit CSTrustDomain(UniqueCERTCertList& certChain);
|
||||
|
||||
virtual Result GetCertTrust(
|
||||
mozilla::pkix::EndEntityOrCA endEntityOrCA,
|
||||
const mozilla::pkix::CertPolicyId& policy,
|
||||
mozilla::pkix::Input candidateCertDER,
|
||||
/*out*/ mozilla::pkix::TrustLevel& trustLevel) override;
|
||||
virtual Result FindIssuer(mozilla::pkix::Input encodedIssuerName,
|
||||
IssuerChecker& checker,
|
||||
mozilla::pkix::Time time) override;
|
||||
virtual Result CheckRevocation(
|
||||
mozilla::pkix::EndEntityOrCA endEntityOrCA,
|
||||
const mozilla::pkix::CertID& certID, mozilla::pkix::Time time,
|
||||
mozilla::pkix::Duration validityDuration,
|
||||
/*optional*/ const mozilla::pkix::Input* stapledOCSPresponse,
|
||||
/*optional*/ const mozilla::pkix::Input* aiaExtension) override;
|
||||
virtual Result IsChainValid(const mozilla::pkix::DERArray& certChain,
|
||||
mozilla::pkix::Time time) override;
|
||||
virtual Result CheckSignatureDigestAlgorithm(
|
||||
mozilla::pkix::DigestAlgorithm digestAlg,
|
||||
mozilla::pkix::EndEntityOrCA endEntityOrCA,
|
||||
mozilla::pkix::Time notBefore) override;
|
||||
virtual Result CheckRSAPublicKeyModulusSizeInBits(
|
||||
mozilla::pkix::EndEntityOrCA endEntityOrCA,
|
||||
unsigned int modulusSizeInBits) override;
|
||||
virtual Result VerifyRSAPKCS1SignedDigest(
|
||||
const mozilla::pkix::SignedDigest& signedDigest,
|
||||
mozilla::pkix::Input subjectPublicKeyInfo) override;
|
||||
virtual Result CheckECDSACurveIsAcceptable(
|
||||
mozilla::pkix::EndEntityOrCA endEntityOrCA,
|
||||
mozilla::pkix::NamedCurve curve) override;
|
||||
virtual Result VerifyECDSASignedDigest(
|
||||
const mozilla::pkix::SignedDigest& signedDigest,
|
||||
mozilla::pkix::Input subjectPublicKeyInfo) override;
|
||||
virtual Result CheckValidityIsAcceptable(
|
||||
mozilla::pkix::Time notBefore, mozilla::pkix::Time notAfter,
|
||||
mozilla::pkix::EndEntityOrCA endEntityOrCA,
|
||||
mozilla::pkix::KeyPurposeId keyPurpose) override;
|
||||
virtual Result NetscapeStepUpMatchesServerAuth(
|
||||
mozilla::pkix::Time notBefore, /*out*/ bool& matches) override;
|
||||
virtual void NoteAuxiliaryExtension(
|
||||
mozilla::pkix::AuxiliaryExtension extension,
|
||||
mozilla::pkix::Input extensionData) override;
|
||||
virtual Result DigestBuf(mozilla::pkix::Input item,
|
||||
mozilla::pkix::DigestAlgorithm digestAlg,
|
||||
/*out*/ uint8_t* digestBuf,
|
||||
size_t digestBufLen) override;
|
||||
|
||||
private:
|
||||
/*out*/ UniqueCERTCertList& mCertChain;
|
||||
nsCOMPtr<nsICertBlocklist> mCertBlocklist;
|
||||
};
|
||||
|
||||
} } // namespace mozilla::psm
|
||||
|
||||
#endif // CSTrustDomain_h
|
||||
671
security/manager/ssl/CertBlocklist.cpp
Normal file
671
security/manager/ssl/CertBlocklist.cpp
Normal file
|
|
@ -0,0 +1,671 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "CertBlocklist.h"
|
||||
|
||||
#include "mozilla/Base64.h"
|
||||
#include "mozilla/Casting.h"
|
||||
#include "mozilla/Logging.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/Unused.h"
|
||||
#include "nsAppDirectoryServiceDefs.h"
|
||||
#include "nsCRTGlue.h"
|
||||
#include "nsDirectoryServiceUtils.h"
|
||||
#include "nsICryptoHash.h"
|
||||
#include "nsIFileStreams.h"
|
||||
#include "nsILineInputStream.h"
|
||||
#include "nsISafeOutputStream.h"
|
||||
#include "nsIX509Cert.h"
|
||||
#include "nsNetCID.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsTHashtable.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "pkix/Input.h"
|
||||
#include "prtime.h"
|
||||
|
||||
NS_IMPL_ISUPPORTS(CertBlocklist, nsICertBlocklist)
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::pkix;
|
||||
|
||||
#define PREF_BACKGROUND_UPDATE_TIMER "app.update.lastUpdateTime.blocklist-background-update-timer"
|
||||
#define PREF_BLOCKLIST_ONECRL_CHECKED "services.blocklist.onecrl.checked"
|
||||
#define PREF_MAX_STALENESS_IN_SECONDS "security.onecrl.maximum_staleness_in_seconds"
|
||||
#define PREF_ONECRL_VIA_AMO "security.onecrl.via.amo"
|
||||
|
||||
static LazyLogModule gCertBlockPRLog("CertBlock");
|
||||
|
||||
uint32_t CertBlocklist::sLastBlocklistUpdate = 0U;
|
||||
uint32_t CertBlocklist::sLastKintoUpdate = 0U;
|
||||
uint32_t CertBlocklist::sMaxStaleness = 0U;
|
||||
bool CertBlocklist::sUseAMO = true;
|
||||
|
||||
CertBlocklistItem::CertBlocklistItem(const uint8_t* DNData,
|
||||
size_t DNLength,
|
||||
const uint8_t* otherData,
|
||||
size_t otherLength,
|
||||
CertBlocklistItemMechanism itemMechanism)
|
||||
: mIsCurrent(false)
|
||||
, mItemMechanism(itemMechanism)
|
||||
{
|
||||
mDNData = new uint8_t[DNLength];
|
||||
memcpy(mDNData, DNData, DNLength);
|
||||
mDNLength = DNLength;
|
||||
|
||||
mOtherData = new uint8_t[otherLength];
|
||||
memcpy(mOtherData, otherData, otherLength);
|
||||
mOtherLength = otherLength;
|
||||
}
|
||||
|
||||
CertBlocklistItem::CertBlocklistItem(const CertBlocklistItem& aItem)
|
||||
{
|
||||
mDNLength = aItem.mDNLength;
|
||||
mDNData = new uint8_t[mDNLength];
|
||||
memcpy(mDNData, aItem.mDNData, mDNLength);
|
||||
|
||||
mOtherLength = aItem.mOtherLength;
|
||||
mOtherData = new uint8_t[mOtherLength];
|
||||
memcpy(mOtherData, aItem.mOtherData, mOtherLength);
|
||||
|
||||
mItemMechanism = aItem.mItemMechanism;
|
||||
|
||||
mIsCurrent = aItem.mIsCurrent;
|
||||
}
|
||||
|
||||
CertBlocklistItem::~CertBlocklistItem()
|
||||
{
|
||||
delete[] mDNData;
|
||||
delete[] mOtherData;
|
||||
}
|
||||
|
||||
nsresult
|
||||
CertBlocklistItem::ToBase64(nsACString& b64DNOut, nsACString& b64OtherOut)
|
||||
{
|
||||
nsDependentCSubstring DNString(BitwiseCast<char*, uint8_t*>(mDNData),
|
||||
mDNLength);
|
||||
nsDependentCSubstring otherString(BitwiseCast<char*, uint8_t*>(mOtherData),
|
||||
mOtherLength);
|
||||
nsresult rv = Base64Encode(DNString, b64DNOut);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
rv = Base64Encode(otherString, b64OtherOut);
|
||||
return rv;
|
||||
}
|
||||
|
||||
bool
|
||||
CertBlocklistItem::operator==(const CertBlocklistItem& aItem) const
|
||||
{
|
||||
if (aItem.mItemMechanism != mItemMechanism) {
|
||||
return false;
|
||||
}
|
||||
if (aItem.mDNLength != mDNLength ||
|
||||
aItem.mOtherLength != mOtherLength) {
|
||||
return false;
|
||||
}
|
||||
return memcmp(aItem.mDNData, mDNData, mDNLength) == 0 &&
|
||||
memcmp(aItem.mOtherData, mOtherData, mOtherLength) == 0;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
CertBlocklistItem::Hash() const
|
||||
{
|
||||
uint32_t hash;
|
||||
// there's no requirement for a serial to be as large as the size of the hash
|
||||
// key; if it's smaller, fall back to the first octet (otherwise, the last
|
||||
// four)
|
||||
if (mItemMechanism == BlockByIssuerAndSerial &&
|
||||
mOtherLength >= sizeof(hash)) {
|
||||
memcpy(&hash, mOtherData + mOtherLength - sizeof(hash), sizeof(hash));
|
||||
} else {
|
||||
hash = *mOtherData;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
CertBlocklist::CertBlocklist()
|
||||
: mMutex("CertBlocklist::mMutex")
|
||||
, mModified(false)
|
||||
, mBackingFileIsInitialized(false)
|
||||
, mBackingFile(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
CertBlocklist::~CertBlocklist()
|
||||
{
|
||||
Preferences::UnregisterCallback(CertBlocklist::PreferenceChanged,
|
||||
PREF_BACKGROUND_UPDATE_TIMER,
|
||||
this);
|
||||
Preferences::UnregisterCallback(CertBlocklist::PreferenceChanged,
|
||||
PREF_MAX_STALENESS_IN_SECONDS,
|
||||
this);
|
||||
Preferences::UnregisterCallback(CertBlocklist::PreferenceChanged,
|
||||
PREF_ONECRL_VIA_AMO,
|
||||
this);
|
||||
Preferences::UnregisterCallback(CertBlocklist::PreferenceChanged,
|
||||
PREF_BLOCKLIST_ONECRL_CHECKED,
|
||||
this);
|
||||
}
|
||||
|
||||
nsresult
|
||||
CertBlocklist::Init()
|
||||
{
|
||||
MOZ_LOG(gCertBlockPRLog, LogLevel::Debug, ("CertBlocklist::Init"));
|
||||
|
||||
// Init must be on main thread for getting the profile directory
|
||||
if (!NS_IsMainThread()) {
|
||||
MOZ_LOG(gCertBlockPRLog, LogLevel::Debug,
|
||||
("CertBlocklist::Init - called off main thread"));
|
||||
return NS_ERROR_NOT_SAME_THREAD;
|
||||
}
|
||||
|
||||
// Register preference callbacks
|
||||
nsresult rv =
|
||||
Preferences::RegisterCallbackAndCall(CertBlocklist::PreferenceChanged,
|
||||
PREF_BACKGROUND_UPDATE_TIMER,
|
||||
this);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
rv = Preferences::RegisterCallbackAndCall(CertBlocklist::PreferenceChanged,
|
||||
PREF_MAX_STALENESS_IN_SECONDS,
|
||||
this);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
rv = Preferences::RegisterCallbackAndCall(CertBlocklist::PreferenceChanged,
|
||||
PREF_ONECRL_VIA_AMO,
|
||||
this);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
rv = Preferences::RegisterCallbackAndCall(CertBlocklist::PreferenceChanged,
|
||||
PREF_BLOCKLIST_ONECRL_CHECKED,
|
||||
this);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Get the profile directory
|
||||
rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
|
||||
getter_AddRefs(mBackingFile));
|
||||
if (NS_FAILED(rv) || !mBackingFile) {
|
||||
MOZ_LOG(gCertBlockPRLog, LogLevel::Debug,
|
||||
("CertBlocklist::Init - couldn't get profile dir"));
|
||||
// Since we're returning NS_OK here, set mBackingFile to a safe value.
|
||||
// (We need initialization to succeed and CertBlocklist to be in a
|
||||
// well-defined state if the profile directory doesn't exist.)
|
||||
mBackingFile = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
rv = mBackingFile->Append(NS_LITERAL_STRING("revocations.txt"));
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
nsAutoCString path;
|
||||
rv = mBackingFile->GetNativePath(path);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
MOZ_LOG(gCertBlockPRLog, LogLevel::Debug,
|
||||
("CertBlocklist::Init certList path: %s", path.get()));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
CertBlocklist::EnsureBackingFileInitialized(MutexAutoLock& lock)
|
||||
{
|
||||
MOZ_LOG(gCertBlockPRLog, LogLevel::Debug,
|
||||
("CertBlocklist::EnsureBackingFileInitialized"));
|
||||
if (mBackingFileIsInitialized || !mBackingFile) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
MOZ_LOG(gCertBlockPRLog, LogLevel::Debug,
|
||||
("CertBlocklist::EnsureBackingFileInitialized - not initialized"));
|
||||
|
||||
bool exists = false;
|
||||
nsresult rv = mBackingFile->Exists(&exists);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
if (!exists) {
|
||||
MOZ_LOG(gCertBlockPRLog, LogLevel::Warning,
|
||||
("CertBlocklist::EnsureBackingFileInitialized no revocations file"));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Load the revocations file into the cert blocklist
|
||||
nsCOMPtr<nsIFileInputStream> fileStream(
|
||||
do_CreateInstance(NS_LOCALFILEINPUTSTREAM_CONTRACTID, &rv));
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = fileStream->Init(mBackingFile, -1, -1, false);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsILineInputStream> lineStream(do_QueryInterface(fileStream, &rv));
|
||||
nsAutoCString line;
|
||||
nsAutoCString DN;
|
||||
nsAutoCString other;
|
||||
CertBlocklistItemMechanism mechanism;
|
||||
// read in the revocations file. The file format is as follows: each line
|
||||
// contains a comment, base64 encoded DER for a DN, base64 encoded DER for a
|
||||
// serial number or a Base64 encoded SHA256 hash of a public key. Comment
|
||||
// lines start with '#', serial number lines, ' ' (a space), public key hashes
|
||||
// with '\t' (a tab) and anything else is assumed to be a DN.
|
||||
bool more = true;
|
||||
do {
|
||||
rv = lineStream->ReadLine(line, &more);
|
||||
if (NS_FAILED(rv)) {
|
||||
break;
|
||||
}
|
||||
// ignore comments and empty lines
|
||||
if (line.IsEmpty() || line.First() == '#') {
|
||||
continue;
|
||||
}
|
||||
if (line.First() != ' ' && line.First() != '\t') {
|
||||
DN = line;
|
||||
continue;
|
||||
}
|
||||
other = line;
|
||||
if (line.First() == ' ') {
|
||||
mechanism = BlockByIssuerAndSerial;
|
||||
} else {
|
||||
mechanism = BlockBySubjectAndPubKey;
|
||||
}
|
||||
other.Trim(" \t", true, false, false);
|
||||
// Serial numbers and public key hashes 'belong' to the last DN line seen;
|
||||
// if no DN has been seen, the serial number or public key hash is ignored.
|
||||
if (DN.IsEmpty() || other.IsEmpty()) {
|
||||
continue;
|
||||
}
|
||||
MOZ_LOG(gCertBlockPRLog, LogLevel::Debug,
|
||||
("CertBlocklist::EnsureBackingFileInitialized adding: %s %s",
|
||||
DN.get(), other.get()));
|
||||
|
||||
MOZ_LOG(gCertBlockPRLog, LogLevel::Debug,
|
||||
("CertBlocklist::EnsureBackingFileInitialized - pre-decode"));
|
||||
|
||||
rv = AddRevokedCertInternal(DN, other, mechanism, CertOldFromLocalCache,
|
||||
lock);
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
// we warn here, rather than abandoning, since we need to
|
||||
// ensure that as many items as possible are read
|
||||
MOZ_LOG(gCertBlockPRLog, LogLevel::Warning,
|
||||
("CertBlocklist::EnsureBackingFileInitialized adding revoked cert "
|
||||
"failed"));
|
||||
}
|
||||
} while (more);
|
||||
mBackingFileIsInitialized = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
CertBlocklist::RevokeCertBySubjectAndPubKey(const char* aSubject,
|
||||
const char* aPubKeyHash)
|
||||
{
|
||||
MOZ_LOG(gCertBlockPRLog, LogLevel::Debug,
|
||||
("CertBlocklist::RevokeCertBySubjectAndPubKey - subject is: %s and pubKeyHash: %s",
|
||||
aSubject, aPubKeyHash));
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
||||
return AddRevokedCertInternal(nsDependentCString(aSubject),
|
||||
nsDependentCString(aPubKeyHash),
|
||||
BlockBySubjectAndPubKey,
|
||||
CertNewFromBlocklist, lock);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
CertBlocklist::RevokeCertByIssuerAndSerial(const char* aIssuer,
|
||||
const char* aSerialNumber)
|
||||
{
|
||||
MOZ_LOG(gCertBlockPRLog, LogLevel::Debug,
|
||||
("CertBlocklist::RevokeCertByIssuerAndSerial - issuer is: %s and serial: %s",
|
||||
aIssuer, aSerialNumber));
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
||||
return AddRevokedCertInternal(nsDependentCString(aIssuer),
|
||||
nsDependentCString(aSerialNumber),
|
||||
BlockByIssuerAndSerial,
|
||||
CertNewFromBlocklist, lock);
|
||||
}
|
||||
|
||||
nsresult
|
||||
CertBlocklist::AddRevokedCertInternal(const nsACString& aEncodedDN,
|
||||
const nsACString& aEncodedOther,
|
||||
CertBlocklistItemMechanism aMechanism,
|
||||
CertBlocklistItemState aItemState,
|
||||
MutexAutoLock& /*proofOfLock*/)
|
||||
{
|
||||
nsCString decodedDN;
|
||||
nsCString decodedOther;
|
||||
|
||||
nsresult rv = Base64Decode(aEncodedDN, decodedDN);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
rv = Base64Decode(aEncodedOther, decodedOther);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
CertBlocklistItem item(
|
||||
BitwiseCast<const uint8_t*, const char*>(decodedDN.get()),
|
||||
decodedDN.Length(),
|
||||
BitwiseCast<const uint8_t*, const char*>(decodedOther.get()),
|
||||
decodedOther.Length(),
|
||||
aMechanism);
|
||||
|
||||
if (aItemState == CertNewFromBlocklist) {
|
||||
// We want SaveEntries to be a no-op if no new entries are added.
|
||||
nsGenericHashKey<CertBlocklistItem>* entry = mBlocklist.GetEntry(item);
|
||||
if (!entry) {
|
||||
mModified = true;
|
||||
} else {
|
||||
// Ensure that any existing item is replaced by a fresh one so we can
|
||||
// use mIsCurrent to decide which entries to write out.
|
||||
mBlocklist.RemoveEntry(entry);
|
||||
}
|
||||
item.mIsCurrent = true;
|
||||
}
|
||||
mBlocklist.PutEntry(item);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Write a line for a given string in the output stream
|
||||
nsresult
|
||||
WriteLine(nsIOutputStream* outputStream, const nsACString& string)
|
||||
{
|
||||
nsAutoCString line(string);
|
||||
line.Append('\n');
|
||||
|
||||
const char* data = line.get();
|
||||
uint32_t length = line.Length();
|
||||
nsresult rv = NS_OK;
|
||||
while (NS_SUCCEEDED(rv) && length) {
|
||||
uint32_t bytesWritten = 0;
|
||||
rv = outputStream->Write(data, length, &bytesWritten);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
// if no data is written, something is wrong
|
||||
if (!bytesWritten) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
length -= bytesWritten;
|
||||
data += bytesWritten;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
// void saveEntries();
|
||||
// Store the blockist in a text file containing base64 encoded issuers and
|
||||
// serial numbers.
|
||||
//
|
||||
// Each item is stored on a separate line; each issuer is followed by its
|
||||
// revoked serial numbers, indented by one space.
|
||||
//
|
||||
// lines starting with a # character are ignored
|
||||
NS_IMETHODIMP
|
||||
CertBlocklist::SaveEntries()
|
||||
{
|
||||
MOZ_LOG(gCertBlockPRLog, LogLevel::Debug,
|
||||
("CertBlocklist::SaveEntries - not initialized"));
|
||||
MutexAutoLock lock(mMutex);
|
||||
if (!mModified) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult rv = EnsureBackingFileInitialized(lock);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (!mBackingFile) {
|
||||
// We allow this to succeed with no profile directory for tests
|
||||
MOZ_LOG(gCertBlockPRLog, LogLevel::Warning,
|
||||
("CertBlocklist::SaveEntries no file in profile to write to"));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Data needed for writing blocklist items out to the revocations file
|
||||
IssuerTable issuerTable;
|
||||
BlocklistStringSet issuers;
|
||||
nsCOMPtr<nsIOutputStream> outputStream;
|
||||
|
||||
rv = NS_NewAtomicFileOutputStream(getter_AddRefs(outputStream),
|
||||
mBackingFile, -1, -1, 0);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = WriteLine(outputStream,
|
||||
NS_LITERAL_CSTRING("# Auto generated contents. Do not edit."));
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Sort blocklist items into lists of serials for each issuer
|
||||
for (auto iter = mBlocklist.Iter(); !iter.Done(); iter.Next()) {
|
||||
CertBlocklistItem item = iter.Get()->GetKey();
|
||||
if (!item.mIsCurrent) {
|
||||
continue;
|
||||
}
|
||||
|
||||
nsAutoCString encDN;
|
||||
nsAutoCString encOther;
|
||||
|
||||
nsresult rv = item.ToBase64(encDN, encOther);
|
||||
if (NS_FAILED(rv)) {
|
||||
MOZ_LOG(gCertBlockPRLog, LogLevel::Warning,
|
||||
("CertBlocklist::SaveEntries writing revocation data failed"));
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// If it's a subject / public key block, write it straight out
|
||||
if (item.mItemMechanism == BlockBySubjectAndPubKey) {
|
||||
WriteLine(outputStream, encDN);
|
||||
WriteLine(outputStream, NS_LITERAL_CSTRING("\t") + encOther);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Otherwise, we have to group entries by issuer
|
||||
issuers.PutEntry(encDN);
|
||||
BlocklistStringSet* issuerSet = issuerTable.Get(encDN);
|
||||
if (!issuerSet) {
|
||||
issuerSet = new BlocklistStringSet();
|
||||
issuerTable.Put(encDN, issuerSet);
|
||||
}
|
||||
issuerSet->PutEntry(encOther);
|
||||
}
|
||||
|
||||
for (auto iter = issuers.Iter(); !iter.Done(); iter.Next()) {
|
||||
nsCStringHashKey* hashKey = iter.Get();
|
||||
nsAutoPtr<BlocklistStringSet> issuerSet;
|
||||
issuerTable.RemoveAndForget(hashKey->GetKey(), issuerSet);
|
||||
|
||||
nsresult rv = WriteLine(outputStream, hashKey->GetKey());
|
||||
if (NS_FAILED(rv)) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Write serial data to the output stream
|
||||
for (auto iter = issuerSet->Iter(); !iter.Done(); iter.Next()) {
|
||||
nsresult rv = WriteLine(outputStream,
|
||||
NS_LITERAL_CSTRING(" ") + iter.Get()->GetKey());
|
||||
if (NS_FAILED(rv)) {
|
||||
MOZ_LOG(gCertBlockPRLog, LogLevel::Warning,
|
||||
("CertBlocklist::SaveEntries writing revocation data failed"));
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsCOMPtr<nsISafeOutputStream> safeStream = do_QueryInterface(outputStream);
|
||||
NS_ASSERTION(safeStream, "expected a safe output stream!");
|
||||
if (!safeStream) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
rv = safeStream->Finish();
|
||||
if (NS_FAILED(rv)) {
|
||||
MOZ_LOG(gCertBlockPRLog, LogLevel::Warning,
|
||||
("CertBlocklist::SaveEntries saving revocation data failed"));
|
||||
return rv;
|
||||
}
|
||||
mModified = false;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
CertBlocklist::IsCertRevoked(const uint8_t* aIssuer,
|
||||
uint32_t aIssuerLength,
|
||||
const uint8_t* aSerial,
|
||||
uint32_t aSerialLength,
|
||||
const uint8_t* aSubject,
|
||||
uint32_t aSubjectLength,
|
||||
const uint8_t* aPubKey,
|
||||
uint32_t aPubKeyLength,
|
||||
bool* _retval)
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
||||
MOZ_LOG(gCertBlockPRLog, LogLevel::Warning,
|
||||
("CertBlocklist::IsCertRevoked?"));
|
||||
nsresult rv = EnsureBackingFileInitialized(lock);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
Input issuer;
|
||||
Input serial;
|
||||
if (issuer.Init(aIssuer, aIssuerLength) != Success) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
if (serial.Init(aSerial, aSerialLength) != Success) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
CertBlocklistItem issuerSerial(aIssuer, aIssuerLength, aSerial, aSerialLength,
|
||||
BlockByIssuerAndSerial);
|
||||
|
||||
nsAutoCString encDN;
|
||||
nsAutoCString encOther;
|
||||
|
||||
issuerSerial.ToBase64(encDN, encOther);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
MOZ_LOG(gCertBlockPRLog, LogLevel::Warning,
|
||||
("CertBlocklist::IsCertRevoked issuer %s - serial %s",
|
||||
encDN.get(), encOther.get()));
|
||||
|
||||
*_retval = mBlocklist.Contains(issuerSerial);
|
||||
|
||||
if (*_retval) {
|
||||
MOZ_LOG(gCertBlockPRLog, LogLevel::Warning,
|
||||
("certblocklist::IsCertRevoked found by issuer / serial"));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsICryptoHash> crypto;
|
||||
crypto = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
|
||||
|
||||
rv = crypto->Init(nsICryptoHash::SHA256);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = crypto->Update(aPubKey, aPubKeyLength);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsCString hashString;
|
||||
rv = crypto->Finish(false, hashString);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
CertBlocklistItem subjectPubKey(
|
||||
aSubject,
|
||||
static_cast<size_t>(aSubjectLength),
|
||||
BitwiseCast<const uint8_t*, const char*>(hashString.get()),
|
||||
hashString.Length(),
|
||||
BlockBySubjectAndPubKey);
|
||||
|
||||
rv = subjectPubKey.ToBase64(encDN, encOther);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
MOZ_LOG(gCertBlockPRLog, LogLevel::Warning,
|
||||
("CertBlocklist::IsCertRevoked subject %s - pubKey hash %s",
|
||||
encDN.get(), encOther.get()));
|
||||
*_retval = mBlocklist.Contains(subjectPubKey);
|
||||
|
||||
MOZ_LOG(gCertBlockPRLog, LogLevel::Warning,
|
||||
("CertBlocklist::IsCertRevoked by subject / pubkey? %s",
|
||||
*_retval ? "true" : "false"));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
CertBlocklist::IsBlocklistFresh(bool* _retval)
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
*_retval = false;
|
||||
|
||||
uint32_t now = uint32_t(PR_Now() / PR_USEC_PER_SEC);
|
||||
uint32_t lastUpdate = sUseAMO ? sLastBlocklistUpdate : sLastKintoUpdate;
|
||||
MOZ_LOG(gCertBlockPRLog, LogLevel::Warning,
|
||||
("CertBlocklist::IsBlocklistFresh using AMO? %i lastUpdate is %i",
|
||||
sUseAMO, lastUpdate));
|
||||
|
||||
if (now > lastUpdate) {
|
||||
int64_t interval = now - lastUpdate;
|
||||
MOZ_LOG(gCertBlockPRLog, LogLevel::Warning,
|
||||
("CertBlocklist::IsBlocklistFresh we're after the last BlocklistUpdate "
|
||||
"interval is %i, staleness %u", interval, sMaxStaleness));
|
||||
*_retval = sMaxStaleness > interval;
|
||||
}
|
||||
MOZ_LOG(gCertBlockPRLog, LogLevel::Warning,
|
||||
("CertBlocklist::IsBlocklistFresh ? %s", *_retval ? "true" : "false"));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
/* static */
|
||||
void
|
||||
CertBlocklist::PreferenceChanged(const char* aPref, void* aClosure)
|
||||
|
||||
{
|
||||
auto blocklist = static_cast<CertBlocklist*>(aClosure);
|
||||
MutexAutoLock lock(blocklist->mMutex);
|
||||
|
||||
MOZ_LOG(gCertBlockPRLog, LogLevel::Warning,
|
||||
("CertBlocklist::PreferenceChanged %s changed", aPref));
|
||||
if (strcmp(aPref, PREF_BACKGROUND_UPDATE_TIMER) == 0) {
|
||||
sLastBlocklistUpdate = Preferences::GetUint(PREF_BACKGROUND_UPDATE_TIMER,
|
||||
uint32_t(0));
|
||||
} else if (strcmp(aPref, PREF_BLOCKLIST_ONECRL_CHECKED) == 0) {
|
||||
sLastKintoUpdate = Preferences::GetUint(PREF_BLOCKLIST_ONECRL_CHECKED,
|
||||
uint32_t(0));
|
||||
} else if (strcmp(aPref, PREF_MAX_STALENESS_IN_SECONDS) == 0) {
|
||||
sMaxStaleness = Preferences::GetUint(PREF_MAX_STALENESS_IN_SECONDS,
|
||||
uint32_t(0));
|
||||
} else if (strcmp(aPref, PREF_ONECRL_VIA_AMO) == 0) {
|
||||
sUseAMO = Preferences::GetBool(PREF_ONECRL_VIA_AMO, true);
|
||||
}
|
||||
}
|
||||
89
security/manager/ssl/CertBlocklist.h
Normal file
89
security/manager/ssl/CertBlocklist.h
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef CertBlocklist_h
|
||||
#define CertBlocklist_h
|
||||
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "nsClassHashtable.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsICertBlocklist.h"
|
||||
#include "nsIOutputStream.h"
|
||||
#include "nsTHashtable.h"
|
||||
#include "nsIX509CertDB.h"
|
||||
#include "pkix/Input.h"
|
||||
|
||||
#define NS_CERT_BLOCKLIST_CID \
|
||||
{0x11aefd53, 0x2fbb, 0x4c92, {0xa0, 0xc1, 0x05, 0x32, 0x12, 0xae, 0x42, 0xd0} }
|
||||
|
||||
enum CertBlocklistItemMechanism {
|
||||
BlockByIssuerAndSerial,
|
||||
BlockBySubjectAndPubKey
|
||||
};
|
||||
|
||||
enum CertBlocklistItemState {
|
||||
CertNewFromBlocklist,
|
||||
CertOldFromLocalCache
|
||||
};
|
||||
|
||||
class CertBlocklistItem
|
||||
{
|
||||
public:
|
||||
CertBlocklistItem(const uint8_t* DNData, size_t DNLength,
|
||||
const uint8_t* otherData, size_t otherLength,
|
||||
CertBlocklistItemMechanism itemMechanism);
|
||||
CertBlocklistItem(const CertBlocklistItem& aItem);
|
||||
~CertBlocklistItem();
|
||||
nsresult ToBase64(nsACString& b64IssuerOut, nsACString& b64SerialOut);
|
||||
bool operator==(const CertBlocklistItem& aItem) const;
|
||||
uint32_t Hash() const;
|
||||
bool mIsCurrent;
|
||||
CertBlocklistItemMechanism mItemMechanism;
|
||||
|
||||
private:
|
||||
size_t mDNLength;
|
||||
uint8_t* mDNData;
|
||||
size_t mOtherLength;
|
||||
uint8_t* mOtherData;
|
||||
};
|
||||
|
||||
typedef nsGenericHashKey<CertBlocklistItem> BlocklistItemKey;
|
||||
typedef nsTHashtable<BlocklistItemKey> BlocklistTable;
|
||||
typedef nsTHashtable<nsCStringHashKey> BlocklistStringSet;
|
||||
typedef nsClassHashtable<nsCStringHashKey, BlocklistStringSet> IssuerTable;
|
||||
|
||||
class CertBlocklist : public nsICertBlocklist
|
||||
{
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSICERTBLOCKLIST
|
||||
CertBlocklist();
|
||||
nsresult Init();
|
||||
|
||||
private:
|
||||
BlocklistTable mBlocklist;
|
||||
nsresult AddRevokedCertInternal(const nsACString& aEncodedDN,
|
||||
const nsACString& aEncodedOther,
|
||||
CertBlocklistItemMechanism aMechanism,
|
||||
CertBlocklistItemState aItemState,
|
||||
mozilla::MutexAutoLock& /*proofOfLock*/);
|
||||
mozilla::Mutex mMutex;
|
||||
bool mModified;
|
||||
bool mBackingFileIsInitialized;
|
||||
// call EnsureBackingFileInitialized before operations that read or
|
||||
// modify CertBlocklist data
|
||||
nsresult EnsureBackingFileInitialized(mozilla::MutexAutoLock& lock);
|
||||
nsCOMPtr<nsIFile> mBackingFile;
|
||||
|
||||
protected:
|
||||
static void PreferenceChanged(const char* aPref, void* aClosure);
|
||||
static uint32_t sLastBlocklistUpdate;
|
||||
static uint32_t sLastKintoUpdate;
|
||||
static uint32_t sMaxStaleness;
|
||||
static bool sUseAMO;
|
||||
virtual ~CertBlocklist();
|
||||
};
|
||||
|
||||
#endif // CertBlocklist_h
|
||||
562
security/manager/ssl/ContentSignatureVerifier.cpp
Normal file
562
security/manager/ssl/ContentSignatureVerifier.cpp
Normal file
|
|
@ -0,0 +1,562 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "ContentSignatureVerifier.h"
|
||||
|
||||
#include "BRNameMatchingPolicy.h"
|
||||
#include "SharedCertVerifier.h"
|
||||
#include "cryptohi.h"
|
||||
#include "keyhi.h"
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/Casting.h"
|
||||
#include "mozilla/Unused.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsISupportsPriority.h"
|
||||
#include "nsIURI.h"
|
||||
#include "nsNSSComponent.h"
|
||||
#include "nsSecurityHeaderParser.h"
|
||||
#include "nsStreamUtils.h"
|
||||
#include "nsWhitespaceTokenizer.h"
|
||||
#include "nsXPCOMStrings.h"
|
||||
#include "nssb64.h"
|
||||
#include "pkix/pkix.h"
|
||||
#include "pkix/pkixtypes.h"
|
||||
#include "secerr.h"
|
||||
|
||||
NS_IMPL_ISUPPORTS(ContentSignatureVerifier,
|
||||
nsIContentSignatureVerifier,
|
||||
nsIInterfaceRequestor,
|
||||
nsIStreamListener)
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::pkix;
|
||||
using namespace mozilla::psm;
|
||||
|
||||
static LazyLogModule gCSVerifierPRLog("ContentSignatureVerifier");
|
||||
#define CSVerifier_LOG(args) MOZ_LOG(gCSVerifierPRLog, LogLevel::Debug, args)
|
||||
|
||||
// Content-Signature prefix
|
||||
const nsLiteralCString kPREFIX = NS_LITERAL_CSTRING("Content-Signature:\x00");
|
||||
|
||||
ContentSignatureVerifier::~ContentSignatureVerifier()
|
||||
{
|
||||
nsNSSShutDownPreventionLock locker;
|
||||
if (isAlreadyShutDown()) {
|
||||
return;
|
||||
}
|
||||
destructorSafeDestroyNSSReference();
|
||||
shutdown(ShutdownCalledFrom::Object);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ContentSignatureVerifier::VerifyContentSignature(
|
||||
const nsACString& aData, const nsACString& aCSHeader,
|
||||
const nsACString& aCertChain, const nsACString& aName, bool* _retval)
|
||||
{
|
||||
NS_ENSURE_ARG(_retval);
|
||||
nsresult rv = CreateContext(aData, aCSHeader, aCertChain, aName);
|
||||
if (NS_FAILED(rv)) {
|
||||
*_retval = false;
|
||||
CSVerifier_LOG(("CSVerifier: Signature verification failed\n"));
|
||||
if (rv == NS_ERROR_INVALID_SIGNATURE) {
|
||||
return NS_OK;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
return End(_retval);
|
||||
}
|
||||
|
||||
bool
|
||||
IsNewLine(char16_t c)
|
||||
{
|
||||
return c == '\n' || c == '\r';
|
||||
}
|
||||
|
||||
nsresult
|
||||
ReadChainIntoCertList(const nsACString& aCertChain, CERTCertList* aCertList,
|
||||
const nsNSSShutDownPreventionLock& /*proofOfLock*/)
|
||||
{
|
||||
bool inBlock = false;
|
||||
bool certFound = false;
|
||||
|
||||
const nsCString header = NS_LITERAL_CSTRING("-----BEGIN CERTIFICATE-----");
|
||||
const nsCString footer = NS_LITERAL_CSTRING("-----END CERTIFICATE-----");
|
||||
|
||||
nsCWhitespaceTokenizerTemplate<IsNewLine> tokenizer(aCertChain);
|
||||
|
||||
nsAutoCString blockData;
|
||||
while (tokenizer.hasMoreTokens()) {
|
||||
nsDependentCSubstring token = tokenizer.nextToken();
|
||||
if (token.IsEmpty()) {
|
||||
continue;
|
||||
}
|
||||
if (inBlock) {
|
||||
if (token.Equals(footer)) {
|
||||
inBlock = false;
|
||||
certFound = true;
|
||||
// base64 decode data, make certs, append to chain
|
||||
ScopedAutoSECItem der;
|
||||
if (!NSSBase64_DecodeBuffer(nullptr, &der, blockData.BeginReading(),
|
||||
blockData.Length())) {
|
||||
CSVerifier_LOG(("CSVerifier: decoding the signature failed\n"));
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
UniqueCERTCertificate tmpCert(
|
||||
CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &der, nullptr, false,
|
||||
true));
|
||||
if (!tmpCert) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
// if adding tmpCert succeeds, tmpCert will now be owned by aCertList
|
||||
SECStatus res = CERT_AddCertToListTail(aCertList, tmpCert.get());
|
||||
if (res != SECSuccess) {
|
||||
return MapSECStatus(res);
|
||||
}
|
||||
Unused << tmpCert.release();
|
||||
} else {
|
||||
blockData.Append(token);
|
||||
}
|
||||
} else if (token.Equals(header)) {
|
||||
inBlock = true;
|
||||
blockData = "";
|
||||
}
|
||||
}
|
||||
if (inBlock || !certFound) {
|
||||
// the PEM data did not end; bad data.
|
||||
CSVerifier_LOG(("CSVerifier: supplied chain contains bad data\n"));
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ContentSignatureVerifier::CreateContextInternal(const nsACString& aData,
|
||||
const nsACString& aCertChain,
|
||||
const nsACString& aName)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
nsNSSShutDownPreventionLock locker;
|
||||
if (isAlreadyShutDown()) {
|
||||
CSVerifier_LOG(("CSVerifier: nss is already shutdown\n"));
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
UniqueCERTCertList certCertList(CERT_NewCertList());
|
||||
if (!certCertList) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
nsresult rv = ReadChainIntoCertList(aCertChain, certCertList.get(), locker);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
CERTCertListNode* node = CERT_LIST_HEAD(certCertList.get());
|
||||
if (!node || CERT_LIST_END(node, certCertList.get()) || !node->cert) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
SECItem* certSecItem = &node->cert->derCert;
|
||||
|
||||
Input certDER;
|
||||
mozilla::pkix::Result result =
|
||||
certDER.Init(BitwiseCast<uint8_t*, unsigned char*>(certSecItem->data),
|
||||
certSecItem->len);
|
||||
if (result != Success) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
|
||||
// Check the signerCert chain is good
|
||||
CSTrustDomain trustDomain(certCertList);
|
||||
result = BuildCertChain(trustDomain, certDER, Now(),
|
||||
EndEntityOrCA::MustBeEndEntity,
|
||||
KeyUsage::noParticularKeyUsageRequired,
|
||||
KeyPurposeId::id_kp_codeSigning,
|
||||
CertPolicyId::anyPolicy,
|
||||
nullptr/*stapledOCSPResponse*/);
|
||||
if (result != Success) {
|
||||
// if there was a library error, return an appropriate error
|
||||
if (IsFatalError(result)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
// otherwise, assume the signature was invalid
|
||||
CSVerifier_LOG(("CSVerifier: The supplied chain is bad\n"));
|
||||
return NS_ERROR_INVALID_SIGNATURE;
|
||||
}
|
||||
|
||||
// Check the SAN
|
||||
Input hostnameInput;
|
||||
|
||||
result = hostnameInput.Init(
|
||||
BitwiseCast<const uint8_t*, const char*>(aName.BeginReading()),
|
||||
aName.Length());
|
||||
if (result != Success) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
BRNameMatchingPolicy nameMatchingPolicy(BRNameMatchingPolicy::Mode::Enforce);
|
||||
result = CheckCertHostname(certDER, hostnameInput, nameMatchingPolicy);
|
||||
if (result != Success) {
|
||||
return NS_ERROR_INVALID_SIGNATURE;
|
||||
}
|
||||
|
||||
mKey.reset(CERT_ExtractPublicKey(node->cert));
|
||||
|
||||
// in case we were not able to extract a key
|
||||
if (!mKey) {
|
||||
CSVerifier_LOG(("CSVerifier: unable to extract a key\n"));
|
||||
return NS_ERROR_INVALID_SIGNATURE;
|
||||
}
|
||||
|
||||
// Base 64 decode the signature
|
||||
ScopedAutoSECItem rawSignatureItem;
|
||||
if (!NSSBase64_DecodeBuffer(nullptr, &rawSignatureItem, mSignature.get(),
|
||||
mSignature.Length())) {
|
||||
CSVerifier_LOG(("CSVerifier: decoding the signature failed\n"));
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// get signature object
|
||||
ScopedAutoSECItem signatureItem;
|
||||
// We have a raw ecdsa signature r||s so we have to DER-encode it first
|
||||
// Note that we have to check rawSignatureItem->len % 2 here as
|
||||
// DSAU_EncodeDerSigWithLen asserts this
|
||||
if (rawSignatureItem.len == 0 || rawSignatureItem.len % 2 != 0) {
|
||||
CSVerifier_LOG(("CSVerifier: signature length is bad\n"));
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
if (DSAU_EncodeDerSigWithLen(&signatureItem, &rawSignatureItem,
|
||||
rawSignatureItem.len) != SECSuccess) {
|
||||
CSVerifier_LOG(("CSVerifier: encoding the signature failed\n"));
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// this is the only OID we support for now
|
||||
SECOidTag oid = SEC_OID_ANSIX962_ECDSA_SHA384_SIGNATURE;
|
||||
|
||||
mCx = UniqueVFYContext(
|
||||
VFY_CreateContext(mKey.get(), &signatureItem, oid, nullptr));
|
||||
if (!mCx) {
|
||||
return NS_ERROR_INVALID_SIGNATURE;
|
||||
}
|
||||
|
||||
if (VFY_Begin(mCx.get()) != SECSuccess) {
|
||||
return NS_ERROR_INVALID_SIGNATURE;
|
||||
}
|
||||
|
||||
rv = UpdateInternal(kPREFIX, locker);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
// add data if we got any
|
||||
return UpdateInternal(aData, locker);
|
||||
}
|
||||
|
||||
nsresult
|
||||
ContentSignatureVerifier::DownloadCertChain()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (mCertChainURL.IsEmpty()) {
|
||||
return NS_ERROR_INVALID_SIGNATURE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIURI> certChainURI;
|
||||
nsresult rv = NS_NewURI(getter_AddRefs(certChainURI), mCertChainURL);
|
||||
if (NS_FAILED(rv) || !certChainURI) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// If the address is not https, fail.
|
||||
bool isHttps = false;
|
||||
rv = certChainURI->SchemeIs("https", &isHttps);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
if (!isHttps) {
|
||||
return NS_ERROR_INVALID_SIGNATURE;
|
||||
}
|
||||
|
||||
rv = NS_NewChannel(getter_AddRefs(mChannel), certChainURI,
|
||||
nsContentUtils::GetSystemPrincipal(),
|
||||
nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
|
||||
nsIContentPolicy::TYPE_OTHER);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// we need this chain soon
|
||||
nsCOMPtr<nsISupportsPriority> priorityChannel = do_QueryInterface(mChannel);
|
||||
if (priorityChannel) {
|
||||
priorityChannel->AdjustPriority(nsISupportsPriority::PRIORITY_HIGHEST);
|
||||
}
|
||||
|
||||
rv = mChannel->AsyncOpen2(this);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Create a context for content signature verification using CreateContext below.
|
||||
// This function doesn't require a cert chain to be passed, but instead aCSHeader
|
||||
// must contain an x5u value that is then used to download the cert chain.
|
||||
NS_IMETHODIMP
|
||||
ContentSignatureVerifier::CreateContextWithoutCertChain(
|
||||
nsIContentSignatureReceiverCallback *aCallback, const nsACString& aCSHeader,
|
||||
const nsACString& aName)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(aCallback);
|
||||
if (mInitialised) {
|
||||
return NS_ERROR_ALREADY_INITIALIZED;
|
||||
}
|
||||
mInitialised = true;
|
||||
|
||||
// we get the raw content-signature header here, so first parse aCSHeader
|
||||
nsresult rv = ParseContentSignatureHeader(aCSHeader);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
mCallback = aCallback;
|
||||
mName.Assign(aName);
|
||||
|
||||
// We must download the cert chain now.
|
||||
// This is async and blocks createContextInternal calls.
|
||||
return DownloadCertChain();
|
||||
}
|
||||
|
||||
// Create a context for a content signature verification.
|
||||
// It sets signature, certificate chain and name that should be used to verify
|
||||
// the data. The data parameter is the first part of the data to verify (this
|
||||
// can be the empty string).
|
||||
NS_IMETHODIMP
|
||||
ContentSignatureVerifier::CreateContext(const nsACString& aData,
|
||||
const nsACString& aCSHeader,
|
||||
const nsACString& aCertChain,
|
||||
const nsACString& aName)
|
||||
{
|
||||
if (mInitialised) {
|
||||
return NS_ERROR_ALREADY_INITIALIZED;
|
||||
}
|
||||
mInitialised = true;
|
||||
// The cert chain is given in aCertChain so we don't have to download anything.
|
||||
mHasCertChain = true;
|
||||
|
||||
// we get the raw content-signature header here, so first parse aCSHeader
|
||||
nsresult rv = ParseContentSignatureHeader(aCSHeader);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
return CreateContextInternal(aData, aCertChain, aName);
|
||||
}
|
||||
|
||||
nsresult
|
||||
ContentSignatureVerifier::UpdateInternal(
|
||||
const nsACString& aData, const nsNSSShutDownPreventionLock& /*proofOfLock*/)
|
||||
{
|
||||
if (!aData.IsEmpty()) {
|
||||
if (VFY_Update(mCx.get(), (const unsigned char*)nsPromiseFlatCString(aData).get(),
|
||||
aData.Length()) != SECSuccess){
|
||||
return NS_ERROR_INVALID_SIGNATURE;
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add data to the context that shold be verified.
|
||||
*/
|
||||
NS_IMETHODIMP
|
||||
ContentSignatureVerifier::Update(const nsACString& aData)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
nsNSSShutDownPreventionLock locker;
|
||||
if (isAlreadyShutDown()) {
|
||||
CSVerifier_LOG(("CSVerifier: nss is already shutdown\n"));
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// If we didn't create the context yet, bail!
|
||||
if (!mHasCertChain) {
|
||||
MOZ_ASSERT_UNREACHABLE(
|
||||
"Someone called ContentSignatureVerifier::Update before "
|
||||
"downloading the cert chain.");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
return UpdateInternal(aData, locker);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finish signature verification and return the result in _retval.
|
||||
*/
|
||||
NS_IMETHODIMP
|
||||
ContentSignatureVerifier::End(bool* _retval)
|
||||
{
|
||||
NS_ENSURE_ARG(_retval);
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
nsNSSShutDownPreventionLock locker;
|
||||
if (isAlreadyShutDown()) {
|
||||
CSVerifier_LOG(("CSVerifier: nss is already shutdown\n"));
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// If we didn't create the context yet, bail!
|
||||
if (!mHasCertChain) {
|
||||
MOZ_ASSERT_UNREACHABLE(
|
||||
"Someone called ContentSignatureVerifier::End before "
|
||||
"downloading the cert chain.");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
*_retval = (VFY_End(mCx.get()) == SECSuccess);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ContentSignatureVerifier::ParseContentSignatureHeader(
|
||||
const nsACString& aContentSignatureHeader)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
// We only support p384 ecdsa according to spec
|
||||
NS_NAMED_LITERAL_CSTRING(signature_var, "p384ecdsa");
|
||||
NS_NAMED_LITERAL_CSTRING(certChainURL_var, "x5u");
|
||||
|
||||
nsSecurityHeaderParser parser(aContentSignatureHeader.BeginReading());
|
||||
nsresult rv = parser.Parse();
|
||||
if (NS_FAILED(rv)) {
|
||||
CSVerifier_LOG(("CSVerifier: could not parse ContentSignature header\n"));
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
LinkedList<nsSecurityHeaderDirective>* directives = parser.GetDirectives();
|
||||
|
||||
for (nsSecurityHeaderDirective* directive = directives->getFirst();
|
||||
directive != nullptr; directive = directive->getNext()) {
|
||||
CSVerifier_LOG(("CSVerifier: found directive %s\n", directive->mName.get()));
|
||||
if (directive->mName.Length() == signature_var.Length() &&
|
||||
directive->mName.EqualsIgnoreCase(signature_var.get(),
|
||||
signature_var.Length())) {
|
||||
if (!mSignature.IsEmpty()) {
|
||||
CSVerifier_LOG(("CSVerifier: found two ContentSignatures\n"));
|
||||
return NS_ERROR_INVALID_SIGNATURE;
|
||||
}
|
||||
|
||||
CSVerifier_LOG(("CSVerifier: found a ContentSignature directive\n"));
|
||||
mSignature = directive->mValue;
|
||||
}
|
||||
if (directive->mName.Length() == certChainURL_var.Length() &&
|
||||
directive->mName.EqualsIgnoreCase(certChainURL_var.get(),
|
||||
certChainURL_var.Length())) {
|
||||
if (!mCertChainURL.IsEmpty()) {
|
||||
CSVerifier_LOG(("CSVerifier: found two x5u values\n"));
|
||||
return NS_ERROR_INVALID_SIGNATURE;
|
||||
}
|
||||
|
||||
CSVerifier_LOG(("CSVerifier: found an x5u directive\n"));
|
||||
mCertChainURL = directive->mValue;
|
||||
}
|
||||
}
|
||||
|
||||
// we have to ensure that we found a signature at this point
|
||||
if (mSignature.IsEmpty()) {
|
||||
CSVerifier_LOG(("CSVerifier: got a Content-Signature header but didn't find a signature.\n"));
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// Bug 769521: We have to change b64 url to regular encoding as long as we
|
||||
// don't have a b64 url decoder. This should change soon, but in the meantime
|
||||
// we have to live with this.
|
||||
mSignature.ReplaceChar('-', '+');
|
||||
mSignature.ReplaceChar('_', '/');
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* nsIStreamListener implementation */
|
||||
|
||||
NS_IMETHODIMP
|
||||
ContentSignatureVerifier::OnStartRequest(nsIRequest* aRequest,
|
||||
nsISupports* aContext)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ContentSignatureVerifier::OnStopRequest(nsIRequest* aRequest,
|
||||
nsISupports* aContext, nsresult aStatus)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
nsCOMPtr<nsIContentSignatureReceiverCallback> callback;
|
||||
callback.swap(mCallback);
|
||||
nsresult rv;
|
||||
|
||||
// Check HTTP status code and return if it's not 200.
|
||||
nsCOMPtr<nsIHttpChannel> http = do_QueryInterface(aRequest, &rv);
|
||||
uint32_t httpResponseCode;
|
||||
if (NS_FAILED(rv) || NS_FAILED(http->GetResponseStatus(&httpResponseCode)) ||
|
||||
httpResponseCode != 200) {
|
||||
callback->ContextCreated(false);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (NS_FAILED(aStatus)) {
|
||||
callback->ContextCreated(false);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsAutoCString certChain;
|
||||
for (uint32_t i = 0; i < mCertChain.Length(); ++i) {
|
||||
certChain.Append(mCertChain[i]);
|
||||
}
|
||||
|
||||
// We got the cert chain now. Let's create the context.
|
||||
rv = CreateContextInternal(NS_LITERAL_CSTRING(""), certChain, mName);
|
||||
if (NS_FAILED(rv)) {
|
||||
callback->ContextCreated(false);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
mHasCertChain = true;
|
||||
callback->ContextCreated(true);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ContentSignatureVerifier::OnDataAvailable(nsIRequest* aRequest,
|
||||
nsISupports* aContext,
|
||||
nsIInputStream* aInputStream,
|
||||
uint64_t aOffset, uint32_t aCount)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
nsAutoCString buffer;
|
||||
|
||||
nsresult rv = NS_ConsumeStream(aInputStream, aCount, buffer);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (!mCertChain.AppendElement(buffer, fallible)) {
|
||||
mCertChain.TruncateLength(0);
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ContentSignatureVerifier::GetInterface(const nsIID& uuid, void** result)
|
||||
{
|
||||
return QueryInterface(uuid, result);
|
||||
}
|
||||
92
security/manager/ssl/ContentSignatureVerifier.h
Normal file
92
security/manager/ssl/ContentSignatureVerifier.h
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
|
||||
#ifndef ContentSignatureVerifier_h
|
||||
#define ContentSignatureVerifier_h
|
||||
|
||||
#include "cert.h"
|
||||
#include "CSTrustDomain.h"
|
||||
#include "nsIContentSignatureVerifier.h"
|
||||
#include "nsIStreamListener.h"
|
||||
#include "nsNSSShutDown.h"
|
||||
#include "ScopedNSSTypes.h"
|
||||
|
||||
// 45a5fe2f-c350-4b86-962d-02d5aaaa955a
|
||||
#define NS_CONTENTSIGNATUREVERIFIER_CID \
|
||||
{ 0x45a5fe2f, 0xc350, 0x4b86, \
|
||||
{ 0x96, 0x2d, 0x02, 0xd5, 0xaa, 0xaa, 0x95, 0x5a } }
|
||||
#define NS_CONTENTSIGNATUREVERIFIER_CONTRACTID \
|
||||
"@mozilla.org/security/contentsignatureverifier;1"
|
||||
|
||||
class ContentSignatureVerifier final : public nsIContentSignatureVerifier
|
||||
, public nsIStreamListener
|
||||
, public nsNSSShutDownObject
|
||||
, public nsIInterfaceRequestor
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSICONTENTSIGNATUREVERIFIER
|
||||
NS_DECL_NSIINTERFACEREQUESTOR
|
||||
NS_DECL_NSISTREAMLISTENER
|
||||
NS_DECL_NSIREQUESTOBSERVER
|
||||
|
||||
ContentSignatureVerifier()
|
||||
: mCx(nullptr)
|
||||
, mInitialised(false)
|
||||
, mHasCertChain(false)
|
||||
{
|
||||
}
|
||||
|
||||
// nsNSSShutDownObject
|
||||
virtual void virtualDestroyNSSReference() override
|
||||
{
|
||||
destructorSafeDestroyNSSReference();
|
||||
}
|
||||
|
||||
private:
|
||||
~ContentSignatureVerifier();
|
||||
|
||||
nsresult UpdateInternal(const nsACString& aData,
|
||||
const nsNSSShutDownPreventionLock& /*proofOfLock*/);
|
||||
nsresult DownloadCertChain();
|
||||
nsresult CreateContextInternal(const nsACString& aData,
|
||||
const nsACString& aCertChain,
|
||||
const nsACString& aName);
|
||||
|
||||
void destructorSafeDestroyNSSReference()
|
||||
{
|
||||
mCx = nullptr;
|
||||
mKey = nullptr;
|
||||
}
|
||||
|
||||
nsresult ParseContentSignatureHeader(const nsACString& aContentSignatureHeader);
|
||||
|
||||
// verifier context for incremental verifications
|
||||
mozilla::UniqueVFYContext mCx;
|
||||
bool mInitialised;
|
||||
// Indicates whether we hold a cert chain to verify the signature or not.
|
||||
// It's set by default in CreateContext or when the channel created in
|
||||
// DownloadCertChain finished. Update and End must only be called after
|
||||
// mHashCertChain is set.
|
||||
bool mHasCertChain;
|
||||
// signature to verify
|
||||
nsCString mSignature;
|
||||
// x5u (X.509 URL) value pointing to pem cert chain
|
||||
nsCString mCertChainURL;
|
||||
// the downloaded cert chain to verify against
|
||||
FallibleTArray<nsCString> mCertChain;
|
||||
// verification key
|
||||
mozilla::UniqueSECKEYPublicKey mKey;
|
||||
// name of the verifying context
|
||||
nsCString mName;
|
||||
// callback to notify when finished
|
||||
nsCOMPtr<nsIContentSignatureReceiverCallback> mCallback;
|
||||
// channel to download the cert chain
|
||||
nsCOMPtr<nsIChannel> mChannel;
|
||||
};
|
||||
|
||||
#endif // ContentSignatureVerifier_h
|
||||
95
security/manager/ssl/CryptoTask.cpp
Normal file
95
security/manager/ssl/CryptoTask.cpp
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "CryptoTask.h"
|
||||
#include "nsNSSComponent.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
CryptoTask::~CryptoTask()
|
||||
{
|
||||
MOZ_ASSERT(mReleasedNSSResources);
|
||||
|
||||
nsNSSShutDownPreventionLock lock;
|
||||
if (!isAlreadyShutDown()) {
|
||||
shutdown(ShutdownCalledFrom::Object);
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
CryptoTask::Dispatch(const nsACString& taskThreadName)
|
||||
{
|
||||
MOZ_ASSERT(taskThreadName.Length() <= 15);
|
||||
|
||||
// Ensure that NSS is initialized, since presumably CalculateResult
|
||||
// will use NSS functions
|
||||
if (!EnsureNSSInitializedChromeOrContent()) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// Can't add 'this' as the event to run, since mThread may not be set yet
|
||||
nsresult rv = NS_NewThread(getter_AddRefs(mThread), nullptr,
|
||||
nsIThreadManager::DEFAULT_STACK_SIZE);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_SetThreadName(mThread, taskThreadName);
|
||||
// Note: event must not null out mThread!
|
||||
return mThread->Dispatch(this, NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
CryptoTask::Run()
|
||||
{
|
||||
if (!NS_IsMainThread()) {
|
||||
nsNSSShutDownPreventionLock locker;
|
||||
if (isAlreadyShutDown()) {
|
||||
mRv = NS_ERROR_NOT_AVAILABLE;
|
||||
} else {
|
||||
mRv = CalculateResult();
|
||||
}
|
||||
NS_DispatchToMainThread(this);
|
||||
} else {
|
||||
// back on the main thread
|
||||
|
||||
// call ReleaseNSSResources now, before calling CallCallback, so that
|
||||
// CryptoTasks have consistent behavior regardless of whether NSS is shut
|
||||
// down between CalculateResult being called and CallCallback being called.
|
||||
if (!mReleasedNSSResources) {
|
||||
mReleasedNSSResources = true;
|
||||
ReleaseNSSResources();
|
||||
}
|
||||
|
||||
CallCallback(mRv);
|
||||
|
||||
// Not all uses of CryptoTask use a transient thread
|
||||
if (mThread) {
|
||||
// Don't leak threads!
|
||||
mThread->Shutdown(); // can't Shutdown from the thread itself, darn
|
||||
// Don't null out mThread!
|
||||
// See bug 999104. We must hold a ref to the thread across Dispatch()
|
||||
// since the internal mThread ref could be released while processing
|
||||
// the Dispatch(), and Dispatch/PutEvent itself doesn't hold a ref; it
|
||||
// assumes the caller does.
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
CryptoTask::virtualDestroyNSSReference()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(),
|
||||
"virtualDestroyNSSReference called off the main thread");
|
||||
if (!mReleasedNSSResources) {
|
||||
mReleasedNSSResources = true;
|
||||
ReleaseNSSResources();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
105
security/manager/ssl/CryptoTask.h
Normal file
105
security/manager/ssl/CryptoTask.h
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla__CryptoTask_h
|
||||
#define mozilla__CryptoTask_h
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsNSSShutDown.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
/**
|
||||
* Frequently we need to run a task on a background thread without blocking
|
||||
* the main thread, and then call a callback on the main thread with the
|
||||
* result. This class provides the framework for that. Subclasses must:
|
||||
*
|
||||
* (1) Override CalculateResult for the off-the-main-thread computation.
|
||||
* NSS functionality may only be accessed within CalculateResult.
|
||||
* (2) Override ReleaseNSSResources to release references to all NSS
|
||||
* resources (that do implement nsNSSShutDownObject themselves).
|
||||
* (3) Override CallCallback() for the on-the-main-thread call of the
|
||||
* callback.
|
||||
*
|
||||
* CalculateResult, ReleaseNSSResources, and CallCallback are called in order,
|
||||
* except CalculateResult might be skipped if NSS is shut down before it can
|
||||
* be called; in that case ReleaseNSSResources will be called and then
|
||||
* CallCallback will be called with an error code.
|
||||
*
|
||||
* That sequence of events is what happens if you call Dispatch. If for
|
||||
* some reason, you decide not to run the task (e.g., due to an error in the
|
||||
* constructor), you may call Skip, in which case the task is cleaned up and
|
||||
* not run. In that case, only ReleaseNSSResources is called. (So a
|
||||
* subclass must be prepared for ReleaseNSSResources to be run without
|
||||
* CalculateResult having been called first.)
|
||||
*
|
||||
* Once a CryptoTask is created, the calling code must call either
|
||||
* Dispatch or Skip.
|
||||
*
|
||||
*/
|
||||
class CryptoTask : public Runnable,
|
||||
public nsNSSShutDownObject
|
||||
{
|
||||
public:
|
||||
template <size_t LEN>
|
||||
nsresult Dispatch(const char (&taskThreadName)[LEN])
|
||||
{
|
||||
static_assert(LEN <= 15,
|
||||
"Thread name must be no more than 15 characters");
|
||||
return Dispatch(nsDependentCString(taskThreadName, LEN - 1));
|
||||
}
|
||||
|
||||
nsresult Dispatch(const nsACString& taskThreadName);
|
||||
|
||||
void Skip()
|
||||
{
|
||||
virtualDestroyNSSReference();
|
||||
}
|
||||
|
||||
protected:
|
||||
CryptoTask()
|
||||
: mRv(NS_ERROR_NOT_INITIALIZED),
|
||||
mReleasedNSSResources(false)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~CryptoTask();
|
||||
|
||||
/**
|
||||
* Called on a background thread (never the main thread). If CalculateResult
|
||||
* is called, then its result will be passed to CallCallback on the main
|
||||
* thread.
|
||||
*/
|
||||
virtual nsresult CalculateResult() = 0;
|
||||
|
||||
/**
|
||||
* Called on the main thread during NSS shutdown or just before CallCallback
|
||||
* has been called. All NSS resources must be released. Usually, this just
|
||||
* means assigning nullptr to the ScopedNSSType-based memory variables.
|
||||
*/
|
||||
virtual void ReleaseNSSResources() = 0;
|
||||
|
||||
/**
|
||||
* Called on the main thread with the result from CalculateResult() or
|
||||
* with an error code if NSS was shut down before CalculateResult could
|
||||
* be called.
|
||||
*/
|
||||
virtual void CallCallback(nsresult rv) = 0;
|
||||
|
||||
private:
|
||||
NS_IMETHOD Run() override final;
|
||||
virtual void virtualDestroyNSSReference() override final;
|
||||
|
||||
nsresult mRv;
|
||||
bool mReleasedNSSResources;
|
||||
|
||||
nsCOMPtr<nsIThread> mThread;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla__CryptoTask_h
|
||||
304
security/manager/ssl/DER.jsm
Normal file
304
security/manager/ssl/DER.jsm
Normal file
|
|
@ -0,0 +1,304 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
// A minimal ASN.1 DER decoder. Supports input lengths up to 65539 (one byte for
|
||||
// the outer tag, one byte for the 0x82 length-length indicator, two bytes
|
||||
// indicating a contents length of 65535, and then the 65535 bytes of contents).
|
||||
// Intended to be used like so:
|
||||
//
|
||||
// let bytes = <an array of bytes describing a SEQUENCE OF INTEGER>;
|
||||
// let der = new DER.DER(bytes);
|
||||
// let contents = new DER.DER(der.readTagAndGetContents(DER.SEQUENCE));
|
||||
// while (!contents.atEnd()) {
|
||||
// let integerBytes = contents.readTagAndGetContents(DER.INTEGER);
|
||||
// <... do something with integerBytes ...>
|
||||
// }
|
||||
// der.assertAtEnd();
|
||||
//
|
||||
// For CHOICE, use readTLVChoice and pass an array of acceptable tags.
|
||||
// The convenience function readBIT_STRING is provided to handle the unused bits
|
||||
// aspect of BIT STRING. It returns an object that has the properties contents
|
||||
// (an array of bytes consisting of the bytes making up the BIT STRING) and
|
||||
// unusedBits (indicating the number of unused bits at the end).
|
||||
// All other functions generally return an array of bytes or a single byte as
|
||||
// appropriate.
|
||||
// peekTag can be used to see if the next tag is an expected given tag.
|
||||
// readTLV reads and returns an entire (tag, length, value) tuple (again
|
||||
// returned as an array of bytes).
|
||||
|
||||
const UNIVERSAL = 0 << 6;
|
||||
const CONSTRUCTED = 1 << 5;
|
||||
const CONTEXT_SPECIFIC = 2 << 6;
|
||||
|
||||
const INTEGER = UNIVERSAL | 0x02; // 0x02
|
||||
const BIT_STRING = UNIVERSAL | 0x03; // 0x03
|
||||
const NULL = UNIVERSAL | 0x05; // 0x05
|
||||
const OBJECT_IDENTIFIER = UNIVERSAL | 0x06; // 0x06
|
||||
const PrintableString = UNIVERSAL | 0x13; // 0x13
|
||||
const TeletexString = UNIVERSAL | 0x14; // 0x14
|
||||
const IA5String = UNIVERSAL | 0x16; // 0x16
|
||||
const UTCTime = UNIVERSAL | 0x17; // 0x17
|
||||
const GeneralizedTime = UNIVERSAL | 0x18; // 0x18
|
||||
const UTF8String = UNIVERSAL | 0x0c; // 0x0c
|
||||
const SEQUENCE = UNIVERSAL | CONSTRUCTED | 0x10; // 0x30
|
||||
const SET = UNIVERSAL | CONSTRUCTED | 0x11; // 0x31
|
||||
|
||||
const ERROR_INVALID_INPUT = "invalid input";
|
||||
const ERROR_DATA_TRUNCATED = "data truncated";
|
||||
const ERROR_EXTRA_DATA = "extra data";
|
||||
const ERROR_INVALID_LENGTH = "invalid length";
|
||||
const ERROR_UNSUPPORTED_ASN1 = "unsupported asn.1";
|
||||
const ERROR_UNSUPPORTED_LENGTH = "unsupported length";
|
||||
const ERROR_INVALID_BIT_STRING = "invalid BIT STRING encoding";
|
||||
|
||||
/** Class representing a decoded BIT STRING. */
|
||||
class BitString {
|
||||
/**
|
||||
* @param {Number} unusedBits the number of unused bits
|
||||
* @param {Number[]} contents an array of bytes comprising the BIT STRING
|
||||
*/
|
||||
constructor(unusedBits, contents) {
|
||||
this._unusedBits = unusedBits;
|
||||
this._contents = contents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of unused bits in the BIT STRING
|
||||
* @return {Number} the number of unused bits
|
||||
*/
|
||||
get unusedBits() {
|
||||
return this._unusedBits;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the contents of the BIT STRING
|
||||
* @return {Number[]} an array of bytes representing the contents
|
||||
*/
|
||||
get contents() {
|
||||
return this._contents;
|
||||
}
|
||||
}
|
||||
|
||||
/** Class representing DER-encoded data. Provides methods for decoding it. */
|
||||
class DER {
|
||||
/**
|
||||
* @param {Number[]} bytes an array of bytes representing the encoded data
|
||||
*/
|
||||
constructor(bytes) {
|
||||
// Reject non-array inputs.
|
||||
if (!Array.isArray(bytes)) {
|
||||
throw new Error(ERROR_INVALID_INPUT);
|
||||
}
|
||||
if (bytes.length > 65539) {
|
||||
throw new Error(ERROR_UNSUPPORTED_LENGTH);
|
||||
}
|
||||
// Reject inputs containing non-integer values or values too small or large.
|
||||
if (bytes.some(b => !Number.isInteger(b) || b < 0 || b > 255)) {
|
||||
throw new Error(ERROR_INVALID_INPUT);
|
||||
}
|
||||
this._bytes = bytes;
|
||||
this._cursor = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the decoder is at the end of the given data. Throws an error
|
||||
* if this is not the case.
|
||||
*/
|
||||
assertAtEnd() {
|
||||
if (!this.atEnd()) {
|
||||
throw new Error(ERROR_EXTRA_DATA);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether or not the decoder is at the end of the given data.
|
||||
* @return {Boolean} true if the decoder is at the end and false otherwise
|
||||
*/
|
||||
atEnd() {
|
||||
return this._cursor == this._bytes.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the next byte of data. Throws if no more data is available.
|
||||
* @return {Number} the next byte of data
|
||||
*/
|
||||
readByte() {
|
||||
if (this._cursor >= this._bytes.length) {
|
||||
throw new Error(ERROR_DATA_TRUNCATED);
|
||||
}
|
||||
let val = this._bytes[this._cursor];
|
||||
this._cursor++;
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given the next expected tag, reads and asserts that the next tag is in fact
|
||||
* the given tag.
|
||||
* @param {Number} expectedTag the expected next tag
|
||||
*/
|
||||
_readExpectedTag(expectedTag) {
|
||||
let tag = this.readByte();
|
||||
if (tag != expectedTag) {
|
||||
throw new Error(`unexpected tag: found ${tag} instead of ${expectedTag}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes and returns the length portion of an ASN.1 TLV tuple. Throws if the
|
||||
* length is incorrectly encoded or if it describes a length greater than
|
||||
* 65535 bytes. Indefinite-length encoding is not supported.
|
||||
* @return {Number} the length of the value of the TLV tuple
|
||||
*/
|
||||
_readLength() {
|
||||
let nextByte = this.readByte();
|
||||
if (nextByte < 0x80) {
|
||||
return nextByte;
|
||||
}
|
||||
if (nextByte == 0x80) {
|
||||
throw new Error(ERROR_UNSUPPORTED_ASN1);
|
||||
}
|
||||
if (nextByte == 0x81) {
|
||||
let length = this.readByte();
|
||||
if (length < 0x80) {
|
||||
throw new Error(ERROR_INVALID_LENGTH);
|
||||
}
|
||||
return length;
|
||||
}
|
||||
if (nextByte == 0x82) {
|
||||
let length1 = this.readByte();
|
||||
let length2 = this.readByte();
|
||||
let length = (length1 << 8) | length2;
|
||||
if (length < 256) {
|
||||
throw new Error(ERROR_INVALID_LENGTH);
|
||||
}
|
||||
return length;
|
||||
}
|
||||
throw new Error(ERROR_UNSUPPORTED_LENGTH);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads <length> bytes of data if available. Throws if less than <length>
|
||||
* bytes are available.
|
||||
* @param {Number} length the number of bytes to read. Must be non-negative.
|
||||
* @return {Number[]} the next <length> bytes of data
|
||||
*/
|
||||
readBytes(length) {
|
||||
if (length < 0) {
|
||||
throw new Error(ERROR_INVALID_LENGTH);
|
||||
}
|
||||
let bytes = [];
|
||||
for (let i = 0; i < length; i++) {
|
||||
bytes.push(this.readByte());
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an expected next ASN.1 tag, ensures that that tag is next and returns
|
||||
* the contents of that tag. Throws if a different tag is encountered or if
|
||||
* the data is otherwise incorrectly encoded.
|
||||
* @param {Number} tag the next expected ASN.1 tag
|
||||
* @return {Number[]} the contents of the tag
|
||||
*/
|
||||
readTagAndGetContents(tag) {
|
||||
this._readExpectedTag(tag);
|
||||
let length = this._readLength();
|
||||
return this.readBytes(length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next byte without advancing the decoder. Throws if no more data
|
||||
* is available.
|
||||
* @return {Number} the next available byte
|
||||
*/
|
||||
_peekByte() {
|
||||
if (this._cursor >= this._bytes.length) {
|
||||
throw new Error(ERROR_DATA_TRUNCATED);
|
||||
}
|
||||
return this._bytes[this._cursor];
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an expected tag, reads the next entire ASN.1 TLV tuple, asserting
|
||||
* that the tag matches.
|
||||
* @param {Number} tag the expected tag
|
||||
* @return {Number[]} an array of bytes representing the TLV tuple
|
||||
*/
|
||||
_readExpectedTLV(tag) {
|
||||
let mark = this._cursor;
|
||||
this._readExpectedTag(tag);
|
||||
let length = this._readLength();
|
||||
// read the bytes so we know they're there (also to advance the cursor)
|
||||
this.readBytes(length);
|
||||
return this._bytes.slice(mark, this._cursor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the next ASN.1 tag, length, and value and returns them as an array of
|
||||
* bytes.
|
||||
* @return {Number[]} an array of bytes representing the next ASN.1 TLV
|
||||
*/
|
||||
readTLV() {
|
||||
let nextTag = this._peekByte();
|
||||
return this._readExpectedTLV(nextTag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience function for decoding a BIT STRING. Reads and returns the
|
||||
* contents of the expected next BIT STRING. Throws if the next TLV isn't a
|
||||
* BIT STRING or if the BIT STRING is incorrectly encoded.
|
||||
* @return {BitString} the next BIT STRING
|
||||
*/
|
||||
readBIT_STRING() {
|
||||
let contents = this.readTagAndGetContents(BIT_STRING);
|
||||
if (contents.length < 1) {
|
||||
throw new Error(ERROR_INVALID_BIT_STRING);
|
||||
}
|
||||
let unusedBits = contents[0];
|
||||
if (unusedBits > 7) {
|
||||
throw new Error(ERROR_INVALID_BIT_STRING);
|
||||
}
|
||||
// Zero bytes of content but some amount of padding is invalid.
|
||||
if (contents.length == 1 && unusedBits != 0) {
|
||||
throw new Error(ERROR_INVALID_BIT_STRING);
|
||||
}
|
||||
return new BitString(unusedBits, contents.slice(1, contents.length));
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks to see if the next ASN.1 tag is the expected given tag.
|
||||
* @param {Number} tag the expected next ASN.1 tag
|
||||
* @return {Boolean} true if the next tag is the given one and false otherwise
|
||||
*/
|
||||
peekTag(tag) {
|
||||
if (this._cursor >= this._bytes.length) {
|
||||
return false;
|
||||
}
|
||||
return this._bytes[this._cursor] == tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a list of possible next ASN.1 tags, returns the next TLV if the next
|
||||
* tag is in the list. Throws if the next tag is not in the list or if the
|
||||
* data is incorrectly encoded.
|
||||
* @param {Number[]} tagList the list of potential next tags
|
||||
* @return {Number[]} the contents of the next TLV if the next tag is in
|
||||
* <tagList>
|
||||
*/
|
||||
readTLVChoice(tagList) {
|
||||
let tag = this._peekByte();
|
||||
if (!tagList.includes(tag)) {
|
||||
throw new Error(
|
||||
`unexpected tag: found ${tag} instead of one of ${tagList}`);
|
||||
}
|
||||
return this._readExpectedTLV(tag);
|
||||
}
|
||||
}
|
||||
|
||||
this.DER = { UNIVERSAL, CONSTRUCTED, CONTEXT_SPECIFIC, INTEGER, BIT_STRING,
|
||||
NULL, OBJECT_IDENTIFIER, PrintableString, TeletexString, IA5String,
|
||||
UTCTime, GeneralizedTime, UTF8String, SEQUENCE, SET, DER };
|
||||
this.EXPORTED_SYMBOLS = ["DER"];
|
||||
940
security/manager/ssl/DataStorage.cpp
Normal file
940
security/manager/ssl/DataStorage.cpp
Normal file
|
|
@ -0,0 +1,940 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "DataStorage.h"
|
||||
|
||||
#include "mozilla/ClearOnShutdown.h"
|
||||
#include "mozilla/dom/PContent.h"
|
||||
#include "mozilla/dom/ContentChild.h"
|
||||
#include "mozilla/dom/ContentParent.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/Services.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
#include "mozilla/Unused.h"
|
||||
#include "nsAppDirectoryServiceDefs.h"
|
||||
#include "nsDirectoryServiceUtils.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "nsITimer.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsStreamUtils.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "prprf.h"
|
||||
|
||||
// NB: Read DataStorage.h first.
|
||||
|
||||
// The default time between data changing and a write, in milliseconds.
|
||||
static const uint32_t sDataStorageDefaultTimerDelay = 5u * 60u * 1000u;
|
||||
// The maximum score an entry can have (prevents overflow)
|
||||
static const uint32_t sMaxScore = UINT32_MAX;
|
||||
// The maximum number of entries per type of data (limits resource use)
|
||||
static const uint32_t sMaxDataEntries = 1024;
|
||||
static const int64_t sOneDayInMicroseconds = int64_t(24 * 60 * 60) *
|
||||
PR_USEC_PER_SEC;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
NS_IMPL_ISUPPORTS(DataStorage,
|
||||
nsIObserver)
|
||||
|
||||
StaticAutoPtr<DataStorage::DataStorages> DataStorage::sDataStorages;
|
||||
|
||||
DataStorage::DataStorage(const nsString& aFilename)
|
||||
: mMutex("DataStorage::mMutex")
|
||||
, mPendingWrite(false)
|
||||
, mShuttingDown(false)
|
||||
, mInitCalled(false)
|
||||
, mReadyMonitor("DataStorage::mReadyMonitor")
|
||||
, mReady(false)
|
||||
, mFilename(aFilename)
|
||||
{
|
||||
}
|
||||
|
||||
DataStorage::~DataStorage()
|
||||
{
|
||||
}
|
||||
|
||||
// static
|
||||
already_AddRefed<DataStorage>
|
||||
DataStorage::Get(const nsString& aFilename)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (!sDataStorages) {
|
||||
sDataStorages = new DataStorages();
|
||||
ClearOnShutdown(&sDataStorages);
|
||||
}
|
||||
RefPtr<DataStorage> storage;
|
||||
if (!sDataStorages->Get(aFilename, getter_AddRefs(storage))) {
|
||||
storage = new DataStorage(aFilename);
|
||||
sDataStorages->Put(aFilename, storage);
|
||||
}
|
||||
return storage.forget();
|
||||
}
|
||||
|
||||
// static
|
||||
already_AddRefed<DataStorage>
|
||||
DataStorage::GetIfExists(const nsString& aFilename)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (!sDataStorages) {
|
||||
sDataStorages = new DataStorages();
|
||||
}
|
||||
RefPtr<DataStorage> storage;
|
||||
sDataStorages->Get(aFilename, getter_AddRefs(storage));
|
||||
return storage.forget();
|
||||
}
|
||||
|
||||
nsresult
|
||||
DataStorage::Init(bool& aDataWillPersist)
|
||||
{
|
||||
// Don't access the observer service or preferences off the main thread.
|
||||
if (!NS_IsMainThread()) {
|
||||
NS_NOTREACHED("DataStorage::Init called off main thread");
|
||||
return NS_ERROR_NOT_SAME_THREAD;
|
||||
}
|
||||
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
||||
// Ignore attempts to initialize several times.
|
||||
if (mInitCalled) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
mInitCalled = true;
|
||||
|
||||
nsresult rv;
|
||||
if (XRE_IsParentProcess()) {
|
||||
rv = NS_NewNamedThread("DataStorage", getter_AddRefs(mWorkerThread));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = AsyncReadData(aDataWillPersist, lock);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
} else {
|
||||
// In the child process, we ask the parent process for the data.
|
||||
MOZ_ASSERT(XRE_IsContentProcess());
|
||||
aDataWillPersist = false;
|
||||
InfallibleTArray<DataStorageItem> items;
|
||||
dom::ContentChild::GetSingleton()->
|
||||
SendReadDataStorageArray(mFilename, &items);
|
||||
for (auto& item : items) {
|
||||
Entry entry;
|
||||
entry.mValue = item.value();
|
||||
rv = PutInternal(item.key(), entry, item.type(), lock);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
mReady = true;
|
||||
NotifyObservers("data-storage-ready");
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIObserverService> os = services::GetObserverService();
|
||||
if (NS_WARN_IF(!os)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
// Clear private data as appropriate.
|
||||
os->AddObserver(this, "last-pb-context-exited", false);
|
||||
// Observe shutdown; save data and prevent any further writes.
|
||||
// In the parent process, we need to write to the profile directory, so
|
||||
// we should listen for profile-before-change so that we can safely
|
||||
// write to the profile. In the content process however we don't have
|
||||
// access to the profile directory and profile notifications are not
|
||||
// dispatched, so we need to clean up on xpcom-shutdown.
|
||||
if (XRE_IsParentProcess()) {
|
||||
os->AddObserver(this, "profile-before-change", false);
|
||||
}
|
||||
// In the Parent process, this is a backstop for xpcshell and other cases
|
||||
// where profile-before-change might not get sent.
|
||||
os->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
|
||||
|
||||
// For test purposes, we can set the write timer to be very fast.
|
||||
mTimerDelay = Preferences::GetInt("test.datastorage.write_timer_ms",
|
||||
sDataStorageDefaultTimerDelay);
|
||||
rv = Preferences::AddStrongObserver(this, "test.datastorage.write_timer_ms");
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
class DataStorage::Reader : public Runnable
|
||||
{
|
||||
public:
|
||||
explicit Reader(DataStorage* aDataStorage)
|
||||
: mDataStorage(aDataStorage)
|
||||
{
|
||||
}
|
||||
~Reader();
|
||||
|
||||
private:
|
||||
NS_DECL_NSIRUNNABLE
|
||||
|
||||
static nsresult ParseLine(nsDependentCSubstring& aLine, nsCString& aKeyOut,
|
||||
Entry& aEntryOut);
|
||||
|
||||
RefPtr<DataStorage> mDataStorage;
|
||||
};
|
||||
|
||||
DataStorage::Reader::~Reader()
|
||||
{
|
||||
// Notify that calls to Get can proceed.
|
||||
{
|
||||
MonitorAutoLock readyLock(mDataStorage->mReadyMonitor);
|
||||
mDataStorage->mReady = true;
|
||||
nsresult rv = mDataStorage->mReadyMonitor.NotifyAll();
|
||||
Unused << NS_WARN_IF(NS_FAILED(rv));
|
||||
}
|
||||
|
||||
// This is for tests.
|
||||
nsCOMPtr<nsIRunnable> job =
|
||||
NewRunnableMethod<const char*>(mDataStorage,
|
||||
&DataStorage::NotifyObservers,
|
||||
"data-storage-ready");
|
||||
nsresult rv = NS_DispatchToMainThread(job, NS_DISPATCH_NORMAL);
|
||||
Unused << NS_WARN_IF(NS_FAILED(rv));
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
DataStorage::Reader::Run()
|
||||
{
|
||||
nsresult rv;
|
||||
// Concurrent operations on nsIFile objects are not guaranteed to be safe,
|
||||
// so we clone the file while holding the lock and then release the lock.
|
||||
// At that point, we can safely operate on the clone.
|
||||
nsCOMPtr<nsIFile> file;
|
||||
{
|
||||
MutexAutoLock lock(mDataStorage->mMutex);
|
||||
// If we don't have a profile, bail.
|
||||
if (!mDataStorage->mBackingFile) {
|
||||
return NS_OK;
|
||||
}
|
||||
rv = mDataStorage->mBackingFile->Clone(getter_AddRefs(file));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
nsCOMPtr<nsIInputStream> fileInputStream;
|
||||
rv = NS_NewLocalFileInputStream(getter_AddRefs(fileInputStream), file);
|
||||
// If we failed for some reason other than the file doesn't exist, bail.
|
||||
if (NS_WARN_IF(NS_FAILED(rv) &&
|
||||
rv != NS_ERROR_FILE_TARGET_DOES_NOT_EXIST && // on Unix
|
||||
rv != NS_ERROR_FILE_NOT_FOUND)) { // on Windows
|
||||
return rv;
|
||||
}
|
||||
|
||||
// If there is a file with data in it, read it. If there isn't,
|
||||
// we'll essentially fall through to notifying that we're good to go.
|
||||
nsCString data;
|
||||
if (fileInputStream) {
|
||||
// Limit to 2MB of data, but only store sMaxDataEntries entries.
|
||||
rv = NS_ConsumeStream(fileInputStream, 1u << 21, data);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
// Atomically parse the data and insert the entries read.
|
||||
// Don't clear existing entries - they may have been inserted between when
|
||||
// this read was kicked-off and when it was run.
|
||||
{
|
||||
MutexAutoLock lock(mDataStorage->mMutex);
|
||||
// The backing file consists of a list of
|
||||
// <key>\t<score>\t<last accessed time>\t<value>\n
|
||||
// The final \n is not optional; if it is not present the line is assumed
|
||||
// to be corrupt.
|
||||
int32_t currentIndex = 0;
|
||||
int32_t newlineIndex = 0;
|
||||
do {
|
||||
newlineIndex = data.FindChar('\n', currentIndex);
|
||||
// If there are no more newlines or the data table has too many
|
||||
// entries, we are done.
|
||||
if (newlineIndex < 0 ||
|
||||
mDataStorage->mPersistentDataTable.Count() >= sMaxDataEntries) {
|
||||
break;
|
||||
}
|
||||
|
||||
nsDependentCSubstring line(data, currentIndex,
|
||||
newlineIndex - currentIndex);
|
||||
currentIndex = newlineIndex + 1;
|
||||
nsCString key;
|
||||
Entry entry;
|
||||
nsresult parseRV = ParseLine(line, key, entry);
|
||||
if (NS_SUCCEEDED(parseRV)) {
|
||||
// It could be the case that a newer entry was added before
|
||||
// we got around to reading the file. Don't overwrite new entries.
|
||||
Entry newerEntry;
|
||||
bool present = mDataStorage->mPersistentDataTable.Get(key, &newerEntry);
|
||||
if (!present) {
|
||||
mDataStorage->mPersistentDataTable.Put(key, entry);
|
||||
}
|
||||
}
|
||||
} while (true);
|
||||
|
||||
Telemetry::Accumulate(Telemetry::DATA_STORAGE_ENTRIES,
|
||||
mDataStorage->mPersistentDataTable.Count());
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// The key must be a non-empty string containing no instances of '\t' or '\n',
|
||||
// and must have a length no more than 256.
|
||||
// The value must not contain '\n' and must have a length no more than 1024.
|
||||
// The length limits are to prevent unbounded memory and disk usage.
|
||||
/* static */
|
||||
nsresult
|
||||
DataStorage::ValidateKeyAndValue(const nsCString& aKey, const nsCString& aValue)
|
||||
{
|
||||
if (aKey.IsEmpty()) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
if (aKey.Length() > 256) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
int32_t delimiterIndex = aKey.FindChar('\t', 0);
|
||||
if (delimiterIndex >= 0) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
delimiterIndex = aKey.FindChar('\n', 0);
|
||||
if (delimiterIndex >= 0) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
delimiterIndex = aValue.FindChar('\n', 0);
|
||||
if (delimiterIndex >= 0) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
if (aValue.Length() > 1024) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Each line is: <key>\t<score>\t<last accessed time>\t<value>
|
||||
// Where <score> is a uint32_t as a string, <last accessed time> is a
|
||||
// int32_t as a string, and the rest are strings.
|
||||
// <value> can contain anything but a newline.
|
||||
// Returns a successful status if the line can be decoded into a key and entry.
|
||||
// Otherwise, an error status is returned and the values assigned to the
|
||||
// output parameters are in an undefined state.
|
||||
/* static */
|
||||
nsresult
|
||||
DataStorage::Reader::ParseLine(nsDependentCSubstring& aLine, nsCString& aKeyOut,
|
||||
Entry& aEntryOut)
|
||||
{
|
||||
// First find the indices to each part of the line.
|
||||
int32_t scoreIndex;
|
||||
scoreIndex = aLine.FindChar('\t', 0) + 1;
|
||||
if (scoreIndex <= 0) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
int32_t accessedIndex = aLine.FindChar('\t', scoreIndex) + 1;
|
||||
if (accessedIndex <= 0) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
int32_t valueIndex = aLine.FindChar('\t', accessedIndex) + 1;
|
||||
if (valueIndex <= 0) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
// Now make substrings based on where each part is.
|
||||
nsDependentCSubstring keyPart(aLine, 0, scoreIndex - 1);
|
||||
nsDependentCSubstring scorePart(aLine, scoreIndex,
|
||||
accessedIndex - scoreIndex - 1);
|
||||
nsDependentCSubstring accessedPart(aLine, accessedIndex,
|
||||
valueIndex - accessedIndex - 1);
|
||||
nsDependentCSubstring valuePart(aLine, valueIndex);
|
||||
|
||||
nsresult rv;
|
||||
rv = DataStorage::ValidateKeyAndValue(nsCString(keyPart),
|
||||
nsCString(valuePart));
|
||||
if (NS_FAILED(rv)) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
// Now attempt to decode the score part as a uint32_t.
|
||||
// XXX nsDependentCSubstring doesn't support ToInteger
|
||||
int32_t integer = nsCString(scorePart).ToInteger(&rv);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
if (integer < 0) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
aEntryOut.mScore = (uint32_t)integer;
|
||||
|
||||
integer = nsCString(accessedPart).ToInteger(&rv);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
if (integer < 0) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
aEntryOut.mLastAccessed = integer;
|
||||
|
||||
// Now set the key and value.
|
||||
aKeyOut.Assign(keyPart);
|
||||
aEntryOut.mValue.Assign(valuePart);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
DataStorage::AsyncReadData(bool& aHaveProfileDir,
|
||||
const MutexAutoLock& /*aProofOfLock*/)
|
||||
{
|
||||
MOZ_ASSERT(XRE_IsParentProcess());
|
||||
aHaveProfileDir = false;
|
||||
// Allocate a Reader so that even if it isn't dispatched,
|
||||
// the data-storage-ready notification will be fired and Get
|
||||
// will be able to proceed (this happens in its destructor).
|
||||
RefPtr<Reader> job(new Reader(this));
|
||||
nsresult rv;
|
||||
// If we don't have a profile directory, this will fail.
|
||||
// That's okay - it just means there is no persistent state.
|
||||
rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
|
||||
getter_AddRefs(mBackingFile));
|
||||
if (NS_FAILED(rv)) {
|
||||
mBackingFile = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
rv = mBackingFile->Append(mFilename);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = mWorkerThread->Dispatch(job, NS_DISPATCH_NORMAL);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
aHaveProfileDir = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
DataStorage::WaitForReady()
|
||||
{
|
||||
MonitorAutoLock readyLock(mReadyMonitor);
|
||||
while (!mReady) {
|
||||
nsresult rv = readyLock.Wait();
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
MOZ_ASSERT(mReady);
|
||||
}
|
||||
|
||||
nsCString
|
||||
DataStorage::Get(const nsCString& aKey, DataStorageType aType)
|
||||
{
|
||||
WaitForReady();
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
||||
Entry entry;
|
||||
bool foundValue = GetInternal(aKey, &entry, aType, lock);
|
||||
if (!foundValue) {
|
||||
return EmptyCString();
|
||||
}
|
||||
|
||||
// If we're here, we found a value. Maybe update its score.
|
||||
if (entry.UpdateScore()) {
|
||||
PutInternal(aKey, entry, aType, lock);
|
||||
}
|
||||
|
||||
return entry.mValue;
|
||||
}
|
||||
|
||||
bool
|
||||
DataStorage::GetInternal(const nsCString& aKey, Entry* aEntry,
|
||||
DataStorageType aType,
|
||||
const MutexAutoLock& aProofOfLock)
|
||||
{
|
||||
DataStorageTable& table = GetTableForType(aType, aProofOfLock);
|
||||
bool foundValue = table.Get(aKey, aEntry);
|
||||
return foundValue;
|
||||
}
|
||||
|
||||
DataStorage::DataStorageTable&
|
||||
DataStorage::GetTableForType(DataStorageType aType,
|
||||
const MutexAutoLock& /*aProofOfLock*/)
|
||||
{
|
||||
switch (aType) {
|
||||
case DataStorage_Persistent:
|
||||
return mPersistentDataTable;
|
||||
case DataStorage_Temporary:
|
||||
return mTemporaryDataTable;
|
||||
case DataStorage_Private:
|
||||
return mPrivateDataTable;
|
||||
}
|
||||
|
||||
MOZ_CRASH("given bad DataStorage storage type");
|
||||
}
|
||||
|
||||
void
|
||||
DataStorage::ReadAllFromTable(DataStorageType aType,
|
||||
InfallibleTArray<dom::DataStorageItem>* aItems,
|
||||
const MutexAutoLock& aProofOfLock)
|
||||
{
|
||||
for (auto iter = GetTableForType(aType, aProofOfLock).Iter();
|
||||
!iter.Done(); iter.Next()) {
|
||||
DataStorageItem* item = aItems->AppendElement();
|
||||
item->key() = iter.Key();
|
||||
item->value() = iter.Data().mValue;
|
||||
item->type() = aType;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DataStorage::GetAll(InfallibleTArray<dom::DataStorageItem>* aItems)
|
||||
{
|
||||
WaitForReady();
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
||||
aItems->SetCapacity(mPersistentDataTable.Count() +
|
||||
mTemporaryDataTable.Count() +
|
||||
mPrivateDataTable.Count());
|
||||
ReadAllFromTable(DataStorage_Persistent, aItems, lock);
|
||||
ReadAllFromTable(DataStorage_Temporary, aItems, lock);
|
||||
ReadAllFromTable(DataStorage_Private, aItems, lock);
|
||||
}
|
||||
|
||||
// Limit the number of entries per table. This is to prevent unbounded
|
||||
// resource use. The eviction strategy is as follows:
|
||||
// - An entry's score is incremented once for every day it is accessed.
|
||||
// - Evict an entry with score no more than any other entry in the table
|
||||
// (this is the same as saying evict the entry with the lowest score,
|
||||
// except for when there are multiple entries with the lowest score,
|
||||
// in which case one of them is evicted - which one is not specified).
|
||||
void
|
||||
DataStorage::MaybeEvictOneEntry(DataStorageType aType,
|
||||
const MutexAutoLock& aProofOfLock)
|
||||
{
|
||||
DataStorageTable& table = GetTableForType(aType, aProofOfLock);
|
||||
if (table.Count() >= sMaxDataEntries) {
|
||||
KeyAndEntry toEvict;
|
||||
// If all entries have score sMaxScore, this won't actually remove
|
||||
// anything. This will never happen, however, because having that high
|
||||
// a score either means someone tampered with the backing file or every
|
||||
// entry has been accessed once a day for ~4 billion days.
|
||||
// The worst that will happen is there will be 1025 entries in the
|
||||
// persistent data table, with the 1025th entry being replaced every time
|
||||
// data with a new key is inserted into the table. This is bad but
|
||||
// ultimately not that concerning, considering that if an attacker can
|
||||
// modify data in the profile, they can cause much worse harm.
|
||||
toEvict.mEntry.mScore = sMaxScore;
|
||||
|
||||
for (auto iter = table.Iter(); !iter.Done(); iter.Next()) {
|
||||
Entry entry = iter.UserData();
|
||||
if (entry.mScore < toEvict.mEntry.mScore) {
|
||||
toEvict.mKey = iter.Key();
|
||||
toEvict.mEntry = entry;
|
||||
}
|
||||
}
|
||||
|
||||
table.Remove(toEvict.mKey);
|
||||
}
|
||||
}
|
||||
|
||||
template <class Functor>
|
||||
static
|
||||
void
|
||||
RunOnAllContentParents(Functor func)
|
||||
{
|
||||
if (!XRE_IsParentProcess()) {
|
||||
return;
|
||||
}
|
||||
using dom::ContentParent;
|
||||
nsTArray<ContentParent*> parents;
|
||||
ContentParent::GetAll(parents);
|
||||
for (auto& parent: parents) {
|
||||
func(parent);
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
DataStorage::Put(const nsCString& aKey, const nsCString& aValue,
|
||||
DataStorageType aType)
|
||||
{
|
||||
WaitForReady();
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
||||
nsresult rv;
|
||||
rv = ValidateKeyAndValue(aKey, aValue);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
Entry entry;
|
||||
bool exists = GetInternal(aKey, &entry, aType, lock);
|
||||
if (exists) {
|
||||
entry.UpdateScore();
|
||||
} else {
|
||||
MaybeEvictOneEntry(aType, lock);
|
||||
}
|
||||
entry.mValue = aValue;
|
||||
rv = PutInternal(aKey, entry, aType, lock);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
RunOnAllContentParents([&](dom::ContentParent* aParent) {
|
||||
DataStorageItem item;
|
||||
item.key() = aKey;
|
||||
item.value() = aValue;
|
||||
item.type() = aType;
|
||||
Unused << aParent->SendDataStoragePut(mFilename, item);
|
||||
});
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
DataStorage::PutInternal(const nsCString& aKey, Entry& aEntry,
|
||||
DataStorageType aType,
|
||||
const MutexAutoLock& aProofOfLock)
|
||||
{
|
||||
DataStorageTable& table = GetTableForType(aType, aProofOfLock);
|
||||
table.Put(aKey, aEntry);
|
||||
|
||||
if (aType == DataStorage_Persistent && !mPendingWrite) {
|
||||
return AsyncSetTimer(aProofOfLock);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
DataStorage::Remove(const nsCString& aKey, DataStorageType aType)
|
||||
{
|
||||
WaitForReady();
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
||||
DataStorageTable& table = GetTableForType(aType, lock);
|
||||
table.Remove(aKey);
|
||||
|
||||
if (aType == DataStorage_Persistent && !mPendingWrite) {
|
||||
Unused << AsyncSetTimer(lock);
|
||||
}
|
||||
|
||||
RunOnAllContentParents([&](dom::ContentParent* aParent) {
|
||||
Unused << aParent->SendDataStorageRemove(mFilename, aKey, aType);
|
||||
});
|
||||
}
|
||||
|
||||
class DataStorage::Writer : public Runnable
|
||||
{
|
||||
public:
|
||||
Writer(nsCString& aData, DataStorage* aDataStorage)
|
||||
: mData(aData)
|
||||
, mDataStorage(aDataStorage)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
NS_DECL_NSIRUNNABLE
|
||||
|
||||
nsCString mData;
|
||||
RefPtr<DataStorage> mDataStorage;
|
||||
};
|
||||
|
||||
NS_IMETHODIMP
|
||||
DataStorage::Writer::Run()
|
||||
{
|
||||
nsresult rv;
|
||||
// Concurrent operations on nsIFile objects are not guaranteed to be safe,
|
||||
// so we clone the file while holding the lock and then release the lock.
|
||||
// At that point, we can safely operate on the clone.
|
||||
nsCOMPtr<nsIFile> file;
|
||||
{
|
||||
MutexAutoLock lock(mDataStorage->mMutex);
|
||||
// If we don't have a profile, bail.
|
||||
if (!mDataStorage->mBackingFile) {
|
||||
return NS_OK;
|
||||
}
|
||||
rv = mDataStorage->mBackingFile->Clone(getter_AddRefs(file));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIOutputStream> outputStream;
|
||||
rv = NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), file,
|
||||
PR_CREATE_FILE | PR_TRUNCATE | PR_WRONLY);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
const char* ptr = mData.get();
|
||||
int32_t remaining = mData.Length();
|
||||
uint32_t written = 0;
|
||||
while (remaining > 0) {
|
||||
rv = outputStream->Write(ptr, remaining, &written);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
remaining -= written;
|
||||
ptr += written;
|
||||
}
|
||||
|
||||
// Observed by tests.
|
||||
nsCOMPtr<nsIRunnable> job =
|
||||
NewRunnableMethod<const char*>(mDataStorage,
|
||||
&DataStorage::NotifyObservers,
|
||||
"data-storage-written");
|
||||
rv = NS_DispatchToMainThread(job, NS_DISPATCH_NORMAL);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
DataStorage::AsyncWriteData(const MutexAutoLock& /*aProofOfLock*/)
|
||||
{
|
||||
MOZ_ASSERT(XRE_IsParentProcess());
|
||||
|
||||
if (mShuttingDown || !mBackingFile) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCString output;
|
||||
for (auto iter = mPersistentDataTable.Iter(); !iter.Done(); iter.Next()) {
|
||||
Entry entry = iter.UserData();
|
||||
output.Append(iter.Key());
|
||||
output.Append('\t');
|
||||
output.AppendInt(entry.mScore);
|
||||
output.Append('\t');
|
||||
output.AppendInt(entry.mLastAccessed);
|
||||
output.Append('\t');
|
||||
output.Append(entry.mValue);
|
||||
output.Append('\n');
|
||||
}
|
||||
|
||||
RefPtr<Writer> job(new Writer(output, this));
|
||||
nsresult rv = mWorkerThread->Dispatch(job, NS_DISPATCH_NORMAL);
|
||||
mPendingWrite = false;
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
DataStorage::Clear()
|
||||
{
|
||||
WaitForReady();
|
||||
MutexAutoLock lock(mMutex);
|
||||
mPersistentDataTable.Clear();
|
||||
mTemporaryDataTable.Clear();
|
||||
mPrivateDataTable.Clear();
|
||||
|
||||
if (XRE_IsParentProcess()) {
|
||||
// Asynchronously clear the file. This is similar to the permission manager
|
||||
// in that it doesn't wait to synchronously remove the data from its backing
|
||||
// storage either.
|
||||
nsresult rv = AsyncWriteData(lock);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
RunOnAllContentParents([&](dom::ContentParent* aParent) {
|
||||
Unused << aParent->SendDataStorageClear(mFilename);
|
||||
});
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* static */
|
||||
void
|
||||
DataStorage::TimerCallback(nsITimer* aTimer, void* aClosure)
|
||||
{
|
||||
MOZ_ASSERT(XRE_IsParentProcess());
|
||||
|
||||
RefPtr<DataStorage> aDataStorage = (DataStorage*)aClosure;
|
||||
MutexAutoLock lock(aDataStorage->mMutex);
|
||||
Unused << aDataStorage->AsyncWriteData(lock);
|
||||
}
|
||||
|
||||
// We only initialize the timer on the worker thread because it's not safe
|
||||
// to mix what threads are operating on the timer.
|
||||
nsresult
|
||||
DataStorage::AsyncSetTimer(const MutexAutoLock& /*aProofOfLock*/)
|
||||
{
|
||||
if (mShuttingDown || !XRE_IsParentProcess()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
mPendingWrite = true;
|
||||
nsCOMPtr<nsIRunnable> job =
|
||||
NewRunnableMethod(this, &DataStorage::SetTimer);
|
||||
nsresult rv = mWorkerThread->Dispatch(job, NS_DISPATCH_NORMAL);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
DataStorage::SetTimer()
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
MOZ_ASSERT(XRE_IsParentProcess());
|
||||
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
||||
nsresult rv;
|
||||
if (!mTimer) {
|
||||
mTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
rv = mTimer->InitWithFuncCallback(TimerCallback, this,
|
||||
mTimerDelay, nsITimer::TYPE_ONE_SHOT);
|
||||
Unused << NS_WARN_IF(NS_FAILED(rv));
|
||||
}
|
||||
|
||||
void
|
||||
DataStorage::NotifyObservers(const char* aTopic)
|
||||
{
|
||||
// Don't access the observer service off the main thread.
|
||||
if (!NS_IsMainThread()) {
|
||||
NS_NOTREACHED("DataStorage::NotifyObservers called off main thread");
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIObserverService> os = services::GetObserverService();
|
||||
if (os) {
|
||||
os->NotifyObservers(nullptr, aTopic, mFilename.get());
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
DataStorage::DispatchShutdownTimer(const MutexAutoLock& /*aProofOfLock*/)
|
||||
{
|
||||
MOZ_ASSERT(XRE_IsParentProcess());
|
||||
|
||||
nsCOMPtr<nsIRunnable> job =
|
||||
NewRunnableMethod(this, &DataStorage::ShutdownTimer);
|
||||
nsresult rv = mWorkerThread->Dispatch(job, NS_DISPATCH_NORMAL);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
DataStorage::ShutdownTimer()
|
||||
{
|
||||
MOZ_ASSERT(XRE_IsParentProcess());
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
MutexAutoLock lock(mMutex);
|
||||
nsresult rv = mTimer->Cancel();
|
||||
Unused << NS_WARN_IF(NS_FAILED(rv));
|
||||
mTimer = nullptr;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------
|
||||
// DataStorage::nsIObserver
|
||||
//------------------------------------------------------------
|
||||
|
||||
NS_IMETHODIMP
|
||||
DataStorage::Observe(nsISupports* aSubject, const char* aTopic,
|
||||
const char16_t* aData)
|
||||
{
|
||||
// Don't access preferences off the main thread.
|
||||
if (!NS_IsMainThread()) {
|
||||
NS_NOTREACHED("DataStorage::Observe called off main thread");
|
||||
return NS_ERROR_NOT_SAME_THREAD;
|
||||
}
|
||||
|
||||
nsresult rv;
|
||||
if (strcmp(aTopic, "last-pb-context-exited") == 0) {
|
||||
MutexAutoLock lock(mMutex);
|
||||
mPrivateDataTable.Clear();
|
||||
} else if (strcmp(aTopic, "profile-before-change") == 0 ||
|
||||
(strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0 &&
|
||||
XRE_IsParentProcess())) {
|
||||
MOZ_ASSERT(XRE_IsParentProcess());
|
||||
// per bug 1271402, this should be safe to run multiple times
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
rv = AsyncWriteData(lock);
|
||||
mShuttingDown = true;
|
||||
Unused << NS_WARN_IF(NS_FAILED(rv));
|
||||
if (mTimer) {
|
||||
rv = DispatchShutdownTimer(lock);
|
||||
Unused << NS_WARN_IF(NS_FAILED(rv));
|
||||
}
|
||||
}
|
||||
// Run the thread to completion and prevent any further events
|
||||
// being scheduled to it. The thread may need the lock, so we can't
|
||||
// hold it here.
|
||||
rv = mWorkerThread->Shutdown();
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
sDataStorages->Clear();
|
||||
} else if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
|
||||
MOZ_ASSERT(!XRE_IsParentProcess());
|
||||
sDataStorages->Clear();
|
||||
} else if (strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0) {
|
||||
MutexAutoLock lock(mMutex);
|
||||
mTimerDelay = Preferences::GetInt("test.datastorage.write_timer_ms",
|
||||
sDataStorageDefaultTimerDelay);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
DataStorage::Entry::Entry()
|
||||
: mScore(0)
|
||||
, mLastAccessed((int32_t)(PR_Now() / sOneDayInMicroseconds))
|
||||
{
|
||||
}
|
||||
|
||||
// Updates this entry's score. Returns true if the score has actually changed.
|
||||
// If it's been less than a day since this entry has been accessed, the score
|
||||
// does not change. Otherwise, the score increases by 1.
|
||||
// The default score is 0. The maximum score is the maximum value that can
|
||||
// be represented by an unsigned 32 bit integer.
|
||||
// This is to handle evictions from our tables, which in turn is to prevent
|
||||
// unbounded resource use.
|
||||
bool
|
||||
DataStorage::Entry::UpdateScore()
|
||||
{
|
||||
|
||||
int32_t nowInDays = (int32_t)(PR_Now() / sOneDayInMicroseconds);
|
||||
int32_t daysSinceAccessed = (nowInDays - mLastAccessed);
|
||||
|
||||
// Update the last accessed time.
|
||||
mLastAccessed = nowInDays;
|
||||
|
||||
// If it's been less than a day since we've been accessed,
|
||||
// the score isn't updated.
|
||||
if (daysSinceAccessed < 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Otherwise, increment the score (but don't overflow).
|
||||
if (mScore < sMaxScore) {
|
||||
mScore++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
202
security/manager/ssl/DataStorage.h
Normal file
202
security/manager/ssl/DataStorage.h
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_DataStorage_h
|
||||
#define mozilla_DataStorage_h
|
||||
|
||||
#include "mozilla/Monitor.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsDataHashtable.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsIThread.h"
|
||||
#include "nsITimer.h"
|
||||
#include "nsRefPtrHashtable.h"
|
||||
#include "nsString.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace dom {
|
||||
class DataStorageItem;
|
||||
}
|
||||
|
||||
/**
|
||||
* DataStorage is a threadsafe, generic, narrow string-based hash map that
|
||||
* persists data on disk and additionally handles temporary and private data.
|
||||
* However, if used in a context where there is no profile directory, data
|
||||
* will not be persisted.
|
||||
*
|
||||
* Its lifecycle is as follows:
|
||||
* - Allocate with a filename (this is or will eventually be a file in the
|
||||
* profile directory, if the profile exists).
|
||||
* - Call Init() from the main thread. This spins off an asynchronous read
|
||||
* of the backing file.
|
||||
* - Eventually observers of the topic "data-storage-ready" will be notified
|
||||
* with the backing filename as the data in the notification when this
|
||||
* has completed.
|
||||
* - Should the profile directory not be available, (e.g. in xpcshell),
|
||||
* DataStorage will not initially read any persistent data. The
|
||||
* "data-storage-ready" event will still be emitted. This follows semantics
|
||||
* similar to the permission manager and allows tests that test
|
||||
* unrelated components to proceed without a profile.
|
||||
* - When any persistent data changes, a timer is initialized that will
|
||||
* eventually asynchronously write all persistent data to the backing file.
|
||||
* When this happens, observers will be notified with the topic
|
||||
* "data-storage-written" and the backing filename as the data.
|
||||
* It is possible to receive a "data-storage-written" event while there exist
|
||||
* pending persistent data changes. However, those changes will cause the
|
||||
* timer to be reinitialized and another "data-storage-written" event will
|
||||
* be sent.
|
||||
* - When DataStorage observes the topic "profile-before-change" in
|
||||
* anticipation of shutdown, all persistent data is synchronously written to
|
||||
* the backing file. The worker thread responsible for these writes is then
|
||||
* disabled to prevent further writes to that file (the delayed-write timer
|
||||
* is cancelled when this happens).
|
||||
* - For testing purposes, the preference "test.datastorage.write_timer_ms" can
|
||||
* be set to cause the asynchronous writing of data to happen more quickly.
|
||||
* - To prevent unbounded memory and disk use, the number of entries in each
|
||||
* table is limited to 1024. Evictions are handled in by a modified LRU scheme
|
||||
* (see implementation comments).
|
||||
* - NB: Instances of DataStorage have long lifetimes because they are strong
|
||||
* observers of events and won't go away until the observer service does.
|
||||
*
|
||||
* For each key/value:
|
||||
* - The key must be a non-empty string containing no instances of '\t' or '\n'
|
||||
* (this is a limitation of how the data is stored and will be addressed in
|
||||
* the future).
|
||||
* - The key must have a length no more than 256.
|
||||
* - The value must not contain '\n' and must have a length no more than 1024.
|
||||
* (the length limits are to prevent unbounded disk and memory usage)
|
||||
*/
|
||||
|
||||
/**
|
||||
* Data that is DataStorage_Persistent is saved on disk. DataStorage_Temporary
|
||||
* and DataStorage_Private are not saved. DataStorage_Private is meant to
|
||||
* only be set and accessed from private contexts. It will be cleared upon
|
||||
* observing the event "last-pb-context-exited".
|
||||
*/
|
||||
enum DataStorageType {
|
||||
DataStorage_Persistent,
|
||||
DataStorage_Temporary,
|
||||
DataStorage_Private
|
||||
};
|
||||
|
||||
class DataStorage : public nsIObserver
|
||||
{
|
||||
typedef dom::DataStorageItem DataStorageItem;
|
||||
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIOBSERVER
|
||||
|
||||
// If there is a profile directory, there is or will eventually be a file
|
||||
// by the name specified by aFilename there.
|
||||
static already_AddRefed<DataStorage> Get(const nsString& aFilename);
|
||||
static already_AddRefed<DataStorage> GetIfExists(const nsString& aFilename);
|
||||
|
||||
// Initializes the DataStorage. Must be called before using.
|
||||
// aDataWillPersist returns whether or not data can be persistently saved.
|
||||
nsresult Init(/*out*/bool& aDataWillPersist);
|
||||
// Given a key and a type of data, returns a value. Returns an empty string if
|
||||
// the key is not present for that type of data. If Get is called before the
|
||||
// "data-storage-ready" event is observed, it will block. NB: It is not
|
||||
// currently possible to differentiate between missing data and data that is
|
||||
// the empty string.
|
||||
nsCString Get(const nsCString& aKey, DataStorageType aType);
|
||||
// Give a key, value, and type of data, adds an entry as appropriate.
|
||||
// Updates existing entries.
|
||||
nsresult Put(const nsCString& aKey, const nsCString& aValue,
|
||||
DataStorageType aType);
|
||||
// Given a key and type of data, removes an entry if present.
|
||||
void Remove(const nsCString& aKey, DataStorageType aType);
|
||||
// Removes all entries of all types of data.
|
||||
nsresult Clear();
|
||||
|
||||
// Read all of the data items.
|
||||
void GetAll(InfallibleTArray<DataStorageItem>* aItems);
|
||||
|
||||
private:
|
||||
explicit DataStorage(const nsString& aFilename);
|
||||
virtual ~DataStorage();
|
||||
|
||||
class Writer;
|
||||
class Reader;
|
||||
|
||||
class Entry
|
||||
{
|
||||
public:
|
||||
Entry();
|
||||
bool UpdateScore();
|
||||
|
||||
uint32_t mScore;
|
||||
int32_t mLastAccessed; // the last accessed time in days since the epoch
|
||||
nsCString mValue;
|
||||
};
|
||||
|
||||
// Utility class for scanning tables for an entry to evict.
|
||||
class KeyAndEntry
|
||||
{
|
||||
public:
|
||||
nsCString mKey;
|
||||
Entry mEntry;
|
||||
};
|
||||
|
||||
typedef nsDataHashtable<nsCStringHashKey, Entry> DataStorageTable;
|
||||
typedef nsRefPtrHashtable<nsStringHashKey, DataStorage> DataStorages;
|
||||
|
||||
void WaitForReady();
|
||||
nsresult AsyncWriteData(const MutexAutoLock& aProofOfLock);
|
||||
nsresult AsyncReadData(bool& aHaveProfileDir,
|
||||
const MutexAutoLock& aProofOfLock);
|
||||
nsresult AsyncSetTimer(const MutexAutoLock& aProofOfLock);
|
||||
nsresult DispatchShutdownTimer(const MutexAutoLock& aProofOfLock);
|
||||
|
||||
static nsresult ValidateKeyAndValue(const nsCString& aKey,
|
||||
const nsCString& aValue);
|
||||
static void TimerCallback(nsITimer* aTimer, void* aClosure);
|
||||
void SetTimer();
|
||||
void ShutdownTimer();
|
||||
void NotifyObservers(const char* aTopic);
|
||||
|
||||
bool GetInternal(const nsCString& aKey, Entry* aEntry, DataStorageType aType,
|
||||
const MutexAutoLock& aProofOfLock);
|
||||
nsresult PutInternal(const nsCString& aKey, Entry& aEntry,
|
||||
DataStorageType aType,
|
||||
const MutexAutoLock& aProofOfLock);
|
||||
void MaybeEvictOneEntry(DataStorageType aType,
|
||||
const MutexAutoLock& aProofOfLock);
|
||||
DataStorageTable& GetTableForType(DataStorageType aType,
|
||||
const MutexAutoLock& aProofOfLock);
|
||||
|
||||
void ReadAllFromTable(DataStorageType aType,
|
||||
InfallibleTArray<DataStorageItem>* aItems,
|
||||
const MutexAutoLock& aProofOfLock);
|
||||
|
||||
Mutex mMutex; // This mutex protects access to the following members:
|
||||
DataStorageTable mPersistentDataTable;
|
||||
DataStorageTable mTemporaryDataTable;
|
||||
DataStorageTable mPrivateDataTable;
|
||||
nsCOMPtr<nsIThread> mWorkerThread;
|
||||
nsCOMPtr<nsIFile> mBackingFile;
|
||||
nsCOMPtr<nsITimer> mTimer; // All uses after init must be on the worker thread
|
||||
uint32_t mTimerDelay; // in milliseconds
|
||||
bool mPendingWrite; // true if a write is needed but hasn't been dispatched
|
||||
bool mShuttingDown;
|
||||
bool mInitCalled; // Indicates that Init() has been called.
|
||||
// (End list of members protected by mMutex)
|
||||
|
||||
Monitor mReadyMonitor; // Do not acquire this at the same time as mMutex.
|
||||
bool mReady; // Indicates that saved data has been read and Get can proceed.
|
||||
|
||||
const nsString mFilename;
|
||||
|
||||
static StaticAutoPtr<DataStorages> sDataStorages;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_DataStorage_h
|
||||
21
security/manager/ssl/DataStorageIPCUtils.h
Normal file
21
security/manager/ssl/DataStorageIPCUtils.h
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef ipc_DataStorageIPCUtils_h
|
||||
#define ipc_DataStorageIPCUtils_h
|
||||
|
||||
#include "ipc/IPCMessageUtils.h"
|
||||
#include "mozilla/DataStorage.h"
|
||||
|
||||
namespace IPC {
|
||||
template<>
|
||||
struct ParamTraits<mozilla::DataStorageType> :
|
||||
public ContiguousEnumSerializer<mozilla::DataStorageType,
|
||||
mozilla::DataStorage_Persistent,
|
||||
mozilla::DataStorageType(mozilla::DataStorage_Private + 1)> {};
|
||||
} // namespace IPC
|
||||
|
||||
#endif // mozilla_DataStorageIPCUtils_hh
|
||||
501
security/manager/ssl/LocalCertService.cpp
Normal file
501
security/manager/ssl/LocalCertService.cpp
Normal file
|
|
@ -0,0 +1,501 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "LocalCertService.h"
|
||||
|
||||
#include "CryptoTask.h"
|
||||
#include "ScopedNSSTypes.h"
|
||||
#include "cert.h"
|
||||
#include "mozilla/Casting.h"
|
||||
#include "mozilla/ModuleUtils.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "nsIPK11Token.h"
|
||||
#include "nsIPK11TokenDB.h"
|
||||
#include "nsIX509Cert.h"
|
||||
#include "nsIX509CertDB.h"
|
||||
#include "nsIX509CertValidity.h"
|
||||
#include "nsLiteralString.h"
|
||||
#include "nsProxyRelease.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsString.h"
|
||||
#include "pk11pub.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class LocalCertTask : public CryptoTask
|
||||
{
|
||||
protected:
|
||||
explicit LocalCertTask(const nsACString& aNickname)
|
||||
: mNickname(aNickname)
|
||||
{
|
||||
}
|
||||
|
||||
nsresult RemoveExisting()
|
||||
{
|
||||
// Search for any existing certs with this name and remove them
|
||||
nsresult rv;
|
||||
|
||||
for (;;) {
|
||||
UniqueCERTCertificate cert(
|
||||
PK11_FindCertFromNickname(mNickname.get(), nullptr));
|
||||
if (!cert) {
|
||||
return NS_OK; // All done
|
||||
}
|
||||
|
||||
// Found a cert, check if generated by this service
|
||||
if (!cert->isRoot) {
|
||||
return NS_ERROR_UNEXPECTED; // Should be self-signed
|
||||
}
|
||||
|
||||
NS_NAMED_LITERAL_CSTRING(commonNamePrefix, "CN=");
|
||||
nsAutoCString subjectNameFromNickname(commonNamePrefix + mNickname);
|
||||
if (!subjectNameFromNickname.Equals(cert->subjectName)) {
|
||||
return NS_ERROR_UNEXPECTED; // Subject should match nickname
|
||||
}
|
||||
if (!subjectNameFromNickname.Equals(cert->issuerName)) {
|
||||
return NS_ERROR_UNEXPECTED; // Issuer should match nickname
|
||||
}
|
||||
|
||||
rv = MapSECStatus(PK11_DeleteTokenCertAndKey(cert.get(), nullptr));
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv; // Some error, abort the loop
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsCString mNickname;
|
||||
};
|
||||
|
||||
class LocalCertGetTask final : public LocalCertTask
|
||||
{
|
||||
public:
|
||||
LocalCertGetTask(const nsACString& aNickname,
|
||||
nsILocalCertGetCallback* aCallback)
|
||||
: LocalCertTask(aNickname)
|
||||
, mCallback(new nsMainThreadPtrHolder<nsILocalCertGetCallback>(aCallback))
|
||||
, mCert(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
virtual nsresult CalculateResult() override
|
||||
{
|
||||
// Try to lookup an existing cert in the DB
|
||||
nsresult rv = GetFromDB();
|
||||
// Make a new one if getting fails
|
||||
if (NS_FAILED(rv)) {
|
||||
rv = Generate();
|
||||
}
|
||||
// If generation fails, we're out of luck
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Validate cert, make a new one if it fails
|
||||
rv = Validate();
|
||||
if (NS_FAILED(rv)) {
|
||||
rv = Generate();
|
||||
}
|
||||
// If generation fails, we're out of luck
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult Generate()
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
// Get the key slot for generation later
|
||||
UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
|
||||
if (!slot) {
|
||||
return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
|
||||
}
|
||||
|
||||
// Remove existing certs with this name (if any)
|
||||
rv = RemoveExisting();
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Generate a new cert
|
||||
NS_NAMED_LITERAL_CSTRING(commonNamePrefix, "CN=");
|
||||
nsAutoCString subjectNameStr(commonNamePrefix + mNickname);
|
||||
UniqueCERTName subjectName(CERT_AsciiToName(subjectNameStr.get()));
|
||||
if (!subjectName) {
|
||||
return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
|
||||
}
|
||||
|
||||
// Use the well-known NIST P-256 curve
|
||||
SECOidData* curveOidData = SECOID_FindOIDByTag(SEC_OID_SECG_EC_SECP256R1);
|
||||
if (!curveOidData) {
|
||||
return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
|
||||
}
|
||||
|
||||
// Get key params from the curve
|
||||
ScopedAutoSECItem keyParams(2 + curveOidData->oid.len);
|
||||
keyParams.data[0] = SEC_ASN1_OBJECT_ID;
|
||||
keyParams.data[1] = curveOidData->oid.len;
|
||||
memcpy(keyParams.data + 2, curveOidData->oid.data, curveOidData->oid.len);
|
||||
|
||||
// Generate cert key pair
|
||||
SECKEYPublicKey* tempPublicKey;
|
||||
UniqueSECKEYPrivateKey privateKey(
|
||||
PK11_GenerateKeyPair(slot.get(), CKM_EC_KEY_PAIR_GEN, &keyParams,
|
||||
&tempPublicKey, true /* token */,
|
||||
true /* sensitive */, nullptr));
|
||||
UniqueSECKEYPublicKey publicKey(tempPublicKey);
|
||||
tempPublicKey = nullptr;
|
||||
if (!privateKey || !publicKey) {
|
||||
return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
|
||||
}
|
||||
|
||||
// Create subject public key info and cert request
|
||||
UniqueCERTSubjectPublicKeyInfo spki(
|
||||
SECKEY_CreateSubjectPublicKeyInfo(publicKey.get()));
|
||||
if (!spki) {
|
||||
return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
|
||||
}
|
||||
UniqueCERTCertificateRequest certRequest(
|
||||
CERT_CreateCertificateRequest(subjectName.get(), spki.get(), nullptr));
|
||||
if (!certRequest) {
|
||||
return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
|
||||
}
|
||||
|
||||
// Valid from one day before to 1 year after
|
||||
static const PRTime oneDay = PRTime(PR_USEC_PER_SEC)
|
||||
* PRTime(60) // sec
|
||||
* PRTime(60) // min
|
||||
* PRTime(24); // hours
|
||||
|
||||
PRTime now = PR_Now();
|
||||
PRTime notBefore = now - oneDay;
|
||||
PRTime notAfter = now + (PRTime(365) * oneDay);
|
||||
UniqueCERTValidity validity(CERT_CreateValidity(notBefore, notAfter));
|
||||
if (!validity) {
|
||||
return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
|
||||
}
|
||||
|
||||
// Generate random serial
|
||||
unsigned long serial;
|
||||
// This serial in principle could collide, but it's unlikely
|
||||
rv = MapSECStatus(PK11_GenerateRandomOnSlot(
|
||||
slot.get(), BitwiseCast<unsigned char*, unsigned long*>(&serial),
|
||||
sizeof(serial)));
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Create the cert from these pieces
|
||||
UniqueCERTCertificate cert(
|
||||
CERT_CreateCertificate(serial, subjectName.get(), validity.get(),
|
||||
certRequest.get()));
|
||||
if (!cert) {
|
||||
return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
|
||||
}
|
||||
|
||||
// Update the cert version to X509v3
|
||||
if (!cert->version.data) {
|
||||
return NS_ERROR_INVALID_POINTER;
|
||||
}
|
||||
*(cert->version.data) = SEC_CERTIFICATE_VERSION_3;
|
||||
cert->version.len = 1;
|
||||
|
||||
// Set cert signature algorithm
|
||||
PLArenaPool* arena = cert->arena;
|
||||
if (!arena) {
|
||||
return NS_ERROR_INVALID_POINTER;
|
||||
}
|
||||
rv = MapSECStatus(
|
||||
SECOID_SetAlgorithmID(arena, &cert->signature,
|
||||
SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE, 0));
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Encode and self-sign the cert
|
||||
UniqueSECItem certDER(
|
||||
SEC_ASN1EncodeItem(nullptr, nullptr, cert.get(),
|
||||
SEC_ASN1_GET(CERT_CertificateTemplate)));
|
||||
if (!certDER) {
|
||||
return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
|
||||
}
|
||||
rv = MapSECStatus(
|
||||
SEC_DerSignData(arena, &cert->derCert, certDER->data, certDER->len,
|
||||
privateKey.get(),
|
||||
SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE));
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Create a CERTCertificate from the signed data
|
||||
UniqueCERTCertificate certFromDER(
|
||||
CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &cert->derCert, nullptr,
|
||||
true /* perm */, true /* copyDER */));
|
||||
if (!certFromDER) {
|
||||
return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
|
||||
}
|
||||
|
||||
// Save the cert in the DB
|
||||
rv = MapSECStatus(PK11_ImportCert(slot.get(), certFromDER.get(),
|
||||
CK_INVALID_HANDLE, mNickname.get(),
|
||||
false /* unused */));
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// We should now have cert in the DB, read it back in nsIX509Cert form
|
||||
return GetFromDB();
|
||||
}
|
||||
|
||||
nsresult GetFromDB()
|
||||
{
|
||||
nsCOMPtr<nsIX509CertDB> certDB = do_GetService(NS_X509CERTDB_CONTRACTID);
|
||||
if (!certDB) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIX509Cert> certFromDB;
|
||||
nsresult rv;
|
||||
rv = certDB->FindCertByNickname(NS_ConvertASCIItoUTF16(mNickname),
|
||||
getter_AddRefs(certFromDB));
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
mCert = certFromDB;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult Validate()
|
||||
{
|
||||
// Verify cert is self-signed
|
||||
bool selfSigned;
|
||||
nsresult rv = mCert->GetIsSelfSigned(&selfSigned);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
if (!selfSigned) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// Check that subject and issuer match nickname
|
||||
nsXPIDLString subjectName;
|
||||
nsXPIDLString issuerName;
|
||||
mCert->GetSubjectName(subjectName);
|
||||
mCert->GetIssuerName(issuerName);
|
||||
if (!subjectName.Equals(issuerName)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
NS_NAMED_LITERAL_STRING(commonNamePrefix, "CN=");
|
||||
nsAutoString subjectNameFromNickname(
|
||||
commonNamePrefix + NS_ConvertASCIItoUTF16(mNickname));
|
||||
if (!subjectName.Equals(subjectNameFromNickname)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIX509CertValidity> validity;
|
||||
mCert->GetValidity(getter_AddRefs(validity));
|
||||
|
||||
PRTime notBefore, notAfter;
|
||||
validity->GetNotBefore(¬Before);
|
||||
validity->GetNotAfter(¬After);
|
||||
|
||||
// Ensure cert will last at least one more day
|
||||
static const PRTime oneDay = PRTime(PR_USEC_PER_SEC)
|
||||
* PRTime(60) // sec
|
||||
* PRTime(60) // min
|
||||
* PRTime(24); // hours
|
||||
PRTime now = PR_Now();
|
||||
if (notBefore > now ||
|
||||
notAfter < (now - oneDay)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
virtual void ReleaseNSSResources() override {}
|
||||
|
||||
virtual void CallCallback(nsresult rv) override
|
||||
{
|
||||
(void) mCallback->HandleCert(mCert, rv);
|
||||
}
|
||||
|
||||
nsMainThreadPtrHandle<nsILocalCertGetCallback> mCallback;
|
||||
nsCOMPtr<nsIX509Cert> mCert; // out
|
||||
};
|
||||
|
||||
class LocalCertRemoveTask final : public LocalCertTask
|
||||
{
|
||||
public:
|
||||
LocalCertRemoveTask(const nsACString& aNickname,
|
||||
nsILocalCertCallback* aCallback)
|
||||
: LocalCertTask(aNickname)
|
||||
, mCallback(new nsMainThreadPtrHolder<nsILocalCertCallback>(aCallback))
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
virtual nsresult CalculateResult() override
|
||||
{
|
||||
return RemoveExisting();
|
||||
}
|
||||
|
||||
virtual void ReleaseNSSResources() override {}
|
||||
|
||||
virtual void CallCallback(nsresult rv) override
|
||||
{
|
||||
(void) mCallback->HandleResult(rv);
|
||||
}
|
||||
|
||||
nsMainThreadPtrHandle<nsILocalCertCallback> mCallback;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(LocalCertService, nsILocalCertService)
|
||||
|
||||
LocalCertService::LocalCertService()
|
||||
{
|
||||
}
|
||||
|
||||
LocalCertService::~LocalCertService()
|
||||
{
|
||||
}
|
||||
|
||||
nsresult
|
||||
LocalCertService::LoginToKeySlot()
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
// Get access to key slot
|
||||
UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
|
||||
if (!slot) {
|
||||
return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
|
||||
}
|
||||
|
||||
// If no user password yet, set it an empty one
|
||||
if (PK11_NeedUserInit(slot.get())) {
|
||||
rv = MapSECStatus(PK11_InitPin(slot.get(), "", ""));
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
// If user has a password set, prompt to login
|
||||
if (PK11_NeedLogin(slot.get()) && !PK11_IsLoggedIn(slot.get(), nullptr)) {
|
||||
// Switching to XPCOM to get the UI prompt that PSM owns
|
||||
nsCOMPtr<nsIPK11TokenDB> tokenDB =
|
||||
do_GetService(NS_PK11TOKENDB_CONTRACTID);
|
||||
if (!tokenDB) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
nsCOMPtr<nsIPK11Token> keyToken;
|
||||
tokenDB->GetInternalKeyToken(getter_AddRefs(keyToken));
|
||||
if (!keyToken) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
// Prompt the user to login
|
||||
return keyToken->Login(false /* force */);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
LocalCertService::GetOrCreateCert(const nsACString& aNickname,
|
||||
nsILocalCertGetCallback* aCallback)
|
||||
{
|
||||
if (NS_WARN_IF(aNickname.IsEmpty())) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
if (NS_WARN_IF(!aCallback)) {
|
||||
return NS_ERROR_INVALID_POINTER;
|
||||
}
|
||||
|
||||
// Before sending off the task, login to key slot if needed
|
||||
nsresult rv = LoginToKeySlot();
|
||||
if (NS_FAILED(rv)) {
|
||||
aCallback->HandleCert(nullptr, rv);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
RefPtr<LocalCertGetTask> task(new LocalCertGetTask(aNickname, aCallback));
|
||||
return task->Dispatch("LocalCertGet");
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
LocalCertService::RemoveCert(const nsACString& aNickname,
|
||||
nsILocalCertCallback* aCallback)
|
||||
{
|
||||
if (NS_WARN_IF(aNickname.IsEmpty())) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
if (NS_WARN_IF(!aCallback)) {
|
||||
return NS_ERROR_INVALID_POINTER;
|
||||
}
|
||||
|
||||
// Before sending off the task, login to key slot if needed
|
||||
nsresult rv = LoginToKeySlot();
|
||||
if (NS_FAILED(rv)) {
|
||||
aCallback->HandleResult(rv);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
RefPtr<LocalCertRemoveTask> task(
|
||||
new LocalCertRemoveTask(aNickname, aCallback));
|
||||
return task->Dispatch("LocalCertRm");
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
LocalCertService::GetLoginPromptRequired(bool* aRequired)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
// Get access to key slot
|
||||
UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
|
||||
if (!slot) {
|
||||
return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
|
||||
}
|
||||
|
||||
// If no user password yet, set it an empty one
|
||||
if (PK11_NeedUserInit(slot.get())) {
|
||||
rv = MapSECStatus(PK11_InitPin(slot.get(), "", ""));
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
*aRequired = PK11_NeedLogin(slot.get()) &&
|
||||
!PK11_IsLoggedIn(slot.get(), nullptr);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
#define LOCALCERTSERVICE_CID \
|
||||
{ 0x47402be2, 0xe653, 0x45d0, \
|
||||
{ 0x8d, 0xaa, 0x9f, 0x0d, 0xce, 0x0a, 0xc1, 0x48 } }
|
||||
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR(LocalCertService)
|
||||
|
||||
NS_DEFINE_NAMED_CID(LOCALCERTSERVICE_CID);
|
||||
|
||||
static const Module::CIDEntry kLocalCertServiceCIDs[] = {
|
||||
{ &kLOCALCERTSERVICE_CID, false, nullptr, LocalCertServiceConstructor },
|
||||
{ nullptr }
|
||||
};
|
||||
|
||||
static const Module::ContractIDEntry kLocalCertServiceContracts[] = {
|
||||
{ LOCALCERTSERVICE_CONTRACTID, &kLOCALCERTSERVICE_CID },
|
||||
{ nullptr }
|
||||
};
|
||||
|
||||
static const Module kLocalCertServiceModule = {
|
||||
Module::kVersion,
|
||||
kLocalCertServiceCIDs,
|
||||
kLocalCertServiceContracts
|
||||
};
|
||||
|
||||
NSMODULE_DEFN(LocalCertServiceModule) = &kLocalCertServiceModule;
|
||||
|
||||
} // namespace mozilla
|
||||
27
security/manager/ssl/LocalCertService.h
Normal file
27
security/manager/ssl/LocalCertService.h
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef LocalCertService_h
|
||||
#define LocalCertService_h
|
||||
|
||||
#include "nsILocalCertService.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class LocalCertService final : public nsILocalCertService
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSILOCALCERTSERVICE
|
||||
|
||||
LocalCertService();
|
||||
|
||||
private:
|
||||
nsresult LoginToKeySlot();
|
||||
~LocalCertService();
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // LocalCertService_h
|
||||
206
security/manager/ssl/NSSErrorsService.cpp
Normal file
206
security/manager/ssl/NSSErrorsService.cpp
Normal file
|
|
@ -0,0 +1,206 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "NSSErrorsService.h"
|
||||
|
||||
#include "nsNSSComponent.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "pkix/pkixnss.h"
|
||||
#include "secerr.h"
|
||||
#include "sslerr.h"
|
||||
|
||||
#define PIPNSS_STRBUNDLE_URL "chrome://pipnss/locale/pipnss.properties"
|
||||
#define NSSERR_STRBUNDLE_URL "chrome://pipnss/locale/nsserrors.properties"
|
||||
|
||||
namespace mozilla {
|
||||
namespace psm {
|
||||
|
||||
static_assert(mozilla::pkix::ERROR_BASE ==
|
||||
nsINSSErrorsService::MOZILLA_PKIX_ERROR_BASE,
|
||||
"MOZILLA_PKIX_ERROR_BASE and "
|
||||
"nsINSSErrorsService::MOZILLA_PKIX_ERROR_BASE do not match.");
|
||||
static_assert(mozilla::pkix::ERROR_LIMIT ==
|
||||
nsINSSErrorsService::MOZILLA_PKIX_ERROR_LIMIT,
|
||||
"MOZILLA_PKIX_ERROR_LIMIT and "
|
||||
"nsINSSErrorsService::MOZILLA_PKIX_ERROR_LIMIT do not match.");
|
||||
|
||||
static bool
|
||||
IsPSMError(PRErrorCode error)
|
||||
{
|
||||
return (error >= mozilla::pkix::ERROR_BASE &&
|
||||
error < mozilla::pkix::ERROR_LIMIT);
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(NSSErrorsService, nsINSSErrorsService)
|
||||
|
||||
NSSErrorsService::~NSSErrorsService() { }
|
||||
|
||||
nsresult
|
||||
NSSErrorsService::Init()
|
||||
{
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIStringBundleService> bundleService(do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv));
|
||||
if (NS_FAILED(rv) || !bundleService)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
bundleService->CreateBundle(PIPNSS_STRBUNDLE_URL,
|
||||
getter_AddRefs(mPIPNSSBundle));
|
||||
if (!mPIPNSSBundle)
|
||||
rv = NS_ERROR_FAILURE;
|
||||
|
||||
bundleService->CreateBundle(NSSERR_STRBUNDLE_URL,
|
||||
getter_AddRefs(mNSSErrorsBundle));
|
||||
if (!mNSSErrorsBundle)
|
||||
rv = NS_ERROR_FAILURE;
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
#define EXPECTED_SEC_ERROR_BASE (-0x2000)
|
||||
#define EXPECTED_SSL_ERROR_BASE (-0x3000)
|
||||
|
||||
#if SEC_ERROR_BASE != EXPECTED_SEC_ERROR_BASE || SSL_ERROR_BASE != EXPECTED_SSL_ERROR_BASE
|
||||
#error "Unexpected change of error code numbers in lib NSS, please adjust the mapping code"
|
||||
/*
|
||||
* Please ensure the NSS error codes are mapped into the positive range 0x1000 to 0xf000
|
||||
* Search for NS_ERROR_MODULE_SECURITY to ensure there are no conflicts.
|
||||
* The current code also assumes that NSS library error codes are negative.
|
||||
*/
|
||||
#endif
|
||||
|
||||
bool
|
||||
IsNSSErrorCode(PRErrorCode code)
|
||||
{
|
||||
return IS_SEC_ERROR(code) || IS_SSL_ERROR(code) || IsPSMError(code);
|
||||
}
|
||||
|
||||
nsresult
|
||||
GetXPCOMFromNSSError(PRErrorCode code)
|
||||
{
|
||||
if (!code) {
|
||||
MOZ_CRASH("Function failed without calling PR_GetError");
|
||||
}
|
||||
|
||||
// The error codes within each module must be a 16 bit value.
|
||||
// For simplicity we use the positive value of the NSS code.
|
||||
return (nsresult)NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_SECURITY,
|
||||
-1 * code);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NSSErrorsService::IsNSSErrorCode(int32_t aNSPRCode, bool *_retval)
|
||||
{
|
||||
if (!_retval) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
*_retval = mozilla::psm::IsNSSErrorCode(aNSPRCode);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NSSErrorsService::GetXPCOMFromNSSError(int32_t aNSPRCode, nsresult *aXPCOMErrorCode)
|
||||
{
|
||||
if (!aXPCOMErrorCode) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
if (!mozilla::psm::IsNSSErrorCode(aNSPRCode)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
*aXPCOMErrorCode = mozilla::psm::GetXPCOMFromNSSError(aNSPRCode);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NSSErrorsService::GetErrorClass(nsresult aXPCOMErrorCode, uint32_t *aErrorClass)
|
||||
{
|
||||
NS_ENSURE_ARG(aErrorClass);
|
||||
|
||||
if (NS_ERROR_GET_MODULE(aXPCOMErrorCode) != NS_ERROR_MODULE_SECURITY ||
|
||||
NS_ERROR_GET_SEVERITY(aXPCOMErrorCode) != NS_ERROR_SEVERITY_ERROR) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
int32_t aNSPRCode = -1 * NS_ERROR_GET_CODE(aXPCOMErrorCode);
|
||||
|
||||
if (!mozilla::psm::IsNSSErrorCode(aNSPRCode)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (mozilla::psm::ErrorIsOverridable(aNSPRCode)) {
|
||||
*aErrorClass = ERROR_CLASS_BAD_CERT;
|
||||
} else {
|
||||
*aErrorClass = ERROR_CLASS_SSL_PROTOCOL;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
ErrorIsOverridable(PRErrorCode code)
|
||||
{
|
||||
switch (code)
|
||||
{
|
||||
// Overridable errors.
|
||||
case mozilla::pkix::MOZILLA_PKIX_ERROR_CA_CERT_USED_AS_END_ENTITY:
|
||||
case mozilla::pkix::MOZILLA_PKIX_ERROR_EMPTY_ISSUER_NAME:
|
||||
case mozilla::pkix::MOZILLA_PKIX_ERROR_INADEQUATE_KEY_SIZE:
|
||||
case mozilla::pkix::MOZILLA_PKIX_ERROR_NOT_YET_VALID_CERTIFICATE:
|
||||
case mozilla::pkix::MOZILLA_PKIX_ERROR_NOT_YET_VALID_ISSUER_CERTIFICATE:
|
||||
case mozilla::pkix::MOZILLA_PKIX_ERROR_V1_CERT_USED_AS_CA:
|
||||
case SEC_ERROR_CA_CERT_INVALID:
|
||||
case SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED:
|
||||
case SEC_ERROR_EXPIRED_CERTIFICATE:
|
||||
case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE:
|
||||
case SEC_ERROR_INVALID_TIME:
|
||||
case SEC_ERROR_UNKNOWN_ISSUER:
|
||||
case SSL_ERROR_BAD_CERT_DOMAIN:
|
||||
return true;
|
||||
// Non-overridable errors.
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NSSErrorsService::GetErrorMessage(nsresult aXPCOMErrorCode, nsAString &aErrorMessage)
|
||||
{
|
||||
if (NS_ERROR_GET_MODULE(aXPCOMErrorCode) != NS_ERROR_MODULE_SECURITY ||
|
||||
NS_ERROR_GET_SEVERITY(aXPCOMErrorCode) != NS_ERROR_SEVERITY_ERROR) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
int32_t aNSPRCode = -1 * NS_ERROR_GET_CODE(aXPCOMErrorCode);
|
||||
|
||||
if (!mozilla::psm::IsNSSErrorCode(aNSPRCode)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIStringBundle> theBundle = mPIPNSSBundle;
|
||||
const char *id_str = nsNSSErrors::getOverrideErrorStringName(aNSPRCode);
|
||||
|
||||
if (!id_str) {
|
||||
id_str = nsNSSErrors::getDefaultErrorStringName(aNSPRCode);
|
||||
theBundle = mNSSErrorsBundle;
|
||||
}
|
||||
|
||||
if (!id_str || !theBundle) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsAutoString msg;
|
||||
nsresult rv =
|
||||
theBundle->GetStringFromName(NS_ConvertASCIItoUTF16(id_str).get(),
|
||||
getter_Copies(msg));
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
aErrorMessage = msg;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
} // namespace psm
|
||||
} // namespace mozilla
|
||||
49
security/manager/ssl/NSSErrorsService.h
Normal file
49
security/manager/ssl/NSSErrorsService.h
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef NSSErrorsService_h
|
||||
#define NSSErrorsService_h
|
||||
|
||||
#include "nsINSSErrorsService.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIStringBundle.h"
|
||||
#include "prerror.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace psm {
|
||||
|
||||
class NSSErrorsService final : public nsINSSErrorsService
|
||||
{
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSINSSERRORSSERVICE
|
||||
|
||||
public:
|
||||
nsresult Init();
|
||||
|
||||
private:
|
||||
// For XPCOM implementations that are not a base class for some other
|
||||
// class, it is good practice to make the destructor non-virtual and
|
||||
// private. Then the only way to delete the object is via Release.
|
||||
#ifdef _MSC_VER
|
||||
// C4265: Class has virtual members but destructor is not virtual
|
||||
__pragma(warning(disable:4265))
|
||||
#endif
|
||||
~NSSErrorsService();
|
||||
|
||||
nsCOMPtr<nsIStringBundle> mPIPNSSBundle;
|
||||
nsCOMPtr<nsIStringBundle> mNSSErrorsBundle;
|
||||
};
|
||||
|
||||
bool IsNSSErrorCode(PRErrorCode code);
|
||||
nsresult GetXPCOMFromNSSError(PRErrorCode code);
|
||||
bool ErrorIsOverridable(PRErrorCode code);
|
||||
|
||||
} // namespace psm
|
||||
} // namespace mozilla
|
||||
|
||||
#define NS_NSSERRORSSERVICE_CID \
|
||||
{ 0x9ef18451, 0xa157, 0x4d17, { 0x81, 0x32, 0x47, 0xaf, 0xef, 0x21, 0x36, 0x89 } }
|
||||
|
||||
#endif // NSSErrorsService_h
|
||||
28
security/manager/ssl/PPSMContentDownloader.ipdl
Normal file
28
security/manager/ssl/PPSMContentDownloader.ipdl
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
/* vim: set sw=2 sts=2 ts=2 et tw=80 ft=cpp: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
include protocol PContent;
|
||||
include protocol PChannelDiverter;
|
||||
|
||||
namespace mozilla {
|
||||
namespace psm {
|
||||
|
||||
protocol PPSMContentDownloader
|
||||
{
|
||||
manager PContent;
|
||||
|
||||
parent:
|
||||
async OnStartRequest(uint32_t contentLength);
|
||||
async OnDataAvailable(nsCString data, uint64_t offset, uint32_t count);
|
||||
async OnStopRequest(nsresult code);
|
||||
|
||||
async DivertToParentUsing(PChannelDiverter diverter);
|
||||
|
||||
child:
|
||||
async __delete__();
|
||||
};
|
||||
|
||||
} // namespace psm
|
||||
} // namespace mozilla
|
||||
454
security/manager/ssl/PSMContentListener.cpp
Normal file
454
security/manager/ssl/PSMContentListener.cpp
Normal file
|
|
@ -0,0 +1,454 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* vim: set sw=2 sts=2 ts=2 et tw=80:
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "PSMContentListener.h"
|
||||
|
||||
#include "nsIDivertableChannel.h"
|
||||
#include "nsIStreamListener.h"
|
||||
#include "nsIX509CertDB.h"
|
||||
#include "nsIXULAppInfo.h"
|
||||
|
||||
#include "mozilla/Casting.h"
|
||||
#include "mozilla/Services.h"
|
||||
#include "mozilla/Unused.h"
|
||||
|
||||
#include "mozilla/dom/ContentChild.h"
|
||||
#include "mozilla/net/ChannelDiverterParent.h"
|
||||
#include "mozilla/net/ChannelDiverterChild.h"
|
||||
|
||||
#include "nsCRT.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsIChannel.h"
|
||||
#include "nsIInputStream.h"
|
||||
#include "nsIURI.h"
|
||||
#include "nsNSSHelper.h"
|
||||
|
||||
#include "mozilla/Logging.h"
|
||||
|
||||
extern mozilla::LazyLogModule gPIPNSSLog;
|
||||
|
||||
namespace mozilla { namespace psm {
|
||||
|
||||
namespace {
|
||||
|
||||
const int32_t kDefaultCertAllocLength = 2048;
|
||||
|
||||
enum {
|
||||
UNKNOWN_TYPE = 0,
|
||||
X509_CA_CERT = 1,
|
||||
X509_USER_CERT = 2,
|
||||
X509_EMAIL_CERT = 3,
|
||||
X509_SERVER_CERT = 4
|
||||
};
|
||||
|
||||
/* other mime types that we should handle sometime:
|
||||
|
||||
application/x-pkcs7-mime
|
||||
application/pkcs7-signature
|
||||
application/pre-encrypted
|
||||
|
||||
*/
|
||||
|
||||
uint32_t
|
||||
getPSMContentType(const char* aContentType)
|
||||
{
|
||||
// Don't forget to update the registration of content listeners in nsNSSModule.cpp
|
||||
// for every supported content type.
|
||||
|
||||
if (!nsCRT::strcasecmp(aContentType, "application/x-x509-ca-cert"))
|
||||
return X509_CA_CERT;
|
||||
if (!nsCRT::strcasecmp(aContentType, "application/x-x509-server-cert"))
|
||||
return X509_SERVER_CERT;
|
||||
if (!nsCRT::strcasecmp(aContentType, "application/x-x509-user-cert"))
|
||||
return X509_USER_CERT;
|
||||
if (!nsCRT::strcasecmp(aContentType, "application/x-x509-email-cert"))
|
||||
return X509_EMAIL_CERT;
|
||||
|
||||
return UNKNOWN_TYPE;
|
||||
}
|
||||
|
||||
int64_t
|
||||
ComputeContentLength(nsIRequest* request)
|
||||
{
|
||||
nsCOMPtr<nsIChannel> channel(do_QueryInterface(request));
|
||||
if (!channel) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int64_t contentLength;
|
||||
nsresult rv = channel->GetContentLength(&contentLength);
|
||||
if (NS_FAILED(rv) || contentLength <= 0) {
|
||||
return kDefaultCertAllocLength;
|
||||
}
|
||||
|
||||
if (contentLength > INT32_MAX) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return contentLength;
|
||||
}
|
||||
|
||||
} // unnamed namespace
|
||||
|
||||
/* ------------------------
|
||||
* PSMContentStreamListener
|
||||
* ------------------------ */
|
||||
|
||||
PSMContentStreamListener::PSMContentStreamListener(uint32_t type)
|
||||
: mType(type)
|
||||
{
|
||||
}
|
||||
|
||||
PSMContentStreamListener::~PSMContentStreamListener()
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(PSMContentStreamListener, nsIStreamListener, nsIRequestObserver)
|
||||
|
||||
NS_IMETHODIMP
|
||||
PSMContentStreamListener::OnStartRequest(nsIRequest* request, nsISupports* context)
|
||||
{
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("CertDownloader::OnStartRequest\n"));
|
||||
|
||||
int64_t contentLength = ComputeContentLength(request);
|
||||
if (contentLength < 0) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
mByteData.SetCapacity(contentLength);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PSMContentStreamListener::OnDataAvailable(nsIRequest* request,
|
||||
nsISupports* context,
|
||||
nsIInputStream* aIStream,
|
||||
uint64_t aSourceOffset,
|
||||
uint32_t aLength)
|
||||
{
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("CertDownloader::OnDataAvailable\n"));
|
||||
|
||||
nsCString chunk;
|
||||
nsresult rv = NS_ReadInputStreamToString(aIStream, chunk, aLength);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
mByteData.Append(chunk);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PSMContentStreamListener::OnStopRequest(nsIRequest* request,
|
||||
nsISupports* context,
|
||||
nsresult aStatus)
|
||||
{
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("CertDownloader::OnStopRequest\n"));
|
||||
|
||||
// Because importing the cert can spin the event loop (via alerts), we can't
|
||||
// do it here. Do it off the event loop instead.
|
||||
nsCOMPtr<nsIRunnable> r =
|
||||
NewRunnableMethod(this, &PSMContentStreamListener::ImportCertificate);
|
||||
MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(r));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
PSMContentStreamListener::ImportCertificate()
|
||||
{
|
||||
nsCOMPtr<nsIX509CertDB> certdb;
|
||||
|
||||
nsCOMPtr<nsIInterfaceRequestor> ctx = new PipUIContext();
|
||||
|
||||
switch (mType) {
|
||||
case X509_CA_CERT:
|
||||
case X509_USER_CERT:
|
||||
case X509_EMAIL_CERT:
|
||||
certdb = do_GetService(NS_X509CERTDB_CONTRACTID);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!certdb) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (mType) {
|
||||
case X509_CA_CERT:
|
||||
certdb->ImportCertificates(BitwiseCast<uint8_t*, char*>(
|
||||
mByteData.BeginWriting()),
|
||||
mByteData.Length(), mType, ctx);
|
||||
break;
|
||||
|
||||
case X509_USER_CERT:
|
||||
certdb->ImportUserCertificate(BitwiseCast<uint8_t*, char*>(
|
||||
mByteData.BeginWriting()),
|
||||
mByteData.Length(), ctx);
|
||||
break;
|
||||
|
||||
case X509_EMAIL_CERT:
|
||||
certdb->ImportEmailCertificate(BitwiseCast<uint8_t*, char*>(
|
||||
mByteData.BeginWriting()),
|
||||
mByteData.Length(), ctx);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------
|
||||
* PSMContentDownloaderParent
|
||||
* ------------------------ */
|
||||
|
||||
PSMContentDownloaderParent::PSMContentDownloaderParent(uint32_t type)
|
||||
: PSMContentStreamListener(type)
|
||||
, mIPCOpen(true)
|
||||
{
|
||||
}
|
||||
|
||||
PSMContentDownloaderParent::~PSMContentDownloaderParent()
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
PSMContentDownloaderParent::RecvOnStartRequest(const uint32_t& contentLength)
|
||||
{
|
||||
mByteData.SetCapacity(contentLength);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
PSMContentDownloaderParent::RecvOnDataAvailable(const nsCString& data,
|
||||
const uint64_t& offset,
|
||||
const uint32_t& count)
|
||||
{
|
||||
mByteData.Append(data);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
PSMContentDownloaderParent::RecvOnStopRequest(const nsresult& code)
|
||||
{
|
||||
if (NS_SUCCEEDED(code)) {
|
||||
// See also PSMContentStreamListener::OnStopRequest. In this case, we don't
|
||||
// have to dispatch ImportCertificate off of an event because we don't have
|
||||
// to worry about Necko sending "clean up" events and destroying us if
|
||||
// ImportCertificate spins the event loop.
|
||||
ImportCertificate();
|
||||
}
|
||||
|
||||
if (mIPCOpen) {
|
||||
mozilla::Unused << Send__delete__(this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PSMContentDownloaderParent::OnStopRequest(nsIRequest* request, nsISupports* context, nsresult code)
|
||||
{
|
||||
nsresult rv = PSMContentStreamListener::OnStopRequest(request, context, code);
|
||||
|
||||
if (mIPCOpen) {
|
||||
mozilla::Unused << Send__delete__(this);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
bool
|
||||
PSMContentDownloaderParent::RecvDivertToParentUsing(mozilla::net::PChannelDiverterParent* diverter)
|
||||
{
|
||||
MOZ_ASSERT(diverter);
|
||||
auto p = static_cast<mozilla::net::ChannelDiverterParent*>(diverter);
|
||||
p->DivertTo(this);
|
||||
mozilla::Unused << p->Send__delete__(p);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
PSMContentDownloaderParent::ActorDestroy(ActorDestroyReason why)
|
||||
{
|
||||
mIPCOpen = false;
|
||||
}
|
||||
|
||||
/* ------------------------
|
||||
* PSMContentDownloaderChild
|
||||
* ------------------------ */
|
||||
|
||||
NS_IMPL_ISUPPORTS(PSMContentDownloaderChild, nsIStreamListener)
|
||||
|
||||
PSMContentDownloaderChild::PSMContentDownloaderChild()
|
||||
{
|
||||
}
|
||||
|
||||
PSMContentDownloaderChild::~PSMContentDownloaderChild()
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PSMContentDownloaderChild::OnStartRequest(nsIRequest* request, nsISupports* context)
|
||||
{
|
||||
nsCOMPtr<nsIDivertableChannel> divertable = do_QueryInterface(request);
|
||||
if (divertable) {
|
||||
mozilla::net::ChannelDiverterChild* diverter = nullptr;
|
||||
nsresult rv = divertable->DivertToParent(&diverter);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
MOZ_ASSERT(diverter);
|
||||
|
||||
return SendDivertToParentUsing(diverter) ? NS_OK : NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
int64_t contentLength = ComputeContentLength(request);
|
||||
if (contentLength < 0) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
mozilla::Unused << SendOnStartRequest(contentLength);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PSMContentDownloaderChild::OnDataAvailable(nsIRequest* request,
|
||||
nsISupports* context,
|
||||
nsIInputStream* aIStream,
|
||||
uint64_t aSourceOffset,
|
||||
uint32_t aLength)
|
||||
{
|
||||
nsCString chunk;
|
||||
nsresult rv = NS_ReadInputStreamToString(aIStream, chunk, aLength);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
mozilla::Unused << SendOnDataAvailable(chunk, aSourceOffset, aLength);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PSMContentDownloaderChild::OnStopRequest(nsIRequest* request,
|
||||
nsISupports* context,
|
||||
nsresult aStatus)
|
||||
{
|
||||
mozilla::Unused << SendOnStopRequest(aStatus);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* ------------------------
|
||||
* PSMContentListener
|
||||
* ------------------------ */
|
||||
|
||||
NS_IMPL_ISUPPORTS(PSMContentListener,
|
||||
nsIURIContentListener,
|
||||
nsISupportsWeakReference)
|
||||
|
||||
PSMContentListener::PSMContentListener()
|
||||
{
|
||||
mLoadCookie = nullptr;
|
||||
mParentContentListener = nullptr;
|
||||
}
|
||||
|
||||
PSMContentListener::~PSMContentListener()
|
||||
{
|
||||
}
|
||||
|
||||
nsresult
|
||||
PSMContentListener::init()
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PSMContentListener::OnStartURIOpen(nsIURI* aURI, bool* aAbortOpen)
|
||||
{
|
||||
//if we don't want to handle the URI, return true in
|
||||
//*aAbortOpen
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PSMContentListener::IsPreferred(const char* aContentType,
|
||||
char** aDesiredContentType,
|
||||
bool* aCanHandleContent)
|
||||
{
|
||||
return CanHandleContent(aContentType, true,
|
||||
aDesiredContentType, aCanHandleContent);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PSMContentListener::CanHandleContent(const char* aContentType,
|
||||
bool aIsContentPreferred,
|
||||
char** aDesiredContentType,
|
||||
bool* aCanHandleContent)
|
||||
{
|
||||
uint32_t type = getPSMContentType(aContentType);
|
||||
*aCanHandleContent = (type != UNKNOWN_TYPE);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PSMContentListener::DoContent(const nsACString& aContentType,
|
||||
bool aIsContentPreferred,
|
||||
nsIRequest* aRequest,
|
||||
nsIStreamListener** aContentHandler,
|
||||
bool* aAbortProcess)
|
||||
{
|
||||
uint32_t type;
|
||||
type = getPSMContentType(PromiseFlatCString(aContentType).get());
|
||||
if (gPIPNSSLog) {
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("PSMContentListener::DoContent\n"));
|
||||
}
|
||||
if (type != UNKNOWN_TYPE) {
|
||||
nsCOMPtr<nsIStreamListener> downloader;
|
||||
if (XRE_IsParentProcess()) {
|
||||
downloader = new PSMContentStreamListener(type);
|
||||
} else {
|
||||
downloader = static_cast<PSMContentDownloaderChild*>(
|
||||
dom::ContentChild::GetSingleton()->SendPPSMContentDownloaderConstructor(type));
|
||||
}
|
||||
|
||||
downloader.forget(aContentHandler);
|
||||
return NS_OK;
|
||||
}
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PSMContentListener::GetLoadCookie(nsISupports** aLoadCookie)
|
||||
{
|
||||
nsCOMPtr<nsISupports> loadCookie(mLoadCookie);
|
||||
loadCookie.forget(aLoadCookie);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PSMContentListener::SetLoadCookie(nsISupports* aLoadCookie)
|
||||
{
|
||||
mLoadCookie = aLoadCookie;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PSMContentListener::GetParentContentListener(nsIURIContentListener** aContentListener)
|
||||
{
|
||||
nsCOMPtr<nsIURIContentListener> listener(mParentContentListener);
|
||||
listener.forget(aContentListener);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PSMContentListener::SetParentContentListener(nsIURIContentListener* aContentListener)
|
||||
{
|
||||
mParentContentListener = aContentListener;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} } // namespace mozilla::psm
|
||||
111
security/manager/ssl/PSMContentListener.h
Normal file
111
security/manager/ssl/PSMContentListener.h
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_psm_PSMCOntentListener_h_
|
||||
#define mozilla_psm_PSMCOntentListener_h_
|
||||
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIURIContentListener.h"
|
||||
#include "nsWeakReference.h"
|
||||
#include "mozilla/psm/PPSMContentDownloaderChild.h"
|
||||
#include "mozilla/psm/PPSMContentDownloaderParent.h"
|
||||
|
||||
#define NS_PSMCONTENTLISTEN_CID {0xc94f4a30, 0x64d7, 0x11d4, {0x99, 0x60, 0x00, 0xb0, 0xd0, 0x23, 0x54, 0xa0}}
|
||||
#define NS_PSMCONTENTLISTEN_CONTRACTID "@mozilla.org/security/psmdownload;1"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
class PChannelDiverterParent;
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
||||
namespace mozilla { namespace psm {
|
||||
|
||||
// PSMContentStreamListener for parent-process downloading.
|
||||
class PSMContentStreamListener : public nsIStreamListener
|
||||
{
|
||||
public:
|
||||
explicit PSMContentStreamListener(uint32_t type);
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIREQUESTOBSERVER
|
||||
NS_DECL_NSISTREAMLISTENER
|
||||
|
||||
void ImportCertificate();
|
||||
|
||||
protected:
|
||||
virtual ~PSMContentStreamListener();
|
||||
|
||||
nsCString mByteData;
|
||||
uint32_t mType;
|
||||
};
|
||||
|
||||
// Parent actor for importing a remote cert when the load was started by the
|
||||
// child.
|
||||
class PSMContentDownloaderParent : public PPSMContentDownloaderParent
|
||||
, public PSMContentStreamListener
|
||||
{
|
||||
public:
|
||||
explicit PSMContentDownloaderParent(uint32_t type);
|
||||
|
||||
virtual bool RecvOnStartRequest(const uint32_t &contentLength) override;
|
||||
virtual bool RecvOnDataAvailable(const nsCString &data,
|
||||
const uint64_t &offset,
|
||||
const uint32_t &count) override;
|
||||
virtual bool RecvOnStopRequest(const nsresult &code) override;
|
||||
|
||||
// We inherit most of nsIStreamListener from PSMContentStreamListener, but
|
||||
// we have to override OnStopRequest to know when we're done with our IPC
|
||||
// ref.
|
||||
NS_IMETHOD OnStopRequest(nsIRequest *request, nsISupports *aContext, nsresult code) override;
|
||||
|
||||
virtual bool RecvDivertToParentUsing(mozilla::net::PChannelDiverterParent *diverter) override;
|
||||
|
||||
protected:
|
||||
virtual ~PSMContentDownloaderParent();
|
||||
|
||||
virtual void ActorDestroy(ActorDestroyReason why) override;
|
||||
bool mIPCOpen;
|
||||
};
|
||||
|
||||
// Child actor for importing a cert.
|
||||
class PSMContentDownloaderChild : public nsIStreamListener
|
||||
, public PPSMContentDownloaderChild
|
||||
{
|
||||
public:
|
||||
PSMContentDownloaderChild();
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIREQUESTOBSERVER
|
||||
NS_DECL_NSISTREAMLISTENER
|
||||
|
||||
private:
|
||||
~PSMContentDownloaderChild();
|
||||
};
|
||||
|
||||
|
||||
class PSMContentListener : public nsIURIContentListener,
|
||||
public nsSupportsWeakReference
|
||||
{
|
||||
public:
|
||||
PSMContentListener();
|
||||
nsresult init();
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIURICONTENTLISTENER
|
||||
|
||||
protected:
|
||||
virtual ~PSMContentListener();
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsISupports> mLoadCookie;
|
||||
nsCOMPtr<nsIURIContentListener> mParentContentListener;
|
||||
};
|
||||
|
||||
} } // namespace mozilla::psm
|
||||
|
||||
#endif // mozilla_psm_PSMCOntentListener_h
|
||||
47
security/manager/ssl/PSMRunnable.cpp
Normal file
47
security/manager/ssl/PSMRunnable.cpp
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "PSMRunnable.h"
|
||||
|
||||
namespace mozilla { namespace psm {
|
||||
|
||||
SyncRunnableBase::SyncRunnableBase()
|
||||
: monitor("SyncRunnableBase::monitor")
|
||||
{
|
||||
}
|
||||
|
||||
nsresult
|
||||
SyncRunnableBase::DispatchToMainThreadAndWait()
|
||||
{
|
||||
nsresult rv;
|
||||
if (NS_IsMainThread()) {
|
||||
RunOnTargetThread();
|
||||
rv = NS_OK;
|
||||
} else {
|
||||
mozilla::MonitorAutoLock lock(monitor);
|
||||
rv = NS_DispatchToMainThread(this);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
lock.Wait();
|
||||
}
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
SyncRunnableBase::Run()
|
||||
{
|
||||
RunOnTargetThread();
|
||||
mozilla::MonitorAutoLock(monitor).Notify();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
NotifyObserverRunnable::Run()
|
||||
{
|
||||
mObserver->Observe(nullptr, mTopic, nullptr);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} } // namespace mozilla::psm
|
||||
46
security/manager/ssl/PSMRunnable.h
Normal file
46
security/manager/ssl/PSMRunnable.h
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef PSMRunnable_h
|
||||
#define PSMRunnable_h
|
||||
|
||||
#include "mozilla/Monitor.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsProxyRelease.h"
|
||||
|
||||
namespace mozilla { namespace psm {
|
||||
|
||||
// Wait for the event to run on the target thread without spinning the event
|
||||
// loop on the calling thread. (Dispatching events to a thread using
|
||||
// NS_DISPATCH_SYNC would cause the event loop on the calling thread to spin.)
|
||||
class SyncRunnableBase : public Runnable
|
||||
{
|
||||
public:
|
||||
NS_DECL_NSIRUNNABLE
|
||||
nsresult DispatchToMainThreadAndWait();
|
||||
protected:
|
||||
SyncRunnableBase();
|
||||
virtual void RunOnTargetThread() = 0;
|
||||
private:
|
||||
mozilla::Monitor monitor;
|
||||
};
|
||||
|
||||
class NotifyObserverRunnable : public Runnable
|
||||
{
|
||||
public:
|
||||
NotifyObserverRunnable(nsIObserver * observer,
|
||||
const char * topicStringLiteral)
|
||||
: mObserver(new nsMainThreadPtrHolder<nsIObserver>(observer)),
|
||||
mTopic(topicStringLiteral) {
|
||||
}
|
||||
NS_DECL_NSIRUNNABLE
|
||||
private:
|
||||
nsMainThreadPtrHandle<nsIObserver> mObserver;
|
||||
const char * const mTopic;
|
||||
};
|
||||
|
||||
} } // namespace mozilla::psm
|
||||
|
||||
#endif
|
||||
374
security/manager/ssl/PublicKeyPinningService.cpp
Normal file
374
security/manager/ssl/PublicKeyPinningService.cpp
Normal file
|
|
@ -0,0 +1,374 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "PublicKeyPinningService.h"
|
||||
|
||||
#include "RootCertificateTelemetryUtils.h"
|
||||
#include "mozilla/Base64.h"
|
||||
#include "mozilla/Casting.h"
|
||||
#include "mozilla/Logging.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
#include "nsISiteSecurityService.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsSiteSecurityService.h"
|
||||
#include "nssb64.h"
|
||||
#include "pkix/pkixtypes.h"
|
||||
#include "seccomon.h"
|
||||
#include "sechash.h"
|
||||
|
||||
#include "StaticHPKPins.h" // autogenerated by genHPKPStaticpins.js
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::pkix;
|
||||
using namespace mozilla::psm;
|
||||
|
||||
LazyLogModule gPublicKeyPinningLog("PublicKeyPinningService");
|
||||
|
||||
/**
|
||||
Computes in the location specified by base64Out the SHA256 digest
|
||||
of the DER Encoded subject Public Key Info for the given cert
|
||||
*/
|
||||
static nsresult
|
||||
GetBase64HashSPKI(const CERTCertificate* cert, nsACString& hashSPKIDigest)
|
||||
{
|
||||
hashSPKIDigest.Truncate();
|
||||
Digest digest;
|
||||
nsresult rv = digest.DigestBuf(SEC_OID_SHA256, cert->derPublicKey.data,
|
||||
cert->derPublicKey.len);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
return Base64Encode(nsDependentCSubstring(
|
||||
BitwiseCast<char*, unsigned char*>(digest.get().data),
|
||||
digest.get().len),
|
||||
hashSPKIDigest);
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets certMatchesPinset to true if a given cert matches any fingerprints from
|
||||
* the given pinset or the dynamicFingerprints array, or to false otherwise.
|
||||
*/
|
||||
static nsresult
|
||||
EvalCert(const CERTCertificate* cert, const StaticFingerprints* fingerprints,
|
||||
const nsTArray<nsCString>* dynamicFingerprints,
|
||||
/*out*/ bool& certMatchesPinset)
|
||||
{
|
||||
certMatchesPinset = false;
|
||||
if (!fingerprints && !dynamicFingerprints) {
|
||||
MOZ_LOG(gPublicKeyPinningLog, LogLevel::Debug,
|
||||
("pkpin: No hashes found\n"));
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
nsAutoCString base64Out;
|
||||
nsresult rv = GetBase64HashSPKI(cert, base64Out);
|
||||
if (NS_FAILED(rv)) {
|
||||
MOZ_LOG(gPublicKeyPinningLog, LogLevel::Debug,
|
||||
("pkpin: GetBase64HashSPKI failed!\n"));
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (fingerprints) {
|
||||
for (size_t i = 0; i < fingerprints->size; i++) {
|
||||
if (base64Out.Equals(fingerprints->data[i])) {
|
||||
MOZ_LOG(gPublicKeyPinningLog, LogLevel::Debug,
|
||||
("pkpin: found pin base_64 ='%s'\n", base64Out.get()));
|
||||
certMatchesPinset = true;
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (dynamicFingerprints) {
|
||||
for (size_t i = 0; i < dynamicFingerprints->Length(); i++) {
|
||||
if (base64Out.Equals((*dynamicFingerprints)[i])) {
|
||||
MOZ_LOG(gPublicKeyPinningLog, LogLevel::Debug,
|
||||
("pkpin: found pin base_64 ='%s'\n", base64Out.get()));
|
||||
certMatchesPinset = true;
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets certListIntersectsPinset to true if a given chain matches any
|
||||
* fingerprints from the given static fingerprints or the
|
||||
* dynamicFingerprints array, or to false otherwise.
|
||||
*/
|
||||
static nsresult
|
||||
EvalChain(const UniqueCERTCertList& certList,
|
||||
const StaticFingerprints* fingerprints,
|
||||
const nsTArray<nsCString>* dynamicFingerprints,
|
||||
/*out*/ bool& certListIntersectsPinset)
|
||||
{
|
||||
certListIntersectsPinset = false;
|
||||
CERTCertificate* currentCert;
|
||||
|
||||
if (!fingerprints && !dynamicFingerprints) {
|
||||
MOZ_ASSERT(false, "Must pass in at least one type of pinset");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
CERTCertListNode* node;
|
||||
for (node = CERT_LIST_HEAD(certList); !CERT_LIST_END(node, certList);
|
||||
node = CERT_LIST_NEXT(node)) {
|
||||
currentCert = node->cert;
|
||||
MOZ_LOG(gPublicKeyPinningLog, LogLevel::Debug,
|
||||
("pkpin: certArray subject: '%s'\n", currentCert->subjectName));
|
||||
MOZ_LOG(gPublicKeyPinningLog, LogLevel::Debug,
|
||||
("pkpin: certArray issuer: '%s'\n", currentCert->issuerName));
|
||||
nsresult rv = EvalCert(currentCert, fingerprints, dynamicFingerprints,
|
||||
certListIntersectsPinset);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
if (certListIntersectsPinset) {
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
MOZ_LOG(gPublicKeyPinningLog, LogLevel::Debug, ("pkpin: no matches found\n"));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
Comparator for the is public key pinned host.
|
||||
*/
|
||||
static int
|
||||
TransportSecurityPreloadCompare(const void* key, const void* entry) {
|
||||
auto keyStr = static_cast<const char*>(key);
|
||||
auto preloadEntry = static_cast<const TransportSecurityPreload*>(entry);
|
||||
|
||||
return strcmp(keyStr, preloadEntry->mHost);
|
||||
}
|
||||
|
||||
nsresult
|
||||
PublicKeyPinningService::ChainMatchesPinset(const UniqueCERTCertList& certList,
|
||||
const nsTArray<nsCString>& aSHA256keys,
|
||||
/*out*/ bool& chainMatchesPinset)
|
||||
{
|
||||
return EvalChain(certList, nullptr, &aSHA256keys, chainMatchesPinset);
|
||||
}
|
||||
|
||||
// Returns via one of the output parameters the most relevant pinning
|
||||
// information that is valid for the given host at the given time.
|
||||
// Dynamic pins are prioritized over static pins.
|
||||
static nsresult
|
||||
FindPinningInformation(const char* hostname, mozilla::pkix::Time time,
|
||||
/*out*/ nsTArray<nsCString>& dynamicFingerprints,
|
||||
/*out*/ TransportSecurityPreload*& staticFingerprints)
|
||||
{
|
||||
if (!hostname || hostname[0] == 0) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
staticFingerprints = nullptr;
|
||||
dynamicFingerprints.Clear();
|
||||
nsCOMPtr<nsISiteSecurityService> sssService =
|
||||
do_GetService(NS_SSSERVICE_CONTRACTID);
|
||||
if (!sssService) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
TransportSecurityPreload* foundEntry = nullptr;
|
||||
char* evalHost = const_cast<char*>(hostname);
|
||||
char* evalPart;
|
||||
// Notice how the (xx = strchr) prevents pins for unqualified domain names.
|
||||
while (!foundEntry && (evalPart = strchr(evalHost, '.'))) {
|
||||
MOZ_LOG(gPublicKeyPinningLog, LogLevel::Debug,
|
||||
("pkpin: Querying pinsets for host: '%s'\n", evalHost));
|
||||
// Attempt dynamic pins first
|
||||
nsresult rv;
|
||||
bool found;
|
||||
bool includeSubdomains;
|
||||
nsTArray<nsCString> pinArray;
|
||||
rv = sssService->GetKeyPinsForHostname(evalHost, time, pinArray,
|
||||
&includeSubdomains, &found);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
if (found && (evalHost == hostname || includeSubdomains)) {
|
||||
MOZ_LOG(gPublicKeyPinningLog, LogLevel::Debug,
|
||||
("pkpin: Found dyn match for host: '%s'\n", evalHost));
|
||||
dynamicFingerprints = pinArray;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
foundEntry = (TransportSecurityPreload *)bsearch(evalHost,
|
||||
kPublicKeyPinningPreloadList,
|
||||
sizeof(kPublicKeyPinningPreloadList) / sizeof(TransportSecurityPreload),
|
||||
sizeof(TransportSecurityPreload),
|
||||
TransportSecurityPreloadCompare);
|
||||
if (foundEntry) {
|
||||
MOZ_LOG(gPublicKeyPinningLog, LogLevel::Debug,
|
||||
("pkpin: Found pinset for host: '%s'\n", evalHost));
|
||||
if (evalHost != hostname) {
|
||||
if (!foundEntry->mIncludeSubdomains) {
|
||||
// Does not apply to this host, continue iterating
|
||||
foundEntry = nullptr;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
MOZ_LOG(gPublicKeyPinningLog, LogLevel::Debug,
|
||||
("pkpin: Didn't find pinset for host: '%s'\n", evalHost));
|
||||
}
|
||||
// Add one for '.'
|
||||
evalHost = evalPart + 1;
|
||||
}
|
||||
|
||||
if (foundEntry && foundEntry->pinset) {
|
||||
if (time > TimeFromEpochInSeconds(kPreloadPKPinsExpirationTime /
|
||||
PR_USEC_PER_SEC)) {
|
||||
return NS_OK;
|
||||
}
|
||||
staticFingerprints = foundEntry;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Returns true via the output parameter if the given certificate list meets
|
||||
// pinning requirements for the given host at the given time. It must be the
|
||||
// case that either there is an intersection between the set of hashes of
|
||||
// subject public key info data in the list and the most relevant non-expired
|
||||
// pinset for the host or there is no pinning information for the host.
|
||||
static nsresult
|
||||
CheckPinsForHostname(const UniqueCERTCertList& certList, const char* hostname,
|
||||
bool enforceTestMode, mozilla::pkix::Time time,
|
||||
/*out*/ bool& chainHasValidPins,
|
||||
/*optional out*/ PinningTelemetryInfo* pinningTelemetryInfo)
|
||||
{
|
||||
chainHasValidPins = false;
|
||||
if (!certList) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
if (!hostname || hostname[0] == 0) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
nsTArray<nsCString> dynamicFingerprints;
|
||||
TransportSecurityPreload* staticFingerprints = nullptr;
|
||||
nsresult rv = FindPinningInformation(hostname, time, dynamicFingerprints,
|
||||
staticFingerprints);
|
||||
// If we have no pinning information, the certificate chain trivially
|
||||
// validates with respect to pinning.
|
||||
if (dynamicFingerprints.Length() == 0 && !staticFingerprints) {
|
||||
chainHasValidPins = true;
|
||||
return NS_OK;
|
||||
}
|
||||
if (dynamicFingerprints.Length() > 0) {
|
||||
return EvalChain(certList, nullptr, &dynamicFingerprints, chainHasValidPins);
|
||||
}
|
||||
if (staticFingerprints) {
|
||||
bool enforceTestModeResult;
|
||||
rv = EvalChain(certList, staticFingerprints->pinset, nullptr,
|
||||
enforceTestModeResult);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
chainHasValidPins = enforceTestModeResult;
|
||||
Telemetry::ID histogram = staticFingerprints->mIsMoz
|
||||
? Telemetry::CERT_PINNING_MOZ_RESULTS
|
||||
: Telemetry::CERT_PINNING_RESULTS;
|
||||
if (staticFingerprints->mTestMode) {
|
||||
histogram = staticFingerprints->mIsMoz
|
||||
? Telemetry::CERT_PINNING_MOZ_TEST_RESULTS
|
||||
: Telemetry::CERT_PINNING_TEST_RESULTS;
|
||||
if (!enforceTestMode) {
|
||||
chainHasValidPins = true;
|
||||
}
|
||||
}
|
||||
// We can collect per-host pinning violations for this host because it is
|
||||
// operationally critical to Firefox.
|
||||
if (pinningTelemetryInfo) {
|
||||
if (staticFingerprints->mId != kUnknownId) {
|
||||
int32_t bucket = staticFingerprints->mId * 2
|
||||
+ (enforceTestModeResult ? 1 : 0);
|
||||
histogram = staticFingerprints->mTestMode
|
||||
? Telemetry::CERT_PINNING_MOZ_TEST_RESULTS_BY_HOST
|
||||
: Telemetry::CERT_PINNING_MOZ_RESULTS_BY_HOST;
|
||||
pinningTelemetryInfo->certPinningResultBucket = bucket;
|
||||
} else {
|
||||
pinningTelemetryInfo->certPinningResultBucket =
|
||||
enforceTestModeResult ? 1 : 0;
|
||||
}
|
||||
pinningTelemetryInfo->accumulateResult = true;
|
||||
pinningTelemetryInfo->certPinningResultHistogram = histogram;
|
||||
}
|
||||
|
||||
// We only collect per-CA pinning statistics upon failures.
|
||||
CERTCertListNode* rootNode = CERT_LIST_TAIL(certList);
|
||||
// Only log telemetry if the certificate list is non-empty.
|
||||
if (!CERT_LIST_END(rootNode, certList)) {
|
||||
if (!enforceTestModeResult && pinningTelemetryInfo) {
|
||||
int32_t binNumber = RootCABinNumber(&rootNode->cert->derCert);
|
||||
if (binNumber != ROOT_CERTIFICATE_UNKNOWN ) {
|
||||
pinningTelemetryInfo->accumulateForRoot = true;
|
||||
pinningTelemetryInfo->rootBucket = binNumber;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MOZ_LOG(gPublicKeyPinningLog, LogLevel::Debug,
|
||||
("pkpin: Pin check %s for %s host '%s' (mode=%s)\n",
|
||||
enforceTestModeResult ? "passed" : "failed",
|
||||
staticFingerprints->mIsMoz ? "mozilla" : "non-mozilla",
|
||||
hostname, staticFingerprints->mTestMode ? "test" : "production"));
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
PublicKeyPinningService::ChainHasValidPins(const UniqueCERTCertList& certList,
|
||||
const char* hostname,
|
||||
mozilla::pkix::Time time,
|
||||
bool enforceTestMode,
|
||||
/*out*/ bool& chainHasValidPins,
|
||||
/*optional out*/ PinningTelemetryInfo* pinningTelemetryInfo)
|
||||
{
|
||||
chainHasValidPins = false;
|
||||
if (!certList) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
if (!hostname || hostname[0] == 0) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
nsAutoCString canonicalizedHostname(CanonicalizeHostname(hostname));
|
||||
return CheckPinsForHostname(certList, canonicalizedHostname.get(),
|
||||
enforceTestMode, time, chainHasValidPins,
|
||||
pinningTelemetryInfo);
|
||||
}
|
||||
|
||||
nsresult
|
||||
PublicKeyPinningService::HostHasPins(const char* hostname,
|
||||
mozilla::pkix::Time time,
|
||||
bool enforceTestMode,
|
||||
/*out*/ bool& hostHasPins)
|
||||
{
|
||||
hostHasPins = false;
|
||||
nsAutoCString canonicalizedHostname(CanonicalizeHostname(hostname));
|
||||
nsTArray<nsCString> dynamicFingerprints;
|
||||
TransportSecurityPreload* staticFingerprints = nullptr;
|
||||
nsresult rv = FindPinningInformation(canonicalizedHostname.get(), time,
|
||||
dynamicFingerprints, staticFingerprints);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
if (dynamicFingerprints.Length() > 0) {
|
||||
hostHasPins = true;
|
||||
} else if (staticFingerprints) {
|
||||
hostHasPins = !staticFingerprints->mTestMode || enforceTestMode;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsAutoCString
|
||||
PublicKeyPinningService::CanonicalizeHostname(const char* hostname)
|
||||
{
|
||||
nsAutoCString canonicalizedHostname(hostname);
|
||||
ToLowerCase(canonicalizedHostname);
|
||||
while (canonicalizedHostname.Length() > 0 &&
|
||||
canonicalizedHostname.Last() == '.') {
|
||||
canonicalizedHostname.Truncate(canonicalizedHostname.Length() - 1);
|
||||
}
|
||||
return canonicalizedHostname;
|
||||
}
|
||||
65
security/manager/ssl/PublicKeyPinningService.h
Normal file
65
security/manager/ssl/PublicKeyPinningService.h
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef PublicKeyPinningService_h
|
||||
#define PublicKeyPinningService_h
|
||||
|
||||
#include "CertVerifier.h"
|
||||
#include "ScopedNSSTypes.h"
|
||||
#include "cert.h"
|
||||
#include "nsString.h"
|
||||
#include "nsTArray.h"
|
||||
#include "pkix/Time.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace psm {
|
||||
|
||||
class PublicKeyPinningService
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Sets chainHasValidPins to true if the given (host, certList) passes pinning
|
||||
* checks, or to false otherwise. If the host is pinned, returns true via
|
||||
* chainHasValidPins if one of the keys in the given certificate chain matches
|
||||
* the pin set specified by the hostname. The certList's head is the EE cert
|
||||
* and the tail is the trust anchor.
|
||||
* Note: if an alt name is a wildcard, it won't necessarily find a pinset
|
||||
* that would otherwise be valid for it
|
||||
*/
|
||||
static nsresult ChainHasValidPins(const UniqueCERTCertList& certList,
|
||||
const char* hostname,
|
||||
mozilla::pkix::Time time,
|
||||
bool enforceTestMode,
|
||||
/*out*/ bool& chainHasValidPins,
|
||||
/*optional out*/ PinningTelemetryInfo* pinningTelemetryInfo);
|
||||
/**
|
||||
* Sets chainMatchesPinset to true if there is any intersection between the
|
||||
* certificate list and the pins specified in the aSHA256keys array.
|
||||
* Values passed in are assumed to be in base64 encoded form.
|
||||
*/
|
||||
static nsresult ChainMatchesPinset(const UniqueCERTCertList& certList,
|
||||
const nsTArray<nsCString>& aSHA256keys,
|
||||
/*out*/ bool& chainMatchesPinset);
|
||||
|
||||
/**
|
||||
* Returns true via the output parameter hostHasPins if there is pinning
|
||||
* information for the given host that is valid at the given time, and false
|
||||
* otherwise.
|
||||
*/
|
||||
static nsresult HostHasPins(const char* hostname,
|
||||
mozilla::pkix::Time time,
|
||||
bool enforceTestMode,
|
||||
/*out*/ bool& hostHasPins);
|
||||
|
||||
/**
|
||||
* Given a hostname of potentially mixed case with potentially multiple
|
||||
* trailing '.' (see bug 1118522), canonicalizes it to lowercase with no
|
||||
* trailing '.'.
|
||||
*/
|
||||
static nsAutoCString CanonicalizeHostname(const char* hostname);
|
||||
};
|
||||
|
||||
}} // namespace mozilla::psm
|
||||
|
||||
#endif // PublicKeyPinningService_h
|
||||
25
security/manager/ssl/PublicSSL.h
Normal file
25
security/manager/ssl/PublicSSL.h
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_SSL_h
|
||||
#define mozilla_SSL_h
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
void ClearPrivateSSLState();
|
||||
|
||||
namespace psm {
|
||||
|
||||
void InitializeSSLServerCertVerificationThreads();
|
||||
void StopSSLServerCertVerificationThreads();
|
||||
void DisableMD5();
|
||||
nsresult InitializeCipherSuite();
|
||||
|
||||
} //namespace psm
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
|
||||
90
security/manager/ssl/RootCertificateTelemetryUtils.cpp
Normal file
90
security/manager/ssl/RootCertificateTelemetryUtils.cpp
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "RootCertificateTelemetryUtils.h"
|
||||
|
||||
#include "mozilla/Logging.h"
|
||||
#include "RootHashes.inc" // Note: Generated by genRootCAHashes.js
|
||||
#include "ScopedNSSTypes.h"
|
||||
#include "mozilla/ArrayUtils.h"
|
||||
|
||||
namespace mozilla { namespace psm {
|
||||
|
||||
mozilla::LazyLogModule gPublicKeyPinningTelemetryLog("PublicKeyPinningTelemetryService");
|
||||
|
||||
// Used in the BinarySearch method, this does a memcmp between the pointer
|
||||
// provided to its construtor and whatever the binary search is looking for.
|
||||
//
|
||||
// This implementation assumes everything to be of HASH_LEN, so it should not
|
||||
// be used generically.
|
||||
class BinaryHashSearchArrayComparator
|
||||
{
|
||||
public:
|
||||
explicit BinaryHashSearchArrayComparator(const uint8_t* aTarget, size_t len)
|
||||
: mTarget(aTarget)
|
||||
{
|
||||
NS_ASSERTION(len == HASH_LEN, "Hashes should be of the same length.");
|
||||
}
|
||||
|
||||
int operator()(const CertAuthorityHash val) const {
|
||||
return memcmp(mTarget, val.hash, HASH_LEN);
|
||||
}
|
||||
|
||||
private:
|
||||
const uint8_t* mTarget;
|
||||
};
|
||||
|
||||
// Perform a hash of the provided cert, then search in the RootHashes.inc data
|
||||
// structure for a matching bin number.
|
||||
int32_t
|
||||
RootCABinNumber(const SECItem* cert)
|
||||
{
|
||||
Digest digest;
|
||||
|
||||
// Compute SHA256 hash of the certificate
|
||||
nsresult rv = digest.DigestBuf(SEC_OID_SHA256, cert->data, cert->len);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return ROOT_CERTIFICATE_HASH_FAILURE;
|
||||
}
|
||||
|
||||
// Compare against list of stored hashes
|
||||
size_t idx;
|
||||
|
||||
MOZ_LOG(gPublicKeyPinningTelemetryLog, LogLevel::Debug,
|
||||
("pkpinTelem: First bytes %02hx %02hx %02hx %02hx\n",
|
||||
digest.get().data[0], digest.get().data[1], digest.get().data[2], digest.get().data[3]));
|
||||
|
||||
if (mozilla::BinarySearchIf(ROOT_TABLE, 0, ArrayLength(ROOT_TABLE),
|
||||
BinaryHashSearchArrayComparator(static_cast<uint8_t*>(digest.get().data),
|
||||
digest.get().len),
|
||||
&idx)) {
|
||||
|
||||
MOZ_LOG(gPublicKeyPinningTelemetryLog, LogLevel::Debug,
|
||||
("pkpinTelem: Telemetry index was %lu, bin is %d\n",
|
||||
idx, ROOT_TABLE[idx].binNumber));
|
||||
return (int32_t) ROOT_TABLE[idx].binNumber;
|
||||
}
|
||||
|
||||
// Didn't match.
|
||||
return ROOT_CERTIFICATE_UNKNOWN;
|
||||
}
|
||||
|
||||
|
||||
// Attempt to increment the appropriate bin in the provided Telemetry probe ID. If
|
||||
// there was a hash failure, we do nothing.
|
||||
void
|
||||
AccumulateTelemetryForRootCA(mozilla::Telemetry::ID probe,
|
||||
const CERTCertificate* cert)
|
||||
{
|
||||
int32_t binId = RootCABinNumber(&cert->derCert);
|
||||
|
||||
if (binId != ROOT_CERTIFICATE_HASH_FAILURE) {
|
||||
Accumulate(probe, binId);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace psm
|
||||
} // namespace mozilla
|
||||
30
security/manager/ssl/RootCertificateTelemetryUtils.h
Normal file
30
security/manager/ssl/RootCertificateTelemetryUtils.h
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef RootCertificateTelemetryUtils_h
|
||||
#define RootCertificateTelemetryUtils_h
|
||||
|
||||
#include "mozilla/Telemetry.h"
|
||||
#include "certt.h"
|
||||
|
||||
namespace mozilla { namespace psm {
|
||||
|
||||
// Note: New CAs will show up as UNKNOWN_ROOT until
|
||||
// RootHashes.inc is updated to include them. 0 is reserved by
|
||||
// genRootCAHashes.js for the unknowns.
|
||||
#define ROOT_CERTIFICATE_UNKNOWN 0
|
||||
#define ROOT_CERTIFICATE_HASH_FAILURE -1
|
||||
|
||||
int32_t
|
||||
RootCABinNumber(const SECItem* cert);
|
||||
|
||||
void
|
||||
AccumulateTelemetryForRootCA(mozilla::Telemetry::ID probe, const CERTCertificate* cert);
|
||||
|
||||
} // namespace psm
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // RootCertificateTelemetryUtils_h
|
||||
1140
security/manager/ssl/RootHashes.inc
Normal file
1140
security/manager/ssl/RootHashes.inc
Normal file
File diff suppressed because it is too large
Load diff
1795
security/manager/ssl/SSLServerCertVerification.cpp
Normal file
1795
security/manager/ssl/SSLServerCertVerification.cpp
Normal file
File diff suppressed because it is too large
Load diff
19
security/manager/ssl/SSLServerCertVerification.h
Normal file
19
security/manager/ssl/SSLServerCertVerification.h
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#ifndef _SSLSERVERCERTVERIFICATION_H
|
||||
#define _SSLSERVERCERTVERIFICATION_H
|
||||
|
||||
#include "seccomon.h"
|
||||
#include "prio.h"
|
||||
|
||||
namespace mozilla { namespace psm {
|
||||
|
||||
SECStatus AuthCertificateHook(void* arg, PRFileDesc* fd,
|
||||
PRBool checkSig, PRBool isServer);
|
||||
|
||||
} } // namespace mozilla::psm
|
||||
|
||||
#endif
|
||||
405
security/manager/ssl/ScopedNSSTypes.h
Normal file
405
security/manager/ssl/ScopedNSSTypes.h
Normal file
|
|
@ -0,0 +1,405 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// This header provides smart pointers and various helpers for code that needs
|
||||
// to interact with NSS.
|
||||
|
||||
#ifndef ScopedNSSTypes_h
|
||||
#define ScopedNSSTypes_h
|
||||
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
|
||||
#include "cert.h"
|
||||
#include "cms.h"
|
||||
#include "cryptohi.h"
|
||||
#include "keyhi.h"
|
||||
#include "mozilla/Likely.h"
|
||||
#include "mozilla/Scoped.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "nsDebug.h"
|
||||
#include "nsError.h"
|
||||
#include "NSSErrorsService.h"
|
||||
#include "pk11pub.h"
|
||||
#include "pkcs12.h"
|
||||
#include "prerror.h"
|
||||
#include "prio.h"
|
||||
#include "sechash.h"
|
||||
#include "secmod.h"
|
||||
#include "secpkcs7.h"
|
||||
#include "secport.h"
|
||||
|
||||
#ifndef MOZ_NO_MOZALLOC
|
||||
#include "mozilla/mozalloc_oom.h"
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
// NSPR APIs use PRStatus/PR_GetError and NSS APIs use SECStatus/PR_GetError to
|
||||
// report success/failure. This function makes it more convenient and *safer*
|
||||
// to translate NSPR/NSS results to nsresult. It is safer because it
|
||||
// refuses to translate any bad PRStatus/SECStatus into an NS_OK, even when the
|
||||
// NSPR/NSS function forgot to call PR_SetError. The actual enforcement of
|
||||
// this happens in mozilla::psm::GetXPCOMFromNSSError.
|
||||
// IMPORTANT: This must be called immediately after the function returning the
|
||||
// SECStatus result. The recommended usage is:
|
||||
// nsresult rv = MapSECStatus(f(x, y, z));
|
||||
inline nsresult
|
||||
MapSECStatus(SECStatus rv)
|
||||
{
|
||||
if (rv == SECSuccess) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
|
||||
}
|
||||
|
||||
// Alphabetical order by NSS type
|
||||
// Deprecated: use the equivalent UniquePtr templates instead.
|
||||
MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedCERTCertificate,
|
||||
CERTCertificate,
|
||||
CERT_DestroyCertificate)
|
||||
MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedCERTCertificateList,
|
||||
CERTCertificateList,
|
||||
CERT_DestroyCertificateList)
|
||||
MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedCERTCertificateRequest,
|
||||
CERTCertificateRequest,
|
||||
CERT_DestroyCertificateRequest)
|
||||
MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedCERTName,
|
||||
CERTName,
|
||||
CERT_DestroyName)
|
||||
MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedCERTSubjectPublicKeyInfo,
|
||||
CERTSubjectPublicKeyInfo,
|
||||
SECKEY_DestroySubjectPublicKeyInfo)
|
||||
MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedCERTValidity,
|
||||
CERTValidity,
|
||||
CERT_DestroyValidity)
|
||||
// Deprecated: use the equivalent UniquePtr templates instead.
|
||||
|
||||
namespace internal {
|
||||
|
||||
inline void
|
||||
PK11_DestroyContext_true(PK11Context * ctx) {
|
||||
PK11_DestroyContext(ctx, true);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
// Deprecated: use the equivalent UniquePtr templates instead.
|
||||
MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedSGNDigestInfo,
|
||||
SGNDigestInfo,
|
||||
SGN_DestroyDigestInfo)
|
||||
|
||||
// Emulates MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE, but for UniquePtrs.
|
||||
#define MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(name, Type, Deleter) \
|
||||
struct name##DeletePolicy \
|
||||
{ \
|
||||
void operator()(Type* aValue) { Deleter(aValue); } \
|
||||
}; \
|
||||
typedef std::unique_ptr<Type, name##DeletePolicy> name;
|
||||
|
||||
MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniquePK11Context,
|
||||
PK11Context,
|
||||
internal::PK11_DestroyContext_true)
|
||||
|
||||
/** A more convenient way of dealing with digests calculated into
|
||||
* stack-allocated buffers. NSS must be initialized on the main thread before
|
||||
* use, and the caller must ensure NSS isn't shut down, typically by
|
||||
* subclassing nsNSSShutDownObject, while Digest is in use.
|
||||
*
|
||||
* Typical usage, for digesting a buffer in memory:
|
||||
*
|
||||
* nsCOMPtr<nsISupports> nssDummy = do_GetService("@mozilla.org/psm;1", &rv);
|
||||
* Digest digest;
|
||||
* nsresult rv = digest.DigestBuf(SEC_OID_SHA256, mybuffer, myBufferLen);
|
||||
* NS_ENSURE_SUCCESS(rv, rv);
|
||||
* rv = MapSECStatus(SomeNSSFunction(..., digest.get(), ...));
|
||||
*
|
||||
* Less typical usage, for digesting while doing streaming I/O and similar:
|
||||
*
|
||||
* Digest digest;
|
||||
* UniquePK11Context digestContext(PK11_CreateDigestContext(SEC_OID_SHA256));
|
||||
* NS_ENSURE_TRUE(digestContext, NS_ERROR_OUT_OF_MEMORY);
|
||||
* rv = MapSECStatus(PK11_DigestBegin(digestContext.get()));
|
||||
* NS_ENSURE_SUCCESS(rv, rv);
|
||||
* for (...) {
|
||||
* rv = MapSECStatus(PK11_DigestOp(digestContext.get(), ...));
|
||||
* NS_ENSURE_SUCCESS(rv, rv);
|
||||
* }
|
||||
* rv = digest.End(SEC_OID_SHA256, digestContext);
|
||||
* NS_ENSURE_SUCCESS(rv, rv)
|
||||
*/
|
||||
class Digest
|
||||
{
|
||||
public:
|
||||
Digest()
|
||||
{
|
||||
mItem.type = siBuffer;
|
||||
mItem.data = mItemBuf;
|
||||
mItem.len = 0;
|
||||
}
|
||||
|
||||
nsresult DigestBuf(SECOidTag hashAlg, const uint8_t * buf, uint32_t len)
|
||||
{
|
||||
if (len > static_cast<uint32_t>(std::numeric_limits<int32_t>::max())) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
nsresult rv = SetLength(hashAlg);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
return MapSECStatus(PK11_HashBuf(hashAlg, mItem.data, buf,
|
||||
static_cast<int32_t>(len)));
|
||||
}
|
||||
|
||||
nsresult End(SECOidTag hashAlg, UniquePK11Context& context)
|
||||
{
|
||||
nsresult rv = SetLength(hashAlg);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
uint32_t len;
|
||||
rv = MapSECStatus(PK11_DigestFinal(context.get(), mItem.data, &len,
|
||||
mItem.len));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
context = nullptr;
|
||||
NS_ENSURE_TRUE(len == mItem.len, NS_ERROR_UNEXPECTED);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
const SECItem & get() const { return mItem; }
|
||||
|
||||
private:
|
||||
nsresult SetLength(SECOidTag hashType)
|
||||
{
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
// C4061: enumerator 'symbol' in switch of enum 'symbol' is not
|
||||
// explicitly handled.
|
||||
#pragma warning(disable:4061)
|
||||
#endif
|
||||
switch (hashType)
|
||||
{
|
||||
case SEC_OID_SHA1: mItem.len = SHA1_LENGTH; break;
|
||||
case SEC_OID_SHA256: mItem.len = SHA256_LENGTH; break;
|
||||
case SEC_OID_SHA384: mItem.len = SHA384_LENGTH; break;
|
||||
case SEC_OID_SHA512: mItem.len = SHA512_LENGTH; break;
|
||||
default:
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
uint8_t mItemBuf[HASH_LENGTH_MAX];
|
||||
SECItem mItem;
|
||||
};
|
||||
|
||||
// Deprecated: use the equivalent UniquePtr templates instead.
|
||||
MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedPK11SlotInfo,
|
||||
PK11SlotInfo,
|
||||
PK11_FreeSlot)
|
||||
MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedPK11SymKey,
|
||||
PK11SymKey,
|
||||
PK11_FreeSymKey)
|
||||
MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedPK11GenericObject,
|
||||
PK11GenericObject,
|
||||
PK11_DestroyGenericObject)
|
||||
MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedSEC_PKCS12DecoderContext,
|
||||
SEC_PKCS12DecoderContext,
|
||||
SEC_PKCS12DecoderFinish)
|
||||
namespace internal {
|
||||
|
||||
inline void
|
||||
PORT_FreeArena_false(PLArenaPool* arena)
|
||||
{
|
||||
// PL_FreeArenaPool can't be used because it doesn't actually free the
|
||||
// memory, which doesn't work well with memory analysis tools.
|
||||
return PORT_FreeArena(arena, false);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
// Deprecated: use the equivalent UniquePtr templates instead.
|
||||
MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedPLArenaPool,
|
||||
PLArenaPool,
|
||||
internal::PORT_FreeArena_false)
|
||||
|
||||
// Wrapper around NSS's SECItem_AllocItem that handles OOM the same way as
|
||||
// other allocators.
|
||||
inline void
|
||||
SECITEM_AllocItem(SECItem & item, uint32_t len)
|
||||
{
|
||||
if (MOZ_UNLIKELY(!SECITEM_AllocItem(nullptr, &item, len))) {
|
||||
#ifndef MOZ_NO_MOZALLOC
|
||||
mozalloc_handle_oom(len);
|
||||
if (MOZ_UNLIKELY(!SECITEM_AllocItem(nullptr, &item, len)))
|
||||
#endif
|
||||
{
|
||||
MOZ_CRASH();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ScopedAutoSECItem final : public SECItem
|
||||
{
|
||||
public:
|
||||
explicit ScopedAutoSECItem(uint32_t initialAllocatedLen = 0)
|
||||
{
|
||||
data = nullptr;
|
||||
len = 0;
|
||||
if (initialAllocatedLen > 0) {
|
||||
SECITEM_AllocItem(*this, initialAllocatedLen);
|
||||
}
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
SECITEM_FreeItem(this, false);
|
||||
}
|
||||
|
||||
~ScopedAutoSECItem()
|
||||
{
|
||||
reset();
|
||||
}
|
||||
};
|
||||
|
||||
class MOZ_RAII AutoSECMODListReadLock final
|
||||
{
|
||||
public:
|
||||
AutoSECMODListReadLock()
|
||||
: mLock(SECMOD_GetDefaultModuleListLock())
|
||||
{
|
||||
MOZ_ASSERT(mLock, "should have SECMOD lock (has NSS been initialized?)");
|
||||
SECMOD_GetReadLock(mLock);
|
||||
}
|
||||
|
||||
~AutoSECMODListReadLock()
|
||||
{
|
||||
SECMOD_ReleaseReadLock(mLock);
|
||||
}
|
||||
|
||||
private:
|
||||
SECMODListLock* mLock;
|
||||
};
|
||||
|
||||
namespace internal {
|
||||
|
||||
inline void SECITEM_FreeItem_true(SECItem * s)
|
||||
{
|
||||
return SECITEM_FreeItem(s, true);
|
||||
}
|
||||
|
||||
inline void SECOID_DestroyAlgorithmID_true(SECAlgorithmID * a)
|
||||
{
|
||||
return SECOID_DestroyAlgorithmID(a, true);
|
||||
}
|
||||
|
||||
inline void SECKEYEncryptedPrivateKeyInfo_true(SECKEYEncryptedPrivateKeyInfo * epki)
|
||||
{
|
||||
return SECKEY_DestroyEncryptedPrivateKeyInfo(epki, PR_TRUE);
|
||||
}
|
||||
|
||||
inline void VFY_DestroyContext_true(VFYContext * ctx)
|
||||
{
|
||||
VFY_DestroyContext(ctx, true);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
// Deprecated: use the equivalent UniquePtr templates instead.
|
||||
MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedSECItem,
|
||||
SECItem,
|
||||
internal::SECITEM_FreeItem_true)
|
||||
MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedSECKEYPrivateKey,
|
||||
SECKEYPrivateKey,
|
||||
SECKEY_DestroyPrivateKey)
|
||||
MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedSECKEYEncryptedPrivateKeyInfo,
|
||||
SECKEYEncryptedPrivateKeyInfo,
|
||||
internal::SECKEYEncryptedPrivateKeyInfo_true)
|
||||
MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedSECKEYPublicKey,
|
||||
SECKEYPublicKey,
|
||||
SECKEY_DestroyPublicKey)
|
||||
MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedSECAlgorithmID,
|
||||
SECAlgorithmID,
|
||||
internal::SECOID_DestroyAlgorithmID_true)
|
||||
|
||||
MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueCERTCertificate,
|
||||
CERTCertificate,
|
||||
CERT_DestroyCertificate)
|
||||
MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueCERTCertificateList,
|
||||
CERTCertificateList,
|
||||
CERT_DestroyCertificateList)
|
||||
MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueCERTCertificatePolicies,
|
||||
CERTCertificatePolicies,
|
||||
CERT_DestroyCertificatePoliciesExtension)
|
||||
MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueCERTCertificateRequest,
|
||||
CERTCertificateRequest,
|
||||
CERT_DestroyCertificateRequest)
|
||||
MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueCERTCertList,
|
||||
CERTCertList,
|
||||
CERT_DestroyCertList)
|
||||
MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueCERTName,
|
||||
CERTName,
|
||||
CERT_DestroyName)
|
||||
MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueCERTOidSequence,
|
||||
CERTOidSequence,
|
||||
CERT_DestroyOidSequence)
|
||||
MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueCERTSubjectPublicKeyInfo,
|
||||
CERTSubjectPublicKeyInfo,
|
||||
SECKEY_DestroySubjectPublicKeyInfo)
|
||||
MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueCERTUserNotice,
|
||||
CERTUserNotice,
|
||||
CERT_DestroyUserNotice)
|
||||
MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueCERTValidity,
|
||||
CERTValidity,
|
||||
CERT_DestroyValidity)
|
||||
|
||||
MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueNSSCMSMessage,
|
||||
NSSCMSMessage,
|
||||
NSS_CMSMessage_Destroy)
|
||||
MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueNSSCMSSignedData,
|
||||
NSSCMSSignedData,
|
||||
NSS_CMSSignedData_Destroy)
|
||||
|
||||
MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniquePK11SlotInfo,
|
||||
PK11SlotInfo,
|
||||
PK11_FreeSlot)
|
||||
MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniquePK11SlotList,
|
||||
PK11SlotList,
|
||||
PK11_FreeSlotList)
|
||||
MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniquePK11SymKey,
|
||||
PK11SymKey,
|
||||
PK11_FreeSymKey)
|
||||
|
||||
MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniquePLArenaPool,
|
||||
PLArenaPool,
|
||||
internal::PORT_FreeArena_false)
|
||||
MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniquePORTString,
|
||||
char,
|
||||
PORT_Free);
|
||||
MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniquePRFileDesc,
|
||||
PRFileDesc,
|
||||
PR_Close)
|
||||
|
||||
MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueSECItem,
|
||||
SECItem,
|
||||
internal::SECITEM_FreeItem_true)
|
||||
MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueSECKEYPrivateKey,
|
||||
SECKEYPrivateKey,
|
||||
SECKEY_DestroyPrivateKey)
|
||||
MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueSECKEYPublicKey,
|
||||
SECKEYPublicKey,
|
||||
SECKEY_DestroyPublicKey)
|
||||
MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueSECMODModule,
|
||||
SECMODModule,
|
||||
SECMOD_DestroyModule)
|
||||
|
||||
MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueVFYContext,
|
||||
VFYContext,
|
||||
internal::VFY_DestroyContext_true)
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // ScopedNSSTypes_h
|
||||
234
security/manager/ssl/SecretDecoderRing.cpp
Normal file
234
security/manager/ssl/SecretDecoderRing.cpp
Normal file
|
|
@ -0,0 +1,234 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "SecretDecoderRing.h"
|
||||
|
||||
#include "ScopedNSSTypes.h"
|
||||
#include "mozilla/Base64.h"
|
||||
#include "mozilla/Casting.h"
|
||||
#include "mozilla/Services.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIInterfaceRequestor.h"
|
||||
#include "nsIInterfaceRequestorUtils.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "nsIServiceManager.h"
|
||||
#include "nsITokenPasswordDialogs.h"
|
||||
#include "nsNSSComponent.h"
|
||||
#include "nsNSSHelper.h"
|
||||
#include "pk11func.h"
|
||||
#include "pk11sdr.h" // For PK11SDR_Encrypt, PK11SDR_Decrypt
|
||||
#include "ssl.h" // For SSL_ClearSessionCache
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
// NOTE: Should these be the thread-safe versions?
|
||||
NS_IMPL_ISUPPORTS(SecretDecoderRing, nsISecretDecoderRing)
|
||||
|
||||
SecretDecoderRing::SecretDecoderRing()
|
||||
{
|
||||
}
|
||||
|
||||
SecretDecoderRing::~SecretDecoderRing()
|
||||
{
|
||||
nsNSSShutDownPreventionLock locker;
|
||||
if (isAlreadyShutDown()) {
|
||||
return;
|
||||
}
|
||||
|
||||
shutdown(ShutdownCalledFrom::Object);
|
||||
}
|
||||
|
||||
nsresult
|
||||
SecretDecoderRing::Encrypt(const nsACString& data, /*out*/ nsACString& result)
|
||||
{
|
||||
nsNSSShutDownPreventionLock locker;
|
||||
if (isAlreadyShutDown()) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
|
||||
if (!slot) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
/* Make sure token is initialized. */
|
||||
nsCOMPtr<nsIInterfaceRequestor> ctx = new PipUIContext();
|
||||
nsresult rv = setPassword(slot.get(), ctx, locker);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
/* Force authentication */
|
||||
if (PK11_Authenticate(slot.get(), true, ctx) != SECSuccess) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
/* Use default key id */
|
||||
SECItem keyid;
|
||||
keyid.data = nullptr;
|
||||
keyid.len = 0;
|
||||
SECItem request;
|
||||
request.data = BitwiseCast<unsigned char*, const char*>(data.BeginReading());
|
||||
request.len = data.Length();
|
||||
ScopedAutoSECItem reply;
|
||||
if (PK11SDR_Encrypt(&keyid, &request, &reply, ctx) != SECSuccess) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
result.Assign(BitwiseCast<char*, unsigned char*>(reply.data), reply.len);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
SecretDecoderRing::Decrypt(const nsACString& data, /*out*/ nsACString& result)
|
||||
{
|
||||
nsNSSShutDownPreventionLock locker;
|
||||
if (isAlreadyShutDown()) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
/* Find token with SDR key */
|
||||
UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
|
||||
if (!slot) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
/* Force authentication */
|
||||
nsCOMPtr<nsIInterfaceRequestor> ctx = new PipUIContext();
|
||||
if (PK11_Authenticate(slot.get(), true, ctx) != SECSuccess) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
SECItem request;
|
||||
request.data = BitwiseCast<unsigned char*, const char*>(data.BeginReading());
|
||||
request.len = data.Length();
|
||||
ScopedAutoSECItem reply;
|
||||
if (PK11SDR_Decrypt(&request, &reply, ctx) != SECSuccess) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
result.Assign(BitwiseCast<char*, unsigned char*>(reply.data), reply.len);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
SecretDecoderRing::EncryptString(const nsACString& text,
|
||||
/*out*/ nsACString& encryptedBase64Text)
|
||||
{
|
||||
nsAutoCString encryptedText;
|
||||
nsresult rv = Encrypt(text, encryptedText);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = Base64Encode(encryptedText, encryptedBase64Text);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
SecretDecoderRing::DecryptString(const nsACString& encryptedBase64Text,
|
||||
/*out*/ nsACString& decryptedText)
|
||||
{
|
||||
nsAutoCString encryptedText;
|
||||
nsresult rv = Base64Decode(encryptedBase64Text, encryptedText);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = Decrypt(encryptedText, decryptedText);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
SecretDecoderRing::ChangePassword()
|
||||
{
|
||||
nsNSSShutDownPreventionLock locker;
|
||||
if (isAlreadyShutDown()) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
|
||||
if (!slot) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
NS_ConvertUTF8toUTF16 tokenName(PK11_GetTokenName(slot.get()));
|
||||
|
||||
nsCOMPtr<nsITokenPasswordDialogs> dialogs;
|
||||
nsresult rv = getNSSDialogs(getter_AddRefs(dialogs),
|
||||
NS_GET_IID(nsITokenPasswordDialogs),
|
||||
NS_TOKENPASSWORDSDIALOG_CONTRACTID);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIInterfaceRequestor> ctx = new PipUIContext();
|
||||
bool canceled; // Ignored
|
||||
return dialogs->SetPassword(ctx, tokenName.get(), &canceled);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
SecretDecoderRing::Logout()
|
||||
{
|
||||
static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID);
|
||||
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsINSSComponent> nssComponent(do_GetService(kNSSComponentCID, &rv));
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
{
|
||||
nsNSSShutDownPreventionLock locker;
|
||||
if (isAlreadyShutDown()) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
PK11_LogoutAll();
|
||||
SSL_ClearSessionCache();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
SecretDecoderRing::LogoutAndTeardown()
|
||||
{
|
||||
static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID);
|
||||
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsINSSComponent> nssComponent(do_GetService(kNSSComponentCID, &rv));
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
{
|
||||
nsNSSShutDownPreventionLock locker;
|
||||
if (isAlreadyShutDown()) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
PK11_LogoutAll();
|
||||
SSL_ClearSessionCache();
|
||||
}
|
||||
|
||||
rv = nssComponent->LogoutAuthenticatedPK11();
|
||||
|
||||
// After we just logged out, we need to prune dead connections to make
|
||||
// sure that all connections that should be stopped, are stopped. See
|
||||
// bug 517584.
|
||||
nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
|
||||
if (os)
|
||||
os->NotifyObservers(nullptr, "net:prune-dead-connections", nullptr);
|
||||
|
||||
return rv;
|
||||
}
|
||||
39
security/manager/ssl/SecretDecoderRing.h
Normal file
39
security/manager/ssl/SecretDecoderRing.h
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef SecretDecoderRing_h
|
||||
#define SecretDecoderRing_h
|
||||
|
||||
#include "nsISecretDecoderRing.h"
|
||||
#include "nsNSSShutDown.h"
|
||||
#include "nsString.h"
|
||||
|
||||
#define NS_SECRETDECODERRING_CONTRACTID "@mozilla.org/security/sdr;1"
|
||||
|
||||
#define NS_SECRETDECODERRING_CID \
|
||||
{ 0x0c4f1ddc, 0x1dd2, 0x11b2, { 0x9d, 0x95, 0xf2, 0xfd, 0xf1, 0x13, 0x04, 0x4b } }
|
||||
|
||||
class SecretDecoderRing : public nsISecretDecoderRing
|
||||
, public nsNSSShutDownObject
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSISECRETDECODERRING
|
||||
|
||||
SecretDecoderRing();
|
||||
|
||||
// Nothing to release.
|
||||
virtual void virtualDestroyNSSReference() override {}
|
||||
|
||||
protected:
|
||||
virtual ~SecretDecoderRing();
|
||||
|
||||
private:
|
||||
nsresult Encrypt(const nsACString& data, /*out*/ nsACString& result);
|
||||
nsresult Decrypt(const nsACString& data, /*out*/ nsACString& result);
|
||||
};
|
||||
|
||||
#endif // SecretDecoderRing_h
|
||||
36
security/manager/ssl/SharedCertVerifier.h
Normal file
36
security/manager/ssl/SharedCertVerifier.h
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef SharedCertVerifier_h
|
||||
#define SharedCertVerifier_h
|
||||
|
||||
#include "CertVerifier.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
|
||||
namespace mozilla { namespace psm {
|
||||
|
||||
class SharedCertVerifier : public mozilla::psm::CertVerifier
|
||||
{
|
||||
protected:
|
||||
~SharedCertVerifier();
|
||||
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SharedCertVerifier)
|
||||
|
||||
SharedCertVerifier(OcspDownloadConfig odc, OcspStrictConfig osc,
|
||||
OcspGetConfig ogc, uint32_t certShortLifetimeInDays,
|
||||
PinningMode pinningMode, SHA1Mode sha1Mode,
|
||||
BRNameMatchingPolicy::Mode nameMatchingMode,
|
||||
NetscapeStepUpPolicy netscapeStepUpPolicy,
|
||||
CertificateTransparencyMode ctMode)
|
||||
: mozilla::psm::CertVerifier(odc, osc, ogc, certShortLifetimeInDays,
|
||||
pinningMode, sha1Mode, nameMatchingMode,
|
||||
netscapeStepUpPolicy, ctMode)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
} } // namespace mozilla::psm
|
||||
|
||||
#endif // SharedCertVerifier_h
|
||||
218
security/manager/ssl/SharedSSLState.cpp
Normal file
218
security/manager/ssl/SharedSSLState.cpp
Normal file
|
|
@ -0,0 +1,218 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "SharedSSLState.h"
|
||||
#include "nsClientAuthRemember.h"
|
||||
#include "nsComponentManagerUtils.h"
|
||||
#include "nsICertOverrideService.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "mozilla/Services.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsCRT.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "PSMRunnable.h"
|
||||
#include "PublicSSL.h"
|
||||
#include "ssl.h"
|
||||
#include "nsNetCID.h"
|
||||
#include "mozilla/Atomics.h"
|
||||
#include "mozilla/Unused.h"
|
||||
|
||||
using mozilla::psm::SyncRunnableBase;
|
||||
using mozilla::Atomic;
|
||||
using mozilla::Unused;
|
||||
|
||||
namespace {
|
||||
|
||||
static Atomic<bool> sCertOverrideSvcExists(false);
|
||||
|
||||
class MainThreadClearer : public SyncRunnableBase
|
||||
{
|
||||
public:
|
||||
MainThreadClearer() : mShouldClearSessionCache(false) {}
|
||||
|
||||
void RunOnTargetThread() {
|
||||
// In some cases it's possible to cause PSM/NSS to initialize while XPCOM shutdown
|
||||
// is in progress. We want to avoid this, since they do not handle the situation well,
|
||||
// hence the flags to avoid instantiating the services if they don't already exist.
|
||||
|
||||
bool certOverrideSvcExists = sCertOverrideSvcExists.exchange(false);
|
||||
if (certOverrideSvcExists) {
|
||||
sCertOverrideSvcExists = true;
|
||||
nsCOMPtr<nsICertOverrideService> icos = do_GetService(NS_CERTOVERRIDE_CONTRACTID);
|
||||
if (icos) {
|
||||
icos->ClearValidityOverride(
|
||||
NS_LITERAL_CSTRING("all:temporary-certificates"),
|
||||
0);
|
||||
}
|
||||
}
|
||||
|
||||
// This needs to be checked on the main thread to avoid racing with NSS
|
||||
// initialization.
|
||||
mShouldClearSessionCache = mozilla::psm::PrivateSSLState() &&
|
||||
mozilla::psm::PrivateSSLState()->SocketCreated();
|
||||
}
|
||||
bool mShouldClearSessionCache;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
void ClearPrivateSSLState()
|
||||
{
|
||||
// This only works if it is called on the socket transport
|
||||
// service thread immediately after closing all private SSL
|
||||
// connections.
|
||||
#ifdef DEBUG
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIEventTarget> sts
|
||||
= do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
|
||||
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
||||
bool onSTSThread;
|
||||
rv = sts->IsOnCurrentThread(&onSTSThread);
|
||||
MOZ_ASSERT(NS_SUCCEEDED(rv) && onSTSThread);
|
||||
#endif
|
||||
|
||||
RefPtr<MainThreadClearer> runnable = new MainThreadClearer;
|
||||
runnable->DispatchToMainThreadAndWait();
|
||||
|
||||
// If NSS isn't initialized, this throws an assertion. We guard it by checking if
|
||||
// the session cache might even have anything worth clearing.
|
||||
if (runnable->mShouldClearSessionCache) {
|
||||
SSL_ClearSessionCache();
|
||||
}
|
||||
}
|
||||
|
||||
namespace psm {
|
||||
|
||||
namespace {
|
||||
class PrivateBrowsingObserver : public nsIObserver {
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIOBSERVER
|
||||
explicit PrivateBrowsingObserver(SharedSSLState* aOwner) : mOwner(aOwner) {}
|
||||
protected:
|
||||
virtual ~PrivateBrowsingObserver() {}
|
||||
private:
|
||||
SharedSSLState* mOwner;
|
||||
};
|
||||
|
||||
SharedSSLState* gPublicState;
|
||||
SharedSSLState* gPrivateState;
|
||||
} // namespace
|
||||
|
||||
NS_IMPL_ISUPPORTS(PrivateBrowsingObserver, nsIObserver)
|
||||
|
||||
NS_IMETHODIMP
|
||||
PrivateBrowsingObserver::Observe(nsISupports *aSubject,
|
||||
const char *aTopic,
|
||||
const char16_t *aData)
|
||||
{
|
||||
if (!nsCRT::strcmp(aTopic, "last-pb-context-exited")) {
|
||||
mOwner->ResetStoredData();
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
SharedSSLState::SharedSSLState()
|
||||
: mClientAuthRemember(new nsClientAuthRememberService)
|
||||
, mMutex("SharedSSLState::mMutex")
|
||||
, mSocketCreated(false)
|
||||
, mOCSPStaplingEnabled(false)
|
||||
, mOCSPMustStapleEnabled(false)
|
||||
{
|
||||
mIOLayerHelpers.Init();
|
||||
mClientAuthRemember->Init();
|
||||
}
|
||||
|
||||
SharedSSLState::~SharedSSLState()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
SharedSSLState::NotePrivateBrowsingStatus()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Not on main thread");
|
||||
mObserver = new PrivateBrowsingObserver(this);
|
||||
nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService();
|
||||
obsSvc->AddObserver(mObserver, "last-pb-context-exited", false);
|
||||
}
|
||||
|
||||
void
|
||||
SharedSSLState::ResetStoredData()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Not on main thread");
|
||||
mClientAuthRemember->ClearRememberedDecisions();
|
||||
mIOLayerHelpers.clearStoredData();
|
||||
}
|
||||
|
||||
void
|
||||
SharedSSLState::NoteSocketCreated()
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
mSocketCreated = true;
|
||||
}
|
||||
|
||||
bool
|
||||
SharedSSLState::SocketCreated()
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
return mSocketCreated;
|
||||
}
|
||||
|
||||
/*static*/ void
|
||||
SharedSSLState::GlobalInit()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Not on main thread");
|
||||
gPublicState = new SharedSSLState();
|
||||
gPrivateState = new SharedSSLState();
|
||||
gPrivateState->NotePrivateBrowsingStatus();
|
||||
}
|
||||
|
||||
/*static*/ void
|
||||
SharedSSLState::GlobalCleanup()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Not on main thread");
|
||||
|
||||
if (gPrivateState) {
|
||||
gPrivateState->Cleanup();
|
||||
delete gPrivateState;
|
||||
gPrivateState = nullptr;
|
||||
}
|
||||
|
||||
if (gPublicState) {
|
||||
gPublicState->Cleanup();
|
||||
delete gPublicState;
|
||||
gPublicState = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/*static*/ void
|
||||
SharedSSLState::NoteCertOverrideServiceInstantiated()
|
||||
{
|
||||
sCertOverrideSvcExists = true;
|
||||
}
|
||||
|
||||
void
|
||||
SharedSSLState::Cleanup()
|
||||
{
|
||||
mIOLayerHelpers.Cleanup();
|
||||
}
|
||||
|
||||
SharedSSLState*
|
||||
PublicSSLState()
|
||||
{
|
||||
return gPublicState;
|
||||
}
|
||||
|
||||
SharedSSLState*
|
||||
PrivateSSLState()
|
||||
{
|
||||
return gPrivateState;
|
||||
}
|
||||
|
||||
} // namespace psm
|
||||
} // namespace mozilla
|
||||
87
security/manager/ssl/SharedSSLState.h
Normal file
87
security/manager/ssl/SharedSSLState.h
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef SharedSSLState_h
|
||||
#define SharedSSLState_h
|
||||
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "nsNSSIOLayer.h"
|
||||
|
||||
class nsClientAuthRememberService;
|
||||
class nsIObserver;
|
||||
|
||||
namespace mozilla {
|
||||
namespace psm {
|
||||
|
||||
class SharedSSLState {
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SharedSSLState)
|
||||
SharedSSLState();
|
||||
|
||||
static void GlobalInit();
|
||||
static void GlobalCleanup();
|
||||
|
||||
nsClientAuthRememberService* GetClientAuthRememberService() {
|
||||
return mClientAuthRemember;
|
||||
}
|
||||
|
||||
nsSSLIOLayerHelpers& IOLayerHelpers() {
|
||||
return mIOLayerHelpers;
|
||||
}
|
||||
|
||||
// Main-thread only
|
||||
void ResetStoredData();
|
||||
void NotePrivateBrowsingStatus();
|
||||
void SetOCSPStaplingEnabled(bool staplingEnabled)
|
||||
{
|
||||
mOCSPStaplingEnabled = staplingEnabled;
|
||||
}
|
||||
void SetOCSPMustStapleEnabled(bool mustStapleEnabled)
|
||||
{
|
||||
mOCSPMustStapleEnabled = mustStapleEnabled;
|
||||
}
|
||||
void SetSignedCertTimestampsEnabled(bool signedCertTimestampsEnabled)
|
||||
{
|
||||
mSignedCertTimestampsEnabled = signedCertTimestampsEnabled;
|
||||
}
|
||||
|
||||
// The following methods may be called from any thread
|
||||
bool SocketCreated();
|
||||
void NoteSocketCreated();
|
||||
static void NoteCertOverrideServiceInstantiated();
|
||||
bool IsOCSPStaplingEnabled() const { return mOCSPStaplingEnabled; }
|
||||
bool IsOCSPMustStapleEnabled() const { return mOCSPMustStapleEnabled; }
|
||||
bool IsSignedCertTimestampsEnabled() const
|
||||
{
|
||||
return mSignedCertTimestampsEnabled;
|
||||
}
|
||||
|
||||
private:
|
||||
~SharedSSLState();
|
||||
|
||||
void Cleanup();
|
||||
|
||||
nsCOMPtr<nsIObserver> mObserver;
|
||||
RefPtr<nsClientAuthRememberService> mClientAuthRemember;
|
||||
nsSSLIOLayerHelpers mIOLayerHelpers;
|
||||
|
||||
// True if any sockets have been created that use this shared data.
|
||||
// Requires synchronization between the socket and main threads for
|
||||
// reading/writing.
|
||||
Mutex mMutex;
|
||||
bool mSocketCreated;
|
||||
bool mOCSPStaplingEnabled;
|
||||
bool mOCSPMustStapleEnabled;
|
||||
bool mSignedCertTimestampsEnabled;
|
||||
};
|
||||
|
||||
SharedSSLState* PublicSSLState();
|
||||
SharedSSLState* PrivateSSLState();
|
||||
|
||||
} // namespace psm
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
28
security/manager/ssl/StaticHPKPins.errors
Normal file
28
security/manager/ssl/StaticHPKPins.errors
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
Can't find hash in builtin certs for Chrome nickname GoogleG2, inserting GOOGLE_PIN_GoogleG2
|
||||
Can't find hash in builtin certs for Chrome nickname RapidSSL, inserting GOOGLE_PIN_RapidSSL
|
||||
Can't find hash in builtin certs for Chrome nickname DigiCertSHA2HighAssuranceServerCA, inserting GOOGLE_PIN_DigiCertSHA2HighAssuranceServerCA
|
||||
Can't find hash in builtin certs for Chrome nickname VeriSignClass1, inserting GOOGLE_PIN_VeriSignClass1
|
||||
Can't find hash in builtin certs for Chrome nickname VeriSignClass4_G3, inserting GOOGLE_PIN_VeriSignClass4_G3
|
||||
Can't find hash in builtin certs for Chrome nickname VeriSignClass3_G2, inserting GOOGLE_PIN_VeriSignClass3_G2
|
||||
Can't find hash in builtin certs for Chrome nickname VeriSignClass2_G2, inserting GOOGLE_PIN_VeriSignClass2_G2
|
||||
Can't find hash in builtin certs for Chrome nickname Entrust_SSL, inserting GOOGLE_PIN_Entrust_SSL
|
||||
Can't find hash in builtin certs for Chrome nickname UTNDATACorpSGC, inserting GOOGLE_PIN_UTNDATACorpSGC
|
||||
Can't find hash in builtin certs for Chrome nickname GTECyberTrustGlobalRoot, inserting GOOGLE_PIN_GTECyberTrustGlobalRoot
|
||||
Can't find hash in builtin certs for Chrome nickname GoDaddySecure, inserting GOOGLE_PIN_GoDaddySecure
|
||||
Can't find hash in builtin certs for Chrome nickname SymantecClass3EVG3, inserting GOOGLE_PIN_SymantecClass3EVG3
|
||||
Can't find hash in builtin certs for Chrome nickname DigiCertECCSecureServerCA, inserting GOOGLE_PIN_DigiCertECCSecureServerCA
|
||||
Can't find hash in builtin certs for Chrome nickname LetsEncryptAuthorityPrimary_X1_X3, inserting GOOGLE_PIN_LetsEncryptAuthorityPrimary_X1_X3
|
||||
Can't find hash in builtin certs for Chrome nickname LetsEncryptAuthorityBackup_X2_X4, inserting GOOGLE_PIN_LetsEncryptAuthorityBackup_X2_X4
|
||||
Can't find hash in builtin certs for Chrome nickname COMODORSADomainValidationSecureServerCA, inserting GOOGLE_PIN_COMODORSADomainValidationSecureServerCA
|
||||
Writing pinset test
|
||||
Writing pinset google
|
||||
Writing pinset tor
|
||||
Writing pinset twitterCom
|
||||
Writing pinset twitterCDN
|
||||
Writing pinset dropbox
|
||||
Writing pinset facebook
|
||||
Writing pinset spideroak
|
||||
Writing pinset yahoo
|
||||
Writing pinset swehackCom
|
||||
Writing pinset ncsccs
|
||||
Writing pinset tumblr
|
||||
1185
security/manager/ssl/StaticHPKPins.h
Normal file
1185
security/manager/ssl/StaticHPKPins.h
Normal file
File diff suppressed because it is too large
Load diff
1100
security/manager/ssl/TransportSecurityInfo.cpp
Normal file
1100
security/manager/ssl/TransportSecurityInfo.cpp
Normal file
File diff suppressed because it is too large
Load diff
171
security/manager/ssl/TransportSecurityInfo.h
Normal file
171
security/manager/ssl/TransportSecurityInfo.h
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef TransportSecurityInfo_h
|
||||
#define TransportSecurityInfo_h
|
||||
|
||||
#include "ScopedNSSTypes.h"
|
||||
#include "certt.h"
|
||||
#include "mozilla/BasePrincipal.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "nsDataHashtable.h"
|
||||
#include "nsIAssociatedContentSecurity.h"
|
||||
#include "nsIInterfaceRequestor.h"
|
||||
#include "nsISSLStatusProvider.h"
|
||||
#include "nsITransportSecurityInfo.h"
|
||||
#include "nsNSSShutDown.h"
|
||||
#include "nsSSLStatus.h"
|
||||
#include "pkix/pkixtypes.h"
|
||||
|
||||
namespace mozilla { namespace psm {
|
||||
|
||||
enum SSLErrorMessageType {
|
||||
OverridableCertErrorMessage = 1, // for *overridable* certificate errors
|
||||
PlainErrorMessage = 2 // all other errors (or "no error")
|
||||
};
|
||||
|
||||
class TransportSecurityInfo : public nsITransportSecurityInfo,
|
||||
public nsIInterfaceRequestor,
|
||||
public nsISSLStatusProvider,
|
||||
public nsIAssociatedContentSecurity,
|
||||
public nsISerializable,
|
||||
public nsIClassInfo,
|
||||
public nsNSSShutDownObject,
|
||||
public nsOnPK11LogoutCancelObject
|
||||
{
|
||||
protected:
|
||||
virtual ~TransportSecurityInfo();
|
||||
public:
|
||||
TransportSecurityInfo();
|
||||
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSITRANSPORTSECURITYINFO
|
||||
NS_DECL_NSIINTERFACEREQUESTOR
|
||||
NS_DECL_NSISSLSTATUSPROVIDER
|
||||
NS_DECL_NSIASSOCIATEDCONTENTSECURITY
|
||||
NS_DECL_NSISERIALIZABLE
|
||||
NS_DECL_NSICLASSINFO
|
||||
|
||||
nsresult SetSecurityState(uint32_t aState);
|
||||
nsresult SetShortSecurityDescription(const char16_t *aText);
|
||||
|
||||
const nsACString & GetHostName() const { return mHostName; }
|
||||
const char * GetHostNameRaw() const { return mHostName.get(); }
|
||||
|
||||
nsresult GetHostName(char **aHostName);
|
||||
nsresult SetHostName(const char *aHostName);
|
||||
|
||||
int32_t GetPort() const { return mPort; }
|
||||
nsresult GetPort(int32_t *aPort);
|
||||
nsresult SetPort(int32_t aPort);
|
||||
|
||||
const NeckoOriginAttributes& GetOriginAttributes() const {
|
||||
return mOriginAttributes;
|
||||
}
|
||||
nsresult SetOriginAttributes(const NeckoOriginAttributes& aOriginAttributes);
|
||||
|
||||
PRErrorCode GetErrorCode() const;
|
||||
|
||||
void GetErrorLogMessage(PRErrorCode errorCode,
|
||||
::mozilla::psm::SSLErrorMessageType errorMessageType,
|
||||
nsString &result);
|
||||
|
||||
void SetCanceled(PRErrorCode errorCode,
|
||||
::mozilla::psm::SSLErrorMessageType errorMessageType);
|
||||
|
||||
/* Set SSL Status values */
|
||||
nsresult SetSSLStatus(nsSSLStatus *aSSLStatus);
|
||||
nsSSLStatus* SSLStatus() { return mSSLStatus; }
|
||||
void SetStatusErrorBits(nsNSSCertificate* cert, uint32_t collected_errors);
|
||||
|
||||
nsresult SetFailedCertChain(UniqueCERTCertList certList);
|
||||
|
||||
private:
|
||||
mutable ::mozilla::Mutex mMutex;
|
||||
|
||||
protected:
|
||||
nsCOMPtr<nsIInterfaceRequestor> mCallbacks;
|
||||
|
||||
private:
|
||||
uint32_t mSecurityState;
|
||||
int32_t mSubRequestsBrokenSecurity;
|
||||
int32_t mSubRequestsNoSecurity;
|
||||
|
||||
PRErrorCode mErrorCode;
|
||||
::mozilla::psm::SSLErrorMessageType mErrorMessageType;
|
||||
nsString mErrorMessageCached;
|
||||
nsresult formatErrorMessage(::mozilla::MutexAutoLock const & proofOfLock,
|
||||
PRErrorCode errorCode,
|
||||
::mozilla::psm::SSLErrorMessageType errorMessageType,
|
||||
bool wantsHtml, bool suppressPort443,
|
||||
nsString &result);
|
||||
|
||||
int32_t mPort;
|
||||
nsXPIDLCString mHostName;
|
||||
NeckoOriginAttributes mOriginAttributes;
|
||||
|
||||
/* SSL Status */
|
||||
RefPtr<nsSSLStatus> mSSLStatus;
|
||||
|
||||
/* Peer cert chain for failed connections (for error reporting) */
|
||||
nsCOMPtr<nsIX509CertList> mFailedCertChain;
|
||||
|
||||
virtual void virtualDestroyNSSReference() override;
|
||||
void destructorSafeDestroyNSSReference();
|
||||
};
|
||||
|
||||
class RememberCertErrorsTable
|
||||
{
|
||||
private:
|
||||
RememberCertErrorsTable();
|
||||
|
||||
struct CertStateBits
|
||||
{
|
||||
bool mIsDomainMismatch;
|
||||
bool mIsNotValidAtThisTime;
|
||||
bool mIsUntrusted;
|
||||
};
|
||||
nsDataHashtable<nsCStringHashKey, CertStateBits> mErrorHosts;
|
||||
|
||||
public:
|
||||
void RememberCertHasError(TransportSecurityInfo * infoobject,
|
||||
nsSSLStatus * status,
|
||||
SECStatus certVerificationResult);
|
||||
void LookupCertErrorBits(TransportSecurityInfo * infoObject,
|
||||
nsSSLStatus* status);
|
||||
|
||||
static nsresult Init()
|
||||
{
|
||||
sInstance = new RememberCertErrorsTable();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
static RememberCertErrorsTable & GetInstance()
|
||||
{
|
||||
MOZ_ASSERT(sInstance);
|
||||
return *sInstance;
|
||||
}
|
||||
|
||||
static void Cleanup()
|
||||
{
|
||||
delete sInstance;
|
||||
sInstance = nullptr;
|
||||
}
|
||||
private:
|
||||
Mutex mMutex;
|
||||
|
||||
static RememberCertErrorsTable * sInstance;
|
||||
};
|
||||
|
||||
} } // namespace mozilla::psm
|
||||
|
||||
// 16786594-0296-4471-8096-8f84497ca428
|
||||
#define TRANSPORTSECURITYINFO_CID \
|
||||
{ 0x16786594, 0x0296, 0x4471, \
|
||||
{ 0x80, 0x96, 0x8f, 0x84, 0x49, 0x7c, 0xa4, 0x28 } }
|
||||
|
||||
#endif // TransportSecurityInfo_h
|
||||
67
security/manager/ssl/WeakCryptoOverride.cpp
Normal file
67
security/manager/ssl/WeakCryptoOverride.cpp
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "WeakCryptoOverride.h"
|
||||
|
||||
#include "MainThreadUtils.h"
|
||||
#include "SharedSSLState.h"
|
||||
#include "nss.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::psm;
|
||||
|
||||
NS_IMPL_ISUPPORTS(WeakCryptoOverride,
|
||||
nsIWeakCryptoOverride)
|
||||
|
||||
WeakCryptoOverride::WeakCryptoOverride()
|
||||
{
|
||||
}
|
||||
|
||||
WeakCryptoOverride::~WeakCryptoOverride()
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WeakCryptoOverride::AddWeakCryptoOverride(const nsACString& aHostName,
|
||||
bool aPrivate, bool aTemporary)
|
||||
{
|
||||
if (!NS_IsMainThread()) {
|
||||
return NS_ERROR_NOT_SAME_THREAD;
|
||||
}
|
||||
|
||||
SharedSSLState* sharedState = aPrivate ? PrivateSSLState()
|
||||
: PublicSSLState();
|
||||
if (!sharedState) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
const nsPromiseFlatCString& host = PromiseFlatCString(aHostName);
|
||||
sharedState->IOLayerHelpers().addInsecureFallbackSite(host, aTemporary);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WeakCryptoOverride::RemoveWeakCryptoOverride(const nsACString& aHostName,
|
||||
int32_t aPort, bool aPrivate)
|
||||
{
|
||||
if (!NS_IsMainThread()) {
|
||||
return NS_ERROR_NOT_SAME_THREAD;
|
||||
}
|
||||
|
||||
SharedSSLState* sharedState = aPrivate ? PrivateSSLState()
|
||||
: PublicSSLState();
|
||||
if (!sharedState) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
const nsPromiseFlatCString& host = PromiseFlatCString(aHostName);
|
||||
sharedState->IOLayerHelpers().removeInsecureFallbackSite(host, aPort);
|
||||
|
||||
// Some servers will fail with SSL_ERROR_ILLEGAL_PARAMETER_ALERT
|
||||
// unless the session cache is cleared.
|
||||
SSL_ClearSessionCache();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
35
security/manager/ssl/WeakCryptoOverride.h
Normal file
35
security/manager/ssl/WeakCryptoOverride.h
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef WEAKCRYPTOOVERRIDE_H
|
||||
#define WEAKCRYPTOOVERRIDE_H
|
||||
|
||||
#include "nsIWeakCryptoOverride.h"
|
||||
#include "nsWeakReference.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace psm {
|
||||
|
||||
class WeakCryptoOverride final : public nsIWeakCryptoOverride
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIWEAKCRYPTOOVERRIDE
|
||||
|
||||
WeakCryptoOverride();
|
||||
|
||||
protected:
|
||||
~WeakCryptoOverride();
|
||||
};
|
||||
|
||||
} // psm
|
||||
} // mozilla
|
||||
|
||||
#define NS_WEAKCRYPTOOVERRIDE_CID /* ffb06724-3c20-447c-8328-ae71513dd618 */ \
|
||||
{ 0xffb06724, 0x3c20, 0x447c, \
|
||||
{ 0x83, 0x28, 0xae, 0x71, 0x51, 0x3d, 0xd6, 0x18 } }
|
||||
|
||||
#endif
|
||||
632
security/manager/ssl/X509.jsm
Normal file
632
security/manager/ssl/X509.jsm
Normal file
|
|
@ -0,0 +1,632 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const Cu = Components.utils;
|
||||
var { DER } = Cu.import("resource://gre/modules/psm/DER.jsm", {});
|
||||
|
||||
const ERROR_UNSUPPORTED_ASN1 = "unsupported asn.1";
|
||||
const ERROR_TIME_NOT_VALID = "Time not valid";
|
||||
const ERROR_LIBRARY_FAILURE = "library failure";
|
||||
|
||||
const X509v3 = 2;
|
||||
|
||||
/**
|
||||
* Helper function to read a NULL tag from the given DER.
|
||||
* @param {DER} der a DER object to read a NULL from
|
||||
* @return {NULL} an object representing an ASN.1 NULL
|
||||
*/
|
||||
function readNULL(der) {
|
||||
return new NULL(der.readTagAndGetContents(DER.NULL));
|
||||
}
|
||||
|
||||
/**
|
||||
* Class representing an ASN.1 NULL. When encoded as DER, the only valid value
|
||||
* is 05 00, and thus the contents should always be an empty array.
|
||||
*/
|
||||
class NULL {
|
||||
/**
|
||||
* @param {Number[]} bytes the contents of the NULL tag (should be empty)
|
||||
*/
|
||||
constructor(bytes) {
|
||||
// Lint TODO: bytes should be an empty array
|
||||
this._contents = bytes;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to read an OBJECT IDENTIFIER from the given DER.
|
||||
* @param {DER} der the DER to read an OBJECT IDENTIFIER from
|
||||
* @return {OID} the value of the OBJECT IDENTIFIER
|
||||
*/
|
||||
function readOID(der) {
|
||||
return new OID(der.readTagAndGetContents(DER.OBJECT_IDENTIFIER));
|
||||
}
|
||||
|
||||
/** Class representing an ASN.1 OBJECT IDENTIFIER */
|
||||
class OID {
|
||||
/**
|
||||
* @param {Number[]} bytes the encoded contents of the OBJECT IDENTIFIER
|
||||
* (not including the ASN.1 tag or length bytes)
|
||||
*/
|
||||
constructor(bytes) {
|
||||
this._values = [];
|
||||
// First octet has value 40 * value1 + value2
|
||||
// Lint TODO: validate that value1 is one of {0, 1, 2}
|
||||
// Lint TODO: validate that value2 is in [0, 39] if value1 is 0 or 1
|
||||
let value1 = Math.floor(bytes[0] / 40);
|
||||
let value2 = bytes[0] - 40 * value1;
|
||||
this._values.push(value1);
|
||||
this._values.push(value2);
|
||||
bytes.shift();
|
||||
let accumulator = 0;
|
||||
// Lint TODO: prevent overflow here
|
||||
while (bytes.length > 0) {
|
||||
let value = bytes.shift();
|
||||
accumulator *= 128;
|
||||
if (value > 128) {
|
||||
accumulator += (value - 128);
|
||||
} else {
|
||||
accumulator += value;
|
||||
this._values.push(accumulator);
|
||||
accumulator = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class that serves as an abstract base class for more specific classes that
|
||||
* represent datatypes from RFC 5280 and others. Given an array of bytes
|
||||
* representing the DER encoding of such types, this framework simplifies the
|
||||
* process of making a new DER object, attempting to parse the given bytes, and
|
||||
* catching and stashing thrown exceptions. Subclasses are to implement
|
||||
* parseOverride, which should read from this._der to fill out the structure's
|
||||
* values.
|
||||
*/
|
||||
class DecodedDER {
|
||||
constructor() {
|
||||
this._der = null;
|
||||
this._error = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the first exception encountered when decoding or null if none has
|
||||
* been encountered.
|
||||
* @return {Error} the first exception encountered when decoding or null
|
||||
*/
|
||||
get error() {
|
||||
return this._error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the actual work of parsing the data. To be overridden by subclasses.
|
||||
* If an implementation of parseOverride throws an exception, parse will catch
|
||||
* that exception and stash it in the error property. This enables parent
|
||||
* levels in a nested decoding hierarchy to continue to decode as much as
|
||||
* possible.
|
||||
*/
|
||||
parseOverride() {
|
||||
throw new Error(ERROR_LIBRARY_FAILURE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Public interface to be called to parse all data. Calls parseOverride inside
|
||||
* a try/catch block. If an exception is thrown, stashes the error, which can
|
||||
* be obtained via the error getter (above).
|
||||
* @param {Number[]} bytes encoded DER to be decoded
|
||||
*/
|
||||
parse(bytes) {
|
||||
this._der = new DER.DER(bytes);
|
||||
try {
|
||||
this.parseOverride();
|
||||
} catch (e) {
|
||||
this._error = e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for reading the next SEQUENCE out of a DER and creating a new
|
||||
* DER out of the resulting bytes.
|
||||
* @param {DER} der the underlying DER object
|
||||
* @return {DER} the contents of the SEQUENCE
|
||||
*/
|
||||
function readSEQUENCEAndMakeDER(der) {
|
||||
return new DER.DER(der.readTagAndGetContents(DER.SEQUENCE));
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for reading the next item identified by tag out of a DER and
|
||||
* creating a new DER out of the resulting bytes.
|
||||
* @param {DER} der the underlying DER object
|
||||
* @param {Number} tag the expected next tag in the DER
|
||||
* @return {DER} the contents of the tag
|
||||
*/
|
||||
function readTagAndMakeDER(der, tag) {
|
||||
return new DER.DER(der.readTagAndGetContents(tag));
|
||||
}
|
||||
|
||||
// Certificate ::= SEQUENCE {
|
||||
// tbsCertificate TBSCertificate,
|
||||
// signatureAlgorithm AlgorithmIdentifier,
|
||||
// signatureValue BIT STRING }
|
||||
class Certificate extends DecodedDER {
|
||||
constructor() {
|
||||
super();
|
||||
this._tbsCertificate = new TBSCertificate();
|
||||
this._signatureAlgorithm = new AlgorithmIdentifier();
|
||||
this._signatureValue = [];
|
||||
}
|
||||
|
||||
get tbsCertificate() {
|
||||
return this._tbsCertificate;
|
||||
}
|
||||
|
||||
get signatureAlgorithm() {
|
||||
return this._signatureAlgorithm;
|
||||
}
|
||||
|
||||
get signatureValue() {
|
||||
return this._signatureValue;
|
||||
}
|
||||
|
||||
parseOverride() {
|
||||
let contents = readSEQUENCEAndMakeDER(this._der);
|
||||
this._tbsCertificate.parse(contents.readTLV());
|
||||
this._signatureAlgorithm.parse(contents.readTLV());
|
||||
|
||||
let signatureValue = contents.readBIT_STRING();
|
||||
if (signatureValue.unusedBits != 0) {
|
||||
throw new Error(ERROR_UNSUPPORTED_ASN1);
|
||||
}
|
||||
this._signatureValue = signatureValue.contents;
|
||||
contents.assertAtEnd();
|
||||
this._der.assertAtEnd();
|
||||
}
|
||||
}
|
||||
|
||||
// TBSCertificate ::= SEQUENCE {
|
||||
// version [0] EXPLICIT Version DEFAULT v1,
|
||||
// serialNumber CertificateSerialNumber,
|
||||
// signature AlgorithmIdentifier,
|
||||
// issuer Name,
|
||||
// validity Validity,
|
||||
// subject Name,
|
||||
// subjectPublicKeyInfo SubjectPublicKeyInfo,
|
||||
// issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL,
|
||||
// -- If present, version MUST be v2 or v3
|
||||
// subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL,
|
||||
// -- If present, version MUST be v2 or v3
|
||||
// extensions [3] EXPLICIT Extensions OPTIONAL
|
||||
// -- If present, version MUST be v3
|
||||
// }
|
||||
class TBSCertificate extends DecodedDER {
|
||||
constructor() {
|
||||
super();
|
||||
this._version = null;
|
||||
this._serialNumber = [];
|
||||
this._signature = new AlgorithmIdentifier();
|
||||
this._issuer = new Name();
|
||||
this._validity = new Validity();
|
||||
this._subject = new Name();
|
||||
this._subjectPublicKeyInfo = new SubjectPublicKeyInfo();
|
||||
this._extensions = [];
|
||||
}
|
||||
|
||||
get version() {
|
||||
return this._version;
|
||||
}
|
||||
|
||||
get serialNumber() {
|
||||
return this._serialNumber;
|
||||
}
|
||||
|
||||
get signature() {
|
||||
return this._signature;
|
||||
}
|
||||
|
||||
get issuer() {
|
||||
return this._issuer;
|
||||
}
|
||||
|
||||
get validity() {
|
||||
return this._validity;
|
||||
}
|
||||
|
||||
get subject() {
|
||||
return this._subject;
|
||||
}
|
||||
|
||||
get subjectPublicKeyInfo() {
|
||||
return this._subjectPublicKeyInfo;
|
||||
}
|
||||
|
||||
get extensions() {
|
||||
return this._extensions;
|
||||
}
|
||||
|
||||
parseOverride() {
|
||||
let contents = readSEQUENCEAndMakeDER(this._der);
|
||||
|
||||
let versionTag = DER.CONTEXT_SPECIFIC | DER.CONSTRUCTED | 0;
|
||||
if (!contents.peekTag(versionTag)) {
|
||||
this._version = 1;
|
||||
} else {
|
||||
let versionContents = readTagAndMakeDER(contents, versionTag);
|
||||
let versionBytes = versionContents.readTagAndGetContents(DER.INTEGER);
|
||||
if (versionBytes.length == 1 && versionBytes[0] == X509v3) {
|
||||
this._version = 3;
|
||||
} else {
|
||||
// Lint TODO: warn about non-v3 certificates (this INTEGER could take up
|
||||
// multiple bytes, be negative, and so on).
|
||||
this._version = versionBytes;
|
||||
}
|
||||
versionContents.assertAtEnd();
|
||||
}
|
||||
|
||||
let serialNumberBytes = contents.readTagAndGetContents(DER.INTEGER);
|
||||
this._serialNumber = serialNumberBytes;
|
||||
this._signature.parse(contents.readTLV());
|
||||
this._issuer.parse(contents.readTLV());
|
||||
this._validity.parse(contents.readTLV());
|
||||
this._subject.parse(contents.readTLV());
|
||||
this._subjectPublicKeyInfo.parse(contents.readTLV());
|
||||
|
||||
// Lint TODO: warn about unsupported features
|
||||
let issuerUniqueIDTag = DER.CONTEXT_SPECIFIC | DER.CONSTRUCTED | 1;
|
||||
if (contents.peekTag(issuerUniqueIDTag)) {
|
||||
contents.readTagAndGetContents(issuerUniqueIDTag);
|
||||
}
|
||||
let subjectUniqueIDTag = DER.CONTEXT_SPECIFIC | DER.CONSTRUCTED | 2;
|
||||
if (contents.peekTag(subjectUniqueIDTag)) {
|
||||
contents.readTagAndGetContents(subjectUniqueIDTag);
|
||||
}
|
||||
|
||||
let extensionsTag = DER.CONTEXT_SPECIFIC | DER.CONSTRUCTED | 3;
|
||||
if (contents.peekTag(extensionsTag)) {
|
||||
let extensionsSequence = readTagAndMakeDER(contents, extensionsTag);
|
||||
let extensionsContents = readSEQUENCEAndMakeDER(extensionsSequence);
|
||||
while (!extensionsContents.atEnd()) {
|
||||
// TODO: parse extensions
|
||||
this._extensions.push(extensionsContents.readTLV());
|
||||
}
|
||||
extensionsContents.assertAtEnd();
|
||||
extensionsSequence.assertAtEnd();
|
||||
}
|
||||
contents.assertAtEnd();
|
||||
this._der.assertAtEnd();
|
||||
}
|
||||
}
|
||||
|
||||
// AlgorithmIdentifier ::= SEQUENCE {
|
||||
// algorithm OBJECT IDENTIFIER,
|
||||
// parameters ANY DEFINED BY algorithm OPTIONAL }
|
||||
class AlgorithmIdentifier extends DecodedDER {
|
||||
constructor() {
|
||||
super();
|
||||
this._algorithm = null;
|
||||
this._parameters = null;
|
||||
}
|
||||
|
||||
get algorithm() {
|
||||
return this._algorithm;
|
||||
}
|
||||
|
||||
get parameters() {
|
||||
return this._parameters;
|
||||
}
|
||||
|
||||
parseOverride() {
|
||||
let contents = readSEQUENCEAndMakeDER(this._der);
|
||||
this._algorithm = readOID(contents);
|
||||
if (!contents.atEnd()) {
|
||||
if (contents.peekTag(DER.NULL)) {
|
||||
this._parameters = readNULL(contents);
|
||||
} else if (contents.peekTag(DER.OBJECT_IDENTIFIER)) {
|
||||
this._parameters = readOID(contents);
|
||||
}
|
||||
}
|
||||
contents.assertAtEnd();
|
||||
this._der.assertAtEnd();
|
||||
}
|
||||
}
|
||||
|
||||
// Name ::= CHOICE { -- only one possibility for now --
|
||||
// rdnSequence RDNSequence }
|
||||
//
|
||||
// RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
|
||||
class Name extends DecodedDER {
|
||||
constructor() {
|
||||
super();
|
||||
this._rdns = [];
|
||||
}
|
||||
|
||||
get rdns() {
|
||||
return this._rdns;
|
||||
}
|
||||
|
||||
parseOverride() {
|
||||
let contents = readSEQUENCEAndMakeDER(this._der);
|
||||
while (!contents.atEnd()) {
|
||||
let rdn = new RelativeDistinguishedName();
|
||||
rdn.parse(contents.readTLV());
|
||||
this._rdns.push(rdn);
|
||||
}
|
||||
contents.assertAtEnd();
|
||||
this._der.assertAtEnd();
|
||||
}
|
||||
}
|
||||
|
||||
// RelativeDistinguishedName ::=
|
||||
// SET SIZE (1..MAX) OF AttributeTypeAndValue
|
||||
class RelativeDistinguishedName extends DecodedDER {
|
||||
constructor() {
|
||||
super();
|
||||
this._avas = [];
|
||||
}
|
||||
|
||||
get avas() {
|
||||
return this._avas;
|
||||
}
|
||||
|
||||
parseOverride() {
|
||||
let contents = readTagAndMakeDER(this._der, DER.SET);
|
||||
// Lint TODO: enforce SET SIZE restrictions
|
||||
while (!contents.atEnd()) {
|
||||
let ava = new AttributeTypeAndValue();
|
||||
ava.parse(contents.readTLV());
|
||||
this._avas.push(ava);
|
||||
}
|
||||
contents.assertAtEnd();
|
||||
this._der.assertAtEnd();
|
||||
}
|
||||
}
|
||||
|
||||
// AttributeTypeAndValue ::= SEQUENCE {
|
||||
// type AttributeType,
|
||||
// value AttributeValue }
|
||||
//
|
||||
// AttributeType ::= OBJECT IDENTIFIER
|
||||
//
|
||||
// AttributeValue ::= ANY -- DEFINED BY AttributeType
|
||||
class AttributeTypeAndValue extends DecodedDER {
|
||||
constructor() {
|
||||
super();
|
||||
this._type = null;
|
||||
this._value = new DirectoryString();
|
||||
}
|
||||
|
||||
get type() {
|
||||
return this._type;
|
||||
}
|
||||
|
||||
get value() {
|
||||
return this._value;
|
||||
}
|
||||
|
||||
parseOverride() {
|
||||
let contents = readSEQUENCEAndMakeDER(this._der);
|
||||
this._type = readOID(contents);
|
||||
// We don't support universalString or bmpString.
|
||||
// IA5String is supported because it is valid if `type == id-emailaddress`.
|
||||
// Lint TODO: validate that the type of string is valid given `type`.
|
||||
this._value.parse(contents.readTLVChoice([ DER.UTF8String,
|
||||
DER.PrintableString,
|
||||
DER.TeletexString,
|
||||
DER.IA5String ]));
|
||||
contents.assertAtEnd();
|
||||
this._der.assertAtEnd();
|
||||
}
|
||||
}
|
||||
|
||||
// DirectoryString ::= CHOICE {
|
||||
// teletexString TeletexString (SIZE (1..MAX)),
|
||||
// printableString PrintableString (SIZE (1..MAX)),
|
||||
// universalString UniversalString (SIZE (1..MAX)),
|
||||
// utf8String UTF8String (SIZE (1..MAX)),
|
||||
// bmpString BMPString (SIZE (1..MAX)) }
|
||||
class DirectoryString extends DecodedDER {
|
||||
constructor() {
|
||||
super();
|
||||
this._type = null;
|
||||
this._value = null;
|
||||
}
|
||||
|
||||
get type() {
|
||||
return this._type;
|
||||
}
|
||||
|
||||
get value() {
|
||||
return this._value;
|
||||
}
|
||||
|
||||
parseOverride() {
|
||||
if (this._der.peekTag(DER.UTF8String)) {
|
||||
this._type = DER.UTF8String;
|
||||
} else if (this._der.peekTag(DER.PrintableString)) {
|
||||
this._type = DER.PrintableString;
|
||||
} else if (this._der.peekTag(DER.TeletexString)) {
|
||||
this._type = DER.TeletexString;
|
||||
} else if (this._der.peekTag(DER.IA5String)) {
|
||||
this._type = DER.IA5String;
|
||||
}
|
||||
// Lint TODO: validate that the contents are actually valid for the type
|
||||
this._value = this._der.readTagAndGetContents(this._type);
|
||||
this._der.assertAtEnd();
|
||||
}
|
||||
}
|
||||
|
||||
// Time ::= CHOICE {
|
||||
// utcTime UTCTime,
|
||||
// generalTime GeneralizedTime }
|
||||
class Time extends DecodedDER {
|
||||
constructor() {
|
||||
super();
|
||||
this._type = null;
|
||||
this._time = null;
|
||||
}
|
||||
|
||||
get time() {
|
||||
return this._time;
|
||||
}
|
||||
|
||||
parseOverride() {
|
||||
if (this._der.peekTag(DER.UTCTime)) {
|
||||
this._type = DER.UTCTime;
|
||||
} else if (this._der.peekTag(DER.GeneralizedTime)) {
|
||||
this._type = DER.GeneralizedTime;
|
||||
}
|
||||
let contents = readTagAndMakeDER(this._der, this._type);
|
||||
let year;
|
||||
// Lint TODO: validate that the appropriate one of {UTCTime,GeneralizedTime}
|
||||
// is used according to RFC 5280 and what the value of the date is.
|
||||
// TODO TODO: explain this better (just quote the rfc).
|
||||
if (this._type == DER.UTCTime) {
|
||||
// UTCTime is YYMMDDHHMMSSZ in RFC 5280. If YY is greater than or equal
|
||||
// to 50, the year is 19YY. Otherwise, it is 20YY.
|
||||
let y1 = this._validateDigit(contents.readByte());
|
||||
let y2 = this._validateDigit(contents.readByte());
|
||||
let yy = y1 * 10 + y2;
|
||||
if (yy >= 50) {
|
||||
year = 1900 + yy;
|
||||
} else {
|
||||
year = 2000 + yy;
|
||||
}
|
||||
} else {
|
||||
// GeneralizedTime is YYYYMMDDHHMMSSZ in RFC 5280.
|
||||
year = 0;
|
||||
for (let i = 0; i < 4; i++) {
|
||||
let y = this._validateDigit(contents.readByte());
|
||||
year = year * 10 + y;
|
||||
}
|
||||
}
|
||||
|
||||
let m1 = this._validateDigit(contents.readByte());
|
||||
let m2 = this._validateDigit(contents.readByte());
|
||||
let month = m1 * 10 + m2;
|
||||
if (month == 0 || month > 12) {
|
||||
throw new Error(ERROR_TIME_NOT_VALID);
|
||||
}
|
||||
|
||||
let d1 = this._validateDigit(contents.readByte());
|
||||
let d2 = this._validateDigit(contents.readByte());
|
||||
let day = d1 * 10 + d2;
|
||||
if (day == 0 || day > 31) {
|
||||
throw new Error(ERROR_TIME_NOT_VALID);
|
||||
}
|
||||
|
||||
let h1 = this._validateDigit(contents.readByte());
|
||||
let h2 = this._validateDigit(contents.readByte());
|
||||
let hour = h1 * 10 + h2;
|
||||
if (hour > 23) {
|
||||
throw new Error(ERROR_TIME_NOT_VALID);
|
||||
}
|
||||
|
||||
let min1 = this._validateDigit(contents.readByte());
|
||||
let min2 = this._validateDigit(contents.readByte());
|
||||
let minute = min1 * 10 + min2;
|
||||
if (minute > 59) {
|
||||
throw new Error(ERROR_TIME_NOT_VALID);
|
||||
}
|
||||
|
||||
let s1 = this._validateDigit(contents.readByte());
|
||||
let s2 = this._validateDigit(contents.readByte());
|
||||
let second = s1 * 10 + s2;
|
||||
if (second > 60) { // leap-seconds mean this can be as much as 60
|
||||
throw new Error(ERROR_TIME_NOT_VALID);
|
||||
}
|
||||
|
||||
let z = contents.readByte();
|
||||
if (z != "Z".charCodeAt(0)) {
|
||||
throw new Error(ERROR_TIME_NOT_VALID);
|
||||
}
|
||||
// Lint TODO: verify that the Time doesn't specify a nonsensical
|
||||
// month/day/etc.
|
||||
// months are zero-indexed in JS
|
||||
this._time = new Date(Date.UTC(year, month - 1, day, hour, minute,
|
||||
second));
|
||||
|
||||
contents.assertAtEnd();
|
||||
this._der.assertAtEnd();
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a byte that is supposed to be in the ASCII range for "0" to "9".
|
||||
* Validates the range and then converts it to the range 0 to 9.
|
||||
* @param {Number} the digit in question (as ASCII in the range ["0", "9"])
|
||||
* @return {Number} the numerical value of the digit (in the range [0, 9])
|
||||
*/
|
||||
_validateDigit(d) {
|
||||
if (d < "0".charCodeAt(0) || d > "9".charCodeAt(0)) {
|
||||
throw new Error(ERROR_TIME_NOT_VALID);
|
||||
}
|
||||
return d - "0".charCodeAt(0);
|
||||
}
|
||||
}
|
||||
|
||||
// Validity ::= SEQUENCE {
|
||||
// notBefore Time,
|
||||
// notAfter Time }
|
||||
class Validity extends DecodedDER {
|
||||
constructor() {
|
||||
super();
|
||||
this._notBefore = new Time();
|
||||
this._notAfter = new Time();
|
||||
}
|
||||
|
||||
get notBefore() {
|
||||
return this._notBefore;
|
||||
}
|
||||
|
||||
get notAfter() {
|
||||
return this._notAfter;
|
||||
}
|
||||
|
||||
parseOverride() {
|
||||
let contents = readSEQUENCEAndMakeDER(this._der);
|
||||
this._notBefore.parse(contents.readTLVChoice(
|
||||
[DER.UTCTime, DER.GeneralizedTime]));
|
||||
this._notAfter.parse(contents.readTLVChoice(
|
||||
[DER.UTCTime, DER.GeneralizedTime]));
|
||||
contents.assertAtEnd();
|
||||
this._der.assertAtEnd();
|
||||
}
|
||||
}
|
||||
|
||||
// SubjectPublicKeyInfo ::= SEQUENCE {
|
||||
// algorithm AlgorithmIdentifier,
|
||||
// subjectPublicKey BIT STRING }
|
||||
class SubjectPublicKeyInfo extends DecodedDER {
|
||||
constructor() {
|
||||
super();
|
||||
this._algorithm = new AlgorithmIdentifier();
|
||||
this._subjectPublicKey = null;
|
||||
}
|
||||
|
||||
get algorithm() {
|
||||
return this._algorithm;
|
||||
}
|
||||
|
||||
get subjectPublicKey() {
|
||||
return this._subjectPublicKey;
|
||||
}
|
||||
|
||||
parseOverride() {
|
||||
let contents = readSEQUENCEAndMakeDER(this._der);
|
||||
this._algorithm.parse(contents.readTLV());
|
||||
let subjectPublicKeyBitString = contents.readBIT_STRING();
|
||||
if (subjectPublicKeyBitString.unusedBits != 0) {
|
||||
throw new Error(ERROR_UNSUPPORTED_ASN1);
|
||||
}
|
||||
this._subjectPublicKey = subjectPublicKeyBitString.contents;
|
||||
|
||||
contents.assertAtEnd();
|
||||
this._der.assertAtEnd();
|
||||
}
|
||||
}
|
||||
|
||||
this.X509 = { Certificate };
|
||||
this.EXPORTED_SYMBOLS = ["X509"];
|
||||
1
security/manager/ssl/crashtests/398665-1.html
Normal file
1
security/manager/ssl/crashtests/398665-1.html
Normal file
|
|
@ -0,0 +1 @@
|
|||
<html><body><keygen></keygen></body></html>
|
||||
1
security/manager/ssl/crashtests/crashtests.list
Normal file
1
security/manager/ssl/crashtests/crashtests.list
Normal file
|
|
@ -0,0 +1 @@
|
|||
load 398665-1.html
|
||||
145
security/manager/ssl/md4.c
Normal file
145
security/manager/ssl/md4.c
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
/* vim:set ts=2 sw=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/*
|
||||
* "clean room" MD4 implementation (see RFC 1320)
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "md4.h"
|
||||
|
||||
/* the "conditional" function */
|
||||
#define F(x,y,z) (((x) & (y)) | (~(x) & (z)))
|
||||
|
||||
/* the "majority" function */
|
||||
#define G(x,y,z) (((x) & (y)) | ((x) & (z)) | ((y) & (z)))
|
||||
|
||||
/* the "parity" function */
|
||||
#define H(x,y,z) ((x) ^ (y) ^ (z))
|
||||
|
||||
/* rotate n-bits to the left */
|
||||
#define ROTL(x,n) (((x) << (n)) | ((x) >> (0x20 - n)))
|
||||
|
||||
/* round 1: [abcd k s]: a = (a + F(b,c,d) + X[k]) <<< s */
|
||||
#define RD1(a,b,c,d,k,s) a += F(b,c,d) + X[k]; a = ROTL(a,s)
|
||||
|
||||
/* round 2: [abcd k s]: a = (a + G(b,c,d) + X[k] + MAGIC) <<< s */
|
||||
#define RD2(a,b,c,d,k,s) a += G(b,c,d) + X[k] + 0x5A827999; a = ROTL(a,s)
|
||||
|
||||
/* round 3: [abcd k s]: a = (a + H(b,c,d) + X[k] + MAGIC) <<< s */
|
||||
#define RD3(a,b,c,d,k,s) a += H(b,c,d) + X[k] + 0x6ED9EBA1; a = ROTL(a,s)
|
||||
|
||||
/* converts from word array to byte array, len is number of bytes */
|
||||
static void w2b(uint8_t *out, const uint32_t *in, uint32_t len)
|
||||
{
|
||||
uint8_t *bp; const uint32_t *wp, *wpend;
|
||||
|
||||
bp = out;
|
||||
wp = in;
|
||||
wpend = wp + (len >> 2);
|
||||
|
||||
for (; wp != wpend; ++wp, bp += 4)
|
||||
{
|
||||
bp[0] = (uint8_t) ((*wp ) & 0xFF);
|
||||
bp[1] = (uint8_t) ((*wp >> 8) & 0xFF);
|
||||
bp[2] = (uint8_t) ((*wp >> 16) & 0xFF);
|
||||
bp[3] = (uint8_t) ((*wp >> 24) & 0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
/* converts from byte array to word array, len is number of bytes */
|
||||
static void b2w(uint32_t *out, const uint8_t *in, uint32_t len)
|
||||
{
|
||||
uint32_t *wp; const uint8_t *bp, *bpend;
|
||||
|
||||
wp = out;
|
||||
bp = in;
|
||||
bpend = in + len;
|
||||
|
||||
for (; bp != bpend; bp += 4, ++wp)
|
||||
{
|
||||
*wp = (uint32_t) (bp[0] ) |
|
||||
(uint32_t) (bp[1] << 8) |
|
||||
(uint32_t) (bp[2] << 16) |
|
||||
(uint32_t) (bp[3] << 24);
|
||||
}
|
||||
}
|
||||
|
||||
/* update state: data is 64 bytes in length */
|
||||
static void md4step(uint32_t state[4], const uint8_t *data)
|
||||
{
|
||||
uint32_t A, B, C, D, X[16];
|
||||
|
||||
b2w(X, data, 64);
|
||||
|
||||
A = state[0];
|
||||
B = state[1];
|
||||
C = state[2];
|
||||
D = state[3];
|
||||
|
||||
RD1(A,B,C,D, 0,3); RD1(D,A,B,C, 1,7); RD1(C,D,A,B, 2,11); RD1(B,C,D,A, 3,19);
|
||||
RD1(A,B,C,D, 4,3); RD1(D,A,B,C, 5,7); RD1(C,D,A,B, 6,11); RD1(B,C,D,A, 7,19);
|
||||
RD1(A,B,C,D, 8,3); RD1(D,A,B,C, 9,7); RD1(C,D,A,B,10,11); RD1(B,C,D,A,11,19);
|
||||
RD1(A,B,C,D,12,3); RD1(D,A,B,C,13,7); RD1(C,D,A,B,14,11); RD1(B,C,D,A,15,19);
|
||||
|
||||
RD2(A,B,C,D, 0,3); RD2(D,A,B,C, 4,5); RD2(C,D,A,B, 8, 9); RD2(B,C,D,A,12,13);
|
||||
RD2(A,B,C,D, 1,3); RD2(D,A,B,C, 5,5); RD2(C,D,A,B, 9, 9); RD2(B,C,D,A,13,13);
|
||||
RD2(A,B,C,D, 2,3); RD2(D,A,B,C, 6,5); RD2(C,D,A,B,10, 9); RD2(B,C,D,A,14,13);
|
||||
RD2(A,B,C,D, 3,3); RD2(D,A,B,C, 7,5); RD2(C,D,A,B,11, 9); RD2(B,C,D,A,15,13);
|
||||
|
||||
RD3(A,B,C,D, 0,3); RD3(D,A,B,C, 8,9); RD3(C,D,A,B, 4,11); RD3(B,C,D,A,12,15);
|
||||
RD3(A,B,C,D, 2,3); RD3(D,A,B,C,10,9); RD3(C,D,A,B, 6,11); RD3(B,C,D,A,14,15);
|
||||
RD3(A,B,C,D, 1,3); RD3(D,A,B,C, 9,9); RD3(C,D,A,B, 5,11); RD3(B,C,D,A,13,15);
|
||||
RD3(A,B,C,D, 3,3); RD3(D,A,B,C,11,9); RD3(C,D,A,B, 7,11); RD3(B,C,D,A,15,15);
|
||||
|
||||
state[0] += A;
|
||||
state[1] += B;
|
||||
state[2] += C;
|
||||
state[3] += D;
|
||||
}
|
||||
|
||||
void md4sum(const uint8_t *input, uint32_t inputLen, uint8_t *result)
|
||||
{
|
||||
uint8_t final[128];
|
||||
uint32_t i, n, m, state[4];
|
||||
uint64_t inputLenBits;
|
||||
uint32_t inputLenBitsLow;
|
||||
uint32_t inputLenBitsHigh;
|
||||
|
||||
/* magic initial states */
|
||||
state[0] = 0x67452301;
|
||||
state[1] = 0xEFCDAB89;
|
||||
state[2] = 0x98BADCFE;
|
||||
state[3] = 0x10325476;
|
||||
|
||||
/* compute number of complete 64-byte segments contained in input */
|
||||
m = inputLen >> 6;
|
||||
|
||||
/* digest first m segments */
|
||||
for (i=0; i<m; ++i)
|
||||
md4step(state, (input + (i << 6)));
|
||||
|
||||
/* build final buffer */
|
||||
n = inputLen % 64;
|
||||
memcpy(final, input + (m << 6), n);
|
||||
final[n] = 0x80;
|
||||
memset(final + n + 1, 0, 120 - (n + 1));
|
||||
|
||||
/* Append the original input length in bits as a 64-bit number. This is done
|
||||
* in two 32-bit chunks, with the least-significant 32 bits first.
|
||||
* w2b will handle endianness. */
|
||||
inputLenBits = inputLen << 3;
|
||||
inputLenBitsLow = (uint32_t)(inputLenBits & 0xFFFFFFFF);
|
||||
w2b(final + (n >= 56 ? 120 : 56), &inputLenBitsLow, 4);
|
||||
inputLenBitsHigh = (uint32_t)((inputLenBits >> 32) & 0xFFFFFFFF);
|
||||
w2b(final + (n >= 56 ? 124 : 60), &inputLenBitsHigh, 4);
|
||||
|
||||
md4step(state, final);
|
||||
if (n >= 56)
|
||||
md4step(state, final + 64);
|
||||
|
||||
/* copy state to result */
|
||||
w2b(result, state, 16);
|
||||
}
|
||||
38
security/manager/ssl/md4.h
Normal file
38
security/manager/ssl/md4.h
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
/* vim:set ts=2 sw=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef md4_h__
|
||||
#define md4_h__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* md4sum - computes the MD4 sum over the input buffer per RFC 1320
|
||||
*
|
||||
* @param input
|
||||
* buffer containing input data
|
||||
* @param inputLen
|
||||
* length of input buffer (number of bytes)
|
||||
* @param result
|
||||
* 16-byte buffer that will contain the MD4 sum upon return
|
||||
*
|
||||
* NOTE: MD4 is superceded by MD5. do not use MD4 unless required by the
|
||||
* protocol you are implementing (e.g., NTLM requires MD4).
|
||||
*
|
||||
* NOTE: this interface is designed for relatively small buffers. A streaming
|
||||
* interface would make more sense if that were a requirement. Currently, this
|
||||
* is good enough for the applications we care about.
|
||||
*/
|
||||
void md4sum(const uint8_t *input, uint32_t inputLen, uint8_t *result);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* md4_h__ */
|
||||
194
security/manager/ssl/moz.build
Normal file
194
security/manager/ssl/moz.build
Normal file
|
|
@ -0,0 +1,194 @@
|
|||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
TEST_DIRS += [ 'tests' ]
|
||||
|
||||
XPIDL_SOURCES += [
|
||||
'nsIASN1Object.idl',
|
||||
'nsIASN1PrintableItem.idl',
|
||||
'nsIASN1Sequence.idl',
|
||||
'nsIAssociatedContentSecurity.idl',
|
||||
'nsIBadCertListener2.idl',
|
||||
'nsICertBlocklist.idl',
|
||||
'nsICertificateDialogs.idl',
|
||||
'nsICertOverrideService.idl',
|
||||
'nsIClientAuthDialogs.idl',
|
||||
'nsIContentSignatureVerifier.idl',
|
||||
'nsIDataSignatureVerifier.idl',
|
||||
'nsIGenKeypairInfoDlg.idl',
|
||||
'nsIKeygenThread.idl',
|
||||
'nsIKeyModule.idl',
|
||||
'nsILocalCertService.idl',
|
||||
'nsINSSU2FToken.idl',
|
||||
'nsINSSVersion.idl',
|
||||
'nsIPK11Token.idl',
|
||||
'nsIPK11TokenDB.idl',
|
||||
'nsIPKCS11.idl',
|
||||
'nsIPKCS11Module.idl',
|
||||
'nsIPKCS11ModuleDB.idl',
|
||||
'nsIPKCS11Slot.idl',
|
||||
'nsIProtectedAuthThread.idl',
|
||||
'nsISecretDecoderRing.idl',
|
||||
'nsISecurityUITelemetry.idl',
|
||||
'nsISiteSecurityService.idl',
|
||||
'nsISSLStatus.idl',
|
||||
'nsISSLStatusProvider.idl',
|
||||
'nsITokenDialogs.idl',
|
||||
'nsITokenPasswordDialogs.idl',
|
||||
'nsIU2FToken.idl',
|
||||
'nsIWeakCryptoOverride.idl',
|
||||
'nsIX509Cert.idl',
|
||||
'nsIX509CertDB.idl',
|
||||
'nsIX509CertList.idl',
|
||||
'nsIX509CertValidity.idl',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_XUL']:
|
||||
XPIDL_SOURCES += [
|
||||
'nsICertTree.idl',
|
||||
]
|
||||
|
||||
XPIDL_MODULE = 'pipnss'
|
||||
|
||||
EXTRA_JS_MODULES.psm += [
|
||||
'DER.jsm',
|
||||
'X509.jsm',
|
||||
]
|
||||
|
||||
EXPORTS += [
|
||||
'CryptoTask.h',
|
||||
'nsClientAuthRemember.h',
|
||||
'nsCrypto.h',
|
||||
'nsNSSCallbacks.h',
|
||||
'nsNSSCertificate.h',
|
||||
'nsNSSComponent.h',
|
||||
'nsNSSHelper.h',
|
||||
'nsNSSShutDown.h',
|
||||
'nsNSSU2FToken.h',
|
||||
'nsRandomGenerator.h',
|
||||
'nsSecurityHeaderParser.h',
|
||||
'NSSErrorsService.h',
|
||||
'ScopedNSSTypes.h',
|
||||
'SharedCertVerifier.h',
|
||||
]
|
||||
|
||||
EXPORTS.mozilla += [
|
||||
'DataStorage.h',
|
||||
'PublicSSL.h',
|
||||
]
|
||||
|
||||
EXPORTS.mozilla.psm += [
|
||||
'PSMContentListener.h',
|
||||
]
|
||||
|
||||
EXPORTS.ipc += [
|
||||
'DataStorageIPCUtils.h',
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'CertBlocklist.cpp',
|
||||
'ContentSignatureVerifier.cpp',
|
||||
'CryptoTask.cpp',
|
||||
'CSTrustDomain.cpp',
|
||||
'DataStorage.cpp',
|
||||
'LocalCertService.cpp',
|
||||
'nsCertOverrideService.cpp',
|
||||
'nsClientAuthRemember.cpp',
|
||||
'nsCrypto.cpp',
|
||||
'nsCryptoHash.cpp',
|
||||
'nsDataSignatureVerifier.cpp',
|
||||
'nsKeygenHandler.cpp',
|
||||
'nsKeygenHandlerContent.cpp',
|
||||
'nsKeygenThread.cpp',
|
||||
'nsKeyModule.cpp',
|
||||
'nsNSSASN1Object.cpp',
|
||||
'nsNSSCallbacks.cpp',
|
||||
'nsNSSCertHelper.cpp',
|
||||
'nsNSSCertificate.cpp',
|
||||
'nsNSSCertificateDB.cpp',
|
||||
'nsNSSCertificateFakeTransport.cpp',
|
||||
'nsNSSCertTrust.cpp',
|
||||
'nsNSSCertValidity.cpp',
|
||||
'nsNSSComponent.cpp',
|
||||
'nsNSSErrors.cpp',
|
||||
'nsNSSIOLayer.cpp',
|
||||
'nsNSSModule.cpp',
|
||||
'nsNSSShutDown.cpp',
|
||||
'nsNSSU2FToken.cpp',
|
||||
'nsNSSVersion.cpp',
|
||||
'nsNTLMAuthModule.cpp',
|
||||
'nsPK11TokenDB.cpp',
|
||||
'nsPKCS11Slot.cpp',
|
||||
'nsPKCS12Blob.cpp',
|
||||
'nsProtectedAuthThread.cpp',
|
||||
'nsRandomGenerator.cpp',
|
||||
'nsSecureBrowserUIImpl.cpp',
|
||||
'nsSecurityHeaderParser.cpp',
|
||||
'NSSErrorsService.cpp',
|
||||
'nsSiteSecurityService.cpp',
|
||||
'nsSSLSocketProvider.cpp',
|
||||
'nsSSLStatus.cpp',
|
||||
'nsTLSSocketProvider.cpp',
|
||||
'PSMContentListener.cpp',
|
||||
'PSMRunnable.cpp',
|
||||
'PublicKeyPinningService.cpp',
|
||||
'RootCertificateTelemetryUtils.cpp',
|
||||
'SecretDecoderRing.cpp',
|
||||
'SharedSSLState.cpp',
|
||||
'SSLServerCertVerification.cpp',
|
||||
'TransportSecurityInfo.cpp',
|
||||
'WeakCryptoOverride.cpp',
|
||||
]
|
||||
|
||||
IPDL_SOURCES += [
|
||||
'PPSMContentDownloader.ipdl',
|
||||
]
|
||||
|
||||
if not CONFIG['MOZ_NO_SMART_CARDS']:
|
||||
UNIFIED_SOURCES += [
|
||||
'nsSmartCardMonitor.cpp',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_XUL']:
|
||||
UNIFIED_SOURCES += [
|
||||
'nsCertTree.cpp',
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'md4.c',
|
||||
]
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'/dom/base',
|
||||
'/dom/crypto',
|
||||
'/security/certverifier',
|
||||
'/security/pkix/include',
|
||||
]
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'!/dist/public/nss',
|
||||
]
|
||||
|
||||
if CONFIG['NSS_DISABLE_DBM']:
|
||||
DEFINES['NSS_DISABLE_DBM'] = '1'
|
||||
|
||||
DEFINES['SSL_DISABLE_DEPRECATED_CIPHER_SUITE_NAMES'] = 'True'
|
||||
DEFINES['NSS_ENABLE_ECC'] = 'True'
|
||||
for var in ('DLL_PREFIX', 'DLL_SUFFIX'):
|
||||
DEFINES[var] = '"%s"' % CONFIG[var]
|
||||
|
||||
DEFINES['CERT_AddTempCertToPerm'] = '__CERT_AddTempCertToPerm'
|
||||
|
||||
USE_LIBS += [
|
||||
'crmf',
|
||||
]
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
if CONFIG['GNU_CXX']:
|
||||
CXXFLAGS += ['-Wno-error=shadow']
|
||||
705
security/manager/ssl/nsCertOverrideService.cpp
Normal file
705
security/manager/ssl/nsCertOverrideService.cpp
Normal file
|
|
@ -0,0 +1,705 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsCertOverrideService.h"
|
||||
|
||||
#include "NSSCertDBTrustDomain.h"
|
||||
#include "ScopedNSSTypes.h"
|
||||
#include "SharedSSLState.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
#include "nsAppDirectoryServiceDefs.h"
|
||||
#include "nsCRT.h"
|
||||
#include "nsILineInputStream.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "nsIOutputStream.h"
|
||||
#include "nsISafeOutputStream.h"
|
||||
#include "nsIX509Cert.h"
|
||||
#include "nsNSSCertHelper.h"
|
||||
#include "nsNSSCertificate.h"
|
||||
#include "nsNSSComponent.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsPromiseFlatString.h"
|
||||
#include "nsStreamUtils.h"
|
||||
#include "nsStringBuffer.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "ssl.h" // For SSL_ClearSessionCache
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::psm;
|
||||
|
||||
#define CERT_OVERRIDE_FILE_NAME "cert_override.txt"
|
||||
|
||||
void
|
||||
nsCertOverride::convertBitsToString(OverrideBits ob, nsACString &str)
|
||||
{
|
||||
str.Truncate();
|
||||
|
||||
if (ob & ob_Mismatch)
|
||||
str.Append('M');
|
||||
|
||||
if (ob & ob_Untrusted)
|
||||
str.Append('U');
|
||||
|
||||
if (ob & ob_Time_error)
|
||||
str.Append('T');
|
||||
}
|
||||
|
||||
void
|
||||
nsCertOverride::convertStringToBits(const nsACString &str, OverrideBits &ob)
|
||||
{
|
||||
const nsPromiseFlatCString &flat = PromiseFlatCString(str);
|
||||
const char *walk = flat.get();
|
||||
|
||||
ob = ob_None;
|
||||
|
||||
for ( ; *walk; ++walk)
|
||||
{
|
||||
switch (*walk)
|
||||
{
|
||||
case 'm':
|
||||
case 'M':
|
||||
ob = (OverrideBits)(ob | ob_Mismatch);
|
||||
break;
|
||||
|
||||
case 'u':
|
||||
case 'U':
|
||||
ob = (OverrideBits)(ob | ob_Untrusted);
|
||||
break;
|
||||
|
||||
case 't':
|
||||
case 'T':
|
||||
ob = (OverrideBits)(ob | ob_Time_error);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(nsCertOverrideService,
|
||||
nsICertOverrideService,
|
||||
nsIObserver,
|
||||
nsISupportsWeakReference)
|
||||
|
||||
nsCertOverrideService::nsCertOverrideService()
|
||||
: monitor("nsCertOverrideService.monitor")
|
||||
{
|
||||
}
|
||||
|
||||
nsCertOverrideService::~nsCertOverrideService()
|
||||
{
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsCertOverrideService::Init()
|
||||
{
|
||||
if (!NS_IsMainThread()) {
|
||||
NS_NOTREACHED("nsCertOverrideService initialized off main thread");
|
||||
return NS_ERROR_NOT_SAME_THREAD;
|
||||
}
|
||||
|
||||
// Note that the names of these variables would seem to indicate that at one
|
||||
// point another hash algorithm was used and is still supported for backwards
|
||||
// compatibility. This is not the case. It has always been SHA256.
|
||||
mOidTagForStoringNewHashes = SEC_OID_SHA256;
|
||||
mDottedOidForStoringNewHashes.Assign("OID.2.16.840.1.101.3.4.2.1");
|
||||
|
||||
nsCOMPtr<nsIObserverService> observerService =
|
||||
mozilla::services::GetObserverService();
|
||||
|
||||
// If we cannot add ourselves as a profile change observer, then we will not
|
||||
// attempt to read/write any settings file. Otherwise, we would end up
|
||||
// reading/writing the wrong settings file after a profile change.
|
||||
if (observerService) {
|
||||
observerService->AddObserver(this, "profile-before-change", true);
|
||||
observerService->AddObserver(this, "profile-do-change", true);
|
||||
// simulate a profile change so we read the current profile's settings file
|
||||
Observe(nullptr, "profile-do-change", nullptr);
|
||||
}
|
||||
|
||||
SharedSSLState::NoteCertOverrideServiceInstantiated();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsCertOverrideService::Observe(nsISupports *,
|
||||
const char *aTopic,
|
||||
const char16_t *aData)
|
||||
{
|
||||
// check the topic
|
||||
if (!nsCRT::strcmp(aTopic, "profile-before-change")) {
|
||||
// The profile is about to change,
|
||||
// or is going away because the application is shutting down.
|
||||
|
||||
RemoveAllFromMemory();
|
||||
} else if (!nsCRT::strcmp(aTopic, "profile-do-change")) {
|
||||
// The profile has already changed.
|
||||
// Now read from the new profile location.
|
||||
// we also need to update the cached file location
|
||||
|
||||
ReentrantMonitorAutoEnter lock(monitor);
|
||||
|
||||
nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(mSettingsFile));
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
mSettingsFile->AppendNative(NS_LITERAL_CSTRING(CERT_OVERRIDE_FILE_NAME));
|
||||
} else {
|
||||
mSettingsFile = nullptr;
|
||||
}
|
||||
Read();
|
||||
CountPermanentOverrideTelemetry();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsCertOverrideService::RemoveAllFromMemory()
|
||||
{
|
||||
ReentrantMonitorAutoEnter lock(monitor);
|
||||
mSettingsTable.Clear();
|
||||
}
|
||||
|
||||
void
|
||||
nsCertOverrideService::RemoveAllTemporaryOverrides()
|
||||
{
|
||||
ReentrantMonitorAutoEnter lock(monitor);
|
||||
for (auto iter = mSettingsTable.Iter(); !iter.Done(); iter.Next()) {
|
||||
nsCertOverrideEntry *entry = iter.Get();
|
||||
if (entry->mSettings.mIsTemporary) {
|
||||
entry->mSettings.mCert = nullptr;
|
||||
iter.Remove();
|
||||
}
|
||||
}
|
||||
// no need to write, as temporaries are never written to disk
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsCertOverrideService::Read()
|
||||
{
|
||||
ReentrantMonitorAutoEnter lock(monitor);
|
||||
|
||||
// If we don't have a profile, then we won't try to read any settings file.
|
||||
if (!mSettingsFile)
|
||||
return NS_OK;
|
||||
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIInputStream> fileInputStream;
|
||||
rv = NS_NewLocalFileInputStream(getter_AddRefs(fileInputStream), mSettingsFile);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsILineInputStream> lineInputStream = do_QueryInterface(fileInputStream, &rv);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsAutoCString buffer;
|
||||
bool isMore = true;
|
||||
int32_t hostIndex = 0, algoIndex, fingerprintIndex, overrideBitsIndex, dbKeyIndex;
|
||||
|
||||
/* file format is:
|
||||
*
|
||||
* host:port \t fingerprint-algorithm \t fingerprint \t override-mask \t dbKey
|
||||
*
|
||||
* where override-mask is a sequence of characters,
|
||||
* M meaning hostname-Mismatch-override
|
||||
* U meaning Untrusted-override
|
||||
* T meaning Time-error-override (expired/not yet valid)
|
||||
*
|
||||
* if this format isn't respected we move onto the next line in the file.
|
||||
*/
|
||||
|
||||
while (isMore && NS_SUCCEEDED(lineInputStream->ReadLine(buffer, &isMore))) {
|
||||
if (buffer.IsEmpty() || buffer.First() == '#') {
|
||||
continue;
|
||||
}
|
||||
|
||||
// this is a cheap, cheesy way of parsing a tab-delimited line into
|
||||
// string indexes, which can be lopped off into substrings. just for
|
||||
// purposes of obfuscation, it also checks that each token was found.
|
||||
// todo: use iterators?
|
||||
if ((algoIndex = buffer.FindChar('\t', hostIndex) + 1) == 0 ||
|
||||
(fingerprintIndex = buffer.FindChar('\t', algoIndex) + 1) == 0 ||
|
||||
(overrideBitsIndex = buffer.FindChar('\t', fingerprintIndex) + 1) == 0 ||
|
||||
(dbKeyIndex = buffer.FindChar('\t', overrideBitsIndex) + 1) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const nsASingleFragmentCString &tmp = Substring(buffer, hostIndex, algoIndex - hostIndex - 1);
|
||||
const nsASingleFragmentCString &algo_string = Substring(buffer, algoIndex, fingerprintIndex - algoIndex - 1);
|
||||
const nsASingleFragmentCString &fingerprint = Substring(buffer, fingerprintIndex, overrideBitsIndex - fingerprintIndex - 1);
|
||||
const nsASingleFragmentCString &bits_string = Substring(buffer, overrideBitsIndex, dbKeyIndex - overrideBitsIndex - 1);
|
||||
const nsASingleFragmentCString &db_key = Substring(buffer, dbKeyIndex, buffer.Length() - dbKeyIndex);
|
||||
|
||||
nsAutoCString host(tmp);
|
||||
nsCertOverride::OverrideBits bits;
|
||||
nsCertOverride::convertStringToBits(bits_string, bits);
|
||||
|
||||
int32_t port;
|
||||
int32_t portIndex = host.RFindChar(':');
|
||||
if (portIndex == kNotFound)
|
||||
continue; // Ignore broken entries
|
||||
|
||||
nsresult portParseError;
|
||||
nsAutoCString portString(Substring(host, portIndex+1));
|
||||
port = portString.ToInteger(&portParseError);
|
||||
if (NS_FAILED(portParseError))
|
||||
continue; // Ignore broken entries
|
||||
|
||||
host.Truncate(portIndex);
|
||||
|
||||
AddEntryToList(host, port,
|
||||
nullptr, // don't have the cert
|
||||
false, // not temporary
|
||||
algo_string, fingerprint, bits, db_key);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsCertOverrideService::Write()
|
||||
{
|
||||
ReentrantMonitorAutoEnter lock(monitor);
|
||||
|
||||
// If we don't have any profile, then we won't try to write any file
|
||||
if (!mSettingsFile) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIOutputStream> fileOutputStream;
|
||||
rv = NS_NewSafeLocalFileOutputStream(getter_AddRefs(fileOutputStream),
|
||||
mSettingsFile,
|
||||
-1,
|
||||
0600);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_ERROR("failed to open cert_warn_settings.txt for writing");
|
||||
return rv;
|
||||
}
|
||||
|
||||
// get a buffered output stream 4096 bytes big, to optimize writes
|
||||
nsCOMPtr<nsIOutputStream> bufferedOutputStream;
|
||||
rv = NS_NewBufferedOutputStream(getter_AddRefs(bufferedOutputStream), fileOutputStream, 4096);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
static const char kHeader[] =
|
||||
"# PSM Certificate Override Settings file" NS_LINEBREAK
|
||||
"# This is a generated file! Do not edit." NS_LINEBREAK;
|
||||
|
||||
/* see ::Read for file format */
|
||||
|
||||
uint32_t unused;
|
||||
bufferedOutputStream->Write(kHeader, sizeof(kHeader) - 1, &unused);
|
||||
|
||||
static const char kTab[] = "\t";
|
||||
for (auto iter = mSettingsTable.Iter(); !iter.Done(); iter.Next()) {
|
||||
nsCertOverrideEntry *entry = iter.Get();
|
||||
|
||||
const nsCertOverride &settings = entry->mSettings;
|
||||
if (settings.mIsTemporary) {
|
||||
continue;
|
||||
}
|
||||
|
||||
nsAutoCString bits_string;
|
||||
nsCertOverride::convertBitsToString(settings.mOverrideBits, bits_string);
|
||||
|
||||
bufferedOutputStream->Write(entry->mHostWithPort.get(),
|
||||
entry->mHostWithPort.Length(), &unused);
|
||||
bufferedOutputStream->Write(kTab, sizeof(kTab) - 1, &unused);
|
||||
bufferedOutputStream->Write(settings.mFingerprintAlgOID.get(),
|
||||
settings.mFingerprintAlgOID.Length(), &unused);
|
||||
bufferedOutputStream->Write(kTab, sizeof(kTab) - 1, &unused);
|
||||
bufferedOutputStream->Write(settings.mFingerprint.get(),
|
||||
settings.mFingerprint.Length(), &unused);
|
||||
bufferedOutputStream->Write(kTab, sizeof(kTab) - 1, &unused);
|
||||
bufferedOutputStream->Write(bits_string.get(),
|
||||
bits_string.Length(), &unused);
|
||||
bufferedOutputStream->Write(kTab, sizeof(kTab) - 1, &unused);
|
||||
bufferedOutputStream->Write(settings.mDBKey.get(),
|
||||
settings.mDBKey.Length(), &unused);
|
||||
bufferedOutputStream->Write(NS_LINEBREAK, NS_LINEBREAK_LEN, &unused);
|
||||
}
|
||||
|
||||
// All went ok. Maybe except for problems in Write(), but the stream detects
|
||||
// that for us
|
||||
nsCOMPtr<nsISafeOutputStream> safeStream = do_QueryInterface(bufferedOutputStream);
|
||||
NS_ASSERTION(safeStream, "expected a safe output stream!");
|
||||
if (safeStream) {
|
||||
rv = safeStream->Finish();
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("failed to save cert warn settings file! possible dataloss");
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
static nsresult
|
||||
GetCertFingerprintByOidTag(nsIX509Cert *aCert,
|
||||
SECOidTag aOidTag,
|
||||
nsCString &fp)
|
||||
{
|
||||
UniqueCERTCertificate nsscert(aCert->GetCert());
|
||||
if (!nsscert) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
return GetCertFingerprintByOidTag(nsscert.get(), aOidTag, fp);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsCertOverrideService::RememberValidityOverride(const nsACString& aHostName,
|
||||
int32_t aPort,
|
||||
nsIX509Cert* aCert,
|
||||
uint32_t aOverrideBits,
|
||||
bool aTemporary)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aCert);
|
||||
if (aHostName.IsEmpty())
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
if (aPort < -1)
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
|
||||
UniqueCERTCertificate nsscert(aCert->GetCert());
|
||||
if (!nsscert) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsAutoCString nickname;
|
||||
nsresult rv = DefaultServerNicknameForCert(nsscert.get(), nickname);
|
||||
if (!aTemporary && NS_SUCCEEDED(rv)) {
|
||||
UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
|
||||
if (!slot) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
SECStatus srv = PK11_ImportCert(slot.get(), nsscert.get(), CK_INVALID_HANDLE,
|
||||
nickname.get(), false);
|
||||
if (srv != SECSuccess) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
nsAutoCString fpStr;
|
||||
rv = GetCertFingerprintByOidTag(nsscert.get(), mOidTagForStoringNewHashes,
|
||||
fpStr);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
nsAutoCString dbkey;
|
||||
rv = aCert->GetDbKey(dbkey);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
{
|
||||
ReentrantMonitorAutoEnter lock(monitor);
|
||||
AddEntryToList(aHostName, aPort,
|
||||
aTemporary ? aCert : nullptr,
|
||||
// keep a reference to the cert for temporary overrides
|
||||
aTemporary,
|
||||
mDottedOidForStoringNewHashes, fpStr,
|
||||
(nsCertOverride::OverrideBits)aOverrideBits,
|
||||
dbkey);
|
||||
if (!aTemporary) {
|
||||
Write();
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsCertOverrideService::RememberTemporaryValidityOverrideUsingFingerprint(
|
||||
const nsACString& aHostName,
|
||||
int32_t aPort,
|
||||
const nsACString& aCertFingerprint,
|
||||
uint32_t aOverrideBits)
|
||||
{
|
||||
if(aCertFingerprint.IsEmpty() || aHostName.IsEmpty() || (aPort < -1)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
ReentrantMonitorAutoEnter lock(monitor);
|
||||
AddEntryToList(aHostName, aPort,
|
||||
nullptr, // No cert to keep alive
|
||||
true, // temporary
|
||||
mDottedOidForStoringNewHashes,
|
||||
aCertFingerprint,
|
||||
(nsCertOverride::OverrideBits)aOverrideBits,
|
||||
EmptyCString()); // dbkey
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsCertOverrideService::HasMatchingOverride(const nsACString & aHostName, int32_t aPort,
|
||||
nsIX509Cert *aCert,
|
||||
uint32_t *aOverrideBits,
|
||||
bool *aIsTemporary,
|
||||
bool *_retval)
|
||||
{
|
||||
if (aHostName.IsEmpty())
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
if (aPort < -1)
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
|
||||
NS_ENSURE_ARG_POINTER(aCert);
|
||||
NS_ENSURE_ARG_POINTER(aOverrideBits);
|
||||
NS_ENSURE_ARG_POINTER(aIsTemporary);
|
||||
NS_ENSURE_ARG_POINTER(_retval);
|
||||
*_retval = false;
|
||||
*aOverrideBits = nsCertOverride::ob_None;
|
||||
|
||||
nsAutoCString hostPort;
|
||||
GetHostWithPort(aHostName, aPort, hostPort);
|
||||
nsCertOverride settings;
|
||||
|
||||
{
|
||||
ReentrantMonitorAutoEnter lock(monitor);
|
||||
nsCertOverrideEntry *entry = mSettingsTable.GetEntry(hostPort.get());
|
||||
|
||||
if (!entry)
|
||||
return NS_OK;
|
||||
|
||||
settings = entry->mSettings; // copy
|
||||
}
|
||||
|
||||
*aOverrideBits = settings.mOverrideBits;
|
||||
*aIsTemporary = settings.mIsTemporary;
|
||||
|
||||
nsAutoCString fpStr;
|
||||
nsresult rv;
|
||||
|
||||
// This code was originally written in a way that suggested that other hash
|
||||
// algorithms are supported for backwards compatibility. However, this was
|
||||
// always unnecessary, because only SHA256 has ever been used here.
|
||||
if (settings.mFingerprintAlgOID.Equals(mDottedOidForStoringNewHashes)) {
|
||||
rv = GetCertFingerprintByOidTag(aCert, mOidTagForStoringNewHashes, fpStr);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
} else {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
*_retval = settings.mFingerprint.Equals(fpStr);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsCertOverrideService::GetValidityOverride(const nsACString & aHostName, int32_t aPort,
|
||||
nsACString & aHashAlg,
|
||||
nsACString & aFingerprint,
|
||||
uint32_t *aOverrideBits,
|
||||
bool *aIsTemporary,
|
||||
bool *_found)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(_found);
|
||||
NS_ENSURE_ARG_POINTER(aIsTemporary);
|
||||
NS_ENSURE_ARG_POINTER(aOverrideBits);
|
||||
*_found = false;
|
||||
*aOverrideBits = nsCertOverride::ob_None;
|
||||
|
||||
nsAutoCString hostPort;
|
||||
GetHostWithPort(aHostName, aPort, hostPort);
|
||||
nsCertOverride settings;
|
||||
|
||||
{
|
||||
ReentrantMonitorAutoEnter lock(monitor);
|
||||
nsCertOverrideEntry *entry = mSettingsTable.GetEntry(hostPort.get());
|
||||
|
||||
if (entry) {
|
||||
*_found = true;
|
||||
settings = entry->mSettings; // copy
|
||||
}
|
||||
}
|
||||
|
||||
if (*_found) {
|
||||
*aOverrideBits = settings.mOverrideBits;
|
||||
*aIsTemporary = settings.mIsTemporary;
|
||||
aFingerprint = settings.mFingerprint;
|
||||
aHashAlg = settings.mFingerprintAlgOID;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsCertOverrideService::AddEntryToList(const nsACString &aHostName, int32_t aPort,
|
||||
nsIX509Cert *aCert,
|
||||
const bool aIsTemporary,
|
||||
const nsACString &fingerprintAlgOID,
|
||||
const nsACString &fingerprint,
|
||||
nsCertOverride::OverrideBits ob,
|
||||
const nsACString &dbKey)
|
||||
{
|
||||
nsAutoCString hostPort;
|
||||
GetHostWithPort(aHostName, aPort, hostPort);
|
||||
|
||||
{
|
||||
ReentrantMonitorAutoEnter lock(monitor);
|
||||
nsCertOverrideEntry *entry = mSettingsTable.PutEntry(hostPort.get());
|
||||
|
||||
if (!entry) {
|
||||
NS_ERROR("can't insert a null entry!");
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
entry->mHostWithPort = hostPort;
|
||||
|
||||
nsCertOverride &settings = entry->mSettings;
|
||||
settings.mAsciiHost = aHostName;
|
||||
settings.mPort = aPort;
|
||||
settings.mIsTemporary = aIsTemporary;
|
||||
settings.mFingerprintAlgOID = fingerprintAlgOID;
|
||||
settings.mFingerprint = fingerprint;
|
||||
settings.mOverrideBits = ob;
|
||||
settings.mDBKey = dbKey;
|
||||
// remove whitespace from stored dbKey for backwards compatibility
|
||||
settings.mDBKey.StripWhitespace();
|
||||
settings.mCert = aCert;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsCertOverrideService::ClearValidityOverride(const nsACString & aHostName, int32_t aPort)
|
||||
{
|
||||
if (aPort == 0 &&
|
||||
aHostName.EqualsLiteral("all:temporary-certificates")) {
|
||||
RemoveAllTemporaryOverrides();
|
||||
return NS_OK;
|
||||
}
|
||||
nsAutoCString hostPort;
|
||||
GetHostWithPort(aHostName, aPort, hostPort);
|
||||
{
|
||||
ReentrantMonitorAutoEnter lock(monitor);
|
||||
mSettingsTable.RemoveEntry(hostPort.get());
|
||||
Write();
|
||||
}
|
||||
|
||||
if (EnsureNSSInitialized(nssEnsure)) {
|
||||
SSL_ClearSessionCache();
|
||||
} else {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsCertOverrideService::CountPermanentOverrideTelemetry()
|
||||
{
|
||||
ReentrantMonitorAutoEnter lock(monitor);
|
||||
uint32_t overrideCount = 0;
|
||||
for (auto iter = mSettingsTable.Iter(); !iter.Done(); iter.Next()) {
|
||||
if (!iter.Get()->mSettings.mIsTemporary) {
|
||||
overrideCount++;
|
||||
}
|
||||
}
|
||||
Telemetry::Accumulate(Telemetry::SSL_PERMANENT_CERT_ERROR_OVERRIDES,
|
||||
overrideCount);
|
||||
}
|
||||
|
||||
static bool
|
||||
matchesDBKey(nsIX509Cert* cert, const nsCString& matchDbKey)
|
||||
{
|
||||
nsAutoCString dbKey;
|
||||
nsresult rv = cert->GetDbKey(dbKey);
|
||||
if (NS_FAILED(rv)) {
|
||||
return false;
|
||||
}
|
||||
return dbKey.Equals(matchDbKey);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsCertOverrideService::IsCertUsedForOverrides(nsIX509Cert *aCert,
|
||||
bool aCheckTemporaries,
|
||||
bool aCheckPermanents,
|
||||
uint32_t *_retval)
|
||||
{
|
||||
NS_ENSURE_ARG(aCert);
|
||||
NS_ENSURE_ARG(_retval);
|
||||
|
||||
uint32_t counter = 0;
|
||||
{
|
||||
ReentrantMonitorAutoEnter lock(monitor);
|
||||
for (auto iter = mSettingsTable.Iter(); !iter.Done(); iter.Next()) {
|
||||
const nsCertOverride &settings = iter.Get()->mSettings;
|
||||
|
||||
if (( settings.mIsTemporary && !aCheckTemporaries) ||
|
||||
(!settings.mIsTemporary && !aCheckPermanents)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (matchesDBKey(aCert, settings.mDBKey)) {
|
||||
nsAutoCString cert_fingerprint;
|
||||
nsresult rv = NS_ERROR_UNEXPECTED;
|
||||
if (settings.mFingerprintAlgOID.Equals(mDottedOidForStoringNewHashes)) {
|
||||
rv = GetCertFingerprintByOidTag(aCert,
|
||||
mOidTagForStoringNewHashes, cert_fingerprint);
|
||||
}
|
||||
if (NS_SUCCEEDED(rv) &&
|
||||
settings.mFingerprint.Equals(cert_fingerprint)) {
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*_retval = counter;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsCertOverrideService::EnumerateCertOverrides(nsIX509Cert *aCert,
|
||||
CertOverrideEnumerator aEnumerator,
|
||||
void *aUserData)
|
||||
{
|
||||
ReentrantMonitorAutoEnter lock(monitor);
|
||||
for (auto iter = mSettingsTable.Iter(); !iter.Done(); iter.Next()) {
|
||||
const nsCertOverride &settings = iter.Get()->mSettings;
|
||||
|
||||
if (!aCert) {
|
||||
aEnumerator(settings, aUserData);
|
||||
} else {
|
||||
if (matchesDBKey(aCert, settings.mDBKey)) {
|
||||
nsAutoCString cert_fingerprint;
|
||||
nsresult rv = NS_ERROR_UNEXPECTED;
|
||||
if (settings.mFingerprintAlgOID.Equals(mDottedOidForStoringNewHashes)) {
|
||||
rv = GetCertFingerprintByOidTag(aCert,
|
||||
mOidTagForStoringNewHashes, cert_fingerprint);
|
||||
}
|
||||
if (NS_SUCCEEDED(rv) &&
|
||||
settings.mFingerprint.Equals(cert_fingerprint)) {
|
||||
aEnumerator(settings, aUserData);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsCertOverrideService::GetHostWithPort(const nsACString & aHostName, int32_t aPort, nsACString& _retval)
|
||||
{
|
||||
nsAutoCString hostPort(aHostName);
|
||||
if (aPort == -1) {
|
||||
aPort = 443;
|
||||
}
|
||||
if (!hostPort.IsEmpty()) {
|
||||
hostPort.Append(':');
|
||||
hostPort.AppendInt(aPort);
|
||||
}
|
||||
_retval.Assign(hostPort);
|
||||
}
|
||||
187
security/manager/ssl/nsCertOverrideService.h
Normal file
187
security/manager/ssl/nsCertOverrideService.h
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef __NSCERTOVERRIDESERVICE_H__
|
||||
#define __NSCERTOVERRIDESERVICE_H__
|
||||
|
||||
#include "mozilla/ReentrantMonitor.h"
|
||||
#include "nsICertOverrideService.h"
|
||||
#include "nsTHashtable.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsString.h"
|
||||
#include "nsIFile.h"
|
||||
#include "secoidt.h"
|
||||
#include "nsWeakReference.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
|
||||
class nsCertOverride
|
||||
{
|
||||
public:
|
||||
|
||||
enum OverrideBits { ob_None=0, ob_Untrusted=1, ob_Mismatch=2,
|
||||
ob_Time_error=4 };
|
||||
|
||||
nsCertOverride()
|
||||
:mPort(-1)
|
||||
,mOverrideBits(ob_None)
|
||||
{
|
||||
}
|
||||
|
||||
nsCertOverride(const nsCertOverride &other)
|
||||
{
|
||||
this->operator=(other);
|
||||
}
|
||||
|
||||
nsCertOverride &operator=(const nsCertOverride &other)
|
||||
{
|
||||
mAsciiHost = other.mAsciiHost;
|
||||
mPort = other.mPort;
|
||||
mIsTemporary = other.mIsTemporary;
|
||||
mFingerprintAlgOID = other.mFingerprintAlgOID;
|
||||
mFingerprint = other.mFingerprint;
|
||||
mOverrideBits = other.mOverrideBits;
|
||||
mDBKey = other.mDBKey;
|
||||
mCert = other.mCert;
|
||||
return *this;
|
||||
}
|
||||
|
||||
nsCString mAsciiHost;
|
||||
int32_t mPort;
|
||||
bool mIsTemporary; // true: session only, false: stored on disk
|
||||
nsCString mFingerprint;
|
||||
nsCString mFingerprintAlgOID;
|
||||
OverrideBits mOverrideBits;
|
||||
nsCString mDBKey;
|
||||
nsCOMPtr <nsIX509Cert> mCert;
|
||||
|
||||
static void convertBitsToString(OverrideBits ob, nsACString &str);
|
||||
static void convertStringToBits(const nsACString &str, OverrideBits &ob);
|
||||
};
|
||||
|
||||
|
||||
// hash entry class
|
||||
class nsCertOverrideEntry final : public PLDHashEntryHdr
|
||||
{
|
||||
public:
|
||||
// Hash methods
|
||||
typedef const char* KeyType;
|
||||
typedef const char* KeyTypePointer;
|
||||
|
||||
// do nothing with aHost - we require mHead to be set before we're live!
|
||||
explicit nsCertOverrideEntry(KeyTypePointer aHostWithPortUTF8)
|
||||
{
|
||||
}
|
||||
|
||||
nsCertOverrideEntry(const nsCertOverrideEntry& toCopy)
|
||||
{
|
||||
mSettings = toCopy.mSettings;
|
||||
mHostWithPort = toCopy.mHostWithPort;
|
||||
}
|
||||
|
||||
~nsCertOverrideEntry()
|
||||
{
|
||||
}
|
||||
|
||||
KeyType GetKey() const
|
||||
{
|
||||
return HostWithPortPtr();
|
||||
}
|
||||
|
||||
KeyTypePointer GetKeyPointer() const
|
||||
{
|
||||
return HostWithPortPtr();
|
||||
}
|
||||
|
||||
bool KeyEquals(KeyTypePointer aKey) const
|
||||
{
|
||||
return !strcmp(HostWithPortPtr(), aKey);
|
||||
}
|
||||
|
||||
static KeyTypePointer KeyToPointer(KeyType aKey)
|
||||
{
|
||||
return aKey;
|
||||
}
|
||||
|
||||
static PLDHashNumber HashKey(KeyTypePointer aKey)
|
||||
{
|
||||
return PLDHashTable::HashStringKey(aKey);
|
||||
}
|
||||
|
||||
enum { ALLOW_MEMMOVE = false };
|
||||
|
||||
// get methods
|
||||
inline const nsCString &HostWithPort() const { return mHostWithPort; }
|
||||
|
||||
inline KeyTypePointer HostWithPortPtr() const
|
||||
{
|
||||
return mHostWithPort.get();
|
||||
}
|
||||
|
||||
nsCertOverride mSettings;
|
||||
nsCString mHostWithPort;
|
||||
};
|
||||
|
||||
class nsCertOverrideService final : public nsICertOverrideService
|
||||
, public nsIObserver
|
||||
, public nsSupportsWeakReference
|
||||
{
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSICERTOVERRIDESERVICE
|
||||
NS_DECL_NSIOBSERVER
|
||||
|
||||
nsCertOverrideService();
|
||||
|
||||
nsresult Init();
|
||||
void RemoveAllTemporaryOverrides();
|
||||
|
||||
typedef void
|
||||
(*CertOverrideEnumerator)(const nsCertOverride &aSettings,
|
||||
void *aUserData);
|
||||
|
||||
// aCert == null: return all overrides
|
||||
// aCert != null: return overrides that match the given cert
|
||||
nsresult EnumerateCertOverrides(nsIX509Cert *aCert,
|
||||
CertOverrideEnumerator enumerator,
|
||||
void *aUserData);
|
||||
|
||||
// Concates host name and the port number. If the port number is -1 then
|
||||
// port 443 is automatically used. This method ensures there is always a port
|
||||
// number separated with colon.
|
||||
static void GetHostWithPort(const nsACString & aHostName, int32_t aPort, nsACString& _retval);
|
||||
|
||||
protected:
|
||||
~nsCertOverrideService();
|
||||
|
||||
mozilla::ReentrantMonitor monitor;
|
||||
nsCOMPtr<nsIFile> mSettingsFile;
|
||||
nsTHashtable<nsCertOverrideEntry> mSettingsTable;
|
||||
|
||||
SECOidTag mOidTagForStoringNewHashes;
|
||||
nsCString mDottedOidForStoringNewHashes;
|
||||
|
||||
void CountPermanentOverrideTelemetry();
|
||||
|
||||
void RemoveAllFromMemory();
|
||||
nsresult Read();
|
||||
nsresult Write();
|
||||
nsresult AddEntryToList(const nsACString &host, int32_t port,
|
||||
nsIX509Cert *aCert,
|
||||
const bool aIsTemporary,
|
||||
const nsACString &algo_oid,
|
||||
const nsACString &fingerprint,
|
||||
nsCertOverride::OverrideBits ob,
|
||||
const nsACString &dbKey);
|
||||
};
|
||||
|
||||
#define NS_CERTOVERRIDE_CID { /* 67ba681d-5485-4fff-952c-2ee337ffdcd6 */ \
|
||||
0x67ba681d, \
|
||||
0x5485, \
|
||||
0x4fff, \
|
||||
{0x95, 0x2c, 0x2e, 0xe3, 0x37, 0xff, 0xdc, 0xd6} \
|
||||
}
|
||||
|
||||
#endif
|
||||
1451
security/manager/ssl/nsCertTree.cpp
Normal file
1451
security/manager/ssl/nsCertTree.cpp
Normal file
File diff suppressed because it is too large
Load diff
154
security/manager/ssl/nsCertTree.h
Normal file
154
security/manager/ssl/nsCertTree.h
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef _NS_CERTTREE_H_
|
||||
#define _NS_CERTTREE_H_
|
||||
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIServiceManager.h"
|
||||
#include "nsICertTree.h"
|
||||
#include "nsITreeView.h"
|
||||
#include "nsITreeBoxObject.h"
|
||||
#include "nsITreeSelection.h"
|
||||
#include "nsIMutableArray.h"
|
||||
#include "nsNSSComponent.h"
|
||||
#include "nsTArray.h"
|
||||
#include "PLDHashTable.h"
|
||||
#include "nsIX509CertDB.h"
|
||||
#include "nsCertOverrideService.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
|
||||
typedef struct treeArrayElStr treeArrayEl;
|
||||
|
||||
struct CompareCacheHashEntry {
|
||||
enum { max_criterions = 3 };
|
||||
CompareCacheHashEntry();
|
||||
|
||||
void *key; // no ownership
|
||||
bool mCritInit[max_criterions];
|
||||
nsXPIDLString mCrit[max_criterions];
|
||||
};
|
||||
|
||||
struct CompareCacheHashEntryPtr : PLDHashEntryHdr {
|
||||
CompareCacheHashEntryPtr();
|
||||
~CompareCacheHashEntryPtr();
|
||||
CompareCacheHashEntry *entry;
|
||||
};
|
||||
|
||||
class nsCertAddonInfo final : public nsISupports
|
||||
{
|
||||
private:
|
||||
~nsCertAddonInfo() {}
|
||||
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
nsCertAddonInfo() : mUsageCount(0) {}
|
||||
|
||||
RefPtr<nsIX509Cert> mCert;
|
||||
// how many display entries reference this?
|
||||
// (and therefore depend on the underlying cert)
|
||||
int32_t mUsageCount;
|
||||
};
|
||||
|
||||
class nsCertTreeDispInfo : public nsICertTreeItem
|
||||
{
|
||||
protected:
|
||||
virtual ~nsCertTreeDispInfo();
|
||||
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSICERTTREEITEM
|
||||
|
||||
nsCertTreeDispInfo();
|
||||
nsCertTreeDispInfo(nsCertTreeDispInfo &other);
|
||||
|
||||
RefPtr<nsCertAddonInfo> mAddonInfo;
|
||||
enum {
|
||||
direct_db, host_port_override
|
||||
} mTypeOfEntry;
|
||||
nsCString mAsciiHost;
|
||||
int32_t mPort;
|
||||
nsCertOverride::OverrideBits mOverrideBits;
|
||||
bool mIsTemporary;
|
||||
nsCOMPtr<nsIX509Cert> mCert;
|
||||
};
|
||||
|
||||
class nsCertTree : public nsICertTree
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSICERTTREE
|
||||
NS_DECL_NSITREEVIEW
|
||||
|
||||
nsCertTree();
|
||||
|
||||
enum sortCriterion { sort_IssuerOrg, sort_Org, sort_Token,
|
||||
sort_CommonName, sort_IssuedDateDescending, sort_Email, sort_None };
|
||||
|
||||
protected:
|
||||
virtual ~nsCertTree();
|
||||
|
||||
void ClearCompareHash();
|
||||
void RemoveCacheEntry(void *key);
|
||||
|
||||
typedef int (*nsCertCompareFunc)(void *, nsIX509Cert *a, nsIX509Cert *b);
|
||||
|
||||
static CompareCacheHashEntry *getCacheEntry(void *cache, void *aCert);
|
||||
static void CmpInitCriterion(nsIX509Cert *cert, CompareCacheHashEntry *entry,
|
||||
sortCriterion crit, int32_t level);
|
||||
static int32_t CmpByCrit(nsIX509Cert *a, CompareCacheHashEntry *ace,
|
||||
nsIX509Cert *b, CompareCacheHashEntry *bce,
|
||||
sortCriterion crit, int32_t level);
|
||||
static int32_t CmpBy(void *cache, nsIX509Cert *a, nsIX509Cert *b,
|
||||
sortCriterion c0, sortCriterion c1, sortCriterion c2);
|
||||
static int32_t CmpCACert(void *cache, nsIX509Cert *a, nsIX509Cert *b);
|
||||
static int32_t CmpWebSiteCert(void *cache, nsIX509Cert *a, nsIX509Cert *b);
|
||||
static int32_t CmpUserCert(void *cache, nsIX509Cert *a, nsIX509Cert *b);
|
||||
static int32_t CmpEmailCert(void *cache, nsIX509Cert *a, nsIX509Cert *b);
|
||||
nsCertCompareFunc GetCompareFuncFromCertType(uint32_t aType);
|
||||
int32_t CountOrganizations();
|
||||
|
||||
nsresult GetCertsByType(uint32_t aType, nsCertCompareFunc aCertCmpFn,
|
||||
void *aCertCmpFnArg);
|
||||
|
||||
nsresult GetCertsByTypeFromCache(nsIX509CertList *aCache, uint32_t aType,
|
||||
nsCertCompareFunc aCertCmpFn, void *aCertCmpFnArg);
|
||||
private:
|
||||
static const uint32_t kInitialCacheLength = 64;
|
||||
|
||||
nsTArray< RefPtr<nsCertTreeDispInfo> > mDispInfo;
|
||||
nsCOMPtr<nsITreeBoxObject> mTree;
|
||||
nsCOMPtr<nsITreeSelection> mSelection;
|
||||
treeArrayEl *mTreeArray;
|
||||
int32_t mNumOrgs;
|
||||
int32_t mNumRows;
|
||||
PLDHashTable mCompareCache;
|
||||
nsCOMPtr<nsINSSComponent> mNSSComponent;
|
||||
nsCOMPtr<nsICertOverrideService> mOverrideService;
|
||||
RefPtr<nsCertOverrideService> mOriginalOverrideService;
|
||||
|
||||
treeArrayEl *GetThreadDescAtIndex(int32_t _index);
|
||||
already_AddRefed<nsIX509Cert>
|
||||
GetCertAtIndex(int32_t _index, int32_t *outAbsoluteCertOffset = nullptr);
|
||||
already_AddRefed<nsCertTreeDispInfo>
|
||||
GetDispInfoAtIndex(int32_t index, int32_t *outAbsoluteCertOffset = nullptr);
|
||||
void FreeCertArray();
|
||||
nsresult UpdateUIContents();
|
||||
|
||||
nsresult GetCertsByTypeFromCertList(CERTCertList *aCertList,
|
||||
uint32_t aType,
|
||||
nsCertCompareFunc aCertCmpFn,
|
||||
void *aCertCmpFnArg);
|
||||
|
||||
nsCOMPtr<nsIMutableArray> mCellText;
|
||||
|
||||
#ifdef DEBUG_CERT_TREE
|
||||
/* for debugging purposes */
|
||||
void dumpMap();
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif /* _NS_CERTTREE_H_ */
|
||||
|
||||
213
security/manager/ssl/nsClientAuthRemember.cpp
Normal file
213
security/manager/ssl/nsClientAuthRemember.cpp
Normal file
|
|
@ -0,0 +1,213 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsClientAuthRemember.h"
|
||||
|
||||
#include "nsIX509Cert.h"
|
||||
#include "mozilla/BasePrincipal.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "nsCRT.h"
|
||||
#include "nsNSSCertHelper.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsISupportsPrimitives.h"
|
||||
#include "nsPromiseFlatString.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsStringBuffer.h"
|
||||
#include "cert.h"
|
||||
#include "nspr.h"
|
||||
#include "pk11pub.h"
|
||||
#include "certdb.h"
|
||||
#include "sechash.h"
|
||||
#include "SharedSSLState.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::psm;
|
||||
|
||||
NS_IMPL_ISUPPORTS(nsClientAuthRememberService,
|
||||
nsIObserver,
|
||||
nsISupportsWeakReference)
|
||||
|
||||
nsClientAuthRememberService::nsClientAuthRememberService()
|
||||
: monitor("nsClientAuthRememberService.monitor")
|
||||
{
|
||||
}
|
||||
|
||||
nsClientAuthRememberService::~nsClientAuthRememberService()
|
||||
{
|
||||
RemoveAllFromMemory();
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsClientAuthRememberService::Init()
|
||||
{
|
||||
if (!NS_IsMainThread()) {
|
||||
NS_ERROR("nsClientAuthRememberService::Init called off the main thread");
|
||||
return NS_ERROR_NOT_SAME_THREAD;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIObserverService> observerService =
|
||||
mozilla::services::GetObserverService();
|
||||
if (observerService) {
|
||||
observerService->AddObserver(this, "profile-before-change", true);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsClientAuthRememberService::Observe(nsISupports* aSubject,
|
||||
const char* aTopic,
|
||||
const char16_t* aData)
|
||||
{
|
||||
// check the topic
|
||||
if (!nsCRT::strcmp(aTopic, "profile-before-change")) {
|
||||
// The profile is about to change,
|
||||
// or is going away because the application is shutting down.
|
||||
|
||||
ReentrantMonitorAutoEnter lock(monitor);
|
||||
RemoveAllFromMemory();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void nsClientAuthRememberService::ClearRememberedDecisions()
|
||||
{
|
||||
ReentrantMonitorAutoEnter lock(monitor);
|
||||
RemoveAllFromMemory();
|
||||
}
|
||||
|
||||
void nsClientAuthRememberService::ClearAllRememberedDecisions()
|
||||
{
|
||||
RefPtr<nsClientAuthRememberService> svc =
|
||||
PublicSSLState()->GetClientAuthRememberService();
|
||||
svc->ClearRememberedDecisions();
|
||||
|
||||
svc = PrivateSSLState()->GetClientAuthRememberService();
|
||||
svc->ClearRememberedDecisions();
|
||||
}
|
||||
|
||||
void
|
||||
nsClientAuthRememberService::RemoveAllFromMemory()
|
||||
{
|
||||
mSettingsTable.Clear();
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsClientAuthRememberService::RememberDecision(
|
||||
const nsACString& aHostName, const NeckoOriginAttributes& aOriginAttributes,
|
||||
CERTCertificate* aServerCert, CERTCertificate* aClientCert)
|
||||
{
|
||||
// aClientCert == nullptr means: remember that user does not want to use a cert
|
||||
NS_ENSURE_ARG_POINTER(aServerCert);
|
||||
if (aHostName.IsEmpty()) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
nsAutoCString fpStr;
|
||||
nsresult rv = GetCertFingerprintByOidTag(aServerCert, SEC_OID_SHA256, fpStr);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
{
|
||||
ReentrantMonitorAutoEnter lock(monitor);
|
||||
if (aClientCert) {
|
||||
RefPtr<nsNSSCertificate> pipCert(new nsNSSCertificate(aClientCert));
|
||||
nsAutoCString dbkey;
|
||||
rv = pipCert->GetDbKey(dbkey);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
AddEntryToList(aHostName, aOriginAttributes, fpStr, dbkey);
|
||||
}
|
||||
} else {
|
||||
nsCString empty;
|
||||
AddEntryToList(aHostName, aOriginAttributes, fpStr, empty);
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsClientAuthRememberService::HasRememberedDecision(
|
||||
const nsACString& aHostName, const NeckoOriginAttributes& aOriginAttributes,
|
||||
CERTCertificate* aCert, nsACString& aCertDBKey, bool* aRetVal)
|
||||
{
|
||||
if (aHostName.IsEmpty())
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
|
||||
NS_ENSURE_ARG_POINTER(aCert);
|
||||
NS_ENSURE_ARG_POINTER(aRetVal);
|
||||
*aRetVal = false;
|
||||
|
||||
nsresult rv;
|
||||
nsAutoCString fpStr;
|
||||
rv = GetCertFingerprintByOidTag(aCert, SEC_OID_SHA256, fpStr);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
nsAutoCString entryKey;
|
||||
GetEntryKey(aHostName, aOriginAttributes, fpStr, entryKey);
|
||||
nsClientAuthRemember settings;
|
||||
|
||||
{
|
||||
ReentrantMonitorAutoEnter lock(monitor);
|
||||
nsClientAuthRememberEntry* entry = mSettingsTable.GetEntry(entryKey.get());
|
||||
if (!entry)
|
||||
return NS_OK;
|
||||
settings = entry->mSettings; // copy
|
||||
}
|
||||
|
||||
aCertDBKey = settings.mDBKey;
|
||||
*aRetVal = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsClientAuthRememberService::AddEntryToList(
|
||||
const nsACString& aHostName, const NeckoOriginAttributes& aOriginAttributes,
|
||||
const nsACString& aFingerprint, const nsACString& aDBKey)
|
||||
{
|
||||
nsAutoCString entryKey;
|
||||
GetEntryKey(aHostName, aOriginAttributes, aFingerprint, entryKey);
|
||||
|
||||
{
|
||||
ReentrantMonitorAutoEnter lock(monitor);
|
||||
nsClientAuthRememberEntry* entry = mSettingsTable.PutEntry(entryKey.get());
|
||||
|
||||
if (!entry) {
|
||||
NS_ERROR("can't insert a null entry!");
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
entry->mEntryKey = entryKey;
|
||||
|
||||
nsClientAuthRemember& settings = entry->mSettings;
|
||||
settings.mAsciiHost = aHostName;
|
||||
settings.mFingerprint = aFingerprint;
|
||||
settings.mDBKey = aDBKey;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsClientAuthRememberService::GetEntryKey(
|
||||
const nsACString& aHostName,
|
||||
const NeckoOriginAttributes& aOriginAttributes,
|
||||
const nsACString& aFingerprint,
|
||||
nsACString& aEntryKey)
|
||||
{
|
||||
nsAutoCString hostCert(aHostName);
|
||||
nsAutoCString suffix;
|
||||
aOriginAttributes.CreateSuffix(suffix);
|
||||
hostCert.Append(suffix);
|
||||
hostCert.Append(':');
|
||||
hostCert.Append(aFingerprint);
|
||||
|
||||
aEntryKey.Assign(hostCert);
|
||||
}
|
||||
155
security/manager/ssl/nsClientAuthRemember.h
Normal file
155
security/manager/ssl/nsClientAuthRemember.h
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef __NSCLIENTAUTHREMEMBER_H__
|
||||
#define __NSCLIENTAUTHREMEMBER_H__
|
||||
|
||||
#include "mozilla/ReentrantMonitor.h"
|
||||
#include "nsTHashtable.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsIX509Cert.h"
|
||||
#include "nsNSSCertificate.h"
|
||||
#include "nsString.h"
|
||||
#include "nsWeakReference.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
|
||||
namespace mozilla {
|
||||
class NeckoOriginAttributes;
|
||||
}
|
||||
|
||||
using mozilla::NeckoOriginAttributes;
|
||||
|
||||
class nsClientAuthRemember
|
||||
{
|
||||
public:
|
||||
|
||||
nsClientAuthRemember()
|
||||
{
|
||||
}
|
||||
|
||||
nsClientAuthRemember(const nsClientAuthRemember& aOther)
|
||||
{
|
||||
this->operator=(aOther);
|
||||
}
|
||||
|
||||
nsClientAuthRemember& operator=(const nsClientAuthRemember& aOther)
|
||||
{
|
||||
mAsciiHost = aOther.mAsciiHost;
|
||||
mFingerprint = aOther.mFingerprint;
|
||||
mDBKey = aOther.mDBKey;
|
||||
return *this;
|
||||
}
|
||||
|
||||
nsCString mAsciiHost;
|
||||
nsCString mFingerprint;
|
||||
nsCString mDBKey;
|
||||
};
|
||||
|
||||
|
||||
// hash entry class
|
||||
class nsClientAuthRememberEntry final : public PLDHashEntryHdr
|
||||
{
|
||||
public:
|
||||
// Hash methods
|
||||
typedef const char* KeyType;
|
||||
typedef const char* KeyTypePointer;
|
||||
|
||||
// do nothing with aHost - we require mHead to be set before we're live!
|
||||
explicit nsClientAuthRememberEntry(KeyTypePointer aHostWithCertUTF8)
|
||||
{
|
||||
}
|
||||
|
||||
nsClientAuthRememberEntry(const nsClientAuthRememberEntry& aToCopy)
|
||||
{
|
||||
mSettings = aToCopy.mSettings;
|
||||
}
|
||||
|
||||
~nsClientAuthRememberEntry()
|
||||
{
|
||||
}
|
||||
|
||||
KeyType GetKey() const
|
||||
{
|
||||
return EntryKeyPtr();
|
||||
}
|
||||
|
||||
KeyTypePointer GetKeyPointer() const
|
||||
{
|
||||
return EntryKeyPtr();
|
||||
}
|
||||
|
||||
bool KeyEquals(KeyTypePointer aKey) const
|
||||
{
|
||||
return !strcmp(EntryKeyPtr(), aKey);
|
||||
}
|
||||
|
||||
static KeyTypePointer KeyToPointer(KeyType aKey)
|
||||
{
|
||||
return aKey;
|
||||
}
|
||||
|
||||
static PLDHashNumber HashKey(KeyTypePointer aKey)
|
||||
{
|
||||
return PLDHashTable::HashStringKey(aKey);
|
||||
}
|
||||
|
||||
enum { ALLOW_MEMMOVE = false };
|
||||
|
||||
// get methods
|
||||
inline const nsCString& GetEntryKey() const { return mEntryKey; }
|
||||
|
||||
inline KeyTypePointer EntryKeyPtr() const
|
||||
{
|
||||
return mEntryKey.get();
|
||||
}
|
||||
|
||||
nsClientAuthRemember mSettings;
|
||||
nsCString mEntryKey;
|
||||
};
|
||||
|
||||
class nsClientAuthRememberService final : public nsIObserver,
|
||||
public nsSupportsWeakReference
|
||||
{
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIOBSERVER
|
||||
|
||||
nsClientAuthRememberService();
|
||||
|
||||
nsresult Init();
|
||||
|
||||
static void GetEntryKey(const nsACString& aHostName,
|
||||
const NeckoOriginAttributes& aOriginAttributes,
|
||||
const nsACString& aFingerprint,
|
||||
/*out*/ nsACString& aEntryKey);
|
||||
|
||||
nsresult RememberDecision(const nsACString& aHostName,
|
||||
const NeckoOriginAttributes& aOriginAttributes,
|
||||
CERTCertificate* aServerCert,
|
||||
CERTCertificate* aClientCert);
|
||||
|
||||
nsresult HasRememberedDecision(const nsACString& aHostName,
|
||||
const NeckoOriginAttributes& aOriginAttributes,
|
||||
CERTCertificate* aServerCert,
|
||||
nsACString& aCertDBKey, bool* aRetVal);
|
||||
|
||||
void ClearRememberedDecisions();
|
||||
static void ClearAllRememberedDecisions();
|
||||
|
||||
protected:
|
||||
~nsClientAuthRememberService();
|
||||
|
||||
mozilla::ReentrantMonitor monitor;
|
||||
nsTHashtable<nsClientAuthRememberEntry> mSettingsTable;
|
||||
|
||||
void RemoveAllFromMemory();
|
||||
nsresult AddEntryToList(const nsACString& aHost,
|
||||
const NeckoOriginAttributes& aOriginAttributes,
|
||||
const nsACString& aServerFingerprint,
|
||||
const nsACString& aDBKey);
|
||||
};
|
||||
|
||||
#endif
|
||||
113
security/manager/ssl/nsCrypto.cpp
Normal file
113
security/manager/ssl/nsCrypto.cpp
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsCrypto.h"
|
||||
|
||||
#include "nsNSSComponent.h"
|
||||
#include "nsNativeCharsetUtils.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "ScopedNSSTypes.h"
|
||||
|
||||
// QueryInterface implementation for nsPkcs11
|
||||
NS_INTERFACE_MAP_BEGIN(nsPkcs11)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIPKCS11)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
NS_IMPL_ADDREF(nsPkcs11)
|
||||
NS_IMPL_RELEASE(nsPkcs11)
|
||||
|
||||
nsPkcs11::nsPkcs11()
|
||||
{
|
||||
}
|
||||
|
||||
nsPkcs11::~nsPkcs11()
|
||||
{
|
||||
nsNSSShutDownPreventionLock locker;
|
||||
if (isAlreadyShutDown()) {
|
||||
return;
|
||||
}
|
||||
shutdown(ShutdownCalledFrom::Object);
|
||||
}
|
||||
|
||||
// Delete a PKCS11 module from the user's profile.
|
||||
NS_IMETHODIMP
|
||||
nsPkcs11::DeleteModule(const nsAString& aModuleName)
|
||||
{
|
||||
nsNSSShutDownPreventionLock locker;
|
||||
if (isAlreadyShutDown()) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
if (aModuleName.IsEmpty()) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
NS_ConvertUTF16toUTF8 moduleName(aModuleName);
|
||||
// Introduce additional scope for module so all references to it are released
|
||||
// before we call SECMOD_DeleteModule, below.
|
||||
#ifndef MOZ_NO_SMART_CARDS
|
||||
{
|
||||
mozilla::UniqueSECMODModule module(SECMOD_FindModule(moduleName.get()));
|
||||
if (!module) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
nsCOMPtr<nsINSSComponent> nssComponent(
|
||||
do_GetService(PSM_COMPONENT_CONTRACTID));
|
||||
nssComponent->ShutdownSmartCardThread(module.get());
|
||||
}
|
||||
#endif
|
||||
|
||||
// modType is an output variable. We ignore it.
|
||||
int32_t modType;
|
||||
SECStatus srv = SECMOD_DeleteModule(moduleName.get(), &modType);
|
||||
if (srv != SECSuccess) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Add a new PKCS11 module to the user's profile.
|
||||
NS_IMETHODIMP
|
||||
nsPkcs11::AddModule(const nsAString& aModuleName,
|
||||
const nsAString& aLibraryFullPath,
|
||||
int32_t aCryptoMechanismFlags,
|
||||
int32_t aCipherFlags)
|
||||
{
|
||||
nsNSSShutDownPreventionLock locker;
|
||||
if (isAlreadyShutDown()) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
if (aModuleName.IsEmpty()) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
NS_ConvertUTF16toUTF8 moduleName(aModuleName);
|
||||
nsCString fullPath;
|
||||
// NSS doesn't support Unicode path. Use native charset
|
||||
NS_CopyUnicodeToNative(aLibraryFullPath, fullPath);
|
||||
uint32_t mechFlags = SECMOD_PubMechFlagstoInternal(aCryptoMechanismFlags);
|
||||
uint32_t cipherFlags = SECMOD_PubCipherFlagstoInternal(aCipherFlags);
|
||||
SECStatus srv = SECMOD_AddNewModule(moduleName.get(), fullPath.get(),
|
||||
mechFlags, cipherFlags);
|
||||
if (srv != SECSuccess) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
#ifndef MOZ_NO_SMART_CARDS
|
||||
mozilla::UniqueSECMODModule module(SECMOD_FindModule(moduleName.get()));
|
||||
if (!module) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
nsCOMPtr<nsINSSComponent> nssComponent(
|
||||
do_GetService(PSM_COMPONENT_CONTRACTID));
|
||||
nssComponent->LaunchSmartCardThread(module.get());
|
||||
#endif
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
32
security/manager/ssl/nsCrypto.h
Normal file
32
security/manager/ssl/nsCrypto.h
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#ifndef _nsCrypto_h_
|
||||
#define _nsCrypto_h_
|
||||
|
||||
#include "nsIPKCS11.h"
|
||||
|
||||
#include "nsNSSShutDown.h"
|
||||
|
||||
#define NS_PKCS11_CID \
|
||||
{0x74b7a390, 0x3b41, 0x11d4, { 0x8a, 0x80, 0x00, 0x60, 0x08, 0xc8, 0x44, 0xc3} }
|
||||
|
||||
class nsPkcs11 : public nsIPKCS11
|
||||
, public nsNSSShutDownObject
|
||||
{
|
||||
public:
|
||||
nsPkcs11();
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIPKCS11
|
||||
|
||||
protected:
|
||||
virtual ~nsPkcs11();
|
||||
|
||||
private:
|
||||
virtual void virtualDestroyNSSReference() override {}
|
||||
};
|
||||
|
||||
#endif //_nsCrypto_h_
|
||||
438
security/manager/ssl/nsCryptoHash.cpp
Normal file
438
security/manager/ssl/nsCryptoHash.cpp
Normal file
|
|
@ -0,0 +1,438 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "nsCryptoHash.h"
|
||||
|
||||
#include "nsIInputStream.h"
|
||||
#include "nsIKeyModule.h"
|
||||
|
||||
#include "nsString.h"
|
||||
|
||||
#include "sechash.h"
|
||||
#include "pk11pub.h"
|
||||
#include "base64.h"
|
||||
|
||||
#define NS_CRYPTO_HASH_BUFFER_SIZE 4096
|
||||
|
||||
//---------------------------------------------
|
||||
// Implementing nsICryptoHash
|
||||
//---------------------------------------------
|
||||
|
||||
nsCryptoHash::nsCryptoHash()
|
||||
: mHashContext(nullptr)
|
||||
, mInitialized(false)
|
||||
{
|
||||
}
|
||||
|
||||
nsCryptoHash::~nsCryptoHash()
|
||||
{
|
||||
nsNSSShutDownPreventionLock locker;
|
||||
if (isAlreadyShutDown()) {
|
||||
return;
|
||||
}
|
||||
destructorSafeDestroyNSSReference();
|
||||
shutdown(ShutdownCalledFrom::Object);
|
||||
}
|
||||
|
||||
void
|
||||
nsCryptoHash::virtualDestroyNSSReference()
|
||||
{
|
||||
destructorSafeDestroyNSSReference();
|
||||
}
|
||||
|
||||
void
|
||||
nsCryptoHash::destructorSafeDestroyNSSReference()
|
||||
{
|
||||
if (mHashContext)
|
||||
HASH_Destroy(mHashContext);
|
||||
mHashContext = nullptr;
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(nsCryptoHash, nsICryptoHash)
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsCryptoHash::Init(uint32_t algorithm)
|
||||
{
|
||||
nsNSSShutDownPreventionLock locker;
|
||||
if (isAlreadyShutDown()) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
HASH_HashType hashType = (HASH_HashType)algorithm;
|
||||
if (mHashContext)
|
||||
{
|
||||
if ((!mInitialized) && (HASH_GetType(mHashContext) == hashType))
|
||||
{
|
||||
mInitialized = true;
|
||||
HASH_Begin(mHashContext);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Destroy current hash context if the type was different
|
||||
// or Finish method wasn't called.
|
||||
HASH_Destroy(mHashContext);
|
||||
mInitialized = false;
|
||||
}
|
||||
|
||||
mHashContext = HASH_Create(hashType);
|
||||
if (!mHashContext)
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
|
||||
HASH_Begin(mHashContext);
|
||||
mInitialized = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsCryptoHash::InitWithString(const nsACString & aAlgorithm)
|
||||
{
|
||||
nsNSSShutDownPreventionLock locker;
|
||||
if (isAlreadyShutDown()) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
if (aAlgorithm.LowerCaseEqualsLiteral("md2"))
|
||||
return Init(nsICryptoHash::MD2);
|
||||
|
||||
if (aAlgorithm.LowerCaseEqualsLiteral("md5"))
|
||||
return Init(nsICryptoHash::MD5);
|
||||
|
||||
if (aAlgorithm.LowerCaseEqualsLiteral("sha1"))
|
||||
return Init(nsICryptoHash::SHA1);
|
||||
|
||||
if (aAlgorithm.LowerCaseEqualsLiteral("sha256"))
|
||||
return Init(nsICryptoHash::SHA256);
|
||||
|
||||
if (aAlgorithm.LowerCaseEqualsLiteral("sha384"))
|
||||
return Init(nsICryptoHash::SHA384);
|
||||
|
||||
if (aAlgorithm.LowerCaseEqualsLiteral("sha512"))
|
||||
return Init(nsICryptoHash::SHA512);
|
||||
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsCryptoHash::Update(const uint8_t *data, uint32_t len)
|
||||
{
|
||||
nsNSSShutDownPreventionLock locker;
|
||||
if (isAlreadyShutDown()) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
if (!mInitialized)
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
|
||||
HASH_Update(mHashContext, data, len);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsCryptoHash::UpdateFromStream(nsIInputStream *data, uint32_t aLen)
|
||||
{
|
||||
nsNSSShutDownPreventionLock locker;
|
||||
if (isAlreadyShutDown()) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
if (!mInitialized)
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
|
||||
if (!data)
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
|
||||
uint64_t n;
|
||||
nsresult rv = data->Available(&n);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
// if the user has passed UINT32_MAX, then read
|
||||
// everything in the stream
|
||||
|
||||
uint64_t len = aLen;
|
||||
if (aLen == UINT32_MAX)
|
||||
len = n;
|
||||
|
||||
// So, if the stream has NO data available for the hash,
|
||||
// or if the data available is less then what the caller
|
||||
// requested, we can not fulfill the hash update. In this
|
||||
// case, just return NS_ERROR_NOT_AVAILABLE indicating
|
||||
// that there is not enough data in the stream to satisify
|
||||
// the request.
|
||||
|
||||
if (n == 0 || n < len)
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
|
||||
char buffer[NS_CRYPTO_HASH_BUFFER_SIZE];
|
||||
uint32_t read, readLimit;
|
||||
|
||||
while(NS_SUCCEEDED(rv) && len>0)
|
||||
{
|
||||
readLimit = (uint32_t)std::min<uint64_t>(NS_CRYPTO_HASH_BUFFER_SIZE, len);
|
||||
|
||||
rv = data->Read(buffer, readLimit, &read);
|
||||
|
||||
if (NS_SUCCEEDED(rv))
|
||||
rv = Update((const uint8_t*)buffer, read);
|
||||
|
||||
len -= read;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsCryptoHash::Finish(bool ascii, nsACString & _retval)
|
||||
{
|
||||
nsNSSShutDownPreventionLock locker;
|
||||
if (isAlreadyShutDown()) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
if (!mInitialized)
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
|
||||
uint32_t hashLen = 0;
|
||||
unsigned char buffer[HASH_LENGTH_MAX];
|
||||
unsigned char* pbuffer = buffer;
|
||||
|
||||
HASH_End(mHashContext, pbuffer, &hashLen, HASH_LENGTH_MAX);
|
||||
|
||||
mInitialized = false;
|
||||
|
||||
if (ascii)
|
||||
{
|
||||
UniquePORTString asciiData(BTOA_DataToAscii(buffer, hashLen));
|
||||
NS_ENSURE_TRUE(asciiData, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
_retval.Assign(asciiData.get());
|
||||
}
|
||||
else
|
||||
{
|
||||
_retval.Assign((const char*)buffer, hashLen);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//---------------------------------------------
|
||||
// Implementing nsICryptoHMAC
|
||||
//---------------------------------------------
|
||||
|
||||
NS_IMPL_ISUPPORTS(nsCryptoHMAC, nsICryptoHMAC)
|
||||
|
||||
nsCryptoHMAC::nsCryptoHMAC()
|
||||
{
|
||||
mHMACContext = nullptr;
|
||||
}
|
||||
|
||||
nsCryptoHMAC::~nsCryptoHMAC()
|
||||
{
|
||||
nsNSSShutDownPreventionLock locker;
|
||||
if (isAlreadyShutDown()) {
|
||||
return;
|
||||
}
|
||||
destructorSafeDestroyNSSReference();
|
||||
shutdown(ShutdownCalledFrom::Object);
|
||||
}
|
||||
|
||||
void
|
||||
nsCryptoHMAC::virtualDestroyNSSReference()
|
||||
{
|
||||
destructorSafeDestroyNSSReference();
|
||||
}
|
||||
|
||||
void
|
||||
nsCryptoHMAC::destructorSafeDestroyNSSReference()
|
||||
{
|
||||
if (mHMACContext)
|
||||
PK11_DestroyContext(mHMACContext, true);
|
||||
mHMACContext = nullptr;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsCryptoHMAC::Init(uint32_t aAlgorithm, nsIKeyObject *aKeyObject)
|
||||
{
|
||||
nsNSSShutDownPreventionLock locker;
|
||||
if (isAlreadyShutDown()) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
if (mHMACContext)
|
||||
{
|
||||
PK11_DestroyContext(mHMACContext, true);
|
||||
mHMACContext = nullptr;
|
||||
}
|
||||
|
||||
CK_MECHANISM_TYPE HMACMechType;
|
||||
switch (aAlgorithm)
|
||||
{
|
||||
case nsCryptoHMAC::MD2:
|
||||
HMACMechType = CKM_MD2_HMAC; break;
|
||||
case nsCryptoHMAC::MD5:
|
||||
HMACMechType = CKM_MD5_HMAC; break;
|
||||
case nsCryptoHMAC::SHA1:
|
||||
HMACMechType = CKM_SHA_1_HMAC; break;
|
||||
case nsCryptoHMAC::SHA256:
|
||||
HMACMechType = CKM_SHA256_HMAC; break;
|
||||
case nsCryptoHMAC::SHA384:
|
||||
HMACMechType = CKM_SHA384_HMAC; break;
|
||||
case nsCryptoHMAC::SHA512:
|
||||
HMACMechType = CKM_SHA512_HMAC; break;
|
||||
default:
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
NS_ENSURE_ARG_POINTER(aKeyObject);
|
||||
|
||||
nsresult rv;
|
||||
|
||||
int16_t keyType;
|
||||
rv = aKeyObject->GetType(&keyType);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
NS_ENSURE_TRUE(keyType == nsIKeyObject::SYM_KEY, NS_ERROR_INVALID_ARG);
|
||||
|
||||
PK11SymKey* key;
|
||||
// GetKeyObj doesn't addref the key
|
||||
rv = aKeyObject->GetKeyObj(&key);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
SECItem rawData;
|
||||
rawData.data = 0;
|
||||
rawData.len = 0;
|
||||
mHMACContext = PK11_CreateContextBySymKey(
|
||||
HMACMechType, CKA_SIGN, key, &rawData);
|
||||
NS_ENSURE_TRUE(mHMACContext, NS_ERROR_FAILURE);
|
||||
|
||||
SECStatus ss = PK11_DigestBegin(mHMACContext);
|
||||
NS_ENSURE_TRUE(ss == SECSuccess, NS_ERROR_FAILURE);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsCryptoHMAC::Update(const uint8_t *aData, uint32_t aLen)
|
||||
{
|
||||
nsNSSShutDownPreventionLock locker;
|
||||
if (isAlreadyShutDown()) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
if (!mHMACContext)
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
|
||||
if (!aData)
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
|
||||
SECStatus ss = PK11_DigestOp(mHMACContext, aData, aLen);
|
||||
NS_ENSURE_TRUE(ss == SECSuccess, NS_ERROR_FAILURE);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsCryptoHMAC::UpdateFromStream(nsIInputStream *aStream, uint32_t aLen)
|
||||
{
|
||||
nsNSSShutDownPreventionLock locker;
|
||||
if (isAlreadyShutDown()) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
if (!mHMACContext)
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
|
||||
if (!aStream)
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
|
||||
uint64_t n;
|
||||
nsresult rv = aStream->Available(&n);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
// if the user has passed UINT32_MAX, then read
|
||||
// everything in the stream
|
||||
|
||||
uint64_t len = aLen;
|
||||
if (aLen == UINT32_MAX)
|
||||
len = n;
|
||||
|
||||
// So, if the stream has NO data available for the hash,
|
||||
// or if the data available is less then what the caller
|
||||
// requested, we can not fulfill the HMAC update. In this
|
||||
// case, just return NS_ERROR_NOT_AVAILABLE indicating
|
||||
// that there is not enough data in the stream to satisify
|
||||
// the request.
|
||||
|
||||
if (n == 0 || n < len)
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
|
||||
char buffer[NS_CRYPTO_HASH_BUFFER_SIZE];
|
||||
uint32_t read, readLimit;
|
||||
|
||||
while(NS_SUCCEEDED(rv) && len > 0)
|
||||
{
|
||||
readLimit = (uint32_t)std::min<uint64_t>(NS_CRYPTO_HASH_BUFFER_SIZE, len);
|
||||
|
||||
rv = aStream->Read(buffer, readLimit, &read);
|
||||
if (read == 0)
|
||||
return NS_BASE_STREAM_CLOSED;
|
||||
|
||||
if (NS_SUCCEEDED(rv))
|
||||
rv = Update((const uint8_t*)buffer, read);
|
||||
|
||||
len -= read;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsCryptoHMAC::Finish(bool aASCII, nsACString & _retval)
|
||||
{
|
||||
nsNSSShutDownPreventionLock locker;
|
||||
if (isAlreadyShutDown()) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
if (!mHMACContext)
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
|
||||
uint32_t hashLen = 0;
|
||||
unsigned char buffer[HASH_LENGTH_MAX];
|
||||
unsigned char* pbuffer = buffer;
|
||||
|
||||
PK11_DigestFinal(mHMACContext, pbuffer, &hashLen, HASH_LENGTH_MAX);
|
||||
if (aASCII)
|
||||
{
|
||||
UniquePORTString asciiData(BTOA_DataToAscii(buffer, hashLen));
|
||||
NS_ENSURE_TRUE(asciiData, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
_retval.Assign(asciiData.get());
|
||||
}
|
||||
else
|
||||
{
|
||||
_retval.Assign((const char*)buffer, hashLen);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsCryptoHMAC::Reset()
|
||||
{
|
||||
nsNSSShutDownPreventionLock locker;
|
||||
if (isAlreadyShutDown()) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
SECStatus ss = PK11_DigestBegin(mHMACContext);
|
||||
NS_ENSURE_TRUE(ss == SECSuccess, NS_ERROR_FAILURE);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
56
security/manager/ssl/nsCryptoHash.h
Normal file
56
security/manager/ssl/nsCryptoHash.h
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef _nsCryptoHash_h_
|
||||
#define _nsCryptoHash_h_
|
||||
|
||||
#include "nsICryptoHash.h"
|
||||
#include "nsICryptoHMAC.h"
|
||||
#include "nsNSSShutDown.h"
|
||||
#include "hasht.h"
|
||||
#include "secmodt.h"
|
||||
|
||||
class nsIInputStream;
|
||||
|
||||
#define NS_CRYPTO_HASH_CID {0x36a1d3b3, 0xd886, 0x4317, {0x96, 0xff, 0x87, 0xb0, 0x00, 0x5c, 0xfe, 0xf7}}
|
||||
#define NS_CRYPTO_HMAC_CID {0xa496d0a2, 0xdff7, 0x4e23, {0xbd, 0x65, 0x1c, 0xa7, 0x42, 0xfa, 0x17, 0x8a}}
|
||||
|
||||
class nsCryptoHash final : public nsICryptoHash, public nsNSSShutDownObject
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSICRYPTOHASH
|
||||
|
||||
nsCryptoHash();
|
||||
|
||||
private:
|
||||
~nsCryptoHash();
|
||||
|
||||
HASHContext* mHashContext;
|
||||
bool mInitialized;
|
||||
|
||||
virtual void virtualDestroyNSSReference() override;
|
||||
void destructorSafeDestroyNSSReference();
|
||||
};
|
||||
|
||||
class nsCryptoHMAC : public nsICryptoHMAC, public nsNSSShutDownObject
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSICRYPTOHMAC
|
||||
|
||||
nsCryptoHMAC();
|
||||
|
||||
private:
|
||||
~nsCryptoHMAC();
|
||||
PK11Context* mHMACContext;
|
||||
|
||||
virtual void virtualDestroyNSSReference() override;
|
||||
void destructorSafeDestroyNSSReference();
|
||||
};
|
||||
|
||||
#endif // _nsCryptoHash_h_
|
||||
|
||||
336
security/manager/ssl/nsDataSignatureVerifier.cpp
Normal file
336
security/manager/ssl/nsDataSignatureVerifier.cpp
Normal file
|
|
@ -0,0 +1,336 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsDataSignatureVerifier.h"
|
||||
|
||||
#include "cms.h"
|
||||
#include "cryptohi.h"
|
||||
#include "keyhi.h"
|
||||
#include "mozilla/Casting.h"
|
||||
#include "mozilla/Unused.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsNSSComponent.h"
|
||||
#include "nssb64.h"
|
||||
#include "pkix/pkixnss.h"
|
||||
#include "pkix/pkixtypes.h"
|
||||
#include "ScopedNSSTypes.h"
|
||||
#include "secerr.h"
|
||||
#include "SharedCertVerifier.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::pkix;
|
||||
using namespace mozilla::psm;
|
||||
|
||||
SEC_ASN1_MKSUB(SECOID_AlgorithmIDTemplate)
|
||||
|
||||
NS_IMPL_ISUPPORTS(nsDataSignatureVerifier, nsIDataSignatureVerifier)
|
||||
|
||||
const SEC_ASN1Template CERT_SignatureDataTemplate[] =
|
||||
{
|
||||
{ SEC_ASN1_SEQUENCE,
|
||||
0, nullptr, sizeof(CERTSignedData) },
|
||||
{ SEC_ASN1_INLINE | SEC_ASN1_XTRN,
|
||||
offsetof(CERTSignedData,signatureAlgorithm),
|
||||
SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate), },
|
||||
{ SEC_ASN1_BIT_STRING,
|
||||
offsetof(CERTSignedData,signature), },
|
||||
{ 0, }
|
||||
};
|
||||
|
||||
nsDataSignatureVerifier::~nsDataSignatureVerifier()
|
||||
{
|
||||
nsNSSShutDownPreventionLock locker;
|
||||
if (isAlreadyShutDown()) {
|
||||
return;
|
||||
}
|
||||
|
||||
shutdown(ShutdownCalledFrom::Object);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDataSignatureVerifier::VerifyData(const nsACString& aData,
|
||||
const nsACString& aSignature,
|
||||
const nsACString& aPublicKey,
|
||||
bool* _retval)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(_retval);
|
||||
|
||||
nsNSSShutDownPreventionLock locker;
|
||||
if (isAlreadyShutDown()) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
// Allocate an arena to handle the majority of the allocations
|
||||
UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
|
||||
if (!arena) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
// Base 64 decode the key
|
||||
SECItem keyItem;
|
||||
PORT_Memset(&keyItem, 0, sizeof(SECItem));
|
||||
if (!NSSBase64_DecodeBuffer(arena.get(), &keyItem,
|
||||
PromiseFlatCString(aPublicKey).get(),
|
||||
aPublicKey.Length())) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// Extract the public key from the data
|
||||
UniqueCERTSubjectPublicKeyInfo pki(
|
||||
SECKEY_DecodeDERSubjectPublicKeyInfo(&keyItem));
|
||||
if (!pki) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
UniqueSECKEYPublicKey publicKey(SECKEY_ExtractPublicKey(pki.get()));
|
||||
if (!publicKey) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// Base 64 decode the signature
|
||||
SECItem signatureItem;
|
||||
PORT_Memset(&signatureItem, 0, sizeof(SECItem));
|
||||
if (!NSSBase64_DecodeBuffer(arena.get(), &signatureItem,
|
||||
PromiseFlatCString(aSignature).get(),
|
||||
aSignature.Length())) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// Decode the signature and algorithm
|
||||
CERTSignedData sigData;
|
||||
PORT_Memset(&sigData, 0, sizeof(CERTSignedData));
|
||||
SECStatus srv = SEC_QuickDERDecodeItem(arena.get(), &sigData,
|
||||
CERT_SignatureDataTemplate,
|
||||
&signatureItem);
|
||||
if (srv != SECSuccess) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// Perform the final verification
|
||||
DER_ConvertBitString(&(sigData.signature));
|
||||
srv = VFY_VerifyDataWithAlgorithmID(
|
||||
BitwiseCast<const unsigned char*, const char*>(
|
||||
PromiseFlatCString(aData).get()),
|
||||
aData.Length(), publicKey.get(), &(sigData.signature),
|
||||
&(sigData.signatureAlgorithm), nullptr, nullptr);
|
||||
|
||||
*_retval = (srv == SECSuccess);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
nsresult
|
||||
VerifyCMSDetachedSignatureIncludingCertificate(
|
||||
const SECItem& buffer, const SECItem& detachedDigest,
|
||||
nsresult (*verifyCertificate)(CERTCertificate* cert, void* context,
|
||||
void* pinArg),
|
||||
void* verifyCertificateContext, void* pinArg,
|
||||
const nsNSSShutDownPreventionLock& /*proofOfLock*/)
|
||||
{
|
||||
// XXX: missing pinArg is tolerated.
|
||||
if (NS_WARN_IF(!buffer.data && buffer.len > 0) ||
|
||||
NS_WARN_IF(!detachedDigest.data && detachedDigest.len > 0) ||
|
||||
(!verifyCertificate) ||
|
||||
NS_WARN_IF(!verifyCertificateContext)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
UniqueNSSCMSMessage
|
||||
cmsMsg(NSS_CMSMessage_CreateFromDER(const_cast<SECItem*>(&buffer), nullptr,
|
||||
nullptr, nullptr, nullptr, nullptr,
|
||||
nullptr));
|
||||
if (!cmsMsg) {
|
||||
return NS_ERROR_CMS_VERIFY_ERROR_PROCESSING;
|
||||
}
|
||||
|
||||
if (!NSS_CMSMessage_IsSigned(cmsMsg.get())) {
|
||||
return NS_ERROR_CMS_VERIFY_NOT_SIGNED;
|
||||
}
|
||||
|
||||
NSSCMSContentInfo* cinfo = NSS_CMSMessage_ContentLevel(cmsMsg.get(), 0);
|
||||
if (!cinfo) {
|
||||
return NS_ERROR_CMS_VERIFY_NO_CONTENT_INFO;
|
||||
}
|
||||
|
||||
// We're expecting this to be a PKCS#7 signedData content info.
|
||||
if (NSS_CMSContentInfo_GetContentTypeTag(cinfo)
|
||||
!= SEC_OID_PKCS7_SIGNED_DATA) {
|
||||
return NS_ERROR_CMS_VERIFY_NO_CONTENT_INFO;
|
||||
}
|
||||
|
||||
// signedData is non-owning
|
||||
NSSCMSSignedData* signedData =
|
||||
static_cast<NSSCMSSignedData*>(NSS_CMSContentInfo_GetContent(cinfo));
|
||||
if (!signedData) {
|
||||
return NS_ERROR_CMS_VERIFY_NO_CONTENT_INFO;
|
||||
}
|
||||
|
||||
// Set digest value.
|
||||
if (NSS_CMSSignedData_SetDigestValue(signedData, SEC_OID_SHA1,
|
||||
const_cast<SECItem*>(&detachedDigest))) {
|
||||
return NS_ERROR_CMS_VERIFY_BAD_DIGEST;
|
||||
}
|
||||
|
||||
// Parse the certificates into CERTCertificate objects held in memory so
|
||||
// verifyCertificate will be able to find them during path building.
|
||||
UniqueCERTCertList certs(CERT_NewCertList());
|
||||
if (!certs) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
if (signedData->rawCerts) {
|
||||
for (size_t i = 0; signedData->rawCerts[i]; ++i) {
|
||||
UniqueCERTCertificate
|
||||
cert(CERT_NewTempCertificate(CERT_GetDefaultCertDB(),
|
||||
signedData->rawCerts[i], nullptr, false,
|
||||
true));
|
||||
// Skip certificates that fail to parse
|
||||
if (!cert) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (CERT_AddCertToListTail(certs.get(), cert.get()) != SECSuccess) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
Unused << cert.release(); // Ownership transferred to the cert list.
|
||||
}
|
||||
}
|
||||
|
||||
// Get the end-entity certificate.
|
||||
int numSigners = NSS_CMSSignedData_SignerInfoCount(signedData);
|
||||
if (NS_WARN_IF(numSigners != 1)) {
|
||||
return NS_ERROR_CMS_VERIFY_ERROR_PROCESSING;
|
||||
}
|
||||
// signer is non-owning.
|
||||
NSSCMSSignerInfo* signer = NSS_CMSSignedData_GetSignerInfo(signedData, 0);
|
||||
if (NS_WARN_IF(!signer)) {
|
||||
return NS_ERROR_CMS_VERIFY_ERROR_PROCESSING;
|
||||
}
|
||||
CERTCertificate* signerCert =
|
||||
NSS_CMSSignerInfo_GetSigningCertificate(signer, CERT_GetDefaultCertDB());
|
||||
if (!signerCert) {
|
||||
return NS_ERROR_CMS_VERIFY_ERROR_PROCESSING;
|
||||
}
|
||||
|
||||
nsresult rv = verifyCertificate(signerCert, verifyCertificateContext, pinArg);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// See NSS_CMSContentInfo_GetContentTypeOID, which isn't exported from NSS.
|
||||
SECOidData* contentTypeOidData =
|
||||
SECOID_FindOID(&signedData->contentInfo.contentType);
|
||||
if (!contentTypeOidData) {
|
||||
return NS_ERROR_CMS_VERIFY_ERROR_PROCESSING;
|
||||
}
|
||||
|
||||
return MapSECStatus(NSS_CMSSignerInfo_Verify(signer,
|
||||
const_cast<SECItem*>(&detachedDigest),
|
||||
&contentTypeOidData->oid));
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
namespace {
|
||||
|
||||
struct VerifyCertificateContext
|
||||
{
|
||||
nsCOMPtr<nsIX509Cert> signingCert;
|
||||
UniqueCERTCertList builtChain;
|
||||
};
|
||||
|
||||
static nsresult
|
||||
VerifyCertificate(CERTCertificate* cert, void* voidContext, void* pinArg)
|
||||
{
|
||||
// XXX: missing pinArg is tolerated
|
||||
if (NS_WARN_IF(!cert) || NS_WARN_IF(!voidContext)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
VerifyCertificateContext* context =
|
||||
static_cast<VerifyCertificateContext*>(voidContext);
|
||||
|
||||
nsCOMPtr<nsIX509Cert> xpcomCert(nsNSSCertificate::Create(cert));
|
||||
if (!xpcomCert) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
context->signingCert = xpcomCert;
|
||||
|
||||
RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
|
||||
NS_ENSURE_TRUE(certVerifier, NS_ERROR_UNEXPECTED);
|
||||
|
||||
mozilla::pkix::Result result =
|
||||
certVerifier->VerifyCert(cert,
|
||||
certificateUsageObjectSigner,
|
||||
Now(), pinArg,
|
||||
nullptr, // hostname
|
||||
context->builtChain);
|
||||
if (result != Success) {
|
||||
return GetXPCOMFromNSSError(MapResultToPRErrorCode(result));
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDataSignatureVerifier::VerifySignature(const char* aRSABuf,
|
||||
uint32_t aRSABufLen,
|
||||
const char* aPlaintext,
|
||||
uint32_t aPlaintextLen,
|
||||
int32_t* aErrorCode,
|
||||
nsIX509Cert** aSigningCert)
|
||||
{
|
||||
if (!aRSABuf || !aPlaintext || !aErrorCode || !aSigningCert) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
nsNSSShutDownPreventionLock locker;
|
||||
if (isAlreadyShutDown()) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
*aErrorCode = VERIFY_ERROR_OTHER;
|
||||
*aSigningCert = nullptr;
|
||||
|
||||
Digest digest;
|
||||
nsresult rv = digest.DigestBuf(
|
||||
SEC_OID_SHA1,
|
||||
BitwiseCast<const uint8_t*, const char*>(aPlaintext),
|
||||
aPlaintextLen);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
SECItem buffer = {
|
||||
siBuffer,
|
||||
BitwiseCast<unsigned char*, const char*>(aRSABuf),
|
||||
aRSABufLen
|
||||
};
|
||||
|
||||
VerifyCertificateContext context;
|
||||
// XXX: pinArg is missing
|
||||
rv = VerifyCMSDetachedSignatureIncludingCertificate(buffer, digest.get(),
|
||||
VerifyCertificate,
|
||||
&context, nullptr, locker);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
*aErrorCode = VERIFY_OK;
|
||||
} else if (NS_ERROR_GET_MODULE(rv) == NS_ERROR_MODULE_SECURITY) {
|
||||
if (rv == GetXPCOMFromNSSError(SEC_ERROR_UNKNOWN_ISSUER)) {
|
||||
*aErrorCode = VERIFY_ERROR_UNKNOWN_ISSUER;
|
||||
} else {
|
||||
*aErrorCode = VERIFY_ERROR_OTHER;
|
||||
}
|
||||
rv = NS_OK;
|
||||
}
|
||||
if (rv == NS_OK) {
|
||||
context.signingCert.forget(aSigningCert);
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
47
security/manager/ssl/nsDataSignatureVerifier.h
Normal file
47
security/manager/ssl/nsDataSignatureVerifier.h
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef nsDataSignatureVerifier_h
|
||||
#define nsDataSignatureVerifier_h
|
||||
|
||||
#include "certt.h"
|
||||
#include "nsIDataSignatureVerifier.h"
|
||||
#include "nsNSSShutDown.h"
|
||||
|
||||
#define NS_DATASIGNATUREVERIFIER_CID \
|
||||
{ 0x296d76aa, 0x275b, 0x4f3c, \
|
||||
{ 0xaf, 0x8a, 0x30, 0xa4, 0x02, 0x6c, 0x18, 0xfc } }
|
||||
#define NS_DATASIGNATUREVERIFIER_CONTRACTID \
|
||||
"@mozilla.org/security/datasignatureverifier;1"
|
||||
|
||||
class nsDataSignatureVerifier final : public nsIDataSignatureVerifier
|
||||
, public nsNSSShutDownObject
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIDATASIGNATUREVERIFIER
|
||||
|
||||
nsDataSignatureVerifier()
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
~nsDataSignatureVerifier();
|
||||
|
||||
// Nothing to release.
|
||||
virtual void virtualDestroyNSSReference() override {}
|
||||
};
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
nsresult VerifyCMSDetachedSignatureIncludingCertificate(
|
||||
const SECItem& buffer, const SECItem& detachedDigest,
|
||||
nsresult (*verifyCertificate)(CERTCertificate* cert, void* context,
|
||||
void* pinArg),
|
||||
void* verifyCertificateContext, void* pinArg,
|
||||
const nsNSSShutDownPreventionLock& proofOfLock);
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // nsDataSignatureVerifier_h
|
||||
70
security/manager/ssl/nsIASN1Object.idl
Normal file
70
security/manager/ssl/nsIASN1Object.idl
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
/**
|
||||
* This represents an ASN.1 object,
|
||||
* where ASN.1 is "Abstract Syntax Notation number One".
|
||||
*
|
||||
* The additional state information carried in this interface
|
||||
* makes it fit for being used as the data structure
|
||||
* when working with visual reprenstation of ASN.1 objects
|
||||
* in a human user interface, like in a tree widget
|
||||
* where open/close state of nodes must be remembered.
|
||||
*/
|
||||
[scriptable, uuid(ba8bf582-1dd1-11b2-898c-f40246bc9a63)]
|
||||
interface nsIASN1Object : nsISupports {
|
||||
|
||||
/**
|
||||
* Identifiers for the possible types of object.
|
||||
*/
|
||||
const unsigned long ASN1_END_CONTENTS = 0;
|
||||
const unsigned long ASN1_BOOLEAN = 1;
|
||||
const unsigned long ASN1_INTEGER = 2;
|
||||
const unsigned long ASN1_BIT_STRING = 3;
|
||||
const unsigned long ASN1_OCTET_STRING = 4;
|
||||
const unsigned long ASN1_NULL = 5;
|
||||
const unsigned long ASN1_OBJECT_ID = 6;
|
||||
const unsigned long ASN1_ENUMERATED = 10;
|
||||
const unsigned long ASN1_UTF8_STRING = 12;
|
||||
const unsigned long ASN1_SEQUENCE = 16;
|
||||
const unsigned long ASN1_SET = 17;
|
||||
const unsigned long ASN1_PRINTABLE_STRING = 19;
|
||||
const unsigned long ASN1_T61_STRING = 20;
|
||||
const unsigned long ASN1_IA5_STRING = 22;
|
||||
const unsigned long ASN1_UTC_TIME = 23;
|
||||
const unsigned long ASN1_GEN_TIME = 24;
|
||||
const unsigned long ASN1_VISIBLE_STRING = 26;
|
||||
const unsigned long ASN1_UNIVERSAL_STRING = 28;
|
||||
const unsigned long ASN1_BMP_STRING = 30;
|
||||
const unsigned long ASN1_HIGH_TAG_NUMBER = 31;
|
||||
const unsigned long ASN1_CONTEXT_SPECIFIC = 32;
|
||||
const unsigned long ASN1_APPLICATION = 33;
|
||||
const unsigned long ASN1_PRIVATE = 34;
|
||||
|
||||
/**
|
||||
* "type" will be equal to one of the defined object identifiers.
|
||||
*/
|
||||
attribute unsigned long type;
|
||||
|
||||
|
||||
/**
|
||||
* This contains a tag as explained in ASN.1 standards documents.
|
||||
*/
|
||||
attribute unsigned long tag;
|
||||
|
||||
/**
|
||||
* "displayName" contains a human readable explanatory label.
|
||||
*/
|
||||
attribute AString displayName;
|
||||
|
||||
/**
|
||||
* "displayValue" contains the human readable value.
|
||||
*/
|
||||
attribute AString displayValue;
|
||||
};
|
||||
|
||||
15
security/manager/ssl/nsIASN1PrintableItem.idl
Normal file
15
security/manager/ssl/nsIASN1PrintableItem.idl
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
#include "nsIASN1Object.idl"
|
||||
|
||||
[scriptable, uuid(114e1142-1dd2-11b2-ac26-b6db19d9184a)]
|
||||
interface nsIASN1PrintableItem : nsIASN1Object {
|
||||
[noscript] void setData(in charPtr data, in unsigned long len);
|
||||
[noscript] void getData(out charPtr data, out unsigned long len);
|
||||
};
|
||||
|
||||
56
security/manager/ssl/nsIASN1Sequence.idl
Normal file
56
security/manager/ssl/nsIASN1Sequence.idl
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
#include "nsIASN1Object.idl"
|
||||
|
||||
interface nsIMutableArray;
|
||||
|
||||
/**
|
||||
* This represents a sequence of ASN.1 objects,
|
||||
* where ASN.1 is "Abstract Syntax Notation number One".
|
||||
*
|
||||
* Overview of how this ASN1 interface is intended to
|
||||
* work.
|
||||
*
|
||||
* First off, the nsIASN1Sequence is any type in ASN1
|
||||
* that consists of sub-elements (ie SEQUENCE, SET)
|
||||
* nsIASN1Printable Items are all the other types that
|
||||
* can be viewed by themselves without interpreting further.
|
||||
* Examples would include INTEGER, UTF-8 STRING, OID.
|
||||
* These are not intended to directly reflect the numberous
|
||||
* types that exist in ASN1, but merely an interface to ease
|
||||
* producing a tree display the ASN1 structure of any DER
|
||||
* object.
|
||||
*
|
||||
* The additional state information carried in this interface
|
||||
* makes it fit for being used as the data structure
|
||||
* when working with visual reprenstation of ASN.1 objects
|
||||
* in a human user interface, like in a tree widget
|
||||
* where open/close state of nodes must be remembered.
|
||||
*/
|
||||
[scriptable, uuid(b6b957e6-1dd1-11b2-89d7-e30624f50b00)]
|
||||
interface nsIASN1Sequence : nsIASN1Object {
|
||||
|
||||
/**
|
||||
* The array of objects stored in the sequence.
|
||||
*/
|
||||
attribute nsIMutableArray ASN1Objects;
|
||||
|
||||
/**
|
||||
* Whether the node at this position in the ASN.1 data structure
|
||||
* sequence contains sub elements understood by the
|
||||
* application.
|
||||
*/
|
||||
attribute boolean isValidContainer;
|
||||
|
||||
/**
|
||||
* Whether the contained objects should be shown or hidden.
|
||||
* A UI implementation can use this flag to store the current
|
||||
* expansion state when shown in a tree widget.
|
||||
*/
|
||||
attribute boolean isExpanded;
|
||||
};
|
||||
23
security/manager/ssl/nsIAssociatedContentSecurity.idl
Normal file
23
security/manager/ssl/nsIAssociatedContentSecurity.idl
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/**
|
||||
* This interface is used to cache associated (sub) content security
|
||||
* state. That is determined asynchronously based on callback notification
|
||||
* while loading the content and its sub content particles.
|
||||
*
|
||||
* Some optimizations like bfcaching removes these callback notifications
|
||||
* and therefor the subcontent state could not be determined. In such
|
||||
* a case it is loaded from this object stored in nsIChannel.securityInfo.
|
||||
*/
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
[scriptable, uuid(a8285dae-f125-454f-9d1b-089e3f01b2c4)]
|
||||
interface nsIAssociatedContentSecurity : nsISupports
|
||||
{
|
||||
attribute long countSubRequestsBrokenSecurity;
|
||||
attribute long countSubRequestsNoSecurity;
|
||||
void flush();
|
||||
};
|
||||
32
security/manager/ssl/nsIBadCertListener2.idl
Normal file
32
security/manager/ssl/nsIBadCertListener2.idl
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
interface nsISSLStatus;
|
||||
interface nsIInterfaceRequestor;
|
||||
|
||||
/**
|
||||
* A mechanism to report a broken SSL status. The recipient should NOT block.
|
||||
* Can be used to obtain the SSL handshake status of a connection
|
||||
* that will be canceled because of improper cert status.
|
||||
*/
|
||||
[scriptable, uuid(2c3d268c-ad82-49f3-99aa-e9ffddd7a0dc)]
|
||||
interface nsIBadCertListener2 : nsISupports {
|
||||
|
||||
/**
|
||||
* @param socketInfo A network communication context that can be used to obtain more information
|
||||
* about the active connection.
|
||||
* @param status The SSL status object that describes the problem(s).
|
||||
* @param targetSite The Site name that was used to open the current connection.
|
||||
*
|
||||
* @return The consumer shall return true if it wants to suppress the error message
|
||||
* related to the bad cert (the connection will still get canceled).
|
||||
*/
|
||||
boolean notifyCertProblem(in nsIInterfaceRequestor socketInfo,
|
||||
in nsISSLStatus status,
|
||||
in AUTF8String targetSite);
|
||||
};
|
||||
61
security/manager/ssl/nsICertBlocklist.idl
Normal file
61
security/manager/ssl/nsICertBlocklist.idl
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
interface nsIX509Cert;
|
||||
|
||||
%{C++
|
||||
#define NS_CERTBLOCKLIST_CONTRACTID "@mozilla.org/security/certblocklist;1"
|
||||
%}
|
||||
|
||||
/**
|
||||
* Represents a service to add certificates as explicitly blocked/distrusted.
|
||||
*/
|
||||
[scriptable, uuid(e0654480-f433-11e4-b939-0800200c9a66)]
|
||||
interface nsICertBlocklist : nsISupports {
|
||||
/**
|
||||
* Add details of a revoked certificate :
|
||||
* issuer name (base-64 encoded DER) and serial number (base-64 encoded DER).
|
||||
*/
|
||||
void revokeCertByIssuerAndSerial(in string issuer, in string serialNumber);
|
||||
|
||||
/**
|
||||
* Add details of a revoked certificate :
|
||||
* subject name (base-64 encoded DER) and hash of public key (base-64 encoded
|
||||
* sha-256 hash of the public key).
|
||||
*/
|
||||
void revokeCertBySubjectAndPubKey(in string subject, in string pubKeyHash);
|
||||
|
||||
/**
|
||||
* Persist (fresh) blocklist entries to the profile (if a profile directory is
|
||||
* available). Note: calling this will result in synchronous I/O.
|
||||
*/
|
||||
void saveEntries();
|
||||
|
||||
/**
|
||||
* Check if a certificate is blocked.
|
||||
* isser - issuer name, DER encoded
|
||||
* serial - serial number, DER encoded
|
||||
* subject - subject name, DER encoded
|
||||
* pubkey - public key, DER encoded
|
||||
*/
|
||||
boolean isCertRevoked([const, array, size_is(issuer_length)] in octet issuer,
|
||||
in unsigned long issuer_length,
|
||||
[const, array, size_is(serial_length)] in octet serial,
|
||||
in unsigned long serial_length,
|
||||
[const, array, size_is(subject_length)] in octet subject,
|
||||
in unsigned long subject_length,
|
||||
[const, array, size_is(pubkey_length)] in octet pubkey,
|
||||
in unsigned long pubkey_length);
|
||||
|
||||
/**
|
||||
* Check that the blocklist data is current. Specifically, that the current
|
||||
* time is no more than security.onecrl.maximum_staleness_in_seconds seconds
|
||||
* after the last blocklist update (as stored in the
|
||||
* app.update.lastUpdateTime.blocklist-background-update-timer pref)
|
||||
*/
|
||||
boolean isBlocklistFresh();
|
||||
};
|
||||
144
security/manager/ssl/nsICertOverrideService.idl
Normal file
144
security/manager/ssl/nsICertOverrideService.idl
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
interface nsIArray;
|
||||
interface nsIX509Cert;
|
||||
|
||||
%{C++
|
||||
#define NS_CERTOVERRIDE_CONTRACTID "@mozilla.org/security/certoverride;1"
|
||||
%}
|
||||
|
||||
/**
|
||||
* This represents the global list of triples
|
||||
* {host:port, cert-fingerprint, allowed-overrides}
|
||||
* that the user wants to accept without further warnings.
|
||||
*/
|
||||
[scriptable, uuid(be019e47-22fc-4355-9f16-9ab047d6742d)]
|
||||
interface nsICertOverrideService : nsISupports {
|
||||
|
||||
/**
|
||||
* Override Untrusted
|
||||
*/
|
||||
const short ERROR_UNTRUSTED = 1;
|
||||
|
||||
/**
|
||||
* Override hostname Mismatch
|
||||
*/
|
||||
const short ERROR_MISMATCH = 2;
|
||||
|
||||
/**
|
||||
* Override Time error
|
||||
*/
|
||||
const short ERROR_TIME = 4;
|
||||
|
||||
/**
|
||||
* The given cert should always be accepted for the given hostname:port,
|
||||
* regardless of errors verifying the cert.
|
||||
* Host:Port is a primary key, only one entry per host:port can exist.
|
||||
* The implementation will store a fingerprint of the cert.
|
||||
* The implementation will decide which fingerprint alg is used.
|
||||
*
|
||||
* Each override is specific to exactly the errors overridden, so
|
||||
* overriding everything won't match certs at the given host:port
|
||||
* which only exhibit some subset of errors.
|
||||
*
|
||||
* @param aHostName The host (punycode) this mapping belongs to
|
||||
* @param aPort The port this mapping belongs to, if it is -1 then it
|
||||
* is internaly treated as 443
|
||||
* @param aCert The cert that should always be accepted
|
||||
* @param aOverrideBits The precise set of errors we want to be overriden
|
||||
*/
|
||||
void rememberValidityOverride(in ACString aHostName,
|
||||
in int32_t aPort,
|
||||
in nsIX509Cert aCert,
|
||||
in uint32_t aOverrideBits,
|
||||
in boolean aTemporary);
|
||||
|
||||
/**
|
||||
* Certs with the given fingerprint should always be accepted for the
|
||||
* given hostname:port, regardless of errors verifying the cert.
|
||||
* Host:Port is a primary key, only one entry per host:port can exist.
|
||||
* The fingerprint should be an SHA-256 hash of the certificate.
|
||||
*
|
||||
* @param aHostName The host (punycode) this mapping belongs to
|
||||
* @param aPort The port this mapping belongs to, if it is -1 then it
|
||||
* is internaly treated as 443
|
||||
* @param aCertFingerprint The cert fingerprint that should be accepted, in
|
||||
* the format 'AA:BB:...' (colon-separated upper-case hex bytes).
|
||||
* @param aOverrideBits The errors we want to be overriden
|
||||
*/
|
||||
void rememberTemporaryValidityOverrideUsingFingerprint(
|
||||
in ACString aHostName,
|
||||
in int32_t aPort,
|
||||
in ACString aCertFingerprint,
|
||||
in uint32_t aOverrideBits);
|
||||
|
||||
/**
|
||||
* Return whether this host, port, cert triple has a stored override.
|
||||
* If so, the outparams will contain the specific errors that were
|
||||
* overridden, and whether the override is permanent, or only for the current
|
||||
* session.
|
||||
*
|
||||
* @param aHostName The host (punycode) this mapping belongs to
|
||||
* @param aPort The port this mapping belongs to, if it is -1 then it
|
||||
* is internally treated as 443
|
||||
* @param aCert The certificate this mapping belongs to
|
||||
* @param aOverrideBits The errors that are currently overridden
|
||||
* @param aIsTemporary Whether the stored override is session-only,
|
||||
* or permanent
|
||||
* @return Whether an override has been stored for this host+port+cert
|
||||
*/
|
||||
boolean hasMatchingOverride(in ACString aHostName,
|
||||
in int32_t aPort,
|
||||
in nsIX509Cert aCert,
|
||||
out uint32_t aOverrideBits,
|
||||
out boolean aIsTemporary);
|
||||
|
||||
/**
|
||||
* Retrieve the stored override for the given hostname:port.
|
||||
*
|
||||
* @param aHostName The host (punycode) whose entry should be tested
|
||||
* @param aPort The port whose entry should be tested, if it is -1 then it
|
||||
* is internaly treated as 443
|
||||
* @param aHashAlg On return value True, the fingerprint hash algorithm
|
||||
* as an OID value in dotted notation.
|
||||
* @param aFingerprint On return value True, the stored fingerprint
|
||||
* @param aOverrideBits The errors that are currently overriden
|
||||
* @return whether a matching override entry for aHostNameWithPort
|
||||
* and aFingerprint is currently on file
|
||||
*/
|
||||
boolean getValidityOverride(in ACString aHostName,
|
||||
in int32_t aPort,
|
||||
out ACString aHashAlg,
|
||||
out ACString aFingerprint,
|
||||
out uint32_t aOverrideBits,
|
||||
out boolean aIsTemporary);
|
||||
|
||||
/**
|
||||
* Remove a override for the given hostname:port.
|
||||
*
|
||||
* @param aHostName The host (punycode) whose entry should be cleared.
|
||||
* @param aPort The port whose entry should be cleared.
|
||||
* If it is -1, then it is internaly treated as 443.
|
||||
* If it is 0 and aHostName is "all:temporary-certificates",
|
||||
* then all temporary certificates should be cleared.
|
||||
*/
|
||||
void clearValidityOverride(in ACString aHostName,
|
||||
in int32_t aPort);
|
||||
|
||||
/**
|
||||
* Is the given cert used in rules?
|
||||
*
|
||||
* @param aCert The cert we're looking for
|
||||
* @return how many override entries are currently on file
|
||||
* for the given certificate
|
||||
*/
|
||||
uint32_t isCertUsedForOverrides(in nsIX509Cert aCert,
|
||||
in boolean aCheckTemporaries,
|
||||
in boolean aCheckPermanents);
|
||||
};
|
||||
37
security/manager/ssl/nsICertTree.idl
Normal file
37
security/manager/ssl/nsICertTree.idl
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
#include "nsITreeView.idl"
|
||||
|
||||
interface nsIX509Cert;
|
||||
interface nsIX509CertList;
|
||||
|
||||
[scriptable, uuid(d0180863-606e-49e6-8324-cf45ed4dd891)]
|
||||
interface nsICertTreeItem : nsISupports {
|
||||
readonly attribute nsIX509Cert cert;
|
||||
readonly attribute AString hostPort;
|
||||
};
|
||||
|
||||
[scriptable, uuid(55d5ad6b-5572-47fe-941c-f01fe723659e)]
|
||||
interface nsICertTree : nsITreeView {
|
||||
void loadCerts(in unsigned long type);
|
||||
void loadCertsFromCache(in nsIX509CertList cache, in unsigned long type);
|
||||
|
||||
nsIX509Cert getCert(in unsigned long index);
|
||||
nsICertTreeItem getTreeItem(in unsigned long index);
|
||||
|
||||
void deleteEntryObject(in unsigned long index);
|
||||
};
|
||||
|
||||
%{C++
|
||||
|
||||
#define NS_CERTTREE_CID { 0x4ea60761, 0x31d6, 0x491d, \
|
||||
{ 0x9e, 0x34, 0x4b, 0x53, 0xa2, 0x6c, 0x41, 0x6c } }
|
||||
|
||||
#define NS_CERTTREE_CONTRACTID "@mozilla.org/security/nsCertTree;1"
|
||||
|
||||
%}
|
||||
77
security/manager/ssl/nsICertificateDialogs.idl
Normal file
77
security/manager/ssl/nsICertificateDialogs.idl
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
interface nsIInterfaceRequestor;
|
||||
interface nsIX509Cert;
|
||||
|
||||
/**
|
||||
* Functions that implement user interface dialogs to manage certificates.
|
||||
*/
|
||||
[scriptable, uuid(da871dab-f69e-4173-ab26-99fcd47b0e85)]
|
||||
interface nsICertificateDialogs : nsISupports
|
||||
{
|
||||
/**
|
||||
* UI shown when a user is asked to download a new CA cert.
|
||||
* Provides user with ability to choose trust settings for the cert.
|
||||
* Asks the user to grant permission to import the certificate.
|
||||
*
|
||||
* @param ctx A user interface context.
|
||||
* @param cert The certificate that is about to get installed.
|
||||
* @param trust a bit mask of trust flags,
|
||||
* see nsIX509CertDB for possible values.
|
||||
*
|
||||
* @return true if the user allows to import the certificate.
|
||||
*/
|
||||
boolean confirmDownloadCACert(in nsIInterfaceRequestor ctx,
|
||||
in nsIX509Cert cert,
|
||||
out unsigned long trust);
|
||||
|
||||
/**
|
||||
* UI shown when a user's personal certificate is going to be
|
||||
* exported to a backup file.
|
||||
* The implementation of this dialog should make sure
|
||||
* to prompt the user to type the password twice in order to
|
||||
* confirm correct input.
|
||||
* The wording in the dialog should also motivate the user
|
||||
* to enter a strong password.
|
||||
*
|
||||
* @param ctx A user interface context.
|
||||
* @param password The password provided by the user.
|
||||
*
|
||||
* @return false if the user requests to cancel.
|
||||
*/
|
||||
boolean setPKCS12FilePassword(in nsIInterfaceRequestor ctx,
|
||||
out AString password);
|
||||
|
||||
/**
|
||||
* UI shown when a user is about to restore a personal
|
||||
* certificate from a backup file.
|
||||
* The user is requested to enter the password
|
||||
* that was used in the past to protect that backup file.
|
||||
*
|
||||
* @param ctx A user interface context.
|
||||
* @param password The password provided by the user.
|
||||
*
|
||||
* @return false if the user requests to cancel.
|
||||
*/
|
||||
boolean getPKCS12FilePassword(in nsIInterfaceRequestor ctx,
|
||||
out AString password);
|
||||
|
||||
/**
|
||||
* UI shown when a certificate needs to be shown to the user.
|
||||
* The implementation should try to display as many attributes
|
||||
* as possible.
|
||||
*
|
||||
* @param ctx A user interface context.
|
||||
* @param cert The certificate to be shown to the user.
|
||||
*/
|
||||
void viewCert(in nsIInterfaceRequestor ctx,
|
||||
in nsIX509Cert cert);
|
||||
};
|
||||
|
||||
%{C++
|
||||
#define NS_CERTIFICATEDIALOGS_CONTRACTID "@mozilla.org/nsCertificateDialogs;1"
|
||||
%}
|
||||
47
security/manager/ssl/nsIClientAuthDialogs.idl
Normal file
47
security/manager/ssl/nsIClientAuthDialogs.idl
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
interface nsIArray;
|
||||
interface nsIInterfaceRequestor;
|
||||
|
||||
/**
|
||||
* Provides UI for SSL client-auth dialogs.
|
||||
*/
|
||||
[scriptable, uuid(fa4c7520-1433-11d5-ba24-00108303b117)]
|
||||
interface nsIClientAuthDialogs : nsISupports
|
||||
{
|
||||
/**
|
||||
* Called when a user is asked to choose a certificate for client auth.
|
||||
*
|
||||
* @param ctx Context that allows at least nsIClientAuthUserDecision to be
|
||||
* queried.
|
||||
* @param hostname Hostname of the server.
|
||||
* @param port Port of the server.
|
||||
* @param organization Organization field of the server cert.
|
||||
* @param issuerOrg Organization field of the issuer cert of the server cert.
|
||||
* @param certList List of certificates the user can choose from.
|
||||
* @param selectedIndex Index of the cert in |certList| that the user chose.
|
||||
* Ignored if the return value is false.
|
||||
* @return true if a certificate was chosen. false if the user canceled.
|
||||
*/
|
||||
boolean chooseCertificate(in nsIInterfaceRequestor ctx,
|
||||
in AUTF8String hostname,
|
||||
in long port,
|
||||
in AUTF8String organization,
|
||||
in AUTF8String issuerOrg,
|
||||
in nsIArray certList,
|
||||
out unsigned long selectedIndex);
|
||||
};
|
||||
|
||||
[scriptable, uuid(95c4373e-bdd4-4a63-b431-f5b000367721)]
|
||||
interface nsIClientAuthUserDecision : nsISupports
|
||||
{
|
||||
attribute boolean rememberClientAuthCertificate;
|
||||
};
|
||||
|
||||
%{C++
|
||||
#define NS_CLIENTAUTHDIALOGS_CONTRACTID "@mozilla.org/nsClientAuthDialogs;1"
|
||||
%}
|
||||
116
security/manager/ssl/nsIContentSignatureVerifier.idl
Normal file
116
security/manager/ssl/nsIContentSignatureVerifier.idl
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
interface nsIContentSignatureReceiverCallback;
|
||||
|
||||
/**
|
||||
* An interface for verifying content-signatures, inspired by
|
||||
* https://tools.ietf.org/html/draft-thomson-http-content-signature-00
|
||||
* described here https://github.com/franziskuskiefer/content-signature/tree/pki
|
||||
*
|
||||
* A new signature verifier instance should be created for each signature
|
||||
* verification - you can create these instances with do_CreateInstance.
|
||||
*
|
||||
* There are two ways to use this functionality:
|
||||
* The first allows a signature to be verified all at once by simply calling
|
||||
* verifyContentSignature.
|
||||
* The second allows for streaming; call createContext with the signature
|
||||
* information (and initial data), call update with more data as it becomes
|
||||
* available then, finally, call end to verify the signature.
|
||||
*/
|
||||
[scriptable, uuid(45a5fe2f-c350-4b86-962d-02d5aaaa955a)]
|
||||
interface nsIContentSignatureVerifier : nsISupports
|
||||
{
|
||||
|
||||
/**
|
||||
* Verifies that the data matches the data that was used to generate the
|
||||
* signature.
|
||||
*
|
||||
* @param aData The data to be tested.
|
||||
* @param aContentSignatureHeader The content-signature header,
|
||||
* url-safe base64 encoded.
|
||||
* @param aCertificateChain The certificate chain to use for verification.
|
||||
* PEM encoded string.
|
||||
* @param aName The (host)name for which the end entity must
|
||||
be valid.
|
||||
* @returns true if the signature matches the data and aCertificateChain is
|
||||
* valid within aContext, false if not.
|
||||
*/
|
||||
boolean verifyContentSignature(in ACString aData,
|
||||
in ACString aContentSignatureHeader,
|
||||
in ACString aCertificateChain,
|
||||
in ACString aName);
|
||||
|
||||
/**
|
||||
* Creates a context to verify a content signature against data that is added
|
||||
* later with update calls.
|
||||
*
|
||||
* @param aData The first chunk of data to be tested.
|
||||
* @param aContentSignatureHeader The signature of the data, url-safe base64
|
||||
* encoded.
|
||||
* @param aCertificateChain The certificate chain to use for
|
||||
* verification. PEM encoded string.
|
||||
* @param aName The (host)name for which the end entity must
|
||||
be valid.
|
||||
*/
|
||||
void createContext(in ACString aData, in ACString aContentSignatureHeader,
|
||||
in ACString aCertificateChain, in ACString aName);
|
||||
|
||||
/**
|
||||
* Creates a context to verify a content signature against data that is added
|
||||
* later with update calls.
|
||||
* This does not require the caller to download the certificate chain. It's
|
||||
* done internally.
|
||||
* It requires the x5u parameter to be present in aContentSignatureHeader
|
||||
*
|
||||
* NOTE: Callers have to wait for aCallback to return before invoking anything
|
||||
* else. Otherwise the ContentSignatureVerifier will fail.
|
||||
*
|
||||
* @param aCallback Callback that's invoked when the cert chain
|
||||
* got fetched.
|
||||
* @param aContentSignatureHeader The signature of the data, url-safe base64
|
||||
* encoded, and the x5u value.
|
||||
* @param aName The (host)name for which the end entity must
|
||||
be valid.
|
||||
*/
|
||||
void createContextWithoutCertChain(in nsIContentSignatureReceiverCallback aCallback,
|
||||
in ACString aContentSignatureHeader,
|
||||
in ACString aName);
|
||||
|
||||
/**
|
||||
* Adds data to the context that was used to generate the signature.
|
||||
*
|
||||
* @param aData More data to be tested.
|
||||
*/
|
||||
void update(in ACString aData);
|
||||
|
||||
/**
|
||||
* Finalises the signature and returns the result of the signature
|
||||
* verification.
|
||||
*
|
||||
* @returns true if the signature matches the data added with createContext
|
||||
* and update, false if not.
|
||||
*/
|
||||
boolean end();
|
||||
};
|
||||
|
||||
/**
|
||||
* Callback for nsIContentSignatureVerifier.
|
||||
* { 0x1eb90707, 0xdf59, 0x48b7, \
|
||||
* { 0x9d, 0x42, 0xd8, 0xbf, 0x63, 0x0a, 0xe7, 0x44 } }
|
||||
*/
|
||||
[scriptable, uuid(1eb90707-df59-48b7-9d42-d8bf630ae744)]
|
||||
interface nsIContentSignatureReceiverCallback : nsISupports
|
||||
{
|
||||
/**
|
||||
* Notification callback that's called by nsIContentSignatureVerifier when
|
||||
* the cert chain is downloaded.
|
||||
* If download and initialisation were successful, successful is true,
|
||||
* otherwise false. If successful is false, the verification must be aborted.
|
||||
*/
|
||||
void contextCreated(in boolean successful);
|
||||
};
|
||||
40
security/manager/ssl/nsIDataSignatureVerifier.idl
Normal file
40
security/manager/ssl/nsIDataSignatureVerifier.idl
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
interface nsIX509Cert;
|
||||
|
||||
/**
|
||||
* An interface for verifying that a given string of data was signed by the
|
||||
* private key matching the given public key.
|
||||
*/
|
||||
[scriptable, uuid(94066a00-37c9-11e4-916c-0800200c9a66)]
|
||||
interface nsIDataSignatureVerifier : nsISupports
|
||||
{
|
||||
/**
|
||||
* Verifies that the data matches the data that was used to generate the
|
||||
* signature.
|
||||
*
|
||||
* @param aData The data to be tested.
|
||||
* @param aSignature The signature of the data, base64 encoded.
|
||||
* @param aPublicKey The public part of the key used for signing, DER encoded
|
||||
* then base64 encoded.
|
||||
* @returns true if the signature matches the data, false if not.
|
||||
*/
|
||||
boolean verifyData(in ACString aData, in ACString aSignature, in ACString aPublicKey);
|
||||
|
||||
/* Sig Verification Error Codes */
|
||||
const long VERIFY_OK = 0;
|
||||
const long VERIFY_ERROR_UNKNOWN_ISSUER = 1;
|
||||
const long VERIFY_ERROR_OTHER = 2;
|
||||
|
||||
nsIX509Cert verifySignature(in string aSignature,
|
||||
in unsigned long aSignatureLen,
|
||||
in string plaintext,
|
||||
in unsigned long plaintextLen,
|
||||
out long errorCode);
|
||||
|
||||
};
|
||||
34
security/manager/ssl/nsIGenKeypairInfoDlg.idl
Normal file
34
security/manager/ssl/nsIGenKeypairInfoDlg.idl
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
interface nsIInterfaceRequestor;
|
||||
interface nsIKeygenThread;
|
||||
|
||||
/**
|
||||
* nsIGeneratingKeypairInfoDialogs
|
||||
* This is the interface for giving feedback to the user
|
||||
* while generating a key pair.
|
||||
*/
|
||||
[scriptable, uuid(11bf5cdc-1dd2-11b2-ba6a-c76afb326fa1)]
|
||||
interface nsIGeneratingKeypairInfoDialogs : nsISupports
|
||||
{
|
||||
void displayGeneratingKeypairInfo(in nsIInterfaceRequestor ctx,
|
||||
in nsIKeygenThread runnable);
|
||||
};
|
||||
|
||||
%{C++
|
||||
/**
|
||||
* This component is to be implemented by the embeddor. It is used to show
|
||||
* feedback to the user while a private key is being generated.
|
||||
*
|
||||
* This component is only ever used on the UI thread.
|
||||
*
|
||||
* INTERFACES THAT NEED TO BE IMPLEMENTED:
|
||||
* nsIGeneratingKeypairInfoDialogs
|
||||
*/
|
||||
#define NS_GENERATINGKEYPAIRINFODIALOGS_CONTRACTID \
|
||||
"@mozilla.org/nsGeneratingKeypairInfoDialogs;1"
|
||||
%}
|
||||
37
security/manager/ssl/nsIKeyModule.idl
Normal file
37
security/manager/ssl/nsIKeyModule.idl
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
%{ C++
|
||||
/* forward declaration */
|
||||
typedef struct PK11SymKeyStr PK11SymKey;
|
||||
%}
|
||||
[ptr] native PK11SymKeyPtr(PK11SymKey);
|
||||
|
||||
// An opaque key object.
|
||||
[scriptable, uuid(ee2dc516-ba7b-4e77-89fe-c43b886ef715)]
|
||||
interface nsIKeyObject : nsISupports
|
||||
{
|
||||
// Key types
|
||||
const short SYM_KEY = 1;
|
||||
|
||||
// Algorithm types
|
||||
const short HMAC = 257;
|
||||
|
||||
// The nsIKeyObject will take ownership of the key and be responsible
|
||||
// for freeing the key memory when destroyed.
|
||||
[noscript] void initKey(in short aAlgorithm, in PK11SymKeyPtr aKey);
|
||||
|
||||
// Returns a pointer to the underlying key object.
|
||||
[noscript] PK11SymKeyPtr getKeyObj();
|
||||
|
||||
short getType();
|
||||
};
|
||||
|
||||
[scriptable, uuid(838bdbf1-8244-448f-8bcd-cede70227d75)]
|
||||
interface nsIKeyObjectFactory : nsISupports
|
||||
{
|
||||
nsIKeyObject keyFromString(in short aAlgorithm, in ACString aKey);
|
||||
};
|
||||
48
security/manager/ssl/nsIKeygenThread.idl
Normal file
48
security/manager/ssl/nsIKeygenThread.idl
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// make sure to include all the required file headers
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
interface nsIObserver;
|
||||
|
||||
/**
|
||||
* nsIKeygenThread
|
||||
* This is used to communicate with the thread generating a key pair,
|
||||
* to be used by the dialog displaying status information.
|
||||
*/
|
||||
[scriptable, uuid(8712a243-5539-447c-9f47-8653f40c3a09)]
|
||||
interface nsIKeygenThread : nsISupports
|
||||
{
|
||||
/**
|
||||
* startKeyGeneration - run the thread
|
||||
* A user interface using this interface needs to
|
||||
* call this method as soon as the status information
|
||||
* is displaying. This will trigger key generation.
|
||||
* To allow the closure of the status information,
|
||||
* the thread needs a handle to an observer.
|
||||
*
|
||||
* observer will be called on the UI thread.
|
||||
* When the key generation is done, the observe method will
|
||||
* be called with a topic of "keygen-finished" and null data
|
||||
* and subject.
|
||||
*/
|
||||
void startKeyGeneration(in nsIObserver observer);
|
||||
|
||||
/**
|
||||
* userCanceled - notify the thread
|
||||
* If the user canceled, the thread is no longer allowed to
|
||||
* close the dialog. However, if the thread already closed
|
||||
* it, we are not allowed to close it.
|
||||
*/
|
||||
void userCanceled(out boolean threadAlreadyClosedDialog);
|
||||
};
|
||||
|
||||
%{ C++
|
||||
// {195763b8-1dd2-11b2-a843-eb44e44aaa37}
|
||||
#define NS_KEYGENTHREAD_CID \
|
||||
{ 0x195763b8, 0x1dd2, 0x11b2, { 0xa8, 0x43, 0xeb, 0x44, 0xe4, 0x4a, 0xaa, 0x37 } }
|
||||
#define NS_KEYGENTHREAD_CONTRACTID "@mozilla.org/security/keygenthread;1"
|
||||
%}
|
||||
69
security/manager/ssl/nsILocalCertService.idl
Normal file
69
security/manager/ssl/nsILocalCertService.idl
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
interface nsIX509Cert;
|
||||
interface nsILocalCertGetCallback;
|
||||
interface nsILocalCertCallback;
|
||||
|
||||
[scriptable, uuid(9702fdd4-4c2c-439c-ba2e-19cda018eb99)]
|
||||
interface nsILocalCertService : nsISupports
|
||||
{
|
||||
/**
|
||||
* Get or create a new self-signed X.509 cert to represent this device over a
|
||||
* secure transport, like TLS.
|
||||
*
|
||||
* The cert is stored permanently in the profile's key store after first use,
|
||||
* and is valid for 1 year. If an expired or otherwise invalid cert is found
|
||||
* with the nickname supplied here, it is removed and a new one is made.
|
||||
*
|
||||
* @param nickname Nickname that identifies the cert
|
||||
* @param cb Callback to be notified with the result
|
||||
*/
|
||||
void getOrCreateCert(in ACString nickname, in nsILocalCertGetCallback cb);
|
||||
|
||||
/**
|
||||
* Remove a X.509 cert with the given nickname.
|
||||
*
|
||||
* @param nickname Nickname that identifies the cert
|
||||
* @param cb Callback to be notified with the result
|
||||
*/
|
||||
void removeCert(in ACString nickname, in nsILocalCertCallback cb);
|
||||
|
||||
/**
|
||||
* Whether calling |getOrCreateCert| or |removeCert| will trigger a login
|
||||
* prompt to be displayed. Generally this happens if the user has set a
|
||||
* master password, but has not yet logged in.
|
||||
*/
|
||||
readonly attribute boolean loginPromptRequired;
|
||||
};
|
||||
|
||||
[scriptable, uuid(cc09633e-7c70-4093-a9cf-79ab676ca8a9)]
|
||||
interface nsILocalCertGetCallback : nsISupports
|
||||
{
|
||||
/**
|
||||
* Called with the result of the getOrCreateCert operation above.
|
||||
*
|
||||
* @param cert Requested cert, or null if some error
|
||||
* @param result Result code from the get operation
|
||||
*/
|
||||
void handleCert(in nsIX509Cert cert, in nsresult result);
|
||||
};
|
||||
|
||||
[scriptable, uuid(518124e9-55e6-4e23-97c0-4995b3a1bec6)]
|
||||
interface nsILocalCertCallback : nsISupports
|
||||
{
|
||||
/**
|
||||
* Called with the result of the removeCert operation above.
|
||||
*
|
||||
* @param result Result code from the operation
|
||||
*/
|
||||
void handleResult(in nsresult result);
|
||||
};
|
||||
|
||||
%{ C++
|
||||
#define LOCALCERTSERVICE_CONTRACTID \
|
||||
"@mozilla.org/security/local-cert-service;1"
|
||||
%}
|
||||
21
security/manager/ssl/nsINSSU2FToken.idl
Normal file
21
security/manager/ssl/nsINSSU2FToken.idl
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsIU2FToken.idl"
|
||||
|
||||
/**
|
||||
* Interface used to interact with the NSS-backed software U2F Token
|
||||
*/
|
||||
[scriptable, uuid(d9104a00-140b-4f86-a4b0-4998878ef4e6 )]
|
||||
interface nsINSSU2FToken : nsIU2FToken {
|
||||
/**
|
||||
* Initializes the token and constructs and persists keys, if needed. Asserts
|
||||
* that it is only called by the main thread.
|
||||
*/
|
||||
void init();
|
||||
};
|
||||
|
||||
%{C++
|
||||
#define NS_NSSU2FTOKEN_CONTRACTID "@mozilla.org/dom/u2f/nss-u2f-token;1"
|
||||
%}
|
||||
27
security/manager/ssl/nsINSSVersion.idl
Normal file
27
security/manager/ssl/nsINSSVersion.idl
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
%{C++
|
||||
#define NS_NSSVERSION_CONTRACTID "@mozilla.org/security/nssversion;1"
|
||||
%}
|
||||
|
||||
[scriptable, uuid(a8a53a2b-75cc-4c68-a9bb-9791dbddaa00)]
|
||||
interface nsINSSVersion : nsISupports {
|
||||
/* Minimal required versions as used at build time */
|
||||
readonly attribute AString NSPR_MinVersion;
|
||||
readonly attribute AString NSS_MinVersion;
|
||||
readonly attribute AString NSSUTIL_MinVersion;
|
||||
readonly attribute AString NSSSSL_MinVersion;
|
||||
readonly attribute AString NSSSMIME_MinVersion;
|
||||
|
||||
/* Versions of libraries currently in use */
|
||||
readonly attribute AString NSPR_Version;
|
||||
readonly attribute AString NSS_Version;
|
||||
readonly attribute AString NSSUTIL_Version;
|
||||
readonly attribute AString NSSSSL_Version;
|
||||
readonly attribute AString NSSSMIME_Version;
|
||||
};
|
||||
74
security/manager/ssl/nsIPK11Token.idl
Normal file
74
security/manager/ssl/nsIPK11Token.idl
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
[scriptable, uuid(51191434-1dd2-11b2-a17c-e49c4e99a4e3)]
|
||||
interface nsIPK11Token : nsISupports
|
||||
{
|
||||
const long ASK_EVERY_TIME = -1;
|
||||
const long ASK_FIRST_TIME = 0;
|
||||
const long ASK_EXPIRE_TIME = 1;
|
||||
|
||||
/*
|
||||
* The name of the token
|
||||
*/
|
||||
readonly attribute AUTF8String tokenName;
|
||||
readonly attribute AUTF8String tokenLabel;
|
||||
/**
|
||||
* Manufacturer ID of the token.
|
||||
*/
|
||||
readonly attribute AUTF8String tokenManID;
|
||||
/**
|
||||
* Hardware version of the token.
|
||||
*/
|
||||
readonly attribute AUTF8String tokenHWVersion;
|
||||
/**
|
||||
* Firmware version of the token.
|
||||
*/
|
||||
readonly attribute AUTF8String tokenFWVersion;
|
||||
readonly attribute AUTF8String tokenSerialNumber;
|
||||
|
||||
/*
|
||||
* Login information
|
||||
*/
|
||||
boolean isLoggedIn();
|
||||
void login(in boolean force);
|
||||
void logoutSimple();
|
||||
void logoutAndDropAuthenticatedResources();
|
||||
|
||||
/*
|
||||
* Reset password
|
||||
*/
|
||||
void reset();
|
||||
|
||||
/*
|
||||
* Password information
|
||||
*/
|
||||
readonly attribute long minimumPasswordLength;
|
||||
readonly attribute boolean needsUserInit;
|
||||
/**
|
||||
* Checks whether the given password is correct. Logs the token out if an
|
||||
* incorrect password is given.
|
||||
*
|
||||
* @param password The password to check.
|
||||
* @return true if the password was correct, false otherwise.
|
||||
*/
|
||||
boolean checkPassword(in AUTF8String password);
|
||||
void initPassword(in AUTF8String initialPassword);
|
||||
void changePassword(in AUTF8String oldPassword, in AUTF8String newPassword);
|
||||
long getAskPasswordTimes();
|
||||
long getAskPasswordTimeout();
|
||||
void setAskPasswordDefaults([const] in long askTimes, [const] in long timeout);
|
||||
|
||||
/*
|
||||
* Other attributes
|
||||
*/
|
||||
boolean isHardwareToken();
|
||||
boolean needsLogin();
|
||||
boolean isFriendly();
|
||||
};
|
||||
|
||||
42
security/manager/ssl/nsIPK11TokenDB.idl
Normal file
42
security/manager/ssl/nsIPK11TokenDB.idl
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
interface nsIPK11Token;
|
||||
interface nsISimpleEnumerator;
|
||||
|
||||
/**
|
||||
* The PK11 Token Database provides access to the PK11 modules
|
||||
* that are installed, and the tokens that are available.
|
||||
* Interfaces: nsIPK11TokenDB
|
||||
* Threading: ??
|
||||
*/
|
||||
%{C++
|
||||
#define NS_PK11TOKENDB_CONTRACTID "@mozilla.org/security/pk11tokendb;1"
|
||||
%}
|
||||
|
||||
/**
|
||||
* nsIPK11TokenDB - Manages PK11 Tokens
|
||||
*/
|
||||
[scriptable, uuid(4ee28c82-1dd2-11b2-aabf-bb4017abe395)]
|
||||
interface nsIPK11TokenDB : nsISupports
|
||||
{
|
||||
/*
|
||||
* Get the internal key database token
|
||||
*/
|
||||
nsIPK11Token getInternalKeyToken();
|
||||
|
||||
/*
|
||||
* Find a token by name
|
||||
*/
|
||||
nsIPK11Token findTokenByName(in AUTF8String tokenName);
|
||||
|
||||
/*
|
||||
* List all tokens
|
||||
*/
|
||||
nsISimpleEnumerator listTokens();
|
||||
};
|
||||
16
security/manager/ssl/nsIPKCS11.idl
Normal file
16
security/manager/ssl/nsIPKCS11.idl
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
[scriptable, uuid(5743f870-958e-4f02-aef2-c0afeef67f05)]
|
||||
interface nsIPKCS11 : nsISupports
|
||||
{
|
||||
void deleteModule(in AString moduleName);
|
||||
void addModule(in AString moduleName,
|
||||
in AString libraryFullPath,
|
||||
in long cryptoMechanismFlags,
|
||||
in long cipherFlags);
|
||||
};
|
||||
21
security/manager/ssl/nsIPKCS11Module.idl
Normal file
21
security/manager/ssl/nsIPKCS11Module.idl
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
interface nsIPKCS11Slot;
|
||||
interface nsISimpleEnumerator;
|
||||
|
||||
[scriptable, uuid(8a44bdf9-d1a5-4734-bd5a-34ed7fe564c2)]
|
||||
interface nsIPKCS11Module : nsISupports
|
||||
{
|
||||
readonly attribute AUTF8String name;
|
||||
readonly attribute AUTF8String libName;
|
||||
|
||||
nsIPKCS11Slot findSlotByName(in AUTF8String name);
|
||||
|
||||
nsISimpleEnumerator listSlots();
|
||||
};
|
||||
35
security/manager/ssl/nsIPKCS11ModuleDB.idl
Normal file
35
security/manager/ssl/nsIPKCS11ModuleDB.idl
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
interface nsIPKCS11Module;
|
||||
interface nsIPKCS11Slot;
|
||||
interface nsISimpleEnumerator;
|
||||
|
||||
%{C++
|
||||
#define NS_PKCS11MODULEDB_CONTRACTID "@mozilla.org/security/pkcs11moduledb;1"
|
||||
%}
|
||||
|
||||
[scriptable, uuid(ff9fbcd7-9517-4334-b97a-ceed78909974)]
|
||||
interface nsIPKCS11ModuleDB : nsISupports
|
||||
{
|
||||
nsIPKCS11Module getInternal();
|
||||
|
||||
nsIPKCS11Module getInternalFIPS();
|
||||
|
||||
nsIPKCS11Module findModuleByName(in AUTF8String name);
|
||||
|
||||
nsIPKCS11Slot findSlotByName(in AUTF8String name);
|
||||
|
||||
nsISimpleEnumerator listModules();
|
||||
|
||||
readonly attribute boolean canToggleFIPS;
|
||||
|
||||
void toggleFIPSMode();
|
||||
|
||||
readonly attribute boolean isFIPSEnabled;
|
||||
};
|
||||
44
security/manager/ssl/nsIPKCS11Slot.idl
Normal file
44
security/manager/ssl/nsIPKCS11Slot.idl
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
interface nsIPK11Token;
|
||||
|
||||
[scriptable, uuid(c2d4f296-ee60-11d4-998b-00b0d02354a0)]
|
||||
interface nsIPKCS11Slot : nsISupports {
|
||||
readonly attribute AUTF8String name;
|
||||
readonly attribute AUTF8String desc;
|
||||
/**
|
||||
* Manufacturer ID of the slot.
|
||||
*/
|
||||
readonly attribute AUTF8String manID;
|
||||
/**
|
||||
* Hardware version of the slot.
|
||||
*/
|
||||
readonly attribute AUTF8String HWVersion;
|
||||
/**
|
||||
* Firmware version of the slot.
|
||||
*/
|
||||
readonly attribute AUTF8String FWVersion;
|
||||
|
||||
const unsigned long SLOT_DISABLED = 0;
|
||||
const unsigned long SLOT_NOT_PRESENT = 1;
|
||||
const unsigned long SLOT_UNINITIALIZED = 2;
|
||||
const unsigned long SLOT_NOT_LOGGED_IN = 3;
|
||||
const unsigned long SLOT_LOGGED_IN = 4;
|
||||
const unsigned long SLOT_READY = 5;
|
||||
readonly attribute unsigned long status;
|
||||
|
||||
/* This is really a workaround for now. All of the "slot" functions
|
||||
* (isTokenPresent(), etc.) are in nsIPK11Token. For now, return the
|
||||
* token and handle those things there.
|
||||
*/
|
||||
nsIPK11Token getToken();
|
||||
|
||||
/* more fun with workarounds - we're referring to everything by token name */
|
||||
readonly attribute AUTF8String tokenName;
|
||||
};
|
||||
46
security/manager/ssl/nsIProtectedAuthThread.idl
Normal file
46
security/manager/ssl/nsIProtectedAuthThread.idl
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
#include "nsIObserver.idl"
|
||||
#include "nsIPKCS11Slot.idl"
|
||||
|
||||
/**
|
||||
* nsIProtectedAuthThread
|
||||
* This is used to communicate with the thread login on to
|
||||
* a token with CKF_PROTECTED_AUTHENTICATION_PATH set.
|
||||
*/
|
||||
[scriptable, uuid(4bb27cb7-8984-4cee-8ce7-9b014c3d091b)]
|
||||
interface nsIProtectedAuthThread : nsISupports
|
||||
{
|
||||
/**
|
||||
* login - run the thread
|
||||
* A user interface implementing this interface needs to
|
||||
* call this method as soon as the message to the user is
|
||||
* displayed. This will trigger login operation. No user
|
||||
* cancellation is possible during login operation.
|
||||
*
|
||||
* When the login is done, the observe method of @observer will
|
||||
* be called on the UI thread with a topic of "login-finished"
|
||||
* and null data and subject.
|
||||
*/
|
||||
void login(in nsIObserver observer);
|
||||
|
||||
/**
|
||||
* The PKCS11 slot
|
||||
*/
|
||||
readonly attribute nsIPKCS11Slot slot;
|
||||
|
||||
/**
|
||||
* Gets token to be logged in name.
|
||||
*/
|
||||
AString getTokenName();
|
||||
};
|
||||
|
||||
%{ C++
|
||||
// {45334489-3D30-47c6-920B-0A55A313AEBF}
|
||||
#define NS_PROTECTEDAUTHTHREAD_CID \
|
||||
{ 0x45334489, 0x3d30, 0x47c6, { 0x92, 0x0b, 0x0a, 0x55, 0xa3, 0x13, 0xae, 0xbf } }
|
||||
#define NS_PROTECTEDAUTHTHREAD_CONTRACTID "@mozilla.org/security/protectedauththread;1"
|
||||
%}
|
||||
49
security/manager/ssl/nsISSLStatus.idl
Normal file
49
security/manager/ssl/nsISSLStatus.idl
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
interface nsIX509Cert;
|
||||
|
||||
[scriptable, uuid(fa9ba95b-ca3b-498a-b889-7c79cf28fee8)]
|
||||
interface nsISSLStatus : nsISupports {
|
||||
readonly attribute nsIX509Cert serverCert;
|
||||
|
||||
readonly attribute ACString cipherName;
|
||||
readonly attribute unsigned long keyLength;
|
||||
readonly attribute unsigned long secretKeyLength;
|
||||
|
||||
const short SSL_VERSION_3 = 0;
|
||||
const short TLS_VERSION_1 = 1;
|
||||
const short TLS_VERSION_1_1 = 2;
|
||||
const short TLS_VERSION_1_2 = 3;
|
||||
const short TLS_VERSION_1_3 = 4;
|
||||
readonly attribute unsigned short protocolVersion;
|
||||
|
||||
const short CERTIFICATE_TRANSPARENCY_NOT_APPLICABLE = 0;
|
||||
const short CERTIFICATE_TRANSPARENCY_NONE = 1;
|
||||
const short CERTIFICATE_TRANSPARENCY_OK = 2;
|
||||
const short CERTIFICATE_TRANSPARENCY_UNKNOWN_LOG = 3;
|
||||
const short CERTIFICATE_TRANSPARENCY_INVALID = 4;
|
||||
readonly attribute unsigned short certificateTransparencyStatus;
|
||||
|
||||
readonly attribute boolean isDomainMismatch;
|
||||
readonly attribute boolean isNotValidAtThisTime;
|
||||
|
||||
/* Note: To distinguish between
|
||||
* "unstrusted because missing or untrusted issuer"
|
||||
* and
|
||||
* "untrusted because self signed"
|
||||
* query nsIX509Cert::isSelfSigned
|
||||
*/
|
||||
readonly attribute boolean isUntrusted;
|
||||
|
||||
/**
|
||||
* True only if (and after) serverCert was successfully validated as
|
||||
* Extended Validation (EV).
|
||||
*/
|
||||
readonly attribute boolean isExtendedValidation;
|
||||
};
|
||||
13
security/manager/ssl/nsISSLStatusProvider.idl
Normal file
13
security/manager/ssl/nsISSLStatusProvider.idl
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
interface nsISSLStatus;
|
||||
|
||||
[scriptable, uuid(179b1ab1-0950-4427-9556-6f496dc4a27f)]
|
||||
interface nsISSLStatusProvider : nsISupports {
|
||||
readonly attribute nsISSLStatus SSLStatus;
|
||||
};
|
||||
49
security/manager/ssl/nsISecretDecoderRing.idl
Normal file
49
security/manager/ssl/nsISecretDecoderRing.idl
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
[scriptable, uuid(0EC80360-075C-11d4-9FD4-00C04F1B83D8)]
|
||||
interface nsISecretDecoderRing: nsISupports {
|
||||
/**
|
||||
* Encrypt to Base64 output.
|
||||
* Note that the input must basically be a byte array (i.e. the code points
|
||||
* must be within the range [0, 255]). Hence, using this method directly to
|
||||
* encrypt passwords (or any text, really) won't work as expected.
|
||||
* Instead, use something like nsIScriptableUnicodeConverter to first convert
|
||||
* the desired password or text to UTF-8, then encrypt that. Remember to
|
||||
* convert back when calling decryptString().
|
||||
*
|
||||
* @param text The text to encrypt.
|
||||
* @return The encrypted text, encoded as Base64.
|
||||
*/
|
||||
ACString encryptString(in ACString text);
|
||||
|
||||
/**
|
||||
* Decrypt Base64 input.
|
||||
* See the encryptString() documentation - this method has basically the same
|
||||
* limitations.
|
||||
*
|
||||
* @param encryptedBase64Text Encrypted input text, encoded as Base64.
|
||||
* @return The decoded text.
|
||||
*/
|
||||
ACString decryptString(in ACString encryptedBase64Text);
|
||||
|
||||
/**
|
||||
* Prompt the user to change the password on the SDR key.
|
||||
*/
|
||||
void changePassword();
|
||||
|
||||
/**
|
||||
* Logout of the security device that protects the SDR key.
|
||||
*/
|
||||
void logout();
|
||||
|
||||
/**
|
||||
* Logout of the security device that protects the SDR key and tear
|
||||
* down authenticated objects.
|
||||
*/
|
||||
void logoutAndTeardown();
|
||||
};
|
||||
150
security/manager/ssl/nsISecurityUITelemetry.idl
Normal file
150
security/manager/ssl/nsISecurityUITelemetry.idl
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
[scriptable, uuid(5d1acf82-223a-46fb-a8f3-a1b16e2ceb04)]
|
||||
|
||||
interface nsISecurityUITelemetry : nsISupports {
|
||||
|
||||
/*
|
||||
* Addon installation warnings
|
||||
*/
|
||||
|
||||
// Firefox prevented this site from asking you to install addon
|
||||
const uint32_t WARNING_ADDON_ASKING_PREVENTED = 1;
|
||||
// User clicks through and allows site to ask to install addons
|
||||
const uint32_t WARNING_ADDON_ASKING_PREVENTED_CLICK_THROUGH = 2;
|
||||
// Are you sure you want to install this addon? Only install addons you trust
|
||||
const uint32_t WARNING_CONFIRM_ADDON_INSTALL = 3;
|
||||
// User clicked she is sure after waiting 3secs
|
||||
const uint32_t WARNING_CONFIRM_ADDON_INSTALL_CLICK_THROUGH = 4;
|
||||
|
||||
|
||||
/*
|
||||
* modal dialogs/warnings
|
||||
*/
|
||||
|
||||
// removed WARNING_ENTERING_SECURE_SITE = 5;
|
||||
// removed WARNING_ENTERING_WEAK_SITE = 6;
|
||||
// removed WARNING_LEAVING_SECURE_SITE = 7;
|
||||
// removed WARNING_MIXED_CONTENT = 8;
|
||||
|
||||
// For confirmation dialogs, the clickthrough constant needs to be 1
|
||||
// more than the dialog constant so that
|
||||
// WARNING_CONFIRM_<X> + 1 == WARNING_CONFIRM_<X>_CLICK_THROUGH
|
||||
const uint32_t WARNING_CONFIRM_POST_TO_INSECURE_FROM_SECURE = 9;
|
||||
const uint32_t WARNING_CONFIRM_POST_TO_INSECURE_FROM_SECURE_CLICK_THROUGH = 10;
|
||||
// removed WARNING_CONFIRM_POST_TO_INSECURE_FROM_INSECURE = 11;
|
||||
// removed WARNING_CONFIRM_POST_TO_INSECURE_FROM_INSECURE_CLICK_THROUGH = 12;
|
||||
|
||||
/*
|
||||
* Phishing / Malware page warnings
|
||||
* deprecated: we use the _TOP and _FRAME versions below
|
||||
*/
|
||||
|
||||
const uint32_t WARNING_MALWARE_PAGE = 13;
|
||||
const uint32_t WARNING_MALWARE_PAGE_WHY_BLOCKED = 14;
|
||||
const uint32_t WARNING_MALWARE_PAGE_GET_ME_OUT_OF_HERE = 15;
|
||||
const uint32_t WARNING_MALWARE_PAGE_IGNORE_WARNING = 16;
|
||||
|
||||
const uint32_t WARNING_PHISHING_PAGE = 17;
|
||||
const uint32_t WARNING_PHISHING_PAGE_WHY_BLOCKED = 18;
|
||||
const uint32_t WARNING_PHISHING_PAGE_GET_ME_OUT_OF_HERE = 19;
|
||||
const uint32_t WARNING_PHISHING_PAGE_IGNORE_WARNING = 20;
|
||||
|
||||
/*
|
||||
* SSL Error dialogs
|
||||
* deprecated: we use the _TOP versions below
|
||||
*/
|
||||
|
||||
const uint32_t WARNING_BAD_CERT = 21;
|
||||
const uint32_t WARNING_BAD_CERT_STS = 22;
|
||||
const uint32_t WARNING_BAD_CERT_CLICK_ADD_EXCEPTION = 23;
|
||||
const uint32_t WARNING_BAD_CERT_CLICK_VIEW_CERT = 24;
|
||||
const uint32_t WARNING_BAD_CERT_DONT_REMEMBER_EXCEPTION = 25;
|
||||
const uint32_t WARNING_BAD_CERT_GET_ME_OUT_OF_HERE = 27;
|
||||
const uint32_t WARNING_BAD_CERT_UNDERSTAND_RISKS = 28;
|
||||
const uint32_t WARNING_BAD_CERT_TECHINICAL_DETAILS = 29;
|
||||
|
||||
/*
|
||||
* Note that if we add more possibilities in the warning dialogs,
|
||||
* it is a new experiment and we shouldn't reuse these buckets.
|
||||
*/
|
||||
const uint32_t WARNING_BAD_CERT_ADD_EXCEPTION_BASE = 30;
|
||||
const uint32_t WARNING_BAD_CERT_ADD_EXCEPTION_FLAG_UNTRUSTED = 1;
|
||||
const uint32_t WARNING_BAD_CERT_ADD_EXCEPTION_FLAG_DOMAIN = 2;
|
||||
const uint32_t WARNING_BAD_CERT_ADD_EXCEPTION_FLAG_TIME = 4;
|
||||
|
||||
const uint32_t WARNING_BAD_CERT_CONFIRM_ADD_EXCEPTION_BASE = 38;
|
||||
const uint32_t WARNING_BAD_CERT_CONFIRM_ADD_EXCEPTION_FLAG_UNTRUSTED = 1;
|
||||
const uint32_t WARNING_BAD_CERT_CONFIRM_ADD_EXCEPTION_FLAG_DOMAIN = 2;
|
||||
const uint32_t WARNING_BAD_CERT_CONFIRM_ADD_EXCEPTION_FLAG_TIME = 4;
|
||||
// This uses up buckets till 45
|
||||
|
||||
/*
|
||||
* Geolocation Popup Telemetry
|
||||
*/
|
||||
|
||||
const uint32_t WARNING_GEOLOCATION_REQUEST = 46;
|
||||
const uint32_t WARNING_GEOLOCATION_REQUEST_SHARE_LOCATION = 47;
|
||||
const uint32_t WARNING_GEOLOCATION_REQUEST_ALWAYS_SHARE = 48;
|
||||
const uint32_t WARNING_GEOLOCATION_REQUEST_NEVER_SHARE = 49;
|
||||
// It would be nice to measure the two cases of user clicking
|
||||
// "not now", and user closing the popup. This is currently not implemented.
|
||||
|
||||
const uint32_t WARNING_MALWARE_PAGE_TOP = 52;
|
||||
const uint32_t WARNING_MALWARE_PAGE_TOP_WHY_BLOCKED = 53;
|
||||
const uint32_t WARNING_MALWARE_PAGE_TOP_GET_ME_OUT_OF_HERE = 54;
|
||||
const uint32_t WARNING_MALWARE_PAGE_TOP_IGNORE_WARNING = 55;
|
||||
|
||||
const uint32_t WARNING_PHISHING_PAGE_TOP = 56;
|
||||
const uint32_t WARNING_PHISHING_PAGE_TOP_WHY_BLOCKED = 57;
|
||||
const uint32_t WARNING_PHISHING_PAGE_TOP_GET_ME_OUT_OF_HERE = 58;
|
||||
const uint32_t WARNING_PHISHING_PAGE_TOP_IGNORE_WARNING = 59;
|
||||
|
||||
const uint32_t WARNING_MALWARE_PAGE_FRAME = 60;
|
||||
const uint32_t WARNING_MALWARE_PAGE_FRAME_WHY_BLOCKED = 61;
|
||||
const uint32_t WARNING_MALWARE_PAGE_FRAME_GET_ME_OUT_OF_HERE = 62;
|
||||
const uint32_t WARNING_MALWARE_PAGE_FRAME_IGNORE_WARNING = 63;
|
||||
|
||||
const uint32_t WARNING_PHISHING_PAGE_FRAME = 64;
|
||||
const uint32_t WARNING_PHISHING_PAGE_FRAME_WHY_BLOCKED = 65;
|
||||
const uint32_t WARNING_PHISHING_PAGE_FRAME_GET_ME_OUT_OF_HERE = 66;
|
||||
const uint32_t WARNING_PHISHING_PAGE_FRAME_IGNORE_WARNING = 67;
|
||||
|
||||
const uint32_t WARNING_BAD_CERT_TOP = 68;
|
||||
const uint32_t WARNING_BAD_CERT_TOP_STS = 69;
|
||||
const uint32_t WARNING_BAD_CERT_TOP_CLICK_ADD_EXCEPTION = 70;
|
||||
const uint32_t WARNING_BAD_CERT_TOP_CLICK_VIEW_CERT = 71;
|
||||
const uint32_t WARNING_BAD_CERT_TOP_DONT_REMEMBER_EXCEPTION = 72;
|
||||
const uint32_t WARNING_BAD_CERT_TOP_GET_ME_OUT_OF_HERE = 73;
|
||||
const uint32_t WARNING_BAD_CERT_TOP_UNDERSTAND_RISKS = 74;
|
||||
// removed WARNING_BAD_CERT_TOP_TECHNICAL_DETAILS = 75;
|
||||
|
||||
const uint32_t WARNING_BAD_CERT_TOP_ADD_EXCEPTION_BASE = 76;
|
||||
const uint32_t WARNING_BAD_CERT_TOP_ADD_EXCEPTION_FLAG_UNTRUSTED = 1;
|
||||
const uint32_t WARNING_BAD_CERT_TOP_ADD_EXCEPTION_FLAG_DOMAIN = 2;
|
||||
const uint32_t WARNING_BAD_CERT_TOP_ADD_EXCEPTION_FLAG_TIME = 4;
|
||||
|
||||
const uint32_t WARNING_BAD_CERT_TOP_CONFIRM_ADD_EXCEPTION_BASE = 84;
|
||||
const uint32_t WARNING_BAD_CERT_TOP_CONFIRM_ADD_EXCEPTION_FLAG_UNTRUSTED = 1;
|
||||
const uint32_t WARNING_BAD_CERT_TOP_CONFIRM_ADD_EXCEPTION_FLAG_DOMAIN = 2;
|
||||
const uint32_t WARNING_BAD_CERT_TOP_CONFIRM_ADD_EXCEPTION_FLAG_TIME = 4;
|
||||
|
||||
// Another Safe Browsing list (like malware & phishing above)
|
||||
const uint32_t WARNING_UNWANTED_PAGE_TOP = 92;
|
||||
const uint32_t WARNING_UNWANTED_PAGE_TOP_WHY_BLOCKED = 93;
|
||||
const uint32_t WARNING_UNWANTED_PAGE_TOP_GET_ME_OUT_OF_HERE = 94;
|
||||
const uint32_t WARNING_UNWANTED_PAGE_TOP_IGNORE_WARNING = 95;
|
||||
const uint32_t WARNING_UNWANTED_PAGE_FRAME = 96;
|
||||
const uint32_t WARNING_UNWANTED_PAGE_FRAME_WHY_BLOCKED = 97;
|
||||
const uint32_t WARNING_UNWANTED_PAGE_FRAME_GET_ME_OUT_OF_HERE = 98;
|
||||
const uint32_t WARNING_UNWANTED_PAGE_FRAME_IGNORE_WARNING = 99;
|
||||
|
||||
// This uses up buckets till 99 (including)
|
||||
// We only have buckets up to 100.
|
||||
};
|
||||
204
security/manager/ssl/nsISiteSecurityService.idl
Normal file
204
security/manager/ssl/nsISiteSecurityService.idl
Normal file
|
|
@ -0,0 +1,204 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
interface nsIURI;
|
||||
interface nsIObserver;
|
||||
interface nsIHttpChannel;
|
||||
interface nsISSLStatus;
|
||||
|
||||
%{C++
|
||||
#include "nsTArrayForwardDeclare.h"
|
||||
class nsCString;
|
||||
namespace mozilla
|
||||
{
|
||||
namespace pkix
|
||||
{
|
||||
class Time;
|
||||
}
|
||||
}
|
||||
%}
|
||||
[ref] native nsCStringTArrayRef(nsTArray<nsCString>);
|
||||
[ref] native mozillaPkixTime(mozilla::pkix::Time);
|
||||
|
||||
[scriptable, uuid(275127f8-dbd7-4681-afbf-6df0c6587a01)]
|
||||
interface nsISiteSecurityService : nsISupports
|
||||
{
|
||||
const uint32_t HEADER_HSTS = 0;
|
||||
const uint32_t HEADER_HPKP = 1;
|
||||
const uint32_t HEADER_OMS = 2;
|
||||
|
||||
const uint32_t Success = 0;
|
||||
const uint32_t ERROR_UNKNOWN = 1;
|
||||
const uint32_t ERROR_UNTRUSTWORTHY_CONNECTION = 2;
|
||||
const uint32_t ERROR_COULD_NOT_PARSE_HEADER = 3;
|
||||
const uint32_t ERROR_NO_MAX_AGE = 4;
|
||||
const uint32_t ERROR_MULTIPLE_MAX_AGES = 5;
|
||||
const uint32_t ERROR_INVALID_MAX_AGE = 6;
|
||||
const uint32_t ERROR_MULTIPLE_INCLUDE_SUBDOMAINS = 7;
|
||||
const uint32_t ERROR_INVALID_INCLUDE_SUBDOMAINS = 8;
|
||||
const uint32_t ERROR_INVALID_PIN = 9;
|
||||
const uint32_t ERROR_MULTIPLE_REPORT_URIS = 10;
|
||||
const uint32_t ERROR_PINSET_DOES_NOT_MATCH_CHAIN = 11;
|
||||
const uint32_t ERROR_NO_BACKUP_PIN = 12;
|
||||
const uint32_t ERROR_COULD_NOT_SAVE_STATE = 13;
|
||||
const uint32_t ERROR_ROOT_NOT_BUILT_IN = 14;
|
||||
|
||||
/**
|
||||
* Parses a given HTTP header and records the results internally.
|
||||
* Currently two header types are supported: HSTS (aka STS) and HPKP
|
||||
* The format of the HSTS header is defined by the HSTS specification:
|
||||
* https://tools.ietf.org/html/rfc6797
|
||||
* and allows a host to specify that future HTTP requests should be
|
||||
* upgraded to HTTPS.
|
||||
* The format of the HPKP header is defined by the HPKP specification:
|
||||
* https://tools.ietf.org/html/rfc7469
|
||||
* and allows a host to specify a subset of trusted anchors to be used
|
||||
* in future HTTPS connections.
|
||||
*
|
||||
* @param aType the type of security header in question.
|
||||
* @param aSourceURI the URI of the resource with the HTTP header.
|
||||
* @param aSSLStatus the SSLStatus of the current channel
|
||||
* @param aHeader the HTTP response header specifying security data.
|
||||
* @param aFlags options for this request as defined in nsISocketProvider:
|
||||
* NO_PERMANENT_STORAGE
|
||||
* @param aMaxAge the parsed max-age directive of the header.
|
||||
* @param aIncludeSubdomains the parsed includeSubdomains directive.
|
||||
* @param aFailureResult a more specific failure result if NS_ERROR_FAILURE
|
||||
was returned.
|
||||
* @return NS_OK if it succeeds
|
||||
* NS_ERROR_FAILURE if it can't be parsed
|
||||
* NS_SUCCESS_LOSS_OF_INSIGNIFICANT_DATA
|
||||
* if there are unrecognized tokens in the header.
|
||||
*/
|
||||
void processHeader(in uint32_t aType,
|
||||
in nsIURI aSourceURI,
|
||||
in string aHeader,
|
||||
in nsISSLStatus aSSLStatus,
|
||||
in uint32_t aFlags,
|
||||
[optional] out unsigned long long aMaxAge,
|
||||
[optional] out boolean aIncludeSubdomains,
|
||||
[optional] out uint32_t aFailureResult);
|
||||
|
||||
/**
|
||||
* Same as processHeader but without checking for the security properties
|
||||
* of the connection. Use ONLY for testing.
|
||||
*/
|
||||
void unsafeProcessHeader(in uint32_t aType,
|
||||
in nsIURI aSourceURI,
|
||||
in string aHeader,
|
||||
in uint32_t aFlags,
|
||||
[optional] out unsigned long long aMaxAge,
|
||||
[optional] out boolean aIncludeSubdomains,
|
||||
[optional] out uint32_t aFailureResult);
|
||||
|
||||
/**
|
||||
* Given a header type, removes state relating to that header of a host,
|
||||
* including the includeSubdomains state that would affect subdomains.
|
||||
* This essentially removes the state for the domain tree rooted at this
|
||||
* host.
|
||||
* @param aType the type of security state in question
|
||||
* @param aURI the URI of the target host
|
||||
* @param aFlags options for this request as defined in nsISocketProvider:
|
||||
* NO_PERMANENT_STORAGE
|
||||
*/
|
||||
void removeState(in uint32_t aType,
|
||||
in nsIURI aURI,
|
||||
in uint32_t aFlags);
|
||||
|
||||
/**
|
||||
* See isSecureURI
|
||||
*
|
||||
* @param aType the type of security state in question.
|
||||
* @param aHost the hostname (punycode) to query for state.
|
||||
* @param aFlags options for this request as defined in nsISocketProvider:
|
||||
* NO_PERMANENT_STORAGE
|
||||
* @param aCached true if we have cached information regarding whether or not
|
||||
* the host is HSTS, false otherwise.
|
||||
*/
|
||||
boolean isSecureHost(in uint32_t aType,
|
||||
in string aHost,
|
||||
in uint32_t aFlags,
|
||||
[optional] out boolean aCached);
|
||||
|
||||
/**
|
||||
* Checks whether or not the URI's hostname has a given security state set.
|
||||
* For example, for HSTS:
|
||||
* The URI is an HSTS URI if either the host has the HSTS state set, or one
|
||||
* of its super-domains has the HSTS "includeSubdomains" flag set.
|
||||
* NOTE: this function makes decisions based only on the
|
||||
* host contained in the URI, and disregards other portions of the URI
|
||||
* such as path and port.
|
||||
*
|
||||
* @param aType the type of security state in question.
|
||||
* @param aURI the URI to query for STS state.
|
||||
* @param aFlags options for this request as defined in nsISocketProvider:
|
||||
* NO_PERMANENT_STORAGE
|
||||
* @param aCached true if we have cached information regarding whether or not
|
||||
* the host is HSTS, false otherwise.
|
||||
*/
|
||||
boolean isSecureURI(in uint32_t aType, in nsIURI aURI, in uint32_t aFlags,
|
||||
[optional] out boolean aCached);
|
||||
|
||||
/**
|
||||
* Removes all non-preloaded security state by resetting to factory-original
|
||||
* settings.
|
||||
*/
|
||||
void clearAll();
|
||||
|
||||
/**
|
||||
* Removes all preloaded security state.
|
||||
*/
|
||||
void clearPreloads();
|
||||
|
||||
/**
|
||||
* Returns an array of sha256-hashed key pins for the given domain, if any.
|
||||
* If these pins also apply to subdomains of the given domain,
|
||||
* aIncludeSubdomains will be true. Pins returned are only for non-built-in
|
||||
* pin entries.
|
||||
*
|
||||
* @param aHostname the hosname (punycode) to be queried about
|
||||
* @param the time at which the pins should be valid. This is in
|
||||
mozilla::pkix::Time which uses internally seconds since 0 AD.
|
||||
* @param aPinArray the set of sha256-hashed key pins for the given domain
|
||||
* @param aIncludeSubdomains true if the pins apply to subdomains of the
|
||||
* given domain
|
||||
*/
|
||||
[noscript] boolean getKeyPinsForHostname(in string aHostname,
|
||||
in mozillaPkixTime evalTime,
|
||||
out nsCStringTArrayRef aPinArray,
|
||||
out boolean aIncludeSubdomains);
|
||||
|
||||
/**
|
||||
* Set public-key pins for a host. The resulting pins will be permanent
|
||||
* and visible from private and non-private contexts. These pins replace
|
||||
* any already set by this mechanism or those built-in to Gecko.
|
||||
*
|
||||
* @param aHost the hostname (punycode) that pins will apply to
|
||||
* @param aIncludeSubdomains whether these pins also apply to subdomains
|
||||
* @param aExpires the time this pin should expire (millis since epoch)
|
||||
* @param aPinCount number of keys being pinnned
|
||||
* @param aSha256Pins array of hashed key fingerprints (SHA-256, base64)
|
||||
* @param aIsPreload are these key pins for a preload entry? (false by
|
||||
* default)
|
||||
*/
|
||||
boolean setKeyPins(in string aHost, in boolean aIncludeSubdomains,
|
||||
in int64_t aExpires, in unsigned long aPinCount,
|
||||
[array, size_is(aPinCount)] in string aSha256Pins,
|
||||
[optional] in boolean aIsPreload);
|
||||
|
||||
/**
|
||||
* Mark a host as declining to provide a given security state so that features
|
||||
* such as HSTS priming will not flood a server with requests.
|
||||
*
|
||||
* @param aURI the nsIURI that this applies to
|
||||
* @param aMaxAge lifetime (in seconds) of this negative cache
|
||||
*/
|
||||
[noscript] void cacheNegativeHSTSResult(in nsIURI aURI, in unsigned long long aMaxAge);
|
||||
};
|
||||
|
||||
%{C++
|
||||
#define NS_SSSERVICE_CONTRACTID "@mozilla.org/ssservice;1"
|
||||
%}
|
||||
30
security/manager/ssl/nsITokenDialogs.idl
Normal file
30
security/manager/ssl/nsITokenDialogs.idl
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
interface nsIInterfaceRequestor;
|
||||
interface nsIProtectedAuthThread;
|
||||
|
||||
[scriptable, uuid(a1cbc159-468c-495d-8068-61dd538cbcca)]
|
||||
interface nsITokenDialogs : nsISupports
|
||||
{
|
||||
void ChooseToken(in nsIInterfaceRequestor ctx,
|
||||
[array, size_is(count)] in wstring tokenNameList,
|
||||
in unsigned long count,
|
||||
out wstring tokenName,
|
||||
out boolean canceled);
|
||||
|
||||
/**
|
||||
* displayProtectedAuth - displays notification dialog to the user
|
||||
* that he is expected to authenticate to the token using its
|
||||
* "protected authentication path" feature
|
||||
*/
|
||||
void displayProtectedAuth(in nsIInterfaceRequestor ctx,
|
||||
in nsIProtectedAuthThread runnable);
|
||||
};
|
||||
|
||||
%{C++
|
||||
#define NS_TOKENDIALOGS_CONTRACTID "@mozilla.org/nsTokenDialogs;1"
|
||||
%}
|
||||
29
security/manager/ssl/nsITokenPasswordDialogs.idl
Normal file
29
security/manager/ssl/nsITokenPasswordDialogs.idl
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
interface nsIInterfaceRequestor;
|
||||
|
||||
|
||||
/**
|
||||
* nsITokenPasswordDialogs
|
||||
* This is the interface for setting and changing password
|
||||
* on a PKCS11 token.
|
||||
*/
|
||||
[scriptable, uuid(87dbd64a-4466-474e-95f5-1ad1cee5702c)]
|
||||
interface nsITokenPasswordDialogs : nsISupports
|
||||
{
|
||||
/**
|
||||
* setPassword - sets the password/PIN on the named token.
|
||||
* The canceled output value should be set to TRUE when
|
||||
* the user (or implementation) cancels the operation.
|
||||
*/
|
||||
void setPassword(in nsIInterfaceRequestor ctx, in wstring tokenName,
|
||||
out boolean canceled);
|
||||
};
|
||||
|
||||
%{C++
|
||||
#define NS_TOKENPASSWORDSDIALOG_CONTRACTID "@mozilla.org/nsTokenPasswordDialogs;1"
|
||||
%}
|
||||
66
security/manager/ssl/nsIU2FToken.idl
Normal file
66
security/manager/ssl/nsIU2FToken.idl
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
interface nsIArray;
|
||||
|
||||
/**
|
||||
* Interface used to interact with U2F Token devices
|
||||
*/
|
||||
[scriptable, uuid(5778242f-1f42-47a2-b514-fa1adde2d904)]
|
||||
interface nsIU2FToken : nsISupports {
|
||||
/**
|
||||
* Is this token compatible with the provided version?
|
||||
*
|
||||
* @param version The offered version to test
|
||||
* @return True if the offered version is compatible
|
||||
*/
|
||||
void isCompatibleVersion(in AString version, [retval] out boolean result);
|
||||
|
||||
/**
|
||||
* Return whether the provided KeyHandle belongs to this Token
|
||||
*
|
||||
* @param keyHandle Key Handle to evaluate.
|
||||
* @return True if the Key Handle is ours.
|
||||
*/
|
||||
void isRegistered([array, size_is(keyHandleLen)] in octet keyHandle,
|
||||
in uint32_t keyHandleLen,
|
||||
[retval] out boolean result);
|
||||
|
||||
/**
|
||||
* Generates a public/private keypair for the provided application
|
||||
* and challenge, returning the pubkey, challenge response, and
|
||||
* key handle in the registration data.
|
||||
*
|
||||
* @param application The FIDO Application data to associate with the key.
|
||||
* @param challenge The Challenge to satisfy in the response.
|
||||
* @param registration An array containing the pubkey, challenge response,
|
||||
* and key handle.
|
||||
*/
|
||||
void register([array, size_is(applicationLen)] in octet application,
|
||||
in uint32_t applicationLen,
|
||||
[array, size_is(challengeLen)] in octet challenge,
|
||||
in uint32_t challengeLen,
|
||||
[array, size_is(registrationLen)] out octet registration,
|
||||
out uint32_t registrationLen);
|
||||
|
||||
/**
|
||||
* Creates a signature over the "param" arguments using the private key
|
||||
* provided in the key handle argument.
|
||||
*
|
||||
* @param application The FIDO Application data to associate with the key.
|
||||
* @param challenge The Challenge to satisfy in the response.
|
||||
* @param keyHandle The Key Handle opaque object to use.
|
||||
* @param signature The resulting signature.
|
||||
*/
|
||||
void sign([array, size_is(applicationLen)] in octet application,
|
||||
in uint32_t applicationLen,
|
||||
[array, size_is(challengeLen)] in octet challenge,
|
||||
in uint32_t challengeLen,
|
||||
[array, size_is(keyHandleLen)] in octet keyHandle,
|
||||
in uint32_t keyHandleLen,
|
||||
[array, size_is(signatureLen)] out octet signature,
|
||||
out uint32_t signatureLen);
|
||||
};
|
||||
45
security/manager/ssl/nsIWeakCryptoOverride.idl
Normal file
45
security/manager/ssl/nsIWeakCryptoOverride.idl
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
%{C++
|
||||
#define NS_WEAKCRYPTOOVERRIDE_CONTRACTID "@mozilla.org/security/weakcryptooverride;1"
|
||||
%}
|
||||
|
||||
/**
|
||||
* This represents the fallback whitelist for
|
||||
* weak crypto servers such as RC4-only.
|
||||
*/
|
||||
[scriptable, uuid(27b4d3df-8f15-4eb4-a35f-474e911b61e7)]
|
||||
interface nsIWeakCryptoOverride : nsISupports {
|
||||
/**
|
||||
* Add a weak crypto override for the given hostname.
|
||||
* Main thread only.
|
||||
*
|
||||
* @param aHostName The host (punycode) this mapping belongs to
|
||||
* @param aPrivate The override info will used for the private browsing
|
||||
* session and no information will be written to the disk.
|
||||
* @param aTemporary The override info will not persist between sessions.
|
||||
* Ignored if aPrivate is true.
|
||||
*/
|
||||
void addWeakCryptoOverride(in ACString aHostName,
|
||||
in boolean aPrivate,
|
||||
[optional] in boolean aTemporary);
|
||||
|
||||
/**
|
||||
* Remove a weak crypto override for the given hostname:port.
|
||||
* Main thread only.
|
||||
*
|
||||
* @param aHostName The host (punycode) whose entry should be cleared.
|
||||
* @param aPort The port whose entry should be cleared.
|
||||
* @param aPrivate The override info will used for the private browsing
|
||||
* session.
|
||||
*/
|
||||
void removeWeakCryptoOverride(in ACString aHostName,
|
||||
in int32_t aPort,
|
||||
in boolean aPrivate);
|
||||
};
|
||||
261
security/manager/ssl/nsIX509Cert.idl
Normal file
261
security/manager/ssl/nsIX509Cert.idl
Normal file
|
|
@ -0,0 +1,261 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
interface nsIArray;
|
||||
interface nsIX509CertValidity;
|
||||
interface nsIASN1Object;
|
||||
interface nsICertVerificationListener;
|
||||
|
||||
%{ C++
|
||||
/* forward declaration */
|
||||
typedef struct CERTCertificateStr CERTCertificate;
|
||||
%}
|
||||
[ptr] native CERTCertificatePtr(CERTCertificate);
|
||||
|
||||
/**
|
||||
* This represents a X.509 certificate.
|
||||
*
|
||||
* NOTE: Service workers persist x.509 certs in object form on disk. If you
|
||||
* change this uuid you probably need a hack in nsBinaryInputStream to
|
||||
* read the old uuid. If you change the format of the object
|
||||
* serialization then more complex changes will be needed.
|
||||
*/
|
||||
[scriptable, uuid(bdc3979a-5422-4cd5-8589-696b6e96ea83)]
|
||||
interface nsIX509Cert : nsISupports {
|
||||
|
||||
/**
|
||||
* A nickname for the certificate.
|
||||
*/
|
||||
readonly attribute AString nickname;
|
||||
|
||||
/**
|
||||
* The primary email address of the certificate, if present.
|
||||
*/
|
||||
readonly attribute AString emailAddress;
|
||||
|
||||
/**
|
||||
* Did this certificate ship with the platform as a built-in root?
|
||||
*/
|
||||
readonly attribute bool isBuiltInRoot;
|
||||
|
||||
/**
|
||||
* Obtain a list of all email addresses
|
||||
* contained in the certificate.
|
||||
*
|
||||
* @param length The number of strings in the returned array.
|
||||
* @return An array of email addresses.
|
||||
*/
|
||||
void getEmailAddresses(out unsigned long length,
|
||||
[retval, array, size_is(length)] out wstring addresses);
|
||||
|
||||
/**
|
||||
* Check whether a given address is contained in the certificate.
|
||||
* The comparison will convert the email address to lowercase.
|
||||
* The behaviour for non ASCII characters is undefined.
|
||||
*
|
||||
* @param aEmailAddress The address to search for.
|
||||
*
|
||||
* @return True if the address is contained in the certificate.
|
||||
*/
|
||||
boolean containsEmailAddress(in AString aEmailAddress);
|
||||
|
||||
/**
|
||||
* The subject owning the certificate.
|
||||
*/
|
||||
readonly attribute AString subjectName;
|
||||
|
||||
/**
|
||||
* The subject's common name.
|
||||
*/
|
||||
readonly attribute AString commonName;
|
||||
|
||||
/**
|
||||
* The subject's organization.
|
||||
*/
|
||||
readonly attribute AString organization;
|
||||
|
||||
/**
|
||||
* The subject's organizational unit.
|
||||
*/
|
||||
readonly attribute AString organizationalUnit;
|
||||
|
||||
/**
|
||||
* The fingerprint of the certificate's DER encoding,
|
||||
* calculated using the SHA-256 algorithm.
|
||||
*/
|
||||
readonly attribute AString sha256Fingerprint;
|
||||
|
||||
/**
|
||||
* The fingerprint of the certificate's DER encoding,
|
||||
* calculated using the SHA1 algorithm.
|
||||
*/
|
||||
readonly attribute AString sha1Fingerprint;
|
||||
|
||||
/**
|
||||
* A human readable name identifying the hardware or
|
||||
* software token the certificate is stored on.
|
||||
*/
|
||||
readonly attribute AString tokenName;
|
||||
|
||||
/**
|
||||
* The subject identifying the issuer certificate.
|
||||
*/
|
||||
readonly attribute AString issuerName;
|
||||
|
||||
/**
|
||||
* The serial number the issuer assigned to this certificate.
|
||||
*/
|
||||
readonly attribute AString serialNumber;
|
||||
|
||||
/**
|
||||
* The issuer subject's common name.
|
||||
*/
|
||||
readonly attribute AString issuerCommonName;
|
||||
|
||||
/**
|
||||
* The issuer subject's organization.
|
||||
*/
|
||||
readonly attribute AString issuerOrganization;
|
||||
|
||||
/**
|
||||
* The issuer subject's organizational unit.
|
||||
*/
|
||||
readonly attribute AString issuerOrganizationUnit;
|
||||
|
||||
/**
|
||||
* The certificate used by the issuer to sign this certificate.
|
||||
*/
|
||||
readonly attribute nsIX509Cert issuer;
|
||||
|
||||
/**
|
||||
* This certificate's validity period.
|
||||
*/
|
||||
readonly attribute nsIX509CertValidity validity;
|
||||
|
||||
/**
|
||||
* A unique identifier of this certificate within the local storage.
|
||||
*/
|
||||
readonly attribute ACString dbKey;
|
||||
|
||||
/**
|
||||
* A human readable identifier to label this certificate.
|
||||
*/
|
||||
readonly attribute AString windowTitle;
|
||||
|
||||
/**
|
||||
* Constants to classify the type of a certificate.
|
||||
*/
|
||||
const unsigned long UNKNOWN_CERT = 0;
|
||||
const unsigned long CA_CERT = 1 << 0;
|
||||
const unsigned long USER_CERT = 1 << 1;
|
||||
const unsigned long EMAIL_CERT = 1 << 2;
|
||||
const unsigned long SERVER_CERT = 1 << 3;
|
||||
const unsigned long ANY_CERT = 0xffff;
|
||||
|
||||
/**
|
||||
* Type of this certificate
|
||||
*/
|
||||
readonly attribute unsigned long certType;
|
||||
|
||||
/**
|
||||
* True if the certificate is self-signed. CA issued
|
||||
* certificates are always self-signed.
|
||||
*/
|
||||
readonly attribute boolean isSelfSigned;
|
||||
|
||||
/**
|
||||
* Constants for specifying the chain mode when exporting a certificate
|
||||
*/
|
||||
const unsigned long CMS_CHAIN_MODE_CertOnly = 1;
|
||||
const unsigned long CMS_CHAIN_MODE_CertChain = 2;
|
||||
const unsigned long CMS_CHAIN_MODE_CertChainWithRoot = 3;
|
||||
|
||||
/**
|
||||
* Obtain a list of certificates that contains this certificate
|
||||
* and the issuing certificates of all involved issuers,
|
||||
* up to the root issuer.
|
||||
*
|
||||
* @return The chain of certifficates including the issuers.
|
||||
*/
|
||||
nsIArray getChain();
|
||||
|
||||
/**
|
||||
* A comma separated list of localized strings representing the contents of
|
||||
* the certificate's key usage extension, if present. The empty string if the
|
||||
* certificate doesn't have the key usage extension, or has an empty extension.
|
||||
*/
|
||||
readonly attribute AString keyUsages;
|
||||
|
||||
/**
|
||||
* This is the attribute which describes the ASN1 layout
|
||||
* of the certificate. This can be used when doing a
|
||||
* "pretty print" of the certificate's ASN1 structure.
|
||||
*/
|
||||
readonly attribute nsIASN1Object ASN1Structure;
|
||||
|
||||
/**
|
||||
* Obtain a raw binary encoding of this certificate
|
||||
* in DER format.
|
||||
*
|
||||
* @param length The number of bytes in the binary encoding.
|
||||
* @param data The bytes representing the DER encoded certificate.
|
||||
*/
|
||||
void getRawDER(out unsigned long length,
|
||||
[retval, array, size_is(length)] out octet data);
|
||||
|
||||
/**
|
||||
* Test whether two certificate instances represent the
|
||||
* same certificate.
|
||||
*
|
||||
* @return Whether the certificates are equal
|
||||
*/
|
||||
boolean equals(in nsIX509Cert other);
|
||||
|
||||
/**
|
||||
* The base64 encoding of the DER encoded public key info using the specified
|
||||
* digest.
|
||||
*/
|
||||
readonly attribute ACString sha256SubjectPublicKeyInfoDigest;
|
||||
|
||||
/**
|
||||
* Obtain the certificate wrapped in a PKCS#7 SignedData structure,
|
||||
* with or without the certificate chain
|
||||
*
|
||||
* @param chainMode Whether to include the chain (with or without the root),
|
||||
see CMS_CHAIN_MODE constants.
|
||||
* @param length The number of bytes of the PKCS#7 data.
|
||||
* @param data The bytes representing the PKCS#7 wrapped certificate.
|
||||
*/
|
||||
void exportAsCMS(in unsigned long chainMode,
|
||||
out unsigned long length,
|
||||
[retval, array, size_is(length)] out octet data);
|
||||
|
||||
/**
|
||||
* Retrieves the NSS certificate object wrapped by this interface
|
||||
*/
|
||||
[notxpcom, noscript] CERTCertificatePtr getCert();
|
||||
|
||||
/**
|
||||
* Human readable names identifying all hardware or
|
||||
* software tokens the certificate is stored on.
|
||||
*
|
||||
* @param length On success, the number of entries in the returned array.
|
||||
* @return On success, an array containing the names of all tokens
|
||||
* the certificate is stored on (may be empty).
|
||||
* On failure the function throws/returns an error.
|
||||
*/
|
||||
void getAllTokenNames(out unsigned long length,
|
||||
[retval, array, size_is(length)] out wstring
|
||||
tokenNames);
|
||||
|
||||
/**
|
||||
* Either delete the certificate from all cert databases,
|
||||
* or mark it as untrusted.
|
||||
*/
|
||||
void markForPermDeletion();
|
||||
};
|
||||
431
security/manager/ssl/nsIX509CertDB.idl
Normal file
431
security/manager/ssl/nsIX509CertDB.idl
Normal file
|
|
@ -0,0 +1,431 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
interface nsIArray;
|
||||
interface nsIX509Cert;
|
||||
interface nsIFile;
|
||||
interface nsIInterfaceRequestor;
|
||||
interface nsIZipReader;
|
||||
interface nsIX509CertList;
|
||||
interface nsIInputStream;
|
||||
|
||||
%{C++
|
||||
#define NS_X509CERTDB_CONTRACTID "@mozilla.org/security/x509certdb;1"
|
||||
%}
|
||||
|
||||
typedef uint32_t AppTrustedRoot;
|
||||
|
||||
[scriptable, function, uuid(fc2b60e5-9a07-47c2-a2cd-b83b68a660ac)]
|
||||
interface nsIOpenSignedAppFileCallback : nsISupports
|
||||
{
|
||||
void openSignedAppFileFinished(in nsresult rv,
|
||||
in nsIZipReader aZipReader,
|
||||
in nsIX509Cert aSignerCert);
|
||||
};
|
||||
|
||||
[scriptable, function, uuid(d5f97827-622a-488f-be08-d850432ac8ec)]
|
||||
interface nsIVerifySignedDirectoryCallback : nsISupports
|
||||
{
|
||||
void verifySignedDirectoryFinished(in nsresult rv,
|
||||
in nsIX509Cert aSignerCert);
|
||||
};
|
||||
|
||||
[scriptable, function, uuid(3d6a9c87-5c5f-46fc-9410-96da6092f0f2)]
|
||||
interface nsIVerifySignedManifestCallback : nsISupports
|
||||
{
|
||||
void verifySignedManifestFinished(in nsresult rv,
|
||||
in nsIX509Cert aSignerCert);
|
||||
};
|
||||
|
||||
/**
|
||||
* Callback type for use with asyncVerifyCertAtTime.
|
||||
* If aPRErrorCode is PRErrorCodeSuccess (i.e. 0), aVerifiedChain represents the
|
||||
* verified certificate chain determined by asyncVerifyCertAtTime. aHasEVPolicy
|
||||
* represents whether or not the end-entity certificate verified as EV.
|
||||
* If aPRErrorCode is non-zero, it represents the error encountered during
|
||||
* verification. aVerifiedChain is null in that case and aHasEVPolicy has no
|
||||
* meaning.
|
||||
*/
|
||||
[scriptable, function, uuid(49e16fc8-efac-4f57-8361-956ef6b960a4)]
|
||||
interface nsICertVerificationCallback : nsISupports {
|
||||
void verifyCertFinished(in int32_t aPRErrorCode,
|
||||
in nsIX509CertList aVerifiedChain,
|
||||
in bool aHasEVPolicy);
|
||||
};
|
||||
|
||||
/**
|
||||
* This represents a service to access and manipulate
|
||||
* X.509 certificates stored in a database.
|
||||
*/
|
||||
[scriptable, uuid(5c16cd9b-5a73-47f1-ab0f-11ede7495cce)]
|
||||
interface nsIX509CertDB : nsISupports {
|
||||
|
||||
/**
|
||||
* Constants that define which usages a certificate
|
||||
* is trusted for.
|
||||
*/
|
||||
const unsigned long UNTRUSTED = 0;
|
||||
const unsigned long TRUSTED_SSL = 1 << 0;
|
||||
const unsigned long TRUSTED_EMAIL = 1 << 1;
|
||||
const unsigned long TRUSTED_OBJSIGN = 1 << 2;
|
||||
|
||||
/**
|
||||
* Given a nickname,
|
||||
* locate the matching certificate.
|
||||
*
|
||||
* @param aNickname The nickname to be used as the key
|
||||
* to find a certificate.
|
||||
*
|
||||
* @return The matching certificate if found.
|
||||
*/
|
||||
nsIX509Cert findCertByNickname(in AString aNickname);
|
||||
|
||||
/**
|
||||
* Will find a certificate based on its dbkey
|
||||
* retrieved by getting the dbKey attribute of
|
||||
* the certificate.
|
||||
*
|
||||
* @param aDBkey Database internal key, as obtained using
|
||||
* attribute dbkey in nsIX509Cert.
|
||||
*/
|
||||
nsIX509Cert findCertByDBKey(in string aDBkey);
|
||||
|
||||
/**
|
||||
* Find user's own email encryption certificate by nickname.
|
||||
*
|
||||
* @param aNickname The nickname to be used as the key
|
||||
* to find the certificate.
|
||||
*
|
||||
* @return The matching certificate if found.
|
||||
*/
|
||||
nsIX509Cert findEmailEncryptionCert(in AString aNickname);
|
||||
|
||||
/**
|
||||
* Find user's own email signing certificate by nickname.
|
||||
*
|
||||
* @param aNickname The nickname to be used as the key
|
||||
* to find the certificate.
|
||||
*
|
||||
* @return The matching certificate if found.
|
||||
*/
|
||||
nsIX509Cert findEmailSigningCert(in AString aNickname);
|
||||
|
||||
/**
|
||||
* Find a certificate by email address.
|
||||
*
|
||||
* @param aEmailAddress The email address to be used as the key
|
||||
* to find the certificate.
|
||||
*
|
||||
* @return The matching certificate if found.
|
||||
*/
|
||||
nsIX509Cert findCertByEmailAddress(in string aEmailAddress);
|
||||
|
||||
/**
|
||||
* Use this to import a stream sent down as a mime type into
|
||||
* the certificate database on the default token.
|
||||
* The stream may consist of one or more certificates.
|
||||
*
|
||||
* @param data The raw data to be imported
|
||||
* @param length The length of the data to be imported
|
||||
* @param type The type of the certificate, see constants in nsIX509Cert
|
||||
* @param ctx A UI context.
|
||||
*/
|
||||
void importCertificates([array, size_is(length)] in octet data,
|
||||
in unsigned long length,
|
||||
in unsigned long type,
|
||||
in nsIInterfaceRequestor ctx);
|
||||
|
||||
/**
|
||||
* Import another person's email certificate into the database.
|
||||
*
|
||||
* @param data The raw data to be imported
|
||||
* @param length The length of the data to be imported
|
||||
* @param ctx A UI context.
|
||||
*/
|
||||
void importEmailCertificate([array, size_is(length)] in octet data,
|
||||
in unsigned long length,
|
||||
in nsIInterfaceRequestor ctx);
|
||||
|
||||
/**
|
||||
* Import a personal certificate into the database, assuming
|
||||
* the database already contains the private key for this certificate.
|
||||
*
|
||||
* @param data The raw data to be imported
|
||||
* @param length The length of the data to be imported
|
||||
* @param ctx A UI context.
|
||||
*/
|
||||
void importUserCertificate([array, size_is(length)] in octet data,
|
||||
in unsigned long length,
|
||||
in nsIInterfaceRequestor ctx);
|
||||
|
||||
/**
|
||||
* Delete a certificate stored in the database.
|
||||
*
|
||||
* @param aCert Delete this certificate.
|
||||
*/
|
||||
void deleteCertificate(in nsIX509Cert aCert);
|
||||
|
||||
/**
|
||||
* Modify the trust that is stored and associated to a certificate within
|
||||
* a database. Separate trust is stored for
|
||||
* One call manipulates the trust for one trust type only.
|
||||
* See the trust type constants defined within this interface.
|
||||
*
|
||||
* @param cert Change the stored trust of this certificate.
|
||||
* @param type The type of the certificate. See nsIX509Cert.
|
||||
* @param trust A bitmask. The new trust for the possible usages.
|
||||
* See the trust constants defined within this interface.
|
||||
*/
|
||||
void setCertTrust(in nsIX509Cert cert,
|
||||
in unsigned long type,
|
||||
in unsigned long trust);
|
||||
|
||||
/**
|
||||
* @param cert The certificate for which to modify trust.
|
||||
* @param trustString decoded by CERT_DecodeTrustString. 3 comma separated
|
||||
* characters, indicating SSL, Email, and Obj signing
|
||||
* trust.
|
||||
*/
|
||||
void setCertTrustFromString(in nsIX509Cert cert, in ACString trustString);
|
||||
|
||||
/**
|
||||
* Query whether a certificate is trusted for a particular use.
|
||||
*
|
||||
* @param cert Obtain the stored trust of this certificate.
|
||||
* @param certType The type of the certificate. See nsIX509Cert.
|
||||
* @param trustType A single bit from the usages constants defined
|
||||
* within this interface.
|
||||
*
|
||||
* @return Returns true if the certificate is trusted for the given use.
|
||||
*/
|
||||
boolean isCertTrusted(in nsIX509Cert cert,
|
||||
in unsigned long certType,
|
||||
in unsigned long trustType);
|
||||
|
||||
/**
|
||||
* Import certificate(s) from file
|
||||
*
|
||||
* @param aFile Identifies a file that contains the certificate
|
||||
* to be imported.
|
||||
* @param aType Describes the type of certificate that is going to
|
||||
* be imported. See type constants in nsIX509Cert.
|
||||
*/
|
||||
void importCertsFromFile(in nsIFile aFile,
|
||||
in unsigned long aType);
|
||||
|
||||
/**
|
||||
* Import a PKCS#12 file containing cert(s) and key(s) into the database.
|
||||
*
|
||||
* @param aToken Optionally limits the scope of
|
||||
* this function to a token device.
|
||||
* Can be null to mean any token.
|
||||
* @param aFile Identifies a file that contains the data
|
||||
* to be imported.
|
||||
*/
|
||||
void importPKCS12File(in nsISupports aToken,
|
||||
in nsIFile aFile);
|
||||
|
||||
/**
|
||||
* Export a set of certs and keys from the database to a PKCS#12 file.
|
||||
*
|
||||
* @param aToken Optionally limits the scope of
|
||||
* this function to a token device.
|
||||
* Can be null to mean any token.
|
||||
* @param aFile Identifies a file that will be filled with the data
|
||||
* to be exported.
|
||||
* @param count The number of certificates to be exported.
|
||||
* @param aCerts The array of all certificates to be exported.
|
||||
*/
|
||||
void exportPKCS12File(in nsISupports aToken,
|
||||
in nsIFile aFile,
|
||||
in unsigned long count,
|
||||
[array, size_is(count)] in nsIX509Cert aCerts);
|
||||
|
||||
/*
|
||||
* Decode a raw data presentation and instantiate an object in memory.
|
||||
*
|
||||
* @param base64 The raw representation of a certificate,
|
||||
* encoded as Base 64.
|
||||
* @return The new certificate object.
|
||||
*/
|
||||
nsIX509Cert constructX509FromBase64(in ACString base64);
|
||||
|
||||
/*
|
||||
* Decode a raw data presentation and instantiate an object in memory.
|
||||
*
|
||||
* @param certDER The raw representation of a certificate,
|
||||
* encoded as raw DER.
|
||||
* @param length The length of the DER string.
|
||||
* @return The new certificate object.
|
||||
*/
|
||||
nsIX509Cert constructX509(in string certDER, in unsigned long length);
|
||||
|
||||
/**
|
||||
* Verifies the signature on the given JAR file to verify that it has a
|
||||
* valid signature. To be considered valid, there must be exactly one
|
||||
* signature on the JAR file and that signature must have signed every
|
||||
* entry. Further, the signature must come from a certificate that
|
||||
* is trusted for code signing.
|
||||
*
|
||||
* On success, NS_OK, a nsIZipReader, and the trusted certificate that
|
||||
* signed the JAR are returned.
|
||||
*
|
||||
* On failure, an error code is returned.
|
||||
*
|
||||
* This method returns a nsIZipReader, instead of taking an nsIZipReader
|
||||
* as input, to encourage users of the API to verify the signature as the
|
||||
* first step in opening the JAR.
|
||||
*/
|
||||
const AppTrustedRoot AppMarketplaceProdPublicRoot = 1;
|
||||
const AppTrustedRoot AppMarketplaceProdReviewersRoot = 2;
|
||||
const AppTrustedRoot AppMarketplaceDevPublicRoot = 3;
|
||||
const AppTrustedRoot AppMarketplaceDevReviewersRoot = 4;
|
||||
const AppTrustedRoot AppMarketplaceStageRoot = 5;
|
||||
const AppTrustedRoot AppXPCShellRoot = 6;
|
||||
const AppTrustedRoot AddonsPublicRoot = 7;
|
||||
const AppTrustedRoot AddonsStageRoot = 8;
|
||||
const AppTrustedRoot PrivilegedPackageRoot = 9;
|
||||
/*
|
||||
* If DeveloperImportedRoot is set as trusted root, a CA from local file
|
||||
* system will be imported. Only used when preference
|
||||
* "network.http.packaged-apps-developer-mode" is set.
|
||||
* The path of the CA is specified by preference
|
||||
* "network.http.packaged-apps-developer-trusted-root".
|
||||
*/
|
||||
const AppTrustedRoot DeveloperImportedRoot = 10;
|
||||
void openSignedAppFileAsync(in AppTrustedRoot trustedRoot,
|
||||
in nsIFile aJarFile,
|
||||
in nsIOpenSignedAppFileCallback callback);
|
||||
|
||||
/**
|
||||
* Verifies the signature on a directory representing an unpacked signed
|
||||
* JAR file. To be considered valid, there must be exactly one signature
|
||||
* on the directory structure and that signature must have signed every
|
||||
* entry. Further, the signature must come from a certificate that
|
||||
* is trusted for code signing.
|
||||
*
|
||||
* On success NS_OK and the trusted certificate that signed the
|
||||
* unpacked JAR are returned.
|
||||
*
|
||||
* On failure, an error code is returned.
|
||||
*/
|
||||
void verifySignedDirectoryAsync(in AppTrustedRoot trustedRoot,
|
||||
in nsIFile aUnpackedDir,
|
||||
in nsIVerifySignedDirectoryCallback callback);
|
||||
|
||||
/**
|
||||
* Given streams containing a signature and a manifest file, verifies
|
||||
* that the signature is valid for the manifest. The signature must
|
||||
* come from a certificate that is trusted for code signing and that
|
||||
* was issued by the given trusted root.
|
||||
*
|
||||
* On success, NS_OK and the trusted certificate that signed the
|
||||
* Manifest are returned.
|
||||
*
|
||||
* On failure, an error code is returned.
|
||||
*/
|
||||
void verifySignedManifestAsync(in AppTrustedRoot trustedRoot,
|
||||
in nsIInputStream aManifestStream,
|
||||
in nsIInputStream aSignatureStream,
|
||||
in nsIVerifySignedManifestCallback callback);
|
||||
|
||||
/*
|
||||
* Add a cert to a cert DB from a binary string.
|
||||
*
|
||||
* @param certDER The raw DER encoding of a certificate.
|
||||
* @param aTrust decoded by CERT_DecodeTrustString. 3 comma separated characters,
|
||||
* indicating SSL, Email, and Obj signing trust
|
||||
* @param aName name of the cert for display purposes.
|
||||
* TODO(bug 857627): aName is currently ignored. It should either
|
||||
* not be ignored, or be removed.
|
||||
*/
|
||||
void addCert(in ACString certDER, in ACString aTrust, in AUTF8String aName);
|
||||
|
||||
// Flags for verifyCertNow (these must match the values in CertVerifier.cpp):
|
||||
// Prevent network traffic. Doesn't work with classic verification.
|
||||
const uint32_t FLAG_LOCAL_ONLY = 1 << 0;
|
||||
// Do not fall back to DV verification after attempting EV validation.
|
||||
// Actually does prevent network traffic, but can cause a valid EV
|
||||
// certificate to not be considered valid.
|
||||
const uint32_t FLAG_MUST_BE_EV = 1 << 1;
|
||||
|
||||
/** Warning: This interface is inteded to use only for testing only as:
|
||||
* 1. It can create IO on the main thread.
|
||||
* 2. It is in constant change, so in/out can change at any release.
|
||||
*
|
||||
* Obtain the verification result for a cert given a particular usage.
|
||||
* On success, the call returns 0, the chain built during verification,
|
||||
* and whether the cert is good for EV usage.
|
||||
* On failure, the call returns the PRErrorCode for the verification failure
|
||||
*
|
||||
* @param aCert Obtain the stored trust of this certificate
|
||||
* @param aUsage a integer representing the usage from NSS
|
||||
* @param aFlags flags as described above
|
||||
* @param aHostname the (optional) hostname to verify for
|
||||
* @param aTime the time at which to verify, in seconds since the epoch
|
||||
* @param aVerifiedChain chain of verification up to the root if success
|
||||
* @param aHasEVPolicy bool that signified that the cert was an EV cert
|
||||
* @return 0 if success or the value or the error code for the verification
|
||||
* failure
|
||||
*/
|
||||
int32_t /*PRErrorCode*/
|
||||
verifyCertAtTime(in nsIX509Cert aCert,
|
||||
in int64_t /*SECCertificateUsage*/ aUsage,
|
||||
in uint32_t aFlags,
|
||||
in string aHostname,
|
||||
in uint64_t aTime,
|
||||
out nsIX509CertList aVerifiedChain,
|
||||
out bool aHasEVPolicy);
|
||||
int32_t /*PRErrorCode*/
|
||||
verifyCertNow(in nsIX509Cert aCert,
|
||||
in int64_t /*SECCertificateUsage*/ aUsage,
|
||||
in uint32_t aFlags,
|
||||
in string aHostname,
|
||||
out nsIX509CertList aVerifiedChain,
|
||||
out bool aHasEVPolicy);
|
||||
|
||||
/**
|
||||
* Similar to the above, but asynchronous. As a result, use of this API is not
|
||||
* limited to tests.
|
||||
*/
|
||||
void asyncVerifyCertAtTime(in nsIX509Cert aCert,
|
||||
in int64_t /*SECCertificateUsage*/ aUsage,
|
||||
in uint32_t aFlags,
|
||||
in string aHostname,
|
||||
in uint64_t aTime,
|
||||
in nsICertVerificationCallback aCallback);
|
||||
|
||||
// Clears the OCSP cache for the current certificate verification
|
||||
// implementation.
|
||||
void clearOCSPCache();
|
||||
|
||||
/*
|
||||
* Add a cert to a cert DB from a base64 encoded string.
|
||||
*
|
||||
* @param base64 The raw representation of a certificate,
|
||||
* encoded as Base 64.
|
||||
* @param aTrust decoded by CERT_DecodeTrustString. 3 comma separated characters,
|
||||
* indicating SSL, Email, and Obj signing trust
|
||||
* @param aName name of the cert for display purposes.
|
||||
* TODO(bug 857627): aName is currently ignored. It should either
|
||||
* not be ignored, or be removed.
|
||||
*/
|
||||
void addCertFromBase64(in ACString base64, in ACString aTrust,
|
||||
in AUTF8String aName);
|
||||
|
||||
/*
|
||||
* Get all the known certs in the database
|
||||
*/
|
||||
nsIX509CertList getCerts();
|
||||
|
||||
/*
|
||||
* Get a list of imported enterprise root certificates (currently only
|
||||
* implemented on Windows).
|
||||
*/
|
||||
nsIX509CertList getEnterpriseRoots();
|
||||
};
|
||||
49
security/manager/ssl/nsIX509CertList.idl
Normal file
49
security/manager/ssl/nsIX509CertList.idl
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
interface nsISimpleEnumerator;
|
||||
interface nsIX509Cert;
|
||||
|
||||
%{C++
|
||||
typedef struct CERTCertListStr CERTCertList;
|
||||
%}
|
||||
[ptr] native CERTCertListPtr(CERTCertList);
|
||||
|
||||
[scriptable, uuid(ae74cda5-cd2f-473f-96f5-f0b7fff62c68)]
|
||||
interface nsIX509CertList : nsISupports {
|
||||
void addCert(in nsIX509Cert cert);
|
||||
void deleteCert(in nsIX509Cert cert);
|
||||
nsISimpleEnumerator getEnumerator();
|
||||
|
||||
/**
|
||||
* Returns the raw, backing cert list.
|
||||
* Must be called only from functions where an nsNSSShutDownPreventionLock
|
||||
* has been acquired.
|
||||
*/
|
||||
[notxpcom, noscript] CERTCertListPtr getRawCertList();
|
||||
|
||||
/**
|
||||
* Test whether two certificate list instances represent the same
|
||||
* certificate list.
|
||||
*
|
||||
* @return Whether the certificate lists are equal
|
||||
*/
|
||||
boolean equals(in nsIX509CertList other);
|
||||
|
||||
};
|
||||
|
||||
%{C++
|
||||
|
||||
#define NS_X509CERTLIST_CID { /* 959fb165-6517-487f-ab9b-d8913be53197 */ \
|
||||
0x959fb165, \
|
||||
0x6517, \
|
||||
0x487f, \
|
||||
{0xab, 0x9b, 0xd8, 0x91, 0x3b, 0xe5, 0x31, 0x97} \
|
||||
}
|
||||
|
||||
#define NS_X509CERTLIST_CONTRACTID "@mozilla.org/security/x509certlist;1"
|
||||
|
||||
%}
|
||||
70
security/manager/ssl/nsIX509CertValidity.idl
Normal file
70
security/manager/ssl/nsIX509CertValidity.idl
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
/**
|
||||
* Information on the validity period of a X.509 certificate.
|
||||
*/
|
||||
[scriptable, uuid(e701dfd8-1dd1-11b2-a172-ffa6cc6156ad)]
|
||||
interface nsIX509CertValidity : nsISupports {
|
||||
|
||||
/**
|
||||
* The earliest point in time where
|
||||
* a certificate is valid.
|
||||
*/
|
||||
readonly attribute PRTime notBefore;
|
||||
|
||||
/**
|
||||
* "notBefore" attribute formatted as a time string
|
||||
* according to the environment locale,
|
||||
* according to the environment time zone.
|
||||
*/
|
||||
readonly attribute AString notBeforeLocalTime;
|
||||
|
||||
/**
|
||||
* The day portion of "notBefore"
|
||||
* formatted as a time string
|
||||
* according to the environment locale,
|
||||
* according to the environment time zone.
|
||||
*/
|
||||
readonly attribute AString notBeforeLocalDay;
|
||||
|
||||
/**
|
||||
* "notBefore" attribute formatted as a string
|
||||
* according to the environment locale,
|
||||
* displayed as GMT / UTC.
|
||||
*/
|
||||
readonly attribute AString notBeforeGMT;
|
||||
|
||||
/**
|
||||
* The latest point in time where
|
||||
* a certificate is valid.
|
||||
*/
|
||||
readonly attribute PRTime notAfter;
|
||||
|
||||
/**
|
||||
* "notAfter" attribute formatted as a time string
|
||||
* according to the environment locale,
|
||||
* according to the environment time zone.
|
||||
*/
|
||||
readonly attribute AString notAfterLocalTime;
|
||||
|
||||
/**
|
||||
* The day portion of "notAfter"
|
||||
* formatted as a time string
|
||||
* according to the environment locale,
|
||||
* according to the environment time zone.
|
||||
*/
|
||||
readonly attribute AString notAfterLocalDay;
|
||||
|
||||
/**
|
||||
* "notAfter" attribute formatted as a time string
|
||||
* according to the environment locale,
|
||||
* displayed as GMT / UTC.
|
||||
*/
|
||||
readonly attribute AString notAfterGMT;
|
||||
};
|
||||
159
security/manager/ssl/nsKeyModule.cpp
Normal file
159
security/manager/ssl/nsKeyModule.cpp
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsComponentManagerUtils.h"
|
||||
#include "nsKeyModule.h"
|
||||
#include "nsString.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::psm;
|
||||
|
||||
NS_IMPL_ISUPPORTS(nsKeyObject, nsIKeyObject)
|
||||
|
||||
nsKeyObject::nsKeyObject()
|
||||
: mSymKey(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
nsKeyObject::~nsKeyObject()
|
||||
{
|
||||
nsNSSShutDownPreventionLock locker;
|
||||
if (isAlreadyShutDown()) {
|
||||
return;
|
||||
}
|
||||
destructorSafeDestroyNSSReference();
|
||||
shutdown(ShutdownCalledFrom::Object);
|
||||
}
|
||||
|
||||
void
|
||||
nsKeyObject::virtualDestroyNSSReference()
|
||||
{
|
||||
destructorSafeDestroyNSSReference();
|
||||
}
|
||||
|
||||
void
|
||||
nsKeyObject::destructorSafeDestroyNSSReference()
|
||||
{
|
||||
mSymKey = nullptr;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// nsIKeyObject
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsKeyObject::InitKey(int16_t aAlgorithm, PK11SymKey* aKey)
|
||||
{
|
||||
if (!aKey || aAlgorithm != nsIKeyObject::HMAC) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
nsNSSShutDownPreventionLock locker;
|
||||
if (isAlreadyShutDown()) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
mSymKey.reset(aKey);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsKeyObject::GetKeyObj(PK11SymKey** _retval)
|
||||
{
|
||||
if (!_retval) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
*_retval = nullptr;
|
||||
|
||||
nsNSSShutDownPreventionLock locker;
|
||||
if (isAlreadyShutDown()) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
if (!mSymKey) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
*_retval = mSymKey.get();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsKeyObject::GetType(int16_t *_retval)
|
||||
{
|
||||
if (!_retval) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
*_retval = nsIKeyObject::SYM_KEY;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// nsIKeyObjectFactory
|
||||
|
||||
NS_IMPL_ISUPPORTS(nsKeyObjectFactory, nsIKeyObjectFactory)
|
||||
|
||||
nsKeyObjectFactory::nsKeyObjectFactory()
|
||||
{
|
||||
}
|
||||
|
||||
nsKeyObjectFactory::~nsKeyObjectFactory()
|
||||
{
|
||||
nsNSSShutDownPreventionLock locker;
|
||||
if (isAlreadyShutDown()) {
|
||||
return;
|
||||
}
|
||||
shutdown(ShutdownCalledFrom::Object);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsKeyObjectFactory::KeyFromString(int16_t aAlgorithm, const nsACString& aKey,
|
||||
nsIKeyObject** _retval)
|
||||
{
|
||||
if (!_retval || aAlgorithm != nsIKeyObject::HMAC) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
nsNSSShutDownPreventionLock locker;
|
||||
if (isAlreadyShutDown()) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
CK_MECHANISM_TYPE cipherMech = CKM_GENERIC_SECRET_KEY_GEN;
|
||||
CK_ATTRIBUTE_TYPE cipherOperation = CKA_SIGN;
|
||||
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIKeyObject> key(
|
||||
do_CreateInstance(NS_KEYMODULEOBJECT_CONTRACTID, &rv));
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Convert the raw string into a SECItem
|
||||
const nsCString& flatKey = PromiseFlatCString(aKey);
|
||||
SECItem keyItem;
|
||||
keyItem.data = (unsigned char*)flatKey.get();
|
||||
keyItem.len = flatKey.Length();
|
||||
|
||||
UniquePK11SlotInfo slot(PK11_GetBestSlot(cipherMech, nullptr));
|
||||
if (!slot) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
UniquePK11SymKey symKey(PK11_ImportSymKey(slot.get(), cipherMech,
|
||||
PK11_OriginUnwrap, cipherOperation,
|
||||
&keyItem, nullptr));
|
||||
if (!symKey) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
rv = key->InitKey(aAlgorithm, symKey.release());
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
key.swap(*_retval);
|
||||
return NS_OK;
|
||||
}
|
||||
63
security/manager/ssl/nsKeyModule.h
Normal file
63
security/manager/ssl/nsKeyModule.h
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef nsKeyModule_h
|
||||
#define nsKeyModule_h
|
||||
|
||||
#include "ScopedNSSTypes.h"
|
||||
#include "nsIKeyModule.h"
|
||||
#include "nsNSSShutDown.h"
|
||||
#include "pk11pub.h"
|
||||
|
||||
#define NS_KEYMODULEOBJECT_CID \
|
||||
{ 0x9d383ddd, 0x6856, 0x4187, {0x84, 0x85, 0xf3, 0x61, 0x95, 0xb2, 0x9a, 0x0e} }
|
||||
#define NS_KEYMODULEOBJECT_CONTRACTID "@mozilla.org/security/keyobject;1"
|
||||
|
||||
#define NS_KEYMODULEOBJECTFACTORY_CID \
|
||||
{ 0x2a35dd47, 0xb026, 0x4e8d, {0xb6, 0xb7, 0x57, 0x40, 0xf6, 0x1a, 0xb9, 0x02} }
|
||||
#define NS_KEYMODULEOBJECTFACTORY_CONTRACTID \
|
||||
"@mozilla.org/security/keyobjectfactory;1"
|
||||
|
||||
class nsKeyObject final : public nsIKeyObject
|
||||
, public nsNSSShutDownObject
|
||||
{
|
||||
public:
|
||||
nsKeyObject();
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIKEYOBJECT
|
||||
|
||||
private:
|
||||
~nsKeyObject();
|
||||
|
||||
// Disallow copy constructor
|
||||
nsKeyObject(nsKeyObject&);
|
||||
|
||||
UniquePK11SymKey mSymKey;
|
||||
|
||||
virtual void virtualDestroyNSSReference() override;
|
||||
void destructorSafeDestroyNSSReference();
|
||||
};
|
||||
|
||||
|
||||
class nsKeyObjectFactory final : public nsIKeyObjectFactory
|
||||
, public nsNSSShutDownObject
|
||||
{
|
||||
public:
|
||||
nsKeyObjectFactory();
|
||||
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIKEYOBJECTFACTORY
|
||||
|
||||
private:
|
||||
~nsKeyObjectFactory();
|
||||
|
||||
// Disallow copy constructor
|
||||
nsKeyObjectFactory(nsKeyObjectFactory&);
|
||||
|
||||
// No NSS resources to release.
|
||||
virtual void virtualDestroyNSSReference() override {}
|
||||
};
|
||||
|
||||
#endif // nsKeyModule_h
|
||||
785
security/manager/ssl/nsKeygenHandler.cpp
Normal file
785
security/manager/ssl/nsKeygenHandler.cpp
Normal file
|
|
@ -0,0 +1,785 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "base64.h"
|
||||
#include "cryptohi.h"
|
||||
#include "keyhi.h"
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
#include "nsIContent.h"
|
||||
#include "nsIDOMHTMLSelectElement.h"
|
||||
#include "nsIGenKeypairInfoDlg.h"
|
||||
#include "nsIServiceManager.h"
|
||||
#include "nsITokenDialogs.h"
|
||||
#include "nsKeygenHandler.h"
|
||||
#include "nsKeygenHandlerContent.h"
|
||||
#include "nsKeygenThread.h"
|
||||
#include "nsNSSComponent.h" // for PIPNSS string bundle calls.
|
||||
#include "nsNSSHelper.h"
|
||||
#include "nsReadableUtils.h"
|
||||
#include "nsUnicharUtils.h"
|
||||
#include "nsXULAppAPI.h"
|
||||
#include "nspr.h"
|
||||
#include "secasn1.h"
|
||||
#include "secder.h"
|
||||
#include "secdert.h"
|
||||
|
||||
//These defines are taken from the PKCS#11 spec
|
||||
#define CKM_RSA_PKCS_KEY_PAIR_GEN 0x00000000
|
||||
#define CKM_DH_PKCS_KEY_PAIR_GEN 0x00000020
|
||||
|
||||
DERTemplate SECAlgorithmIDTemplate[] = {
|
||||
{ DER_SEQUENCE,
|
||||
0, nullptr, sizeof(SECAlgorithmID) },
|
||||
{ DER_OBJECT_ID,
|
||||
offsetof(SECAlgorithmID,algorithm), },
|
||||
{ DER_OPTIONAL | DER_ANY,
|
||||
offsetof(SECAlgorithmID,parameters), },
|
||||
{ 0, }
|
||||
};
|
||||
|
||||
DERTemplate CERTSubjectPublicKeyInfoTemplate[] = {
|
||||
{ DER_SEQUENCE,
|
||||
0, nullptr, sizeof(CERTSubjectPublicKeyInfo) },
|
||||
{ DER_INLINE,
|
||||
offsetof(CERTSubjectPublicKeyInfo,algorithm),
|
||||
SECAlgorithmIDTemplate, },
|
||||
{ DER_BIT_STRING,
|
||||
offsetof(CERTSubjectPublicKeyInfo,subjectPublicKey), },
|
||||
{ 0, }
|
||||
};
|
||||
|
||||
DERTemplate CERTPublicKeyAndChallengeTemplate[] =
|
||||
{
|
||||
{ DER_SEQUENCE, 0, nullptr, sizeof(CERTPublicKeyAndChallenge) },
|
||||
{ DER_ANY, offsetof(CERTPublicKeyAndChallenge,spki), },
|
||||
{ DER_IA5_STRING, offsetof(CERTPublicKeyAndChallenge,challenge), },
|
||||
{ 0, }
|
||||
};
|
||||
|
||||
typedef struct curveNameTagPairStr {
|
||||
const char *curveName;
|
||||
SECOidTag curveOidTag;
|
||||
} CurveNameTagPair;
|
||||
|
||||
static CurveNameTagPair nameTagPair[] =
|
||||
{
|
||||
{ "prime192v1", SEC_OID_ANSIX962_EC_PRIME192V1 },
|
||||
{ "prime192v2", SEC_OID_ANSIX962_EC_PRIME192V2 },
|
||||
{ "prime192v3", SEC_OID_ANSIX962_EC_PRIME192V3 },
|
||||
{ "prime239v1", SEC_OID_ANSIX962_EC_PRIME239V1 },
|
||||
{ "prime239v2", SEC_OID_ANSIX962_EC_PRIME239V2 },
|
||||
{ "prime239v3", SEC_OID_ANSIX962_EC_PRIME239V3 },
|
||||
{ "prime256v1", SEC_OID_ANSIX962_EC_PRIME256V1 },
|
||||
|
||||
{ "secp112r1", SEC_OID_SECG_EC_SECP112R1},
|
||||
{ "secp112r2", SEC_OID_SECG_EC_SECP112R2},
|
||||
{ "secp128r1", SEC_OID_SECG_EC_SECP128R1},
|
||||
{ "secp128r2", SEC_OID_SECG_EC_SECP128R2},
|
||||
{ "secp160k1", SEC_OID_SECG_EC_SECP160K1},
|
||||
{ "secp160r1", SEC_OID_SECG_EC_SECP160R1},
|
||||
{ "secp160r2", SEC_OID_SECG_EC_SECP160R2},
|
||||
{ "secp192k1", SEC_OID_SECG_EC_SECP192K1},
|
||||
{ "secp192r1", SEC_OID_ANSIX962_EC_PRIME192V1 },
|
||||
{ "nistp192", SEC_OID_ANSIX962_EC_PRIME192V1 },
|
||||
{ "secp224k1", SEC_OID_SECG_EC_SECP224K1},
|
||||
{ "secp224r1", SEC_OID_SECG_EC_SECP224R1},
|
||||
{ "nistp224", SEC_OID_SECG_EC_SECP224R1},
|
||||
{ "secp256k1", SEC_OID_SECG_EC_SECP256K1},
|
||||
{ "secp256r1", SEC_OID_ANSIX962_EC_PRIME256V1 },
|
||||
{ "nistp256", SEC_OID_ANSIX962_EC_PRIME256V1 },
|
||||
{ "secp384r1", SEC_OID_SECG_EC_SECP384R1},
|
||||
{ "nistp384", SEC_OID_SECG_EC_SECP384R1},
|
||||
{ "secp521r1", SEC_OID_SECG_EC_SECP521R1},
|
||||
{ "nistp521", SEC_OID_SECG_EC_SECP521R1},
|
||||
|
||||
{ "c2pnb163v1", SEC_OID_ANSIX962_EC_C2PNB163V1 },
|
||||
{ "c2pnb163v2", SEC_OID_ANSIX962_EC_C2PNB163V2 },
|
||||
{ "c2pnb163v3", SEC_OID_ANSIX962_EC_C2PNB163V3 },
|
||||
{ "c2pnb176v1", SEC_OID_ANSIX962_EC_C2PNB176V1 },
|
||||
{ "c2tnb191v1", SEC_OID_ANSIX962_EC_C2TNB191V1 },
|
||||
{ "c2tnb191v2", SEC_OID_ANSIX962_EC_C2TNB191V2 },
|
||||
{ "c2tnb191v3", SEC_OID_ANSIX962_EC_C2TNB191V3 },
|
||||
{ "c2onb191v4", SEC_OID_ANSIX962_EC_C2ONB191V4 },
|
||||
{ "c2onb191v5", SEC_OID_ANSIX962_EC_C2ONB191V5 },
|
||||
{ "c2pnb208w1", SEC_OID_ANSIX962_EC_C2PNB208W1 },
|
||||
{ "c2tnb239v1", SEC_OID_ANSIX962_EC_C2TNB239V1 },
|
||||
{ "c2tnb239v2", SEC_OID_ANSIX962_EC_C2TNB239V2 },
|
||||
{ "c2tnb239v3", SEC_OID_ANSIX962_EC_C2TNB239V3 },
|
||||
{ "c2onb239v4", SEC_OID_ANSIX962_EC_C2ONB239V4 },
|
||||
{ "c2onb239v5", SEC_OID_ANSIX962_EC_C2ONB239V5 },
|
||||
{ "c2pnb272w1", SEC_OID_ANSIX962_EC_C2PNB272W1 },
|
||||
{ "c2pnb304w1", SEC_OID_ANSIX962_EC_C2PNB304W1 },
|
||||
{ "c2tnb359v1", SEC_OID_ANSIX962_EC_C2TNB359V1 },
|
||||
{ "c2pnb368w1", SEC_OID_ANSIX962_EC_C2PNB368W1 },
|
||||
{ "c2tnb431r1", SEC_OID_ANSIX962_EC_C2TNB431R1 },
|
||||
|
||||
{ "sect113r1", SEC_OID_SECG_EC_SECT113R1},
|
||||
{ "sect113r2", SEC_OID_SECG_EC_SECT113R2},
|
||||
{ "sect131r1", SEC_OID_SECG_EC_SECT131R1},
|
||||
{ "sect131r2", SEC_OID_SECG_EC_SECT131R2},
|
||||
{ "sect163k1", SEC_OID_SECG_EC_SECT163K1},
|
||||
{ "nistk163", SEC_OID_SECG_EC_SECT163K1},
|
||||
{ "sect163r1", SEC_OID_SECG_EC_SECT163R1},
|
||||
{ "sect163r2", SEC_OID_SECG_EC_SECT163R2},
|
||||
{ "nistb163", SEC_OID_SECG_EC_SECT163R2},
|
||||
{ "sect193r1", SEC_OID_SECG_EC_SECT193R1},
|
||||
{ "sect193r2", SEC_OID_SECG_EC_SECT193R2},
|
||||
{ "sect233k1", SEC_OID_SECG_EC_SECT233K1},
|
||||
{ "nistk233", SEC_OID_SECG_EC_SECT233K1},
|
||||
{ "sect233r1", SEC_OID_SECG_EC_SECT233R1},
|
||||
{ "nistb233", SEC_OID_SECG_EC_SECT233R1},
|
||||
{ "sect239k1", SEC_OID_SECG_EC_SECT239K1},
|
||||
{ "sect283k1", SEC_OID_SECG_EC_SECT283K1},
|
||||
{ "nistk283", SEC_OID_SECG_EC_SECT283K1},
|
||||
{ "sect283r1", SEC_OID_SECG_EC_SECT283R1},
|
||||
{ "nistb283", SEC_OID_SECG_EC_SECT283R1},
|
||||
{ "sect409k1", SEC_OID_SECG_EC_SECT409K1},
|
||||
{ "nistk409", SEC_OID_SECG_EC_SECT409K1},
|
||||
{ "sect409r1", SEC_OID_SECG_EC_SECT409R1},
|
||||
{ "nistb409", SEC_OID_SECG_EC_SECT409R1},
|
||||
{ "sect571k1", SEC_OID_SECG_EC_SECT571K1},
|
||||
{ "nistk571", SEC_OID_SECG_EC_SECT571K1},
|
||||
{ "sect571r1", SEC_OID_SECG_EC_SECT571R1},
|
||||
{ "nistb571", SEC_OID_SECG_EC_SECT571R1},
|
||||
|
||||
};
|
||||
|
||||
mozilla::UniqueSECItem
|
||||
DecodeECParams(const char* curve)
|
||||
{
|
||||
SECOidData *oidData = nullptr;
|
||||
SECOidTag curveOidTag = SEC_OID_UNKNOWN; /* default */
|
||||
int i, numCurves;
|
||||
|
||||
if (curve && *curve) {
|
||||
numCurves = sizeof(nameTagPair)/sizeof(CurveNameTagPair);
|
||||
for (i = 0; ((i < numCurves) && (curveOidTag == SEC_OID_UNKNOWN));
|
||||
i++) {
|
||||
if (PL_strcmp(curve, nameTagPair[i].curveName) == 0)
|
||||
curveOidTag = nameTagPair[i].curveOidTag;
|
||||
}
|
||||
}
|
||||
|
||||
/* Return nullptr if curve name is not recognized */
|
||||
if ((curveOidTag == SEC_OID_UNKNOWN) ||
|
||||
(oidData = SECOID_FindOIDByTag(curveOidTag)) == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
mozilla::UniqueSECItem ecparams(SECITEM_AllocItem(nullptr, nullptr,
|
||||
2 + oidData->oid.len));
|
||||
if (!ecparams) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/*
|
||||
* ecparams->data needs to contain the ASN encoding of an object ID (OID)
|
||||
* representing the named curve. The actual OID is in
|
||||
* oidData->oid.data so we simply prepend 0x06 and OID length
|
||||
*/
|
||||
ecparams->data[0] = SEC_ASN1_OBJECT_ID;
|
||||
ecparams->data[1] = oidData->oid.len;
|
||||
memcpy(ecparams->data + 2, oidData->oid.data, oidData->oid.len);
|
||||
|
||||
return ecparams;
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(nsKeygenFormProcessor, nsIFormProcessor)
|
||||
|
||||
nsKeygenFormProcessor::nsKeygenFormProcessor()
|
||||
{
|
||||
m_ctx = new PipUIContext();
|
||||
}
|
||||
|
||||
nsKeygenFormProcessor::~nsKeygenFormProcessor()
|
||||
{
|
||||
nsNSSShutDownPreventionLock locker;
|
||||
if (isAlreadyShutDown()) {
|
||||
return;
|
||||
}
|
||||
|
||||
shutdown(ShutdownCalledFrom::Object);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsKeygenFormProcessor::Create(nsISupports* aOuter, const nsIID& aIID, void* *aResult)
|
||||
{
|
||||
if (GeckoProcessType_Content == XRE_GetProcessType()) {
|
||||
nsCOMPtr<nsISupports> contentProcessor = new nsKeygenFormProcessorContent();
|
||||
return contentProcessor->QueryInterface(aIID, aResult);
|
||||
}
|
||||
|
||||
nsresult rv;
|
||||
NS_ENSURE_NO_AGGREGATION(aOuter);
|
||||
nsKeygenFormProcessor* formProc = new nsKeygenFormProcessor();
|
||||
|
||||
nsCOMPtr<nsISupports> stabilize = formProc;
|
||||
rv = formProc->Init();
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
rv = formProc->QueryInterface(aIID, aResult);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsKeygenFormProcessor::Init()
|
||||
{
|
||||
static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID);
|
||||
|
||||
nsresult rv;
|
||||
|
||||
nsCOMPtr<nsINSSComponent> nssComponent;
|
||||
nssComponent = do_GetService(kNSSComponentCID, &rv);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
// Init possible key size choices.
|
||||
nssComponent->GetPIPNSSBundleString("HighGrade", mSECKeySizeChoiceList[0].name);
|
||||
mSECKeySizeChoiceList[0].size = 2048;
|
||||
|
||||
nssComponent->GetPIPNSSBundleString("MediumGrade", mSECKeySizeChoiceList[1].name);
|
||||
mSECKeySizeChoiceList[1].size = 1024;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsKeygenFormProcessor::GetSlot(uint32_t aMechanism, PK11SlotInfo** aSlot)
|
||||
{
|
||||
nsNSSShutDownPreventionLock locker;
|
||||
if (isAlreadyShutDown()) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
return GetSlotWithMechanism(aMechanism, m_ctx, aSlot, locker);
|
||||
}
|
||||
|
||||
uint32_t MapGenMechToAlgoMech(uint32_t mechanism)
|
||||
{
|
||||
uint32_t searchMech;
|
||||
|
||||
/* We are interested in slots based on the ability to perform
|
||||
a given algorithm, not on their ability to generate keys usable
|
||||
by that algorithm. Therefore, map keygen-specific mechanism tags
|
||||
to tags for the corresponding crypto algorithm. */
|
||||
switch(mechanism)
|
||||
{
|
||||
case CKM_RSA_PKCS_KEY_PAIR_GEN:
|
||||
searchMech = CKM_RSA_PKCS;
|
||||
break;
|
||||
case CKM_RC4_KEY_GEN:
|
||||
searchMech = CKM_RC4;
|
||||
break;
|
||||
case CKM_DH_PKCS_KEY_PAIR_GEN:
|
||||
searchMech = CKM_DH_PKCS_DERIVE; /* ### mwelch is this right? */
|
||||
break;
|
||||
case CKM_DES_KEY_GEN:
|
||||
/* What do we do about DES keygen? Right now, we're just using
|
||||
DES_KEY_GEN to look for tokens, because otherwise we'll have
|
||||
to search the token list three times. */
|
||||
case CKM_EC_KEY_PAIR_GEN:
|
||||
/* The default should also work for EC key pair generation. */
|
||||
default:
|
||||
searchMech = mechanism;
|
||||
break;
|
||||
}
|
||||
return searchMech;
|
||||
}
|
||||
|
||||
|
||||
nsresult
|
||||
GetSlotWithMechanism(uint32_t aMechanism, nsIInterfaceRequestor* m_ctx,
|
||||
PK11SlotInfo** aSlot, nsNSSShutDownPreventionLock& /*proofOfLock*/)
|
||||
{
|
||||
PK11SlotList * slotList = nullptr;
|
||||
char16_t** tokenNameList = nullptr;
|
||||
nsCOMPtr<nsITokenDialogs> dialogs;
|
||||
char16_t *unicodeTokenChosen;
|
||||
PK11SlotListElement *slotElement, *tmpSlot;
|
||||
uint32_t numSlots = 0, i = 0;
|
||||
bool canceled;
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
*aSlot = nullptr;
|
||||
|
||||
// Get the slot
|
||||
slotList = PK11_GetAllTokens(MapGenMechToAlgoMech(aMechanism),
|
||||
true, true, m_ctx);
|
||||
if (!slotList || !slotList->head) {
|
||||
rv = NS_ERROR_FAILURE;
|
||||
goto loser;
|
||||
}
|
||||
|
||||
if (!slotList->head->next) {
|
||||
/* only one slot available, just return it */
|
||||
*aSlot = slotList->head->slot;
|
||||
} else {
|
||||
// Gerenate a list of slots and ask the user to choose //
|
||||
tmpSlot = slotList->head;
|
||||
while (tmpSlot) {
|
||||
numSlots++;
|
||||
tmpSlot = tmpSlot->next;
|
||||
}
|
||||
|
||||
// Allocate the slot name buffer //
|
||||
tokenNameList = static_cast<char16_t**>(moz_xmalloc(sizeof(char16_t *) * numSlots));
|
||||
if (!tokenNameList) {
|
||||
rv = NS_ERROR_OUT_OF_MEMORY;
|
||||
goto loser;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
slotElement = PK11_GetFirstSafe(slotList);
|
||||
while (slotElement) {
|
||||
tokenNameList[i] = UTF8ToNewUnicode(nsDependentCString(PK11_GetTokenName(slotElement->slot)));
|
||||
slotElement = PK11_GetNextSafe(slotList, slotElement, false);
|
||||
if (tokenNameList[i])
|
||||
i++;
|
||||
else {
|
||||
// OOM. adjust numSlots so we don't free unallocated memory.
|
||||
numSlots = i;
|
||||
PK11_FreeSlotListElement(slotList, slotElement);
|
||||
rv = NS_ERROR_OUT_OF_MEMORY;
|
||||
goto loser;
|
||||
}
|
||||
}
|
||||
|
||||
// Throw up the token list dialog and get back the token.
|
||||
rv = getNSSDialogs(getter_AddRefs(dialogs), NS_GET_IID(nsITokenDialogs),
|
||||
NS_TOKENDIALOGS_CONTRACTID);
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
goto loser;
|
||||
}
|
||||
|
||||
if (!tokenNameList || !*tokenNameList) {
|
||||
rv = NS_ERROR_OUT_OF_MEMORY;
|
||||
} else {
|
||||
rv = dialogs->ChooseToken(m_ctx, (const char16_t**)tokenNameList,
|
||||
numSlots, &unicodeTokenChosen, &canceled);
|
||||
}
|
||||
if (NS_FAILED(rv)) goto loser;
|
||||
|
||||
if (canceled) { rv = NS_ERROR_NOT_AVAILABLE; goto loser; }
|
||||
|
||||
// Get the slot //
|
||||
slotElement = PK11_GetFirstSafe(slotList);
|
||||
nsAutoString tokenStr(unicodeTokenChosen);
|
||||
while (slotElement) {
|
||||
if (tokenStr.Equals(NS_ConvertUTF8toUTF16(PK11_GetTokenName(slotElement->slot)))) {
|
||||
*aSlot = slotElement->slot;
|
||||
PK11_FreeSlotListElement(slotList, slotElement);
|
||||
break;
|
||||
}
|
||||
slotElement = PK11_GetNextSafe(slotList, slotElement, false);
|
||||
}
|
||||
if(!(*aSlot)) {
|
||||
rv = NS_ERROR_FAILURE;
|
||||
goto loser;
|
||||
}
|
||||
}
|
||||
|
||||
// Get a reference to the slot //
|
||||
PK11_ReferenceSlot(*aSlot);
|
||||
loser:
|
||||
if (slotList) {
|
||||
PK11_FreeSlotList(slotList);
|
||||
}
|
||||
if (tokenNameList) {
|
||||
NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(numSlots, tokenNameList);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
GatherKeygenTelemetry(uint32_t keyGenMechanism, int keysize, char* curve)
|
||||
{
|
||||
if (keyGenMechanism == CKM_RSA_PKCS_KEY_PAIR_GEN) {
|
||||
if (keysize > 8196 || keysize < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCString telemetryValue("rsa");
|
||||
telemetryValue.AppendPrintf("%d", keysize);
|
||||
mozilla::Telemetry::Accumulate(
|
||||
mozilla::Telemetry::KEYGEN_GENERATED_KEY_TYPE, telemetryValue);
|
||||
} else if (keyGenMechanism == CKM_EC_KEY_PAIR_GEN) {
|
||||
nsCString secp384r1 = NS_LITERAL_CSTRING("secp384r1");
|
||||
nsCString secp256r1 = NS_LITERAL_CSTRING("secp256r1");
|
||||
|
||||
mozilla::UniqueSECItem decoded = DecodeECParams(curve);
|
||||
if (!decoded) {
|
||||
switch (keysize) {
|
||||
case 2048:
|
||||
mozilla::Telemetry::Accumulate(
|
||||
mozilla::Telemetry::KEYGEN_GENERATED_KEY_TYPE, secp384r1);
|
||||
break;
|
||||
case 1024:
|
||||
case 512:
|
||||
mozilla::Telemetry::Accumulate(
|
||||
mozilla::Telemetry::KEYGEN_GENERATED_KEY_TYPE, secp256r1);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (secp384r1.EqualsIgnoreCase(curve, secp384r1.Length())) {
|
||||
mozilla::Telemetry::Accumulate(
|
||||
mozilla::Telemetry::KEYGEN_GENERATED_KEY_TYPE, secp384r1);
|
||||
} else if (secp256r1.EqualsIgnoreCase(curve, secp256r1.Length())) {
|
||||
mozilla::Telemetry::Accumulate(
|
||||
mozilla::Telemetry::KEYGEN_GENERATED_KEY_TYPE, secp256r1);
|
||||
} else {
|
||||
mozilla::Telemetry::Accumulate(
|
||||
mozilla::Telemetry::KEYGEN_GENERATED_KEY_TYPE, NS_LITERAL_CSTRING("other_ec"));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
MOZ_CRASH("Unknown keygen algorithm");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsKeygenFormProcessor::GetPublicKey(const nsAString& aValue,
|
||||
const nsAString& aChallenge,
|
||||
const nsAFlatString& aKeyType,
|
||||
nsAString& aOutPublicKey,
|
||||
const nsAString& aKeyParams)
|
||||
{
|
||||
nsNSSShutDownPreventionLock locker;
|
||||
if (isAlreadyShutDown()) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
nsresult rv = NS_ERROR_FAILURE;
|
||||
UniquePORTString keystring;
|
||||
char *keyparamsString = nullptr;
|
||||
uint32_t keyGenMechanism;
|
||||
PK11SlotInfo *slot = nullptr;
|
||||
PK11RSAGenParams rsaParams;
|
||||
mozilla::UniqueSECItem ecParams;
|
||||
SECOidTag algTag;
|
||||
int keysize = 0;
|
||||
void *params = nullptr; // Non-owning.
|
||||
SECKEYPrivateKey *privateKey = nullptr;
|
||||
SECKEYPublicKey *publicKey = nullptr;
|
||||
CERTSubjectPublicKeyInfo *spkInfo = nullptr;
|
||||
SECStatus srv = SECFailure;
|
||||
SECItem spkiItem;
|
||||
SECItem pkacItem;
|
||||
SECItem signedItem;
|
||||
CERTPublicKeyAndChallenge pkac;
|
||||
pkac.challenge.data = nullptr;
|
||||
nsCOMPtr<nsIGeneratingKeypairInfoDialogs> dialogs;
|
||||
nsKeygenThread *KeygenRunnable = 0;
|
||||
nsCOMPtr<nsIKeygenThread> runnable;
|
||||
|
||||
// permanent and sensitive flags for keygen
|
||||
PK11AttrFlags attrFlags = PK11_ATTR_TOKEN | PK11_ATTR_SENSITIVE | PK11_ATTR_PRIVATE;
|
||||
|
||||
UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
|
||||
if (!arena) {
|
||||
goto loser;
|
||||
}
|
||||
|
||||
// Get the key size //
|
||||
for (size_t i = 0; i < number_of_key_size_choices; ++i) {
|
||||
if (aValue.Equals(mSECKeySizeChoiceList[i].name)) {
|
||||
keysize = mSECKeySizeChoiceList[i].size;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!keysize) {
|
||||
goto loser;
|
||||
}
|
||||
|
||||
// Set the keygen mechanism
|
||||
if (aKeyType.IsEmpty() || aKeyType.LowerCaseEqualsLiteral("rsa")) {
|
||||
keyGenMechanism = CKM_RSA_PKCS_KEY_PAIR_GEN;
|
||||
} else if (aKeyType.LowerCaseEqualsLiteral("ec")) {
|
||||
keyparamsString = ToNewCString(aKeyParams);
|
||||
if (!keyparamsString) {
|
||||
rv = NS_ERROR_OUT_OF_MEMORY;
|
||||
goto loser;
|
||||
}
|
||||
|
||||
keyGenMechanism = CKM_EC_KEY_PAIR_GEN;
|
||||
/* ecParams are initialized later */
|
||||
} else {
|
||||
goto loser;
|
||||
}
|
||||
|
||||
// Get the slot
|
||||
rv = GetSlot(keyGenMechanism, &slot);
|
||||
if (NS_FAILED(rv)) {
|
||||
goto loser;
|
||||
}
|
||||
switch (keyGenMechanism) {
|
||||
case CKM_RSA_PKCS_KEY_PAIR_GEN:
|
||||
rsaParams.keySizeInBits = keysize;
|
||||
rsaParams.pe = DEFAULT_RSA_KEYGEN_PE;
|
||||
algTag = DEFAULT_RSA_KEYGEN_ALG;
|
||||
params = &rsaParams;
|
||||
break;
|
||||
case CKM_EC_KEY_PAIR_GEN:
|
||||
/* XXX We ought to rethink how the KEYGEN tag is
|
||||
* displayed. The pulldown selections presented
|
||||
* to the user must depend on the keytype.
|
||||
* The displayed selection could be picked
|
||||
* from the keyparams attribute (this is currently called
|
||||
* the pqg attribute).
|
||||
* For now, we pick ecparams from the keyparams field
|
||||
* if it specifies a valid supported curve, or else
|
||||
* we pick one of secp384r1, secp256r1 or secp192r1
|
||||
* respectively depending on the user's selection
|
||||
* (High, Medium, Low).
|
||||
* (RSA uses RSA-2048, RSA-1024 and RSA-512 for historical
|
||||
* reasons, while ECC choices represent a stronger mapping)
|
||||
* NOTE: The user's selection
|
||||
* is silently ignored when a valid curve is presented
|
||||
* in keyparams.
|
||||
*/
|
||||
ecParams = DecodeECParams(keyparamsString);
|
||||
if (!ecParams) {
|
||||
/* The keyparams attribute did not specify a valid
|
||||
* curve name so use a curve based on the keysize.
|
||||
* NOTE: Here keysize is used only as an indication of
|
||||
* High/Medium/Low strength; elliptic curve
|
||||
* cryptography uses smaller keys than RSA to provide
|
||||
* equivalent security.
|
||||
*/
|
||||
switch (keysize) {
|
||||
case 2048:
|
||||
ecParams = DecodeECParams("secp384r1");
|
||||
break;
|
||||
case 1024:
|
||||
case 512:
|
||||
ecParams = DecodeECParams("secp256r1");
|
||||
break;
|
||||
}
|
||||
}
|
||||
MOZ_ASSERT(ecParams);
|
||||
params = ecParams.get();
|
||||
/* XXX The signature algorithm ought to choose the hashing
|
||||
* algorithm based on key size once ECDSA variations based
|
||||
* on SHA256 SHA384 and SHA512 are standardized.
|
||||
*/
|
||||
algTag = SEC_OID_ANSIX962_ECDSA_SIGNATURE_WITH_SHA1_DIGEST;
|
||||
break;
|
||||
default:
|
||||
goto loser;
|
||||
}
|
||||
|
||||
/* Make sure token is initialized. */
|
||||
rv = setPassword(slot, m_ctx, locker);
|
||||
if (NS_FAILED(rv))
|
||||
goto loser;
|
||||
|
||||
srv = PK11_Authenticate(slot, true, m_ctx);
|
||||
if (srv != SECSuccess) {
|
||||
goto loser;
|
||||
}
|
||||
|
||||
rv = getNSSDialogs(getter_AddRefs(dialogs),
|
||||
NS_GET_IID(nsIGeneratingKeypairInfoDialogs),
|
||||
NS_GENERATINGKEYPAIRINFODIALOGS_CONTRACTID);
|
||||
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
KeygenRunnable = new nsKeygenThread();
|
||||
NS_IF_ADDREF(KeygenRunnable);
|
||||
}
|
||||
|
||||
if (NS_FAILED(rv) || !KeygenRunnable) {
|
||||
rv = NS_OK;
|
||||
privateKey = PK11_GenerateKeyPairWithFlags(slot, keyGenMechanism, params,
|
||||
&publicKey, attrFlags, m_ctx);
|
||||
} else {
|
||||
KeygenRunnable->SetParams( slot, attrFlags, nullptr, 0,
|
||||
keyGenMechanism, params, m_ctx );
|
||||
|
||||
runnable = do_QueryInterface(KeygenRunnable);
|
||||
if (runnable) {
|
||||
rv = dialogs->DisplayGeneratingKeypairInfo(m_ctx, runnable);
|
||||
// We call join on the thread so we can be sure that no
|
||||
// simultaneous access to the passed parameters will happen.
|
||||
KeygenRunnable->Join();
|
||||
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
PK11SlotInfo *used_slot = nullptr;
|
||||
rv = KeygenRunnable->ConsumeResult(&used_slot, &privateKey, &publicKey);
|
||||
if (NS_SUCCEEDED(rv) && used_slot) {
|
||||
PK11_FreeSlot(used_slot);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (NS_FAILED(rv) || !privateKey) {
|
||||
goto loser;
|
||||
}
|
||||
// just in case we'll need to authenticate to the db -jp //
|
||||
privateKey->wincx = m_ctx;
|
||||
|
||||
/*
|
||||
* Create a subject public key info from the public key.
|
||||
*/
|
||||
spkInfo = SECKEY_CreateSubjectPublicKeyInfo(publicKey);
|
||||
if ( !spkInfo ) {
|
||||
goto loser;
|
||||
}
|
||||
|
||||
/*
|
||||
* Now DER encode the whole subjectPublicKeyInfo.
|
||||
*/
|
||||
srv = DER_Encode(arena.get(), &spkiItem, CERTSubjectPublicKeyInfoTemplate,
|
||||
spkInfo);
|
||||
if (srv != SECSuccess) {
|
||||
goto loser;
|
||||
}
|
||||
|
||||
/*
|
||||
* set up the PublicKeyAndChallenge data structure, then DER encode it
|
||||
*/
|
||||
pkac.spki = spkiItem;
|
||||
pkac.challenge.len = aChallenge.Length();
|
||||
pkac.challenge.data = (unsigned char *)ToNewCString(aChallenge);
|
||||
if (!pkac.challenge.data) {
|
||||
rv = NS_ERROR_OUT_OF_MEMORY;
|
||||
goto loser;
|
||||
}
|
||||
|
||||
srv = DER_Encode(arena.get(), &pkacItem, CERTPublicKeyAndChallengeTemplate,
|
||||
&pkac);
|
||||
if (srv != SECSuccess) {
|
||||
goto loser;
|
||||
}
|
||||
|
||||
/*
|
||||
* now sign the DER encoded PublicKeyAndChallenge
|
||||
*/
|
||||
srv = SEC_DerSignData(arena.get(), &signedItem, pkacItem.data, pkacItem.len,
|
||||
privateKey, algTag);
|
||||
if (srv != SECSuccess) {
|
||||
goto loser;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert the signed public key and challenge into base64/ascii.
|
||||
*/
|
||||
keystring = UniquePORTString(
|
||||
BTOA_DataToAscii(signedItem.data, signedItem.len));
|
||||
if (!keystring) {
|
||||
rv = NS_ERROR_OUT_OF_MEMORY;
|
||||
goto loser;
|
||||
}
|
||||
|
||||
CopyASCIItoUTF16(keystring.get(), aOutPublicKey);
|
||||
|
||||
rv = NS_OK;
|
||||
|
||||
GatherKeygenTelemetry(keyGenMechanism, keysize, keyparamsString);
|
||||
loser:
|
||||
if (srv != SECSuccess) {
|
||||
if ( privateKey ) {
|
||||
PK11_DestroyTokenObject(privateKey->pkcs11Slot,privateKey->pkcs11ID);
|
||||
}
|
||||
if ( publicKey ) {
|
||||
PK11_DestroyTokenObject(publicKey->pkcs11Slot,publicKey->pkcs11ID);
|
||||
}
|
||||
}
|
||||
if ( spkInfo ) {
|
||||
SECKEY_DestroySubjectPublicKeyInfo(spkInfo);
|
||||
}
|
||||
if ( publicKey ) {
|
||||
SECKEY_DestroyPublicKey(publicKey);
|
||||
}
|
||||
if ( privateKey ) {
|
||||
SECKEY_DestroyPrivateKey(privateKey);
|
||||
}
|
||||
if (slot) {
|
||||
PK11_FreeSlot(slot);
|
||||
}
|
||||
if (KeygenRunnable) {
|
||||
NS_RELEASE(KeygenRunnable);
|
||||
}
|
||||
if (keyparamsString) {
|
||||
free(keyparamsString);
|
||||
}
|
||||
if (pkac.challenge.data) {
|
||||
free(pkac.challenge.data);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
// static
|
||||
void
|
||||
nsKeygenFormProcessor::ExtractParams(nsIDOMHTMLElement* aElement,
|
||||
nsAString& challengeValue,
|
||||
nsAString& keyTypeValue,
|
||||
nsAString& keyParamsValue)
|
||||
{
|
||||
aElement->GetAttribute(NS_LITERAL_STRING("keytype"), keyTypeValue);
|
||||
if (keyTypeValue.IsEmpty()) {
|
||||
// If this field is not present, we default to rsa.
|
||||
keyTypeValue.AssignLiteral("rsa");
|
||||
}
|
||||
|
||||
aElement->GetAttribute(NS_LITERAL_STRING("pqg"),
|
||||
keyParamsValue);
|
||||
/* XXX We can still support the pqg attribute in the keygen
|
||||
* tag for backward compatibility while introducing a more
|
||||
* general attribute named keyparams.
|
||||
*/
|
||||
if (keyParamsValue.IsEmpty()) {
|
||||
aElement->GetAttribute(NS_LITERAL_STRING("keyparams"),
|
||||
keyParamsValue);
|
||||
}
|
||||
|
||||
aElement->GetAttribute(NS_LITERAL_STRING("challenge"), challengeValue);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsKeygenFormProcessor::ProcessValue(nsIDOMHTMLElement* aElement,
|
||||
const nsAString& aName,
|
||||
nsAString& aValue)
|
||||
{
|
||||
nsAutoString challengeValue;
|
||||
nsAutoString keyTypeValue;
|
||||
nsAutoString keyParamsValue;
|
||||
ExtractParams(aElement, challengeValue, keyTypeValue, keyParamsValue);
|
||||
|
||||
return GetPublicKey(aValue, challengeValue, keyTypeValue,
|
||||
aValue, keyParamsValue);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsKeygenFormProcessor::ProcessValueIPC(const nsAString& aOldValue,
|
||||
const nsAString& aChallenge,
|
||||
const nsAString& aKeyType,
|
||||
const nsAString& aKeyParams,
|
||||
nsAString& newValue)
|
||||
{
|
||||
return GetPublicKey(aOldValue, aChallenge, PromiseFlatString(aKeyType),
|
||||
newValue, aKeyParams);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsKeygenFormProcessor::ProvideContent(const nsAString& aFormType,
|
||||
nsTArray<nsString>& aContent,
|
||||
nsAString& aAttribute)
|
||||
{
|
||||
if (Compare(aFormType, NS_LITERAL_STRING("SELECT"),
|
||||
nsCaseInsensitiveStringComparator()) == 0) {
|
||||
|
||||
for (size_t i = 0; i < number_of_key_size_choices; ++i) {
|
||||
aContent.AppendElement(mSECKeySizeChoiceList[i].name);
|
||||
}
|
||||
aAttribute.AssignLiteral("-mozilla-keygen");
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue