bug 1006710 - add class of PSM errors to SEC and SSL errors r=briansmith

This commit is contained in:
David Keeler 2014-05-28 15:28:03 -07:00
parent d368714df5
commit 165f7dcea8
14 changed files with 136 additions and 84 deletions

View File

@ -6,7 +6,7 @@
#include "nsISupports.idl"
[scriptable, uuid(3a5c7a0f-f5da-4a8b-a748-d7c5a528f33b)]
[scriptable, uuid(dc8013f2-4aed-47a8-bc4e-9fd7a6cc60fa)]
interface nsINSSErrorsService : nsISupports
{
/**
@ -50,4 +50,16 @@ interface nsINSSErrorsService : nsISupports
const long NSS_SEC_ERROR_LIMIT = (NSS_SEC_ERROR_BASE + 1000);
const long NSS_SSL_ERROR_BASE = -(0x3000);
const long NSS_SSL_ERROR_LIMIT = (NSS_SSL_ERROR_BASE + 1000);
/**
* The error codes within each module must fit in 16 bits. We want these
* errors to fit in the same module as the NSS errors but not overlap with
* any of them. Converting an NSS SEC, NSS SSL, or PSM error to an NS error
* involves negating the value of the error and then synthesizing an error
* in the NS_ERROR_MODULE_SECURITY module. Hence, PSM errors will start at
* a negative value that both doesn't overlap with the current value
* ranges for NSS errors and that will fit in 16 bits when negated.
*/
const long PSM_ERROR_BASE = -(0x4000);
const long PSM_ERROR_LIMIT = (PSM_ERROR_BASE + 1000);
};

View File

