|
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) key; |
|
83 } |
|
84 |
|
85 static PRIntn |
|
86 nss_cmstype_compare_keys(const void *v1, const void *v2) |
|
87 { |
|
88 PLHashNumber value1 = (PLHashNumber) v1; |
|
89 PLHashNumber value2 = (PLHashNumber) 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, PL_CompareValues, NULL, NULL); |
|
113 if (nsscmstypeHash == NULL) { |
|
114 goto fail; |
|
115 } |
|
116 nsscmstypeArena = PORT_NewArena(2048); |
|
117 if (nsscmstypeArena == NULL) { |
|
118 goto fail; |
|
119 } |
|
120 rv = NSS_RegisterShutdown(nss_cmstype_shutdown, NULL); |
|
121 if (rv != SECSuccess) { |
|
122 goto fail; |
|
123 } |
|
124 return PR_SUCCESS; |
|
125 |
|
126 fail: |
|
127 nss_cmstype_shutdown(&nsscmstypeOnce, NULL); |
|
128 return PR_FAILURE; |
|
129 } |
|
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 if (!nsscmstypeHash) { |
|
140 return NULL; |
|
141 } |
|
142 PR_Lock(nsscmstypeHashLock); |
|
143 if (nsscmstypeHash) { |
|
144 typeInfo = PL_HashTableLookupConst(nsscmstypeHash, (void *)type); |
|
145 } |
|
146 PR_Unlock(nsscmstypeHashLock); |
|
147 return typeInfo; |
|
148 } |
|
149 |
|
150 /* |
|
151 * add a new type to the SMIME type table |
|
152 */ |
|
153 static SECStatus |
|
154 nss_cmstype_add(SECOidTag type, nsscmstypeInfo *typeinfo) |
|
155 { |
|
156 PLHashEntry *entry; |
|
157 |
|
158 if (!nsscmstypeHash) { |
|
159 /* assert? this shouldn't happen */ |
|
160 return SECFailure; |
|
161 } |
|
162 PR_Lock(nsscmstypeHashLock); |
|
163 /* this is really paranoia. If we really are racing nsscmstypeHash, we'll |
|
164 * also be racing nsscmstypeHashLock... */ |
|
165 if (!nsscmstypeHash) { |
|
166 PR_Unlock(nsscmstypeHashLock); |
|
167 return SECFailure; |
|
168 } |
|
169 entry = PL_HashTableAdd(nsscmstypeHash, (void *)type, typeinfo); |
|
170 PR_Unlock(nsscmstypeHashLock); |
|
171 return entry ? SECSuccess : SECFailure; |
|
172 } |
|
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 |
|
238 void |
|
239 NSS_CMSGenericWrapperData_Destroy(SECOidTag type, NSSCMSGenericWrapperData *gd) |
|
240 { |
|
241 const nsscmstypeInfo *typeInfo = nss_cmstype_lookup(type); |
|
242 |
|
243 if (typeInfo && typeInfo->destroy) { |
|
244 (*typeInfo->destroy)(gd); |
|
245 } |
|
246 |
|
247 } |
|
248 |
|
249 |
|
250 SECStatus |
|
251 NSS_CMSGenericWrapperData_Decode_BeforeData(SECOidTag type, |
|
252 NSSCMSGenericWrapperData *gd) |
|
253 { |
|
254 const nsscmstypeInfo *typeInfo; |
|
255 |
|
256 /* short cut common case */ |
|
257 if (type == SEC_OID_PKCS7_DATA) { |
|
258 return SECSuccess; |
|
259 } |
|
260 |
|
261 typeInfo = nss_cmstype_lookup(type); |
|
262 if (typeInfo) { |
|
263 if (typeInfo->decode_before) { |
|
264 return (*typeInfo->decode_before)(gd); |
|
265 } |
|
266 /* decoder ops optional for data tags */ |
|
267 if (typeInfo->isData) { |
|
268 return SECSuccess; |
|
269 } |
|
270 } |
|
271 /* expected a function, but none existed */ |
|
272 return SECFailure; |
|
273 |
|
274 } |
|
275 |
|
276 SECStatus |
|
277 NSS_CMSGenericWrapperData_Decode_AfterData(SECOidTag type, |
|
278 NSSCMSGenericWrapperData *gd) |
|
279 { |
|
280 const nsscmstypeInfo *typeInfo; |
|
281 |
|
282 /* short cut common case */ |
|
283 if (type == SEC_OID_PKCS7_DATA) { |
|
284 return SECSuccess; |
|
285 } |
|
286 |
|
287 typeInfo = nss_cmstype_lookup(type); |
|
288 if (typeInfo) { |
|
289 if (typeInfo->decode_after) { |
|
290 return (*typeInfo->decode_after)(gd); |
|
291 } |
|
292 /* decoder ops optional for data tags */ |
|
293 if (typeInfo->isData) { |
|
294 return SECSuccess; |
|
295 } |
|
296 } |
|
297 /* expected a function, but none existed */ |
|
298 return SECFailure; |
|
299 } |
|
300 |
|
301 SECStatus |
|
302 NSS_CMSGenericWrapperData_Decode_AfterEnd(SECOidTag type, |
|
303 NSSCMSGenericWrapperData *gd) |
|
304 { |
|
305 const nsscmstypeInfo *typeInfo; |
|
306 |
|
307 /* short cut common case */ |
|
308 if (type == SEC_OID_PKCS7_DATA) { |
|
309 return SECSuccess; |
|
310 } |
|
311 |
|
312 typeInfo = nss_cmstype_lookup(type); |
|
313 if (typeInfo) { |
|
314 if (typeInfo->decode_end) { |
|
315 return (*typeInfo->decode_end)(gd); |
|
316 } |
|
317 /* decoder ops optional for data tags */ |
|
318 if (typeInfo->isData) { |
|
319 return SECSuccess; |
|
320 } |
|
321 } |
|
322 /* expected a function, but none existed */ |
|
323 return SECFailure; |
|
324 } |
|
325 |
|
326 SECStatus |
|
327 NSS_CMSGenericWrapperData_Encode_BeforeStart(SECOidTag type, |
|
328 NSSCMSGenericWrapperData *gd) |
|
329 { |
|
330 const nsscmstypeInfo *typeInfo; |
|
331 |
|
332 /* short cut common case */ |
|
333 if (type == SEC_OID_PKCS7_DATA) { |
|
334 return SECSuccess; |
|
335 } |
|
336 |
|
337 typeInfo = nss_cmstype_lookup(type); |
|
338 if (typeInfo) { |
|
339 if (typeInfo->encode_start) { |
|
340 return (*typeInfo->encode_start)(gd); |
|
341 } |
|
342 /* decoder ops optional for data tags */ |
|
343 if (typeInfo->isData) { |
|
344 return SECSuccess; |
|
345 } |
|
346 } |
|
347 /* expected a function, but none existed */ |
|
348 return SECFailure; |
|
349 } |
|
350 |
|
351 SECStatus |
|
352 NSS_CMSGenericWrapperData_Encode_BeforeData(SECOidTag type, |
|
353 NSSCMSGenericWrapperData *gd) |
|
354 { |
|
355 const nsscmstypeInfo *typeInfo; |
|
356 |
|
357 /* short cut common case */ |
|
358 if (type == SEC_OID_PKCS7_DATA) { |
|
359 return SECSuccess; |
|
360 } |
|
361 |
|
362 typeInfo = nss_cmstype_lookup(type); |
|
363 if (typeInfo) { |
|
364 if (typeInfo->encode_before) { |
|
365 return (*typeInfo->encode_before)(gd); |
|
366 } |
|
367 /* decoder ops optional for data tags */ |
|
368 if (typeInfo->isData) { |
|
369 return SECSuccess; |
|
370 } |
|
371 } |
|
372 /* expected a function, but none existed */ |
|
373 return SECFailure; |
|
374 } |
|
375 |
|
376 SECStatus |
|
377 NSS_CMSGenericWrapperData_Encode_AfterData(SECOidTag type, |
|
378 NSSCMSGenericWrapperData *gd) |
|
379 { |
|
380 const nsscmstypeInfo *typeInfo; |
|
381 |
|
382 /* short cut common case */ |
|
383 if (type == SEC_OID_PKCS7_DATA) { |
|
384 return SECSuccess; |
|
385 } |
|
386 |
|
387 typeInfo = nss_cmstype_lookup(type); |
|
388 if (typeInfo) { |
|
389 if (typeInfo->encode_after) { |
|
390 return (*typeInfo->encode_after)(gd); |
|
391 } |
|
392 /* decoder ops optional for data tags */ |
|
393 if (typeInfo->isData) { |
|
394 return SECSuccess; |
|
395 } |
|
396 } |
|
397 /* expected a function, but none existed */ |
|
398 return SECFailure; |
|
399 } |
|
400 |
|
401 |
|
402 SECStatus |
|
403 NSS_CMSType_RegisterContentType(SECOidTag type, |
|
404 SEC_ASN1Template *asn1Template, size_t size, |
|
405 NSSCMSGenericWrapperDataDestroy destroy, |
|
406 NSSCMSGenericWrapperDataCallback decode_before, |
|
407 NSSCMSGenericWrapperDataCallback decode_after, |
|
408 NSSCMSGenericWrapperDataCallback decode_end, |
|
409 NSSCMSGenericWrapperDataCallback encode_start, |
|
410 NSSCMSGenericWrapperDataCallback encode_before, |
|
411 NSSCMSGenericWrapperDataCallback encode_after, |
|
412 PRBool isData) |
|
413 { |
|
414 PRStatus rc; |
|
415 SECStatus rv; |
|
416 nsscmstypeInfo *typeInfo; |
|
417 const nsscmstypeInfo *exists; |
|
418 |
|
419 rc = PR_CallOnce( &nsscmstypeOnce, nss_cmstype_init); |
|
420 if (rc == PR_FAILURE) { |
|
421 return SECFailure; |
|
422 } |
|
423 PR_Lock(nsscmstypeAddLock); |
|
424 exists = nss_cmstype_lookup(type); |
|
425 if (exists) { |
|
426 PR_Unlock(nsscmstypeAddLock); |
|
427 /* already added */ |
|
428 return SECSuccess; |
|
429 } |
|
430 typeInfo = PORT_ArenaNew(nsscmstypeArena, nsscmstypeInfo); |
|
431 typeInfo->type = type; |
|
432 typeInfo->size = size; |
|
433 typeInfo->isData = isData; |
|
434 typeInfo->template = asn1Template; |
|
435 typeInfo->destroy = destroy; |
|
436 typeInfo->decode_before = decode_before; |
|
437 typeInfo->decode_after = decode_after; |
|
438 typeInfo->decode_end = decode_end; |
|
439 typeInfo->encode_start = encode_start; |
|
440 typeInfo->encode_before = encode_before; |
|
441 typeInfo->encode_after = encode_after; |
|
442 rv = nss_cmstype_add(type, typeInfo); |
|
443 PR_Unlock(nsscmstypeAddLock); |
|
444 return rv; |
|
445 } |
|
446 |