/src/nss/lib/certdb/certxutl.c
Line | Count | Source (jump to first uncovered line) |
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 | 0 | { |
26 | 0 | CERTCertExtension **exts; |
27 | 0 | CERTCertExtension *ext = NULL; |
28 | 0 | SECComparison comp; |
29 | |
|
30 | 0 | exts = extensions; |
31 | |
|
32 | 0 | if (exts) { |
33 | 0 | while (*exts) { |
34 | 0 | ext = *exts; |
35 | 0 | comp = SECITEM_CompareItem(oid, &ext->id); |
36 | 0 | if (comp == SECEqual) |
37 | 0 | break; |
38 | | |
39 | 0 | exts++; |
40 | 0 | } |
41 | 0 | return (*exts ? ext : NULL); |
42 | 0 | } |
43 | 0 | return (NULL); |
44 | 0 | } |
45 | | |
46 | | SECStatus |
47 | | cert_FindExtensionByOID(CERTCertExtension **extensions, SECItem *oid, |
48 | | SECItem *value) |
49 | 0 | { |
50 | 0 | CERTCertExtension *ext; |
51 | 0 | SECStatus rv = SECSuccess; |
52 | |
|
53 | 0 | ext = GetExtension(extensions, oid); |
54 | 0 | if (ext == NULL) { |
55 | 0 | PORT_SetError(SEC_ERROR_EXTENSION_NOT_FOUND); |
56 | 0 | return (SECFailure); |
57 | 0 | } |
58 | 0 | if (value) |
59 | 0 | rv = SECITEM_CopyItem(NULL, value, &ext->value); |
60 | 0 | return (rv); |
61 | 0 | } |
62 | | |
63 | | SECStatus |
64 | | CERT_GetExtenCriticality(CERTCertExtension **extensions, int tag, |
65 | | PRBool *isCritical) |
66 | 0 | { |
67 | 0 | CERTCertExtension *ext; |
68 | 0 | SECOidData *oid; |
69 | |
|
70 | 0 | if (!isCritical) |
71 | 0 | return (SECSuccess); |
72 | | |
73 | | /* find the extension in the extensions list */ |
74 | 0 | oid = SECOID_FindOIDByTag((SECOidTag)tag); |
75 | 0 | if (!oid) { |
76 | 0 | return (SECFailure); |
77 | 0 | } |
78 | 0 | ext = GetExtension(extensions, &oid->oid); |
79 | 0 | if (ext == NULL) { |
80 | 0 | PORT_SetError(SEC_ERROR_EXTENSION_NOT_FOUND); |
81 | 0 | return (SECFailure); |
82 | 0 | } |
83 | | |
84 | | /* If the criticality is omitted, then it is false by default. |
85 | | ex->critical.data is NULL */ |
86 | 0 | if (ext->critical.data == NULL) |
87 | 0 | *isCritical = PR_FALSE; |
88 | 0 | else |
89 | 0 | *isCritical = (ext->critical.data[0] == 0xff) ? PR_TRUE : PR_FALSE; |
90 | 0 | return (SECSuccess); |
91 | 0 | } |
92 | | |
93 | | SECStatus |
94 | | cert_FindExtension(CERTCertExtension **extensions, int tag, SECItem *value) |
95 | 0 | { |
96 | 0 | SECOidData *oid; |
97 | |
|
98 | 0 | oid = SECOID_FindOIDByTag((SECOidTag)tag); |
99 | 0 | if (!oid) { |
100 | 0 | return (SECFailure); |
101 | 0 | } |
102 | | |
103 | 0 | return (cert_FindExtensionByOID(extensions, &oid->oid, value)); |
104 | 0 | } |
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 | 0 | { |
130 | 0 | PLArenaPool *arena; |
131 | 0 | extRec *handle; |
132 | |
|
133 | 0 | arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); |
134 | 0 | if (!arena) { |
135 | 0 | return (0); |
136 | 0 | } |
137 | | |
138 | 0 | handle = (extRec *)PORT_ArenaAlloc(arena, sizeof(extRec)); |
139 | 0 | if (!handle) { |
140 | 0 | PORT_FreeArena(arena, PR_FALSE); |
141 | 0 | return (0); |
142 | 0 | } |
143 | | |
144 | 0 | handle->object = owner; |
145 | 0 | handle->ownerArena = ownerArena; |
146 | 0 | handle->setExts = setExts; |
147 | |
|
148 | 0 | handle->arena = arena; |
149 | 0 | handle->head = 0; |
150 | 0 | handle->count = 0; |
151 | |
|
152 | 0 | return (handle); |
153 | 0 | } |
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 | 0 | { |
164 | 0 | CERTCertExtension *ext; |
165 | 0 | SECStatus rv; |
166 | 0 | extNode *node; |
167 | 0 | extRec *handle; |
168 | |
|
169 | 0 | handle = (extRec *)exthandle; |
170 | | |
171 | | /* allocate space for extension and list node */ |
172 | 0 | ext = (CERTCertExtension *)PORT_ArenaZAlloc(handle->ownerArena, |
173 | 0 | sizeof(CERTCertExtension)); |
174 | 0 | if (!ext) { |
175 | 0 | return (SECFailure); |
176 | 0 | } |
177 | | |
178 | 0 | node = (extNode *)PORT_ArenaAlloc(handle->arena, sizeof(extNode)); |
179 | 0 | if (!node) { |
180 | 0 | return (SECFailure); |
181 | 0 | } |
182 | | |
183 | | /* add to list */ |
184 | 0 | node->next = handle->head; |
185 | 0 | handle->head = node; |
186 | | |
187 | | /* point to ext struct */ |
188 | 0 | node->ext = ext; |
189 | | |
190 | | /* set critical field */ |
191 | 0 | if (critical) { |
192 | 0 | ext->critical.data = (unsigned char *)&hextrue; |
193 | 0 | ext->critical.len = 1; |
194 | 0 | } |
195 | | |
196 | | /* set object ID of the extension and its value */ |
197 | 0 | if (copyData) { |
198 | 0 | rv = SECITEM_CopyItem(handle->ownerArena, &ext->id, oid); |
199 | 0 | if (rv) { |
200 | 0 | return (SECFailure); |
201 | 0 | } |
202 | | |
203 | 0 | rv = SECITEM_CopyItem(handle->ownerArena, &ext->value, value); |
204 | 0 | if (rv) { |
205 | 0 | return (SECFailure); |
206 | 0 | } |
207 | 0 | } else { |
208 | 0 | ext->id = *oid; |
209 | 0 | ext->value = *value; |
210 | 0 | } |
211 | | |
212 | 0 | handle->count++; |
213 | |
|
214 | 0 | return (SECSuccess); |
215 | 0 | } |
216 | | |
217 | | SECStatus |
218 | | CERT_AddExtension(void *exthandle, int idtag, SECItem *value, PRBool critical, |
219 | | PRBool copyData) |
220 | 0 | { |
221 | 0 | SECOidData *oid; |
222 | |
|
223 | 0 | oid = SECOID_FindOIDByTag((SECOidTag)idtag); |
224 | 0 | if (!oid) { |
225 | 0 | return (SECFailure); |
226 | 0 | } |
227 | | |
228 | 0 | return (CERT_AddExtensionByOID(exthandle, &oid->oid, value, critical, |
229 | 0 | copyData)); |
230 | 0 | } |
231 | | |
232 | | SECStatus |
233 | | CERT_EncodeAndAddExtension(void *exthandle, int idtag, void *value, |
234 | | PRBool critical, const SEC_ASN1Template *atemplate) |
235 | 0 | { |
236 | 0 | extRec *handle; |
237 | 0 | SECItem *encitem; |
238 | |
|
239 | 0 | handle = (extRec *)exthandle; |
240 | |
|
241 | 0 | encitem = SEC_ASN1EncodeItem(handle->ownerArena, NULL, value, atemplate); |
242 | 0 | if (encitem == NULL) { |
243 | 0 | return (SECFailure); |
244 | 0 | } |
245 | | |
246 | 0 | return CERT_AddExtension(exthandle, idtag, encitem, critical, PR_FALSE); |
247 | 0 | } |
248 | | |
249 | | void |
250 | | PrepareBitStringForEncoding(SECItem *bitsmap, SECItem *value) |
251 | 0 | { |
252 | 0 | unsigned char onebyte; |
253 | 0 | unsigned int i, len = 0; |
254 | | |
255 | | /* to prevent warning on some platform at compile time */ |
256 | 0 | onebyte = '\0'; |
257 | | /* Get the position of the right-most turn-on bit */ |
258 | 0 | for (i = 0; i < (value->len) * 8; ++i) { |
259 | 0 | if (i % 8 == 0) |
260 | 0 | onebyte = value->data[i / 8]; |
261 | 0 | if (onebyte & 0x80) |
262 | 0 | len = i; |
263 | 0 | onebyte <<= 1; |
264 | 0 | } |
265 | 0 | bitsmap->data = value->data; |
266 | | /* Add one here since we work with base 1 */ |
267 | 0 | bitsmap->len = len + 1; |
268 | 0 | } |
269 | | |
270 | | SECStatus |
271 | | CERT_EncodeAndAddBitStrExtension(void *exthandle, int idtag, SECItem *value, |
272 | | PRBool critical) |
273 | 0 | { |
274 | 0 | SECItem bitsmap; |
275 | |
|
276 | 0 | PrepareBitStringForEncoding(&bitsmap, value); |
277 | 0 | return (CERT_EncodeAndAddExtension(exthandle, idtag, &bitsmap, critical, |
278 | 0 | SEC_ASN1_GET(SEC_BitStringTemplate))); |
279 | 0 | } |
280 | | |
281 | | SECStatus |
282 | | CERT_FinishExtensions(void *exthandle) |
283 | 0 | { |
284 | 0 | extRec *handle; |
285 | 0 | extNode *node; |
286 | 0 | CERTCertExtension **exts; |
287 | 0 | SECStatus rv = SECFailure; |
288 | |
|
289 | 0 | handle = (extRec *)exthandle; |
290 | | |
291 | | /* allocate space for extensions array */ |
292 | 0 | exts = PORT_ArenaNewArray(handle->ownerArena, CERTCertExtension *, |
293 | 0 | handle->count + 1); |
294 | 0 | if (exts == NULL) { |
295 | 0 | goto loser; |
296 | 0 | } |
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 | 0 | handle->setExts(handle->object, exts); |
325 | | |
326 | | /* update the version number */ |
327 | | |
328 | | /* copy each extension pointer */ |
329 | 0 | node = handle->head; |
330 | 0 | while (node) { |
331 | 0 | *exts = node->ext; |
332 | |
|
333 | 0 | node = node->next; |
334 | 0 | exts++; |
335 | 0 | } |
336 | | |
337 | | /* terminate the array of extensions */ |
338 | 0 | *exts = 0; |
339 | |
|
340 | 0 | rv = SECSuccess; |
341 | |
|
342 | 0 | loser: |
343 | | /* free working arena */ |
344 | 0 | PORT_FreeArena(handle->arena, PR_FALSE); |
345 | 0 | return rv; |
346 | 0 | } |
347 | | |
348 | | SECStatus |
349 | | CERT_MergeExtensions(void *exthandle, CERTCertExtension **extensions) |
350 | 0 | { |
351 | 0 | CERTCertExtension *ext; |
352 | 0 | SECStatus rv = SECSuccess; |
353 | 0 | SECOidTag tag; |
354 | 0 | extNode *node; |
355 | 0 | extRec *handle = exthandle; |
356 | |
|
357 | 0 | if (!exthandle || !extensions) { |
358 | 0 | PORT_SetError(SEC_ERROR_INVALID_ARGS); |
359 | 0 | return SECFailure; |
360 | 0 | } |
361 | 0 | while ((ext = *extensions++) != NULL) { |
362 | 0 | tag = SECOID_FindOIDTag(&ext->id); |
363 | 0 | for (node = handle->head; node != NULL; node = node->next) { |
364 | 0 | if (tag == 0) { |
365 | 0 | if (SECITEM_ItemsAreEqual(&ext->id, &node->ext->id)) |
366 | 0 | break; |
367 | 0 | } else { |
368 | 0 | if (SECOID_FindOIDTag(&node->ext->id) == tag) { |
369 | 0 | break; |
370 | 0 | } |
371 | 0 | } |
372 | 0 | } |
373 | 0 | if (node == NULL) { |
374 | 0 | PRBool critical = (ext->critical.len != 0 && |
375 | 0 | ext->critical.data[ext->critical.len - 1] != 0); |
376 | 0 | if (critical && tag == SEC_OID_UNKNOWN) { |
377 | 0 | PORT_SetError(SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION); |
378 | 0 | rv = SECFailure; |
379 | 0 | break; |
380 | 0 | } |
381 | | /* add to list */ |
382 | 0 | rv = CERT_AddExtensionByOID(exthandle, &ext->id, &ext->value, |
383 | 0 | critical, PR_TRUE); |
384 | 0 | if (rv != SECSuccess) |
385 | 0 | break; |
386 | 0 | } |
387 | 0 | } |
388 | 0 | return rv; |
389 | 0 | } |
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 | 0 | { |
398 | 0 | SECItem wrapperItem, tmpItem = { siBuffer, 0 }; |
399 | 0 | SECStatus rv; |
400 | 0 | PORTCheapArenaPool tmpArena; |
401 | |
|
402 | 0 | wrapperItem.data = NULL; |
403 | 0 | tmpItem.data = NULL; |
404 | |
|
405 | 0 | PORT_InitCheapArena(&tmpArena, DER_DEFAULT_CHUNKSIZE); |
406 | |
|
407 | 0 | rv = cert_FindExtension(extensions, tag, &wrapperItem); |
408 | 0 | if (rv != SECSuccess) { |
409 | 0 | goto loser; |
410 | 0 | } |
411 | | |
412 | 0 | rv = SEC_QuickDERDecodeItem(&tmpArena.arena, &tmpItem, |
413 | 0 | SEC_ASN1_GET(SEC_BitStringTemplate), |
414 | 0 | &wrapperItem); |
415 | |
|
416 | 0 | if (rv != SECSuccess) { |
417 | 0 | goto loser; |
418 | 0 | } |
419 | | |
420 | 0 | retItem->data = (unsigned char *)PORT_ZAlloc((tmpItem.len + 7) >> 3); |
421 | 0 | if (retItem->data == NULL) { |
422 | 0 | goto loser; |
423 | 0 | } |
424 | | |
425 | 0 | if (tmpItem.len > 0) { |
426 | 0 | PORT_Memcpy(retItem->data, tmpItem.data, (tmpItem.len + 7) >> 3); |
427 | 0 | } |
428 | |
|
429 | 0 | retItem->len = tmpItem.len; |
430 | |
|
431 | 0 | rv = SECSuccess; |
432 | 0 | goto done; |
433 | | |
434 | 0 | loser: |
435 | 0 | rv = SECFailure; |
436 | |
|
437 | 0 | done: |
438 | 0 | PORT_DestroyCheapArena(&tmpArena); |
439 | |
|
440 | 0 | if (wrapperItem.data) { |
441 | 0 | PORT_Free(wrapperItem.data); |
442 | 0 | } |
443 | |
|
444 | 0 | return (rv); |
445 | 0 | } |
446 | | |
447 | | PRBool |
448 | | cert_HasCriticalExtension(CERTCertExtension **extensions) |
449 | 0 | { |
450 | 0 | CERTCertExtension **exts; |
451 | 0 | CERTCertExtension *ext = NULL; |
452 | 0 | PRBool hasCriticalExten = PR_FALSE; |
453 | |
|
454 | 0 | exts = extensions; |
455 | |
|
456 | 0 | if (exts) { |
457 | 0 | while (*exts) { |
458 | 0 | ext = *exts; |
459 | | /* If the criticality is omitted, it's non-critical */ |
460 | 0 | if (ext->critical.data && ext->critical.data[0] == 0xff) { |
461 | 0 | hasCriticalExten = PR_TRUE; |
462 | 0 | break; |
463 | 0 | } |
464 | 0 | exts++; |
465 | 0 | } |
466 | 0 | } |
467 | 0 | return (hasCriticalExten); |
468 | 0 | } |
469 | | |
470 | | PRBool |
471 | | cert_HasUnknownCriticalExten(CERTCertExtension **extensions) |
472 | 0 | { |
473 | 0 | CERTCertExtension **exts; |
474 | 0 | CERTCertExtension *ext = NULL; |
475 | 0 | PRBool hasUnknownCriticalExten = PR_FALSE; |
476 | |
|
477 | 0 | exts = extensions; |
478 | |
|
479 | 0 | if (exts) { |
480 | 0 | while (*exts) { |
481 | 0 | ext = *exts; |
482 | | /* If the criticality is omitted, it's non-critical. |
483 | | If an extension is critical, make sure that we know |
484 | | how to process the extension. |
485 | | */ |
486 | 0 | if (ext->critical.data && ext->critical.data[0] == 0xff) { |
487 | 0 | if (SECOID_KnownCertExtenOID(&ext->id) == PR_FALSE) { |
488 | 0 | hasUnknownCriticalExten = PR_TRUE; |
489 | 0 | break; |
490 | 0 | } |
491 | 0 | } |
492 | 0 | exts++; |
493 | 0 | } |
494 | 0 | } |
495 | 0 | return (hasUnknownCriticalExten); |
496 | 0 | } |