Bug 327181, Improve error reporting for invalid-certificate errors (error page for https, or combined dialog) Code in mozilla/security/manager: r=rrelyea Code elsewhere: r=mconnor, sr=dveditz blocking1.9=mconnor

This commit is contained in:
kaie@kuix.de 2007-10-03 04:43:54 -07:00
Родитель f9911aaf0d
Коммит 61d16d4cd9
20 изменённых файлов: 2005 добавлений и 546 удалений

Просмотреть файл

@ -865,6 +865,7 @@ sslt.h
smime.h
cms.h
sechash.h
secoidt.h
certdb.h
secerr.h
nssb64.h

Просмотреть файл

@ -311,20 +311,6 @@ static const nsModuleComponentInfo defaultAppComps[] = {
NS_TOKENPASSWORDSDIALOG_CONTRACTID,
EmbedCertificatesConstructor
},
{
EMBED_CERTIFICATES_DESCRIPTION,
EMBED_CERTIFICATES_CID,
NS_BADCERTLISTENER_CONTRACTID,
EmbedCertificatesConstructor
},
#ifdef BAD_CERT_LISTENER2
{
EMBED_CERTIFICATES_DESCRIPTION,
EMBED_CERTIFICATES_CID,
NS_BADCERTLISTENER2_CONTRACTID,
EmbedCertificatesConstructor
},
#endif
{
EMBED_CERTIFICATES_DESCRIPTION,
EMBED_CERTIFICATES_CID,

Просмотреть файл

@ -336,6 +336,12 @@ PSMERR_HostReusedIssuerSerial=You have received an invalid certificate. Please
SSLConnectionErrorPrefix=An error occurred during a connection to %S.
certErrorIntro=An error occurred during a connection to %S because it uses an invalid security certificate.
certErrorUntrusted=The certificate is not trusted or its issuer certificate is invalid.
certErrorMismatch=The certificate is not valid for domain name %S.
certErrorExpired=The certificate has expired on %S.
certErrorNotYetValid=The certificate will not be valid until %S.
CertInfoIssuedFor=Issued to:
CertInfoIssuedBy=Issued by:
CertInfoValid=Valid
@ -377,3 +383,5 @@ NotACACert=This is not a certificate authority certificate, so it can't be impor
NotImportingUnverifiedCert=This certificate can't be verified and will not be imported. The certificate issuer might be unknown or untrusted, the certificate might have expired or been revoked, or the certificate might not have been approved.
UserCertIgnoredNoPrivateKey=This personal certificate can't be installed because you do not own the corresponding private key which was created when the certificate was requested.
UserCertImported=Your personal certificate has been installed. You should keep a backup copy of this certificate.
CertOrgUnknown=(Unknown)
CertNotStored=(Not Stored)

Просмотреть файл

@ -93,6 +93,8 @@
<!ENTITY certmgr.deleteusercert.aftername "Once you have deleted this certificate, you will not be able to read mail that has been encrypted with it.">
<!ENTITY certmgr.certname "Certificate Name">
<!ENTITY certmgr.certsite "Site">
<!ENTITY certmgr.typesofoverrides "Exceptions">
<!ENTITY certmgr.tokenname "Security Device">
<!ENTITY certmgr.purpose "Purposes">
<!ENTITY certmgr.issued "Issued On">

Просмотреть файл

@ -22,6 +22,7 @@
- Contributor(s):
- Bob Lord <lord@netscape.com>
- Ian McGreer <mcgreer@netscape.com>
- Kai Engert <kengert@redhat.com>
-
- Alternatively, the contents of this file may be used under the terms of
- either the GNU General Public License Version 2 or later (the "GPL"), or
@ -55,6 +56,14 @@
<treecol id="certcol" label="&certmgr.certname;" primary="true"
persist="hidden width ordinal" flex="1"/>
<splitter class="tree-splitter"/>
<treecol id="sitecol" label="&certmgr.certsite;"
persist="hidden width ordinal" flex="1"/>
<splitter class="tree-splitter"/>
<!-- this is too geeky, leave out for now
<treecol id="overridetypecol" label="&certmgr.typesofoverrides;"
persist="hidden width ordinal" flex="1"/>
<splitter class="tree-splitter"/>
-->
<!-- disable the purposes column until we get a solution
to fill in this information that is compatible with OCSP
and does not block the whole interface

Просмотреть файл

@ -16,8 +16,6 @@ pippki.jar:
#ifndef MOZ_PHOENIX
content/pippki/PageInfoOverlay.xul (content/PageInfoOverlay.xul)
#endif
content/pippki/newserver.js (content/newserver.js)
content/pippki/newserver.xul (content/newserver.xul)
content/pippki/downloadcert.js (content/downloadcert.js)
content/pippki/downloadcert.xul (content/downloadcert.xul)
content/pippki/cacertexists.xul (content/cacertexists.xul)
@ -39,10 +37,6 @@ pippki.jar:
content/pippki/getp12password.xul (content/getp12password.xul)
content/pippki/setp12password.xul (content/setp12password.xul)
content/pippki/pippki.js (content/pippki.js)
content/pippki/domainMismatch.xul (content/domainMismatch.xul)
content/pippki/domainMismatch.js (content/domainMismatch.js)
content/pippki/serverCertExpired.xul (content/serverCertExpired.xul)
content/pippki/serverCertExpired.js (content/serverCertExpired.js)
content/pippki/clientauthask.xul (content/clientauthask.xul)
content/pippki/clientauthask.js (content/clientauthask.js)
content/pippki/certpicker.xul (content/certpicker.xul)

Просмотреть файл

@ -78,8 +78,7 @@ nsNSSDialogs::~nsNSSDialogs()
{
}
NS_IMPL_THREADSAFE_ISUPPORTS8(nsNSSDialogs, nsITokenPasswordDialogs,
nsIBadCertListener,
NS_IMPL_THREADSAFE_ISUPPORTS7(nsNSSDialogs, nsITokenPasswordDialogs,
nsICertificateDialogs,
nsIClientAuthDialogs,
nsICertPickDialogs,
@ -169,216 +168,6 @@ nsNSSDialogs::GetPassword(nsIInterfaceRequestor *ctx,
return rv;
}
NS_IMETHODIMP
nsNSSDialogs::ConfirmUnknownIssuer(nsIInterfaceRequestor *socketInfo,
nsIX509Cert *cert, PRInt16 *outAddType,
PRBool *_retval)
{
nsresult rv;
PRInt32 addType;
*_retval = PR_FALSE;
nsCOMPtr<nsIPKIParamBlock> block =
do_CreateInstance(NS_PKIPARAMBLOCK_CONTRACTID);
if (!block)
return NS_ERROR_FAILURE;
nsXPIDLString commonName;
rv = block->SetISupportAtIndex(1, cert);
if (NS_FAILED(rv))
return rv;
rv = nsNSSDialogHelper::openDialog(nsnull,
"chrome://pippki/content/newserver.xul",
block);
if (NS_FAILED(rv))
return rv;
PRInt32 status;
nsCOMPtr<nsIDialogParamBlock> dialogBlock = do_QueryInterface(block);
rv = dialogBlock->GetInt(1, &status);
if (NS_FAILED(rv))
return rv;
if (status == 0) {
*_retval = PR_FALSE;
} else {
// The user wants to continue, let's figure out
// what to do with this cert.
rv = dialogBlock->GetInt(2, &addType);
switch (addType) {
case 0:
*outAddType = ADD_TRUSTED_PERMANENTLY;
*_retval = PR_TRUE;
break;
case 1:
*outAddType = ADD_TRUSTED_FOR_SESSION;
*_retval = PR_TRUE;
break;
default:
*outAddType = UNINIT_ADD_FLAG;
*_retval = PR_FALSE;
break;
}
}
return NS_OK;
}
NS_IMETHODIMP
nsNSSDialogs::ConfirmMismatchDomain(nsIInterfaceRequestor *socketInfo,
const nsACString &targetURL,
nsIX509Cert *cert, PRBool *_retval)
{
nsresult rv;
*_retval = PR_FALSE;
nsCOMPtr<nsIPKIParamBlock> block =
do_CreateInstance(NS_PKIPARAMBLOCK_CONTRACTID);
if (!block)
return NS_ERROR_FAILURE;
nsCOMPtr<nsIDialogParamBlock> dialogBlock = do_QueryInterface(block);
rv = dialogBlock->SetString(1, NS_ConvertUTF8toUTF16(targetURL).get());
if (NS_FAILED(rv))
return rv;
rv = block->SetISupportAtIndex(1, cert);
if (NS_FAILED(rv))
return rv;
rv = nsNSSDialogHelper::openDialog(nsnull,
"chrome://pippki/content/domainMismatch.xul",
block);
if (NS_FAILED(rv))
return rv;
PRInt32 status;
rv = dialogBlock->GetInt(1, &status);
if (NS_FAILED(rv))
return rv;
*_retval = (status) ? PR_TRUE : PR_FALSE;
return NS_OK;
}
NS_IMETHODIMP
nsNSSDialogs::ConfirmCertExpired(nsIInterfaceRequestor *socketInfo,
nsIX509Cert *cert, PRBool *_retval)
{
nsresult rv;
PRTime now = PR_Now();
PRTime notAfter, notBefore, timeToUse;
nsCOMPtr<nsIX509CertValidity> validity;
const char *key;
const char *titleKey;
*_retval = PR_FALSE;
nsCOMPtr<nsIPKIParamBlock> block =
do_CreateInstance(NS_PKIPARAMBLOCK_CONTRACTID);
if (!block)
return NS_ERROR_FAILURE;
rv = cert->GetValidity(getter_AddRefs(validity));
if (NS_FAILED(rv))
return rv;
rv = validity->GetNotAfter(&notAfter);
if (NS_FAILED(rv))
return rv;
rv = validity->GetNotBefore(&notBefore);
if (NS_FAILED(rv))
return rv;
if (LL_CMP(now, >, notAfter)) {
key = "serverCertExpiredMsg1";
titleKey = "serverCertExpiredTitle";
timeToUse = notAfter;
} else {
key = "serverCertNotYetValedMsg1";
titleKey = "serverCertNotYetValidTitle";
timeToUse = notBefore;
}
nsXPIDLString message1;
nsXPIDLString title;
nsAutoString commonName;
nsAutoString formattedDate;
rv = cert->GetCommonName(commonName);
nsIDateTimeFormat *aDateTimeFormat;
rv = CallCreateInstance(NS_DATETIMEFORMAT_CONTRACTID, &aDateTimeFormat);
aDateTimeFormat->FormatPRTime(nsnull, kDateFormatShort,
kTimeFormatNoSeconds, timeToUse,
formattedDate);
const PRUnichar *formatStrings[2] = { commonName.get(), formattedDate.get() };
NS_ConvertASCIItoUTF16 keyString(key);
NS_ConvertASCIItoUTF16 titleKeyString(titleKey);
mPIPStringBundle->FormatStringFromName(keyString.get(), formatStrings,
2, getter_Copies(message1));
mPIPStringBundle->FormatStringFromName(titleKeyString.get(), formatStrings,
2, getter_Copies(title));
nsCOMPtr<nsIDialogParamBlock> dialogBlock = do_QueryInterface(block);
rv = dialogBlock->SetString(1,message1);
rv = dialogBlock->SetString(2,title);
if (NS_FAILED(rv))
return rv;
rv = block->SetISupportAtIndex(1, cert);
if (NS_FAILED(rv))
return rv;
rv = nsNSSDialogHelper::openDialog(nsnull,
"chrome://pippki/content/serverCertExpired.xul",
block);
PRInt32 status;
rv = dialogBlock->GetInt(1, &status);
if (NS_FAILED(rv))
return rv;
*_retval = (status) ? PR_TRUE : PR_FALSE;
return NS_OK;
}
NS_IMETHODIMP
nsNSSDialogs::NotifyCrlNextupdate(nsIInterfaceRequestor *socketInfo,
const nsACString &targetURL, nsIX509Cert *cert)
{
nsresult rv;
nsCOMPtr<nsIPKIParamBlock> block =
do_CreateInstance(NS_PKIPARAMBLOCK_CONTRACTID);
nsCOMPtr<nsIDialogParamBlock> dialogBlock = do_QueryInterface(block);
rv = dialogBlock->SetString(1, NS_ConvertUTF8toUTF16(targetURL).get());
if (NS_FAILED(rv))
return rv;
rv = block->SetISupportAtIndex(1, cert);
if (NS_FAILED(rv))
return rv;
rv = nsNSSDialogHelper::openDialog(nsnull,
"chrome://pippki/content/serverCrlNextupdate.xul",
block);
return NS_OK;
}
NS_IMETHODIMP
nsNSSDialogs::CrlImportStatusDialog(nsIInterfaceRequestor *ctx, nsICRLInfo *crl)
{
@ -769,5 +558,3 @@ nsNSSDialogs::ConfirmKeyEscrow(nsIX509Cert *escrowAuthority, PRBool *_retval)
}
return rv;
}

Просмотреть файл

@ -0,0 +1,65 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* Red Hat, Inc.
* Portions created by the Initial Developer are Copyright (C) 2007
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Kai Engert <kengert@redhat.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#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 cert The certificate that is not trusted and that is having the problem.
* @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);
};

