| 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 #include "plarena.h" | |
| 6 #include "seccomon.h" | |
| 7 #include "secitem.h" | |
| 8 #include "secoidt.h" | |
| 9 #include "secasn1.h" | |
| 10 #include "secder.h" | |
| 11 #include "certt.h" | |
| 12 #include "cert.h" | |
| 13 #include "certi.h" | |
| 14 #include "xconst.h" | |
| 15 #include "secerr.h" | |
| 16 #include "secoid.h" | |
| 17 #include "prprf.h" | |
| 18 #include "genname.h" | |
| 19 | |
| 20 SEC_ASN1_MKSUB(SEC_AnyTemplate) | |
| 21 SEC_ASN1_MKSUB(SEC_IntegerTemplate) | |
| 22 SEC_ASN1_MKSUB(SEC_IA5StringTemplate) | |
| 23 SEC_ASN1_MKSUB(SEC_ObjectIDTemplate) | |
| 24 SEC_ASN1_MKSUB(SEC_OctetStringTemplate) | |
| 25 | |
| 26 static const SEC_ASN1Template CERTNameConstraintTemplate[] = { | |
| 27 { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CERTNameConstraint) }, | |
| 28 { SEC_ASN1_ANY, offsetof(CERTNameConstraint, DERName) }, | |
| 29 { SEC_ASN1_OPTIONAL | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0, | |
| 30 offsetof(CERTNameConstraint, min), SEC_ASN1_SUB(SEC_IntegerTemplate) }, | |
| 31 { SEC_ASN1_OPTIONAL | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 1, | |
| 32 offsetof(CERTNameConstraint, max), SEC_ASN1_SUB(SEC_IntegerTemplate) }, | |
| 33 { 0 } | |
| 34 }; | |
| 35 | |
| 36 const SEC_ASN1Template CERT_NameConstraintSubtreeSubTemplate[] = { | |
| 37 { SEC_ASN1_SEQUENCE_OF | SEC_ASN1_XTRN, 0, SEC_ASN1_SUB(SEC_AnyTemplate) } | |
| 38 }; | |
| 39 | |
| 40 static const SEC_ASN1Template CERTNameConstraintsTemplate[] = { | |
| 41 { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CERTNameConstraints) }, | |
| 42 { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0, | |
| 43 offsetof(CERTNameConstraints, DERPermited), | |
| 44 CERT_NameConstraintSubtreeSubTemplate }, | |
| 45 { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 1, | |
| 46 offsetof(CERTNameConstraints, DERExcluded), | |
| 47 CERT_NameConstraintSubtreeSubTemplate }, | |
| 48 { 0 } | |
| 49 }; | |
| 50 | |
| 51 static const SEC_ASN1Template CERTOthNameTemplate[] = { | |
| 52 { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(OtherName) }, | |
| 53 { SEC_ASN1_OBJECT_ID, offsetof(OtherName, oid) }, | |
| 54 { SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_CONSTRUCTED | SEC_ASN1_EXPLICIT | | |
| 55 SEC_ASN1_XTRN | 0, | |
| 56 offsetof(OtherName, name), SEC_ASN1_SUB(SEC_AnyTemplate) }, | |
| 57 { 0 } | |
| 58 }; | |
| 59 | |
| 60 static const SEC_ASN1Template CERTOtherNameTemplate[] = { | |
| 61 { SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_CONSTRUCTED | 0, | |
| 62 offsetof(CERTGeneralName, name.OthName), CERTOthNameTemplate, | |
| 63 sizeof(CERTGeneralName) } | |
| 64 }; | |
| 65 | |
| 66 static const SEC_ASN1Template CERT_RFC822NameTemplate[] = { | |
| 67 { SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 1, | |
| 68 offsetof(CERTGeneralName, name.other), | |
| 69 SEC_ASN1_SUB(SEC_IA5StringTemplate), sizeof(CERTGeneralName) } | |
| 70 }; | |
| 71 | |
| 72 static const SEC_ASN1Template CERT_DNSNameTemplate[] = { | |
| 73 { SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 2, | |
| 74 offsetof(CERTGeneralName, name.other), | |
| 75 SEC_ASN1_SUB(SEC_IA5StringTemplate), sizeof(CERTGeneralName) } | |
| 76 }; | |
| 77 | |
| 78 static const SEC_ASN1Template CERT_X400AddressTemplate[] = { | |
| 79 { SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_CONSTRUCTED | SEC_ASN1_XTRN | 3, | |
| 80 offsetof(CERTGeneralName, name.other), SEC_ASN1_SUB(SEC_AnyTemplate), | |
| 81 sizeof(CERTGeneralName) } | |
| 82 }; | |
| 83 | |
| 84 static const SEC_ASN1Template CERT_DirectoryNameTemplate[] = { | |
| 85 { SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_CONSTRUCTED | SEC_ASN1_EXPLICIT | | |
| 86 SEC_ASN1_XTRN | 4, | |
| 87 offsetof(CERTGeneralName, derDirectoryName), | |
| 88 SEC_ASN1_SUB(SEC_AnyTemplate), sizeof(CERTGeneralName) } | |
| 89 }; | |
| 90 | |
| 91 static const SEC_ASN1Template CERT_EDIPartyNameTemplate[] = { | |
| 92 { SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_CONSTRUCTED | SEC_ASN1_XTRN | 5, | |
| 93 offsetof(CERTGeneralName, name.other), SEC_ASN1_SUB(SEC_AnyTemplate), | |
| 94 sizeof(CERTGeneralName) } | |
| 95 }; | |
| 96 | |
| 97 static const SEC_ASN1Template CERT_URITemplate[] = { | |
| 98 { SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 6, | |
| 99 offsetof(CERTGeneralName, name.other), | |
| 100 SEC_ASN1_SUB(SEC_IA5StringTemplate), sizeof(CERTGeneralName) } | |
| 101 }; | |
| 102 | |
| 103 static const SEC_ASN1Template CERT_IPAddressTemplate[] = { | |
| 104 { SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 7, | |
| 105 offsetof(CERTGeneralName, name.other), | |
| 106 SEC_ASN1_SUB(SEC_OctetStringTemplate), sizeof(CERTGeneralName) } | |
| 107 }; | |
| 108 | |
| 109 static const SEC_ASN1Template CERT_RegisteredIDTemplate[] = { | |
| 110 { SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 8, | |
| 111 offsetof(CERTGeneralName, name.other), SEC_ASN1_SUB(SEC_ObjectIDTemplate), | |
| 112 sizeof(CERTGeneralName) } | |
| 113 }; | |
| 114 | |
| 115 const SEC_ASN1Template CERT_GeneralNamesTemplate[] = { | |
| 116 { SEC_ASN1_SEQUENCE_OF | SEC_ASN1_XTRN, 0, SEC_ASN1_SUB(SEC_AnyTemplate) } | |
| 117 }; | |
| 118 | |
| 119 static struct { | |
| 120 CERTGeneralNameType type; | |
| 121 char *name; | |
| 122 } typesArray[] = { { certOtherName, "other" }, | |
| 123 { certRFC822Name, "email" }, | |
| 124 { certRFC822Name, "rfc822" }, | |
| 125 { certDNSName, "dns" }, | |
| 126 { certX400Address, "x400" }, | |
| 127 { certX400Address, "x400addr" }, | |
| 128 { certDirectoryName, "directory" }, | |
| 129 { certDirectoryName, "dn" }, | |
| 130 { certEDIPartyName, "edi" }, | |
| 131 { certEDIPartyName, "ediparty" }, | |
| 132 { certURI, "uri" }, | |
| 133 { certIPAddress, "ip" }, | |
| 134 { certIPAddress, "ipaddr" }, | |
| 135 { certRegisterID, "registerid" } }; | |
| 136 | |
| 137 CERTGeneralNameType | |
| 138 CERT_GetGeneralNameTypeFromString(const char *string) | |
| 139 { | |
| 140 int types_count = sizeof(typesArray) / sizeof(typesArray[0]); | |
| 141 int i; | |
| 142 | |
| 143 for (i = 0; i < types_count; i++) { | |
| 144 if (PORT_Strcasecmp(string, typesArray[i].name) == 0) { | |
| 145 return typesArray[i].type; | |
| 146 } | |
| 147 } | |
| 148 return 0; | |
| 149 } | |
| 150 | |
| 151 CERTGeneralName * | |
| 152 CERT_NewGeneralName(PLArenaPool *arena, CERTGeneralNameType type) | |
| 153 { | |
| 154 CERTGeneralName *name = arena ? PORT_ArenaZNew(arena, CERTGeneralName) | |
| 155 : PORT_ZNew(CERTGeneralName); | |
| 156 if (name) { | |
| 157 name->type = type; | |
| 158 name->l.prev = name->l.next = &name->l; | |
| 159 } | |
| 160 return name; | |
| 161 } | |
| 162 | |
| 163 /* Copy content of one General Name to another. | |
| 164 ** Caller has allocated destination general name. | |
| 165 ** This function does not change the destinate's GeneralName's list linkage. | |
| 166 */ | |
| 167 SECStatus | |
| 168 cert_CopyOneGeneralName(PLArenaPool *arena, CERTGeneralName *dest, | |
| 169 CERTGeneralName *src) | |
| 170 { | |
| 171 SECStatus rv; | |
| 172 void *mark = NULL; | |
| 173 | |
| 174 PORT_Assert(dest != NULL); | |
| 175 dest->type = src->type; | |
| 176 | |
| 177 mark = PORT_ArenaMark(arena); | |
| 178 | |
| 179 switch (src->type) { | |
| 180 case certDirectoryName: | |
| 181 rv = SECITEM_CopyItem(arena, &dest->derDirectoryName, | |
| 182 &src->derDirectoryName); | |
| 183 if (rv == SECSuccess) | |
| 184 rv = CERT_CopyName(arena, &dest->name.directoryName, | |
| 185 &src->name.directoryName); | |
| 186 break; | |
| 187 | |
| 188 case certOtherName: | |
| 189 rv = SECITEM_CopyItem(arena, &dest->name.OthName.name, | |
| 190 &src->name.OthName.name); | |
| 191 if (rv == SECSuccess) | |
| 192 rv = SECITEM_CopyItem(arena, &dest->name.OthName.oid, | |
| 193 &src->name.OthName.oid); | |
| 194 break; | |
| 195 | |
| 196 default: | |
| 197 rv = SECITEM_CopyItem(arena, &dest->name.other, &src->name.other); | |
| 198 break; | |
| 199 } | |
| 200 if (rv != SECSuccess) { | |
| 201 PORT_ArenaRelease(arena, mark); | |
| 202 } else { | |
| 203 PORT_ArenaUnmark(arena, mark); | |
| 204 } | |
| 205 return rv; | |
| 206 } | |
| 207 | |
| 208 void | |
| 209 CERT_DestroyGeneralNameList(CERTGeneralNameList *list) | |
| 210 { | |
| 211 PZLock *lock; | |
| 212 | |
| 213 if (list != NULL) { | |
| 214 lock = list->lock; | |
| 215 PZ_Lock(lock); | |
| 216 if (--list->refCount <= 0 && list->arena != NULL) { | |
| 217 PORT_FreeArena(list->arena, PR_FALSE); | |
| 218 PZ_Unlock(lock); | |
| 219 PZ_DestroyLock(lock); | |
| 220 } else { | |
| 221 PZ_Unlock(lock); | |
| 222 } | |
| 223 } | |
| 224 return; | |
| 225 } | |
| 226 | |
| 227 CERTGeneralNameList * | |
| 228 CERT_CreateGeneralNameList(CERTGeneralName *name) | |
| 229 { | |
| 230 PLArenaPool *arena; | |
| 231 CERTGeneralNameList *list = NULL; | |
| 232 | |
| 233 arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); | |
| 234 if (arena == NULL) { | |
| 235 goto done; | |
| 236 } | |
| 237 list = PORT_ArenaZNew(arena, CERTGeneralNameList); | |
| 238 if (!list) | |
| 239 goto loser; | |
| 240 if (name != NULL) { | |
| 241 SECStatus rv; | |
| 242 list->name = CERT_NewGeneralName(arena, (CERTGeneralNameType)0); | |
| 243 if (!list->name) | |
| 244 goto loser; | |
| 245 rv = CERT_CopyGeneralName(arena, list->name, name); | |
| 246 if (rv != SECSuccess) | |
| 247 goto loser; | |
| 248 } | |
| 249 list->lock = PZ_NewLock(nssILockList); | |
| 250 if (!list->lock) | |
| 251 goto loser; | |
| 252 list->arena = arena; | |
| 253 list->refCount = 1; | |
| 254 done: | |
| 255 return list; | |
| 256 | |
| 257 loser: | |
| 258 PORT_FreeArena(arena, PR_FALSE); | |
| 259 return NULL; | |
| 260 } | |
| 261 | |
| 262 CERTGeneralName * | |
| 263 CERT_GetNextGeneralName(CERTGeneralName *current) | |
| 264 { | |
| 265 PRCList *next; | |
| 266 | |
| 267 next = current->l.next; | |
| 268 return (CERTGeneralName *)(((char *)next) - offsetof(CERTGeneralName, l)); | |
| 269 } | |
| 270 | |
| 271 CERTGeneralName * | |
| 272 CERT_GetPrevGeneralName(CERTGeneralName *current) | |
| 273 { | |
| 274 PRCList *prev; | |
| 275 prev = current->l.prev; | |
| 276 return (CERTGeneralName *)(((char *)prev) - offsetof(CERTGeneralName, l)); | |
| 277 } | |
| 278 | |
| 279 CERTNameConstraint * | |
| 280 CERT_GetNextNameConstraint(CERTNameConstraint *current) | |
| 281 { | |
| 282 PRCList *next; | |
| 283 | |
| 284 next = current->l.next; | |
| 285 return (CERTNameConstraint *)(((char *)next) - | |
| 286 offsetof(CERTNameConstraint, l)); | |
| 287 } | |
| 288 | |
| 289 CERTNameConstraint * | |
| 290 CERT_GetPrevNameConstraint(CERTNameConstraint *current) | |
| 291 { | |
| 292 PRCList *prev; | |
| 293 prev = current->l.prev; | |
| 294 return (CERTNameConstraint *)(((char *)prev) - | |
| 295 offsetof(CERTNameConstraint, l)); | |
| 296 } | |
| 297 | |
| 298 SECItem * | |
| 299 CERT_EncodeGeneralName(CERTGeneralName *genName, SECItem *dest, | |
| 300 PLArenaPool *arena) | |
| 301 { | |
| 302 | |
| 303 const SEC_ASN1Template *template; | |
| 304 | |
| 305 PORT_Assert(arena); | |
| 306 if (arena == NULL) { | |
| 307 PORT_SetError(SEC_ERROR_INVALID_ARGS); | |
| 308 return NULL; | |
| 309 } | |
| 310 /* TODO: mark arena */ | |
| 311 if (dest == NULL) { | |
| 312 dest = PORT_ArenaZNew(arena, SECItem); | |
| 313 if (!dest) | |
| 314 goto loser; | |
| 315 } | |
| 316 if (genName->type == certDirectoryName) { | |
| 317 if (genName->derDirectoryName.data == NULL) { | |
| 318 /* The field hasn't been encoded yet. */ | |
| 319 SECItem *pre_dest = SEC_ASN1EncodeItem( | |
| 320 arena, &(genName->derDirectoryName), | |
| 321 &(genName->name.directoryName), CERT_NameTemplate); | |
| 322 if (!pre_dest) | |
| 323 goto loser; | |
| 324 } | |
| 325 if (genName->derDirectoryName.data == NULL) { | |
| 326 goto loser; | |
| 327 } | |
| 328 } | |
| 329 switch (genName->type) { | |
| 330 case certURI: | |
| 331 template = CERT_URITemplate; | |
| 332 break; | |
| 333 case certRFC822Name: | |
| 334 template = CERT_RFC822NameTemplate; | |
| 335 break; | |
| 336 case certDNSName: | |
| 337 template = CERT_DNSNameTemplate; | |
| 338 break; | |
| 339 case certIPAddress: | |
| 340 template = CERT_IPAddressTemplate; | |
| 341 break; | |
| 342 case certOtherName: | |
| 343 template = CERTOtherNameTemplate; | |
| 344 break; | |
| 345 case certRegisterID: | |
| 346 template = CERT_RegisteredIDTemplate; | |
| 347 break; | |
| 348 /* for this type, we expect the value is already encoded */ | |
| 349 case certEDIPartyName: | |
| 350 template = CERT_EDIPartyNameTemplate; | |
| 351 break; | |
| 352 /* for this type, we expect the value is already encoded */ | |
| 353 case certX400Address: | |
| 354 template = CERT_X400AddressTemplate; | |
| 355 break; | |
| 356 case certDirectoryName: | |
| 357 template = CERT_DirectoryNameTemplate; | |
| 358 break; | |
| 359 default: | |
| 360 PORT_Assert(0); | |
| 361 goto loser; | |
| 362 } | |
| 363 dest = SEC_ASN1EncodeItem(arena, dest, genName, template); | |
| 364 if (!dest) { | |
| 365 goto loser; | |
| 366 } | |
| 367 /* TODO: unmark arena */ | |
| 368 return dest; | |
| 369 loser: | |
| 370 /* TODO: release arena back to mark */ | |
| 371 return NULL; | |
| 372 } | |
| 373 | |
| 374 SECItem ** | |
| 375 cert_EncodeGeneralNames(PLArenaPool *arena, CERTGeneralName *names) | |
| 376 { | |
| 377 CERTGeneralName *current_name; | |
| 378 SECItem **items = NULL; | |
| 379 int count = 0; | |
| 380 int i; | |
| 381 PRCList *head; | |
| 382 | |
| 383 PORT_Assert(arena); | |
| 384 /* TODO: mark arena */ | |
| 385 current_name = names; | |
| 386 if (names != NULL) { | |
| 387 count = 1; | |
| 388 } | |
| 389 head = &(names->l); | |
| 390 while (current_name->l.next != head) { | |
| 391 current_name = CERT_GetNextGeneralName(current_name); | |
| 392 ++count; | |
| 393 } | |
| 394 current_name = CERT_GetNextGeneralName(current_name); | |
| 395 items = PORT_ArenaNewArray(arena, SECItem *, count + 1); | |
| 396 if (items == NULL) { | |
| 397 goto loser; | |
| 398 } | |
| 399 for (i = 0; i < count; i++) { | |
| 400 items[i] = CERT_EncodeGeneralName(current_name, (SECItem *)NULL, arena); | |
| 401 if (items[i] == NULL) { | |
| 402 goto loser; | |
| 403 } | |
| 404 current_name = CERT_GetNextGeneralName(current_name); | |
| 405 } | |
| 406 items[i] = NULL; | |
| 407 /* TODO: unmark arena */ | |
| 408 return items; | |
| 409 loser: | |
| 410 /* TODO: release arena to mark */ | |
| 411 return NULL; | |
| 412 } | |
| 413 | |
| 414 CERTGeneralName * | |
| 415 CERT_DecodeGeneralName(PLArenaPool *reqArena, SECItem *encodedName, | |
| 416 CERTGeneralName *genName) | |
| 417 { | |
| 418 const SEC_ASN1Template *template; | |
| 419 CERTGeneralNameType genNameType; | |
| 420 SECStatus rv = SECSuccess; | |
| 421 SECItem *newEncodedName; | |
| 422 | |
| 423 if (!reqArena) { | |
| 424 PORT_SetError(SEC_ERROR_INVALID_ARGS); | |
| 425 return NULL; | |
| 426 } | |
| 427 /* make a copy for decoding so the data decoded with QuickDER doesn't | |
| 428 point to temporary memory */ | |
| 429 newEncodedName = SECITEM_ArenaDupItem(reqArena, encodedName); | |
| 430 if (!newEncodedName) { | |
| 431 return NULL; | |
| 432 } | |
| 433 /* TODO: mark arena */ | |
| 434 genNameType = (CERTGeneralNameType)((*(newEncodedName->data) & 0x0f) + 1); | |
| 435 if (genName == NULL) { | |
| 436 genName = CERT_NewGeneralName(reqArena, genNameType); | |
| 437 if (!genName) | |
| 438 goto loser; | |
| 439 } else { | |
| 440 genName->type = genNameType; | |
| 441 genName->l.prev = genName->l.next = &genName->l; | |
| 442 } | |
| 443 | |
| 444 switch (genNameType) { | |
| 445 case certURI: | |
| 446 template = CERT_URITemplate; | |
| 447 break; | |
| 448 case certRFC822Name: | |
| 449 template = CERT_RFC822NameTemplate; | |
| 450 break; | |
| 451 case certDNSName: | |
| 452 template = CERT_DNSNameTemplate; | |
| 453 break; | |
| 454 case certIPAddress: | |
| 455 template = CERT_IPAddressTemplate; | |
| 456 break; | |
| 457 case certOtherName: | |
| 458 template = CERTOtherNameTemplate; | |
| 459 break; | |
| 460 case certRegisterID: | |
| 461 template = CERT_RegisteredIDTemplate; | |
| 462 break; | |
| 463 case certEDIPartyName: | |
| 464 template = CERT_EDIPartyNameTemplate; | |
| 465 break; | |
| 466 case certX400Address: | |
| 467 template = CERT_X400AddressTemplate; | |
| 468 break; | |
| 469 case certDirectoryName: | |
| 470 template = CERT_DirectoryNameTemplate; | |
| 471 break; | |
| 472 default: | |
| 473 goto loser; | |
| 474 } | |
| 475 rv = SEC_QuickDERDecodeItem(reqArena, genName, template, newEncodedName); | |
| 476 if (rv != SECSuccess) | |
| 477 goto loser; | |
| 478 if (genNameType == certDirectoryName) { | |
| 479 rv = SEC_QuickDERDecodeItem(reqArena, &(genName->name.directoryName), | |
| 480 CERT_NameTemplate, | |
| 481 &(genName->derDirectoryName)); | |
| 482 if (rv != SECSuccess) | |
| 483 goto loser; | |
| 484 } | |
| 485 | |
| 486 /* TODO: unmark arena */ | |
| 487 return genName; | |
| 488 loser: | |
| 489 /* TODO: release arena to mark */ | |
| 490 return NULL; | |
| 491 } | |
| 492 | |
| 493 CERTGeneralName * | |
| 494 cert_DecodeGeneralNames(PLArenaPool *arena, SECItem **encodedGenName) | |
| 495 { | |
| 496 PRCList *head = NULL; | |
| 497 PRCList *tail = NULL; | |
| 498 CERTGeneralName *currentName = NULL; | |
| 499 | |
| 500 PORT_Assert(arena); | |
| 501 if (!encodedGenName || !arena) { | |
| 502 PORT_SetError(SEC_ERROR_INVALID_ARGS); | |
| 503 return NULL; | |
| 504 } | |
| 505 /* TODO: mark arena */ | |
| 506 while (*encodedGenName != NULL) { | |
| 507 currentName = CERT_DecodeGeneralName(arena, *encodedGenName, NULL); | |
| 508 if (currentName == NULL) | |
| 509 break; | |
| 510 if (head == NULL) { | |
| 511 head = &(currentName->l); | |
| 512 tail = head; | |
| 513 } | |
| 514 currentName->l.next = head; | |
| 515 currentName->l.prev = tail; | |
| 516 tail = head->prev = tail->next = &(currentName->l); | |
| 517 encodedGenName++; | |
| 518 } | |
| 519 if (currentName) { | |
| 520 /* TODO: unmark arena */ | |
| 521 return CERT_GetNextGeneralName(currentName); | |
| 522 } | |
| 523 /* TODO: release arena to mark */ | |
| 524 return NULL; | |
| 525 } | |
| 526 | |
| 527 void | |
| 528 CERT_DestroyGeneralName(CERTGeneralName *name) | |
| 529 { | |
| 530 cert_DestroyGeneralNames(name); | |
| 531 } | |
| 532 | |
| 533 SECStatus | |
| 534 cert_DestroyGeneralNames(CERTGeneralName *name) | |
| 535 { | |
| 536 CERTGeneralName *first; | |
| 537 CERTGeneralName *next = NULL; | |
| 538 | |
| 539 first = name; | |
| 540 do { | |
| 541 next = CERT_GetNextGeneralName(name); | |
| 542 PORT_Free(name); | |
| 543 name = next; | |
| 544 } while (name != first); | |
| 545 return SECSuccess; | |
| 546 } | |
| 547 | |
| 548 static SECItem * | |
| 549 cert_EncodeNameConstraint(CERTNameConstraint *constraint, SECItem *dest, | |
| 550 PLArenaPool *arena) | |
| 551 { | |
| 552 PORT_Assert(arena); | |
| 553 if (dest == NULL) { | |
| 554 dest = PORT_ArenaZNew(arena, SECItem); | |
| 555 if (dest == NULL) { | |
| 556 return NULL; | |
| 557 } | |
| 558 } | |
| 559 CERT_EncodeGeneralName(&(constraint->name), &(constraint->DERName), arena); | |
| 560 | |
| 561 dest = | |
| 562 SEC_ASN1EncodeItem(arena, dest, constraint, CERTNameConstraintTemplate); | |
| 563 return dest; | |
| 564 } | |
| 565 | |
| 566 SECStatus | |
| 567 cert_EncodeNameConstraintSubTree(CERTNameConstraint *constraints, | |
| 568 PLArenaPool *arena, SECItem ***dest, | |
| 569 PRBool permited) | |
| 570 { | |
| 571 CERTNameConstraint *current_constraint = constraints; | |
| 572 SECItem **items = NULL; | |
| 573 int count = 0; | |
| 574 int i; | |
| 575 PRCList *head; | |
| 576 | |
| 577 PORT_Assert(arena); | |
| 578 /* TODO: mark arena */ | |
| 579 if (constraints != NULL) { | |
| 580 count = 1; | |
| 581 } | |
| 582 head = &constraints->l; | |
| 583 while (current_constraint->l.next != head) { | |
| 584 current_constraint = CERT_GetNextNameConstraint(current_constraint); | |
| 585 ++count; | |
| 586 } | |
| 587 current_constraint = CERT_GetNextNameConstraint(current_constraint); | |
| 588 items = PORT_ArenaZNewArray(arena, SECItem *, count + 1); | |
| 589 if (items == NULL) { | |
| 590 goto loser; | |
| 591 } | |
| 592 for (i = 0; i < count; i++) { | |
| 593 items[i] = cert_EncodeNameConstraint(current_constraint, | |
| 594 (SECItem *)NULL, arena); | |
| 595 if (items[i] == NULL) { | |
| 596 goto loser; | |
| 597 } | |
| 598 current_constraint = CERT_GetNextNameConstraint(current_constraint); | |
| 599 } | |
| 600 *dest = items; | |
| 601 if (*dest == NULL) { | |
| 602 goto loser; | |
| 603 } | |
| 604 /* TODO: unmark arena */ | |
| 605 return SECSuccess; | |
| 606 loser: | |
| 607 /* TODO: release arena to mark */ | |
| 608 return SECFailure; | |
| 609 } | |
| 610 | |
| 611 SECStatus | |
| 612 cert_EncodeNameConstraints(CERTNameConstraints *constraints, PLArenaPool *arena, | |
| 613 SECItem *dest) | |
| 614 { | |
| 615 SECStatus rv = SECSuccess; | |
| 616 | |
| 617 PORT_Assert(arena); | |
| 618 /* TODO: mark arena */ | |
| 619 if (constraints->permited != NULL) { | |
| 620 rv = cert_EncodeNameConstraintSubTree( | |
| 621 constraints->permited, arena, &constraints->DERPermited, PR_TRUE); | |
| 622 if (rv == SECFailure) { | |
| 623 goto loser; | |
| 624 } | |
| 625 } | |
| 626 if (constraints->excluded != NULL) { | |
| 627 rv = cert_EncodeNameConstraintSubTree( | |
| 628 constraints->excluded, arena, &constraints->DERExcluded, PR_FALSE); | |
| 629 if (rv == SECFailure) { | |
| 630 goto loser; | |
| 631 } | |
| 632 } | |
| 633 dest = SEC_ASN1EncodeItem(arena, dest, constraints, | |
| 634 CERTNameConstraintsTemplate); | |
| 635 if (dest == NULL) { | |
| 636 goto loser; | |
| 637 } | |
| 638 /* TODO: unmark arena */ | |
| 639 return SECSuccess; | |
| 640 loser: | |
| 641 /* TODO: release arena to mark */ | |
| 642 return SECFailure; | |
| 643 } | |
| 644 | |
| 645 CERTNameConstraint * | |
| 646 cert_DecodeNameConstraint(PLArenaPool *reqArena, SECItem *encodedConstraint) | |
| 647 { | |
| 648 CERTNameConstraint *constraint; | |
| 649 SECStatus rv = SECSuccess; | |
| 650 CERTGeneralName *temp; | |
| 651 SECItem *newEncodedConstraint; | |
| 652 | |
| 653 if (!reqArena) { | |
| 654 PORT_SetError(SEC_ERROR_INVALID_ARGS); | |
| 655 return NULL; | |
| 656 } | |
| 657 newEncodedConstraint = SECITEM_ArenaDupItem(reqArena, encodedConstraint); | |
| 658 if (!newEncodedConstraint) { | |
| 659 return NULL; | |
| 660 } | |
| 661 /* TODO: mark arena */ | |
| 662 constraint = PORT_ArenaZNew(reqArena, CERTNameConstraint); | |
| 663 if (!constraint) | |
| 664 goto loser; | |
| 665 rv = SEC_QuickDERDecodeItem( | |
| 666 reqArena, constraint, CERTNameConstraintTemplate, newEncodedConstraint); | |
| 667 if (rv != SECSuccess) { | |
| 668 goto loser; | |
| 669 } | |
| 670 temp = CERT_DecodeGeneralName(reqArena, &(constraint->DERName), | |
| 671 &(constraint->name)); | |
| 672 if (temp != &(constraint->name)) { | |
| 673 goto loser; | |
| 674 } | |
| 675 | |
| 676 /* ### sjlee: since the name constraint contains only one | |
| 677 * CERTGeneralName, the list within CERTGeneralName shouldn't | |
| 678 * point anywhere else. Otherwise, bad things will happen. | |
| 679 */ | |
| 680 constraint->name.l.prev = constraint->name.l.next = &(constraint->name.l); | |
| 681 /* TODO: unmark arena */ | |
| 682 return constraint; | |
| 683 loser: | |
| 684 /* TODO: release arena back to mark */ | |
| 685 return NULL; | |
| 686 } | |
| 687 | |
| 688 static CERTNameConstraint * | |
| 689 cert_DecodeNameConstraintSubTree(PLArenaPool *arena, SECItem **subTree, | |
| 690 PRBool permited) | |
| 691 { | |
| 692 CERTNameConstraint *current = NULL; | |
| 693 CERTNameConstraint *first = NULL; | |
| 694 CERTNameConstraint *last = NULL; | |
| 695 int i = 0; | |
| 696 | |
| 697 PORT_Assert(arena); | |
| 698 /* TODO: mark arena */ | |
| 699 while (subTree[i] != NULL) { | |
| 700 current = cert_DecodeNameConstraint(arena, subTree[i]); | |
| 701 if (current == NULL) { | |
| 702 goto loser; | |
| 703 } | |
| 704 if (first == NULL) { | |
| 705 first = current; | |
| 706 } else { | |
| 707 current->l.prev = &(last->l); | |
| 708 last->l.next = &(current->l); | |
| 709 } | |
| 710 last = current; | |
| 711 i++; | |
| 712 } | |
| 713 first->l.prev = &(last->l); | |
| 714 last->l.next = &(first->l); | |
| 715 /* TODO: unmark arena */ | |
| 716 return first; | |
| 717 loser: | |
| 718 /* TODO: release arena back to mark */ | |
| 719 return NULL; | |
| 720 } | |
| 721 | |
| 722 CERTNameConstraints * | |
| 723 cert_DecodeNameConstraints(PLArenaPool *reqArena, | |
| 724 const SECItem *encodedConstraints) | |
| 725 { | |
| 726 CERTNameConstraints *constraints; | |
| 727 SECStatus rv; | |
| 728 SECItem *newEncodedConstraints; | |
| 729 | |
| 730 if (!reqArena) { | |
| 731 PORT_SetError(SEC_ERROR_INVALID_ARGS); | |
| 732 return NULL; | |
| 733 } | |
| 734 PORT_Assert(encodedConstraints); | |
| 735 newEncodedConstraints = SECITEM_ArenaDupItem(reqArena, encodedConstraints); | |
| 736 | |
| 737 /* TODO: mark arena */ | |
| 738 constraints = PORT_ArenaZNew(reqArena, CERTNameConstraints); | |
| 739 if (constraints == NULL) { | |
| 740 goto loser; | |
| 741 } | |
| 742 rv = SEC_QuickDERDecodeItem(reqArena, constraints, | |
| 743 CERTNameConstraintsTemplate, | |
| 744 newEncodedConstraints); | |
| 745 if (rv != SECSuccess) { | |
| 746 goto loser; | |
| 747 } | |
| 748 if (constraints->DERPermited != NULL && | |
| 749 constraints->DERPermited[0] != NULL) { | |
| 750 constraints->permited = cert_DecodeNameConstraintSubTree( | |
| 751 reqArena, constraints->DERPermited, PR_TRUE); | |
| 752 if (constraints->permited == NULL) { | |
| 753 goto loser; | |
| 754 } | |
| 755 } | |
| 756 if (constraints->DERExcluded != NULL && | |
| 757 constraints->DERExcluded[0] != NULL) { | |
| 758 constraints->excluded = cert_DecodeNameConstraintSubTree( | |
| 759 reqArena, constraints->DERExcluded, PR_FALSE); | |
| 760 if (constraints->excluded == NULL) { | |
| 761 goto loser; | |
| 762 } | |
| 763 } | |
| 764 /* TODO: unmark arena */ | |
| 765 return constraints; | |
| 766 loser: | |
| 767 /* TODO: release arena back to mark */ | |
| 768 return NULL; | |
| 769 } | |
| 770 | |
| 771 /* Copy a chain of one or more general names to a destination chain. | |
| 772 ** Caller has allocated at least the first destination GeneralName struct. | |
| 773 ** Both source and destination chains are circular doubly-linked lists. | |
| 774 ** The first source struct is copied to the first destination struct. | |
| 775 ** If the source chain has more than one member, and the destination chain | |
| 776 ** has only one member, then this function allocates new structs for all but | |
| 777 ** the first copy from the arena and links them into the destination list. | |
| 778 ** If the destination struct is part of a list with more than one member, | |
| 779 ** then this function traverses both the source and destination lists, | |
| 780 ** copying each source struct to the corresponding dest struct. | |
| 781 ** In that case, the destination list MUST contain at least as many | |
| 782 ** structs as the source list or some dest entries will be overwritten. | |
| 783 */ | |
| 784 SECStatus | |
| 785 CERT_CopyGeneralName(PLArenaPool *arena, CERTGeneralName *dest, | |
| 786 CERTGeneralName *src) | |
| 787 { | |
| 788 SECStatus rv; | |
| 789 CERTGeneralName *destHead = dest; | |
| 790 CERTGeneralName *srcHead = src; | |
| 791 | |
| 792 PORT_Assert(dest != NULL); | |
| 793 if (!dest) { | |
| 794 PORT_SetError(SEC_ERROR_INVALID_ARGS); | |
| 795 return SECFailure; | |
| 796 } | |
| 797 /* TODO: mark arena */ | |
| 798 do { | |
| 799 rv = cert_CopyOneGeneralName(arena, dest, src); | |
| 800 if (rv != SECSuccess) | |
| 801 goto loser; | |
| 802 src = CERT_GetNextGeneralName(src); | |
| 803 /* if there is only one general name, we shouldn't do this */ | |
| 804 if (src != srcHead) { | |
| 805 if (dest->l.next == &destHead->l) { | |
| 806 CERTGeneralName *temp; | |
| 807 temp = CERT_NewGeneralName(arena, (CERTGeneralNameType)0); | |
| 808 if (!temp) | |
| 809 goto loser; | |
| 810 temp->l.next = &destHead->l; | |
| 811 temp->l.prev = &dest->l; | |
| 812 destHead->l.prev = &temp->l; | |
| 813 dest->l.next = &temp->l; | |
| 814 dest = temp; | |
| 815 } else { | |
| 816 dest = CERT_GetNextGeneralName(dest); | |
| 817 } | |
| 818 } | |
| 819 } while (src != srcHead && rv == SECSuccess); | |
| 820 /* TODO: unmark arena */ | |
| 821 return rv; | |
| 822 loser: | |
| 823 /* TODO: release back to mark */ | |
| 824 return SECFailure; | |
| 825 } | |
| 826 | |
| 827 CERTGeneralNameList * | |
| 828 CERT_DupGeneralNameList(CERTGeneralNameList *list) | |
| 829 { | |
| 830 if (list != NULL) { | |
| 831 PZ_Lock(list->lock); | |
| 832 list->refCount++; | |
| 833 PZ_Unlock(list->lock); | |
| 834 } | |
| 835 return list; | |
| 836 } | |
| 837 | |
| 838 /* Allocate space and copy CERTNameConstraint from src to dest */ | |
| 839 CERTNameConstraint * | |
| 840 CERT_CopyNameConstraint(PLArenaPool *arena, CERTNameConstraint *dest, | |
| 841 CERTNameConstraint *src) | |
| 842 { | |
| 843 SECStatus rv; | |
| 844 | |
| 845 /* TODO: mark arena */ | |
| 846 if (dest == NULL) { | |
| 847 dest = PORT_ArenaZNew(arena, CERTNameConstraint); | |
| 848 if (!dest) | |
| 849 goto loser; | |
| 850 /* mark that it is not linked */ | |
| 851 dest->name.l.prev = dest->name.l.next = &(dest->name.l); | |
| 852 } | |
| 853 rv = CERT_CopyGeneralName(arena, &dest->name, &src->name); | |
| 854 if (rv != SECSuccess) { | |
| 855 goto loser; | |
| 856 } | |
| 857 rv = SECITEM_CopyItem(arena, &dest->DERName, &src->DERName); | |
| 858 if (rv != SECSuccess) { | |
| 859 goto loser; | |
| 860 } | |
| 861 rv = SECITEM_CopyItem(arena, &dest->min, &src->min); | |
| 862 if (rv != SECSuccess) { | |
| 863 goto loser; | |
| 864 } | |
| 865 rv = SECITEM_CopyItem(arena, &dest->max, &src->max); | |
| 866 if (rv != SECSuccess) { | |
| 867 goto loser; | |
| 868 } | |
| 869 dest->l.prev = dest->l.next = &dest->l; | |
| 870 /* TODO: unmark arena */ | |
| 871 return dest; | |
| 872 loser: | |
| 873 /* TODO: release arena to mark */ | |
| 874 return NULL; | |
| 875 } | |
| 876 | |
| 877 CERTGeneralName * | |
| 878 cert_CombineNamesLists(CERTGeneralName *list1, CERTGeneralName *list2) | |
| 879 { | |
| 880 PRCList *begin1; | |
| 881 PRCList *begin2; | |
| 882 PRCList *end1; | |
| 883 PRCList *end2; | |
| 884 | |
| 885 if (list1 == NULL) { | |
| 886 return list2; | |
| 887 } else if (list2 == NULL) { | |
| 888 return list1; | |
| 889 } else { | |
| 890 begin1 = &list1->l; | |
| 891 begin2 = &list2->l; | |
| 892 end1 = list1->l.prev; | |
| 893 end2 = list2->l.prev; | |
| 894 end1->next = begin2; | |
| 895 end2->next = begin1; | |
| 896 begin1->prev = end2; | |
| 897 begin2->prev = end1; | |
| 898 return list1; | |
| 899 } | |
| 900 } | |
| 901 | |
| 902 CERTNameConstraint * | |
| 903 cert_CombineConstraintsLists(CERTNameConstraint *list1, | |
| 904 CERTNameConstraint *list2) | |
| 905 { | |
| 906 PRCList *begin1; | |
| 907 PRCList *begin2; | |
| 908 PRCList *end1; | |
| 909 PRCList *end2; | |
| 910 | |
| 911 if (list1 == NULL) { | |
| 912 return list2; | |
| 913 } else if (list2 == NULL) { | |
| 914 return list1; | |
| 915 } else { | |
| 916 begin1 = &list1->l; | |
| 917 begin2 = &list2->l; | |
| 918 end1 = list1->l.prev; | |
| 919 end2 = list2->l.prev; | |
| 920 end1->next = begin2; | |
| 921 end2->next = begin1; | |
| 922 begin1->prev = end2; | |
| 923 begin2->prev = end1; | |
| 924 return list1; | |
| 925 } | |
| 926 } | |
| 927 | |
| 928 /* Add a CERTNameConstraint to the CERTNameConstraint list */ | |
| 929 CERTNameConstraint * | |
| 930 CERT_AddNameConstraint(CERTNameConstraint *list, CERTNameConstraint *constraint) | |
| 931 { | |
| 932 PORT_Assert(constraint != NULL); | |
| 933 constraint->l.next = constraint->l.prev = &constraint->l; | |
| 934 list = cert_CombineConstraintsLists(list, constraint); | |
| 935 return list; | |
| 936 } | |
| 937 | |
| 938 SECStatus | |
| 939 CERT_GetNameConstraintByType(CERTNameConstraint *constraints, | |
| 940 CERTGeneralNameType type, | |
| 941 CERTNameConstraint **returnList, | |
| 942 PLArenaPool *arena) | |
| 943 { | |
| 944 CERTNameConstraint *current = NULL; | |
| 945 void *mark = NULL; | |
| 946 | |
| 947 *returnList = NULL; | |
| 948 if (!constraints) | |
| 949 return SECSuccess; | |
| 950 | |
| 951 mark = PORT_ArenaMark(arena); | |
| 952 | |
| 953 current = constraints; | |
| 954 do { | |
| 955 PORT_Assert(current->name.type); | |
| 956 if (current->name.type == type) { | |
| 957 CERTNameConstraint *temp; | |
| 958 temp = CERT_CopyNameConstraint(arena, NULL, current); | |
| 959 if (temp == NULL) | |
| 960 goto loser; | |
| 961 *returnList = CERT_AddNameConstraint(*returnList, temp); | |
| 962 } | |
| 963 current = CERT_GetNextNameConstraint(current); | |
| 964 } while (current != constraints); | |
| 965 PORT_ArenaUnmark(arena, mark); | |
| 966 return SECSuccess; | |
| 967 | |
| 968 loser: | |
| 969 PORT_ArenaRelease(arena, mark); | |
| 970 return SECFailure; | |
| 971 } | |
| 972 | |
| 973 void * | |
| 974 CERT_GetGeneralNameByType(CERTGeneralName *genNames, CERTGeneralNameType type, | |
| 975 PRBool derFormat) | |
| 976 { | |
| 977 CERTGeneralName *current; | |
| 978 | |
| 979 if (!genNames) | |
| 980 return NULL; | |
| 981 current = genNames; | |
| 982 | |
| 983 do { | |
| 984 if (current->type == type) { | |
| 985 switch (type) { | |
| 986 case certDNSName: | |
| 987 case certEDIPartyName: | |
| 988 case certIPAddress: | |
| 989 case certRegisterID: | |
| 990 case certRFC822Name: | |
| 991 case certX400Address: | |
| 992 case certURI: | |
| 993 return (void *)¤t->name.other; /* SECItem * */ | |
| 994 | |
| 995 case certOtherName: | |
| 996 return (void *)¤t->name.OthName; /* OthName * */ | |
| 997 | |
| 998 case certDirectoryName: | |
| 999 return derFormat | |
| 1000 ? (void *)¤t | |
| 1001 ->derDirectoryName /* SECItem * */ | |
| 1002 : (void *)¤t->name | |
| 1003 .directoryName; /* CERTName * */ | |
| 1004 } | |
| 1005 PORT_Assert(0); | |
| 1006 return NULL; | |
| 1007 } | |
| 1008 current = CERT_GetNextGeneralName(current); | |
| 1009 } while (current != genNames); | |
| 1010 return NULL; | |
| 1011 } | |
| 1012 | |
| 1013 int | |
| 1014 CERT_GetNamesLength(CERTGeneralName *names) | |
| 1015 { | |
| 1016 int length = 0; | |
| 1017 CERTGeneralName *first; | |
| 1018 | |
| 1019 first = names; | |
| 1020 if (names != NULL) { | |
| 1021 do { | |
| 1022 length++; | |
| 1023 names = CERT_GetNextGeneralName(names); | |
| 1024 } while (names != first); | |
| 1025 } | |
| 1026 return length; | |
| 1027 } | |
| 1028 | |
| 1029 /* Creates new GeneralNames for any email addresses found in the | |
| 1030 ** input DN, and links them onto the list for the DN. | |
| 1031 */ | |
| 1032 SECStatus | |
| 1033 cert_ExtractDNEmailAddrs(CERTGeneralName *name, PLArenaPool *arena) | |
| 1034 { | |
| 1035 CERTGeneralName *nameList = NULL; | |
| 1036 const CERTRDN **nRDNs = (const CERTRDN **)(name->name.directoryName.rdns); | |
| 1037 SECStatus rv = SECSuccess; | |
| 1038 | |
| 1039 PORT_Assert(name->type == certDirectoryName); | |
| 1040 if (name->type != certDirectoryName) { | |
| 1041 PORT_SetError(SEC_ERROR_INVALID_ARGS); | |
| 1042 return SECFailure; | |
| 1043 } | |
| 1044 /* TODO: mark arena */ | |
| 1045 while (nRDNs && *nRDNs) { /* loop over RDNs */ | |
| 1046 const CERTRDN *nRDN = *nRDNs++; | |
| 1047 CERTAVA **nAVAs = nRDN->avas; | |
| 1048 while (nAVAs && *nAVAs) { /* loop over AVAs */ | |
| 1049 int tag; | |
| 1050 CERTAVA *nAVA = *nAVAs++; | |
| 1051 tag = CERT_GetAVATag(nAVA); | |
| 1052 if (tag == SEC_OID_PKCS9_EMAIL_ADDRESS || | |
| 1053 tag == SEC_OID_RFC1274_MAIL) { /* email AVA */ | |
| 1054 CERTGeneralName *newName = NULL; | |
| 1055 SECItem *avaValue = CERT_DecodeAVAValue(&nAVA->value); | |
| 1056 if (!avaValue) | |
| 1057 goto loser; | |
| 1058 rv = SECFailure; | |
| 1059 newName = CERT_NewGeneralName(arena, certRFC822Name); | |
| 1060 if (newName) { | |
| 1061 rv = | |
| 1062 SECITEM_CopyItem(arena, &newName->name.other, avaValue); | |
| 1063 } | |
| 1064 SECITEM_FreeItem(avaValue, PR_TRUE); | |
| 1065 if (rv != SECSuccess) | |
| 1066 goto loser; | |
| 1067 nameList = cert_CombineNamesLists(nameList, newName); | |
| 1068 } /* handle one email AVA */ | |
| 1069 } /* loop over AVAs */ | |
| 1070 } /* loop over RDNs */ | |
| 1071 /* combine new names with old one. */ | |
| 1072 name = cert_CombineNamesLists(name, nameList); | |
| 1073 /* TODO: unmark arena */ | |
| 1074 return SECSuccess; | |
| 1075 | |
| 1076 loser: | |
| 1077 /* TODO: release arena back to mark */ | |
| 1078 return SECFailure; | |
| 1079 } | |
| 1080 | |
| 1081 /* Extract all names except Subject Common Name from a cert | |
| 1082 ** in preparation for a name constraints test. | |
| 1083 */ | |
| 1084 CERTGeneralName * | |
| 1085 CERT_GetCertificateNames(CERTCertificate *cert, PLArenaPool *arena) | |
| 1086 { | |
| 1087 return CERT_GetConstrainedCertificateNames(cert, arena, PR_FALSE); | |
| 1088 } | |
| 1089 | |
| 1090 /* This function is called by CERT_VerifyCertChain to extract all | |
| 1091 ** names from a cert in preparation for a name constraints test. | |
| 1092 */ | |
| 1093 CERTGeneralName * | |
| 1094 CERT_GetConstrainedCertificateNames(const CERTCertificate *cert, | |
| 1095 PLArenaPool *arena, | |
| 1096 PRBool includeSubjectCommonName) | |
| 1097 { | |
| 1098 CERTGeneralName *DN; | |
| 1099 CERTGeneralName *SAN; | |
| 1100 PRUint32 numDNSNames = 0; | |
| 1101 SECStatus rv; | |
| 1102 | |
| 1103 if (!arena) { | |
| 1104 PORT_SetError(SEC_ERROR_INVALID_ARGS); | |
| 1105 return NULL; | |
| 1106 } | |
| 1107 /* TODO: mark arena */ | |
| 1108 DN = CERT_NewGeneralName(arena, certDirectoryName); | |
| 1109 if (DN == NULL) { | |
| 1110 goto loser; | |
| 1111 } | |
| 1112 rv = CERT_CopyName(arena, &DN->name.directoryName, &cert->subject); | |
| 1113 if (rv != SECSuccess) { | |
| 1114 goto loser; | |
| 1115 } | |
| 1116 rv = SECITEM_CopyItem(arena, &DN->derDirectoryName, &cert->derSubject); | |
| 1117 if (rv != SECSuccess) { | |
| 1118 goto loser; | |
| 1119 } | |
| 1120 /* Extract email addresses from DN, construct CERTGeneralName structs | |
| 1121 ** for them, add them to the name list | |
| 1122 */ | |
| 1123 rv = cert_ExtractDNEmailAddrs(DN, arena); | |
| 1124 if (rv != SECSuccess) | |
| 1125 goto loser; | |
| 1126 | |
| 1127 /* Now extract any GeneralNames from the subject name names extension. */ | |
| 1128 SAN = cert_GetSubjectAltNameList(cert, arena); | |
| 1129 if (SAN) { | |
| 1130 numDNSNames = cert_CountDNSPatterns(SAN); | |
| 1131 DN = cert_CombineNamesLists(DN, SAN); | |
| 1132 } | |
| 1133 if (!numDNSNames && includeSubjectCommonName) { | |
| 1134 char *cn = CERT_GetCommonName(&cert->subject); | |
| 1135 if (cn) { | |
| 1136 CERTGeneralName *CN = CERT_NewGeneralName(arena, certDNSName); | |
| 1137 if (CN) { | |
| 1138 SECItem cnItem = { siBuffer, NULL, 0 }; | |
| 1139 cnItem.data = (unsigned char *)cn; | |
| 1140 cnItem.len = strlen(cn); | |
| 1141 rv = SECITEM_CopyItem(arena, &CN->name.other, &cnItem); | |
| 1142 if (rv == SECSuccess) { | |
| 1143 DN = cert_CombineNamesLists(DN, CN); | |
| 1144 } | |
| 1145 } | |
| 1146 PORT_Free(cn); | |
| 1147 } | |
| 1148 } | |
| 1149 if (rv == SECSuccess) { | |
| 1150 /* TODO: unmark arena */ | |
| 1151 return DN; | |
| 1152 } | |
| 1153 loser: | |
| 1154 /* TODO: release arena to mark */ | |
| 1155 return NULL; | |
| 1156 } | |
| 1157 | |
| 1158 /* Returns SECSuccess if name matches constraint per RFC 3280 rules for | |
| 1159 ** URI name constraints. SECFailure otherwise. | |
| 1160 ** If the constraint begins with a dot, it is a domain name, otherwise | |
| 1161 ** It is a host name. Examples: | |
| 1162 ** Constraint Name Result | |
| 1163 ** ------------ --------------- -------- | |
| 1164 ** foo.bar.com foo.bar.com matches | |
| 1165 ** foo.bar.com FoO.bAr.CoM matches | |
| 1166 ** foo.bar.com www.foo.bar.com no match | |
| 1167 ** foo.bar.com nofoo.bar.com no match | |
| 1168 ** .foo.bar.com www.foo.bar.com matches | |
| 1169 ** .foo.bar.com nofoo.bar.com no match | |
| 1170 ** .foo.bar.com foo.bar.com no match | |
| 1171 ** .foo.bar.com www..foo.bar.com no match | |
| 1172 */ | |
| 1173 static SECStatus | |
| 1174 compareURIN2C(const SECItem *name, const SECItem *constraint) | |
| 1175 { | |
| 1176 int offset; | |
| 1177 /* The spec is silent on intepreting zero-length constraints. | |
| 1178 ** We interpret them as matching no URI names. | |
| 1179 */ | |
| 1180 if (!constraint->len) | |
| 1181 return SECFailure; | |
| 1182 if (constraint->data[0] != '.') { | |
| 1183 /* constraint is a host name. */ | |
| 1184 if (name->len != constraint->len || | |
| 1185 PL_strncasecmp((char *)name->data, (char *)constraint->data, | |
| 1186 constraint->len)) | |
| 1187 return SECFailure; | |
| 1188 return SECSuccess; | |
| 1189 } | |
| 1190 /* constraint is a domain name. */ | |
| 1191 if (name->len < constraint->len) | |
| 1192 return SECFailure; | |
| 1193 offset = name->len - constraint->len; | |
| 1194 if (PL_strncasecmp((char *)(name->data + offset), (char *)constraint->data, | |
| 1195 constraint->len)) | |
| 1196 return SECFailure; | |
| 1197 if (!offset || | |
| 1198 (name->data[offset - 1] == '.') + (constraint->data[0] == '.') == 1) | |
| 1199 return SECSuccess; | |
| 1200 return SECFailure; | |
| 1201 } | |
| 1202 | |
| 1203 /* for DNSname constraints, RFC 3280 says, (section 4.2.1.11, page 38) | |
| 1204 ** | |
| 1205 ** DNS name restrictions are expressed as foo.bar.com. Any DNS name | |
| 1206 ** that can be constructed by simply adding to the left hand side of the | |
| 1207 ** name satisfies the name constraint. For example, www.foo.bar.com | |
| 1208 ** would satisfy the constraint but foo1.bar.com would not. | |
| 1209 ** | |
| 1210 ** But NIST's PKITS test suite requires that the constraint be treated | |
| 1211 ** as a domain name, and requires that any name added to the left hand | |
| 1212 ** side end in a dot ".". Sensible, but not strictly following the RFC. | |
| 1213 ** | |
| 1214 ** Constraint Name RFC 3280 NIST PKITS | |
| 1215 ** ------------ --------------- -------- ---------- | |
| 1216 ** foo.bar.com foo.bar.com matches matches | |
| 1217 ** foo.bar.com FoO.bAr.CoM matches matches | |
| 1218 ** foo.bar.com www.foo.bar.com matches matches | |
| 1219 ** foo.bar.com nofoo.bar.com MATCHES NO MATCH | |
| 1220 ** .foo.bar.com www.foo.bar.com matches matches? disallowed? | |
| 1221 ** .foo.bar.com foo.bar.com no match no match | |
| 1222 ** .foo.bar.com www..foo.bar.com matches probably not | |
| 1223 ** | |
| 1224 ** We will try to conform to NIST's PKITS tests, and the unstated | |
| 1225 ** rules they imply. | |
| 1226 */ | |
| 1227 static SECStatus | |
| 1228 compareDNSN2C(const SECItem *name, const SECItem *constraint) | |
| 1229 { | |
| 1230 int offset; | |
| 1231 /* The spec is silent on intepreting zero-length constraints. | |
| 1232 ** We interpret them as matching all DNSnames. | |
| 1233 */ | |
| 1234 if (!constraint->len) | |
| 1235 return SECSuccess; | |
| 1236 if (name->len < constraint->len) | |
| 1237 return SECFailure; | |
| 1238 offset = name->len - constraint->len; | |
| 1239 if (PL_strncasecmp((char *)(name->data + offset), (char *)constraint->data, | |
| 1240 constraint->len)) | |
| 1241 return SECFailure; | |
| 1242 if (!offset || | |
| 1243 (name->data[offset - 1] == '.') + (constraint->data[0] == '.') == 1) | |
| 1244 return SECSuccess; | |
| 1245 return SECFailure; | |
| 1246 } | |
| 1247 | |
| 1248 /* Returns SECSuccess if name matches constraint per RFC 3280 rules for | |
| 1249 ** internet email addresses. SECFailure otherwise. | |
| 1250 ** If constraint contains a '@' then the two strings much match exactly. | |
| 1251 ** Else if constraint starts with a '.'. then it must match the right-most | |
| 1252 ** substring of the name, | |
| 1253 ** else constraint string must match entire name after the name's '@'. | |
| 1254 ** Empty constraint string matches all names. All comparisons case insensitive. | |
| 1255 */ | |
| 1256 static SECStatus | |
| 1257 compareRFC822N2C(const SECItem *name, const SECItem *constraint) | |
| 1258 { | |
| 1259 int offset; | |
| 1260 if (!constraint->len) | |
| 1261 return SECSuccess; | |
| 1262 if (name->len < constraint->len) | |
| 1263 return SECFailure; | |
| 1264 if (constraint->len == 1 && constraint->data[0] == '.') | |
| 1265 return SECSuccess; | |
| 1266 for (offset = constraint->len - 1; offset >= 0; --offset) { | |
| 1267 if (constraint->data[offset] == '@') { | |
| 1268 return (name->len == constraint->len && | |
| 1269 !PL_strncasecmp((char *)name->data, | |
| 1270 (char *)constraint->data, constraint->len)) | |
| 1271 ? SECSuccess | |
| 1272 : SECFailure; | |
| 1273 } | |
| 1274 } | |
| 1275 offset = name->len - constraint->len; | |
| 1276 if (PL_strncasecmp((char *)(name->data + offset), (char *)constraint->data, | |
| 1277 constraint->len)) | |
| 1278 return SECFailure; | |
| 1279 if (constraint->data[0] == '.') | |
| 1280 return SECSuccess; | |
| 1281 if (offset > 0 && name->data[offset - 1] == '@') | |
| 1282 return SECSuccess; | |
| 1283 return SECFailure; | |
| 1284 } | |
| 1285 | |
| 1286 /* name contains either a 4 byte IPv4 address or a 16 byte IPv6 address. | |
| 1287 ** constraint contains an address of the same length, and a subnet mask | |
| 1288 ** of the same length. Compare name's address to the constraint's | |
| 1289 ** address, subject to the mask. | |
| 1290 ** Return SECSuccess if they match, SECFailure if they don't. | |
| 1291 */ | |
| 1292 static SECStatus | |
| 1293 compareIPaddrN2C(const SECItem *name, const SECItem *constraint) | |
| 1294 { | |
| 1295 int i; | |
| 1296 if (name->len == 4 && constraint->len == 8) { /* ipv4 addr */ | |
| 1297 for (i = 0; i < 4; i++) { | |
| 1298 if ((name->data[i] ^ constraint->data[i]) & constraint->data[i + 4]) | |
| 1299 goto loser; | |
| 1300 } | |
| 1301 return SECSuccess; | |
| 1302 } | |
| 1303 if (name->len == 16 && constraint->len == 32) { /* ipv6 addr */ | |
| 1304 for (i = 0; i < 16; i++) { | |
| 1305 if ((name->data[i] ^ constraint->data[i]) & | |
| 1306 constraint->data[i + 16]) | |
| 1307 goto loser; | |
| 1308 } | |
| 1309 return SECSuccess; | |
| 1310 } | |
| 1311 loser: | |
| 1312 return SECFailure; | |
| 1313 } | |
| 1314 | |
| 1315 /* start with a SECItem that points to a URI. Parse it lookingg for | |
| 1316 ** a hostname. Modify item->data and item->len to define the hostname, | |
| 1317 ** but do not modify and data at item->data. | |
| 1318 ** If anything goes wrong, the contents of *item are undefined. | |
| 1319 */ | |
| 1320 static SECStatus | |
| 1321 parseUriHostname(SECItem *item) | |
| 1322 { | |
| 1323 int i; | |
| 1324 PRBool found = PR_FALSE; | |
| 1325 for (i = 0; (unsigned)(i + 2) < item->len; ++i) { | |
| 1326 if (item->data[i] == ':' && item->data[i + 1] == '/' && | |
| 1327 item->data[i + 2] == '/') { | |
| 1328 i += 3; | |
| 1329 item->data += i; | |
| 1330 item->len -= i; | |
| 1331 found = PR_TRUE; | |
| 1332 break; | |
| 1333 } | |
| 1334 } | |
| 1335 if (!found) | |
| 1336 return SECFailure; | |
| 1337 /* now look for a '/', which is an upper bound in the end of the name */ | |
| 1338 for (i = 0; (unsigned)i < item->len; ++i) { | |
| 1339 if (item->data[i] == '/') { | |
| 1340 item->len = i; | |
| 1341 break; | |
| 1342 } | |
| 1343 } | |
| 1344 /* now look for a ':', which marks the end of the name */ | |
| 1345 for (i = item->len; --i >= 0;) { | |
| 1346 if (item->data[i] == ':') { | |
| 1347 item->len = i; | |
| 1348 break; | |
| 1349 } | |
| 1350 } | |
| 1351 /* now look for an '@', which marks the beginning of the hostname */ | |
| 1352 for (i = 0; (unsigned)i < item->len; ++i) { | |
| 1353 if (item->data[i] == '@') { | |
| 1354 ++i; | |
| 1355 item->data += i; | |
| 1356 item->len -= i; | |
| 1357 break; | |
| 1358 } | |
| 1359 } | |
| 1360 return item->len ? SECSuccess : SECFailure; | |
| 1361 } | |
| 1362 | |
| 1363 /* This function takes one name, and a list of constraints. | |
| 1364 ** It searches the constraints looking for a match. | |
| 1365 ** It returns SECSuccess if the name satisfies the constraints, i.e., | |
| 1366 ** if excluded, then the name does not match any constraint, | |
| 1367 ** if permitted, then the name matches at least one constraint. | |
| 1368 ** It returns SECFailure if the name fails to satisfy the constraints, | |
| 1369 ** or if some code fails (e.g. out of memory, or invalid constraint) | |
| 1370 */ | |
| 1371 SECStatus | |
| 1372 cert_CompareNameWithConstraints(const CERTGeneralName *name, | |
| 1373 const CERTNameConstraint *constraints, | |
| 1374 PRBool excluded) | |
| 1375 { | |
| 1376 SECStatus rv = SECSuccess; | |
| 1377 SECStatus matched = SECFailure; | |
| 1378 const CERTNameConstraint *current; | |
| 1379 | |
| 1380 PORT_Assert(constraints); /* caller should not call with NULL */ | |
| 1381 if (!constraints) { | |
| 1382 PORT_SetError(SEC_ERROR_INVALID_ARGS); | |
| 1383 return SECFailure; | |
| 1384 } | |
| 1385 | |
| 1386 current = constraints; | |
| 1387 do { | |
| 1388 rv = SECSuccess; | |
| 1389 matched = SECFailure; | |
| 1390 PORT_Assert(name->type == current->name.type); | |
| 1391 switch (name->type) { | |
| 1392 | |
| 1393 case certDNSName: | |
| 1394 matched = | |
| 1395 compareDNSN2C(&name->name.other, ¤t->name.name.other); | |
| 1396 break; | |
| 1397 | |
| 1398 case certRFC822Name: | |
| 1399 matched = compareRFC822N2C(&name->name.other, | |
| 1400 ¤t->name.name.other); | |
| 1401 break; | |
| 1402 | |
| 1403 case certURI: { | |
| 1404 /* make a modifiable copy of the URI SECItem. */ | |
| 1405 SECItem uri = name->name.other; | |
| 1406 /* find the hostname in the URI */ | |
| 1407 rv = parseUriHostname(&uri); | |
| 1408 if (rv == SECSuccess) { | |
| 1409 /* does our hostname meet the constraint? */ | |
| 1410 matched = compareURIN2C(&uri, ¤t->name.name.other); | |
| 1411 } | |
| 1412 } break; | |
| 1413 | |
| 1414 case certDirectoryName: | |
| 1415 /* Determine if the constraint directory name is a "prefix" | |
| 1416 ** for the directory name being tested. | |
| 1417 */ | |
| 1418 { | |
| 1419 /* status defaults to SECEqual, so that a constraint with | |
| 1420 ** no AVAs will be a wildcard, matching all directory names. | |
| 1421 */ | |
| 1422 SECComparison status = SECEqual; | |
| 1423 const CERTRDN **cRDNs = | |
| 1424 (const CERTRDN **)current->name.name.directoryName.rdns; | |
| 1425 const CERTRDN **nRDNs = | |
| 1426 (const CERTRDN **)name->name.directoryName.rdns; | |
| 1427 while (cRDNs && *cRDNs && nRDNs && *nRDNs) { | |
| 1428 /* loop over name RDNs and constraint RDNs in lock step | |
| 1429 */ | |
| 1430 const CERTRDN *cRDN = *cRDNs++; | |
| 1431 const CERTRDN *nRDN = *nRDNs++; | |
| 1432 CERTAVA **cAVAs = cRDN->avas; | |
| 1433 while (cAVAs && | |
| 1434 *cAVAs) { /* loop over constraint AVAs */ | |
| 1435 CERTAVA *cAVA = *cAVAs++; | |
| 1436 CERTAVA **nAVAs = nRDN->avas; | |
| 1437 while (nAVAs && *nAVAs) { /* loop over name AVAs */ | |
| 1438 CERTAVA *nAVA = *nAVAs++; | |
| 1439 status = CERT_CompareAVA(cAVA, nAVA); | |
| 1440 if (status == SECEqual) | |
| 1441 break; | |
| 1442 } /* loop over name AVAs */ | |
| 1443 if (status != SECEqual) | |
| 1444 break; | |
| 1445 } /* loop over constraint AVAs */ | |
| 1446 if (status != SECEqual) | |
| 1447 break; | |
| 1448 } /* loop over name RDNs and constraint RDNs */ | |
| 1449 matched = (status == SECEqual) ? SECSuccess : SECFailure; | |
| 1450 break; | |
| 1451 } | |
| 1452 | |
| 1453 case certIPAddress: /* type 8 */ | |
| 1454 matched = compareIPaddrN2C(&name->name.other, | |
| 1455 ¤t->name.name.other); | |
| 1456 break; | |
| 1457 | |
| 1458 /* NSS does not know how to compare these "Other" type names with | |
| 1459 ** their respective constraints. But it does know how to tell | |
| 1460 ** if the constraint applies to the type of name (by comparing | |
| 1461 ** the constraint OID to the name OID). NSS makes no use of "Other" | |
| 1462 ** type names at all, so NSS errs on the side of leniency for these | |
| 1463 ** types, provided that their OIDs match. So, when an "Other" | |
| 1464 ** name constraint appears in an excluded subtree, it never causes | |
| 1465 ** a name to fail. When an "Other" name constraint appears in a | |
| 1466 ** permitted subtree, AND the constraint's OID matches the name's | |
| 1467 ** OID, then name is treated as if it matches the constraint. | |
| 1468 */ | |
| 1469 case certOtherName: /* type 1 */ | |
| 1470 matched = | |
| 1471 (!excluded && name->type == current->name.type && | |
| 1472 SECITEM_ItemsAreEqual(&name->name.OthName.oid, | |
| 1473 ¤t->name.name.OthName.oid)) | |
| 1474 ? SECSuccess | |
| 1475 : SECFailure; | |
| 1476 break; | |
| 1477 | |
| 1478 /* NSS does not know how to compare these types of names with their | |
| 1479 ** respective constraints. But NSS makes no use of these types of | |
| 1480 ** names at all, so it errs on the side of leniency for these types. | |
| 1481 ** Constraints for these types of names never cause the name to | |
| 1482 ** fail the constraints test. NSS behaves as if the name matched | |
| 1483 ** for permitted constraints, and did not match for excluded ones. | |
| 1484 */ | |
| 1485 case certX400Address: /* type 4 */ | |
| 1486 case certEDIPartyName: /* type 6 */ | |
| 1487 case certRegisterID: /* type 9 */ | |
| 1488 matched = excluded ? SECFailure : SECSuccess; | |
| 1489 break; | |
| 1490 | |
| 1491 default: /* non-standard types are not supported */ | |
| 1492 rv = SECFailure; | |
| 1493 break; | |
| 1494 } | |
| 1495 if (matched == SECSuccess || rv != SECSuccess) | |
| 1496 break; | |
| 1497 current = CERT_GetNextNameConstraint((CERTNameConstraint *)current); | |
| 1498 } while (current != constraints); | |
| 1499 if (rv == SECSuccess) { | |
| 1500 if (matched == SECSuccess) | |
| 1501 rv = excluded ? SECFailure : SECSuccess; | |
| 1502 else | |
| 1503 rv = excluded ? SECSuccess : SECFailure; | |
| 1504 return rv; | |
| 1505 } | |
| 1506 | |
| 1507 return SECFailure; | |
| 1508 } | |
| 1509 | |
| 1510 /* Add and link a CERTGeneralName to a CERTNameConstraint list. Most | |
| 1511 ** likely the CERTNameConstraint passed in is either the permitted | |
| 1512 ** list or the excluded list of a CERTNameConstraints. | |
| 1513 */ | |
| 1514 SECStatus | |
| 1515 CERT_AddNameConstraintByGeneralName(PLArenaPool *arena, | |
| 1516 CERTNameConstraint **constraints, | |
| 1517 CERTGeneralName *name) | |
| 1518 { | |
| 1519 SECStatus rv; | |
| 1520 CERTNameConstraint *current = NULL; | |
| 1521 CERTNameConstraint *first = *constraints; | |
| 1522 void *mark = NULL; | |
| 1523 | |
| 1524 mark = PORT_ArenaMark(arena); | |
| 1525 | |
| 1526 current = PORT_ArenaZNew(arena, CERTNameConstraint); | |
| 1527 if (current == NULL) { | |
| 1528 rv = SECFailure; | |
| 1529 goto done; | |
| 1530 } | |
| 1531 | |
| 1532 rv = cert_CopyOneGeneralName(arena, ¤t->name, name); | |
| 1533 if (rv != SECSuccess) { | |
| 1534 goto done; | |
| 1535 } | |
| 1536 | |
| 1537 current->name.l.prev = current->name.l.next = &(current->name.l); | |
| 1538 | |
| 1539 if (first == NULL) { | |
| 1540 *constraints = current; | |
| 1541 PR_INIT_CLIST(¤t->l); | |
| 1542 } else { | |
| 1543 PR_INSERT_BEFORE(¤t->l, &first->l); | |
| 1544 } | |
| 1545 | |
| 1546 done: | |
| 1547 if (rv == SECFailure) { | |
| 1548 PORT_ArenaRelease(arena, mark); | |
| 1549 } else { | |
| 1550 PORT_ArenaUnmark(arena, mark); | |
| 1551 } | |
| 1552 return rv; | |
| 1553 } | |
| 1554 | |
| 1555 /* | |
| 1556 * Here we define a list of name constraints to be imposed on | |
| 1557 * certain certificates, most importantly root certificates. | |
| 1558 * | |
| 1559 * Each entry in the name constraints list is constructed with this | |
| 1560 * macro. An entry contains two SECItems, which have names in | |
| 1561 * specific forms to make the macro work: | |
| 1562 * | |
| 1563 * * ${CA}_SUBJECT_DN - The subject DN for which the constraints | |
| 1564 * should be applied | |
| 1565 * * ${CA}_NAME_CONSTRAINTS - The name constraints extension | |
| 1566 * | |
| 1567 * Entities subject to name constraints are identified by subject name | |
| 1568 * so that we can cover all certificates for that entity, including, e.g., | |
| 1569 * cross-certificates. We use subject rather than public key because | |
| 1570 * calling methods often have easy access to that field (vs., say, a key ID), | |
| 1571 * and in practice, subject names and public keys are usually in one-to-one | |
| 1572 * correspondence anyway. | |
| 1573 * | |
| 1574 */ | |
| 1575 | |
| 1576 #define STRING_TO_SECITEM(str) \ | |
| 1577 { \ | |
| 1578 siBuffer, (unsigned char *)str, sizeof(str) - 1 \ | |
| 1579 } | |
| 1580 | |
| 1581 #define NAME_CONSTRAINTS_ENTRY(CA) \ | |
| 1582 { \ | |
| 1583 STRING_TO_SECITEM(CA##_SUBJECT_DN), \ | |
| 1584 STRING_TO_SECITEM(CA##_NAME_CONSTRAINTS) \ | |
| 1585 } | |
| 1586 | |
| 1587 /* Agence Nationale de la Securite des Systemes d'Information (ANSSI) */ | |
| 1588 | |
| 1589 /* clang-format off */ | |
| 1590 | |
| 1591 #define ANSSI_SUBJECT_DN \ | |
| 1592 "\x30\x81\x85" \ | |
| 1593 "\x31\x0B\x30\x09\x06\x03\x55\x04\x06\x13\x02" "FR" /* C */ \ | |
| 1594 "\x31\x0F\x30\x0D\x06\x03\x55\x04\x08\x13\x06" "France" /* ST */ \ | |
| 1595 "\x31\x0E\x30\x0C\x06\x03\x55\x04\x07\x13\x05" "Paris" /* L */ \ | |
| 1596 "\x31\x10\x30\x0E\x06\x03\x55\x04\x0A\x13\x07" "PM/SGDN" /* O */ \ | |
| 1597 "\x31\x0E\x30\x0C\x06\x03\x55\x04\x0B\x13\x05" "DCSSI" /* OU */ \ | |
| 1598 "\x31\x0E\x30\x0C\x06\x03\x55\x04\x03\x13\x05" "IGC/A" /* CN */ \ | |
| 1599 "\x31\x23\x30\x21\x06\x09\x2A\x86\x48\x86\xF7\x0D\x01\x09\x01" \ | |
| 1600 "\x16\x14" "igca@sgdn.pm.gouv.fr" /* emailAddress */ \ | |
| 1601 | |
| 1602 #define ANSSI_NAME_CONSTRAINTS \ | |
| 1603 "\x30\x5D\xA0\x5B" \ | |
| 1604 "\x30\x05\x82\x03" ".fr" \ | |
| 1605 "\x30\x05\x82\x03" ".gp" \ | |
| 1606 "\x30\x05\x82\x03" ".gf" \ | |
| 1607 "\x30\x05\x82\x03" ".mq" \ | |
| 1608 "\x30\x05\x82\x03" ".re" \ | |
| 1609 "\x30\x05\x82\x03" ".yt" \ | |
| 1610 "\x30\x05\x82\x03" ".pm" \ | |
| 1611 "\x30\x05\x82\x03" ".bl" \ | |
| 1612 "\x30\x05\x82\x03" ".mf" \ | |
| 1613 "\x30\x05\x82\x03" ".wf" \ | |
| 1614 "\x30\x05\x82\x03" ".pf" \ | |
| 1615 "\x30\x05\x82\x03" ".nc" \ | |
| 1616 "\x30\x05\x82\x03" ".tf" | |
| 1617 | |
| 1618 /* clang-format on */ | |
| 1619 | |
| 1620 static const SECItem builtInNameConstraints[][2] = { NAME_CONSTRAINTS_ENTRY( | |
| 1621 ANSSI) }; | |
| 1622 | |
| 1623 SECStatus | |
| 1624 CERT_GetImposedNameConstraints(const SECItem *derSubject, SECItem *extensions) | |
| 1625 { | |
| 1626 size_t i; | |
| 1627 | |
| 1628 if (!extensions) { | |
| 1629 PORT_SetError(SEC_ERROR_INVALID_ARGS); | |
| 1630 return SECFailure; | |
| 1631 } | |
| 1632 | |
| 1633 for (i = 0; i < PR_ARRAY_SIZE(builtInNameConstraints); ++i) { | |
| 1634 if (SECITEM_ItemsAreEqual(derSubject, &builtInNameConstraints[i][0])) { | |
| 1635 return SECITEM_CopyItem(NULL, extensions, | |
| 1636 &builtInNameConstraints[i][1]); | |
| 1637 } | |
| 1638 } | |
| 1639 | |
| 1640 PORT_SetError(SEC_ERROR_EXTENSION_NOT_FOUND); | |
| 1641 return SECFailure; | |
| 1642 } | |
| 1643 | |
| 1644 /* | |
| 1645 * Extract the name constraints extension from the CA cert. | |
| 1646 * If the certificate contains no name constraints extension, but | |
| 1647 * CERT_GetImposedNameConstraints returns a name constraints extension | |
| 1648 * for the subject of the certificate, then that extension will be returned. | |
| 1649 */ | |
| 1650 SECStatus | |
| 1651 CERT_FindNameConstraintsExten(PLArenaPool *arena, CERTCertificate *cert, | |
| 1652 CERTNameConstraints **constraints) | |
| 1653 { | |
| 1654 SECStatus rv = SECSuccess; | |
| 1655 SECItem constraintsExtension; | |
| 1656 void *mark = NULL; | |
| 1657 | |
| 1658 *constraints = NULL; | |
| 1659 | |
| 1660 rv = CERT_FindCertExtension(cert, SEC_OID_X509_NAME_CONSTRAINTS, | |
| 1661 &constraintsExtension); | |
| 1662 if (rv != SECSuccess) { | |
| 1663 if (PORT_GetError() != SEC_ERROR_EXTENSION_NOT_FOUND) { | |
| 1664 return rv; | |
| 1665 } | |
| 1666 rv = CERT_GetImposedNameConstraints(&cert->derSubject, | |
| 1667 &constraintsExtension); | |
| 1668 if (rv != SECSuccess) { | |
| 1669 if (PORT_GetError() == SEC_ERROR_EXTENSION_NOT_FOUND) { | |
| 1670 return SECSuccess; | |
| 1671 } | |
| 1672 return rv; | |
| 1673 } | |
| 1674 } | |
| 1675 | |
| 1676 mark = PORT_ArenaMark(arena); | |
| 1677 | |
| 1678 *constraints = cert_DecodeNameConstraints(arena, &constraintsExtension); | |
| 1679 if (*constraints == NULL) { /* decode failed */ | |
| 1680 rv = SECFailure; | |
| 1681 } | |
| 1682 PORT_Free(constraintsExtension.data); | |
| 1683 | |
| 1684 if (rv == SECFailure) { | |
| 1685 PORT_ArenaRelease(arena, mark); | |
| 1686 } else { | |
| 1687 PORT_ArenaUnmark(arena, mark); | |
| 1688 } | |
| 1689 | |
| 1690 return rv; | |
| 1691 } | |
| 1692 | |
| 1693 /* Verify name against all the constraints relevant to that type of | |
| 1694 ** the name. | |
| 1695 */ | |
| 1696 SECStatus | |
| 1697 CERT_CheckNameSpace(PLArenaPool *arena, const CERTNameConstraints *constraints, | |
| 1698 const CERTGeneralName *currentName) | |
| 1699 { | |
| 1700 CERTNameConstraint *matchingConstraints; | |
| 1701 SECStatus rv = SECSuccess; | |
| 1702 | |
| 1703 if (constraints->excluded != NULL) { | |
| 1704 rv = CERT_GetNameConstraintByType(constraints->excluded, | |
| 1705 currentName->type, | |
| 1706 &matchingConstraints, arena); | |
| 1707 if (rv == SECSuccess && matchingConstraints != NULL) { | |
| 1708 rv = cert_CompareNameWithConstraints(currentName, | |
| 1709 matchingConstraints, PR_TRUE); | |
| 1710 } | |
| 1711 if (rv != SECSuccess) { | |
| 1712 return (rv); | |
| 1713 } | |
| 1714 } | |
| 1715 | |
| 1716 if (constraints->permited != NULL) { | |
| 1717 rv = CERT_GetNameConstraintByType(constraints->permited, | |
| 1718 currentName->type, | |
| 1719 &matchingConstraints, arena); | |
| 1720 if (rv == SECSuccess && matchingConstraints != NULL) { | |
| 1721 rv = cert_CompareNameWithConstraints(currentName, | |
| 1722 matchingConstraints, PR_FALSE); | |
| 1723 } | |
| 1724 if (rv != SECSuccess) { | |
| 1725 return (rv); | |
| 1726 } | |
| 1727 } | |
| 1728 | |
| 1729 return (SECSuccess); | |
| 1730 } | |
| 1731 | |
| 1732 /* Extract the name constraints extension from the CA cert. | |
| 1733 ** Test each and every name in namesList against all the constraints | |
| 1734 ** relevant to that type of name. | |
| 1735 ** Returns NULL in pBadCert for success, if all names are acceptable. | |
| 1736 ** If some name is not acceptable, returns a pointer to the cert that | |
| 1737 ** contained that name. | |
| 1738 */ | |
| 1739 SECStatus | |
| 1740 CERT_CompareNameSpace(CERTCertificate *cert, CERTGeneralName *namesList, | |
| 1741 CERTCertificate **certsList, PLArenaPool *reqArena, | |
| 1742 CERTCertificate **pBadCert) | |
| 1743 { | |
| 1744 SECStatus rv = SECSuccess; | |
| 1745 CERTNameConstraints *constraints; | |
| 1746 CERTGeneralName *currentName; | |
| 1747 int count = 0; | |
| 1748 CERTCertificate *badCert = NULL; | |
| 1749 | |
| 1750 /* If no names to check, then no names can be bad. */ | |
| 1751 if (!namesList) | |
| 1752 goto done; | |
| 1753 rv = CERT_FindNameConstraintsExten(reqArena, cert, &constraints); | |
| 1754 if (rv != SECSuccess) { | |
| 1755 count = -1; | |
| 1756 goto done; | |
| 1757 } | |
| 1758 | |
| 1759 currentName = namesList; | |
| 1760 do { | |
| 1761 if (constraints) { | |
| 1762 rv = CERT_CheckNameSpace(reqArena, constraints, currentName); | |
| 1763 if (rv != SECSuccess) { | |
| 1764 break; | |
| 1765 } | |
| 1766 } | |
| 1767 currentName = CERT_GetNextGeneralName(currentName); | |
| 1768 count++; | |
| 1769 } while (currentName != namesList); | |
| 1770 | |
| 1771 done: | |
| 1772 if (rv != SECSuccess) { | |
| 1773 badCert = (count >= 0) ? certsList[count] : cert; | |
| 1774 } | |
| 1775 if (pBadCert) | |
| 1776 *pBadCert = badCert; | |
| 1777 | |
| 1778 return rv; | |
| 1779 } | |
| 1780 | |
| 1781 #if 0 | |
| 1782 /* not exported from shared libs, not used. Turn on if we ever need it. */ | |
| 1783 SECStatus | |
| 1784 CERT_CompareGeneralName(CERTGeneralName *a, CERTGeneralName *b) | |
| 1785 { | |
| 1786 CERTGeneralName *currentA; | |
| 1787 CERTGeneralName *currentB; | |
| 1788 PRBool found; | |
| 1789 | |
| 1790 currentA = a; | |
| 1791 currentB = b; | |
| 1792 if (a != NULL) { | |
| 1793 do { | |
| 1794 if (currentB == NULL) { | |
| 1795 return SECFailure; | |
| 1796 } | |
| 1797 currentB = CERT_GetNextGeneralName(currentB); | |
| 1798 currentA = CERT_GetNextGeneralName(currentA); | |
| 1799 } while (currentA != a); | |
| 1800 } | |
| 1801 if (currentB != b) { | |
| 1802 return SECFailure; | |
| 1803 } | |
| 1804 currentA = a; | |
| 1805 do { | |
| 1806 currentB = b; | |
| 1807 found = PR_FALSE; | |
| 1808 do { | |
| 1809 if (currentB->type == currentA->type) { | |
| 1810 switch (currentB->type) { | |
| 1811 case certDNSName: | |
| 1812 case certEDIPartyName: | |
| 1813 case certIPAddress: | |
| 1814 case certRegisterID: | |
| 1815 case certRFC822Name: | |
| 1816 case certX400Address: | |
| 1817 case certURI: | |
| 1818 if (SECITEM_CompareItem(¤tA->name.other, | |
| 1819 ¤tB->name.other) | |
| 1820 == SECEqual) { | |
| 1821 found = PR_TRUE; | |
| 1822 } | |
| 1823 break; | |
| 1824 case certOtherName: | |
| 1825 if (SECITEM_CompareItem(¤tA->name.OthName.oid, | |
| 1826 ¤tB->name.OthName.oid) | |
| 1827 == SECEqual && | |
| 1828 SECITEM_CompareItem(¤tA->name.OthName.name, | |
| 1829 ¤tB->name.OthName.name) | |
| 1830 == SECEqual) { | |
| 1831 found = PR_TRUE; | |
| 1832 } | |
| 1833 break; | |
| 1834 case certDirectoryName: | |
| 1835 if (CERT_CompareName(¤tA->name.directoryName, | |
| 1836 ¤tB->name.directoryName) | |
| 1837 == SECEqual) { | |
| 1838 found = PR_TRUE; | |
| 1839 } | |
| 1840 } | |
| 1841 | |
| 1842 } | |
| 1843 currentB = CERT_GetNextGeneralName(currentB); | |
| 1844 } while (currentB != b && found != PR_TRUE); | |
| 1845 if (found != PR_TRUE) { | |
| 1846 return SECFailure; | |
| 1847 } | |
| 1848 currentA = CERT_GetNextGeneralName(currentA); | |
| 1849 } while (currentA != a); | |
| 1850 return SECSuccess; | |
| 1851 } | |
| 1852 | |
| 1853 SECStatus | |
| 1854 CERT_CompareGeneralNameLists(CERTGeneralNameList *a, CERTGeneralNameList *b) | |
| 1855 { | |
| 1856 SECStatus rv; | |
| 1857 | |
| 1858 if (a == b) { | |
| 1859 return SECSuccess; | |
| 1860 } | |
| 1861 if (a != NULL && b != NULL) { | |
| 1862 PZ_Lock(a->lock); | |
| 1863 PZ_Lock(b->lock); | |
| 1864 rv = CERT_CompareGeneralName(a->name, b->name); | |
| 1865 PZ_Unlock(a->lock); | |
| 1866 PZ_Unlock(b->lock); | |
| 1867 } else { | |
| 1868 rv = SECFailure; | |
| 1869 } | |
| 1870 return rv; | |
| 1871 } | |
| 1872 #endif | |
| 1873 | |
| 1874 #if 0 | |
| 1875 /* This function is not exported from NSS shared libraries, and is not | |
| 1876 ** used inside of NSS. | |
| 1877 ** XXX it doesn't check for failed allocations. :-( | |
| 1878 */ | |
| 1879 void * | |
| 1880 CERT_GetGeneralNameFromListByType(CERTGeneralNameList *list, | |
| 1881 CERTGeneralNameType type, | |
| 1882 PLArenaPool *arena) | |
| 1883 { | |
| 1884 CERTName *name = NULL; | |
| 1885 SECItem *item = NULL; | |
| 1886 OtherName *other = NULL; | |
| 1887 OtherName *tmpOther = NULL; | |
| 1888 void *data; | |
| 1889 | |
| 1890 PZ_Lock(list->lock); | |
| 1891 data = CERT_GetGeneralNameByType(list->name, type, PR_FALSE); | |
| 1892 if (data != NULL) { | |
| 1893 switch (type) { | |
| 1894 case certDNSName: | |
| 1895 case certEDIPartyName: | |
| 1896 case certIPAddress: | |
| 1897 case certRegisterID: | |
| 1898 case certRFC822Name: | |
| 1899 case certX400Address: | |
| 1900 case certURI: | |
| 1901 if (arena != NULL) { | |
| 1902 item = PORT_ArenaNew(arena, SECItem); | |
| 1903 if (item != NULL) { | |
| 1904 XXX SECITEM_CopyItem(arena, item, (SECItem *) data); | |
| 1905 } | |
| 1906 } else { | |
| 1907 item = SECITEM_DupItem((SECItem *) data); | |
| 1908 } | |
| 1909 PZ_Unlock(list->lock); | |
| 1910 return item; | |
| 1911 case certOtherName: | |
| 1912 other = (OtherName *) data; | |
| 1913 if (arena != NULL) { | |
| 1914 tmpOther = PORT_ArenaNew(arena, OtherName); | |
| 1915 } else { | |
| 1916 tmpOther = PORT_New(OtherName); | |
| 1917 } | |
| 1918 if (tmpOther != NULL) { | |
| 1919 XXX SECITEM_CopyItem(arena, &tmpOther->oid, &other->oid); | |
| 1920 XXX SECITEM_CopyItem(arena, &tmpOther->name, &other->name); | |
| 1921 } | |
| 1922 PZ_Unlock(list->lock); | |
| 1923 return tmpOther; | |
| 1924 case certDirectoryName: | |
| 1925 if (arena) { | |
| 1926 name = PORT_ArenaZNew(list->arena, CERTName); | |
| 1927 if (name) { | |
| 1928 XXX CERT_CopyName(arena, name, (CERTName *) data); | |
| 1929 } | |
| 1930 } | |
| 1931 PZ_Unlock(list->lock); | |
| 1932 return name; | |
| 1933 } | |
| 1934 } | |
| 1935 PZ_Unlock(list->lock); | |
| 1936 return NULL; | |
| 1937 } | |
| 1938 #endif | |
| 1939 | |
| 1940 #if 0 | |
| 1941 /* This function is not exported from NSS shared libraries, and is not | |
| 1942 ** used inside of NSS. | |
| 1943 ** XXX it should NOT be a void function, since it does allocations | |
| 1944 ** that can fail. | |
| 1945 */ | |
| 1946 void | |
| 1947 CERT_AddGeneralNameToList(CERTGeneralNameList *list, | |
| 1948 CERTGeneralNameType type, | |
| 1949 void *data, SECItem *oid) | |
| 1950 { | |
| 1951 CERTGeneralName *name; | |
| 1952 | |
| 1953 if (list != NULL && data != NULL) { | |
| 1954 PZ_Lock(list->lock); | |
| 1955 name = CERT_NewGeneralName(list->arena, type); | |
| 1956 if (!name) | |
| 1957 goto done; | |
| 1958 switch (type) { | |
| 1959 case certDNSName: | |
| 1960 case certEDIPartyName: | |
| 1961 case certIPAddress: | |
| 1962 case certRegisterID: | |
| 1963 case certRFC822Name: | |
| 1964 case certX400Address: | |
| 1965 case certURI: | |
| 1966 XXX SECITEM_CopyItem(list->arena, &name->name.other, (SECItem *)data); | |
| 1967 break; | |
| 1968 case certOtherName: | |
| 1969 XXX SECITEM_CopyItem(list->arena, &name->name.OthName.name, | |
| 1970 (SECItem *) data); | |
| 1971 XXX SECITEM_CopyItem(list->arena, &name->name.OthName.oid, | |
| 1972 oid); | |
| 1973 break; | |
| 1974 case certDirectoryName: | |
| 1975 XXX CERT_CopyName(list->arena, &name->name.directoryName, | |
| 1976 (CERTName *) data); | |
| 1977 break; | |
| 1978 } | |
| 1979 list->name = cert_CombineNamesLists(list->name, name); | |
| 1980 list->len++; | |
| 1981 done: | |
| 1982 PZ_Unlock(list->lock); | |
| 1983 } | |
| 1984 return; | |
| 1985 } | |
| 1986 #endif | |
| OLD | NEW |