summaryrefslogtreecommitdiff
path: root/mozilla/security/nss/lib/certdb/genname.c
diff options
context:
space:
mode:
Diffstat (limited to 'mozilla/security/nss/lib/certdb/genname.c')
-rw-r--r--mozilla/security/nss/lib/certdb/genname.c1998
1 files changed, 1998 insertions, 0 deletions
diff --git a/mozilla/security/nss/lib/certdb/genname.c b/mozilla/security/nss/lib/certdb/genname.c
new file mode 100644
index 0000000..a7cebd1
--- /dev/null
+++ b/mozilla/security/nss/lib/certdb/genname.c
@@ -0,0 +1,1998 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1994-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#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 CERTOtherName2Template[] = {
+ { SEC_ASN1_SEQUENCE | SEC_ASN1_CONTEXT_SPECIFIC | 0 ,
+ 0, NULL, sizeof(CERTGeneralName) },
+ { SEC_ASN1_OBJECT_ID,
+ offsetof(CERTGeneralName, name.OthName) + offsetof(OtherName, oid) },
+ { SEC_ASN1_ANY,
+ offsetof(CERTGeneralName, name.OthName) + offsetof(OtherName, name) },
+ { 0, }
+};
+
+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) }
+};
+
+
+
+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(PRArenaPool *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) {
+ PRArenaPool *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, PRArenaPool *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(PRArenaPool *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(PRArenaPool *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 (PRArenaPool *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,
+ PRArenaPool *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,
+ PRArenaPool *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,
+ PRArenaPool *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(PRArenaPool *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;
+}
+
+CERTNameConstraint *
+cert_DecodeNameConstraintSubTree(PRArenaPool *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 (last == NULL) {
+ first = last = current;
+ }
+ current->l.prev = &(last->l);
+ current->l.next = last->l.next;
+ last->l.next = &(current->l);
+ i++;
+ }
+ first->l.prev = &(current->l);
+ /* TODO: unmark arena */
+ return first;
+loser:
+ /* TODO: release arena back to mark */
+ return NULL;
+}
+
+CERTNameConstraints *
+cert_DecodeNameConstraints(PRArenaPool *reqArena,
+ 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(PRArenaPool *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(PRArenaPool *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,
+ PRArenaPool *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 *)&current->name.other; /* SECItem * */
+
+ case certOtherName:
+ return (void *)&current->name.OthName; /* OthName * */
+
+ case certDirectoryName:
+ return derFormat
+ ? (void *)&current->derDirectoryName /* SECItem * */
+ : (void *)&current->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, PRArenaPool *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(CERTCertificate *cert, PRArenaPool *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(CERTGeneralName *name,
+ CERTNameConstraint *constraints,
+ PRBool excluded)
+{
+ SECStatus rv = SECSuccess;
+ SECStatus matched = SECFailure;
+ 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,
+ &current->name.name.other);
+ break;
+
+ case certRFC822Name:
+ matched = compareRFC822N2C(&name->name.other,
+ &current->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, &current->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,
+ &current->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,
+ &current->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(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, &current->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(&current->l);
+ } else {
+ PR_INSERT_BEFORE(&current->l, &first->l);
+ }
+
+done:
+ if (rv == SECFailure) {
+ PORT_ArenaRelease(arena, mark);
+ } else {
+ PORT_ArenaUnmark(arena, mark);
+ }
+ return rv;
+}
+
+/* Extract the name constraints extension from the CA cert. */
+SECStatus
+CERT_FindNameConstraintsExten(PRArenaPool *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) {
+ rv = 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(PRArenaPool *arena,
+ CERTNameConstraints *constraints,
+ 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,
+ PRArenaPool *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;
+}
+
+/* Search the cert for an X509_SUBJECT_ALT_NAME extension.
+** ASN1 Decode it into a list of alternate names.
+** Search the list of alternate names for one with the NETSCAPE_NICKNAME OID.
+** ASN1 Decode that name. Turn the result into a zString.
+** Look for duplicate nickname already in the certdb.
+** If one is found, create a nickname string that is not a duplicate.
+*/
+char *
+CERT_GetNickName(CERTCertificate *cert,
+ CERTCertDBHandle *handle,
+ PRArenaPool *nicknameArena)
+{
+ CERTGeneralName *current;
+ CERTGeneralName *names;
+ char *nickname = NULL;
+ char *returnName = NULL;
+ char *basename = NULL;
+ PRArenaPool *arena = NULL;
+ CERTCertificate *tmpcert;
+ SECStatus rv;
+ int count;
+ int found = 0;
+ SECItem altNameExtension;
+ SECItem nick;
+
+ if (handle == NULL) {
+ handle = CERT_GetDefaultCertDB();
+ }
+ altNameExtension.data = NULL;
+ rv = CERT_FindCertExtension(cert, SEC_OID_X509_SUBJECT_ALT_NAME,
+ &altNameExtension);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (arena == NULL) {
+ goto loser;
+ }
+ names = CERT_DecodeAltNameExtension(arena, &altNameExtension);
+ if (names == NULL) {
+ goto loser;
+ }
+ current = names;
+ do {
+ if (current->type == certOtherName &&
+ SECOID_FindOIDTag(&current->name.OthName.oid) ==
+ SEC_OID_NETSCAPE_NICKNAME) {
+ found = 1;
+ break;
+ }
+ current = CERT_GetNextGeneralName(current);
+ } while (current != names);
+ if (!found)
+ goto loser;
+
+ rv = SEC_QuickDERDecodeItem(arena, &nick,
+ SEC_ASN1_GET(SEC_IA5StringTemplate),
+ &current->name.OthName.name);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ /* make a null terminated string out of nick, with room enough at
+ ** the end to add on a number of up to 21 digits in length, (a signed
+ ** 64-bit number in decimal) plus a space and a "#".
+ */
+ nickname = (char*)PORT_ZAlloc(nick.len + 24);
+ if (!nickname)
+ goto loser;
+ PORT_Strncpy(nickname, (char *)nick.data, nick.len);
+
+ /* Don't let this cert's nickname duplicate one already in the DB.
+ ** If it does, create a variant of the nickname that doesn't.
+ */
+ count = 0;
+ while ((tmpcert = CERT_FindCertByNickname(handle, nickname)) != NULL) {
+ CERT_DestroyCertificate(tmpcert);
+ if (!basename) {
+ basename = PORT_Strdup(nickname);
+ if (!basename)
+ goto loser;
+ }
+ count++;
+ sprintf(nickname, "%s #%d", basename, count);
+ }
+
+ /* success */
+ if (nicknameArena) {
+ returnName = PORT_ArenaStrdup(nicknameArena, nickname);
+ } else {
+ returnName = nickname;
+ nickname = NULL;
+ }
+loser:
+ if (arena != NULL)
+ PORT_FreeArena(arena, PR_FALSE);
+ if (nickname)
+ PORT_Free(nickname);
+ if (basename)
+ PORT_Free(basename);
+ if (altNameExtension.data)
+ PORT_Free(altNameExtension.data);
+ return returnName;
+}
+
+#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(&currentA->name.other,
+ &currentB->name.other)
+ == SECEqual) {
+ found = PR_TRUE;
+ }
+ break;
+ case certOtherName:
+ if (SECITEM_CompareItem(&currentA->name.OthName.oid,
+ &currentB->name.OthName.oid)
+ == SECEqual &&
+ SECITEM_CompareItem(&currentA->name.OthName.name,
+ &currentB->name.OthName.name)
+ == SECEqual) {
+ found = PR_TRUE;
+ }
+ break;
+ case certDirectoryName:
+ if (CERT_CompareName(&currentA->name.directoryName,
+ &currentB->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,
+ PRArenaPool *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