Просмотреть файл

@ -0,0 +1,144 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* Red Hat, Inc.
* Portions created by the Initial Developer are Copyright (C) 2006
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Kai Engert <kengert@redhat.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#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(7e646227-485d-49c6-8e37-e2c62cb204e1)]
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.
*
* @param aHostNameWithPort The host:port this mapping belongs to
* @param aCert The cert that should always be accepted
* @param aOverrideBits The errors we want to be overriden
*/
void rememberValidityOverride(in AString aHostNameWithPort,
in nsIX509Cert aCert,
in PRUint32 aOverrideBits);
/**
* 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.
*
* @param aHostNameWithPort The host:port this mapping belongs to
* @param aCert The cert that should always be accepted
* @param aOverrideBits The errors that are currently overriden
* @return whether an override entry for aHostNameWithPort is currently on file
* that matches the given certificate
*/
boolean hasMatchingOverride(in AString aHostNameWithPort,
in nsIX509Cert aCert,
out PRUint32 aOverrideBits);
/**
* Retrieve the stored override for the given hostname:port.
*
* @param aHostNameWithPort The host:port whose entry should be tested
* @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 AString aHostNameWithPort,
out ACString aHashAlg,
out ACString aFingerprint,
out PRUint32 aOverrideBits);
/**
* Remove a stored override for the given hostname:port.
*
* @param aHostNameWithPort The host:port whose entry should be cleared.
*/
void clearValidityOverride(in AString aHostNameWithPort);
/**
* Obtain the full list of hostname:port for which overrides are stored.
*
* @param aCount The number of host:port entries returned
* @param aHostsWithPortsArray The array of host:port entries returned
*/
void getAllOverrideHostsWithPorts(out PRUint32 aCount,
[array, size_is(aCount)] out wstring aHostsWithPortsArray);
/**
* 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
*/
PRUint32 isCertUsedForOverrides(in nsIX509Cert aCert);
};

Просмотреть файл

