Source code

Revision control

Copy as Markdown

Other Tools

/* -*- 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 "TransportSecurityInfo.h"
#include "ipc/IPCMessageUtils.h"
#include "mozilla/Base64.h"
#include "mozpkix/pkixtypes.h"
#include "nsBase64Encoder.h"
#include "nsIObjectInputStream.h"
#include "nsIObjectOutputStream.h"
#include "nsIWebProgressListener.h"
#include "nsNSSCertHelper.h"
#include "nsNSSComponent.h"
#include "nsNSSHelper.h"
#include "nsReadableUtils.h"
#include "nsServiceManagerUtils.h"
#include "nsStringStream.h"
#include "nsXULAppAPI.h"
#include "nsIX509Cert.h"
#include "secerr.h"
#include "ssl.h"
#include "mozilla/ipc/IPDLParamTraits.h"
// nsITransportSecurityInfo should not be created via do_CreateInstance. This
// stub prevents that.
template <>
already_AddRefed<nsISupports>
mozCreateComponent<mozilla::psm::TransportSecurityInfo>() {
return nullptr;
}
namespace mozilla {
namespace psm {
TransportSecurityInfo::TransportSecurityInfo(
uint32_t aSecurityState, PRErrorCode aErrorCode,
nsTArray<RefPtr<nsIX509Cert>>&& aFailedCertChain,
nsCOMPtr<nsIX509Cert>& aServerCert,
nsTArray<RefPtr<nsIX509Cert>>&& aSucceededCertChain,
Maybe<uint16_t> aCipherSuite, Maybe<nsCString> aKeaGroupName,
Maybe<nsCString> aSignatureSchemeName, Maybe<uint16_t> aProtocolVersion,
uint16_t aCertificateTransparencyStatus, Maybe<bool> aIsAcceptedEch,
Maybe<bool> aIsDelegatedCredential,
Maybe<OverridableErrorCategory> aOverridableErrorCategory,
bool aMadeOCSPRequests, bool aUsedPrivateDNS, Maybe<bool> aIsEV,
bool aNPNCompleted, const nsCString& aNegotiatedNPN, bool aResumed,
bool aIsBuiltCertChainRootBuiltInRoot, const nsCString& aPeerId)
: mSecurityState(aSecurityState),
mErrorCode(aErrorCode),
mFailedCertChain(std::move(aFailedCertChain)),
mServerCert(aServerCert),
mSucceededCertChain(std::move(aSucceededCertChain)),
mCipherSuite(aCipherSuite),
mKeaGroupName(aKeaGroupName),
mSignatureSchemeName(aSignatureSchemeName),
mProtocolVersion(aProtocolVersion),
mCertificateTransparencyStatus(aCertificateTransparencyStatus),
mIsAcceptedEch(aIsAcceptedEch),
mIsDelegatedCredential(aIsDelegatedCredential),
mOverridableErrorCategory(aOverridableErrorCategory),
mMadeOCSPRequests(aMadeOCSPRequests),
mUsedPrivateDNS(aUsedPrivateDNS),
mIsEV(aIsEV),
mNPNCompleted(aNPNCompleted),
mNegotiatedNPN(aNegotiatedNPN),
mResumed(aResumed),
mIsBuiltCertChainRootBuiltInRoot(aIsBuiltCertChainRootBuiltInRoot),
mPeerId(aPeerId) {}
NS_IMPL_ISUPPORTS(TransportSecurityInfo, nsITransportSecurityInfo)
NS_IMETHODIMP
TransportSecurityInfo::GetSecurityState(uint32_t* state) {
*state = mSecurityState;
return NS_OK;
}
NS_IMETHODIMP
TransportSecurityInfo::GetErrorCode(int32_t* state) {
*state = mErrorCode;
return NS_OK;
}
NS_IMETHODIMP
TransportSecurityInfo::GetErrorCodeString(nsAString& aErrorString) {
const char* codeName = PR_ErrorToName(mErrorCode);
aErrorString.Truncate();
if (codeName) {
aErrorString = NS_ConvertASCIItoUTF16(codeName);
}
return NS_OK;
}
// 16786594-0296-4471-8096-8f84497ca428
#define TRANSPORTSECURITYINFO_CID \
{ \
0x16786594, 0x0296, 0x4471, { \
0x80, 0x96, 0x8f, 0x84, 0x49, 0x7c, 0xa4, 0x28 \
} \
}
static NS_DEFINE_CID(kTransportSecurityInfoCID, TRANSPORTSECURITYINFO_CID);
// This is a new magic value. However, it re-uses the first 4 bytes
// of the previous value. This is so when older versions attempt to
// read a newer serialized TransportSecurityInfo, they will actually
// fail and return NS_ERROR_FAILURE instead of silently failing.
#define TRANSPORTSECURITYINFOMAGIC \
{ \
0xa9863a23, 0x1faa, 0x4169, { \
0xb0, 0xd2, 0x81, 0x29, 0xec, 0x7c, 0xb1, 0xde \
} \
}
static NS_DEFINE_CID(kTransportSecurityInfoMagic, TRANSPORTSECURITYINFOMAGIC);
NS_IMETHODIMP
TransportSecurityInfo::ToString(nsACString& aResult) {
RefPtr<nsBase64Encoder> stream(new nsBase64Encoder());
nsCOMPtr<nsIObjectOutputStream> objStream(NS_NewObjectOutputStream(stream));
nsresult rv = objStream->WriteID(kTransportSecurityInfoCID);
if (NS_FAILED(rv)) {
return rv;
}
rv = objStream->WriteID(NS_ISUPPORTS_IID);
if (NS_FAILED(rv)) {
return rv;
}
rv = objStream->WriteID(kTransportSecurityInfoMagic);
if (NS_FAILED(rv)) {
return rv;
}
rv = objStream->Write32(mSecurityState);
if (NS_FAILED(rv)) {
return rv;
}
// mSubRequestsBrokenSecurity was removed in bug 748809
rv = objStream->Write32(0);
if (NS_FAILED(rv)) {
return rv;
}
// mSubRequestsNoSecurity was removed in bug 748809
rv = objStream->Write32(0);
if (NS_FAILED(rv)) {
return rv;
}
rv = objStream->Write32(static_cast<uint32_t>(mErrorCode));
if (NS_FAILED(rv)) {
return rv;
}
// Re-purpose mErrorMessageCached to represent serialization version
// If string doesn't match exact version it will be treated as older
// serialization.
rv = objStream->WriteWStringZ(NS_ConvertUTF8toUTF16("9").get());
if (NS_FAILED(rv)) {
return rv;
}
// moved from nsISSLStatus
rv = NS_WriteOptionalCompoundObject(objStream, mServerCert,
NS_GET_IID(nsIX509Cert), true);
NS_ENSURE_SUCCESS(rv, rv);
rv = objStream->Write16(mCipherSuite.isSome() ? *mCipherSuite : 0);
NS_ENSURE_SUCCESS(rv, rv);
rv = objStream->Write16(mProtocolVersion.isSome() ? *mProtocolVersion : 0);
NS_ENSURE_SUCCESS(rv, rv);
rv = objStream->Write32(mOverridableErrorCategory.isSome()
? *mOverridableErrorCategory
: OverridableErrorCategory::ERROR_UNSET);
NS_ENSURE_SUCCESS(rv, rv);
rv = objStream->WriteBoolean(mIsEV.isSome() ? *mIsEV : false);
NS_ENSURE_SUCCESS(rv, rv);
rv = objStream->WriteBoolean(mIsEV.isSome()); // previously mHasIsEV
NS_ENSURE_SUCCESS(rv, rv);
rv = objStream->WriteBoolean(
mCipherSuite.isSome()); // previously mHaveCipherSuiteAndProtocol
NS_ENSURE_SUCCESS(rv, rv);
rv = objStream->WriteBoolean(
mOverridableErrorCategory.isSome()); // previously mHaveCertErrorBits
NS_ENSURE_SUCCESS(rv, rv);
rv = objStream->Write16(mCertificateTransparencyStatus);
NS_ENSURE_SUCCESS(rv, rv);
rv = objStream->WriteStringZ(mKeaGroupName.isSome() ? (*mKeaGroupName).get()
: "");
NS_ENSURE_SUCCESS(rv, rv);
rv = objStream->WriteStringZ(
mSignatureSchemeName.isSome() ? (*mSignatureSchemeName).get() : "");
NS_ENSURE_SUCCESS(rv, rv);
rv = objStream->Write16(mSucceededCertChain.Length());
NS_ENSURE_SUCCESS(rv, rv);
for (const auto& cert : mSucceededCertChain) {
rv = objStream->WriteCompoundObject(cert, NS_GET_IID(nsIX509Cert), true);
NS_ENSURE_SUCCESS(rv, rv);
}
// END moved from nsISSLStatus
rv = objStream->Write16(mFailedCertChain.Length());
NS_ENSURE_SUCCESS(rv, rv);
for (const auto& cert : mFailedCertChain) {
rv = objStream->WriteCompoundObject(cert, NS_GET_IID(nsIX509Cert), true);
NS_ENSURE_SUCCESS(rv, rv);
}
rv = objStream->WriteBoolean(
mIsDelegatedCredential.isSome() ? *mIsDelegatedCredential : false);
if (NS_FAILED(rv)) {
return rv;
}
rv = objStream->WriteBoolean(mNPNCompleted);
if (NS_FAILED(rv)) {
return rv;
}
rv = objStream->WriteStringZ(mNegotiatedNPN.get());
if (NS_FAILED(rv)) {
return rv;
}
rv = objStream->WriteBoolean(mResumed);
if (NS_FAILED(rv)) {
return rv;
}
rv = objStream->WriteBoolean(mIsBuiltCertChainRootBuiltInRoot);
if (NS_FAILED(rv)) {
return rv;
}
rv = objStream->WriteBoolean(mIsAcceptedEch.isSome() ? *mIsAcceptedEch
: false);
if (NS_FAILED(rv)) {
return rv;
}
rv = objStream->WriteStringZ(mPeerId.get());
if (NS_FAILED(rv)) {
return rv;
}
rv = objStream->WriteBoolean(mMadeOCSPRequests);
if (NS_FAILED(rv)) {
return rv;
}
rv = objStream->WriteBoolean(mUsedPrivateDNS);
if (NS_FAILED(rv)) {
return rv;
}
rv = stream->Finish(aResult);
if (NS_FAILED(rv)) {
return rv;
}
return NS_OK;
}
nsresult TransportSecurityInfo::ReadOldOverridableErrorBits(
nsIObjectInputStream* aStream,
OverridableErrorCategory& aOverridableErrorCategory) {
bool isDomainMismatch;
nsresult rv = aStream->ReadBoolean(&isDomainMismatch);
NS_ENSURE_SUCCESS(rv, rv);
bool isNotValidAtThisTime;
rv = aStream->ReadBoolean(&isNotValidAtThisTime);
NS_ENSURE_SUCCESS(rv, rv);
bool isUntrusted;
rv = aStream->ReadBoolean(&isUntrusted);
NS_ENSURE_SUCCESS(rv, rv);
if (isUntrusted) {
aOverridableErrorCategory =
nsITransportSecurityInfo::OverridableErrorCategory::ERROR_TRUST;
} else if (isDomainMismatch) {
aOverridableErrorCategory =
nsITransportSecurityInfo::OverridableErrorCategory::ERROR_DOMAIN;
} else if (isNotValidAtThisTime) {
aOverridableErrorCategory =
nsITransportSecurityInfo::OverridableErrorCategory::ERROR_TIME;
} else {
aOverridableErrorCategory =
nsITransportSecurityInfo::OverridableErrorCategory::ERROR_UNSET;
}
return NS_OK;
}
// This is for backward compatibility to be able to read nsISSLStatus
// serialized object.
nsresult TransportSecurityInfo::ReadSSLStatus(
nsIObjectInputStream* aStream, nsCOMPtr<nsIX509Cert>& aServerCert,
Maybe<uint16_t>& aCipherSuite, Maybe<uint16_t>& aProtocolVersion,
Maybe<OverridableErrorCategory>& aOverridableErrorCategory,
Maybe<bool>& aIsEV, uint16_t& aCertificateTransparencyStatus,
Maybe<nsCString>& aKeaGroupName, Maybe<nsCString>& aSignatureSchemeName,
nsTArray<RefPtr<nsIX509Cert>>& aSucceededCertChain) {
bool nsISSLStatusPresent;
nsresult rv = aStream->ReadBoolean(&nsISSLStatusPresent);
NS_ENSURE_SUCCESS(rv, rv);
if (!nsISSLStatusPresent) {
return NS_OK;
}
// nsISSLStatus present. Prepare to read elements.
// Throw away cid, validate iid
nsCID cid;
nsIID iid;
rv = aStream->ReadID(&cid);
NS_ENSURE_SUCCESS(rv, rv);
rv = aStream->ReadID(&iid);
NS_ENSURE_SUCCESS(rv, rv);
static const nsIID nsSSLStatusIID = {
0xfa9ba95b,
0xca3b,
0x498a,
{0xb8, 0x89, 0x7c, 0x79, 0xcf, 0x28, 0xfe, 0xe8}};
if (!iid.Equals(nsSSLStatusIID)) {
return NS_ERROR_UNEXPECTED;
}
nsCOMPtr<nsISupports> cert;
rv = aStream->ReadObject(true, getter_AddRefs(cert));
NS_ENSURE_SUCCESS(rv, rv);
if (cert) {
aServerCert = do_QueryInterface(cert);
if (!aServerCert) {
return NS_NOINTERFACE;
}
}
uint16_t cipherSuite;
rv = aStream->Read16(&cipherSuite);
NS_ENSURE_SUCCESS(rv, rv);
// The code below is a workaround to allow serializing new fields
// while preserving binary compatibility with older streams. For more details
// on the binary compatibility requirement, refer to bug 1248628.
// Here, we take advantage of the fact that mProtocolVersion was originally
// stored as a 16 bits integer, but the highest 8 bits were never used.
// These bits are now used for stream versioning.
uint16_t protocolVersionAndStreamFormatVersion;
rv = aStream->Read16(&protocolVersionAndStreamFormatVersion);
NS_ENSURE_SUCCESS(rv, rv);
const uint8_t streamFormatVersion =
(protocolVersionAndStreamFormatVersion >> 8) & 0xFF;
OverridableErrorCategory overridableErrorCategory;
rv = ReadOldOverridableErrorBits(aStream, overridableErrorCategory);
NS_ENSURE_SUCCESS(rv, rv);
bool isEV;
rv = aStream->ReadBoolean(&isEV);
NS_ENSURE_SUCCESS(rv, rv);
bool hasIsEVStatus;
rv = aStream->ReadBoolean(&hasIsEVStatus);
NS_ENSURE_SUCCESS(rv, rv);
if (hasIsEVStatus) {
aIsEV.emplace(isEV);
}
bool haveCipherSuiteAndProtocol;
rv = aStream->ReadBoolean(&haveCipherSuiteAndProtocol);
if (haveCipherSuiteAndProtocol) {
aCipherSuite.emplace(cipherSuite);
aProtocolVersion.emplace(protocolVersionAndStreamFormatVersion & 0xFF);
}
NS_ENSURE_SUCCESS(rv, rv);
bool haveCertErrorBits;
rv = aStream->ReadBoolean(&haveCertErrorBits);
NS_ENSURE_SUCCESS(rv, rv);
if (haveCertErrorBits) {
aOverridableErrorCategory.emplace(overridableErrorCategory);
}
// Added in version 1 (see bug 1305289).
if (streamFormatVersion >= 1) {
rv = aStream->Read16(&aCertificateTransparencyStatus);
NS_ENSURE_SUCCESS(rv, rv);
}
// Added in version 2 (see bug 1304923).
if (streamFormatVersion >= 2) {
nsCString keaGroupName;
rv = aStream->ReadCString(keaGroupName);
NS_ENSURE_SUCCESS(rv, rv);
if (haveCipherSuiteAndProtocol) {
aKeaGroupName.emplace(keaGroupName);
}
nsCString signatureSchemeName;
rv = aStream->ReadCString(signatureSchemeName);
NS_ENSURE_SUCCESS(rv, rv);
if (haveCipherSuiteAndProtocol) {
aSignatureSchemeName.emplace(signatureSchemeName);
}
}
// Added in version 3 (see bug 1406856).
if (streamFormatVersion >= 3) {
rv = ReadCertList(aStream, aSucceededCertChain);
if (NS_FAILED(rv)) {
return rv;
}
// Read only to consume bytes from the stream.
nsTArray<RefPtr<nsIX509Cert>> failedCertChain;
rv = ReadCertList(aStream, failedCertChain);
if (NS_FAILED(rv)) {
return rv;
}
}
return rv;
}
// This is for backward compatability to be able to read nsIX509CertList
// serialized object.
nsresult TransportSecurityInfo::ReadCertList(
nsIObjectInputStream* aStream, nsTArray<RefPtr<nsIX509Cert>>& aCertList) {
bool nsIX509CertListPresent;
nsresult rv = aStream->ReadBoolean(&nsIX509CertListPresent);
NS_ENSURE_SUCCESS(rv, rv);
if (!nsIX509CertListPresent) {
return NS_OK;
}
// nsIX509CertList present. Prepare to read elements.
// Throw away cid, validate iid
nsCID cid;
nsIID iid;
rv = aStream->ReadID(&cid);
NS_ENSURE_SUCCESS(rv, rv);
rv = aStream->ReadID(&iid);