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