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