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_tools.c michael@0: * michael@0: * Private Utility Functions michael@0: * michael@0: */ michael@0: michael@0: #include "pkix_tools.h" michael@0: michael@0: #define CACHE_ITEM_PERIOD_SECONDS (3600) /* one hour */ michael@0: michael@0: /* michael@0: * This cahce period is only for CertCache. A Cert from a trusted CertStore michael@0: * should be checked more frequently for update new arrival, etc. michael@0: */ michael@0: #define CACHE_TRUST_ITEM_PERIOD_SECONDS (CACHE_ITEM_PERIOD_SECONDS/10) michael@0: michael@0: extern PKIX_PL_HashTable *cachedCertChainTable; michael@0: extern PKIX_PL_HashTable *cachedCertTable; michael@0: extern PKIX_PL_HashTable *cachedCrlEntryTable; michael@0: michael@0: /* Following variables are used to checked cache hits - can be taken out */ michael@0: extern int pkix_ccAddCount; michael@0: extern int pkix_ccLookupCount; michael@0: extern int pkix_ccRemoveCount; michael@0: extern int pkix_cAddCount; michael@0: extern int pkix_cLookupCount; michael@0: extern int pkix_cRemoveCount; michael@0: extern int pkix_ceAddCount; michael@0: extern int pkix_ceLookupCount; michael@0: michael@0: #ifdef PKIX_OBJECT_LEAK_TEST michael@0: /* Following variables are used for object leak test */ michael@0: char *nonNullValue = "Non Empty Value"; michael@0: PKIX_Boolean noErrorState = PKIX_TRUE; michael@0: PKIX_Boolean runningLeakTest; michael@0: PKIX_Boolean errorGenerated; michael@0: PKIX_UInt32 stackPosition; michael@0: PKIX_UInt32 *fnStackInvCountArr; michael@0: char **fnStackNameArr; michael@0: PLHashTable *fnInvTable; michael@0: PKIX_UInt32 testStartFnStackPosition; michael@0: char *errorFnStackString; michael@0: #endif /* PKIX_OBJECT_LEAK_TEST */ michael@0: michael@0: /* --Private-Functions-------------------------------------------- */ michael@0: michael@0: #ifdef PKIX_OBJECT_LEAK_TEST michael@0: /* michael@0: * FUNCTION: pkix_ErrorGen_Hash michael@0: * DESCRIPTION: michael@0: * michael@0: * Hash function to be used in object leak test hash table. michael@0: * michael@0: */ michael@0: PLHashNumber PR_CALLBACK michael@0: pkix_ErrorGen_Hash (const void *key) michael@0: { michael@0: char *str = NULL; michael@0: PLHashNumber rv = (*(PRUint8*)key) << 5; michael@0: PRUint32 i, counter = 0; michael@0: PRUint8 *rvc = (PRUint8 *)&rv; michael@0: michael@0: while ((str = fnStackNameArr[counter++]) != NULL) { michael@0: PRUint32 len = strlen(str); michael@0: for( i = 0; i < len; i++ ) { michael@0: rvc[ i % sizeof(rv) ] ^= *str; michael@0: str++; michael@0: } michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: #endif /* PKIX_OBJECT_LEAK_TEST */ michael@0: michael@0: /* michael@0: * FUNCTION: pkix_IsCertSelfIssued michael@0: * DESCRIPTION: michael@0: * michael@0: * Checks whether the Cert pointed to by "cert" is self-issued and stores the michael@0: * Boolean result at "pSelfIssued". A Cert is considered self-issued if the michael@0: * Cert's issuer matches the Cert's subject. If the subject or issuer is michael@0: * not specified, a PKIX_FALSE is returned. michael@0: * michael@0: * PARAMETERS: michael@0: * "cert" michael@0: * Address of Cert used to determine whether Cert is self-issued. michael@0: * Must be non-NULL. michael@0: * "pSelfIssued" michael@0: * Address where Boolean 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 Cert 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_IsCertSelfIssued( michael@0: PKIX_PL_Cert *cert, michael@0: PKIX_Boolean *pSelfIssued, michael@0: void *plContext) michael@0: { michael@0: PKIX_PL_X500Name *subject = NULL; michael@0: PKIX_PL_X500Name *issuer = NULL; michael@0: michael@0: PKIX_ENTER(CERT, "pkix_IsCertSelfIssued"); michael@0: PKIX_NULLCHECK_TWO(cert, pSelfIssued); michael@0: michael@0: PKIX_CHECK(PKIX_PL_Cert_GetSubject(cert, &subject, plContext), michael@0: PKIX_CERTGETSUBJECTFAILED); michael@0: michael@0: PKIX_CHECK(PKIX_PL_Cert_GetIssuer(cert, &issuer, plContext), michael@0: PKIX_CERTGETISSUERFAILED); michael@0: michael@0: if (subject == NULL || issuer == NULL) { michael@0: *pSelfIssued = PKIX_FALSE; michael@0: } else { michael@0: michael@0: PKIX_CHECK(PKIX_PL_X500Name_Match michael@0: (subject, issuer, pSelfIssued, plContext), michael@0: PKIX_X500NAMEMATCHFAILED); michael@0: } michael@0: michael@0: cleanup: michael@0: PKIX_DECREF(subject); michael@0: PKIX_DECREF(issuer); michael@0: michael@0: PKIX_RETURN(CERT); michael@0: } michael@0: michael@0: /* michael@0: * FUNCTION: pkix_Throw michael@0: * DESCRIPTION: michael@0: * michael@0: * Creates an Error using the value of "errorCode", the character array michael@0: * pointed to by "funcName", the character array pointed to by "errorText", michael@0: * and the Error pointed to by "cause" (if any), and stores it at "pError". michael@0: * michael@0: * If "cause" is not NULL and has an errorCode of "PKIX_FATAL_ERROR", michael@0: * then there is no point creating a new Error object. Rather, we simply michael@0: * store "cause" at "pError". michael@0: * michael@0: * PARAMETERS: michael@0: * "errorCode" michael@0: * Value of error code. michael@0: * "funcName" michael@0: * Address of EscASCII array representing name of function throwing error. michael@0: * Must be non-NULL. michael@0: * "errnum" michael@0: * PKIX_ERRMSGNUM of error description for new error. michael@0: * "cause" michael@0: * Address of Error representing error's cause. michael@0: * "pError" 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 an Error 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_Throw( michael@0: PKIX_ERRORCLASS errorClass, michael@0: const char *funcName, michael@0: PKIX_ERRORCODE errorCode, michael@0: PKIX_ERRORCLASS overrideClass, michael@0: PKIX_Error *cause, michael@0: PKIX_Error **pError, michael@0: void *plContext) michael@0: { michael@0: PKIX_Error *error = NULL; michael@0: michael@0: PKIX_ENTER(ERROR, "pkix_Throw"); michael@0: PKIX_NULLCHECK_TWO(funcName, pError); michael@0: michael@0: *pError = NULL; michael@0: michael@0: #ifdef PKIX_OBJECT_LEAK_TEST michael@0: noErrorState = PKIX_TRUE; michael@0: if (pkixLog) { michael@0: #ifdef PKIX_ERROR_DESCRIPTION michael@0: PR_LOG(pkixLog, 4, ("Error in function \"%s\":\"%s\" with cause \"%s\"\n", michael@0: funcName, PKIX_ErrorText[errorCode], michael@0: (cause ? PKIX_ErrorText[cause->errCode] : "null"))); michael@0: #else michael@0: PR_LOG(pkixLog, 4, ("Error in function \"%s\": error code \"%d\"\n", michael@0: funcName, errorCode)); michael@0: #endif /* PKIX_ERROR_DESCRIPTION */ michael@0: PORT_Assert(strcmp(funcName, "PKIX_PL_Object_DecRef")); michael@0: } michael@0: #endif /* PKIX_OBJECT_LEAK_TEST */ michael@0: michael@0: /* if cause has error class of PKIX_FATAL_ERROR, return immediately */ michael@0: if (cause) { michael@0: if (cause->errClass == PKIX_FATAL_ERROR){ michael@0: PKIX_INCREF(cause); michael@0: *pError = cause; michael@0: goto cleanup; michael@0: } michael@0: } michael@0: michael@0: if (overrideClass == PKIX_FATAL_ERROR){ michael@0: errorClass = overrideClass; michael@0: } michael@0: michael@0: pkixTempResult = PKIX_Error_Create(errorClass, cause, NULL, michael@0: errorCode, &error, plContext); michael@0: michael@0: if (!pkixTempResult) { michael@0: /* Setting plErr error code: michael@0: * get it from PORT_GetError if it is a leaf error and michael@0: * default error code does not exist(eq 0) */ michael@0: if (!cause && !error->plErr) { michael@0: error->plErr = PKIX_PL_GetPLErrorCode(); michael@0: } michael@0: } michael@0: michael@0: *pError = error; michael@0: michael@0: cleanup: michael@0: michael@0: PKIX_DEBUG_EXIT(ERROR); michael@0: pkixErrorClass = 0; michael@0: #ifdef PKIX_OBJECT_LEAK_TEST michael@0: noErrorState = PKIX_FALSE; michael@0: michael@0: if (runningLeakTest && fnStackNameArr) { michael@0: PR_LOG(pkixLog, 5, michael@0: ("%s%*s<- %s(%d) - %s\n", (errorGenerated ? "*" : " "), michael@0: stackPosition, " ", fnStackNameArr[stackPosition], michael@0: stackPosition, myFuncName)); michael@0: fnStackNameArr[stackPosition--] = NULL; michael@0: } michael@0: #endif /* PKIX_OBJECT_LEAK_TEST */ michael@0: return (pkixTempResult); michael@0: } michael@0: michael@0: /* michael@0: * FUNCTION: pkix_CheckTypes michael@0: * DESCRIPTION: michael@0: * michael@0: * Checks that the types of the Object pointed to by "first" and the Object michael@0: * pointed to by "second" are both equal to the value of "type". If they michael@0: * are not equal, a PKIX_Error is returned. michael@0: * michael@0: * PARAMETERS: michael@0: * "first" michael@0: * Address of first Object. Must be non-NULL. michael@0: * "second" michael@0: * Address of second Object. Must be non-NULL. michael@0: * "type" michael@0: * Value of type to check against. 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 an Error 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_CheckTypes( michael@0: PKIX_PL_Object *first, michael@0: PKIX_PL_Object *second, michael@0: PKIX_UInt32 type, michael@0: void *plContext) michael@0: { michael@0: PKIX_UInt32 firstType, secondType; michael@0: michael@0: PKIX_ENTER(OBJECT, "pkix_CheckTypes"); michael@0: PKIX_NULLCHECK_TWO(first, second); michael@0: michael@0: PKIX_CHECK(PKIX_PL_Object_GetType(first, &firstType, plContext), michael@0: PKIX_COULDNOTGETFIRSTOBJECTTYPE); michael@0: michael@0: PKIX_CHECK(PKIX_PL_Object_GetType(second, &secondType, plContext), michael@0: PKIX_COULDNOTGETSECONDOBJECTTYPE); michael@0: michael@0: if ((firstType != type)||(firstType != secondType)) { michael@0: PKIX_ERROR(PKIX_OBJECTTYPESDONOTMATCH); michael@0: } michael@0: michael@0: cleanup: michael@0: michael@0: PKIX_RETURN(OBJECT); michael@0: } michael@0: michael@0: /* michael@0: * FUNCTION: pkix_CheckType michael@0: * DESCRIPTION: michael@0: * michael@0: * Checks that the type of the Object pointed to by "object" is equal to the michael@0: * value of "type". If it is not equal, a PKIX_Error is returned. michael@0: * michael@0: * PARAMETERS: michael@0: * "object" michael@0: * Address of Object. Must be non-NULL. michael@0: * "type" michael@0: * Value of type to check against. 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 an Error 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_CheckType( michael@0: PKIX_PL_Object *object, michael@0: PKIX_UInt32 type, michael@0: void *plContext) michael@0: { michael@0: return (pkix_CheckTypes(object, object, type, plContext)); michael@0: } michael@0: michael@0: /* michael@0: * FUNCTION: pkix_hash michael@0: * DESCRIPTION: michael@0: * michael@0: * Computes a hash value for "length" bytes starting at the array of bytes michael@0: * pointed to by "bytes" and stores the result at "pHash". michael@0: * michael@0: * XXX To speed this up, we could probably read 32 bits at a time from michael@0: * bytes (maybe even 64 bits on some platforms) michael@0: * michael@0: * PARAMETERS: michael@0: * "bytes" michael@0: * Address of array of bytes to hash. Must be non-NULL. michael@0: * "length" michael@0: * Number of bytes to hash. michael@0: * "pHash" 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 Fatal Error if the function fails in an unrecoverable way. michael@0: */ michael@0: PKIX_Error * michael@0: pkix_hash( michael@0: const unsigned char *bytes, michael@0: PKIX_UInt32 length, michael@0: PKIX_UInt32 *pHash, michael@0: void *plContext) michael@0: { michael@0: PKIX_UInt32 i; michael@0: PKIX_UInt32 hash; michael@0: michael@0: PKIX_ENTER(OBJECT, "pkix_hash"); michael@0: if (length != 0) { michael@0: PKIX_NULLCHECK_ONE(bytes); michael@0: } michael@0: PKIX_NULLCHECK_ONE(pHash); michael@0: michael@0: hash = 0; michael@0: for (i = 0; i < length; i++) { michael@0: /* hash = 31 * hash + bytes[i]; */ michael@0: hash = (hash << 5) - hash + bytes[i]; michael@0: } michael@0: michael@0: *pHash = hash; michael@0: michael@0: PKIX_RETURN(OBJECT); michael@0: } michael@0: michael@0: /* michael@0: * FUNCTION: pkix_countArray michael@0: * DESCRIPTION: michael@0: * michael@0: * Counts the number of elements in the null-terminated array of pointers michael@0: * pointed to by "array" and returns the result. michael@0: * michael@0: * PARAMETERS michael@0: * "array" michael@0: * Address of null-terminated array of pointers. michael@0: * THREAD SAFETY: michael@0: * Thread Safe (see Thread Safety Definitions in Programmer's Guide) michael@0: * RETURNS: michael@0: * Returns the number of elements in the array. michael@0: */ michael@0: PKIX_UInt32 michael@0: pkix_countArray(void **array) michael@0: { michael@0: PKIX_UInt32 count = 0; michael@0: michael@0: if (array) { michael@0: while (*array++) { michael@0: count++; michael@0: } michael@0: } michael@0: return (count); michael@0: } michael@0: michael@0: /* michael@0: * FUNCTION: pkix_duplicateImmutable michael@0: * DESCRIPTION: michael@0: * michael@0: * Convenience callback function used for duplicating immutable objects. michael@0: * Since the objects can not be modified, this function simply increments the michael@0: * reference count on the object, and returns a reference to that object. michael@0: * michael@0: * (see comments for PKIX_PL_DuplicateCallback in pkix_pl_system.h) michael@0: */ michael@0: PKIX_Error * michael@0: pkix_duplicateImmutable( michael@0: PKIX_PL_Object *object, michael@0: PKIX_PL_Object **pNewObject, michael@0: void *plContext) michael@0: { michael@0: PKIX_ENTER(OBJECT, "pkix_duplicateImmutable"); michael@0: PKIX_NULLCHECK_TWO(object, pNewObject); michael@0: michael@0: PKIX_INCREF(object); michael@0: michael@0: *pNewObject = object; michael@0: michael@0: cleanup: michael@0: PKIX_RETURN(OBJECT); michael@0: } michael@0: michael@0: /* --String-Encoding-Conversion-Functions------------------------ */ michael@0: michael@0: /* michael@0: * FUNCTION: pkix_hex2i michael@0: * DESCRIPTION: michael@0: * michael@0: * Converts hexadecimal character "c" to its integer value and returns result. michael@0: * michael@0: * PARAMETERS michael@0: * "c" michael@0: * Character to convert to a hex value. michael@0: * THREAD SAFETY: michael@0: * Thread Safe (see Thread Safety Definitions in Programmer's Guide) michael@0: * RETURNS: michael@0: * The hexadecimal value of "c". Otherwise -1. (Unsigned 0xFFFFFFFF). michael@0: */ michael@0: PKIX_UInt32 michael@0: pkix_hex2i(char c) michael@0: { michael@0: if ((c >= '0')&&(c <= '9')) michael@0: return (c-'0'); michael@0: else if ((c >= 'a')&&(c <= 'f')) michael@0: return (c-'a'+10); michael@0: else if ((c >= 'A')&&(c <= 'F')) michael@0: return (c-'A'+10); michael@0: else michael@0: return ((PKIX_UInt32)(-1)); michael@0: } michael@0: michael@0: /* michael@0: * FUNCTION: pkix_i2hex michael@0: * DESCRIPTION: michael@0: * michael@0: * Converts integer value "digit" to its ASCII hex value michael@0: * michael@0: * PARAMETERS michael@0: * "digit" michael@0: * Value of integer to convert to ASCII hex value. Must be 0-15. michael@0: * THREAD SAFETY: michael@0: * Thread Safe (see Thread Safety Definitions in Programmer's Guide) michael@0: * RETURNS: michael@0: * The ASCII hexadecimal value of "digit". michael@0: */ michael@0: char michael@0: pkix_i2hex(char digit) michael@0: { michael@0: if ((digit >= 0)&&(digit <= 9)) michael@0: return (digit+'0'); michael@0: else if ((digit >= 0xa)&&(digit <= 0xf)) michael@0: return (digit - 10 + 'a'); michael@0: else michael@0: return (-1); michael@0: } michael@0: michael@0: /* michael@0: * FUNCTION: pkix_isPlaintext michael@0: * DESCRIPTION: michael@0: * michael@0: * Returns whether character "c" is plaintext using EscASCII or EscASCII_Debug michael@0: * depending on the value of "debug". michael@0: * michael@0: * In EscASCII, [01, 7E] except '&' are plaintext. michael@0: * In EscASCII_Debug [20, 7E] except '&' are plaintext. michael@0: * michael@0: * PARAMETERS: michael@0: * "c" michael@0: * Character to check. michael@0: * "debug" michael@0: * Value of debug flag. michael@0: * THREAD SAFETY: michael@0: * Thread Safe (see Thread Safety Definitions in Programmer's Guide) michael@0: * RETURNS: michael@0: * True if "c" is plaintext. michael@0: */ michael@0: PKIX_Boolean michael@0: pkix_isPlaintext(unsigned char c, PKIX_Boolean debug) { michael@0: return ((c >= 0x01)&&(c <= 0x7E)&&(c != '&')&&(!debug || (c >= 20))); michael@0: } michael@0: michael@0: /* --Cache-Functions------------------------ */ michael@0: michael@0: /* michael@0: * FUNCTION: pkix_CacheCertChain_Lookup michael@0: * DESCRIPTION: michael@0: * michael@0: * Look up CertChain Hash Table for a cached BuildResult based on "targetCert" michael@0: * and "anchors" as the hash keys. If there is no item to match the key, michael@0: * PKIX_FALSE is stored at "pFound". If an item is found, its cache time is michael@0: * compared to "testDate". If expired, the item is removed and PKIX_FALSE is michael@0: * stored at "pFound". Otherwise, PKIX_TRUE is stored at "pFound" and the michael@0: * BuildResult is stored at "pBuildResult". michael@0: * The hashtable is maintained in the following ways: michael@0: * 1) When creating the hashtable, maximum bucket size can be specified (0 for michael@0: * unlimited). If items in a bucket reaches its full size, an new addition michael@0: * will trigger the removal of the old as FIFO sequence. michael@0: * 2) A PKIX_PL_Date created with current time offset by constant michael@0: * CACHE_ITEM_PERIOD_SECONDS is attached to each item in the Hash Table. michael@0: * When an item is retrieved, this date is compared against "testDate" for michael@0: * validity. If comparison indicates this item is expired, the item is michael@0: * removed from the bucket. michael@0: * michael@0: * PARAMETERS: michael@0: * "targetCert" michael@0: * Address of Target Cert as key to retrieve this CertChain. Must be michael@0: * non-NULL. michael@0: * "anchors" michael@0: * Address of PKIX_List of "anchors" is used as key to retrive CertChain. michael@0: * Must be non-NULL. michael@0: * "testDate" michael@0: * Address of PKIX_PL_Date for verifying time validity and cache validity. michael@0: * May be NULL. If testDate is NULL, this cache item will not be out-dated. michael@0: * "pFound" michael@0: * Address of PKIX_Boolean indicating valid data is found. michael@0: * Must be non-NULL. michael@0: * "pBuildResult" michael@0: * Address where BuildResult 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 an Error 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_CacheCertChain_Lookup( michael@0: PKIX_PL_Cert* targetCert, michael@0: PKIX_List* anchors, michael@0: PKIX_PL_Date *testDate, michael@0: PKIX_Boolean *pFound, michael@0: PKIX_BuildResult **pBuildResult, michael@0: void *plContext) michael@0: { michael@0: PKIX_List *cachedValues = NULL; michael@0: PKIX_List *cachedKeys = NULL; michael@0: PKIX_Error *cachedCertChainError = NULL; michael@0: PKIX_PL_Date *cacheValidUntilDate = NULL; michael@0: PKIX_PL_Date *validityDate = NULL; michael@0: PKIX_Int32 cmpValidTimeResult = 0; michael@0: PKIX_Int32 cmpCacheTimeResult = 0; michael@0: michael@0: PKIX_ENTER(BUILD, "pkix_CacheCertChain_Lookup"); michael@0: michael@0: PKIX_NULLCHECK_FOUR(targetCert, anchors, pFound, pBuildResult); michael@0: michael@0: *pFound = PKIX_FALSE; michael@0: michael@0: /* use trust anchors and target cert as hash key */ michael@0: michael@0: PKIX_CHECK(PKIX_List_Create(&cachedKeys, plContext), michael@0: PKIX_LISTCREATEFAILED); michael@0: michael@0: PKIX_CHECK(PKIX_List_AppendItem michael@0: (cachedKeys, michael@0: (PKIX_PL_Object *)targetCert, michael@0: plContext), michael@0: PKIX_LISTAPPENDITEMFAILED); michael@0: michael@0: PKIX_CHECK(PKIX_List_AppendItem michael@0: (cachedKeys, michael@0: (PKIX_PL_Object *)anchors, michael@0: plContext), michael@0: PKIX_LISTAPPENDITEMFAILED); michael@0: michael@0: cachedCertChainError = PKIX_PL_HashTable_Lookup michael@0: (cachedCertChainTable, michael@0: (PKIX_PL_Object *) cachedKeys, michael@0: (PKIX_PL_Object **) &cachedValues, michael@0: plContext); michael@0: michael@0: pkix_ccLookupCount++; michael@0: michael@0: /* retrieve data from hashed value list */ michael@0: michael@0: if (cachedValues != NULL && cachedCertChainError == NULL) { michael@0: michael@0: PKIX_CHECK(PKIX_List_GetItem michael@0: (cachedValues, michael@0: 0, michael@0: (PKIX_PL_Object **) &cacheValidUntilDate, michael@0: plContext), michael@0: PKIX_LISTGETITEMFAILED); michael@0: michael@0: /* check validity time and cache age time */ michael@0: PKIX_CHECK(PKIX_List_GetItem michael@0: (cachedValues, michael@0: 1, michael@0: (PKIX_PL_Object **) &validityDate, michael@0: plContext), michael@0: PKIX_LISTGETITEMFAILED); michael@0: michael@0: /* if testDate is not set, this cache item is not out-dated */ michael@0: if (testDate) { michael@0: michael@0: PKIX_CHECK(PKIX_PL_Object_Compare michael@0: ((PKIX_PL_Object *)testDate, michael@0: (PKIX_PL_Object *)cacheValidUntilDate, michael@0: &cmpCacheTimeResult, michael@0: plContext), michael@0: PKIX_OBJECTCOMPARATORFAILED); michael@0: michael@0: PKIX_CHECK(PKIX_PL_Object_Compare michael@0: ((PKIX_PL_Object *)testDate, michael@0: (PKIX_PL_Object *)validityDate, michael@0: &cmpValidTimeResult, michael@0: plContext), michael@0: PKIX_OBJECTCOMPARATORFAILED); michael@0: } michael@0: michael@0: /* certs' date are all valid and cache item is not old */ michael@0: if (cmpValidTimeResult <= 0 && cmpCacheTimeResult <=0) { michael@0: michael@0: PKIX_CHECK(PKIX_List_GetItem michael@0: (cachedValues, michael@0: 2, michael@0: (PKIX_PL_Object **) pBuildResult, michael@0: plContext), michael@0: PKIX_LISTGETITEMFAILED); michael@0: michael@0: *pFound = PKIX_TRUE; michael@0: michael@0: } else { michael@0: michael@0: pkix_ccRemoveCount++; michael@0: *pFound = PKIX_FALSE; michael@0: michael@0: /* out-dated item, remove it from cache */ michael@0: PKIX_CHECK(PKIX_PL_HashTable_Remove michael@0: (cachedCertChainTable, michael@0: (PKIX_PL_Object *) cachedKeys, michael@0: plContext), michael@0: PKIX_HASHTABLEREMOVEFAILED); michael@0: } michael@0: } michael@0: michael@0: cleanup: michael@0: michael@0: PKIX_DECREF(cachedValues); michael@0: PKIX_DECREF(cachedKeys); michael@0: PKIX_DECREF(cachedCertChainError); michael@0: PKIX_DECREF(cacheValidUntilDate); michael@0: PKIX_DECREF(validityDate); michael@0: michael@0: PKIX_RETURN(BUILD); michael@0: michael@0: } michael@0: michael@0: /* michael@0: * FUNCTION: pkix_CacheCertChain_Remove michael@0: * DESCRIPTION: michael@0: * michael@0: * Remove CertChain Hash Table entry based on "targetCert" and "anchors" michael@0: * as the hash keys. If there is no item to match the key, no action is michael@0: * taken. michael@0: * The hashtable is maintained in the following ways: michael@0: * 1) When creating the hashtable, maximum bucket size can be specified (0 for michael@0: * unlimited). If items in a bucket reaches its full size, an new addition michael@0: * will trigger the removal of the old as FIFO sequence. michael@0: * 2) A PKIX_PL_Date created with current time offset by constant michael@0: * CACHE_ITEM_PERIOD_SECONDS is attached to each item in the Hash Table. michael@0: * When an item is retrieved, this date is compared against "testDate" for michael@0: * validity. If comparison indicates this item is expired, the item is michael@0: * removed from the bucket. michael@0: * michael@0: * PARAMETERS: michael@0: * "targetCert" michael@0: * Address of Target Cert as key to retrieve this CertChain. Must be michael@0: * non-NULL. michael@0: * "anchors" michael@0: * Address of PKIX_List of "anchors" is used as key to retrive CertChain. michael@0: * 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 an Error 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_CacheCertChain_Remove( michael@0: PKIX_PL_Cert* targetCert, michael@0: PKIX_List* anchors, michael@0: void *plContext) michael@0: { michael@0: PKIX_List *cachedKeys = NULL; michael@0: michael@0: PKIX_ENTER(BUILD, "pkix_CacheCertChain_Remove"); michael@0: PKIX_NULLCHECK_TWO(targetCert, anchors); michael@0: michael@0: /* use trust anchors and target cert as hash key */ michael@0: michael@0: PKIX_CHECK(PKIX_List_Create(&cachedKeys, plContext), michael@0: PKIX_LISTCREATEFAILED); michael@0: michael@0: PKIX_CHECK(PKIX_List_AppendItem michael@0: (cachedKeys, michael@0: (PKIX_PL_Object *)targetCert, michael@0: plContext), michael@0: PKIX_LISTAPPENDITEMFAILED); michael@0: michael@0: PKIX_CHECK(PKIX_List_AppendItem michael@0: (cachedKeys, michael@0: (PKIX_PL_Object *)anchors, michael@0: plContext), michael@0: PKIX_LISTAPPENDITEMFAILED); michael@0: michael@0: PKIX_CHECK_ONLY_FATAL(PKIX_PL_HashTable_Remove michael@0: (cachedCertChainTable, michael@0: (PKIX_PL_Object *) cachedKeys, michael@0: plContext), michael@0: PKIX_HASHTABLEREMOVEFAILED); michael@0: michael@0: pkix_ccRemoveCount++; michael@0: michael@0: cleanup: michael@0: michael@0: PKIX_DECREF(cachedKeys); michael@0: michael@0: PKIX_RETURN(BUILD); michael@0: michael@0: } michael@0: michael@0: /* michael@0: * FUNCTION: pkix_CacheCertChain_Add michael@0: * DESCRIPTION: michael@0: * michael@0: * Add a BuildResult to the CertChain Hash Table for a "buildResult" with michael@0: * "targetCert" and "anchors" as the hash keys. michael@0: * "validityDate" is the most restricted notAfter date of all Certs in michael@0: * this CertChain and is verified when this BuildChain is retrieved. michael@0: * The hashtable is maintained in the following ways: michael@0: * 1) When creating the hashtable, maximum bucket size can be specified (0 for michael@0: * unlimited). If items in a bucket reaches its full size, an new addition michael@0: * will trigger the removal of the old as FIFO sequence. michael@0: * 2) A PKIX_PL_Date created with current time offset by constant michael@0: * CACHE_ITEM_PERIOD_SECONDS is attached to each item in the Hash Table. michael@0: * When an item is retrieved, this date is compared against "testDate" for michael@0: * validity. If comparison indicates this item is expired, the item is michael@0: * removed from the bucket. michael@0: * michael@0: * PARAMETERS: michael@0: * "targetCert" michael@0: * Address of Target Cert as key to retrieve this CertChain. Must be michael@0: * non-NULL. michael@0: * "anchors" michael@0: * Address of PKIX_List of "anchors" is used as key to retrive CertChain. michael@0: * Must be non-NULL. michael@0: * "validityDate" michael@0: * Address of PKIX_PL_Date contains the most restriced notAfter time of michael@0: * all "certs". Must be non-NULL. michael@0: * Address of PKIX_Boolean indicating valid data is found. michael@0: * Must be non-NULL. michael@0: * "buildResult" michael@0: * Address of BuildResult to be cached. 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 an Error 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_CacheCertChain_Add( michael@0: PKIX_PL_Cert* targetCert, michael@0: PKIX_List* anchors, michael@0: PKIX_PL_Date *validityDate, michael@0: PKIX_BuildResult *buildResult, michael@0: void *plContext) michael@0: { michael@0: PKIX_List *cachedValues = NULL; michael@0: PKIX_List *cachedKeys = NULL; michael@0: PKIX_Error *cachedCertChainError = NULL; michael@0: PKIX_PL_Date *cacheValidUntilDate = NULL; michael@0: michael@0: PKIX_ENTER(BUILD, "pkix_CacheCertChain_Add"); michael@0: michael@0: PKIX_NULLCHECK_FOUR(targetCert, anchors, validityDate, buildResult); michael@0: michael@0: PKIX_CHECK(PKIX_List_Create(&cachedKeys, plContext), michael@0: PKIX_LISTCREATEFAILED); michael@0: michael@0: PKIX_CHECK(PKIX_List_AppendItem michael@0: (cachedKeys, (PKIX_PL_Object *)targetCert, plContext), michael@0: PKIX_LISTAPPENDITEMFAILED); michael@0: michael@0: PKIX_CHECK(PKIX_List_AppendItem michael@0: (cachedKeys, (PKIX_PL_Object *)anchors, plContext), michael@0: PKIX_LISTAPPENDITEMFAILED); michael@0: michael@0: PKIX_CHECK(PKIX_List_Create(&cachedValues, plContext), michael@0: PKIX_LISTCREATEFAILED); michael@0: michael@0: PKIX_CHECK(PKIX_PL_Date_Create_CurrentOffBySeconds michael@0: (CACHE_ITEM_PERIOD_SECONDS, michael@0: &cacheValidUntilDate, michael@0: plContext), michael@0: PKIX_DATECREATECURRENTOFFBYSECONDSFAILED); michael@0: michael@0: PKIX_CHECK(PKIX_List_AppendItem michael@0: (cachedValues, michael@0: (PKIX_PL_Object *)cacheValidUntilDate, michael@0: plContext), michael@0: PKIX_LISTAPPENDITEMFAILED); michael@0: michael@0: PKIX_CHECK(PKIX_List_AppendItem michael@0: (cachedValues, (PKIX_PL_Object *)validityDate, plContext), michael@0: PKIX_LISTAPPENDITEMFAILED); michael@0: michael@0: PKIX_CHECK(PKIX_List_AppendItem michael@0: (cachedValues, (PKIX_PL_Object *)buildResult, plContext), michael@0: PKIX_LISTAPPENDITEMFAILED); michael@0: michael@0: cachedCertChainError = PKIX_PL_HashTable_Add michael@0: (cachedCertChainTable, michael@0: (PKIX_PL_Object *) cachedKeys, michael@0: (PKIX_PL_Object *) cachedValues, michael@0: plContext); michael@0: michael@0: pkix_ccAddCount++; michael@0: michael@0: if (cachedCertChainError != NULL) { michael@0: PKIX_DEBUG("PKIX_PL_HashTable_Add for CertChain skipped: " michael@0: "entry existed\n"); michael@0: } michael@0: michael@0: cleanup: michael@0: michael@0: PKIX_DECREF(cachedValues); michael@0: PKIX_DECREF(cachedKeys); michael@0: PKIX_DECREF(cachedCertChainError); michael@0: PKIX_DECREF(cacheValidUntilDate); michael@0: michael@0: PKIX_RETURN(BUILD); michael@0: } michael@0: michael@0: /* michael@0: * FUNCTION: pkix_CacheCert_Lookup michael@0: * DESCRIPTION: michael@0: * michael@0: * Look up Cert Hash Table for a cached item based on "store" and Subject in michael@0: * "certSelParams" as the hash keys and returns values Certs in "pCerts". michael@0: * If there isn't an item to match the key, a PKIX_FALSE is returned at michael@0: * "pFound". The item's cache time is verified with "testDate". If out-dated, michael@0: * this item is removed and PKIX_FALSE is returned at "pFound". michael@0: * This hashtable is maintained in the following ways: michael@0: * 1) When creating the hashtable, maximum bucket size can be specified (0 for michael@0: * unlimited). If items in a bucket reaches its full size, an new addition michael@0: * will trigger the removal of the old as FIFO sequence. michael@0: * 2) A PKIX_PL_Date created with current time offset by constant michael@0: * CACHE_ITEM_PERIOD_SECONDS is attached to each item in the Hash Table. michael@0: * If the CertStore this Cert is from is a trusted one, the cache period is michael@0: * shorter so cache can be updated more frequently. michael@0: * When an item is retrieved, this date is compared against "testDate" for michael@0: * validity. If comparison indicates this item is expired, the item is michael@0: * removed from the bucket. michael@0: * michael@0: * PARAMETERS: michael@0: * "store" michael@0: * Address of CertStore as key to retrieve this CertChain. Must be michael@0: * non-NULL. michael@0: * "certSelParams" michael@0: * Address of ComCertSelParams that its subject is used as key to retrieve michael@0: * this CertChain. Must be non-NULL. michael@0: * "testDate" michael@0: * Address of PKIX_PL_Date for verifying time cache validity. michael@0: * Must be non-NULL. If testDate is NULL, this cache item won't be out michael@0: * dated. michael@0: * "pFound" michael@0: * Address of KPKIX_Boolean indicating valid data is found. michael@0: * Must be non-NULL. michael@0: * "pCerts" michael@0: * Address PKIX_List where the CertChain will be stored. Must be no-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 an Error 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_CacheCert_Lookup( michael@0: PKIX_CertStore *store, michael@0: PKIX_ComCertSelParams *certSelParams, michael@0: PKIX_PL_Date *testDate, michael@0: PKIX_Boolean *pFound, michael@0: PKIX_List** pCerts, michael@0: void *plContext) michael@0: { michael@0: PKIX_PL_Cert *cert = NULL; michael@0: PKIX_List *cachedKeys = NULL; michael@0: PKIX_List *cachedValues = NULL; michael@0: PKIX_List *cachedCertList = NULL; michael@0: PKIX_List *selCertList = NULL; michael@0: PKIX_PL_X500Name *subject = NULL; michael@0: PKIX_PL_Date *invalidAfterDate = NULL; michael@0: PKIX_PL_Date *cacheValidUntilDate = NULL; michael@0: PKIX_CertSelector *certSel = NULL; michael@0: PKIX_Error *cachedCertError = NULL; michael@0: PKIX_Error *selectorError = NULL; michael@0: PKIX_CertSelector_MatchCallback selectorMatch = NULL; michael@0: PKIX_Int32 cmpValidTimeResult = PKIX_FALSE; michael@0: PKIX_Int32 cmpCacheTimeResult = 0; michael@0: PKIX_UInt32 numItems = 0; michael@0: PKIX_UInt32 i; michael@0: michael@0: PKIX_ENTER(BUILD, "pkix_CacheCert_Lookup"); michael@0: PKIX_NULLCHECK_TWO(store, certSelParams); michael@0: PKIX_NULLCHECK_TWO(pFound, pCerts); michael@0: michael@0: *pFound = PKIX_FALSE; michael@0: michael@0: PKIX_CHECK(PKIX_List_Create(&cachedKeys, plContext), michael@0: PKIX_LISTCREATEFAILED); michael@0: michael@0: PKIX_CHECK(PKIX_List_AppendItem michael@0: (cachedKeys, (PKIX_PL_Object *)store, plContext), michael@0: PKIX_LISTAPPENDITEMFAILED); michael@0: michael@0: PKIX_CHECK(PKIX_ComCertSelParams_GetSubject michael@0: (certSelParams, &subject, plContext), michael@0: PKIX_COMCERTSELPARAMSGETSUBJECTFAILED); michael@0: michael@0: PKIX_NULLCHECK_ONE(subject); michael@0: michael@0: PKIX_CHECK(PKIX_List_AppendItem michael@0: (cachedKeys, (PKIX_PL_Object *)subject, plContext), michael@0: PKIX_LISTAPPENDITEMFAILED); michael@0: michael@0: cachedCertError = PKIX_PL_HashTable_Lookup michael@0: (cachedCertTable, michael@0: (PKIX_PL_Object *) cachedKeys, michael@0: (PKIX_PL_Object **) &cachedValues, michael@0: plContext); michael@0: pkix_cLookupCount++; michael@0: michael@0: if (cachedValues != NULL && cachedCertError == NULL) { michael@0: michael@0: PKIX_CHECK(PKIX_List_GetItem michael@0: (cachedValues, michael@0: 0, michael@0: (PKIX_PL_Object **) &cacheValidUntilDate, michael@0: plContext), michael@0: PKIX_LISTGETITEMFAILED); michael@0: michael@0: if (testDate) { michael@0: PKIX_CHECK(PKIX_PL_Object_Compare michael@0: ((PKIX_PL_Object *)testDate, michael@0: (PKIX_PL_Object *)cacheValidUntilDate, michael@0: &cmpCacheTimeResult, michael@0: plContext), michael@0: PKIX_OBJECTCOMPARATORFAILED); michael@0: } michael@0: michael@0: if (cmpCacheTimeResult <= 0) { michael@0: michael@0: PKIX_CHECK(PKIX_List_GetItem michael@0: (cachedValues, michael@0: 1, michael@0: (PKIX_PL_Object **) &cachedCertList, michael@0: plContext), michael@0: PKIX_LISTGETITEMFAILED); michael@0: michael@0: /* michael@0: * Certs put on cache satifies only for Subject, michael@0: * user selector and ComCertSelParams to filter. michael@0: */ michael@0: PKIX_CHECK(PKIX_CertSelector_Create michael@0: (NULL, NULL, &certSel, plContext), michael@0: PKIX_CERTSELECTORCREATEFAILED); michael@0: michael@0: PKIX_CHECK(PKIX_CertSelector_SetCommonCertSelectorParams michael@0: (certSel, certSelParams, plContext), michael@0: PKIX_CERTSELECTORSETCOMMONCERTSELECTORPARAMSFAILED); michael@0: michael@0: PKIX_CHECK(PKIX_CertSelector_GetMatchCallback michael@0: (certSel, &selectorMatch, plContext), michael@0: PKIX_CERTSELECTORGETMATCHCALLBACKFAILED); michael@0: michael@0: PKIX_CHECK(PKIX_List_Create(&selCertList, plContext), michael@0: PKIX_LISTCREATEFAILED); michael@0: michael@0: /* michael@0: * If any of the Cert on the list is out-dated, invalidate michael@0: * this cache item. michael@0: */ michael@0: PKIX_CHECK(PKIX_List_GetLength michael@0: (cachedCertList, &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: (cachedCertList, michael@0: i, michael@0: (PKIX_PL_Object **)&cert, michael@0: plContext), michael@0: PKIX_LISTGETITEMFAILED); michael@0: michael@0: PKIX_CHECK(PKIX_PL_Cert_GetValidityNotAfter michael@0: (cert, &invalidAfterDate, plContext), michael@0: PKIX_CERTGETVALIDITYNOTAFTERFAILED); michael@0: michael@0: if (testDate) { michael@0: PKIX_CHECK(PKIX_PL_Object_Compare michael@0: ((PKIX_PL_Object *)invalidAfterDate, michael@0: (PKIX_PL_Object *)testDate, michael@0: &cmpValidTimeResult, michael@0: plContext), michael@0: PKIX_OBJECTCOMPARATORFAILED); michael@0: } michael@0: michael@0: if (cmpValidTimeResult < 0) { michael@0: michael@0: pkix_cRemoveCount++; michael@0: *pFound = PKIX_FALSE; michael@0: michael@0: /* one cert is out-dated, remove item from cache */ michael@0: PKIX_CHECK(PKIX_PL_HashTable_Remove michael@0: (cachedCertTable, michael@0: (PKIX_PL_Object *) cachedKeys, michael@0: plContext), michael@0: PKIX_HASHTABLEREMOVEFAILED); michael@0: goto cleanup; michael@0: } michael@0: michael@0: selectorError = selectorMatch(certSel, cert, plContext); michael@0: if (!selectorError){ michael@0: /* put on the return list */ michael@0: PKIX_CHECK(PKIX_List_AppendItem michael@0: (selCertList, michael@0: (PKIX_PL_Object *)cert, michael@0: plContext), michael@0: PKIX_LISTAPPENDITEMFAILED); michael@0: } else { michael@0: PKIX_DECREF(selectorError); michael@0: } michael@0: michael@0: PKIX_DECREF(cert); michael@0: PKIX_DECREF(invalidAfterDate); michael@0: michael@0: } michael@0: michael@0: if (*pFound) { michael@0: PKIX_INCREF(selCertList); michael@0: *pCerts = selCertList; michael@0: } michael@0: michael@0: } else { michael@0: michael@0: pkix_cRemoveCount++; michael@0: *pFound = PKIX_FALSE; michael@0: /* cache item is out-dated, remove it from cache */ michael@0: PKIX_CHECK(PKIX_PL_HashTable_Remove michael@0: (cachedCertTable, michael@0: (PKIX_PL_Object *) cachedKeys, michael@0: plContext), michael@0: PKIX_HASHTABLEREMOVEFAILED); michael@0: } michael@0: michael@0: } michael@0: michael@0: cleanup: michael@0: michael@0: PKIX_DECREF(subject); michael@0: PKIX_DECREF(certSel); michael@0: PKIX_DECREF(cachedKeys); michael@0: PKIX_DECREF(cachedValues); michael@0: PKIX_DECREF(cacheValidUntilDate); michael@0: PKIX_DECREF(cert); michael@0: PKIX_DECREF(cachedCertList); michael@0: PKIX_DECREF(selCertList); michael@0: PKIX_DECREF(invalidAfterDate); michael@0: PKIX_DECREF(cachedCertError); michael@0: PKIX_DECREF(selectorError); michael@0: michael@0: PKIX_RETURN(BUILD); michael@0: } michael@0: michael@0: /* michael@0: * FUNCTION: pkix_CacheCert_Add michael@0: * DESCRIPTION: michael@0: * michael@0: * Add Cert Hash Table for a cached item based on "store" and Subject in michael@0: * "certSelParams" as the hash keys and have "certs" as the key value. michael@0: * This hashtable is maintained in the following ways: michael@0: * 1) When creating the hashtable, maximum bucket size can be specified (0 for michael@0: * unlimited). If items in a bucket reaches its full size, an new addition michael@0: * will trigger the removal of the old as FIFO sequence. michael@0: * 2) A PKIX_PL_Date created with current time offset by constant michael@0: * CACHE_ITEM_PERIOD_SECONDS is attached to each item in the Hash Table. michael@0: * If the CertStore this Cert is from is a trusted one, the cache period is michael@0: * shorter so cache can be updated more frequently. michael@0: * When an item is retrieved, this date is compared against "testDate" for michael@0: * validity. If comparison indicates this item is expired, the item is michael@0: * removed from the bucket. michael@0: * michael@0: * PARAMETERS: michael@0: * "store" michael@0: * Address of CertStore as key to retrieve this CertChain. Must be michael@0: * non-NULL. michael@0: * "certSelParams" michael@0: * Address of ComCertSelParams that its subject is used as key to retrieve michael@0: * this CertChain. Must be non-NULL. michael@0: * "certs" michael@0: * Address PKIX_List of Certs will be stored. Must be no-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 an Error 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_CacheCert_Add( michael@0: PKIX_CertStore *store, michael@0: PKIX_ComCertSelParams *certSelParams, michael@0: PKIX_List* certs, michael@0: void *plContext) michael@0: { michael@0: PKIX_List *cachedKeys = NULL; michael@0: PKIX_List *cachedValues = NULL; michael@0: PKIX_PL_Date *cacheValidUntilDate = NULL; michael@0: PKIX_PL_X500Name *subject = NULL; michael@0: PKIX_Error *cachedCertError = NULL; michael@0: PKIX_CertStore_CheckTrustCallback trustCallback = NULL; michael@0: PKIX_UInt32 cachePeriod = CACHE_ITEM_PERIOD_SECONDS; michael@0: PKIX_UInt32 numCerts = 0; michael@0: michael@0: PKIX_ENTER(BUILD, "pkix_CacheCert_Add"); michael@0: PKIX_NULLCHECK_THREE(store, certSelParams, certs); michael@0: michael@0: PKIX_CHECK(PKIX_List_GetLength(certs, &numCerts, michael@0: plContext), michael@0: PKIX_LISTGETLENGTHFAILED); michael@0: if (numCerts == 0) { michael@0: /* Don't want to add an empty list. */ michael@0: goto cleanup; michael@0: } michael@0: michael@0: PKIX_CHECK(PKIX_List_Create(&cachedKeys, plContext), michael@0: PKIX_LISTCREATEFAILED); michael@0: michael@0: PKIX_CHECK(PKIX_List_AppendItem michael@0: (cachedKeys, (PKIX_PL_Object *)store, plContext), michael@0: PKIX_LISTAPPENDITEMFAILED); michael@0: michael@0: PKIX_CHECK(PKIX_ComCertSelParams_GetSubject michael@0: (certSelParams, &subject, plContext), michael@0: PKIX_COMCERTSELPARAMSGETSUBJECTFAILED); michael@0: michael@0: PKIX_NULLCHECK_ONE(subject); michael@0: michael@0: PKIX_CHECK(PKIX_List_AppendItem michael@0: (cachedKeys, (PKIX_PL_Object *)subject, plContext), michael@0: PKIX_LISTAPPENDITEMFAILED); michael@0: michael@0: PKIX_CHECK(PKIX_List_Create(&cachedValues, plContext), michael@0: PKIX_LISTCREATEFAILED); michael@0: michael@0: PKIX_CHECK(PKIX_CertStore_GetTrustCallback michael@0: (store, &trustCallback, plContext), michael@0: PKIX_CERTSTOREGETTRUSTCALLBACKFAILED); michael@0: michael@0: if (trustCallback) { michael@0: cachePeriod = CACHE_TRUST_ITEM_PERIOD_SECONDS; michael@0: } michael@0: michael@0: PKIX_CHECK(PKIX_PL_Date_Create_CurrentOffBySeconds michael@0: (cachePeriod, &cacheValidUntilDate, plContext), michael@0: PKIX_DATECREATECURRENTOFFBYSECONDSFAILED); michael@0: michael@0: PKIX_CHECK(PKIX_List_AppendItem michael@0: (cachedValues, michael@0: (PKIX_PL_Object *)cacheValidUntilDate, michael@0: plContext), michael@0: PKIX_LISTAPPENDITEMFAILED); michael@0: michael@0: PKIX_CHECK(PKIX_List_AppendItem michael@0: (cachedValues, michael@0: (PKIX_PL_Object *)certs, michael@0: plContext), michael@0: PKIX_LISTAPPENDITEMFAILED); michael@0: michael@0: cachedCertError = PKIX_PL_HashTable_Add michael@0: (cachedCertTable, michael@0: (PKIX_PL_Object *) cachedKeys, michael@0: (PKIX_PL_Object *) cachedValues, michael@0: plContext); michael@0: michael@0: pkix_cAddCount++; michael@0: michael@0: if (cachedCertError != NULL) { michael@0: PKIX_DEBUG("PKIX_PL_HashTable_Add for Certs skipped: " michael@0: "entry existed\n"); michael@0: } michael@0: michael@0: cleanup: michael@0: michael@0: PKIX_DECREF(subject); michael@0: PKIX_DECREF(cachedKeys); michael@0: PKIX_DECREF(cachedValues); michael@0: PKIX_DECREF(cacheValidUntilDate); michael@0: PKIX_DECREF(cachedCertError); michael@0: michael@0: PKIX_RETURN(BUILD); michael@0: } michael@0: michael@0: /* michael@0: * FUNCTION: pkix_CacheCrlEntry_Lookup michael@0: * DESCRIPTION: michael@0: * michael@0: * Look up CrlEntry Hash Table for a cached item based on "store", michael@0: * "certIssuer" and "certSerialNumber" as the hash keys and returns values michael@0: * "pCrls". If there isn't an item to match the key, a PKIX_FALSE is michael@0: * returned at "pFound". michael@0: * This hashtable is maintained in the following way: michael@0: * 1) When creating the hashtable, maximum bucket size can be specified (0 for michael@0: * unlimited). If items in a bucket reaches its full size, an new addition michael@0: * will trigger the removal of the old as FIFO sequence. michael@0: * michael@0: * PARAMETERS: michael@0: * "store" michael@0: * Address of CertStore as key to retrieve this CertChain. Must be michael@0: * non-NULL. michael@0: * "certIssuer" michael@0: * Address of X500Name that is used as key to retrieve the CRLEntries. michael@0: * Must be non-NULL. michael@0: * "certSerialNumber" michael@0: * Address of BigInt that is used as key to retrieve the CRLEntries. michael@0: * Must be non-NULL. michael@0: * "pFound" michael@0: * Address of KPKIX_Boolean indicating valid data is found. michael@0: * Must be non-NULL. michael@0: * "pCrls" michael@0: * Address PKIX_List where the CRLEntry will be stored. Must be no-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 an Error 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_CacheCrlEntry_Lookup( michael@0: PKIX_CertStore *store, michael@0: PKIX_PL_X500Name *certIssuer, michael@0: PKIX_PL_BigInt *certSerialNumber, michael@0: PKIX_Boolean *pFound, michael@0: PKIX_List** pCrls, michael@0: void *plContext) michael@0: { michael@0: PKIX_List *cachedKeys = NULL; michael@0: PKIX_List *cachedCrlEntryList = NULL; michael@0: PKIX_Error *cachedCrlEntryError = NULL; michael@0: michael@0: PKIX_ENTER(BUILD, "pkix_CacheCrlEntry_Lookup"); michael@0: PKIX_NULLCHECK_THREE(store, certIssuer, certSerialNumber); michael@0: PKIX_NULLCHECK_TWO(pFound, pCrls); michael@0: michael@0: *pFound = PKIX_FALSE; michael@0: michael@0: /* Find CrlEntry(s) by issuer and serial number */ michael@0: michael@0: PKIX_CHECK(PKIX_List_Create(&cachedKeys, plContext), michael@0: PKIX_LISTCREATEFAILED); michael@0: michael@0: PKIX_CHECK(PKIX_List_AppendItem michael@0: (cachedKeys, (PKIX_PL_Object *)store, plContext), michael@0: PKIX_LISTAPPENDITEMFAILED); michael@0: michael@0: PKIX_CHECK(PKIX_List_AppendItem michael@0: (cachedKeys, (PKIX_PL_Object *)certIssuer, plContext), michael@0: PKIX_LISTAPPENDITEMFAILED); michael@0: michael@0: PKIX_CHECK(PKIX_List_AppendItem michael@0: (cachedKeys, michael@0: (PKIX_PL_Object *)certSerialNumber, michael@0: plContext), michael@0: PKIX_LISTAPPENDITEMFAILED); michael@0: michael@0: cachedCrlEntryError = PKIX_PL_HashTable_Lookup michael@0: (cachedCrlEntryTable, michael@0: (PKIX_PL_Object *) cachedKeys, michael@0: (PKIX_PL_Object **) &cachedCrlEntryList, michael@0: plContext); michael@0: pkix_ceLookupCount++; michael@0: michael@0: /* michael@0: * We don't need check Date to invalidate this cache item, michael@0: * the item is uniquely defined and won't be reverted. Let michael@0: * the FIFO for cleaning up. michael@0: */ michael@0: michael@0: if (cachedCrlEntryList != NULL && cachedCrlEntryError == NULL ) { michael@0: michael@0: PKIX_INCREF(cachedCrlEntryList); michael@0: *pCrls = cachedCrlEntryList; michael@0: michael@0: *pFound = PKIX_TRUE; michael@0: michael@0: } else { michael@0: michael@0: *pFound = PKIX_FALSE; michael@0: } michael@0: michael@0: cleanup: michael@0: michael@0: PKIX_DECREF(cachedKeys); michael@0: PKIX_DECREF(cachedCrlEntryList); michael@0: PKIX_DECREF(cachedCrlEntryError); michael@0: michael@0: PKIX_RETURN(BUILD); michael@0: } michael@0: michael@0: /* michael@0: * FUNCTION: pkix_CacheCrlEntry_Add michael@0: * DESCRIPTION: michael@0: * michael@0: * Look up CrlEntry Hash Table for a cached item based on "store", michael@0: * "certIssuer" and "certSerialNumber" as the hash keys and have "pCrls" as michael@0: * the hash value. If there isn't an item to match the key, a PKIX_FALSE is michael@0: * returned at "pFound". michael@0: * This hashtable is maintained in the following way: michael@0: * 1) When creating the hashtable, maximum bucket size can be specified (0 for michael@0: * unlimited). If items in a bucket reaches its full size, an new addition michael@0: * will trigger the removal of the old as FIFO sequence. michael@0: * michael@0: * PARAMETERS: michael@0: * "store" michael@0: * Address of CertStore as key to retrieve this CertChain. Must be michael@0: * non-NULL. michael@0: * "certIssuer" michael@0: * Address of X500Name that is used as key to retrieve the CRLEntries. michael@0: * Must be non-NULL. michael@0: * "certSerialNumber" michael@0: * Address of BigInt that is used as key to retrieve the CRLEntries. michael@0: * Must be non-NULL. michael@0: * "crls" michael@0: * Address PKIX_List where the CRLEntry is stored. Must be no-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 an Error 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_CacheCrlEntry_Add( michael@0: PKIX_CertStore *store, michael@0: PKIX_PL_X500Name *certIssuer, michael@0: PKIX_PL_BigInt *certSerialNumber, michael@0: PKIX_List* crls, michael@0: void *plContext) michael@0: { michael@0: PKIX_List *cachedKeys = NULL; michael@0: PKIX_Error *cachedCrlEntryError = NULL; michael@0: michael@0: PKIX_ENTER(BUILD, "pkix_CacheCrlEntry_Add"); michael@0: PKIX_NULLCHECK_THREE(store, certIssuer, certSerialNumber); michael@0: PKIX_NULLCHECK_ONE(crls); michael@0: michael@0: /* Add CrlEntry(s) by issuer and serial number */ michael@0: michael@0: PKIX_CHECK(PKIX_List_Create(&cachedKeys, plContext), michael@0: PKIX_LISTCREATEFAILED); michael@0: michael@0: PKIX_CHECK(PKIX_List_AppendItem michael@0: (cachedKeys, (PKIX_PL_Object *)store, plContext), michael@0: PKIX_LISTAPPENDITEMFAILED); michael@0: michael@0: PKIX_CHECK(PKIX_List_AppendItem michael@0: (cachedKeys, (PKIX_PL_Object *)certIssuer, plContext), michael@0: PKIX_LISTAPPENDITEMFAILED); michael@0: michael@0: PKIX_CHECK(PKIX_List_AppendItem michael@0: (cachedKeys, michael@0: (PKIX_PL_Object *)certSerialNumber, michael@0: plContext), michael@0: PKIX_LISTAPPENDITEMFAILED); michael@0: michael@0: cachedCrlEntryError = PKIX_PL_HashTable_Add michael@0: (cachedCrlEntryTable, michael@0: (PKIX_PL_Object *) cachedKeys, michael@0: (PKIX_PL_Object *) crls, michael@0: plContext); michael@0: pkix_ceAddCount++; michael@0: michael@0: cleanup: michael@0: michael@0: PKIX_DECREF(cachedKeys); michael@0: PKIX_DECREF(cachedCrlEntryError); michael@0: michael@0: PKIX_RETURN(BUILD); michael@0: } michael@0: michael@0: #ifdef PKIX_OBJECT_LEAK_TEST michael@0: michael@0: /* TEST_START_FN and testStartFnStackPosition define at what state michael@0: * of the stack the object leak testing should begin. The condition michael@0: * in pkix_CheckForGeneratedError works the following way: do leak michael@0: * testing if at position testStartFnStackPosition in stack array michael@0: * (fnStackNameArr) we have called function TEST_START_FN. michael@0: * Note, that stack array get filled only when executing libpkix michael@0: * functions. michael@0: * */ michael@0: #define TEST_START_FN "PKIX_BuildChain" michael@0: michael@0: PKIX_Error* michael@0: pkix_CheckForGeneratedError(PKIX_StdVars * stdVars, michael@0: PKIX_ERRORCLASS errClass, michael@0: char * fnName, michael@0: PKIX_Boolean *errSetFlag, michael@0: void * plContext) michael@0: { michael@0: PKIX_Error *genErr = NULL; michael@0: PKIX_UInt32 pos = 0; michael@0: PKIX_UInt32 strLen = 0; michael@0: michael@0: if (fnName) { michael@0: if (fnStackNameArr[testStartFnStackPosition] == NULL || michael@0: strcmp(fnStackNameArr[testStartFnStackPosition], TEST_START_FN) michael@0: ) { michael@0: /* return with out error if not with in boundary */ michael@0: return NULL; michael@0: } michael@0: if (!strcmp(fnName, TEST_START_FN)) { michael@0: *errSetFlag = PKIX_TRUE; michael@0: noErrorState = PKIX_FALSE; michael@0: errorGenerated = PKIX_FALSE; michael@0: } michael@0: } michael@0: michael@0: if (noErrorState || errorGenerated) return NULL; michael@0: michael@0: if (fnName && ( michael@0: !strcmp(fnName, "PKIX_PL_Object_DecRef") || michael@0: !strcmp(fnName, "PKIX_PL_Object_Unlock") || michael@0: !strcmp(fnName, "pkix_UnlockObject") || michael@0: !strcmp(fnName, "pkix_Throw") || michael@0: !strcmp(fnName, "pkix_trace_dump_cert") || michael@0: !strcmp(fnName, "PKIX_PL_Free"))) { michael@0: /* do not generate error for this functions */ michael@0: noErrorState = PKIX_TRUE; michael@0: *errSetFlag = PKIX_TRUE; michael@0: return NULL; michael@0: } michael@0: michael@0: if (PL_HashTableLookup(fnInvTable, &fnStackInvCountArr[stackPosition - 1])) { michael@0: return NULL; michael@0: } michael@0: michael@0: PL_HashTableAdd(fnInvTable, &fnStackInvCountArr[stackPosition - 1], nonNullValue); michael@0: errorGenerated = PKIX_TRUE; michael@0: noErrorState = PKIX_TRUE; michael@0: genErr = PKIX_DoThrow(stdVars, errClass, PKIX_MEMLEAKGENERATEDERROR, michael@0: errClass, plContext); michael@0: while(fnStackNameArr[pos]) { michael@0: strLen += PORT_Strlen(fnStackNameArr[pos++]) + 1; michael@0: } michael@0: strLen += 1; /* end of line. */ michael@0: pos = 0; michael@0: errorFnStackString = PORT_ZAlloc(strLen); michael@0: while(fnStackNameArr[pos]) { michael@0: strcat(errorFnStackString, "/"); michael@0: strcat(errorFnStackString, fnStackNameArr[pos++]); michael@0: } michael@0: noErrorState = PKIX_FALSE; michael@0: michael@0: return genErr; michael@0: } michael@0: #endif /* PKIX_OBJECT_LEAK_TEST */