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 *
GetExtension(CERTCertExtension ** extensions,SECItem * oid)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
cert_FindExtensionByOID(CERTCertExtension ** extensions,SECItem * oid,SECItem * value)47 cert_FindExtensionByOID(CERTCertExtension **extensions, SECItem *oid,
48 SECItem *value)
49 {
50 CERTCertExtension *ext;
51 SECStatus rv = SECSuccess;
52
53 ext = GetExtension(extensions, oid);
54 if (ext == NULL) {
55 PORT_SetError(SEC_ERROR_EXTENSION_NOT_FOUND);
56 return (SECFailure);
57 }
58 if (value)
59 rv = SECITEM_CopyItem(NULL, value, &ext->value);
60 return (rv);
61 }
62
63 SECStatus
CERT_GetExtenCriticality(CERTCertExtension ** extensions,int tag,PRBool * isCritical)64 CERT_GetExtenCriticality(CERTCertExtension **extensions, int tag,
65 PRBool *isCritical)
66 {
67 CERTCertExtension *ext;
68 SECOidData *oid;
69
70 if (!isCritical)
71 return (SECSuccess);
72
73 /* find the extension in the extensions list */
74 oid = SECOID_FindOIDByTag((SECOidTag)tag);
75 if (!oid) {
76 return (SECFailure);
77 }
78 ext = GetExtension(extensions, &oid->oid);
79 if (ext == NULL) {
80 PORT_SetError(SEC_ERROR_EXTENSION_NOT_FOUND);
81 return (SECFailure);
82 }
83
84 /* If the criticality is omitted, then it is false by default.
85 ex->critical.data is NULL */
86 if (ext->critical.data == NULL)
87 *isCritical = PR_FALSE;
88 else
89 *isCritical = (ext->critical.data[0] == 0xff) ? PR_TRUE : PR_FALSE;
90 return (SECSuccess);
91 }
92
93 SECStatus
cert_FindExtension(CERTCertExtension ** extensions,int tag,SECItem * value)94 cert_FindExtension(CERTCertExtension **extensions, int tag, SECItem *value)
95 {
96 SECOidData *oid;
97
98 oid = SECOID_FindOIDByTag((SECOidTag)tag);
99 if (!oid) {
100 return (SECFailure);
101 }
102
103 return (cert_FindExtensionByOID(extensions, &oid->oid, value));
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 *
cert_StartExtensions(void * owner,PLArenaPool * ownerArena,void (* setExts)(void * object,CERTCertExtension ** exts))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
CERT_AddExtensionByOID(void * exthandle,SECItem * oid,SECItem * value,PRBool critical,PRBool copyData)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 /* set critical field */
191 if (critical) {
192 ext->critical.data = (unsigned char *)&hextrue;
193 ext->critical.len = 1;
194 }
195
196 /* set object ID of the extension and its value */
197 if (copyData) {
198 rv = SECITEM_CopyItem(handle->ownerArena, &ext->id, oid);
199 if (rv) {
200 return (SECFailure);
201 }
202
203 rv = SECITEM_CopyItem(handle->ownerArena, &ext->value, value);
204 if (rv) {
205 return (SECFailure);
206 }
207 } else {
208 ext->id = *oid;
209 ext->value = *value;
210 }
211
212 handle->count++;
213
214 return (SECSuccess);
215 }
216
217 SECStatus
CERT_AddExtension(void * exthandle,int idtag,SECItem * value,PRBool critical,PRBool copyData)218 CERT_AddExtension(void *exthandle, int idtag, SECItem *value, PRBool critical,
219 PRBool copyData)
220 {
221 SECOidData *oid;
222
223 oid = SECOID_FindOIDByTag((SECOidTag)idtag);
224 if (!oid) {
225 return (SECFailure);
226 }
227
228 return (CERT_AddExtensionByOID(exthandle, &oid->oid, value, critical,
229 copyData));
230 }
231
232 SECStatus
CERT_EncodeAndAddExtension(void * exthandle,int idtag,void * value,PRBool critical,const SEC_ASN1Template * atemplate)233 CERT_EncodeAndAddExtension(void *exthandle, int idtag, void *value,
234 PRBool critical, const SEC_ASN1Template *atemplate)
235 {
236 extRec *handle;
237 SECItem *encitem;
238
239 handle = (extRec *)exthandle;
240
241 encitem = SEC_ASN1EncodeItem(handle->ownerArena, NULL, value, atemplate);
242 if (encitem == NULL) {
243 return (SECFailure);
244 }
245
246 return CERT_AddExtension(exthandle, idtag, encitem, critical, PR_FALSE);
247 }
248
249 void
PrepareBitStringForEncoding(SECItem * bitsmap,SECItem * value)250 PrepareBitStringForEncoding(SECItem *bitsmap, SECItem *value)
251 {
252 unsigned char onebyte;
253 unsigned int i, len = 0;
254
255 /* to prevent warning on some platform at compile time */
256 onebyte = '\0';
257 /* Get the position of the right-most turn-on bit */
258 for (i = 0; i < (value->len) * 8; ++i) {
259 if (i % 8 == 0)
260 onebyte = value->data[i / 8];
261 if (onebyte & 0x80)
262 len = i;
263 onebyte <<= 1;
264 }
265 bitsmap->data = value->data;
266 /* Add one here since we work with base 1 */
267 bitsmap->len = len + 1;
268 }
269
270 SECStatus
CERT_EncodeAndAddBitStrExtension(void * exthandle,int idtag,SECItem * value,PRBool critical)271 CERT_EncodeAndAddBitStrExtension(void *exthandle, int idtag, SECItem *value,
272 PRBool critical)
273 {
274 SECItem bitsmap;
275
276 PrepareBitStringForEncoding(&bitsmap, value);
277 return (CERT_EncodeAndAddExtension(exthandle, idtag, &bitsmap, critical,
278 SEC_ASN1_GET(SEC_BitStringTemplate)));
279 }
280
281 SECStatus
CERT_FinishExtensions(void * exthandle)282 CERT_FinishExtensions(void *exthandle)
283 {
284 extRec *handle;
285 extNode *node;
286 CERTCertExtension **exts;
287 SECStatus rv = SECFailure;
288
289 handle = (extRec *)exthandle;
290
291 /* allocate space for extensions array */
292 exts = PORT_ArenaNewArray(handle->ownerArena, CERTCertExtension *,
293 handle->count + 1);
294 if (exts == NULL) {
295 goto loser;
296 }
297
298 /* put extensions in owner object and update its version number */
299
300 #ifdef OLD
301 switch (handle->type) {
302 case CertificateExtensions:
303 handle->owner.cert->extensions = exts;
304 DER_SetUInteger(ownerArena, &(handle->owner.cert->version),
305 SEC_CERTIFICATE_VERSION_3);
306 break;
307 case CrlExtensions:
308 handle->owner.crl->extensions = exts;
309 DER_SetUInteger(ownerArena, &(handle->owner.crl->version),
310 SEC_CRL_VERSION_2);
311 break;
312 case OCSPRequestExtensions:
313 handle->owner.request->tbsRequest->requestExtensions = exts;
314 break;
315 case OCSPSingleRequestExtensions:
316 handle->owner.singleRequest->singleRequestExtensions = exts;
317 break;
318 case OCSPResponseSingleExtensions:
319 handle->owner.singleResponse->singleExtensions = exts;
320 break;
321 }
322 #endif
323
324 handle->setExts(handle->object, exts);
325
326 /* update the version number */
327
328 /* copy each extension pointer */
329 node = handle->head;
330 while (node) {
331 *exts = node->ext;
332
333 node = node->next;
334 exts++;
335 }
336
337 /* terminate the array of extensions */
338 *exts = 0;
339
340 rv = SECSuccess;
341
342 loser:
343 /* free working arena */
344 PORT_FreeArena(handle->arena, PR_FALSE);
345 return rv;
346 }
347
348 SECStatus
CERT_MergeExtensions(void * exthandle,CERTCertExtension ** extensions)349 CERT_MergeExtensions(void *exthandle, CERTCertExtension **extensions)
350 {
351 CERTCertExtension *ext;
352 SECStatus rv = SECSuccess;
353 SECOidTag tag;
354 extNode *node;
355 extRec *handle = exthandle;
356
357 if (!exthandle || !extensions) {
358 PORT_SetError(SEC_ERROR_INVALID_ARGS);
359 return SECFailure;
360 }
361 while ((ext = *extensions++) != NULL) {
362 tag = SECOID_FindOIDTag(&ext->id);
363 for (node = handle->head; node != NULL; node = node->next) {
364 if (tag == 0) {
365 if (SECITEM_ItemsAreEqual(&ext->id, &node->ext->id))
366 break;
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
CERT_FindBitStringExtension(CERTCertExtension ** extensions,int tag,SECItem * retItem)395 CERT_FindBitStringExtension(CERTCertExtension **extensions, int tag,
396 SECItem *retItem)
397 {
398 SECItem wrapperItem, tmpItem = { siBuffer, 0 };
399 SECStatus rv;
400 PORTCheapArenaPool tmpArena;
401
402 wrapperItem.data = NULL;
403 tmpItem.data = NULL;
404
405 PORT_InitCheapArena(&tmpArena, DER_DEFAULT_CHUNKSIZE);
406
407 rv = cert_FindExtension(extensions, tag, &wrapperItem);
408 if (rv != SECSuccess) {
409 goto loser;
410 }
411
412 rv = SEC_QuickDERDecodeItem(&tmpArena.arena, &tmpItem,
413 SEC_ASN1_GET(SEC_BitStringTemplate),
414 &wrapperItem);
415
416 if (rv != SECSuccess) {
417 goto loser;
418 }
419
420 retItem->data = (unsigned char *)PORT_Alloc((tmpItem.len + 7) >> 3);
421 if (retItem->data == NULL) {
422 goto loser;
423 }
424
425 PORT_Memcpy(retItem->data, tmpItem.data, (tmpItem.len + 7) >> 3);
426 retItem->len = tmpItem.len;
427
428 rv = SECSuccess;
429 goto done;
430
431 loser:
432 rv = SECFailure;
433
434 done:
435 PORT_DestroyCheapArena(&tmpArena);
436
437 if (wrapperItem.data) {
438 PORT_Free(wrapperItem.data);
439 }
440
441 return (rv);
442 }
443
444 PRBool
cert_HasCriticalExtension(CERTCertExtension ** extensions)445 cert_HasCriticalExtension(CERTCertExtension **extensions)
446 {
447 CERTCertExtension **exts;
448 CERTCertExtension *ext = NULL;
449 PRBool hasCriticalExten = PR_FALSE;
450
451 exts = extensions;
452
453 if (exts) {
454 while (*exts) {
455 ext = *exts;
456 /* If the criticality is omitted, it's non-critical */
457 if (ext->critical.data && ext->critical.data[0] == 0xff) {
458 hasCriticalExten = PR_TRUE;
459 break;
460 }
461 exts++;
462 }
463 }
464 return (hasCriticalExten);
465 }
466
467 PRBool
cert_HasUnknownCriticalExten(CERTCertExtension ** extensions)468 cert_HasUnknownCriticalExten(CERTCertExtension **extensions)
469 {
470 CERTCertExtension **exts;
471 CERTCertExtension *ext = NULL;
472 PRBool hasUnknownCriticalExten = PR_FALSE;
473
474 exts = extensions;
475
476 if (exts) {
477 while (*exts) {
478 ext = *exts;
479 /* If the criticality is omitted, it's non-critical.
480 If an extension is critical, make sure that we know
481 how to process the extension.
482 */
483 if (ext->critical.data && ext->critical.data[0] == 0xff) {
484 if (SECOID_KnownCertExtenOID(&ext->id) == PR_FALSE) {
485 hasUnknownCriticalExten = PR_TRUE;
486 break;
487 }
488 }
489 exts++;
490 }
491 }
492 return (hasUnknownCriticalExten);
493 }
494