andre@0: /* This Source Code Form is subject to the terms of the Mozilla Public andre@0: * License, v. 2.0. If a copy of the MPL was not distributed with this andre@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ andre@0: andre@0: #include "plarena.h" andre@0: #include "seccomon.h" andre@0: #include "secitem.h" andre@0: #include "secoidt.h" andre@0: #include "secasn1.h" andre@0: #include "secder.h" andre@0: #include "certt.h" andre@0: #include "cert.h" andre@0: #include "certi.h" andre@0: #include "xconst.h" andre@0: #include "secerr.h" andre@0: #include "secoid.h" andre@0: #include "prprf.h" andre@0: #include "genname.h" andre@0: andre@0: SEC_ASN1_MKSUB(SEC_AnyTemplate) andre@0: SEC_ASN1_MKSUB(SEC_IntegerTemplate) andre@0: SEC_ASN1_MKSUB(SEC_IA5StringTemplate) andre@0: SEC_ASN1_MKSUB(SEC_ObjectIDTemplate) andre@0: SEC_ASN1_MKSUB(SEC_OctetStringTemplate) andre@0: andre@0: static const SEC_ASN1Template CERTNameConstraintTemplate[] = { andre@0: { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CERTNameConstraint) }, andre@0: { SEC_ASN1_ANY, offsetof(CERTNameConstraint, DERName) }, andre@0: { SEC_ASN1_OPTIONAL | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0, andre@0: offsetof(CERTNameConstraint, min), andre@0: SEC_ASN1_SUB(SEC_IntegerTemplate) }, andre@0: { SEC_ASN1_OPTIONAL | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 1, andre@0: offsetof(CERTNameConstraint, max), andre@0: SEC_ASN1_SUB(SEC_IntegerTemplate) }, andre@0: { 0, } andre@0: }; andre@0: andre@0: const SEC_ASN1Template CERT_NameConstraintSubtreeSubTemplate[] = { andre@0: { SEC_ASN1_SEQUENCE_OF | SEC_ASN1_XTRN, 0, SEC_ASN1_SUB(SEC_AnyTemplate) } andre@0: }; andre@0: andre@0: static const SEC_ASN1Template CERTNameConstraintsTemplate[] = { andre@0: { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CERTNameConstraints) }, andre@0: { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0, andre@0: offsetof(CERTNameConstraints, DERPermited), andre@0: CERT_NameConstraintSubtreeSubTemplate}, andre@0: { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 1, andre@0: offsetof(CERTNameConstraints, DERExcluded), andre@0: CERT_NameConstraintSubtreeSubTemplate}, andre@0: { 0, } andre@0: }; andre@0: andre@0: andre@0: static const SEC_ASN1Template CERTOthNameTemplate[] = { andre@0: { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(OtherName) }, andre@0: { SEC_ASN1_OBJECT_ID, andre@0: offsetof(OtherName, oid) }, andre@0: { SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_CONSTRUCTED | SEC_ASN1_EXPLICIT | andre@0: SEC_ASN1_XTRN | 0, offsetof(OtherName, name), andre@0: SEC_ASN1_SUB(SEC_AnyTemplate) }, andre@0: { 0, } andre@0: }; andre@0: andre@0: static const SEC_ASN1Template CERTOtherNameTemplate[] = { andre@0: { SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_CONSTRUCTED | 0 , andre@0: offsetof(CERTGeneralName, name.OthName), CERTOthNameTemplate, andre@0: sizeof(CERTGeneralName) } andre@0: }; andre@0: andre@0: static const SEC_ASN1Template CERTOtherName2Template[] = { andre@0: { SEC_ASN1_SEQUENCE | SEC_ASN1_CONTEXT_SPECIFIC | 0 , andre@0: 0, NULL, sizeof(CERTGeneralName) }, andre@0: { SEC_ASN1_OBJECT_ID, andre@0: offsetof(CERTGeneralName, name.OthName) + offsetof(OtherName, oid) }, andre@0: { SEC_ASN1_ANY, andre@0: offsetof(CERTGeneralName, name.OthName) + offsetof(OtherName, name) }, andre@0: { 0, } andre@0: }; andre@0: andre@0: static const SEC_ASN1Template CERT_RFC822NameTemplate[] = { andre@0: { SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 1 , andre@0: offsetof(CERTGeneralName, name.other), andre@0: SEC_ASN1_SUB(SEC_IA5StringTemplate), andre@0: sizeof (CERTGeneralName)} andre@0: }; andre@0: andre@0: static const SEC_ASN1Template CERT_DNSNameTemplate[] = { andre@0: { SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 2 , andre@0: offsetof(CERTGeneralName, name.other), andre@0: SEC_ASN1_SUB(SEC_IA5StringTemplate), andre@0: sizeof (CERTGeneralName)} andre@0: }; andre@0: andre@0: static const SEC_ASN1Template CERT_X400AddressTemplate[] = { andre@0: { SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_CONSTRUCTED | SEC_ASN1_XTRN | 3, andre@0: offsetof(CERTGeneralName, name.other), SEC_ASN1_SUB(SEC_AnyTemplate), andre@0: sizeof (CERTGeneralName)} andre@0: }; andre@0: andre@0: static const SEC_ASN1Template CERT_DirectoryNameTemplate[] = { andre@0: { SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_CONSTRUCTED | SEC_ASN1_EXPLICIT | andre@0: SEC_ASN1_XTRN | 4, offsetof(CERTGeneralName, derDirectoryName), andre@0: SEC_ASN1_SUB(SEC_AnyTemplate), sizeof (CERTGeneralName)} andre@0: }; andre@0: andre@0: andre@0: static const SEC_ASN1Template CERT_EDIPartyNameTemplate[] = { andre@0: { SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_CONSTRUCTED | SEC_ASN1_XTRN | 5, andre@0: offsetof(CERTGeneralName, name.other), SEC_ASN1_SUB(SEC_AnyTemplate), andre@0: sizeof (CERTGeneralName)} andre@0: }; andre@0: andre@0: static const SEC_ASN1Template CERT_URITemplate[] = { andre@0: { SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 6 , andre@0: offsetof(CERTGeneralName, name.other), andre@0: SEC_ASN1_SUB(SEC_IA5StringTemplate), andre@0: sizeof (CERTGeneralName)} andre@0: }; andre@0: andre@0: static const SEC_ASN1Template CERT_IPAddressTemplate[] = { andre@0: { SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 7 , andre@0: offsetof(CERTGeneralName, name.other), andre@0: SEC_ASN1_SUB(SEC_OctetStringTemplate), andre@0: sizeof (CERTGeneralName)} andre@0: }; andre@0: andre@0: static const SEC_ASN1Template CERT_RegisteredIDTemplate[] = { andre@0: { SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 8 , andre@0: offsetof(CERTGeneralName, name.other), andre@0: SEC_ASN1_SUB(SEC_ObjectIDTemplate), andre@0: sizeof (CERTGeneralName)} andre@0: }; andre@0: andre@0: andre@0: const SEC_ASN1Template CERT_GeneralNamesTemplate[] = { andre@0: { SEC_ASN1_SEQUENCE_OF | SEC_ASN1_XTRN , 0, SEC_ASN1_SUB(SEC_AnyTemplate) } andre@0: }; andre@0: andre@0: andre@0: static struct { andre@0: CERTGeneralNameType type; andre@0: char *name; andre@0: } typesArray[] = { andre@0: { certOtherName, "other" }, andre@0: { certRFC822Name, "email" }, andre@0: { certRFC822Name, "rfc822" }, andre@0: { certDNSName, "dns" }, andre@0: { certX400Address, "x400" }, andre@0: { certX400Address, "x400addr" }, andre@0: { certDirectoryName, "directory" }, andre@0: { certDirectoryName, "dn" }, andre@0: { certEDIPartyName, "edi" }, andre@0: { certEDIPartyName, "ediparty" }, andre@0: { certURI, "uri" }, andre@0: { certIPAddress, "ip" }, andre@0: { certIPAddress, "ipaddr" }, andre@0: { certRegisterID, "registerid" } andre@0: }; andre@0: andre@0: CERTGeneralNameType andre@0: CERT_GetGeneralNameTypeFromString(const char *string) andre@0: { andre@0: int types_count = sizeof(typesArray)/sizeof(typesArray[0]); andre@0: int i; andre@0: andre@0: for (i=0; i < types_count; i++) { andre@0: if (PORT_Strcasecmp(string, typesArray[i].name) == 0) { andre@0: return typesArray[i].type; andre@0: } andre@0: } andre@0: return 0; andre@0: } andre@0: andre@0: CERTGeneralName * andre@0: CERT_NewGeneralName(PLArenaPool *arena, CERTGeneralNameType type) andre@0: { andre@0: CERTGeneralName *name = arena andre@0: ? PORT_ArenaZNew(arena, CERTGeneralName) andre@0: : PORT_ZNew(CERTGeneralName); andre@0: if (name) { andre@0: name->type = type; andre@0: name->l.prev = name->l.next = &name->l; andre@0: } andre@0: return name; andre@0: } andre@0: andre@0: /* Copy content of one General Name to another. andre@0: ** Caller has allocated destination general name. andre@0: ** This function does not change the destinate's GeneralName's list linkage. andre@0: */ andre@0: SECStatus andre@0: cert_CopyOneGeneralName(PLArenaPool *arena, andre@0: CERTGeneralName *dest, andre@0: CERTGeneralName *src) andre@0: { andre@0: SECStatus rv; andre@0: void *mark = NULL; andre@0: andre@0: PORT_Assert(dest != NULL); andre@0: dest->type = src->type; andre@0: andre@0: mark = PORT_ArenaMark(arena); andre@0: andre@0: switch (src->type) { andre@0: case certDirectoryName: andre@0: rv = SECITEM_CopyItem(arena, &dest->derDirectoryName, andre@0: &src->derDirectoryName); andre@0: if (rv == SECSuccess) andre@0: rv = CERT_CopyName(arena, &dest->name.directoryName, andre@0: &src->name.directoryName); andre@0: break; andre@0: andre@0: case certOtherName: andre@0: rv = SECITEM_CopyItem(arena, &dest->name.OthName.name, andre@0: &src->name.OthName.name); andre@0: if (rv == SECSuccess) andre@0: rv = SECITEM_CopyItem(arena, &dest->name.OthName.oid, andre@0: &src->name.OthName.oid); andre@0: break; andre@0: andre@0: default: andre@0: rv = SECITEM_CopyItem(arena, &dest->name.other, andre@0: &src->name.other); andre@0: break; andre@0: andre@0: } andre@0: if (rv != SECSuccess) { andre@0: PORT_ArenaRelease(arena, mark); andre@0: } else { andre@0: PORT_ArenaUnmark(arena, mark); andre@0: } andre@0: return rv; andre@0: } andre@0: andre@0: andre@0: void andre@0: CERT_DestroyGeneralNameList(CERTGeneralNameList *list) andre@0: { andre@0: PZLock *lock; andre@0: andre@0: if (list != NULL) { andre@0: lock = list->lock; andre@0: PZ_Lock(lock); andre@0: if (--list->refCount <= 0 && list->arena != NULL) { andre@0: PORT_FreeArena(list->arena, PR_FALSE); andre@0: PZ_Unlock(lock); andre@0: PZ_DestroyLock(lock); andre@0: } else { andre@0: PZ_Unlock(lock); andre@0: } andre@0: } andre@0: return; andre@0: } andre@0: andre@0: CERTGeneralNameList * andre@0: CERT_CreateGeneralNameList(CERTGeneralName *name) { andre@0: PLArenaPool *arena; andre@0: CERTGeneralNameList *list = NULL; andre@0: andre@0: arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); andre@0: if (arena == NULL) { andre@0: goto done; andre@0: } andre@0: list = PORT_ArenaZNew(arena, CERTGeneralNameList); andre@0: if (!list) andre@0: goto loser; andre@0: if (name != NULL) { andre@0: SECStatus rv; andre@0: list->name = CERT_NewGeneralName(arena, (CERTGeneralNameType)0); andre@0: if (!list->name) andre@0: goto loser; andre@0: rv = CERT_CopyGeneralName(arena, list->name, name); andre@0: if (rv != SECSuccess) andre@0: goto loser; andre@0: } andre@0: list->lock = PZ_NewLock(nssILockList); andre@0: if (!list->lock) andre@0: goto loser; andre@0: list->arena = arena; andre@0: list->refCount = 1; andre@0: done: andre@0: return list; andre@0: andre@0: loser: andre@0: PORT_FreeArena(arena, PR_FALSE); andre@0: return NULL; andre@0: } andre@0: andre@0: CERTGeneralName * andre@0: CERT_GetNextGeneralName(CERTGeneralName *current) andre@0: { andre@0: PRCList *next; andre@0: andre@0: next = current->l.next; andre@0: return (CERTGeneralName *) (((char *) next) - offsetof(CERTGeneralName, l)); andre@0: } andre@0: andre@0: CERTGeneralName * andre@0: CERT_GetPrevGeneralName(CERTGeneralName *current) andre@0: { andre@0: PRCList *prev; andre@0: prev = current->l.prev; andre@0: return (CERTGeneralName *) (((char *) prev) - offsetof(CERTGeneralName, l)); andre@0: } andre@0: andre@0: CERTNameConstraint * andre@0: CERT_GetNextNameConstraint(CERTNameConstraint *current) andre@0: { andre@0: PRCList *next; andre@0: andre@0: next = current->l.next; andre@0: return (CERTNameConstraint *) (((char *) next) - offsetof(CERTNameConstraint, l)); andre@0: } andre@0: andre@0: CERTNameConstraint * andre@0: CERT_GetPrevNameConstraint(CERTNameConstraint *current) andre@0: { andre@0: PRCList *prev; andre@0: prev = current->l.prev; andre@0: return (CERTNameConstraint *) (((char *) prev) - offsetof(CERTNameConstraint, l)); andre@0: } andre@0: andre@0: SECItem * andre@0: CERT_EncodeGeneralName(CERTGeneralName *genName, SECItem *dest, PLArenaPool *arena) andre@0: { andre@0: andre@0: const SEC_ASN1Template * template; andre@0: andre@0: PORT_Assert(arena); andre@0: if (arena == NULL) { andre@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); andre@0: return NULL; andre@0: } andre@0: /* TODO: mark arena */ andre@0: if (dest == NULL) { andre@0: dest = PORT_ArenaZNew(arena, SECItem); andre@0: if (!dest) andre@0: goto loser; andre@0: } andre@0: if (genName->type == certDirectoryName) { andre@0: if (genName->derDirectoryName.data == NULL) { andre@0: /* The field hasn't been encoded yet. */ andre@0: SECItem * pre_dest = andre@0: SEC_ASN1EncodeItem (arena, &(genName->derDirectoryName), andre@0: &(genName->name.directoryName), andre@0: CERT_NameTemplate); andre@0: if (!pre_dest) andre@0: goto loser; andre@0: } andre@0: if (genName->derDirectoryName.data == NULL) { andre@0: goto loser; andre@0: } andre@0: } andre@0: switch (genName->type) { andre@0: case certURI: template = CERT_URITemplate; break; andre@0: case certRFC822Name: template = CERT_RFC822NameTemplate; break; andre@0: case certDNSName: template = CERT_DNSNameTemplate; break; andre@0: case certIPAddress: template = CERT_IPAddressTemplate; break; andre@0: case certOtherName: template = CERTOtherNameTemplate; break; andre@0: case certRegisterID: template = CERT_RegisteredIDTemplate; break; andre@0: /* for this type, we expect the value is already encoded */ andre@0: case certEDIPartyName: template = CERT_EDIPartyNameTemplate; break; andre@0: /* for this type, we expect the value is already encoded */ andre@0: case certX400Address: template = CERT_X400AddressTemplate; break; andre@0: case certDirectoryName: template = CERT_DirectoryNameTemplate; break; andre@0: default: andre@0: PORT_Assert(0); goto loser; andre@0: } andre@0: dest = SEC_ASN1EncodeItem(arena, dest, genName, template); andre@0: if (!dest) { andre@0: goto loser; andre@0: } andre@0: /* TODO: unmark arena */ andre@0: return dest; andre@0: loser: andre@0: /* TODO: release arena back to mark */ andre@0: return NULL; andre@0: } andre@0: andre@0: SECItem ** andre@0: cert_EncodeGeneralNames(PLArenaPool *arena, CERTGeneralName *names) andre@0: { andre@0: CERTGeneralName *current_name; andre@0: SECItem **items = NULL; andre@0: int count = 0; andre@0: int i; andre@0: PRCList *head; andre@0: andre@0: PORT_Assert(arena); andre@0: /* TODO: mark arena */ andre@0: current_name = names; andre@0: if (names != NULL) { andre@0: count = 1; andre@0: } andre@0: head = &(names->l); andre@0: while (current_name->l.next != head) { andre@0: current_name = CERT_GetNextGeneralName(current_name); andre@0: ++count; andre@0: } andre@0: current_name = CERT_GetNextGeneralName(current_name); andre@0: items = PORT_ArenaNewArray(arena, SECItem *, count + 1); andre@0: if (items == NULL) { andre@0: goto loser; andre@0: } andre@0: for (i = 0; i < count; i++) { andre@0: items[i] = CERT_EncodeGeneralName(current_name, (SECItem *)NULL, arena); andre@0: if (items[i] == NULL) { andre@0: goto loser; andre@0: } andre@0: current_name = CERT_GetNextGeneralName(current_name); andre@0: } andre@0: items[i] = NULL; andre@0: /* TODO: unmark arena */ andre@0: return items; andre@0: loser: andre@0: /* TODO: release arena to mark */ andre@0: return NULL; andre@0: } andre@0: andre@0: CERTGeneralName * andre@0: CERT_DecodeGeneralName(PLArenaPool *reqArena, andre@0: SECItem *encodedName, andre@0: CERTGeneralName *genName) andre@0: { andre@0: const SEC_ASN1Template * template; andre@0: CERTGeneralNameType genNameType; andre@0: SECStatus rv = SECSuccess; andre@0: SECItem* newEncodedName; andre@0: andre@0: if (!reqArena) { andre@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); andre@0: return NULL; andre@0: } andre@0: /* make a copy for decoding so the data decoded with QuickDER doesn't andre@0: point to temporary memory */ andre@0: newEncodedName = SECITEM_ArenaDupItem(reqArena, encodedName); andre@0: if (!newEncodedName) { andre@0: return NULL; andre@0: } andre@0: /* TODO: mark arena */ andre@0: genNameType = (CERTGeneralNameType)((*(newEncodedName->data) & 0x0f) + 1); andre@0: if (genName == NULL) { andre@0: genName = CERT_NewGeneralName(reqArena, genNameType); andre@0: if (!genName) andre@0: goto loser; andre@0: } else { andre@0: genName->type = genNameType; andre@0: genName->l.prev = genName->l.next = &genName->l; andre@0: } andre@0: andre@0: switch (genNameType) { andre@0: case certURI: template = CERT_URITemplate; break; andre@0: case certRFC822Name: template = CERT_RFC822NameTemplate; break; andre@0: case certDNSName: template = CERT_DNSNameTemplate; break; andre@0: case certIPAddress: template = CERT_IPAddressTemplate; break; andre@0: case certOtherName: template = CERTOtherNameTemplate; break; andre@0: case certRegisterID: template = CERT_RegisteredIDTemplate; break; andre@0: case certEDIPartyName: template = CERT_EDIPartyNameTemplate; break; andre@0: case certX400Address: template = CERT_X400AddressTemplate; break; andre@0: case certDirectoryName: template = CERT_DirectoryNameTemplate; break; andre@0: default: andre@0: goto loser; andre@0: } andre@0: rv = SEC_QuickDERDecodeItem(reqArena, genName, template, newEncodedName); andre@0: if (rv != SECSuccess) andre@0: goto loser; andre@0: if (genNameType == certDirectoryName) { andre@0: rv = SEC_QuickDERDecodeItem(reqArena, &(genName->name.directoryName), andre@0: CERT_NameTemplate, andre@0: &(genName->derDirectoryName)); andre@0: if (rv != SECSuccess) andre@0: goto loser; andre@0: } andre@0: andre@0: /* TODO: unmark arena */ andre@0: return genName; andre@0: loser: andre@0: /* TODO: release arena to mark */ andre@0: return NULL; andre@0: } andre@0: andre@0: CERTGeneralName * andre@0: cert_DecodeGeneralNames (PLArenaPool *arena, andre@0: SECItem **encodedGenName) andre@0: { andre@0: PRCList *head = NULL; andre@0: PRCList *tail = NULL; andre@0: CERTGeneralName *currentName = NULL; andre@0: andre@0: PORT_Assert(arena); andre@0: if (!encodedGenName || !arena) { andre@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); andre@0: return NULL; andre@0: } andre@0: /* TODO: mark arena */ andre@0: while (*encodedGenName != NULL) { andre@0: currentName = CERT_DecodeGeneralName(arena, *encodedGenName, NULL); andre@0: if (currentName == NULL) andre@0: break; andre@0: if (head == NULL) { andre@0: head = &(currentName->l); andre@0: tail = head; andre@0: } andre@0: currentName->l.next = head; andre@0: currentName->l.prev = tail; andre@0: tail = head->prev = tail->next = &(currentName->l); andre@0: encodedGenName++; andre@0: } andre@0: if (currentName) { andre@0: /* TODO: unmark arena */ andre@0: return CERT_GetNextGeneralName(currentName); andre@0: } andre@0: /* TODO: release arena to mark */ andre@0: return NULL; andre@0: } andre@0: andre@0: void andre@0: CERT_DestroyGeneralName(CERTGeneralName *name) andre@0: { andre@0: cert_DestroyGeneralNames(name); andre@0: } andre@0: andre@0: SECStatus andre@0: cert_DestroyGeneralNames(CERTGeneralName *name) andre@0: { andre@0: CERTGeneralName *first; andre@0: CERTGeneralName *next = NULL; andre@0: andre@0: andre@0: first = name; andre@0: do { andre@0: next = CERT_GetNextGeneralName(name); andre@0: PORT_Free(name); andre@0: name = next; andre@0: } while (name != first); andre@0: return SECSuccess; andre@0: } andre@0: andre@0: static SECItem * andre@0: cert_EncodeNameConstraint(CERTNameConstraint *constraint, andre@0: SECItem *dest, andre@0: PLArenaPool *arena) andre@0: { andre@0: PORT_Assert(arena); andre@0: if (dest == NULL) { andre@0: dest = PORT_ArenaZNew(arena, SECItem); andre@0: if (dest == NULL) { andre@0: return NULL; andre@0: } andre@0: } andre@0: CERT_EncodeGeneralName(&(constraint->name), &(constraint->DERName), arena); andre@0: andre@0: dest = SEC_ASN1EncodeItem (arena, dest, constraint, andre@0: CERTNameConstraintTemplate); andre@0: return dest; andre@0: } andre@0: andre@0: SECStatus andre@0: cert_EncodeNameConstraintSubTree(CERTNameConstraint *constraints, andre@0: PLArenaPool *arena, andre@0: SECItem ***dest, andre@0: PRBool permited) andre@0: { andre@0: CERTNameConstraint *current_constraint = constraints; andre@0: SECItem **items = NULL; andre@0: int count = 0; andre@0: int i; andre@0: PRCList *head; andre@0: andre@0: PORT_Assert(arena); andre@0: /* TODO: mark arena */ andre@0: if (constraints != NULL) { andre@0: count = 1; andre@0: } andre@0: head = &constraints->l; andre@0: while (current_constraint->l.next != head) { andre@0: current_constraint = CERT_GetNextNameConstraint(current_constraint); andre@0: ++count; andre@0: } andre@0: current_constraint = CERT_GetNextNameConstraint(current_constraint); andre@0: items = PORT_ArenaZNewArray(arena, SECItem *, count + 1); andre@0: if (items == NULL) { andre@0: goto loser; andre@0: } andre@0: for (i = 0; i < count; i++) { andre@0: items[i] = cert_EncodeNameConstraint(current_constraint, andre@0: (SECItem *) NULL, arena); andre@0: if (items[i] == NULL) { andre@0: goto loser; andre@0: } andre@0: current_constraint = CERT_GetNextNameConstraint(current_constraint); andre@0: } andre@0: *dest = items; andre@0: if (*dest == NULL) { andre@0: goto loser; andre@0: } andre@0: /* TODO: unmark arena */ andre@0: return SECSuccess; andre@0: loser: andre@0: /* TODO: release arena to mark */ andre@0: return SECFailure; andre@0: } andre@0: andre@0: SECStatus andre@0: cert_EncodeNameConstraints(CERTNameConstraints *constraints, andre@0: PLArenaPool *arena, andre@0: SECItem *dest) andre@0: { andre@0: SECStatus rv = SECSuccess; andre@0: andre@0: PORT_Assert(arena); andre@0: /* TODO: mark arena */ andre@0: if (constraints->permited != NULL) { andre@0: rv = cert_EncodeNameConstraintSubTree(constraints->permited, arena, andre@0: &constraints->DERPermited, andre@0: PR_TRUE); andre@0: if (rv == SECFailure) { andre@0: goto loser; andre@0: } andre@0: } andre@0: if (constraints->excluded != NULL) { andre@0: rv = cert_EncodeNameConstraintSubTree(constraints->excluded, arena, andre@0: &constraints->DERExcluded, andre@0: PR_FALSE); andre@0: if (rv == SECFailure) { andre@0: goto loser; andre@0: } andre@0: } andre@0: dest = SEC_ASN1EncodeItem(arena, dest, constraints, andre@0: CERTNameConstraintsTemplate); andre@0: if (dest == NULL) { andre@0: goto loser; andre@0: } andre@0: /* TODO: unmark arena */ andre@0: return SECSuccess; andre@0: loser: andre@0: /* TODO: release arena to mark */ andre@0: return SECFailure; andre@0: } andre@0: andre@0: andre@0: CERTNameConstraint * andre@0: cert_DecodeNameConstraint(PLArenaPool *reqArena, andre@0: SECItem *encodedConstraint) andre@0: { andre@0: CERTNameConstraint *constraint; andre@0: SECStatus rv = SECSuccess; andre@0: CERTGeneralName *temp; andre@0: SECItem* newEncodedConstraint; andre@0: andre@0: if (!reqArena) { andre@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); andre@0: return NULL; andre@0: } andre@0: newEncodedConstraint = SECITEM_ArenaDupItem(reqArena, encodedConstraint); andre@0: if (!newEncodedConstraint) { andre@0: return NULL; andre@0: } andre@0: /* TODO: mark arena */ andre@0: constraint = PORT_ArenaZNew(reqArena, CERTNameConstraint); andre@0: if (!constraint) andre@0: goto loser; andre@0: rv = SEC_QuickDERDecodeItem(reqArena, constraint, andre@0: CERTNameConstraintTemplate, andre@0: newEncodedConstraint); andre@0: if (rv != SECSuccess) { andre@0: goto loser; andre@0: } andre@0: temp = CERT_DecodeGeneralName(reqArena, &(constraint->DERName), andre@0: &(constraint->name)); andre@0: if (temp != &(constraint->name)) { andre@0: goto loser; andre@0: } andre@0: andre@0: /* ### sjlee: since the name constraint contains only one andre@0: * CERTGeneralName, the list within CERTGeneralName shouldn't andre@0: * point anywhere else. Otherwise, bad things will happen. andre@0: */ andre@0: constraint->name.l.prev = constraint->name.l.next = &(constraint->name.l); andre@0: /* TODO: unmark arena */ andre@0: return constraint; andre@0: loser: andre@0: /* TODO: release arena back to mark */ andre@0: return NULL; andre@0: } andre@0: andre@0: CERTNameConstraint * andre@0: cert_DecodeNameConstraintSubTree(PLArenaPool *arena, andre@0: SECItem **subTree, andre@0: PRBool permited) andre@0: { andre@0: CERTNameConstraint *current = NULL; andre@0: CERTNameConstraint *first = NULL; andre@0: CERTNameConstraint *last = NULL; andre@0: int i = 0; andre@0: andre@0: PORT_Assert(arena); andre@0: /* TODO: mark arena */ andre@0: while (subTree[i] != NULL) { andre@0: current = cert_DecodeNameConstraint(arena, subTree[i]); andre@0: if (current == NULL) { andre@0: goto loser; andre@0: } andre@0: if (last == NULL) { andre@0: first = last = current; andre@0: } andre@0: current->l.prev = &(last->l); andre@0: current->l.next = last->l.next; andre@0: last->l.next = &(current->l); andre@0: i++; andre@0: } andre@0: first->l.prev = &(current->l); andre@0: /* TODO: unmark arena */ andre@0: return first; andre@0: loser: andre@0: /* TODO: release arena back to mark */ andre@0: return NULL; andre@0: } andre@0: andre@0: CERTNameConstraints * andre@0: cert_DecodeNameConstraints(PLArenaPool *reqArena, andre@0: const SECItem *encodedConstraints) andre@0: { andre@0: CERTNameConstraints *constraints; andre@0: SECStatus rv; andre@0: SECItem* newEncodedConstraints; andre@0: andre@0: if (!reqArena) { andre@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); andre@0: return NULL; andre@0: } andre@0: PORT_Assert(encodedConstraints); andre@0: newEncodedConstraints = SECITEM_ArenaDupItem(reqArena, encodedConstraints); andre@0: andre@0: /* TODO: mark arena */ andre@0: constraints = PORT_ArenaZNew(reqArena, CERTNameConstraints); andre@0: if (constraints == NULL) { andre@0: goto loser; andre@0: } andre@0: rv = SEC_QuickDERDecodeItem(reqArena, constraints, andre@0: CERTNameConstraintsTemplate, andre@0: newEncodedConstraints); andre@0: if (rv != SECSuccess) { andre@0: goto loser; andre@0: } andre@0: if (constraints->DERPermited != NULL && andre@0: constraints->DERPermited[0] != NULL) { andre@0: constraints->permited = andre@0: cert_DecodeNameConstraintSubTree(reqArena, andre@0: constraints->DERPermited, andre@0: PR_TRUE); andre@0: if (constraints->permited == NULL) { andre@0: goto loser; andre@0: } andre@0: } andre@0: if (constraints->DERExcluded != NULL && andre@0: constraints->DERExcluded[0] != NULL) { andre@0: constraints->excluded = andre@0: cert_DecodeNameConstraintSubTree(reqArena, andre@0: constraints->DERExcluded, andre@0: PR_FALSE); andre@0: if (constraints->excluded == NULL) { andre@0: goto loser; andre@0: } andre@0: } andre@0: /* TODO: unmark arena */ andre@0: return constraints; andre@0: loser: andre@0: /* TODO: release arena back to mark */ andre@0: return NULL; andre@0: } andre@0: andre@0: /* Copy a chain of one or more general names to a destination chain. andre@0: ** Caller has allocated at least the first destination GeneralName struct. andre@0: ** Both source and destination chains are circular doubly-linked lists. andre@0: ** The first source struct is copied to the first destination struct. andre@0: ** If the source chain has more than one member, and the destination chain andre@0: ** has only one member, then this function allocates new structs for all but andre@0: ** the first copy from the arena and links them into the destination list. andre@0: ** If the destination struct is part of a list with more than one member, andre@0: ** then this function traverses both the source and destination lists, andre@0: ** copying each source struct to the corresponding dest struct. andre@0: ** In that case, the destination list MUST contain at least as many andre@0: ** structs as the source list or some dest entries will be overwritten. andre@0: */ andre@0: SECStatus andre@0: CERT_CopyGeneralName(PLArenaPool *arena, andre@0: CERTGeneralName *dest, andre@0: CERTGeneralName *src) andre@0: { andre@0: SECStatus rv; andre@0: CERTGeneralName *destHead = dest; andre@0: CERTGeneralName *srcHead = src; andre@0: andre@0: PORT_Assert(dest != NULL); andre@0: if (!dest) { andre@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); andre@0: return SECFailure; andre@0: } andre@0: /* TODO: mark arena */ andre@0: do { andre@0: rv = cert_CopyOneGeneralName(arena, dest, src); andre@0: if (rv != SECSuccess) andre@0: goto loser; andre@0: src = CERT_GetNextGeneralName(src); andre@0: /* if there is only one general name, we shouldn't do this */ andre@0: if (src != srcHead) { andre@0: if (dest->l.next == &destHead->l) { andre@0: CERTGeneralName *temp; andre@0: temp = CERT_NewGeneralName(arena, (CERTGeneralNameType)0); andre@0: if (!temp) andre@0: goto loser; andre@0: temp->l.next = &destHead->l; andre@0: temp->l.prev = &dest->l; andre@0: destHead->l.prev = &temp->l; andre@0: dest->l.next = &temp->l; andre@0: dest = temp; andre@0: } else { andre@0: dest = CERT_GetNextGeneralName(dest); andre@0: } andre@0: } andre@0: } while (src != srcHead && rv == SECSuccess); andre@0: /* TODO: unmark arena */ andre@0: return rv; andre@0: loser: andre@0: /* TODO: release back to mark */ andre@0: return SECFailure; andre@0: } andre@0: andre@0: andre@0: CERTGeneralNameList * andre@0: CERT_DupGeneralNameList(CERTGeneralNameList *list) andre@0: { andre@0: if (list != NULL) { andre@0: PZ_Lock(list->lock); andre@0: list->refCount++; andre@0: PZ_Unlock(list->lock); andre@0: } andre@0: return list; andre@0: } andre@0: andre@0: /* Allocate space and copy CERTNameConstraint from src to dest */ andre@0: CERTNameConstraint * andre@0: CERT_CopyNameConstraint(PLArenaPool *arena, andre@0: CERTNameConstraint *dest, andre@0: CERTNameConstraint *src) andre@0: { andre@0: SECStatus rv; andre@0: andre@0: /* TODO: mark arena */ andre@0: if (dest == NULL) { andre@0: dest = PORT_ArenaZNew(arena, CERTNameConstraint); andre@0: if (!dest) andre@0: goto loser; andre@0: /* mark that it is not linked */ andre@0: dest->name.l.prev = dest->name.l.next = &(dest->name.l); andre@0: } andre@0: rv = CERT_CopyGeneralName(arena, &dest->name, &src->name); andre@0: if (rv != SECSuccess) { andre@0: goto loser; andre@0: } andre@0: rv = SECITEM_CopyItem(arena, &dest->DERName, &src->DERName); andre@0: if (rv != SECSuccess) { andre@0: goto loser; andre@0: } andre@0: rv = SECITEM_CopyItem(arena, &dest->min, &src->min); andre@0: if (rv != SECSuccess) { andre@0: goto loser; andre@0: } andre@0: rv = SECITEM_CopyItem(arena, &dest->max, &src->max); andre@0: if (rv != SECSuccess) { andre@0: goto loser; andre@0: } andre@0: dest->l.prev = dest->l.next = &dest->l; andre@0: /* TODO: unmark arena */ andre@0: return dest; andre@0: loser: andre@0: /* TODO: release arena to mark */ andre@0: return NULL; andre@0: } andre@0: andre@0: andre@0: CERTGeneralName * andre@0: cert_CombineNamesLists(CERTGeneralName *list1, CERTGeneralName *list2) andre@0: { andre@0: PRCList *begin1; andre@0: PRCList *begin2; andre@0: PRCList *end1; andre@0: PRCList *end2; andre@0: andre@0: if (list1 == NULL){ andre@0: return list2; andre@0: } else if (list2 == NULL) { andre@0: return list1; andre@0: } else { andre@0: begin1 = &list1->l; andre@0: begin2 = &list2->l; andre@0: end1 = list1->l.prev; andre@0: end2 = list2->l.prev; andre@0: end1->next = begin2; andre@0: end2->next = begin1; andre@0: begin1->prev = end2; andre@0: begin2->prev = end1; andre@0: return list1; andre@0: } andre@0: } andre@0: andre@0: andre@0: CERTNameConstraint * andre@0: cert_CombineConstraintsLists(CERTNameConstraint *list1, CERTNameConstraint *list2) andre@0: { andre@0: PRCList *begin1; andre@0: PRCList *begin2; andre@0: PRCList *end1; andre@0: PRCList *end2; andre@0: andre@0: if (list1 == NULL){ andre@0: return list2; andre@0: } else if (list2 == NULL) { andre@0: return list1; andre@0: } else { andre@0: begin1 = &list1->l; andre@0: begin2 = &list2->l; andre@0: end1 = list1->l.prev; andre@0: end2 = list2->l.prev; andre@0: end1->next = begin2; andre@0: end2->next = begin1; andre@0: begin1->prev = end2; andre@0: begin2->prev = end1; andre@0: return list1; andre@0: } andre@0: } andre@0: andre@0: andre@0: /* Add a CERTNameConstraint to the CERTNameConstraint list */ andre@0: CERTNameConstraint * andre@0: CERT_AddNameConstraint(CERTNameConstraint *list, andre@0: CERTNameConstraint *constraint) andre@0: { andre@0: PORT_Assert(constraint != NULL); andre@0: constraint->l.next = constraint->l.prev = &constraint->l; andre@0: list = cert_CombineConstraintsLists(list, constraint); andre@0: return list; andre@0: } andre@0: andre@0: andre@0: SECStatus andre@0: CERT_GetNameConstraintByType (CERTNameConstraint *constraints, andre@0: CERTGeneralNameType type, andre@0: CERTNameConstraint **returnList, andre@0: PLArenaPool *arena) andre@0: { andre@0: CERTNameConstraint *current = NULL; andre@0: void *mark = NULL; andre@0: andre@0: *returnList = NULL; andre@0: if (!constraints) andre@0: return SECSuccess; andre@0: andre@0: mark = PORT_ArenaMark(arena); andre@0: andre@0: current = constraints; andre@0: do { andre@0: PORT_Assert(current->name.type); andre@0: if (current->name.type == type) { andre@0: CERTNameConstraint *temp; andre@0: temp = CERT_CopyNameConstraint(arena, NULL, current); andre@0: if (temp == NULL) andre@0: goto loser; andre@0: *returnList = CERT_AddNameConstraint(*returnList, temp); andre@0: } andre@0: current = CERT_GetNextNameConstraint(current); andre@0: } while (current != constraints); andre@0: PORT_ArenaUnmark(arena, mark); andre@0: return SECSuccess; andre@0: andre@0: loser: andre@0: PORT_ArenaRelease(arena, mark); andre@0: return SECFailure; andre@0: } andre@0: andre@0: void * andre@0: CERT_GetGeneralNameByType (CERTGeneralName *genNames, andre@0: CERTGeneralNameType type, PRBool derFormat) andre@0: { andre@0: CERTGeneralName *current; andre@0: andre@0: if (!genNames) andre@0: return NULL; andre@0: current = genNames; andre@0: andre@0: do { andre@0: if (current->type == type) { andre@0: switch (type) { andre@0: case certDNSName: andre@0: case certEDIPartyName: andre@0: case certIPAddress: andre@0: case certRegisterID: andre@0: case certRFC822Name: andre@0: case certX400Address: andre@0: case certURI: andre@0: return (void *)¤t->name.other; /* SECItem * */ andre@0: andre@0: case certOtherName: andre@0: return (void *)¤t->name.OthName; /* OthName * */ andre@0: andre@0: case certDirectoryName: andre@0: return derFormat andre@0: ? (void *)¤t->derDirectoryName /* SECItem * */ andre@0: : (void *)¤t->name.directoryName; /* CERTName * */ andre@0: } andre@0: PORT_Assert(0); andre@0: return NULL; andre@0: } andre@0: current = CERT_GetNextGeneralName(current); andre@0: } while (current != genNames); andre@0: return NULL; andre@0: } andre@0: andre@0: int andre@0: CERT_GetNamesLength(CERTGeneralName *names) andre@0: { andre@0: int length = 0; andre@0: CERTGeneralName *first; andre@0: andre@0: first = names; andre@0: if (names != NULL) { andre@0: do { andre@0: length++; andre@0: names = CERT_GetNextGeneralName(names); andre@0: } while (names != first); andre@0: } andre@0: return length; andre@0: } andre@0: andre@0: /* Creates new GeneralNames for any email addresses found in the andre@0: ** input DN, and links them onto the list for the DN. andre@0: */ andre@0: SECStatus andre@0: cert_ExtractDNEmailAddrs(CERTGeneralName *name, PLArenaPool *arena) andre@0: { andre@0: CERTGeneralName *nameList = NULL; andre@0: const CERTRDN **nRDNs = (const CERTRDN **)(name->name.directoryName.rdns); andre@0: SECStatus rv = SECSuccess; andre@0: andre@0: PORT_Assert(name->type == certDirectoryName); andre@0: if (name->type != certDirectoryName) { andre@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); andre@0: return SECFailure; andre@0: } andre@0: /* TODO: mark arena */ andre@0: while (nRDNs && *nRDNs) { /* loop over RDNs */ andre@0: const CERTRDN *nRDN = *nRDNs++; andre@0: CERTAVA **nAVAs = nRDN->avas; andre@0: while (nAVAs && *nAVAs) { /* loop over AVAs */ andre@0: int tag; andre@0: CERTAVA *nAVA = *nAVAs++; andre@0: tag = CERT_GetAVATag(nAVA); andre@0: if ( tag == SEC_OID_PKCS9_EMAIL_ADDRESS || andre@0: tag == SEC_OID_RFC1274_MAIL) { /* email AVA */ andre@0: CERTGeneralName *newName = NULL; andre@0: SECItem *avaValue = CERT_DecodeAVAValue(&nAVA->value); andre@0: if (!avaValue) andre@0: goto loser; andre@0: rv = SECFailure; andre@0: newName = CERT_NewGeneralName(arena, certRFC822Name); andre@0: if (newName) { andre@0: rv = SECITEM_CopyItem(arena, &newName->name.other, avaValue); andre@0: } andre@0: SECITEM_FreeItem(avaValue, PR_TRUE); andre@0: if (rv != SECSuccess) andre@0: goto loser; andre@0: nameList = cert_CombineNamesLists(nameList, newName); andre@0: } /* handle one email AVA */ andre@0: } /* loop over AVAs */ andre@0: } /* loop over RDNs */ andre@0: /* combine new names with old one. */ andre@0: name = cert_CombineNamesLists(name, nameList); andre@0: /* TODO: unmark arena */ andre@0: return SECSuccess; andre@0: andre@0: loser: andre@0: /* TODO: release arena back to mark */ andre@0: return SECFailure; andre@0: } andre@0: andre@0: /* Extract all names except Subject Common Name from a cert andre@0: ** in preparation for a name constraints test. andre@0: */ andre@0: CERTGeneralName * andre@0: CERT_GetCertificateNames(CERTCertificate *cert, PLArenaPool *arena) andre@0: { andre@0: return CERT_GetConstrainedCertificateNames(cert, arena, PR_FALSE); andre@0: } andre@0: andre@0: /* This function is called by CERT_VerifyCertChain to extract all andre@0: ** names from a cert in preparation for a name constraints test. andre@0: */ andre@0: CERTGeneralName * andre@0: CERT_GetConstrainedCertificateNames(const CERTCertificate *cert, andre@0: PLArenaPool *arena, andre@0: PRBool includeSubjectCommonName) andre@0: { andre@0: CERTGeneralName *DN; andre@0: CERTGeneralName *SAN; andre@0: PRUint32 numDNSNames = 0; andre@0: SECStatus rv; andre@0: andre@0: if (!arena) { andre@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); andre@0: return NULL; andre@0: } andre@0: /* TODO: mark arena */ andre@0: DN = CERT_NewGeneralName(arena, certDirectoryName); andre@0: if (DN == NULL) { andre@0: goto loser; andre@0: } andre@0: rv = CERT_CopyName(arena, &DN->name.directoryName, &cert->subject); andre@0: if (rv != SECSuccess) { andre@0: goto loser; andre@0: } andre@0: rv = SECITEM_CopyItem(arena, &DN->derDirectoryName, &cert->derSubject); andre@0: if (rv != SECSuccess) { andre@0: goto loser; andre@0: } andre@0: /* Extract email addresses from DN, construct CERTGeneralName structs andre@0: ** for them, add them to the name list andre@0: */ andre@0: rv = cert_ExtractDNEmailAddrs(DN, arena); andre@0: if (rv != SECSuccess) andre@0: goto loser; andre@0: andre@0: /* Now extract any GeneralNames from the subject name names extension. */ andre@0: SAN = cert_GetSubjectAltNameList(cert, arena); andre@0: if (SAN) { andre@0: numDNSNames = cert_CountDNSPatterns(SAN); andre@0: DN = cert_CombineNamesLists(DN, SAN); andre@0: } andre@0: if (!numDNSNames && includeSubjectCommonName) { andre@0: char *cn = CERT_GetCommonName(&cert->subject); andre@0: if (cn) { andre@0: CERTGeneralName *CN = CERT_NewGeneralName(arena, certDNSName); andre@0: if (CN) { andre@0: SECItem cnItem = {siBuffer, NULL, 0}; andre@0: cnItem.data = (unsigned char *)cn; andre@0: cnItem.len = strlen(cn); andre@0: rv = SECITEM_CopyItem(arena, &CN->name.other, &cnItem); andre@0: if (rv == SECSuccess) { andre@0: DN = cert_CombineNamesLists(DN, CN); andre@0: } andre@0: } andre@0: PORT_Free(cn); andre@0: } andre@0: } andre@0: if (rv == SECSuccess) { andre@0: /* TODO: unmark arena */ andre@0: return DN; andre@0: } andre@0: loser: andre@0: /* TODO: release arena to mark */ andre@0: return NULL; andre@0: } andre@0: andre@0: /* Returns SECSuccess if name matches constraint per RFC 3280 rules for andre@0: ** URI name constraints. SECFailure otherwise. andre@0: ** If the constraint begins with a dot, it is a domain name, otherwise andre@0: ** It is a host name. Examples: andre@0: ** Constraint Name Result andre@0: ** ------------ --------------- -------- andre@0: ** foo.bar.com foo.bar.com matches andre@0: ** foo.bar.com FoO.bAr.CoM matches andre@0: ** foo.bar.com www.foo.bar.com no match andre@0: ** foo.bar.com nofoo.bar.com no match andre@0: ** .foo.bar.com www.foo.bar.com matches andre@0: ** .foo.bar.com nofoo.bar.com no match andre@0: ** .foo.bar.com foo.bar.com no match andre@0: ** .foo.bar.com www..foo.bar.com no match andre@0: */ andre@0: static SECStatus andre@0: compareURIN2C(const SECItem *name, const SECItem *constraint) andre@0: { andre@0: int offset; andre@0: /* The spec is silent on intepreting zero-length constraints. andre@0: ** We interpret them as matching no URI names. andre@0: */ andre@0: if (!constraint->len) andre@0: return SECFailure; andre@0: if (constraint->data[0] != '.') { andre@0: /* constraint is a host name. */ andre@0: if (name->len != constraint->len || andre@0: PL_strncasecmp((char *)name->data, andre@0: (char *)constraint->data, constraint->len)) andre@0: return SECFailure; andre@0: return SECSuccess; andre@0: } andre@0: /* constraint is a domain name. */ andre@0: if (name->len < constraint->len) andre@0: return SECFailure; andre@0: offset = name->len - constraint->len; andre@0: if (PL_strncasecmp((char *)(name->data + offset), andre@0: (char *)constraint->data, constraint->len)) andre@0: return SECFailure; andre@0: if (!offset || andre@0: (name->data[offset - 1] == '.') + (constraint->data[0] == '.') == 1) andre@0: return SECSuccess; andre@0: return SECFailure; andre@0: } andre@0: andre@0: /* for DNSname constraints, RFC 3280 says, (section 4.2.1.11, page 38) andre@0: ** andre@0: ** DNS name restrictions are expressed as foo.bar.com. Any DNS name andre@0: ** that can be constructed by simply adding to the left hand side of the andre@0: ** name satisfies the name constraint. For example, www.foo.bar.com andre@0: ** would satisfy the constraint but foo1.bar.com would not. andre@0: ** andre@0: ** But NIST's PKITS test suite requires that the constraint be treated andre@0: ** as a domain name, and requires that any name added to the left hand andre@0: ** side end in a dot ".". Sensible, but not strictly following the RFC. andre@0: ** andre@0: ** Constraint Name RFC 3280 NIST PKITS andre@0: ** ------------ --------------- -------- ---------- andre@0: ** foo.bar.com foo.bar.com matches matches andre@0: ** foo.bar.com FoO.bAr.CoM matches matches andre@0: ** foo.bar.com www.foo.bar.com matches matches andre@0: ** foo.bar.com nofoo.bar.com MATCHES NO MATCH andre@0: ** .foo.bar.com www.foo.bar.com matches matches? disallowed? andre@0: ** .foo.bar.com foo.bar.com no match no match andre@0: ** .foo.bar.com www..foo.bar.com matches probably not andre@0: ** andre@0: ** We will try to conform to NIST's PKITS tests, and the unstated andre@0: ** rules they imply. andre@0: */ andre@0: static SECStatus andre@0: compareDNSN2C(const SECItem *name, const SECItem *constraint) andre@0: { andre@0: int offset; andre@0: /* The spec is silent on intepreting zero-length constraints. andre@0: ** We interpret them as matching all DNSnames. andre@0: */ andre@0: if (!constraint->len) andre@0: return SECSuccess; andre@0: if (name->len < constraint->len) andre@0: return SECFailure; andre@0: offset = name->len - constraint->len; andre@0: if (PL_strncasecmp((char *)(name->data + offset), andre@0: (char *)constraint->data, constraint->len)) andre@0: return SECFailure; andre@0: if (!offset || andre@0: (name->data[offset - 1] == '.') + (constraint->data[0] == '.') == 1) andre@0: return SECSuccess; andre@0: return SECFailure; andre@0: } andre@0: andre@0: /* Returns SECSuccess if name matches constraint per RFC 3280 rules for andre@0: ** internet email addresses. SECFailure otherwise. andre@0: ** If constraint contains a '@' then the two strings much match exactly. andre@0: ** Else if constraint starts with a '.'. then it must match the right-most andre@0: ** substring of the name, andre@0: ** else constraint string must match entire name after the name's '@'. andre@0: ** Empty constraint string matches all names. All comparisons case insensitive. andre@0: */ andre@0: static SECStatus andre@0: compareRFC822N2C(const SECItem *name, const SECItem *constraint) andre@0: { andre@0: int offset; andre@0: if (!constraint->len) andre@0: return SECSuccess; andre@0: if (name->len < constraint->len) andre@0: return SECFailure; andre@0: if (constraint->len == 1 && constraint->data[0] == '.') andre@0: return SECSuccess; andre@0: for (offset = constraint->len - 1; offset >= 0; --offset) { andre@0: if (constraint->data[offset] == '@') { andre@0: return (name->len == constraint->len && andre@0: !PL_strncasecmp((char *)name->data, andre@0: (char *)constraint->data, constraint->len)) andre@0: ? SECSuccess : SECFailure; andre@0: } andre@0: } andre@0: offset = name->len - constraint->len; andre@0: if (PL_strncasecmp((char *)(name->data + offset), andre@0: (char *)constraint->data, constraint->len)) andre@0: return SECFailure; andre@0: if (constraint->data[0] == '.') andre@0: return SECSuccess; andre@0: if (offset > 0 && name->data[offset - 1] == '@') andre@0: return SECSuccess; andre@0: return SECFailure; andre@0: } andre@0: andre@0: /* name contains either a 4 byte IPv4 address or a 16 byte IPv6 address. andre@0: ** constraint contains an address of the same length, and a subnet mask andre@0: ** of the same length. Compare name's address to the constraint's andre@0: ** address, subject to the mask. andre@0: ** Return SECSuccess if they match, SECFailure if they don't. andre@0: */ andre@0: static SECStatus andre@0: compareIPaddrN2C(const SECItem *name, const SECItem *constraint) andre@0: { andre@0: int i; andre@0: if (name->len == 4 && constraint->len == 8) { /* ipv4 addr */ andre@0: for (i = 0; i < 4; i++) { andre@0: if ((name->data[i] ^ constraint->data[i]) & constraint->data[i+4]) andre@0: goto loser; andre@0: } andre@0: return SECSuccess; andre@0: } andre@0: if (name->len == 16 && constraint->len == 32) { /* ipv6 addr */ andre@0: for (i = 0; i < 16; i++) { andre@0: if ((name->data[i] ^ constraint->data[i]) & constraint->data[i+16]) andre@0: goto loser; andre@0: } andre@0: return SECSuccess; andre@0: } andre@0: loser: andre@0: return SECFailure; andre@0: } andre@0: andre@0: /* start with a SECItem that points to a URI. Parse it lookingg for andre@0: ** a hostname. Modify item->data and item->len to define the hostname, andre@0: ** but do not modify and data at item->data. andre@0: ** If anything goes wrong, the contents of *item are undefined. andre@0: */ andre@0: static SECStatus andre@0: parseUriHostname(SECItem * item) andre@0: { andre@0: int i; andre@0: PRBool found = PR_FALSE; andre@0: for (i = 0; (unsigned)(i+2) < item->len; ++i) { andre@0: if (item->data[i ] == ':' && andre@0: item->data[i+1] == '/' && andre@0: item->data[i+2] == '/') { andre@0: i += 3; andre@0: item->data += i; andre@0: item->len -= i; andre@0: found = PR_TRUE; andre@0: break; andre@0: } andre@0: } andre@0: if (!found) andre@0: return SECFailure; andre@0: /* now look for a '/', which is an upper bound in the end of the name */ andre@0: for (i = 0; (unsigned)i < item->len; ++i) { andre@0: if (item->data[i] == '/') { andre@0: item->len = i; andre@0: break; andre@0: } andre@0: } andre@0: /* now look for a ':', which marks the end of the name */ andre@0: for (i = item->len; --i >= 0; ) { andre@0: if (item->data[i] == ':') { andre@0: item->len = i; andre@0: break; andre@0: } andre@0: } andre@0: /* now look for an '@', which marks the beginning of the hostname */ andre@0: for (i = 0; (unsigned)i < item->len; ++i) { andre@0: if (item->data[i] == '@') { andre@0: ++i; andre@0: item->data += i; andre@0: item->len -= i; andre@0: break; andre@0: } andre@0: } andre@0: return item->len ? SECSuccess : SECFailure; andre@0: } andre@0: andre@0: /* This function takes one name, and a list of constraints. andre@0: ** It searches the constraints looking for a match. andre@0: ** It returns SECSuccess if the name satisfies the constraints, i.e., andre@0: ** if excluded, then the name does not match any constraint, andre@0: ** if permitted, then the name matches at least one constraint. andre@0: ** It returns SECFailure if the name fails to satisfy the constraints, andre@0: ** or if some code fails (e.g. out of memory, or invalid constraint) andre@0: */ andre@0: SECStatus andre@0: cert_CompareNameWithConstraints(const CERTGeneralName *name, andre@0: const CERTNameConstraint *constraints, andre@0: PRBool excluded) andre@0: { andre@0: SECStatus rv = SECSuccess; andre@0: SECStatus matched = SECFailure; andre@0: const CERTNameConstraint *current; andre@0: andre@0: PORT_Assert(constraints); /* caller should not call with NULL */ andre@0: if (!constraints) { andre@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); andre@0: return SECFailure; andre@0: } andre@0: andre@0: current = constraints; andre@0: do { andre@0: rv = SECSuccess; andre@0: matched = SECFailure; andre@0: PORT_Assert(name->type == current->name.type); andre@0: switch (name->type) { andre@0: andre@0: case certDNSName: andre@0: matched = compareDNSN2C(&name->name.other, andre@0: ¤t->name.name.other); andre@0: break; andre@0: andre@0: case certRFC822Name: andre@0: matched = compareRFC822N2C(&name->name.other, andre@0: ¤t->name.name.other); andre@0: break; andre@0: andre@0: case certURI: andre@0: { andre@0: /* make a modifiable copy of the URI SECItem. */ andre@0: SECItem uri = name->name.other; andre@0: /* find the hostname in the URI */ andre@0: rv = parseUriHostname(&uri); andre@0: if (rv == SECSuccess) { andre@0: /* does our hostname meet the constraint? */ andre@0: matched = compareURIN2C(&uri, ¤t->name.name.other); andre@0: } andre@0: } andre@0: break; andre@0: andre@0: case certDirectoryName: andre@0: /* Determine if the constraint directory name is a "prefix" andre@0: ** for the directory name being tested. andre@0: */ andre@0: { andre@0: /* status defaults to SECEqual, so that a constraint with andre@0: ** no AVAs will be a wildcard, matching all directory names. andre@0: */ andre@0: SECComparison status = SECEqual; andre@0: const CERTRDN **cRDNs = andre@0: (const CERTRDN **)current->name.name.directoryName.rdns; andre@0: const CERTRDN **nRDNs = andre@0: (const CERTRDN **)name->name.directoryName.rdns; andre@0: while (cRDNs && *cRDNs && nRDNs && *nRDNs) { andre@0: /* loop over name RDNs and constraint RDNs in lock step */ andre@0: const CERTRDN *cRDN = *cRDNs++; andre@0: const CERTRDN *nRDN = *nRDNs++; andre@0: CERTAVA **cAVAs = cRDN->avas; andre@0: while (cAVAs && *cAVAs) { /* loop over constraint AVAs */ andre@0: CERTAVA *cAVA = *cAVAs++; andre@0: CERTAVA **nAVAs = nRDN->avas; andre@0: while (nAVAs && *nAVAs) { /* loop over name AVAs */ andre@0: CERTAVA *nAVA = *nAVAs++; andre@0: status = CERT_CompareAVA(cAVA, nAVA); andre@0: if (status == SECEqual) andre@0: break; andre@0: } /* loop over name AVAs */ andre@0: if (status != SECEqual) andre@0: break; andre@0: } /* loop over constraint AVAs */ andre@0: if (status != SECEqual) andre@0: break; andre@0: } /* loop over name RDNs and constraint RDNs */ andre@0: matched = (status == SECEqual) ? SECSuccess : SECFailure; andre@0: break; andre@0: } andre@0: andre@0: case certIPAddress: /* type 8 */ andre@0: matched = compareIPaddrN2C(&name->name.other, andre@0: ¤t->name.name.other); andre@0: break; andre@0: andre@0: /* NSS does not know how to compare these "Other" type names with andre@0: ** their respective constraints. But it does know how to tell andre@0: ** if the constraint applies to the type of name (by comparing andre@0: ** the constraint OID to the name OID). NSS makes no use of "Other" andre@0: ** type names at all, so NSS errs on the side of leniency for these andre@0: ** types, provided that their OIDs match. So, when an "Other" andre@0: ** name constraint appears in an excluded subtree, it never causes andre@0: ** a name to fail. When an "Other" name constraint appears in a andre@0: ** permitted subtree, AND the constraint's OID matches the name's andre@0: ** OID, then name is treated as if it matches the constraint. andre@0: */ andre@0: case certOtherName: /* type 1 */ andre@0: matched = (!excluded && andre@0: name->type == current->name.type && andre@0: SECITEM_ItemsAreEqual(&name->name.OthName.oid, andre@0: ¤t->name.name.OthName.oid)) andre@0: ? SECSuccess : SECFailure; andre@0: break; andre@0: andre@0: /* NSS does not know how to compare these types of names with their andre@0: ** respective constraints. But NSS makes no use of these types of andre@0: ** names at all, so it errs on the side of leniency for these types. andre@0: ** Constraints for these types of names never cause the name to andre@0: ** fail the constraints test. NSS behaves as if the name matched andre@0: ** for permitted constraints, and did not match for excluded ones. andre@0: */ andre@0: case certX400Address: /* type 4 */ andre@0: case certEDIPartyName: /* type 6 */ andre@0: case certRegisterID: /* type 9 */ andre@0: matched = excluded ? SECFailure : SECSuccess; andre@0: break; andre@0: andre@0: default: /* non-standard types are not supported */ andre@0: rv = SECFailure; andre@0: break; andre@0: } andre@0: if (matched == SECSuccess || rv != SECSuccess) andre@0: break; andre@0: current = CERT_GetNextNameConstraint((CERTNameConstraint*)current); andre@0: } while (current != constraints); andre@0: if (rv == SECSuccess) { andre@0: if (matched == SECSuccess) andre@0: rv = excluded ? SECFailure : SECSuccess; andre@0: else andre@0: rv = excluded ? SECSuccess : SECFailure; andre@0: return rv; andre@0: } andre@0: andre@0: return SECFailure; andre@0: } andre@0: andre@0: /* Add and link a CERTGeneralName to a CERTNameConstraint list. Most andre@0: ** likely the CERTNameConstraint passed in is either the permitted andre@0: ** list or the excluded list of a CERTNameConstraints. andre@0: */ andre@0: SECStatus andre@0: CERT_AddNameConstraintByGeneralName(PLArenaPool *arena, andre@0: CERTNameConstraint **constraints, andre@0: CERTGeneralName *name) andre@0: { andre@0: SECStatus rv; andre@0: CERTNameConstraint *current = NULL; andre@0: CERTNameConstraint *first = *constraints; andre@0: void *mark = NULL; andre@0: andre@0: mark = PORT_ArenaMark(arena); andre@0: andre@0: current = PORT_ArenaZNew(arena, CERTNameConstraint); andre@0: if (current == NULL) { andre@0: rv = SECFailure; andre@0: goto done; andre@0: } andre@0: andre@0: rv = cert_CopyOneGeneralName(arena, ¤t->name, name); andre@0: if (rv != SECSuccess) { andre@0: goto done; andre@0: } andre@0: andre@0: current->name.l.prev = current->name.l.next = &(current->name.l); andre@0: andre@0: if (first == NULL) { andre@0: *constraints = current; andre@0: PR_INIT_CLIST(¤t->l); andre@0: } else { andre@0: PR_INSERT_BEFORE(¤t->l, &first->l); andre@0: } andre@0: andre@0: done: andre@0: if (rv == SECFailure) { andre@0: PORT_ArenaRelease(arena, mark); andre@0: } else { andre@0: PORT_ArenaUnmark(arena, mark); andre@0: } andre@0: return rv; andre@0: } andre@0: andre@0: /* Add name constraints to certain certs that do not include name constraints andre@0: * This is the core of the implementation for bug 952572. andre@0: */ andre@0: andre@0: static SECStatus andre@0: getNameExtensionsBuiltIn(CERTCertificate *cert, andre@0: SECItem *extensions) andre@0: { andre@0: const char constraintFranceGov[] = "\x30\x5D" /* sequence len = 93*/ andre@0: "\xA0\x5B" /* element len =91 */ andre@0: "\x30\x05" /* sequence len 5 */ andre@0: "\x82\x03" /* entry len 3 */ andre@0: ".fr" andre@0: "\x30\x05\x82\x03" /* sequence len5, entry len 3 */ andre@0: ".gp" andre@0: "\x30\x05\x82\x03" andre@0: ".gf" andre@0: "\x30\x05\x82\x03" andre@0: ".mq" andre@0: "\x30\x05\x82\x03" andre@0: ".re" andre@0: "\x30\x05\x82\x03" andre@0: ".yt" andre@0: "\x30\x05\x82\x03" andre@0: ".pm" andre@0: "\x30\x05\x82\x03" andre@0: ".bl" andre@0: "\x30\x05\x82\x03" andre@0: ".mf" andre@0: "\x30\x05\x82\x03" andre@0: ".wf" andre@0: "\x30\x05\x82\x03" andre@0: ".pf" andre@0: "\x30\x05\x82\x03" andre@0: ".nc" andre@0: "\x30\x05\x82\x03" andre@0: ".tf"; andre@0: andre@0: /* The stringified value for the subject is: andre@0: E=igca@sgdn.pm.gouv.fr,CN=IGC/A,OU=DCSSI,O=PM/SGDN,L=Paris,ST=France,C=FR andre@0: */ andre@0: const char rawANSSISubject[] = "\x30\x81\x85\x31\x0B\x30\x09\x06\x03\x55\x04" andre@0: "\x06\x13\x02\x46\x52\x31\x0F\x30\x0D\x06\x03" andre@0: "\x55\x04\x08\x13\x06\x46\x72\x61\x6E\x63\x65" andre@0: "\x31\x0E\x30\x0C\x06\x03\x55\x04\x07\x13\x05" andre@0: "\x50\x61\x72\x69\x73\x31\x10\x30\x0E\x06\x03" andre@0: "\x55\x04\x0A\x13\x07\x50\x4D\x2F\x53\x47\x44" andre@0: "\x4E\x31\x0E\x30\x0C\x06\x03\x55\x04\x0B\x13" andre@0: "\x05\x44\x43\x53\x53\x49\x31\x0E\x30\x0C\x06" andre@0: "\x03\x55\x04\x03\x13\x05\x49\x47\x43\x2F\x41" andre@0: "\x31\x23\x30\x21\x06\x09\x2A\x86\x48\x86\xF7" andre@0: "\x0D\x01\x09\x01\x16\x14\x69\x67\x63\x61\x40" andre@0: "\x73\x67\x64\x6E\x2E\x70\x6D\x2E\x67\x6F\x75" andre@0: "\x76\x2E\x66\x72"; andre@0: andre@0: const SECItem anssi_subject = {0, (unsigned char *) rawANSSISubject, andre@0: sizeof(rawANSSISubject)-1}; andre@0: const SECItem permitFranceGovNC = {0, (unsigned char *) constraintFranceGov, andre@0: sizeof(constraintFranceGov)-1}; andre@0: andre@0: if (SECITEM_ItemsAreEqual(&cert->derSubject, &anssi_subject)) { andre@0: SECStatus rv; andre@0: rv = SECITEM_CopyItem(NULL, extensions, &permitFranceGovNC); andre@0: return rv; andre@0: } andre@0: PORT_SetError(SEC_ERROR_EXTENSION_NOT_FOUND); andre@0: return SECFailure; andre@0: } andre@0: andre@0: /* Extract the name constraints extension from the CA cert. */ andre@0: SECStatus andre@0: CERT_FindNameConstraintsExten(PLArenaPool *arena, andre@0: CERTCertificate *cert, andre@0: CERTNameConstraints **constraints) andre@0: { andre@0: SECStatus rv = SECSuccess; andre@0: SECItem constraintsExtension; andre@0: void *mark = NULL; andre@0: andre@0: *constraints = NULL; andre@0: andre@0: rv = CERT_FindCertExtension(cert, SEC_OID_X509_NAME_CONSTRAINTS, andre@0: &constraintsExtension); andre@0: if (rv != SECSuccess) { andre@0: if (PORT_GetError() != SEC_ERROR_EXTENSION_NOT_FOUND) { andre@0: return rv; andre@0: } andre@0: rv = getNameExtensionsBuiltIn(cert, &constraintsExtension); andre@0: if (rv != SECSuccess) { andre@0: if (PORT_GetError() == SEC_ERROR_EXTENSION_NOT_FOUND) { andre@0: return SECSuccess; andre@0: } andre@0: return rv; andre@0: } andre@0: } andre@0: andre@0: mark = PORT_ArenaMark(arena); andre@0: andre@0: *constraints = cert_DecodeNameConstraints(arena, &constraintsExtension); andre@0: if (*constraints == NULL) { /* decode failed */ andre@0: rv = SECFailure; andre@0: } andre@0: PORT_Free (constraintsExtension.data); andre@0: andre@0: if (rv == SECFailure) { andre@0: PORT_ArenaRelease(arena, mark); andre@0: } else { andre@0: PORT_ArenaUnmark(arena, mark); andre@0: } andre@0: andre@0: return rv; andre@0: } andre@0: andre@0: /* Verify name against all the constraints relevant to that type of andre@0: ** the name. andre@0: */ andre@0: SECStatus andre@0: CERT_CheckNameSpace(PLArenaPool *arena, andre@0: const CERTNameConstraints *constraints, andre@0: const CERTGeneralName *currentName) andre@0: { andre@0: CERTNameConstraint *matchingConstraints; andre@0: SECStatus rv = SECSuccess; andre@0: andre@0: if (constraints->excluded != NULL) { andre@0: rv = CERT_GetNameConstraintByType(constraints->excluded, andre@0: currentName->type, andre@0: &matchingConstraints, arena); andre@0: if (rv == SECSuccess && matchingConstraints != NULL) { andre@0: rv = cert_CompareNameWithConstraints(currentName, andre@0: matchingConstraints, andre@0: PR_TRUE); andre@0: } andre@0: if (rv != SECSuccess) { andre@0: return(rv); andre@0: } andre@0: } andre@0: andre@0: if (constraints->permited != NULL) { andre@0: rv = CERT_GetNameConstraintByType(constraints->permited, andre@0: currentName->type, andre@0: &matchingConstraints, arena); andre@0: if (rv == SECSuccess && matchingConstraints != NULL) { andre@0: rv = cert_CompareNameWithConstraints(currentName, andre@0: matchingConstraints, andre@0: PR_FALSE); andre@0: } andre@0: if (rv != SECSuccess) { andre@0: return(rv); andre@0: } andre@0: } andre@0: andre@0: return(SECSuccess); andre@0: } andre@0: andre@0: /* Extract the name constraints extension from the CA cert. andre@0: ** Test each and every name in namesList against all the constraints andre@0: ** relevant to that type of name. andre@0: ** Returns NULL in pBadCert for success, if all names are acceptable. andre@0: ** If some name is not acceptable, returns a pointer to the cert that andre@0: ** contained that name. andre@0: */ andre@0: SECStatus andre@0: CERT_CompareNameSpace(CERTCertificate *cert, andre@0: CERTGeneralName *namesList, andre@0: CERTCertificate **certsList, andre@0: PLArenaPool *reqArena, andre@0: CERTCertificate **pBadCert) andre@0: { andre@0: SECStatus rv = SECSuccess; andre@0: CERTNameConstraints *constraints; andre@0: CERTGeneralName *currentName; andre@0: int count = 0; andre@0: CERTCertificate *badCert = NULL; andre@0: andre@0: /* If no names to check, then no names can be bad. */ andre@0: if (!namesList) andre@0: goto done; andre@0: rv = CERT_FindNameConstraintsExten(reqArena, cert, &constraints); andre@0: if (rv != SECSuccess) { andre@0: count = -1; andre@0: goto done; andre@0: } andre@0: andre@0: currentName = namesList; andre@0: do { andre@0: if (constraints){ andre@0: rv = CERT_CheckNameSpace(reqArena, constraints, currentName); andre@0: if (rv != SECSuccess) { andre@0: break; andre@0: } andre@0: } andre@0: currentName = CERT_GetNextGeneralName(currentName); andre@0: count ++; andre@0: } while (currentName != namesList); andre@0: andre@0: done: andre@0: if (rv != SECSuccess) { andre@0: badCert = (count >= 0) ? certsList[count] : cert; andre@0: } andre@0: if (pBadCert) andre@0: *pBadCert = badCert; andre@0: andre@0: return rv; andre@0: } andre@0: andre@0: #if 0 andre@0: /* not exported from shared libs, not used. Turn on if we ever need it. */ andre@0: SECStatus andre@0: CERT_CompareGeneralName(CERTGeneralName *a, CERTGeneralName *b) andre@0: { andre@0: CERTGeneralName *currentA; andre@0: CERTGeneralName *currentB; andre@0: PRBool found; andre@0: andre@0: currentA = a; andre@0: currentB = b; andre@0: if (a != NULL) { andre@0: do { andre@0: if (currentB == NULL) { andre@0: return SECFailure; andre@0: } andre@0: currentB = CERT_GetNextGeneralName(currentB); andre@0: currentA = CERT_GetNextGeneralName(currentA); andre@0: } while (currentA != a); andre@0: } andre@0: if (currentB != b) { andre@0: return SECFailure; andre@0: } andre@0: currentA = a; andre@0: do { andre@0: currentB = b; andre@0: found = PR_FALSE; andre@0: do { andre@0: if (currentB->type == currentA->type) { andre@0: switch (currentB->type) { andre@0: case certDNSName: andre@0: case certEDIPartyName: andre@0: case certIPAddress: andre@0: case certRegisterID: andre@0: case certRFC822Name: andre@0: case certX400Address: andre@0: case certURI: andre@0: if (SECITEM_CompareItem(¤tA->name.other, andre@0: ¤tB->name.other) andre@0: == SECEqual) { andre@0: found = PR_TRUE; andre@0: } andre@0: break; andre@0: case certOtherName: andre@0: if (SECITEM_CompareItem(¤tA->name.OthName.oid, andre@0: ¤tB->name.OthName.oid) andre@0: == SECEqual && andre@0: SECITEM_CompareItem(¤tA->name.OthName.name, andre@0: ¤tB->name.OthName.name) andre@0: == SECEqual) { andre@0: found = PR_TRUE; andre@0: } andre@0: break; andre@0: case certDirectoryName: andre@0: if (CERT_CompareName(¤tA->name.directoryName, andre@0: ¤tB->name.directoryName) andre@0: == SECEqual) { andre@0: found = PR_TRUE; andre@0: } andre@0: } andre@0: andre@0: } andre@0: currentB = CERT_GetNextGeneralName(currentB); andre@0: } while (currentB != b && found != PR_TRUE); andre@0: if (found != PR_TRUE) { andre@0: return SECFailure; andre@0: } andre@0: currentA = CERT_GetNextGeneralName(currentA); andre@0: } while (currentA != a); andre@0: return SECSuccess; andre@0: } andre@0: andre@0: SECStatus andre@0: CERT_CompareGeneralNameLists(CERTGeneralNameList *a, CERTGeneralNameList *b) andre@0: { andre@0: SECStatus rv; andre@0: andre@0: if (a == b) { andre@0: return SECSuccess; andre@0: } andre@0: if (a != NULL && b != NULL) { andre@0: PZ_Lock(a->lock); andre@0: PZ_Lock(b->lock); andre@0: rv = CERT_CompareGeneralName(a->name, b->name); andre@0: PZ_Unlock(a->lock); andre@0: PZ_Unlock(b->lock); andre@0: } else { andre@0: rv = SECFailure; andre@0: } andre@0: return rv; andre@0: } andre@0: #endif andre@0: andre@0: #if 0 andre@0: /* This function is not exported from NSS shared libraries, and is not andre@0: ** used inside of NSS. andre@0: ** XXX it doesn't check for failed allocations. :-( andre@0: */ andre@0: void * andre@0: CERT_GetGeneralNameFromListByType(CERTGeneralNameList *list, andre@0: CERTGeneralNameType type, andre@0: PLArenaPool *arena) andre@0: { andre@0: CERTName *name = NULL; andre@0: SECItem *item = NULL; andre@0: OtherName *other = NULL; andre@0: OtherName *tmpOther = NULL; andre@0: void *data; andre@0: andre@0: PZ_Lock(list->lock); andre@0: data = CERT_GetGeneralNameByType(list->name, type, PR_FALSE); andre@0: if (data != NULL) { andre@0: switch (type) { andre@0: case certDNSName: andre@0: case certEDIPartyName: andre@0: case certIPAddress: andre@0: case certRegisterID: andre@0: case certRFC822Name: andre@0: case certX400Address: andre@0: case certURI: andre@0: if (arena != NULL) { andre@0: item = PORT_ArenaNew(arena, SECItem); andre@0: if (item != NULL) { andre@0: XXX SECITEM_CopyItem(arena, item, (SECItem *) data); andre@0: } andre@0: } else { andre@0: item = SECITEM_DupItem((SECItem *) data); andre@0: } andre@0: PZ_Unlock(list->lock); andre@0: return item; andre@0: case certOtherName: andre@0: other = (OtherName *) data; andre@0: if (arena != NULL) { andre@0: tmpOther = PORT_ArenaNew(arena, OtherName); andre@0: } else { andre@0: tmpOther = PORT_New(OtherName); andre@0: } andre@0: if (tmpOther != NULL) { andre@0: XXX SECITEM_CopyItem(arena, &tmpOther->oid, &other->oid); andre@0: XXX SECITEM_CopyItem(arena, &tmpOther->name, &other->name); andre@0: } andre@0: PZ_Unlock(list->lock); andre@0: return tmpOther; andre@0: case certDirectoryName: andre@0: if (arena) { andre@0: name = PORT_ArenaZNew(list->arena, CERTName); andre@0: if (name) { andre@0: XXX CERT_CopyName(arena, name, (CERTName *) data); andre@0: } andre@0: } andre@0: PZ_Unlock(list->lock); andre@0: return name; andre@0: } andre@0: } andre@0: PZ_Unlock(list->lock); andre@0: return NULL; andre@0: } andre@0: #endif andre@0: andre@0: #if 0 andre@0: /* This function is not exported from NSS shared libraries, and is not andre@0: ** used inside of NSS. andre@0: ** XXX it should NOT be a void function, since it does allocations andre@0: ** that can fail. andre@0: */ andre@0: void andre@0: CERT_AddGeneralNameToList(CERTGeneralNameList *list, andre@0: CERTGeneralNameType type, andre@0: void *data, SECItem *oid) andre@0: { andre@0: CERTGeneralName *name; andre@0: andre@0: if (list != NULL && data != NULL) { andre@0: PZ_Lock(list->lock); andre@0: name = CERT_NewGeneralName(list->arena, type); andre@0: if (!name) andre@0: goto done; andre@0: switch (type) { andre@0: case certDNSName: andre@0: case certEDIPartyName: andre@0: case certIPAddress: andre@0: case certRegisterID: andre@0: case certRFC822Name: andre@0: case certX400Address: andre@0: case certURI: andre@0: XXX SECITEM_CopyItem(list->arena, &name->name.other, (SECItem *)data); andre@0: break; andre@0: case certOtherName: andre@0: XXX SECITEM_CopyItem(list->arena, &name->name.OthName.name, andre@0: (SECItem *) data); andre@0: XXX SECITEM_CopyItem(list->arena, &name->name.OthName.oid, andre@0: oid); andre@0: break; andre@0: case certDirectoryName: andre@0: XXX CERT_CopyName(list->arena, &name->name.directoryName, andre@0: (CERTName *) data); andre@0: break; andre@0: } andre@0: list->name = cert_CombineNamesLists(list->name, name); andre@0: list->len++; andre@0: done: andre@0: PZ_Unlock(list->lock); andre@0: } andre@0: return; andre@0: } andre@0: #endif