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