| OLD | NEW |
| (Empty) |
| 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 *isCri
tical) | |
| 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 PRArenaPool *ownerArena; | |
| 115 PRArenaPool *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, PRArenaPool *ownerArena, | |
| 128 void (*setExts)(void *object, CERTCertExtension **exts)) | |
| 129 { | |
| 130 PRArenaPool *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, copyDat
a)); | |
| 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 PRArenaPool *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 } | |
| OLD | NEW |