cmsudf.c (11735B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 /* 6 * CMS User Define Types 7 */ 8 9 #include "cmslocal.h" 10 11 #include "prinit.h" 12 #include "pk11func.h" 13 #include "secitem.h" 14 #include "secoid.h" 15 #include "secerr.h" 16 #include "nss.h" 17 18 typedef struct nsscmstypeInfoStr nsscmstypeInfo; 19 struct nsscmstypeInfoStr { 20 SECOidTag type; 21 SEC_ASN1Template *template; 22 size_t size; 23 PRBool isData; 24 NSSCMSGenericWrapperDataDestroy destroy; 25 NSSCMSGenericWrapperDataCallback decode_before; 26 NSSCMSGenericWrapperDataCallback decode_after; 27 NSSCMSGenericWrapperDataCallback decode_end; 28 NSSCMSGenericWrapperDataCallback encode_start; 29 NSSCMSGenericWrapperDataCallback encode_before; 30 NSSCMSGenericWrapperDataCallback encode_after; 31 }; 32 33 /* make sure the global tables are only initialized once */ 34 static PRCallOnceType nsscmstypeOnce; 35 static PRCallOnceType nsscmstypeClearOnce; 36 /* lock for adding a new entry */ 37 static PRLock *nsscmstypeAddLock; 38 /* lock for the hash table */ 39 static PRLock *nsscmstypeHashLock; 40 /* the hash table itself */ 41 static PLHashTable *nsscmstypeHash; 42 /* arena to hold all the hash table data */ 43 static PLArenaPool *nsscmstypeArena; 44 45 /* 46 * clean up our global tables 47 */ 48 SECStatus 49 nss_cmstype_shutdown(void *appData, void *reserved) 50 { 51 if (nsscmstypeHashLock) { 52 PR_Lock(nsscmstypeHashLock); 53 } 54 if (nsscmstypeHash) { 55 PL_HashTableDestroy(nsscmstypeHash); 56 nsscmstypeHash = NULL; 57 } 58 if (nsscmstypeArena) { 59 PORT_FreeArena(nsscmstypeArena, PR_FALSE); 60 nsscmstypeArena = NULL; 61 } 62 if (nsscmstypeAddLock) { 63 PR_DestroyLock(nsscmstypeAddLock); 64 } 65 if (nsscmstypeHashLock) { 66 PRLock *oldLock = nsscmstypeHashLock; 67 nsscmstypeHashLock = NULL; 68 PR_Unlock(oldLock); 69 PR_DestroyLock(oldLock); 70 } 71 72 /* don't clear out the PR_ONCE data if we failed our inital call */ 73 if (appData == NULL) { 74 nsscmstypeOnce = nsscmstypeClearOnce; 75 } 76 return SECSuccess; 77 } 78 79 static PLHashNumber 80 nss_cmstype_hash_key(const void *key) 81 { 82 return (PLHashNumber)((char *)key - (char *)NULL); 83 } 84 85 static PRIntn 86 nss_cmstype_compare_keys(const void *v1, const void *v2) 87 { 88 PLHashNumber value1 = nss_cmstype_hash_key(v1); 89 PLHashNumber value2 = nss_cmstype_hash_key(v2); 90 91 return (value1 == value2); 92 } 93 94 /* 95 * initialize our hash tables, called once on the first attemat to register 96 * a new SMIME type. 97 */ 98 static PRStatus 99 nss_cmstype_init(void) 100 { 101 SECStatus rv; 102 103 nsscmstypeHashLock = PR_NewLock(); 104 if (nsscmstypeHashLock == NULL) { 105 return PR_FAILURE; 106 } 107 nsscmstypeAddLock = PR_NewLock(); 108 if (nsscmstypeHashLock == NULL) { 109 goto fail; 110 } 111 nsscmstypeHash = PL_NewHashTable(64, nss_cmstype_hash_key, 112 nss_cmstype_compare_keys, 113 PL_CompareValues, NULL, NULL); 114 if (nsscmstypeHash == NULL) { 115 goto fail; 116 } 117 nsscmstypeArena = PORT_NewArena(2048); 118 if (nsscmstypeArena == NULL) { 119 goto fail; 120 } 121 rv = NSS_RegisterShutdown(nss_cmstype_shutdown, NULL); 122 if (rv != SECSuccess) { 123 goto fail; 124 } 125 return PR_SUCCESS; 126 127 fail: 128 nss_cmstype_shutdown(&nsscmstypeOnce, NULL); 129 return PR_FAILURE; 130 } 131 132 /* 133 * look up and registered SIME type 134 */ 135 static const nsscmstypeInfo * 136 nss_cmstype_lookup(SECOidTag type) 137 { 138 nsscmstypeInfo *typeInfo = NULL; 139 ; 140 if (!nsscmstypeHash) { 141 return NULL; 142 } 143 PR_Lock(nsscmstypeHashLock); 144 if (nsscmstypeHash) { 145 typeInfo = PL_HashTableLookupConst(nsscmstypeHash, (void *)type); 146 } 147 PR_Unlock(nsscmstypeHashLock); 148 return typeInfo; 149 } 150 151 /* 152 * add a new type to the SMIME type table 153 */ 154 static SECStatus 155 nss_cmstype_add(SECOidTag type, nsscmstypeInfo *typeinfo) 156 { 157 PLHashEntry *entry; 158 159 if (!nsscmstypeHash) { 160 /* assert? this shouldn't happen */ 161 return SECFailure; 162 } 163 PR_Lock(nsscmstypeHashLock); 164 /* this is really paranoia. If we really are racing nsscmstypeHash, we'll 165 * also be racing nsscmstypeHashLock... */ 166 if (!nsscmstypeHash) { 167 PR_Unlock(nsscmstypeHashLock); 168 return SECFailure; 169 } 170 entry = PL_HashTableAdd(nsscmstypeHash, (void *)type, typeinfo); 171 PR_Unlock(nsscmstypeHashLock); 172 return entry ? SECSuccess : SECFailure; 173 } 174 175 /* helper functions to manage new content types 176 */ 177 178 PRBool 179 NSS_CMSType_IsWrapper(SECOidTag type) 180 { 181 const nsscmstypeInfo *typeInfo = NULL; 182 183 switch (type) { 184 case SEC_OID_PKCS7_SIGNED_DATA: 185 case SEC_OID_PKCS7_ENVELOPED_DATA: 186 case SEC_OID_PKCS7_DIGESTED_DATA: 187 case SEC_OID_PKCS7_ENCRYPTED_DATA: 188 return PR_TRUE; 189 default: 190 typeInfo = nss_cmstype_lookup(type); 191 if (typeInfo && !typeInfo->isData) { 192 return PR_TRUE; 193 } 194 } 195 return PR_FALSE; 196 } 197 198 PRBool 199 NSS_CMSType_IsData(SECOidTag type) 200 { 201 const nsscmstypeInfo *typeInfo = NULL; 202 203 switch (type) { 204 case SEC_OID_PKCS7_DATA: 205 return PR_TRUE; 206 default: 207 typeInfo = nss_cmstype_lookup(type); 208 if (typeInfo && typeInfo->isData) { 209 return PR_TRUE; 210 } 211 } 212 return PR_FALSE; 213 } 214 215 const SEC_ASN1Template * 216 NSS_CMSType_GetTemplate(SECOidTag type) 217 { 218 const nsscmstypeInfo *typeInfo = nss_cmstype_lookup(type); 219 220 if (typeInfo && typeInfo->template) { 221 return typeInfo->template; 222 } 223 return SEC_ASN1_GET(SEC_PointerToOctetStringTemplate); 224 } 225 226 size_t 227 NSS_CMSType_GetContentSize(SECOidTag type) 228 { 229 const nsscmstypeInfo *typeInfo = nss_cmstype_lookup(type); 230 231 if (typeInfo) { 232 return typeInfo->size; 233 } 234 return sizeof(SECItem *); 235 } 236 237 void 238 NSS_CMSGenericWrapperData_Destroy(SECOidTag type, NSSCMSGenericWrapperData *gd) 239 { 240 const nsscmstypeInfo *typeInfo = nss_cmstype_lookup(type); 241 242 if (typeInfo && (typeInfo->destroy) && (gd != NULL)) { 243 (*typeInfo->destroy)(gd); 244 } 245 } 246 247 SECStatus 248 NSS_CMSGenericWrapperData_Decode_BeforeData(SECOidTag type, 249 NSSCMSGenericWrapperData *gd) 250 { 251 const nsscmstypeInfo *typeInfo; 252 253 /* short cut common case */ 254 if (type == SEC_OID_PKCS7_DATA) { 255 return SECSuccess; 256 } 257 258 typeInfo = nss_cmstype_lookup(type); 259 if (typeInfo) { 260 if (typeInfo->decode_before) { 261 return (*typeInfo->decode_before)(gd); 262 } 263 /* decoder ops optional for data tags */ 264 if (typeInfo->isData) { 265 return SECSuccess; 266 } 267 } 268 /* expected a function, but none existed */ 269 return SECFailure; 270 } 271 272 SECStatus 273 NSS_CMSGenericWrapperData_Decode_AfterData(SECOidTag type, 274 NSSCMSGenericWrapperData *gd) 275 { 276 const nsscmstypeInfo *typeInfo; 277 278 /* short cut common case */ 279 if (type == SEC_OID_PKCS7_DATA) { 280 return SECSuccess; 281 } 282 283 typeInfo = nss_cmstype_lookup(type); 284 if (typeInfo) { 285 if (typeInfo->decode_after) { 286 return (*typeInfo->decode_after)(gd); 287 } 288 /* decoder ops optional for data tags */ 289 if (typeInfo->isData) { 290 return SECSuccess; 291 } 292 } 293 /* expected a function, but none existed */ 294 return SECFailure; 295 } 296 297 SECStatus 298 NSS_CMSGenericWrapperData_Decode_AfterEnd(SECOidTag type, 299 NSSCMSGenericWrapperData *gd) 300 { 301 const nsscmstypeInfo *typeInfo; 302 303 /* short cut common case */ 304 if (type == SEC_OID_PKCS7_DATA) { 305 return SECSuccess; 306 } 307 308 typeInfo = nss_cmstype_lookup(type); 309 if (typeInfo) { 310 if (typeInfo->decode_end) { 311 return (*typeInfo->decode_end)(gd); 312 } 313 /* decoder ops optional for data tags */ 314 if (typeInfo->isData) { 315 return SECSuccess; 316 } 317 } 318 /* expected a function, but none existed */ 319 return SECFailure; 320 } 321 322 SECStatus 323 NSS_CMSGenericWrapperData_Encode_BeforeStart(SECOidTag type, 324 NSSCMSGenericWrapperData *gd) 325 { 326 const nsscmstypeInfo *typeInfo; 327 328 /* short cut common case */ 329 if (type == SEC_OID_PKCS7_DATA) { 330 return SECSuccess; 331 } 332 333 typeInfo = nss_cmstype_lookup(type); 334 if (typeInfo) { 335 if (typeInfo->encode_start) { 336 return (*typeInfo->encode_start)(gd); 337 } 338 /* decoder ops optional for data tags */ 339 if (typeInfo->isData) { 340 return SECSuccess; 341 } 342 } 343 /* expected a function, but none existed */ 344 return SECFailure; 345 } 346 347 SECStatus 348 NSS_CMSGenericWrapperData_Encode_BeforeData(SECOidTag type, 349 NSSCMSGenericWrapperData *gd) 350 { 351 const nsscmstypeInfo *typeInfo; 352 353 /* short cut common case */ 354 if (type == SEC_OID_PKCS7_DATA) { 355 return SECSuccess; 356 } 357 358 typeInfo = nss_cmstype_lookup(type); 359 if (typeInfo) { 360 if (typeInfo->encode_before) { 361 return (*typeInfo->encode_before)(gd); 362 } 363 /* decoder ops optional for data tags */ 364 if (typeInfo->isData) { 365 return SECSuccess; 366 } 367 } 368 /* expected a function, but none existed */ 369 return SECFailure; 370 } 371 372 SECStatus 373 NSS_CMSGenericWrapperData_Encode_AfterData(SECOidTag type, 374 NSSCMSGenericWrapperData *gd) 375 { 376 const nsscmstypeInfo *typeInfo; 377 378 /* short cut common case */ 379 if (type == SEC_OID_PKCS7_DATA) { 380 return SECSuccess; 381 } 382 383 typeInfo = nss_cmstype_lookup(type); 384 if (typeInfo) { 385 if (typeInfo->encode_after) { 386 return (*typeInfo->encode_after)(gd); 387 } 388 /* decoder ops optional for data tags */ 389 if (typeInfo->isData) { 390 return SECSuccess; 391 } 392 } 393 /* expected a function, but none existed */ 394 return SECFailure; 395 } 396 397 SECStatus 398 NSS_CMSType_RegisterContentType(SECOidTag type, 399 SEC_ASN1Template *asn1Template, size_t size, 400 NSSCMSGenericWrapperDataDestroy destroy, 401 NSSCMSGenericWrapperDataCallback decode_before, 402 NSSCMSGenericWrapperDataCallback decode_after, 403 NSSCMSGenericWrapperDataCallback decode_end, 404 NSSCMSGenericWrapperDataCallback encode_start, 405 NSSCMSGenericWrapperDataCallback encode_before, 406 NSSCMSGenericWrapperDataCallback encode_after, 407 PRBool isData) 408 { 409 PRStatus rc; 410 SECStatus rv; 411 nsscmstypeInfo *typeInfo; 412 const nsscmstypeInfo *exists; 413 414 rc = PR_CallOnce(&nsscmstypeOnce, nss_cmstype_init); 415 if (rc == PR_FAILURE) { 416 return SECFailure; 417 } 418 PR_Lock(nsscmstypeAddLock); 419 exists = nss_cmstype_lookup(type); 420 if (exists) { 421 PR_Unlock(nsscmstypeAddLock); 422 /* already added */ 423 return SECSuccess; 424 } 425 typeInfo = PORT_ArenaNew(nsscmstypeArena, nsscmstypeInfo); 426 typeInfo->type = type; 427 typeInfo->size = size; 428 typeInfo->isData = isData; 429 typeInfo->template = asn1Template; 430 typeInfo->destroy = destroy; 431 typeInfo->decode_before = decode_before; 432 typeInfo->decode_after = decode_after; 433 typeInfo->decode_end = decode_end; 434 typeInfo->encode_start = encode_start; 435 typeInfo->encode_before = encode_before; 436 typeInfo->encode_after = encode_after; 437 rv = nss_cmstype_add(type, typeInfo); 438 PR_Unlock(nsscmstypeAddLock); 439 return rv; 440 }