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);
NS_ENSURE_SUCCESS(rv, rv);
static const nsIID nsIX509CertListIID = {
0xae74cda5,
0xcd2f,
0x473f,
{0x96, 0xf5, 0xf0, 0xb7, 0xff, 0xf6, 0x2c, 0x68}};
if (!iid.Equals(nsIX509CertListIID)) {
return NS_ERROR_UNEXPECTED;
}
uint32_t certListSize;
rv = aStream->Read32(&certListSize);
NS_ENSURE_SUCCESS(rv, rv);
return ReadCertificatesFromStream(aStream, certListSize, aCertList);
}
nsresult TransportSecurityInfo::ReadCertificatesFromStream(
nsIObjectInputStream* aStream, uint32_t aSize,
nsTArray<RefPtr<nsIX509Cert>>& aCertList) {
nsresult rv;
for (uint32_t i = 0; i < aSize; ++i) {
nsCOMPtr<nsISupports> support;
rv = aStream->ReadObject(true, getter_AddRefs(support));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIX509Cert> cert = do_QueryInterface(support);
if (!cert) {
return NS_ERROR_UNEXPECTED;
}
RefPtr<nsIX509Cert> castedCert(cert.get());
aCertList.AppendElement(castedCert);
}
return NS_OK;
}
static nsITransportSecurityInfo::OverridableErrorCategory
IntToOverridableErrorCategory(uint32_t intVal) {
switch (intVal) {
case static_cast<uint32_t>(
nsITransportSecurityInfo::OverridableErrorCategory::ERROR_TRUST):
return nsITransportSecurityInfo::OverridableErrorCategory::ERROR_TRUST;
case static_cast<uint32_t>(
nsITransportSecurityInfo::OverridableErrorCategory::ERROR_DOMAIN):
return nsITransportSecurityInfo::OverridableErrorCategory::ERROR_DOMAIN;
case static_cast<uint32_t>(
nsITransportSecurityInfo::OverridableErrorCategory::ERROR_TIME):
return nsITransportSecurityInfo::OverridableErrorCategory::ERROR_TIME;
default:
break;
}
return nsITransportSecurityInfo::OverridableErrorCategory::ERROR_UNSET;
}
nsresult TransportSecurityInfo::Read(const nsCString& aSerializedSecurityInfo,
nsITransportSecurityInfo** aResult) {
*aResult = nullptr;
nsCString decodedSecurityInfo;
nsresult rv = Base64Decode(aSerializedSecurityInfo, decodedSecurityInfo);
if (NS_FAILED(rv)) {
return rv;
}
nsCOMPtr<nsIInputStream> inputStream;
rv = NS_NewCStringInputStream(getter_AddRefs(inputStream),
std::move(decodedSecurityInfo));
if (NS_FAILED(rv)) {
return rv;
}
nsCOMPtr<nsIObjectInputStream> objStream(
NS_NewObjectInputStream(inputStream));
if (!objStream) {
return rv;
}
nsCID cid;
rv = objStream->ReadID(&cid);
if (NS_FAILED(rv)) {
return rv;
}
if (!cid.Equals(kTransportSecurityInfoCID)) {
return NS_ERROR_UNEXPECTED;
}
nsIID iid;
rv = objStream->ReadID(&iid);
if (NS_FAILED(rv)) {
return rv;
}
if (!iid.Equals(NS_ISUPPORTS_IID)) {
return rv;
}
nsID id;
rv = objStream->ReadID(&id);
if (NS_FAILED(rv)) {
return rv;
}
if (!id.Equals(kTransportSecurityInfoMagic)) {
return NS_ERROR_UNEXPECTED;
}
uint32_t aSecurityState = 0;
PRErrorCode aErrorCode = 0;
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 = false;
bool aUsedPrivateDNS = false;
Maybe<bool> aIsEV;
bool aNPNCompleted = false;
nsCString aNegotiatedNPN;
bool aResumed = false;
bool aIsBuiltCertChainRootBuiltInRoot = false;
nsCString aPeerId;
rv = objStream->Read32(&aSecurityState);
if (NS_FAILED(rv)) {
return rv;
}
// mSubRequestsBrokenSecurity was removed in bug 748809
uint32_t unusedSubRequestsBrokenSecurity;
rv = objStream->Read32(&unusedSubRequestsBrokenSecurity);
if (NS_FAILED(rv)) {
return rv;
}
// mSubRequestsNoSecurity was removed in bug 748809
uint32_t unusedSubRequestsNoSecurity;
rv = objStream->Read32(&unusedSubRequestsNoSecurity);
if (NS_FAILED(rv)) {
return rv;
}
uint32_t errorCode;
rv = objStream->Read32(&errorCode);
if (NS_FAILED(rv)) {
return rv;
}
// PRErrorCode will be a negative value
aErrorCode = static_cast<PRErrorCode>(errorCode);
// Re-purpose mErrorMessageCached to represent serialization version
// If string doesn't match exact version it will be treated as older
// serialization.
nsAutoString serVersion;
rv = objStream->ReadString(serVersion);
if (NS_FAILED(rv)) {
return rv;
}
int32_t serVersionParsedToInt = 0;
if (!serVersion.IsEmpty()) {
char first = serVersion.First();
// Check whether the first character of serVersion is a number
// since ToInteger() skipps some non integer values.
if (first >= '0' && first <= '9') {
nsresult error = NS_OK;
serVersionParsedToInt = serVersion.ToInteger(&error);
if (NS_FAILED(error)) {
return error;
}
}
}
// moved from nsISSLStatus
if (serVersionParsedToInt < 1) {
// nsISSLStatus may be present
rv = ReadSSLStatus(objStream, aServerCert, aCipherSuite, aProtocolVersion,
aOverridableErrorCategory, aIsEV,
aCertificateTransparencyStatus, aKeaGroupName,
aSignatureSchemeName, aSucceededCertChain);
NS_ENSURE_SUCCESS(rv, rv);
} else {
nsCOMPtr<nsISupports> cert;
rv = NS_ReadOptionalObject(objStream, true, getter_AddRefs(cert));
NS_ENSURE_SUCCESS(rv, rv);
if (cert) {
aServerCert = do_QueryInterface(cert);
if (!aServerCert) {
return NS_NOINTERFACE;
}
}
uint16_t cipherSuite;
rv = objStream->Read16(&cipherSuite);
NS_ENSURE_SUCCESS(rv, rv);
uint16_t protocolVersion;
rv = objStream->Read16(&protocolVersion);
NS_ENSURE_SUCCESS(rv, rv);
OverridableErrorCategory overridableErrorCategory;
if (serVersionParsedToInt < 8) {
rv = ReadOldOverridableErrorBits(objStream, overridableErrorCategory);
NS_ENSURE_SUCCESS(rv, rv);
} else {
uint32_t overridableErrorCategoryInt;
rv = objStream->Read32(&overridableErrorCategoryInt);
NS_ENSURE_SUCCESS(rv, rv);
overridableErrorCategory =
IntToOverridableErrorCategory(overridableErrorCategoryInt);
}
bool isEV;
rv = objStream->ReadBoolean(&isEV);
NS_ENSURE_SUCCESS(rv, rv);
bool hasIsEVStatus;
rv = objStream->ReadBoolean(&hasIsEVStatus);
NS_ENSURE_SUCCESS(rv, rv);
if (hasIsEVStatus) {
aIsEV.emplace(isEV);
}
bool haveCipherSuiteAndProtocol;
rv = objStream->ReadBoolean(&haveCipherSuiteAndProtocol);
NS_ENSURE_SUCCESS(rv, rv);
if (haveCipherSuiteAndProtocol) {
aCipherSuite.emplace(cipherSuite);
aProtocolVersion.emplace(protocolVersion);
}
bool haveCertErrorBits;
rv = objStream->ReadBoolean(&haveCertErrorBits);
NS_ENSURE_SUCCESS(rv, rv);
if (haveCertErrorBits) {
aOverridableErrorCategory.emplace(overridableErrorCategory);
}
rv = objStream->Read16(&aCertificateTransparencyStatus);
NS_ENSURE_SUCCESS(rv, rv);
nsCString keaGroupName;
rv = objStream->ReadCString(keaGroupName);
NS_ENSURE_SUCCESS(rv, rv);
if (haveCipherSuiteAndProtocol) {
aKeaGroupName.emplace(keaGroupName);
}
nsCString signatureSchemeName;
rv = objStream->ReadCString(signatureSchemeName);
NS_ENSURE_SUCCESS(rv, rv);
if (haveCipherSuiteAndProtocol) {
aSignatureSchemeName.emplace(signatureSchemeName);
}
if (serVersionParsedToInt < 3) {
// The old data structure of certList(nsIX509CertList) presents
rv = ReadCertList(objStream, aSucceededCertChain);
NS_ENSURE_SUCCESS(rv, rv);
} else {
uint16_t certCount;
rv = objStream->Read16(&certCount);
NS_ENSURE_SUCCESS(rv, rv);
rv =
ReadCertificatesFromStream(objStream, certCount, aSucceededCertChain);
NS_ENSURE_SUCCESS(rv, rv);
}
}
// END moved from nsISSLStatus
if (serVersionParsedToInt < 3) {
// The old data structure of certList(nsIX509CertList) presents
rv = ReadCertList(objStream, aFailedCertChain);
NS_ENSURE_SUCCESS(rv, rv);
} else {
uint16_t certCount;
rv = objStream->Read16(&certCount);
NS_ENSURE_SUCCESS(rv, rv);
rv = ReadCertificatesFromStream(objStream, certCount, aFailedCertChain);
NS_ENSURE_SUCCESS(rv, rv);
}
// mIsDelegatedCredential added in bug 1562773
if (serVersionParsedToInt >= 2) {
bool isDelegatedCredential;
rv = objStream->ReadBoolean(&isDelegatedCredential);
if (NS_FAILED(rv)) {
return rv;
}
// If aCipherSuite is Some, the serialized TransportSecurityinfo had its
// cipher suite and protocol information, which means it has this
// information.
if (aCipherSuite.isSome()) {
aIsDelegatedCredential.emplace(isDelegatedCredential);
}
}
// mNPNCompleted, mNegotiatedNPN, mResumed added in bug 1584104
if (serVersionParsedToInt >= 4) {
rv = objStream->ReadBoolean(&aNPNCompleted);
if (NS_FAILED(rv)) {
return rv;
}
rv = objStream->ReadCString(aNegotiatedNPN);
if (NS_FAILED(rv)) {
return rv;
}
rv = objStream->ReadBoolean(&aResumed);
if (NS_FAILED(rv)) {
return rv;
}
}
// mIsBuiltCertChainRootBuiltInRoot added in bug 1485652
if (serVersionParsedToInt >= 5) {
rv = objStream->ReadBoolean(&aIsBuiltCertChainRootBuiltInRoot);
if (NS_FAILED(rv)) {
return rv;
}
}
// mIsAcceptedEch added in bug 1678079
if (serVersionParsedToInt >= 6) {
bool isAcceptedEch;
rv = objStream->ReadBoolean(&isAcceptedEch);
if (NS_FAILED(rv)) {
return rv;
}
// If aCipherSuite is Some, the serialized TransportSecurityinfo had its
// cipher suite and protocol information, which means it has this
// information.
if (aCipherSuite.isSome()) {
aIsAcceptedEch.emplace(isAcceptedEch);
}
}
// mPeerId added in bug 1738664
if (serVersionParsedToInt >= 7) {
rv = objStream->ReadCString(aPeerId);
if (NS_FAILED(rv)) {
return rv;
}
}
if (serVersionParsedToInt >= 9) {
rv = objStream->ReadBoolean(&aMadeOCSPRequests);
if (NS_FAILED(rv)) {
return rv;
}
rv = objStream->ReadBoolean(&aUsedPrivateDNS);
if (NS_FAILED(rv)) {
return rv;
};
}
RefPtr<nsITransportSecurityInfo> securityInfo(new TransportSecurityInfo(
aSecurityState, aErrorCode, std::move(aFailedCertChain), aServerCert,
std::move(aSucceededCertChain), aCipherSuite, aKeaGroupName,
aSignatureSchemeName, aProtocolVersion, aCertificateTransparencyStatus,
aIsAcceptedEch, aIsDelegatedCredential, aOverridableErrorCategory,
aMadeOCSPRequests, aUsedPrivateDNS, aIsEV, aNPNCompleted, aNegotiatedNPN,
aResumed, aIsBuiltCertChainRootBuiltInRoot, aPeerId));
securityInfo.forget(aResult);
return NS_OK;
}
void TransportSecurityInfo::SerializeToIPC(IPC::MessageWriter* aWriter) {
WriteParam(aWriter, mSecurityState);
WriteParam(aWriter, mErrorCode);
WriteParam(aWriter, mFailedCertChain);
WriteParam(aWriter, mServerCert);
WriteParam(aWriter, mSucceededCertChain);
WriteParam(aWriter, mCipherSuite);
WriteParam(aWriter, mKeaGroupName);
WriteParam(aWriter, mSignatureSchemeName);
WriteParam(aWriter, mProtocolVersion);
WriteParam(aWriter, mCertificateTransparencyStatus);
WriteParam(aWriter, mIsAcceptedEch);
WriteParam(aWriter, mIsDelegatedCredential);
WriteParam(aWriter, mOverridableErrorCategory);
WriteParam(aWriter, mMadeOCSPRequests);
WriteParam(aWriter, mUsedPrivateDNS);
WriteParam(aWriter, mIsEV);
WriteParam(aWriter, mNPNCompleted);
WriteParam(aWriter, mNegotiatedNPN);
WriteParam(aWriter, mResumed);
WriteParam(aWriter, mIsBuiltCertChainRootBuiltInRoot);
WriteParam(aWriter, mPeerId);
}
bool TransportSecurityInfo::DeserializeFromIPC(
IPC::MessageReader* aReader, RefPtr<nsITransportSecurityInfo>* aResult) {
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;
nsCString aNegotiatedNPN;
bool aResumed;
bool aIsBuiltCertChainRootBuiltInRoot;
nsCString aPeerId;
if (!ReadParam(aReader, &aSecurityState) ||
!ReadParam(aReader, &aErrorCode) ||
!ReadParam(aReader, &aFailedCertChain) ||
!ReadParam(aReader, &aServerCert) ||
!ReadParam(aReader, &aSucceededCertChain) ||
!ReadParam(aReader, &aCipherSuite) ||
!ReadParam(aReader, &aKeaGroupName) ||
!ReadParam(aReader, &aSignatureSchemeName) ||
!ReadParam(aReader, &aProtocolVersion) ||
!ReadParam(aReader, &aCertificateTransparencyStatus) ||
!ReadParam(aReader, &aIsAcceptedEch) ||
!ReadParam(aReader, &aIsDelegatedCredential) ||
!ReadParam(aReader, &aOverridableErrorCategory) ||
!ReadParam(aReader, &aMadeOCSPRequests) ||
!ReadParam(aReader, &aUsedPrivateDNS) || !ReadParam(aReader, &aIsEV) ||
!ReadParam(aReader, &aNPNCompleted) ||
!ReadParam(aReader, &aNegotiatedNPN) || !ReadParam(aReader, &aResumed) ||
!ReadParam(aReader, &aIsBuiltCertChainRootBuiltInRoot) ||
!ReadParam(aReader, &aPeerId)) {
return false;
}
RefPtr<nsITransportSecurityInfo> securityInfo(new TransportSecurityInfo(
aSecurityState, aErrorCode, std::move(aFailedCertChain), aServerCert,
std::move(aSucceededCertChain), aCipherSuite, aKeaGroupName,
aSignatureSchemeName, aProtocolVersion, aCertificateTransparencyStatus,
aIsAcceptedEch, aIsDelegatedCredential, aOverridableErrorCategory,
aMadeOCSPRequests, aUsedPrivateDNS, aIsEV, aNPNCompleted, aNegotiatedNPN,
aResumed, aIsBuiltCertChainRootBuiltInRoot, aPeerId));
*aResult = std::move(securityInfo);
return true;
}
NS_IMETHODIMP
TransportSecurityInfo::GetFailedCertChain(
nsTArray<RefPtr<nsIX509Cert>>& aFailedCertChain) {
MOZ_ASSERT(aFailedCertChain.IsEmpty());
if (!aFailedCertChain.IsEmpty()) {
return NS_ERROR_INVALID_ARG;
}
aFailedCertChain.AppendElements(mFailedCertChain);
return NS_OK;
}
NS_IMETHODIMP TransportSecurityInfo::GetServerCert(nsIX509Cert** aServerCert) {
NS_ENSURE_ARG_POINTER(aServerCert);
nsCOMPtr<nsIX509Cert> cert = mServerCert;
cert.forget(aServerCert);
return NS_OK;
}
NS_IMETHODIMP
TransportSecurityInfo::GetSucceededCertChain(
nsTArray<RefPtr<nsIX509Cert>>& aSucceededCertChain) {
MOZ_ASSERT(aSucceededCertChain.IsEmpty());
if (!aSucceededCertChain.IsEmpty()) {
return NS_ERROR_INVALID_ARG;
}
aSucceededCertChain.AppendElements(mSucceededCertChain);
return NS_OK;
}
NS_IMETHODIMP TransportSecurityInfo::GetIsBuiltCertChainRootBuiltInRoot(
bool* aIsBuiltInRoot) {
NS_ENSURE_ARG_POINTER(aIsBuiltInRoot);
*aIsBuiltInRoot = mIsBuiltCertChainRootBuiltInRoot;
return NS_OK;
}
NS_IMETHODIMP
TransportSecurityInfo::GetCipherName(nsACString& aCipherName) {
if (mCipherSuite.isNothing()) {
return NS_ERROR_NOT_AVAILABLE;
}
SSLCipherSuiteInfo cipherInfo;
if (SSL_GetCipherSuiteInfo(*mCipherSuite, &cipherInfo, sizeof(cipherInfo)) !=
SECSuccess) {
return NS_ERROR_FAILURE;
}
aCipherName.Assign(cipherInfo.cipherSuiteName);
return NS_OK;
}
NS_IMETHODIMP
TransportSecurityInfo::GetKeyLength(uint32_t* aKeyLength) {
NS_ENSURE_ARG_POINTER(aKeyLength);
if (mCipherSuite.isNothing()) {
return NS_ERROR_NOT_AVAILABLE;
}
SSLCipherSuiteInfo cipherInfo;
if (SSL_GetCipherSuiteInfo(*mCipherSuite, &cipherInfo, sizeof(cipherInfo)) !=
SECSuccess) {
return NS_ERROR_FAILURE;
}
*aKeyLength = cipherInfo.symKeyBits;
return NS_OK;
}
NS_IMETHODIMP
TransportSecurityInfo::GetSecretKeyLength(uint32_t* aSecretKeyLength) {
NS_ENSURE_ARG_POINTER(aSecretKeyLength);
if (mCipherSuite.isNothing()) {
return NS_ERROR_NOT_AVAILABLE;
}
SSLCipherSuiteInfo cipherInfo;
if (SSL_GetCipherSuiteInfo(*mCipherSuite, &cipherInfo, sizeof(cipherInfo)) !=
SECSuccess) {
return NS_ERROR_FAILURE;
}
*aSecretKeyLength = cipherInfo.effectiveKeyBits;
return NS_OK;
}
NS_IMETHODIMP
TransportSecurityInfo::GetKeaGroupName(nsACString& aKeaGroupName) {
if (mKeaGroupName.isNothing()) {
return NS_ERROR_NOT_AVAILABLE;
}
aKeaGroupName.Assign(*mKeaGroupName);
return NS_OK;
}
NS_IMETHODIMP
TransportSecurityInfo::GetSignatureSchemeName(nsACString& aSignatureScheme) {
if (mSignatureSchemeName.isNothing()) {
return NS_ERROR_NOT_AVAILABLE;
}
aSignatureScheme.Assign(*mSignatureSchemeName);
return NS_OK;
}
NS_IMETHODIMP
TransportSecurityInfo::GetProtocolVersion(uint16_t* aProtocolVersion) {
if (mProtocolVersion.isNothing()) {
return NS_ERROR_NOT_AVAILABLE;
}
*aProtocolVersion = *mProtocolVersion;
return NS_OK;
}
NS_IMETHODIMP
TransportSecurityInfo::GetCertificateTransparencyStatus(
uint16_t* aCertificateTransparencyStatus) {
NS_ENSURE_ARG_POINTER(aCertificateTransparencyStatus);
*aCertificateTransparencyStatus = mCertificateTransparencyStatus;
return NS_OK;
}
NS_IMETHODIMP
TransportSecurityInfo::GetMadeOCSPRequests(bool* aMadeOCSPRequests) {
*aMadeOCSPRequests = mMadeOCSPRequests;
return NS_OK;
}
NS_IMETHODIMP
TransportSecurityInfo::GetUsedPrivateDNS(bool* aUsedPrivateDNS) {
*aUsedPrivateDNS = mUsedPrivateDNS;
return NS_OK;
}
// static
uint16_t TransportSecurityInfo::ConvertCertificateTransparencyInfoToStatus(
const mozilla::psm::CertificateTransparencyInfo& info) {
using mozilla::ct::CTPolicyCompliance;
if (!info.enabled) {
// CT disabled.
return nsITransportSecurityInfo::CERTIFICATE_TRANSPARENCY_NOT_APPLICABLE;
}
switch (info.policyCompliance) {
case CTPolicyCompliance::Compliant:
return nsITransportSecurityInfo::
CERTIFICATE_TRANSPARENCY_POLICY_COMPLIANT;
case CTPolicyCompliance::NotEnoughScts:
return nsITransportSecurityInfo ::
CERTIFICATE_TRANSPARENCY_POLICY_NOT_ENOUGH_SCTS;
case CTPolicyCompliance::NotDiverseScts:
return nsITransportSecurityInfo ::
CERTIFICATE_TRANSPARENCY_POLICY_NOT_DIVERSE_SCTS;
case CTPolicyCompliance::Unknown:
default:
MOZ_ASSERT_UNREACHABLE("Unexpected CTPolicyCompliance type");
}
return nsITransportSecurityInfo::CERTIFICATE_TRANSPARENCY_NOT_APPLICABLE;
}
NS_IMETHODIMP
TransportSecurityInfo::GetOverridableErrorCategory(
OverridableErrorCategory* aOverridableErrorCategory) {
NS_ENSURE_ARG_POINTER(aOverridableErrorCategory);
if (mOverridableErrorCategory.isSome()) {
*aOverridableErrorCategory = *mOverridableErrorCategory;
} else {
*aOverridableErrorCategory = OverridableErrorCategory::ERROR_UNSET;
}
return NS_OK;
}
NS_IMETHODIMP
TransportSecurityInfo::GetIsExtendedValidation(bool* aIsEV) {
NS_ENSURE_ARG_POINTER(aIsEV);
*aIsEV = false;
// Never allow bad certs for EV, regardless of overrides.
if (mOverridableErrorCategory.isSome()) {
return NS_OK;
}
if (!mIsEV.isSome()) {
return NS_ERROR_NOT_AVAILABLE;
}
*aIsEV = *mIsEV;
return NS_OK;
}
NS_IMETHODIMP
TransportSecurityInfo::GetIsAcceptedEch(bool* aIsAcceptedEch) {
NS_ENSURE_ARG_POINTER(aIsAcceptedEch);
if (mIsAcceptedEch.isNothing()) {
return NS_ERROR_NOT_AVAILABLE;
}
*aIsAcceptedEch = *mIsAcceptedEch;
return NS_OK;
}
NS_IMETHODIMP
TransportSecurityInfo::GetIsDelegatedCredential(bool* aIsDelegatedCredential) {
NS_ENSURE_ARG_POINTER(aIsDelegatedCredential);
if (mIsDelegatedCredential.isNothing()) {
return NS_ERROR_NOT_AVAILABLE;
}
*aIsDelegatedCredential = *mIsDelegatedCredential;
return NS_OK;
}
NS_IMETHODIMP
TransportSecurityInfo::GetNegotiatedNPN(nsACString& aNegotiatedNPN) {
if (!mNPNCompleted) {
return NS_ERROR_NOT_CONNECTED;
}
aNegotiatedNPN = mNegotiatedNPN;
return NS_OK;
}
NS_IMETHODIMP
TransportSecurityInfo::GetResumed(bool* aResumed) {
NS_ENSURE_ARG_POINTER(aResumed);
*aResumed = mResumed;
return NS_OK;
}
NS_IMETHODIMP
TransportSecurityInfo::GetPeerId(nsACString& aResult) {
aResult.Assign(mPeerId);
return NS_OK;
}
} // namespace psm
} // namespace mozilla