@ -23,12 +23,12 @@
#include "plstr.h"
#include "prerr.h"
#include "NetworkActivityMonitor.h"
#include "NSSErrorsService.h"
#include "mozilla/VisualEventTracer.h"
#include "nsThreadUtils.h"
#include "nsISocketProviderService.h"
#include "nsISocketProvider.h"
#include "nsISSLSocketControl.h"
#include "nsINSSErrorsService.h"
#include "nsIPipe.h"
#include "nsIProgrammingLanguage.h"
#include "nsIClassInfoImpl.h"
@ -131,28 +131,6 @@ static PRErrorCode RandomizeConnectError(PRErrorCode code)
//-----------------------------------------------------------------------------
static bool
IsNSSErrorCode(PRErrorCode code)
{
return
((code >= nsINSSErrorsService::NSS_SEC_ERROR_BASE) &&
(code < nsINSSErrorsService::NSS_SEC_ERROR_LIMIT))
||
((code >= nsINSSErrorsService::NSS_SSL_ERROR_BASE) &&
(code < nsINSSErrorsService::NSS_SSL_ERROR_LIMIT));
}
// this logic is duplicated from the implementation of
// nsINSSErrorsService::getXPCOMFromNSSError
// It might have been better to implement that interface here...
static nsresult
GetXPCOMFromNSSError(PRErrorCode code)
{
// XXX Don't make up nsresults, it's supposed to be an enum (bug 778113)
return (nsresult)NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_SECURITY,
-1 * code);
}
nsresult
ErrorAccordingToNSPR(PRErrorCode errorCode)
{
@ -224,8 +202,9 @@ ErrorAccordingToNSPR(PRErrorCode errorCode)
rv = NS_ERROR_FILE_READ_ONLY;
break;
default:
if (IsNSSErrorCode(errorCode))
rv = GetXPCOMFromNSSError(errorCode);
if (mozilla::psm::IsNSSErrorCode(errorCode)) {
rv = mozilla::psm::GetXPCOMFromNSSError(errorCode);
}
break;
// NSPR's socket code can return these, but they're not worth breaking out

View File

@ -156,7 +156,7 @@ VerifyEntryContentDigest(nsIZipReader * zip, const nsACString & aFilename,
ScopedPK11Context digestContext(PK11_CreateDigestContext(SEC_OID_SHA1));
if (!digestContext) {
return PRErrorCode_to_nsresult(PR_GetError());
return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
}
rv = MapSECStatus(PK11_DigestBegin(digestContext));

View File

@ -8,15 +8,16 @@
#include <stdint.h>
#include "pkix/pkix.h"
#include "ExtendedValidation.h"
#include "NSSCertDBTrustDomain.h"
#include "NSSErrorsService.h"
#include "PublicKeyPinningService.h"
#include "cert.h"
#include "ocsp.h"
#include "secerr.h"
#include "pk11pub.h"
#include "pkix/pkix.h"
#include "prerror.h"
#include "secerr.h"
#include "sslerr.h"
// ScopedXXX in this file are mozilla::pkix::ScopedXXX, not
@ -269,10 +270,10 @@ ClassicVerifyCert(CERTCertificate* cert,
if (chainOK != PR_TRUE) {
if (verifyLog) {
insertErrorIntoVerifyLog(cert,
SEC_ERROR_APPLICATION_CALLBACK_ERROR,
PSM_ERROR_KEY_PINNING_FAILURE,
verifyLog);
}
PR_SetError(SEC_ERROR_APPLICATION_CALLBACK_ERROR, 0); // same as libpkix
PR_SetError(PSM_ERROR_KEY_PINNING_FAILURE, 0);
return SECFailure;
}
}

View File

@ -9,6 +9,7 @@
#include <stdint.h>
#include "ExtendedValidation.h"
#include "NSSErrorsService.h"
#include "OCSPRequestor.h"
#include "certdb.h"
#include "mozilla/Telemetry.h"
@ -461,7 +462,7 @@ NSSCertDBTrustDomain::IsChainValid(const CERTCertList* certChain) {
if (chainOK) {
return SECSuccess;
}
PR_SetError(SEC_ERROR_APPLICATION_CALLBACK_ERROR, 0);
PR_SetError(PSM_ERROR_KEY_PINNING_FAILURE, 0);
return SECFailure;
}

View File

@ -15,6 +15,33 @@
namespace mozilla {
namespace psm {
static const struct PRErrorMessage PSMErrorTableText[] = {
{ "PSM_ERROR_KEY_PINNING_FAILURE",
"The server uses key pinning (HPKP) but no trusted certificate chain "
"could be constructed that matches the pinset. Key pinning violations "
"cannot be overridden." }
};
static const struct PRErrorTable PSMErrorTable = {
PSMErrorTableText,
"psmerrors",
nsINSSErrorsService::PSM_ERROR_BASE,
PR_ARRAY_SIZE(PSMErrorTableText)
};
void
RegisterPSMErrorTable()
{
PR_ErrorInstallTable(&PSMErrorTable);
}
static bool
IsPSMError(PRErrorCode error)
{
return (error >= nsINSSErrorsService::PSM_ERROR_BASE &&
error < nsINSSErrorsService::PSM_ERROR_LIMIT);
}
NS_IMPL_ISUPPORTS(NSSErrorsService, nsINSSErrorsService)
nsresult
@ -50,32 +77,49 @@ NSSErrorsService::Init()
*/
#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_FAILURE;
if (!_retval) {
return NS_ERROR_INVALID_ARG;
}
*_retval = IS_SEC_ERROR(aNSPRCode) || IS_SSL_ERROR(aNSPRCode);
*_retval = mozilla::psm::IsNSSErrorCode(aNSPRCode);
return NS_OK;
}
NS_IMETHODIMP
NSSErrorsService::GetXPCOMFromNSSError(int32_t aNSPRCode, nsresult *aXPCOMErrorCode)
{
if (!IS_SEC_ERROR(aNSPRCode) && !IS_SSL_ERROR(aNSPRCode))
return NS_ERROR_FAILURE;
if (!aXPCOMErrorCode)
if (!aXPCOMErrorCode) {
return NS_ERROR_INVALID_ARG;
}
// The error codes within each module may be a 16 bit value.
// For simplicity let's use the positive value of the NSS code.
// XXX Don't make up nsresults, it's supposed to be an enum (bug 778113)
if (!mozilla::psm::IsNSSErrorCode(aNSPRCode)) {
return NS_ERROR_INVALID_ARG;
}
*aXPCOMErrorCode = mozilla::psm::GetXPCOMFromNSSError(aNSPRCode);
*aXPCOMErrorCode =
(nsresult)NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_SECURITY,
-1 * aNSPRCode);
return NS_OK;
}
@ -84,14 +128,16 @@ 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)
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 (!IS_SEC_ERROR(aNSPRCode) && !IS_SSL_ERROR(aNSPRCode))
if (!mozilla::psm::IsNSSErrorCode(aNSPRCode)) {
return NS_ERROR_FAILURE;
}
switch (aNSPRCode)
{
@ -116,14 +162,16 @@ NSSErrorsService::GetErrorClass(nsresult aXPCOMErrorCode, uint32_t *aErrorClass)
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)
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 (!IS_SEC_ERROR(aNSPRCode) && !IS_SSL_ERROR(aNSPRCode))
if (!mozilla::psm::IsNSSErrorCode(aNSPRCode)) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIStringBundle> theBundle = mPIPNSSBundle;
const char *id_str = nsNSSErrors::getOverrideErrorStringName(aNSPRCode);
@ -133,8 +181,9 @@ NSSErrorsService::GetErrorMessage(nsresult aXPCOMErrorCode, nsAString &aErrorMes
theBundle = mNSSErrorsBundle;
}
if (!id_str || !theBundle)
if (!id_str || !theBundle) {
return NS_ERROR_FAILURE;
}
nsAutoString msg;
nsresult rv =

View File

@ -2,15 +2,25 @@
* 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 "nsIStringBundle.h"
#include "nsCOMPtr.h"
#include "mozilla/Attributes.h"
#include "nsCOMPtr.h"
#include "nsIStringBundle.h"
#include "prerror.h"
namespace mozilla {
namespace psm {
enum PSMErrorCodes {
PSM_ERROR_KEY_PINNING_FAILURE = (nsINSSErrorsService::PSM_ERROR_BASE + 0)
};
void RegisterPSMErrorTable();
class NSSErrorsService MOZ_FINAL : public nsINSSErrorsService
{
NS_DECL_ISUPPORTS
@ -24,8 +34,13 @@ private:
nsCOMPtr<nsIStringBundle> mNSSErrorsBundle;
};
bool IsNSSErrorCode(PRErrorCode code);
nsresult GetXPCOMFromNSSError(PRErrorCode code);
} // psm
} // mozilla
#define NS_NSSERRORSSERVICE_CID \
{ 0x9ef18451, 0xa157, 0x4d17, { 0x81, 0x32, 0x47, 0xaf, 0xef, 0x21, 0x36, 0x89 } }
#endif // NSSErrorsService_h

View File

@ -7,6 +7,7 @@
#ifndef mozilla_ScopedNSSTypes_h
#define mozilla_ScopedNSSTypes_h
#include "NSSErrorsService.h"
#include "mozilla/Likely.h"
#include "mozilla/mozalloc_oom.h"
#include "mozilla/Scoped.h"
@ -42,37 +43,22 @@ inline const uint8_t *
uint8_t_ptr_cast(const char * p) { return reinterpret_cast<const uint8_t*>(p); }
// NSPR APIs use PRStatus/PR_GetError and NSS APIs use SECStatus/PR_GetError to
// report success/failure. These funtions make it more convenient and *safer*
// to translate NSPR/NSS results to nsresult. They are safer because they
// refuse to traslate any bad PRStatus/SECStatus into an NS_OK, even when the
// NSPR/NSS function forgot to call PR_SetError.
// IMPORTANT: This must be called immediately after the function that set the
// error code. Prefer using MapSECStatus to this.
inline nsresult
PRErrorCode_to_nsresult(PRErrorCode error)
{
if (!error) {
MOZ_CRASH("Function failed without calling PR_GetError");
}
// From NSSErrorsService::GetXPCOMFromNSSError
// XXX Don't make up nsresults, it's supposed to be an enum (bug 778113)
return (nsresult)NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_SECURITY,
-1 * error);
}
// report success/failure. This funtion makes it more convenient and *safer*
// to translate NSPR/NSS results to nsresult. It is safer because it
// refuses to traslate 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)
if (rv == SECSuccess) {
return NS_OK;
}
PRErrorCode error = PR_GetError();
return PRErrorCode_to_nsresult(error);
return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
}
// Alphabetical order by NSS type

