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