/* 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); }