| /* 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/. */ |
| |
| /* |
| * CMS User Define Types |
| */ |
| |
| #include "cmslocal.h" |
| |
| #include "prinit.h" |
| #include "pk11func.h" |
| #include "secitem.h" |
| #include "secoid.h" |
| #include "secerr.h" |
| #include "nss.h" |
| |
| typedef struct nsscmstypeInfoStr nsscmstypeInfo; |
| struct nsscmstypeInfoStr { |
| SECOidTag type; |
| SEC_ASN1Template *template; |
| size_t size; |
| PRBool isData; |
| NSSCMSGenericWrapperDataDestroy destroy; |
| NSSCMSGenericWrapperDataCallback decode_before; |
| NSSCMSGenericWrapperDataCallback decode_after; |
| NSSCMSGenericWrapperDataCallback decode_end; |
| NSSCMSGenericWrapperDataCallback encode_start; |
| NSSCMSGenericWrapperDataCallback encode_before; |
| NSSCMSGenericWrapperDataCallback encode_after; |
| }; |
| |
| /* make sure the global tables are only initialized once */ |
| static PRCallOnceType nsscmstypeOnce; |
| static PRCallOnceType nsscmstypeClearOnce; |
| /* lock for adding a new entry */ |
| static PRLock *nsscmstypeAddLock; |
| /* lock for the hash table */ |
| static PRLock *nsscmstypeHashLock; |
| /* the hash table itself */ |
| static PLHashTable *nsscmstypeHash; |
| /* arena to hold all the hash table data */ |
| static PLArenaPool *nsscmstypeArena; |
| |
| /* |
| * clean up our global tables |
| */ |
| SECStatus |
| nss_cmstype_shutdown(void *appData, void *reserved) |
| { |
| if (nsscmstypeHashLock) { |
| PR_Lock(nsscmstypeHashLock); |
| } |
| if (nsscmstypeHash) { |
| PL_HashTableDestroy(nsscmstypeHash); |
| nsscmstypeHash = NULL; |
| } |
| if (nsscmstypeArena) { |
| PORT_FreeArena(nsscmstypeArena, PR_FALSE); |
| nsscmstypeArena = NULL; |
| } |
| if (nsscmstypeAddLock) { |
| PR_DestroyLock(nsscmstypeAddLock); |
| } |
| if (nsscmstypeHashLock) { |
| PRLock *oldLock = nsscmstypeHashLock; |
| nsscmstypeHashLock = NULL; |
| PR_Unlock(oldLock); |
| PR_DestroyLock(oldLock); |
| } |
| |
| /* don't clear out the PR_ONCE data if we failed our inital call */ |
| if (appData == NULL) { |
| nsscmstypeOnce = nsscmstypeClearOnce; |
| } |
| return SECSuccess; |
| } |
| |
| static PLHashNumber |
| nss_cmstype_hash_key(const void *key) |
| { |
| return (PLHashNumber)((char *)key - (char *)NULL); |
| } |
| |
| static PRIntn |
| nss_cmstype_compare_keys(const void *v1, const void *v2) |
| { |
| PLHashNumber value1 = nss_cmstype_hash_key(v1); |
| PLHashNumber value2 = nss_cmstype_hash_key(v2); |
| |
| return (value1 == value2); |
| } |
| |
| /* |
| * initialize our hash tables, called once on the first attemat to register |
| * a new SMIME type. |
| */ |
| static PRStatus |
| nss_cmstype_init(void) |
| { |
| SECStatus rv; |
| |
| nsscmstypeHashLock = PR_NewLock(); |
| if (nsscmstypeHashLock == NULL) { |
| return PR_FAILURE; |
| } |
| nsscmstypeAddLock = PR_NewLock(); |
| if (nsscmstypeHashLock == NULL) { |
| goto fail; |
| } |
| nsscmstypeHash = PL_NewHashTable(64, nss_cmstype_hash_key, |
| nss_cmstype_compare_keys, |
| PL_CompareValues, NULL, NULL); |
| if (nsscmstypeHash == NULL) { |
| goto fail; |
| } |
| nsscmstypeArena = PORT_NewArena(2048); |
| if (nsscmstypeArena == NULL) { |
| goto fail; |
| } |
| rv = NSS_RegisterShutdown(nss_cmstype_shutdown, NULL); |
| if (rv != SECSuccess) { |
| goto fail; |
| } |
| return PR_SUCCESS; |
| |
| fail: |
| nss_cmstype_shutdown(&nsscmstypeOnce, NULL); |
| return PR_FAILURE; |
| } |
| |
| /* |
| * look up and registered SIME type |
| */ |
| static const nsscmstypeInfo * |
| nss_cmstype_lookup(SECOidTag type) |
| { |
| nsscmstypeInfo *typeInfo = NULL; |
| ; |
| if (!nsscmstypeHash) { |
| return NULL; |
| } |
| PR_Lock(nsscmstypeHashLock); |
| if (nsscmstypeHash) { |
| typeInfo = PL_HashTableLookupConst(nsscmstypeHash, (void *)type); |
| } |
| PR_Unlock(nsscmstypeHashLock); |
| return typeInfo; |
| } |
| |
| /* |
| * add a new type to the SMIME type table |
| */ |
| static SECStatus |
| nss_cmstype_add(SECOidTag type, nsscmstypeInfo *typeinfo) |
| { |
| PLHashEntry *entry; |
| |
| if (!nsscmstypeHash) { |
| /* assert? this shouldn't happen */ |
| return SECFailure; |
| } |
| PR_Lock(nsscmstypeHashLock); |
| /* this is really paranoia. If we really are racing nsscmstypeHash, we'll |
| * also be racing nsscmstypeHashLock... */ |
| if (!nsscmstypeHash) { |
| PR_Unlock(nsscmstypeHashLock); |
| return SECFailure; |
| } |
| entry = PL_HashTableAdd(nsscmstypeHash, (void *)type, typeinfo); |
| PR_Unlock(nsscmstypeHashLock); |
| return entry ? SECSuccess : SECFailure; |
| } |
| |
| /* helper functions to manage new content types |
| */ |
| |
| PRBool |
| NSS_CMSType_IsWrapper(SECOidTag type) |
| { |
| const nsscmstypeInfo *typeInfo = NULL; |
| |
| switch (type) { |
| case SEC_OID_PKCS7_SIGNED_DATA: |
| case SEC_OID_PKCS7_ENVELOPED_DATA: |
| case SEC_OID_PKCS7_DIGESTED_DATA: |
| case SEC_OID_PKCS7_ENCRYPTED_DATA: |
| return PR_TRUE; |
| default: |
| typeInfo = nss_cmstype_lookup(type); |
| if (typeInfo && !typeInfo->isData) { |
| return PR_TRUE; |
| } |
| } |
| return PR_FALSE; |
| } |
| |
| PRBool |
| NSS_CMSType_IsData(SECOidTag type) |
| { |
| const nsscmstypeInfo *typeInfo = NULL; |
| |
| switch (type) { |
| case SEC_OID_PKCS7_DATA: |
| return PR_TRUE; |
| default: |
| typeInfo = nss_cmstype_lookup(type); |
| if (typeInfo && typeInfo->isData) { |
| return PR_TRUE; |
| } |
| } |
| return PR_FALSE; |
| } |
| |
| const SEC_ASN1Template * |
| NSS_CMSType_GetTemplate(SECOidTag type) |
| { |
| const nsscmstypeInfo *typeInfo = nss_cmstype_lookup(type); |
| |
| if (typeInfo && typeInfo->template) { |
| return typeInfo->template; |
| } |
| return SEC_ASN1_GET(SEC_PointerToOctetStringTemplate); |
| } |
| |
| size_t |
| NSS_CMSType_GetContentSize(SECOidTag type) |
| { |
| const nsscmstypeInfo *typeInfo = nss_cmstype_lookup(type); |
| |
| if (typeInfo) { |
| return typeInfo->size; |
| } |
| return sizeof(SECItem *); |
| } |
| |
| void |
| NSS_CMSGenericWrapperData_Destroy(SECOidTag type, NSSCMSGenericWrapperData *gd) |
| { |
| const nsscmstypeInfo *typeInfo = nss_cmstype_lookup(type); |
| |
| if (typeInfo && (typeInfo->destroy) && (gd != NULL)) { |
| (*typeInfo->destroy)(gd); |
| } |
| } |
| |
| SECStatus |
| NSS_CMSGenericWrapperData_Decode_BeforeData(SECOidTag type, |
| NSSCMSGenericWrapperData *gd) |
| { |
| const nsscmstypeInfo *typeInfo; |
| |
| /* short cut common case */ |
| if (type == SEC_OID_PKCS7_DATA) { |
| return SECSuccess; |
| } |
| |
| typeInfo = nss_cmstype_lookup(type); |
| if (typeInfo) { |
| if (typeInfo->decode_before) { |
| return (*typeInfo->decode_before)(gd); |
| } |
| /* decoder ops optional for data tags */ |
| if (typeInfo->isData) { |
| return SECSuccess; |
| } |
| } |
| /* expected a function, but none existed */ |
| return SECFailure; |
| } |
| |
| SECStatus |
| NSS_CMSGenericWrapperData_Decode_AfterData(SECOidTag type, |
| NSSCMSGenericWrapperData *gd) |
| { |
| const nsscmstypeInfo *typeInfo; |
| |
| /* short cut common case */ |
| if (type == SEC_OID_PKCS7_DATA) { |
| return SECSuccess; |
| } |
| |
| typeInfo = nss_cmstype_lookup(type); |
| if (typeInfo) { |
| if (typeInfo->decode_after) { |
| return (*typeInfo->decode_after)(gd); |
| } |
| /* decoder ops optional for data tags */ |
| if (typeInfo->isData) { |
| return SECSuccess; |
| } |
| } |
| /* expected a function, but none existed */ |
| return SECFailure; |
| } |
| |
| SECStatus |
| NSS_CMSGenericWrapperData_Decode_AfterEnd(SECOidTag type, |
| NSSCMSGenericWrapperData *gd) |
| { |
| const nsscmstypeInfo *typeInfo; |
| |
| /* short cut common case */ |
| if (type == SEC_OID_PKCS7_DATA) { |
| return SECSuccess; |
| } |
| |
| typeInfo = nss_cmstype_lookup(type); |
| if (typeInfo) { |
| if (typeInfo->decode_end) { |
| return (*typeInfo->decode_end)(gd); |
| } |
| /* decoder ops optional for data tags */ |
| if (typeInfo->isData) { |
| return SECSuccess; |
| } |
| } |
| /* expected a function, but none existed */ |
| return SECFailure; |
| } |
| |
| SECStatus |
| NSS_CMSGenericWrapperData_Encode_BeforeStart(SECOidTag type, |
| NSSCMSGenericWrapperData *gd) |
| { |
| const nsscmstypeInfo *typeInfo; |
| |
| /* short cut common case */ |
| if (type == SEC_OID_PKCS7_DATA) { |
| return SECSuccess; |
| } |
| |
| typeInfo = nss_cmstype_lookup(type); |
| if (typeInfo) { |
| if (typeInfo->encode_start) { |
| return (*typeInfo->encode_start)(gd); |
| } |
| /* decoder ops optional for data tags */ |
| if (typeInfo->isData) { |
| return SECSuccess; |
| } |
| } |
| /* expected a function, but none existed */ |
| return SECFailure; |
| } |
| |
| SECStatus |
| NSS_CMSGenericWrapperData_Encode_BeforeData(SECOidTag type, |
| NSSCMSGenericWrapperData *gd) |
| { |
| const nsscmstypeInfo *typeInfo; |
| |
| /* short cut common case */ |
| if (type == SEC_OID_PKCS7_DATA) { |
| return SECSuccess; |
| } |
| |
| typeInfo = nss_cmstype_lookup(type); |
| if (typeInfo) { |
| if (typeInfo->encode_before) { |
| return (*typeInfo->encode_before)(gd); |
| } |
| /* decoder ops optional for data tags */ |
| if (typeInfo->isData) { |
| return SECSuccess; |
| } |
| } |
| /* expected a function, but none existed */ |
| return SECFailure; |
| } |
| |
| SECStatus |
| NSS_CMSGenericWrapperData_Encode_AfterData(SECOidTag type, |
| NSSCMSGenericWrapperData *gd) |
| { |
| const nsscmstypeInfo *typeInfo; |
| |
| /* short cut common case */ |
| if (type == SEC_OID_PKCS7_DATA) { |
| return SECSuccess; |
| } |
| |
| typeInfo = nss_cmstype_lookup(type); |
| if (typeInfo) { |
| if (typeInfo->encode_after) { |
| return (*typeInfo->encode_after)(gd); |
| } |
| /* decoder ops optional for data tags */ |
| if (typeInfo->isData) { |
| return SECSuccess; |
| } |
| } |
| /* expected a function, but none existed */ |
| return SECFailure; |
| } |
| |
| SECStatus |
| NSS_CMSType_RegisterContentType(SECOidTag type, |
| SEC_ASN1Template *asn1Template, size_t size, |
| NSSCMSGenericWrapperDataDestroy destroy, |
| NSSCMSGenericWrapperDataCallback decode_before, |
| NSSCMSGenericWrapperDataCallback decode_after, |
| NSSCMSGenericWrapperDataCallback decode_end, |
| NSSCMSGenericWrapperDataCallback encode_start, |
| NSSCMSGenericWrapperDataCallback encode_before, |
| NSSCMSGenericWrapperDataCallback encode_after, |
| PRBool isData) |
| { |
| PRStatus rc; |
| SECStatus rv; |
| nsscmstypeInfo *typeInfo; |
| const nsscmstypeInfo *exists; |
| |
| rc = PR_CallOnce(&nsscmstypeOnce, nss_cmstype_init); |
| if (rc == PR_FAILURE) { |
| return SECFailure; |
| } |
| PR_Lock(nsscmstypeAddLock); |
| exists = nss_cmstype_lookup(type); |
| if (exists) { |
| PR_Unlock(nsscmstypeAddLock); |
| /* already added */ |
| return SECSuccess; |
| } |
| typeInfo = PORT_ArenaNew(nsscmstypeArena, nsscmstypeInfo); |
| typeInfo->type = type; |
| typeInfo->size = size; |
| typeInfo->isData = isData; |
| typeInfo->template = asn1Template; |
| typeInfo->destroy = destroy; |
| typeInfo->decode_before = decode_before; |
| typeInfo->decode_after = decode_after; |
| typeInfo->decode_end = decode_end; |
| typeInfo->encode_start = encode_start; |
| typeInfo->encode_before = encode_before; |
| typeInfo->encode_after = encode_after; |
| rv = nss_cmstype_add(type, typeInfo); |
| PR_Unlock(nsscmstypeAddLock); |
| return rv; |
| } |