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