Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
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/. */
5 /*
6 * Certificate Extensions handling code
7 *
8 */
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"
18 #ifdef OLD
19 #include "ocspti.h" /* XXX a better extensions interface would not
20 * require knowledge of data structures of callers */
21 #endif
23 static CERTCertExtension *
24 GetExtension (CERTCertExtension **extensions, SECItem *oid)
25 {
26 CERTCertExtension **exts;
27 CERTCertExtension *ext = NULL;
28 SECComparison comp;
30 exts = extensions;
32 if (exts) {
33 while ( *exts ) {
34 ext = *exts;
35 comp = SECITEM_CompareItem(oid, &ext->id);
36 if ( comp == SECEqual )
37 break;
39 exts++;
40 }
41 return (*exts ? ext : NULL);
42 }
43 return (NULL);
44 }
46 SECStatus
47 cert_FindExtensionByOID (CERTCertExtension **extensions, SECItem *oid, SECItem *value)
48 {
49 CERTCertExtension *ext;
50 SECStatus rv = SECSuccess;
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 }
63 SECStatus
64 CERT_GetExtenCriticality (CERTCertExtension **extensions, int tag, PRBool *isCritical)
65 {
66 CERTCertExtension *ext;
67 SECOidData *oid;
69 if (!isCritical)
70 return (SECSuccess);
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 }
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 }
92 SECStatus
93 cert_FindExtension(CERTCertExtension **extensions, int tag, SECItem *value)
94 {
95 SECOidData *oid;
97 oid = SECOID_FindOIDByTag((SECOidTag)tag);
98 if ( !oid ) {
99 return(SECFailure);
100 }
102 return(cert_FindExtensionByOID(extensions, &oid->oid, value));
103 }
106 typedef struct _extNode {
107 struct _extNode *next;
108 CERTCertExtension *ext;
109 } extNode;
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;
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;
133 arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
134 if ( !arena ) {
135 return(0);
136 }
138 handle = (extRec *)PORT_ArenaAlloc(arena, sizeof(extRec));
139 if ( !handle ) {
140 PORT_FreeArena(arena, PR_FALSE);
141 return(0);
142 }
144 handle->object = owner;
145 handle->ownerArena = ownerArena;
146 handle->setExts = setExts;
148 handle->arena = arena;
149 handle->head = 0;
150 handle->count = 0;
152 return(handle);
153 }
155 static unsigned char hextrue = 0xff;
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;
169 handle = (extRec *)exthandle;
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 }
178 node = (extNode*)PORT_ArenaAlloc(handle->arena, sizeof(extNode));
179 if ( !node ) {
180 return(SECFailure);
181 }
183 /* add to list */
184 node->next = handle->head;
185 handle->head = node;
187 /* point to ext struct */
188 node->ext = ext;
190 /* the object ID of the extension */
191 ext->id = *oid;
193 /* set critical field */
194 if ( critical ) {
195 ext->critical.data = (unsigned char*)&hextrue;
196 ext->critical.len = 1;
197 }
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 }
209 handle->count++;
211 return(SECSuccess);
213 }
215 SECStatus
216 CERT_AddExtension(void *exthandle, int idtag, SECItem *value,
217 PRBool critical, PRBool copyData)
218 {
219 SECOidData *oid;
221 oid = SECOID_FindOIDByTag((SECOidTag)idtag);
222 if ( !oid ) {
223 return(SECFailure);
224 }
226 return(CERT_AddExtensionByOID(exthandle, &oid->oid, value, critical, copyData));
227 }
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;
236 handle = (extRec *)exthandle;
238 encitem = SEC_ASN1EncodeItem(handle->ownerArena, NULL, value, atemplate);
239 if ( encitem == NULL ) {
240 return(SECFailure);
241 }
243 return CERT_AddExtension(exthandle, idtag, encitem, critical, PR_FALSE);
244 }
246 void
247 PrepareBitStringForEncoding (SECItem *bitsmap, SECItem *value)
248 {
249 unsigned char onebyte;
250 unsigned int i, len = 0;
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;
262 }
263 bitsmap->data = value->data;
264 /* Add one here since we work with base 1 */
265 bitsmap->len = len + 1;
266 }
268 SECStatus
269 CERT_EncodeAndAddBitStrExtension (void *exthandle, int idtag,
270 SECItem *value, PRBool critical)
271 {
272 SECItem bitsmap;
274 PrepareBitStringForEncoding (&bitsmap, value);
275 return (CERT_EncodeAndAddExtension
276 (exthandle, idtag, &bitsmap, critical,
277 SEC_ASN1_GET(SEC_BitStringTemplate)));
278 }
280 SECStatus
281 CERT_FinishExtensions(void *exthandle)
282 {
283 extRec *handle;
284 extNode *node;
285 CERTCertExtension **exts;
286 SECStatus rv = SECFailure;
288 handle = (extRec *)exthandle;
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 }
297 /* put extensions in owner object and update its version number */
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
323 handle->setExts(handle->object, exts);
325 /* update the version number */
327 /* copy each extension pointer */
328 node = handle->head;
329 while ( node ) {
330 *exts = node->ext;
332 node = node->next;
333 exts++;
334 }
336 /* terminate the array of extensions */
337 *exts = 0;
339 rv = SECSuccess;
341 loser:
342 /* free working arena */
343 PORT_FreeArena(handle->arena, PR_FALSE);
344 return rv;
345 }
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;
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 }
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;
402 wrapperItem.data = NULL;
403 tmpItem.data = NULL;
405 arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
407 if ( ! arena ) {
408 return(SECFailure);
409 }
411 rv = cert_FindExtension(extensions, tag, &wrapperItem);
412 if ( rv != SECSuccess ) {
413 goto loser;
414 }
416 rv = SEC_QuickDERDecodeItem(arena, &tmpItem,
417 SEC_ASN1_GET(SEC_BitStringTemplate),
418 &wrapperItem);
420 if ( rv != SECSuccess ) {
421 goto loser;
422 }
424 retItem->data = (unsigned char *)PORT_Alloc( ( tmpItem.len + 7 ) >> 3 );
425 if ( retItem->data == NULL ) {
426 goto loser;
427 }
429 PORT_Memcpy(retItem->data, tmpItem.data, ( tmpItem.len + 7 ) >> 3);
430 retItem->len = tmpItem.len;
432 rv = SECSuccess;
433 goto done;
435 loser:
436 rv = SECFailure;
438 done:
439 if ( arena ) {
440 PORT_FreeArena(arena, PR_FALSE);
441 }
443 if ( wrapperItem.data ) {
444 PORT_Free(wrapperItem.data);
445 }
447 return(rv);
448 }
450 PRBool
451 cert_HasCriticalExtension (CERTCertExtension **extensions)
452 {
453 CERTCertExtension **exts;
454 CERTCertExtension *ext = NULL;
455 PRBool hasCriticalExten = PR_FALSE;
457 exts = extensions;
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 }
473 PRBool
474 cert_HasUnknownCriticalExten (CERTCertExtension **extensions)
475 {
476 CERTCertExtension **exts;
477 CERTCertExtension *ext = NULL;
478 PRBool hasUnknownCriticalExten = PR_FALSE;
480 exts = extensions;
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 }