import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo

This commit is contained in:
Roy Tam 2018-01-19 03:59:58 +08:00
commit dcd9973243
150858 changed files with 23884658 additions and 0 deletions

View 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

View 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

View 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);
}
}

View 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

View 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);
}

View 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

View 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

View 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

View 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"];

View 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

View 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

View 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

View 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(&notBefore);
validity->GetNotAfter(&notAfter);
// 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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;
}

View 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

View 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

View 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

View 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

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View 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

View 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

View 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;
}

View 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

View 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

View 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

View 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

View 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

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View 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

View 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;
}

View 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

View 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"];

View file

@ -0,0 +1 @@
<html><body><keygen></keygen></body></html>

View file

@ -0,0 +1 @@
load 398665-1.html

145
security/manager/ssl/md4.c Normal file
View 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);
}

View 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__ */

View 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']

View 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);
}

View 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

File diff suppressed because it is too large Load diff

View 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_ */

View 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);
}

View 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

View 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;
}

View 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_

View 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;
}

View 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_

View 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;
}

View 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

View 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;
};

View 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);
};

View 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;
};

View 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();
};

View 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);
};

View 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();
};

View 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);
};

View 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"
%}

View 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"
%}

View 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"
%}

View 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);
};

View 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);
};

View 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"
%}

View 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);
};

View 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"
%}

View 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"
%}

View 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"
%}

View 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;
};

View 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();
};

View 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();
};

View 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);
};

View 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();
};

View 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;
};

View 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;
};

View 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"
%}

View 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;
};

View 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;
};

View 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();
};

View 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.
};

View 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"
%}

View 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"
%}

View 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"
%}

View 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);
};

View 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);
};

View 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();
};

View 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();
};

View 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"
%}

View 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;
};

View 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;
}

View 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

View 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