|
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 * Certificate Extensions handling code |
|
7 * |
|
8 */ |
|
9 |
|
10 #include "cert.h" |
|
11 #include "secitem.h" |
|
12 #include "secoid.h" |
|
13 #include "secder.h" |
|
14 #include "secasn1.h" |
|
15 #include "certxutl.h" |
|
16 #include "secerr.h" |
|
17 |
|
18 #ifdef OLD |
|
19 #include "ocspti.h" /* XXX a better extensions interface would not |
|
20 * require knowledge of data structures of callers */ |
|
21 #endif |
|
22 |
|
23 static CERTCertExtension * |
|
24 GetExtension (CERTCertExtension **extensions, SECItem *oid) |
|
25 { |
|
26 CERTCertExtension **exts; |
|
27 CERTCertExtension *ext = NULL; |
|
28 SECComparison comp; |
|
29 |
|
30 exts = extensions; |
|
31 |
|
32 if (exts) { |
|
33 while ( *exts ) { |
|
34 ext = *exts; |
|
35 comp = SECITEM_CompareItem(oid, &ext->id); |
|
36 if ( comp == SECEqual ) |
|
37 break; |
|
38 |
|
39 exts++; |
|
40 } |
|
41 return (*exts ? ext : NULL); |
|
42 } |
|
43 return (NULL); |
|
44 } |
|
45 |
|
46 SECStatus |
|
47 cert_FindExtensionByOID (CERTCertExtension **extensions, SECItem *oid, SECItem *value) |
|
48 { |
|
49 CERTCertExtension *ext; |
|
50 SECStatus rv = SECSuccess; |
|
51 |
|
52 ext = GetExtension (extensions, oid); |
|
53 if (ext == NULL) { |
|
54 PORT_SetError (SEC_ERROR_EXTENSION_NOT_FOUND); |
|
55 return (SECFailure); |
|
56 } |
|
57 if (value) |
|
58 rv = SECITEM_CopyItem(NULL, value, &ext->value); |
|
59 return (rv); |
|
60 } |
|
61 |
|
62 |
|
63 SECStatus |
|
64 CERT_GetExtenCriticality (CERTCertExtension **extensions, int tag, PRBool *isCritical) |
|
65 { |
|
66 CERTCertExtension *ext; |
|
67 SECOidData *oid; |
|
68 |
|
69 if (!isCritical) |
|
70 return (SECSuccess); |
|
71 |
|
72 /* find the extension in the extensions list */ |
|
73 oid = SECOID_FindOIDByTag((SECOidTag)tag); |
|
74 if ( !oid ) { |
|
75 return(SECFailure); |
|
76 } |
|
77 ext = GetExtension (extensions, &oid->oid); |
|
78 if (ext == NULL) { |
|
79 PORT_SetError (SEC_ERROR_EXTENSION_NOT_FOUND); |
|
80 return (SECFailure); |
|
81 } |
|
82 |
|
83 /* If the criticality is omitted, then it is false by default. |
|
84 ex->critical.data is NULL */ |
|
85 if (ext->critical.data == NULL) |
|
86 *isCritical = PR_FALSE; |
|
87 else |
|
88 *isCritical = (ext->critical.data[0] == 0xff) ? PR_TRUE : PR_FALSE; |
|
89 return (SECSuccess); |
|
90 } |
|
91 |
|
92 SECStatus |
|
93 cert_FindExtension(CERTCertExtension **extensions, int tag, SECItem *value) |
|
94 { |
|
95 SECOidData *oid; |
|
96 |
|
97 oid = SECOID_FindOIDByTag((SECOidTag)tag); |
|
98 if ( !oid ) { |
|
99 return(SECFailure); |
|
100 } |
|
101 |
|
102 return(cert_FindExtensionByOID(extensions, &oid->oid, value)); |
|
103 } |
|
104 |
|
105 |
|
106 typedef struct _extNode { |
|
107 struct _extNode *next; |
|
108 CERTCertExtension *ext; |
|
109 } extNode; |
|
110 |
|
111 typedef struct { |
|
112 void (*setExts)(void *object, CERTCertExtension **exts); |
|
113 void *object; |
|
114 PLArenaPool *ownerArena; |
|
115 PLArenaPool *arena; |
|
116 extNode *head; |
|
117 int count; |
|
118 }extRec; |
|
119 |
|
120 /* |
|
121 * cert_StartExtensions |
|
122 * |
|
123 * NOTE: This interface changed significantly to remove knowledge |
|
124 * about callers data structures (owner objects) |
|
125 */ |
|
126 void * |
|
127 cert_StartExtensions(void *owner, PLArenaPool *ownerArena, |
|
128 void (*setExts)(void *object, CERTCertExtension **exts)) |
|
129 { |
|
130 PLArenaPool *arena; |
|
131 extRec *handle; |
|
132 |
|
133 arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); |
|
134 if ( !arena ) { |
|
135 return(0); |
|
136 } |
|
137 |
|
138 handle = (extRec *)PORT_ArenaAlloc(arena, sizeof(extRec)); |
|
139 if ( !handle ) { |
|
140 PORT_FreeArena(arena, PR_FALSE); |
|
141 return(0); |
|
142 } |
|
143 |
|
144 handle->object = owner; |
|
145 handle->ownerArena = ownerArena; |
|
146 handle->setExts = setExts; |
|
147 |
|
148 handle->arena = arena; |
|
149 handle->head = 0; |
|
150 handle->count = 0; |
|
151 |
|
152 return(handle); |
|
153 } |
|
154 |
|
155 static unsigned char hextrue = 0xff; |
|
156 |
|
157 /* |
|
158 * Note - assumes that data pointed to by oid->data will not move |
|
159 */ |
|
160 SECStatus |
|
161 CERT_AddExtensionByOID (void *exthandle, SECItem *oid, SECItem *value, |
|
162 PRBool critical, PRBool copyData) |
|
163 { |
|
164 CERTCertExtension *ext; |
|
165 SECStatus rv; |
|
166 extNode *node; |
|
167 extRec *handle; |
|
168 |
|
169 handle = (extRec *)exthandle; |
|
170 |
|
171 /* allocate space for extension and list node */ |
|
172 ext = (CERTCertExtension*)PORT_ArenaZAlloc(handle->ownerArena, |
|
173 sizeof(CERTCertExtension)); |
|
174 if ( !ext ) { |
|
175 return(SECFailure); |
|
176 } |
|
177 |
|
178 node = (extNode*)PORT_ArenaAlloc(handle->arena, sizeof(extNode)); |
|
179 if ( !node ) { |
|
180 return(SECFailure); |
|
181 } |
|
182 |
|
183 /* add to list */ |
|
184 node->next = handle->head; |
|
185 handle->head = node; |
|
186 |
|
187 /* point to ext struct */ |
|
188 node->ext = ext; |
|
189 |
|
190 /* the object ID of the extension */ |
|
191 ext->id = *oid; |
|
192 |
|
193 /* set critical field */ |
|
194 if ( critical ) { |
|
195 ext->critical.data = (unsigned char*)&hextrue; |
|
196 ext->critical.len = 1; |
|
197 } |
|
198 |
|
199 /* set the value */ |
|
200 if ( copyData ) { |
|
201 rv = SECITEM_CopyItem(handle->ownerArena, &ext->value, value); |
|
202 if ( rv ) { |
|
203 return(SECFailure); |
|
204 } |
|
205 } else { |
|
206 ext->value = *value; |
|
207 } |
|
208 |
|
209 handle->count++; |
|
210 |
|
211 return(SECSuccess); |
|
212 |
|
213 } |
|
214 |
|
215 SECStatus |
|
216 CERT_AddExtension(void *exthandle, int idtag, SECItem *value, |
|
217 PRBool critical, PRBool copyData) |
|
218 { |
|
219 SECOidData *oid; |
|
220 |
|
221 oid = SECOID_FindOIDByTag((SECOidTag)idtag); |
|
222 if ( !oid ) { |
|
223 return(SECFailure); |
|
224 } |
|
225 |
|
226 return(CERT_AddExtensionByOID(exthandle, &oid->oid, value, critical, copyData)); |
|
227 } |
|
228 |
|
229 SECStatus |
|
230 CERT_EncodeAndAddExtension(void *exthandle, int idtag, void *value, |
|
231 PRBool critical, const SEC_ASN1Template *atemplate) |
|
232 { |
|
233 extRec *handle; |
|
234 SECItem *encitem; |
|
235 |
|
236 handle = (extRec *)exthandle; |
|
237 |
|
238 encitem = SEC_ASN1EncodeItem(handle->ownerArena, NULL, value, atemplate); |
|
239 if ( encitem == NULL ) { |
|
240 return(SECFailure); |
|
241 } |
|
242 |
|
243 return CERT_AddExtension(exthandle, idtag, encitem, critical, PR_FALSE); |
|
244 } |
|
245 |
|
246 void |
|
247 PrepareBitStringForEncoding (SECItem *bitsmap, SECItem *value) |
|
248 { |
|
249 unsigned char onebyte; |
|
250 unsigned int i, len = 0; |
|
251 |
|
252 /* to prevent warning on some platform at compile time */ |
|
253 onebyte = '\0'; |
|
254 /* Get the position of the right-most turn-on bit */ |
|
255 for (i = 0; i < (value->len ) * 8; ++i) { |
|
256 if (i % 8 == 0) |
|
257 onebyte = value->data[i/8]; |
|
258 if (onebyte & 0x80) |
|
259 len = i; |
|
260 onebyte <<= 1; |
|
261 |
|
262 } |
|
263 bitsmap->data = value->data; |
|
264 /* Add one here since we work with base 1 */ |
|
265 bitsmap->len = len + 1; |
|
266 } |
|
267 |
|
268 SECStatus |
|
269 CERT_EncodeAndAddBitStrExtension (void *exthandle, int idtag, |
|
270 SECItem *value, PRBool critical) |
|
271 { |
|
272 SECItem bitsmap; |
|
273 |
|
274 PrepareBitStringForEncoding (&bitsmap, value); |
|
275 return (CERT_EncodeAndAddExtension |
|
276 (exthandle, idtag, &bitsmap, critical, |
|
277 SEC_ASN1_GET(SEC_BitStringTemplate))); |
|
278 } |
|
279 |
|
280 SECStatus |
|
281 CERT_FinishExtensions(void *exthandle) |
|
282 { |
|
283 extRec *handle; |
|
284 extNode *node; |
|
285 CERTCertExtension **exts; |
|
286 SECStatus rv = SECFailure; |
|
287 |
|
288 handle = (extRec *)exthandle; |
|
289 |
|
290 /* allocate space for extensions array */ |
|
291 exts = PORT_ArenaNewArray(handle->ownerArena, CERTCertExtension *, |
|
292 handle->count + 1); |
|
293 if (exts == NULL) { |
|
294 goto loser; |
|
295 } |
|
296 |
|
297 /* put extensions in owner object and update its version number */ |
|
298 |
|
299 #ifdef OLD |
|
300 switch (handle->type) { |
|
301 case CertificateExtensions: |
|
302 handle->owner.cert->extensions = exts; |
|
303 DER_SetUInteger (ownerArena, &(handle->owner.cert->version), |
|
304 SEC_CERTIFICATE_VERSION_3); |
|
305 break; |
|
306 case CrlExtensions: |
|
307 handle->owner.crl->extensions = exts; |
|
308 DER_SetUInteger (ownerArena, &(handle->owner.crl->version), |
|
309 SEC_CRL_VERSION_2); |
|
310 break; |
|
311 case OCSPRequestExtensions: |
|
312 handle->owner.request->tbsRequest->requestExtensions = exts; |
|
313 break; |
|
314 case OCSPSingleRequestExtensions: |
|
315 handle->owner.singleRequest->singleRequestExtensions = exts; |
|
316 break; |
|
317 case OCSPResponseSingleExtensions: |
|
318 handle->owner.singleResponse->singleExtensions = exts; |
|
319 break; |
|
320 } |
|
321 #endif |
|
322 |
|
323 handle->setExts(handle->object, exts); |
|
324 |
|
325 /* update the version number */ |
|
326 |
|
327 /* copy each extension pointer */ |
|
328 node = handle->head; |
|
329 while ( node ) { |
|
330 *exts = node->ext; |
|
331 |
|
332 node = node->next; |
|
333 exts++; |
|
334 } |
|
335 |
|
336 /* terminate the array of extensions */ |
|
337 *exts = 0; |
|
338 |
|
339 rv = SECSuccess; |
|
340 |
|
341 loser: |
|
342 /* free working arena */ |
|
343 PORT_FreeArena(handle->arena, PR_FALSE); |
|
344 return rv; |
|
345 } |
|
346 |
|
347 SECStatus |
|
348 CERT_MergeExtensions(void *exthandle, CERTCertExtension **extensions) |
|
349 { |
|
350 CERTCertExtension *ext; |
|
351 SECStatus rv = SECSuccess; |
|
352 SECOidTag tag; |
|
353 extNode *node; |
|
354 extRec *handle = exthandle; |
|
355 |
|
356 if (!exthandle || !extensions) { |
|
357 PORT_SetError(SEC_ERROR_INVALID_ARGS); |
|
358 return SECFailure; |
|
359 } |
|
360 while ((ext = *extensions++) != NULL) { |
|
361 tag = SECOID_FindOIDTag(&ext->id); |
|
362 for (node=handle->head; node != NULL; node=node->next) { |
|
363 if (tag == 0) { |
|
364 if (SECITEM_ItemsAreEqual(&ext->id, &node->ext->id)) |
|
365 break; |
|
366 } |
|
367 else { |
|
368 if (SECOID_FindOIDTag(&node->ext->id) == tag) { |
|
369 break; |
|
370 } |
|
371 } |
|
372 } |
|
373 if (node == NULL) { |
|
374 PRBool critical = (ext->critical.len != 0 && |
|
375 ext->critical.data[ext->critical.len - 1] != 0); |
|
376 if (critical && tag == SEC_OID_UNKNOWN) { |
|
377 PORT_SetError(SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION); |
|
378 rv = SECFailure; |
|
379 break; |
|
380 } |
|
381 /* add to list */ |
|
382 rv = CERT_AddExtensionByOID (exthandle, &ext->id, &ext->value, |
|
383 critical, PR_TRUE); |
|
384 if (rv != SECSuccess) |
|
385 break; |
|
386 } |
|
387 } |
|
388 return rv; |
|
389 } |
|
390 |
|
391 /* |
|
392 * get the value of the Netscape Certificate Type Extension |
|
393 */ |
|
394 SECStatus |
|
395 CERT_FindBitStringExtension (CERTCertExtension **extensions, int tag, |
|
396 SECItem *retItem) |
|
397 { |
|
398 SECItem wrapperItem, tmpItem = {siBuffer,0}; |
|
399 SECStatus rv; |
|
400 PLArenaPool *arena = NULL; |
|
401 |
|
402 wrapperItem.data = NULL; |
|
403 tmpItem.data = NULL; |
|
404 |
|
405 arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); |
|
406 |
|
407 if ( ! arena ) { |
|
408 return(SECFailure); |
|
409 } |
|
410 |
|
411 rv = cert_FindExtension(extensions, tag, &wrapperItem); |
|
412 if ( rv != SECSuccess ) { |
|
413 goto loser; |
|
414 } |
|
415 |
|
416 rv = SEC_QuickDERDecodeItem(arena, &tmpItem, |
|
417 SEC_ASN1_GET(SEC_BitStringTemplate), |
|
418 &wrapperItem); |
|
419 |
|
420 if ( rv != SECSuccess ) { |
|
421 goto loser; |
|
422 } |
|
423 |
|
424 retItem->data = (unsigned char *)PORT_Alloc( ( tmpItem.len + 7 ) >> 3 ); |
|
425 if ( retItem->data == NULL ) { |
|
426 goto loser; |
|
427 } |
|
428 |
|
429 PORT_Memcpy(retItem->data, tmpItem.data, ( tmpItem.len + 7 ) >> 3); |
|
430 retItem->len = tmpItem.len; |
|
431 |
|
432 rv = SECSuccess; |
|
433 goto done; |
|
434 |
|
435 loser: |
|
436 rv = SECFailure; |
|
437 |
|
438 done: |
|
439 if ( arena ) { |
|
440 PORT_FreeArena(arena, PR_FALSE); |
|
441 } |
|
442 |
|
443 if ( wrapperItem.data ) { |
|
444 PORT_Free(wrapperItem.data); |
|
445 } |
|
446 |
|
447 return(rv); |
|
448 } |
|
449 |
|
450 PRBool |
|
451 cert_HasCriticalExtension (CERTCertExtension **extensions) |
|
452 { |
|
453 CERTCertExtension **exts; |
|
454 CERTCertExtension *ext = NULL; |
|
455 PRBool hasCriticalExten = PR_FALSE; |
|
456 |
|
457 exts = extensions; |
|
458 |
|
459 if (exts) { |
|
460 while ( *exts ) { |
|
461 ext = *exts; |
|
462 /* If the criticality is omitted, it's non-critical */ |
|
463 if (ext->critical.data && ext->critical.data[0] == 0xff) { |
|
464 hasCriticalExten = PR_TRUE; |
|
465 break; |
|
466 } |
|
467 exts++; |
|
468 } |
|
469 } |
|
470 return (hasCriticalExten); |
|
471 } |
|
472 |
|
473 PRBool |
|
474 cert_HasUnknownCriticalExten (CERTCertExtension **extensions) |
|
475 { |
|
476 CERTCertExtension **exts; |
|
477 CERTCertExtension *ext = NULL; |
|
478 PRBool hasUnknownCriticalExten = PR_FALSE; |
|
479 |
|
480 exts = extensions; |
|
481 |
|
482 if (exts) { |
|
483 while ( *exts ) { |
|
484 ext = *exts; |
|
485 /* If the criticality is omitted, it's non-critical. |
|
486 If an extension is critical, make sure that we know |
|
487 how to process the extension. |
|
488 */ |
|
489 if (ext->critical.data && ext->critical.data[0] == 0xff) { |
|
490 if (SECOID_KnownCertExtenOID (&ext->id) == PR_FALSE) { |
|
491 hasUnknownCriticalExten = PR_TRUE; |
|
492 break; |
|
493 } |
|
494 } |
|
495 exts++; |
|
496 } |
|
497 } |
|
498 return (hasUnknownCriticalExten); |
|
499 } |