@ -37,10 +37,9 @@
*
* ***** END LICENSE BLOCK ***** */
#include "nsISupports.idl"
#include "nsIX509Cert.idl"
interface nsIArray;
interface nsIX509CertValidity;
interface nsIASN1Object;
%{ C++
@ -52,8 +51,8 @@ interface nsIASN1Object;
/**
* This represents additional interfaces to X.509 certificates
*/
[scriptable, uuid(648f0d58-eedf-4b45-9174-3b92fb1fc06d)]
interface nsIX509Cert2 : nsISupports {
[scriptable, uuid(5b62c61c-f898-4dab-8ace-51109bb459b4)]
interface nsIX509Cert2 : nsIX509Cert {
/**
* Additional constants to classify the type of a certificate.
*/

Просмотреть файл

@ -35,19 +35,15 @@
*
* ***** END LICENSE BLOCK ***** */
#include "nsISupports.idl"
#include "nsIX509Cert2.idl"
interface nsIX509Cert;
interface nsICertVerificationListener;
/**
* Extending nsIX509Cert
*
* TODO: nsIX509Cert3 should be derived from nsIX509Cert2
* (and nsIX509Cert2 derived from nsIX509Cert)
*/
[scriptable, uuid(89d9f248-1160-4935-9f99-2bdbf67b5849)]
interface nsIX509Cert3 : nsISupports {
[scriptable, uuid(1362ffab-a683-4504-8038-25ce63b45370)]
interface nsIX509Cert3 : nsIX509Cert2 {
/**
* Constants for specifying the chain mode when exporting a certificate

Просмотреть файл

@ -0,0 +1,920 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* Red Hat, Inc.
* Portions created by the Initial Developer are Copyright (C) 2006
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Kai Engert <kengert@redhat.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsCertOverrideService.h"
#include "nsIX509Cert.h"
#include "nsNSSCertificate.h"
#include "nsCRT.h"
#include "nsAppDirectoryServiceDefs.h"
#include "nsStreamUtils.h"
#include "nsNetUtil.h"
#include "nsILineInputStream.h"
#include "nsIObserver.h"
#include "nsIObserverService.h"
#include "nsPromiseFlatString.h"
#include "nsStringBuffer.h"
#include "nsAutoLock.h"
#include "nsAutoPtr.h"
#include "nspr.h"
#include "pk11pub.h"
#include "certdb.h"
#include "sechash.h"
#include "nsNSSCleaner.h"
NSSCleanupAutoPtrClass(CERTCertificate, CERT_DestroyCertificate)
static const char kCertOverrideFileName[] = "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_THREADSAFE_ISUPPORTS2(nsCertOverrideService,
nsICertOverrideService,
nsIObserver)
nsCertOverrideService::nsCertOverrideService()
{
monitor = PR_NewMonitor();
}
nsCertOverrideService::~nsCertOverrideService()
{
if (monitor)
PR_DestroyMonitor(monitor);
}
nsresult
nsCertOverrideService::Init()
{
if (!mSettingsTable.Init())
return NS_ERROR_OUT_OF_MEMORY;
mOidTagForStoringNewHashes = SEC_OID_SHA256;
SECOidData *od = SECOID_FindOIDByTag(mOidTagForStoringNewHashes);
if (!od)
return NS_ERROR_FAILURE;
char *dotted_oid = CERT_GetOidString(&od->oid);
if (!dotted_oid)
return NS_ERROR_FAILURE;
mDottedOidForStoringNewHashes = dotted_oid;
PR_smprintf_free(dotted_oid);
// cache mSettingsFile
NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(mSettingsFile));
if (mSettingsFile) {
mSettingsFile->AppendNative(NS_LITERAL_CSTRING(kCertOverrideFileName));
}
Read();
nsCOMPtr<nsIObserverService> mObserverService =
do_GetService("@mozilla.org/observer-service;1");
if (mObserverService) {
mObserverService->AddObserver(this, "profile-before-change", PR_TRUE);
mObserverService->AddObserver(this, "profile-do-change", PR_TRUE);
mObserverService->AddObserver(this, "shutdown-cleanse", PR_TRUE);
}
return NS_OK;
}
NS_IMETHODIMP
nsCertOverrideService::Observe(nsISupports *aSubject,
const char *aTopic,
const PRUnichar *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.
nsAutoMonitor lock(monitor);
if (!nsCRT::strcmp(aData, NS_LITERAL_STRING("shutdown-cleanse").get())) {
RemoveAllFromMemory();
// delete the storage file
if (mSettingsFile) {
mSettingsFile->Remove(PR_FALSE);
}
} else {
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
nsAutoMonitor lock(monitor);
nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(mSettingsFile));
if (NS_SUCCEEDED(rv)) {
mSettingsFile->AppendNative(NS_LITERAL_CSTRING(kCertOverrideFileName));
}
Read();
}
return NS_OK;
}
void
nsCertOverrideService::RemoveAllFromMemory()
{
nsAutoMonitor lock(monitor);
mSettingsTable.Clear();
}
nsresult
nsCertOverrideService::Read()
{
nsAutoMonitor lock(monitor);
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;
}
nsCAutoString buffer;
PRBool isMore = PR_TRUE;
PRInt32 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 &host = 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);
nsCertOverride::OverrideBits bits;
nsCertOverride::convertStringToBits(bits_string, bits);
AddEntryToList(host, algo_string, fingerprint, bits, db_key);
}
return NS_OK;
}
PR_STATIC_CALLBACK(PLDHashOperator)
WriteEntryCallback(nsCertOverrideEntry *aEntry,
void *aArg)
{
static const char kNew[] = "\n";
static const char kTab[] = "\t";
nsIOutputStream *rawStreamPtr = (nsIOutputStream *)aArg;
nsresult rv;
if (rawStreamPtr && aEntry)
{
const nsCertOverride &settings = aEntry->mSettings;
nsCAutoString bits_string;
nsCertOverride::convertBitsToString(settings.mOverrideBits,
bits_string);
rawStreamPtr->Write(settings.mHostWithPortUTF8.get(), settings.mHostWithPortUTF8.Length(), &rv);
rawStreamPtr->Write(kTab, sizeof(kTab) - 1, &rv);
rawStreamPtr->Write(settings.mFingerprintAlgOID.get(),
settings.mFingerprintAlgOID.Length(), &rv);
rawStreamPtr->Write(kTab, sizeof(kTab) - 1, &rv);
rawStreamPtr->Write(settings.mFingerprint.get(),
settings.mFingerprint.Length(), &rv);
rawStreamPtr->Write(kTab, sizeof(kTab) - 1, &rv);
rawStreamPtr->Write(bits_string.get(),
bits_string.Length(), &rv);
rawStreamPtr->Write(kTab, sizeof(kTab) - 1, &rv);
rawStreamPtr->Write(settings.mDBKey.get(), settings.mDBKey.Length(), &rv);
rawStreamPtr->Write(kNew, sizeof(kNew) - 1, &rv);
}
return PL_DHASH_NEXT;
}
nsresult
nsCertOverrideService::Write()
{
nsAutoMonitor lock(monitor);
if (!mSettingsFile) {
return NS_ERROR_NULL_POINTER;
}
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\n"
"# This is a generated file! Do not edit.\n";
/* see ::Read for file format */
bufferedOutputStream->Write(kHeader, sizeof(kHeader) - 1, &rv);
nsIOutputStream *rawStreamPtr = bufferedOutputStream;
mSettingsTable.EnumerateEntries(WriteEntryCallback, rawStreamPtr);
// 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(CERTCertificate* nsscert,
SECOidTag aOidTag,
nsCString &fp)
{
unsigned int hash_len = HASH_ResultLenByOidTag(aOidTag);
nsRefPtr<nsStringBuffer> fingerprint = nsStringBuffer::Alloc(hash_len);
if (!fingerprint)
return NS_ERROR_OUT_OF_MEMORY;
PK11_HashBuf(aOidTag, (unsigned char*)fingerprint->Data(),
nsscert->derCert.data, nsscert->derCert.len);
SECItem fpItem;
fpItem.data = (unsigned char*)fingerprint->Data();
fpItem.len = hash_len;
fp.Adopt(CERT_Hexify(&fpItem, 1));
return NS_OK;
}
static nsresult
GetCertFingerprintByOidTag(nsIX509Cert *aCert,
SECOidTag aOidTag,
nsCString &fp)
{
nsCOMPtr<nsIX509Cert2> cert2 = do_QueryInterface(aCert);
if (!cert2)
return NS_ERROR_FAILURE;
CERTCertificate* nsscert = cert2->GetCert();
if (!nsscert)
return NS_ERROR_FAILURE;
CERTCertificateCleaner nsscertCleaner(nsscert);
return GetCertFingerprintByOidTag(nsscert, aOidTag, fp);
}
#include <string.h>
#include "secitem.h"
#include "secport.h"
#include "secerr.h"
// FIXME: This is a temporary copy of NSS function SEC_StringToOID,
// already available on NSS trunk, but not yet delivered to
// the client application. Remove this function and the include
// statements after a new tag landed with bug 397296.
static SECStatus
_psm_copy_SEC_StringToOID(PLArenaPool *pool, SECItem *to, const char *from, PRUint32 len)
{
PRUint32 decimal_numbers = 0;
PRUint32 result_bytes = 0;
SECStatus rv;
PRUint8 result[1024];
static const PRUint32 max_decimal = (0xffffffff / 10);
static const char OIDstring[] = {"OID."};
if (!from || !to) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure;
}
if (!len) {
len = PL_strlen(from);
}
if (len >= 4 && !PL_strncasecmp(from, OIDstring, 4)) {
from += 4; /* skip leading "OID." if present */
len -= 4;
}
if (!len) {
bad_data:
PORT_SetError(SEC_ERROR_BAD_DATA);
return SECFailure;
}
do {
PRUint32 decimal = 0;
while (len > 0 && isdigit(*from)) {
PRUint32 addend = (*from++ - '0');
--len;
if (decimal > max_decimal) /* overflow */
goto bad_data;
decimal = (decimal * 10) + addend;
if (decimal < addend) /* overflow */
goto bad_data;
}
if (len != 0 && *from != '.') {
goto bad_data;
}
if (decimal_numbers == 0) {
if (decimal > 2)
goto bad_data;
result[0] = decimal * 40;
result_bytes = 1;
} else if (decimal_numbers == 1) {
if (decimal > 40)
goto bad_data;
result[0] += decimal;
} else {
/* encode the decimal number, */
PRUint8 * rp;
PRUint32 num_bytes = 0;
PRUint32 tmp = decimal;
while (tmp) {
num_bytes++;
tmp >>= 7;
}
if (!num_bytes )
++num_bytes; /* use one byte for a zero value */
if (num_bytes + result_bytes > sizeof result)
goto bad_data;
tmp = num_bytes;
rp = result + result_bytes - 1;
rp[tmp] = (PRUint8)(decimal & 0x7f);
decimal >>= 7;
while (--tmp > 0) {
rp[tmp] = (PRUint8)(decimal | 0x80);
decimal >>= 7;
}
result_bytes += num_bytes;
}
++decimal_numbers;
if (len > 0) { /* skip trailing '.' */
++from;
--len;
}
} while (len > 0);
/* now result contains result_bytes of data */
if (to->data && to->len >= result_bytes) {
PORT_Memcpy(to->data, result, to->len = result_bytes);
rv = SECSuccess;
} else {
SECItem result_item = {siBuffer, NULL, 0 };
result_item.data = result;
result_item.len = result_bytes;
rv = SECITEM_CopyItem(pool, to, &result_item);
}
return rv;
}
static nsresult
GetCertFingerprintByDottedOidString(CERTCertificate* nsscert,
const nsCString &dottedOid,
nsCString &fp)
{
SECItem oid;
oid.data = nsnull;
oid.len = 0;
SECStatus srv = _psm_copy_SEC_StringToOID(nsnull, &oid,
dottedOid.get(), dottedOid.Length());
if (srv != SECSuccess)
return NS_ERROR_FAILURE;
SECOidTag oid_tag = SECOID_FindOIDTag(&oid);
SECITEM_FreeItem(&oid, PR_FALSE);
if (oid_tag == SEC_OID_UNKNOWN)
return NS_ERROR_FAILURE;
return GetCertFingerprintByOidTag(nsscert, oid_tag, fp);
}
static nsresult
GetCertFingerprintByDottedOidString(nsIX509Cert *aCert,
const nsCString &dottedOid,
nsCString &fp)
{
nsCOMPtr<nsIX509Cert2> cert2 = do_QueryInterface(aCert);
if (!cert2)
return NS_ERROR_FAILURE;
CERTCertificate* nsscert = cert2->GetCert();
if (!nsscert)
return NS_ERROR_FAILURE;
CERTCertificateCleaner nsscertCleaner(nsscert);
return GetCertFingerprintByDottedOidString(nsscert, dottedOid, fp);
}
NS_IMETHODIMP
nsCertOverrideService::RememberValidityOverride(const nsAString & aHostNameWithPort,
nsIX509Cert *aCert,
PRUint32 aOverrideBits)
{
NS_ENSURE_ARG_POINTER(aCert);
if (aHostNameWithPort.IsEmpty())
return NS_ERROR_INVALID_ARG;
nsCOMPtr<nsIX509Cert2> cert2 = do_QueryInterface(aCert);
if (!cert2)
return NS_ERROR_FAILURE;
CERTCertificate* nsscert = cert2->GetCert();
if (!nsscert)
return NS_ERROR_FAILURE;
CERTCertificateCleaner nsscertCleaner(nsscert);
nsCAutoString nickname;
nickname = nsNSSCertificate::defaultServerNickname(nsscert);
if (nickname.IsEmpty())
return NS_ERROR_FAILURE;
PK11SlotInfo *slot = PK11_GetInternalKeySlot();
if (!slot)
return NS_ERROR_FAILURE;
SECStatus srv = PK11_ImportCert(slot, nsscert, CK_INVALID_HANDLE,
const_cast<char*>(nickname.get()), PR_FALSE);
PK11_FreeSlot(slot);
if (srv != SECSuccess)
return NS_ERROR_FAILURE;
nsCString myHostPort;
myHostPort = NS_ConvertUTF16toUTF8(aHostNameWithPort);
PRInt32 find_colon = myHostPort.FindChar(':');
if (find_colon == -1) {
myHostPort.AppendLiteral(":443");
}
nsCAutoString fpStr;
nsresult rv = GetCertFingerprintByOidTag(nsscert,
mOidTagForStoringNewHashes, fpStr);
if (NS_FAILED(rv))
return rv;
char *dbkey = NULL;
rv = aCert->GetDbKey(&dbkey);
if (NS_FAILED(rv) || !dbkey)
return rv;
// change \n and \r to spaces in the possibly multi-line-base64-encoded key
for (char *dbkey_walk = dbkey;
*dbkey_walk;
++dbkey_walk) {
char c = *dbkey_walk;
if (c == '\r' || c == '\n') {
*dbkey_walk = ' ';
}
}
{
nsAutoMonitor lock(monitor);
AddEntryToList(myHostPort, mDottedOidForStoringNewHashes, fpStr,
(nsCertOverride::OverrideBits)aOverrideBits,
nsDependentCString(dbkey));
Write();
}
PR_Free(dbkey);
return NS_OK;
}
NS_IMETHODIMP
nsCertOverrideService::HasMatchingOverride(const nsAString & aHostNameWithPort,
nsIX509Cert *aCert,
PRUint32 *aOverrideBits,
PRBool *_retval)
{
if (aHostNameWithPort.IsEmpty())
return NS_ERROR_INVALID_ARG;
NS_ENSURE_ARG_POINTER(aCert);
NS_ENSURE_ARG_POINTER(aOverrideBits);
NS_ENSURE_ARG_POINTER(_retval);
*_retval = PR_FALSE;
*aOverrideBits = nsCertOverride::ob_None;
NS_ConvertUTF16toUTF8 hp8(aHostNameWithPort);
nsCertOverride settings;
{
nsAutoMonitor lock(monitor);
nsCertOverrideEntry *entry = mSettingsTable.GetEntry(hp8.get());
if (!entry)
return NS_OK;
settings = entry->mSettings; // copy
}
*aOverrideBits = settings.mOverrideBits;
nsCAutoString fpStr;
nsresult rv;
if (settings.mFingerprintAlgOID.Equals(mDottedOidForStoringNewHashes)) {
rv = GetCertFingerprintByOidTag(aCert, mOidTagForStoringNewHashes, fpStr);
}
else {
rv = GetCertFingerprintByDottedOidString(aCert, settings.mFingerprintAlgOID, fpStr);
}
if (NS_FAILED(rv))
return rv;
*_retval = settings.mFingerprint.Equals(fpStr);
return NS_OK;
}
NS_IMETHODIMP
nsCertOverrideService::GetValidityOverride(const nsAString & aHostNameWithPort,
nsACString & aHashAlg,
nsACString & aFingerprint,
PRUint32 *aOverrideBits,
PRBool *_found)
{
NS_ENSURE_ARG_POINTER(_found);
NS_ENSURE_ARG_POINTER(aOverrideBits);
*_found = PR_FALSE;
*aOverrideBits = nsCertOverride::ob_None;
NS_ConvertUTF16toUTF8 hp8(aHostNameWithPort);
nsCertOverride settings;
{
nsAutoMonitor lock(monitor);
nsCertOverrideEntry *entry = mSettingsTable.GetEntry(hp8.get());
if (entry) {
*_found = PR_TRUE;
settings = entry->mSettings; // copy
}
}
if (*_found) {
*aOverrideBits = settings.mOverrideBits;
aFingerprint = settings.mFingerprint;
aHashAlg = settings.mFingerprintAlgOID;
}
return NS_OK;
}
nsresult
nsCertOverrideService::AddEntryToList(const nsACString &hostWithPortUTF8,
const nsACString &fingerprintAlgOID,
const nsACString &fingerprint,
nsCertOverride::OverrideBits ob,
const nsACString &dbKey)
{
const nsPromiseFlatCString &flat = PromiseFlatCString(hostWithPortUTF8);
{
nsAutoMonitor lock(monitor);
nsCertOverrideEntry *entry = mSettingsTable.PutEntry(flat.get());
if (!entry) {
NS_ERROR("can't insert a null entry!");
return NS_ERROR_OUT_OF_MEMORY;
}
nsCertOverride &settings = entry->mSettings;
settings.mHostWithPortUTF8 = hostWithPortUTF8;
settings.mFingerprintAlgOID = fingerprintAlgOID;
settings.mFingerprint = fingerprint;
settings.mOverrideBits = ob;
settings.mDBKey = dbKey;
}
return NS_OK;
}
NS_IMETHODIMP
nsCertOverrideService::ClearValidityOverride(const nsAString & aHostNameWithPort)
{
NS_ConvertUTF16toUTF8 hp8(aHostNameWithPort);
{
nsAutoMonitor lock(monitor);
mSettingsTable.RemoveEntry(hp8.get());
Write();
}
return NS_OK;
}
NS_IMETHODIMP
nsCertOverrideService::GetAllOverrideHostsWithPorts(PRUint32 *aCount,
PRUnichar ***aHostsWithPortsArray)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
static PRBool
matchesDBKey(nsIX509Cert *cert, const char *match_dbkey)
{
char *dbkey = NULL;
nsresult rv = cert->GetDbKey(&dbkey);
if (NS_FAILED(rv) || !dbkey)
return PR_FALSE;
PRBool found_mismatch = PR_FALSE;
const char *key1 = dbkey;
const char *key2 = match_dbkey;
// skip over any whitespace when comparing
while (*key1 && *key2) {
char c1 = *key1;
char c2 = *key2;
switch (c1) {
case ' ':
case '\t':
case '\n':
case '\r':
++key1;
continue;
}
switch (c2) {
case ' ':
case '\t':
case '\n':
case '\r':
++key2;
continue;
}
if (c1 != c2) {
found_mismatch = PR_TRUE;
break;
}
++key1;
++key2;
}
PR_Free(dbkey);
return !found_mismatch;
}
struct nsCertAndInt
{
nsIX509Cert *cert;
PRUint32 counter;
SECOidTag mOidTagForStoringNewHashes;
nsCString mDottedOidForStoringNewHashes;
};
PR_STATIC_CALLBACK(PLDHashOperator)
FindMatchingCertCallback(nsCertOverrideEntry *aEntry,
void *aArg)
{
nsCertAndInt *cai = (nsCertAndInt *)aArg;
if (cai && aEntry)
{
const nsCertOverride &settings = aEntry->mSettings;
if (matchesDBKey(cai->cert, settings.mDBKey.get())) {
nsCAutoString cert_fingerprint;
nsresult rv;
if (settings.mFingerprintAlgOID.Equals(cai->mDottedOidForStoringNewHashes)) {
rv = GetCertFingerprintByOidTag(cai->cert,
cai->mOidTagForStoringNewHashes, cert_fingerprint);
}
else {
rv = GetCertFingerprintByDottedOidString(cai->cert,
settings.mFingerprintAlgOID, cert_fingerprint);
}
if (NS_SUCCEEDED(rv) &&
settings.mFingerprint.Equals(cert_fingerprint)) {
cai->counter++;
}
}
}
return PL_DHASH_NEXT;
}
NS_IMETHODIMP
nsCertOverrideService::IsCertUsedForOverrides(nsIX509Cert *aCert,
PRUint32 *_retval)
{
NS_ENSURE_ARG(aCert);
NS_ENSURE_ARG(_retval);
nsCertAndInt cai;
cai.cert = aCert;
cai.counter = 0;
cai.mOidTagForStoringNewHashes = mOidTagForStoringNewHashes;
cai.mDottedOidForStoringNewHashes = mDottedOidForStoringNewHashes;
{
nsAutoMonitor lock(monitor);
mSettingsTable.EnumerateEntries(FindMatchingCertCallback, &cai);
}
*_retval = cai.counter;
return NS_OK;
}
struct nsCertAndPointerAndCallback
{
nsIX509Cert *cert;
void *userdata;
nsCertOverrideService::CertOverrideEnumerator enumerator;
SECOidTag mOidTagForStoringNewHashes;
nsCString mDottedOidForStoringNewHashes;
};
PR_STATIC_CALLBACK(PLDHashOperator)
EnumerateCertOverridesCallback(nsCertOverrideEntry *aEntry,
void *aArg)
{
nsCertAndPointerAndCallback *capac = (nsCertAndPointerAndCallback *)aArg;
if (capac && aEntry)
{
const nsCertOverride &settings = aEntry->mSettings;
if (!capac->cert) {
(*capac->enumerator)(settings, capac->userdata);
}
else {
if (matchesDBKey(capac->cert, settings.mDBKey.get())) {
nsCAutoString cert_fingerprint;
nsresult rv;
if (settings.mFingerprintAlgOID.Equals(capac->mDottedOidForStoringNewHashes)) {
rv = GetCertFingerprintByOidTag(capac->cert,
capac->mOidTagForStoringNewHashes, cert_fingerprint);
}
else {
rv = GetCertFingerprintByDottedOidString(capac->cert,
settings.mFingerprintAlgOID, cert_fingerprint);
}
if (NS_SUCCEEDED(rv) &&
settings.mFingerprint.Equals(cert_fingerprint)) {
(*capac->enumerator)(settings, capac->userdata);
}
}
}
}
return PL_DHASH_NEXT;
}
nsresult
nsCertOverrideService::EnumerateCertOverrides(nsIX509Cert *aCert,
CertOverrideEnumerator enumerator,
void *aUserData)
{
nsCertAndPointerAndCallback capac;
capac.cert = aCert;
capac.userdata = aUserData;
capac.enumerator = enumerator;
capac.mOidTagForStoringNewHashes = mOidTagForStoringNewHashes;
capac.mDottedOidForStoringNewHashes = mDottedOidForStoringNewHashes;
{
nsAutoMonitor lock(monitor);
mSettingsTable.EnumerateEntries(EnumerateCertOverridesCallback, &capac);
}
return NS_OK;
}

Просмотреть файл

@ -0,0 +1,199 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* Red Hat, Inc.
* Portions created by the Initial Developer are Copyright (C) 2006
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Kai Engert <kengert@redhat.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef __NSCERTOVERRIDESERVICE_H__
#define __NSCERTOVERRIDESERVICE_H__
#include "nsICertOverrideService.h"
#include "nsTHashtable.h"
#include "nsIObserver.h"
#include "nsString.h"
#include "nsIFile.h"
#include "prmon.h"
#include "secoidt.h"
class nsCertOverride
{
public:
enum OverrideBits { ob_None=0, ob_Untrusted=1, ob_Mismatch=2,
ob_Time_error=4 };
nsCertOverride()
:mOverrideBits(ob_None)
{
}
nsCertOverride(const nsCertOverride &other)
{
this->operator=(other);
}
nsCertOverride &operator=(const nsCertOverride &other)
{
mHostWithPortUTF8 = other.mHostWithPortUTF8;
mFingerprintAlgOID = other.mFingerprintAlgOID;
mFingerprint = other.mFingerprint;
mOverrideBits = other.mOverrideBits;
mDBKey = other.mDBKey;
return *this;
}
nsCString mHostWithPortUTF8;
nsCString mFingerprint;
nsCString mFingerprintAlgOID;
OverrideBits mOverrideBits;
nsCString mDBKey;
static void convertBitsToString(OverrideBits ob, nsACString &str);
static void convertStringToBits(const nsACString &str, OverrideBits &ob);
};
// hash entry class
class nsCertOverrideEntry : 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!
nsCertOverrideEntry(KeyTypePointer aHostWithPortUTF8)
{
}
nsCertOverrideEntry(const nsCertOverrideEntry& toCopy)
{
mSettings = toCopy.mSettings;
}
~nsCertOverrideEntry()
{
}
KeyType GetKey() const
{
return HostWithPortPtr();
}
KeyTypePointer GetKeyPointer() const
{
return HostWithPortPtr();
}
PRBool KeyEquals(KeyTypePointer aKey) const
{
return !strcmp(HostWithPortPtr(), aKey);
}
static KeyTypePointer KeyToPointer(KeyType aKey)
{
return aKey;
}
static PLDHashNumber HashKey(KeyTypePointer aKey)
{
// PL_DHashStringKey doesn't use the table parameter, so we can safely
// pass nsnull
return PL_DHashStringKey(nsnull, aKey);
}
enum { ALLOW_MEMMOVE = PR_FALSE };
// get methods
inline const nsCString &HostWithPort() const { return mSettings.mHostWithPortUTF8; }
inline KeyTypePointer HostWithPortPtr() const
{
return mSettings.mHostWithPortUTF8.get();
}
nsCertOverride mSettings;
};
class nsCertOverrideService : public nsICertOverrideService
, public nsIObserver
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSICERTOVERRIDESERVICE
NS_DECL_NSIOBSERVER
nsCertOverrideService();
~nsCertOverrideService();
nsresult Init();
typedef void
(*PR_CALLBACK 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);
protected:
PRMonitor *monitor;
nsCOMPtr<nsIFile> mSettingsFile;
nsTHashtable<nsCertOverrideEntry> mSettingsTable;
SECOidTag mOidTagForStoringNewHashes;
nsCString mDottedOidForStoringNewHashes;
void RemoveAllFromMemory();
nsresult Read();
nsresult Write();
nsresult AddEntryToList(const nsACString &hostWithPortUTF8,
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

Просмотреть файл

@ -59,9 +59,7 @@ class nsINSSComponent;
class nsIASN1Sequence;
/* Certificate */
class nsNSSCertificate : public nsIX509Cert,
public nsIX509Cert2,
public nsIX509Cert3,
class nsNSSCertificate : public nsIX509Cert3,
public nsISMimeCert,
public nsISerializable,
public nsIClassInfo,

Просмотреть файл

@ -0,0 +1,58 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla Communicator.
*
* The Initial Developer of the Original Code is
* Red Hat, Inc.
* Portions created by the Initial Developer are Copyright (C) 2006
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Kai Engert <kengert@redhat.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsNSSCleaner.h"
#include "cert.h"
CERTVerifyLogContentsCleaner::CERTVerifyLogContentsCleaner(CERTVerifyLog *&cvl)
:m_cvl(cvl)
{
}
CERTVerifyLogContentsCleaner::~CERTVerifyLogContentsCleaner()
{
if (!m_cvl)
return;
CERTVerifyLogNode *i_node;
for (i_node = m_cvl->head; i_node; i_node = i_node->next)
{
if (i_node->cert)
CERT_DestroyCertificate(i_node->cert);
}
}

Просмотреть файл

@ -124,4 +124,15 @@ public: \
void detach() {object=nsnull;} \
};
#include "certt.h"
class CERTVerifyLogContentsCleaner
{
public:
CERTVerifyLogContentsCleaner(CERTVerifyLog *&cvl);
~CERTVerifyLogContentsCleaner();
private:
CERTVerifyLog *&m_cvl;
};
#endif

Просмотреть файл

@ -51,13 +51,15 @@
#include "nsIServiceManager.h"
#include "nsIWebProgressListener.h"
#include "nsIChannel.h"
#include "nsIBadCertListener.h"
#include "nsNSSCertificate.h"
#include "nsIX509CertValidity.h"
#include "nsIProxyObjectManager.h"
#include "nsProxiedService.h"
#include "nsIDateTimeFormat.h"
#include "nsDateTimeFormatCID.h"
#include "nsIClientAuthDialogs.h"
#include "nsICertOverrideService.h"
#include "nsIBadCertListener2.h"
#include "nsXPIDLString.h"
#include "nsReadableUtils.h"
@ -68,6 +70,7 @@
#include "nsAutoLock.h"
#include "nsSSLThread.h"
#include "nsNSSShutDown.h"
#include "nsSSLStatus.h"
#include "nsNSSCertHelper.h"
#include "nsNSSCleaner.h"
#include "nsThreadUtils.h"
@ -83,6 +86,7 @@
#include "certdb.h"
#include "cert.h"
#include "keyhi.h"
#include "secport.h"
//#define DEBUG_SSL_VERBOSE //Enable this define to get minimal
@ -96,6 +100,8 @@
//file.
NSSCleanupAutoPtrClass(CERTCertificate, CERT_DestroyCertificate)
NSSCleanupAutoPtrClass(char, PR_FREEIF)
NSSCleanupAutoPtrClass_WithParam(PRArenaPool, PORT_FreeArena, FalseParam, PR_FALSE)
/* SSM_UserCertChoice: enum for cert choice info */
typedef enum {ASK, AUTO} SSM_UserCertChoice;
@ -195,7 +201,6 @@ nsNSSSocketInfo::nsNSSSocketInfo()
mHasCleartextPhase(PR_FALSE),
mHandshakeInProgress(PR_FALSE),
mAllowTLSIntoleranceTimeout(PR_TRUE),
mBadCertUIStatus(bcuis_not_shown),
mHandshakeStartTime(0),
mPort(0),
mCAChain(nsnull)
@ -562,20 +567,6 @@ void nsNSSSocketInfo::SetHandshakeInProgress(PRBool aIsIn)
}
}
void nsNSSSocketInfo::SetBadCertUIStatus(nsNSSSocketInfo::BadCertUIStatusType aNewStatus)
{
if (mBadCertUIStatus == bcuis_active &&
aNewStatus == bcuis_was_shown)
{
// we were blocked and going back to unblocked,
// so let's reset the handshake start time, in order to ensure
// we do not count the amount of time while the UI was shown.
mHandshakeStartTime = PR_IntervalNow();
}
mBadCertUIStatus = aNewStatus;
}
void nsNSSSocketInfo::SetAllowTLSIntoleranceTimeout(PRBool aAllow)
{
mAllowTLSIntoleranceTimeout = aAllow;
@ -585,8 +576,7 @@ void nsNSSSocketInfo::SetAllowTLSIntoleranceTimeout(PRBool aAllow)
PRBool nsNSSSocketInfo::HandshakeTimeout()
{
if (!mHandshakeInProgress || !mAllowTLSIntoleranceTimeout ||
mBadCertUIStatus == bcuis_active)
if (!mHandshakeInProgress || !mAllowTLSIntoleranceTimeout)
return PR_FALSE;
return ((PRIntervalTime)(PR_IntervalNow() - mHandshakeStartTime)
@ -640,6 +630,128 @@ getErrorMessage(PRInt32 err, const nsString &host,
return NS_OK;
}
static nsresult
getInvalidCertErrorMessage(PRUint32 multipleCollectedErrors,
PRInt32 errorCodeToReport,
const nsString &host,
const nsString &hostWithPort,
nsIX509Cert* ix509,
nsINSSComponent *component,
nsString &returnedMessage)
{
NS_ENSURE_ARG_POINTER(component);
const PRUnichar *params[1];
nsresult rv;
if (hostWithPort.Length())
{
params[0] = hostWithPort.get();
nsString formattedString;
rv = component->PIPBundleFormatStringFromName("certErrorIntro",
params, 1,
formattedString);
if (NS_SUCCEEDED(rv))
{
returnedMessage.Append(formattedString);
returnedMessage.Append(NS_LITERAL_STRING("\n"));
}
}
if (multipleCollectedErrors & nsICertOverrideService::ERROR_UNTRUSTED)
{
params[0] = host.get();
nsString formattedString;
rv = component->GetPIPNSSBundleString("certErrorUntrusted",
formattedString);
if (NS_SUCCEEDED(rv))
{
returnedMessage.Append(formattedString);
returnedMessage.Append(NS_LITERAL_STRING("\n"));
}
}
if (multipleCollectedErrors & nsICertOverrideService::ERROR_MISMATCH)
{
params[0] = host.get();
nsString formattedString;
rv = component->PIPBundleFormatStringFromName("certErrorMismatch",
params, 1,
formattedString);
if (NS_SUCCEEDED(rv))
{
returnedMessage.Append(formattedString);
returnedMessage.Append(NS_LITERAL_STRING("\n"));
}
}
if (multipleCollectedErrors & nsICertOverrideService::ERROR_TIME)
{
PRTime now = PR_Now();
PRTime notAfter, notBefore, timeToUse;
nsCOMPtr<nsIX509CertValidity> validity;
const char *key;
rv = ix509->GetValidity(getter_AddRefs(validity));
if (NS_FAILED(rv))
return rv;
rv = validity->GetNotAfter(&notAfter);
if (NS_FAILED(rv))
return rv;
rv = validity->GetNotBefore(&notBefore);
if (NS_FAILED(rv))
return rv;
if (LL_CMP(now, >, notAfter)) {
key = "certErrorExpired";
timeToUse = notAfter;
} else {
key = "certErrorNotYetValid";
timeToUse = notBefore;
}
nsAutoString formattedDate;
nsIDateTimeFormat* aDateTimeFormat;
rv = CallCreateInstance(NS_DATETIMEFORMAT_CONTRACTID, &aDateTimeFormat);
if (NS_FAILED(rv))
return rv;
aDateTimeFormat->FormatPRTime(nsnull, kDateFormatShort,
kTimeFormatNoSeconds, timeToUse,
formattedDate);
NS_IF_RELEASE(aDateTimeFormat);
params[0] = formattedDate.get();
nsString formattedString;
rv = component->PIPBundleFormatStringFromName(key, params,
1, formattedString);
if (NS_SUCCEEDED(rv))
{
returnedMessage.Append(formattedString);
returnedMessage.Append(NS_LITERAL_STRING("\n"));
}
}
const char *codeName = nsNSSErrors::getDefaultErrorStringName(errorCodeToReport);
if (codeName)
{
nsCString error_id(codeName);
ToLowerCase(error_id);
NS_ConvertASCIItoUTF16 idU(error_id);
returnedMessage.Append(NS_LITERAL_STRING(" ("));
returnedMessage.Append(idU);
returnedMessage.Append(NS_LITERAL_STRING(")"));
}
return NS_OK;
}
static nsresult
displayAlert(nsAFlatString &formattedString, nsNSSSocketInfo *infoObject)
{
@ -690,9 +802,6 @@ nsHandleSSLError(nsNSSSocketInfo *socketInfo, PRInt32 err)
socketInfo->GetHostName(getter_Copies(hostName));
NS_ConvertASCIItoUTF16 hostNameU(hostName);
nsCOMPtr<nsIStringBundleService> service =
do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
nsString formattedString;
rv = getErrorMessage(err, hostNameU, nssComponent, formattedString);
@ -716,6 +825,51 @@ nsHandleSSLError(nsNSSSocketInfo *socketInfo, PRInt32 err)
return rv;
}
static nsresult
nsHandleInvalidCertError(nsNSSSocketInfo *socketInfo,
PRUint32 multipleCollectedErrors,
const nsACString &host,
const nsACString &hostWithPort,
PRInt32 err,
nsIX509Cert* ix509)
{
nsresult rv;
NS_DEFINE_CID(nssComponentCID, NS_NSSCOMPONENT_CID);
nsCOMPtr<nsINSSComponent> nssComponent(do_GetService(nssComponentCID, &rv));
if (NS_FAILED(rv))
return rv;
NS_ConvertASCIItoUTF16 hostU(host);
NS_ConvertASCIItoUTF16 hostWithPortU(hostWithPort);
nsString formattedString;
rv = getInvalidCertErrorMessage(multipleCollectedErrors, err, hostU, hostWithPortU,
ix509, nssComponent, formattedString);
// What mechanism is used to inform the user?
// The highest priority has the "external error reporting" feature,
// if set, we'll provide the strings to be used by the nsINSSErrorsService
PRBool external = PR_FALSE;
socketInfo->GetExternalErrorReporting(&external);
if (external)
{
socketInfo->SetErrorMessage(formattedString.get());
}
else
{
nsPSMUITracker tracker;
if (tracker.isUIForbidden()) {
rv = NS_ERROR_NOT_AVAILABLE;
}
else {
rv = displayAlert(formattedString, socketInfo);
}
}
return rv;
}
static PRStatus PR_CALLBACK
nsSSLIOLayerConnect(PRFileDesc* fd, const PRNetAddr* addr,
@ -943,37 +1097,6 @@ isTLSIntoleranceError(PRInt32 err, PRBool withInitialCleartext)
return PR_FALSE;
}
static PRBool
isClosedConnectionAfterBadCertUIWasShown(PRInt32 bytesTransfered,
PRBool wasReading,
PRInt32 err,
nsNSSSocketInfo::BadCertUIStatusType aBadCertUIStatus)
{
if (aBadCertUIStatus != nsNSSSocketInfo::bcuis_not_shown)
{
// Bad cert UI was shown for this socket.
// Server timeout possible.
// Retry on a simple connection close.
if (wasReading && 0 == bytesTransfered)
return PR_TRUE;
if (0 > bytesTransfered)
{
switch (err)
{
case PR_CONNECT_RESET_ERROR:
case PR_END_OF_FILE_ERROR:
return PR_TRUE;
default:
break;
}
}
}
return PR_FALSE;
}
PRInt32
nsSSLThread::checkHandshake(PRInt32 bytesTransfered,
PRBool wasReading,
@ -1025,12 +1148,6 @@ nsSSLThread::checkHandshake(PRInt32 bytesTransfered,
return bytesTransfered;
}
wantRetry =
isClosedConnectionAfterBadCertUIWasShown(bytesTransfered,
wasReading,
err,
socketInfo->GetBadCertUIStatus());
if (!wantRetry // no decision yet
&& isTLSIntoleranceError(err, socketInfo->GetHasCleartextPhase()))
{
@ -1048,12 +1165,6 @@ nsSSLThread::checkHandshake(PRInt32 bytesTransfered,
{
if (handleHandshakeResultNow)
{
wantRetry =
isClosedConnectionAfterBadCertUIWasShown(bytesTransfered,
wasReading,
0,
socketInfo->GetBadCertUIStatus());
if (!wantRetry // no decision yet
&& !socketInfo->GetHasCleartextPhase()) // mirror PR_CONNECT_RESET_ERROR treament
{
@ -1357,212 +1468,6 @@ nsSSLIOLayerNewSocket(PRInt32 family,
return NS_OK;
}
static nsresult
addCertToDB(CERTCertificate *peerCert, PRInt16 addType)
{
CERTCertTrust trust;
SECStatus rv;
nsresult retVal = NS_ERROR_FAILURE;
char *nickname;
switch (addType) {
case nsIBadCertListener::ADD_TRUSTED_PERMANENTLY:
nickname = nsNSSCertificate::defaultServerNickname(peerCert);
if (nsnull == nickname)
break;
memset((void*)&trust, 0, sizeof(trust));
rv = CERT_DecodeTrustString(&trust, "P");
if (rv != SECSuccess) {
return NS_ERROR_FAILURE;
}
rv = CERT_AddTempCertToPerm(peerCert, nickname, &trust);
if (rv == SECSuccess)
retVal = NS_OK;
PR_Free(nickname);
break;
case nsIBadCertListener::ADD_TRUSTED_FOR_SESSION:
// XXX We need an API from NSS to do this so
// that we don't have to access the fields
// in the cert directly.
peerCert->keepSession = PR_TRUE;
CERTCertTrust *trustPtr;
if (!peerCert->trust) {
trustPtr = (CERTCertTrust*)PORT_ArenaZAlloc(peerCert->arena,
sizeof(CERTCertTrust));
if (!trustPtr)
break;
peerCert->trust = trustPtr;
} else {
trustPtr = peerCert->trust;
}
rv = CERT_DecodeTrustString(trustPtr, "P");
if (rv != SECSuccess)
break;
retVal = NS_OK;
break;
default:
PR_ASSERT(!"Invalid value for addType passed to addCertDB");
break;
}
return retVal;
}
static PRBool
nsContinueDespiteCertError(nsNSSSocketInfo *infoObject,
PRFileDesc *sslSocket,
int error,
nsNSSCertificate *nssCert)
{
PRBool retVal = PR_FALSE;
nsIBadCertListener *badCertHandler = nsnull;
PRInt16 addType = nsIBadCertListener::UNINIT_ADD_FLAG;
nsresult rv;
if (!nssCert)
return PR_FALSE;
// Try to get a nsIBadCertListener implementation from the socket consumer
// first. If that fails, fallback to the default UI.
nsCOMPtr<nsIInterfaceRequestor> callbacks;
infoObject->GetNotificationCallbacks(getter_AddRefs(callbacks));
if (callbacks) {
nsCOMPtr<nsIBadCertListener> handler = do_GetInterface(callbacks);
if (handler) {
NS_GetProxyForObject(NS_PROXY_TO_MAIN_THREAD,
NS_GET_IID(nsIBadCertListener),
handler,
NS_PROXY_SYNC,
(void**)&badCertHandler);
}
}
if (!badCertHandler) {
rv = getNSSDialogs((void**)&badCertHandler,
NS_GET_IID(nsIBadCertListener),
NS_BADCERTLISTENER_CONTRACTID);
if (NS_FAILED(rv))
return PR_FALSE;
}
nsIInterfaceRequestor *csi = static_cast<nsIInterfaceRequestor*>
(infoObject);
nsIX509Cert *callBackCert = static_cast<nsIX509Cert*>(nssCert);
CERTCertificate *peerCert = nssCert->GetCert();
NS_ASSERTION(peerCert, "Got nsnull cert back from nsNSSCertificate");
switch (error) {
case SEC_ERROR_UNKNOWN_ISSUER:
case SEC_ERROR_CA_CERT_INVALID:
case SEC_ERROR_UNTRUSTED_ISSUER:
/* This is a temporay fix for bug# - We are showing a unknown ca dialog,
when actually the ca cert has expired/not yet valid. We need to change
this in future - need to define a proper ui for this situation
*/
case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE:
{
nsPSMUITracker tracker;
if (tracker.isUIForbidden()) {
rv = NS_ERROR_NOT_AVAILABLE;
}
else {
rv = badCertHandler->ConfirmUnknownIssuer(csi, callBackCert, &addType, &retVal);
}
}
break;
case SSL_ERROR_BAD_CERT_DOMAIN:
{
nsXPIDLCString url; url.Adopt(SSL_RevealURL(sslSocket));
NS_ASSERTION(url.get(), "could not find valid URL in ssl socket");
{
nsPSMUITracker tracker;
if (tracker.isUIForbidden()) {
rv = NS_ERROR_NOT_AVAILABLE;
}
else {
rv = badCertHandler->ConfirmMismatchDomain(csi, url,
callBackCert, &retVal);
}
}
if (NS_SUCCEEDED(rv) && retVal) {
rv = CERT_AddOKDomainName(peerCert, url);
}
}
break;
case SEC_ERROR_EXPIRED_CERTIFICATE:
{
nsPSMUITracker tracker;
if (tracker.isUIForbidden()) {
rv = NS_ERROR_NOT_AVAILABLE;
}
else {
rv = badCertHandler->ConfirmCertExpired(csi, callBackCert, & retVal);
}
}
if (rv == SECSuccess && retVal) {
// XXX We need an NSS API for this equivalent functionality.
// Having to reach inside the cert is evil.
peerCert->timeOK = PR_TRUE;
}
break;
case SEC_ERROR_CRL_EXPIRED:
{
nsXPIDLCString url; url.Adopt(SSL_RevealURL(sslSocket));
NS_ASSERTION(url, "could not find valid URL in ssl socket");
{
nsPSMUITracker tracker;
if (tracker.isUIForbidden()) {
rv = NS_ERROR_NOT_AVAILABLE;
}
else {
rv = badCertHandler->NotifyCrlNextupdate(csi, url, callBackCert);
}
}
retVal = PR_FALSE;
}
break;
default:
nsHandleSSLError(infoObject,error);
retVal = PR_FALSE;
rv = NS_ERROR_FAILURE;
}
if (retVal && addType != nsIBadCertListener::UNINIT_ADD_FLAG) {
addCertToDB(peerCert, addType);
}
NS_RELEASE(badCertHandler);
CERT_DestroyCertificate(peerCert);
return NS_FAILED(rv) ? PR_FALSE : retVal;
}
static SECStatus
verifyCertAgain(CERTCertificate *cert,
PRFileDesc *sslSocket,
nsNSSSocketInfo *infoObject)
{
SECStatus rv;
// If we get here, the user has accepted the cert so
// far, so we don't check the signature again.
rv = CERT_VerifyCertificateNow(CERT_GetDefaultCertDB(), cert,
PR_FALSE, certificateUsageSSLServer,
(void*)infoObject, NULL);
if (rv != SECSuccess) {
return rv;
}
// Check the name field against the desired hostname.
char *hostname = SSL_RevealURL(sslSocket);
if (hostname && hostname[0]) {
rv = CERT_VerifyCertName(cert, hostname);
} else {
rv = SECFailure;
}
if (rv != SECSuccess) {
PR_SetError(SSL_ERROR_BAD_CERT_DOMAIN, 0);
}
PR_FREEIF(hostname);
return rv;
}
/*
* Function: SECStatus nsConvertCANamesToStrings()
* Purpose: creates CA names strings from (CERTDistNames* caNames)
@ -2388,42 +2293,210 @@ done:
return ret;
}
static SECStatus
cancel_and_failure(nsNSSSocketInfo* infoObject)
{
infoObject->SetCanceled(PR_TRUE);
return SECFailure;
}
static SECStatus
nsNSSBadCertHandler(void *arg, PRFileDesc *sslSocket)
{
nsNSSShutDownPreventionLock locker;
SECStatus rv = SECFailure;
int error;
nsNSSSocketInfo* infoObject = (nsNSSSocketInfo *)arg;
CERTCertificate *peerCert;
nsNSSCertificate *nssCert;
error = PR_GetError();
peerCert = SSL_PeerCertificate(sslSocket);
nssCert = new nsNSSCertificate(peerCert);
if (!nssCert) {
if (!infoObject)
return SECFailure;
}
NS_ADDREF(nssCert);
infoObject->SetBadCertUIStatus(nsNSSSocketInfo::bcuis_active);
while (rv != SECSuccess) {
//Func nsContinueDespiteCertError does the same set of checks as func.
//nsCertErrorNeedsDialog. So, removing call to nsCertErrorNeedsDialog
if (!nsContinueDespiteCertError(infoObject, sslSocket,
error, nssCert)) {
break;
CERTCertificate *peerCert = nsnull;
CERTCertificateCleaner peerCertCleaner(peerCert);
peerCert = SSL_PeerCertificate(sslSocket);
if (!peerCert)
return cancel_and_failure(infoObject);
nsRefPtr<nsNSSCertificate> nssCert;
nssCert = new nsNSSCertificate(peerCert);
if (!nssCert)
return cancel_and_failure(infoObject);
nsCOMPtr<nsIX509Cert> ix509 = static_cast<nsIX509Cert*>(nssCert.get());
SECStatus srv;
nsresult nsrv;
PRUint32 collected_errors = 0;
PRUint32 remaining_display_errors = 0;
// There may be multiple problems with a cert, but we can only report
// a single error code to the caller. We'll use the first code we see.
// However, in our error string we'll use a string that mentions
// all of expired/not-yet-valid/domain-mismatch/untrusted.
PRErrorCode errorCodeToReport = SECSuccess;
char *hostname = SSL_RevealURL(sslSocket);
charCleaner hostnameCleaner(hostname);
nsDependentCString hostString(hostname);
PRInt32 port;
infoObject->GetPort(&port);
nsCString hostWithPortString = hostString;
hostWithPortString.AppendLiteral(":");
hostWithPortString.AppendInt(port);
// Check the name field against the desired hostname.
if (hostname && hostname[0] &&
CERT_VerifyCertName(peerCert, hostname) != SECSuccess) {
collected_errors |= nsICertOverrideService::ERROR_MISMATCH;
errorCodeToReport = SSL_ERROR_BAD_CERT_DOMAIN;
}
{
PRArenaPool *log_arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if (!log_arena)
return cancel_and_failure(infoObject);
PRArenaPoolCleanerFalseParam log_arena_cleaner(log_arena);
CERTVerifyLog *verify_log = PORT_ArenaZNew(log_arena, CERTVerifyLog);
if (!verify_log)
return cancel_and_failure(infoObject);
CERTVerifyLogContentsCleaner verify_log_cleaner(verify_log);
verify_log->arena = log_arena;
srv = CERT_VerifyCertificate(CERT_GetDefaultCertDB(), peerCert,
PR_TRUE, certificateUsageSSLServer,
PR_Now(), (void*)infoObject,
verify_log, NULL);
// We ignore the result code of the cert verification.
// Either it is a failure, which is expected, and we'll process the
// verify log below.
// Or it is a success, then a domain mismatch is the only
// possible failure.
CERTVerifyLogNode *i_node;
for (i_node = verify_log->head; i_node; i_node = i_node->next)
{
if (errorCodeToReport == SECSuccess) {
errorCodeToReport = i_node->error;
}
switch (i_node->error)
{
case SEC_ERROR_UNKNOWN_ISSUER:
case SEC_ERROR_CA_CERT_INVALID:
case SEC_ERROR_UNTRUSTED_ISSUER:
case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE:
case SEC_ERROR_UNTRUSTED_CERT:
// We group all these errors as "cert not trusted"
collected_errors |= nsICertOverrideService::ERROR_UNTRUSTED;
break;
case SSL_ERROR_BAD_CERT_DOMAIN:
collected_errors |= nsICertOverrideService::ERROR_MISMATCH;
break;
case SEC_ERROR_EXPIRED_CERTIFICATE:
collected_errors |= nsICertOverrideService::ERROR_TIME;
break;
default:
// we are not willing to continue on any other error
nsHandleSSLError(infoObject, i_node->error);
return cancel_and_failure(infoObject);
}
}
rv = verifyCertAgain(peerCert, sslSocket, infoObject);
error = PR_GetError();
}
infoObject->SetBadCertUIStatus(nsNSSSocketInfo::bcuis_was_shown);
NS_RELEASE(nssCert);
CERT_DestroyCertificate(peerCert);
if (rv != SECSuccess) {
// if the cert is bad, we don't want to connect
infoObject->SetCanceled(PR_TRUE);
if (!collected_errors)
{
NS_NOTREACHED("why did NSS call our bad cert handler if all looks good? Let's cancel the connection");
return SECFailure;
}
return rv;
nsCOMPtr<nsSSLStatus> status;
infoObject->GetSSLStatus(getter_AddRefs(status));
if (!status) {
status = new nsSSLStatus();
infoObject->SetSSLStatus(status);
}
if (status) {
if (!status->mServerCert) {
status->mServerCert = nssCert;
}
status->mHaveCertStatus = PR_TRUE;
status->mIsDomainMismatch = collected_errors & nsICertOverrideService::ERROR_MISMATCH;
status->mIsNotValidAtThisTime = collected_errors & nsICertOverrideService::ERROR_TIME;
status->mIsUntrusted = collected_errors & nsICertOverrideService::ERROR_UNTRUSTED;
}
remaining_display_errors = collected_errors;
nsCOMPtr<nsICertOverrideService> overrideService =
do_GetService(NS_CERTOVERRIDE_CONTRACTID);
// it is fine to continue without the nsICertOverrideService
PRUint32 storedOverrideBits = 0;
if (overrideService)
{
PRBool haveStoredOverride;
nsrv = overrideService->HasMatchingOverride(NS_ConvertUTF8toUTF16(hostWithPortString),
ix509,
&storedOverrideBits,
&haveStoredOverride);
if (NS_SUCCEEDED(nsrv) && haveStoredOverride)
{
// remove the errors that are already overriden
remaining_display_errors -= storedOverrideBits;
}
}
if (!remaining_display_errors) {
// all errors are covered by override rules, so let's accept the cert
return SECSuccess;
}
// Ok, this is a full stop.
// First, deliver the technical details of the broken SSL status,
// giving the caller a chance to suppress the error messages.
PRBool suppressMessage = PR_FALSE;
nsresult rv;
// Try to get a nsIBadCertListener2 implementation from the socket consumer.
nsCOMPtr<nsIInterfaceRequestor> callbacks;
infoObject->GetNotificationCallbacks(getter_AddRefs(callbacks));
if (callbacks) {
nsCOMPtr<nsIBadCertListener2> bcl = do_GetInterface(callbacks);
if (bcl) {
nsIBadCertListener2 *proxy_bcl = nsnull;
NS_GetProxyForObject(NS_PROXY_TO_MAIN_THREAD,
NS_GET_IID(nsIBadCertListener2),
bcl,
NS_PROXY_SYNC,
(void**)&proxy_bcl);
if (proxy_bcl) {
nsIInterfaceRequestor *csi = static_cast<nsIInterfaceRequestor*>(infoObject);
rv = proxy_bcl->NotifyCertProblem(csi, status, hostWithPortString,
&suppressMessage);
}
}
}
PR_SetError(errorCodeToReport, 0);
if (!suppressMessage) {
nsHandleInvalidCertError(infoObject,
remaining_display_errors,
hostString,
hostWithPortString,
errorCodeToReport,
ix509);
}
return cancel_and_failure(infoObject);
}
static PRFileDesc*

Просмотреть файл

@ -0,0 +1,140 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* Red Hat, Inc.
* Portions created by the Initial Developer are Copyright (C) 2006
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Kai Engert <kengert@redhat.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsSSLStatus.h"
#include "plstr.h"
NS_IMETHODIMP
nsSSLStatus::GetServerCert(nsIX509Cert** _result)
{
NS_ASSERTION(_result, "non-NULL destination required");
*_result = mServerCert;
NS_IF_ADDREF(*_result);
return NS_OK;
}
NS_IMETHODIMP
nsSSLStatus::GetKeyLength(PRUint32* _result)
{
NS_ASSERTION(_result, "non-NULL destination required");
if (!mHaveKeyLengthAndCipher)
return NS_ERROR_NOT_AVAILABLE;
*_result = mKeyLength;
return NS_OK;
}
NS_IMETHODIMP
nsSSLStatus::GetSecretKeyLength(PRUint32* _result)
{
NS_ASSERTION(_result, "non-NULL destination required");
if (!mHaveKeyLengthAndCipher)
return NS_ERROR_NOT_AVAILABLE;
*_result = mSecretKeyLength;
return NS_OK;
}
NS_IMETHODIMP
nsSSLStatus::GetCipherName(char** _result)
{
NS_ASSERTION(_result, "non-NULL destination required");
if (!mHaveKeyLengthAndCipher)
return NS_ERROR_NOT_AVAILABLE;
*_result = PL_strdup(mCipherName.get());
return NS_OK;
}
NS_IMETHODIMP
nsSSLStatus::GetIsDomainMismatch(PRBool* _result)
{
NS_ASSERTION(_result, "non-NULL destination required");
if (!mHaveCertStatus)
return NS_ERROR_NOT_AVAILABLE;
*_result = mIsDomainMismatch;
return NS_OK;
}
NS_IMETHODIMP
nsSSLStatus::GetIsNotValidAtThisTime(PRBool* _result)
{
NS_ASSERTION(_result, "non-NULL destination required");
if (!mHaveCertStatus)
return NS_ERROR_NOT_AVAILABLE;
*_result = mIsNotValidAtThisTime;
return NS_OK;
}
NS_IMETHODIMP
nsSSLStatus::GetIsUntrusted(PRBool* _result)
{
NS_ASSERTION(_result, "non-NULL destination required");
if (!mHaveCertStatus)
return NS_ERROR_NOT_AVAILABLE;
*_result = mIsUntrusted;
return NS_OK;
}
nsSSLStatus::nsSSLStatus()
: mKeyLength(0), mSecretKeyLength(0)
, mIsDomainMismatch(PR_FALSE)
, mIsNotValidAtThisTime(PR_FALSE)
, mIsUntrusted(PR_FALSE)
, mHaveKeyLengthAndCipher(PR_FALSE)
, mHaveCertStatus(PR_FALSE)
{
}
NS_IMPL_THREADSAFE_ISUPPORTS1(nsSSLStatus, nsISSLStatus)
nsSSLStatus::~nsSSLStatus()
{
}

Просмотреть файл

@ -0,0 +1,69 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* Red Hat, Inc.
* Portions created by the Initial Developer are Copyright (C) 2006
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Kai Engert <kengert@redhat.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsISSLStatus.h"
#include "nsAutoPtr.h"
#include "nsXPIDLString.h"
#include "nsIX509Cert.h"
class nsSSLStatus
: public nsISSLStatus
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSISSLSTATUS
nsSSLStatus();
virtual ~nsSSLStatus();
/* public for initilization in this file */
nsCOMPtr<nsIX509Cert> mServerCert;
PRUint32 mKeyLength;
PRUint32 mSecretKeyLength;
nsXPIDLCString mCipherName;
PRBool mIsDomainMismatch;
PRBool mIsNotValidAtThisTime;
PRBool mIsUntrusted;
PRBool mHaveKeyLengthAndCipher;
PRBool mHaveCertStatus;
};

Просмотреть файл

@ -44,7 +44,7 @@
// DOM event class to handle progress notifications
nsSmartCardEvent::nsSmartCardEvent(const nsAString &aTokenName)
: mTokenName(aTokenName), mInner(nsnull), mPrivate(nsnull)
: mInner(nsnull), mPrivate(nsnull), mTokenName(aTokenName)
{
}