certxutl.c (12608B)
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 /* 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 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 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 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 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 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 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 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_ZAlloc((tmpItem.len + 7) >> 3); 421 if (retItem->data == NULL) { 422 goto loser; 423 } 424 425 if (tmpItem.len > 0) { 426 PORT_Memcpy(retItem->data, tmpItem.data, (tmpItem.len + 7) >> 3); 427 } 428 429 retItem->len = tmpItem.len; 430 431 rv = SECSuccess; 432 goto done; 433 434 loser: 435 rv = SECFailure; 436 437 done: 438 PORT_DestroyCheapArena(&tmpArena); 439 440 if (wrapperItem.data) { 441 PORT_Free(wrapperItem.data); 442 } 443 444 return (rv); 445 } 446 447 PRBool 448 cert_HasCriticalExtension(CERTCertExtension **extensions) 449 { 450 CERTCertExtension **exts; 451 CERTCertExtension *ext = NULL; 452 PRBool hasCriticalExten = PR_FALSE; 453 454 exts = extensions; 455 456 if (exts) { 457 while (*exts) { 458 ext = *exts; 459 /* If the criticality is omitted, it's non-critical */ 460 if (ext->critical.data && ext->critical.data[0] == 0xff) { 461 hasCriticalExten = PR_TRUE; 462 break; 463 } 464 exts++; 465 } 466 } 467 return (hasCriticalExten); 468 } 469 470 PRBool 471 cert_HasUnknownCriticalExten(CERTCertExtension **extensions) 472 { 473 CERTCertExtension **exts; 474 CERTCertExtension *ext = NULL; 475 PRBool hasUnknownCriticalExten = PR_FALSE; 476 477 exts = extensions; 478 479 if (exts) { 480 while (*exts) { 481 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 if (ext->critical.data && ext->critical.data[0] == 0xff) { 487 if (SECOID_KnownCertExtenOID(&ext->id) == PR_FALSE) { 488 hasUnknownCriticalExten = PR_TRUE; 489 break; 490 } 491 } 492 exts++; 493 } 494 } 495 return (hasUnknownCriticalExten); 496 }