1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/security/nss/lib/smime/cmsudf.c Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,446 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +/* 1.9 + * CMS User Define Types 1.10 + */ 1.11 + 1.12 +#include "cmslocal.h" 1.13 + 1.14 +#include "prinit.h" 1.15 +#include "pk11func.h" 1.16 +#include "secitem.h" 1.17 +#include "secoid.h" 1.18 +#include "secerr.h" 1.19 +#include "nss.h" 1.20 + 1.21 +typedef struct nsscmstypeInfoStr nsscmstypeInfo; 1.22 +struct nsscmstypeInfoStr { 1.23 + SECOidTag type; 1.24 + SEC_ASN1Template *template; 1.25 + size_t size; 1.26 + PRBool isData; 1.27 + NSSCMSGenericWrapperDataDestroy destroy; 1.28 + NSSCMSGenericWrapperDataCallback decode_before; 1.29 + NSSCMSGenericWrapperDataCallback decode_after; 1.30 + NSSCMSGenericWrapperDataCallback decode_end; 1.31 + NSSCMSGenericWrapperDataCallback encode_start; 1.32 + NSSCMSGenericWrapperDataCallback encode_before; 1.33 + NSSCMSGenericWrapperDataCallback encode_after; 1.34 +}; 1.35 + 1.36 +/* make sure the global tables are only initialized once */ 1.37 +static PRCallOnceType nsscmstypeOnce; 1.38 +static PRCallOnceType nsscmstypeClearOnce; 1.39 +/* lock for adding a new entry */ 1.40 +static PRLock *nsscmstypeAddLock; 1.41 +/* lock for the hash table */ 1.42 +static PRLock *nsscmstypeHashLock; 1.43 +/* the hash table itself */ 1.44 +static PLHashTable *nsscmstypeHash; 1.45 +/* arena to hold all the hash table data */ 1.46 +static PLArenaPool *nsscmstypeArena; 1.47 + 1.48 +/* 1.49 + * clean up our global tables 1.50 + */ 1.51 +SECStatus 1.52 +nss_cmstype_shutdown(void *appData, void *reserved) 1.53 +{ 1.54 + if (nsscmstypeHashLock) { 1.55 + PR_Lock(nsscmstypeHashLock); 1.56 + } 1.57 + if (nsscmstypeHash) { 1.58 + PL_HashTableDestroy(nsscmstypeHash); 1.59 + nsscmstypeHash = NULL; 1.60 + } 1.61 + if (nsscmstypeArena) { 1.62 + PORT_FreeArena(nsscmstypeArena, PR_FALSE); 1.63 + nsscmstypeArena = NULL; 1.64 + } 1.65 + if (nsscmstypeAddLock) { 1.66 + PR_DestroyLock(nsscmstypeAddLock); 1.67 + } 1.68 + if (nsscmstypeHashLock) { 1.69 + PRLock *oldLock = nsscmstypeHashLock; 1.70 + nsscmstypeHashLock = NULL; 1.71 + PR_Unlock(oldLock); 1.72 + PR_DestroyLock(oldLock); 1.73 + } 1.74 + 1.75 + /* don't clear out the PR_ONCE data if we failed our inital call */ 1.76 + if (appData == NULL) { 1.77 + nsscmstypeOnce = nsscmstypeClearOnce; 1.78 + } 1.79 + return SECSuccess; 1.80 +} 1.81 + 1.82 +static PLHashNumber 1.83 +nss_cmstype_hash_key(const void *key) 1.84 +{ 1.85 + return (PLHashNumber) key; 1.86 +} 1.87 + 1.88 +static PRIntn 1.89 +nss_cmstype_compare_keys(const void *v1, const void *v2) 1.90 +{ 1.91 + PLHashNumber value1 = (PLHashNumber) v1; 1.92 + PLHashNumber value2 = (PLHashNumber) v2; 1.93 + 1.94 + return (value1 == value2); 1.95 +} 1.96 + 1.97 +/* 1.98 + * initialize our hash tables, called once on the first attemat to register 1.99 + * a new SMIME type. 1.100 + */ 1.101 +static PRStatus 1.102 +nss_cmstype_init(void) 1.103 +{ 1.104 + SECStatus rv; 1.105 + 1.106 + nsscmstypeHashLock = PR_NewLock(); 1.107 + if (nsscmstypeHashLock == NULL) { 1.108 + return PR_FAILURE; 1.109 + } 1.110 + nsscmstypeAddLock = PR_NewLock(); 1.111 + if (nsscmstypeHashLock == NULL) { 1.112 + goto fail; 1.113 + } 1.114 + nsscmstypeHash = PL_NewHashTable(64, nss_cmstype_hash_key, 1.115 + nss_cmstype_compare_keys, PL_CompareValues, NULL, NULL); 1.116 + if (nsscmstypeHash == NULL) { 1.117 + goto fail; 1.118 + } 1.119 + nsscmstypeArena = PORT_NewArena(2048); 1.120 + if (nsscmstypeArena == NULL) { 1.121 + goto fail; 1.122 + } 1.123 + rv = NSS_RegisterShutdown(nss_cmstype_shutdown, NULL); 1.124 + if (rv != SECSuccess) { 1.125 + goto fail; 1.126 + } 1.127 + return PR_SUCCESS; 1.128 + 1.129 +fail: 1.130 + nss_cmstype_shutdown(&nsscmstypeOnce, NULL); 1.131 + return PR_FAILURE; 1.132 +} 1.133 + 1.134 + 1.135 +/* 1.136 + * look up and registered SIME type 1.137 + */ 1.138 +static const nsscmstypeInfo * 1.139 +nss_cmstype_lookup(SECOidTag type) 1.140 +{ 1.141 + nsscmstypeInfo *typeInfo = NULL;; 1.142 + if (!nsscmstypeHash) { 1.143 + return NULL; 1.144 + } 1.145 + PR_Lock(nsscmstypeHashLock); 1.146 + if (nsscmstypeHash) { 1.147 + typeInfo = PL_HashTableLookupConst(nsscmstypeHash, (void *)type); 1.148 + } 1.149 + PR_Unlock(nsscmstypeHashLock); 1.150 + return typeInfo; 1.151 +} 1.152 + 1.153 +/* 1.154 + * add a new type to the SMIME type table 1.155 + */ 1.156 +static SECStatus 1.157 +nss_cmstype_add(SECOidTag type, nsscmstypeInfo *typeinfo) 1.158 +{ 1.159 + PLHashEntry *entry; 1.160 + 1.161 + if (!nsscmstypeHash) { 1.162 + /* assert? this shouldn't happen */ 1.163 + return SECFailure; 1.164 + } 1.165 + PR_Lock(nsscmstypeHashLock); 1.166 + /* this is really paranoia. If we really are racing nsscmstypeHash, we'll 1.167 + * also be racing nsscmstypeHashLock... */ 1.168 + if (!nsscmstypeHash) { 1.169 + PR_Unlock(nsscmstypeHashLock); 1.170 + return SECFailure; 1.171 + } 1.172 + entry = PL_HashTableAdd(nsscmstypeHash, (void *)type, typeinfo); 1.173 + PR_Unlock(nsscmstypeHashLock); 1.174 + return entry ? SECSuccess : SECFailure; 1.175 +} 1.176 + 1.177 + 1.178 +/* helper functions to manage new content types 1.179 + */ 1.180 + 1.181 +PRBool 1.182 +NSS_CMSType_IsWrapper(SECOidTag type) 1.183 +{ 1.184 + const nsscmstypeInfo *typeInfo = NULL; 1.185 + 1.186 + switch (type) { 1.187 + case SEC_OID_PKCS7_SIGNED_DATA: 1.188 + case SEC_OID_PKCS7_ENVELOPED_DATA: 1.189 + case SEC_OID_PKCS7_DIGESTED_DATA: 1.190 + case SEC_OID_PKCS7_ENCRYPTED_DATA: 1.191 + return PR_TRUE; 1.192 + default: 1.193 + typeInfo = nss_cmstype_lookup(type); 1.194 + if (typeInfo && !typeInfo->isData) { 1.195 + return PR_TRUE; 1.196 + } 1.197 + } 1.198 + return PR_FALSE; 1.199 +} 1.200 + 1.201 +PRBool 1.202 +NSS_CMSType_IsData(SECOidTag type) 1.203 +{ 1.204 + const nsscmstypeInfo *typeInfo = NULL; 1.205 + 1.206 + switch (type) { 1.207 + case SEC_OID_PKCS7_DATA: 1.208 + return PR_TRUE; 1.209 + default: 1.210 + typeInfo = nss_cmstype_lookup(type); 1.211 + if (typeInfo && typeInfo->isData) { 1.212 + return PR_TRUE; 1.213 + } 1.214 + } 1.215 + return PR_FALSE; 1.216 +} 1.217 + 1.218 +const SEC_ASN1Template * 1.219 +NSS_CMSType_GetTemplate(SECOidTag type) 1.220 +{ 1.221 + const nsscmstypeInfo *typeInfo = nss_cmstype_lookup(type); 1.222 + 1.223 + if (typeInfo && typeInfo->template) { 1.224 + return typeInfo->template; 1.225 + } 1.226 + return SEC_ASN1_GET(SEC_PointerToOctetStringTemplate); 1.227 +} 1.228 + 1.229 +size_t 1.230 +NSS_CMSType_GetContentSize(SECOidTag type) 1.231 +{ 1.232 + const nsscmstypeInfo *typeInfo = nss_cmstype_lookup(type); 1.233 + 1.234 + if (typeInfo) { 1.235 + return typeInfo->size; 1.236 + } 1.237 + return sizeof(SECItem *); 1.238 + 1.239 +} 1.240 + 1.241 +void 1.242 +NSS_CMSGenericWrapperData_Destroy(SECOidTag type, NSSCMSGenericWrapperData *gd) 1.243 +{ 1.244 + const nsscmstypeInfo *typeInfo = nss_cmstype_lookup(type); 1.245 + 1.246 + if (typeInfo && typeInfo->destroy) { 1.247 + (*typeInfo->destroy)(gd); 1.248 + } 1.249 + 1.250 +} 1.251 + 1.252 + 1.253 +SECStatus 1.254 +NSS_CMSGenericWrapperData_Decode_BeforeData(SECOidTag type, 1.255 + NSSCMSGenericWrapperData *gd) 1.256 +{ 1.257 + const nsscmstypeInfo *typeInfo; 1.258 + 1.259 + /* short cut common case */ 1.260 + if (type == SEC_OID_PKCS7_DATA) { 1.261 + return SECSuccess; 1.262 + } 1.263 + 1.264 + typeInfo = nss_cmstype_lookup(type); 1.265 + if (typeInfo) { 1.266 + if (typeInfo->decode_before) { 1.267 + return (*typeInfo->decode_before)(gd); 1.268 + } 1.269 + /* decoder ops optional for data tags */ 1.270 + if (typeInfo->isData) { 1.271 + return SECSuccess; 1.272 + } 1.273 + } 1.274 + /* expected a function, but none existed */ 1.275 + return SECFailure; 1.276 + 1.277 +} 1.278 + 1.279 +SECStatus 1.280 +NSS_CMSGenericWrapperData_Decode_AfterData(SECOidTag type, 1.281 + NSSCMSGenericWrapperData *gd) 1.282 +{ 1.283 + const nsscmstypeInfo *typeInfo; 1.284 + 1.285 + /* short cut common case */ 1.286 + if (type == SEC_OID_PKCS7_DATA) { 1.287 + return SECSuccess; 1.288 + } 1.289 + 1.290 + typeInfo = nss_cmstype_lookup(type); 1.291 + if (typeInfo) { 1.292 + if (typeInfo->decode_after) { 1.293 + return (*typeInfo->decode_after)(gd); 1.294 + } 1.295 + /* decoder ops optional for data tags */ 1.296 + if (typeInfo->isData) { 1.297 + return SECSuccess; 1.298 + } 1.299 + } 1.300 + /* expected a function, but none existed */ 1.301 + return SECFailure; 1.302 +} 1.303 + 1.304 +SECStatus 1.305 +NSS_CMSGenericWrapperData_Decode_AfterEnd(SECOidTag type, 1.306 + NSSCMSGenericWrapperData *gd) 1.307 +{ 1.308 + const nsscmstypeInfo *typeInfo; 1.309 + 1.310 + /* short cut common case */ 1.311 + if (type == SEC_OID_PKCS7_DATA) { 1.312 + return SECSuccess; 1.313 + } 1.314 + 1.315 + typeInfo = nss_cmstype_lookup(type); 1.316 + if (typeInfo) { 1.317 + if (typeInfo->decode_end) { 1.318 + return (*typeInfo->decode_end)(gd); 1.319 + } 1.320 + /* decoder ops optional for data tags */ 1.321 + if (typeInfo->isData) { 1.322 + return SECSuccess; 1.323 + } 1.324 + } 1.325 + /* expected a function, but none existed */ 1.326 + return SECFailure; 1.327 +} 1.328 + 1.329 +SECStatus 1.330 +NSS_CMSGenericWrapperData_Encode_BeforeStart(SECOidTag type, 1.331 + NSSCMSGenericWrapperData *gd) 1.332 +{ 1.333 + const nsscmstypeInfo *typeInfo; 1.334 + 1.335 + /* short cut common case */ 1.336 + if (type == SEC_OID_PKCS7_DATA) { 1.337 + return SECSuccess; 1.338 + } 1.339 + 1.340 + typeInfo = nss_cmstype_lookup(type); 1.341 + if (typeInfo) { 1.342 + if (typeInfo->encode_start) { 1.343 + return (*typeInfo->encode_start)(gd); 1.344 + } 1.345 + /* decoder ops optional for data tags */ 1.346 + if (typeInfo->isData) { 1.347 + return SECSuccess; 1.348 + } 1.349 + } 1.350 + /* expected a function, but none existed */ 1.351 + return SECFailure; 1.352 +} 1.353 + 1.354 +SECStatus 1.355 +NSS_CMSGenericWrapperData_Encode_BeforeData(SECOidTag type, 1.356 + NSSCMSGenericWrapperData *gd) 1.357 +{ 1.358 + const nsscmstypeInfo *typeInfo; 1.359 + 1.360 + /* short cut common case */ 1.361 + if (type == SEC_OID_PKCS7_DATA) { 1.362 + return SECSuccess; 1.363 + } 1.364 + 1.365 + typeInfo = nss_cmstype_lookup(type); 1.366 + if (typeInfo) { 1.367 + if (typeInfo->encode_before) { 1.368 + return (*typeInfo->encode_before)(gd); 1.369 + } 1.370 + /* decoder ops optional for data tags */ 1.371 + if (typeInfo->isData) { 1.372 + return SECSuccess; 1.373 + } 1.374 + } 1.375 + /* expected a function, but none existed */ 1.376 + return SECFailure; 1.377 +} 1.378 + 1.379 +SECStatus 1.380 +NSS_CMSGenericWrapperData_Encode_AfterData(SECOidTag type, 1.381 + NSSCMSGenericWrapperData *gd) 1.382 +{ 1.383 + const nsscmstypeInfo *typeInfo; 1.384 + 1.385 + /* short cut common case */ 1.386 + if (type == SEC_OID_PKCS7_DATA) { 1.387 + return SECSuccess; 1.388 + } 1.389 + 1.390 + typeInfo = nss_cmstype_lookup(type); 1.391 + if (typeInfo) { 1.392 + if (typeInfo->encode_after) { 1.393 + return (*typeInfo->encode_after)(gd); 1.394 + } 1.395 + /* decoder ops optional for data tags */ 1.396 + if (typeInfo->isData) { 1.397 + return SECSuccess; 1.398 + } 1.399 + } 1.400 + /* expected a function, but none existed */ 1.401 + return SECFailure; 1.402 +} 1.403 + 1.404 + 1.405 +SECStatus 1.406 +NSS_CMSType_RegisterContentType(SECOidTag type, 1.407 + SEC_ASN1Template *asn1Template, size_t size, 1.408 + NSSCMSGenericWrapperDataDestroy destroy, 1.409 + NSSCMSGenericWrapperDataCallback decode_before, 1.410 + NSSCMSGenericWrapperDataCallback decode_after, 1.411 + NSSCMSGenericWrapperDataCallback decode_end, 1.412 + NSSCMSGenericWrapperDataCallback encode_start, 1.413 + NSSCMSGenericWrapperDataCallback encode_before, 1.414 + NSSCMSGenericWrapperDataCallback encode_after, 1.415 + PRBool isData) 1.416 +{ 1.417 + PRStatus rc; 1.418 + SECStatus rv; 1.419 + nsscmstypeInfo *typeInfo; 1.420 + const nsscmstypeInfo *exists; 1.421 + 1.422 + rc = PR_CallOnce( &nsscmstypeOnce, nss_cmstype_init); 1.423 + if (rc == PR_FAILURE) { 1.424 + return SECFailure; 1.425 + } 1.426 + PR_Lock(nsscmstypeAddLock); 1.427 + exists = nss_cmstype_lookup(type); 1.428 + if (exists) { 1.429 + PR_Unlock(nsscmstypeAddLock); 1.430 + /* already added */ 1.431 + return SECSuccess; 1.432 + } 1.433 + typeInfo = PORT_ArenaNew(nsscmstypeArena, nsscmstypeInfo); 1.434 + typeInfo->type = type; 1.435 + typeInfo->size = size; 1.436 + typeInfo->isData = isData; 1.437 + typeInfo->template = asn1Template; 1.438 + typeInfo->destroy = destroy; 1.439 + typeInfo->decode_before = decode_before; 1.440 + typeInfo->decode_after = decode_after; 1.441 + typeInfo->decode_end = decode_end; 1.442 + typeInfo->encode_start = encode_start; 1.443 + typeInfo->encode_before = encode_before; 1.444 + typeInfo->encode_after = encode_after; 1.445 + rv = nss_cmstype_add(type, typeInfo); 1.446 + PR_Unlock(nsscmstypeAddLock); 1.447 + return rv; 1.448 +} 1.449 +