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