View File

@ -9,6 +9,7 @@ EXPORTS += [
'nsCrypto.h',
'nsNSSShutDown.h',
'nsRandomGenerator.h',
'NSSErrorsService.h',
'ScopedNSSTypes.h',
]

View File

@ -51,6 +51,7 @@
#include "nsNSSShutDown.h"
#include "GeneratedEvents.h"
#include "SharedSSLState.h"
#include "NSSErrorsService.h"
#include "nss.h"
#include "ssl.h"
@ -1280,6 +1281,8 @@ nsNSSComponent::InitializeNSS()
LaunchSmartCardThreads();
#endif
RegisterPSMErrorTable();
PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("NSS Initialization done\n"));
return NS_OK;
}

View File

@ -39,6 +39,7 @@
#include "SharedSSLState.h"
#include "mozilla/Preferences.h"
#include "nsContentUtils.h"
#include "NSSErrorsService.h"
#include "ssl.h"
#include "sslproto.h"
@ -1096,7 +1097,7 @@ checkHandshake(int32_t bytesTransfered, bool wasReading,
// nsHandleSSLError, which has logic to avoid replacing the error message,
// so without the !socketInfo->GetErrorCode(), it would just be an
// expensive no-op.)
if (!wantRetry && (IS_SSL_ERROR(err) || IS_SEC_ERROR(err)) &&
if (!wantRetry && mozilla::psm::IsNSSErrorCode(err) &&
!socketInfo->GetErrorCode()) {
RefPtr<SyncRunnableBase> runnable(new SSLErrorRunnable(socketInfo,
PlainErrorMessage,

View File

@ -20,6 +20,7 @@ const isDebugBuild = Cc["@mozilla.org/xpcom/debug;1"]
const SEC_ERROR_BASE = Ci.nsINSSErrorsService.NSS_SEC_ERROR_BASE;
const SSL_ERROR_BASE = Ci.nsINSSErrorsService.NSS_SSL_ERROR_BASE;
const PSM_ERROR_BASE = Ci.nsINSSErrorsService.PSM_ERROR_BASE;
// Sort in numerical order
const SEC_ERROR_INVALID_ARGS = SEC_ERROR_BASE + 5; // -8187
@ -56,6 +57,8 @@ const SEC_ERROR_APPLICATION_CALLBACK_ERROR = SEC_ERROR_BASE + 178;
const SSL_ERROR_BAD_CERT_DOMAIN = SSL_ERROR_BASE + 12;
const PSM_ERROR_KEY_PINNING_FAILURE = PSM_ERROR_BASE + 0;
// Supported Certificate Usages
const certificateUsageSSLClient = 0x0001;
const certificateUsageSSLServer = 0x0002;

View File

@ -39,7 +39,7 @@ function test_strict() {
// Issued by otherCA, which is not in the pinset for pinning.example.com.
add_connection_test("bad.include-subdomains.pinning.example.com",
getXPCOMStatusFromNSS(SEC_ERROR_APPLICATION_CALLBACK_ERROR));
getXPCOMStatusFromNSS(PSM_ERROR_KEY_PINNING_FAILURE));
// These domains serve certs that match the pinset.
add_connection_test("include-subdomains.pinning.example.com", Cr.NS_OK);
@ -101,7 +101,7 @@ function test_enforce_test_mode() {
// Issued by otherCA, which is not in the pinset for pinning.example.com.
add_connection_test("bad.include-subdomains.pinning.example.com",
getXPCOMStatusFromNSS(SEC_ERROR_APPLICATION_CALLBACK_ERROR));
getXPCOMStatusFromNSS(PSM_ERROR_KEY_PINNING_FAILURE));
// These domains serve certs that match the pinset.
add_connection_test("include-subdomains.pinning.example.com", Cr.NS_OK);
@ -117,7 +117,7 @@ function test_enforce_test_mode() {
// bad.include-subdomains.pinning.example.com, is in test-mode, but we are
// enforcing test mode pins.
add_connection_test("test-mode.pinning.example.com",
getXPCOMStatusFromNSS(SEC_ERROR_APPLICATION_CALLBACK_ERROR));
getXPCOMStatusFromNSS(PSM_ERROR_KEY_PINNING_FAILURE));
}
function check_pinning_telemetry() {

View File

@ -16,6 +16,7 @@
#include "mozilla/ArrayUtils.h" // ArrayLength
#include "mozilla/Base64.h"
#include "ScopedNSSTypes.h"
#include "NSSErrorsService.h"
#include "nss.h"
#include "pk11pub.h"
@ -353,7 +354,7 @@ GenerateKeyPair(PK11SlotInfo * slot,
nullptr /*&pwdata*/);
if (!*privateKey) {
MOZ_ASSERT(!*publicKey);
return PRErrorCode_to_nsresult(PR_GetError());
return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
}
if (!*publicKey) {
SECKEY_DestroyPrivateKey(*privateKey);
@ -511,9 +512,9 @@ SignRunnable::Run()
SECItem sig = { siBuffer, nullptr, 0 };
int sigLength = PK11_SignatureLen(mPrivateKey);
if (sigLength <= 0) {
mRv = PRErrorCode_to_nsresult(PR_GetError());
mRv = mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
} else if (!SECITEM_AllocItem(nullptr, &sig, sigLength)) {
mRv = PRErrorCode_to_nsresult(PR_GetError());
mRv = mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
} else {
uint8_t hash[32]; // big enough for SHA-1 or SHA-256
SECOidTag hashAlg = mPrivateKey->keyType == dsaKey ? SEC_OID_SHA1