blob: 46fe07112b0f9da4c82851c86c1b02f87bd87aa9 [file] [log] [blame]
/* 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/. */
/*
* pkix_targetcertchecker.c
*
* Functions for target cert validation
*
*/
#include "pkix_targetcertchecker.h"
/* --Private-TargetCertCheckerState-Functions------------------------------- */
/*
* FUNCTION: pkix_TargetCertCheckerState_Destroy
* (see comments for PKIX_PL_DestructorCallback in pkix_pl_system.h)
*/
static PKIX_Error *
pkix_TargetCertCheckerState_Destroy(
PKIX_PL_Object *object,
void *plContext)
{
pkix_TargetCertCheckerState *state = NULL;
PKIX_ENTER(TARGETCERTCHECKERSTATE,
"pkix_TargetCertCheckerState_Destroy");
PKIX_NULLCHECK_ONE(object);
/* Check that this object is a target cert checker state */
PKIX_CHECK(pkix_CheckType
(object, PKIX_TARGETCERTCHECKERSTATE_TYPE, plContext),
PKIX_OBJECTNOTTARGETCERTCHECKERSTATE);
state = (pkix_TargetCertCheckerState *)object;
PKIX_DECREF(state->certSelector);
PKIX_DECREF(state->extKeyUsageOID);
PKIX_DECREF(state->subjAltNameOID);
PKIX_DECREF(state->pathToNameList);
PKIX_DECREF(state->extKeyUsageList);
PKIX_DECREF(state->subjAltNameList);
cleanup:
PKIX_RETURN(TARGETCERTCHECKERSTATE);
}
/*
* FUNCTION: pkix_TargetCertCheckerState_RegisterSelf
* DESCRIPTION:
* Registers PKIX_TARGETCERTCHECKERSTATE_TYPE and its related functions with
* systemClasses[]
* THREAD SAFETY:
* Not Thread Safe - for performance and complexity reasons
*
* Since this function is only called by PKIX_PL_Initialize, which should
* only be called once, it is acceptable that this function is not
* thread-safe.
*/
PKIX_Error *
pkix_TargetCertCheckerState_RegisterSelf(void *plContext)
{
extern pkix_ClassTable_Entry systemClasses[PKIX_NUMTYPES];
pkix_ClassTable_Entry entry;
PKIX_ENTER(TARGETCERTCHECKERSTATE,
"pkix_TargetCertCheckerState_RegisterSelf");
entry.description = "TargetCertCheckerState";
entry.objCounter = 0;
entry.typeObjectSize = sizeof(pkix_TargetCertCheckerState);
entry.destructor = pkix_TargetCertCheckerState_Destroy;
entry.equalsFunction = NULL;
entry.hashcodeFunction = NULL;
entry.toStringFunction = NULL;
entry.comparator = NULL;
entry.duplicateFunction = NULL;
systemClasses[PKIX_TARGETCERTCHECKERSTATE_TYPE] = entry;
PKIX_RETURN(TARGETCERTCHECKERSTATE);
}
/*
* FUNCTION: pkix_TargetCertCheckerState_Create
* DESCRIPTION:
*
* Creates a new TargetCertCheckerState using the CertSelector pointed to
* by "certSelector" and the number of certs represented by "certsRemaining"
* and stores it at "pState".
*
* PARAMETERS:
* "certSelector"
* Address of CertSelector representing the criteria against which the
* final certificate in a chain is to be matched. Must be non-NULL.
* "certsRemaining"
* Number of certificates remaining in the chain.
* "pState"
* Address where object pointer will be stored. Must be non-NULL.
* "plContext"
* Platform-specific context pointer.
* THREAD SAFETY:
* Thread Safe (see Thread Safety Definitions in Programmer's Guide)
* RETURNS:
* Returns NULL if the function succeeds.
* Returns a TargetCertCheckerState Error if the function fails in a
* non-fatal way.
* Returns a Fatal Error if the function fails in an unrecoverable way.
*/
PKIX_Error *
pkix_TargetCertCheckerState_Create(
PKIX_CertSelector *certSelector,
PKIX_UInt32 certsRemaining,
pkix_TargetCertCheckerState **pState,
void *plContext)
{
pkix_TargetCertCheckerState *state = NULL;
PKIX_ComCertSelParams *certSelectorParams = NULL;
PKIX_List *pathToNameList = NULL;
PKIX_List *extKeyUsageList = NULL;
PKIX_List *subjAltNameList = NULL;
PKIX_PL_OID *extKeyUsageOID = NULL;
PKIX_PL_OID *subjAltNameOID = NULL;
PKIX_Boolean subjAltNameMatchAll = PKIX_TRUE;
PKIX_ENTER(TARGETCERTCHECKERSTATE,
"pkix_TargetCertCheckerState_Create");
PKIX_NULLCHECK_ONE(pState);
PKIX_CHECK(PKIX_PL_OID_Create
(PKIX_EXTENDEDKEYUSAGE_OID,
&extKeyUsageOID,
plContext),
PKIX_OIDCREATEFAILED);
PKIX_CHECK(PKIX_PL_OID_Create
(PKIX_CERTSUBJALTNAME_OID,
&subjAltNameOID,
plContext),
PKIX_OIDCREATEFAILED);
PKIX_CHECK(PKIX_PL_Object_Alloc
(PKIX_TARGETCERTCHECKERSTATE_TYPE,
sizeof (pkix_TargetCertCheckerState),
(PKIX_PL_Object **)&state,
plContext),
PKIX_COULDNOTCREATETARGETCERTCHECKERSTATEOBJECT);
/* initialize fields */
if (certSelector != NULL) {
PKIX_CHECK(PKIX_CertSelector_GetCommonCertSelectorParams
(certSelector, &certSelectorParams, plContext),
PKIX_CERTSELECTORGETCOMMONCERTSELECTORPARAMFAILED);
if (certSelectorParams != NULL) {
PKIX_CHECK(PKIX_ComCertSelParams_GetPathToNames
(certSelectorParams,
&pathToNameList,
plContext),
PKIX_COMCERTSELPARAMSGETPATHTONAMESFAILED);
PKIX_CHECK(PKIX_ComCertSelParams_GetExtendedKeyUsage
(certSelectorParams,
&extKeyUsageList,
plContext),
PKIX_COMCERTSELPARAMSGETEXTENDEDKEYUSAGEFAILED);
PKIX_CHECK(PKIX_ComCertSelParams_GetSubjAltNames
(certSelectorParams,
&subjAltNameList,
plContext),
PKIX_COMCERTSELPARAMSGETSUBJALTNAMESFAILED);
PKIX_CHECK(PKIX_ComCertSelParams_GetMatchAllSubjAltNames
(certSelectorParams,
&subjAltNameMatchAll,
plContext),
PKIX_COMCERTSELPARAMSGETSUBJALTNAMESFAILED);
}
}
state->certsRemaining = certsRemaining;
state->subjAltNameMatchAll = subjAltNameMatchAll;
PKIX_INCREF(certSelector);
state->certSelector = certSelector;
state->pathToNameList = pathToNameList;
pathToNameList = NULL;
state->extKeyUsageList = extKeyUsageList;
extKeyUsageList = NULL;
state->subjAltNameList = subjAltNameList;
subjAltNameList = NULL;
state->extKeyUsageOID = extKeyUsageOID;
extKeyUsageOID = NULL;
state->subjAltNameOID = subjAltNameOID;
subjAltNameOID = NULL;
*pState = state;
state = NULL;
cleanup:
PKIX_DECREF(extKeyUsageOID);
PKIX_DECREF(subjAltNameOID);
PKIX_DECREF(pathToNameList);
PKIX_DECREF(extKeyUsageList);
PKIX_DECREF(subjAltNameList);
PKIX_DECREF(state);
PKIX_DECREF(certSelectorParams);
PKIX_RETURN(TARGETCERTCHECKERSTATE);
}
/* --Private-TargetCertChecker-Functions------------------------------- */
/*
* FUNCTION: pkix_TargetCertChecker_Check
* (see comments for PKIX_CertChainChecker_CheckCallback in pkix_checker.h)
*/
PKIX_Error *
pkix_TargetCertChecker_Check(
PKIX_CertChainChecker *checker,
PKIX_PL_Cert *cert,
PKIX_List *unresolvedCriticalExtensions,
void **pNBIOContext,
void *plContext)
{
pkix_TargetCertCheckerState *state = NULL;
PKIX_CertSelector_MatchCallback certSelectorMatch = NULL;
PKIX_PL_CertNameConstraints *nameConstraints = NULL;
PKIX_List *certSubjAltNames = NULL;
PKIX_List *certExtKeyUsageList = NULL;
PKIX_PL_GeneralName *name = NULL;
PKIX_PL_X500Name *certSubjectName = NULL;
PKIX_Boolean checkPassed = PKIX_FALSE;
PKIX_UInt32 numItems, i;
PKIX_UInt32 matchCount = 0;
PKIX_ENTER(CERTCHAINCHECKER, "pkix_TargetCertChecker_Check");
PKIX_NULLCHECK_THREE(checker, cert, pNBIOContext);
*pNBIOContext = NULL; /* we never block on pending I/O */
PKIX_CHECK(PKIX_CertChainChecker_GetCertChainCheckerState
(checker, (PKIX_PL_Object **)&state, plContext),
PKIX_CERTCHAINCHECKERGETCERTCHAINCHECKERSTATEFAILED);
(state->certsRemaining)--;
if (state->pathToNameList != NULL) {
PKIX_CHECK(PKIX_PL_Cert_GetNameConstraints
(cert, &nameConstraints, plContext),
PKIX_CERTGETNAMECONSTRAINTSFAILED);
/*
* XXX We should either make the following call a public one
* so it is legal to call from the portability layer or we
* should try to create pathToNameList as CertNameConstraints
* then call the existing check function.
*/
PKIX_CHECK(PKIX_PL_CertNameConstraints_CheckNamesInNameSpace
(state->pathToNameList,
nameConstraints,
&checkPassed,
plContext),
PKIX_CERTNAMECONSTRAINTSCHECKNAMEINNAMESPACEFAILED);
if (checkPassed != PKIX_TRUE) {
PKIX_ERROR(PKIX_VALIDATIONFAILEDPATHTONAMECHECKFAILED);
}
}
PKIX_CHECK(PKIX_PL_Cert_GetSubjectAltNames
(cert, &certSubjAltNames, plContext),
PKIX_CERTGETSUBJALTNAMESFAILED);
if (state->subjAltNameList != NULL && certSubjAltNames != NULL) {
PKIX_CHECK(PKIX_List_GetLength
(state->subjAltNameList, &numItems, plContext),
PKIX_LISTGETLENGTHFAILED);
for (i = 0; i < numItems; i++) {
PKIX_CHECK(PKIX_List_GetItem
(state->subjAltNameList,
i,
(PKIX_PL_Object **) &name,
plContext),
PKIX_LISTGETITEMFAILED);
PKIX_CHECK(pkix_List_Contains
(certSubjAltNames,
(PKIX_PL_Object *) name,
&checkPassed,
plContext),
PKIX_LISTCONTAINSFAILED);
PKIX_DECREF(name);
if (checkPassed == PKIX_TRUE) {
if (state->subjAltNameMatchAll == PKIX_FALSE) {
matchCount = numItems;
break;
} else {
/* else continue checking next */
matchCount++;
}
}
}
if (matchCount != numItems) {
PKIX_ERROR(PKIX_SUBJALTNAMECHECKFAILED);
}
}
if (state->certsRemaining == 0) {
if (state->certSelector != NULL) {
PKIX_CHECK(PKIX_CertSelector_GetMatchCallback
(state->certSelector,
&certSelectorMatch,
plContext),
PKIX_CERTSELECTORGETMATCHCALLBACKFAILED);
PKIX_CHECK(certSelectorMatch
(state->certSelector,
cert,
plContext),
PKIX_CERTSELECTORMATCHFAILED);
} else {
/* Check at least cert/key usages if target cert selector
* is not set. */
PKIX_CHECK(PKIX_PL_Cert_VerifyCertAndKeyType(cert,
PKIX_FALSE /* is chain cert*/,
plContext),
PKIX_CERTVERIFYCERTTYPEFAILED);
}
/*
* There are two Extended Key Usage Checkings
* available :
* 1) here at the targetcertchecker where we
* verify the Extended Key Usage OIDs application
* specifies via ComCertSelParams are included
* in Cert's Extended Key Usage OID's. Note,
* this is an OID to OID comparison and only last
* Cert is checked.
* 2) at user defined ekuchecker where checking
* is applied to all Certs on the chain and
* the NSS Extended Key Usage algorithm is
* used. In order to invoke this checking, not
* only does the ComCertSelparams needs to be
* set, the EKU initialize call is required to
* activate the checking.
*
* XXX We use the same ComCertSelParams Set/Get
* functions to set the parameters for both cases.
* We may want to separate them in the future.
*/
PKIX_CHECK(PKIX_PL_Cert_GetExtendedKeyUsage
(cert, &certExtKeyUsageList, plContext),
PKIX_CERTGETEXTENDEDKEYUSAGEFAILED);
if (state->extKeyUsageList != NULL &&
certExtKeyUsageList != NULL) {
PKIX_CHECK(PKIX_List_GetLength
(state->extKeyUsageList, &numItems, plContext),
PKIX_LISTGETLENGTHFAILED);
for (i = 0; i < numItems; i++) {
PKIX_CHECK(PKIX_List_GetItem
(state->extKeyUsageList,
i,
(PKIX_PL_Object **) &name,
plContext),
PKIX_LISTGETITEMFAILED);
PKIX_CHECK(pkix_List_Contains
(certExtKeyUsageList,
(PKIX_PL_Object *) name,
&checkPassed,
plContext),
PKIX_LISTCONTAINSFAILED);
PKIX_DECREF(name);
if (checkPassed != PKIX_TRUE) {
PKIX_ERROR
(PKIX_EXTENDEDKEYUSAGECHECKINGFAILED);
}
}
}
} else {
/* Check key usage and cert type based on certificate usage. */
PKIX_CHECK(PKIX_PL_Cert_VerifyCertAndKeyType(cert, PKIX_TRUE,
plContext),
PKIX_CERTVERIFYCERTTYPEFAILED);
}
/* Remove Critical Extension OID from list */
if (unresolvedCriticalExtensions != NULL) {
PKIX_CHECK(pkix_List_Remove
(unresolvedCriticalExtensions,
(PKIX_PL_Object *) state->extKeyUsageOID,
plContext),
PKIX_LISTREMOVEFAILED);
PKIX_CHECK(PKIX_PL_Cert_GetSubject
(cert, &certSubjectName, plContext),
PKIX_CERTGETSUBJECTFAILED);
if (certSubjAltNames != NULL) {
PKIX_CHECK(pkix_List_Remove
(unresolvedCriticalExtensions,
(PKIX_PL_Object *) state->subjAltNameOID,
plContext),
PKIX_LISTREMOVEFAILED);
}
}
cleanup:
PKIX_DECREF(name);
PKIX_DECREF(nameConstraints);
PKIX_DECREF(certSubjAltNames);
PKIX_DECREF(certExtKeyUsageList);
PKIX_DECREF(certSubjectName);
PKIX_DECREF(state);
PKIX_RETURN(CERTCHAINCHECKER);
}
/*
* FUNCTION: pkix_TargetCertChecker_Initialize
* DESCRIPTION:
*
* Creates a new CertChainChecker and stores it at "pChecker", where it will
* used by pkix_TargetCertChecker_Check to check that the final certificate
* of a chain meets the criteria of the CertSelector pointed to by
* "certSelector". The number of certs remaining in the chain, represented by
* "certsRemaining" is used to initialize the checker's state.
*
* PARAMETERS:
* "certSelector"
* Address of CertSelector representing the criteria against which the
* final certificate in a chain is to be matched. May be NULL.
* "certsRemaining"
* Number of certificates remaining in the chain.
* "pChecker"
* Address where object pointer will be stored. Must be non-NULL.
* "plContext"
* Platform-specific context pointer.
* THREAD SAFETY:
* Thread Safe (see Thread Safety Definitions in Programmer's Guide)
* RETURNS:
* Returns NULL if the function succeeds.
* Returns a CertChainChecker Error if the function fails in a non-fatal way.
* Returns a Fatal Error if the function fails in an unrecoverable way.
*/
PKIX_Error *
pkix_TargetCertChecker_Initialize(
PKIX_CertSelector *certSelector,
PKIX_UInt32 certsRemaining,
PKIX_CertChainChecker **pChecker,
void *plContext)
{
pkix_TargetCertCheckerState *state = NULL;
PKIX_ENTER(CERTCHAINCHECKER, "pkix_TargetCertChecker_Initialize");
PKIX_NULLCHECK_ONE(pChecker);
PKIX_CHECK(pkix_TargetCertCheckerState_Create
(certSelector, certsRemaining, &state, plContext),
PKIX_TARGETCERTCHECKERSTATECREATEFAILED);
PKIX_CHECK(PKIX_CertChainChecker_Create
(pkix_TargetCertChecker_Check,
PKIX_FALSE,
PKIX_FALSE,
NULL,
(PKIX_PL_Object *)state,
pChecker,
plContext),
PKIX_CERTCHAINCHECKERCREATEFAILED);
cleanup:
PKIX_DECREF(state);
PKIX_RETURN(CERTCHAINCHECKER);
}