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