| 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, | |
| 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 | |
| 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 | |
| 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 * | |
| 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 | |
| 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 SECStatus | |
| 215 CERT_AddExtension(void *exthandle, int idtag, SECItem *value, PRBool critical, | |
| 216 PRBool copyData) | |
| 217 { | |
| 218 SECOidData *oid; | |
| 219 | |
| 220 oid = SECOID_FindOIDByTag((SECOidTag)idtag); | |
| 221 if (!oid) { | |
| 222 return (SECFailure); | |
| 223 } | |
| 224 | |
| 225 return (CERT_AddExtensionByOID(exthandle, &oid->oid, value, critical, | |
| 226 copyData)); | |
| 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 bitsmap->data = value->data; | |
| 263 /* Add one here since we work with base 1 */ | |
| 264 bitsmap->len = len + 1; | |
| 265 } | |
| 266 | |
| 267 SECStatus | |
| 268 CERT_EncodeAndAddBitStrExtension(void *exthandle, int idtag, SECItem *value, | |
| 269 PRBool critical) | |
| 270 { | |
| 271 SECItem bitsmap; | |
| 272 | |
| 273 PrepareBitStringForEncoding(&bitsmap, value); | |
| 274 return (CERT_EncodeAndAddExtension(exthandle, idtag, &bitsmap, critical, | |
| 275 SEC_ASN1_GET(SEC_BitStringTemplate))); | |
| 276 } | |
| 277 | |
| 278 SECStatus | |
| 279 CERT_FinishExtensions(void *exthandle) | |
| 280 { | |
| 281 extRec *handle; | |
| 282 extNode *node; | |
| 283 CERTCertExtension **exts; | |
| 284 SECStatus rv = SECFailure; | |
| 285 | |
| 286 handle = (extRec *)exthandle; | |
| 287 | |
| 288 /* allocate space for extensions array */ | |
| 289 exts = PORT_ArenaNewArray(handle->ownerArena, CERTCertExtension *, | |
| 290 handle->count + 1); | |
| 291 if (exts == NULL) { | |
| 292 goto loser; | |
| 293 } | |
| 294 | |
| 295 /* put extensions in owner object and update its version number */ | |
| 296 | |
| 297 #ifdef OLD | |
| 298 switch (handle->type) { | |
| 299 case CertificateExtensions: | |
| 300 handle->owner.cert->extensions = exts; | |
| 301 DER_SetUInteger(ownerArena, &(handle->owner.cert->version), | |
| 302 SEC_CERTIFICATE_VERSION_3); | |
| 303 break; | |
| 304 case CrlExtensions: | |
| 305 handle->owner.crl->extensions = exts; | |
| 306 DER_SetUInteger(ownerArena, &(handle->owner.crl->version), | |
| 307 SEC_CRL_VERSION_2); | |
| 308 break; | |
| 309 case OCSPRequestExtensions: | |
| 310 handle->owner.request->tbsRequest->requestExtensions = exts; | |
| 311 break; | |
| 312 case OCSPSingleRequestExtensions: | |
| 313 handle->owner.singleRequest->singleRequestExtensions = exts; | |
| 314 break; | |
| 315 case OCSPResponseSingleExtensions: | |
| 316 handle->owner.singleResponse->singleExtensions = exts; | |
| 317 break; | |
| 318 } | |
| 319 #endif | |
| 320 | |
| 321 handle->setExts(handle->object, exts); | |
| 322 | |
| 323 /* update the version number */ | |
| 324 | |
| 325 /* copy each extension pointer */ | |
| 326 node = handle->head; | |
| 327 while (node) { | |
| 328 *exts = node->ext; | |
| 329 | |
| 330 node = node->next; | |
| 331 exts++; | |
| 332 } | |
| 333 | |
| 334 /* terminate the array of extensions */ | |
| 335 *exts = 0; | |
| 336 | |
| 337 rv = SECSuccess; | |
| 338 | |
| 339 loser: | |
| 340 /* free working arena */ | |
| 341 PORT_FreeArena(handle->arena, PR_FALSE); | |
| 342 return rv; | |
| 343 } | |
| 344 | |
| 345 SECStatus | |
| 346 CERT_MergeExtensions(void *exthandle, CERTCertExtension **extensions) | |
| 347 { | |
| 348 CERTCertExtension *ext; | |
| 349 SECStatus rv = SECSuccess; | |
| 350 SECOidTag tag; | |
| 351 extNode *node; | |
| 352 extRec *handle = exthandle; | |
| 353 | |
| 354 if (!exthandle || !extensions) { | |
| 355 PORT_SetError(SEC_ERROR_INVALID_ARGS); | |
| 356 return SECFailure; | |
| 357 } | |
| 358 while ((ext = *extensions++) != NULL) { | |
| 359 tag = SECOID_FindOIDTag(&ext->id); | |
| 360 for (node = handle->head; node != NULL; node = node->next) { | |
| 361 if (tag == 0) { | |
| 362 if (SECITEM_ItemsAreEqual(&ext->id, &node->ext->id)) | |
| 363 break; | |
| 364 } else { | |
| 365 if (SECOID_FindOIDTag(&node->ext->id) == tag) { | |
| 366 break; | |
| 367 } | |
| 368 } | |
| 369 } | |
| 370 if (node == NULL) { | |
| 371 PRBool critical = (ext->critical.len != 0 && | |
| 372 ext->critical.data[ext->critical.len - 1] != 0); | |
| 373 if (critical && tag == SEC_OID_UNKNOWN) { | |
| 374 PORT_SetError(SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION); | |
| 375 rv = SECFailure; | |
| 376 break; | |
| 377 } | |
| 378 /* add to list */ | |
| 379 rv = CERT_AddExtensionByOID(exthandle, &ext->id, &ext->value, | |
| 380 critical, PR_TRUE); | |
| 381 if (rv != SECSuccess) | |
| 382 break; | |
| 383 } | |
| 384 } | |
| 385 return rv; | |
| 386 } | |
| 387 | |
| 388 /* | |
| 389 * get the value of the Netscape Certificate Type Extension | |
| 390 */ | |
| 391 SECStatus | |
| 392 CERT_FindBitStringExtension(CERTCertExtension **extensions, int tag, | |
| 393 SECItem *retItem) | |
| 394 { | |
| 395 SECItem wrapperItem, tmpItem = { siBuffer, 0 }; | |
| 396 SECStatus rv; | |
| 397 PLArenaPool *arena = NULL; | |
| 398 | |
| 399 wrapperItem.data = NULL; | |
| 400 tmpItem.data = NULL; | |
| 401 | |
| 402 arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); | |
| 403 | |
| 404 if (!arena) { | |
| 405 return (SECFailure); | |
| 406 } | |
| 407 | |
| 408 rv = cert_FindExtension(extensions, tag, &wrapperItem); | |
| 409 if (rv != SECSuccess) { | |
| 410 goto loser; | |
| 411 } | |
| 412 | |
| 413 rv = SEC_QuickDERDecodeItem( | |
| 414 arena, &tmpItem, SEC_ASN1_GET(SEC_BitStringTemplate), &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 if (arena) { | |
| 436 PORT_FreeArena(arena, PR_FALSE); | |
| 437 } | |
| 438 | |
| 439 if (wrapperItem.data) { | |
| 440 PORT_Free(wrapperItem.data); | |
| 441 } | |
| 442 | |
| 443 return (rv); | |
| 444 } | |
| 445 | |
| 446 PRBool | |
| 447 cert_HasCriticalExtension(CERTCertExtension **extensions) | |
| 448 { | |
| 449 CERTCertExtension **exts; | |
| 450 CERTCertExtension *ext = NULL; | |
| 451 PRBool hasCriticalExten = PR_FALSE; | |
| 452 | |
| 453 exts = extensions; | |
| 454 | |
| 455 if (exts) { | |
| 456 while (*exts) { | |
| 457 ext = *exts; | |
| 458 /* If the criticality is omitted, it's non-critical */ | |
| 459 if (ext->critical.data && ext->critical.data[0] == 0xff) { | |
| 460 hasCriticalExten = PR_TRUE; | |
| 461 break; | |
| 462 } | |
| 463 exts++; | |
| 464 } | |
| 465 } | |
| 466 return (hasCriticalExten); | |
| 467 } | |
| 468 | |
| 469 PRBool | |
| 470 cert_HasUnknownCriticalExten(CERTCertExtension **extensions) | |
| 471 { | |
| 472 CERTCertExtension **exts; | |
| 473 CERTCertExtension *ext = NULL; | |
| 474 PRBool hasUnknownCriticalExten = PR_FALSE; | |
| 475 | |
| 476 exts = extensions; | |
| 477 | |
| 478 if (exts) { | |
| 479 while (*exts) { | |
| 480 ext = *exts; | |
| 481 /* If the criticality is omitted, it's non-critical. | |
| 482 If an extension is critical, make sure that we know | |
| 483 how to process the extension. | |
| 484 */ | |
| 485 if (ext->critical.data && ext->critical.data[0] == 0xff) { | |
| 486 if (SECOID_KnownCertExtenOID(&ext->id) == PR_FALSE) { | |
| 487 hasUnknownCriticalExten = PR_TRUE; | |
| 488 break; | |
| 489 } | |
| 490 } | |
| 491 exts++; | |
| 492 } | |
| 493 } | |
| 494 return (hasUnknownCriticalExten); | |
| 495 } | |
| OLD | NEW |