summaryrefslogtreecommitdiff
path: root/security/nss/lib/softoken/legacydb
diff options
context:
space:
mode:
authorrrelyea%redhat.com <devnull@localhost>2007-06-13 00:24:57 +0000
committerrrelyea%redhat.com <devnull@localhost>2007-06-13 00:24:57 +0000
commitbc02abfdccbf6e07ce9036c85c3371c4bac5a5bc (patch)
tree58cb311a457ccceb1b291ada3cd9983b608c6e76 /security/nss/lib/softoken/legacydb
parent892018c57ede2a3b4d8d00a41fc583316a71d4e6 (diff)
downloadnss-hg-bc02abfdccbf6e07ce9036c85c3371c4bac5a5bc.tar.gz
Bug 217538 Add shared database to nss.
core Checking.
Diffstat (limited to 'security/nss/lib/softoken/legacydb')
-rw-r--r--security/nss/lib/softoken/legacydb/Makefile80
-rw-r--r--security/nss/lib/softoken/legacydb/cdbhdl.h85
-rw-r--r--security/nss/lib/softoken/legacydb/config.mk102
-rw-r--r--security/nss/lib/softoken/legacydb/dbmshim.c647
-rw-r--r--security/nss/lib/softoken/legacydb/keydb.c2270
-rw-r--r--security/nss/lib/softoken/legacydb/keydbi.h86
-rw-r--r--security/nss/lib/softoken/legacydb/legacydb.def64
-rw-r--r--security/nss/lib/softoken/legacydb/lgattr.c1748
-rw-r--r--security/nss/lib/softoken/legacydb/lgcreate.c955
-rw-r--r--security/nss/lib/softoken/legacydb/lgdb.h197
-rw-r--r--security/nss/lib/softoken/legacydb/lgdestroy.c144
-rw-r--r--security/nss/lib/softoken/legacydb/lgfind.c941
-rw-r--r--security/nss/lib/softoken/legacydb/lginit.c644
-rw-r--r--security/nss/lib/softoken/legacydb/lgutil.c424
-rw-r--r--security/nss/lib/softoken/legacydb/lowcert.c824
-rw-r--r--security/nss/lib/softoken/legacydb/lowkey.c462
-rw-r--r--security/nss/lib/softoken/legacydb/lowkeyi.h198
-rw-r--r--security/nss/lib/softoken/legacydb/lowkeyti.h161
-rw-r--r--security/nss/lib/softoken/legacydb/manifest.mn68
-rw-r--r--security/nss/lib/softoken/legacydb/pcert.h261
-rw-r--r--security/nss/lib/softoken/legacydb/pcertdb.c5363
-rw-r--r--security/nss/lib/softoken/legacydb/pcertt.h450
-rw-r--r--security/nss/lib/softoken/legacydb/pk11db.c773
23 files changed, 16947 insertions, 0 deletions
diff --git a/security/nss/lib/softoken/legacydb/Makefile b/security/nss/lib/softoken/legacydb/Makefile
new file mode 100644
index 000000000..a41aa72c6
--- /dev/null
+++ b/security/nss/lib/softoken/legacydb/Makefile
@@ -0,0 +1,80 @@
+#! gmake
+#
+# ***** 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 *****
+
+#######################################################################
+# (1) Include initial platform-independent assignments (MANDATORY). #
+#######################################################################
+
+include manifest.mn
+
+#######################################################################
+# (2) Include "global" configuration information. (OPTIONAL) #
+#######################################################################
+
+include $(CORE_DEPTH)/coreconf/config.mk
+
+#######################################################################
+# (3) Include "component" configuration information. (OPTIONAL) #
+#######################################################################
+
+
+
+#######################################################################
+# (4) Include "local" platform-dependent assignments (OPTIONAL). #
+#######################################################################
+
+include config.mk
+
+#######################################################################
+# (5) Execute "global" rules. (OPTIONAL) #
+#######################################################################
+
+include $(CORE_DEPTH)/coreconf/rules.mk
+
+#######################################################################
+# (6) Execute "component" rules. (OPTIONAL) #
+#######################################################################
+
+
+
+#######################################################################
+# (7) Execute "local" rules. (OPTIONAL). #
+#######################################################################
+
+export:: private_export
+
diff --git a/security/nss/lib/softoken/legacydb/cdbhdl.h b/security/nss/lib/softoken/legacydb/cdbhdl.h
new file mode 100644
index 000000000..aec617b73
--- /dev/null
+++ b/security/nss/lib/softoken/legacydb/cdbhdl.h
@@ -0,0 +1,85 @@
+/* ***** 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 ***** */
+/*
+ * cdbhdl.h - certificate database handle
+ * private to the certdb module
+ *
+ * $Id$
+ */
+#ifndef _CDBHDL_H_
+#define _CDBHDL_H_
+
+#include "nspr.h"
+#include "mcom_db.h"
+#include "pcertt.h"
+#include "prtypes.h"
+
+/*
+ * Handle structure for open certificate databases
+ */
+struct NSSLOWCERTCertDBHandleStr {
+ DB *permCertDB;
+ PZMonitor *dbMon;
+ PRBool dbVerify;
+ PRInt32 ref; /* reference count */
+};
+
+#ifdef DBM_USING_NSPR
+#define NO_RDONLY PR_RDONLY
+#define NO_RDWR PR_RDWR
+#define NO_CREATE (PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE)
+#else
+#define NO_RDONLY O_RDONLY
+#define NO_RDWR O_RDWR
+#define NO_CREATE (O_RDWR | O_CREAT | O_TRUNC)
+#endif
+
+typedef DB * (*rdbfunc)(const char *appName, const char *prefix,
+ const char *type, int flags);
+typedef int (*rdbstatusfunc)(void);
+
+#define RDB_FAIL 1
+#define RDB_RETRY 2
+
+DB * rdbopen(const char *appName, const char *prefix,
+ const char *type, int flags, int *status);
+
+DB *dbsopen (const char *dbname , int flags, int mode, DBTYPE type,
+ const void * appData);
+SECStatus db_Copy(DB *dest,DB *src);
+int db_InitComplete(DB *db);
+
+#endif
diff --git a/security/nss/lib/softoken/legacydb/config.mk b/security/nss/lib/softoken/legacydb/config.mk
new file mode 100644
index 000000000..815f25785
--- /dev/null
+++ b/security/nss/lib/softoken/legacydb/config.mk
@@ -0,0 +1,102 @@
+#
+# ***** 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 *****
+
+# $(PROGRAM) has explicit dependencies on $(EXTRA_LIBS)
+CRYPTOLIB=$(DIST)/lib/$(LIB_PREFIX)freebl.$(LIB_SUFFIX)
+CRYPTODIR=../freebl
+ifdef MOZILLA_SECURITY_BUILD
+ CRYPTOLIB=$(DIST)/lib/$(LIB_PREFIX)crypto.$(LIB_SUFFIX)
+ CRYPTODIR=../crypto
+endif
+
+EXTRA_LIBS += \
+ $(CRYPTOLIB) \
+ $(DIST)/lib/$(LIB_PREFIX)secutil.$(LIB_SUFFIX)
+ $(DIST)/lib/$(LIB_PREFIX)dbm.$(LIB_SUFFIX) \
+ $(NULL)
+
+# can't do this in manifest.mn because OS_TARGET isn't defined there.
+ifeq (,$(filter-out WIN%,$(OS_TARGET)))
+
+# don't want the 32 in the shared library name
+SHARED_LIBRARY = $(OBJDIR)/$(DLL_PREFIX)$(LIBRARY_NAME)$(LIBRARY_VERSION).$(DLL_SUFFIX)
+IMPORT_LIBRARY = $(OBJDIR)/$(IMPORT_LIB_PREFIX)$(LIBRARY_NAME)$(LIBRARY_VERSION)$(IMPORT_LIB_SUFFIX)
+
+RES = $(OBJDIR)/$(LIBRARY_NAME).res
+RESNAME = $(LIBRARY_NAME).rc
+
+ifdef NS_USE_GCC
+EXTRA_SHARED_LIBS += \
+ -L$(NSPR_LIB_DIR) \
+ -lplc4 \
+ -lplds4 \
+ -lnspr4 \
+ $(NULL)
+else # ! NS_USE_GCC
+
+EXTRA_SHARED_LIBS += \
+ $(NSPR_LIB_DIR)/$(NSPR31_LIB_PREFIX)plc4.lib \
+ $(NSPR_LIB_DIR)/$(NSPR31_LIB_PREFIX)plds4.lib \
+ $(NSPR_LIB_DIR)/$(NSPR31_LIB_PREFIX)nspr4.lib \
+ $(NULL)
+endif # NS_USE_GCC
+
+else
+
+# $(PROGRAM) has NO explicit dependencies on $(EXTRA_SHARED_LIBS)
+# $(EXTRA_SHARED_LIBS) come before $(OS_LIBS), except on AIX.
+EXTRA_SHARED_LIBS += \
+ -L$(NSPR_LIB_DIR) \
+ -lplc4 \
+ -lplds4 \
+ -lnspr4 \
+ $(NULL)
+endif
+
+ifeq ($(OS_TARGET),SunOS)
+# The -R '$ORIGIN' linker option instructs this library to search for its
+# dependencies in the same directory where it resides.
+MKSHLIB += -R '$$ORIGIN'
+OS_LIBS += -lbsm
+endif
+
+ifeq ($(OS_TARGET),WINCE)
+DEFINES += -DDBM_USING_NSPR
+endif
+
+# indicates dependency on freebl static lib
+$(SHARED_LIBRARY): $(CRYPTOLIB)
diff --git a/security/nss/lib/softoken/legacydb/dbmshim.c b/security/nss/lib/softoken/legacydb/dbmshim.c
new file mode 100644
index 000000000..9d512ac41
--- /dev/null
+++ b/security/nss/lib/softoken/legacydb/dbmshim.c
@@ -0,0 +1,647 @@
+/* ***** 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 ***** */
+
+/*
+ * Berkeley DB 1.85 Shim code to handle blobs.
+ *
+ * $Id$
+ */
+#include "mcom_db.h"
+#include "secitem.h"
+#include "nssb64.h"
+#include "blapi.h"
+#include "secerr.h"
+
+#include "lgdb.h"
+
+/*
+ * Blob block:
+ * Byte 0 CERTDB Version -+ -+
+ * Byte 1 certDBEntryTypeBlob | BLOB_HEAD_LEN |
+ * Byte 2 flags (always '0'); | |
+ * Byte 3 reserved (always '0'); -+ |
+ * Byte 4 LSB length | <--BLOB_LENGTH_START | BLOB_BUF_LEN
+ * Byte 5 . | |
+ * Byte 6 . | BLOB_LENGTH_LEN |
+ * Byte 7 MSB length | |
+ * Byte 8 blob_filename -+ -+ <-- BLOB_NAME_START |
+ * Byte 9 . | BLOB_NAME_LEN |
+ * . . | |
+ * Byte 37 . -+ -+
+ */
+#define DBS_BLOCK_SIZE (16*1024) /* 16 k */
+#define DBS_MAX_ENTRY_SIZE (DBS_BLOCK_SIZE - (2048)) /* 14 k */
+#define DBS_CACHE_SIZE DBS_BLOCK_SIZE*8
+#define ROUNDDIV(x,y) (x+(y-1))/y
+#define BLOB_HEAD_LEN 4
+#define BLOB_LENGTH_START BLOB_HEAD_LEN
+#define BLOB_LENGTH_LEN 4
+#define BLOB_NAME_START BLOB_LENGTH_START+BLOB_LENGTH_LEN
+#define BLOB_NAME_LEN 1+ROUNDDIV(SHA1_LENGTH,3)*4+1
+#define BLOB_BUF_LEN BLOB_HEAD_LEN+BLOB_LENGTH_LEN+BLOB_NAME_LEN
+
+/* a Shim data structure. This data structure has a db built into it. */
+typedef struct DBSStr DBS;
+
+struct DBSStr {
+ DB db;
+ char *blobdir;
+ int mode;
+ PRBool readOnly;
+ PRFileMap *dbs_mapfile;
+ unsigned char *dbs_addr;
+ PRUint32 dbs_len;
+ char staticBlobArea[BLOB_BUF_LEN];
+};
+
+
+
+/*
+ * return true if the Datablock contains a blobtype
+ */
+static PRBool
+dbs_IsBlob(DBT *blobData)
+{
+ unsigned char *addr = (unsigned char *)blobData->data;
+ if (blobData->size < BLOB_BUF_LEN) {
+ return PR_FALSE;
+ }
+ return addr && ((certDBEntryType) addr[1] == certDBEntryTypeBlob);
+}
+
+/*
+ * extract the filename in the blob of the real data set.
+ * This value is not malloced (does not need to be freed by the caller.
+ */
+static const char *
+dbs_getBlobFileName(DBT *blobData)
+{
+ char *addr = (char *)blobData->data;
+
+ return &addr[BLOB_NAME_START];
+}
+
+/*
+ * extract the size of the actual blob from the blob record
+ */
+static PRUint32
+dbs_getBlobSize(DBT *blobData)
+{
+ unsigned char *addr = (unsigned char *)blobData->data;
+
+ return (PRUint32)(addr[BLOB_LENGTH_START+3] << 24) |
+ (addr[BLOB_LENGTH_START+2] << 16) |
+ (addr[BLOB_LENGTH_START+1] << 8) |
+ addr[BLOB_LENGTH_START];
+}
+
+
+/* We are using base64 data for the filename, but base64 data can include a
+ * '/' which is interpreted as a path separator on many platforms. Replace it
+ * with an inocuous '-'. We don't need to convert back because we never actual
+ * decode the filename.
+ */
+
+static void
+dbs_replaceSlash(char *cp, int len)
+{
+ while (len--) {
+ if (*cp == '/') *cp = '-';
+ cp++;
+ }
+}
+
+/*
+ * create a blob record from a key, data and return it in blobData.
+ * NOTE: The data element is static data (keeping with the dbm model).
+ */
+static void
+dbs_mkBlob(DBS *dbsp,const DBT *key, const DBT *data, DBT *blobData)
+{
+ unsigned char sha1_data[SHA1_LENGTH];
+ char *b = dbsp->staticBlobArea;
+ PRUint32 length = data->size;
+ SECItem sha1Item;
+
+ b[0] = CERT_DB_FILE_VERSION; /* certdb version number */
+ b[1] = (char) certDBEntryTypeBlob; /* type */
+ b[2] = 0; /* flags */
+ b[3] = 0; /* reserved */
+ b[BLOB_LENGTH_START] = length & 0xff;
+ b[BLOB_LENGTH_START+1] = (length >> 8) & 0xff;
+ b[BLOB_LENGTH_START+2] = (length >> 16) & 0xff;
+ b[BLOB_LENGTH_START+3] = (length >> 24) & 0xff;
+ sha1Item.data = sha1_data;
+ sha1Item.len = SHA1_LENGTH;
+ SHA1_HashBuf(sha1_data,key->data,key->size);
+ b[BLOB_NAME_START]='b'; /* Make sure we start with a alpha */
+ NSSBase64_EncodeItem(NULL,&b[BLOB_NAME_START+1],BLOB_NAME_LEN-1,&sha1Item);
+ b[BLOB_BUF_LEN-1] = 0;
+ dbs_replaceSlash(&b[BLOB_NAME_START+1],BLOB_NAME_LEN-1);
+ blobData->data = b;
+ blobData->size = BLOB_BUF_LEN;
+ return;
+}
+
+
+/*
+ * construct a path to the actual blob. The string returned must be
+ * freed by the caller with PR_smprintf_free.
+ *
+ * Note: this file does lots of consistancy checks on the DBT. The
+ * routines that call this depend on these checks, so they don't worry
+ * about them (success of this routine implies a good blobdata record).
+ */
+static char *
+dbs_getBlobFilePath(char *blobdir,DBT *blobData)
+{
+ const char *name;
+
+ if (blobdir == NULL) {
+ PR_SetError(SEC_ERROR_BAD_DATABASE,0);
+ return NULL;
+ }
+ if (!dbs_IsBlob(blobData)) {
+ PR_SetError(SEC_ERROR_BAD_DATABASE,0);
+ return NULL;
+ }
+ name = dbs_getBlobFileName(blobData);
+ if (!name || *name == 0) {
+ PR_SetError(SEC_ERROR_BAD_DATABASE,0);
+ return NULL;
+ }
+ return PR_smprintf("%s" PATH_SEPARATOR "%s", blobdir, name);
+}
+
+/*
+ * Delete a blob file pointed to by the blob record.
+ */
+static void
+dbs_removeBlob(DBS *dbsp, DBT *blobData)
+{
+ char *file;
+
+ file = dbs_getBlobFilePath(dbsp->blobdir, blobData);
+ if (!file) {
+ return;
+ }
+ PR_Delete(file);
+ PR_smprintf_free(file);
+}
+
+/*
+ * Directory modes are slightly different, the 'x' bit needs to be on to
+ * access them. Copy all the read bits to 'x' bits
+ */
+static int
+dbs_DirMode(int mode)
+{
+ int x_bits = (mode >> 2) & 0111;
+ return mode | x_bits;
+}
+
+/*
+ * write a data blob to it's file. blobdData is the blob record that will be
+ * stored in the database. data is the actual data to go out on disk.
+ */
+static int
+dbs_writeBlob(DBS *dbsp, int mode, DBT *blobData, const DBT *data)
+{
+ char *file = NULL;
+ PRFileDesc *filed;
+ PRStatus status;
+ int len;
+ int error = 0;
+
+ file = dbs_getBlobFilePath(dbsp->blobdir, blobData);
+ if (!file) {
+ goto loser;
+ }
+ if (PR_Access(dbsp->blobdir, PR_ACCESS_EXISTS) != PR_SUCCESS) {
+ status = PR_MkDir(dbsp->blobdir,dbs_DirMode(mode));
+ if (status != PR_SUCCESS) {
+ goto loser;
+ }
+ }
+ filed = PR_OpenFile(file,PR_CREATE_FILE|PR_TRUNCATE|PR_WRONLY, mode);
+ if (filed == NULL) {
+ error = PR_GetError();
+ goto loser;
+ }
+ len = PR_Write(filed,data->data,data->size);
+ error = PR_GetError();
+ PR_Close(filed);
+ if (len < (int)data->size) {
+ goto loser;
+ }
+ PR_smprintf_free(file);
+ return 0;
+
+loser:
+ if (file) {
+ PR_Delete(file);
+ PR_smprintf_free(file);
+ }
+ /* don't let close or delete reset the error */
+ PR_SetError(error,0);
+ return -1;
+}
+
+
+/*
+ * we need to keep a address map in memory between calls to DBM.
+ * remember what we have mapped can close it when we get another dbm
+ * call.
+ *
+ * NOTE: Not all platforms support mapped files. This code is designed to
+ * detect this at runtime. If map files aren't supported the OS will indicate
+ * this by failing the PR_Memmap call. In this case we emulate mapped files
+ * by just reading in the file into regular memory. We signal this state by
+ * making dbs_mapfile NULL and dbs_addr non-NULL.
+ */
+
+static void
+dbs_freemap(DBS *dbsp)
+{
+ if (dbsp->dbs_mapfile) {
+ PR_MemUnmap(dbsp->dbs_addr,dbsp->dbs_len);
+ PR_CloseFileMap(dbsp->dbs_mapfile);
+ dbsp->dbs_mapfile = NULL;
+ dbsp->dbs_addr = NULL;
+ dbsp->dbs_len = 0;
+ } else if (dbsp->dbs_addr) {
+ PORT_Free(dbsp->dbs_addr);
+ dbsp->dbs_addr = NULL;
+ dbsp->dbs_len = 0;
+ }
+ return;
+}
+
+static void
+dbs_setmap(DBS *dbsp, PRFileMap *mapfile, unsigned char *addr, PRUint32 len)
+{
+ dbsp->dbs_mapfile = mapfile;
+ dbsp->dbs_addr = addr;
+ dbsp->dbs_len = len;
+}
+
+/*
+ * platforms that cannot map the file need to read it into a temp buffer.
+ */
+static unsigned char *
+dbs_EmulateMap(PRFileDesc *filed, int len)
+{
+ unsigned char *addr;
+ PRInt32 dataRead;
+
+ addr = PORT_Alloc(len);
+ if (addr == NULL) {
+ return NULL;
+ }
+
+ dataRead = PR_Read(filed,addr,len);
+ if (dataRead != len) {
+ PORT_Free(addr);
+ if (dataRead > 0) {
+ /* PR_Read didn't set an error, we need to */
+ PR_SetError(SEC_ERROR_BAD_DATABASE,0);
+ }
+ return NULL;
+ }
+
+ return addr;
+}
+
+
+/*
+ * pull a database record off the disk
+ * data points to the blob record on input and the real record (if we could
+ * read it) on output. if there is an error data is not modified.
+ */
+static int
+dbs_readBlob(DBS *dbsp, DBT *data)
+{
+ char *file = NULL;
+ PRFileDesc *filed = NULL;
+ PRFileMap *mapfile = NULL;
+ unsigned char *addr = NULL;
+ int error;
+ int len = -1;
+
+ file = dbs_getBlobFilePath(dbsp->blobdir, data);
+ if (!file) {
+ goto loser;
+ }
+ filed = PR_OpenFile(file,PR_RDONLY,0);
+ PR_smprintf_free(file); file = NULL;
+ if (filed == NULL) {
+ goto loser;
+ }
+
+ len = dbs_getBlobSize(data);
+ mapfile = PR_CreateFileMap(filed, len, PR_PROT_READONLY);
+ if (mapfile == NULL) {
+ /* USE PR_GetError instead of PORT_GetError here
+ * because we are getting the error from PR_xxx
+ * function */
+ if (PR_GetError() != PR_NOT_IMPLEMENTED_ERROR) {
+ goto loser;
+ }
+ addr = dbs_EmulateMap(filed, len);
+ } else {
+ addr = PR_MemMap(mapfile, 0, len);
+ }
+ if (addr == NULL) {
+ goto loser;
+ }
+ PR_Close(filed);
+ dbs_setmap(dbsp,mapfile,addr,len);
+
+ data->data = addr;
+ data->size = len;
+ return 0;
+
+loser:
+ /* preserve the error code */
+ error = PR_GetError();
+ if (mapfile) {
+ PR_CloseFileMap(mapfile);
+ }
+ if (filed) {
+ PR_Close(filed);
+ }
+ PR_SetError(error,0);
+ return -1;
+}
+
+/*
+ * actual DBM shims
+ */
+static int
+dbs_get(const DB *dbs, const DBT *key, DBT *data, unsigned int flags)
+{
+ int ret;
+ DBS *dbsp = (DBS *)dbs;
+ DB *db = (DB *)dbs->internal;
+
+
+ dbs_freemap(dbsp);
+
+ ret = (* db->get)(db, key, data, flags);
+ if ((ret == 0) && dbs_IsBlob(data)) {
+ ret = dbs_readBlob(dbsp,data);
+ }
+
+ return(ret);
+}
+
+static int
+dbs_put(const DB *dbs, DBT *key, const DBT *data, unsigned int flags)
+{
+ DBT blob;
+ int ret = 0;
+ DBS *dbsp = (DBS *)dbs;
+ DB *db = (DB *)dbs->internal;
+
+ dbs_freemap(dbsp);
+
+ /* If the db is readonly, just pass the data down to rdb and let it fail */
+ if (!dbsp->readOnly) {
+ DBT oldData;
+ int ret1;
+
+ /* make sure the current record is deleted if it's a blob */
+ ret1 = (*db->get)(db,key,&oldData,0);
+ if ((ret1 == 0) && flags == R_NOOVERWRITE) {
+ /* let DBM return the error to maintain consistancy */
+ return (* db->put)(db, key, data, flags);
+ }
+ if ((ret1 == 0) && dbs_IsBlob(&oldData)) {
+ dbs_removeBlob(dbsp, &oldData);
+ }
+
+ if (data->size > DBS_MAX_ENTRY_SIZE) {
+ dbs_mkBlob(dbsp,key,data,&blob);
+ ret = dbs_writeBlob(dbsp, dbsp->mode, &blob, data);
+ data = &blob;
+ }
+ }
+
+ if (ret == 0) {
+ ret = (* db->put)(db, key, data, flags);
+ }
+ return(ret);
+}
+
+static int
+dbs_sync(const DB *dbs, unsigned int flags)
+{
+ DB *db = (DB *)dbs->internal;
+ DBS *dbsp = (DBS *)dbs;
+
+ dbs_freemap(dbsp);
+
+ return (* db->sync)(db, flags);
+}
+
+static int
+dbs_del(const DB *dbs, const DBT *key, unsigned int flags)
+{
+ int ret;
+ DBS *dbsp = (DBS *)dbs;
+ DB *db = (DB *)dbs->internal;
+
+ dbs_freemap(dbsp);
+
+ if (!dbsp->readOnly) {
+ DBT oldData;
+ ret = (*db->get)(db,key,&oldData,0);
+ if ((ret == 0) && dbs_IsBlob(&oldData)) {
+ dbs_removeBlob(dbsp,&oldData);
+ }
+ }
+
+ return (* db->del)(db, key, flags);
+}
+
+static int
+dbs_seq(const DB *dbs, DBT *key, DBT *data, unsigned int flags)
+{
+ int ret;
+ DBS *dbsp = (DBS *)dbs;
+ DB *db = (DB *)dbs->internal;
+
+ dbs_freemap(dbsp);
+
+ ret = (* db->seq)(db, key, data, flags);
+ if ((ret == 0) && dbs_IsBlob(data)) {
+ /* don't return a blob read as an error so traversals keep going */
+ (void) dbs_readBlob(dbsp,data);
+ }
+
+ return(ret);
+}
+
+static int
+dbs_close(DB *dbs)
+{
+ DBS *dbsp = (DBS *)dbs;
+ DB *db = (DB *)dbs->internal;
+ int ret;
+
+ dbs_freemap(dbsp);
+ ret = (* db->close)(db);
+ PORT_Free(dbsp->blobdir);
+ PORT_Free(dbsp);
+ return ret;
+}
+
+static int
+dbs_fd(const DB *dbs)
+{
+ DB *db = (DB *)dbs->internal;
+
+ return (* db->fd)(db);
+}
+
+/*
+ * the naming convention we use is
+ * change the .xxx into .dir. (for nss it's always .db);
+ * if no .extension exists or is equal to .dir, add a .dir
+ * the returned data must be freed.
+ */
+#define DIRSUFFIX ".dir"
+static char *
+dbs_mkBlobDirName(const char *dbname)
+{
+ int dbname_len = PORT_Strlen(dbname);
+ int dbname_end = dbname_len;
+ const char *cp;
+ char *blobDir = NULL;
+
+ /* scan back from the end looking for either a directory separator, a '.',
+ * or the end of the string. NOTE: Windows should check for both separators
+ * here. For now this is safe because we know NSS always uses a '.'
+ */
+ for (cp = &dbname[dbname_len];
+ (cp > dbname) && (*cp != '.') && (*cp != *PATH_SEPARATOR) ;
+ cp--)
+ /* Empty */ ;
+ if (*cp == '.') {
+ dbname_end = cp - dbname;
+ if (PORT_Strcmp(cp,DIRSUFFIX) == 0) {
+ dbname_end = dbname_len;
+ }
+ }
+ blobDir = PORT_ZAlloc(dbname_end+sizeof(DIRSUFFIX));
+ if (blobDir == NULL) {
+ return NULL;
+ }
+ PORT_Memcpy(blobDir,dbname,dbname_end);
+ PORT_Memcpy(&blobDir[dbname_end],DIRSUFFIX,sizeof(DIRSUFFIX));
+ return blobDir;
+}
+
+#define DBM_DEFAULT 0
+static const HASHINFO dbs_hashInfo = {
+ DBS_BLOCK_SIZE, /* bucket size, must be greater than = to
+ * or maximum entry size (+ header)
+ * we allow before blobing */
+ DBM_DEFAULT, /* Fill Factor */
+ DBM_DEFAULT, /* number of elements */
+ DBS_CACHE_SIZE, /* cache size */
+ DBM_DEFAULT, /* hash function */
+ DBM_DEFAULT, /* byte order */
+};
+
+/*
+ * the open function. NOTE: this is the only exposed function in this file.
+ * everything else is called through the function table pointer.
+ */
+DB *
+dbsopen(const char *dbname, int flags, int mode, DBTYPE type,
+ const void *userData)
+{
+ DB *db = NULL,*dbs = NULL;
+ DBS *dbsp = NULL;
+
+ /* NOTE: we are overriding userData with dbs_hashInfo. since all known
+ * callers pass 0, this is ok, otherwise we should merge the two */
+
+ dbsp = (DBS *)PORT_ZAlloc(sizeof(DBS));
+ if (!dbsp) {
+ return NULL;
+ }
+ dbs = &dbsp->db;
+
+ dbsp->blobdir=dbs_mkBlobDirName(dbname);
+ if (dbsp->blobdir == NULL) {
+ goto loser;
+ }
+ dbsp->mode = mode;
+ dbsp->readOnly = (PRBool)(flags == NO_RDONLY);
+ dbsp->dbs_mapfile = NULL;
+ dbsp->dbs_addr = NULL;
+ dbsp->dbs_len = 0;
+
+ /* the real dbm call */
+ db = dbopen(dbname, flags, mode, type, &dbs_hashInfo);
+ if (db == NULL) {
+ goto loser;
+ }
+ dbs->internal = (void *) db;
+ dbs->type = type;
+ dbs->close = dbs_close;
+ dbs->get = dbs_get;
+ dbs->del = dbs_del;
+ dbs->put = dbs_put;
+ dbs->seq = dbs_seq;
+ dbs->sync = dbs_sync;
+ dbs->fd = dbs_fd;
+
+ return dbs;
+loser:
+ if (db) {
+ (*db->close)(db);
+ }
+ if (dbsp) {
+ if (dbsp->blobdir) {
+ PORT_Free(dbsp->blobdir);
+ }
+ PORT_Free(dbsp);
+ }
+ return NULL;
+}
diff --git a/security/nss/lib/softoken/legacydb/keydb.c b/security/nss/lib/softoken/legacydb/keydb.c
new file mode 100644
index 000000000..a57c59fb9
--- /dev/null
+++ b/security/nss/lib/softoken/legacydb/keydb.c
@@ -0,0 +1,2270 @@
+/* ***** 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):
+ * Dr Vipul Gupta <vipul.gupta@sun.com>, Sun Microsystems Laboratories
+ *
+ * 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 ***** */
+/* $Id$ */
+
+#include "lowkeyi.h"
+#include "secasn1.h"
+#include "secder.h"
+#include "secoid.h"
+#include "blapi.h"
+#include "secitem.h"
+#include "pcert.h"
+#include "mcom_db.h"
+#include "secerr.h"
+#include "nsslocks.h"
+
+#include "keydbi.h"
+#include "lgdb.h"
+
+/*
+ * Record keys for keydb
+ */
+#define SALT_STRING "global-salt"
+#define VERSION_STRING "Version"
+#define KEYDB_PW_CHECK_STRING "password-check"
+#define KEYDB_PW_CHECK_LEN 14
+#define KEYDB_FAKE_PW_CHECK_STRING "fake-password-check"
+#define KEYDB_FAKE_PW_CHECK_LEN 19
+
+/* Size of the global salt for key database */
+#define SALT_LENGTH 16
+
+const SEC_ASN1Template nsslowkey_EncryptedPrivateKeyInfoTemplate[] = {
+ { SEC_ASN1_SEQUENCE,
+ 0, NULL, sizeof(NSSLOWKEYEncryptedPrivateKeyInfo) },
+ { SEC_ASN1_INLINE,
+ offsetof(NSSLOWKEYEncryptedPrivateKeyInfo,algorithm),
+ SECOID_AlgorithmIDTemplate },
+ { SEC_ASN1_OCTET_STRING,
+ offsetof(NSSLOWKEYEncryptedPrivateKeyInfo,encryptedData) },
+ { 0 }
+};
+
+const SEC_ASN1Template nsslowkey_PointerToEncryptedPrivateKeyInfoTemplate[] = {
+ { SEC_ASN1_POINTER, 0, nsslowkey_EncryptedPrivateKeyInfoTemplate }
+};
+
+
+/* ====== Default key databse encryption algorithm ====== */
+static void
+sec_destroy_dbkey(NSSLOWKEYDBKey *dbkey)
+{
+ if ( dbkey && dbkey->arena ) {
+ PORT_FreeArena(dbkey->arena, PR_FALSE);
+ }
+}
+
+static void
+free_dbt(DBT *dbt)
+{
+ if ( dbt ) {
+ PORT_Free(dbt->data);
+ PORT_Free(dbt);
+ }
+
+ return;
+}
+
+static int keydb_Get(NSSLOWKEYDBHandle *db, DBT *key, DBT *data,
+ unsigned int flags);
+static int keydb_Put(NSSLOWKEYDBHandle *db, DBT *key, DBT *data,
+ unsigned int flags);
+static int keydb_Sync(NSSLOWKEYDBHandle *db, unsigned int flags);
+static int keydb_Del(NSSLOWKEYDBHandle *db, DBT *key, unsigned int flags);
+static int keydb_Seq(NSSLOWKEYDBHandle *db, DBT *key, DBT *data,
+ unsigned int flags);
+static void keydb_Close(NSSLOWKEYDBHandle *db);
+
+static void
+keydb_InitLocks(NSSLOWKEYDBHandle *handle)
+{
+ if (handle->lock == NULL) {
+ nss_InitLock(&handle->lock, nssILockKeyDB);
+ }
+
+ return;
+}
+
+static void
+keydb_DestroyLocks(NSSLOWKEYDBHandle *handle)
+{
+ if (handle->lock != NULL) {
+ PZ_DestroyLock(handle->lock);
+ handle->lock = NULL;
+ }
+
+ return;
+}
+
+/*
+ * format of key database entries for version 3 of database:
+ * byte offset field
+ * ----------- -----
+ * 0 version
+ * 1 salt-len
+ * 2 nn-len
+ * 3.. salt-data
+ * ... nickname
+ * ... encrypted-key-data
+ */
+static DBT *
+encode_dbkey(NSSLOWKEYDBKey *dbkey,unsigned char version)
+{
+ DBT *bufitem = NULL;
+ unsigned char *buf;
+ int nnlen;
+ char *nn;
+
+ bufitem = (DBT *)PORT_ZAlloc(sizeof(DBT));
+ if ( bufitem == NULL ) {
+ goto loser;
+ }
+
+ if ( dbkey->nickname ) {
+ nn = dbkey->nickname;
+ nnlen = PORT_Strlen(nn) + 1;
+ } else {
+ nn = "";
+ nnlen = 1;
+ }
+
+ /* compute the length of the record */
+ /* 1 + 1 + 1 == version number header + salt length + nn len */
+ bufitem->size = dbkey->salt.len + nnlen + dbkey->derPK.len + 1 + 1 + 1;
+
+ bufitem->data = (void *)PORT_ZAlloc(bufitem->size);
+ if ( bufitem->data == NULL ) {
+ goto loser;
+ }
+
+ buf = (unsigned char *)bufitem->data;
+
+ /* set version number */
+ buf[0] = version;
+
+ /* set length of salt */
+ PORT_Assert(dbkey->salt.len < 256);
+ buf[1] = dbkey->salt.len;
+
+ /* set length of nickname */
+ PORT_Assert(nnlen < 256);
+ buf[2] = nnlen;
+
+ /* copy salt */
+ PORT_Memcpy(&buf[3], dbkey->salt.data, dbkey->salt.len);
+
+ /* copy nickname */
+ PORT_Memcpy(&buf[3 + dbkey->salt.len], nn, nnlen);
+
+ /* copy encrypted key */
+ PORT_Memcpy(&buf[3 + dbkey->salt.len + nnlen], dbkey->derPK.data,
+ dbkey->derPK.len);
+
+ return(bufitem);
+
+loser:
+ if ( bufitem ) {
+ free_dbt(bufitem);
+ }
+
+ return(NULL);
+}
+
+static NSSLOWKEYDBKey *
+decode_dbkey(DBT *bufitem, int expectedVersion)
+{
+ NSSLOWKEYDBKey *dbkey;
+ PLArenaPool *arena = NULL;
+ unsigned char *buf;
+ int version;
+ int keyoff;
+ int nnlen;
+ int saltoff;
+
+ buf = (unsigned char *)bufitem->data;
+
+ version = buf[0];
+
+ if ( version != expectedVersion ) {
+ goto loser;
+ }
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( arena == NULL ) {
+ goto loser;
+ }
+
+ dbkey = (NSSLOWKEYDBKey *)PORT_ArenaZAlloc(arena, sizeof(NSSLOWKEYDBKey));
+ if ( dbkey == NULL ) {
+ goto loser;
+ }
+
+ dbkey->arena = arena;
+ dbkey->salt.data = NULL;
+ dbkey->derPK.data = NULL;
+
+ dbkey->salt.len = buf[1];
+ dbkey->salt.data = (unsigned char *)PORT_ArenaZAlloc(arena, dbkey->salt.len);
+ if ( dbkey->salt.data == NULL ) {
+ goto loser;
+ }
+
+ saltoff = 2;
+ keyoff = 2 + dbkey->salt.len;
+
+ if ( expectedVersion >= 3 ) {
+ nnlen = buf[2];
+ if ( nnlen ) {
+ dbkey->nickname = (char *)PORT_ArenaZAlloc(arena, nnlen + 1);
+ if ( dbkey->nickname ) {
+ PORT_Memcpy(dbkey->nickname, &buf[keyoff+1], nnlen);
+ }
+ }
+ keyoff += ( nnlen + 1 );
+ saltoff = 3;
+ }
+
+ PORT_Memcpy(dbkey->salt.data, &buf[saltoff], dbkey->salt.len);
+
+ dbkey->derPK.len = bufitem->size - keyoff;
+ dbkey->derPK.data = (unsigned char *)PORT_ArenaZAlloc(arena,dbkey->derPK.len);
+ if ( dbkey->derPK.data == NULL ) {
+ goto loser;
+ }
+
+ PORT_Memcpy(dbkey->derPK.data, &buf[keyoff], dbkey->derPK.len);
+
+ return(dbkey);
+
+loser:
+
+ if ( arena ) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+
+ return(NULL);
+}
+
+static NSSLOWKEYDBKey *
+get_dbkey(NSSLOWKEYDBHandle *handle, DBT *index)
+{
+ NSSLOWKEYDBKey *dbkey;
+ DBT entry;
+ int ret;
+
+ /* get it from the database */
+ ret = keydb_Get(handle, index, &entry, 0);
+ if ( ret ) {
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ return NULL;
+ }
+
+ /* set up dbkey struct */
+
+ dbkey = decode_dbkey(&entry, handle->version);
+
+ return(dbkey);
+}
+
+static SECStatus
+put_dbkey(NSSLOWKEYDBHandle *handle, DBT *index, NSSLOWKEYDBKey *dbkey, PRBool update)
+{
+ DBT *keydata = NULL;
+ int status;
+
+ keydata = encode_dbkey(dbkey, handle->version);
+ if ( keydata == NULL ) {
+ goto loser;
+ }
+
+ /* put it in the database */
+ if ( update ) {
+ status = keydb_Put(handle, index, keydata, 0);
+ } else {
+ status = keydb_Put(handle, index, keydata, R_NOOVERWRITE);
+ }
+
+ if ( status ) {
+ goto loser;
+ }
+
+ /* sync the database */
+ status = keydb_Sync(handle, 0);
+ if ( status ) {
+ goto loser;
+ }
+
+ free_dbt(keydata);
+ return(SECSuccess);
+
+loser:
+ if ( keydata ) {
+ free_dbt(keydata);
+ }
+
+ return(SECFailure);
+}
+
+SECStatus
+nsslowkey_TraverseKeys(NSSLOWKEYDBHandle *handle,
+ SECStatus (* keyfunc)(DBT *k, DBT *d, void *pdata),
+ void *udata )
+{
+ DBT data;
+ DBT key;
+ SECStatus status;
+ int ret;
+
+ if (handle == NULL) {
+ return(SECFailure);
+ }
+
+ ret = keydb_Seq(handle, &key, &data, R_FIRST);
+ if ( ret ) {
+ return(SECFailure);
+ }
+
+ do {
+ /* skip version record */
+ if ( data.size > 1 ) {
+ if ( key.size == ( sizeof(SALT_STRING) - 1 ) ) {
+ if ( PORT_Memcmp(key.data, SALT_STRING, key.size) == 0 ) {
+ continue;
+ }
+ }
+
+ /* skip password check */
+ if ( key.size == KEYDB_PW_CHECK_LEN ) {
+ if ( PORT_Memcmp(key.data, KEYDB_PW_CHECK_STRING,
+ KEYDB_PW_CHECK_LEN) == 0 ) {
+ continue;
+ }
+ }
+
+ status = (* keyfunc)(&key, &data, udata);
+ if (status != SECSuccess) {
+ return(status);
+ }
+ }
+ } while ( keydb_Seq(handle, &key, &data, R_NEXT) == 0 );
+
+ return(SECSuccess);
+}
+
+#ifdef notdef
+typedef struct keyNode {
+ struct keyNode *next;
+ DBT key;
+} keyNode;
+
+typedef struct {
+ PLArenaPool *arena;
+ keyNode *head;
+} keyList;
+
+static SECStatus
+sec_add_key_to_list(DBT *key, DBT *data, void *arg)
+{
+ keyList *keylist;
+ keyNode *node;
+ void *keydata;
+
+ keylist = (keyList *)arg;
+
+ /* allocate the node struct */
+ node = (keyNode*)PORT_ArenaZAlloc(keylist->arena, sizeof(keyNode));
+ if ( node == NULL ) {
+ return(SECFailure);
+ }
+
+ /* allocate room for key data */
+ keydata = PORT_ArenaZAlloc(keylist->arena, key->size);
+ if ( keydata == NULL ) {
+ return(SECFailure);
+ }
+
+ /* link node into list */
+ node->next = keylist->head;
+ keylist->head = node;
+
+ /* copy key into node */
+ PORT_Memcpy(keydata, key->data, key->size);
+ node->key.size = key->size;
+ node->key.data = keydata;
+
+ return(SECSuccess);
+}
+#endif
+
+static SECItem *
+decodeKeyDBGlobalSalt(DBT *saltData)
+{
+ SECItem *saltitem;
+
+ saltitem = (SECItem *)PORT_ZAlloc(sizeof(SECItem));
+ if ( saltitem == NULL ) {
+ return(NULL);
+ }
+
+ saltitem->data = (unsigned char *)PORT_ZAlloc(saltData->size);
+ if ( saltitem->data == NULL ) {
+ PORT_Free(saltitem);
+ return(NULL);
+ }
+
+ saltitem->len = saltData->size;
+ PORT_Memcpy(saltitem->data, saltData->data, saltitem->len);
+
+ return(saltitem);
+}
+
+static SECItem *
+GetKeyDBGlobalSalt(NSSLOWKEYDBHandle *handle)
+{
+ DBT saltKey;
+ DBT saltData;
+ int ret;
+
+ saltKey.data = SALT_STRING;
+ saltKey.size = sizeof(SALT_STRING) - 1;
+
+ ret = keydb_Get(handle, &saltKey, &saltData, 0);
+ if ( ret ) {
+ return(NULL);
+ }
+
+ return(decodeKeyDBGlobalSalt(&saltData));
+}
+
+static SECStatus
+StoreKeyDBGlobalSalt(NSSLOWKEYDBHandle *handle, SECItem *salt)
+{
+ DBT saltKey;
+ DBT saltData;
+ int status;
+
+ saltKey.data = SALT_STRING;
+ saltKey.size = sizeof(SALT_STRING) - 1;
+
+ saltData.data = (void *)salt->data;
+ saltData.size = salt->len;
+
+ /* put global salt into the database now */
+ status = keydb_Put(handle, &saltKey, &saltData, 0);
+ if ( status ) {
+ return(SECFailure);
+ }
+
+ return(SECSuccess);
+}
+
+static SECStatus
+makeGlobalVersion(NSSLOWKEYDBHandle *handle)
+{
+ unsigned char version;
+ DBT versionData;
+ DBT versionKey;
+ int status;
+
+ version = NSSLOWKEY_DB_FILE_VERSION;
+ versionData.data = &version;
+ versionData.size = 1;
+ versionKey.data = VERSION_STRING;
+ versionKey.size = sizeof(VERSION_STRING)-1;
+
+ /* put version string into the database now */
+ status = keydb_Put(handle, &versionKey, &versionData, 0);
+ if ( status ) {
+ return(SECFailure);
+ }
+ handle->version = version;
+
+ return(SECSuccess);
+}
+
+
+static SECStatus
+makeGlobalSalt(NSSLOWKEYDBHandle *handle)
+{
+ DBT saltKey;
+ DBT saltData;
+ unsigned char saltbuf[16];
+ int status;
+
+ saltKey.data = SALT_STRING;
+ saltKey.size = sizeof(SALT_STRING) - 1;
+
+ saltData.data = (void *)saltbuf;
+ saltData.size = sizeof(saltbuf);
+ RNG_GenerateGlobalRandomBytes(saltbuf, sizeof(saltbuf));
+
+ /* put global salt into the database now */
+ status = keydb_Put(handle, &saltKey, &saltData, 0);
+ if ( status ) {
+ return(SECFailure);
+ }
+
+ return(SECSuccess);
+}
+
+static SECStatus
+encodePWCheckEntry(PLArenaPool *arena, SECItem *entry, SECOidTag alg,
+ SECItem *encCheck);
+
+static unsigned char
+nsslowkey_version(NSSLOWKEYDBHandle *handle)
+{
+ DBT versionKey;
+ DBT versionData;
+ int ret;
+ versionKey.data = VERSION_STRING;
+ versionKey.size = sizeof(VERSION_STRING)-1;
+
+ if (handle->db == NULL) {
+ return 255;
+ }
+
+ /* lookup version string in database */
+ ret = keydb_Get( handle, &versionKey, &versionData, 0 );
+
+ /* error accessing the database */
+ if ( ret < 0 ) {
+ return 255;
+ }
+
+ if ( ret >= 1 ) {
+ return 0;
+ }
+ return *( (unsigned char *)versionData.data);
+}
+
+static PRBool
+seckey_HasAServerKey(NSSLOWKEYDBHandle *handle)
+{
+ DBT key;
+ DBT data;
+ int ret;
+ PRBool found = PR_FALSE;
+
+ ret = keydb_Seq(handle, &key, &data, R_FIRST);
+ if ( ret ) {
+ return PR_FALSE;
+ }
+
+ do {
+ /* skip version record */
+ if ( data.size > 1 ) {
+ /* skip salt */
+ if ( key.size == ( sizeof(SALT_STRING) - 1 ) ) {
+ if ( PORT_Memcmp(key.data, SALT_STRING, key.size) == 0 ) {
+ continue;
+ }
+ }
+ /* skip pw check entry */
+ if ( key.size == KEYDB_PW_CHECK_LEN ) {
+ if ( PORT_Memcmp(key.data, KEYDB_PW_CHECK_STRING,
+ KEYDB_PW_CHECK_LEN) == 0 ) {
+ continue;
+ }
+ }
+
+ /* keys stored by nickname will have 0 as the last byte of the
+ * db key. Other keys must be stored by modulus. We will not
+ * update those because they are left over from a keygen that
+ * never resulted in a cert.
+ */
+ if ( ((unsigned char *)key.data)[key.size-1] != 0 ) {
+ continue;
+ }
+
+ if (PORT_Strcmp(key.data,"Server-Key") == 0) {
+ found = PR_TRUE;
+ break;
+ }
+
+ }
+ } while ( keydb_Seq(handle, &key, &data, R_NEXT) == 0 );
+
+ return found;
+}
+
+/* forward declare local create function */
+static NSSLOWKEYDBHandle * nsslowkey_NewHandle(DB *dbHandle);
+
+/*
+ * currently updates key database from v2 to v3
+ */
+static SECStatus
+nsslowkey_UpdateKeyDBPass1(NSSLOWKEYDBHandle *handle)
+{
+ SECStatus rv;
+ DBT checkKey;
+ DBT checkData;
+ DBT saltKey;
+ DBT saltData;
+ DBT key;
+ DBT data;
+ unsigned char version;
+ NSSLOWKEYDBKey *dbkey = NULL;
+ NSSLOWKEYDBHandle *update = NULL;
+ SECItem *oldSalt = NULL;
+ int ret;
+ SECItem checkitem;
+
+ if ( handle->updatedb == NULL ) {
+ return SECSuccess;
+ }
+
+ /* create a full DB Handle for our update so we
+ * can use the correct locks for the db primatives */
+ update = nsslowkey_NewHandle(handle->updatedb);
+ if ( update == NULL) {
+ return SECSuccess;
+ }
+
+ /* update has now inherited the database handle */
+ handle->updatedb = NULL;
+
+ /*
+ * check the version record
+ */
+ version = nsslowkey_version(update);
+ if (version != 2) {
+ goto done;
+ }
+
+ saltKey.data = SALT_STRING;
+ saltKey.size = sizeof(SALT_STRING) - 1;
+
+ ret = keydb_Get(update, &saltKey, &saltData, 0);
+ if ( ret ) {
+ /* no salt in old db, so it is corrupted */
+ goto done;
+ }
+
+ oldSalt = decodeKeyDBGlobalSalt(&saltData);
+ if ( oldSalt == NULL ) {
+ /* bad salt in old db, so it is corrupted */
+ goto done;
+ }
+
+ /*
+ * look for a pw check entry
+ */
+ checkKey.data = KEYDB_PW_CHECK_STRING;
+ checkKey.size = KEYDB_PW_CHECK_LEN;
+
+ ret = keydb_Get(update, &checkKey, &checkData, 0 );
+ if (ret) {
+ /*
+ * if we have a key, but no KEYDB_PW_CHECK_STRING, then this must
+ * be an old server database, and it does have a password associated
+ * with it. Put a fake entry in so we can identify this db when we do
+ * get the password for it.
+ */
+ if (seckey_HasAServerKey(update)) {
+ DBT fcheckKey;
+ DBT fcheckData;
+
+ /*
+ * include a fake string
+ */
+ fcheckKey.data = KEYDB_FAKE_PW_CHECK_STRING;
+ fcheckKey.size = KEYDB_FAKE_PW_CHECK_LEN;
+ fcheckData.data = "1";
+ fcheckData.size = 1;
+ /* put global salt into the new database now */
+ ret = keydb_Put( handle, &saltKey, &saltData, 0);
+ if ( ret ) {
+ goto done;
+ }
+ ret = keydb_Put( handle, &fcheckKey, &fcheckData, 0);
+ if ( ret ) {
+ goto done;
+ }
+ } else {
+ goto done;
+ }
+ } else {
+ /* put global salt into the new database now */
+ ret = keydb_Put( handle, &saltKey, &saltData, 0);
+ if ( ret ) {
+ goto done;
+ }
+
+ dbkey = decode_dbkey(&checkData, 2);
+ if ( dbkey == NULL ) {
+ goto done;
+ }
+ checkitem = dbkey->derPK;
+ dbkey->derPK.data = NULL;
+
+ /* format the new pw check entry */
+ rv = encodePWCheckEntry(NULL, &dbkey->derPK, SEC_OID_RC4, &checkitem);
+ if ( rv != SECSuccess ) {
+ goto done;
+ }
+
+ rv = put_dbkey(handle, &checkKey, dbkey, PR_TRUE);
+ if ( rv != SECSuccess ) {
+ goto done;
+ }
+
+ /* free the dbkey */
+ sec_destroy_dbkey(dbkey);
+ dbkey = NULL;
+ }
+
+
+ /* now traverse the database */
+ ret = keydb_Seq(update, &key, &data, R_FIRST);
+ if ( ret ) {
+ goto done;
+ }
+
+ do {
+ /* skip version record */
+ if ( data.size > 1 ) {
+ /* skip salt */
+ if ( key.size == ( sizeof(SALT_STRING) - 1 ) ) {
+ if ( PORT_Memcmp(key.data, SALT_STRING, key.size) == 0 ) {
+ continue;
+ }
+ }
+ /* skip pw check entry */
+ if ( key.size == checkKey.size ) {
+ if ( PORT_Memcmp(key.data, checkKey.data, key.size) == 0 ) {
+ continue;
+ }
+ }
+
+ /* keys stored by nickname will have 0 as the last byte of the
+ * db key. Other keys must be stored by modulus. We will not
+ * update those because they are left over from a keygen that
+ * never resulted in a cert.
+ */
+ if ( ((unsigned char *)key.data)[key.size-1] != 0 ) {
+ continue;
+ }
+
+ dbkey = decode_dbkey(&data, 2);
+ if ( dbkey == NULL ) {
+ continue;
+ }
+
+ /* This puts the key into the new database with the same
+ * index (nickname) that it had before. The second pass
+ * of the update will have the password. It will decrypt
+ * and re-encrypt the entries using a new algorithm.
+ */
+ dbkey->nickname = (char *)key.data;
+ rv = put_dbkey(handle, &key, dbkey, PR_FALSE);
+ dbkey->nickname = NULL;
+
+ sec_destroy_dbkey(dbkey);
+ }
+ } while ( keydb_Seq(update, &key, &data, R_NEXT) == 0 );
+
+ dbkey = NULL;
+
+done:
+ /* sync the database */
+ ret = keydb_Sync(handle, 0);
+
+ nsslowkey_CloseKeyDB(update);
+
+ if ( oldSalt ) {
+ SECITEM_FreeItem(oldSalt, PR_TRUE);
+ }
+
+ if ( dbkey ) {
+ sec_destroy_dbkey(dbkey);
+ }
+
+ return(SECSuccess);
+}
+
+static SECStatus
+openNewDB(const char *appName, const char *prefix, const char *dbname,
+ NSSLOWKEYDBHandle *handle, NSSLOWKEYDBNameFunc namecb, void *cbarg)
+{
+ SECStatus rv = SECFailure;
+ int status = RDB_FAIL;
+ char *updname = NULL;
+ DB *updatedb = NULL;
+ PRBool updated = PR_FALSE;
+ int ret;
+
+ if (appName) {
+ handle->db = rdbopen( appName, prefix, "key", NO_CREATE, &status);
+ } else {
+ handle->db = dbopen( dbname, NO_CREATE, 0600, DB_HASH, 0 );
+ }
+ /* if create fails then we lose */
+ if ( handle->db == NULL ) {
+ return (status == RDB_RETRY) ? SECWouldBlock: SECFailure;
+ }
+
+ /* force a transactional read, which will verify that one and only one
+ * process attempts the update. */
+ if (nsslowkey_version(handle) == NSSLOWKEY_DB_FILE_VERSION) {
+ /* someone else has already updated the database for us */
+ db_InitComplete(handle->db);
+ return SECSuccess;
+ }
+
+ /*
+ * if we are creating a multiaccess database, see if there is a
+ * local database we can update from.
+ */
+ if (appName) {
+ NSSLOWKEYDBHandle *updateHandle;
+ updatedb = dbopen( dbname, NO_RDONLY, 0600, DB_HASH, 0 );
+ if (!updatedb) {
+ goto noupdate;
+ }
+
+ /* nsslowkey_version needs a full handle because it calls
+ * the kdb_Get() function, which needs to lock.
+ */
+ updateHandle = nsslowkey_NewHandle(updatedb);
+ if (!updateHandle) {
+ updatedb->close(updatedb);
+ goto noupdate;
+ }
+
+ handle->version = nsslowkey_version(updateHandle);
+ if (handle->version != NSSLOWKEY_DB_FILE_VERSION) {
+ nsslowkey_CloseKeyDB(updateHandle);
+ goto noupdate;
+ }
+
+ /* copy the new DB from the old one */
+ db_Copy(handle->db, updatedb);
+ nsslowkey_CloseKeyDB(updateHandle);
+ db_InitComplete(handle->db);
+ return SECSuccess;
+ }
+noupdate:
+
+ /* update the version number */
+ rv = makeGlobalVersion(handle);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ /*
+ * try to update from v2 db
+ */
+ updname = (*namecb)(cbarg, 2);
+ if ( updname != NULL ) {
+ handle->updatedb = dbopen( updname, NO_RDONLY, 0600, DB_HASH, 0 );
+ PORT_Free( updname );
+
+ if ( handle->updatedb ) {
+ /*
+ * Try to update the db using a null password. If the db
+ * doesn't have a password, then this will work. If it does
+ * have a password, then this will fail and we will do the
+ * update later
+ */
+ rv = nsslowkey_UpdateKeyDBPass1(handle);
+ if ( rv == SECSuccess ) {
+ updated = PR_TRUE;
+ }
+ }
+
+ }
+
+ /* we are using the old salt if we updated from an old db */
+ if ( ! updated ) {
+ rv = makeGlobalSalt(handle);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+ }
+
+ /* sync the database */
+ ret = keydb_Sync(handle, 0);
+ if ( ret ) {
+ rv = SECFailure;
+ goto loser;
+ }
+ rv = SECSuccess;
+
+loser:
+ db_InitComplete(handle->db);
+ return rv;
+}
+
+
+static DB *
+openOldDB(const char *appName, const char *prefix, const char *dbname,
+ PRBool openflags) {
+ DB *db = NULL;
+
+ if (appName) {
+ db = rdbopen( appName, prefix, "key", openflags, NULL);
+ } else {
+ db = dbopen( dbname, openflags, 0600, DB_HASH, 0 );
+ }
+
+ return db;
+}
+
+/* check for correct version number */
+static PRBool
+verifyVersion(NSSLOWKEYDBHandle *handle)
+{
+ int version = nsslowkey_version(handle);
+
+ handle->version = version;
+ if (version != NSSLOWKEY_DB_FILE_VERSION ) {
+ if (handle->db) {
+ keydb_Close(handle);
+ handle->db = NULL;
+ }
+ }
+ return handle->db != NULL;
+}
+
+static NSSLOWKEYDBHandle *
+nsslowkey_NewHandle(DB *dbHandle)
+{
+ NSSLOWKEYDBHandle *handle;
+ handle = (NSSLOWKEYDBHandle *)PORT_ZAlloc (sizeof(NSSLOWKEYDBHandle));
+ if (handle == NULL) {
+ PORT_SetError (SEC_ERROR_NO_MEMORY);
+ return NULL;
+ }
+
+ handle->appname = NULL;
+ handle->dbname = NULL;
+ handle->global_salt = NULL;
+ handle->updatedb = NULL;
+ handle->db = dbHandle;
+ handle->ref = 1;
+
+ keydb_InitLocks(handle);
+ return handle;
+}
+
+NSSLOWKEYDBHandle *
+nsslowkey_OpenKeyDB(PRBool readOnly, const char *appName, const char *prefix,
+ NSSLOWKEYDBNameFunc namecb, void *cbarg)
+{
+ NSSLOWKEYDBHandle *handle = NULL;
+ SECStatus rv;
+ int openflags;
+ char *dbname = NULL;
+
+
+ handle = nsslowkey_NewHandle(NULL);
+
+ openflags = readOnly ? NO_RDONLY : NO_RDWR;
+
+
+ dbname = (*namecb)(cbarg, NSSLOWKEY_DB_FILE_VERSION);
+ if ( dbname == NULL ) {
+ goto loser;
+ }
+ handle->appname = appName ? PORT_Strdup(appName) : NULL ;
+ handle->dbname = (appName == NULL) ? PORT_Strdup(dbname) :
+ (prefix ? PORT_Strdup(prefix) : NULL);
+ handle->readOnly = readOnly;
+
+
+
+ handle->db = openOldDB(appName, prefix, dbname, openflags);
+ if (handle->db) {
+ verifyVersion(handle);
+ if (handle->version == 255) {
+ goto loser;
+ }
+ }
+
+ /* if first open fails, try to create a new DB */
+ if ( handle->db == NULL ) {
+ if ( readOnly ) {
+ goto loser;
+ }
+
+ rv = openNewDB(appName, prefix, dbname, handle, namecb, cbarg);
+ /* two processes started to initialize the database at the same time.
+ * The multiprocess code blocked the second one, then had it retry to
+ * see if it can just open the database normally */
+ if (rv == SECWouldBlock) {
+ handle->db = openOldDB(appName,prefix,dbname, openflags);
+ verifyVersion(handle);
+ if (handle->db == NULL) {
+ goto loser;
+ }
+ } else if (rv != SECSuccess) {
+ goto loser;
+ }
+ }
+
+ handle->global_salt = GetKeyDBGlobalSalt(handle);
+ if ( dbname )
+ PORT_Free( dbname );
+ return handle;
+
+loser:
+
+ if ( dbname )
+ PORT_Free( dbname );
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ nsslowkey_CloseKeyDB(handle);
+ return NULL;
+}
+
+/*
+ * Close the database
+ */
+void
+nsslowkey_CloseKeyDB(NSSLOWKEYDBHandle *handle)
+{
+ if (handle != NULL) {
+ if (handle->db != NULL) {
+ keydb_Close(handle);
+ }
+ if (handle->updatedb) {
+ handle->updatedb->close(handle->updatedb);
+ }
+ if (handle->dbname) PORT_Free(handle->dbname);
+ if (handle->appname) PORT_Free(handle->appname);
+ if (handle->global_salt) {
+ SECITEM_FreeItem(handle->global_salt,PR_TRUE);
+ }
+ keydb_DestroyLocks(handle);
+
+ PORT_Free(handle);
+ }
+}
+
+/* Get the key database version */
+int
+nsslowkey_GetKeyDBVersion(NSSLOWKEYDBHandle *handle)
+{
+ PORT_Assert(handle != NULL);
+
+ return handle->version;
+}
+
+/*
+ * Delete a private key that was stored in the database
+ */
+SECStatus
+nsslowkey_DeleteKey(NSSLOWKEYDBHandle *handle, const SECItem *pubkey)
+{
+ DBT namekey;
+ int ret;
+
+ if (handle == NULL) {
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ return(SECFailure);
+ }
+
+ /* set up db key and data */
+ namekey.data = pubkey->data;
+ namekey.size = pubkey->len;
+
+ /* delete it from the database */
+ ret = keydb_Del(handle, &namekey, 0);
+ if ( ret ) {
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ return(SECFailure);
+ }
+
+ /* sync the database */
+ ret = keydb_Sync(handle, 0);
+ if ( ret ) {
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ return(SECFailure);
+ }
+
+ return(SECSuccess);
+}
+
+/*
+ * Store a key in the database, indexed by its public key modulus.(value!)
+ */
+SECStatus
+nsslowkey_StoreKeyByPublicKey(NSSLOWKEYDBHandle *handle,
+ NSSLOWKEYPrivateKey *privkey,
+ SECItem *pubKeyData,
+ char *nickname,
+ SDB *sdb)
+{
+ return nsslowkey_StoreKeyByPublicKeyAlg(handle, privkey, pubKeyData,
+ nickname, sdb, PR_FALSE);
+}
+
+SECStatus
+nsslowkey_UpdateNickname(NSSLOWKEYDBHandle *handle,
+ NSSLOWKEYPrivateKey *privkey,
+ SECItem *pubKeyData,
+ char *nickname,
+ SDB *sdb)
+{
+ return nsslowkey_StoreKeyByPublicKeyAlg(handle, privkey, pubKeyData,
+ nickname, sdb, PR_TRUE);
+}
+
+/* see if the symetric CKA_ID already Exists.
+ */
+PRBool
+nsslowkey_KeyForIDExists(NSSLOWKEYDBHandle *handle, SECItem *id)
+{
+ DBT namekey;
+ DBT dummy;
+ int status;
+
+ namekey.data = (char *)id->data;
+ namekey.size = id->len;
+ status = keydb_Get(handle, &namekey, &dummy, 0);
+ if ( status ) {
+ return PR_FALSE;
+ }
+
+ return PR_TRUE;
+}
+
+/* see if the public key for this cert is in the database filed
+ * by modulus
+ */
+PRBool
+nsslowkey_KeyForCertExists(NSSLOWKEYDBHandle *handle, NSSLOWCERTCertificate *cert)
+{
+ NSSLOWKEYPublicKey *pubkey = NULL;
+ DBT namekey;
+ DBT dummy;
+ int status;
+
+ /* get cert's public key */
+ pubkey = nsslowcert_ExtractPublicKey(cert);
+ if ( pubkey == NULL ) {
+ return PR_FALSE;
+ }
+
+ /* TNH - make key from NSSLOWKEYPublicKey */
+ switch (pubkey->keyType) {
+ case NSSLOWKEYRSAKey:
+ namekey.data = pubkey->u.rsa.modulus.data;
+ namekey.size = pubkey->u.rsa.modulus.len;
+ break;
+ case NSSLOWKEYDSAKey:
+ namekey.data = pubkey->u.dsa.publicValue.data;
+ namekey.size = pubkey->u.dsa.publicValue.len;
+ break;
+ case NSSLOWKEYDHKey:
+ namekey.data = pubkey->u.dh.publicValue.data;
+ namekey.size = pubkey->u.dh.publicValue.len;
+ break;
+#ifdef NSS_ENABLE_ECC
+ case NSSLOWKEYECKey:
+ namekey.data = pubkey->u.ec.publicValue.data;
+ namekey.size = pubkey->u.ec.publicValue.len;
+ break;
+#endif /* NSS_ENABLE_ECC */
+ default:
+ /* XXX We don't do Fortezza or DH yet. */
+ return PR_FALSE;
+ }
+
+ if (handle->version != 3) {
+ unsigned char buf[SHA1_LENGTH];
+ SHA1_HashBuf(buf,namekey.data,namekey.size);
+ /* NOTE: don't use pubkey after this! it's now thrashed */
+ PORT_Memcpy(namekey.data,buf,sizeof(buf));
+ namekey.size = sizeof(buf);
+ }
+
+ status = keydb_Get(handle, &namekey, &dummy, 0);
+ /* some databases have the key stored as a signed value */
+ if (status) {
+ unsigned char *buf = (unsigned char *)PORT_Alloc(namekey.size+1);
+ if (buf) {
+ PORT_Memcpy(&buf[1], namekey.data, namekey.size);
+ buf[0] = 0;
+ namekey.data = buf;
+ namekey.size ++;
+ status = keydb_Get(handle, &namekey, &dummy, 0);
+ PORT_Free(buf);
+ }
+ }
+ nsslowkey_DestroyPublicKey(pubkey);
+ if ( status ) {
+ return PR_FALSE;
+ }
+
+ return PR_TRUE;
+}
+
+typedef struct NSSLowPasswordDataParamStr {
+ SECItem salt;
+ SECItem iter;
+} NSSLowPasswordDataParam;
+
+static const SEC_ASN1Template NSSLOWPasswordParamTemplate[] =
+{
+ {SEC_ASN1_SEQUENCE, 0, NULL, sizeof(NSSLowPasswordDataParam) },
+ {SEC_ASN1_OCTET_STRING, offsetof(NSSLowPasswordDataParam, salt) },
+ {SEC_ASN1_INTEGER, offsetof(NSSLowPasswordDataParam, iter) },
+ {0}
+};
+struct LGEncryptedDataInfoStr {
+ SECAlgorithmID algorithm;
+ SECItem encryptedData;
+};
+typedef struct LGEncryptedDataInfoStr LGEncryptedDataInfo;
+
+const SEC_ASN1Template lg_EncryptedDataInfoTemplate[] = {
+ { SEC_ASN1_SEQUENCE,
+ 0, NULL, sizeof(LGEncryptedDataInfo) },
+ { SEC_ASN1_INLINE,
+ offsetof(LGEncryptedDataInfo,algorithm),
+ SECOID_AlgorithmIDTemplate },
+ { SEC_ASN1_OCTET_STRING,
+ offsetof(LGEncryptedDataInfo,encryptedData) },
+ { 0 }
+};
+static const unsigned char def_iter_data[] = { SEC_ASN1_INTEGER, 0x01, 0x01 };
+static const SECItem def_iter = { siBuffer ,
+ (unsigned char *)def_iter_data,
+ sizeof(def_iter_data) };
+
+static SECItem *
+nsslowkey_EncodePW(SECOidTag alg, const SECItem *salt, SECItem *data)
+{
+ NSSLowPasswordDataParam param;
+ LGEncryptedDataInfo edi;
+ PLArenaPool *arena;
+ unsigned char one = 1;
+ SECItem *epw = NULL;
+ SECItem *encParam;
+ SECStatus rv;
+
+ param.salt = *salt;
+ param.iter.data = &one;
+ param.iter.len = 1;
+ edi.encryptedData = *data;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (arena == NULL) {
+ return NULL;
+ }
+
+ encParam = SEC_ASN1EncodeItem(arena, NULL, &param,
+ NSSLOWPasswordParamTemplate);
+ if (encParam == NULL) {
+ goto loser;
+ }
+ rv = SECOID_SetAlgorithmID(arena, &edi.algorithm, alg, encParam);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ epw = SEC_ASN1EncodeItem(NULL, NULL, &edi, lg_EncryptedDataInfoTemplate);
+
+loser:
+ PORT_FreeArena(arena, PR_FALSE);
+ return epw;
+}
+
+static SECItem *
+nsslowkey_DecodePW(const SECItem *derData, SECOidTag *alg, SECItem *salt)
+{
+ NSSLowPasswordDataParam param;
+ LGEncryptedDataInfo edi;
+ PLArenaPool *arena;
+ SECItem *pwe = NULL;
+ SECStatus rv;
+
+ salt->data = NULL;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (arena == NULL) {
+ return NULL;
+ }
+
+ rv = SEC_QuickDERDecodeItem(arena, &edi, lg_EncryptedDataInfoTemplate,
+ derData);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ *alg = SECOID_GetAlgorithmTag(&edi.algorithm);
+ rv = SEC_QuickDERDecodeItem(arena, &param, NSSLOWPasswordParamTemplate,
+ &edi.algorithm.parameters);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ if (SECITEM_ItemsAreEqual(&param.iter, &def_iter) ) {
+ goto loser;
+ }
+ rv = SECITEM_CopyItem(NULL, salt, &param.salt);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ pwe = SECITEM_DupItem(&edi.encryptedData);
+
+loser:
+ if (!pwe && salt->data) {
+ PORT_Free(salt->data);
+ salt->data = NULL;
+ }
+ PORT_FreeArena(arena, PR_FALSE);
+ return pwe;
+}
+
+
+/*
+ * check to see if the user has a password
+ */
+static SECStatus
+nsslowkey_GetPWCheckEntry(NSSLOWKEYDBHandle *handle,SDBPasswordEntry *entry)
+{
+ DBT checkkey; /*, checkdata; */
+ NSSLOWKEYDBKey *dbkey = NULL;
+ SECItem *global_salt = NULL;
+ SECItem *item = NULL;
+ SECItem entryData, oid;
+ SECStatus rv = SECFailure;
+ SECOidTag algorithm;
+
+ if (handle == NULL) {
+ /* PORT_SetError */
+ return(SECFailure);
+ }
+
+ global_salt = GetKeyDBGlobalSalt(handle);
+ if (!global_salt) {
+ return SECFailure;
+ }
+ if (global_salt->len > sizeof(entry->data)) {
+ /* PORT_SetError */
+ goto loser;
+ }
+
+ PORT_Memcpy(entry->data, global_salt->data, global_salt->len);
+ entry->salt.data = entry->data;
+ entry->salt.len = global_salt->len;
+ entry->value.data = &entry->data[entry->salt.len];
+
+ checkkey.data = KEYDB_PW_CHECK_STRING;
+ checkkey.size = KEYDB_PW_CHECK_LEN;
+ dbkey = get_dbkey(handle, &checkkey);
+ if (dbkey == NULL) {
+ /* handle 'FAKE' check here */
+ goto loser;
+ }
+
+ oid.len = dbkey->derPK.data[0];
+ oid.data = &dbkey->derPK.data[1];
+
+ if (dbkey->derPK.len < (KEYDB_PW_CHECK_LEN + 1 +oid.len)) {
+ goto loser;
+ }
+ algorithm = SECOID_FindOIDTag(&oid);
+ entryData.len = dbkey->derPK.len - (oid.len+1);
+ entryData.data = &dbkey->derPK.data[oid.len+1];
+
+ item = nsslowkey_EncodePW(algorithm, &dbkey->salt, &entryData);
+ if (!item || (item->len + entry->salt.len) > sizeof(entry->data)) {
+ goto loser;
+ }
+ PORT_Memcpy(entry->value.data, item->data, item->len);
+ entry->value.len = item->len;
+ rv = SECSuccess;
+
+loser:
+ if (item) {
+ SECITEM_FreeItem(item, PR_TRUE);
+ }
+ if (dbkey) {
+ sec_destroy_dbkey(dbkey);
+ }
+ if (global_salt) {
+ SECITEM_FreeItem(global_salt,PR_TRUE);
+ }
+ return rv;
+}
+
+/*
+ * check to see if the user has a password
+ */
+static SECStatus
+nsslowkey_PutPWCheckEntry(NSSLOWKEYDBHandle *handle,SDBPasswordEntry *entry)
+{
+ DBT checkkey;
+ NSSLOWKEYDBKey *dbkey = NULL;
+ SECItem *item = NULL;
+ SECItem salt;
+ SECOidTag algid;
+ SECStatus rv = SECFailure;
+ PLArenaPool *arena;
+
+ if (handle == NULL) {
+ /* PORT_SetError */
+ return(SECFailure);
+ }
+
+ checkkey.data = KEYDB_PW_CHECK_STRING;
+ checkkey.size = KEYDB_PW_CHECK_LEN;
+
+ salt.data = NULL;
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (arena == NULL) {
+ return SECFailure;
+ }
+
+ item = nsslowkey_DecodePW(&entry->value, &algid, &salt);
+ if (item == NULL) {
+ goto loser;
+ }
+
+ dbkey = PORT_ArenaZNew(arena, NSSLOWKEYDBKey);
+ if (dbkey == NULL) {
+ goto loser;
+ }
+
+ dbkey->arena = arena;
+
+ rv = SECITEM_CopyItem(arena, &dbkey->salt, &salt);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ rv = encodePWCheckEntry(arena, &dbkey->derPK, algid, item);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ rv = put_dbkey(handle, &checkkey, dbkey, PR_TRUE);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ if (handle->global_salt) {
+ SECITEM_FreeItem(handle->global_salt, PR_TRUE);
+ handle->global_salt = NULL;
+ }
+ rv = StoreKeyDBGlobalSalt(handle, &entry->salt);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ handle->global_salt = GetKeyDBGlobalSalt(handle);
+
+loser:
+ if (item) {
+ SECITEM_FreeItem(item, PR_TRUE);
+ }
+ if (arena) {
+ PORT_FreeArena(arena, PR_TRUE);
+ }
+ if (salt.data) {
+ PORT_Free(salt.data);
+ }
+ return rv;
+}
+
+#ifdef EC_DEBUG
+#define SEC_PRINT(str1, str2, num, sitem) \
+ printf("pkcs11c.c:%s:%s (keytype=%d) [len=%d]\n", \
+ str1, str2, num, sitem->len); \
+ for (i = 0; i < sitem->len; i++) { \
+ printf("%02x:", sitem->data[i]); \
+ } \
+ printf("\n")
+#else
+#define SEC_PRINT(a, b, c, d)
+#endif /* EC_DEBUG */
+
+
+SECStatus
+seckey_encrypt_private_key( PLArenaPool *permarena, NSSLOWKEYPrivateKey *pk,
+ SDB *sdbpw, SECItem *result)
+{
+ NSSLOWKEYPrivateKeyInfo *pki = NULL;
+ SECStatus rv = SECFailure;
+ PLArenaPool *temparena = NULL;
+ SECItem *der_item = NULL;
+ SECItem *cipherText = NULL;
+ SECItem *dummy = NULL;
+#ifdef NSS_ENABLE_ECC
+ SECItem *fordebug = NULL;
+ int savelen;
+#endif
+
+ temparena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
+ if(temparena == NULL)
+ goto loser;
+
+ /* allocate structures */
+ pki = (NSSLOWKEYPrivateKeyInfo *)PORT_ArenaZAlloc(temparena,
+ sizeof(NSSLOWKEYPrivateKeyInfo));
+ der_item = (SECItem *)PORT_ArenaZAlloc(temparena, sizeof(SECItem));
+ if((pki == NULL) || (der_item == NULL))
+ goto loser;
+
+
+ /* setup private key info */
+ dummy = SEC_ASN1EncodeInteger(temparena, &(pki->version),
+ NSSLOWKEY_PRIVATE_KEY_INFO_VERSION);
+ if(dummy == NULL)
+ goto loser;
+
+ /* Encode the key, and set the algorithm (with params) */
+ switch (pk->keyType) {
+ case NSSLOWKEYRSAKey:
+ prepare_low_rsa_priv_key_for_asn1(pk);
+ dummy = SEC_ASN1EncodeItem(temparena, &(pki->privateKey), pk,
+ nsslowkey_RSAPrivateKeyTemplate);
+ if (dummy == NULL) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ rv = SECOID_SetAlgorithmID(temparena, &(pki->algorithm),
+ SEC_OID_PKCS1_RSA_ENCRYPTION, 0);
+ if (rv == SECFailure) {
+ goto loser;
+ }
+
+ break;
+ case NSSLOWKEYDSAKey:
+ prepare_low_dsa_priv_key_for_asn1(pk);
+ dummy = SEC_ASN1EncodeItem(temparena, &(pki->privateKey), pk,
+ nsslowkey_DSAPrivateKeyTemplate);
+ if (dummy == NULL) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ prepare_low_pqg_params_for_asn1(&pk->u.dsa.params);
+ dummy = SEC_ASN1EncodeItem(temparena, NULL, &pk->u.dsa.params,
+ nsslowkey_PQGParamsTemplate);
+ if (dummy == NULL) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ rv = SECOID_SetAlgorithmID(temparena, &(pki->algorithm),
+ SEC_OID_ANSIX9_DSA_SIGNATURE, dummy);
+ if (rv == SECFailure) {
+ goto loser;
+ }
+
+ break;
+ case NSSLOWKEYDHKey:
+ prepare_low_dh_priv_key_for_asn1(pk);
+ dummy = SEC_ASN1EncodeItem(temparena, &(pki->privateKey), pk,
+ nsslowkey_DHPrivateKeyTemplate);
+ if (dummy == NULL) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ rv = SECOID_SetAlgorithmID(temparena, &(pki->algorithm),
+ SEC_OID_X942_DIFFIE_HELMAN_KEY, dummy);
+ if (rv == SECFailure) {
+ goto loser;
+ }
+ break;
+#ifdef NSS_ENABLE_ECC
+ case NSSLOWKEYECKey:
+ prepare_low_ec_priv_key_for_asn1(pk);
+ /* Public value is encoded as a bit string so adjust length
+ * to be in bits before ASN encoding and readjust
+ * immediately after.
+ *
+ * Since the SECG specification recommends not including the
+ * parameters as part of ECPrivateKey, we zero out the curveOID
+ * length before encoding and restore it later.
+ */
+ pk->u.ec.publicValue.len <<= 3;
+ savelen = pk->u.ec.ecParams.curveOID.len;
+ pk->u.ec.ecParams.curveOID.len = 0;
+ dummy = SEC_ASN1EncodeItem(temparena, &(pki->privateKey), pk,
+ nsslowkey_ECPrivateKeyTemplate);
+ pk->u.ec.ecParams.curveOID.len = savelen;
+ pk->u.ec.publicValue.len >>= 3;
+
+ if (dummy == NULL) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ dummy = &pk->u.ec.ecParams.DEREncoding;
+
+ /* At this point dummy should contain the encoded params */
+ rv = SECOID_SetAlgorithmID(temparena, &(pki->algorithm),
+ SEC_OID_ANSIX962_EC_PUBLIC_KEY, dummy);
+
+ if (rv == SECFailure) {
+ goto loser;
+ }
+
+ fordebug = &(pki->privateKey);
+ SEC_PRINT("seckey_encrypt_private_key()", "PrivateKey",
+ pk->keyType, fordebug);
+
+ break;
+#endif /* NSS_ENABLE_ECC */
+ default:
+ /* We don't support DH or Fortezza private keys yet */
+ PORT_Assert(PR_FALSE);
+ break;
+ }
+
+ /* setup encrypted private key info */
+ dummy = SEC_ASN1EncodeItem(temparena, der_item, pki,
+ nsslowkey_PrivateKeyInfoTemplate);
+
+ SEC_PRINT("seckey_encrypt_private_key()", "PrivateKeyInfo",
+ pk->keyType, der_item);
+
+ if(dummy == NULL) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ rv = lg_util_encrypt(temparena, sdbpw, dummy, &cipherText);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ rv = SECITEM_CopyItem ( permarena, result, cipherText);
+
+loser:
+
+ if(temparena != NULL)
+ PORT_FreeArena(temparena, PR_TRUE);
+
+ return rv;
+}
+
+static SECStatus
+seckey_put_private_key(NSSLOWKEYDBHandle *keydb, DBT *index, SDB *sdbpw,
+ NSSLOWKEYPrivateKey *pk, char *nickname, PRBool update)
+{
+ NSSLOWKEYDBKey *dbkey = NULL;
+ PLArenaPool *arena = NULL;
+ SECStatus rv = SECFailure;
+
+ if((keydb == NULL) || (index == NULL) || (sdbpw == NULL) ||
+ (pk == NULL))
+ return SECFailure;
+
+ arena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
+ if(arena == NULL)
+ return SECFailure;
+
+ dbkey = (NSSLOWKEYDBKey *)PORT_ArenaZAlloc(arena, sizeof(NSSLOWKEYDBKey));
+ if(dbkey == NULL)
+ goto loser;
+ dbkey->arena = arena;
+ dbkey->nickname = nickname;
+
+ rv = seckey_encrypt_private_key(arena, pk, sdbpw, &dbkey->derPK);
+ if(rv != SECSuccess)
+ goto loser;
+
+ rv = put_dbkey(keydb, index, dbkey, update);
+
+ /* let success fall through */
+loser:
+ if(arena != NULL)
+ PORT_FreeArena(arena, PR_TRUE);
+
+ return rv;
+}
+
+/*
+ * Store a key in the database, indexed by its public key modulus.
+ * Note that the nickname is optional. It was only used by keyutil.
+ */
+SECStatus
+nsslowkey_StoreKeyByPublicKeyAlg(NSSLOWKEYDBHandle *handle,
+ NSSLOWKEYPrivateKey *privkey,
+ SECItem *pubKeyData,
+ char *nickname,
+ SDB *sdbpw,
+ PRBool update)
+{
+ DBT namekey;
+ SECStatus rv;
+
+ if (handle == NULL) {
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ return(SECFailure);
+ }
+
+ /* set up db key and data */
+ namekey.data = pubKeyData->data;
+ namekey.size = pubKeyData->len;
+
+ /* encrypt the private key */
+ rv = seckey_put_private_key(handle, &namekey, sdbpw, privkey, nickname,
+ update);
+
+ return(rv);
+}
+
+static NSSLOWKEYPrivateKey *
+seckey_decrypt_private_key(SECItem*epki,
+ SDB *sdbpw)
+{
+ NSSLOWKEYPrivateKey *pk = NULL;
+ NSSLOWKEYPrivateKeyInfo *pki = NULL;
+ SECStatus rv = SECFailure;
+ PLArenaPool *temparena = NULL, *permarena = NULL;
+ SECItem *dest = NULL;
+#ifdef NSS_ENABLE_ECC
+ SECItem *fordebug = NULL;
+#endif
+
+ if((epki == NULL) || (sdbpw == NULL))
+ goto loser;
+
+ temparena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
+ permarena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
+ if((temparena == NULL) || (permarena == NULL))
+ goto loser;
+
+ /* allocate temporary items */
+ pki = (NSSLOWKEYPrivateKeyInfo *)PORT_ArenaZAlloc(temparena,
+ sizeof(NSSLOWKEYPrivateKeyInfo));
+
+ /* allocate permanent arena items */
+ pk = (NSSLOWKEYPrivateKey *)PORT_ArenaZAlloc(permarena,
+ sizeof(NSSLOWKEYPrivateKey));
+
+ if((pk == NULL) || (pki == NULL))
+ goto loser;
+
+ pk->arena = permarena;
+
+ rv = lg_util_decrypt(sdbpw, epki, &dest);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ if(dest != NULL)
+ {
+ SECItem newPrivateKey;
+ SECItem newAlgParms;
+
+ SEC_PRINT("seckey_decrypt_private_key()", "PrivateKeyInfo", -1,
+ dest);
+
+ rv = SEC_QuickDERDecodeItem(temparena, pki,
+ nsslowkey_PrivateKeyInfoTemplate, dest);
+ if(rv == SECSuccess)
+ {
+ switch(SECOID_GetAlgorithmTag(&pki->algorithm)) {
+ case SEC_OID_X500_RSA_ENCRYPTION:
+ case SEC_OID_PKCS1_RSA_ENCRYPTION:
+ pk->keyType = NSSLOWKEYRSAKey;
+ prepare_low_rsa_priv_key_for_asn1(pk);
+ if (SECSuccess != SECITEM_CopyItem(permarena, &newPrivateKey,
+ &pki->privateKey) ) break;
+ rv = SEC_QuickDERDecodeItem(permarena, pk,
+ nsslowkey_RSAPrivateKeyTemplate,
+ &newPrivateKey);
+ break;
+ case SEC_OID_ANSIX9_DSA_SIGNATURE:
+ pk->keyType = NSSLOWKEYDSAKey;
+ prepare_low_dsa_priv_key_for_asn1(pk);
+ if (SECSuccess != SECITEM_CopyItem(permarena, &newPrivateKey,
+ &pki->privateKey) ) break;
+ rv = SEC_QuickDERDecodeItem(permarena, pk,
+ nsslowkey_DSAPrivateKeyTemplate,
+ &newPrivateKey);
+ if (rv != SECSuccess)
+ goto loser;
+ prepare_low_pqg_params_for_asn1(&pk->u.dsa.params);
+ if (SECSuccess != SECITEM_CopyItem(permarena, &newAlgParms,
+ &pki->algorithm.parameters) ) break;
+ rv = SEC_QuickDERDecodeItem(permarena, &pk->u.dsa.params,
+ nsslowkey_PQGParamsTemplate,
+ &newAlgParms);
+ break;
+ case SEC_OID_X942_DIFFIE_HELMAN_KEY:
+ pk->keyType = NSSLOWKEYDHKey;
+ prepare_low_dh_priv_key_for_asn1(pk);
+ if (SECSuccess != SECITEM_CopyItem(permarena, &newPrivateKey,
+ &pki->privateKey) ) break;
+ rv = SEC_QuickDERDecodeItem(permarena, pk,
+ nsslowkey_DHPrivateKeyTemplate,
+ &newPrivateKey);
+ break;
+#ifdef NSS_ENABLE_ECC
+ case SEC_OID_ANSIX962_EC_PUBLIC_KEY:
+ pk->keyType = NSSLOWKEYECKey;
+ prepare_low_ec_priv_key_for_asn1(pk);
+
+ fordebug = &pki->privateKey;
+ SEC_PRINT("seckey_decrypt_private_key()", "PrivateKey",
+ pk->keyType, fordebug);
+ if (SECSuccess != SECITEM_CopyItem(permarena, &newPrivateKey,
+ &pki->privateKey) ) break;
+ rv = SEC_QuickDERDecodeItem(permarena, pk,
+ nsslowkey_ECPrivateKeyTemplate,
+ &newPrivateKey);
+ if (rv != SECSuccess)
+ goto loser;
+
+ prepare_low_ecparams_for_asn1(&pk->u.ec.ecParams);
+
+ rv = SECITEM_CopyItem(permarena,
+ &pk->u.ec.ecParams.DEREncoding,
+ &pki->algorithm.parameters);
+
+ if (rv != SECSuccess)
+ goto loser;
+
+ /* Fill out the rest of EC params */
+ rv = LGEC_FillParams(permarena, &pk->u.ec.ecParams.DEREncoding,
+ &pk->u.ec.ecParams);
+
+ if (rv != SECSuccess)
+ goto loser;
+
+ if (pk->u.ec.publicValue.len != 0) {
+ pk->u.ec.publicValue.len >>= 3;
+ }
+
+ break;
+#endif /* NSS_ENABLE_ECC */
+ default:
+ rv = SECFailure;
+ break;
+ }
+ }
+ else if(PORT_GetError() == SEC_ERROR_BAD_DER)
+ {
+ PORT_SetError(SEC_ERROR_BAD_PASSWORD);
+ goto loser;
+ }
+ }
+
+ /* let success fall through */
+loser:
+ if(temparena != NULL)
+ PORT_FreeArena(temparena, PR_TRUE);
+ if(dest != NULL)
+ SECITEM_ZfreeItem(dest, PR_TRUE);
+
+ if(rv != SECSuccess)
+ {
+ if(permarena != NULL)
+ PORT_FreeArena(permarena, PR_TRUE);
+ pk = NULL;
+ }
+
+ return pk;
+}
+
+static NSSLOWKEYPrivateKey *
+seckey_decode_encrypted_private_key(NSSLOWKEYDBKey *dbkey, SDB *sdbpw)
+{
+ if( ( dbkey == NULL ) || ( sdbpw == NULL ) ) {
+ return NULL;
+ }
+
+ return seckey_decrypt_private_key(&(dbkey->derPK), sdbpw);
+}
+
+static NSSLOWKEYPrivateKey *
+seckey_get_private_key(NSSLOWKEYDBHandle *keydb, DBT *index, char **nickname,
+ SDB *sdbpw)
+{
+ NSSLOWKEYDBKey *dbkey = NULL;
+ NSSLOWKEYPrivateKey *pk = NULL;
+
+ if( ( keydb == NULL ) || ( index == NULL ) || ( sdbpw == NULL ) ) {
+ return NULL;
+ }
+
+ dbkey = get_dbkey(keydb, index);
+ if(dbkey == NULL) {
+ goto loser;
+ }
+
+ if ( nickname ) {
+ if ( dbkey->nickname && ( dbkey->nickname[0] != 0 ) ) {
+ *nickname = PORT_Strdup(dbkey->nickname);
+ } else {
+ *nickname = NULL;
+ }
+ }
+
+ pk = seckey_decode_encrypted_private_key(dbkey, sdbpw);
+
+ /* let success fall through */
+loser:
+
+ if ( dbkey != NULL ) {
+ sec_destroy_dbkey(dbkey);
+ }
+
+ return pk;
+}
+
+/*
+ * Find a key in the database, indexed by its public key modulus
+ * This is used to find keys that have been stored before their
+ * certificate arrives. Once the certificate arrives the key
+ * is looked up by the public modulus in the certificate, and the
+ * re-stored by its nickname.
+ */
+NSSLOWKEYPrivateKey *
+nsslowkey_FindKeyByPublicKey(NSSLOWKEYDBHandle *handle, SECItem *modulus,
+ SDB *sdbpw)
+{
+ DBT namekey;
+ NSSLOWKEYPrivateKey *pk = NULL;
+
+ if (handle == NULL) {
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ return NULL;
+ }
+
+ /* set up db key */
+ namekey.data = modulus->data;
+ namekey.size = modulus->len;
+
+ pk = seckey_get_private_key(handle, &namekey, NULL, sdbpw);
+
+ /* no need to free dbkey, since its on the stack, and the data it
+ * points to is owned by the database
+ */
+ return(pk);
+}
+
+char *
+nsslowkey_FindKeyNicknameByPublicKey(NSSLOWKEYDBHandle *handle,
+ SECItem *modulus, SDB *sdbpw)
+{
+ DBT namekey;
+ NSSLOWKEYPrivateKey *pk = NULL;
+ char *nickname = NULL;
+
+ if (handle == NULL) {
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ return NULL;
+ }
+
+ /* set up db key */
+ namekey.data = modulus->data;
+ namekey.size = modulus->len;
+
+ pk = seckey_get_private_key(handle, &namekey, &nickname, sdbpw);
+ if (pk) {
+ nsslowkey_DestroyPrivateKey(pk);
+ }
+
+ /* no need to free dbkey, since its on the stack, and the data it
+ * points to is owned by the database
+ */
+ return(nickname);
+}
+/* ===== ENCODING ROUTINES ===== */
+
+static SECStatus
+encodePWCheckEntry(PLArenaPool *arena, SECItem *entry, SECOidTag alg,
+ SECItem *encCheck)
+{
+ SECOidData *oidData;
+ SECStatus rv;
+
+ oidData = SECOID_FindOIDByTag(alg);
+ if ( oidData == NULL ) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ entry->len = 1 + oidData->oid.len + encCheck->len;
+ if ( arena ) {
+ entry->data = (unsigned char *)PORT_ArenaAlloc(arena, entry->len);
+ } else {
+ entry->data = (unsigned char *)PORT_Alloc(entry->len);
+ }
+
+ if ( entry->data == NULL ) {
+ goto loser;
+ }
+
+ /* first length of oid */
+ entry->data[0] = (unsigned char)oidData->oid.len;
+ /* next oid itself */
+ PORT_Memcpy(&entry->data[1], oidData->oid.data, oidData->oid.len);
+ /* finally the encrypted check string */
+ PORT_Memcpy(&entry->data[1+oidData->oid.len], encCheck->data,
+ encCheck->len);
+
+ return(SECSuccess);
+
+loser:
+ return(SECFailure);
+}
+
+
+#define MAX_DB_SIZE 0xffff
+/*
+ * Clear out all the keys in the existing database
+ */
+static SECStatus
+nsslowkey_ResetKeyDB(NSSLOWKEYDBHandle *handle)
+{
+ SECStatus rv;
+ int ret;
+ int errors = 0;
+
+ if ( handle->db == NULL ) {
+ return(SECSuccess);
+ }
+
+ if (handle->readOnly) {
+ /* set an error code */
+ return SECFailure;
+ }
+
+ if (handle->appname == NULL && handle->dbname == NULL) {
+ return SECFailure;
+ }
+
+ keydb_Close(handle);
+ if (handle->appname) {
+ handle->db=
+ rdbopen(handle->appname, handle->dbname, "key", NO_CREATE, NULL);
+ } else {
+ handle->db = dbopen( handle->dbname, NO_CREATE, 0600, DB_HASH, 0 );
+ }
+ if (handle->db == NULL) {
+ /* set an error code */
+ return SECFailure;
+ }
+
+ rv = makeGlobalVersion(handle);
+ if ( rv != SECSuccess ) {
+ errors++;
+ goto done;
+ }
+
+ if (handle->global_salt) {
+ rv = StoreKeyDBGlobalSalt(handle, handle->global_salt);
+ } else {
+ rv = makeGlobalSalt(handle);
+ if ( rv == SECSuccess ) {
+ handle->global_salt = GetKeyDBGlobalSalt(handle);
+ }
+ }
+ if ( rv != SECSuccess ) {
+ errors++;
+ }
+
+done:
+ /* sync the database */
+ ret = keydb_Sync(handle, 0);
+ db_InitComplete(handle->db);
+
+ return (errors == 0 ? SECSuccess : SECFailure);
+}
+
+static int
+keydb_Get(NSSLOWKEYDBHandle *kdb, DBT *key, DBT *data, unsigned int flags)
+{
+ PRStatus prstat;
+ int ret;
+ PRLock *kdbLock = kdb->lock;
+ DB *db = kdb->db;
+
+ PORT_Assert(kdbLock != NULL);
+ PZ_Lock(kdbLock);
+
+ ret = (* db->get)(db, key, data, flags);
+
+ prstat = PZ_Unlock(kdbLock);
+
+ return(ret);
+}
+
+static int
+keydb_Put(NSSLOWKEYDBHandle *kdb, DBT *key, DBT *data, unsigned int flags)
+{
+ PRStatus prstat;
+ int ret = 0;
+ PRLock *kdbLock = kdb->lock;
+ DB *db = kdb->db;
+
+ PORT_Assert(kdbLock != NULL);
+ PZ_Lock(kdbLock);
+
+ ret = (* db->put)(db, key, data, flags);
+
+ prstat = PZ_Unlock(kdbLock);
+
+ return(ret);
+}
+
+static int
+keydb_Sync(NSSLOWKEYDBHandle *kdb, unsigned int flags)
+{
+ PRStatus prstat;
+ int ret;
+ PRLock *kdbLock = kdb->lock;
+ DB *db = kdb->db;
+
+ PORT_Assert(kdbLock != NULL);
+ PZ_Lock(kdbLock);
+
+ ret = (* db->sync)(db, flags);
+
+ prstat = PZ_Unlock(kdbLock);
+
+ return(ret);
+}
+
+static int
+keydb_Del(NSSLOWKEYDBHandle *kdb, DBT *key, unsigned int flags)
+{
+ PRStatus prstat;
+ int ret;
+ PRLock *kdbLock = kdb->lock;
+ DB *db = kdb->db;
+
+ PORT_Assert(kdbLock != NULL);
+ PZ_Lock(kdbLock);
+
+ ret = (* db->del)(db, key, flags);
+
+ prstat = PZ_Unlock(kdbLock);
+
+ return(ret);
+}
+
+static int
+keydb_Seq(NSSLOWKEYDBHandle *kdb, DBT *key, DBT *data, unsigned int flags)
+{
+ PRStatus prstat;
+ int ret;
+ PRLock *kdbLock = kdb->lock;
+ DB *db = kdb->db;
+
+ PORT_Assert(kdbLock != NULL);
+ PZ_Lock(kdbLock);
+
+ ret = (* db->seq)(db, key, data, flags);
+
+ prstat = PZ_Unlock(kdbLock);
+
+ return(ret);
+}
+
+static void
+keydb_Close(NSSLOWKEYDBHandle *kdb)
+{
+ PRStatus prstat;
+ PRLock *kdbLock = kdb->lock;
+ DB *db = kdb->db;
+
+ PORT_Assert(kdbLock != NULL);
+ PZ_Lock(kdbLock);
+
+ (* db->close)(db);
+
+ prstat = PZ_Unlock(kdbLock);
+
+ return;
+}
+
+/*
+ * SDB Entry Points for the Key DB
+ */
+
+CK_RV
+lg_GetPWEntry(SDB *sdb, SDBPasswordEntry *entry)
+{
+ NSSLOWKEYDBHandle *keydb;
+ SECStatus rv;
+
+ keydb = lg_getKeyDB(sdb);
+ if (keydb == NULL) {
+ return CKR_TOKEN_WRITE_PROTECTED;
+ }
+ rv = nsslowkey_GetPWCheckEntry(keydb, entry);
+ if (rv != SECSuccess) {
+ return CKR_GENERAL_ERROR;
+ }
+ return CKR_OK;
+}
+
+CK_RV
+lg_PutPWEntry(SDB *sdb, SDBPasswordEntry *entry)
+{
+ NSSLOWKEYDBHandle *keydb;
+ SECStatus rv;
+
+ keydb = lg_getKeyDB(sdb);
+ if (keydb == NULL) {
+ return CKR_TOKEN_WRITE_PROTECTED;
+ }
+ rv = nsslowkey_PutPWCheckEntry(keydb, entry);
+ if (rv != SECSuccess) {
+ return CKR_GENERAL_ERROR;
+ }
+ return CKR_OK;
+}
+
+CK_RV
+lg_Reset(SDB *sdb)
+{
+ NSSLOWKEYDBHandle *keydb;
+ SECStatus rv;
+
+ keydb = lg_getKeyDB(sdb);
+ if (keydb == NULL) {
+ return CKR_TOKEN_WRITE_PROTECTED;
+ }
+ rv = nsslowkey_ResetKeyDB(keydb);
+ if (rv != SECSuccess) {
+ return CKR_GENERAL_ERROR;
+ }
+ return CKR_OK;
+}
+
diff --git a/security/nss/lib/softoken/legacydb/keydbi.h b/security/nss/lib/softoken/legacydb/keydbi.h
new file mode 100644
index 000000000..f7f427266
--- /dev/null
+++ b/security/nss/lib/softoken/legacydb/keydbi.h
@@ -0,0 +1,86 @@
+/*
+ * private.h - Private data structures for the software token library
+ *
+ * ***** 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 ***** */
+/* $Id$ */
+
+#ifndef _KEYDBI_H_
+#define _KEYDBI_H_
+
+#include "nspr.h"
+#include "seccomon.h"
+#include "mcom_db.h"
+
+/*
+ * Handle structure for open key databases
+ */
+struct NSSLOWKEYDBHandleStr {
+ DB *db;
+ DB *updatedb; /* used when updating an old version */
+ SECItem *global_salt; /* password hashing salt for this db */
+ int version; /* version of the database */
+ char *appname; /* multiaccess app name */
+ char *dbname; /* name of the openned DB */
+ PRBool readOnly; /* is the DB read only */
+ PRLock *lock;
+ PRInt32 ref; /* reference count */
+};
+
+/*
+** Typedef for callback for traversing key database.
+** "key" is the key used to index the data in the database (nickname)
+** "data" is the key data
+** "pdata" is the user's data
+*/
+typedef SECStatus (* NSSLOWKEYTraverseKeysFunc)(DBT *key, DBT *data, void *pdata);
+
+
+SEC_BEGIN_PROTOS
+
+/*
+** Traverse the entire key database, and pass the nicknames and keys to a
+** user supplied function.
+** "f" is the user function to call for each key
+** "udata" is the user's data, which is passed through to "f"
+*/
+extern SECStatus nsslowkey_TraverseKeys(NSSLOWKEYDBHandle *handle,
+ NSSLOWKEYTraverseKeysFunc f,
+ void *udata);
+
+SEC_END_PROTOS
+
+#endif /* _KEYDBI_H_ */
diff --git a/security/nss/lib/softoken/legacydb/legacydb.def b/security/nss/lib/softoken/legacydb/legacydb.def
new file mode 100644
index 000000000..3e3247099
--- /dev/null
+++ b/security/nss/lib/softoken/legacydb/legacydb.def
@@ -0,0 +1,64 @@
+;+#
+;+# ***** 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) 2000
+;+# the Initial Developer. All Rights Reserved.
+;+#
+;+# Contributor(s):
+;+# Dr Stephen Henson <stephen.henson@gemplus.com>
+;+#
+;+# 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 *****
+;+#
+;+# OK, this file is meant to support SUN, LINUX, AIX and WINDOWS
+;+# 1. For all unix platforms, the string ";-" means "remove this line"
+;+# 2. For all unix platforms, the string " DATA " will be removed from any
+;+# line on which it occurs.
+;+# 3. Lines containing ";+" will have ";+" removed on SUN and LINUX.
+;+# On AIX, lines containing ";+" will be removed.
+;+# 4. For all unix platforms, the string ";;" will thave the ";;" removed.
+;+# 5. For all unix platforms, after the above processing has taken place,
+;+# all characters after the first ";" on the line will be removed.
+;+# And for AIX, the first ";" will also be removed.
+;+# This file is passed directly to windows. Since ';' is a comment, all UNIX
+;+# directives are hidden behind ";", ";+", and ";-"
+;+LGDBM_3.12 { # NSS 3.12 release
+;+ global:
+LIBRARY lgdbm3 ;-
+EXPORTS ;-
+legacy_Open;
+legacy_Shutdown;
+legacy_ReadSecmodDB;
+legacy_ReleaseSecmodDBData;
+legacy_AddSecmodDB;
+legacy_DeleteSecmodDB;
+legacy_SetCryptFunctions;
+;+ local:
+;+ *;
+;+};
diff --git a/security/nss/lib/softoken/legacydb/lgattr.c b/security/nss/lib/softoken/legacydb/lgattr.c
new file mode 100644
index 000000000..428d2ae66
--- /dev/null
+++ b/security/nss/lib/softoken/legacydb/lgattr.c
@@ -0,0 +1,1748 @@
+/* ***** 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):
+ * Dr Vipul Gupta <vipul.gupta@sun.com>, Sun Microsystems Laboratories
+ *
+ * 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 ***** */
+/*
+ * Internal PKCS #11 functions. Should only be called by pkcs11.c
+ */
+#include "pkcs11.h"
+#include "lgdb.h"
+
+#include "pcertt.h"
+#include "lowkeyi.h"
+#include "pcert.h"
+#include "blapi.h"
+#include "secerr.h"
+
+/*
+ * Cache the object we are working on during Set's and Get's
+ */
+typedef struct LGObjectCacheStr {
+ CK_OBJECT_CLASS objclass;
+ CK_OBJECT_HANDLE handle;
+ SDB *sdb;
+ void *objectInfo;
+ LGFreeFunc infoFree;
+ SECItem dbKey;
+} LGObjectCache;
+
+static const CK_OBJECT_HANDLE lg_classArray[] = {
+ 0, CKO_PRIVATE_KEY, CKO_PUBLIC_KEY, CKO_SECRET_KEY,
+ CKO_NETSCAPE_TRUST, CKO_NETSCAPE_CRL, CKO_NETSCAPE_SMIME,
+ CKO_CERTIFICATE };
+
+#define handleToClass(handle) \
+ lg_classArray[((handle & LG_TOKEN_TYPE_MASK))>>LG_TOKEN_TYPE_SHIFT]
+
+
+static void lg_DestroyObjectCache(LGObjectCache *obj);
+
+static LGObjectCache *
+lg_NewObjectCache(SDB *sdb, const SECItem *dbKey, CK_OBJECT_HANDLE handle)
+{
+ LGObjectCache *obj = NULL;
+ SECStatus rv;
+
+ obj = PORT_New(LGObjectCache);
+ if (obj == NULL) {
+ return NULL;
+ }
+
+ obj->objclass = handleToClass(handle);
+ obj->handle = handle;
+ obj->sdb = sdb;
+ obj->objectInfo = NULL;
+ obj->infoFree = NULL;
+ lg_DBLock(sdb);
+ if (dbKey == NULL) {
+ dbKey = lg_lookupTokenKeyByHandle(sdb,handle);
+ }
+ if (dbKey == NULL) {
+ lg_DBUnlock(sdb);
+ goto loser;
+ }
+ rv = SECITEM_CopyItem(NULL,&obj->dbKey,dbKey);
+ lg_DBUnlock(sdb);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ return obj;
+loser:
+ if (obj) {
+ (void) lg_DestroyObjectCache(obj);
+ }
+ return NULL;
+
+}
+
+/*
+ * free all the data associated with an object. Object reference count must
+ * be 'zero'.
+ */
+static void
+lg_DestroyObjectCache(LGObjectCache *obj)
+{
+ if (obj->dbKey.data) {
+ PORT_Free(obj->dbKey.data);
+ obj->dbKey.data = NULL;
+ }
+ if (obj->objectInfo) {
+ (*obj->infoFree)(obj->objectInfo);
+ obj->objectInfo = NULL;
+ obj->infoFree = NULL;
+ }
+ PORT_Free(obj);
+}
+/*
+ * ******************** Attribute Utilities *******************************
+ */
+
+static CK_RV
+lg_ULongAttribute(CK_ATTRIBUTE *attr, CK_ATTRIBUTE_TYPE type, CK_ULONG value)
+{
+ unsigned char *data;
+ int i;
+
+ if (attr->pValue == NULL) {
+ attr->ulValueLen = 4;
+ return CKR_OK;
+ }
+ if (attr->ulValueLen < 4) {
+ attr->ulValueLen = (CK_ULONG) -1;
+ return CKR_BUFFER_TOO_SMALL;
+ }
+
+ data = (unsigned char *)attr->pValue;
+ for (i=0; i < 4; i++) {
+ data[i] = (value >> ((3-i)*8)) & 0xff;
+ }
+ attr->ulValueLen = 4;
+ return CKR_OK;
+}
+
+static CK_RV
+lg_CopyAttribute(CK_ATTRIBUTE *attr, CK_ATTRIBUTE_TYPE type,
+ CK_VOID_PTR value, CK_ULONG len)
+{
+
+ if (attr->pValue == NULL) {
+ attr->ulValueLen = len;
+ return CKR_OK;
+ }
+ if (attr->ulValueLen < len) {
+ attr->ulValueLen = (CK_ULONG) -1;
+ return CKR_BUFFER_TOO_SMALL;
+ }
+ PORT_Memcpy(attr->pValue,value,len);
+ attr->ulValueLen = len;
+ return CKR_OK;
+}
+
+static CK_RV
+lg_CopyAttributeSigned(CK_ATTRIBUTE *attribute, CK_ATTRIBUTE_TYPE type,
+ void *value, CK_ULONG len)
+{
+ unsigned char * dval = (unsigned char *)value;
+ if (*dval == 0) {
+ dval++;
+ len--;
+ }
+ return lg_CopyAttribute(attribute,type,dval,len);
+}
+
+static CK_RV
+lg_CopyPrivAttribute(CK_ATTRIBUTE *attribute, CK_ATTRIBUTE_TYPE type,
+ void *value, CK_ULONG len, SDB *sdbpw)
+{
+ SECItem plainText, *cipherText = NULL;
+ CK_RV crv = CKR_USER_NOT_LOGGED_IN;
+ SECStatus rv;
+
+ plainText.data = value;
+ plainText.len = len;
+ rv = lg_util_encrypt(NULL, sdbpw, &plainText, &cipherText);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ crv = lg_CopyAttribute(attribute,type,cipherText->data,cipherText->len);
+loser:
+ if (cipherText) {
+ SECITEM_FreeItem(cipherText,PR_TRUE);
+ }
+ return crv;
+}
+
+static CK_RV
+lg_CopyPrivAttrSigned(CK_ATTRIBUTE *attribute, CK_ATTRIBUTE_TYPE type,
+ void *value, CK_ULONG len, SDB *sdbpw)
+{
+ unsigned char * dval = (unsigned char *)value;
+
+ if (*dval == 0) {
+ dval++;
+ len--;
+ }
+ return lg_CopyPrivAttribute(attribute,type,dval,len,sdbpw);
+}
+
+static CK_RV
+lg_invalidAttribute(CK_ATTRIBUTE *attr)
+{
+ attr->ulValueLen = (CK_ULONG) -1;
+ return CKR_ATTRIBUTE_TYPE_INVALID;
+}
+
+
+#define LG_DEF_ATTRIBUTE(value,len) \
+ { 0, value, len }
+
+#define LG_CLONE_ATTR(attribute, type, staticAttr) \
+ lg_CopyAttribute(attribute, type, staticAttr.pValue, staticAttr.ulValueLen)
+
+CK_BBOOL lg_staticTrueValue = CK_TRUE;
+CK_BBOOL lg_staticFalseValue = CK_FALSE;
+static const CK_ATTRIBUTE lg_StaticTrueAttr =
+ LG_DEF_ATTRIBUTE(&lg_staticTrueValue,sizeof(lg_staticTrueValue));
+static const CK_ATTRIBUTE lg_StaticFalseAttr =
+ LG_DEF_ATTRIBUTE(&lg_staticFalseValue,sizeof(lg_staticFalseValue));
+static const CK_ATTRIBUTE lg_StaticNullAttr = LG_DEF_ATTRIBUTE(NULL,0);
+char lg_StaticOneValue = 1;
+static const CK_ATTRIBUTE lg_StaticOneAttr =
+ LG_DEF_ATTRIBUTE(&lg_StaticOneValue,sizeof(lg_StaticOneValue));
+
+/*
+ * helper functions which get the database and call the underlying
+ * low level database function.
+ */
+static char *
+lg_FindKeyNicknameByPublicKey(SDB *sdb, SECItem *dbKey)
+{
+ NSSLOWKEYDBHandle *keyHandle;
+ char * label;
+
+ keyHandle = lg_getKeyDB(sdb);
+ if (!keyHandle) {
+ return NULL;
+ }
+
+ label = nsslowkey_FindKeyNicknameByPublicKey(keyHandle, dbKey,
+ sdb);
+ return label;
+}
+
+
+NSSLOWKEYPrivateKey *
+lg_FindKeyByPublicKey(SDB *sdb, SECItem *dbKey)
+{
+ NSSLOWKEYPrivateKey *privKey;
+ NSSLOWKEYDBHandle *keyHandle;
+
+ keyHandle = lg_getKeyDB(sdb);
+ if (keyHandle == NULL) {
+ return NULL;
+ }
+ privKey = nsslowkey_FindKeyByPublicKey(keyHandle, dbKey, sdb);
+ if (privKey == NULL) {
+ return NULL;
+ }
+ return privKey;
+}
+
+static certDBEntrySMime *
+lg_getSMime(LGObjectCache *obj)
+{
+ certDBEntrySMime *entry;
+ NSSLOWCERTCertDBHandle *certHandle;
+
+ if (obj->objclass != CKO_NETSCAPE_SMIME) {
+ return NULL;
+ }
+ if (obj->objectInfo) {
+ return (certDBEntrySMime *)obj->objectInfo;
+ }
+
+ certHandle = lg_getCertDB(obj->sdb);
+ if (!certHandle) {
+ return NULL;
+ }
+ entry = nsslowcert_ReadDBSMimeEntry(certHandle, (char *)obj->dbKey.data);
+ obj->objectInfo = (void *)entry;
+ obj->infoFree = (LGFreeFunc) nsslowcert_DestroyDBEntry;
+ return entry;
+}
+
+static certDBEntryRevocation *
+lg_getCrl(LGObjectCache *obj)
+{
+ certDBEntryRevocation *crl;
+ PRBool isKrl;
+ NSSLOWCERTCertDBHandle *certHandle;
+
+ if (obj->objclass != CKO_NETSCAPE_CRL) {
+ return NULL;
+ }
+ if (obj->objectInfo) {
+ return (certDBEntryRevocation *)obj->objectInfo;
+ }
+
+ isKrl = (PRBool) (obj->handle == LG_TOKEN_KRL_HANDLE);
+ certHandle = lg_getCertDB(obj->sdb);
+ if (!certHandle) {
+ return NULL;
+ }
+
+ crl = nsslowcert_FindCrlByKey(certHandle, &obj->dbKey, isKrl);
+ obj->objectInfo = (void *)crl;
+ obj->infoFree = (LGFreeFunc) nsslowcert_DestroyDBEntry;
+ return crl;
+}
+
+static NSSLOWCERTCertificate *
+lg_getCert(LGObjectCache *obj, NSSLOWCERTCertDBHandle *certHandle)
+{
+ NSSLOWCERTCertificate *cert;
+ CK_OBJECT_CLASS objClass = obj->objclass;
+
+ if ((objClass != CKO_CERTIFICATE) && (objClass != CKO_NETSCAPE_TRUST)) {
+ return NULL;
+ }
+ if (objClass == CKO_CERTIFICATE && obj->objectInfo) {
+ return (NSSLOWCERTCertificate *)obj->objectInfo;
+ }
+ cert = nsslowcert_FindCertByKey(certHandle, &obj->dbKey);
+ if (objClass == CKO_CERTIFICATE) {
+ obj->objectInfo = (void *)cert;
+ obj->infoFree = (LGFreeFunc) nsslowcert_DestroyCertificate ;
+ }
+ return cert;
+}
+
+static NSSLOWCERTTrust *
+lg_getTrust(LGObjectCache *obj, NSSLOWCERTCertDBHandle *certHandle)
+{
+ NSSLOWCERTTrust *trust;
+
+ if (obj->objclass != CKO_NETSCAPE_TRUST) {
+ return NULL;
+ }
+ if (obj->objectInfo) {
+ return (NSSLOWCERTTrust *)obj->objectInfo;
+ }
+ trust = nsslowcert_FindTrustByKey(certHandle, &obj->dbKey);
+ obj->objectInfo = (void *)trust;
+ obj->infoFree = (LGFreeFunc) nsslowcert_DestroyTrust ;
+ return trust;
+}
+
+static NSSLOWKEYPublicKey *
+lg_GetPublicKey(LGObjectCache *obj)
+{
+ NSSLOWKEYPublicKey *pubKey;
+ NSSLOWKEYPrivateKey *privKey;
+
+ if (obj->objclass != CKO_PUBLIC_KEY) {
+ return NULL;
+ }
+ if (obj->objectInfo) {
+ return (NSSLOWKEYPublicKey *)obj->objectInfo;
+ }
+ privKey = lg_FindKeyByPublicKey(obj->sdb, &obj->dbKey);
+ if (privKey == NULL) {
+ return NULL;
+ }
+ pubKey = nsslowkey_ConvertToPublicKey(privKey);
+ nsslowkey_DestroyPrivateKey(privKey);
+ obj->objectInfo = (void *) pubKey;
+ obj->infoFree = (LGFreeFunc) nsslowkey_DestroyPublicKey ;
+ return pubKey;
+}
+
+/*
+ * we need two versions of lg_GetPrivateKey. One version that takes the
+ * DB handle so we can pass the handle we have already acquired in,
+ * rather than going through the 'getKeyDB' code again,
+ * which may fail the second time and another which just aquires
+ * the key handle from the sdb (where we don't already have a key handle.
+ * This version does the former.
+ */
+static NSSLOWKEYPrivateKey *
+lg_GetPrivateKeyWithDB(LGObjectCache *obj, NSSLOWKEYDBHandle *keyHandle)
+{
+ NSSLOWKEYPrivateKey *privKey;
+
+ if ((obj->objclass != CKO_PRIVATE_KEY) &&
+ (obj->objclass != CKO_SECRET_KEY)) {
+ return NULL;
+ }
+ if (obj->objectInfo) {
+ return (NSSLOWKEYPrivateKey *)obj->objectInfo;
+ }
+ privKey = nsslowkey_FindKeyByPublicKey(keyHandle, &obj->dbKey, obj->sdb);
+ if (privKey == NULL) {
+ return NULL;
+ }
+ obj->objectInfo = (void *) privKey;
+ obj->infoFree = (LGFreeFunc) nsslowkey_DestroyPrivateKey ;
+ return privKey;
+}
+
+/* this version does the latter */
+static NSSLOWKEYPrivateKey *
+lg_GetPrivateKey(LGObjectCache *obj)
+{
+ NSSLOWKEYDBHandle *keyHandle;
+ NSSLOWKEYPrivateKey *privKey;
+
+ keyHandle = lg_getKeyDB(obj->sdb);
+ if (!keyHandle) {
+ return NULL;
+ }
+ privKey = lg_GetPrivateKeyWithDB(obj, keyHandle);
+ return privKey;
+}
+
+/* lg_GetPubItem returns data associated with the public key.
+ * one only needs to free the public key. This comment is here
+ * because this sematic would be non-obvious otherwise. All callers
+ * should include this comment.
+ */
+static SECItem *
+lg_GetPubItem(NSSLOWKEYPublicKey *pubKey) {
+ SECItem *pubItem = NULL;
+ /* get value to compare from the cert's public key */
+ switch ( pubKey->keyType ) {
+ case NSSLOWKEYRSAKey:
+ pubItem = &pubKey->u.rsa.modulus;
+ break;
+ case NSSLOWKEYDSAKey:
+ pubItem = &pubKey->u.dsa.publicValue;
+ break;
+ case NSSLOWKEYDHKey:
+ pubItem = &pubKey->u.dh.publicValue;
+ break;
+#ifdef NSS_ENABLE_ECC
+ case NSSLOWKEYECKey:
+ pubItem = &pubKey->u.ec.publicValue;
+ break;
+#endif /* NSS_ENABLE_ECC */
+ default:
+ break;
+ }
+ return pubItem;
+}
+
+static const SEC_ASN1Template lg_SerialTemplate[] = {
+ { SEC_ASN1_INTEGER, offsetof(NSSLOWCERTCertificate,serialNumber) },
+ { 0 }
+};
+
+static CK_RV
+lg_FindRSAPublicKeyAttribute(NSSLOWKEYPublicKey *key, CK_ATTRIBUTE_TYPE type,
+ CK_ATTRIBUTE *attribute)
+{
+ unsigned char hash[SHA1_LENGTH];
+ CK_KEY_TYPE keyType = CKK_RSA;
+
+ switch (type) {
+ case CKA_KEY_TYPE:
+ return lg_ULongAttribute(attribute, type, keyType);
+ case CKA_ID:
+ SHA1_HashBuf(hash,key->u.rsa.modulus.data,key->u.rsa.modulus.len);
+ return lg_CopyAttribute(attribute,type,hash,SHA1_LENGTH);
+ case CKA_DERIVE:
+ return LG_CLONE_ATTR(attribute,type,lg_StaticFalseAttr);
+ case CKA_ENCRYPT:
+ case CKA_VERIFY:
+ case CKA_VERIFY_RECOVER:
+ case CKA_WRAP:
+ return LG_CLONE_ATTR(attribute,type,lg_StaticTrueAttr);
+ case CKA_MODULUS:
+ return lg_CopyAttributeSigned(attribute,type,key->u.rsa.modulus.data,
+ key->u.rsa.modulus.len);
+ case CKA_PUBLIC_EXPONENT:
+ return lg_CopyAttributeSigned(attribute, type,
+ key->u.rsa.publicExponent.data,
+ key->u.rsa.publicExponent.len);
+ default:
+ break;
+ }
+ return lg_invalidAttribute(attribute);
+}
+
+static CK_RV
+lg_FindDSAPublicKeyAttribute(NSSLOWKEYPublicKey *key, CK_ATTRIBUTE_TYPE type,
+ CK_ATTRIBUTE *attribute)
+{
+ unsigned char hash[SHA1_LENGTH];
+ CK_KEY_TYPE keyType = CKK_DSA;
+
+ switch (type) {
+ case CKA_KEY_TYPE:
+ return lg_ULongAttribute(attribute, type, keyType);
+ case CKA_ID:
+ SHA1_HashBuf(hash,key->u.dsa.publicValue.data,
+ key->u.dsa.publicValue.len);
+ return lg_CopyAttribute(attribute,type,hash,SHA1_LENGTH);
+ case CKA_DERIVE:
+ case CKA_ENCRYPT:
+ case CKA_VERIFY_RECOVER:
+ case CKA_WRAP:
+ return LG_CLONE_ATTR(attribute,type,lg_StaticFalseAttr);
+ case CKA_VERIFY:
+ return LG_CLONE_ATTR(attribute,type,lg_StaticTrueAttr);
+ case CKA_VALUE:
+ return lg_CopyAttributeSigned(attribute,type,
+ key->u.dsa.publicValue.data,
+ key->u.dsa.publicValue.len);
+ case CKA_PRIME:
+ return lg_CopyAttributeSigned(attribute,type,
+ key->u.dsa.params.prime.data,
+ key->u.dsa.params.prime.len);
+ case CKA_SUBPRIME:
+ return lg_CopyAttributeSigned(attribute,type,
+ key->u.dsa.params.subPrime.data,
+ key->u.dsa.params.subPrime.len);
+ case CKA_BASE:
+ return lg_CopyAttributeSigned(attribute,type,
+ key->u.dsa.params.base.data,
+ key->u.dsa.params.base.len);
+ default:
+ break;
+ }
+ return lg_invalidAttribute(attribute);
+}
+
+static CK_RV
+lg_FindDHPublicKeyAttribute(NSSLOWKEYPublicKey *key, CK_ATTRIBUTE_TYPE type,
+ CK_ATTRIBUTE *attribute)
+{
+ unsigned char hash[SHA1_LENGTH];
+ CK_KEY_TYPE keyType = CKK_DH;
+
+ switch (type) {
+ case CKA_KEY_TYPE:
+ return lg_ULongAttribute(attribute, type, keyType);
+ case CKA_ID:
+ SHA1_HashBuf(hash,key->u.dh.publicValue.data,key->u.dh.publicValue.len);
+ return lg_CopyAttribute(attribute,type,hash,SHA1_LENGTH);
+ case CKA_DERIVE:
+ return LG_CLONE_ATTR(attribute,type,lg_StaticTrueAttr);
+ case CKA_ENCRYPT:
+ case CKA_VERIFY:
+ case CKA_VERIFY_RECOVER:
+ case CKA_WRAP:
+ return LG_CLONE_ATTR(attribute,type,lg_StaticFalseAttr);
+ case CKA_VALUE:
+ return lg_CopyAttributeSigned(attribute,type,
+ key->u.dh.publicValue.data,
+ key->u.dh.publicValue.len);
+ case CKA_PRIME:
+ return lg_CopyAttributeSigned(attribute,type,key->u.dh.prime.data,
+ key->u.dh.prime.len);
+ case CKA_BASE:
+ return lg_CopyAttributeSigned(attribute,type,key->u.dh.base.data,
+ key->u.dh.base.len);
+ default:
+ break;
+ }
+ return lg_invalidAttribute(attribute);
+}
+
+#ifdef NSS_ENABLE_ECC
+static CK_RV
+lg_FindECPublicKeyAttribute(NSSLOWKEYPublicKey *key, CK_ATTRIBUTE_TYPE type,
+ CK_ATTRIBUTE *attribute)
+{
+ unsigned char hash[SHA1_LENGTH];
+ CK_KEY_TYPE keyType = CKK_EC;
+
+ switch (type) {
+ case CKA_KEY_TYPE:
+ return lg_ULongAttribute(attribute, type, keyType);
+ case CKA_ID:
+ SHA1_HashBuf(hash, key->u.ec.publicValue.data,
+ key->u.ec.publicValue.len);
+ return lg_CopyAttribute(attribute,type,hash,SHA1_LENGTH);
+ case CKA_DERIVE:
+ case CKA_VERIFY:
+ return LG_CLONE_ATTR(attribute,type,lg_StaticTrueAttr);
+ case CKA_ENCRYPT:
+ case CKA_VERIFY_RECOVER:
+ case CKA_WRAP:
+ return LG_CLONE_ATTR(attribute,type,lg_StaticFalseAttr);
+ case CKA_EC_PARAMS:
+ return lg_CopyAttributeSigned(attribute,type,
+ key->u.ec.ecParams.DEREncoding.data,
+ key->u.ec.ecParams.DEREncoding.len);
+ case CKA_EC_POINT:
+ return lg_CopyAttributeSigned(attribute, type,
+ key->u.ec.publicValue.data,
+ key->u.ec.publicValue.len);
+ default:
+ break;
+ }
+ return lg_invalidAttribute(attribute);
+}
+#endif /* NSS_ENABLE_ECC */
+
+
+static CK_RV
+lg_FindPublicKeyAttribute(LGObjectCache *obj, CK_ATTRIBUTE_TYPE type,
+ CK_ATTRIBUTE *attribute)
+{
+ NSSLOWKEYPublicKey *key;
+ CK_RV crv;
+ char *label;
+
+ switch (type) {
+ case CKA_PRIVATE:
+ case CKA_SENSITIVE:
+ case CKA_ALWAYS_SENSITIVE:
+ case CKA_NEVER_EXTRACTABLE:
+ return LG_CLONE_ATTR(attribute,type,lg_StaticFalseAttr);
+ case CKA_MODIFIABLE:
+ case CKA_EXTRACTABLE:
+ return LG_CLONE_ATTR(attribute,type,lg_StaticTrueAttr);
+ case CKA_LABEL:
+ label = lg_FindKeyNicknameByPublicKey(obj->sdb, &obj->dbKey);
+ if (label == NULL) {
+ return LG_CLONE_ATTR(attribute,type,lg_StaticOneAttr);
+ }
+ crv = lg_CopyAttribute(attribute,type,label,PORT_Strlen(label));
+ PORT_Free(label);
+ return crv;
+ default:
+ break;
+ }
+
+ key = lg_GetPublicKey(obj);
+ if (key == NULL) {
+ return CKR_OBJECT_HANDLE_INVALID;
+ }
+
+ switch (key->keyType) {
+ case NSSLOWKEYRSAKey:
+ return lg_FindRSAPublicKeyAttribute(key,type,attribute);
+ case NSSLOWKEYDSAKey:
+ return lg_FindDSAPublicKeyAttribute(key,type,attribute);
+ case NSSLOWKEYDHKey:
+ return lg_FindDHPublicKeyAttribute(key,type,attribute);
+#ifdef NSS_ENABLE_ECC
+ case NSSLOWKEYECKey:
+ return lg_FindECPublicKeyAttribute(key,type,attribute);
+#endif /* NSS_ENABLE_ECC */
+ default:
+ break;
+ }
+
+ return lg_invalidAttribute(attribute);
+}
+
+static CK_RV
+lg_FindSecretKeyAttribute(LGObjectCache *obj, CK_ATTRIBUTE_TYPE type,
+ CK_ATTRIBUTE *attribute)
+{
+ NSSLOWKEYPrivateKey *key;
+ char *label;
+ unsigned char *keyString;
+ CK_RV crv;
+ int keyTypeLen;
+ CK_ULONG keyLen;
+ CK_KEY_TYPE keyType;
+ PRUint32 keyTypeStorage;
+
+ switch (type) {
+ case CKA_PRIVATE:
+ case CKA_SENSITIVE:
+ case CKA_ALWAYS_SENSITIVE:
+ case CKA_EXTRACTABLE:
+ case CKA_DERIVE:
+ case CKA_ENCRYPT:
+ case CKA_DECRYPT:
+ case CKA_SIGN:
+ case CKA_VERIFY:
+ case CKA_WRAP:
+ case CKA_UNWRAP:
+ case CKA_MODIFIABLE:
+ return LG_CLONE_ATTR(attribute,type,lg_StaticTrueAttr);
+ case CKA_NEVER_EXTRACTABLE:
+ return LG_CLONE_ATTR(attribute,type,lg_StaticFalseAttr);
+ case CKA_LABEL:
+ label = lg_FindKeyNicknameByPublicKey(obj->sdb, &obj->dbKey);
+ if (label == NULL) {
+ return LG_CLONE_ATTR(attribute,type,lg_StaticNullAttr);
+ }
+ crv = lg_CopyAttribute(attribute,type,label,PORT_Strlen(label));
+ PORT_Free(label);
+ return crv;
+ case CKA_ID:
+ return lg_CopyAttribute(attribute,type,obj->dbKey.data,
+ obj->dbKey.len);
+ case CKA_KEY_TYPE:
+ case CKA_VALUE_LEN:
+ case CKA_VALUE:
+ break;
+ default:
+ return lg_invalidAttribute(attribute);
+ }
+
+ key = lg_GetPrivateKey(obj);
+ if (key == NULL) {
+ return CKR_OBJECT_HANDLE_INVALID;
+ }
+ switch (type) {
+ case CKA_KEY_TYPE:
+ /* handle legacy databases. In legacy databases key_type was stored
+ * in host order, with any leading zeros stripped off. Only key types
+ * under 0x1f (AES) were stored. We assume that any values which are
+ * either 1 byte long (big endian), or have byte[0] between 0 and
+ * 0x7f and bytes[1]-bytes[3] equal to '0' (little endian). All other
+ * values are assumed to be from the new database, which is always 4
+ * bytes in network order */
+ keyType=0;
+ keyString = key->u.rsa.coefficient.data;
+ keyTypeLen = key->u.rsa.coefficient.len;
+
+
+ /*
+ * Because of various endian and word lengths The database may have
+ * stored the keyType value in one of the following formats:
+ * (kt) <= 0x1f
+ * length data
+ * Big Endian, pre-3.9, all lengths: 1 (kt)
+ * Little Endian, pre-3.9, 32 bits: 4 (kt) 0 0 0
+ * Little Endian, pre-3.9, 64 bits: 8 (kt) 0 0 0 0 0 0 0
+ * All platforms, 3.9, 32 bits: 4 0 0 0 (kt)
+ * Big Endian, 3.9, 64 bits: 8 0 0 0 (kt) 0 0 0 0
+ * Little Endian, 3.9, 64 bits: 8 0 0 0 0 0 0 0 (kt)
+ * All platforms, >= 3.9.1, all lengths: 4 (a) k1 k2 k3
+ * where (a) is 0 or >= 0x80. currently (a) can only be 0.
+ */
+ /*
+ * this key was written on a 64 bit platform with a using NSS 3.9
+ * or earlier. Reduce the 64 bit possibilities above. When we are
+ * through, we will only have:
+ *
+ * Big Endian, pre-3.9, all lengths: 1 (kt)
+ * Little Endian, pre-3.9, all lengths: 4 (kt) 0 0 0
+ * All platforms, 3.9, all lengths: 4 0 0 0 (kt)
+ * All platforms, => 3.9.1, all lengths: 4 (a) k1 k2 k3
+ */
+ if (keyTypeLen == 8) {
+ keyTypeStorage = *(PRUint32 *) keyString;
+ if (keyTypeStorage == 0) {
+ keyString += sizeof(PRUint32);
+ }
+ keyTypeLen = 4;
+ }
+ /*
+ * Now Handle:
+ *
+ * All platforms, 3.9, all lengths: 4 0 0 0 (kt)
+ * All platforms, => 3.9.1, all lengths: 4 (a) k1 k2 k3
+ *
+ * NOTE: if kt == 0 or ak1k2k3 == 0, the test fails and
+ * we handle it as:
+ *
+ * Little Endian, pre-3.9, all lengths: 4 (kt) 0 0 0
+ */
+ if (keyTypeLen == sizeof(keyTypeStorage) &&
+ (((keyString[0] & 0x80) == 0x80) ||
+ !((keyString[1] == 0) && (keyString[2] == 0)
+ && (keyString[3] == 0))) ) {
+ PORT_Memcpy(&keyTypeStorage, keyString, sizeof(keyTypeStorage));
+ keyType = (CK_KEY_TYPE) PR_ntohl(keyTypeStorage);
+ } else {
+ /*
+ * Now Handle:
+ *
+ * Big Endian, pre-3.9, all lengths: 1 (kt)
+ * Little Endian, pre-3.9, all lengths: 4 (kt) 0 0 0
+ * -- KeyType == 0 all other cases ---: 4 0 0 0 0
+ */
+ keyType = (CK_KEY_TYPE) keyString[0] ;
+ }
+ return lg_ULongAttribute(attribute, type, keyType);
+ case CKA_VALUE:
+ return lg_CopyPrivAttribute(attribute,type,key->u.rsa.privateExponent.data,
+ key->u.rsa.privateExponent.len, obj->sdb);
+ case CKA_VALUE_LEN:
+ keyLen=key->u.rsa.privateExponent.len;
+ return lg_ULongAttribute(attribute,type, keyLen);
+ }
+ return lg_invalidAttribute(attribute);
+}
+
+static CK_RV
+lg_FindRSAPrivateKeyAttribute(NSSLOWKEYPrivateKey *key, CK_ATTRIBUTE_TYPE type,
+ CK_ATTRIBUTE *attribute, SDB *sdbpw)
+{
+ unsigned char hash[SHA1_LENGTH];
+ CK_KEY_TYPE keyType = CKK_RSA;
+
+ switch (type) {
+ case CKA_KEY_TYPE:
+ return lg_ULongAttribute(attribute, type, keyType);
+ case CKA_ID:
+ SHA1_HashBuf(hash,key->u.rsa.modulus.data,key->u.rsa.modulus.len);
+ return lg_CopyAttribute(attribute,type,hash,SHA1_LENGTH);
+ case CKA_DERIVE:
+ return LG_CLONE_ATTR(attribute,type,lg_StaticFalseAttr);
+ case CKA_DECRYPT:
+ case CKA_SIGN:
+ case CKA_SIGN_RECOVER:
+ case CKA_UNWRAP:
+ return LG_CLONE_ATTR(attribute, type,lg_StaticTrueAttr);
+ case CKA_MODULUS:
+ return lg_CopyAttributeSigned(attribute,type,key->u.rsa.modulus.data,
+ key->u.rsa.modulus.len);
+ case CKA_PUBLIC_EXPONENT:
+ return lg_CopyAttributeSigned(attribute, type,
+ key->u.rsa.publicExponent.data,
+ key->u.rsa.publicExponent.len);
+ case CKA_PRIVATE_EXPONENT:
+ return lg_CopyPrivAttrSigned(attribute,type,
+ key->u.rsa.privateExponent.data,
+ key->u.rsa.privateExponent.len, sdbpw);
+ case CKA_PRIME_1:
+ return lg_CopyPrivAttrSigned(attribute, type, key->u.rsa.prime1.data,
+ key->u.rsa.prime1.len, sdbpw);
+ case CKA_PRIME_2:
+ return lg_CopyPrivAttrSigned(attribute, type, key->u.rsa.prime2.data,
+ key->u.rsa.prime2.len, sdbpw);
+ case CKA_EXPONENT_1:
+ return lg_CopyPrivAttrSigned(attribute, type,
+ key->u.rsa.exponent1.data,
+ key->u.rsa.exponent1.len, sdbpw);
+ case CKA_EXPONENT_2:
+ return lg_CopyPrivAttrSigned(attribute, type,
+ key->u.rsa.exponent2.data,
+ key->u.rsa.exponent2.len, sdbpw);
+ case CKA_COEFFICIENT:
+ return lg_CopyPrivAttrSigned(attribute, type,
+ key->u.rsa.coefficient.data,
+ key->u.rsa.coefficient.len, sdbpw);
+ default:
+ break;
+ }
+ return lg_invalidAttribute(attribute);
+}
+
+static CK_RV
+lg_FindDSAPrivateKeyAttribute(NSSLOWKEYPrivateKey *key, CK_ATTRIBUTE_TYPE type,
+ CK_ATTRIBUTE *attribute, SDB *sdbpw)
+{
+ unsigned char hash[SHA1_LENGTH];
+ CK_KEY_TYPE keyType = CKK_DSA;
+
+ switch (type) {
+ case CKA_KEY_TYPE:
+ return lg_ULongAttribute(attribute, type, keyType);
+ case CKA_ID:
+ SHA1_HashBuf(hash,key->u.dsa.publicValue.data,
+ key->u.dsa.publicValue.len);
+ return lg_CopyAttribute(attribute,type,hash,SHA1_LENGTH);
+ case CKA_DERIVE:
+ case CKA_DECRYPT:
+ case CKA_SIGN_RECOVER:
+ case CKA_UNWRAP:
+ return LG_CLONE_ATTR(attribute,type,lg_StaticFalseAttr);
+ case CKA_SIGN:
+ return LG_CLONE_ATTR(attribute,type,lg_StaticTrueAttr);
+ case CKA_VALUE:
+ return lg_CopyPrivAttrSigned(attribute, type,
+ key->u.dsa.privateValue.data,
+ key->u.dsa.privateValue.len, sdbpw);
+ case CKA_PRIME:
+ return lg_CopyAttributeSigned(attribute, type,
+ key->u.dsa.params.prime.data,
+ key->u.dsa.params.prime.len);
+ case CKA_SUBPRIME:
+ return lg_CopyAttributeSigned(attribute, type,
+ key->u.dsa.params.subPrime.data,
+ key->u.dsa.params.subPrime.len);
+ case CKA_BASE:
+ return lg_CopyAttributeSigned(attribute, type,
+ key->u.dsa.params.base.data,
+ key->u.dsa.params.base.len);
+ case CKA_NETSCAPE_DB:
+ return lg_CopyAttributeSigned(attribute, type,
+ key->u.dsa.publicValue.data,
+ key->u.dsa.publicValue.len);
+ default:
+ break;
+ }
+ return lg_invalidAttribute(attribute);
+}
+
+static CK_RV
+lg_FindDHPrivateKeyAttribute(NSSLOWKEYPrivateKey *key, CK_ATTRIBUTE_TYPE type,
+ CK_ATTRIBUTE *attribute, SDB *sdbpw)
+{
+ unsigned char hash[SHA1_LENGTH];
+ CK_KEY_TYPE keyType = CKK_DH;
+
+ switch (type) {
+ case CKA_KEY_TYPE:
+ return lg_ULongAttribute(attribute, type, keyType);
+ case CKA_ID:
+ SHA1_HashBuf(hash,key->u.dh.publicValue.data,key->u.dh.publicValue.len);
+ return lg_CopyAttribute(attribute,type,hash,SHA1_LENGTH);
+ case CKA_DERIVE:
+ return LG_CLONE_ATTR(attribute,type,lg_StaticTrueAttr);
+ case CKA_DECRYPT:
+ case CKA_SIGN:
+ case CKA_SIGN_RECOVER:
+ case CKA_UNWRAP:
+ return LG_CLONE_ATTR(attribute,type,lg_StaticFalseAttr);
+ case CKA_VALUE:
+ return lg_CopyPrivAttrSigned(attribute, type,
+ key->u.dh.privateValue.data,
+ key->u.dh.privateValue.len, sdbpw);
+ case CKA_PRIME:
+ return lg_CopyAttributeSigned(attribute, type, key->u.dh.prime.data,
+ key->u.dh.prime.len);
+ case CKA_BASE:
+ return lg_CopyAttributeSigned(attribute, type, key->u.dh.base.data,
+ key->u.dh.base.len);
+ case CKA_NETSCAPE_DB:
+ return lg_CopyAttributeSigned(attribute, type,
+ key->u.dh.publicValue.data,
+ key->u.dh.publicValue.len);
+ default:
+ break;
+ }
+ return lg_invalidAttribute(attribute);
+}
+
+#ifdef NSS_ENABLE_ECC
+static CK_RV
+lg_FindECPrivateKeyAttribute(NSSLOWKEYPrivateKey *key, CK_ATTRIBUTE_TYPE type,
+ CK_ATTRIBUTE *attribute, SDB *sdbpw)
+{
+ unsigned char hash[SHA1_LENGTH];
+ CK_KEY_TYPE keyType = CKK_EC;
+
+ switch (type) {
+ case CKA_KEY_TYPE:
+ return lg_ULongAttribute(attribute, type, keyType);
+ case CKA_ID:
+ SHA1_HashBuf(hash,key->u.ec.publicValue.data,key->u.ec.publicValue.len);
+ return lg_CopyAttribute(attribute,type,hash,SHA1_LENGTH);
+ case CKA_DERIVE:
+ case CKA_SIGN:
+ return LG_CLONE_ATTR(attribute,type,lg_StaticTrueAttr);
+ case CKA_DECRYPT:
+ case CKA_SIGN_RECOVER:
+ case CKA_UNWRAP:
+ return LG_CLONE_ATTR(attribute,type,lg_StaticFalseAttr);
+ case CKA_VALUE:
+ return lg_CopyPrivAttrSigned(attribute, type,
+ key->u.ec.privateValue.data,
+ key->u.ec.privateValue.len, sdbpw);
+ case CKA_EC_PARAMS:
+ return lg_CopyAttributeSigned(attribute, type,
+ key->u.ec.ecParams.DEREncoding.data,
+ key->u.ec.ecParams.DEREncoding.len);
+ case CKA_NETSCAPE_DB:
+ return lg_CopyAttributeSigned(attribute, type,
+ key->u.ec.publicValue.data,
+ key->u.ec.publicValue.len);
+ default:
+ break;
+ }
+ return lg_invalidAttribute(attribute);
+}
+#endif /* NSS_ENABLE_ECC */
+
+static CK_RV
+lg_FindPrivateKeyAttribute(LGObjectCache *obj, CK_ATTRIBUTE_TYPE type,
+ CK_ATTRIBUTE *attribute)
+{
+ NSSLOWKEYPrivateKey *key;
+ char *label;
+ CK_RV crv;
+
+ switch (type) {
+ case CKA_PRIVATE:
+ case CKA_SENSITIVE:
+ case CKA_ALWAYS_SENSITIVE:
+ case CKA_EXTRACTABLE:
+ case CKA_MODIFIABLE:
+ return LG_CLONE_ATTR(attribute,type,lg_StaticTrueAttr);
+ case CKA_NEVER_EXTRACTABLE:
+ return LG_CLONE_ATTR(attribute,type,lg_StaticFalseAttr);
+ case CKA_SUBJECT:
+ return LG_CLONE_ATTR(attribute,type,lg_StaticNullAttr);
+ case CKA_LABEL:
+ label = lg_FindKeyNicknameByPublicKey(obj->sdb, &obj->dbKey);
+ if (label == NULL) {
+ return LG_CLONE_ATTR(attribute,type,lg_StaticNullAttr);
+ }
+ crv = lg_CopyAttribute(attribute,type,label,PORT_Strlen(label));
+ PORT_Free(label);
+ return crv;
+ default:
+ break;
+ }
+ key = lg_GetPrivateKey(obj);
+ if (key == NULL) {
+ return CKR_OBJECT_HANDLE_INVALID;
+ }
+ switch (key->keyType) {
+ case NSSLOWKEYRSAKey:
+ return lg_FindRSAPrivateKeyAttribute(key,type,attribute,obj->sdb);
+ case NSSLOWKEYDSAKey:
+ return lg_FindDSAPrivateKeyAttribute(key,type,attribute,obj->sdb);
+ case NSSLOWKEYDHKey:
+ return lg_FindDHPrivateKeyAttribute(key,type,attribute,obj->sdb);
+#ifdef NSS_ENABLE_ECC
+ case NSSLOWKEYECKey:
+ return lg_FindECPrivateKeyAttribute(key,type,attribute,obj->sdb);
+#endif /* NSS_ENABLE_ECC */
+ default:
+ break;
+ }
+
+ return lg_invalidAttribute(attribute);
+}
+
+static CK_RV
+lg_FindSMIMEAttribute(LGObjectCache *obj, CK_ATTRIBUTE_TYPE type,
+ CK_ATTRIBUTE *attribute)
+{
+ certDBEntrySMime *entry;
+ switch (type) {
+ case CKA_PRIVATE:
+ case CKA_MODIFIABLE:
+ return LG_CLONE_ATTR(attribute,type,lg_StaticFalseAttr);
+ case CKA_NETSCAPE_EMAIL:
+ return lg_CopyAttribute(attribute,type,obj->dbKey.data,
+ obj->dbKey.len-1);
+ case CKA_NETSCAPE_SMIME_TIMESTAMP:
+ case CKA_SUBJECT:
+ case CKA_VALUE:
+ break;
+ default:
+ return lg_invalidAttribute(attribute);
+ }
+ entry = lg_getSMime(obj);
+ if (entry == NULL) {
+ return CKR_OBJECT_HANDLE_INVALID;
+ }
+ switch (type) {
+ case CKA_NETSCAPE_SMIME_TIMESTAMP:
+ return lg_CopyAttribute(attribute,type,entry->optionsDate.data,
+ entry->optionsDate.len);
+ case CKA_SUBJECT:
+ return lg_CopyAttribute(attribute,type,entry->subjectName.data,
+ entry->subjectName.len);
+ case CKA_VALUE:
+ return lg_CopyAttribute(attribute,type,entry->smimeOptions.data,
+ entry->smimeOptions.len);
+ default:
+ break;
+ }
+ return lg_invalidAttribute(attribute);
+}
+
+static CK_RV
+lg_FindTrustAttribute(LGObjectCache *obj, CK_ATTRIBUTE_TYPE type,
+ CK_ATTRIBUTE *attribute)
+{
+ NSSLOWCERTTrust *trust;
+ NSSLOWCERTCertDBHandle *certHandle;
+ NSSLOWCERTCertificate *cert;
+ unsigned char hash[SHA1_LENGTH];
+ unsigned int trustFlags;
+ CK_RV crv;
+
+ switch (type) {
+ case CKA_PRIVATE:
+ return LG_CLONE_ATTR(attribute,type,lg_StaticFalseAttr);
+ case CKA_MODIFIABLE:
+ return LG_CLONE_ATTR(attribute,type,lg_StaticTrueAttr);
+ case CKA_CERT_SHA1_HASH:
+ case CKA_CERT_MD5_HASH:
+ case CKA_TRUST_CLIENT_AUTH:
+ case CKA_TRUST_SERVER_AUTH:
+ case CKA_TRUST_EMAIL_PROTECTION:
+ case CKA_TRUST_CODE_SIGNING:
+ case CKA_TRUST_STEP_UP_APPROVED:
+ case CKA_ISSUER:
+ case CKA_SERIAL_NUMBER:
+ break;
+ default:
+ return lg_invalidAttribute(attribute);
+ }
+ certHandle = lg_getCertDB(obj->sdb);
+ if (!certHandle) {
+ return CKR_OBJECT_HANDLE_INVALID;
+ }
+ trust = lg_getTrust(obj, certHandle);
+ if (trust == NULL) {
+ return CKR_OBJECT_HANDLE_INVALID;
+ }
+ switch (type) {
+ case CKA_CERT_SHA1_HASH:
+ SHA1_HashBuf(hash,trust->derCert->data,trust->derCert->len);
+ return lg_CopyAttribute(attribute, type, hash, SHA1_LENGTH);
+ case CKA_CERT_MD5_HASH:
+ MD5_HashBuf(hash,trust->derCert->data,trust->derCert->len);
+ return lg_CopyAttribute(attribute, type, hash, MD5_LENGTH);
+ case CKA_TRUST_CLIENT_AUTH:
+ trustFlags = trust->trust->sslFlags & CERTDB_TRUSTED_CLIENT_CA ?
+ trust->trust->sslFlags | CERTDB_TRUSTED_CA : 0 ;
+ goto trust;
+ case CKA_TRUST_SERVER_AUTH:
+ trustFlags = trust->trust->sslFlags;
+ goto trust;
+ case CKA_TRUST_EMAIL_PROTECTION:
+ trustFlags = trust->trust->emailFlags;
+ goto trust;
+ case CKA_TRUST_CODE_SIGNING:
+ trustFlags = trust->trust->objectSigningFlags;
+trust:
+ if (trustFlags & CERTDB_TRUSTED_CA ) {
+ return lg_ULongAttribute(attribute, type,
+ CKT_NETSCAPE_TRUSTED_DELEGATOR);
+ }
+ if (trustFlags & CERTDB_TRUSTED) {
+ return lg_ULongAttribute(attribute, type, CKT_NETSCAPE_TRUSTED);
+ }
+ if (trustFlags & CERTDB_NOT_TRUSTED) {
+ return lg_ULongAttribute(attribute, type, CKT_NETSCAPE_UNTRUSTED);
+ }
+ if (trustFlags & CERTDB_TRUSTED_UNKNOWN) {
+ return lg_ULongAttribute(attribute, type,
+ CKT_NETSCAPE_TRUST_UNKNOWN);
+ }
+ if (trustFlags & CERTDB_VALID_CA) {
+ return lg_ULongAttribute(attribute, type,
+ CKT_NETSCAPE_VALID_DELEGATOR);
+ }
+ if (trustFlags & CERTDB_VALID_PEER) {
+ return lg_ULongAttribute(attribute, type, CKT_NETSCAPE_VALID);
+ }
+ return lg_ULongAttribute(attribute, type, CKT_NETSCAPE_MUST_VERIFY);
+ case CKA_TRUST_STEP_UP_APPROVED:
+ if (trust->trust->sslFlags & CERTDB_GOVT_APPROVED_CA) {
+ return LG_CLONE_ATTR(attribute,type,lg_StaticTrueAttr);
+ } else {
+ return LG_CLONE_ATTR(attribute,type,lg_StaticFalseAttr);
+ }
+ default:
+ break;
+ }
+
+
+ switch (type) {
+ case CKA_ISSUER:
+ cert = lg_getCert(obj, certHandle);
+ if (cert == NULL) break;
+ crv = lg_CopyAttribute(attribute,type,cert->derIssuer.data,
+ cert->derIssuer.len);
+ break;
+ case CKA_SERIAL_NUMBER:
+ cert = lg_getCert(obj, certHandle);
+ if (cert == NULL) break;
+ crv = lg_CopyAttribute(attribute,type,cert->derSN.data,
+ cert->derSN.len);
+ break;
+ default:
+ cert = NULL;
+ break;
+ }
+ if (cert) {
+ nsslowcert_DestroyCertificate(cert);
+ return crv;
+ }
+ return lg_invalidAttribute(attribute);
+}
+
+static CK_RV
+lg_FindCrlAttribute(LGObjectCache *obj, CK_ATTRIBUTE_TYPE type,
+ CK_ATTRIBUTE *attribute)
+{
+ certDBEntryRevocation *crl;
+
+ switch (type) {
+ case CKA_PRIVATE:
+ case CKA_MODIFIABLE:
+ return LG_CLONE_ATTR(attribute,type,lg_StaticFalseAttr);
+ case CKA_NETSCAPE_KRL:
+ return ((obj->handle == LG_TOKEN_KRL_HANDLE)
+ ? LG_CLONE_ATTR(attribute,type,lg_StaticTrueAttr)
+ : LG_CLONE_ATTR(attribute,type,lg_StaticFalseAttr));
+ case CKA_SUBJECT:
+ return lg_CopyAttribute(attribute,type,obj->dbKey.data,
+ obj->dbKey.len);
+ case CKA_NETSCAPE_URL:
+ case CKA_VALUE:
+ break;
+ default:
+ return lg_invalidAttribute(attribute);
+ }
+ crl = lg_getCrl(obj);
+ if (!crl) {
+ return CKR_OBJECT_HANDLE_INVALID;
+ }
+ switch (type) {
+ case CKA_NETSCAPE_URL:
+ if (crl->url == NULL) {
+ return LG_CLONE_ATTR(attribute,type,lg_StaticNullAttr);
+ }
+ return lg_CopyAttribute(attribute, type, crl->url,
+ PORT_Strlen(crl->url)+1);
+ case CKA_VALUE:
+ return lg_CopyAttribute(attribute, type, crl->derCrl.data,
+ crl->derCrl.len);
+ default:
+ break;
+ }
+ return lg_invalidAttribute(attribute);
+}
+
+static CK_RV
+lg_FindCertAttribute(LGObjectCache *obj, CK_ATTRIBUTE_TYPE type,
+ CK_ATTRIBUTE *attribute)
+{
+ NSSLOWCERTCertificate *cert;
+ NSSLOWCERTCertDBHandle *certHandle;
+ NSSLOWKEYPublicKey *pubKey;
+ unsigned char hash[SHA1_LENGTH];
+ SECItem *item;
+
+ switch (type) {
+ case CKA_PRIVATE:
+ return LG_CLONE_ATTR(attribute,type,lg_StaticFalseAttr);
+ case CKA_MODIFIABLE:
+ return LG_CLONE_ATTR(attribute,type,lg_StaticTrueAttr);
+ case CKA_CERTIFICATE_TYPE:
+ /* hardcoding X.509 into here */
+ return lg_ULongAttribute(attribute, type, CKC_X_509);
+ case CKA_VALUE:
+ case CKA_ID:
+ case CKA_LABEL:
+ case CKA_SUBJECT:
+ case CKA_ISSUER:
+ case CKA_SERIAL_NUMBER:
+ case CKA_NETSCAPE_EMAIL:
+ break;
+ default:
+ return lg_invalidAttribute(attribute);
+ }
+
+ certHandle = lg_getCertDB(obj->sdb);
+ if (certHandle == NULL) {
+ return CKR_OBJECT_HANDLE_INVALID;
+ }
+
+ cert = lg_getCert(obj, certHandle);
+ if (cert == NULL) {
+ return CKR_OBJECT_HANDLE_INVALID;
+ }
+ switch (type) {
+ case CKA_VALUE:
+ return lg_CopyAttribute(attribute,type,cert->derCert.data,
+ cert->derCert.len);
+ case CKA_ID:
+ if (((cert->trust->sslFlags & CERTDB_USER) == 0) &&
+ ((cert->trust->emailFlags & CERTDB_USER) == 0) &&
+ ((cert->trust->objectSigningFlags & CERTDB_USER) == 0)) {
+ return LG_CLONE_ATTR(attribute,type,lg_StaticNullAttr);
+ }
+ pubKey = nsslowcert_ExtractPublicKey(cert);
+ if (pubKey == NULL) break;
+ item = lg_GetPubItem(pubKey);
+ if (item == NULL) {
+ nsslowkey_DestroyPublicKey(pubKey);
+ break;
+ }
+ SHA1_HashBuf(hash,item->data,item->len);
+ /* item is imbedded in pubKey, just free the key */
+ nsslowkey_DestroyPublicKey(pubKey);
+ return lg_CopyAttribute(attribute, type, hash, SHA1_LENGTH);
+ case CKA_LABEL:
+ return cert->nickname
+ ? lg_CopyAttribute(attribute, type, cert->nickname,
+ PORT_Strlen(cert->nickname))
+ : LG_CLONE_ATTR(attribute,type,lg_StaticNullAttr);
+ case CKA_SUBJECT:
+ return lg_CopyAttribute(attribute,type,cert->derSubject.data,
+ cert->derSubject.len);
+ case CKA_ISSUER:
+ return lg_CopyAttribute(attribute,type,cert->derIssuer.data,
+ cert->derIssuer.len);
+ case CKA_SERIAL_NUMBER:
+ return lg_CopyAttribute(attribute,type,cert->derSN.data,
+ cert->derSN.len);
+ case CKA_NETSCAPE_EMAIL:
+ return (cert->emailAddr && cert->emailAddr[0])
+ ? lg_CopyAttribute(attribute, type, cert->emailAddr,
+ PORT_Strlen(cert->emailAddr))
+ : LG_CLONE_ATTR(attribute,type,lg_StaticNullAttr);
+ default:
+ break;
+ }
+ return lg_invalidAttribute(attribute);
+}
+
+CK_RV
+lg_GetSingleAttribute(LGObjectCache *obj, CK_ATTRIBUTE *attribute)
+{
+ /* handle the common ones */
+ CK_ATTRIBUTE_TYPE type = attribute->type;
+ switch (type) {
+ case CKA_CLASS:
+ return lg_ULongAttribute(attribute,type,obj->objclass);
+ case CKA_TOKEN:
+ return LG_CLONE_ATTR(attribute, type,lg_StaticTrueAttr);
+ case CKA_LABEL:
+ if ( (obj->objclass == CKO_CERTIFICATE)
+ || (obj->objclass == CKO_PRIVATE_KEY)
+ || (obj->objclass == CKO_PUBLIC_KEY)
+ || (obj->objclass == CKO_SECRET_KEY)) {
+ break;
+ }
+ return LG_CLONE_ATTR(attribute,type,lg_StaticNullAttr);
+ default:
+ break;
+ }
+ switch (obj->objclass) {
+ case CKO_CERTIFICATE:
+ return lg_FindCertAttribute(obj,type,attribute);
+ case CKO_NETSCAPE_CRL:
+ return lg_FindCrlAttribute(obj,type,attribute);
+ case CKO_NETSCAPE_TRUST:
+ return lg_FindTrustAttribute(obj,type,attribute);
+ case CKO_NETSCAPE_SMIME:
+ return lg_FindSMIMEAttribute(obj,type,attribute);
+ case CKO_PUBLIC_KEY:
+ return lg_FindPublicKeyAttribute(obj,type,attribute);
+ case CKO_PRIVATE_KEY:
+ return lg_FindPrivateKeyAttribute(obj,type,attribute);
+ case CKO_SECRET_KEY:
+ return lg_FindSecretKeyAttribute(obj,type,attribute);
+ default:
+ break;
+ }
+ return lg_invalidAttribute(attribute);
+}
+
+/*
+ * Fill in the attribute template based on the data in the database.
+ */
+CK_RV
+lg_GetAttributeValue(SDB *sdb, CK_OBJECT_HANDLE handle, CK_ATTRIBUTE *templ,
+ CK_ULONG count)
+{
+ LGObjectCache *obj = lg_NewObjectCache(sdb, NULL, handle & ~LG_TOKEN_MASK);
+ CK_RV crv, crvCollect = CKR_OK;
+ int i;
+
+ if (obj == NULL) {
+ return CKR_OBJECT_HANDLE_INVALID;
+ }
+
+ for (i=0; i < count; i++) {
+ crv = lg_GetSingleAttribute(obj, &templ[i]);
+ if (crvCollect == CKR_OK) crvCollect = crv;
+ }
+
+ lg_DestroyObjectCache(obj);
+ return crvCollect;
+}
+
+PRBool
+lg_cmpAttribute(LGObjectCache *obj, const CK_ATTRIBUTE *attribute)
+{
+ unsigned char buf[LG_BUF_SPACE];
+ CK_ATTRIBUTE testAttr;
+ unsigned char *tempBuf = NULL;
+ PRBool match = PR_TRUE;
+ CK_RV crv;
+
+ /* we're going to compare 'attribute' with the actual attribute from
+ * the object. We'll use the length of 'attribute' to decide how much
+ * space we need to read the test attribute. If 'attribute' doesn't give
+ * enough space, then we know the values don't match and that will
+ * show up as ckr != CKR_OK */
+ testAttr = *attribute;
+ testAttr.pValue = buf;
+
+ /* if we don't have enough space, malloc it */
+ if (attribute->ulValueLen > LG_BUF_SPACE) {
+ tempBuf = PORT_Alloc(attribute->ulValueLen);
+ if (!tempBuf) {
+ return PR_FALSE;
+ }
+ testAttr.pValue = tempBuf;
+ }
+
+ /* get the attribute */
+ crv = lg_GetSingleAttribute(obj, &testAttr);
+ /* if the attribute was read OK, compare it */
+ if ((crv != CKR_OK) || (attribute->ulValueLen != testAttr.ulValueLen) ||
+ (PORT_Memcmp(attribute->pValue,testAttr.pValue,testAttr.ulValueLen)!= 0)){
+ /* something didn't match, this isn't the object we are looking for */
+ match = PR_FALSE;
+ }
+ /* free the buffer we may have allocated */
+ if (tempBuf) {
+ PORT_Free(tempBuf);
+ }
+ return match;
+}
+
+PRBool
+lg_tokenMatch(SDB *sdb, const SECItem *dbKey, CK_OBJECT_HANDLE class,
+ const CK_ATTRIBUTE *templ, CK_ULONG count)
+{
+ PRBool match = PR_TRUE;
+ LGObjectCache *obj = lg_NewObjectCache(sdb, dbKey, class);
+ int i;
+
+ if (obj == NULL) {
+ return PR_FALSE;
+ }
+
+ for (i=0; i < count; i++) {
+ match = lg_cmpAttribute(obj, &templ[i]);
+ if (!match) {
+ break;
+ }
+ }
+
+ /* done looking, free up our cache */
+ lg_DestroyObjectCache(obj);
+
+ /* if we get through the whole list without finding a mismatched attribute,
+ * then this object fits the criteria we are matching */
+ return match;
+}
+
+static CK_RV
+lg_SetCertAttribute(LGObjectCache *obj, CK_ATTRIBUTE_TYPE type,
+ const void *value, unsigned int len)
+{
+ NSSLOWCERTCertificate *cert;
+ NSSLOWCERTCertDBHandle *certHandle;
+ char *nickname = NULL;
+ SECStatus rv;
+ CK_RV crv;
+
+ /* we can't change the EMAIL values, but let the
+ * upper layers feel better about the fact we tried to set these */
+ if (type == CKA_NETSCAPE_EMAIL) {
+ return CKR_OK;
+ }
+
+ certHandle = lg_getCertDB(obj->sdb);
+ if (certHandle == NULL) {
+ crv = CKR_TOKEN_WRITE_PROTECTED;
+ goto done;
+ }
+
+ if ((type != CKA_LABEL) && (type != CKA_ID)) {
+ crv = CKR_ATTRIBUTE_READ_ONLY;
+ goto done;
+ }
+
+ cert = lg_getCert(obj, certHandle);
+ if (cert == NULL) {
+ crv = CKR_OBJECT_HANDLE_INVALID;
+ goto done;
+ }
+
+ /* if the app is trying to set CKA_ID, it's probably because it just
+ * imported the key. Look to see if we need to set the CERTDB_USER bits.
+ */
+ if (type == CKA_ID) {
+ if (((cert->trust->sslFlags & CERTDB_USER) == 0) &&
+ ((cert->trust->emailFlags & CERTDB_USER) == 0) &&
+ ((cert->trust->objectSigningFlags & CERTDB_USER) == 0)) {
+ NSSLOWKEYDBHandle *keyHandle;
+
+ keyHandle = lg_getKeyDB(obj->sdb);
+ if (keyHandle) {
+ if (nsslowkey_KeyForCertExists(keyHandle, cert)) {
+ NSSLOWCERTCertTrust trust = *cert->trust;
+ trust.sslFlags |= CERTDB_USER;
+ trust.emailFlags |= CERTDB_USER;
+ trust.objectSigningFlags |= CERTDB_USER;
+ nsslowcert_ChangeCertTrust(certHandle,cert,&trust);
+ }
+ }
+ }
+ crv = CKR_OK;
+ goto done;
+ }
+
+ /* must be CKA_LABEL */
+ if (value != NULL) {
+ nickname = PORT_ZAlloc(len+1);
+ if (nickname == NULL) {
+ crv = CKR_HOST_MEMORY;
+ goto done;
+ }
+ PORT_Memcpy(nickname,value,len);
+ nickname[len] = 0;
+ }
+ rv = nsslowcert_AddPermNickname(certHandle, cert, nickname);
+ crv = (rv == SECSuccess) ? CKR_OK : CKR_DEVICE_ERROR;
+
+done:
+ if (nickname) {
+ PORT_Free(nickname);
+ }
+ return crv;
+}
+
+static CK_RV
+lg_SetPrivateKeyAttribute(LGObjectCache *obj, CK_ATTRIBUTE_TYPE type,
+ const void *value, unsigned int len,
+ PRBool *writePrivate)
+{
+ NSSLOWKEYPrivateKey *privKey;
+ NSSLOWKEYDBHandle *keyHandle;
+ char *nickname = NULL;
+ SECStatus rv;
+ CK_RV crv;
+
+ /* we can't change the ID and we don't store the subject, but let the
+ * upper layers feel better about the fact we tried to set these */
+ if ((type == CKA_ID) || (type == CKA_SUBJECT)) {
+ return CKR_OK;
+ }
+
+ keyHandle = lg_getKeyDB(obj->sdb);
+ if (keyHandle == NULL) {
+ crv = CKR_TOKEN_WRITE_PROTECTED;
+ goto done;
+ }
+
+ privKey = lg_GetPrivateKeyWithDB(obj, keyHandle);
+ if (privKey == NULL) {
+ crv = CKR_OBJECT_HANDLE_INVALID;
+ goto done;
+ }
+
+ crv = CKR_ATTRIBUTE_READ_ONLY;
+ switch(type) {
+ case CKA_LABEL:
+ if (value != NULL) {
+ nickname = PORT_ZAlloc(len+1);
+ if (nickname == NULL) {
+ crv = CKR_HOST_MEMORY;
+ goto done;
+ }
+ PORT_Memcpy(nickname,value,len);
+ nickname[len] = 0;
+ }
+ rv = nsslowkey_UpdateNickname(keyHandle, privKey, &obj->dbKey,
+ nickname, obj->sdb);
+ crv = (rv == SECSuccess) ? CKR_OK : CKR_DEVICE_ERROR;
+ break;
+ case CKA_VALUE:
+ case CKA_PRIVATE_EXPONENT:
+ case CKA_PRIME_1:
+ case CKA_PRIME_2:
+ case CKA_EXPONENT_1:
+ case CKA_EXPONENT_2:
+ case CKA_COEFFICIENT:
+ /* We aren't really changing these values, we are just triggering
+ * the database to update it's entry */
+ *writePrivate = 1;
+ crv = CKR_OK;
+ break;
+ default:
+ crv = CKR_ATTRIBUTE_READ_ONLY;
+ break;
+ }
+done:
+ if (nickname) {
+ PORT_Free(nickname);
+ }
+ return crv;
+}
+
+static CK_RV
+lg_SetTrustAttribute(LGObjectCache *obj, const CK_ATTRIBUTE *attr)
+{
+ unsigned int flags;
+ CK_TRUST trust;
+ NSSLOWCERTCertificate *cert;
+ NSSLOWCERTCertDBHandle *certHandle;
+ NSSLOWCERTCertTrust dbTrust;
+ SECStatus rv;
+ CK_RV crv;
+
+ crv = lg_GetULongAttribute(attr->type, attr, 1, &trust);
+ if (crv != CKR_OK) {
+ return crv;
+ }
+ flags = lg_MapTrust(trust, (PRBool) (attr->type == CKA_TRUST_SERVER_AUTH));
+
+ certHandle = lg_getCertDB(obj->sdb);
+
+ if (certHandle == NULL) {
+ crv = CKR_TOKEN_WRITE_PROTECTED;
+ goto done;
+ }
+
+ cert = lg_getCert(obj, certHandle);
+ if (cert == NULL) {
+ crv = CKR_OBJECT_HANDLE_INVALID;
+ goto done;
+ }
+ dbTrust = *cert->trust;
+
+ switch (attr->type) {
+ case CKA_TRUST_EMAIL_PROTECTION:
+ dbTrust.emailFlags = flags |
+ (cert->trust->emailFlags & CERTDB_PRESERVE_TRUST_BITS);
+ break;
+ case CKA_TRUST_CODE_SIGNING:
+ dbTrust.objectSigningFlags = flags |
+ (cert->trust->objectSigningFlags & CERTDB_PRESERVE_TRUST_BITS);
+ break;
+ case CKA_TRUST_CLIENT_AUTH:
+ dbTrust.sslFlags = flags | (cert->trust->sslFlags &
+ (CERTDB_PRESERVE_TRUST_BITS|CERTDB_TRUSTED_CA));
+ break;
+ case CKA_TRUST_SERVER_AUTH:
+ dbTrust.sslFlags = flags | (cert->trust->sslFlags &
+ (CERTDB_PRESERVE_TRUST_BITS|CERTDB_TRUSTED_CLIENT_CA));
+ break;
+ default:
+ crv = CKR_ATTRIBUTE_READ_ONLY;
+ goto done;
+ }
+
+ rv = nsslowcert_ChangeCertTrust(certHandle, cert, &dbTrust);
+ crv = (rv == SECSuccess) ? CKR_OK : CKR_DEVICE_ERROR;
+done:
+ return crv;
+}
+
+static CK_RV
+lg_SetSingleAttribute(LGObjectCache *obj, const CK_ATTRIBUTE *attr,
+ PRBool *writePrivate)
+{
+ CK_ATTRIBUTE attribLocal;
+ CK_RV crv;
+
+ /* Make sure the attribute exists first */
+ attribLocal.type = attr->type;
+ attribLocal.pValue = NULL;
+ attribLocal.ulValueLen = 0;
+ crv = lg_GetSingleAttribute(obj, &attribLocal);
+ if (crv != CKR_OK) {
+ return crv;
+ }
+
+ /* if we are just setting it to the value we already have,
+ * allow it to happen. Let label setting go through so
+ * we have the opportunity to repair any database corruption. */
+ if (attr->type != CKA_LABEL) {
+ if (lg_cmpAttribute(obj,attr)) {
+ return CKR_OK;
+ }
+ }
+
+ crv = CKR_ATTRIBUTE_READ_ONLY;
+ switch (obj->objclass) {
+ case CKO_CERTIFICATE:
+ /* change NICKNAME, EMAIL, */
+ crv = lg_SetCertAttribute(obj,attr->type,
+ attr->pValue,attr->ulValueLen);
+ break;
+ case CKO_NETSCAPE_CRL:
+ /* change URL */
+ break;
+ case CKO_NETSCAPE_TRUST:
+ crv = lg_SetTrustAttribute(obj,attr);
+ break;
+ case CKO_PRIVATE_KEY:
+ case CKO_SECRET_KEY:
+ crv = lg_SetPrivateKeyAttribute(obj,attr->type,
+ attr->pValue,attr->ulValueLen, writePrivate);
+ break;
+ }
+ return crv;
+}
+
+/*
+ * Fill in the attribute template based on the data in the database.
+ */
+CK_RV
+lg_SetAttributeValue(SDB *sdb, CK_OBJECT_HANDLE handle,
+ const CK_ATTRIBUTE *templ, CK_ULONG count)
+{
+ LGObjectCache *obj = lg_NewObjectCache(sdb, NULL, handle & ~LG_TOKEN_MASK);
+ CK_RV crv, crvCollect = CKR_OK;
+ PRBool writePrivate = PR_FALSE;
+ int i;
+
+ if (obj == NULL) {
+ return CKR_OBJECT_HANDLE_INVALID;
+ }
+
+ for (i=0; i < count; i++) {
+ crv = lg_SetSingleAttribute(obj, &templ[i], &writePrivate);
+ if (crvCollect == CKR_OK) crvCollect = crv;
+ }
+
+ /* Write any collected changes out for private and secret keys.
+ * don't do the write for just the label */
+ if (writePrivate) {
+ NSSLOWKEYPrivateKey *privKey = lg_GetPrivateKey(obj);
+ SECStatus rv = SECFailure;
+ char * label = lg_FindKeyNicknameByPublicKey(obj->sdb, &obj->dbKey);
+
+ if (privKey) {
+ rv = nsslowkey_StoreKeyByPublicKey(lg_getKeyDB(sdb), privKey,
+ &obj->dbKey, label, sdb );
+ }
+ if (rv != SECSuccess) {
+ crv = CKR_DEVICE_ERROR;
+ }
+ }
+
+ lg_DestroyObjectCache(obj);
+ return crvCollect;
+}
diff --git a/security/nss/lib/softoken/legacydb/lgcreate.c b/security/nss/lib/softoken/legacydb/lgcreate.c
new file mode 100644
index 000000000..c3ab0781d
--- /dev/null
+++ b/security/nss/lib/softoken/legacydb/lgcreate.c
@@ -0,0 +1,955 @@
+/* ***** 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):
+ * Dr Vipul Gupta <vipul.gupta@sun.com>, Sun Microsystems Laboratories
+ *
+ * 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 "secitem.h"
+#include "pkcs11.h"
+#include "lgdb.h"
+#include "pcert.h"
+#include "lowkeyi.h"
+#include "blapi.h"
+#include "secder.h"
+
+#include "keydbi.h"
+
+/*
+ * ******************** Object Creation Utilities ***************************
+ */
+
+/*
+ * check the consistancy and initialize a Certificate Object
+ */
+static CK_RV
+lg_createCertObject(SDB *sdb, CK_OBJECT_HANDLE *handle,
+ const CK_ATTRIBUTE *templ, CK_ULONG count)
+{
+ SECItem derCert;
+ NSSLOWCERTCertificate *cert;
+ NSSLOWCERTCertTrust *trust = NULL;
+ NSSLOWCERTCertTrust userTrust =
+ { CERTDB_USER, CERTDB_USER, CERTDB_USER };
+ NSSLOWCERTCertTrust defTrust =
+ { CERTDB_TRUSTED_UNKNOWN,
+ CERTDB_TRUSTED_UNKNOWN, CERTDB_TRUSTED_UNKNOWN };
+ char *label = NULL;
+ char *email = NULL;
+ SECStatus rv;
+ PRBool inDB = PR_TRUE;
+ NSSLOWCERTCertDBHandle *certHandle = lg_getCertDB(sdb);
+ NSSLOWKEYDBHandle *keyHandle = NULL;
+ CK_CERTIFICATE_TYPE type;
+ const CK_ATTRIBUTE *attribute;
+
+ /* we can't store any certs private */
+ if (lg_isTrue(CKA_PRIVATE, templ, count)) {
+ return CKR_ATTRIBUTE_VALUE_INVALID;
+ }
+
+ /* We only support X.509 Certs for now */
+ attribute = lg_FindAttribute(CKA_CERTIFICATE_TYPE, templ, count);
+ if (attribute == NULL) return CKR_TEMPLATE_INCOMPLETE;
+ type = *(CK_CERTIFICATE_TYPE *)attribute->pValue;
+
+ if (type != CKC_X_509) {
+ return CKR_ATTRIBUTE_VALUE_INVALID;
+ }
+
+ /* X.509 Certificate */
+
+
+ if (certHandle == NULL) {
+ return CKR_TOKEN_WRITE_PROTECTED;
+ }
+
+ /* get the der cert */
+ attribute = lg_FindAttribute(CKA_VALUE, templ, count);
+ if (!attribute) {
+ return CKR_ATTRIBUTE_VALUE_INVALID;
+ }
+
+ derCert.type = 0;
+ derCert.data = (unsigned char *)attribute->pValue;
+ derCert.len = attribute->ulValueLen ;
+
+ label = lg_getString(CKA_LABEL, templ, count);
+
+ cert = nsslowcert_FindCertByDERCert(certHandle, &derCert);
+ if (cert == NULL) {
+ cert = nsslowcert_DecodeDERCertificate(&derCert, label);
+ inDB = PR_FALSE;
+ }
+ if (cert == NULL) {
+ if (label) PORT_Free(label);
+ return CKR_ATTRIBUTE_VALUE_INVALID;
+ }
+
+ keyHandle = lg_getKeyDB(sdb);
+ if (keyHandle) {
+ if (nsslowkey_KeyForCertExists(keyHandle,cert)) {
+ trust = &userTrust;
+ }
+ }
+
+ if (!inDB) {
+ if (!trust) trust = &defTrust;
+ rv = nsslowcert_AddPermCert(certHandle, cert, label, trust);
+ } else {
+ rv = trust ? nsslowcert_ChangeCertTrust(certHandle,cert,trust) :
+ SECSuccess;
+ }
+
+ if (label) PORT_Free(label);
+
+ if (rv != SECSuccess) {
+ nsslowcert_DestroyCertificate(cert);
+ return CKR_DEVICE_ERROR;
+ }
+
+ /*
+ * Add a NULL S/MIME profile if necessary.
+ */
+ email = lg_getString(CKA_NETSCAPE_EMAIL, templ, count);
+ if (email) {
+ certDBEntrySMime *entry;
+
+ entry = nsslowcert_ReadDBSMimeEntry(certHandle,email);
+ if (!entry) {
+ nsslowcert_SaveSMimeProfile(certHandle, email,
+ &cert->derSubject, NULL, NULL);
+ } else {
+ nsslowcert_DestroyDBEntry((certDBEntry *)entry);
+ }
+ PORT_Free(email);
+ }
+ *handle=lg_mkHandle(sdb,&cert->certKey,LG_TOKEN_TYPE_CERT);
+ nsslowcert_DestroyCertificate(cert);
+
+ return CKR_OK;
+}
+
+unsigned int
+lg_MapTrust(CK_TRUST trust, PRBool clientAuth)
+{
+ unsigned int trustCA = clientAuth ? CERTDB_TRUSTED_CLIENT_CA :
+ CERTDB_TRUSTED_CA;
+ switch (trust) {
+ case CKT_NETSCAPE_TRUSTED:
+ return CERTDB_VALID_PEER|CERTDB_TRUSTED;
+ case CKT_NETSCAPE_TRUSTED_DELEGATOR:
+ return CERTDB_VALID_CA|trustCA;
+ case CKT_NETSCAPE_UNTRUSTED:
+ return CERTDB_NOT_TRUSTED;
+ case CKT_NETSCAPE_MUST_VERIFY:
+ return 0;
+ case CKT_NETSCAPE_VALID: /* implies must verify */
+ return CERTDB_VALID_PEER;
+ case CKT_NETSCAPE_VALID_DELEGATOR: /* implies must verify */
+ return CERTDB_VALID_CA;
+ default:
+ break;
+ }
+ return CERTDB_TRUSTED_UNKNOWN;
+}
+
+
+/*
+ * check the consistancy and initialize a Trust Object
+ */
+static CK_RV
+lg_createTrustObject(SDB *sdb, CK_OBJECT_HANDLE *handle,
+ const CK_ATTRIBUTE *templ, CK_ULONG count)
+{
+ const CK_ATTRIBUTE *issuer = NULL;
+ const CK_ATTRIBUTE *serial = NULL;
+ NSSLOWCERTCertificate *cert = NULL;
+ const CK_ATTRIBUTE *trust;
+ CK_TRUST sslTrust = CKT_NETSCAPE_TRUST_UNKNOWN;
+ CK_TRUST clientTrust = CKT_NETSCAPE_TRUST_UNKNOWN;
+ CK_TRUST emailTrust = CKT_NETSCAPE_TRUST_UNKNOWN;
+ CK_TRUST signTrust = CKT_NETSCAPE_TRUST_UNKNOWN;
+ CK_BBOOL stepUp;
+ NSSLOWCERTCertTrust dbTrust = { 0 };
+ SECStatus rv;
+ NSSLOWCERTCertDBHandle *certHandle = lg_getCertDB(sdb);
+ NSSLOWCERTIssuerAndSN issuerSN;
+
+ /* we can't store any certs private */
+ if (lg_isTrue(CKA_PRIVATE, templ, count)) {
+ return CKR_ATTRIBUTE_VALUE_INVALID;
+ }
+
+ if (certHandle == NULL) {
+ return CKR_TOKEN_WRITE_PROTECTED;
+ }
+
+ issuer = lg_FindAttribute(CKA_ISSUER, templ, count);
+ serial = lg_FindAttribute(CKA_SERIAL_NUMBER, templ, count);
+
+ if (issuer && serial) {
+ issuerSN.derIssuer.data = (unsigned char *)issuer->pValue;
+ issuerSN.derIssuer.len = issuer->ulValueLen ;
+
+ issuerSN.serialNumber.data = (unsigned char *)serial->pValue;
+ issuerSN.serialNumber.len = serial->ulValueLen ;
+
+ cert = nsslowcert_FindCertByIssuerAndSN(certHandle,&issuerSN);
+ }
+
+ if (cert == NULL) {
+ return CKR_ATTRIBUTE_VALUE_INVALID;
+ }
+
+ lg_GetULongAttribute(CKA_TRUST_SERVER_AUTH, templ, count, &sslTrust);
+ lg_GetULongAttribute(CKA_TRUST_CLIENT_AUTH, templ, count, &clientTrust);
+ lg_GetULongAttribute(CKA_TRUST_EMAIL_PROTECTION, templ, count, &emailTrust);
+ lg_GetULongAttribute(CKA_TRUST_CODE_SIGNING, templ, count, &signTrust);
+ stepUp = CK_FALSE;
+ trust = lg_FindAttribute(CKA_TRUST_STEP_UP_APPROVED, templ, count);
+ if (trust) {
+ if (trust->ulValueLen == sizeof(CK_BBOOL)) {
+ stepUp = *(CK_BBOOL*)trust->pValue;
+ }
+ }
+
+ /* preserve certain old fields */
+ if (cert->trust) {
+ dbTrust.sslFlags = cert->trust->sslFlags & CERTDB_PRESERVE_TRUST_BITS;
+ dbTrust.emailFlags=
+ cert->trust->emailFlags & CERTDB_PRESERVE_TRUST_BITS;
+ dbTrust.objectSigningFlags =
+ cert->trust->objectSigningFlags & CERTDB_PRESERVE_TRUST_BITS;
+ }
+
+ dbTrust.sslFlags |= lg_MapTrust(sslTrust,PR_FALSE);
+ dbTrust.sslFlags |= lg_MapTrust(clientTrust,PR_TRUE);
+ dbTrust.emailFlags |= lg_MapTrust(emailTrust,PR_FALSE);
+ dbTrust.objectSigningFlags |= lg_MapTrust(signTrust,PR_FALSE);
+ if (stepUp) {
+ dbTrust.sslFlags |= CERTDB_GOVT_APPROVED_CA;
+ }
+
+ rv = nsslowcert_ChangeCertTrust(certHandle,cert,&dbTrust);
+ *handle=lg_mkHandle(sdb,&cert->certKey,LG_TOKEN_TYPE_TRUST);
+ nsslowcert_DestroyCertificate(cert);
+ if (rv != SECSuccess) {
+ return CKR_DEVICE_ERROR;
+ }
+
+ return CKR_OK;
+}
+
+/*
+ * check the consistancy and initialize a Trust Object
+ */
+static CK_RV
+lg_createSMimeObject(SDB *sdb, CK_OBJECT_HANDLE *handle,
+ const CK_ATTRIBUTE *templ, CK_ULONG count)
+{
+ SECItem derSubj,rawProfile,rawTime,emailKey;
+ SECItem *pRawProfile = NULL;
+ SECItem *pRawTime = NULL;
+ char *email = NULL;
+ const CK_ATTRIBUTE *subject = NULL,
+ *profile = NULL,
+ *time = NULL;
+ SECStatus rv;
+ NSSLOWCERTCertDBHandle *certHandle;
+ CK_RV ck_rv = CKR_OK;
+
+ /* we can't store any certs private */
+ if (lg_isTrue(CKA_PRIVATE,templ,count)) {
+ return CKR_ATTRIBUTE_VALUE_INVALID;
+ }
+
+ certHandle = lg_getCertDB(sdb);
+ if (certHandle == NULL) {
+ return CKR_TOKEN_WRITE_PROTECTED;
+ }
+
+ /* lookup SUBJECT */
+ subject = lg_FindAttribute(CKA_SUBJECT,templ,count);
+ PORT_Assert(subject);
+ if (!subject) {
+ ck_rv = CKR_ATTRIBUTE_VALUE_INVALID;
+ goto loser;
+ }
+
+ derSubj.data = (unsigned char *)subject->pValue;
+ derSubj.len = subject->ulValueLen ;
+ derSubj.type = 0;
+
+ /* lookup VALUE */
+ profile = lg_FindAttribute(CKA_VALUE,templ,count);
+ if (profile) {
+ rawProfile.data = (unsigned char *)profile->pValue;
+ rawProfile.len = profile->ulValueLen ;
+ rawProfile.type = siBuffer;
+ pRawProfile = &rawProfile;
+ }
+
+ /* lookup Time */
+ time = lg_FindAttribute(CKA_NETSCAPE_SMIME_TIMESTAMP,templ,count);
+ if (time) {
+ rawTime.data = (unsigned char *)time->pValue;
+ rawTime.len = time->ulValueLen ;
+ rawTime.type = siBuffer;
+ pRawTime = &rawTime;
+ }
+
+
+ email = lg_getString(CKA_NETSCAPE_EMAIL,templ,count);
+ if (!email) {
+ ck_rv = CKR_ATTRIBUTE_VALUE_INVALID;
+ goto loser;
+ }
+
+ /* Store S/MIME Profile by SUBJECT */
+ rv = nsslowcert_SaveSMimeProfile(certHandle, email, &derSubj,
+ pRawProfile,pRawTime);
+ if (rv != SECSuccess) {
+ ck_rv = CKR_DEVICE_ERROR;
+ goto loser;
+ }
+ emailKey.data = (unsigned char *)email;
+ emailKey.len = PORT_Strlen(email)+1;
+
+ *handle = lg_mkHandle(sdb, &emailKey, LG_TOKEN_TYPE_SMIME);
+
+loser:
+ if (email) PORT_Free(email);
+
+ return ck_rv;
+}
+
+/*
+ * check the consistancy and initialize a Trust Object
+ */
+static CK_RV
+lg_createCrlObject(SDB *sdb, CK_OBJECT_HANDLE *handle,
+ const CK_ATTRIBUTE *templ, CK_ULONG count)
+{
+ PRBool isKRL = PR_FALSE;
+ SECItem derSubj,derCrl;
+ char *url = NULL;
+ const CK_ATTRIBUTE *subject,*crl;
+ SECStatus rv;
+ NSSLOWCERTCertDBHandle *certHandle;
+
+ certHandle = lg_getCertDB(sdb);
+
+ /* we can't store any private crls */
+ if (lg_isTrue(CKA_PRIVATE,templ,count)) {
+ return CKR_ATTRIBUTE_VALUE_INVALID;
+ }
+
+ if (certHandle == NULL) {
+ return CKR_TOKEN_WRITE_PROTECTED;
+ }
+
+ /* lookup SUBJECT */
+ subject = lg_FindAttribute(CKA_SUBJECT,templ,count);
+ if (!subject) {
+ return CKR_ATTRIBUTE_VALUE_INVALID;
+ }
+
+ derSubj.data = (unsigned char *)subject->pValue;
+ derSubj.len = subject->ulValueLen ;
+
+ /* lookup VALUE */
+ crl = lg_FindAttribute(CKA_VALUE,templ,count);
+ PORT_Assert(crl);
+ if (!crl) {
+ return CKR_ATTRIBUTE_VALUE_INVALID;
+ }
+ derCrl.data = (unsigned char *)crl->pValue;
+ derCrl.len = crl->ulValueLen ;
+
+ url = lg_getString(CKA_NETSCAPE_URL,templ,count);
+ isKRL = lg_isTrue(CKA_NETSCAPE_KRL,templ,count);
+
+ /* Store CRL by SUBJECT */
+ rv = nsslowcert_AddCrl(certHandle, &derCrl, &derSubj, url, isKRL);
+
+ if (url) {
+ PORT_Free(url);
+ }
+ if (rv != SECSuccess) {
+ return CKR_DEVICE_ERROR;
+ }
+
+ /* if we overwrote the existing CRL, poison the handle entry so we get
+ * a new object handle */
+ (void) lg_poisonHandle(sdb, &derSubj,
+ isKRL ? LG_TOKEN_KRL_HANDLE : LG_TOKEN_TYPE_CRL);
+ *handle = lg_mkHandle(sdb, &derSubj,
+ isKRL ? LG_TOKEN_KRL_HANDLE : LG_TOKEN_TYPE_CRL);
+
+ return CKR_OK;
+}
+
+/*
+ * check the consistancy and initialize a Public Key Object
+ */
+static CK_RV
+lg_createPublicKeyObject(SDB *sdb, CK_KEY_TYPE key_type,
+ CK_OBJECT_HANDLE *handle, const CK_ATTRIBUTE *templ, CK_ULONG count)
+{
+ CK_ATTRIBUTE_TYPE pubKeyAttr = CKA_VALUE;
+ CK_RV crv;
+ NSSLOWKEYPrivateKey *priv;
+ SECItem pubKey;
+ NSSLOWKEYDBHandle *keyHandle = NULL;
+
+ switch (key_type) {
+ case CKK_RSA:
+ pubKeyAttr = CKA_MODULUS;
+ break;
+#ifdef NSS_ENABLE_ECC
+ case CKK_EC:
+ pubKeyAttr = CKA_EC_POINT;
+ break;
+#endif /* NSS_ENABLE_ECC */
+ case CKK_DSA:
+ case CKK_DH:
+ break;
+ default:
+ return CKR_ATTRIBUTE_VALUE_INVALID;
+ }
+
+
+ crv = lg_Attribute2SSecItem(NULL,pubKeyAttr,templ,count,&pubKey);
+ if (crv != CKR_OK) return crv;
+
+ PORT_Assert(pubKey.data);
+ keyHandle = lg_getKeyDB(sdb);
+ if (keyHandle == NULL) {
+ PORT_Free(pubKey.data);
+ return CKR_TOKEN_WRITE_PROTECTED;
+ }
+ if (keyHandle->version != 3) {
+ unsigned char buf[SHA1_LENGTH];
+ SHA1_HashBuf(buf,pubKey.data,pubKey.len);
+ PORT_Memcpy(pubKey.data,buf,sizeof(buf));
+ pubKey.len = sizeof(buf);
+ }
+ /* make sure the associated private key already exists */
+ /* only works if we are logged in */
+ priv = nsslowkey_FindKeyByPublicKey(keyHandle, &pubKey, sdb /*password*/);
+ if (priv == NULL) {
+ PORT_Free(pubKey.data);
+ return crv;
+ }
+ nsslowkey_DestroyPrivateKey(priv);
+
+ *handle = lg_mkHandle(sdb, &pubKey, LG_TOKEN_TYPE_PUB);
+ PORT_Free(pubKey.data);
+
+ return CKR_OK;
+}
+
+/* make a private key from a verified object */
+static NSSLOWKEYPrivateKey *
+lg_mkPrivKey(SDB *sdb, const CK_ATTRIBUTE *templ, CK_ULONG count,
+ CK_KEY_TYPE key_type, CK_RV *crvp)
+{
+ NSSLOWKEYPrivateKey *privKey;
+ PLArenaPool *arena;
+ CK_RV crv = CKR_OK;
+ SECStatus rv;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (arena == NULL) {
+ *crvp = CKR_HOST_MEMORY;
+ return NULL;
+ }
+
+ privKey = (NSSLOWKEYPrivateKey *)
+ PORT_ArenaZAlloc(arena,sizeof(NSSLOWKEYPrivateKey));
+ if (privKey == NULL) {
+ PORT_FreeArena(arena,PR_FALSE);
+ *crvp = CKR_HOST_MEMORY;
+ return NULL;
+ }
+
+ /* in future this would be a switch on key_type */
+ privKey->arena = arena;
+ switch (key_type) {
+ case CKK_RSA:
+ privKey->keyType = NSSLOWKEYRSAKey;
+ crv=lg_Attribute2SSecItem(arena,CKA_MODULUS,templ,count,
+ &privKey->u.rsa.modulus);
+ if (crv != CKR_OK) break;
+ crv=lg_Attribute2SSecItem(arena,CKA_PUBLIC_EXPONENT,templ,count,
+ &privKey->u.rsa.publicExponent);
+ if (crv != CKR_OK) break;
+ crv=lg_PrivAttr2SSecItem(arena,CKA_PRIVATE_EXPONENT,templ,count,
+ &privKey->u.rsa.privateExponent, sdb);
+ if (crv != CKR_OK) break;
+ crv=lg_PrivAttr2SSecItem(arena,CKA_PRIME_1,templ,count,
+ &privKey->u.rsa.prime1, sdb);
+ if (crv != CKR_OK) break;
+ crv=lg_PrivAttr2SSecItem(arena,CKA_PRIME_2,templ,count,
+ &privKey->u.rsa.prime2, sdb);
+ if (crv != CKR_OK) break;
+ crv=lg_PrivAttr2SSecItem(arena,CKA_EXPONENT_1,templ,count,
+ &privKey->u.rsa.exponent1, sdb);
+ if (crv != CKR_OK) break;
+ crv=lg_PrivAttr2SSecItem(arena,CKA_EXPONENT_2,templ,count,
+ &privKey->u.rsa.exponent2, sdb);
+ if (crv != CKR_OK) break;
+ crv=lg_PrivAttr2SSecItem(arena,CKA_COEFFICIENT,templ,count,
+ &privKey->u.rsa.coefficient, sdb);
+ if (crv != CKR_OK) break;
+ rv = DER_SetUInteger(privKey->arena, &privKey->u.rsa.version,
+ NSSLOWKEY_VERSION);
+ if (rv != SECSuccess) crv = CKR_HOST_MEMORY;
+ break;
+
+ case CKK_DSA:
+ privKey->keyType = NSSLOWKEYDSAKey;
+ crv = lg_Attribute2SSecItem(arena,CKA_PRIME,templ,count,
+ &privKey->u.dsa.params.prime);
+ if (crv != CKR_OK) break;
+ crv = lg_Attribute2SSecItem(arena,CKA_SUBPRIME,templ,count,
+ &privKey->u.dsa.params.subPrime);
+ if (crv != CKR_OK) break;
+ crv = lg_Attribute2SSecItem(arena,CKA_BASE,templ,count,
+ &privKey->u.dsa.params.base);
+ if (crv != CKR_OK) break;
+ crv = lg_PrivAttr2SSecItem(arena,CKA_VALUE,templ,count,
+ &privKey->u.dsa.privateValue, sdb);
+ if (crv != CKR_OK) break;
+ if (lg_hasAttribute(CKA_NETSCAPE_DB, templ,count)) {
+ crv = lg_Attribute2SSecItem(arena, CKA_NETSCAPE_DB,templ,count,
+ &privKey->u.dsa.publicValue);
+ /* privKey was zero'd so public value is already set to NULL, 0
+ * if we don't set it explicitly */
+ }
+ break;
+
+ case CKK_DH:
+ privKey->keyType = NSSLOWKEYDHKey;
+ crv = lg_Attribute2SSecItem(arena,CKA_PRIME,templ,count,
+ &privKey->u.dh.prime);
+ if (crv != CKR_OK) break;
+ crv = lg_Attribute2SSecItem(arena,CKA_BASE,templ,count,
+ &privKey->u.dh.base);
+ if (crv != CKR_OK) break;
+ crv = lg_PrivAttr2SSecItem(arena,CKA_VALUE,templ,count,
+ &privKey->u.dh.privateValue, sdb);
+ if (crv != CKR_OK) break;
+ if (lg_hasAttribute(CKA_NETSCAPE_DB, templ, count)) {
+ crv = lg_Attribute2SSecItem(arena, CKA_NETSCAPE_DB,templ,count,
+ &privKey->u.dh.publicValue);
+ /* privKey was zero'd so public value is already set to NULL, 0
+ * if we don't set it explicitly */
+ }
+ break;
+
+#ifdef NSS_ENABLE_ECC
+ case CKK_EC:
+ privKey->keyType = NSSLOWKEYECKey;
+ crv = lg_Attribute2SSecItem(arena, CKA_EC_PARAMS,templ,count,
+ &privKey->u.ec.ecParams.DEREncoding);
+ if (crv != CKR_OK) break;
+
+ /* Fill out the rest of the ecParams structure
+ * based on the encoded params
+ */
+ if (LGEC_FillParams(arena, &privKey->u.ec.ecParams.DEREncoding,
+ &privKey->u.ec.ecParams) != SECSuccess) {
+ crv = CKR_DOMAIN_PARAMS_INVALID;
+ break;
+ }
+ crv = lg_PrivAttr2SSecItem(arena,CKA_VALUE,templ,count,
+ &privKey->u.ec.privateValue, sdb);
+ if (crv != CKR_OK) break;
+ if (lg_hasAttribute(CKA_NETSCAPE_DB,templ,count)) {
+ crv = lg_Attribute2SSecItem(arena, CKA_NETSCAPE_DB,templ,count,
+ &privKey->u.ec.publicValue);
+ if (crv != CKR_OK) break;
+ /* privKey was zero'd so public value is already set to NULL, 0
+ * if we don't set it explicitly */
+ }
+ rv = DER_SetUInteger(privKey->arena, &privKey->u.ec.version,
+ NSSLOWKEY_EC_PRIVATE_KEY_VERSION);
+ if (rv != SECSuccess) crv = CKR_HOST_MEMORY;
+ break;
+#endif /* NSS_ENABLE_ECC */
+
+ default:
+ crv = CKR_KEY_TYPE_INCONSISTENT;
+ break;
+ }
+ *crvp = crv;
+ if (crv != CKR_OK) {
+ PORT_FreeArena(arena,PR_FALSE);
+ return NULL;
+ }
+ return privKey;
+}
+
+/*
+ * check the consistancy and initialize a Private Key Object
+ */
+static CK_RV
+lg_createPrivateKeyObject(SDB *sdb, CK_KEY_TYPE key_type,
+ CK_OBJECT_HANDLE *handle, const CK_ATTRIBUTE *templ, CK_ULONG count)
+{
+ NSSLOWKEYPrivateKey *privKey;
+ char *label;
+ SECStatus rv = SECSuccess;
+ CK_RV crv = CKR_DEVICE_ERROR;
+ SECItem pubKey;
+ NSSLOWKEYDBHandle *keyHandle = lg_getKeyDB(sdb);
+
+ if (keyHandle == NULL) {
+ return CKR_TOKEN_WRITE_PROTECTED;
+ }
+
+ privKey=lg_mkPrivKey(sdb, templ,count,key_type,&crv);
+ if (privKey == NULL) return crv;
+ label = lg_getString(CKA_LABEL,templ,count);
+
+ crv = lg_Attribute2SSecItem(NULL,CKA_NETSCAPE_DB,templ,count,&pubKey);
+ if (crv != CKR_OK) {
+ crv = CKR_TEMPLATE_INCOMPLETE;
+ rv = SECFailure;
+ goto fail;
+ }
+#ifdef notdef
+ if (keyHandle->version != 3) {
+ unsigned char buf[SHA1_LENGTH];
+ SHA1_HashBuf(buf,pubKey.data,pubKey.len);
+ PORT_Memcpy(pubKey.data,buf,sizeof(buf));
+ pubKey.len = sizeof(buf);
+ }
+#endif
+ /* get the key type */
+ if (key_type == CKK_RSA) {
+ rv = RSA_PrivateKeyCheck(&privKey->u.rsa);
+ if (rv == SECFailure) {
+ goto fail;
+ }
+ }
+ rv = nsslowkey_StoreKeyByPublicKey(keyHandle, privKey, &pubKey,
+ label, sdb /*->password*/);
+
+fail:
+ if (label) PORT_Free(label);
+ *handle = lg_mkHandle(sdb,&pubKey,LG_TOKEN_TYPE_PRIV);
+ if (pubKey.data) PORT_Free(pubKey.data);
+ nsslowkey_DestroyPrivateKey(privKey);
+ if (rv != SECSuccess) return crv;
+
+ return CKR_OK;
+}
+
+
+#define LG_KEY_MAX_RETRIES 10 /* don't hang if we are having problems with the rng */
+#define LG_KEY_ID_SIZE 18 /* don't use either SHA1 or MD5 sizes */
+/*
+ * Secret keys must have a CKA_ID value to be stored in the database. This code
+ * will generate one if there wasn't one already.
+ */
+static CK_RV
+lg_GenerateSecretCKA_ID(NSSLOWKEYDBHandle *handle, SECItem *id, char *label)
+{
+ unsigned int retries;
+ SECStatus rv = SECSuccess;
+ CK_RV crv = CKR_OK;
+
+ id->data = NULL;
+ if (label) {
+ id->data = (unsigned char *)PORT_Strdup(label);
+ if (id->data == NULL) {
+ return CKR_HOST_MEMORY;
+ }
+ id->len = PORT_Strlen(label)+1;
+ if (!nsslowkey_KeyForIDExists(handle,id)) {
+ return CKR_OK;
+ }
+ PORT_Free(id->data);
+ id->data = NULL;
+ id->len = 0;
+ }
+ id->data = (unsigned char *)PORT_Alloc(LG_KEY_ID_SIZE);
+ if (id->data == NULL) {
+ return CKR_HOST_MEMORY;
+ }
+ id->len = LG_KEY_ID_SIZE;
+
+ retries = 0;
+ do {
+ rv = RNG_GenerateGlobalRandomBytes(id->data,id->len);
+ } while (rv == SECSuccess && nsslowkey_KeyForIDExists(handle,id) &&
+ (++retries <= LG_KEY_MAX_RETRIES));
+
+ if ((rv != SECSuccess) || (retries > LG_KEY_MAX_RETRIES)) {
+ crv = CKR_DEVICE_ERROR; /* random number generator is bad */
+ PORT_Free(id->data);
+ id->data = NULL;
+ id->len = 0;
+ }
+ return crv;
+}
+
+
+static NSSLOWKEYPrivateKey *lg_mkSecretKeyRep(const CK_ATTRIBUTE *templ,
+ CK_ULONG count, CK_KEY_TYPE key_type,
+ SECItem *pubkey, SDB *sdbpw)
+{
+ NSSLOWKEYPrivateKey *privKey = 0;
+ PLArenaPool *arena = 0;
+ CK_KEY_TYPE keyType;
+ PRUint32 keyTypeStorage;
+ SECItem keyTypeItem;
+ CK_RV crv;
+ SECStatus rv;
+ static unsigned char derZero[1] = { 0 };
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (arena == NULL) { crv = CKR_HOST_MEMORY; goto loser; }
+
+ privKey = (NSSLOWKEYPrivateKey *)
+ PORT_ArenaZAlloc(arena,sizeof(NSSLOWKEYPrivateKey));
+ if (privKey == NULL) { crv = CKR_HOST_MEMORY; goto loser; }
+
+ privKey->arena = arena;
+
+ /* Secret keys are represented in the database as "fake" RSA keys.
+ * The RSA key is marked as a secret key representation by setting the
+ * public exponent field to 0, which is an invalid RSA exponent.
+ * The other fields are set as follows:
+ * modulus - CKA_ID value for the secret key
+ * private exponent - CKA_VALUE (the key itself)
+ * coefficient - CKA_KEY_TYPE, which indicates what encryption algorithm
+ * is used for the key.
+ * all others - set to integer 0
+ */
+ privKey->keyType = NSSLOWKEYRSAKey;
+
+ /* The modulus is set to the key id of the symmetric key */
+ crv = lg_Attribute2SecItem(arena, CKA_ID, templ, count,
+ &privKey->u.rsa.modulus);
+ if (crv != CKR_OK) goto loser;
+
+ /* The public exponent is set to 0 length to indicate a special key */
+ privKey->u.rsa.publicExponent.len = sizeof derZero;
+ privKey->u.rsa.publicExponent.data = derZero;
+
+ /* The private exponent is the actual key value */
+ crv = lg_PrivAttr2SecItem(arena, CKA_VALUE, templ, count,
+ &privKey->u.rsa.privateExponent, sdbpw);
+ if (crv != CKR_OK) goto loser;
+
+ /* All other fields empty - needs testing */
+ privKey->u.rsa.prime1.len = sizeof derZero;
+ privKey->u.rsa.prime1.data = derZero;
+
+ privKey->u.rsa.prime2.len = sizeof derZero;
+ privKey->u.rsa.prime2.data = derZero;
+
+ privKey->u.rsa.exponent1.len = sizeof derZero;
+ privKey->u.rsa.exponent1.data = derZero;
+
+ privKey->u.rsa.exponent2.len = sizeof derZero;
+ privKey->u.rsa.exponent2.data = derZero;
+
+ /* Coeficient set to KEY_TYPE */
+ crv = lg_GetULongAttribute(CKA_KEY_TYPE, templ, count, &keyType);
+ if (crv != CKR_OK) goto loser;
+ /* on 64 bit platforms, we still want to store 32 bits of keyType (This is
+ * safe since the PKCS #11 defines for all types are 32 bits or less). */
+ keyTypeStorage = (PRUint32) keyType;
+ keyTypeStorage = PR_htonl(keyTypeStorage);
+ keyTypeItem.data = (unsigned char *)&keyTypeStorage;
+ keyTypeItem.len = sizeof (keyTypeStorage);
+ rv = SECITEM_CopyItem(arena, &privKey->u.rsa.coefficient, &keyTypeItem);
+ if (rv != SECSuccess) {
+ crv = CKR_HOST_MEMORY;
+ goto loser;
+ }
+
+ /* Private key version field set normally for compatibility */
+ rv = DER_SetUInteger(privKey->arena,
+ &privKey->u.rsa.version, NSSLOWKEY_VERSION);
+ if (rv != SECSuccess) { crv = CKR_HOST_MEMORY; goto loser; }
+
+loser:
+ if (crv != CKR_OK) {
+ PORT_FreeArena(arena,PR_FALSE);
+ privKey = 0;
+ }
+
+ return privKey;
+}
+
+/*
+ * check the consistancy and initialize a Secret Key Object
+ */
+static CK_RV
+lg_createSecretKeyObject(SDB *sdb, CK_KEY_TYPE key_type,
+ CK_OBJECT_HANDLE *handle, const CK_ATTRIBUTE *templ, CK_ULONG count)
+{
+ CK_RV crv;
+ NSSLOWKEYPrivateKey *privKey = NULL;
+ NSSLOWKEYDBHandle *keyHandle = NULL;
+ SECItem pubKey;
+ char *label = NULL;
+ SECStatus rv = SECSuccess;
+
+ pubKey.data = 0;
+
+ /* If the object is a TOKEN object, store in the database */
+ keyHandle = lg_getKeyDB(sdb);
+
+ if (keyHandle == NULL) {
+ return CKR_TOKEN_WRITE_PROTECTED;
+ }
+
+ label = lg_getString(CKA_LABEL,templ,count);
+
+ crv = lg_Attribute2SecItem(NULL,CKA_ID,templ,count,&pubKey);
+ /* Should this be ID? */
+ if (crv != CKR_OK) goto loser;
+
+ /* if we don't have an ID, generate one */
+ if (pubKey.len == 0) {
+ if (pubKey.data) {
+ PORT_Free(pubKey.data);
+ pubKey.data = NULL;
+ }
+ crv = lg_GenerateSecretCKA_ID(keyHandle, &pubKey, label);
+ if (crv != CKR_OK) goto loser;
+ }
+
+ privKey = lg_mkSecretKeyRep(templ, count, key_type, &pubKey, sdb);
+ if (privKey == NULL) {
+ crv = CKR_HOST_MEMORY;
+ goto loser;
+ }
+
+ rv = nsslowkey_StoreKeyByPublicKey(keyHandle,
+ privKey, &pubKey, label, sdb /*->password*/);
+ if (rv != SECSuccess) {
+ crv = CKR_DEVICE_ERROR;
+ goto loser;
+ }
+
+ *handle = lg_mkHandle(sdb, &pubKey, LG_TOKEN_TYPE_KEY);
+
+loser:
+ if (label) PORT_Free(label);
+ if (privKey) nsslowkey_DestroyPrivateKey(privKey);
+ if (pubKey.data) PORT_Free(pubKey.data);
+
+ return crv;
+}
+
+/*
+ * check the consistancy and initialize a Key Object
+ */
+static CK_RV
+lg_createKeyObject(SDB *sdb, CK_OBJECT_CLASS objclass,
+ CK_OBJECT_HANDLE *handle, const CK_ATTRIBUTE *templ, CK_ULONG count)
+{
+ CK_RV crv;
+ CK_KEY_TYPE key_type;
+
+ /* get the key type */
+ crv = lg_GetULongAttribute(CKA_KEY_TYPE, templ, count, &key_type);
+ if (crv != CKR_OK) {
+ return crv;
+ }
+
+ switch (objclass) {
+ case CKO_PUBLIC_KEY:
+ return lg_createPublicKeyObject(sdb,key_type,handle,templ,count);
+ case CKO_PRIVATE_KEY:
+ return lg_createPrivateKeyObject(sdb,key_type,handle,templ,count);
+ case CKO_SECRET_KEY:
+ return lg_createSecretKeyObject(sdb,key_type,handle,templ,count);
+ default:
+ break;
+ }
+ return CKR_ATTRIBUTE_VALUE_INVALID;
+}
+
+/*
+ * Parse the template and create an object stored in the DB that reflects.
+ * the object specified in the database.
+ */
+CK_RV
+lg_CreateObject(SDB *sdb, CK_OBJECT_HANDLE *handle,
+ const CK_ATTRIBUTE *templ, CK_ULONG count)
+{
+ CK_RV crv;
+ CK_OBJECT_CLASS objclass;
+
+ /* get the object class */
+ crv = lg_GetULongAttribute(CKA_CLASS, templ, count, &objclass);
+ if (crv != CKR_OK) {
+ return crv;
+ }
+
+ /* Now handle the specific object class.
+ */
+ switch (objclass) {
+ case CKO_CERTIFICATE:
+ crv = lg_createCertObject(sdb,handle,templ,count);
+ break;
+ case CKO_NETSCAPE_TRUST:
+ crv = lg_createTrustObject(sdb,handle,templ,count);
+ break;
+ case CKO_NETSCAPE_CRL:
+ crv = lg_createCrlObject(sdb,handle,templ,count);
+ break;
+ case CKO_NETSCAPE_SMIME:
+ crv = lg_createSMimeObject(sdb,handle,templ,count);
+ break;
+ case CKO_PRIVATE_KEY:
+ case CKO_PUBLIC_KEY:
+ case CKO_SECRET_KEY:
+ crv = lg_createKeyObject(sdb,objclass,handle,templ,count);
+ break;
+ default:
+ crv = CKR_ATTRIBUTE_VALUE_INVALID;
+ break;
+ }
+
+ return crv;
+}
+
diff --git a/security/nss/lib/softoken/legacydb/lgdb.h b/security/nss/lib/softoken/legacydb/lgdb.h
new file mode 100644
index 000000000..1c5744571
--- /dev/null
+++ b/security/nss/lib/softoken/legacydb/lgdb.h
@@ -0,0 +1,197 @@
+/* ***** 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 ***** */
+/*
+ * Internal data structures and functions used by pkcs11.c
+ */
+#ifndef _LGDB_H_
+#define _LGDB_H_ 1
+
+#include "nssilock.h"
+#include "seccomon.h"
+#include "secoidt.h"
+#include "lowkeyti.h"
+#include "pkcs11t.h"
+#include "sdb.h"
+#include "cdbhdl.h"
+
+
+#define MULTIACCESS "multiaccess:"
+
+
+/* machine dependent path stuff used by dbinit.c and pk11db.c */
+#ifdef macintosh
+#define PATH_SEPARATOR ":"
+#define SECMOD_DB "Security Modules"
+#define CERT_DB_FMT "%sCertificates%s"
+#define KEY_DB_FMT "%sKey Database%s"
+#else
+#define PATH_SEPARATOR "/"
+#define SECMOD_DB "secmod.db"
+#define CERT_DB_FMT "%scert%s.db"
+#define KEY_DB_FMT "%skey%s.db"
+#endif
+
+SEC_BEGIN_PROTOS
+
+
+/* internal utility functions used by pkcs11.c */
+extern const CK_ATTRIBUTE *lg_FindAttribute(CK_ATTRIBUTE_TYPE type,
+ const CK_ATTRIBUTE *templ, CK_ULONG count);
+extern CK_RV lg_Attribute2SecItem(PLArenaPool *,CK_ATTRIBUTE_TYPE type,
+ const CK_ATTRIBUTE *templ, CK_ULONG count,
+ SECItem *item);
+extern CK_RV lg_Attribute2SSecItem(PLArenaPool *,CK_ATTRIBUTE_TYPE type,
+ const CK_ATTRIBUTE *templ, CK_ULONG count,
+ SECItem *item);
+extern CK_RV lg_PrivAttr2SecItem(PLArenaPool *,CK_ATTRIBUTE_TYPE type,
+ const CK_ATTRIBUTE *templ, CK_ULONG count,
+ SECItem *item, SDB *sdbpw);
+extern CK_RV lg_PrivAttr2SSecItem(PLArenaPool *,CK_ATTRIBUTE_TYPE type,
+ const CK_ATTRIBUTE *templ, CK_ULONG count,
+ SECItem *item, SDB *sdbpw);
+extern CK_RV lg_GetULongAttribute(CK_ATTRIBUTE_TYPE type,
+ const CK_ATTRIBUTE *templ, CK_ULONG count,
+ CK_ULONG *out);
+extern PRBool lg_hasAttribute(CK_ATTRIBUTE_TYPE type,
+ const CK_ATTRIBUTE *templ, CK_ULONG count);
+extern PRBool lg_isTrue(CK_ATTRIBUTE_TYPE type,
+ const CK_ATTRIBUTE *templ, CK_ULONG count);
+extern PRBool lg_isSensitive(CK_ATTRIBUTE_TYPE type, CK_OBJECT_CLASS inClass);
+extern char *lg_getString(CK_ATTRIBUTE_TYPE type,
+ const CK_ATTRIBUTE *templ, CK_ULONG count);
+extern unsigned int lg_MapTrust(CK_TRUST trust, PRBool clientAuth);
+
+/* clear out all the existing object ID to database key mappings.
+ * used to reinit a token */
+extern CK_RV SFTK_ClearTokenKeyHashTable(SDB *sdb);
+
+
+extern void lg_FreeSearch(SDBFind *search);
+
+NSSLOWCERTCertDBHandle *lg_getCertDB(SDB *sdb);
+NSSLOWKEYDBHandle *lg_getKeyDB(SDB *sdb);
+
+const char *lg_EvaluateConfigDir(const char *configdir, char **domain);
+
+
+/*
+ * object handle modifiers
+ */
+#define LG_TOKEN_MASK 0xc0000000L
+#define LG_TOKEN_TYPE_MASK 0x38000000L
+#define LG_TOKEN_TYPE_SHIFT 27
+/* keydb (high bit == 0) */
+#define LG_TOKEN_TYPE_PRIV 0x08000000L
+#define LG_TOKEN_TYPE_PUB 0x10000000L
+#define LG_TOKEN_TYPE_KEY 0x18000000L
+/* certdb (high bit == 1) */
+#define LG_TOKEN_TYPE_TRUST 0x20000000L
+#define LG_TOKEN_TYPE_CRL 0x28000000L
+#define LG_TOKEN_TYPE_SMIME 0x30000000L
+#define LG_TOKEN_TYPE_CERT 0x38000000L
+
+#define LG_TOKEN_KRL_HANDLE (LG_TOKEN_TYPE_CRL|1)
+
+#define LG_SEARCH_BLOCK_SIZE 10
+#define LG_BUF_SPACE 50
+#define LG_STRICT PR_FALSE
+
+/*
+ * token object utilities
+ */
+void lg_addHandle(SDBFind *search, CK_OBJECT_HANDLE handle);
+PRBool lg_poisonHandle(SDB *sdb, SECItem *dbkey, CK_OBJECT_HANDLE handle);
+PRBool lg_tokenMatch(SDB *sdb, const SECItem *dbKey, CK_OBJECT_HANDLE class,
+ const CK_ATTRIBUTE *templ, CK_ULONG count);
+const SECItem *lg_lookupTokenKeyByHandle(SDB *sdb, CK_OBJECT_HANDLE handle);
+CK_OBJECT_HANDLE lg_mkHandle(SDB *sdb, SECItem *dbKey, CK_OBJECT_HANDLE class);
+SECStatus lg_deleteTokenKeyByHandle(SDB *sdb, CK_OBJECT_HANDLE handle);
+
+SECStatus lg_util_encrypt(PLArenaPool *arena, SDB *sdbpw,
+ SECItem *plainText, SECItem **cipherText);
+SECStatus lg_util_decrypt(SDB *sdbpw,
+ SECItem *cipherText, SECItem **plainText);
+PLHashTable *lg_GetHashTable(SDB *sdb);
+void lg_DBLock(SDB *sdb);
+void lg_DBUnlock(SDB *sdb);
+
+typedef void (*LGFreeFunc)(void *);
+
+
+/*
+ * database functions
+ */
+
+/* lg_FindObjectsInit initializes a search for token and session objects
+ * that match a template. */
+CK_RV lg_FindObjectsInit(SDB *sdb, const CK_ATTRIBUTE *pTemplate,
+ CK_ULONG ulCount, SDBFind **search);
+/* lg_FindObjects continues a search for token and session objects
+ * that match a template, obtaining additional object handles. */
+CK_RV lg_FindObjects(SDB *sdb, SDBFind *search,
+ CK_OBJECT_HANDLE *phObject,CK_ULONG ulMaxObjectCount,
+ CK_ULONG *pulObjectCount);
+
+/* lg_FindObjectsFinal finishes a search for token and session objects. */
+CK_RV lg_FindObjectsFinal(SDB* lgdb, SDBFind *search);
+
+/* lg_CreateObject parses the template and create an object stored in the
+ * DB that reflects the object specified in the template. */
+CK_RV lg_CreateObject(SDB *sdb, CK_OBJECT_HANDLE *handle,
+ const CK_ATTRIBUTE *templ, CK_ULONG count);
+
+CK_RV lg_GetAttributeValue(SDB *sdb, CK_OBJECT_HANDLE object_id,
+ CK_ATTRIBUTE *template, CK_ULONG count);
+CK_RV lg_SetAttributeValue(SDB *sdb, CK_OBJECT_HANDLE object_id,
+ const CK_ATTRIBUTE *template, CK_ULONG count);
+CK_RV lg_DestroyObject(SDB *sdb, CK_OBJECT_HANDLE object_id);
+
+CK_RV lg_Close(SDB *sdb);
+CK_RV lg_Reset(SDB *sdb);
+
+/*
+ * The old database doesn't share and doesn't support
+ * transactions.
+ */
+CK_RV lg_Begin(SDB *sdb);
+CK_RV lg_Commit(SDB *sdb);
+CK_RV lg_Abort(SDB *sdb);
+CK_RV lg_GetPWEntry(SDB *sdb, SDBPasswordEntry *entry);
+CK_RV lg_PutPWEntry(SDB *sdb, SDBPasswordEntry *entry);
+
+SEC_END_PROTOS
+
+#endif /* _LGDB_H_ */
diff --git a/security/nss/lib/softoken/legacydb/lgdestroy.c b/security/nss/lib/softoken/legacydb/lgdestroy.c
new file mode 100644
index 000000000..37db2e50e
--- /dev/null
+++ b/security/nss/lib/softoken/legacydb/lgdestroy.c
@@ -0,0 +1,144 @@
+/* ***** 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):
+ * Dr Vipul Gupta <vipul.gupta@sun.com>, Sun Microsystems Laboratories
+ *
+ * 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 ***** */
+/*
+ * Internal PKCS #11 functions. Should only be called by pkcs11.c
+ */
+#include "pkcs11.h"
+#include "lgdb.h"
+#include "pcert.h"
+#include "lowkeyi.h"
+
+/*
+ * remove an object.
+ */
+CK_RV
+lg_DestroyObject(SDB *sdb, CK_OBJECT_HANDLE object_id)
+{
+ CK_RV crv = CKR_OK;
+ SECStatus rv;
+ NSSLOWCERTCertificate *cert;
+ NSSLOWCERTCertTrust tmptrust;
+ PRBool isKrl;
+ NSSLOWKEYDBHandle *keyHandle;
+ NSSLOWCERTCertDBHandle *certHandle;
+ const SECItem *dbKey;
+
+ object_id &= ~LG_TOKEN_MASK;
+ dbKey = lg_lookupTokenKeyByHandle(sdb,object_id);
+ if (dbKey == NULL) {
+ return CKR_OBJECT_HANDLE_INVALID;
+ }
+
+ /* remove the objects from the real data base */
+ switch (object_id & LG_TOKEN_TYPE_MASK) {
+ case LG_TOKEN_TYPE_PRIV:
+ case LG_TOKEN_TYPE_KEY:
+ /* KEYID is the public KEY for DSA and DH, and the MODULUS for
+ * RSA */
+ keyHandle = lg_getKeyDB(sdb);
+ if (!keyHandle) {
+ crv = CKR_TOKEN_WRITE_PROTECTED;
+ break;
+ }
+ rv = nsslowkey_DeleteKey(keyHandle, dbKey);
+ if (rv != SECSuccess) {
+ crv = CKR_DEVICE_ERROR;
+ }
+ break;
+ case LG_TOKEN_TYPE_PUB:
+ break; /* public keys only exist at the behest of the priv key */
+ case LG_TOKEN_TYPE_CERT:
+ certHandle = lg_getCertDB(sdb);
+ if (!certHandle) {
+ crv = CKR_TOKEN_WRITE_PROTECTED;
+ break;
+ }
+ cert = nsslowcert_FindCertByKey(certHandle,dbKey);
+ if (cert == NULL) {
+ crv = CKR_DEVICE_ERROR;
+ break;
+ }
+ rv = nsslowcert_DeletePermCertificate(cert);
+ if (rv != SECSuccess) {
+ crv = CKR_DEVICE_ERROR;
+ }
+ nsslowcert_DestroyCertificate(cert);
+ break;
+ case LG_TOKEN_TYPE_CRL:
+ certHandle = lg_getCertDB(sdb);
+ if (!certHandle) {
+ crv = CKR_TOKEN_WRITE_PROTECTED;
+ break;
+ }
+ isKrl = (PRBool) (object_id == LG_TOKEN_KRL_HANDLE);
+ rv = nsslowcert_DeletePermCRL(certHandle, dbKey, isKrl);
+ if (rv == SECFailure) crv = CKR_DEVICE_ERROR;
+ break;
+ case LG_TOKEN_TYPE_TRUST:
+ certHandle = lg_getCertDB(sdb);
+ if (!certHandle) {
+ crv = CKR_TOKEN_WRITE_PROTECTED;
+ break;
+ }
+ cert = nsslowcert_FindCertByKey(certHandle, dbKey);
+ if (cert == NULL) {
+ crv = CKR_DEVICE_ERROR;
+ break;
+ }
+ tmptrust = *cert->trust;
+ tmptrust.sslFlags &= CERTDB_PRESERVE_TRUST_BITS;
+ tmptrust.emailFlags &= CERTDB_PRESERVE_TRUST_BITS;
+ tmptrust.objectSigningFlags &= CERTDB_PRESERVE_TRUST_BITS;
+ tmptrust.sslFlags |= CERTDB_TRUSTED_UNKNOWN;
+ tmptrust.emailFlags |= CERTDB_TRUSTED_UNKNOWN;
+ tmptrust.objectSigningFlags |= CERTDB_TRUSTED_UNKNOWN;
+ rv = nsslowcert_ChangeCertTrust(certHandle, cert, &tmptrust);
+ if (rv != SECSuccess) crv = CKR_DEVICE_ERROR;
+ nsslowcert_DestroyCertificate(cert);
+ break;
+ default:
+ break;
+ }
+ lg_DBLock(sdb);
+ lg_deleteTokenKeyByHandle(sdb,object_id);
+ lg_DBUnlock(sdb);
+
+ return crv;
+}
+
+
+
diff --git a/security/nss/lib/softoken/legacydb/lgfind.c b/security/nss/lib/softoken/legacydb/lgfind.c
new file mode 100644
index 000000000..b7029c7d5
--- /dev/null
+++ b/security/nss/lib/softoken/legacydb/lgfind.c
@@ -0,0 +1,941 @@
+/* ***** 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.
+ *
+ *
+ * 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 "secitem.h"
+#include "pkcs11.h"
+#include "lgdb.h"
+#include "lowkeyi.h"
+#include "pcert.h"
+#include "blapi.h"
+
+#include "keydbi.h"
+
+/*
+ * This code maps PKCS #11 Finds to legacy database searches. This code
+ * was orginally in pkcs11.c in previous versions of NSS.
+ */
+
+struct SDBFindStr {
+ CK_OBJECT_HANDLE *handles;
+ int size;
+ int index;
+ int array_size;
+};
+
+
+/*
+ * free a search structure
+ */
+void
+lg_FreeSearch(SDBFind *search)
+{
+ if (search->handles) {
+ PORT_Free(search->handles);
+ }
+ PORT_Free(search);
+}
+
+void
+lg_addHandle(SDBFind *search, CK_OBJECT_HANDLE handle)
+{
+ if (search->handles == NULL) {
+ return;
+ }
+ if (search->size >= search->array_size) {
+ search->array_size += LG_SEARCH_BLOCK_SIZE;
+ search->handles = (CK_OBJECT_HANDLE *) PORT_Realloc(search->handles,
+ sizeof(CK_OBJECT_HANDLE)* search->array_size);
+ if (search->handles == NULL) {
+ return;
+ }
+ }
+ search->handles[search->size] = handle;
+ search->size++;
+}
+
+/*
+ * find any certs that may match the template and load them.
+ */
+#define LG_CERT 0x00000001
+#define LG_TRUST 0x00000002
+#define LG_CRL 0x00000004
+#define LG_SMIME 0x00000008
+#define LG_PRIVATE 0x00000010
+#define LG_PUBLIC 0x00000020
+#define LG_KEY 0x00000040
+
+/*
+ * structure to collect key handles.
+ */
+typedef struct lgEntryDataStr {
+ SDB *sdb;
+ SDBFind *searchHandles;
+ const CK_ATTRIBUTE *template;
+ CK_ULONG templ_count;
+} lgEntryData;
+
+
+static SECStatus
+lg_crl_collect(SECItem *data, SECItem *key, certDBEntryType type, void *arg)
+{
+ lgEntryData *crlData;
+ CK_OBJECT_HANDLE class_handle;
+ SDB *sdb;
+
+ crlData = (lgEntryData *)arg;
+ sdb = crlData->sdb;
+
+ class_handle = (type == certDBEntryTypeRevocation) ? LG_TOKEN_TYPE_CRL :
+ LG_TOKEN_KRL_HANDLE;
+ if (lg_tokenMatch(sdb, key, class_handle,
+ crlData->template, crlData->templ_count)) {
+ lg_addHandle(crlData->searchHandles,
+ lg_mkHandle(sdb,key,class_handle));
+ }
+ return(SECSuccess);
+}
+
+static void
+lg_searchCrls(SDB *sdb, SECItem *derSubject, PRBool isKrl,
+ unsigned long classFlags, SDBFind *search,
+ const CK_ATTRIBUTE *pTemplate, CK_ULONG ulCount)
+{
+ NSSLOWCERTCertDBHandle *certHandle = NULL;
+
+ certHandle = lg_getCertDB(sdb);
+ if (certHandle == NULL) {
+ return;
+ }
+ if (derSubject->data != NULL) {
+ certDBEntryRevocation *crl =
+ nsslowcert_FindCrlByKey(certHandle, derSubject, isKrl);
+
+ if (crl != NULL) {
+ lg_addHandle(search, lg_mkHandle(sdb, derSubject,
+ isKrl ? LG_TOKEN_KRL_HANDLE : LG_TOKEN_TYPE_CRL));
+ nsslowcert_DestroyDBEntry((certDBEntry *)crl);
+ }
+ } else {
+ lgEntryData crlData;
+
+ /* traverse */
+ crlData.sdb = sdb;
+ crlData.searchHandles = search;
+ crlData.template = pTemplate;
+ crlData.templ_count = ulCount;
+ nsslowcert_TraverseDBEntries(certHandle, certDBEntryTypeRevocation,
+ lg_crl_collect, (void *)&crlData);
+ nsslowcert_TraverseDBEntries(certHandle, certDBEntryTypeKeyRevocation,
+ lg_crl_collect, (void *)&crlData);
+ }
+}
+
+/*
+ * structure to collect key handles.
+ */
+typedef struct lgKeyDataStr {
+ SDB *sdb;
+ NSSLOWKEYDBHandle *keyHandle;
+ SDBFind *searchHandles;
+ SECItem *id;
+ const CK_ATTRIBUTE *template;
+ CK_ULONG templ_count;
+ unsigned long classFlags;
+ PRBool strict;
+} lgKeyData;
+
+static PRBool
+isSecretKey(NSSLOWKEYPrivateKey *privKey)
+{
+ if (privKey->keyType == NSSLOWKEYRSAKey &&
+ privKey->u.rsa.publicExponent.len == 1 &&
+ privKey->u.rsa.publicExponent.data[0] == 0)
+ return PR_TRUE;
+
+ return PR_FALSE;
+}
+
+
+
+static SECStatus
+lg_key_collect(DBT *key, DBT *data, void *arg)
+{
+ lgKeyData *keyData;
+ NSSLOWKEYPrivateKey *privKey = NULL;
+ SECItem tmpDBKey;
+ SDB *sdb;
+ unsigned long classFlags;
+
+ keyData = (lgKeyData *)arg;
+ sdb = keyData->sdb;
+ classFlags = keyData->classFlags;
+
+ tmpDBKey.data = key->data;
+ tmpDBKey.len = key->size;
+ tmpDBKey.type = siBuffer;
+
+ PORT_Assert(keyData->keyHandle);
+ if (!keyData->strict && keyData->id) {
+ SECItem result;
+ PRBool haveMatch= PR_FALSE;
+ unsigned char hashKey[SHA1_LENGTH];
+ result.data = hashKey;
+ result.len = sizeof(hashKey);
+
+ if (keyData->id->len == 0) {
+ /* Make sure this isn't a LG_KEY */
+ privKey = nsslowkey_FindKeyByPublicKey(keyData->keyHandle,
+ &tmpDBKey, keyData->sdb/*->password*/);
+ if (privKey) {
+ /* turn off the unneeded class flags */
+ classFlags &= isSecretKey(privKey) ? ~(LG_PRIVATE|LG_PUBLIC) :
+ ~LG_KEY;
+ haveMatch = (PRBool)
+ ((classFlags & (LG_KEY|LG_PRIVATE|LG_PUBLIC)) != 0);
+ nsslowkey_DestroyPrivateKey(privKey);
+ }
+ } else {
+ SHA1_HashBuf( hashKey, key->data, key->size ); /* match id */
+ haveMatch = SECITEM_ItemsAreEqual(keyData->id,&result);
+ if (!haveMatch && ((unsigned char *)key->data)[0] == 0) {
+ /* This is a fix for backwards compatibility. The key
+ * database indexes private keys by the public key, and
+ * versions of NSS prior to 3.4 stored the public key as
+ * a signed integer. The public key is now treated as an
+ * unsigned integer, with no leading zero. In order to
+ * correctly compute the hash of an old key, it is necessary
+ * to fallback and detect the leading zero.
+ */
+ SHA1_HashBuf(hashKey,
+ (unsigned char *)key->data + 1, key->size - 1);
+ haveMatch = SECITEM_ItemsAreEqual(keyData->id,&result);
+ }
+ }
+ if (haveMatch) {
+ if (classFlags & LG_PRIVATE) {
+ lg_addHandle(keyData->searchHandles,
+ lg_mkHandle(sdb,&tmpDBKey,LG_TOKEN_TYPE_PRIV));
+ }
+ if (classFlags & LG_PUBLIC) {
+ lg_addHandle(keyData->searchHandles,
+ lg_mkHandle(sdb,&tmpDBKey,LG_TOKEN_TYPE_PUB));
+ }
+ if (classFlags & LG_KEY) {
+ lg_addHandle(keyData->searchHandles,
+ lg_mkHandle(sdb,&tmpDBKey,LG_TOKEN_TYPE_KEY));
+ }
+ }
+ return SECSuccess;
+ }
+
+ privKey = nsslowkey_FindKeyByPublicKey(keyData->keyHandle, &tmpDBKey,
+ keyData->sdb/*->password*/);
+ if ( privKey == NULL ) {
+ goto loser;
+ }
+
+ if (isSecretKey(privKey)) {
+ if ((classFlags & LG_KEY) &&
+ lg_tokenMatch(keyData->sdb, &tmpDBKey, LG_TOKEN_TYPE_KEY,
+ keyData->template, keyData->templ_count)) {
+ lg_addHandle(keyData->searchHandles,
+ lg_mkHandle(keyData->sdb, &tmpDBKey, LG_TOKEN_TYPE_KEY));
+ }
+ } else {
+ if ((classFlags & LG_PRIVATE) &&
+ lg_tokenMatch(keyData->sdb, &tmpDBKey, LG_TOKEN_TYPE_PRIV,
+ keyData->template, keyData->templ_count)) {
+ lg_addHandle(keyData->searchHandles,
+ lg_mkHandle(keyData->sdb,&tmpDBKey,LG_TOKEN_TYPE_PRIV));
+ }
+ if ((classFlags & LG_PUBLIC) &&
+ lg_tokenMatch(keyData->sdb, &tmpDBKey, LG_TOKEN_TYPE_PUB,
+ keyData->template, keyData->templ_count)) {
+ lg_addHandle(keyData->searchHandles,
+ lg_mkHandle(keyData->sdb, &tmpDBKey,LG_TOKEN_TYPE_PUB));
+ }
+ }
+
+loser:
+ if ( privKey ) {
+ nsslowkey_DestroyPrivateKey(privKey);
+ }
+ return(SECSuccess);
+}
+
+static void
+lg_searchKeys(SDB *sdb, SECItem *key_id,
+ unsigned long classFlags, SDBFind *search, PRBool mustStrict,
+ const CK_ATTRIBUTE *pTemplate, CK_ULONG ulCount)
+{
+ NSSLOWKEYDBHandle *keyHandle = NULL;
+ NSSLOWKEYPrivateKey *privKey;
+ lgKeyData keyData;
+ PRBool found = PR_FALSE;
+
+ keyHandle = lg_getKeyDB(sdb);
+ if (keyHandle == NULL) {
+ return;
+ }
+
+ if (key_id->data) {
+ privKey = nsslowkey_FindKeyByPublicKey(keyHandle, key_id, sdb);
+ if (privKey) {
+ if ((classFlags & LG_KEY) && isSecretKey(privKey)) {
+ lg_addHandle(search,
+ lg_mkHandle(sdb,key_id,LG_TOKEN_TYPE_KEY));
+ found = PR_TRUE;
+ }
+ if ((classFlags & LG_PRIVATE) && !isSecretKey(privKey)) {
+ lg_addHandle(search,
+ lg_mkHandle(sdb,key_id,LG_TOKEN_TYPE_PRIV));
+ found = PR_TRUE;
+ }
+ if ((classFlags & LG_PUBLIC) && !isSecretKey(privKey)) {
+ lg_addHandle(search,
+ lg_mkHandle(sdb,key_id,LG_TOKEN_TYPE_PUB));
+ found = PR_TRUE;
+ }
+ nsslowkey_DestroyPrivateKey(privKey);
+ }
+ /* don't do the traversal if we have an up to date db */
+ if (keyHandle->version != 3) {
+ goto loser;
+ }
+ /* don't do the traversal if it can't possibly be the correct id */
+ /* all soft token id's are SHA1_HASH_LEN's */
+ if (key_id->len != SHA1_LENGTH) {
+ goto loser;
+ }
+ if (found) {
+ /* if we already found some keys, don't do the traversal */
+ goto loser;
+ }
+ }
+ keyData.sdb = sdb;
+ keyData.keyHandle = keyHandle;
+ keyData.searchHandles = search;
+ keyData.id = key_id;
+ keyData.template = pTemplate;
+ keyData.templ_count = ulCount;
+ keyData.classFlags = classFlags;
+ keyData.strict = mustStrict ? mustStrict : LG_STRICT;
+
+ nsslowkey_TraverseKeys(keyHandle, lg_key_collect, &keyData);
+
+loser:
+ return;
+}
+
+/*
+ * structure to collect certs into
+ */
+typedef struct lgCertDataStr {
+ SDB *sdb;
+ int cert_count;
+ int max_cert_count;
+ NSSLOWCERTCertificate **certs;
+ const CK_ATTRIBUTE *template;
+ CK_ULONG templ_count;
+ unsigned long classFlags;
+ PRBool strict;
+} lgCertData;
+
+/*
+ * collect all the certs from the traverse call.
+ */
+static SECStatus
+lg_cert_collect(NSSLOWCERTCertificate *cert,void *arg)
+{
+ lgCertData *cd = (lgCertData *)arg;
+
+ if (cert == NULL) {
+ return SECSuccess;
+ }
+
+ if (cd->certs == NULL) {
+ return SECFailure;
+ }
+
+ if (cd->strict) {
+ if ((cd->classFlags & LG_CERT) && !lg_tokenMatch(cd->sdb,
+ &cert->certKey, LG_TOKEN_TYPE_CERT, cd->template,cd->templ_count)) {
+ return SECSuccess;
+ }
+ if ((cd->classFlags & LG_TRUST) && !lg_tokenMatch(cd->sdb,
+ &cert->certKey, LG_TOKEN_TYPE_TRUST,
+ cd->template, cd->templ_count)) {
+ return SECSuccess;
+ }
+ }
+
+ /* allocate more space if we need it. This should only happen in
+ * the general traversal case */
+ if (cd->cert_count >= cd->max_cert_count) {
+ int size;
+ cd->max_cert_count += LG_SEARCH_BLOCK_SIZE;
+ size = cd->max_cert_count * sizeof (NSSLOWCERTCertificate *);
+ cd->certs = (NSSLOWCERTCertificate **)PORT_Realloc(cd->certs,size);
+ if (cd->certs == NULL) {
+ return SECFailure;
+ }
+ }
+
+ cd->certs[cd->cert_count++] = nsslowcert_DupCertificate(cert);
+ return SECSuccess;
+}
+
+/* provide impedence matching ... */
+static SECStatus
+lg_cert_collect2(NSSLOWCERTCertificate *cert, SECItem *dymmy, void *arg)
+{
+ return lg_cert_collect(cert, arg);
+}
+
+static void
+lg_searchSingleCert(lgCertData *certData,NSSLOWCERTCertificate *cert)
+{
+ if (cert == NULL) {
+ return;
+ }
+ if (certData->strict &&
+ !lg_tokenMatch(certData->sdb, &cert->certKey, LG_TOKEN_TYPE_CERT,
+ certData->template,certData->templ_count)) {
+ nsslowcert_DestroyCertificate(cert);
+ return;
+ }
+ certData->certs = (NSSLOWCERTCertificate **)
+ PORT_Alloc(sizeof (NSSLOWCERTCertificate *));
+ if (certData->certs == NULL) {
+ nsslowcert_DestroyCertificate(cert);
+ return;
+ }
+ certData->certs[0] = cert;
+ certData->cert_count = 1;
+}
+
+static void
+lg_CertSetupData(lgCertData *certData,int count)
+{
+ certData->max_cert_count = count;
+
+ if (certData->max_cert_count <= 0) {
+ return;
+ }
+ certData->certs = (NSSLOWCERTCertificate **)
+ PORT_Alloc( count * sizeof(NSSLOWCERTCertificate *));
+ return;
+}
+
+static void
+lg_searchCertsAndTrust(SDB *sdb, SECItem *derCert, SECItem *name,
+ SECItem *derSubject, NSSLOWCERTIssuerAndSN *issuerSN,
+ SECItem *email,
+ unsigned long classFlags, SDBFind *handles,
+ const CK_ATTRIBUTE *pTemplate, CK_LONG ulCount)
+{
+ NSSLOWCERTCertDBHandle *certHandle = NULL;
+ lgCertData certData;
+ int i;
+
+ certHandle = lg_getCertDB(sdb);
+ if (certHandle == NULL) return;
+
+ certData.sdb = sdb;
+ certData.max_cert_count = 0;
+ certData.certs = NULL;
+ certData.cert_count = 0;
+ certData.template = pTemplate;
+ certData.templ_count = ulCount;
+ certData.classFlags = classFlags;
+ certData.strict = LG_STRICT;
+
+
+ /*
+ * Find the Cert.
+ */
+ if (derCert->data != NULL) {
+ NSSLOWCERTCertificate *cert =
+ nsslowcert_FindCertByDERCert(certHandle,derCert);
+ lg_searchSingleCert(&certData,cert);
+ } else if (name->data != NULL) {
+ char *tmp_name = (char*)PORT_Alloc(name->len+1);
+ int count;
+
+ if (tmp_name == NULL) {
+ return;
+ }
+ PORT_Memcpy(tmp_name,name->data,name->len);
+ tmp_name[name->len] = 0;
+
+ count= nsslowcert_NumPermCertsForNickname(certHandle,tmp_name);
+ lg_CertSetupData(&certData,count);
+ nsslowcert_TraversePermCertsForNickname(certHandle,tmp_name,
+ lg_cert_collect, &certData);
+ PORT_Free(tmp_name);
+ } else if (derSubject->data != NULL) {
+ int count;
+
+ count = nsslowcert_NumPermCertsForSubject(certHandle,derSubject);
+ lg_CertSetupData(&certData,count);
+ nsslowcert_TraversePermCertsForSubject(certHandle,derSubject,
+ lg_cert_collect, &certData);
+ } else if ((issuerSN->derIssuer.data != NULL) &&
+ (issuerSN->serialNumber.data != NULL)) {
+ if (classFlags & LG_CERT) {
+ NSSLOWCERTCertificate *cert =
+ nsslowcert_FindCertByIssuerAndSN(certHandle,issuerSN);
+
+ lg_searchSingleCert(&certData,cert);
+ }
+ if (classFlags & LG_TRUST) {
+ NSSLOWCERTTrust *trust =
+ nsslowcert_FindTrustByIssuerAndSN(certHandle, issuerSN);
+
+ if (trust) {
+ lg_addHandle(handles,
+ lg_mkHandle(sdb,&trust->dbKey,LG_TOKEN_TYPE_TRUST));
+ nsslowcert_DestroyTrust(trust);
+ }
+ }
+ } else if (email->data != NULL) {
+ char *tmp_name = (char*)PORT_Alloc(email->len+1);
+ certDBEntrySMime *entry = NULL;
+
+ if (tmp_name == NULL) {
+ return;
+ }
+ PORT_Memcpy(tmp_name,email->data,email->len);
+ tmp_name[email->len] = 0;
+
+ entry = nsslowcert_ReadDBSMimeEntry(certHandle,tmp_name);
+ if (entry) {
+ int count;
+ SECItem *subjectName = &entry->subjectName;
+
+ count = nsslowcert_NumPermCertsForSubject(certHandle, subjectName);
+ lg_CertSetupData(&certData,count);
+ nsslowcert_TraversePermCertsForSubject(certHandle, subjectName,
+ lg_cert_collect, &certData);
+
+ nsslowcert_DestroyDBEntry((certDBEntry *)entry);
+ }
+ PORT_Free(tmp_name);
+ } else {
+ /* we aren't filtering the certs, we are working on all, so turn
+ * on the strict filters. */
+ certData.strict = PR_TRUE;
+ lg_CertSetupData(&certData,LG_SEARCH_BLOCK_SIZE);
+ nsslowcert_TraversePermCerts(certHandle, lg_cert_collect2, &certData);
+ }
+
+ /*
+ * build the handles
+ */
+ for (i=0 ; i < certData.cert_count ; i++) {
+ NSSLOWCERTCertificate *cert = certData.certs[i];
+
+ /* if we filtered it would have been on the stuff above */
+ if (classFlags & LG_CERT) {
+ lg_addHandle(handles,
+ lg_mkHandle(sdb,&cert->certKey,LG_TOKEN_TYPE_CERT));
+ }
+ if ((classFlags & LG_TRUST) && nsslowcert_hasTrust(cert->trust)) {
+ lg_addHandle(handles,
+ lg_mkHandle(sdb,&cert->certKey,LG_TOKEN_TYPE_TRUST));
+ }
+ nsslowcert_DestroyCertificate(cert);
+ }
+
+ if (certData.certs) PORT_Free(certData.certs);
+ return;
+}
+
+static SECStatus
+lg_smime_collect(SECItem *data, SECItem *key, certDBEntryType type, void *arg)
+{
+ lgEntryData *smimeData;
+ SDB *sdb;
+
+ smimeData = (lgEntryData *)arg;
+ sdb = smimeData->sdb;
+
+ if (lg_tokenMatch(sdb, key, LG_TOKEN_TYPE_SMIME,
+ smimeData->template, smimeData->templ_count)) {
+ lg_addHandle(smimeData->searchHandles,
+ lg_mkHandle(sdb,key,LG_TOKEN_TYPE_SMIME));
+ }
+ return(SECSuccess);
+}
+
+static void
+lg_searchSMime(SDB *sdb, SECItem *email, SDBFind *handles,
+ const CK_ATTRIBUTE *pTemplate, CK_LONG ulCount)
+{
+ NSSLOWCERTCertDBHandle *certHandle = NULL;
+ certDBEntrySMime *entry;
+
+ certHandle = lg_getCertDB(sdb);
+ if (certHandle == NULL) return;
+
+ if (email->data != NULL) {
+ char *tmp_name = (char*)PORT_Alloc(email->len+1);
+
+ if (tmp_name == NULL) {
+ return;
+ }
+ PORT_Memcpy(tmp_name,email->data,email->len);
+ tmp_name[email->len] = 0;
+
+ entry = nsslowcert_ReadDBSMimeEntry(certHandle,tmp_name);
+ if (entry) {
+ SECItem emailKey;
+
+ emailKey.data = (unsigned char *)tmp_name;
+ emailKey.len = PORT_Strlen(tmp_name)+1;
+ emailKey.type = 0;
+ lg_addHandle(handles,
+ lg_mkHandle(sdb,&emailKey,LG_TOKEN_TYPE_SMIME));
+ nsslowcert_DestroyDBEntry((certDBEntry *)entry);
+ }
+ PORT_Free(tmp_name);
+ } else {
+ /* traverse */
+ lgEntryData smimeData;
+
+ /* traverse */
+ smimeData.sdb = sdb;
+ smimeData.searchHandles = handles;
+ smimeData.template = pTemplate;
+ smimeData.templ_count = ulCount;
+ nsslowcert_TraverseDBEntries(certHandle, certDBEntryTypeSMimeProfile,
+ lg_smime_collect, (void *)&smimeData);
+ }
+ return;
+}
+
+static CK_RV
+lg_searchTokenList(SDB *sdb, SDBFind *search,
+ const CK_ATTRIBUTE *pTemplate, CK_LONG ulCount)
+{
+ int i;
+ PRBool isKrl = PR_FALSE;
+ SECItem derCert = { siBuffer, NULL, 0 };
+ SECItem derSubject = { siBuffer, NULL, 0 };
+ SECItem name = { siBuffer, NULL, 0 };
+ SECItem email = { siBuffer, NULL, 0 };
+ SECItem key_id = { siBuffer, NULL, 0 };
+ SECItem cert_sha1_hash = { siBuffer, NULL, 0 };
+ SECItem cert_md5_hash = { siBuffer, NULL, 0 };
+ NSSLOWCERTIssuerAndSN issuerSN = {
+ { siBuffer, NULL, 0 },
+ { siBuffer, NULL, 0 }
+ };
+ SECItem *copy = NULL;
+ CK_CERTIFICATE_TYPE certType;
+ CK_OBJECT_CLASS objectClass;
+ CK_RV crv;
+ unsigned long classFlags =
+ LG_CERT|LG_TRUST|LG_PRIVATE|LG_PUBLIC|LG_KEY|LG_SMIME|LG_CRL;
+
+ if (lg_getCertDB(sdb) == NULL) {
+ classFlags = LG_PRIVATE|LG_KEY;
+ } else {
+ classFlags = LG_CERT|LG_TRUST|LG_PUBLIC|LG_SMIME|LG_CRL;
+ }
+
+ /*
+ * look for things to search on token objects for. If the right options
+ * are specified, we can use them as direct indeces into the database
+ * (rather than using linear searches. We can also use the attributes to
+ * limit the kinds of objects we are searching for. Later we can use this
+ * array to filter the remaining objects more finely.
+ */
+ for (i=0 ;classFlags && i < (int)ulCount; i++) {
+
+ switch (pTemplate[i].type) {
+ case CKA_SUBJECT:
+ copy = &derSubject;
+ classFlags &= (LG_CERT|LG_PRIVATE|LG_PUBLIC|LG_SMIME|LG_CRL);
+ break;
+ case CKA_ISSUER:
+ copy = &issuerSN.derIssuer;
+ classFlags &= (LG_CERT|LG_TRUST);
+ break;
+ case CKA_SERIAL_NUMBER:
+ copy = &issuerSN.serialNumber;
+ classFlags &= (LG_CERT|LG_TRUST);
+ break;
+ case CKA_VALUE:
+ copy = &derCert;
+ classFlags &= (LG_CERT|LG_CRL|LG_SMIME);
+ break;
+ case CKA_LABEL:
+ copy = &name;
+ break;
+ case CKA_NETSCAPE_EMAIL:
+ copy = &email;
+ classFlags &= LG_SMIME|LG_CERT;
+ break;
+ case CKA_NETSCAPE_SMIME_TIMESTAMP:
+ classFlags &= LG_SMIME;
+ break;
+ case CKA_CLASS:
+ crv = lg_GetULongAttribute(CKA_CLASS,&pTemplate[i],1, &objectClass);
+ if (crv != CKR_OK) {
+ classFlags = 0;
+ break;;
+ }
+ switch (objectClass) {
+ case CKO_CERTIFICATE:
+ classFlags &= LG_CERT;
+ break;
+ case CKO_NETSCAPE_TRUST:
+ classFlags &= LG_TRUST;
+ break;
+ case CKO_NETSCAPE_CRL:
+ classFlags &= LG_CRL;
+ break;
+ case CKO_NETSCAPE_SMIME:
+ classFlags &= LG_SMIME;
+ break;
+ case CKO_PRIVATE_KEY:
+ classFlags &= LG_PRIVATE;
+ break;
+ case CKO_PUBLIC_KEY:
+ classFlags &= LG_PUBLIC;
+ break;
+ case CKO_SECRET_KEY:
+ classFlags &= LG_KEY;
+ break;
+ default:
+ classFlags = 0;
+ break;
+ }
+ break;
+ case CKA_PRIVATE:
+ if (pTemplate[i].ulValueLen != sizeof(CK_BBOOL)) {
+ classFlags = 0;
+ }
+ if (*((CK_BBOOL *)pTemplate[i].pValue) == CK_TRUE) {
+ classFlags &= (LG_PRIVATE|LG_KEY);
+ } else {
+ classFlags &= ~(LG_PRIVATE|LG_KEY);
+ }
+ break;
+ case CKA_SENSITIVE:
+ if (pTemplate[i].ulValueLen != sizeof(CK_BBOOL)) {
+ classFlags = 0;
+ }
+ if (*((CK_BBOOL *)pTemplate[i].pValue) == CK_TRUE) {
+ classFlags &= (LG_PRIVATE|LG_KEY);
+ } else {
+ classFlags = 0;
+ }
+ break;
+ case CKA_TOKEN:
+ if (pTemplate[i].ulValueLen != sizeof(CK_BBOOL)) {
+ classFlags = 0;
+ }
+ if (*((CK_BBOOL *)pTemplate[i].pValue) != CK_TRUE) {
+ classFlags = 0;
+ }
+ break;
+ case CKA_CERT_SHA1_HASH:
+ classFlags &= LG_TRUST;
+ copy = &cert_sha1_hash; break;
+ case CKA_CERT_MD5_HASH:
+ classFlags &= LG_TRUST;
+ copy = &cert_md5_hash; break;
+ case CKA_CERTIFICATE_TYPE:
+ crv = lg_GetULongAttribute(CKA_CLASS,&pTemplate[i],1,&certType);
+ if (crv != CKR_OK) {
+ classFlags = 0;
+ }
+ classFlags &= LG_CERT;
+ if (certType != CKC_X_509) {
+ classFlags = 0;
+ }
+ break;
+ case CKA_ID:
+ copy = &key_id;
+ classFlags &= (LG_CERT|LG_PRIVATE|LG_KEY|LG_PUBLIC);
+ break;
+ case CKA_NETSCAPE_KRL:
+ if (pTemplate[i].ulValueLen != sizeof(CK_BBOOL)) {
+ classFlags = 0;
+ }
+ classFlags &= LG_CRL;
+ isKrl = (PRBool)(*((CK_BBOOL *)pTemplate[i].pValue) == CK_TRUE);
+ break;
+ case CKA_MODIFIABLE:
+ break;
+ case CKA_KEY_TYPE:
+ case CKA_DERIVE:
+ classFlags &= LG_PUBLIC|LG_PRIVATE|LG_KEY;
+ break;
+ case CKA_VERIFY_RECOVER:
+ classFlags &= LG_PUBLIC;
+ break;
+ case CKA_SIGN_RECOVER:
+ classFlags &= LG_PRIVATE;
+ break;
+ case CKA_ENCRYPT:
+ case CKA_VERIFY:
+ case CKA_WRAP:
+ classFlags &= LG_PUBLIC|LG_KEY;
+ break;
+ case CKA_DECRYPT:
+ case CKA_SIGN:
+ case CKA_UNWRAP:
+ case CKA_ALWAYS_SENSITIVE:
+ case CKA_EXTRACTABLE:
+ case CKA_NEVER_EXTRACTABLE:
+ classFlags &= LG_PRIVATE|LG_KEY;
+ break;
+ /* can't be a certificate if it doesn't match one of the above
+ * attributes */
+ default:
+ classFlags = 0;
+ break;
+ }
+ if (copy) {
+ copy->data = (unsigned char*)pTemplate[i].pValue;
+ copy->len = pTemplate[i].ulValueLen;
+ }
+ copy = NULL;
+ }
+
+ /* certs */
+ if (classFlags & (LG_CERT|LG_TRUST)) {
+ lg_searchCertsAndTrust(sdb,&derCert,&name,&derSubject,
+ &issuerSN, &email,classFlags,search,
+ pTemplate, ulCount);
+ }
+
+ /* keys */
+ if (classFlags & (LG_PRIVATE|LG_PUBLIC|LG_KEY)) {
+ PRBool mustStrict = ((classFlags & LG_KEY) != 0) && (name.len != 0);
+ lg_searchKeys(sdb, &key_id, classFlags, search,
+ mustStrict, pTemplate, ulCount);
+ }
+
+ /* crl's */
+ if (classFlags & LG_CRL) {
+ lg_searchCrls(sdb, &derSubject, isKrl, classFlags, search,
+ pTemplate, ulCount);
+ }
+ /* Add S/MIME entry stuff */
+ if (classFlags & LG_SMIME) {
+ lg_searchSMime(sdb, &email, search, pTemplate, ulCount);
+ }
+ return CKR_OK;
+}
+
+
+/* lg_FindObjectsInit initializes a search for token and session objects
+ * that match a template. */
+CK_RV lg_FindObjectsInit(SDB *sdb, const CK_ATTRIBUTE *pTemplate,
+ CK_ULONG ulCount, SDBFind **retSearch)
+{
+ SDBFind *search;
+ CK_RV crv = CKR_OK;
+
+ *retSearch = NULL;
+
+ search = (SDBFind *)PORT_Alloc(sizeof(SDBFind));
+ if (search == NULL) {
+ crv = CKR_HOST_MEMORY;
+ goto loser;
+ }
+ search->handles = (CK_OBJECT_HANDLE *)
+ PORT_Alloc(sizeof(CK_OBJECT_HANDLE) * LG_SEARCH_BLOCK_SIZE);
+ if (search->handles == NULL) {
+ crv = CKR_HOST_MEMORY;
+ goto loser;
+ }
+ search->index = 0;
+ search->size = 0;
+ search->array_size = LG_SEARCH_BLOCK_SIZE;
+ /* FIXME - do we still need to get Login state? */
+
+ crv = lg_searchTokenList(sdb, search, pTemplate, ulCount);
+ if (crv != CKR_OK) {
+ goto loser;
+ }
+
+ *retSearch = search;
+ return CKR_OK;
+
+loser:
+ if (search) {
+ lg_FreeSearch(search);
+ }
+ return crv;
+}
+
+
+/* lg_FindObjects continues a search for token and session objects
+ * that match a template, obtaining additional object handles. */
+CK_RV lg_FindObjects(SDB *sdb, SDBFind *search,
+ CK_OBJECT_HANDLE *phObject,CK_ULONG ulMaxObjectCount,
+ CK_ULONG *pulObjectCount)
+{
+ int transfer;
+ int left;
+
+ *pulObjectCount = 0;
+ left = search->size - search->index;
+ transfer = ((int)ulMaxObjectCount > left) ? left : ulMaxObjectCount;
+ if (transfer > 0) {
+ PORT_Memcpy(phObject,&search->handles[search->index],
+ transfer*sizeof(CK_OBJECT_HANDLE_PTR));
+ } else {
+ *phObject = CK_INVALID_HANDLE;
+ }
+
+ search->index += transfer;
+ *pulObjectCount = transfer;
+ return CKR_OK;
+}
+
+/* lg_FindObjectsFinal finishes a search for token and session objects. */
+CK_RV lg_FindObjectsFinal(SDB* lgdb, SDBFind *search)
+{
+
+ if (search != NULL) {
+ lg_FreeSearch(search);
+ }
+ return CKR_OK;
+}
diff --git a/security/nss/lib/softoken/legacydb/lginit.c b/security/nss/lib/softoken/legacydb/lginit.c
new file mode 100644
index 000000000..ed5ec4a47
--- /dev/null
+++ b/security/nss/lib/softoken/legacydb/lginit.c
@@ -0,0 +1,644 @@
+/*
+ * NSS utility functions
+ *
+ * ***** 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 ***** */
+/* $Id$ */
+
+#include "lowkeyi.h"
+#include "pcert.h"
+#include "keydbi.h"
+#include "lgdb.h"
+
+typedef struct LGPrivateStr {
+ NSSLOWCERTCertDBHandle *certDB;
+ NSSLOWKEYDBHandle *keyDB;
+ PRLock *dbLock;
+ PLHashTable *hashTable;
+} LGPrivate;
+
+static char *
+lg_certdb_name_cb(void *arg, int dbVersion)
+{
+ const char *configdir = (const char *)arg;
+ const char *dbver;
+ char *smpname = NULL;
+ char *dbname = NULL;
+
+ switch (dbVersion) {
+ case 8:
+ dbver = "8";
+ break;
+ case 7:
+ dbver = "7";
+ break;
+ case 6:
+ dbver = "6";
+ break;
+ case 5:
+ dbver = "5";
+ break;
+ case 4:
+ default:
+ dbver = "";
+ break;
+ }
+
+ /* make sure we return something allocated with PORT_ so we have properly
+ * matched frees at the end */
+ smpname = PR_smprintf(CERT_DB_FMT, configdir, dbver);
+ if (smpname) {
+ dbname = PORT_Strdup(smpname);
+ PR_smprintf_free(smpname);
+ }
+ return dbname;
+}
+
+static char *
+lg_keydb_name_cb(void *arg, int dbVersion)
+{
+ const char *configdir = (const char *)arg;
+ const char *dbver;
+ char *smpname = NULL;
+ char *dbname = NULL;
+
+ switch (dbVersion) {
+ case 4:
+ dbver = "4";
+ break;
+ case 3:
+ dbver = "3";
+ break;
+ case 1:
+ dbver = "1";
+ break;
+ case 2:
+ default:
+ dbver = "";
+ break;
+ }
+
+ smpname = PR_smprintf(KEY_DB_FMT, configdir, dbver);
+ if (smpname) {
+ dbname = PORT_Strdup(smpname);
+ PR_smprintf_free(smpname);
+ }
+ return dbname;
+}
+
+const char *
+lg_EvaluateConfigDir(const char *configdir,char **appName)
+{
+ if (PORT_Strncmp(configdir, MULTIACCESS, sizeof(MULTIACCESS)-1) == 0) {
+ char *cdir;
+
+ *appName = PORT_Strdup(configdir+sizeof(MULTIACCESS)-1);
+ if (*appName == NULL) {
+ return configdir;
+ }
+ cdir = *appName;
+ while (*cdir && *cdir != ':') {
+ cdir++;
+ }
+ if (*cdir == ':') {
+ *cdir = 0;
+ cdir++;
+ }
+ configdir = cdir;
+ }
+ return configdir;
+}
+
+static int rdbmapflags(int flags);
+static rdbfunc lg_rdbfunc = NULL;
+static rdbstatusfunc lg_rdbstatusfunc = NULL;
+
+/* NOTE: SHLIB_SUFFIX is defined on the command line */
+#define RDBLIB SHLIB_PREFIX"rdb."SHLIB_SUFFIX
+
+DB * rdbopen(const char *appName, const char *prefix,
+ const char *type, int flags, int *status)
+{
+ PRLibrary *lib;
+ DB *db;
+
+ if (lg_rdbfunc) {
+ db = (*lg_rdbfunc)(appName,prefix,type,rdbmapflags(flags));
+ if (!db && status && lg_rdbstatusfunc) {
+ *status = (*lg_rdbstatusfunc)();
+ }
+ return db;
+ }
+
+ /*
+ * try to open the library.
+ */
+ lib = PR_LoadLibrary(RDBLIB);
+
+ if (!lib) {
+ return NULL;
+ }
+
+ /* get the entry points */
+ lg_rdbstatusfunc = (rdbstatusfunc) PR_FindSymbol(lib,"rdbstatus");
+ lg_rdbfunc = (rdbfunc) PR_FindSymbol(lib,"rdbopen");
+ if (lg_rdbfunc) {
+ db = (*lg_rdbfunc)(appName,prefix,type,rdbmapflags(flags));
+ if (!db && status && lg_rdbstatusfunc) {
+ *status = (*lg_rdbstatusfunc)();
+ }
+ return db;
+ }
+
+ /* couldn't find the entry point, unload the library and fail */
+ PR_UnloadLibrary(lib);
+ return NULL;
+}
+
+/*
+ * the following data structures are from rdb.h.
+ */
+struct RDBStr {
+ DB db;
+ int (*xactstart)(DB *db);
+ int (*xactdone)(DB *db, PRBool abort);
+ int version;
+ int (*dbinitcomplete)(DB *db);
+};
+
+#define DB_RDB ((DBTYPE) 0xff)
+#define RDB_RDONLY 1
+#define RDB_RDWR 2
+#define RDB_CREATE 4
+
+static int
+rdbmapflags(int flags) {
+ switch (flags) {
+ case NO_RDONLY:
+ return RDB_RDONLY;
+ case NO_RDWR:
+ return RDB_RDWR;
+ case NO_CREATE:
+ return RDB_CREATE;
+ default:
+ break;
+ }
+ return 0;
+}
+
+PRBool
+db_IsRDB(DB *db)
+{
+ return (PRBool) db->type == DB_RDB;
+}
+
+int
+db_BeginTransaction(DB *db)
+{
+ struct RDBStr *rdb = (struct RDBStr *)db;
+ if (db->type != DB_RDB) {
+ return 0;
+ }
+
+ return rdb->xactstart(db);
+}
+
+int
+db_FinishTransaction(DB *db, PRBool abort)
+{
+ struct RDBStr *rdb = (struct RDBStr *)db;
+ if (db->type != DB_RDB) {
+ return 0;
+ }
+
+ return rdb->xactdone(db, abort);
+}
+
+static DB *
+lg_getRawDB(SDB *sdb)
+{
+ NSSLOWCERTCertDBHandle *certDB;
+ NSSLOWKEYDBHandle *keyDB;
+
+ certDB = lg_getCertDB(sdb);
+ if (certDB) {
+ return certDB->permCertDB;
+ }
+ keyDB = lg_getKeyDB(sdb);
+ if (keyDB) {
+ return keyDB->db;
+ }
+ return NULL;
+}
+
+CK_RV
+lg_Begin(SDB *sdb)
+{
+ DB *db = lg_getRawDB(sdb);
+ int ret;
+
+ if (db == NULL) {
+ return CKR_GENERAL_ERROR; /* shouldn't happen */
+ }
+ ret = db_BeginTransaction(db);
+ if (ret != 0) {
+ return CKR_GENERAL_ERROR; /* could happen */
+ }
+ return CKR_OK;
+}
+
+CK_RV
+lg_Commit(SDB *sdb)
+{
+ DB *db = lg_getRawDB(sdb);
+ int ret;
+
+ if (db == NULL) {
+ return CKR_GENERAL_ERROR; /* shouldn't happen */
+ }
+ ret = db_FinishTransaction(db, PR_FALSE);
+ if (ret != 0) {
+ return CKR_GENERAL_ERROR; /* could happen */
+ }
+ return CKR_OK;
+}
+
+CK_RV
+lg_Abort(SDB *sdb)
+{
+ DB *db = lg_getRawDB(sdb);
+ int ret;
+
+ if (db == NULL) {
+ return CKR_GENERAL_ERROR; /* shouldn't happen */
+ }
+ ret = db_FinishTransaction(db, PR_TRUE);
+ if (ret != 0) {
+ return CKR_GENERAL_ERROR; /* could happen */
+ }
+ return CKR_OK;
+}
+
+int
+db_InitComplete(DB *db)
+{
+ struct RDBStr *rdb = (struct RDBStr *)db;
+ if (db->type != DB_RDB) {
+ return 0;
+ }
+ /* we should have added a version number to the RDBS structure. Since we
+ * didn't, we detect that we have and 'extended' structure if the rdbstatus
+ * func exists */
+ if (!lg_rdbstatusfunc) {
+ return 0;
+ }
+
+ return rdb->dbinitcomplete(db);
+}
+
+
+
+SECStatus
+db_Copy(DB *dest,DB *src)
+{
+ int ret;
+ DBT key,data;
+ ret = (*src->seq)(src, &key, &data, R_FIRST);
+ if (ret) {
+ return SECSuccess;
+ }
+
+ do {
+ (void)(*dest->put)(dest,&key,&data, R_NOOVERWRITE);
+ } while ( (*src->seq)(src, &key, &data, R_NEXT) == 0);
+ (void)(*dest->sync)(dest,0);
+
+ return SECSuccess;
+}
+
+
+static CK_RV
+lg_OpenCertDB(const char * configdir, const char *prefix, PRBool readOnly,
+ NSSLOWCERTCertDBHandle **certdbPtr)
+{
+ NSSLOWCERTCertDBHandle *certdb = NULL;
+ CK_RV crv = CKR_NETSCAPE_CERTDB_FAILED;
+ SECStatus rv;
+ char * name = NULL;
+ char * appName = NULL;
+
+ if (prefix == NULL) {
+ prefix = "";
+ }
+
+ configdir = lg_EvaluateConfigDir(configdir, &appName);
+
+ name = PR_smprintf("%s" PATH_SEPARATOR "%s",configdir,prefix);
+ if (name == NULL) goto loser;
+
+ certdb = (NSSLOWCERTCertDBHandle*)PORT_ZAlloc(sizeof(NSSLOWCERTCertDBHandle));
+ if (certdb == NULL)
+ goto loser;
+
+ certdb->ref = 1;
+/* fix when we get the DB in */
+ rv = nsslowcert_OpenCertDB(certdb, readOnly, appName, prefix,
+ lg_certdb_name_cb, (void *)name, PR_FALSE);
+ if (rv == SECSuccess) {
+ crv = CKR_OK;
+ *certdbPtr = certdb;
+ certdb = NULL;
+ }
+loser:
+ if (certdb) PR_Free(certdb);
+ if (name) PR_smprintf_free(name);
+ if (appName) PORT_Free(appName);
+ return crv;
+}
+
+static CK_RV
+lg_OpenKeyDB(const char * configdir, const char *prefix, PRBool readOnly,
+ NSSLOWKEYDBHandle **keydbPtr)
+{
+ NSSLOWKEYDBHandle *keydb;
+ char * name = NULL;
+ char * appName = NULL;
+
+ if (prefix == NULL) {
+ prefix = "";
+ }
+ configdir = lg_EvaluateConfigDir(configdir, &appName);
+
+ name = PR_smprintf("%s" PATH_SEPARATOR "%s",configdir,prefix);
+ if (name == NULL)
+ return CKR_HOST_MEMORY;
+ keydb = nsslowkey_OpenKeyDB(readOnly, appName, prefix,
+ lg_keydb_name_cb, (void *)name);
+ PR_smprintf_free(name);
+ if (appName) PORT_Free(appName);
+ if (keydb == NULL)
+ return CKR_NETSCAPE_KEYDB_FAILED;
+ *keydbPtr = keydb;
+
+ return CKR_OK;
+}
+
+/*
+ * Accessors for the private parts of the sdb structure.
+ */
+void
+lg_DBLock(SDB *sdb)
+{
+ LGPrivate *lgdb_p = (LGPrivate *)sdb->private;
+ PR_Lock(lgdb_p->dbLock);
+}
+
+void
+lg_DBUnlock(SDB *sdb)
+{
+ LGPrivate *lgdb_p = (LGPrivate *)sdb->private;
+ PR_Unlock(lgdb_p->dbLock);
+}
+
+PLHashTable *
+lg_GetHashTable(SDB *sdb)
+{
+ LGPrivate *lgdb_p = (LGPrivate *)sdb->private;
+ return lgdb_p->hashTable;
+}
+
+NSSLOWCERTCertDBHandle *
+lg_getCertDB(SDB *sdb)
+{
+ LGPrivate *lgdb_p = (LGPrivate *)sdb->private;
+
+ return lgdb_p->certDB;
+}
+
+NSSLOWKEYDBHandle *
+lg_getKeyDB(SDB *sdb)
+{
+ LGPrivate *lgdb_p = (LGPrivate *)sdb->private;
+
+ return lgdb_p->keyDB;
+}
+
+CK_RV
+lg_Close(SDB *sdb)
+{
+ LGPrivate *lgdb_p = (LGPrivate *)sdb->private;
+ if (lgdb_p) {
+ if (lgdb_p->certDB) {
+ nsslowcert_ClosePermCertDB(lgdb_p->certDB);
+ } else if (lgdb_p->keyDB) {
+ nsslowkey_CloseKeyDB(lgdb_p->keyDB);
+ }
+ if (lgdb_p->dbLock) {
+ PR_DestroyLock(lgdb_p->dbLock);
+ }
+ if (lgdb_p->hashTable) {
+ PL_HashTableDestroy(lgdb_p->hashTable);
+ }
+ PORT_Free(lgdb_p);
+ }
+ PORT_Free(sdb);
+ return CKR_OK;
+}
+
+static PLHashNumber
+lg_HashNumber(const void *key)
+{
+ return (PLHashNumber) key;
+}
+
+
+/*
+ * helper function to wrap a NSSLOWCERTCertDBHandle or a NSSLOWKEYDBHandle
+ * with and sdb structure.
+ */
+CK_RV
+lg_init(SDB **pSdb, int flags, NSSLOWCERTCertDBHandle *certdbPtr,
+ NSSLOWKEYDBHandle *keydbPtr)
+{
+ SDB *sdb = NULL;
+ LGPrivate *lgdb_p = NULL;
+ CK_RV error = CKR_HOST_MEMORY;
+
+ *pSdb = NULL;
+ sdb = (SDB *) PORT_Alloc(sizeof(SDB));
+ if (sdb == NULL) {
+ goto loser;
+ }
+ lgdb_p = (LGPrivate *) PORT_Alloc(sizeof(LGPrivate));
+ if (lgdb_p == NULL) {
+ goto loser;
+ }
+ /* invariant fields */
+ lgdb_p->certDB = certdbPtr;
+ lgdb_p->keyDB = keydbPtr;
+ lgdb_p->dbLock = PR_NewLock();
+ if (lgdb_p->dbLock == NULL) {
+ goto loser;
+ }
+ lgdb_p->hashTable = PL_NewHashTable(64, lg_HashNumber, PL_CompareValues,
+ SECITEM_HashCompare, NULL, 0);
+ if (lgdb_p->hashTable == NULL) {
+ goto loser;
+ }
+
+ sdb->sdb_type = SDB_LEGACY;
+ sdb->sdb_flags = flags;
+ sdb->private = lgdb_p;
+ sdb->sdb_FindObjectsInit = lg_FindObjectsInit;
+ sdb->sdb_FindObjects = lg_FindObjects;
+ sdb->sdb_FindObjectsFinal = lg_FindObjectsFinal;
+ sdb->sdb_GetAttributeValue = lg_GetAttributeValue;
+ sdb->sdb_SetAttributeValue = lg_SetAttributeValue;
+ sdb->sdb_CreateObject = lg_CreateObject;
+ sdb->sdb_DestroyObject = lg_DestroyObject;
+ sdb->sdb_GetPWEntry = lg_GetPWEntry;
+ sdb->sdb_PutPWEntry = lg_PutPWEntry;
+ sdb->sdb_Begin = lg_Begin;
+ sdb->sdb_Commit = lg_Commit;
+ sdb->sdb_Abort = lg_Abort;
+ sdb->sdb_Close = lg_Reset;
+ sdb->sdb_Close = lg_Close;
+
+
+ *pSdb = sdb;
+ return CKR_OK;
+
+loser:
+ if (sdb) {
+ PORT_Free(sdb);
+ }
+ if (lgdb_p) {
+ if (lgdb_p->dbLock) {
+ PR_DestroyLock(lgdb_p->dbLock);
+ }
+ if (lgdb_p->hashTable) {
+ PL_HashTableDestroy(lgdb_p->hashTable);
+ }
+ PORT_Free(lgdb_p);
+ }
+ return error;
+
+}
+
+extern SECStatus secoid_Init(void); /* util *REALLY* needs
+ * to be a shared library */
+/*
+ * OK there are now lots of options here, lets go through them all:
+ *
+ * configdir - base directory where all the cert, key, and module datbases live.
+ * certPrefix - prefix added to the beginning of the cert database example: "
+ * "https-server1-"
+ * keyPrefix - prefix added to the beginning of the key database example: "
+ * "https-server1-"
+ * secmodName - name of the security module database (usually "secmod.db").
+ * readOnly - Boolean: true if the databases are to be openned read only.
+ * nocertdb - Don't open the cert DB and key DB's, just initialize the
+ * Volatile certdb.
+ * nomoddb - Don't open the security module DB, just initialize the
+ * PKCS #11 module.
+ * forceOpen - Continue to force initializations even if the databases cannot
+ * be opened.
+ */
+CK_RV
+legacy_Open(const char *configdir, const char *certPrefix,
+ const char *keyPrefix, int certVersion, int keyVersion,
+ int flags, SDB **certDB, SDB **keyDB)
+{
+ CK_RV crv = CKR_OK;
+ PRBool readOnly = (flags == SDB_RDONLY)? PR_TRUE: PR_FALSE;
+
+ secoid_Init();
+ nsslowcert_InitLocks();
+
+ if (keyDB) *keyDB = NULL;
+ if (certDB) *certDB = NULL;
+
+ if (certDB) {
+ NSSLOWCERTCertDBHandle *certdbPtr;
+
+ crv = lg_OpenCertDB(configdir, certPrefix, readOnly, &certdbPtr);
+ if (crv != CKR_OK) {
+ goto loser;
+ }
+ crv = lg_init(certDB, flags, certdbPtr, NULL);
+ if (crv != CKR_OK) {
+ nsslowcert_ClosePermCertDB(certdbPtr);
+ goto loser;
+ }
+ }
+ if (keyDB) {
+ NSSLOWKEYDBHandle *keydbPtr;
+
+ crv = lg_OpenKeyDB(configdir, keyPrefix, readOnly, &keydbPtr);
+ if (crv != CKR_OK) {
+ goto loser;
+ }
+ crv = lg_init(keyDB, flags, NULL, keydbPtr);
+ if (crv != CKR_OK) {
+ nsslowkey_CloseKeyDB(keydbPtr);
+ goto loser;
+ }
+ if (certDB && *certDB) {
+ LGPrivate *lgdb_p = (LGPrivate *)(*certDB)->private;
+ lgdb_p->keyDB = keydbPtr;
+ }
+ }
+
+loser:
+ if (crv != CKR_OK) {
+ if (keyDB && *keyDB) {
+ lg_Close(*keyDB);
+ *keyDB = NULL;
+ }
+ if (certDB && *certDB) {
+ lg_Close(*certDB);
+ *certDB = NULL;
+ }
+ }
+ return crv;
+}
+
+CK_RV
+legacy_Shutdown(void)
+{
+ nsslowcert_DestroyFreeLists();
+ nsslowcert_DestroyGlobalLocks();
+}
diff --git a/security/nss/lib/softoken/legacydb/lgutil.c b/security/nss/lib/softoken/legacydb/lgutil.c
new file mode 100644
index 000000000..3df08de09
--- /dev/null
+++ b/security/nss/lib/softoken/legacydb/lgutil.c
@@ -0,0 +1,424 @@
+/* ***** 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):
+ * Dr Vipul Gupta <vipul.gupta@sun.com>, Sun Microsystems Laboratories
+ *
+ * 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 "lgdb.h"
+#include "secerr.h"
+#include "lgglue.h"
+
+/*
+ * ******************** Attribute Utilities *******************************
+ */
+
+/*
+ * look up and attribute structure from a type and Object structure.
+ * The returned attribute is referenced and needs to be freed when
+ * it is no longer needed.
+ */
+const CK_ATTRIBUTE *
+lg_FindAttribute(CK_ATTRIBUTE_TYPE type, const CK_ATTRIBUTE *templ,
+ CK_ULONG count )
+{
+ int i;
+
+ for (i=0; i < count; i++) {
+ if (templ[i].type == type) {
+ return &templ[i];
+ }
+ }
+ return NULL;
+}
+
+
+/*
+ * return true if object has attribute
+ */
+PRBool
+lg_hasAttribute(CK_ATTRIBUTE_TYPE type, const CK_ATTRIBUTE *templ,
+ CK_ULONG count )
+{
+ if (lg_FindAttribute(type, templ, count) == NULL) {
+ return PR_FALSE;
+ }
+ return PR_TRUE;
+}
+
+/*
+ * copy an attribute into a SECItem. Secitem is allocated in the specified
+ * arena.
+ */
+CK_RV
+lg_Attribute2SecItem(PLArenaPool *arena, CK_ATTRIBUTE_TYPE type,
+ const CK_ATTRIBUTE *templ, CK_ULONG count,
+ SECItem *item)
+{
+ int len;
+ const CK_ATTRIBUTE *attribute;
+
+ attribute = lg_FindAttribute(type, templ, count);
+ if (attribute == NULL) return CKR_TEMPLATE_INCOMPLETE;
+ len = attribute->ulValueLen;
+
+ if (arena) {
+ item->data = (unsigned char *) PORT_ArenaAlloc(arena,len);
+ } else {
+ item->data = (unsigned char *) PORT_Alloc(len);
+ }
+ if (item->data == NULL) {
+ return CKR_HOST_MEMORY;
+ }
+ item->len = len;
+ PORT_Memcpy(item->data, attribute->pValue, len);
+ return CKR_OK;
+}
+
+
+/*
+ * copy an unsigned attribute into a SECItem. Secitem is allocated in
+ * the specified arena.
+ */
+CK_RV
+lg_Attribute2SSecItem(PLArenaPool *arena, CK_ATTRIBUTE_TYPE type,
+ const CK_ATTRIBUTE *templ, CK_ULONG count,
+ SECItem *item)
+{
+ const CK_ATTRIBUTE *attribute;
+ item->data = NULL;
+
+ attribute = lg_FindAttribute(type, templ, count);
+ if (attribute == NULL) return CKR_TEMPLATE_INCOMPLETE;
+
+ (void)SECITEM_AllocItem(arena, item, attribute->ulValueLen);
+ if (item->data == NULL) {
+ return CKR_HOST_MEMORY;
+ }
+ PORT_Memcpy(item->data, attribute->pValue, item->len);
+ return CKR_OK;
+}
+
+/*
+ * copy an unsigned attribute into a SECItem. Secitem is allocated in
+ * the specified arena.
+ */
+CK_RV
+lg_PrivAttr2SSecItem(PLArenaPool *arena, CK_ATTRIBUTE_TYPE type,
+ const CK_ATTRIBUTE *templ, CK_ULONG count,
+ SECItem *item, SDB *sdbpw)
+{
+ const CK_ATTRIBUTE *attribute;
+ SECItem epki, *dest = NULL;
+ SECStatus rv;
+
+ item->data = NULL;
+
+ attribute = lg_FindAttribute(type, templ, count);
+ if (attribute == NULL) return CKR_TEMPLATE_INCOMPLETE;
+
+ epki.data = attribute->pValue;
+ epki.len = attribute->ulValueLen;
+
+ rv = lg_util_decrypt(sdbpw, &epki, &dest);
+ if (rv != SECSuccess) {
+ return CKR_USER_NOT_LOGGED_IN;
+ }
+ (void)SECITEM_AllocItem(arena, item, dest->len);
+ if (item->data == NULL) {
+ SECITEM_FreeItem(dest, PR_TRUE);
+ return CKR_HOST_MEMORY;
+ }
+
+ PORT_Memcpy(item->data, dest->data, item->len);
+ SECITEM_FreeItem(dest, PR_TRUE);
+ return CKR_OK;
+}
+
+CK_RV
+lg_PrivAttr2SecItem(PLArenaPool *arena, CK_ATTRIBUTE_TYPE type,
+ const CK_ATTRIBUTE *templ, CK_ULONG count,
+ SECItem *item, SDB *sdbpw)
+{
+ return lg_PrivAttr2SSecItem(arena, type, templ, count, item, sdbpw);
+}
+
+/*
+ * this is only valid for CK_BBOOL type attributes. Return the state
+ * of that attribute.
+ */
+PRBool
+lg_isTrue(CK_ATTRIBUTE_TYPE type, const CK_ATTRIBUTE *templ, CK_ULONG count)
+{
+ const CK_ATTRIBUTE *attribute;
+ PRBool tok = PR_FALSE;
+
+ attribute=lg_FindAttribute(type, templ, count);
+ if (attribute == NULL) { return PR_FALSE; }
+ tok = (PRBool)(*(CK_BBOOL *)attribute->pValue);
+
+ return tok;
+}
+
+/*
+ * return a null terminated string from attribute 'type'. This string
+ * is allocated and needs to be freed with PORT_Free() When complete.
+ */
+char *
+lg_getString(CK_ATTRIBUTE_TYPE type, const CK_ATTRIBUTE *templ, CK_ULONG count)
+{
+ const CK_ATTRIBUTE *attribute;
+ char *label = NULL;
+
+ attribute = lg_FindAttribute(type, templ, count);
+ if (attribute == NULL) return NULL;
+
+ if (attribute->pValue != NULL) {
+ label = (char *) PORT_Alloc(attribute->ulValueLen+1);
+ if (label == NULL) {
+ return NULL;
+ }
+
+ PORT_Memcpy(label,attribute->pValue, attribute->ulValueLen);
+ label[attribute->ulValueLen] = 0;
+ }
+ return label;
+}
+
+CK_RV
+lg_GetULongAttribute(CK_ATTRIBUTE_TYPE type, const CK_ATTRIBUTE *templ,
+ CK_ULONG count, CK_ULONG *longData)
+{
+ const CK_ATTRIBUTE *attribute;
+ CK_ULONG value = 0;
+ const unsigned char *data;
+ int i;
+
+ attribute = lg_FindAttribute(type, templ, count);
+ if (attribute == NULL) return CKR_TEMPLATE_INCOMPLETE;
+
+ if (attribute->ulValueLen != sizeof(CK_ULONG)) {
+ return CKR_ATTRIBUTE_VALUE_INVALID;
+ }
+ data = (const unsigned char *)attribute->pValue;
+ for (i=0; i < 4; i++) {
+ value |= (CK_ULONG)(data[i]) << ((3-i)*8);
+ }
+
+ *longData = value;
+ return CKR_OK;
+}
+
+/*
+ * ******************** Object Utilities *******************************
+ */
+
+SECStatus
+lg_deleteTokenKeyByHandle(SDB *sdb, CK_OBJECT_HANDLE handle)
+{
+ SECItem *item;
+ PRBool rem;
+ PLHashTable *hashTable= lg_GetHashTable(sdb);
+
+ item = (SECItem *)PL_HashTableLookup(hashTable, (void *)handle);
+ rem = PL_HashTableRemove(hashTable,(void *)handle) ;
+ if (rem && item) {
+ SECITEM_FreeItem(item,PR_TRUE);
+ }
+ return rem ? SECSuccess : SECFailure;
+}
+
+/* must be called holding lg_DBLock(sdb) */
+static SECStatus
+lg_addTokenKeyByHandle(SDB *sdb, CK_OBJECT_HANDLE handle, SECItem *key)
+{
+ PLHashEntry *entry;
+ SECItem *item;
+ PLHashTable *hashTable= lg_GetHashTable(sdb);
+
+ item = SECITEM_DupItem(key);
+ if (item == NULL) {
+ return SECFailure;
+ }
+ entry = PL_HashTableAdd(hashTable,(void *)handle,item);
+ if (entry == NULL) {
+ SECITEM_FreeItem(item,PR_TRUE);
+ return SECFailure;
+ }
+ return SECSuccess;
+}
+
+/* must be called holding lg_DBLock(sdb) */
+const SECItem *
+lg_lookupTokenKeyByHandle(SDB *sdb, CK_OBJECT_HANDLE handle)
+{
+ PLHashTable *hashTable= lg_GetHashTable(sdb);
+ return (const SECItem *)PL_HashTableLookup(hashTable, (void *)handle);
+}
+
+
+static PRIntn
+lg_freeHashItem(PLHashEntry* entry, PRIntn index, void *arg)
+{
+ SECItem *item = (SECItem *)entry->value;
+
+ SECITEM_FreeItem(item, PR_TRUE);
+ return HT_ENUMERATE_NEXT;
+}
+
+CK_RV
+LG_ClearTokenKeyHashTable(SDB *sdb)
+{
+ PLHashTable *hashTable;
+ lg_DBLock(sdb);
+ hashTable= lg_GetHashTable(sdb);
+ PL_HashTableEnumerateEntries(hashTable, lg_freeHashItem, NULL);
+ lg_DBLock(sdb);
+ return CKR_OK;
+}
+
+/*
+ * handle Token Object stuff
+ */
+static void
+lg_XORHash(unsigned char *key, unsigned char *dbkey, int len)
+{
+ int i;
+
+ PORT_Memset(key, 0, 4);
+
+ for (i=0; i < len-4; i += 4) {
+ key[0] ^= dbkey[i];
+ key[1] ^= dbkey[i+1];
+ key[2] ^= dbkey[i+2];
+ key[3] ^= dbkey[i+3];
+ }
+}
+
+/* Make a token handle for an object and record it so we can find it again */
+CK_OBJECT_HANDLE
+lg_mkHandle(SDB *sdb, SECItem *dbKey, CK_OBJECT_HANDLE class)
+{
+ unsigned char hashBuf[4];
+ CK_OBJECT_HANDLE handle;
+ const SECItem *key;
+
+ handle = class;
+ /* there is only one KRL, use a fixed handle for it */
+ if (handle != LG_TOKEN_KRL_HANDLE) {
+ lg_XORHash(hashBuf,dbKey->data,dbKey->len);
+ handle = (hashBuf[0] << 24) | (hashBuf[1] << 16) |
+ (hashBuf[2] << 8) | hashBuf[3];
+ handle = class | (handle & ~(LG_TOKEN_TYPE_MASK|LG_TOKEN_MASK));
+ /* we have a CRL who's handle has randomly matched the reserved KRL
+ * handle, increment it */
+ if (handle == LG_TOKEN_KRL_HANDLE) {
+ handle++;
+ }
+ }
+
+ lg_DBLock(sdb);
+ while ((key = lg_lookupTokenKeyByHandle(sdb,handle)) != NULL) {
+ if (SECITEM_ItemsAreEqual(key,dbKey)) {
+ lg_DBUnlock(sdb);
+ return handle;
+ }
+ handle++;
+ }
+ lg_addTokenKeyByHandle(sdb,handle,dbKey);
+ lg_DBUnlock(sdb);
+ return handle;
+}
+
+PRBool
+lg_poisonHandle(SDB *sdb, SECItem *dbKey, CK_OBJECT_HANDLE class)
+{
+ unsigned char hashBuf[4];
+ CK_OBJECT_HANDLE handle;
+ const SECItem *key;
+
+ handle = class;
+ /* there is only one KRL, use a fixed handle for it */
+ if (handle != LG_TOKEN_KRL_HANDLE) {
+ lg_XORHash(hashBuf,dbKey->data,dbKey->len);
+ handle = (hashBuf[0] << 24) | (hashBuf[1] << 16) |
+ (hashBuf[2] << 8) | hashBuf[3];
+ handle = class | (handle & ~(LG_TOKEN_TYPE_MASK|LG_TOKEN_MASK));
+ /* we have a CRL who's handle has randomly matched the reserved KRL
+ * handle, increment it */
+ if (handle == LG_TOKEN_KRL_HANDLE) {
+ handle++;
+ }
+ }
+ lg_DBLock(sdb);
+ while ((key = lg_lookupTokenKeyByHandle(sdb,handle)) != NULL) {
+ if (SECITEM_ItemsAreEqual(key,dbKey)) {
+ key->data[0] ^= 0x80;
+ lg_DBUnlock(sdb);
+ return PR_TRUE;
+ }
+ handle++;
+ }
+ lg_DBUnlock(sdb);
+ return PR_FALSE;
+}
+
+static LGEncryptFunc lg_encrypt_stub = NULL;
+static LGDecryptFunc lg_decrypt_stub = NULL;
+
+void
+legacy_SetCryptFunctions(LGEncryptFunc enc, LGDecryptFunc dec)
+{
+ lg_encrypt_stub = enc;
+ lg_decrypt_stub = dec;
+}
+
+SECStatus lg_util_encrypt(PLArenaPool *arena, SDB *sdb,
+ SECItem *plainText, SECItem **cipherText)
+{
+ if (lg_encrypt_stub == NULL) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+ return (*lg_encrypt_stub)(arena, sdb, plainText, cipherText);
+}
+
+SECStatus lg_util_decrypt(SDB *sdb, SECItem *cipherText, SECItem **plainText)
+{
+ if (lg_decrypt_stub == NULL) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+ return (*lg_decrypt_stub)(sdb, cipherText, plainText);
+}
+
+
diff --git a/security/nss/lib/softoken/legacydb/lowcert.c b/security/nss/lib/softoken/legacydb/lowcert.c
new file mode 100644
index 000000000..6aa7e0960
--- /dev/null
+++ b/security/nss/lib/softoken/legacydb/lowcert.c
@@ -0,0 +1,824 @@
+/* ***** 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):
+ * Dr Vipul Gupta <vipul.gupta@sun.com>, Sun Microsystems Laboratories
+ *
+ * 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 ***** */
+
+/*
+ * Certificate handling code
+ *
+ * $Id$
+ */
+
+#include "seccomon.h"
+#include "secder.h"
+#include "nssilock.h"
+#include "lowkeyi.h"
+#include "secasn1.h"
+#include "secoid.h"
+#include "secerr.h"
+#include "pcert.h"
+
+static const SEC_ASN1Template nsslowcert_SubjectPublicKeyInfoTemplate[] = {
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(NSSLOWCERTSubjectPublicKeyInfo) },
+ { SEC_ASN1_INLINE, offsetof(NSSLOWCERTSubjectPublicKeyInfo,algorithm),
+ SECOID_AlgorithmIDTemplate },
+ { SEC_ASN1_BIT_STRING,
+ offsetof(NSSLOWCERTSubjectPublicKeyInfo,subjectPublicKey), },
+ { 0, }
+};
+
+static const SEC_ASN1Template nsslowcert_RSAPublicKeyTemplate[] = {
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(NSSLOWKEYPublicKey) },
+ { SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPublicKey,u.rsa.modulus), },
+ { SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPublicKey,u.rsa.publicExponent), },
+ { 0, }
+};
+static const SEC_ASN1Template nsslowcert_DSAPublicKeyTemplate[] = {
+ { SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPublicKey,u.dsa.publicValue), },
+ { 0, }
+};
+static const SEC_ASN1Template nsslowcert_DHPublicKeyTemplate[] = {
+ { SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPublicKey,u.dh.publicValue), },
+ { 0, }
+};
+
+/*
+ * See bugzilla bug 125359
+ * Since NSS (via PKCS#11) wants to handle big integers as unsigned ints,
+ * all of the templates above that en/decode into integers must be converted
+ * from ASN.1's signed integer type. This is done by marking either the
+ * source or destination (encoding or decoding, respectively) type as
+ * siUnsignedInteger.
+ */
+
+static void
+prepare_low_rsa_pub_key_for_asn1(NSSLOWKEYPublicKey *pubk)
+{
+ pubk->u.rsa.modulus.type = siUnsignedInteger;
+ pubk->u.rsa.publicExponent.type = siUnsignedInteger;
+}
+
+static void
+prepare_low_dsa_pub_key_for_asn1(NSSLOWKEYPublicKey *pubk)
+{
+ pubk->u.dsa.publicValue.type = siUnsignedInteger;
+ pubk->u.dsa.params.prime.type = siUnsignedInteger;
+ pubk->u.dsa.params.subPrime.type = siUnsignedInteger;
+ pubk->u.dsa.params.base.type = siUnsignedInteger;
+}
+
+static void
+prepare_low_dh_pub_key_for_asn1(NSSLOWKEYPublicKey *pubk)
+{
+ pubk->u.dh.prime.type = siUnsignedInteger;
+ pubk->u.dh.base.type = siUnsignedInteger;
+ pubk->u.dh.publicValue.type = siUnsignedInteger;
+}
+
+/*
+ * simple cert decoder to avoid the cost of asn1 engine
+ */
+static unsigned char *
+nsslowcert_dataStart(unsigned char *buf, unsigned int length,
+ unsigned int *data_length, PRBool includeTag,
+ unsigned char* rettag) {
+ unsigned char tag;
+ unsigned int used_length= 0;
+
+ tag = buf[used_length++];
+
+ if (rettag) {
+ *rettag = tag;
+ }
+
+ /* blow out when we come to the end */
+ if (tag == 0) {
+ return NULL;
+ }
+
+ *data_length = buf[used_length++];
+
+ if (*data_length&0x80) {
+ int len_count = *data_length & 0x7f;
+
+ *data_length = 0;
+
+ while (len_count-- > 0) {
+ *data_length = (*data_length << 8) | buf[used_length++];
+ }
+ }
+
+ if (*data_length > (length-used_length) ) {
+ *data_length = length-used_length;
+ return NULL;
+ }
+ if (includeTag) *data_length += used_length;
+
+ return (buf + (includeTag ? 0 : used_length));
+}
+
+static void SetTimeType(SECItem* item, unsigned char tagtype)
+{
+ switch (tagtype) {
+ case SEC_ASN1_UTC_TIME:
+ item->type = siUTCTime;
+ break;
+
+ case SEC_ASN1_GENERALIZED_TIME:
+ item->type = siGeneralizedTime;
+ break;
+
+ default:
+ PORT_Assert(0);
+ break;
+ }
+}
+
+static int
+nsslowcert_GetValidityFields(unsigned char *buf,int buf_length,
+ SECItem *notBefore, SECItem *notAfter)
+{
+ unsigned char tagtype;
+ notBefore->data = nsslowcert_dataStart(buf,buf_length,
+ &notBefore->len,PR_FALSE, &tagtype);
+ if (notBefore->data == NULL) return SECFailure;
+ SetTimeType(notBefore, tagtype);
+ buf_length -= (notBefore->data-buf) + notBefore->len;
+ buf = notBefore->data + notBefore->len;
+ notAfter->data = nsslowcert_dataStart(buf,buf_length,
+ &notAfter->len,PR_FALSE, &tagtype);
+ if (notAfter->data == NULL) return SECFailure;
+ SetTimeType(notAfter, tagtype);
+ return SECSuccess;
+}
+
+static int
+nsslowcert_GetCertFields(unsigned char *cert,int cert_length,
+ SECItem *issuer, SECItem *serial, SECItem *derSN, SECItem *subject,
+ SECItem *valid, SECItem *subjkey, SECItem *extensions)
+{
+ unsigned char *buf;
+ unsigned int buf_length;
+ unsigned char *dummy;
+ unsigned int dummylen;
+
+ /* get past the signature wrap */
+ buf = nsslowcert_dataStart(cert,cert_length,&buf_length,PR_FALSE, NULL);
+ if (buf == NULL) return SECFailure;
+ /* get into the raw cert data */
+ buf = nsslowcert_dataStart(buf,buf_length,&buf_length,PR_FALSE, NULL);
+ if (buf == NULL) return SECFailure;
+ /* skip past any optional version number */
+ if ((buf[0] & 0xa0) == 0xa0) {
+ dummy = nsslowcert_dataStart(buf,buf_length,&dummylen,PR_FALSE, NULL);
+ if (dummy == NULL) return SECFailure;
+ buf_length -= (dummy-buf) + dummylen;
+ buf = dummy + dummylen;
+ }
+ /* serial number */
+ if (derSN) {
+ derSN->data=nsslowcert_dataStart(buf,buf_length,&derSN->len,PR_TRUE, NULL);
+ }
+ serial->data = nsslowcert_dataStart(buf,buf_length,&serial->len,PR_FALSE, NULL);
+ if (serial->data == NULL) return SECFailure;
+ buf_length -= (serial->data-buf) + serial->len;
+ buf = serial->data + serial->len;
+ /* skip the OID */
+ dummy = nsslowcert_dataStart(buf,buf_length,&dummylen,PR_FALSE, NULL);
+ if (dummy == NULL) return SECFailure;
+ buf_length -= (dummy-buf) + dummylen;
+ buf = dummy + dummylen;
+ /* issuer */
+ issuer->data = nsslowcert_dataStart(buf,buf_length,&issuer->len,PR_TRUE, NULL);
+ if (issuer->data == NULL) return SECFailure;
+ buf_length -= (issuer->data-buf) + issuer->len;
+ buf = issuer->data + issuer->len;
+
+ /* only wanted issuer/SN */
+ if (valid == NULL) {
+ return SECSuccess;
+ }
+ /* validity */
+ valid->data = nsslowcert_dataStart(buf,buf_length,&valid->len,PR_FALSE, NULL);
+ if (valid->data == NULL) return SECFailure;
+ buf_length -= (valid->data-buf) + valid->len;
+ buf = valid->data + valid->len;
+ /*subject */
+ subject->data=nsslowcert_dataStart(buf,buf_length,&subject->len,PR_TRUE, NULL);
+ if (subject->data == NULL) return SECFailure;
+ buf_length -= (subject->data-buf) + subject->len;
+ buf = subject->data + subject->len;
+ /* subject key info */
+ subjkey->data=nsslowcert_dataStart(buf,buf_length,&subjkey->len,PR_TRUE, NULL);
+ if (subjkey->data == NULL) return SECFailure;
+ buf_length -= (subjkey->data-buf) + subjkey->len;
+ buf = subjkey->data + subjkey->len;
+
+ extensions->data = NULL;
+ extensions->len = 0;
+ while (buf_length > 0) {
+ /* EXTENSIONS */
+ if (buf[0] == 0xa3) {
+ extensions->data = nsslowcert_dataStart(buf,buf_length,
+ &extensions->len, PR_FALSE, NULL);
+ break;
+ }
+ dummy = nsslowcert_dataStart(buf,buf_length,&dummylen,PR_FALSE,NULL);
+ if (dummy == NULL) return SECFailure;
+ buf_length -= (dummy - buf) + dummylen;
+ buf = dummy + dummylen;
+ }
+ return SECSuccess;
+}
+
+static SECStatus
+nsslowcert_GetCertTimes(NSSLOWCERTCertificate *c, PRTime *notBefore, PRTime *notAfter)
+{
+ int rv;
+ NSSLOWCERTValidity validity;
+
+ rv = nsslowcert_GetValidityFields(c->validity.data,c->validity.len,
+ &validity.notBefore,&validity.notAfter);
+ if (rv != SECSuccess) {
+ return rv;
+ }
+
+ /* convert DER not-before time */
+ rv = DER_DecodeTimeChoice(notBefore, &validity.notBefore);
+ if (rv) {
+ return(SECFailure);
+ }
+
+ /* convert DER not-after time */
+ rv = DER_DecodeTimeChoice(notAfter, &validity.notAfter);
+ if (rv) {
+ return(SECFailure);
+ }
+
+ return(SECSuccess);
+}
+
+/*
+ * is certa newer than certb? If one is expired, pick the other one.
+ */
+PRBool
+nsslowcert_IsNewer(NSSLOWCERTCertificate *certa, NSSLOWCERTCertificate *certb)
+{
+ PRTime notBeforeA, notAfterA, notBeforeB, notAfterB, now;
+ SECStatus rv;
+ PRBool newerbefore, newerafter;
+
+ rv = nsslowcert_GetCertTimes(certa, &notBeforeA, &notAfterA);
+ if ( rv != SECSuccess ) {
+ return(PR_FALSE);
+ }
+
+ rv = nsslowcert_GetCertTimes(certb, &notBeforeB, &notAfterB);
+ if ( rv != SECSuccess ) {
+ return(PR_TRUE);
+ }
+
+ newerbefore = PR_FALSE;
+ if ( LL_CMP(notBeforeA, >, notBeforeB) ) {
+ newerbefore = PR_TRUE;
+ }
+
+ newerafter = PR_FALSE;
+ if ( LL_CMP(notAfterA, >, notAfterB) ) {
+ newerafter = PR_TRUE;
+ }
+
+ if ( newerbefore && newerafter ) {
+ return(PR_TRUE);
+ }
+
+ if ( ( !newerbefore ) && ( !newerafter ) ) {
+ return(PR_FALSE);
+ }
+
+ /* get current time */
+ now = PR_Now();
+
+ if ( newerbefore ) {
+ /* cert A was issued after cert B, but expires sooner */
+ /* if A is expired, then pick B */
+ if ( LL_CMP(notAfterA, <, now ) ) {
+ return(PR_FALSE);
+ }
+ return(PR_TRUE);
+ } else {
+ /* cert B was issued after cert A, but expires sooner */
+ /* if B is expired, then pick A */
+ if ( LL_CMP(notAfterB, <, now ) ) {
+ return(PR_TRUE);
+ }
+ return(PR_FALSE);
+ }
+}
+
+#define SOFT_DEFAULT_CHUNKSIZE 2048
+
+static SECStatus
+nsslowcert_KeyFromIssuerAndSN(PRArenaPool *arena,
+ SECItem *issuer, SECItem *sn, SECItem *key)
+{
+ unsigned int len = sn->len + issuer->len;
+
+ if (!arena) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ goto loser;
+ }
+ key->data = (unsigned char*)PORT_ArenaAlloc(arena, len);
+ if ( !key->data ) {
+ goto loser;
+ }
+
+ key->len = len;
+ /* copy the serialNumber */
+ PORT_Memcpy(key->data, sn->data, sn->len);
+
+ /* copy the issuer */
+ PORT_Memcpy(&key->data[sn->len], issuer->data, issuer->len);
+
+ return(SECSuccess);
+
+loser:
+ return(SECFailure);
+}
+
+static SECStatus
+nsslowcert_KeyFromIssuerAndSNStatic(unsigned char *space,
+ int spaceLen, SECItem *issuer, SECItem *sn, SECItem *key)
+{
+ unsigned int len = sn->len + issuer->len;
+
+ key->data = pkcs11_allocStaticData(len, space, spaceLen);
+ if ( !key->data ) {
+ goto loser;
+ }
+
+ key->len = len;
+ /* copy the serialNumber */
+ PORT_Memcpy(key->data, sn->data, sn->len);
+
+ /* copy the issuer */
+ PORT_Memcpy(&key->data[sn->len], issuer->data, issuer->len);
+
+ return(SECSuccess);
+
+loser:
+ return(SECFailure);
+}
+
+
+static char *
+nsslowcert_EmailName(SECItem *derDN, char *space, unsigned int len)
+{
+ unsigned char *buf;
+ unsigned int buf_length;
+
+ /* unwrap outer sequence */
+ buf=nsslowcert_dataStart(derDN->data,derDN->len,&buf_length,PR_FALSE,NULL);
+ if (buf == NULL) return NULL;
+
+ /* Walk each RDN */
+ while (buf_length > 0) {
+ unsigned char *rdn;
+ unsigned int rdn_length;
+
+ /* grab next rdn */
+ rdn=nsslowcert_dataStart(buf, buf_length, &rdn_length, PR_FALSE, NULL);
+ if (rdn == NULL) { return NULL; }
+ buf_length -= (rdn - buf) + rdn_length;
+ buf = rdn+rdn_length;
+
+ while (rdn_length > 0) {
+ unsigned char *ava;
+ unsigned int ava_length;
+ unsigned char *oid;
+ unsigned int oid_length;
+ unsigned char *name;
+ unsigned int name_length;
+ SECItem oidItem;
+ SECOidTag type;
+
+ /* unwrap the ava */
+ ava=nsslowcert_dataStart(rdn, rdn_length, &ava_length, PR_FALSE,
+ NULL);
+ if (ava == NULL) return NULL;
+ rdn_length -= (ava-rdn)+ava_length;
+ rdn = ava + ava_length;
+
+ oid=nsslowcert_dataStart(ava, ava_length, &oid_length, PR_FALSE,
+ NULL);
+ if (oid == NULL) { return NULL; }
+ ava_length -= (oid-ava)+oid_length;
+ ava = oid+oid_length;
+
+ name=nsslowcert_dataStart(ava, ava_length, &name_length, PR_FALSE,
+ NULL);
+ if (oid == NULL) { return NULL; }
+ ava_length -= (name-ava)+name_length;
+ ava = name+name_length;
+
+ oidItem.data = oid;
+ oidItem.len = oid_length;
+ type = SECOID_FindOIDTag(&oidItem);
+ if ((type == SEC_OID_PKCS9_EMAIL_ADDRESS) ||
+ (type == SEC_OID_RFC1274_MAIL)) {
+ /* Email is supposed to be IA5String, so no
+ * translation necessary */
+ char *emailAddr;
+ emailAddr = (char *)pkcs11_copyStaticData(name,name_length+1,
+ (unsigned char *)space,len);
+ if (emailAddr) {
+ emailAddr[name_length] = 0;
+ }
+ return emailAddr;
+ }
+ }
+ }
+ return NULL;
+}
+
+static char *
+nsslowcert_EmailAltName(NSSLOWCERTCertificate *cert, char *space,
+ unsigned int len)
+{
+ unsigned char *exts;
+ unsigned int exts_length;
+
+ /* unwrap the sequence */
+ exts = nsslowcert_dataStart(cert->extensions.data, cert->extensions.len,
+ &exts_length, PR_FALSE, NULL);
+ /* loop through extension */
+ while (exts && exts_length > 0) {
+ unsigned char * ext;
+ unsigned int ext_length;
+ unsigned char *oid;
+ unsigned int oid_length;
+ unsigned char *nameList;
+ unsigned int nameList_length;
+ SECItem oidItem;
+ SECOidTag type;
+
+ ext = nsslowcert_dataStart(exts, exts_length, &ext_length,
+ PR_FALSE, NULL);
+ if (ext == NULL) { break; }
+ exts_length -= (ext - exts) + ext_length;
+ exts = ext+ext_length;
+
+ oid=nsslowcert_dataStart(ext, ext_length, &oid_length, PR_FALSE, NULL);
+ if (oid == NULL) { break; }
+ ext_length -= (oid - ext) + oid_length;
+ ext = oid+oid_length;
+ oidItem.data = oid;
+ oidItem.len = oid_length;
+ type = SECOID_FindOIDTag(&oidItem);
+
+ /* get Alt Extension */
+ if (type != SEC_OID_X509_SUBJECT_ALT_NAME) {
+ continue;
+ }
+
+ /* skip passed the critical flag */
+ if (ext[0] == 0x01) { /* BOOLEAN */
+ unsigned char *dummy;
+ unsigned int dummy_length;
+ dummy = nsslowcert_dataStart(ext, ext_length, &dummy_length,
+ PR_FALSE, NULL);
+ if (dummy == NULL) { break; }
+ ext_length -= (dummy - ext) + dummy_length;
+ ext = dummy+dummy_length;
+ }
+
+
+ /* unwrap the name list */
+ nameList = nsslowcert_dataStart(ext, ext_length, &nameList_length,
+ PR_FALSE, NULL);
+ if (nameList == NULL) { break; }
+ ext_length -= (nameList - ext) + nameList_length;
+ ext = nameList+nameList_length;
+ nameList = nsslowcert_dataStart(nameList, nameList_length,
+ &nameList_length, PR_FALSE, NULL);
+ /* loop through the name list */
+ while (nameList && nameList_length > 0) {
+ unsigned char *thisName;
+ unsigned int thisName_length;
+
+ thisName = nsslowcert_dataStart(nameList, nameList_length,
+ &thisName_length, PR_FALSE, NULL);
+ if (thisName == NULL) { break; }
+ if (nameList[0] == 0xa2) { /* DNS Name */
+ SECItem dn;
+ char *emailAddr;
+
+ dn.data = thisName;
+ dn.len = thisName_length;
+ emailAddr = nsslowcert_EmailName(&dn, space, len);
+ if (emailAddr) {
+ return emailAddr;
+ }
+ }
+ if (nameList[0] == 0x81) { /* RFC 822name */
+ char *emailAddr;
+ emailAddr = (char *)pkcs11_copyStaticData(thisName,
+ thisName_length+1, (unsigned char *)space,len);
+ if (emailAddr) {
+ emailAddr[thisName_length] = 0;
+ }
+ return emailAddr;
+ }
+ nameList_length -= (thisName-nameList) + thisName_length;
+ nameList = thisName + thisName_length;
+ }
+ break;
+ }
+ return NULL;
+}
+
+static char *
+nsslowcert_GetCertificateEmailAddress(NSSLOWCERTCertificate *cert)
+{
+ char *emailAddr = NULL;
+ char *str;
+
+ emailAddr = nsslowcert_EmailName(&cert->derSubject,cert->emailAddrSpace,
+ sizeof(cert->emailAddrSpace));
+ /* couldn't find the email address in the DN, check the subject Alt name */
+ if (!emailAddr && cert->extensions.data) {
+ emailAddr = nsslowcert_EmailAltName(cert, cert->emailAddrSpace,
+ sizeof(cert->emailAddrSpace));
+ }
+
+
+ /* make it lower case */
+ str = emailAddr;
+ while ( str && *str ) {
+ *str = tolower( *str );
+ str++;
+ }
+ return emailAddr;
+
+}
+
+/*
+ * take a DER certificate and decode it into a certificate structure
+ */
+NSSLOWCERTCertificate *
+nsslowcert_DecodeDERCertificate(SECItem *derSignedCert, char *nickname)
+{
+ NSSLOWCERTCertificate *cert;
+ int rv;
+
+ /* allocate the certificate structure */
+ cert = nsslowcert_CreateCert();
+
+ if ( !cert ) {
+ goto loser;
+ }
+
+ /* point to passed in DER data */
+ cert->derCert = *derSignedCert;
+ cert->nickname = NULL;
+ cert->certKey.data = NULL;
+ cert->referenceCount = 1;
+
+ /* decode the certificate info */
+ rv = nsslowcert_GetCertFields(cert->derCert.data, cert->derCert.len,
+ &cert->derIssuer, &cert->serialNumber, &cert->derSN, &cert->derSubject,
+ &cert->validity, &cert->derSubjKeyInfo, &cert->extensions);
+
+ /* cert->subjectKeyID; x509v3 subject key identifier */
+ cert->subjectKeyID.data = NULL;
+ cert->subjectKeyID.len = 0;
+ cert->dbEntry = NULL;
+ cert ->trust = NULL;
+ cert ->dbhandle = NULL;
+
+ /* generate and save the database key for the cert */
+ rv = nsslowcert_KeyFromIssuerAndSNStatic(cert->certKeySpace,
+ sizeof(cert->certKeySpace), &cert->derIssuer,
+ &cert->serialNumber, &cert->certKey);
+ if ( rv ) {
+ goto loser;
+ }
+
+ /* set the nickname */
+ if ( nickname == NULL ) {
+ cert->nickname = NULL;
+ } else {
+ /* copy and install the nickname */
+ cert->nickname = pkcs11_copyNickname(nickname,cert->nicknameSpace,
+ sizeof(cert->nicknameSpace));
+ }
+
+#ifdef FIXME
+ /* initialize the subjectKeyID */
+ rv = cert_GetKeyID(cert);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+#endif
+
+ /* set the email address */
+ cert->emailAddr = nsslowcert_GetCertificateEmailAddress(cert);
+
+
+ cert->referenceCount = 1;
+
+ return(cert);
+
+loser:
+ if (cert) {
+ nsslowcert_DestroyCertificate(cert);
+ }
+
+ return(0);
+}
+
+char *
+nsslowcert_FixupEmailAddr(char *emailAddr)
+{
+ char *retaddr;
+ char *str;
+
+ if ( emailAddr == NULL ) {
+ return(NULL);
+ }
+
+ /* copy the string */
+ str = retaddr = PORT_Strdup(emailAddr);
+ if ( str == NULL ) {
+ return(NULL);
+ }
+
+ /* make it lower case */
+ while ( *str ) {
+ *str = tolower( *str );
+ str++;
+ }
+
+ return(retaddr);
+}
+
+
+/*
+ * Generate a database key, based on serial number and issuer, from a
+ * DER certificate.
+ */
+SECStatus
+nsslowcert_KeyFromDERCert(PRArenaPool *arena, SECItem *derCert, SECItem *key)
+{
+ int rv;
+ NSSLOWCERTCertKey certkey;
+
+ PORT_Memset(&certkey, 0, sizeof(NSSLOWCERTCertKey));
+
+ rv = nsslowcert_GetCertFields(derCert->data, derCert->len,
+ &certkey.derIssuer, &certkey.serialNumber, NULL, NULL,
+ NULL, NULL, NULL);
+
+ if ( rv ) {
+ goto loser;
+ }
+
+ return(nsslowcert_KeyFromIssuerAndSN(arena, &certkey.derIssuer,
+ &certkey.serialNumber, key));
+loser:
+ return(SECFailure);
+}
+
+NSSLOWKEYPublicKey *
+nsslowcert_ExtractPublicKey(NSSLOWCERTCertificate *cert)
+{
+ NSSLOWCERTSubjectPublicKeyInfo spki;
+ NSSLOWKEYPublicKey *pubk;
+ SECItem os;
+ SECStatus rv;
+ PRArenaPool *arena;
+ SECOidTag tag;
+ SECItem newDerSubjKeyInfo;
+
+ arena = PORT_NewArena (DER_DEFAULT_CHUNKSIZE);
+ if (arena == NULL)
+ return NULL;
+
+ pubk = (NSSLOWKEYPublicKey *)
+ PORT_ArenaZAlloc(arena, sizeof(NSSLOWKEYPublicKey));
+ if (pubk == NULL) {
+ PORT_FreeArena (arena, PR_FALSE);
+ return NULL;
+ }
+
+ pubk->arena = arena;
+ PORT_Memset(&spki,0,sizeof(spki));
+
+ /* copy the DER into the arena, since Quick DER returns data that points
+ into the DER input, which may get freed by the caller */
+ rv = SECITEM_CopyItem(arena, &newDerSubjKeyInfo, &cert->derSubjKeyInfo);
+ if ( rv != SECSuccess ) {
+ PORT_FreeArena (arena, PR_FALSE);
+ return NULL;
+ }
+
+ /* we haven't bothered decoding the spki struct yet, do it now */
+ rv = SEC_QuickDERDecodeItem(arena, &spki,
+ nsslowcert_SubjectPublicKeyInfoTemplate, &newDerSubjKeyInfo);
+ if (rv != SECSuccess) {
+ PORT_FreeArena (arena, PR_FALSE);
+ return NULL;
+ }
+
+ /* Convert bit string length from bits to bytes */
+ os = spki.subjectPublicKey;
+ DER_ConvertBitString (&os);
+
+ tag = SECOID_GetAlgorithmTag(&spki.algorithm);
+ switch ( tag ) {
+ case SEC_OID_X500_RSA_ENCRYPTION:
+ case SEC_OID_PKCS1_RSA_ENCRYPTION:
+ pubk->keyType = NSSLOWKEYRSAKey;
+ prepare_low_rsa_pub_key_for_asn1(pubk);
+ rv = SEC_QuickDERDecodeItem(arena, pubk,
+ nsslowcert_RSAPublicKeyTemplate, &os);
+ if (rv == SECSuccess)
+ return pubk;
+ break;
+ case SEC_OID_ANSIX9_DSA_SIGNATURE:
+ pubk->keyType = NSSLOWKEYDSAKey;
+ prepare_low_dsa_pub_key_for_asn1(pubk);
+ rv = SEC_QuickDERDecodeItem(arena, pubk,
+ nsslowcert_DSAPublicKeyTemplate, &os);
+ if (rv == SECSuccess) return pubk;
+ break;
+ case SEC_OID_X942_DIFFIE_HELMAN_KEY:
+ pubk->keyType = NSSLOWKEYDHKey;
+ prepare_low_dh_pub_key_for_asn1(pubk);
+ rv = SEC_QuickDERDecodeItem(arena, pubk,
+ nsslowcert_DHPublicKeyTemplate, &os);
+ if (rv == SECSuccess) return pubk;
+ break;
+#ifdef NSS_ENABLE_ECC
+ case SEC_OID_ANSIX962_EC_PUBLIC_KEY:
+ pubk->keyType = NSSLOWKEYECKey;
+ /* Since PKCS#11 directly takes the DER encoding of EC params
+ * and public value, we don't need any decoding here.
+ */
+ rv = SECITEM_CopyItem(arena, &pubk->u.ec.ecParams.DEREncoding,
+ &spki.algorithm.parameters);
+ if ( rv != SECSuccess )
+ break;
+
+ /* Fill out the rest of the ecParams structure
+ * based on the encoded params
+ */
+ if (LGEC_FillParams(arena, &pubk->u.ec.ecParams.DEREncoding,
+ &pubk->u.ec.ecParams) != SECSuccess)
+ break;
+
+ rv = SECITEM_CopyItem(arena, &pubk->u.ec.publicValue, &os);
+ if (rv == SECSuccess) return pubk;
+ break;
+#endif /* NSS_ENABLE_ECC */
+ default:
+ rv = SECFailure;
+ break;
+ }
+
+ nsslowkey_DestroyPublicKey (pubk);
+ return NULL;
+}
+
diff --git a/security/nss/lib/softoken/legacydb/lowkey.c b/security/nss/lib/softoken/legacydb/lowkey.c
new file mode 100644
index 000000000..c9ed72e53
--- /dev/null
+++ b/security/nss/lib/softoken/legacydb/lowkey.c
@@ -0,0 +1,462 @@
+/* ***** 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):
+ * Dr Vipul Gupta <vipul.gupta@sun.com>, Sun Microsystems Laboratories
+ *
+ * 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 "lowkeyi.h"
+#include "secoid.h"
+#include "secitem.h"
+#include "secder.h"
+#include "secasn1.h"
+#include "secerr.h"
+
+static const SEC_ASN1Template nsslowkey_AttributeTemplate[] = {
+ { SEC_ASN1_SEQUENCE,
+ 0, NULL, sizeof(NSSLOWKEYAttribute) },
+ { SEC_ASN1_OBJECT_ID, offsetof(NSSLOWKEYAttribute, attrType) },
+ { SEC_ASN1_SET_OF, offsetof(NSSLOWKEYAttribute, attrValue),
+ SEC_AnyTemplate },
+ { 0 }
+};
+
+static const SEC_ASN1Template nsslowkey_SetOfAttributeTemplate[] = {
+ { SEC_ASN1_SET_OF, 0, nsslowkey_AttributeTemplate },
+};
+/* ASN1 Templates for new decoder/encoder */
+const SEC_ASN1Template nsslowkey_PrivateKeyInfoTemplate[] = {
+ { SEC_ASN1_SEQUENCE,
+ 0, NULL, sizeof(NSSLOWKEYPrivateKeyInfo) },
+ { SEC_ASN1_INTEGER,
+ offsetof(NSSLOWKEYPrivateKeyInfo,version) },
+ { SEC_ASN1_INLINE,
+ offsetof(NSSLOWKEYPrivateKeyInfo,algorithm),
+ SECOID_AlgorithmIDTemplate },
+ { SEC_ASN1_OCTET_STRING,
+ offsetof(NSSLOWKEYPrivateKeyInfo,privateKey) },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0,
+ offsetof(NSSLOWKEYPrivateKeyInfo, attributes),
+ nsslowkey_SetOfAttributeTemplate },
+ { 0 }
+};
+
+const SEC_ASN1Template nsslowkey_PQGParamsTemplate[] = {
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(PQGParams) },
+ { SEC_ASN1_INTEGER, offsetof(PQGParams,prime) },
+ { SEC_ASN1_INTEGER, offsetof(PQGParams,subPrime) },
+ { SEC_ASN1_INTEGER, offsetof(PQGParams,base) },
+ { 0, }
+};
+
+const SEC_ASN1Template nsslowkey_RSAPrivateKeyTemplate[] = {
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(NSSLOWKEYPrivateKey) },
+ { SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPrivateKey,u.rsa.version) },
+ { SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPrivateKey,u.rsa.modulus) },
+ { SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPrivateKey,u.rsa.publicExponent) },
+ { SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPrivateKey,u.rsa.privateExponent) },
+ { SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPrivateKey,u.rsa.prime1) },
+ { SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPrivateKey,u.rsa.prime2) },
+ { SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPrivateKey,u.rsa.exponent1) },
+ { SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPrivateKey,u.rsa.exponent2) },
+ { SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPrivateKey,u.rsa.coefficient) },
+ { 0 }
+};
+
+
+const SEC_ASN1Template nsslowkey_DSAPrivateKeyTemplate[] = {
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(NSSLOWKEYPrivateKey) },
+ { SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPrivateKey,u.dsa.publicValue) },
+ { SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPrivateKey,u.dsa.privateValue) },
+ { 0, }
+};
+
+const SEC_ASN1Template nsslowkey_DSAPrivateKeyExportTemplate[] = {
+ { SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPrivateKey,u.dsa.privateValue) },
+};
+
+const SEC_ASN1Template nsslowkey_DHPrivateKeyTemplate[] = {
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(NSSLOWKEYPrivateKey) },
+ { SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPrivateKey,u.dh.publicValue) },
+ { SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPrivateKey,u.dh.privateValue) },
+ { SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPrivateKey,u.dh.base) },
+ { SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPrivateKey,u.dh.prime) },
+ { 0, }
+};
+
+#ifdef NSS_ENABLE_ECC
+
+/* XXX This is just a placeholder for later when we support
+ * generic curves and need full-blown support for parsing EC
+ * parameters. For now, we only support named curves in which
+ * EC params are simply encoded as an object ID and we don't
+ * use nsslowkey_ECParamsTemplate.
+ */
+const SEC_ASN1Template nsslowkey_ECParamsTemplate[] = {
+ { SEC_ASN1_CHOICE, offsetof(ECParams,type), NULL, sizeof(ECParams) },
+ { SEC_ASN1_OBJECT_ID, offsetof(ECParams,curveOID), NULL, ec_params_named },
+ { 0, }
+};
+
+
+/* NOTE: The SECG specification allows the private key structure
+ * to contain curve parameters but recommends that they be stored
+ * in the PrivateKeyAlgorithmIdentifier field of the PrivateKeyInfo
+ * instead.
+ */
+const SEC_ASN1Template nsslowkey_ECPrivateKeyTemplate[] = {
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(NSSLOWKEYPrivateKey) },
+ { SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPrivateKey,u.ec.version) },
+ { SEC_ASN1_OCTET_STRING,
+ offsetof(NSSLOWKEYPrivateKey,u.ec.privateValue) },
+ /* XXX The following template works for now since we only
+ * support named curves for which the parameters are
+ * encoded as an object ID. When we support generic curves,
+ * we'll need to define nsslowkey_ECParamsTemplate
+ */
+#if 1
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED |
+ SEC_ASN1_EXPLICIT | SEC_ASN1_CONTEXT_SPECIFIC | 0,
+ offsetof(NSSLOWKEYPrivateKey,u.ec.ecParams.curveOID),
+ SEC_ObjectIDTemplate },
+#else
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED |
+ SEC_ASN1_EXPLICIT | SEC_ASN1_CONTEXT_SPECIFIC | 0,
+ offsetof(NSSLOWKEYPrivateKey,u.ec.ecParams),
+ nsslowkey_ECParamsTemplate },
+#endif
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED |
+ SEC_ASN1_EXPLICIT | SEC_ASN1_CONTEXT_SPECIFIC | 1,
+ offsetof(NSSLOWKEYPrivateKey,u.ec.publicValue),
+ SEC_BitStringTemplate },
+ { 0, }
+};
+
+
+/*
+ * smaller version of EC_FillParams. In this code, we only need
+ * oid and DER data.
+ */
+SECStatus
+LGEC_FillParams(PRArenaPool *arena, const SECItem *encodedParams,
+ ECParams *params)
+{
+ SECOidTag tag;
+ SECItem oid = { siBuffer, NULL, 0};
+
+#if EC_DEBUG
+ int i;
+
+ printf("Encoded params in EC_DecodeParams: ");
+ for (i = 0; i < encodedParams->len; i++) {
+ printf("%02x:", encodedParams->data[i]);
+ }
+ printf("\n");
+#endif
+
+ oid.len = encodedParams->len - 2;
+ oid.data = encodedParams->data + 2;
+ if ((encodedParams->data[0] != SEC_ASN1_OBJECT_ID) ||
+ ((tag = SECOID_FindOIDTag(&oid)) == SEC_OID_UNKNOWN)) {
+ PORT_SetError(SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE);
+ return SECFailure;
+ }
+
+ params->arena = arena;
+
+ /* For named curves, fill out curveOID */
+ params->curveOID.len = oid.len;
+ params->curveOID.data = (unsigned char *) PORT_ArenaAlloc(arena, oid.len);
+ if (params->curveOID.data == NULL) {
+ return SECFailure;
+ }
+ memcpy(params->curveOID.data, oid.data, oid.len);
+
+ return SECSuccess;
+}
+
+/* Copy all of the fields from srcParams into dstParams
+ */
+SECStatus
+LGEC_CopyParams(PRArenaPool *arena, ECParams *dstParams,
+ const ECParams *srcParams)
+{
+ SECStatus rv = SECFailure;
+
+ dstParams->arena = arena;
+ rv = SECITEM_CopyItem(arena, &dstParams->DEREncoding,
+ &srcParams->DEREncoding);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ rv =SECITEM_CopyItem(arena, &dstParams->curveOID,
+ &srcParams->curveOID);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ return SECSuccess;
+
+loser:
+ return SECFailure;
+}
+#endif /* NSS_ENABLE_ECC */
+/*
+ * See bugzilla bug 125359
+ * Since NSS (via PKCS#11) wants to handle big integers as unsigned ints,
+ * all of the templates above that en/decode into integers must be converted
+ * from ASN.1's signed integer type. This is done by marking either the
+ * source or destination (encoding or decoding, respectively) type as
+ * siUnsignedInteger.
+ */
+
+void
+prepare_low_rsa_priv_key_for_asn1(NSSLOWKEYPrivateKey *key)
+{
+ key->u.rsa.modulus.type = siUnsignedInteger;
+ key->u.rsa.publicExponent.type = siUnsignedInteger;
+ key->u.rsa.privateExponent.type = siUnsignedInteger;
+ key->u.rsa.prime1.type = siUnsignedInteger;
+ key->u.rsa.prime2.type = siUnsignedInteger;
+ key->u.rsa.exponent1.type = siUnsignedInteger;
+ key->u.rsa.exponent2.type = siUnsignedInteger;
+ key->u.rsa.coefficient.type = siUnsignedInteger;
+}
+
+void
+prepare_low_pqg_params_for_asn1(PQGParams *params)
+{
+ params->prime.type = siUnsignedInteger;
+ params->subPrime.type = siUnsignedInteger;
+ params->base.type = siUnsignedInteger;
+}
+
+void
+prepare_low_dsa_priv_key_for_asn1(NSSLOWKEYPrivateKey *key)
+{
+ key->u.dsa.publicValue.type = siUnsignedInteger;
+ key->u.dsa.privateValue.type = siUnsignedInteger;
+ key->u.dsa.params.prime.type = siUnsignedInteger;
+ key->u.dsa.params.subPrime.type = siUnsignedInteger;
+ key->u.dsa.params.base.type = siUnsignedInteger;
+}
+
+void
+prepare_low_dsa_priv_key_export_for_asn1(NSSLOWKEYPrivateKey *key)
+{
+ key->u.dsa.privateValue.type = siUnsignedInteger;
+}
+
+void
+prepare_low_dh_priv_key_for_asn1(NSSLOWKEYPrivateKey *key)
+{
+ key->u.dh.prime.type = siUnsignedInteger;
+ key->u.dh.base.type = siUnsignedInteger;
+ key->u.dh.publicValue.type = siUnsignedInteger;
+ key->u.dh.privateValue.type = siUnsignedInteger;
+}
+
+#ifdef NSS_ENABLE_ECC
+void
+prepare_low_ecparams_for_asn1(ECParams *params)
+{
+ params->DEREncoding.type = siUnsignedInteger;
+ params->curveOID.type = siUnsignedInteger;
+}
+
+void
+prepare_low_ec_priv_key_for_asn1(NSSLOWKEYPrivateKey *key)
+{
+ key->u.ec.version.type = siUnsignedInteger;
+ key->u.ec.ecParams.DEREncoding.type = siUnsignedInteger;
+ key->u.ec.ecParams.curveOID.type = siUnsignedInteger;
+ key->u.ec.privateValue.type = siUnsignedInteger;
+ key->u.ec.publicValue.type = siUnsignedInteger;
+}
+#endif /* NSS_ENABLE_ECC */
+
+void
+nsslowkey_DestroyPrivateKey(NSSLOWKEYPrivateKey *privk)
+{
+ if (privk && privk->arena) {
+ PORT_FreeArena(privk->arena, PR_TRUE);
+ }
+}
+
+void
+nsslowkey_DestroyPublicKey(NSSLOWKEYPublicKey *pubk)
+{
+ if (pubk && pubk->arena) {
+ PORT_FreeArena(pubk->arena, PR_FALSE);
+ }
+}
+unsigned
+nsslowkey_PublicModulusLen(NSSLOWKEYPublicKey *pubk)
+{
+ unsigned char b0;
+
+ /* interpret modulus length as key strength... in
+ * fortezza that's the public key length */
+
+ switch (pubk->keyType) {
+ case NSSLOWKEYRSAKey:
+ b0 = pubk->u.rsa.modulus.data[0];
+ return b0 ? pubk->u.rsa.modulus.len : pubk->u.rsa.modulus.len - 1;
+ default:
+ break;
+ }
+ return 0;
+}
+
+unsigned
+nsslowkey_PrivateModulusLen(NSSLOWKEYPrivateKey *privk)
+{
+
+ unsigned char b0;
+
+ switch (privk->keyType) {
+ case NSSLOWKEYRSAKey:
+ b0 = privk->u.rsa.modulus.data[0];
+ return b0 ? privk->u.rsa.modulus.len : privk->u.rsa.modulus.len - 1;
+ default:
+ break;
+ }
+ return 0;
+}
+
+NSSLOWKEYPublicKey *
+nsslowkey_ConvertToPublicKey(NSSLOWKEYPrivateKey *privk)
+{
+ NSSLOWKEYPublicKey *pubk;
+ PLArenaPool *arena;
+
+
+ arena = PORT_NewArena (DER_DEFAULT_CHUNKSIZE);
+ if (arena == NULL) {
+ PORT_SetError (SEC_ERROR_NO_MEMORY);
+ return NULL;
+ }
+
+ switch(privk->keyType) {
+ case NSSLOWKEYRSAKey:
+ case NSSLOWKEYNullKey:
+ pubk = (NSSLOWKEYPublicKey *)PORT_ArenaZAlloc(arena,
+ sizeof (NSSLOWKEYPublicKey));
+ if (pubk != NULL) {
+ SECStatus rv;
+
+ pubk->arena = arena;
+ pubk->keyType = privk->keyType;
+ if (privk->keyType == NSSLOWKEYNullKey) return pubk;
+ rv = SECITEM_CopyItem(arena, &pubk->u.rsa.modulus,
+ &privk->u.rsa.modulus);
+ if (rv == SECSuccess) {
+ rv = SECITEM_CopyItem (arena, &pubk->u.rsa.publicExponent,
+ &privk->u.rsa.publicExponent);
+ if (rv == SECSuccess)
+ return pubk;
+ }
+ } else {
+ PORT_SetError (SEC_ERROR_NO_MEMORY);
+ }
+ break;
+ case NSSLOWKEYDSAKey:
+ pubk = (NSSLOWKEYPublicKey *)PORT_ArenaZAlloc(arena,
+ sizeof(NSSLOWKEYPublicKey));
+ if (pubk != NULL) {
+ SECStatus rv;
+
+ pubk->arena = arena;
+ pubk->keyType = privk->keyType;
+ rv = SECITEM_CopyItem(arena, &pubk->u.dsa.publicValue,
+ &privk->u.dsa.publicValue);
+ if (rv != SECSuccess) break;
+ rv = SECITEM_CopyItem(arena, &pubk->u.dsa.params.prime,
+ &privk->u.dsa.params.prime);
+ if (rv != SECSuccess) break;
+ rv = SECITEM_CopyItem(arena, &pubk->u.dsa.params.subPrime,
+ &privk->u.dsa.params.subPrime);
+ if (rv != SECSuccess) break;
+ rv = SECITEM_CopyItem(arena, &pubk->u.dsa.params.base,
+ &privk->u.dsa.params.base);
+ if (rv == SECSuccess) return pubk;
+ }
+ break;
+ case NSSLOWKEYDHKey:
+ pubk = (NSSLOWKEYPublicKey *)PORT_ArenaZAlloc(arena,
+ sizeof(NSSLOWKEYPublicKey));
+ if (pubk != NULL) {
+ SECStatus rv;
+
+ pubk->arena = arena;
+ pubk->keyType = privk->keyType;
+ rv = SECITEM_CopyItem(arena, &pubk->u.dh.publicValue,
+ &privk->u.dh.publicValue);
+ if (rv != SECSuccess) break;
+ rv = SECITEM_CopyItem(arena, &pubk->u.dh.prime,
+ &privk->u.dh.prime);
+ if (rv != SECSuccess) break;
+ rv = SECITEM_CopyItem(arena, &pubk->u.dh.base,
+ &privk->u.dh.base);
+ if (rv == SECSuccess) return pubk;
+ }
+ break;
+#ifdef NSS_ENABLE_ECC
+ case NSSLOWKEYECKey:
+ pubk = (NSSLOWKEYPublicKey *)PORT_ArenaZAlloc(arena,
+ sizeof(NSSLOWKEYPublicKey));
+ if (pubk != NULL) {
+ SECStatus rv;
+
+ pubk->arena = arena;
+ pubk->keyType = privk->keyType;
+ rv = SECITEM_CopyItem(arena, &pubk->u.ec.publicValue,
+ &privk->u.ec.publicValue);
+ if (rv != SECSuccess) break;
+ pubk->u.ec.ecParams.arena = arena;
+ /* Copy the rest of the params */
+ rv = LGEC_CopyParams(arena, &(pubk->u.ec.ecParams),
+ &(privk->u.ec.ecParams));
+ if (rv == SECSuccess) return pubk;
+ }
+ break;
+#endif /* NSS_ENABLE_ECC */
+ /* No Fortezza in Low Key implementations (Fortezza keys aren't
+ * stored in our data base */
+ default:
+ break;
+ }
+
+ PORT_FreeArena (arena, PR_FALSE);
+ return NULL;
+}
+
diff --git a/security/nss/lib/softoken/legacydb/lowkeyi.h b/security/nss/lib/softoken/legacydb/lowkeyi.h
new file mode 100644
index 000000000..45359d4ef
--- /dev/null
+++ b/security/nss/lib/softoken/legacydb/lowkeyi.h
@@ -0,0 +1,198 @@
+/* ***** 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):
+ * Dr Vipul Gupta <vipul.gupta@sun.com>, Sun Microsystems Laboratories
+ *
+ * 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 ***** */
+/* $Id$ */
+
+#ifndef _LOWKEYI_H_
+#define _LOWKEYI_H_
+
+#include "prtypes.h"
+#include "seccomon.h"
+#include "secoidt.h"
+#include "pcertt.h"
+#include "lowkeyti.h"
+#include "sdb.h"
+
+SEC_BEGIN_PROTOS
+
+/*
+ * See bugzilla bug 125359
+ * Since NSS (via PKCS#11) wants to handle big integers as unsigned ints,
+ * all of the templates above that en/decode into integers must be converted
+ * from ASN.1's signed integer type. This is done by marking either the
+ * source or destination (encoding or decoding, respectively) type as
+ * siUnsignedInteger.
+ */
+extern void prepare_low_rsa_priv_key_for_asn1(NSSLOWKEYPrivateKey *key);
+extern void prepare_low_pqg_params_for_asn1(PQGParams *params);
+extern void prepare_low_dsa_priv_key_for_asn1(NSSLOWKEYPrivateKey *key);
+extern void prepare_low_dsa_priv_key_export_for_asn1(NSSLOWKEYPrivateKey *key);
+extern void prepare_low_dh_priv_key_for_asn1(NSSLOWKEYPrivateKey *key);
+#ifdef NSS_ENABLE_ECC
+extern void prepare_low_ec_priv_key_for_asn1(NSSLOWKEYPrivateKey *key);
+extern void prepare_low_ecparams_for_asn1(ECParams *params);
+#endif /* NSS_ENABLE_ECC */
+
+typedef char * (* NSSLOWKEYDBNameFunc)(void *arg, int dbVersion);
+
+/*
+** Open a key database.
+*/
+extern NSSLOWKEYDBHandle *nsslowkey_OpenKeyDB(PRBool readOnly,
+ const char *domain,
+ const char *prefix,
+ NSSLOWKEYDBNameFunc namecb,
+ void *cbarg);
+
+/*
+** Close the specified key database.
+*/
+extern void nsslowkey_CloseKeyDB(NSSLOWKEYDBHandle *handle);
+
+/*
+ * Get the version number of the database
+ */
+extern int nsslowkey_GetKeyDBVersion(NSSLOWKEYDBHandle *handle);
+
+/*
+** Delete a key from the database
+*/
+extern SECStatus nsslowkey_DeleteKey(NSSLOWKEYDBHandle *handle,
+ const SECItem *pubkey);
+
+/*
+** Store a key in the database, indexed by its public key modulus.
+** "pk" is the private key to store
+** "f" is a the callback function for getting the password
+** "arg" is the argument for the callback
+*/
+extern SECStatus nsslowkey_StoreKeyByPublicKey(NSSLOWKEYDBHandle *handle,
+ NSSLOWKEYPrivateKey *pk,
+ SECItem *pubKeyData,
+ char *nickname,
+ SDB *sdb);
+
+/* does the key for this cert exist in the database filed by modulus */
+extern PRBool nsslowkey_KeyForCertExists(NSSLOWKEYDBHandle *handle,
+ NSSLOWCERTCertificate *cert);
+/* does a key with this ID already exist? */
+extern PRBool nsslowkey_KeyForIDExists(NSSLOWKEYDBHandle *handle, SECItem *id);
+
+/*
+** Destroy a private key object.
+** "key" the object
+** "freeit" if PR_TRUE then free the object as well as its sub-objects
+*/
+extern void nsslowkey_DestroyPrivateKey(NSSLOWKEYPrivateKey *key);
+
+/*
+** Destroy a public key object.
+** "key" the object
+** "freeit" if PR_TRUE then free the object as well as its sub-objects
+*/
+extern void nsslowkey_DestroyPublicKey(NSSLOWKEYPublicKey *key);
+
+/*
+** Return the modulus length of "pubKey".
+*/
+extern unsigned int nsslowkey_PublicModulusLen(NSSLOWKEYPublicKey *pubKey);
+
+
+/*
+** Return the modulus length of "privKey".
+*/
+extern unsigned int nsslowkey_PrivateModulusLen(NSSLOWKEYPrivateKey *privKey);
+
+
+/*
+** Convert a low private key "privateKey" into a public low key
+*/
+extern NSSLOWKEYPublicKey
+ *nsslowkey_ConvertToPublicKey(NSSLOWKEYPrivateKey *privateKey);
+
+
+SECStatus
+nsslowkey_UpdateNickname(NSSLOWKEYDBHandle *handle,
+ NSSLOWKEYPrivateKey *privkey,
+ SECItem *pubKeyData,
+ char *nickname,
+ SDB *sdb);
+
+/* Store key by modulus and specify an encryption algorithm to use.
+ * handle is the pointer to the key database,
+ * privkey is the private key to be stored,
+ * f and arg are the function and arguments to the callback
+ * to get a password,
+ * algorithm is the algorithm which the privKey is to be stored.
+ * A return of anything but SECSuccess indicates failure.
+ */
+extern SECStatus
+nsslowkey_StoreKeyByPublicKeyAlg(NSSLOWKEYDBHandle *handle,
+ NSSLOWKEYPrivateKey *privkey,
+ SECItem *pubKeyData,
+ char *nickname,
+ SDB *sdb,
+ PRBool update);
+
+/* Find key by modulus. This function is the inverse of store key
+ * by modulus. An attempt to locate the key with "modulus" is
+ * performed. If the key is found, the private key is returned,
+ * else NULL is returned.
+ * modulus is the modulus to locate
+ */
+extern NSSLOWKEYPrivateKey *
+nsslowkey_FindKeyByPublicKey(NSSLOWKEYDBHandle *handle, SECItem *modulus,
+ SDB *sdb);
+
+extern char *
+nsslowkey_FindKeyNicknameByPublicKey(NSSLOWKEYDBHandle *handle,
+ SECItem *modulus, SDB *sdb);
+
+#ifdef NSS_ENABLE_ECC
+/*
+ * smaller version of EC_FillParams. In this code, we only need
+ * oid and DER data.
+ */
+SECStatus LGEC_FillParams(PRArenaPool *arena, const SECItem *encodedParams,
+ ECParams *params);
+
+/* Copy all of the fields from srcParams into dstParams */
+SECStatus LGEC_CopyParams(PRArenaPool *arena, ECParams *dstParams,
+ const ECParams *srcParams);
+#endif
+SEC_END_PROTOS
+
+#endif /* _LOWKEYI_H_ */
diff --git a/security/nss/lib/softoken/legacydb/lowkeyti.h b/security/nss/lib/softoken/legacydb/lowkeyti.h
new file mode 100644
index 000000000..57f8105e3
--- /dev/null
+++ b/security/nss/lib/softoken/legacydb/lowkeyti.h
@@ -0,0 +1,161 @@
+/* ***** 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):
+ * Dr Vipul Gupta <vipul.gupta@sun.com>, Sun Microsystems Laboratories
+ *
+ * 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 ***** */
+#ifndef _LOWKEYTI_H_
+#define _LOWKEYTI_H_ 1
+
+#include "blapit.h"
+#include "prtypes.h"
+#include "plarena.h"
+#include "secitem.h"
+#include "secasn1t.h"
+#include "secoidt.h"
+
+
+/*
+ * a key in/for the data base
+ */
+struct NSSLOWKEYDBKeyStr {
+ PLArenaPool *arena;
+ int version;
+ char *nickname;
+ SECItem salt;
+ SECItem derPK;
+};
+typedef struct NSSLOWKEYDBKeyStr NSSLOWKEYDBKey;
+
+typedef struct NSSLOWKEYDBHandleStr NSSLOWKEYDBHandle;
+
+#ifdef NSS_USE_KEY4_DB
+#define NSSLOWKEY_DB_FILE_VERSION 4
+#else
+#define NSSLOWKEY_DB_FILE_VERSION 3
+#endif
+
+#define NSSLOWKEY_VERSION 0 /* what we *create* */
+
+/*
+** Typedef for callback to get a password "key".
+*/
+extern const SEC_ASN1Template nsslowkey_PQGParamsTemplate[];
+extern const SEC_ASN1Template nsslowkey_RSAPrivateKeyTemplate[];
+extern const SEC_ASN1Template nsslowkey_DSAPrivateKeyTemplate[];
+extern const SEC_ASN1Template nsslowkey_DSAPrivateKeyExportTemplate[];
+extern const SEC_ASN1Template nsslowkey_DHPrivateKeyTemplate[];
+extern const SEC_ASN1Template nsslowkey_DHPrivateKeyExportTemplate[];
+#ifdef NSS_ENABLE_ECC
+#define NSSLOWKEY_EC_PRIVATE_KEY_VERSION 1 /* as per SECG 1 C.4 */
+extern const SEC_ASN1Template nsslowkey_ECParamsTemplate[];
+extern const SEC_ASN1Template nsslowkey_ECPrivateKeyTemplate[];
+#endif /* NSS_ENABLE_ECC */
+
+extern const SEC_ASN1Template nsslowkey_PrivateKeyInfoTemplate[];
+extern const SEC_ASN1Template nsslowkey_EncryptedPrivateKeyInfoTemplate[];
+
+/*
+ * PKCS #8 attributes
+ */
+struct NSSLOWKEYAttributeStr {
+ SECItem attrType;
+ SECItem *attrValue;
+};
+typedef struct NSSLOWKEYAttributeStr NSSLOWKEYAttribute;
+
+/*
+** A PKCS#8 private key info object
+*/
+struct NSSLOWKEYPrivateKeyInfoStr {
+ PLArenaPool *arena;
+ SECItem version;
+ SECAlgorithmID algorithm;
+ SECItem privateKey;
+ NSSLOWKEYAttribute **attributes;
+};
+typedef struct NSSLOWKEYPrivateKeyInfoStr NSSLOWKEYPrivateKeyInfo;
+#define NSSLOWKEY_PRIVATE_KEY_INFO_VERSION 0 /* what we *create* */
+
+/*
+** A PKCS#8 private key info object
+*/
+struct NSSLOWKEYEncryptedPrivateKeyInfoStr {
+ PLArenaPool *arena;
+ SECAlgorithmID algorithm;
+ SECItem encryptedData;
+};
+typedef struct NSSLOWKEYEncryptedPrivateKeyInfoStr NSSLOWKEYEncryptedPrivateKeyInfo;
+
+
+typedef enum {
+ NSSLOWKEYNullKey = 0,
+ NSSLOWKEYRSAKey = 1,
+ NSSLOWKEYDSAKey = 2,
+ NSSLOWKEYDHKey = 4,
+ NSSLOWKEYECKey = 5
+} NSSLOWKEYType;
+
+/*
+** An RSA public key object.
+*/
+struct NSSLOWKEYPublicKeyStr {
+ PLArenaPool *arena;
+ NSSLOWKEYType keyType ;
+ union {
+ RSAPublicKey rsa;
+ DSAPublicKey dsa;
+ DHPublicKey dh;
+ ECPublicKey ec;
+ } u;
+};
+typedef struct NSSLOWKEYPublicKeyStr NSSLOWKEYPublicKey;
+
+/*
+** Low Level private key object
+** This is only used by the raw Crypto engines (crypto), keydb (keydb),
+** and PKCS #11. Everyone else uses the high level key structure.
+*/
+struct NSSLOWKEYPrivateKeyStr {
+ PLArenaPool *arena;
+ NSSLOWKEYType keyType;
+ union {
+ RSAPrivateKey rsa;
+ DSAPrivateKey dsa;
+ DHPrivateKey dh;
+ ECPrivateKey ec;
+ } u;
+};
+typedef struct NSSLOWKEYPrivateKeyStr NSSLOWKEYPrivateKey;
+
+#endif /* _LOWKEYTI_H_ */
diff --git a/security/nss/lib/softoken/legacydb/manifest.mn b/security/nss/lib/softoken/legacydb/manifest.mn
new file mode 100644
index 000000000..4c5fe7b95
--- /dev/null
+++ b/security/nss/lib/softoken/legacydb/manifest.mn
@@ -0,0 +1,68 @@
+#
+# ***** 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 *****
+CORE_DEPTH = ../../../..
+
+MODULE = nss
+
+REQUIRES = dbm
+
+LIBRARY_NAME = legacydb
+LIBRARY_VERSION = 3
+MAPFILE = $(OBJDIR)/legacydb.def
+
+DEFINES += -DSHLIB_SUFFIX=\"$(DLL_SUFFIX)\" -DSHLIB_PREFIX=\"$(DLL_PREFIX)\" -DSOFTOKEN_LIB_NAME=\"$(notdir $(SHARED_LIBRARY))\"
+
+
+CSRCS = \
+ dbmshim.c \
+ keydb.c \
+ lgattr.c \
+ lgcreate.c \
+ lgdestroy.c \
+ lgfind.c \
+ lginit.c \
+ lgutil.c \
+ lowcert.c \
+ lowkey.c \
+ pcertdb.c \
+ pk11db.c \
+ $(NULL)
+
+ifdef NSS_ENABLE_ECC
+DEFINES += -DNSS_ENABLE_ECC
+endif
+
diff --git a/security/nss/lib/softoken/legacydb/pcert.h b/security/nss/lib/softoken/legacydb/pcert.h
new file mode 100644
index 000000000..68956abd6
--- /dev/null
+++ b/security/nss/lib/softoken/legacydb/pcert.h
@@ -0,0 +1,261 @@
+/* ***** 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 ***** */
+
+#ifndef _PCERTDB_H_
+#define _PCERTDB_H_
+
+#include "plarena.h"
+#include "prlong.h"
+#include "pcertt.h"
+
+#include "lowkeyti.h" /* for struct NSSLOWKEYPublicKeyStr */
+
+SEC_BEGIN_PROTOS
+
+/*
+ * initialize any global certificate locks
+ */
+SECStatus nsslowcert_InitLocks(void);
+
+/*
+** Add a DER encoded certificate to the permanent database.
+** "derCert" is the DER encoded certificate.
+** "nickname" is the nickname to use for the cert
+** "trust" is the trust parameters for the cert
+*/
+SECStatus nsslowcert_AddPermCert(NSSLOWCERTCertDBHandle *handle,
+ NSSLOWCERTCertificate *cert,
+ char *nickname, NSSLOWCERTCertTrust *trust);
+SECStatus nsslowcert_AddPermNickname(NSSLOWCERTCertDBHandle *dbhandle,
+ NSSLOWCERTCertificate *cert, char *nickname);
+
+SECStatus nsslowcert_DeletePermCertificate(NSSLOWCERTCertificate *cert);
+
+typedef SECStatus (PR_CALLBACK * PermCertCallback)(NSSLOWCERTCertificate *cert,
+ SECItem *k, void *pdata);
+/*
+** Traverse the entire permanent database, and pass the certs off to a
+** user supplied function.
+** "certfunc" is the user function to call for each certificate
+** "udata" is the user's data, which is passed through to "certfunc"
+*/
+SECStatus
+nsslowcert_TraversePermCerts(NSSLOWCERTCertDBHandle *handle,
+ PermCertCallback certfunc,
+ void *udata );
+
+PRBool
+nsslowcert_CertDBKeyConflict(SECItem *derCert, NSSLOWCERTCertDBHandle *handle);
+
+certDBEntryRevocation *
+nsslowcert_FindCrlByKey(NSSLOWCERTCertDBHandle *handle,
+ SECItem *crlKey, PRBool isKRL);
+
+SECStatus
+nsslowcert_DeletePermCRL(NSSLOWCERTCertDBHandle *handle,const SECItem *derName,
+ PRBool isKRL);
+SECStatus
+nsslowcert_AddCrl(NSSLOWCERTCertDBHandle *handle, SECItem *derCrl ,
+ SECItem *derKey, char *url, PRBool isKRL);
+
+NSSLOWCERTCertDBHandle *nsslowcert_GetDefaultCertDB();
+NSSLOWKEYPublicKey *nsslowcert_ExtractPublicKey(NSSLOWCERTCertificate *);
+
+NSSLOWCERTCertificate *
+nsslowcert_NewTempCertificate(NSSLOWCERTCertDBHandle *handle, SECItem *derCert,
+ char *nickname, PRBool isperm, PRBool copyDER);
+NSSLOWCERTCertificate *
+nsslowcert_DupCertificate(NSSLOWCERTCertificate *cert);
+void nsslowcert_DestroyCertificate(NSSLOWCERTCertificate *cert);
+void nsslowcert_DestroyTrust(NSSLOWCERTTrust *Trust);
+
+/*
+ * Lookup a certificate in the databases without locking
+ * "certKey" is the database key to look for
+ *
+ * XXX - this should be internal, but pkcs 11 needs to call it during a
+ * traversal.
+ */
+NSSLOWCERTCertificate *
+nsslowcert_FindCertByKey(NSSLOWCERTCertDBHandle *handle, const SECItem *certKey);
+
+/*
+ * Lookup trust for a certificate in the databases without locking
+ * "certKey" is the database key to look for
+ *
+ * XXX - this should be internal, but pkcs 11 needs to call it during a
+ * traversal.
+ */
+NSSLOWCERTTrust *
+nsslowcert_FindTrustByKey(NSSLOWCERTCertDBHandle *handle, const SECItem *certKey);
+
+/*
+** Generate a certificate key from the issuer and serialnumber, then look it
+** up in the database. Return the cert if found.
+** "issuerAndSN" is the issuer and serial number to look for
+*/
+extern NSSLOWCERTCertificate *
+nsslowcert_FindCertByIssuerAndSN (NSSLOWCERTCertDBHandle *handle, NSSLOWCERTIssuerAndSN *issuerAndSN);
+
+/*
+** Generate a certificate key from the issuer and serialnumber, then look it
+** up in the database. Return the cert if found.
+** "issuerAndSN" is the issuer and serial number to look for
+*/
+extern NSSLOWCERTTrust *
+nsslowcert_FindTrustByIssuerAndSN (NSSLOWCERTCertDBHandle *handle, NSSLOWCERTIssuerAndSN *issuerAndSN);
+
+/*
+** Find a certificate in the database by a DER encoded certificate
+** "derCert" is the DER encoded certificate
+*/
+extern NSSLOWCERTCertificate *
+nsslowcert_FindCertByDERCert(NSSLOWCERTCertDBHandle *handle, SECItem *derCert);
+
+/* convert an email address to lower case */
+char *nsslowcert_FixupEmailAddr(char *emailAddr);
+
+/*
+** Decode a DER encoded certificate into an NSSLOWCERTCertificate structure
+** "derSignedCert" is the DER encoded signed certificate
+** "copyDER" is true if the DER should be copied, false if the
+** existing copy should be referenced
+** "nickname" is the nickname to use in the database. If it is NULL
+** then a temporary nickname is generated.
+*/
+extern NSSLOWCERTCertificate *
+nsslowcert_DecodeDERCertificate (SECItem *derSignedCert, char *nickname);
+
+SECStatus
+nsslowcert_KeyFromDERCert(PRArenaPool *arena, SECItem *derCert, SECItem *key);
+
+certDBEntrySMime *
+nsslowcert_ReadDBSMimeEntry(NSSLOWCERTCertDBHandle *certHandle,
+ char *emailAddr);
+void
+nsslowcert_DestroyDBEntry(certDBEntry *entry);
+
+SECStatus
+nsslowcert_OpenCertDB(NSSLOWCERTCertDBHandle *handle, PRBool readOnly,
+ const char *domain, const char *prefix,
+ NSSLOWCERTDBNameFunc namecb, void *cbarg, PRBool openVolatile);
+
+void
+nsslowcert_ClosePermCertDB(NSSLOWCERTCertDBHandle *handle);
+
+/*
+ * is certa newer than certb? If one is expired, pick the other one.
+ */
+PRBool
+nsslowcert_IsNewer(NSSLOWCERTCertificate *certa, NSSLOWCERTCertificate *certb);
+
+
+SECStatus
+nsslowcert_TraverseDBEntries(NSSLOWCERTCertDBHandle *handle,
+ certDBEntryType type,
+ SECStatus (* callback)(SECItem *data, SECItem *key,
+ certDBEntryType type, void *pdata),
+ void *udata );
+SECStatus
+nsslowcert_TraversePermCertsForSubject(NSSLOWCERTCertDBHandle *handle,
+ SECItem *derSubject,
+ NSSLOWCERTCertCallback cb, void *cbarg);
+int
+nsslowcert_NumPermCertsForSubject(NSSLOWCERTCertDBHandle *handle,
+ SECItem *derSubject);
+SECStatus
+nsslowcert_TraversePermCertsForNickname(NSSLOWCERTCertDBHandle *handle,
+ char *nickname, NSSLOWCERTCertCallback cb, void *cbarg);
+
+int
+nsslowcert_NumPermCertsForNickname(NSSLOWCERTCertDBHandle *handle,
+ char *nickname);
+SECStatus
+nsslowcert_GetCertTrust(NSSLOWCERTCertificate *cert,
+ NSSLOWCERTCertTrust *trust);
+
+SECStatus
+nsslowcert_SaveSMimeProfile(NSSLOWCERTCertDBHandle *dbhandle, char *emailAddr,
+ SECItem *derSubject, SECItem *emailProfile, SECItem *profileTime);
+
+/*
+ * Change the trust attributes of a certificate and make them permanent
+ * in the database.
+ */
+SECStatus
+nsslowcert_ChangeCertTrust(NSSLOWCERTCertDBHandle *handle,
+ NSSLOWCERTCertificate *cert, NSSLOWCERTCertTrust *trust);
+
+PRBool
+nsslowcert_needDBVerify(NSSLOWCERTCertDBHandle *handle);
+
+void
+nsslowcert_setDBVerify(NSSLOWCERTCertDBHandle *handle, PRBool value);
+
+PRBool
+nsslowcert_hasTrust(NSSLOWCERTCertTrust *trust);
+
+void
+nsslowcert_DestroyFreeLists(void);
+
+void
+nsslowcert_DestroyGlobalLocks(void);
+
+void
+pkcs11_freeNickname(char *nickname, char *space);
+
+char *
+pkcs11_copyNickname(char *nickname, char *space, int spaceLen);
+
+void
+pkcs11_freeStaticData(unsigned char *data, unsigned char *space);
+
+unsigned char *
+pkcs11_allocStaticData(int datalen, unsigned char *space, int spaceLen);
+
+unsigned char *
+pkcs11_copyStaticData(unsigned char *data, int datalen, unsigned char *space,
+ int spaceLen);
+NSSLOWCERTCertificate *
+nsslowcert_CreateCert(void);
+
+certDBEntry *
+nsslowcert_DecodeAnyDBEntry(SECItem *dbData, const SECItem *dbKey,
+ certDBEntryType entryType, void *pdata);
+
+SEC_END_PROTOS
+
+ #endif /* _PCERTDB_H_ */
diff --git a/security/nss/lib/softoken/legacydb/pcertdb.c b/security/nss/lib/softoken/legacydb/pcertdb.c
new file mode 100644
index 000000000..39c928f1e
--- /dev/null
+++ b/security/nss/lib/softoken/legacydb/pcertdb.c
@@ -0,0 +1,5363 @@
+/* ***** 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 ***** */
+
+/*
+ * Permanent Certificate database handling code
+ *
+ * $Id$
+ */
+#include "lowkeyti.h"
+#include "pcert.h"
+#include "mcom_db.h"
+#include "pcert.h"
+#include "secitem.h"
+#include "secder.h"
+
+#include "nsslocks.h"
+#include "secerr.h"
+#include "lgdb.h"
+
+/* forward declaration */
+NSSLOWCERTCertificate *
+nsslowcert_FindCertByDERCertNoLocking(NSSLOWCERTCertDBHandle *handle, SECItem *derCert);
+static SECStatus
+nsslowcert_UpdateSMimeProfile(NSSLOWCERTCertDBHandle *dbhandle,
+ char *emailAddr, SECItem *derSubject, SECItem *emailProfile,
+ SECItem *profileTime);
+static SECStatus
+nsslowcert_UpdatePermCert(NSSLOWCERTCertDBHandle *dbhandle,
+ NSSLOWCERTCertificate *cert, char *nickname, NSSLOWCERTCertTrust *trust);
+static SECStatus
+nsslowcert_UpdateCrl(NSSLOWCERTCertDBHandle *handle, SECItem *derCrl,
+ SECItem *crlKey, char *url, PRBool isKRL);
+
+static NSSLOWCERTCertificate *certListHead = NULL;
+static NSSLOWCERTTrust *trustListHead = NULL;
+static certDBEntryCert *entryListHead = NULL;
+static int certListCount = 0;
+static int trustListCount = 0;
+static int entryListCount = 0;
+#define MAX_CERT_LIST_COUNT 10
+#define MAX_TRUST_LIST_COUNT 10
+#define MAX_ENTRY_LIST_COUNT 10
+
+/*
+ * the following functions are wrappers for the db library that implement
+ * a global lock to make the database thread safe.
+ */
+static PZLock *dbLock = NULL;
+static PZLock *certRefCountLock = NULL;
+static PZLock *certTrustLock = NULL;
+static PZLock *freeListLock = NULL;
+
+void
+certdb_InitDBLock(NSSLOWCERTCertDBHandle *handle)
+{
+ if (dbLock == NULL) {
+ nss_InitLock(&dbLock, nssILockCertDB);
+ PORT_Assert(dbLock != NULL);
+ }
+}
+
+SECStatus
+nsslowcert_InitLocks(void)
+{
+ if (freeListLock == NULL) {
+ nss_InitLock(&freeListLock, nssILockRefLock);
+ if (freeListLock == NULL) {
+ return SECFailure;
+ }
+ }
+ if (certRefCountLock == NULL) {
+ nss_InitLock(&certRefCountLock, nssILockRefLock);
+ if (certRefCountLock == NULL) {
+ return SECFailure;
+ }
+ }
+ if (certTrustLock == NULL ) {
+ nss_InitLock(&certTrustLock, nssILockCertDB);
+ if (certTrustLock == NULL) {
+ return SECFailure;
+ }
+ }
+
+ return SECSuccess;
+}
+
+/*
+ * Acquire the global lock on the cert database.
+ * This lock is currently used for the following operations:
+ * adding or deleting a cert to either the temp or perm databases
+ * converting a temp to perm or perm to temp
+ * changing (maybe just adding!?) the trust of a cert
+ * chaning the DB status checking Configuration
+ */
+static void
+nsslowcert_LockDB(NSSLOWCERTCertDBHandle *handle)
+{
+ PZ_EnterMonitor(handle->dbMon);
+ return;
+}
+
+/*
+ * Free the global cert database lock.
+ */
+static void
+nsslowcert_UnlockDB(NSSLOWCERTCertDBHandle *handle)
+{
+ PRStatus prstat;
+
+ prstat = PZ_ExitMonitor(handle->dbMon);
+
+ PORT_Assert(prstat == PR_SUCCESS);
+
+ return;
+}
+
+
+/*
+ * Acquire the cert reference count lock
+ * There is currently one global lock for all certs, but I'm putting a cert
+ * arg here so that it will be easy to make it per-cert in the future if
+ * that turns out to be necessary.
+ */
+static void
+nsslowcert_LockCertRefCount(NSSLOWCERTCertificate *cert)
+{
+ PORT_Assert(certRefCountLock != NULL);
+
+ PZ_Lock(certRefCountLock);
+ return;
+}
+
+/*
+ * Free the cert reference count lock
+ */
+static void
+nsslowcert_UnlockCertRefCount(NSSLOWCERTCertificate *cert)
+{
+ PRStatus prstat;
+
+ PORT_Assert(certRefCountLock != NULL);
+
+ prstat = PZ_Unlock(certRefCountLock);
+
+ PORT_Assert(prstat == PR_SUCCESS);
+
+ return;
+}
+
+/*
+ * Acquire the cert trust lock
+ * There is currently one global lock for all certs, but I'm putting a cert
+ * arg here so that it will be easy to make it per-cert in the future if
+ * that turns out to be necessary.
+ */
+static void
+nsslowcert_LockCertTrust(NSSLOWCERTCertificate *cert)
+{
+ PORT_Assert(certTrustLock != NULL);
+
+ PZ_Lock(certTrustLock);
+ return;
+}
+
+/*
+ * Free the cert trust lock
+ */
+static void
+nsslowcert_UnlockCertTrust(NSSLOWCERTCertificate *cert)
+{
+ PRStatus prstat;
+
+ PORT_Assert(certTrustLock != NULL);
+
+ prstat = PZ_Unlock(certTrustLock);
+
+ PORT_Assert(prstat == PR_SUCCESS);
+
+ return;
+}
+
+
+/*
+ * Acquire the cert reference count lock
+ * There is currently one global lock for all certs, but I'm putting a cert
+ * arg here so that it will be easy to make it per-cert in the future if
+ * that turns out to be necessary.
+ */
+static void
+nsslowcert_LockFreeList(void)
+{
+ PORT_Assert(freeListLock != NULL);
+
+ PZ_Lock(freeListLock);
+ return;
+}
+
+/*
+ * Free the cert reference count lock
+ */
+static void
+nsslowcert_UnlockFreeList(void)
+{
+ PRStatus prstat;
+
+ PORT_Assert(freeListLock != NULL);
+
+ prstat = PZ_Unlock(freeListLock);
+
+ PORT_Assert(prstat == PR_SUCCESS);
+
+ return;
+}
+
+NSSLOWCERTCertificate *
+nsslowcert_DupCertificate(NSSLOWCERTCertificate *c)
+{
+ if (c) {
+ nsslowcert_LockCertRefCount(c);
+ ++c->referenceCount;
+ nsslowcert_UnlockCertRefCount(c);
+ }
+ return c;
+}
+
+static int
+certdb_Get(DB *db, DBT *key, DBT *data, unsigned int flags)
+{
+ PRStatus prstat;
+ int ret;
+
+ PORT_Assert(dbLock != NULL);
+ PZ_Lock(dbLock);
+
+ ret = (* db->get)(db, key, data, flags);
+
+ prstat = PZ_Unlock(dbLock);
+
+ return(ret);
+}
+
+static int
+certdb_Put(DB *db, DBT *key, DBT *data, unsigned int flags)
+{
+ PRStatus prstat;
+ int ret = 0;
+
+ PORT_Assert(dbLock != NULL);
+ PZ_Lock(dbLock);
+
+ ret = (* db->put)(db, key, data, flags);
+
+ prstat = PZ_Unlock(dbLock);
+
+ return(ret);
+}
+
+static int
+certdb_Sync(DB *db, unsigned int flags)
+{
+ PRStatus prstat;
+ int ret;
+
+ PORT_Assert(dbLock != NULL);
+ PZ_Lock(dbLock);
+
+ ret = (* db->sync)(db, flags);
+
+ prstat = PZ_Unlock(dbLock);
+
+ return(ret);
+}
+
+#define DB_NOT_FOUND -30991 /* from DBM 3.2 */
+static int
+certdb_Del(DB *db, DBT *key, unsigned int flags)
+{
+ PRStatus prstat;
+ int ret;
+
+ PORT_Assert(dbLock != NULL);
+ PZ_Lock(dbLock);
+
+ ret = (* db->del)(db, key, flags);
+
+ prstat = PZ_Unlock(dbLock);
+
+ /* don't fail if the record is already deleted */
+ if (ret == DB_NOT_FOUND) {
+ ret = 0;
+ }
+
+ return(ret);
+}
+
+static int
+certdb_Seq(DB *db, DBT *key, DBT *data, unsigned int flags)
+{
+ PRStatus prstat;
+ int ret;
+
+ PORT_Assert(dbLock != NULL);
+ PZ_Lock(dbLock);
+
+ ret = (* db->seq)(db, key, data, flags);
+
+ prstat = PZ_Unlock(dbLock);
+
+ return(ret);
+}
+
+static void
+certdb_Close(DB *db)
+{
+ PRStatus prstat;
+
+ PORT_Assert(dbLock != NULL);
+ PZ_Lock(dbLock);
+
+ (* db->close)(db);
+
+ prstat = PZ_Unlock(dbLock);
+
+ return;
+}
+
+void
+pkcs11_freeNickname(char *nickname, char *space)
+{
+ if (nickname && nickname != space) {
+ PORT_Free(nickname);
+ }
+}
+
+char *
+pkcs11_copyNickname(char *nickname,char *space, int spaceLen)
+{
+ int len;
+ char *copy = NULL;
+
+ len = PORT_Strlen(nickname)+1;
+ if (len <= spaceLen) {
+ copy = space;
+ PORT_Memcpy(copy,nickname,len);
+ } else {
+ copy = PORT_Strdup(nickname);
+ }
+
+ return copy;
+}
+
+void
+pkcs11_freeStaticData (unsigned char *data, unsigned char *space)
+{
+ if (data && data != space) {
+ PORT_Free(data);
+ }
+}
+
+unsigned char *
+pkcs11_allocStaticData(int len, unsigned char *space, int spaceLen)
+{
+ unsigned char *data = NULL;
+
+ if (len <= spaceLen) {
+ data = space;
+ } else {
+ data = (unsigned char *) PORT_Alloc(len);
+ }
+
+ return data;
+}
+
+unsigned char *
+pkcs11_copyStaticData(unsigned char *data, int len,
+ unsigned char *space, int spaceLen)
+{
+ unsigned char *copy = pkcs11_allocStaticData(len, space, spaceLen);
+ if (copy) {
+ PORT_Memcpy(copy,data,len);
+ }
+
+ return copy;
+}
+
+/*
+ * destroy a database entry
+ */
+static void
+DestroyDBEntry(certDBEntry *entry)
+{
+ PRArenaPool *arena = entry->common.arena;
+
+ /* must be one of our certDBEntry from the free list */
+ if (arena == NULL) {
+ certDBEntryCert *certEntry;
+ if ( entry->common.type != certDBEntryTypeCert) {
+ return;
+ }
+ certEntry = (certDBEntryCert *)entry;
+
+ pkcs11_freeStaticData(certEntry->derCert.data, certEntry->derCertSpace);
+ pkcs11_freeNickname(certEntry->nickname, certEntry->nicknameSpace);
+
+ nsslowcert_LockFreeList();
+ if (entryListCount > MAX_ENTRY_LIST_COUNT) {
+ PORT_Free(certEntry);
+ } else {
+ entryListCount++;
+ PORT_Memset(certEntry, 0, sizeof( *certEntry));
+ certEntry->next = entryListHead;
+ entryListHead = certEntry;
+ }
+ nsslowcert_UnlockFreeList();
+ return;
+ }
+
+
+ /* Zero out the entry struct, so that any further attempts to use it
+ * will cause an exception (e.g. null pointer reference). */
+ PORT_Memset(&entry->common, 0, sizeof entry->common);
+ PORT_FreeArena(arena, PR_FALSE);
+
+ return;
+}
+
+/* forward references */
+static void nsslowcert_DestroyCertificateNoLocking(NSSLOWCERTCertificate *cert);
+
+static SECStatus
+DeleteDBEntry(NSSLOWCERTCertDBHandle *handle, certDBEntryType type, SECItem *dbkey)
+{
+ DBT key;
+ int ret;
+
+ /* init the database key */
+ key.data = dbkey->data;
+ key.size = dbkey->len;
+
+ dbkey->data[0] = (unsigned char)type;
+
+ /* delete entry from database */
+ ret = certdb_Del(handle->permCertDB, &key, 0 );
+ if ( ret != 0 ) {
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ goto loser;
+ }
+
+ ret = certdb_Sync(handle->permCertDB, 0);
+ if ( ret ) {
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ goto loser;
+ }
+
+ return(SECSuccess);
+
+loser:
+ return(SECFailure);
+}
+
+static SECStatus
+ReadDBEntry(NSSLOWCERTCertDBHandle *handle, certDBEntryCommon *entry,
+ SECItem *dbkey, SECItem *dbentry, PRArenaPool *arena)
+{
+ DBT data, key;
+ int ret;
+ unsigned char *buf;
+
+ /* init the database key */
+ key.data = dbkey->data;
+ key.size = dbkey->len;
+
+ dbkey->data[0] = (unsigned char)entry->type;
+
+ /* read entry from database */
+ ret = certdb_Get(handle->permCertDB, &key, &data, 0 );
+ if ( ret != 0 ) {
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ goto loser;
+ }
+
+ /* validate the entry */
+ if ( data.size < SEC_DB_ENTRY_HEADER_LEN ) {
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ goto loser;
+ }
+ buf = (unsigned char *)data.data;
+ /* version 7 has the same schema, we may be using a v7 db if we openned
+ * the databases readonly. */
+ if (!((buf[0] == (unsigned char)CERT_DB_FILE_VERSION)
+ || (buf[0] == (unsigned char) CERT_DB_V7_FILE_VERSION))) {
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ goto loser;
+ }
+ if ( buf[1] != (unsigned char)entry->type ) {
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ goto loser;
+ }
+
+ /* copy out header information */
+ entry->version = (unsigned int)buf[0];
+ entry->type = (certDBEntryType)buf[1];
+ entry->flags = (unsigned int)buf[2];
+
+ /* format body of entry for return to caller */
+ dbentry->len = data.size - SEC_DB_ENTRY_HEADER_LEN;
+ if ( dbentry->len ) {
+ if (arena) {
+ dbentry->data = (unsigned char *)
+ PORT_ArenaAlloc(arena, dbentry->len);
+ if ( dbentry->data == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ PORT_Memcpy(dbentry->data, &buf[SEC_DB_ENTRY_HEADER_LEN],
+ dbentry->len);
+ } else {
+ dbentry->data = &buf[SEC_DB_ENTRY_HEADER_LEN];
+ }
+ } else {
+ dbentry->data = NULL;
+ }
+
+ return(SECSuccess);
+
+loser:
+ return(SECFailure);
+}
+
+/**
+ ** Implement low level database access
+ **/
+static SECStatus
+WriteDBEntry(NSSLOWCERTCertDBHandle *handle, certDBEntryCommon *entry,
+ SECItem *dbkey, SECItem *dbentry)
+{
+ int ret;
+ DBT data, key;
+ unsigned char *buf;
+
+ data.data = dbentry->data;
+ data.size = dbentry->len;
+
+ buf = (unsigned char*)data.data;
+
+ buf[0] = (unsigned char)entry->version;
+ buf[1] = (unsigned char)entry->type;
+ buf[2] = (unsigned char)entry->flags;
+
+ key.data = dbkey->data;
+ key.size = dbkey->len;
+
+ dbkey->data[0] = (unsigned char)entry->type;
+
+ /* put the record into the database now */
+ ret = certdb_Put(handle->permCertDB, &key, &data, 0);
+
+ if ( ret != 0 ) {
+ goto loser;
+ }
+
+ ret = certdb_Sync( handle->permCertDB, 0 );
+
+ if ( ret ) {
+ goto loser;
+ }
+
+ return(SECSuccess);
+
+loser:
+ return(SECFailure);
+}
+
+/*
+ * encode a database cert record
+ */
+static SECStatus
+EncodeDBCertEntry(certDBEntryCert *entry, PRArenaPool *arena, SECItem *dbitem)
+{
+ unsigned int nnlen;
+ unsigned char *buf;
+ char *nn;
+ char zbuf = 0;
+
+ if ( entry->nickname ) {
+ nn = entry->nickname;
+ } else {
+ nn = &zbuf;
+ }
+ nnlen = PORT_Strlen(nn) + 1;
+
+ /* allocate space for encoded database record, including space
+ * for low level header
+ */
+ dbitem->len = entry->derCert.len + nnlen + DB_CERT_ENTRY_HEADER_LEN +
+ SEC_DB_ENTRY_HEADER_LEN;
+
+ dbitem->data = (unsigned char *)PORT_ArenaAlloc(arena, dbitem->len);
+ if ( dbitem->data == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ /* fill in database record */
+ buf = &dbitem->data[SEC_DB_ENTRY_HEADER_LEN];
+
+ buf[0] = ( entry->trust.sslFlags >> 8 ) & 0xff;
+ buf[1] = entry->trust.sslFlags & 0xff;
+ buf[2] = ( entry->trust.emailFlags >> 8 ) & 0xff;
+ buf[3] = entry->trust.emailFlags & 0xff;
+ buf[4] = ( entry->trust.objectSigningFlags >> 8 ) & 0xff;
+ buf[5] = entry->trust.objectSigningFlags & 0xff;
+ buf[6] = ( entry->derCert.len >> 8 ) & 0xff;
+ buf[7] = entry->derCert.len & 0xff;
+ buf[8] = ( nnlen >> 8 ) & 0xff;
+ buf[9] = nnlen & 0xff;
+
+ PORT_Memcpy(&buf[DB_CERT_ENTRY_HEADER_LEN], entry->derCert.data,
+ entry->derCert.len);
+
+ PORT_Memcpy(&buf[DB_CERT_ENTRY_HEADER_LEN + entry->derCert.len],
+ nn, nnlen);
+
+ return(SECSuccess);
+
+loser:
+ return(SECFailure);
+}
+
+/*
+ * encode a database key for a cert record
+ */
+static SECStatus
+EncodeDBCertKey(const SECItem *certKey, PRArenaPool *arena, SECItem *dbkey)
+{
+ unsigned int len = certKey->len + SEC_DB_KEY_HEADER_LEN;
+ if (arena) {
+ dbkey->data = (unsigned char *)PORT_ArenaAlloc(arena, len);
+ } else {
+ if (dbkey->len < len) {
+ dbkey->data = (unsigned char *)PORT_Alloc(len);
+ }
+ }
+ dbkey->len = len;
+ if ( dbkey->data == NULL ) {
+ goto loser;
+ }
+ PORT_Memcpy(&dbkey->data[SEC_DB_KEY_HEADER_LEN],
+ certKey->data, certKey->len);
+ dbkey->data[0] = certDBEntryTypeCert;
+
+ return(SECSuccess);
+loser:
+ return(SECFailure);
+}
+
+static SECStatus
+EncodeDBGenericKey(const SECItem *certKey, PRArenaPool *arena, SECItem *dbkey,
+ certDBEntryType entryType)
+{
+ /*
+ * we only allow _one_ KRL key!
+ */
+ if (entryType == certDBEntryTypeKeyRevocation) {
+ dbkey->len = SEC_DB_KEY_HEADER_LEN;
+ dbkey->data = (unsigned char *)PORT_ArenaAlloc(arena, dbkey->len);
+ if ( dbkey->data == NULL ) {
+ goto loser;
+ }
+ dbkey->data[0] = (unsigned char) entryType;
+ return(SECSuccess);
+ }
+
+
+ dbkey->len = certKey->len + SEC_DB_KEY_HEADER_LEN;
+ dbkey->data = (unsigned char *)PORT_ArenaAlloc(arena, dbkey->len);
+ if ( dbkey->data == NULL ) {
+ goto loser;
+ }
+ PORT_Memcpy(&dbkey->data[SEC_DB_KEY_HEADER_LEN],
+ certKey->data, certKey->len);
+ dbkey->data[0] = (unsigned char) entryType;
+
+ return(SECSuccess);
+loser:
+ return(SECFailure);
+}
+
+static SECStatus
+DecodeDBCertEntry(certDBEntryCert *entry, SECItem *dbentry)
+{
+ unsigned int nnlen;
+ unsigned int headerlen;
+ int lenoff;
+
+ /* allow updates of old versions of the database */
+ switch ( entry->common.version ) {
+ case 5:
+ headerlen = DB_CERT_V5_ENTRY_HEADER_LEN;
+ lenoff = 3;
+ break;
+ case 6:
+ /* should not get here */
+ PORT_Assert(0);
+ headerlen = DB_CERT_V6_ENTRY_HEADER_LEN;
+ lenoff = 3;
+ break;
+ case 7:
+ case 8:
+ headerlen = DB_CERT_ENTRY_HEADER_LEN;
+ lenoff = 6;
+ break;
+ default:
+ /* better not get here */
+ PORT_Assert(0);
+ headerlen = DB_CERT_V5_ENTRY_HEADER_LEN;
+ lenoff = 3;
+ break;
+ }
+
+ /* is record long enough for header? */
+ if ( dbentry->len < headerlen ) {
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ goto loser;
+ }
+
+ /* is database entry correct length? */
+ entry->derCert.len = ( ( dbentry->data[lenoff] << 8 ) |
+ dbentry->data[lenoff+1] );
+ nnlen = ( ( dbentry->data[lenoff+2] << 8 ) | dbentry->data[lenoff+3] );
+ if ( ( entry->derCert.len + nnlen + headerlen )
+ != dbentry->len) {
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ goto loser;
+ }
+
+ /* copy the dercert */
+
+ entry->derCert.data = pkcs11_copyStaticData(&dbentry->data[headerlen],
+ entry->derCert.len,entry->derCertSpace,sizeof(entry->derCertSpace));
+ if ( entry->derCert.data == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ /* copy the nickname */
+ if ( nnlen > 1 ) {
+ entry->nickname = (char *)pkcs11_copyStaticData(
+ &dbentry->data[headerlen+entry->derCert.len], nnlen,
+ (unsigned char *)entry->nicknameSpace,
+ sizeof(entry->nicknameSpace));
+ if ( entry->nickname == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+ } else {
+ entry->nickname = NULL;
+ }
+
+ if ( entry->common.version < 7 ) {
+ /* allow updates of v5 db */
+ entry->trust.sslFlags = dbentry->data[0];
+ entry->trust.emailFlags = dbentry->data[1];
+ entry->trust.objectSigningFlags = dbentry->data[2];
+ } else {
+ entry->trust.sslFlags = ( dbentry->data[0] << 8 ) | dbentry->data[1];
+ entry->trust.emailFlags = ( dbentry->data[2] << 8 ) | dbentry->data[3];
+ entry->trust.objectSigningFlags =
+ ( dbentry->data[4] << 8 ) | dbentry->data[5];
+ }
+
+ return(SECSuccess);
+loser:
+ return(SECFailure);
+}
+
+
+/*
+ * Create a new certDBEntryCert from existing data
+ */
+static certDBEntryCert *
+NewDBCertEntry(SECItem *derCert, char *nickname,
+ NSSLOWCERTCertTrust *trust, int flags)
+{
+ certDBEntryCert *entry;
+ PRArenaPool *arena = NULL;
+ int nnlen;
+
+ arena = PORT_NewArena( DER_DEFAULT_CHUNKSIZE );
+
+ if ( !arena ) {
+ goto loser;
+ }
+
+ entry = PORT_ArenaZNew(arena, certDBEntryCert);
+ if ( entry == NULL ) {
+ goto loser;
+ }
+
+ /* fill in the dbCert */
+ entry->common.arena = arena;
+ entry->common.type = certDBEntryTypeCert;
+ entry->common.version = CERT_DB_FILE_VERSION;
+ entry->common.flags = flags;
+
+ if ( trust ) {
+ entry->trust = *trust;
+ }
+
+ entry->derCert.data = (unsigned char *)PORT_ArenaAlloc(arena, derCert->len);
+ if ( !entry->derCert.data ) {
+ goto loser;
+ }
+ entry->derCert.len = derCert->len;
+ PORT_Memcpy(entry->derCert.data, derCert->data, derCert->len);
+
+ nnlen = ( nickname ? strlen(nickname) + 1 : 0 );
+
+ if ( nnlen ) {
+ entry->nickname = (char *)PORT_ArenaAlloc(arena, nnlen);
+ if ( !entry->nickname ) {
+ goto loser;
+ }
+ PORT_Memcpy(entry->nickname, nickname, nnlen);
+
+ } else {
+ entry->nickname = 0;
+ }
+
+ return(entry);
+
+loser:
+
+ /* allocation error, free arena and return */
+ if ( arena ) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return(0);
+}
+
+/*
+ * Decode a version 4 DBCert from the byte stream database format
+ * and construct a current database entry struct
+ */
+static certDBEntryCert *
+DecodeV4DBCertEntry(unsigned char *buf, int len)
+{
+ certDBEntryCert *entry;
+ int certlen;
+ int nnlen;
+ PRArenaPool *arena;
+
+ /* make sure length is at least long enough for the header */
+ if ( len < DBCERT_V4_HEADER_LEN ) {
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ return(0);
+ }
+
+ /* get other lengths */
+ certlen = buf[3] << 8 | buf[4];
+ nnlen = buf[5] << 8 | buf[6];
+
+ /* make sure DB entry is the right size */
+ if ( ( certlen + nnlen + DBCERT_V4_HEADER_LEN ) != len ) {
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ return(0);
+ }
+
+ /* allocate arena */
+ arena = PORT_NewArena( DER_DEFAULT_CHUNKSIZE );
+
+ if ( !arena ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return(0);
+ }
+
+ /* allocate structure and members */
+ entry = (certDBEntryCert *) PORT_ArenaAlloc(arena, sizeof(certDBEntryCert));
+
+ if ( !entry ) {
+ goto loser;
+ }
+
+ entry->common.arena = arena;
+ entry->common.version = CERT_DB_FILE_VERSION;
+ entry->common.type = certDBEntryTypeCert;
+ entry->common.flags = 0;
+ entry->trust.sslFlags = buf[0];
+ entry->trust.emailFlags = buf[1];
+ entry->trust.objectSigningFlags = buf[2];
+
+ entry->derCert.data = (unsigned char *)PORT_ArenaAlloc(arena, certlen);
+ if ( !entry->derCert.data ) {
+ goto loser;
+ }
+ entry->derCert.len = certlen;
+ PORT_Memcpy(entry->derCert.data, &buf[DBCERT_V4_HEADER_LEN], certlen);
+
+ if ( nnlen ) {
+ entry->nickname = (char *) PORT_ArenaAlloc(arena, nnlen);
+ if ( !entry->nickname ) {
+ goto loser;
+ }
+ PORT_Memcpy(entry->nickname, &buf[DBCERT_V4_HEADER_LEN + certlen], nnlen);
+
+ if (PORT_Strcmp(entry->nickname, "Server-Cert") == 0) {
+ entry->trust.sslFlags |= CERTDB_USER;
+ }
+ } else {
+ entry->nickname = 0;
+ }
+
+ return(entry);
+
+loser:
+ PORT_FreeArena(arena, PR_FALSE);
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return(0);
+}
+
+/*
+ * Encode a Certificate database entry into byte stream suitable for
+ * the database
+ */
+static SECStatus
+WriteDBCertEntry(NSSLOWCERTCertDBHandle *handle, certDBEntryCert *entry)
+{
+ SECItem dbitem, dbkey;
+ PRArenaPool *tmparena = NULL;
+ SECItem tmpitem;
+ SECStatus rv;
+
+ tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( tmparena == NULL ) {
+ goto loser;
+ }
+
+ rv = EncodeDBCertEntry(entry, tmparena, &dbitem);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ /* get the database key and format it */
+ rv = nsslowcert_KeyFromDERCert(tmparena, &entry->derCert, &tmpitem);
+ if ( rv == SECFailure ) {
+ goto loser;
+ }
+
+ rv = EncodeDBCertKey(&tmpitem, tmparena, &dbkey);
+ if ( rv == SECFailure ) {
+ goto loser;
+ }
+
+ /* now write it to the database */
+ rv = WriteDBEntry(handle, &entry->common, &dbkey, &dbitem);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ PORT_FreeArena(tmparena, PR_FALSE);
+ return(SECSuccess);
+
+loser:
+ if ( tmparena ) {
+ PORT_FreeArena(tmparena, PR_FALSE);
+ }
+ return(SECFailure);
+}
+
+
+/*
+ * delete a certificate entry
+ */
+static SECStatus
+DeleteDBCertEntry(NSSLOWCERTCertDBHandle *handle, SECItem *certKey)
+{
+ SECItem dbkey;
+ SECStatus rv;
+
+ dbkey.data= NULL;
+ dbkey.len = 0;
+
+ rv = EncodeDBCertKey(certKey, NULL, &dbkey);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ rv = DeleteDBEntry(handle, certDBEntryTypeCert, &dbkey);
+ if ( rv == SECFailure ) {
+ goto loser;
+ }
+
+ if (dbkey.data) {
+ PORT_Free(dbkey.data);
+ }
+ return(SECSuccess);
+
+loser:
+ if (dbkey.data) {
+ PORT_Free(dbkey.data);
+ }
+ return(SECFailure);
+}
+
+static certDBEntryCert *
+CreateCertEntry(void)
+{
+ certDBEntryCert *entry;
+
+ nsslowcert_LockFreeList();
+ entry = entryListHead;
+ if (entry) {
+ entryListCount--;
+ entryListHead = entry->next;
+ }
+ PORT_Assert(entryListCount >= 0);
+ nsslowcert_UnlockFreeList();
+ if (entry) {
+ return entry;
+ }
+
+ return PORT_ZNew(certDBEntryCert);
+}
+
+static void
+DestroyCertEntryFreeList(void)
+{
+ certDBEntryCert *entry;
+
+ nsslowcert_LockFreeList();
+ while (NULL != (entry = entryListHead)) {
+ entryListCount--;
+ entryListHead = entry->next;
+ PORT_Free(entry);
+ }
+ PORT_Assert(!entryListCount);
+ entryListCount = 0;
+ nsslowcert_UnlockFreeList();
+}
+
+/*
+ * Read a certificate entry
+ */
+static certDBEntryCert *
+ReadDBCertEntry(NSSLOWCERTCertDBHandle *handle, const SECItem *certKey)
+{
+ certDBEntryCert *entry;
+ SECItem dbkey;
+ SECItem dbentry;
+ SECStatus rv;
+ unsigned char buf[512];
+
+ dbkey.data = buf;
+ dbkey.len = sizeof(buf);
+
+ entry = CreateCertEntry();
+ if ( entry == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+ entry->common.arena = NULL;
+ entry->common.type = certDBEntryTypeCert;
+
+ rv = EncodeDBCertKey(certKey, NULL, &dbkey);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ rv = ReadDBEntry(handle, &entry->common, &dbkey, &dbentry, NULL);
+ if ( rv == SECFailure ) {
+ goto loser;
+ }
+
+ rv = DecodeDBCertEntry(entry, &dbentry);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ pkcs11_freeStaticData(dbkey.data,buf);
+ dbkey.data = NULL;
+ return(entry);
+
+loser:
+ pkcs11_freeStaticData(dbkey.data,buf);
+ dbkey.data = NULL;
+ if ( entry ) {
+ DestroyDBEntry((certDBEntry *)entry);
+ }
+
+ return(NULL);
+}
+
+/*
+ * encode a database cert record
+ */
+static SECStatus
+EncodeDBCrlEntry(certDBEntryRevocation *entry, PRArenaPool *arena, SECItem *dbitem)
+{
+ unsigned int nnlen = 0;
+ unsigned char *buf;
+
+ if (entry->url) {
+ nnlen = PORT_Strlen(entry->url) + 1;
+ }
+
+ /* allocate space for encoded database record, including space
+ * for low level header
+ */
+ dbitem->len = entry->derCrl.len + nnlen
+ + SEC_DB_ENTRY_HEADER_LEN + DB_CRL_ENTRY_HEADER_LEN;
+
+ dbitem->data = (unsigned char *)PORT_ArenaAlloc(arena, dbitem->len);
+ if ( dbitem->data == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ /* fill in database record */
+ buf = &dbitem->data[SEC_DB_ENTRY_HEADER_LEN];
+
+ buf[0] = ( entry->derCrl.len >> 8 ) & 0xff;
+ buf[1] = entry->derCrl.len & 0xff;
+ buf[2] = ( nnlen >> 8 ) & 0xff;
+ buf[3] = nnlen & 0xff;
+
+ PORT_Memcpy(&buf[DB_CRL_ENTRY_HEADER_LEN], entry->derCrl.data,
+ entry->derCrl.len);
+
+ if (nnlen != 0) {
+ PORT_Memcpy(&buf[DB_CRL_ENTRY_HEADER_LEN + entry->derCrl.len],
+ entry->url, nnlen);
+ }
+
+ return(SECSuccess);
+
+loser:
+ return(SECFailure);
+}
+
+static SECStatus
+DecodeDBCrlEntry(certDBEntryRevocation *entry, SECItem *dbentry)
+{
+ unsigned int nnlen;
+
+ /* is record long enough for header? */
+ if ( dbentry->len < DB_CRL_ENTRY_HEADER_LEN ) {
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ goto loser;
+ }
+
+ /* is database entry correct length? */
+ entry->derCrl.len = ( ( dbentry->data[0] << 8 ) | dbentry->data[1] );
+ nnlen = ( ( dbentry->data[2] << 8 ) | dbentry->data[3] );
+ if ( ( entry->derCrl.len + nnlen + DB_CRL_ENTRY_HEADER_LEN )
+ != dbentry->len) {
+ /* CRL entry is greater than 64 K. Hack to make this continue to work */
+ if (dbentry->len >= (0xffff - DB_CRL_ENTRY_HEADER_LEN) - nnlen) {
+ entry->derCrl.len =
+ (dbentry->len - DB_CRL_ENTRY_HEADER_LEN) - nnlen;
+ } else {
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ goto loser;
+ }
+ }
+
+ /* copy the dercert */
+ entry->derCrl.data = (unsigned char *)PORT_ArenaAlloc(entry->common.arena,
+ entry->derCrl.len);
+ if ( entry->derCrl.data == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+ PORT_Memcpy(entry->derCrl.data, &dbentry->data[DB_CRL_ENTRY_HEADER_LEN],
+ entry->derCrl.len);
+
+ /* copy the url */
+ entry->url = NULL;
+ if (nnlen != 0) {
+ entry->url = (char *)PORT_ArenaAlloc(entry->common.arena, nnlen);
+ if ( entry->url == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+ PORT_Memcpy(entry->url,
+ &dbentry->data[DB_CRL_ENTRY_HEADER_LEN + entry->derCrl.len],
+ nnlen);
+ }
+
+ return(SECSuccess);
+loser:
+ return(SECFailure);
+}
+
+/*
+ * Create a new certDBEntryRevocation from existing data
+ */
+static certDBEntryRevocation *
+NewDBCrlEntry(SECItem *derCrl, char * url, certDBEntryType crlType, int flags)
+{
+ certDBEntryRevocation *entry;
+ PRArenaPool *arena = NULL;
+ int nnlen;
+
+ arena = PORT_NewArena( DER_DEFAULT_CHUNKSIZE );
+
+ if ( !arena ) {
+ goto loser;
+ }
+
+ entry = PORT_ArenaZNew(arena, certDBEntryRevocation);
+ if ( entry == NULL ) {
+ goto loser;
+ }
+
+ /* fill in the dbRevolcation */
+ entry->common.arena = arena;
+ entry->common.type = crlType;
+ entry->common.version = CERT_DB_FILE_VERSION;
+ entry->common.flags = flags;
+
+
+ entry->derCrl.data = (unsigned char *)PORT_ArenaAlloc(arena, derCrl->len);
+ if ( !entry->derCrl.data ) {
+ goto loser;
+ }
+
+ if (url) {
+ nnlen = PORT_Strlen(url) + 1;
+ entry->url = (char *)PORT_ArenaAlloc(arena, nnlen);
+ if ( !entry->url ) {
+ goto loser;
+ }
+ PORT_Memcpy(entry->url, url, nnlen);
+ } else {
+ entry->url = NULL;
+ }
+
+
+ entry->derCrl.len = derCrl->len;
+ PORT_Memcpy(entry->derCrl.data, derCrl->data, derCrl->len);
+
+ return(entry);
+
+loser:
+
+ /* allocation error, free arena and return */
+ if ( arena ) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return(0);
+}
+
+
+static SECStatus
+WriteDBCrlEntry(NSSLOWCERTCertDBHandle *handle, certDBEntryRevocation *entry,
+ SECItem *crlKey )
+{
+ SECItem dbkey;
+ PRArenaPool *tmparena = NULL;
+ SECItem encodedEntry;
+ SECStatus rv;
+
+ tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( tmparena == NULL ) {
+ goto loser;
+ }
+
+ rv = EncodeDBCrlEntry(entry, tmparena, &encodedEntry);
+ if ( rv == SECFailure ) {
+ goto loser;
+ }
+
+ rv = EncodeDBGenericKey(crlKey, tmparena, &dbkey, entry->common.type);
+ if ( rv == SECFailure ) {
+ goto loser;
+ }
+
+ /* now write it to the database */
+ rv = WriteDBEntry(handle, &entry->common, &dbkey, &encodedEntry);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ PORT_FreeArena(tmparena, PR_FALSE);
+ return(SECSuccess);
+
+loser:
+ if ( tmparena ) {
+ PORT_FreeArena(tmparena, PR_FALSE);
+ }
+ return(SECFailure);
+}
+/*
+ * delete a crl entry
+ */
+static SECStatus
+DeleteDBCrlEntry(NSSLOWCERTCertDBHandle *handle, const SECItem *crlKey,
+ certDBEntryType crlType)
+{
+ SECItem dbkey;
+ PRArenaPool *arena = NULL;
+ SECStatus rv;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( arena == NULL ) {
+ goto loser;
+ }
+
+ rv = EncodeDBGenericKey(crlKey, arena, &dbkey, crlType);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ rv = DeleteDBEntry(handle, crlType, &dbkey);
+ if ( rv == SECFailure ) {
+ goto loser;
+ }
+
+ PORT_FreeArena(arena, PR_FALSE);
+ return(SECSuccess);
+
+loser:
+ if ( arena ) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+
+ return(SECFailure);
+}
+
+/*
+ * Read a certificate entry
+ */
+static certDBEntryRevocation *
+ReadDBCrlEntry(NSSLOWCERTCertDBHandle *handle, SECItem *certKey,
+ certDBEntryType crlType)
+{
+ PRArenaPool *arena = NULL;
+ PRArenaPool *tmparena = NULL;
+ certDBEntryRevocation *entry;
+ SECItem dbkey;
+ SECItem dbentry;
+ SECStatus rv;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( arena == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( tmparena == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ entry = (certDBEntryRevocation *)
+ PORT_ArenaAlloc(arena, sizeof(certDBEntryRevocation));
+ if ( entry == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+ entry->common.arena = arena;
+ entry->common.type = crlType;
+
+ rv = EncodeDBGenericKey(certKey, tmparena, &dbkey, crlType);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ rv = ReadDBEntry(handle, &entry->common, &dbkey, &dbentry, NULL);
+ if ( rv == SECFailure ) {
+ goto loser;
+ }
+
+ rv = DecodeDBCrlEntry(entry, &dbentry);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ PORT_FreeArena(tmparena, PR_FALSE);
+ return(entry);
+
+loser:
+ if ( tmparena ) {
+ PORT_FreeArena(tmparena, PR_FALSE);
+ }
+ if ( arena ) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+
+ return(NULL);
+}
+
+void
+nsslowcert_DestroyDBEntry(certDBEntry *entry)
+{
+ DestroyDBEntry(entry);
+ return;
+}
+
+/*
+ * Encode a database nickname record
+ */
+static SECStatus
+EncodeDBNicknameEntry(certDBEntryNickname *entry, PRArenaPool *arena,
+ SECItem *dbitem)
+{
+ unsigned char *buf;
+
+ /* allocate space for encoded database record, including space
+ * for low level header
+ */
+ dbitem->len = entry->subjectName.len + DB_NICKNAME_ENTRY_HEADER_LEN +
+ SEC_DB_ENTRY_HEADER_LEN;
+
+ dbitem->data = (unsigned char *)PORT_ArenaAlloc(arena, dbitem->len);
+ if ( dbitem->data == NULL) {
+ goto loser;
+ }
+
+ /* fill in database record */
+ buf = &dbitem->data[SEC_DB_ENTRY_HEADER_LEN];
+
+ buf[0] = ( entry->subjectName.len >> 8 ) & 0xff;
+ buf[1] = entry->subjectName.len & 0xff;
+
+ PORT_Memcpy(&buf[DB_NICKNAME_ENTRY_HEADER_LEN], entry->subjectName.data,
+ entry->subjectName.len);
+
+ return(SECSuccess);
+
+loser:
+ return(SECFailure);
+}
+
+/*
+ * Encode a database key for a nickname record
+ */
+static SECStatus
+EncodeDBNicknameKey(char *nickname, PRArenaPool *arena,
+ SECItem *dbkey)
+{
+ unsigned int nnlen;
+
+ nnlen = PORT_Strlen(nickname) + 1; /* includes null */
+
+ /* now get the database key and format it */
+ dbkey->len = nnlen + SEC_DB_KEY_HEADER_LEN;
+ dbkey->data = (unsigned char *)PORT_ArenaAlloc(arena, dbkey->len);
+ if ( dbkey->data == NULL ) {
+ goto loser;
+ }
+ PORT_Memcpy(&dbkey->data[SEC_DB_KEY_HEADER_LEN], nickname, nnlen);
+ dbkey->data[0] = certDBEntryTypeNickname;
+
+ return(SECSuccess);
+
+loser:
+ return(SECFailure);
+}
+
+static SECStatus
+DecodeDBNicknameEntry(certDBEntryNickname *entry, SECItem *dbentry,
+ char *nickname)
+{
+ /* is record long enough for header? */
+ if ( dbentry->len < DB_NICKNAME_ENTRY_HEADER_LEN ) {
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ goto loser;
+ }
+
+ /* is database entry correct length? */
+ entry->subjectName.len = ( ( dbentry->data[0] << 8 ) | dbentry->data[1] );
+ if (( entry->subjectName.len + DB_NICKNAME_ENTRY_HEADER_LEN ) !=
+ dbentry->len ){
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ goto loser;
+ }
+
+ /* copy the certkey */
+ entry->subjectName.data =
+ (unsigned char *)PORT_ArenaAlloc(entry->common.arena,
+ entry->subjectName.len);
+ if ( entry->subjectName.data == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+ PORT_Memcpy(entry->subjectName.data,
+ &dbentry->data[DB_NICKNAME_ENTRY_HEADER_LEN],
+ entry->subjectName.len);
+ entry->subjectName.type = siBuffer;
+
+ entry->nickname = (char *)PORT_ArenaAlloc(entry->common.arena,
+ PORT_Strlen(nickname)+1);
+ if ( entry->nickname ) {
+ PORT_Strcpy(entry->nickname, nickname);
+ }
+
+ return(SECSuccess);
+
+loser:
+ return(SECFailure);
+}
+
+/*
+ * create a new nickname entry
+ */
+static certDBEntryNickname *
+NewDBNicknameEntry(char *nickname, SECItem *subjectName, unsigned int flags)
+{
+ PRArenaPool *arena = NULL;
+ certDBEntryNickname *entry;
+ int nnlen;
+ SECStatus rv;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( arena == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ entry = (certDBEntryNickname *)PORT_ArenaAlloc(arena,
+ sizeof(certDBEntryNickname));
+ if ( entry == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ /* init common fields */
+ entry->common.arena = arena;
+ entry->common.type = certDBEntryTypeNickname;
+ entry->common.version = CERT_DB_FILE_VERSION;
+ entry->common.flags = flags;
+
+ /* copy the nickname */
+ nnlen = PORT_Strlen(nickname) + 1;
+
+ entry->nickname = (char*)PORT_ArenaAlloc(arena, nnlen);
+ if ( entry->nickname == NULL ) {
+ goto loser;
+ }
+
+ PORT_Memcpy(entry->nickname, nickname, nnlen);
+
+ rv = SECITEM_CopyItem(arena, &entry->subjectName, subjectName);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ return(entry);
+loser:
+ if ( arena ) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+
+ return(NULL);
+}
+
+/*
+ * delete a nickname entry
+ */
+static SECStatus
+DeleteDBNicknameEntry(NSSLOWCERTCertDBHandle *handle, char *nickname)
+{
+ PRArenaPool *arena = NULL;
+ SECStatus rv;
+ SECItem dbkey;
+
+ if ( nickname == NULL ) {
+ return(SECSuccess);
+ }
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( arena == NULL ) {
+ goto loser;
+ }
+
+ rv = EncodeDBNicknameKey(nickname, arena, &dbkey);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ rv = DeleteDBEntry(handle, certDBEntryTypeNickname, &dbkey);
+ if ( rv == SECFailure ) {
+ goto loser;
+ }
+
+ PORT_FreeArena(arena, PR_FALSE);
+ return(SECSuccess);
+
+loser:
+ if ( arena ) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+
+ return(SECFailure);
+}
+
+/*
+ * Read a nickname entry
+ */
+static certDBEntryNickname *
+ReadDBNicknameEntry(NSSLOWCERTCertDBHandle *handle, char *nickname)
+{
+ PRArenaPool *arena = NULL;
+ PRArenaPool *tmparena = NULL;
+ certDBEntryNickname *entry;
+ SECItem dbkey;
+ SECItem dbentry;
+ SECStatus rv;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( arena == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( tmparena == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ entry = (certDBEntryNickname *)PORT_ArenaAlloc(arena,
+ sizeof(certDBEntryNickname));
+ if ( entry == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+ entry->common.arena = arena;
+ entry->common.type = certDBEntryTypeNickname;
+
+ rv = EncodeDBNicknameKey(nickname, tmparena, &dbkey);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ rv = ReadDBEntry(handle, &entry->common, &dbkey, &dbentry, tmparena);
+ if ( rv == SECFailure ) {
+ goto loser;
+ }
+
+ /* is record long enough for header? */
+ if ( dbentry.len < DB_NICKNAME_ENTRY_HEADER_LEN ) {
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ goto loser;
+ }
+
+ rv = DecodeDBNicknameEntry(entry, &dbentry, nickname);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ PORT_FreeArena(tmparena, PR_FALSE);
+ return(entry);
+
+loser:
+ if ( tmparena ) {
+ PORT_FreeArena(tmparena, PR_FALSE);
+ }
+ if ( arena ) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+
+ return(NULL);
+}
+
+/*
+ * Encode a nickname entry into byte stream suitable for
+ * the database
+ */
+static SECStatus
+WriteDBNicknameEntry(NSSLOWCERTCertDBHandle *handle, certDBEntryNickname *entry)
+{
+ SECItem dbitem, dbkey;
+ PRArenaPool *tmparena = NULL;
+ SECStatus rv;
+
+ tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( tmparena == NULL ) {
+ goto loser;
+ }
+
+ rv = EncodeDBNicknameEntry(entry, tmparena, &dbitem);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ rv = EncodeDBNicknameKey(entry->nickname, tmparena, &dbkey);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ /* now write it to the database */
+ rv = WriteDBEntry(handle, &entry->common, &dbkey, &dbitem);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ PORT_FreeArena(tmparena, PR_FALSE);
+ return(SECSuccess);
+
+loser:
+ if ( tmparena ) {
+ PORT_FreeArena(tmparena, PR_FALSE);
+ }
+ return(SECFailure);
+
+}
+
+static SECStatus
+EncodeDBSMimeEntry(certDBEntrySMime *entry, PRArenaPool *arena,
+ SECItem *dbitem)
+{
+ unsigned char *buf;
+
+ /* allocate space for encoded database record, including space
+ * for low level header
+ */
+ dbitem->len = entry->subjectName.len + entry->smimeOptions.len +
+ entry->optionsDate.len +
+ DB_SMIME_ENTRY_HEADER_LEN + SEC_DB_ENTRY_HEADER_LEN;
+
+ dbitem->data = (unsigned char *)PORT_ArenaAlloc(arena, dbitem->len);
+ if ( dbitem->data == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ /* fill in database record */
+ buf = &dbitem->data[SEC_DB_ENTRY_HEADER_LEN];
+
+ buf[0] = ( entry->subjectName.len >> 8 ) & 0xff;
+ buf[1] = entry->subjectName.len & 0xff;
+ buf[2] = ( entry->smimeOptions.len >> 8 ) & 0xff;
+ buf[3] = entry->smimeOptions.len & 0xff;
+ buf[4] = ( entry->optionsDate.len >> 8 ) & 0xff;
+ buf[5] = entry->optionsDate.len & 0xff;
+
+ /* if no smime options, then there should not be an options date either */
+ PORT_Assert( ! ( ( entry->smimeOptions.len == 0 ) &&
+ ( entry->optionsDate.len != 0 ) ) );
+
+ PORT_Memcpy(&buf[DB_SMIME_ENTRY_HEADER_LEN], entry->subjectName.data,
+ entry->subjectName.len);
+ if ( entry->smimeOptions.len ) {
+ PORT_Memcpy(&buf[DB_SMIME_ENTRY_HEADER_LEN+entry->subjectName.len],
+ entry->smimeOptions.data,
+ entry->smimeOptions.len);
+ PORT_Memcpy(&buf[DB_SMIME_ENTRY_HEADER_LEN + entry->subjectName.len +
+ entry->smimeOptions.len],
+ entry->optionsDate.data,
+ entry->optionsDate.len);
+ }
+
+ return(SECSuccess);
+
+loser:
+ return(SECFailure);
+}
+
+/*
+ * Encode a database key for a SMIME record
+ */
+static SECStatus
+EncodeDBSMimeKey(char *emailAddr, PRArenaPool *arena,
+ SECItem *dbkey)
+{
+ unsigned int addrlen;
+
+ addrlen = PORT_Strlen(emailAddr) + 1; /* includes null */
+
+ /* now get the database key and format it */
+ dbkey->len = addrlen + SEC_DB_KEY_HEADER_LEN;
+ dbkey->data = (unsigned char *)PORT_ArenaAlloc(arena, dbkey->len);
+ if ( dbkey->data == NULL ) {
+ goto loser;
+ }
+ PORT_Memcpy(&dbkey->data[SEC_DB_KEY_HEADER_LEN], emailAddr, addrlen);
+ dbkey->data[0] = certDBEntryTypeSMimeProfile;
+
+ return(SECSuccess);
+
+loser:
+ return(SECFailure);
+}
+
+/*
+ * Decode a database SMIME record
+ */
+static SECStatus
+DecodeDBSMimeEntry(certDBEntrySMime *entry, SECItem *dbentry, char *emailAddr)
+{
+ /* is record long enough for header? */
+ if ( dbentry->len < DB_SMIME_ENTRY_HEADER_LEN ) {
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ goto loser;
+ }
+
+ /* is database entry correct length? */
+ entry->subjectName.len = ( ( dbentry->data[0] << 8 ) | dbentry->data[1] );
+ entry->smimeOptions.len = ( ( dbentry->data[2] << 8 ) | dbentry->data[3] );
+ entry->optionsDate.len = ( ( dbentry->data[4] << 8 ) | dbentry->data[5] );
+ if (( entry->subjectName.len + entry->smimeOptions.len +
+ entry->optionsDate.len + DB_SMIME_ENTRY_HEADER_LEN ) != dbentry->len){
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ goto loser;
+ }
+
+ /* copy the subject name */
+ entry->subjectName.data =
+ (unsigned char *)PORT_ArenaAlloc(entry->common.arena,
+ entry->subjectName.len);
+ if ( entry->subjectName.data == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+ PORT_Memcpy(entry->subjectName.data,
+ &dbentry->data[DB_SMIME_ENTRY_HEADER_LEN],
+ entry->subjectName.len);
+
+ /* copy the smime options */
+ if ( entry->smimeOptions.len ) {
+ entry->smimeOptions.data =
+ (unsigned char *)PORT_ArenaAlloc(entry->common.arena,
+ entry->smimeOptions.len);
+ if ( entry->smimeOptions.data == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+ PORT_Memcpy(entry->smimeOptions.data,
+ &dbentry->data[DB_SMIME_ENTRY_HEADER_LEN +
+ entry->subjectName.len],
+ entry->smimeOptions.len);
+ }
+ if ( entry->optionsDate.len ) {
+ entry->optionsDate.data =
+ (unsigned char *)PORT_ArenaAlloc(entry->common.arena,
+ entry->optionsDate.len);
+ if ( entry->optionsDate.data == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+ PORT_Memcpy(entry->optionsDate.data,
+ &dbentry->data[DB_SMIME_ENTRY_HEADER_LEN +
+ entry->subjectName.len +
+ entry->smimeOptions.len],
+ entry->optionsDate.len);
+ }
+
+ /* both options and options date must either exist or not exist */
+ if ( ( ( entry->optionsDate.len == 0 ) ||
+ ( entry->smimeOptions.len == 0 ) ) &&
+ entry->smimeOptions.len != entry->optionsDate.len ) {
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ goto loser;
+ }
+
+ entry->emailAddr = (char *)PORT_ArenaAlloc(entry->common.arena,
+ PORT_Strlen(emailAddr)+1);
+ if ( entry->emailAddr ) {
+ PORT_Strcpy(entry->emailAddr, emailAddr);
+ }
+
+ return(SECSuccess);
+
+loser:
+ return(SECFailure);
+}
+
+/*
+ * create a new SMIME entry
+ */
+static certDBEntrySMime *
+NewDBSMimeEntry(char *emailAddr, SECItem *subjectName, SECItem *smimeOptions,
+ SECItem *optionsDate, unsigned int flags)
+{
+ PRArenaPool *arena = NULL;
+ certDBEntrySMime *entry;
+ int addrlen;
+ SECStatus rv;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( arena == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ entry = (certDBEntrySMime *)PORT_ArenaAlloc(arena,
+ sizeof(certDBEntrySMime));
+ if ( entry == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ /* init common fields */
+ entry->common.arena = arena;
+ entry->common.type = certDBEntryTypeSMimeProfile;
+ entry->common.version = CERT_DB_FILE_VERSION;
+ entry->common.flags = flags;
+
+ /* copy the email addr */
+ addrlen = PORT_Strlen(emailAddr) + 1;
+
+ entry->emailAddr = (char*)PORT_ArenaAlloc(arena, addrlen);
+ if ( entry->emailAddr == NULL ) {
+ goto loser;
+ }
+
+ PORT_Memcpy(entry->emailAddr, emailAddr, addrlen);
+
+ /* copy the subject name */
+ rv = SECITEM_CopyItem(arena, &entry->subjectName, subjectName);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ /* copy the smime options */
+ if ( smimeOptions ) {
+ rv = SECITEM_CopyItem(arena, &entry->smimeOptions, smimeOptions);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+ } else {
+ PORT_Assert(optionsDate == NULL);
+ entry->smimeOptions.data = NULL;
+ entry->smimeOptions.len = 0;
+ }
+
+ /* copy the options date */
+ if ( optionsDate ) {
+ rv = SECITEM_CopyItem(arena, &entry->optionsDate, optionsDate);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+ } else {
+ PORT_Assert(smimeOptions == NULL);
+ entry->optionsDate.data = NULL;
+ entry->optionsDate.len = 0;
+ }
+
+ return(entry);
+loser:
+ if ( arena ) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+
+ return(NULL);
+}
+
+/*
+ * delete a SMIME entry
+ */
+static SECStatus
+DeleteDBSMimeEntry(NSSLOWCERTCertDBHandle *handle, char *emailAddr)
+{
+ PRArenaPool *arena = NULL;
+ SECStatus rv;
+ SECItem dbkey;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( arena == NULL ) {
+ goto loser;
+ }
+
+ rv = EncodeDBSMimeKey(emailAddr, arena, &dbkey);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ rv = DeleteDBEntry(handle, certDBEntryTypeSMimeProfile, &dbkey);
+ if ( rv == SECFailure ) {
+ goto loser;
+ }
+
+ PORT_FreeArena(arena, PR_FALSE);
+ return(SECSuccess);
+
+loser:
+ if ( arena ) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+
+ return(SECFailure);
+}
+
+/*
+ * Read a SMIME entry
+ */
+certDBEntrySMime *
+nsslowcert_ReadDBSMimeEntry(NSSLOWCERTCertDBHandle *handle, char *emailAddr)
+{
+ PRArenaPool *arena = NULL;
+ PRArenaPool *tmparena = NULL;
+ certDBEntrySMime *entry;
+ SECItem dbkey;
+ SECItem dbentry;
+ SECStatus rv;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( arena == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( tmparena == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ entry = (certDBEntrySMime *)PORT_ArenaAlloc(arena,
+ sizeof(certDBEntrySMime));
+ if ( entry == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+ entry->common.arena = arena;
+ entry->common.type = certDBEntryTypeSMimeProfile;
+
+ rv = EncodeDBSMimeKey(emailAddr, tmparena, &dbkey);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ rv = ReadDBEntry(handle, &entry->common, &dbkey, &dbentry, tmparena);
+ if ( rv == SECFailure ) {
+ goto loser;
+ }
+
+ /* is record long enough for header? */
+ if ( dbentry.len < DB_SMIME_ENTRY_HEADER_LEN ) {
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ goto loser;
+ }
+
+ rv = DecodeDBSMimeEntry(entry, &dbentry, emailAddr);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ PORT_FreeArena(tmparena, PR_FALSE);
+ return(entry);
+
+loser:
+ if ( tmparena ) {
+ PORT_FreeArena(tmparena, PR_FALSE);
+ }
+ if ( arena ) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+
+ return(NULL);
+}
+
+/*
+ * Encode a SMIME entry into byte stream suitable for
+ * the database
+ */
+static SECStatus
+WriteDBSMimeEntry(NSSLOWCERTCertDBHandle *handle, certDBEntrySMime *entry)
+{
+ SECItem dbitem, dbkey;
+ PRArenaPool *tmparena = NULL;
+ SECStatus rv;
+
+ tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( tmparena == NULL ) {
+ goto loser;
+ }
+
+ rv = EncodeDBSMimeEntry(entry, tmparena, &dbitem);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ rv = EncodeDBSMimeKey(entry->emailAddr, tmparena, &dbkey);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ /* now write it to the database */
+ rv = WriteDBEntry(handle, &entry->common, &dbkey, &dbitem);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ PORT_FreeArena(tmparena, PR_FALSE);
+ return(SECSuccess);
+
+loser:
+ if ( tmparena ) {
+ PORT_FreeArena(tmparena, PR_FALSE);
+ }
+ return(SECFailure);
+
+}
+
+/*
+ * Encode a database subject record
+ */
+static SECStatus
+EncodeDBSubjectEntry(certDBEntrySubject *entry, PRArenaPool *arena,
+ SECItem *dbitem)
+{
+ unsigned char *buf;
+ int len;
+ unsigned int ncerts;
+ unsigned int i;
+ unsigned char *tmpbuf;
+ unsigned int nnlen = 0;
+ unsigned int eaddrslen = 0;
+ int keyidoff;
+ SECItem *certKeys;
+ SECItem *keyIDs;
+
+ if ( entry->nickname ) {
+ nnlen = PORT_Strlen(entry->nickname) + 1;
+ }
+ if ( entry->emailAddrs ) {
+ eaddrslen = 2;
+ for (i=0; i < entry->nemailAddrs; i++) {
+ eaddrslen += PORT_Strlen(entry->emailAddrs[i]) + 1 + 2;
+ }
+ }
+
+ ncerts = entry->ncerts;
+
+ /* compute the length of the entry */
+ keyidoff = DB_SUBJECT_ENTRY_HEADER_LEN + nnlen ;
+ len = keyidoff + 4 * ncerts + eaddrslen;
+ for ( i = 0; i < ncerts; i++ ) {
+ len += entry->certKeys[i].len;
+ len += entry->keyIDs[i].len;
+ }
+
+ /* allocate space for encoded database record, including space
+ * for low level header
+ */
+ dbitem->len = len + SEC_DB_ENTRY_HEADER_LEN;
+
+ dbitem->data = (unsigned char *)PORT_ArenaAlloc(arena, dbitem->len);
+ if ( dbitem->data == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ /* fill in database record */
+ buf = &dbitem->data[SEC_DB_ENTRY_HEADER_LEN];
+
+ buf[0] = ( ncerts >> 8 ) & 0xff;
+ buf[1] = ncerts & 0xff;
+ buf[2] = ( nnlen >> 8 ) & 0xff;
+ buf[3] = nnlen & 0xff;
+ /* v7 email field is NULL in v8 */
+ buf[4] = 0;
+ buf[5] = 0;
+
+ PORT_Memcpy(&buf[DB_SUBJECT_ENTRY_HEADER_LEN], entry->nickname, nnlen);
+
+ for ( i = 0; i < ncerts; i++ ) {
+
+ certKeys = entry->certKeys;
+ keyIDs = entry->keyIDs;
+
+ buf[keyidoff+i*2] = ( certKeys[i].len >> 8 ) & 0xff;
+ buf[keyidoff+1+i*2] = certKeys[i].len & 0xff;
+ buf[keyidoff+ncerts*2+i*2] = ( keyIDs[i].len >> 8 ) & 0xff;
+ buf[keyidoff+1+ncerts*2+i*2] = keyIDs[i].len & 0xff;
+ }
+
+ /* temp pointer used to stuff certkeys and keyids into the buffer */
+ tmpbuf = &buf[keyidoff+ncerts*4];
+
+ for ( i = 0; i < ncerts; i++ ) {
+ certKeys = entry->certKeys;
+ PORT_Memcpy(tmpbuf, certKeys[i].data, certKeys[i].len);
+ tmpbuf = tmpbuf + certKeys[i].len;
+ }
+
+ for ( i = 0; i < ncerts; i++ ) {
+ keyIDs = entry->keyIDs;
+ PORT_Memcpy(tmpbuf, keyIDs[i].data, keyIDs[i].len);
+ tmpbuf = tmpbuf + keyIDs[i].len;
+ }
+
+ if (entry->emailAddrs) {
+ tmpbuf[0] = (entry->nemailAddrs >> 8) & 0xff;
+ tmpbuf[1] = entry->nemailAddrs & 0xff;
+ tmpbuf += 2;
+ for (i=0; i < entry->nemailAddrs; i++) {
+ int nameLen = PORT_Strlen(entry->emailAddrs[i]) + 1;
+ tmpbuf[0] = (nameLen >> 8) & 0xff;
+ tmpbuf[1] = nameLen & 0xff;
+ tmpbuf += 2;
+ PORT_Memcpy(tmpbuf,entry->emailAddrs[i],nameLen);
+ tmpbuf +=nameLen;
+ }
+ }
+
+ PORT_Assert(tmpbuf == &buf[len]);
+
+ return(SECSuccess);
+
+loser:
+ return(SECFailure);
+}
+
+/*
+ * Encode a database key for a subject record
+ */
+static SECStatus
+EncodeDBSubjectKey(SECItem *derSubject, PRArenaPool *arena,
+ SECItem *dbkey)
+{
+ dbkey->len = derSubject->len + SEC_DB_KEY_HEADER_LEN;
+ dbkey->data = (unsigned char *)PORT_ArenaAlloc(arena, dbkey->len);
+ if ( dbkey->data == NULL ) {
+ goto loser;
+ }
+ PORT_Memcpy(&dbkey->data[SEC_DB_KEY_HEADER_LEN], derSubject->data,
+ derSubject->len);
+ dbkey->data[0] = certDBEntryTypeSubject;
+
+ return(SECSuccess);
+
+loser:
+ return(SECFailure);
+}
+
+static SECStatus
+DecodeDBSubjectEntry(certDBEntrySubject *entry, SECItem *dbentry,
+ const SECItem *derSubject)
+{
+ unsigned int ncerts;
+ PRArenaPool *arena;
+ unsigned int len, itemlen;
+ unsigned char *tmpbuf;
+ unsigned char *end;
+ unsigned int i;
+ SECStatus rv;
+ unsigned int keyidoff;
+ unsigned int nnlen, eaddrlen;
+ unsigned int stdlen;
+
+ arena = entry->common.arena;
+
+ rv = SECITEM_CopyItem(arena, &entry->derSubject, derSubject);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ /* is record long enough for header? */
+ if ( dbentry->len < DB_SUBJECT_ENTRY_HEADER_LEN ) {
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ goto loser;
+ }
+
+ entry->ncerts = ncerts = ( ( dbentry->data[0] << 8 ) | dbentry->data[1] );
+ nnlen = ( ( dbentry->data[2] << 8 ) | dbentry->data[3] );
+ eaddrlen = ( ( dbentry->data[4] << 8 ) | dbentry->data[5] );
+ stdlen = ncerts * 4 + DB_SUBJECT_ENTRY_HEADER_LEN + nnlen + eaddrlen;
+ if ( dbentry->len < stdlen) {
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ goto loser;
+ }
+
+ entry->certKeys = (SECItem *)PORT_ArenaAlloc(arena,
+ sizeof(SECItem) * ncerts);
+ entry->keyIDs = (SECItem *)PORT_ArenaAlloc(arena,
+ sizeof(SECItem) * ncerts);
+
+ if ( ( entry->certKeys == NULL ) || ( entry->keyIDs == NULL ) ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ if ( nnlen > 1 ) { /* null terminator is stored */
+ entry->nickname = (char *)PORT_ArenaAlloc(arena, nnlen);
+ if ( entry->nickname == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+ PORT_Memcpy(entry->nickname,
+ &dbentry->data[DB_SUBJECT_ENTRY_HEADER_LEN],
+ nnlen);
+ } else {
+ entry->nickname = NULL;
+ }
+
+ /* if we have an old style email entry, there is only one */
+ entry->nemailAddrs = 0;
+ if ( eaddrlen > 1 ) { /* null terminator is stored */
+ entry->emailAddrs = (char **)PORT_ArenaAlloc(arena, sizeof(char *));
+ if ( entry->emailAddrs == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+ entry->emailAddrs[0] = (char *)PORT_ArenaAlloc(arena, eaddrlen);
+ if ( entry->emailAddrs[0] == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+ PORT_Memcpy(entry->emailAddrs[0],
+ &dbentry->data[DB_SUBJECT_ENTRY_HEADER_LEN+nnlen],
+ eaddrlen);
+ entry->nemailAddrs = 1;
+ } else {
+ entry->emailAddrs = NULL;
+ }
+
+ /* collect the lengths of the certKeys and keyIDs, and total the
+ * overall length.
+ */
+ keyidoff = DB_SUBJECT_ENTRY_HEADER_LEN + nnlen + eaddrlen;
+ len = keyidoff + 4 * ncerts;
+
+ tmpbuf = &dbentry->data[0];
+
+ for ( i = 0; i < ncerts; i++ ) {
+
+ itemlen = ( tmpbuf[keyidoff + 2*i] << 8 ) | tmpbuf[keyidoff + 1 + 2*i] ;
+ len += itemlen;
+ entry->certKeys[i].len = itemlen;
+
+ itemlen = ( tmpbuf[keyidoff + 2*ncerts + 2*i] << 8 ) |
+ tmpbuf[keyidoff + 1 + 2*ncerts + 2*i] ;
+ len += itemlen;
+ entry->keyIDs[i].len = itemlen;
+ }
+
+ /* is database entry correct length? */
+ if ( len > dbentry->len ){
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ goto loser;
+ }
+
+ tmpbuf = &tmpbuf[keyidoff + 4*ncerts];
+ for ( i = 0; i < ncerts; i++ ) {
+ entry->certKeys[i].data =
+ (unsigned char *)PORT_ArenaAlloc(arena, entry->certKeys[i].len);
+ if ( entry->certKeys[i].data == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+ PORT_Memcpy(entry->certKeys[i].data, tmpbuf, entry->certKeys[i].len);
+ tmpbuf = &tmpbuf[entry->certKeys[i].len];
+ }
+
+ for ( i = 0; i < ncerts; i++ ) {
+ entry->keyIDs[i].data =
+ (unsigned char *)PORT_ArenaAlloc(arena, entry->keyIDs[i].len);
+ if ( entry->keyIDs[i].data == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+ PORT_Memcpy(entry->keyIDs[i].data, tmpbuf, entry->keyIDs[i].len);
+ tmpbuf = &tmpbuf[entry->keyIDs[i].len];
+ }
+
+ end = &dbentry->data[dbentry->len];
+ if ((eaddrlen == 0) && (tmpbuf+1 < end)) {
+ /* read in the additional email addresses */
+ entry->nemailAddrs = tmpbuf[0] << 8 | tmpbuf[1];
+ tmpbuf += 2;
+ entry->emailAddrs = (char **)
+ PORT_ArenaAlloc(arena, entry->nemailAddrs * sizeof(char *));
+ if (entry->emailAddrs == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+ for (i=0; i < entry->nemailAddrs; i++) {
+ int nameLen;
+ if (tmpbuf + 2 > end) {
+ goto loser;
+ }
+
+ nameLen = tmpbuf[0] << 8 | tmpbuf[1];
+ entry->emailAddrs[i] = PORT_ArenaAlloc(arena,nameLen);
+ if (entry->emailAddrs == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+ if (tmpbuf + (nameLen+2) > end) {
+ goto loser;
+ }
+ PORT_Memcpy(entry->emailAddrs[i],&tmpbuf[2],nameLen);
+ tmpbuf += 2 + nameLen;
+ }
+ }
+
+
+ return(SECSuccess);
+
+loser:
+ return(SECFailure);
+}
+
+/*
+ * create a new subject entry with a single cert
+ */
+static certDBEntrySubject *
+NewDBSubjectEntry(SECItem *derSubject, SECItem *certKey,
+ SECItem *keyID, char *nickname, char *emailAddr,
+ unsigned int flags)
+{
+ PRArenaPool *arena = NULL;
+ certDBEntrySubject *entry;
+ SECStatus rv;
+ unsigned int nnlen;
+ unsigned int eaddrlen;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( arena == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ entry = (certDBEntrySubject *)PORT_ArenaAlloc(arena,
+ sizeof(certDBEntrySubject));
+ if ( entry == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ /* init common fields */
+ entry->common.arena = arena;
+ entry->common.type = certDBEntryTypeSubject;
+ entry->common.version = CERT_DB_FILE_VERSION;
+ entry->common.flags = flags;
+
+ /* copy the subject */
+ rv = SECITEM_CopyItem(arena, &entry->derSubject, derSubject);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ entry->ncerts = 1;
+ entry->nemailAddrs = 0;
+ /* copy nickname */
+ if ( nickname && ( *nickname != '\0' ) ) {
+ nnlen = PORT_Strlen(nickname) + 1;
+ entry->nickname = (char *)PORT_ArenaAlloc(arena, nnlen);
+ if ( entry->nickname == NULL ) {
+ goto loser;
+ }
+
+ PORT_Memcpy(entry->nickname, nickname, nnlen);
+ } else {
+ entry->nickname = NULL;
+ }
+
+ /* copy email addr */
+ if ( emailAddr && ( *emailAddr != '\0' ) ) {
+ emailAddr = nsslowcert_FixupEmailAddr(emailAddr);
+ if ( emailAddr == NULL ) {
+ entry->emailAddrs = NULL;
+ goto loser;
+ }
+
+ eaddrlen = PORT_Strlen(emailAddr) + 1;
+ entry->emailAddrs = (char **)PORT_ArenaAlloc(arena, sizeof(char *));
+ if ( entry->emailAddrs == NULL ) {
+ PORT_Free(emailAddr);
+ goto loser;
+ }
+ entry->emailAddrs[0] = PORT_ArenaStrdup(arena,emailAddr);
+ if (entry->emailAddrs[0]) {
+ entry->nemailAddrs = 1;
+ }
+
+ PORT_Free(emailAddr);
+ } else {
+ entry->emailAddrs = NULL;
+ }
+
+ /* allocate space for certKeys and keyIDs */
+ entry->certKeys = (SECItem *)PORT_ArenaAlloc(arena, sizeof(SECItem));
+ entry->keyIDs = (SECItem *)PORT_ArenaAlloc(arena, sizeof(SECItem));
+ if ( ( entry->certKeys == NULL ) || ( entry->keyIDs == NULL ) ) {
+ goto loser;
+ }
+
+ /* copy the certKey and keyID */
+ rv = SECITEM_CopyItem(arena, &entry->certKeys[0], certKey);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+ rv = SECITEM_CopyItem(arena, &entry->keyIDs[0], keyID);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ return(entry);
+loser:
+ if ( arena ) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+
+ return(NULL);
+}
+
+/*
+ * delete a subject entry
+ */
+static SECStatus
+DeleteDBSubjectEntry(NSSLOWCERTCertDBHandle *handle, SECItem *derSubject)
+{
+ SECItem dbkey;
+ PRArenaPool *arena = NULL;
+ SECStatus rv;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( arena == NULL ) {
+ goto loser;
+ }
+
+ rv = EncodeDBSubjectKey(derSubject, arena, &dbkey);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ rv = DeleteDBEntry(handle, certDBEntryTypeSubject, &dbkey);
+ if ( rv == SECFailure ) {
+ goto loser;
+ }
+
+ PORT_FreeArena(arena, PR_FALSE);
+ return(SECSuccess);
+
+loser:
+ if ( arena ) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+
+ return(SECFailure);
+}
+
+/*
+ * Read the subject entry
+ */
+static certDBEntrySubject *
+ReadDBSubjectEntry(NSSLOWCERTCertDBHandle *handle, SECItem *derSubject)
+{
+ PRArenaPool *arena = NULL;
+ PRArenaPool *tmparena = NULL;
+ certDBEntrySubject *entry;
+ SECItem dbkey;
+ SECItem dbentry;
+ SECStatus rv;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( arena == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( tmparena == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ entry = (certDBEntrySubject *)PORT_ArenaAlloc(arena,
+ sizeof(certDBEntrySubject));
+ if ( entry == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+ entry->common.arena = arena;
+ entry->common.type = certDBEntryTypeSubject;
+
+ rv = EncodeDBSubjectKey(derSubject, tmparena, &dbkey);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ rv = ReadDBEntry(handle, &entry->common, &dbkey, &dbentry, tmparena);
+ if ( rv == SECFailure ) {
+ goto loser;
+ }
+
+ rv = DecodeDBSubjectEntry(entry, &dbentry, derSubject);
+ if ( rv == SECFailure ) {
+ goto loser;
+ }
+
+ PORT_FreeArena(tmparena, PR_FALSE);
+ return(entry);
+
+loser:
+ if ( tmparena ) {
+ PORT_FreeArena(tmparena, PR_FALSE);
+ }
+ if ( arena ) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+
+ return(NULL);
+}
+
+/*
+ * Encode a subject name entry into byte stream suitable for
+ * the database
+ */
+static SECStatus
+WriteDBSubjectEntry(NSSLOWCERTCertDBHandle *handle, certDBEntrySubject *entry)
+{
+ SECItem dbitem, dbkey;
+ PRArenaPool *tmparena = NULL;
+ SECStatus rv;
+
+ tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( tmparena == NULL ) {
+ goto loser;
+ }
+
+ rv = EncodeDBSubjectEntry(entry, tmparena, &dbitem);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ rv = EncodeDBSubjectKey(&entry->derSubject, tmparena, &dbkey);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ /* now write it to the database */
+ rv = WriteDBEntry(handle, &entry->common, &dbkey, &dbitem);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ PORT_FreeArena(tmparena, PR_FALSE);
+ return(SECSuccess);
+
+loser:
+ if ( tmparena ) {
+ PORT_FreeArena(tmparena, PR_FALSE);
+ }
+ return(SECFailure);
+
+}
+
+typedef enum { nsslowcert_remove, nsslowcert_add } nsslowcertUpdateType;
+
+static SECStatus
+nsslowcert_UpdateSubjectEmailAddr(NSSLOWCERTCertDBHandle *dbhandle,
+ SECItem *derSubject, char *emailAddr, nsslowcertUpdateType updateType)
+{
+ certDBEntrySubject *entry = NULL;
+ int index = -1, i;
+ SECStatus rv;
+
+ if (emailAddr) {
+ emailAddr = nsslowcert_FixupEmailAddr(emailAddr);
+ if (emailAddr == NULL) {
+ return SECFailure;
+ }
+ } else {
+ return SECSuccess;
+ }
+
+ entry = ReadDBSubjectEntry(dbhandle,derSubject);
+ if (entry == NULL) {
+ rv = SECFailure;
+ goto done;
+ }
+
+ for (i=0; i < (int)(entry->nemailAddrs); i++) {
+ if (PORT_Strcmp(entry->emailAddrs[i],emailAddr) == 0) {
+ index = i;
+ }
+ }
+
+ if (updateType == nsslowcert_remove) {
+ if (index == -1) {
+ rv = SECSuccess;
+ goto done;
+ }
+ entry->nemailAddrs--;
+ for (i=index; i < (int)(entry->nemailAddrs); i++) {
+ entry->emailAddrs[i] = entry->emailAddrs[i+1];
+ }
+ } else {
+ char **newAddrs = NULL;
+
+ if (index != -1) {
+ rv = SECSuccess;
+ goto done;
+ }
+ newAddrs = (char **)PORT_ArenaAlloc(entry->common.arena,
+ (entry->nemailAddrs+1)* sizeof(char *));
+ if (!newAddrs) {
+ rv = SECFailure;
+ goto done;
+ }
+ for (i=0; i < (int)(entry->nemailAddrs); i++) {
+ newAddrs[i] = entry->emailAddrs[i];
+ }
+ newAddrs[entry->nemailAddrs] =
+ PORT_ArenaStrdup(entry->common.arena,emailAddr);
+ if (!newAddrs[entry->nemailAddrs]) {
+ rv = SECFailure;
+ goto done;
+ }
+ entry->emailAddrs = newAddrs;
+ entry->nemailAddrs++;
+ }
+
+ /* delete the subject entry */
+ DeleteDBSubjectEntry(dbhandle, derSubject);
+
+ /* write the new one */
+ rv = WriteDBSubjectEntry(dbhandle, entry);
+
+ done:
+ if (entry) DestroyDBEntry((certDBEntry *)entry);
+ if (emailAddr) PORT_Free(emailAddr);
+ return rv;
+}
+
+/*
+ * writes a nickname to an existing subject entry that does not currently
+ * have one
+ */
+static SECStatus
+AddNicknameToSubject(NSSLOWCERTCertDBHandle *dbhandle,
+ NSSLOWCERTCertificate *cert, char *nickname)
+{
+ certDBEntrySubject *entry;
+ SECStatus rv;
+
+ if ( nickname == NULL ) {
+ return(SECFailure);
+ }
+
+ entry = ReadDBSubjectEntry(dbhandle,&cert->derSubject);
+ PORT_Assert(entry != NULL);
+ if ( entry == NULL ) {
+ goto loser;
+ }
+
+ PORT_Assert(entry->nickname == NULL);
+ if ( entry->nickname != NULL ) {
+ goto loser;
+ }
+
+ entry->nickname = PORT_ArenaStrdup(entry->common.arena, nickname);
+
+ if ( entry->nickname == NULL ) {
+ goto loser;
+ }
+
+ /* delete the subject entry */
+ DeleteDBSubjectEntry(dbhandle, &cert->derSubject);
+
+ /* write the new one */
+ rv = WriteDBSubjectEntry(dbhandle, entry);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ return(SECSuccess);
+
+loser:
+ return(SECFailure);
+}
+
+/*
+ * create a new version entry
+ */
+static certDBEntryVersion *
+NewDBVersionEntry(unsigned int flags)
+{
+ PRArenaPool *arena = NULL;
+ certDBEntryVersion *entry;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( arena == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ entry = (certDBEntryVersion *)PORT_ArenaAlloc(arena,
+ sizeof(certDBEntryVersion));
+ if ( entry == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+ entry->common.arena = arena;
+ entry->common.type = certDBEntryTypeVersion;
+ entry->common.version = CERT_DB_FILE_VERSION;
+ entry->common.flags = flags;
+
+ return(entry);
+loser:
+ if ( arena ) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+
+ return(NULL);
+}
+
+/*
+ * Read the version entry
+ */
+static certDBEntryVersion *
+ReadDBVersionEntry(NSSLOWCERTCertDBHandle *handle)
+{
+ PRArenaPool *arena = NULL;
+ PRArenaPool *tmparena = NULL;
+ certDBEntryVersion *entry;
+ SECItem dbkey;
+ SECItem dbentry;
+ SECStatus rv;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( arena == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( tmparena == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ entry = PORT_ArenaZNew(arena, certDBEntryVersion);
+ if ( entry == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+ entry->common.arena = arena;
+ entry->common.type = certDBEntryTypeVersion;
+
+ /* now get the database key and format it */
+ dbkey.len = SEC_DB_VERSION_KEY_LEN + SEC_DB_KEY_HEADER_LEN;
+ dbkey.data = (unsigned char *)PORT_ArenaAlloc(tmparena, dbkey.len);
+ if ( dbkey.data == NULL ) {
+ goto loser;
+ }
+ PORT_Memcpy(&dbkey.data[SEC_DB_KEY_HEADER_LEN], SEC_DB_VERSION_KEY,
+ SEC_DB_VERSION_KEY_LEN);
+
+ rv = ReadDBEntry(handle, &entry->common, &dbkey, &dbentry, tmparena);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ PORT_FreeArena(tmparena, PR_FALSE);
+ return(entry);
+
+loser:
+ if ( tmparena ) {
+ PORT_FreeArena(tmparena, PR_FALSE);
+ }
+ if ( arena ) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+
+ return(NULL);
+}
+
+
+/*
+ * Encode a version entry into byte stream suitable for
+ * the database
+ */
+static SECStatus
+WriteDBVersionEntry(NSSLOWCERTCertDBHandle *handle, certDBEntryVersion *entry)
+{
+ SECItem dbitem, dbkey;
+ PRArenaPool *tmparena = NULL;
+ SECStatus rv;
+
+ tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( tmparena == NULL ) {
+ goto loser;
+ }
+
+ /* allocate space for encoded database record, including space
+ * for low level header
+ */
+ dbitem.len = SEC_DB_ENTRY_HEADER_LEN;
+
+ dbitem.data = (unsigned char *)PORT_ArenaAlloc(tmparena, dbitem.len);
+ if ( dbitem.data == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ /* now get the database key and format it */
+ dbkey.len = SEC_DB_VERSION_KEY_LEN + SEC_DB_KEY_HEADER_LEN;
+ dbkey.data = (unsigned char *)PORT_ArenaAlloc(tmparena, dbkey.len);
+ if ( dbkey.data == NULL ) {
+ goto loser;
+ }
+ PORT_Memcpy(&dbkey.data[SEC_DB_KEY_HEADER_LEN], SEC_DB_VERSION_KEY,
+ SEC_DB_VERSION_KEY_LEN);
+
+ /* now write it to the database */
+ rv = WriteDBEntry(handle, &entry->common, &dbkey, &dbitem);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ PORT_FreeArena(tmparena, PR_FALSE);
+ return(SECSuccess);
+
+loser:
+ if ( tmparena ) {
+ PORT_FreeArena(tmparena, PR_FALSE);
+ }
+ return(SECFailure);
+}
+
+/*
+ * cert is no longer a perm cert, but will remain a temp cert
+ */
+static SECStatus
+RemovePermSubjectNode(NSSLOWCERTCertificate *cert)
+{
+ certDBEntrySubject *entry;
+ unsigned int i;
+ SECStatus rv;
+
+ entry = ReadDBSubjectEntry(cert->dbhandle,&cert->derSubject);
+ if ( entry == NULL ) {
+ return(SECFailure);
+ }
+
+ PORT_Assert(entry->ncerts);
+ rv = SECFailure;
+
+ if ( entry->ncerts > 1 ) {
+ for ( i = 0; i < entry->ncerts; i++ ) {
+ if ( SECITEM_CompareItem(&entry->certKeys[i], &cert->certKey) ==
+ SECEqual ) {
+ /* copy rest of list forward one entry */
+ for ( i = i + 1; i < entry->ncerts; i++ ) {
+ entry->certKeys[i-1] = entry->certKeys[i];
+ entry->keyIDs[i-1] = entry->keyIDs[i];
+ }
+ entry->ncerts--;
+ DeleteDBSubjectEntry(cert->dbhandle, &cert->derSubject);
+ rv = WriteDBSubjectEntry(cert->dbhandle, entry);
+ break;
+ }
+ }
+ } else {
+ /* no entries left, delete the perm entry in the DB */
+ if ( entry->emailAddrs ) {
+ /* if the subject had an email record, then delete it too */
+ for (i=0; i < entry->nemailAddrs; i++) {
+ DeleteDBSMimeEntry(cert->dbhandle, entry->emailAddrs[i]);
+ }
+ }
+ if ( entry->nickname ) {
+ DeleteDBNicknameEntry(cert->dbhandle, entry->nickname);
+ }
+
+ DeleteDBSubjectEntry(cert->dbhandle, &cert->derSubject);
+ }
+ DestroyDBEntry((certDBEntry *)entry);
+
+ return(rv);
+}
+
+/*
+ * add a cert to the perm subject list
+ */
+static SECStatus
+AddPermSubjectNode(certDBEntrySubject *entry, NSSLOWCERTCertificate *cert,
+ char *nickname)
+{
+ SECItem *newCertKeys, *newKeyIDs;
+ unsigned int i, new_i;
+ SECStatus rv;
+ unsigned int ncerts;
+
+ PORT_Assert(entry);
+ ncerts = entry->ncerts;
+
+ if ( nickname && entry->nickname ) {
+ /* nicknames must be the same */
+ PORT_Assert(PORT_Strcmp(nickname, entry->nickname) == 0);
+ }
+
+ if ( ( entry->nickname == NULL ) && ( nickname != NULL ) ) {
+ /* copy nickname into the entry */
+ entry->nickname = PORT_ArenaStrdup(entry->common.arena, nickname);
+ if ( entry->nickname == NULL ) {
+ return(SECFailure);
+ }
+ }
+
+ /* a DB entry already exists, so add this cert */
+ newCertKeys = PORT_ArenaZNewArray(entry->common.arena, SECItem, ncerts + 1);
+ newKeyIDs = PORT_ArenaZNewArray(entry->common.arena, SECItem, ncerts + 1);
+
+ if ( ( newCertKeys == NULL ) || ( newKeyIDs == NULL ) ) {
+ return(SECFailure);
+ }
+
+ /* Step 1: copy certs older than "cert" into new entry. */
+ for ( i = 0, new_i=0; i < ncerts; i++ ) {
+ NSSLOWCERTCertificate *cmpcert;
+ PRBool isNewer;
+ cmpcert = nsslowcert_FindCertByKey(cert->dbhandle,
+ &entry->certKeys[i]);
+ /* The entry has been corrupted, remove it from the list */
+ if (!cmpcert) {
+ continue;
+ }
+
+ isNewer = nsslowcert_IsNewer(cert, cmpcert);
+ nsslowcert_DestroyCertificate(cmpcert);
+ if ( isNewer )
+ break;
+ /* copy this cert entry */
+ newCertKeys[new_i] = entry->certKeys[i];
+ newKeyIDs[new_i] = entry->keyIDs[i];
+ new_i++;
+ }
+
+ /* Step 2: Add "cert" to the entry. */
+ rv = SECITEM_CopyItem(entry->common.arena, &newCertKeys[new_i],
+ &cert->certKey);
+ if ( rv != SECSuccess ) {
+ return(SECFailure);
+ }
+ rv = SECITEM_CopyItem(entry->common.arena, &newKeyIDs[new_i],
+ &cert->subjectKeyID);
+ if ( rv != SECSuccess ) {
+ return(SECFailure);
+ }
+ new_i++;
+
+ /* Step 3: copy remaining certs (if any) from old entry to new. */
+ for ( ; i < ncerts; i++ ,new_i++) {
+ newCertKeys[new_i] = entry->certKeys[i];
+ newKeyIDs[new_i] = entry->keyIDs[i];
+ }
+
+ /* update certKeys and keyIDs */
+ entry->certKeys = newCertKeys;
+ entry->keyIDs = newKeyIDs;
+
+ /* set new count value */
+ entry->ncerts = new_i;
+
+ DeleteDBSubjectEntry(cert->dbhandle, &cert->derSubject);
+ rv = WriteDBSubjectEntry(cert->dbhandle, entry);
+ return(rv);
+}
+
+
+SECStatus
+nsslowcert_TraversePermCertsForSubject(NSSLOWCERTCertDBHandle *handle,
+ SECItem *derSubject,
+ NSSLOWCERTCertCallback cb, void *cbarg)
+{
+ certDBEntrySubject *entry;
+ unsigned int i;
+ NSSLOWCERTCertificate *cert;
+ SECStatus rv = SECSuccess;
+
+ entry = ReadDBSubjectEntry(handle, derSubject);
+
+ if ( entry == NULL ) {
+ return(SECFailure);
+ }
+
+ for( i = 0; i < entry->ncerts; i++ ) {
+ cert = nsslowcert_FindCertByKey(handle, &entry->certKeys[i]);
+ if (!cert) {
+ continue;
+ }
+ rv = (* cb)(cert, cbarg);
+ nsslowcert_DestroyCertificate(cert);
+ if ( rv == SECFailure ) {
+ break;
+ }
+ }
+
+ DestroyDBEntry((certDBEntry *)entry);
+
+ return(rv);
+}
+
+int
+nsslowcert_NumPermCertsForSubject(NSSLOWCERTCertDBHandle *handle,
+ SECItem *derSubject)
+{
+ certDBEntrySubject *entry;
+ int ret;
+
+ entry = ReadDBSubjectEntry(handle, derSubject);
+
+ if ( entry == NULL ) {
+ return(SECFailure);
+ }
+
+ ret = entry->ncerts;
+
+ DestroyDBEntry((certDBEntry *)entry);
+
+ return(ret);
+}
+
+SECStatus
+nsslowcert_TraversePermCertsForNickname(NSSLOWCERTCertDBHandle *handle,
+ char *nickname, NSSLOWCERTCertCallback cb, void *cbarg)
+{
+ certDBEntryNickname *nnentry = NULL;
+ certDBEntrySMime *smentry = NULL;
+ SECStatus rv;
+ SECItem *derSubject = NULL;
+
+ nnentry = ReadDBNicknameEntry(handle, nickname);
+ if ( nnentry ) {
+ derSubject = &nnentry->subjectName;
+ } else {
+ smentry = nsslowcert_ReadDBSMimeEntry(handle, nickname);
+ if ( smentry ) {
+ derSubject = &smentry->subjectName;
+ }
+ }
+
+ if ( derSubject ) {
+ rv = nsslowcert_TraversePermCertsForSubject(handle, derSubject,
+ cb, cbarg);
+ } else {
+ rv = SECFailure;
+ }
+
+ if ( nnentry ) {
+ DestroyDBEntry((certDBEntry *)nnentry);
+ }
+ if ( smentry ) {
+ DestroyDBEntry((certDBEntry *)smentry);
+ }
+
+ return(rv);
+}
+
+int
+nsslowcert_NumPermCertsForNickname(NSSLOWCERTCertDBHandle *handle,
+ char *nickname)
+{
+ certDBEntryNickname *entry;
+ int ret;
+
+ entry = ReadDBNicknameEntry(handle, nickname);
+
+ if ( entry ) {
+ ret = nsslowcert_NumPermCertsForSubject(handle, &entry->subjectName);
+ DestroyDBEntry((certDBEntry *)entry);
+ } else {
+ ret = 0;
+ }
+ return(ret);
+}
+
+/*
+ * add a nickname to a cert that doesn't have one
+ */
+static SECStatus
+AddNicknameToPermCert(NSSLOWCERTCertDBHandle *dbhandle,
+ NSSLOWCERTCertificate *cert, char *nickname)
+{
+ certDBEntryCert *entry;
+ int rv;
+
+ entry = cert->dbEntry;
+ PORT_Assert(entry != NULL);
+ if ( entry == NULL ) {
+ goto loser;
+ }
+
+ pkcs11_freeNickname(entry->nickname,entry->nicknameSpace);
+ entry->nickname = NULL;
+ entry->nickname = pkcs11_copyNickname(nickname,entry->nicknameSpace,
+ sizeof(entry->nicknameSpace));
+
+ rv = WriteDBCertEntry(dbhandle, entry);
+ if ( rv ) {
+ goto loser;
+ }
+
+ pkcs11_freeNickname(cert->nickname,cert->nicknameSpace);
+ cert->nickname = NULL;
+ cert->nickname = pkcs11_copyNickname(nickname,cert->nicknameSpace,
+ sizeof(cert->nicknameSpace));
+
+ return(SECSuccess);
+
+loser:
+ return(SECFailure);
+}
+
+/*
+ * add a nickname to a cert that is already in the perm database, but doesn't
+ * have one yet (it is probably an e-mail cert).
+ */
+SECStatus
+nsslowcert_AddPermNickname(NSSLOWCERTCertDBHandle *dbhandle,
+ NSSLOWCERTCertificate *cert, char *nickname)
+{
+ SECStatus rv = SECFailure;
+ certDBEntrySubject *entry = NULL;
+ certDBEntryNickname *nicknameEntry = NULL;
+
+ nsslowcert_LockDB(dbhandle);
+
+ entry = ReadDBSubjectEntry(dbhandle, &cert->derSubject);
+ if (entry == NULL) goto loser;
+
+ if ( entry->nickname == NULL ) {
+
+ /* no nickname for subject */
+ rv = AddNicknameToSubject(dbhandle, cert, nickname);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+ rv = AddNicknameToPermCert(dbhandle, cert, nickname);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+ nicknameEntry = NewDBNicknameEntry(nickname, &cert->derSubject, 0);
+ if ( nicknameEntry == NULL ) {
+ goto loser;
+ }
+
+ rv = WriteDBNicknameEntry(dbhandle, nicknameEntry);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+ } else {
+ /* subject already has a nickname */
+ rv = AddNicknameToPermCert(dbhandle, cert, entry->nickname);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+ /* make sure nickname entry exists. If the database was corrupted,
+ * we may have lost the nickname entry. Add it back now */
+ nicknameEntry = ReadDBNicknameEntry(dbhandle, entry->nickname);
+ if (nicknameEntry == NULL ) {
+ nicknameEntry = NewDBNicknameEntry(entry->nickname,
+ &cert->derSubject, 0);
+ if ( nicknameEntry == NULL ) {
+ goto loser;
+ }
+
+ rv = WriteDBNicknameEntry(dbhandle, nicknameEntry);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+ }
+ }
+ rv = SECSuccess;
+
+loser:
+ if (entry) {
+ DestroyDBEntry((certDBEntry *)entry);
+ }
+ if (nicknameEntry) {
+ DestroyDBEntry((certDBEntry *)nicknameEntry);
+ }
+ nsslowcert_UnlockDB(dbhandle);
+ return(rv);
+}
+
+static certDBEntryCert *
+AddCertToPermDB(NSSLOWCERTCertDBHandle *handle, NSSLOWCERTCertificate *cert,
+ char *nickname, NSSLOWCERTCertTrust *trust)
+{
+ certDBEntryCert *certEntry = NULL;
+ certDBEntryNickname *nicknameEntry = NULL;
+ certDBEntrySubject *subjectEntry = NULL;
+ int state = 0;
+ SECStatus rv;
+ PRBool donnentry = PR_FALSE;
+
+ if ( nickname ) {
+ donnentry = PR_TRUE;
+ }
+
+ subjectEntry = ReadDBSubjectEntry(handle, &cert->derSubject);
+
+ if ( subjectEntry && subjectEntry->nickname ) {
+ donnentry = PR_FALSE;
+ nickname = subjectEntry->nickname;
+ }
+
+ certEntry = NewDBCertEntry(&cert->derCert, nickname, trust, 0);
+ if ( certEntry == NULL ) {
+ goto loser;
+ }
+
+ if ( donnentry ) {
+ nicknameEntry = NewDBNicknameEntry(nickname, &cert->derSubject, 0);
+ if ( nicknameEntry == NULL ) {
+ goto loser;
+ }
+ }
+
+ rv = WriteDBCertEntry(handle, certEntry);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+ state = 1;
+
+ if ( nicknameEntry ) {
+ rv = WriteDBNicknameEntry(handle, nicknameEntry);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+ }
+
+ state = 2;
+
+ /* "Change" handles if necessary */
+ cert->dbhandle = handle;
+
+ /* add to or create new subject entry */
+ if ( subjectEntry ) {
+ /* REWRITE BASED ON SUBJECT ENTRY */
+ rv = AddPermSubjectNode(subjectEntry, cert, nickname);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+ } else {
+ /* make a new subject entry - this case is only used when updating
+ * an old version of the database. This is OK because the oldnickname
+ * db format didn't allow multiple certs with the same subject.
+ */
+ /* where does subjectKeyID and certKey come from? */
+ subjectEntry = NewDBSubjectEntry(&cert->derSubject, &cert->certKey,
+ &cert->subjectKeyID, nickname,
+ NULL, 0);
+ if ( subjectEntry == NULL ) {
+ goto loser;
+ }
+ rv = WriteDBSubjectEntry(handle, subjectEntry);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+ }
+
+ state = 3;
+
+ if ( nicknameEntry ) {
+ DestroyDBEntry((certDBEntry *)nicknameEntry);
+ }
+
+ if ( subjectEntry ) {
+ DestroyDBEntry((certDBEntry *)subjectEntry);
+ }
+
+ return(certEntry);
+
+loser:
+ /* don't leave partial entry in the database */
+ if ( state > 0 ) {
+ rv = DeleteDBCertEntry(handle, &cert->certKey);
+ }
+ if ( ( state > 1 ) && donnentry ) {
+ rv = DeleteDBNicknameEntry(handle, nickname);
+ }
+ if ( state > 2 ) {
+ rv = DeleteDBSubjectEntry(handle, &cert->derSubject);
+ }
+ if ( certEntry ) {
+ DestroyDBEntry((certDBEntry *)certEntry);
+ }
+ if ( nicknameEntry ) {
+ DestroyDBEntry((certDBEntry *)nicknameEntry);
+ }
+ if ( subjectEntry ) {
+ DestroyDBEntry((certDBEntry *)subjectEntry);
+ }
+
+ return(NULL);
+}
+
+/* forward declaration */
+static SECStatus
+UpdateV7DB(NSSLOWCERTCertDBHandle *handle, DB *updatedb);
+
+/*
+ * version 8 uses the same schema as version 7. The only differences are
+ * 1) version 8 db uses the blob shim to store data entries > 32k.
+ * 2) version 8 db sets the db block size to 32k.
+ * both of these are dealt with by the handle.
+ */
+
+static SECStatus
+UpdateV8DB(NSSLOWCERTCertDBHandle *handle, DB *updatedb)
+{
+ return UpdateV7DB(handle,updatedb);
+}
+
+
+/*
+ * we could just blindly sequence through reading key data pairs and writing
+ * them back out, but some cert.db's have gotten quite large and may have some
+ * subtle corruption problems, so instead we cycle through the certs and
+ * CRL's and S/MIME profiles and rebuild our subject lists from those records.
+ */
+static SECStatus
+UpdateV7DB(NSSLOWCERTCertDBHandle *handle, DB *updatedb)
+{
+ DBT key, data;
+ int ret;
+ NSSLOWCERTCertificate *cert;
+ PRBool isKRL = PR_FALSE;
+ certDBEntryType entryType;
+ SECItem dbEntry, dbKey;
+ certDBEntryRevocation crlEntry;
+ certDBEntryCert certEntry;
+ certDBEntrySMime smimeEntry;
+ SECStatus rv;
+
+ ret = (* updatedb->seq)(updatedb, &key, &data, R_FIRST);
+
+ if ( ret ) {
+ return(SECFailure);
+ }
+
+ do {
+ unsigned char *dataBuf = (unsigned char *)data.data;
+ unsigned char *keyBuf = (unsigned char *)key.data;
+ dbEntry.data = &dataBuf[SEC_DB_ENTRY_HEADER_LEN];
+ dbEntry.len = data.size - SEC_DB_ENTRY_HEADER_LEN;
+ entryType = (certDBEntryType) keyBuf[0];
+ dbKey.data = &keyBuf[SEC_DB_KEY_HEADER_LEN];
+ dbKey.len = key.size - SEC_DB_KEY_HEADER_LEN;
+ if ((dbEntry.len <= 0) || (dbKey.len <= 0)) {
+ continue;
+ }
+
+ switch (entryType) {
+ /* these entries will get regenerated as we read the
+ * rest of the data from the database */
+ case certDBEntryTypeVersion:
+ case certDBEntryTypeSubject:
+ case certDBEntryTypeContentVersion:
+ case certDBEntryTypeNickname:
+ /* smime profiles need entries created after the certs have
+ * been imported, loop over them in a second run */
+ case certDBEntryTypeSMimeProfile:
+ break;
+
+ case certDBEntryTypeCert:
+ /* decode Entry */
+ certEntry.common.version = (unsigned int)dataBuf[0];
+ certEntry.common.type = entryType;
+ certEntry.common.flags = (unsigned int)dataBuf[2];
+ rv = DecodeDBCertEntry(&certEntry,&dbEntry);
+ if (rv != SECSuccess) {
+ break;
+ }
+ /* should we check for existing duplicates? */
+ cert = nsslowcert_DecodeDERCertificate(&certEntry.derCert,
+ certEntry.nickname);
+ if (cert) {
+ nsslowcert_UpdatePermCert(handle, cert, certEntry.nickname,
+ &certEntry.trust);
+ nsslowcert_DestroyCertificate(cert);
+ }
+ /* free any data the decode may have allocated. */
+ pkcs11_freeStaticData(certEntry.derCert.data,
+ certEntry.derCertSpace);
+ pkcs11_freeNickname(certEntry.nickname, certEntry.nicknameSpace);
+ break;
+
+ case certDBEntryTypeKeyRevocation:
+ isKRL = PR_TRUE;
+ /* fall through */
+ case certDBEntryTypeRevocation:
+ crlEntry.common.version = (unsigned int)dataBuf[0];
+ crlEntry.common.type = entryType;
+ crlEntry.common.flags = (unsigned int)dataBuf[2];
+ crlEntry.common.arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (crlEntry.common.arena == NULL) {
+ break;
+ }
+ rv = DecodeDBCrlEntry(&crlEntry,&dbEntry);
+ if (rv != SECSuccess) {
+ break;
+ }
+ nsslowcert_UpdateCrl(handle, &crlEntry.derCrl, &dbKey,
+ crlEntry.url, isKRL);
+ /* free data allocated by the decode */
+ PORT_FreeArena(crlEntry.common.arena, PR_FALSE);
+ crlEntry.common.arena = NULL;
+ break;
+
+ default:
+ break;
+ }
+ } while ( (* updatedb->seq)(updatedb, &key, &data, R_NEXT) == 0 );
+
+ /* now loop again updating just the SMimeProfile. */
+ ret = (* updatedb->seq)(updatedb, &key, &data, R_FIRST);
+
+ if ( ret ) {
+ return(SECFailure);
+ }
+
+ do {
+ unsigned char *dataBuf = (unsigned char *)data.data;
+ unsigned char *keyBuf = (unsigned char *)key.data;
+ dbEntry.data = &dataBuf[SEC_DB_ENTRY_HEADER_LEN];
+ dbEntry.len = data.size - SEC_DB_ENTRY_HEADER_LEN;
+ entryType = (certDBEntryType) keyBuf[0];
+ if (entryType != certDBEntryTypeSMimeProfile) {
+ continue;
+ }
+ dbKey.data = &keyBuf[SEC_DB_KEY_HEADER_LEN];
+ dbKey.len = key.size - SEC_DB_KEY_HEADER_LEN;
+ if ((dbEntry.len <= 0) || (dbKey.len <= 0)) {
+ continue;
+ }
+ smimeEntry.common.version = (unsigned int)dataBuf[0];
+ smimeEntry.common.type = entryType;
+ smimeEntry.common.flags = (unsigned int)dataBuf[2];
+ smimeEntry.common.arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ /* decode entry */
+ rv = DecodeDBSMimeEntry(&smimeEntry,&dbEntry,(char *)dbKey.data);
+ if (rv == SECSuccess) {
+ nsslowcert_UpdateSMimeProfile(handle, smimeEntry.emailAddr,
+ &smimeEntry.subjectName, &smimeEntry.smimeOptions,
+ &smimeEntry.optionsDate);
+ }
+ PORT_FreeArena(smimeEntry.common.arena, PR_FALSE);
+ smimeEntry.common.arena = NULL;
+ } while ( (* updatedb->seq)(updatedb, &key, &data, R_NEXT) == 0 );
+
+ (* updatedb->close)(updatedb);
+
+ /* a database update is a good time to go back and verify the integrity of
+ * the keys and certs */
+ handle->dbVerify = PR_TRUE;
+ return(SECSuccess);
+}
+
+/*
+ * NOTE - Version 6 DB did not go out to the real world in a release,
+ * so we can remove this function in a later release.
+ */
+static SECStatus
+UpdateV6DB(NSSLOWCERTCertDBHandle *handle, DB *updatedb)
+{
+ int ret;
+ DBT key, data;
+ unsigned char *buf, *tmpbuf = NULL;
+ certDBEntryType type;
+ certDBEntryNickname *nnEntry = NULL;
+ certDBEntrySubject *subjectEntry = NULL;
+ certDBEntrySMime *emailEntry = NULL;
+ char *nickname;
+ char *emailAddr;
+ SECStatus rv;
+
+ /*
+ * Sequence through the old database and copy all of the entries
+ * to the new database. Subject name entries will have the new
+ * fields inserted into them (with zero length).
+ */
+ ret = (* updatedb->seq)(updatedb, &key, &data, R_FIRST);
+ if ( ret ) {
+ return(SECFailure);
+ }
+
+ do {
+ buf = (unsigned char *)data.data;
+
+ if ( data.size >= 3 ) {
+ if ( buf[0] == 6 ) { /* version number */
+ type = (certDBEntryType)buf[1];
+ if ( type == certDBEntryTypeSubject ) {
+ /* expando subjecto entrieo */
+ tmpbuf = (unsigned char *)PORT_Alloc(data.size + 4);
+ if ( tmpbuf ) {
+ /* copy header stuff */
+ PORT_Memcpy(tmpbuf, buf, SEC_DB_ENTRY_HEADER_LEN + 2);
+ /* insert 4 more bytes of zero'd header */
+ PORT_Memset(&tmpbuf[SEC_DB_ENTRY_HEADER_LEN + 2],
+ 0, 4);
+ /* copy rest of the data */
+ PORT_Memcpy(&tmpbuf[SEC_DB_ENTRY_HEADER_LEN + 6],
+ &buf[SEC_DB_ENTRY_HEADER_LEN + 2],
+ data.size - (SEC_DB_ENTRY_HEADER_LEN + 2));
+
+ data.data = (void *)tmpbuf;
+ data.size += 4;
+ buf = tmpbuf;
+ }
+ } else if ( type == certDBEntryTypeCert ) {
+ /* expando certo entrieo */
+ tmpbuf = (unsigned char *)PORT_Alloc(data.size + 3);
+ if ( tmpbuf ) {
+ /* copy header stuff */
+ PORT_Memcpy(tmpbuf, buf, SEC_DB_ENTRY_HEADER_LEN);
+
+ /* copy trust flage, setting msb's to 0 */
+ tmpbuf[SEC_DB_ENTRY_HEADER_LEN] = 0;
+ tmpbuf[SEC_DB_ENTRY_HEADER_LEN+1] =
+ buf[SEC_DB_ENTRY_HEADER_LEN];
+ tmpbuf[SEC_DB_ENTRY_HEADER_LEN+2] = 0;
+ tmpbuf[SEC_DB_ENTRY_HEADER_LEN+3] =
+ buf[SEC_DB_ENTRY_HEADER_LEN+1];
+ tmpbuf[SEC_DB_ENTRY_HEADER_LEN+4] = 0;
+ tmpbuf[SEC_DB_ENTRY_HEADER_LEN+5] =
+ buf[SEC_DB_ENTRY_HEADER_LEN+2];
+
+ /* copy rest of the data */
+ PORT_Memcpy(&tmpbuf[SEC_DB_ENTRY_HEADER_LEN + 6],
+ &buf[SEC_DB_ENTRY_HEADER_LEN + 3],
+ data.size - (SEC_DB_ENTRY_HEADER_LEN + 3));
+
+ data.data = (void *)tmpbuf;
+ data.size += 3;
+ buf = tmpbuf;
+ }
+
+ }
+
+ /* update the record version number */
+ buf[0] = CERT_DB_FILE_VERSION;
+
+ /* copy to the new database */
+ ret = certdb_Put(handle->permCertDB, &key, &data, 0);
+ if ( tmpbuf ) {
+ PORT_Free(tmpbuf);
+ tmpbuf = NULL;
+ }
+ }
+ }
+ } while ( (* updatedb->seq)(updatedb, &key, &data, R_NEXT) == 0 );
+
+ ret = certdb_Sync(handle->permCertDB, 0);
+
+ ret = (* updatedb->seq)(updatedb, &key, &data, R_FIRST);
+ if ( ret ) {
+ return(SECFailure);
+ }
+
+ do {
+ buf = (unsigned char *)data.data;
+
+ if ( data.size >= 3 ) {
+ if ( buf[0] == CERT_DB_FILE_VERSION ) { /* version number */
+ type = (certDBEntryType)buf[1];
+ if ( type == certDBEntryTypeNickname ) {
+ nickname = &((char *)key.data)[1];
+
+ /* get the matching nickname entry in the new DB */
+ nnEntry = ReadDBNicknameEntry(handle, nickname);
+ if ( nnEntry == NULL ) {
+ goto endloop;
+ }
+
+ /* find the subject entry pointed to by nickname */
+ subjectEntry = ReadDBSubjectEntry(handle,
+ &nnEntry->subjectName);
+ if ( subjectEntry == NULL ) {
+ goto endloop;
+ }
+
+ subjectEntry->nickname =
+ (char *)PORT_ArenaAlloc(subjectEntry->common.arena,
+ key.size - 1);
+ if ( subjectEntry->nickname ) {
+ PORT_Memcpy(subjectEntry->nickname, nickname,
+ key.size - 1);
+ rv = WriteDBSubjectEntry(handle, subjectEntry);
+ }
+ } else if ( type == certDBEntryTypeSMimeProfile ) {
+ emailAddr = &((char *)key.data)[1];
+
+ /* get the matching smime entry in the new DB */
+ emailEntry = nsslowcert_ReadDBSMimeEntry(handle, emailAddr);
+ if ( emailEntry == NULL ) {
+ goto endloop;
+ }
+
+ /* find the subject entry pointed to by nickname */
+ subjectEntry = ReadDBSubjectEntry(handle,
+ &emailEntry->subjectName);
+ if ( subjectEntry == NULL ) {
+ goto endloop;
+ }
+
+ subjectEntry->emailAddrs = (char **)
+ PORT_ArenaAlloc(subjectEntry->common.arena,
+ sizeof(char *));
+ if ( subjectEntry->emailAddrs ) {
+ subjectEntry->emailAddrs[0] =
+ (char *)PORT_ArenaAlloc(subjectEntry->common.arena,
+ key.size - 1);
+ if ( subjectEntry->emailAddrs[0] ) {
+ PORT_Memcpy(subjectEntry->emailAddrs[0], emailAddr,
+ key.size - 1);
+ subjectEntry->nemailAddrs = 1;
+ rv = WriteDBSubjectEntry(handle, subjectEntry);
+ }
+ }
+ }
+
+endloop:
+ if ( subjectEntry ) {
+ DestroyDBEntry((certDBEntry *)subjectEntry);
+ subjectEntry = NULL;
+ }
+ if ( nnEntry ) {
+ DestroyDBEntry((certDBEntry *)nnEntry);
+ nnEntry = NULL;
+ }
+ if ( emailEntry ) {
+ DestroyDBEntry((certDBEntry *)emailEntry);
+ emailEntry = NULL;
+ }
+ }
+ }
+ } while ( (* updatedb->seq)(updatedb, &key, &data, R_NEXT) == 0 );
+
+ ret = certdb_Sync(handle->permCertDB, 0);
+
+ (* updatedb->close)(updatedb);
+ return(SECSuccess);
+}
+
+
+static SECStatus
+updateV5Callback(NSSLOWCERTCertificate *cert, SECItem *k, void *pdata)
+{
+ NSSLOWCERTCertDBHandle *handle;
+ certDBEntryCert *entry;
+ NSSLOWCERTCertTrust *trust;
+
+ handle = (NSSLOWCERTCertDBHandle *)pdata;
+ trust = &cert->dbEntry->trust;
+
+ /* SSL user certs can be used for email if they have an email addr */
+ if ( cert->emailAddr && ( trust->sslFlags & CERTDB_USER ) &&
+ ( trust->emailFlags == 0 ) ) {
+ trust->emailFlags = CERTDB_USER;
+ }
+ /* servers didn't set the user flags on the server cert.. */
+ if (PORT_Strcmp(cert->dbEntry->nickname,"Server-Cert") == 0) {
+ trust->sslFlags |= CERTDB_USER;
+ }
+
+ entry = AddCertToPermDB(handle, cert, cert->dbEntry->nickname,
+ &cert->dbEntry->trust);
+ if ( entry ) {
+ DestroyDBEntry((certDBEntry *)entry);
+ }
+
+ return(SECSuccess);
+}
+
+static SECStatus
+UpdateV5DB(NSSLOWCERTCertDBHandle *handle, DB *updatedb)
+{
+ NSSLOWCERTCertDBHandle updatehandle;
+ SECStatus rv;
+
+ updatehandle.permCertDB = updatedb;
+ updatehandle.dbMon = PZ_NewMonitor(nssILockCertDB);
+ updatehandle.dbVerify = 0;
+ updatehandle.ref = 1; /* prevent premature close */
+
+ rv = nsslowcert_TraversePermCerts(&updatehandle, updateV5Callback,
+ (void *)handle);
+
+ PZ_DestroyMonitor(updatehandle.dbMon);
+
+ (* updatedb->close)(updatedb);
+ return(SECSuccess);
+}
+
+static PRBool
+isV4DB(DB *db) {
+ DBT key,data;
+ int ret;
+
+ key.data = "Version";
+ key.size = 7;
+
+ ret = (*db->get)(db, &key, &data, 0);
+ if (ret) {
+ return PR_FALSE;
+ }
+
+ if ((data.size == 1) && (*(unsigned char *)data.data <= 4)) {
+ return PR_TRUE;
+ }
+
+ return PR_FALSE;
+}
+
+static SECStatus
+UpdateV4DB(NSSLOWCERTCertDBHandle *handle, DB *updatedb)
+{
+ DBT key, data;
+ certDBEntryCert *entry, *entry2;
+ int ret;
+ PRArenaPool *arena = NULL;
+ NSSLOWCERTCertificate *cert;
+
+ ret = (* updatedb->seq)(updatedb, &key, &data, R_FIRST);
+
+ if ( ret ) {
+ return(SECFailure);
+ }
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (arena == NULL) {
+ return(SECFailure);
+ }
+
+ do {
+ if ( data.size != 1 ) { /* skip version number */
+
+ /* decode the old DB entry */
+ entry = (certDBEntryCert *)
+ DecodeV4DBCertEntry((unsigned char*)data.data, data.size);
+
+ if ( entry ) {
+ cert = nsslowcert_DecodeDERCertificate(&entry->derCert,
+ entry->nickname);
+
+ if ( cert != NULL ) {
+ /* add to new database */
+ entry2 = AddCertToPermDB(handle, cert, entry->nickname,
+ &entry->trust);
+
+ nsslowcert_DestroyCertificate(cert);
+ if ( entry2 ) {
+ DestroyDBEntry((certDBEntry *)entry2);
+ }
+ }
+ DestroyDBEntry((certDBEntry *)entry);
+ }
+ }
+ } while ( (* updatedb->seq)(updatedb, &key, &data, R_NEXT) == 0 );
+
+ PORT_FreeArena(arena, PR_FALSE);
+ (* updatedb->close)(updatedb);
+ return(SECSuccess);
+}
+
+
+/*
+ * return true if a database key conflict exists
+ */
+PRBool
+nsslowcert_CertDBKeyConflict(SECItem *derCert, NSSLOWCERTCertDBHandle *handle)
+{
+ SECStatus rv;
+ DBT tmpdata;
+ DBT namekey;
+ int ret;
+ SECItem keyitem;
+ PRArenaPool *arena = NULL;
+ SECItem derKey;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( arena == NULL ) {
+ goto loser;
+ }
+
+ /* get the db key of the cert */
+ rv = nsslowcert_KeyFromDERCert(arena, derCert, &derKey);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ rv = EncodeDBCertKey(&derKey, arena, &keyitem);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ namekey.data = keyitem.data;
+ namekey.size = keyitem.len;
+
+ ret = certdb_Get(handle->permCertDB, &namekey, &tmpdata, 0);
+ if ( ret == 0 ) {
+ goto loser;
+ }
+
+ PORT_FreeArena(arena, PR_FALSE);
+
+ return(PR_FALSE);
+loser:
+ if ( arena ) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+
+ return(PR_TRUE);
+}
+
+/*
+ * return true if a nickname conflict exists
+ * NOTE: caller must have already made sure that this exact cert
+ * doesn't exist in the DB
+ */
+static PRBool
+nsslowcert_CertNicknameConflict(char *nickname, SECItem *derSubject,
+ NSSLOWCERTCertDBHandle *handle)
+{
+ PRBool rv;
+ certDBEntryNickname *entry;
+
+ if ( nickname == NULL ) {
+ return(PR_FALSE);
+ }
+
+ entry = ReadDBNicknameEntry(handle, nickname);
+
+ if ( entry == NULL ) {
+ /* no entry for this nickname, so no conflict */
+ return(PR_FALSE);
+ }
+
+ rv = PR_TRUE;
+ if ( SECITEM_CompareItem(derSubject, &entry->subjectName) == SECEqual ) {
+ /* if subject names are the same, then no conflict */
+ rv = PR_FALSE;
+ }
+
+ DestroyDBEntry((certDBEntry *)entry);
+ return(rv);
+}
+
+#ifdef DBM_USING_NSPR
+#define NO_RDONLY PR_RDONLY
+#define NO_RDWR PR_RDWR
+#define NO_CREATE (PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE)
+#else
+#define NO_RDONLY O_RDONLY
+#define NO_RDWR O_RDWR
+#define NO_CREATE (O_RDWR | O_CREAT | O_TRUNC)
+#endif
+
+/*
+ * open an old database that needs to be updated
+ */
+static DB *
+nsslowcert_openolddb(NSSLOWCERTDBNameFunc namecb, void *cbarg, int version)
+{
+ char * tmpname;
+ DB *updatedb = NULL;
+
+ tmpname = (* namecb)(cbarg, version); /* get v6 db name */
+ if ( tmpname ) {
+ updatedb = dbopen( tmpname, NO_RDONLY, 0600, DB_HASH, 0 );
+ PORT_Free(tmpname);
+ }
+ return updatedb;
+}
+
+static SECStatus
+openNewCertDB(const char *appName, const char *prefix, const char *certdbname,
+ NSSLOWCERTCertDBHandle *handle, NSSLOWCERTDBNameFunc namecb, void *cbarg)
+{
+ SECStatus rv;
+ certDBEntryVersion *versionEntry = NULL;
+ DB *updatedb = NULL;
+ int status = RDB_FAIL;
+
+ if (appName) {
+ handle->permCertDB=rdbopen( appName, prefix, "cert", NO_CREATE, &status);
+ } else {
+ handle->permCertDB=dbsopen(certdbname, NO_CREATE, 0600, DB_HASH, 0);
+ }
+
+ /* if create fails then we lose */
+ if ( handle->permCertDB == 0 ) {
+ return status == RDB_RETRY ? SECWouldBlock : SECFailure;
+ }
+
+ /* Verify version number; */
+ versionEntry = NewDBVersionEntry(0);
+ if ( versionEntry == NULL ) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ rv = WriteDBVersionEntry(handle, versionEntry);
+
+ DestroyDBEntry((certDBEntry *)versionEntry);
+
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ /* rv must already be Success here because of previous if statement */
+ /* try to upgrade old db here */
+ if (appName &&
+ (updatedb = dbsopen(certdbname, NO_RDONLY, 0600, DB_HASH, 0)) != NULL) {
+ rv = UpdateV8DB(handle, updatedb);
+ } else if ((updatedb = nsslowcert_openolddb(namecb,cbarg,7)) != NULL) {
+ rv = UpdateV7DB(handle, updatedb);
+ } else if ((updatedb = nsslowcert_openolddb(namecb,cbarg,6)) != NULL) {
+ rv = UpdateV6DB(handle, updatedb);
+ } else if ((updatedb = nsslowcert_openolddb(namecb,cbarg,5)) != NULL) {
+ rv = UpdateV5DB(handle, updatedb);
+ } else if ((updatedb = nsslowcert_openolddb(namecb,cbarg,4)) != NULL) {
+ /* NES has v5 format db's with v4 db names! */
+ if (isV4DB(updatedb)) {
+ rv = UpdateV4DB(handle,updatedb);
+ } else {
+ rv = UpdateV5DB(handle,updatedb);
+ }
+ }
+
+
+loser:
+ db_InitComplete(handle->permCertDB);
+ return rv;
+}
+
+static int
+nsslowcert_GetVersionNumber( NSSLOWCERTCertDBHandle *handle)
+{
+ certDBEntryVersion *versionEntry = NULL;
+ int version = 0;
+
+ versionEntry = ReadDBVersionEntry(handle);
+ if ( versionEntry == NULL ) {
+ return 0;
+ }
+ version = versionEntry->common.version;
+ DestroyDBEntry((certDBEntry *)versionEntry);
+ return version;
+}
+
+/*
+ * Open the certificate database and index databases. Create them if
+ * they are not there or bad.
+ */
+static SECStatus
+nsslowcert_OpenPermCertDB(NSSLOWCERTCertDBHandle *handle, PRBool readOnly,
+ const char *appName, const char *prefix,
+ NSSLOWCERTDBNameFunc namecb, void *cbarg)
+{
+ SECStatus rv;
+ int openflags;
+ char *certdbname;
+ int version = 0;
+
+ certdbname = (* namecb)(cbarg, CERT_DB_FILE_VERSION);
+ if ( certdbname == NULL ) {
+ return(SECFailure);
+ }
+
+ openflags = readOnly ? NO_RDONLY : NO_RDWR;
+
+ /*
+ * first open the permanent file based database.
+ */
+ if (appName) {
+ handle->permCertDB = rdbopen( appName, prefix, "cert", openflags, NULL);
+ } else {
+ handle->permCertDB = dbsopen( certdbname, openflags, 0600, DB_HASH, 0 );
+ }
+
+ /* check for correct version number */
+ if ( handle->permCertDB ) {
+ version = nsslowcert_GetVersionNumber(handle);
+ if ((version != CERT_DB_FILE_VERSION) &&
+ !(appName && version == CERT_DB_V7_FILE_VERSION)) {
+ goto loser;
+ }
+ } else if ( readOnly ) {
+ /* don't create if readonly */
+ /* Try openning a version 7 database */
+ handle->permCertDB = nsslowcert_openolddb(namecb,cbarg, 7);
+ if (!handle->permCertDB) {
+ goto loser;
+ }
+ if (nsslowcert_GetVersionNumber(handle) != 7) {
+ goto loser;
+ }
+ } else {
+ /* if first open fails, try to create a new DB */
+ rv = openNewCertDB(appName,prefix,certdbname,handle,namecb,cbarg);
+ if (rv == SECWouldBlock) {
+ /* only the rdb version can fail with wouldblock */
+ handle->permCertDB =
+ rdbopen( appName, prefix, "cert", openflags, NULL);
+
+ /* check for correct version number */
+ if ( !handle->permCertDB ) {
+ goto loser;
+ }
+ version = nsslowcert_GetVersionNumber(handle);
+ if ((version != CERT_DB_FILE_VERSION) &&
+ !(appName && version == CERT_DB_V7_FILE_VERSION)) {
+ goto loser;
+ }
+ } else if (rv != SECSuccess) {
+ goto loser;
+ }
+ }
+
+ PORT_Free(certdbname);
+
+ return (SECSuccess);
+
+loser:
+
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+
+ if ( handle->permCertDB ) {
+ certdb_Close(handle->permCertDB);
+ handle->permCertDB = 0;
+ }
+
+ PORT_Free(certdbname);
+
+ return(SECFailure);
+}
+
+/*
+ * delete all DB records associated with a particular certificate
+ */
+static SECStatus
+DeletePermCert(NSSLOWCERTCertificate *cert)
+{
+ SECStatus rv;
+ SECStatus ret;
+
+ ret = SECSuccess;
+
+ rv = DeleteDBCertEntry(cert->dbhandle, &cert->certKey);
+ if ( rv != SECSuccess ) {
+ ret = SECFailure;
+ }
+
+ rv = RemovePermSubjectNode(cert);
+
+
+ return(ret);
+}
+
+/*
+ * Delete a certificate from the permanent database.
+ */
+SECStatus
+nsslowcert_DeletePermCertificate(NSSLOWCERTCertificate *cert)
+{
+ SECStatus rv;
+
+ nsslowcert_LockDB(cert->dbhandle);
+
+ /* delete the records from the permanent database */
+ rv = DeletePermCert(cert);
+
+ /* get rid of dbcert and stuff pointing to it */
+ DestroyDBEntry((certDBEntry *)cert->dbEntry);
+ cert->dbEntry = NULL;
+ cert->trust = NULL;
+
+ nsslowcert_UnlockDB(cert->dbhandle);
+ return(rv);
+}
+
+/*
+ * Traverse all of the entries in the database of a particular type
+ * call the given function for each one.
+ */
+SECStatus
+nsslowcert_TraverseDBEntries(NSSLOWCERTCertDBHandle *handle,
+ certDBEntryType type,
+ SECStatus (* callback)(SECItem *data, SECItem *key,
+ certDBEntryType type, void *pdata),
+ void *udata )
+{
+ DBT data;
+ DBT key;
+ SECStatus rv;
+ int ret;
+ SECItem dataitem;
+ SECItem keyitem;
+ unsigned char *buf;
+ unsigned char *keybuf;
+
+ ret = certdb_Seq(handle->permCertDB, &key, &data, R_FIRST);
+
+ if ( ret ) {
+ return(SECFailure);
+ }
+
+ do {
+ buf = (unsigned char *)data.data;
+
+ if ( buf[1] == (unsigned char)type ) {
+ dataitem.len = data.size;
+ dataitem.data = buf;
+ dataitem.type = siBuffer;
+ keyitem.len = key.size - SEC_DB_KEY_HEADER_LEN;
+ keybuf = (unsigned char *)key.data;
+ keyitem.data = &keybuf[SEC_DB_KEY_HEADER_LEN];
+ keyitem.type = siBuffer;
+ /* type should equal keybuf[0]. */
+
+ rv = (* callback)(&dataitem, &keyitem, type, udata);
+ if ( rv != SECSuccess ) {
+ return(rv);
+ }
+ }
+ } while ( certdb_Seq(handle->permCertDB, &key, &data, R_NEXT) == 0 );
+
+ return(SECSuccess);
+}
+/*
+ * Decode a certificate and enter it into the temporary certificate database.
+ * Deal with nicknames correctly
+ *
+ * This is the private entry point.
+ */
+static NSSLOWCERTCertificate *
+DecodeACert(NSSLOWCERTCertDBHandle *handle, certDBEntryCert *entry)
+{
+ NSSLOWCERTCertificate *cert = NULL;
+
+ cert = nsslowcert_DecodeDERCertificate(&entry->derCert, entry->nickname );
+
+ if ( cert == NULL ) {
+ goto loser;
+ }
+
+ cert->dbhandle = handle;
+ cert->dbEntry = entry;
+ cert->trust = &entry->trust;
+
+ return(cert);
+
+loser:
+ return(0);
+}
+
+static NSSLOWCERTTrust *
+CreateTrust(void)
+{
+ NSSLOWCERTTrust *trust = NULL;
+
+ nsslowcert_LockFreeList();
+ trust = trustListHead;
+ if (trust) {
+ trustListCount--;
+ trustListHead = trust->next;
+ }
+ PORT_Assert(trustListCount >= 0);
+ nsslowcert_UnlockFreeList();
+ if (trust) {
+ return trust;
+ }
+
+ return PORT_ZNew(NSSLOWCERTTrust);
+}
+
+static void
+DestroyTrustFreeList(void)
+{
+ NSSLOWCERTTrust *trust;
+
+ nsslowcert_LockFreeList();
+ while (NULL != (trust = trustListHead)) {
+ trustListCount--;
+ trustListHead = trust->next;
+ PORT_Free(trust);
+ }
+ PORT_Assert(!trustListCount);
+ trustListCount = 0;
+ nsslowcert_UnlockFreeList();
+}
+
+static NSSLOWCERTTrust *
+DecodeTrustEntry(NSSLOWCERTCertDBHandle *handle, certDBEntryCert *entry,
+ const SECItem *dbKey)
+{
+ NSSLOWCERTTrust *trust = CreateTrust();
+ if (trust == NULL) {
+ return trust;
+ }
+ trust->dbhandle = handle;
+ trust->dbEntry = entry;
+ trust->dbKey.data = pkcs11_copyStaticData(dbKey->data,dbKey->len,
+ trust->dbKeySpace, sizeof(trust->dbKeySpace));
+ if (!trust->dbKey.data) {
+ PORT_Free(trust);
+ return NULL;
+ }
+ trust->dbKey.len = dbKey->len;
+
+ trust->trust = &entry->trust;
+ trust->derCert = &entry->derCert;
+
+ return(trust);
+}
+
+typedef struct {
+ PermCertCallback certfunc;
+ NSSLOWCERTCertDBHandle *handle;
+ void *data;
+} PermCertCallbackState;
+
+/*
+ * traversal callback to decode certs and call callers callback
+ */
+static SECStatus
+certcallback(SECItem *dbdata, SECItem *dbkey, certDBEntryType type, void *data)
+{
+ PermCertCallbackState *mystate;
+ SECStatus rv;
+ certDBEntryCert *entry;
+ SECItem entryitem;
+ NSSLOWCERTCertificate *cert;
+ PRArenaPool *arena = NULL;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( arena == NULL ) {
+ goto loser;
+ }
+
+ entry = (certDBEntryCert *)PORT_ArenaAlloc(arena, sizeof(certDBEntryCert));
+ mystate = (PermCertCallbackState *)data;
+ entry->common.version = (unsigned int)dbdata->data[0];
+ entry->common.type = (certDBEntryType)dbdata->data[1];
+ entry->common.flags = (unsigned int)dbdata->data[2];
+ entry->common.arena = arena;
+
+ entryitem.len = dbdata->len - SEC_DB_ENTRY_HEADER_LEN;
+ entryitem.data = &dbdata->data[SEC_DB_ENTRY_HEADER_LEN];
+
+ rv = DecodeDBCertEntry(entry, &entryitem);
+ if (rv != SECSuccess ) {
+ goto loser;
+ }
+ entry->derCert.type = siBuffer;
+
+ /* note: Entry is 'inheritted'. */
+ cert = DecodeACert(mystate->handle, entry);
+
+ rv = (* mystate->certfunc)(cert, dbkey, mystate->data);
+
+ /* arena stored in entry destroyed by nsslowcert_DestroyCertificate */
+ nsslowcert_DestroyCertificateNoLocking(cert);
+
+ return(rv);
+
+loser:
+ if ( arena ) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+ return(SECFailure);
+}
+
+/*
+ * Traverse all of the certificates in the permanent database and
+ * call the given function for each one; expect the caller to have lock.
+ */
+static SECStatus
+TraversePermCertsNoLocking(NSSLOWCERTCertDBHandle *handle,
+ SECStatus (* certfunc)(NSSLOWCERTCertificate *cert,
+ SECItem *k,
+ void *pdata),
+ void *udata )
+{
+ SECStatus rv;
+ PermCertCallbackState mystate;
+
+ mystate.certfunc = certfunc;
+ mystate.handle = handle;
+ mystate.data = udata;
+ rv = nsslowcert_TraverseDBEntries(handle, certDBEntryTypeCert, certcallback,
+ (void *)&mystate);
+
+ return(rv);
+}
+
+/*
+ * Traverse all of the certificates in the permanent database and
+ * call the given function for each one.
+ */
+SECStatus
+nsslowcert_TraversePermCerts(NSSLOWCERTCertDBHandle *handle,
+ SECStatus (* certfunc)(NSSLOWCERTCertificate *cert, SECItem *k,
+ void *pdata),
+ void *udata )
+{
+ SECStatus rv;
+
+ nsslowcert_LockDB(handle);
+ rv = TraversePermCertsNoLocking(handle, certfunc, udata);
+ nsslowcert_UnlockDB(handle);
+
+ return(rv);
+}
+
+
+
+/*
+ * Close the database
+ */
+void
+nsslowcert_ClosePermCertDB(NSSLOWCERTCertDBHandle *handle)
+{
+ if ( handle ) {
+ if ( handle->permCertDB ) {
+ certdb_Close( handle->permCertDB );
+ handle->permCertDB = NULL;
+ }
+ if (handle->dbMon) {
+ PZ_DestroyMonitor(handle->dbMon);
+ handle->dbMon = NULL;
+ }
+ }
+ return;
+}
+
+/*
+ * Get the trust attributes from a certificate
+ */
+SECStatus
+nsslowcert_GetCertTrust(NSSLOWCERTCertificate *cert, NSSLOWCERTCertTrust *trust)
+{
+ SECStatus rv;
+
+ nsslowcert_LockCertTrust(cert);
+
+ if ( cert->trust == NULL ) {
+ rv = SECFailure;
+ } else {
+ *trust = *cert->trust;
+ rv = SECSuccess;
+ }
+
+ nsslowcert_UnlockCertTrust(cert);
+ return(rv);
+}
+
+/*
+ * Change the trust attributes of a certificate and make them permanent
+ * in the database.
+ */
+SECStatus
+nsslowcert_ChangeCertTrust(NSSLOWCERTCertDBHandle *handle,
+ NSSLOWCERTCertificate *cert, NSSLOWCERTCertTrust *trust)
+{
+ certDBEntryCert *entry;
+ int rv;
+ SECStatus ret;
+
+ nsslowcert_LockDB(handle);
+ nsslowcert_LockCertTrust(cert);
+ /* only set the trust on permanent certs */
+ if ( cert->trust == NULL ) {
+ ret = SECFailure;
+ goto done;
+ }
+
+ *cert->trust = *trust;
+ if ( cert->dbEntry == NULL ) {
+ ret = SECSuccess; /* not in permanent database */
+ goto done;
+ }
+
+ entry = cert->dbEntry;
+ entry->trust = *trust;
+
+ rv = WriteDBCertEntry(handle, entry);
+ if ( rv ) {
+ ret = SECFailure;
+ goto done;
+ }
+
+ ret = SECSuccess;
+
+done:
+ nsslowcert_UnlockCertTrust(cert);
+ nsslowcert_UnlockDB(handle);
+ return(ret);
+}
+
+
+static SECStatus
+nsslowcert_UpdatePermCert(NSSLOWCERTCertDBHandle *dbhandle,
+ NSSLOWCERTCertificate *cert, char *nickname, NSSLOWCERTCertTrust *trust)
+{
+ char *oldnn;
+ certDBEntryCert *entry;
+ PRBool conflict;
+ SECStatus ret;
+
+ PORT_Assert(!cert->dbEntry);
+
+ /* don't add a conflicting nickname */
+ conflict = nsslowcert_CertNicknameConflict(nickname, &cert->derSubject,
+ dbhandle);
+ if ( conflict ) {
+ ret = SECFailure;
+ goto done;
+ }
+
+ /* save old nickname so that we can delete it */
+ oldnn = cert->nickname;
+
+ entry = AddCertToPermDB(dbhandle, cert, nickname, trust);
+
+ if ( entry == NULL ) {
+ ret = SECFailure;
+ goto done;
+ }
+
+ pkcs11_freeNickname(oldnn,cert->nicknameSpace);
+
+ cert->nickname = (entry->nickname) ? pkcs11_copyNickname(entry->nickname,
+ cert->nicknameSpace, sizeof(cert->nicknameSpace)) : NULL;
+ cert->trust = &entry->trust;
+ cert->dbEntry = entry;
+
+ ret = SECSuccess;
+done:
+ return(ret);
+}
+
+SECStatus
+nsslowcert_AddPermCert(NSSLOWCERTCertDBHandle *dbhandle,
+ NSSLOWCERTCertificate *cert, char *nickname, NSSLOWCERTCertTrust *trust)
+{
+ SECStatus ret;
+
+ nsslowcert_LockDB(dbhandle);
+
+ ret = nsslowcert_UpdatePermCert(dbhandle, cert, nickname, trust);
+
+ nsslowcert_UnlockDB(dbhandle);
+ return(ret);
+}
+
+/*
+ * Open the certificate database and index databases. Create them if
+ * they are not there or bad.
+ */
+SECStatus
+nsslowcert_OpenCertDB(NSSLOWCERTCertDBHandle *handle, PRBool readOnly,
+ const char *appName, const char *prefix,
+ NSSLOWCERTDBNameFunc namecb, void *cbarg, PRBool openVolatile)
+{
+ int rv;
+
+ certdb_InitDBLock(handle);
+
+ handle->dbMon = PZ_NewMonitor(nssILockCertDB);
+ PORT_Assert(handle->dbMon != NULL);
+ handle->dbVerify = PR_FALSE;
+
+ rv = nsslowcert_OpenPermCertDB(handle, readOnly, appName, prefix,
+ namecb, cbarg);
+ if ( rv ) {
+ goto loser;
+ }
+
+ return (SECSuccess);
+
+loser:
+
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ return(SECFailure);
+}
+
+PRBool
+nsslowcert_needDBVerify(NSSLOWCERTCertDBHandle *handle)
+{
+ if (!handle) return PR_FALSE;
+ return handle->dbVerify;
+}
+
+void
+nsslowcert_setDBVerify(NSSLOWCERTCertDBHandle *handle, PRBool value)
+{
+ handle->dbVerify = value;
+}
+
+
+/*
+ * Lookup a certificate in the databases.
+ */
+static NSSLOWCERTCertificate *
+FindCertByKey(NSSLOWCERTCertDBHandle *handle, const SECItem *certKey, PRBool lockdb)
+{
+ NSSLOWCERTCertificate *cert = NULL;
+ certDBEntryCert *entry;
+ PRBool locked = PR_FALSE;
+
+ if ( lockdb ) {
+ locked = PR_TRUE;
+ nsslowcert_LockDB(handle);
+ }
+
+ /* find in perm database */
+ entry = ReadDBCertEntry(handle, certKey);
+
+ if ( entry == NULL ) {
+ goto loser;
+ }
+
+ /* inherit entry */
+ cert = DecodeACert(handle, entry);
+
+loser:
+ if (cert == NULL) {
+ if (entry) {
+ DestroyDBEntry((certDBEntry *)entry);
+ }
+ }
+
+ if ( locked ) {
+ nsslowcert_UnlockDB(handle);
+ }
+
+ return(cert);
+}
+
+/*
+ * Lookup a certificate in the databases.
+ */
+static NSSLOWCERTTrust *
+FindTrustByKey(NSSLOWCERTCertDBHandle *handle, const SECItem *certKey, PRBool lockdb)
+{
+ NSSLOWCERTTrust *trust = NULL;
+ certDBEntryCert *entry;
+ PRBool locked = PR_FALSE;
+
+ if ( lockdb ) {
+ locked = PR_TRUE;
+ nsslowcert_LockDB(handle);
+ }
+
+ /* find in perm database */
+ entry = ReadDBCertEntry(handle, certKey);
+
+ if ( entry == NULL ) {
+ goto loser;
+ }
+
+ if (!nsslowcert_hasTrust(&entry->trust)) {
+ goto loser;
+ }
+
+ /* inherit entry */
+ trust = DecodeTrustEntry(handle, entry, certKey);
+
+loser:
+ if (trust == NULL) {
+ if (entry) {
+ DestroyDBEntry((certDBEntry *)entry);
+ }
+ }
+
+ if ( locked ) {
+ nsslowcert_UnlockDB(handle);
+ }
+
+ return(trust);
+}
+
+/*
+ * Lookup a certificate in the databases without locking
+ */
+NSSLOWCERTCertificate *
+nsslowcert_FindCertByKey(NSSLOWCERTCertDBHandle *handle, const SECItem *certKey)
+{
+ return(FindCertByKey(handle, certKey, PR_FALSE));
+}
+
+/*
+ * Lookup a trust object in the databases without locking
+ */
+NSSLOWCERTTrust *
+nsslowcert_FindTrustByKey(NSSLOWCERTCertDBHandle *handle, const SECItem *certKey)
+{
+ return(FindTrustByKey(handle, certKey, PR_FALSE));
+}
+
+/*
+ * Generate a key from an issuerAndSerialNumber, and find the
+ * associated cert in the database.
+ */
+NSSLOWCERTCertificate *
+nsslowcert_FindCertByIssuerAndSN(NSSLOWCERTCertDBHandle *handle, NSSLOWCERTIssuerAndSN *issuerAndSN)
+{
+ SECItem certKey;
+ SECItem *sn = &issuerAndSN->serialNumber;
+ SECItem *issuer = &issuerAndSN->derIssuer;
+ NSSLOWCERTCertificate *cert;
+ int data_left = sn->len-1;
+ int data_len = sn->len;
+ int index = 0;
+
+ /* automatically detect DER encoded serial numbers and remove the der
+ * encoding since the database expects unencoded data.
+ * if it's DER encoded, there must be at least 3 bytes, tag, len, data */
+ if ((sn->len >= 3) && (sn->data[0] == 0x2)) {
+ /* remove the der encoding of the serial number before generating the
+ * key.. */
+ data_left = sn->len-2;
+ data_len = sn->data[1];
+ index = 2;
+
+ /* extended length ? (not very likely for a serial number) */
+ if (data_len & 0x80) {
+ int len_count = data_len & 0x7f;
+
+ data_len = 0;
+ data_left -= len_count;
+ if (data_left > 0) {
+ while (len_count --) {
+ data_len = (data_len << 8) | sn->data[index++];
+ }
+ }
+ }
+ /* XXX leaving any leading zeros on the serial number for backwards
+ * compatibility
+ */
+ /* not a valid der, must be just an unlucky serial number value */
+ if (data_len != data_left) {
+ data_len = sn->len;
+ index = 0;
+ }
+ }
+
+ certKey.type = 0;
+ certKey.data = (unsigned char*)PORT_Alloc(sn->len + issuer->len);
+ certKey.len = data_len + issuer->len;
+
+ if ( certKey.data == NULL ) {
+ return(0);
+ }
+
+ /* first try the serial number as hand-decoded above*/
+ /* copy the serialNumber */
+ PORT_Memcpy(certKey.data, &sn->data[index], data_len);
+
+ /* copy the issuer */
+ PORT_Memcpy( &certKey.data[data_len],issuer->data,issuer->len);
+
+ cert = nsslowcert_FindCertByKey(handle, &certKey);
+ if (cert) {
+ PORT_Free(certKey.data);
+ return (cert);
+ }
+
+ /* didn't find it, try by der encoded serial number */
+ /* copy the serialNumber */
+ PORT_Memcpy(certKey.data, sn->data, sn->len);
+
+ /* copy the issuer */
+ PORT_Memcpy( &certKey.data[sn->len], issuer->data, issuer->len);
+ certKey.len = sn->len + issuer->len;
+
+ cert = nsslowcert_FindCertByKey(handle, &certKey);
+
+ PORT_Free(certKey.data);
+
+ return(cert);
+}
+
+/*
+ * Generate a key from an issuerAndSerialNumber, and find the
+ * associated cert in the database.
+ */
+NSSLOWCERTTrust *
+nsslowcert_FindTrustByIssuerAndSN(NSSLOWCERTCertDBHandle *handle,
+ NSSLOWCERTIssuerAndSN *issuerAndSN)
+{
+ SECItem certKey;
+ SECItem *sn = &issuerAndSN->serialNumber;
+ SECItem *issuer = &issuerAndSN->derIssuer;
+ NSSLOWCERTTrust *trust;
+ unsigned char keyBuf[512];
+ int data_left = sn->len-1;
+ int data_len = sn->len;
+ int index = 0;
+ int len;
+
+ /* automatically detect DER encoded serial numbers and remove the der
+ * encoding since the database expects unencoded data.
+ * if it's DER encoded, there must be at least 3 bytes, tag, len, data */
+ if ((sn->len >= 3) && (sn->data[0] == 0x2)) {
+ /* remove the der encoding of the serial number before generating the
+ * key.. */
+ data_left = sn->len-2;
+ data_len = sn->data[1];
+ index = 2;
+
+ /* extended length ? (not very likely for a serial number) */
+ if (data_len & 0x80) {
+ int len_count = data_len & 0x7f;
+
+ data_len = 0;
+ data_left -= len_count;
+ if (data_left > 0) {
+ while (len_count --) {
+ data_len = (data_len << 8) | sn->data[index++];
+ }
+ }
+ }
+ /* XXX leaving any leading zeros on the serial number for backwards
+ * compatibility
+ */
+ /* not a valid der, must be just an unlucky serial number value */
+ if (data_len != data_left) {
+ data_len = sn->len;
+ index = 0;
+ }
+ }
+
+ certKey.type = 0;
+ certKey.len = data_len + issuer->len;
+ len = sn->len + issuer->len;
+ if (len > sizeof (keyBuf)) {
+ certKey.data = (unsigned char*)PORT_Alloc(len);
+ } else {
+ certKey.data = keyBuf;
+ }
+
+ if ( certKey.data == NULL ) {
+ return(0);
+ }
+
+ /* first try the serial number as hand-decoded above*/
+ /* copy the serialNumber */
+ PORT_Memcpy(certKey.data, &sn->data[index], data_len);
+
+ /* copy the issuer */
+ PORT_Memcpy( &certKey.data[data_len],issuer->data,issuer->len);
+
+ trust = nsslowcert_FindTrustByKey(handle, &certKey);
+ if (trust) {
+ pkcs11_freeStaticData(certKey.data, keyBuf);
+ return (trust);
+ }
+
+ if (index == 0) {
+ pkcs11_freeStaticData(certKey.data, keyBuf);
+ return NULL;
+ }
+
+ /* didn't find it, try by der encoded serial number */
+ /* copy the serialNumber */
+ PORT_Memcpy(certKey.data, sn->data, sn->len);
+
+ /* copy the issuer */
+ PORT_Memcpy( &certKey.data[sn->len], issuer->data, issuer->len);
+ certKey.len = sn->len + issuer->len;
+
+ trust = nsslowcert_FindTrustByKey(handle, &certKey);
+
+ pkcs11_freeStaticData(certKey.data, keyBuf);
+
+ return(trust);
+}
+
+/*
+ * look for the given DER certificate in the database
+ */
+NSSLOWCERTCertificate *
+nsslowcert_FindCertByDERCert(NSSLOWCERTCertDBHandle *handle, SECItem *derCert)
+{
+ PRArenaPool *arena;
+ SECItem certKey;
+ SECStatus rv;
+ NSSLOWCERTCertificate *cert = NULL;
+
+ /* create a scratch arena */
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( arena == NULL ) {
+ return(NULL);
+ }
+
+ /* extract the database key from the cert */
+ rv = nsslowcert_KeyFromDERCert(arena, derCert, &certKey);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ /* find the certificate */
+ cert = nsslowcert_FindCertByKey(handle, &certKey);
+
+loser:
+ PORT_FreeArena(arena, PR_FALSE);
+ return(cert);
+}
+
+static void
+DestroyCertificate(NSSLOWCERTCertificate *cert, PRBool lockdb)
+{
+ int refCount;
+ NSSLOWCERTCertDBHandle *handle;
+
+ if ( cert ) {
+
+ handle = cert->dbhandle;
+
+ /*
+ * handle may be NULL, for example if the cert was created with
+ * nsslowcert_DecodeDERCertificate.
+ */
+ if ( lockdb && handle ) {
+ nsslowcert_LockDB(handle);
+ }
+
+ nsslowcert_LockCertRefCount(cert);
+ PORT_Assert(cert->referenceCount > 0);
+ refCount = --cert->referenceCount;
+ nsslowcert_UnlockCertRefCount(cert);
+
+ if ( ( refCount == 0 ) ) {
+ certDBEntryCert *entry = cert->dbEntry;
+
+ if ( entry ) {
+ DestroyDBEntry((certDBEntry *)entry);
+ }
+
+ pkcs11_freeNickname(cert->nickname,cert->nicknameSpace);
+ pkcs11_freeNickname(cert->emailAddr,cert->emailAddrSpace);
+ pkcs11_freeStaticData(cert->certKey.data,cert->certKeySpace);
+ cert->certKey.data = NULL;
+ cert->nickname = NULL;
+
+ /* zero cert before freeing. Any stale references to this cert
+ * after this point will probably cause an exception. */
+ PORT_Memset(cert, 0, sizeof *cert);
+
+ /* use reflock to protect the free list */
+ nsslowcert_LockFreeList();
+ if (certListCount > MAX_CERT_LIST_COUNT) {
+ PORT_Free(cert);
+ } else {
+ certListCount++;
+ cert->next = certListHead;
+ certListHead = cert;
+ }
+ nsslowcert_UnlockFreeList();
+ cert = NULL;
+ }
+ if ( lockdb && handle ) {
+ nsslowcert_UnlockDB(handle);
+ }
+ }
+
+ return;
+}
+
+NSSLOWCERTCertificate *
+nsslowcert_CreateCert(void)
+{
+ NSSLOWCERTCertificate *cert;
+ nsslowcert_LockFreeList();
+ cert = certListHead;
+ if (cert) {
+ certListHead = cert->next;
+ certListCount--;
+ }
+ PORT_Assert(certListCount >= 0);
+ nsslowcert_UnlockFreeList();
+ if (cert) {
+ return cert;
+ }
+ return PORT_ZNew(NSSLOWCERTCertificate);
+}
+
+static void
+DestroyCertFreeList(void)
+{
+ NSSLOWCERTCertificate *cert;
+
+ nsslowcert_LockFreeList();
+ while (NULL != (cert = certListHead)) {
+ certListCount--;
+ certListHead = cert->next;
+ PORT_Free(cert);
+ }
+ PORT_Assert(!certListCount);
+ certListCount = 0;
+ nsslowcert_UnlockFreeList();
+}
+
+void
+nsslowcert_DestroyTrust(NSSLOWCERTTrust *trust)
+{
+ certDBEntryCert *entry = trust->dbEntry;
+
+ if ( entry ) {
+ DestroyDBEntry((certDBEntry *)entry);
+ }
+ pkcs11_freeStaticData(trust->dbKey.data,trust->dbKeySpace);
+ PORT_Memset(trust, 0, sizeof(*trust));
+
+ nsslowcert_LockFreeList();
+ if (trustListCount > MAX_TRUST_LIST_COUNT) {
+ PORT_Free(trust);
+ } else {
+ trustListCount++;
+ trust->next = trustListHead;
+ trustListHead = trust;
+ }
+ nsslowcert_UnlockFreeList();
+
+ return;
+}
+
+void
+nsslowcert_DestroyCertificate(NSSLOWCERTCertificate *cert)
+{
+ DestroyCertificate(cert, PR_TRUE);
+ return;
+}
+
+static void
+nsslowcert_DestroyCertificateNoLocking(NSSLOWCERTCertificate *cert)
+{
+ DestroyCertificate(cert, PR_FALSE);
+ return;
+}
+
+/*
+ * Lookup a CRL in the databases. We mirror the same fast caching data base
+ * caching stuff used by certificates....?
+ */
+certDBEntryRevocation *
+nsslowcert_FindCrlByKey(NSSLOWCERTCertDBHandle *handle,
+ SECItem *crlKey, PRBool isKRL)
+{
+ SECItem keyitem;
+ DBT key;
+ SECStatus rv;
+ PRArenaPool *arena = NULL;
+ certDBEntryRevocation *entry = NULL;
+ certDBEntryType crlType = isKRL ? certDBEntryTypeKeyRevocation
+ : certDBEntryTypeRevocation;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( arena == NULL ) {
+ goto loser;
+ }
+
+ rv = EncodeDBGenericKey(crlKey, arena, &keyitem, crlType);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ key.data = keyitem.data;
+ key.size = keyitem.len;
+
+ /* find in perm database */
+ entry = ReadDBCrlEntry(handle, crlKey, crlType);
+
+ if ( entry == NULL ) {
+ goto loser;
+ }
+
+loser:
+ if ( arena ) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+
+ return entry;
+}
+
+/*
+ * replace the existing URL in the data base with a new one
+ */
+static SECStatus
+nsslowcert_UpdateCrl(NSSLOWCERTCertDBHandle *handle, SECItem *derCrl,
+ SECItem *crlKey, char *url, PRBool isKRL)
+{
+ SECStatus rv = SECFailure;
+ certDBEntryRevocation *entry = NULL;
+ certDBEntryType crlType = isKRL ? certDBEntryTypeKeyRevocation
+ : certDBEntryTypeRevocation;
+ DeleteDBCrlEntry(handle, crlKey, crlType);
+
+ /* Write the new entry into the data base */
+ entry = NewDBCrlEntry(derCrl, url, crlType, 0);
+ if (entry == NULL) goto done;
+
+ rv = WriteDBCrlEntry(handle, entry, crlKey);
+ if (rv != SECSuccess) goto done;
+
+done:
+ if (entry) {
+ DestroyDBEntry((certDBEntry *)entry);
+ }
+ return rv;
+}
+
+SECStatus
+nsslowcert_AddCrl(NSSLOWCERTCertDBHandle *handle, SECItem *derCrl,
+ SECItem *crlKey, char *url, PRBool isKRL)
+{
+ SECStatus rv;
+
+ rv = nsslowcert_UpdateCrl(handle, derCrl, crlKey, url, isKRL);
+
+ return rv;
+}
+
+SECStatus
+nsslowcert_DeletePermCRL(NSSLOWCERTCertDBHandle *handle, const SECItem *derName,
+ PRBool isKRL)
+{
+ SECStatus rv;
+ certDBEntryType crlType = isKRL ? certDBEntryTypeKeyRevocation
+ : certDBEntryTypeRevocation;
+
+ rv = DeleteDBCrlEntry(handle, derName, crlType);
+ if (rv != SECSuccess) goto done;
+
+done:
+ return rv;
+}
+
+
+PRBool
+nsslowcert_hasTrust(NSSLOWCERTCertTrust *trust)
+{
+ if (trust == NULL) {
+ return PR_FALSE;
+ }
+ return !((trust->sslFlags & CERTDB_TRUSTED_UNKNOWN) &&
+ (trust->emailFlags & CERTDB_TRUSTED_UNKNOWN) &&
+ (trust->objectSigningFlags & CERTDB_TRUSTED_UNKNOWN));
+}
+
+/*
+ * This function has the logic that decides if another person's cert and
+ * email profile from an S/MIME message should be saved. It can deal with
+ * the case when there is no profile.
+ */
+static SECStatus
+nsslowcert_UpdateSMimeProfile(NSSLOWCERTCertDBHandle *dbhandle,
+ char *emailAddr, SECItem *derSubject, SECItem *emailProfile,
+ SECItem *profileTime)
+{
+ certDBEntrySMime *entry = NULL;
+ SECStatus rv = SECFailure;;
+
+
+ /* find our existing entry */
+ entry = nsslowcert_ReadDBSMimeEntry(dbhandle, emailAddr);
+
+ if ( entry ) {
+ /* keep our old db entry consistant for old applications. */
+ if (!SECITEM_ItemsAreEqual(derSubject, &entry->subjectName)) {
+ nsslowcert_UpdateSubjectEmailAddr(dbhandle, &entry->subjectName,
+ emailAddr, nsslowcert_remove);
+ }
+ DestroyDBEntry((certDBEntry *)entry);
+ entry = NULL;
+ }
+
+ /* now save the entry */
+ entry = NewDBSMimeEntry(emailAddr, derSubject, emailProfile,
+ profileTime, 0);
+ if ( entry == NULL ) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ nsslowcert_LockDB(dbhandle);
+
+ rv = DeleteDBSMimeEntry(dbhandle, emailAddr);
+ /* if delete fails, try to write new entry anyway... */
+
+ /* link subject entry back here */
+ rv = nsslowcert_UpdateSubjectEmailAddr(dbhandle, derSubject, emailAddr,
+ nsslowcert_add);
+ if ( rv != SECSuccess ) {
+ nsslowcert_UnlockDB(dbhandle);
+ goto loser;
+ }
+
+ rv = WriteDBSMimeEntry(dbhandle, entry);
+ if ( rv != SECSuccess ) {
+ nsslowcert_UnlockDB(dbhandle);
+ goto loser;
+ }
+
+ nsslowcert_UnlockDB(dbhandle);
+
+ rv = SECSuccess;
+
+loser:
+ if ( entry ) {
+ DestroyDBEntry((certDBEntry *)entry);
+ }
+ return(rv);
+}
+
+SECStatus
+nsslowcert_SaveSMimeProfile(NSSLOWCERTCertDBHandle *dbhandle, char *emailAddr,
+ SECItem *derSubject, SECItem *emailProfile, SECItem *profileTime)
+{
+ SECStatus rv = SECFailure;;
+
+
+ rv = nsslowcert_UpdateSMimeProfile(dbhandle, emailAddr,
+ derSubject, emailProfile, profileTime);
+
+ return(rv);
+}
+
+void
+nsslowcert_DestroyFreeLists(void)
+{
+ DestroyCertEntryFreeList();
+ DestroyTrustFreeList();
+ DestroyCertFreeList();
+ PZ_DestroyLock(freeListLock);
+ freeListLock = NULL;
+}
+
+void
+nsslowcert_DestroyGlobalLocks(void)
+{
+ if (dbLock) {
+ PZ_DestroyLock(dbLock);
+ dbLock = NULL;
+ }
+ if (certRefCountLock) {
+ PZ_DestroyLock(certRefCountLock);
+ certRefCountLock = NULL;
+ }
+ if (certTrustLock) {
+ PZ_DestroyLock(certTrustLock);
+ certTrustLock = NULL;
+ }
+}
+
+certDBEntry *
+nsslowcert_DecodeAnyDBEntry(SECItem *dbData, const SECItem *dbKey,
+ certDBEntryType entryType, void *pdata)
+{
+ PLArenaPool *arena = NULL;
+ certDBEntry *entry;
+ SECStatus rv;
+ SECItem dbEntry;
+
+
+ if ((dbData->len < SEC_DB_ENTRY_HEADER_LEN) || (dbKey->len == 0)) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ goto loser;
+ }
+ dbEntry.data = &dbData->data[SEC_DB_ENTRY_HEADER_LEN];
+ dbEntry.len = dbData->len - SEC_DB_ENTRY_HEADER_LEN;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (arena == NULL) {
+ goto loser;
+ }
+ entry = PORT_ArenaZNew(arena, certDBEntry);
+ if (!entry)
+ goto loser;
+
+ entry->common.version = (unsigned int)dbData->data[0];
+ entry->common.flags = (unsigned int)dbData->data[2];
+ entry->common.type = entryType;
+ entry->common.arena = arena;
+
+ switch (entryType) {
+ case certDBEntryTypeContentVersion: /* This type appears to be unused */
+ case certDBEntryTypeVersion: /* This type has only the common hdr */
+ rv = SECSuccess;
+ break;
+
+ case certDBEntryTypeSubject:
+ rv = DecodeDBSubjectEntry(&entry->subject, &dbEntry, dbKey);
+ break;
+
+ case certDBEntryTypeNickname:
+ rv = DecodeDBNicknameEntry(&entry->nickname, &dbEntry,
+ (char *)dbKey->data);
+ break;
+
+ /* smime profiles need entries created after the certs have
+ * been imported, loop over them in a second run */
+ case certDBEntryTypeSMimeProfile:
+ rv = DecodeDBSMimeEntry(&entry->smime, &dbEntry, (char *)dbKey->data);
+ break;
+
+ case certDBEntryTypeCert:
+ rv = DecodeDBCertEntry(&entry->cert, &dbEntry);
+ break;
+
+ case certDBEntryTypeKeyRevocation:
+ case certDBEntryTypeRevocation:
+ rv = DecodeDBCrlEntry(&entry->revocation, &dbEntry);
+ break;
+
+ default:
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ rv = SECFailure;
+ }
+
+ if (rv == SECSuccess)
+ return entry;
+
+loser:
+ if (arena)
+ PORT_FreeArena(arena, PR_FALSE);
+ return NULL;
+}
+
diff --git a/security/nss/lib/softoken/legacydb/pcertt.h b/security/nss/lib/softoken/legacydb/pcertt.h
new file mode 100644
index 000000000..476a2d079
--- /dev/null
+++ b/security/nss/lib/softoken/legacydb/pcertt.h
@@ -0,0 +1,450 @@
+/* ***** 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 ***** */
+/*
+ * certt.h - public data structures for the certificate library
+ *
+ * $Id$
+ */
+#ifndef _PCERTT_H_
+#define _PCERTT_H_
+
+#include "prclist.h"
+#include "pkcs11t.h"
+#include "seccomon.h"
+#include "secoidt.h"
+#include "plarena.h"
+#include "prcvar.h"
+#include "nssilock.h"
+#include "prio.h"
+#include "prmon.h"
+
+/* Non-opaque objects */
+typedef struct NSSLOWCERTCertDBHandleStr NSSLOWCERTCertDBHandle;
+typedef struct NSSLOWCERTCertKeyStr NSSLOWCERTCertKey;
+
+typedef struct NSSLOWCERTTrustStr NSSLOWCERTTrust;
+typedef struct NSSLOWCERTCertTrustStr NSSLOWCERTCertTrust;
+typedef struct NSSLOWCERTCertificateStr NSSLOWCERTCertificate;
+typedef struct NSSLOWCERTCertificateListStr NSSLOWCERTCertificateList;
+typedef struct NSSLOWCERTIssuerAndSNStr NSSLOWCERTIssuerAndSN;
+typedef struct NSSLOWCERTSignedDataStr NSSLOWCERTSignedData;
+typedef struct NSSLOWCERTSubjectPublicKeyInfoStr NSSLOWCERTSubjectPublicKeyInfo;
+typedef struct NSSLOWCERTValidityStr NSSLOWCERTValidity;
+
+/*
+** An X.509 validity object
+*/
+struct NSSLOWCERTValidityStr {
+ PRArenaPool *arena;
+ SECItem notBefore;
+ SECItem notAfter;
+};
+
+/*
+ * A serial number and issuer name, which is used as a database key
+ */
+struct NSSLOWCERTCertKeyStr {
+ SECItem serialNumber;
+ SECItem derIssuer;
+};
+
+/*
+** A signed data object. Used to implement the "signed" macro used
+** in the X.500 specs.
+*/
+struct NSSLOWCERTSignedDataStr {
+ SECItem data;
+ SECAlgorithmID signatureAlgorithm;
+ SECItem signature;
+};
+
+/*
+** An X.509 subject-public-key-info object
+*/
+struct NSSLOWCERTSubjectPublicKeyInfoStr {
+ PRArenaPool *arena;
+ SECAlgorithmID algorithm;
+ SECItem subjectPublicKey;
+};
+
+typedef struct _certDBEntryCert certDBEntryCert;
+typedef struct _certDBEntryRevocation certDBEntryRevocation;
+
+struct NSSLOWCERTCertTrustStr {
+ unsigned int sslFlags;
+ unsigned int emailFlags;
+ unsigned int objectSigningFlags;
+};
+
+/*
+** PKCS11 Trust representation
+*/
+struct NSSLOWCERTTrustStr {
+ NSSLOWCERTTrust *next;
+ NSSLOWCERTCertDBHandle *dbhandle;
+ SECItem dbKey; /* database key for this cert */
+ certDBEntryCert *dbEntry; /* database entry struct */
+ NSSLOWCERTCertTrust *trust;
+ SECItem *derCert; /* original DER for the cert */
+ unsigned char dbKeySpace[512];
+};
+
+/*
+** An X.509 certificate object (the unsigned form)
+*/
+struct NSSLOWCERTCertificateStr {
+ /* the arena is used to allocate any data structures that have the same
+ * lifetime as the cert. This is all stuff that hangs off of the cert
+ * structure, and is all freed at the same time. I is used when the
+ * cert is decoded, destroyed, and at some times when it changes
+ * state
+ */
+ NSSLOWCERTCertificate *next;
+ NSSLOWCERTCertDBHandle *dbhandle;
+
+ SECItem derCert; /* original DER for the cert */
+ SECItem derIssuer; /* DER for issuer name */
+ SECItem derSN;
+ SECItem serialNumber;
+ SECItem derSubject; /* DER for subject name */
+ SECItem derSubjKeyInfo;
+ NSSLOWCERTSubjectPublicKeyInfo *subjectPublicKeyInfo;
+ SECItem certKey; /* database key for this cert */
+ SECItem validity;
+ certDBEntryCert *dbEntry; /* database entry struct */
+ SECItem subjectKeyID; /* x509v3 subject key identifier */
+ SECItem extensions;
+ char *nickname;
+ char *emailAddr;
+ NSSLOWCERTCertTrust *trust;
+
+ /* the reference count is modified whenever someone looks up, dups
+ * or destroys a certificate
+ */
+ int referenceCount;
+
+ char nicknameSpace[200];
+ char emailAddrSpace[200];
+ unsigned char certKeySpace[512];
+};
+
+#define SEC_CERTIFICATE_VERSION_1 0 /* default created */
+#define SEC_CERTIFICATE_VERSION_2 1 /* v2 */
+#define SEC_CERTIFICATE_VERSION_3 2 /* v3 extensions */
+
+#define SEC_CRL_VERSION_1 0 /* default */
+#define SEC_CRL_VERSION_2 1 /* v2 extensions */
+
+struct NSSLOWCERTIssuerAndSNStr {
+ SECItem derIssuer;
+ SECItem serialNumber;
+};
+
+typedef SECStatus (* NSSLOWCERTCertCallback)(NSSLOWCERTCertificate *cert, void *arg);
+
+/* This is the typedef for the callback passed to nsslowcert_OpenCertDB() */
+/* callback to return database name based on version number */
+typedef char * (*NSSLOWCERTDBNameFunc)(void *arg, int dbVersion);
+
+/* XXX Lisa thinks the template declarations belong in cert.h, not here? */
+
+#include "secasn1t.h" /* way down here because I expect template stuff to
+ * move out of here anyway */
+
+/*
+ * Certificate Database related definitions and data structures
+ */
+
+/* version number of certificate database */
+#define CERT_DB_FILE_VERSION 8
+#define CERT_DB_V7_FILE_VERSION 7
+#define CERT_DB_CONTENT_VERSION 2
+
+#define SEC_DB_ENTRY_HEADER_LEN 3
+#define SEC_DB_KEY_HEADER_LEN 1
+
+/* All database entries have this form:
+ *
+ * byte offset field
+ * ----------- -----
+ * 0 version
+ * 1 type
+ * 2 flags
+ */
+
+/* database entry types */
+typedef enum {
+ certDBEntryTypeVersion = 0,
+ certDBEntryTypeCert = 1,
+ certDBEntryTypeNickname = 2,
+ certDBEntryTypeSubject = 3,
+ certDBEntryTypeRevocation = 4,
+ certDBEntryTypeKeyRevocation = 5,
+ certDBEntryTypeSMimeProfile = 6,
+ certDBEntryTypeContentVersion = 7,
+ certDBEntryTypeBlob = 8
+} certDBEntryType;
+
+typedef struct {
+ certDBEntryType type;
+ unsigned int version;
+ unsigned int flags;
+ PRArenaPool *arena;
+} certDBEntryCommon;
+
+/*
+ * Certificate entry:
+ *
+ * byte offset field
+ * ----------- -----
+ * 0 sslFlags-msb
+ * 1 sslFlags-lsb
+ * 2 emailFlags-msb
+ * 3 emailFlags-lsb
+ * 4 objectSigningFlags-msb
+ * 5 objectSigningFlags-lsb
+ * 6 derCert-len-msb
+ * 7 derCert-len-lsb
+ * 8 nickname-len-msb
+ * 9 nickname-len-lsb
+ * ... derCert
+ * ... nickname
+ *
+ * NOTE: the nickname string as stored in the database is null terminated,
+ * in other words, the last byte of the db entry is always 0
+ * if a nickname is present.
+ * NOTE: if nickname is not present, then nickname-len-msb and
+ * nickname-len-lsb will both be zero.
+ */
+struct _certDBEntryCert {
+ certDBEntryCommon common;
+ certDBEntryCert *next;
+ NSSLOWCERTCertTrust trust;
+ SECItem derCert;
+ char *nickname;
+ char nicknameSpace[200];
+ unsigned char derCertSpace[2048];
+};
+
+/*
+ * Certificate Nickname entry:
+ *
+ * byte offset field
+ * ----------- -----
+ * 0 subjectname-len-msb
+ * 1 subjectname-len-lsb
+ * 2... subjectname
+ *
+ * The database key for this type of entry is a nickname string
+ * The "subjectname" value is the DER encoded DN of the identity
+ * that matches this nickname.
+ */
+typedef struct {
+ certDBEntryCommon common;
+ char *nickname;
+ SECItem subjectName;
+} certDBEntryNickname;
+
+#define DB_NICKNAME_ENTRY_HEADER_LEN 2
+
+/*
+ * Certificate Subject entry:
+ *
+ * byte offset field
+ * ----------- -----
+ * 0 ncerts-msb
+ * 1 ncerts-lsb
+ * 2 nickname-msb
+ * 3 nickname-lsb
+ * 4 emailAddr-msb
+ * 5 emailAddr-lsb
+ * ... nickname
+ * ... emailAddr
+ * ...+2*i certkey-len-msb
+ * ...+1+2*i certkey-len-lsb
+ * ...+2*ncerts+2*i keyid-len-msb
+ * ...+1+2*ncerts+2*i keyid-len-lsb
+ * ... certkeys
+ * ... keyids
+ *
+ * The database key for this type of entry is the DER encoded subject name
+ * The "certkey" value is an array of certificate database lookup keys that
+ * points to the database entries for the certificates that matche
+ * this subject.
+ *
+ */
+typedef struct _certDBEntrySubject {
+ certDBEntryCommon common;
+ SECItem derSubject;
+ unsigned int ncerts;
+ char *nickname;
+ SECItem *certKeys;
+ SECItem *keyIDs;
+ char **emailAddrs;
+ unsigned int nemailAddrs;
+} certDBEntrySubject;
+
+#define DB_SUBJECT_ENTRY_HEADER_LEN 6
+
+/*
+ * Certificate SMIME profile entry:
+ *
+ * byte offset field
+ * ----------- -----
+ * 0 subjectname-len-msb
+ * 1 subjectname-len-lsb
+ * 2 smimeoptions-len-msb
+ * 3 smimeoptions-len-lsb
+ * 4 options-date-len-msb
+ * 5 options-date-len-lsb
+ * 6... subjectname
+ * ... smimeoptions
+ * ... options-date
+ *
+ * The database key for this type of entry is the email address string
+ * The "subjectname" value is the DER encoded DN of the identity
+ * that matches this nickname.
+ * The "smimeoptions" value is a string that represents the algorithm
+ * capabilities on the remote user.
+ * The "options-date" is the date that the smime options value was created.
+ * This is generally the signing time of the signed message that contained
+ * the options. It is a UTCTime value.
+ */
+typedef struct {
+ certDBEntryCommon common;
+ char *emailAddr;
+ SECItem subjectName;
+ SECItem smimeOptions;
+ SECItem optionsDate;
+} certDBEntrySMime;
+
+#define DB_SMIME_ENTRY_HEADER_LEN 6
+
+/*
+ * Crl/krl entry:
+ *
+ * byte offset field
+ * ----------- -----
+ * 0 derCert-len-msb
+ * 1 derCert-len-lsb
+ * 2 url-len-msb
+ * 3 url-len-lsb
+ * ... derCert
+ * ... url
+ *
+ * NOTE: the url string as stored in the database is null terminated,
+ * in other words, the last byte of the db entry is always 0
+ * if a nickname is present.
+ * NOTE: if url is not present, then url-len-msb and
+ * url-len-lsb will both be zero.
+ */
+#define DB_CRL_ENTRY_HEADER_LEN 4
+struct _certDBEntryRevocation {
+ certDBEntryCommon common;
+ SECItem derCrl;
+ char *url; /* where to load the crl from */
+};
+
+/*
+ * Database Version Entry:
+ *
+ * byte offset field
+ * ----------- -----
+ * only the low level header...
+ *
+ * The database key for this type of entry is the string "Version"
+ */
+typedef struct {
+ certDBEntryCommon common;
+} certDBEntryVersion;
+
+#define SEC_DB_VERSION_KEY "Version"
+#define SEC_DB_VERSION_KEY_LEN sizeof(SEC_DB_VERSION_KEY)
+
+/*
+ * Database Content Version Entry:
+ *
+ * byte offset field
+ * ----------- -----
+ * 0 contentVersion
+ *
+ * The database key for this type of entry is the string "ContentVersion"
+ */
+typedef struct {
+ certDBEntryCommon common;
+ char contentVersion;
+} certDBEntryContentVersion;
+
+#define SEC_DB_CONTENT_VERSION_KEY "ContentVersion"
+#define SEC_DB_CONTENT_VERSION_KEY_LEN sizeof(SEC_DB_CONTENT_VERSION_KEY)
+
+typedef union {
+ certDBEntryCommon common;
+ certDBEntryCert cert;
+ certDBEntryContentVersion content;
+ certDBEntryNickname nickname;
+ certDBEntryRevocation revocation;
+ certDBEntrySMime smime;
+ certDBEntrySubject subject;
+ certDBEntryVersion version;
+} certDBEntry;
+
+/* length of the fixed part of a database entry */
+#define DBCERT_V4_HEADER_LEN 7
+#define DB_CERT_V5_ENTRY_HEADER_LEN 7
+#define DB_CERT_V6_ENTRY_HEADER_LEN 7
+#define DB_CERT_ENTRY_HEADER_LEN 10
+
+/* common flags for all types of certificates */
+#define CERTDB_VALID_PEER (1<<0)
+#define CERTDB_TRUSTED (1<<1)
+#define CERTDB_SEND_WARN (1<<2)
+#define CERTDB_VALID_CA (1<<3)
+#define CERTDB_TRUSTED_CA (1<<4) /* trusted for issuing server certs */
+#define CERTDB_NS_TRUSTED_CA (1<<5)
+#define CERTDB_USER (1<<6)
+#define CERTDB_TRUSTED_CLIENT_CA (1<<7) /* trusted for issuing client certs */
+#define CERTDB_INVISIBLE_CA (1<<8) /* don't show in UI */
+#define CERTDB_GOVT_APPROVED_CA (1<<9) /* can do strong crypto in export ver */
+#define CERTDB_NOT_TRUSTED (1<<10) /* explicitly don't trust this cert */
+#define CERTDB_TRUSTED_UNKNOWN (1<<11) /* accept trust from another source */
+
+/* bits not affected by the CKO_NETSCAPE_TRUST object */
+#define CERTDB_PRESERVE_TRUST_BITS (CERTDB_USER | CERTDB_VALID_PEER | \
+ CERTDB_NS_TRUSTED_CA | CERTDB_VALID_CA | CERTDB_INVISIBLE_CA | \
+ CERTDB_GOVT_APPROVED_CA)
+
+#endif /* _PCERTT_H_ */
diff --git a/security/nss/lib/softoken/legacydb/pk11db.c b/security/nss/lib/softoken/legacydb/pk11db.c
new file mode 100644
index 000000000..f85a0a6c3
--- /dev/null
+++ b/security/nss/lib/softoken/legacydb/pk11db.c
@@ -0,0 +1,773 @@
+/* ***** 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 ***** */
+/*
+ * The following code handles the storage of PKCS 11 modules used by the
+ * NSS. This file is written to abstract away how the modules are
+ * stored so we can deside that later.
+ */
+
+#include "pk11pars.h"
+#include "lgdb.h"
+#include "mcom_db.h"
+#include "secerr.h"
+
+#define FREE_CLEAR(p) if (p) { PORT_Free(p); p = NULL; }
+
+/* Construct a database key for a given module */
+static SECStatus secmod_MakeKey(DBT *key, char * module) {
+ int len = 0;
+ char *commonName;
+
+ commonName = secmod_argGetParamValue("name",module);
+ if (commonName == NULL) {
+ commonName = secmod_argGetParamValue("library",module);
+ }
+ if (commonName == NULL) return SECFailure;
+ len = PORT_Strlen(commonName);
+ key->data = commonName;
+ key->size = len;
+ return SECSuccess;
+}
+
+/* free out constructed database key */
+static void
+secmod_FreeKey(DBT *key)
+{
+ if (key->data) {
+ PORT_Free(key->data);
+ }
+ key->data = NULL;
+ key->size = 0;
+}
+
+typedef struct secmodDataStr secmodData;
+typedef struct secmodSlotDataStr secmodSlotData;
+struct secmodDataStr {
+ unsigned char major;
+ unsigned char minor;
+ unsigned char nameStart[2];
+ unsigned char slotOffset[2];
+ unsigned char internal;
+ unsigned char fips;
+ unsigned char ssl[8];
+ unsigned char trustOrder[4];
+ unsigned char cipherOrder[4];
+ unsigned char reserved1;
+ unsigned char isModuleDB;
+ unsigned char isModuleDBOnly;
+ unsigned char isCritical;
+ unsigned char reserved[4];
+ unsigned char names[6]; /* enough space for the length fields */
+};
+
+struct secmodSlotDataStr {
+ unsigned char slotID[4];
+ unsigned char defaultFlags[4];
+ unsigned char timeout[4];
+ unsigned char askpw;
+ unsigned char hasRootCerts;
+ unsigned char reserved[18]; /* this makes it a round 32 bytes */
+};
+
+#define SECMOD_DB_VERSION_MAJOR 0
+#define SECMOD_DB_VERSION_MINOR 6
+#define SECMOD_DB_EXT1_VERSION_MAJOR 0
+#define SECMOD_DB_EXT1_VERSION_MINOR 6
+#define SECMOD_DB_NOUI_VERSION_MAJOR 0
+#define SECMOD_DB_NOUI_VERSION_MINOR 4
+
+#define SECMOD_PUTSHORT(dest,src) \
+ (dest)[1] = (unsigned char) ((src)&0xff); \
+ (dest)[0] = (unsigned char) (((src) >> 8) & 0xff);
+#define SECMOD_PUTLONG(dest,src) \
+ (dest)[3] = (unsigned char) ((src)&0xff); \
+ (dest)[2] = (unsigned char) (((src) >> 8) & 0xff); \
+ (dest)[1] = (unsigned char) (((src) >> 16) & 0xff); \
+ (dest)[0] = (unsigned char) (((src) >> 24) & 0xff);
+#define SECMOD_GETSHORT(src) \
+ ((unsigned short) (((src)[0] << 8) | (src)[1]))
+#define SECMOD_GETLONG(src) \
+ ((unsigned long) (( (unsigned long) (src)[0] << 24) | \
+ ( (unsigned long) (src)[1] << 16) | \
+ ( (unsigned long) (src)[2] << 8) | \
+ (unsigned long) (src)[3]))
+
+/*
+ * build a data base entry from a module
+ */
+static SECStatus
+secmod_EncodeData(DBT *data, char * module)
+{
+ secmodData *encoded = NULL;
+ secmodSlotData *slot;
+ unsigned char *dataPtr;
+ unsigned short len, len2 = 0, len3 = 0;
+ int count = 0;
+ unsigned short offset;
+ int dataLen, i;
+ unsigned long order;
+ unsigned long ssl[2];
+ char *commonName = NULL , *dllName = NULL, *param = NULL, *nss = NULL;
+ char *slotParams, *ciphers;
+ PK11PreSlotInfo *slotInfo = NULL;
+ SECStatus rv = SECFailure;
+
+ rv = secmod_argParseModuleSpec(module,&dllName,&commonName,&param,&nss);
+ if (rv != SECSuccess) return rv;
+ rv = SECFailure;
+
+ if (commonName == NULL) {
+ /* set error */
+ goto loser;
+ }
+
+ len = PORT_Strlen(commonName);
+ if (dllName) {
+ len2 = PORT_Strlen(dllName);
+ }
+ if (param) {
+ len3 = PORT_Strlen(param);
+ }
+
+ slotParams = secmod_argGetParamValue("slotParams",nss);
+ slotInfo = secmod_argParseSlotInfo(NULL,slotParams,&count);
+ if (slotParams) PORT_Free(slotParams);
+
+ if (count && slotInfo == NULL) {
+ /* set error */
+ goto loser;
+ }
+
+ dataLen = sizeof(secmodData) + len + len2 + len3 + sizeof(unsigned short) +
+ count*sizeof(secmodSlotData);
+
+ data->data = (unsigned char *) PORT_ZAlloc(dataLen);
+ encoded = (secmodData *)data->data;
+ dataPtr = (unsigned char *) data->data;
+ data->size = dataLen;
+
+ if (encoded == NULL) {
+ /* set error */
+ goto loser;
+ }
+
+ encoded->major = SECMOD_DB_VERSION_MAJOR;
+ encoded->minor = SECMOD_DB_VERSION_MINOR;
+ encoded->internal = (unsigned char)
+ (secmod_argHasFlag("flags","internal",nss) ? 1 : 0);
+ encoded->fips = (unsigned char)
+ (secmod_argHasFlag("flags","FIPS",nss) ? 1 : 0);
+ encoded->isModuleDB = (unsigned char)
+ (secmod_argHasFlag("flags","isModuleDB",nss) ? 1 : 0);
+ encoded->isModuleDBOnly = (unsigned char)
+ (secmod_argHasFlag("flags","isModuleDBOnly",nss) ? 1 : 0);
+ encoded->isCritical = (unsigned char)
+ (secmod_argHasFlag("flags","critical",nss) ? 1 : 0);
+
+ order = secmod_argReadLong("trustOrder", nss, SECMOD_DEFAULT_TRUST_ORDER,
+ NULL);
+ SECMOD_PUTLONG(encoded->trustOrder,order);
+ order = secmod_argReadLong("cipherOrder", nss, SECMOD_DEFAULT_CIPHER_ORDER,
+ NULL);
+ SECMOD_PUTLONG(encoded->cipherOrder,order);
+
+
+ ciphers = secmod_argGetParamValue("ciphers",nss);
+ secmod_argSetNewCipherFlags(&ssl[0], ciphers);
+ SECMOD_PUTLONG(encoded->ssl,ssl[0]);
+ SECMOD_PUTLONG(&encoded->ssl[4],ssl[1]);
+ if (ciphers) PORT_Free(ciphers);
+
+ offset = (unsigned short) &(((secmodData *)0)->names[0]);
+ SECMOD_PUTSHORT(encoded->nameStart,offset);
+ offset = offset + len + len2 + len3 + 3*sizeof(unsigned short);
+ SECMOD_PUTSHORT(encoded->slotOffset,offset);
+
+
+ SECMOD_PUTSHORT(&dataPtr[offset],((unsigned short)count));
+ slot = (secmodSlotData *)(dataPtr+offset+sizeof(unsigned short));
+
+ offset = 0;
+ SECMOD_PUTSHORT(encoded->names,len);
+ offset += sizeof(unsigned short);
+ PORT_Memcpy(&encoded->names[offset],commonName,len);
+ offset += len;
+
+
+ SECMOD_PUTSHORT(&encoded->names[offset],len2);
+ offset += sizeof(unsigned short);
+ if (len2) PORT_Memcpy(&encoded->names[offset],dllName,len2);
+ offset += len2;
+
+ SECMOD_PUTSHORT(&encoded->names[offset],len3);
+ offset += sizeof(unsigned short);
+ if (len3) PORT_Memcpy(&encoded->names[offset],param,len3);
+ offset += len3;
+
+ if (count) {
+ for (i=0; i < count; i++) {
+ SECMOD_PUTLONG(slot[i].slotID, slotInfo[i].slotID);
+ SECMOD_PUTLONG(slot[i].defaultFlags,
+ slotInfo[i].defaultFlags);
+ SECMOD_PUTLONG(slot[i].timeout,slotInfo[i].timeout);
+ slot[i].askpw = slotInfo[i].askpw;
+ slot[i].hasRootCerts = slotInfo[i].hasRootCerts;
+ PORT_Memset(slot[i].reserved, 0, sizeof(slot[i].reserved));
+ }
+ }
+ rv = SECSuccess;
+
+loser:
+ if (commonName) PORT_Free(commonName);
+ if (dllName) PORT_Free(dllName);
+ if (param) PORT_Free(param);
+ if (slotInfo) PORT_Free(slotInfo);
+ if (nss) PORT_Free(nss);
+ return rv;
+
+}
+
+static void
+secmod_FreeData(DBT *data)
+{
+ if (data->data) {
+ PORT_Free(data->data);
+ }
+}
+
+static void
+secmod_FreeSlotStrings(char **slotStrings, int count)
+{
+ int i;
+
+ for (i=0; i < count; i++) {
+ if (slotStrings[i]) {
+ PR_smprintf_free(slotStrings[i]);
+ slotStrings[i] = NULL;
+ }
+ }
+}
+
+/*
+ * build a module from the data base entry.
+ */
+static char *
+secmod_DecodeData(char *defParams, DBT *data, PRBool *retInternal)
+{
+ secmodData *encoded;
+ secmodSlotData *slots;
+ PLArenaPool *arena;
+ char *commonName = NULL;
+ char *dllName = NULL;
+ char *parameters = NULL;
+ char *nss;
+ char *moduleSpec;
+ char **slotStrings = NULL;
+ unsigned char *names;
+ unsigned long slotCount;
+ unsigned long ssl0 =0;
+ unsigned long ssl1 =0;
+ unsigned long slotID;
+ unsigned long defaultFlags;
+ unsigned long timeout;
+ unsigned long trustOrder =SECMOD_DEFAULT_TRUST_ORDER;
+ unsigned long cipherOrder =SECMOD_DEFAULT_CIPHER_ORDER;
+ unsigned short len;
+ unsigned short namesOffset = 0; /* start of the names block */
+ unsigned long namesRunningOffset; /* offset to name we are
+ * currently processing */
+ unsigned short slotOffset;
+ PRBool isOldVersion = PR_FALSE;
+ PRBool internal;
+ PRBool isFIPS;
+ PRBool isModuleDB =PR_FALSE;
+ PRBool isModuleDBOnly =PR_FALSE;
+ PRBool extended =PR_FALSE;
+ int i;
+
+
+ arena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
+ if (arena == NULL)
+ return NULL;
+
+#define CHECK_SIZE(x) \
+ if ((unsigned int) data->size < (unsigned int)(x)) goto db_loser
+
+ /* -------------------------------------------------------------
+ ** Process the buffer header, which is the secmodData struct.
+ ** It may be an old or new version. Check the length for each.
+ */
+
+ CHECK_SIZE( offsetof(secmodData, trustOrder[0]) );
+
+ encoded = (secmodData *)data->data;
+
+ internal = (encoded->internal != 0) ? PR_TRUE: PR_FALSE;
+ isFIPS = (encoded->fips != 0) ? PR_TRUE: PR_FALSE;
+
+ if (retInternal)
+ *retInternal = internal;
+ if (internal) {
+ parameters = PORT_ArenaStrdup(arena,defParams);
+ if (parameters == NULL)
+ goto loser;
+ }
+ if (internal && (encoded->major == SECMOD_DB_NOUI_VERSION_MAJOR) &&
+ (encoded->minor <= SECMOD_DB_NOUI_VERSION_MINOR)) {
+ isOldVersion = PR_TRUE;
+ }
+ if ((encoded->major == SECMOD_DB_EXT1_VERSION_MAJOR) &&
+ (encoded->minor >= SECMOD_DB_EXT1_VERSION_MINOR)) {
+ CHECK_SIZE( sizeof(secmodData));
+ trustOrder = SECMOD_GETLONG(encoded->trustOrder);
+ cipherOrder = SECMOD_GETLONG(encoded->cipherOrder);
+ isModuleDB = (encoded->isModuleDB != 0) ? PR_TRUE: PR_FALSE;
+ isModuleDBOnly = (encoded->isModuleDBOnly != 0) ? PR_TRUE: PR_FALSE;
+ extended = PR_TRUE;
+ }
+ if (internal && !extended) {
+ trustOrder = 0;
+ cipherOrder = 100;
+ }
+ /* decode SSL cipher enable flags */
+ ssl0 = SECMOD_GETLONG(encoded->ssl);
+ ssl1 = SECMOD_GETLONG(encoded->ssl + 4);
+
+ slotOffset = SECMOD_GETSHORT(encoded->slotOffset);
+ namesOffset = SECMOD_GETSHORT(encoded->nameStart);
+
+
+ /*--------------------------------------------------------------
+ ** Now process the variable length set of names.
+ ** The names have this structure:
+ ** struct {
+ ** BYTE commonNameLen[ 2 ];
+ ** BYTE commonName [ commonNameLen ];
+ ** BTTE libNameLen [ 2 ];
+ ** BYTE libName [ libNameLen ];
+ ** If it is "extended" it also has these members:
+ ** BYTE initStringLen[ 2 ];
+ ** BYTE initString [ initStringLen ];
+ ** }
+ */
+
+ namesRunningOffset = namesOffset;
+ /* copy the module's common name */
+ CHECK_SIZE( namesRunningOffset + 2);
+ names = (unsigned char *)data->data;
+ len = SECMOD_GETSHORT(names+namesRunningOffset);
+
+ CHECK_SIZE( namesRunningOffset + 2 + len);
+ commonName = (char*)PORT_ArenaAlloc(arena,len+1);
+ if (commonName == NULL)
+ goto loser;
+ PORT_Memcpy(commonName, names + namesRunningOffset + 2, len);
+ commonName[len] = 0;
+ namesRunningOffset += len + 2;
+
+ /* copy the module's shared library file name. */
+ CHECK_SIZE( namesRunningOffset + 2);
+ len = SECMOD_GETSHORT(names + namesRunningOffset);
+ if (len) {
+ CHECK_SIZE( namesRunningOffset + 2 + len);
+ dllName = (char*)PORT_ArenaAlloc(arena,len + 1);
+ if (dllName == NULL)
+ goto loser;
+ PORT_Memcpy(dllName, names + namesRunningOffset + 2, len);
+ dllName[len] = 0;
+ }
+ namesRunningOffset += len + 2;
+
+ /* copy the module's initialization string, if present. */
+ if (!internal && extended) {
+ CHECK_SIZE( namesRunningOffset + 2);
+ len = SECMOD_GETSHORT(names+namesRunningOffset);
+ if (len) {
+ CHECK_SIZE( namesRunningOffset + 2 + len );
+ parameters = (char*)PORT_ArenaAlloc(arena,len + 1);
+ if (parameters == NULL)
+ goto loser;
+ PORT_Memcpy(parameters,names + namesRunningOffset + 2, len);
+ parameters[len] = 0;
+ }
+ namesRunningOffset += len + 2;
+ }
+
+ /*
+ * Consistency check: Make sure the slot and names blocks don't
+ * overlap. These blocks can occur in any order, so this check is made
+ * in 2 parts. First we check the case where the slot block starts
+ * after the name block. Later, when we have the slot block length,
+ * we check the case where slot block starts before the name block.
+ * NOTE: in most cases any overlap will likely be detected by invalid
+ * data read from the blocks, but it's better to find out sooner
+ * than later.
+ */
+ if (slotOffset >= namesOffset) { /* slot block starts after name block */
+ if (slotOffset < namesRunningOffset) {
+ goto db_loser;
+ }
+ }
+
+ /* ------------------------------------------------------------------
+ ** Part 3, process the slot table.
+ ** This part has this structure:
+ ** struct {
+ ** BYTE slotCount [ 2 ];
+ ** secmodSlotData [ slotCount ];
+ ** {
+ */
+
+ CHECK_SIZE( slotOffset + 2 );
+ slotCount = SECMOD_GETSHORT((unsigned char *)data->data + slotOffset);
+
+ /*
+ * Consistency check: Part 2. We now have the slot block length, we can
+ * check the case where the slotblock procedes the name block.
+ */
+ if (slotOffset < namesOffset) { /* slot block starts before name block */
+ if (namesOffset < slotOffset + 2 + slotCount*sizeof(secmodSlotData)) {
+ goto db_loser;
+ }
+ }
+
+ CHECK_SIZE( (slotOffset + 2 + slotCount * sizeof(secmodSlotData)));
+ slots = (secmodSlotData *) ((unsigned char *)data->data + slotOffset + 2);
+
+ /* slotCount; */
+ slotStrings = (char **)PORT_ArenaZAlloc(arena, slotCount * sizeof(char *));
+ if (slotStrings == NULL)
+ goto loser;
+ for (i=0; i < (int) slotCount; i++, slots++) {
+ PRBool hasRootCerts =PR_FALSE;
+ PRBool hasRootTrust =PR_FALSE;
+ slotID = SECMOD_GETLONG(slots->slotID);
+ defaultFlags = SECMOD_GETLONG(slots->defaultFlags);
+ timeout = SECMOD_GETLONG(slots->timeout);
+ hasRootCerts = slots->hasRootCerts;
+ if (isOldVersion && internal && (slotID != 2)) {
+ unsigned long internalFlags=
+ secmod_argSlotFlags("slotFlags",SECMOD_SLOT_FLAGS);
+ defaultFlags |= internalFlags;
+ }
+ if (hasRootCerts && !extended) {
+ trustOrder = 100;
+ }
+
+ slotStrings[i] = secmod_mkSlotString(slotID, defaultFlags, timeout,
+ (unsigned char)slots->askpw,
+ hasRootCerts, hasRootTrust);
+ if (slotStrings[i] == NULL) {
+ secmod_FreeSlotStrings(slotStrings,i);
+ goto loser;
+ }
+ }
+
+ nss = secmod_mkNSS(slotStrings, slotCount, internal, isFIPS, isModuleDB,
+ isModuleDBOnly, internal, trustOrder, cipherOrder,
+ ssl0, ssl1);
+ secmod_FreeSlotStrings(slotStrings,slotCount);
+ /* it's permissible (and normal) for nss to be NULL. it simply means
+ * there are no NSS specific parameters in the database */
+ moduleSpec = secmod_mkNewModuleSpec(dllName,commonName,parameters,nss);
+ PR_smprintf_free(nss);
+ PORT_FreeArena(arena,PR_TRUE);
+ return moduleSpec;
+
+db_loser:
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+loser:
+ PORT_FreeArena(arena,PR_TRUE);
+ return NULL;
+}
+
+static DB *
+secmod_OpenDB(const char *appName, const char *filename, const char *dbName,
+ PRBool readOnly, PRBool update)
+{
+ DB *pkcs11db = NULL;
+
+
+ if (appName) {
+ char *secname = PORT_Strdup(filename);
+ int len = strlen(secname);
+ int status = RDB_FAIL;
+
+ if (len >= 3 && PORT_Strcmp(&secname[len-3],".db") == 0) {
+ secname[len-3] = 0;
+ }
+ pkcs11db=
+ rdbopen(appName, "", secname, readOnly ? NO_RDONLY:NO_RDWR, NULL);
+ if (update && !pkcs11db) {
+ DB *updatedb;
+
+ pkcs11db = rdbopen(appName, "", secname, NO_CREATE, &status);
+ if (!pkcs11db) {
+ if (status == RDB_RETRY) {
+ pkcs11db= rdbopen(appName, "", secname,
+ readOnly ? NO_RDONLY:NO_RDWR, NULL);
+ }
+ PORT_Free(secname);
+ return pkcs11db;
+ }
+ updatedb = dbopen(dbName, NO_RDONLY, 0600, DB_HASH, 0);
+ if (updatedb) {
+ db_Copy(pkcs11db,updatedb);
+ (*updatedb->close)(updatedb);
+ } else {
+ (*pkcs11db->close)(pkcs11db);
+ PORT_Free(secname);
+ return NULL;
+ }
+ }
+ PORT_Free(secname);
+ return pkcs11db;
+ }
+
+ /* I'm sure we should do more checks here sometime... */
+ pkcs11db = dbopen(dbName, readOnly ? NO_RDONLY : NO_RDWR, 0600, DB_HASH, 0);
+
+ /* didn't exist? create it */
+ if (pkcs11db == NULL) {
+ if (readOnly)
+ return NULL;
+
+ pkcs11db = dbopen( dbName, NO_CREATE, 0600, DB_HASH, 0 );
+ if (pkcs11db)
+ (* pkcs11db->sync)(pkcs11db, 0);
+ }
+ return pkcs11db;
+}
+
+static void
+secmod_CloseDB(DB *pkcs11db)
+{
+ (*pkcs11db->close)(pkcs11db);
+}
+
+static char *
+secmod_addEscape(const char *string, char quote)
+{
+ char *newString = 0;
+ int escapes = 0, size = 0;
+ const char *src;
+ char *dest;
+
+ for (src=string; *src ; src++) {
+ if ((*src == quote) || (*src == '\\')) escapes++;
+ size++;
+ }
+
+ newString = PORT_ZAlloc(escapes+size+1);
+ if (newString == NULL) {
+ return NULL;
+ }
+
+ for (src=string, dest=newString; *src; src++,dest++) {
+ if ((*src == '\\') || (*src == quote)) {
+ *dest++ = '\\';
+ }
+ *dest = *src;
+ }
+
+ return newString;
+}
+
+SECStatus legacy_AddSecmodDB(const char *appName, const char *filename,
+ const char *dbname, char *module, PRBool rw);
+
+#define SECMOD_STEP 10
+#define SFTK_DEFAULT_INTERNAL_INIT "library= name=\"NSS Internal PKCS #11 Module\" parameters=\"%s\" NSS=\"Flags=internal,critical trustOrder=75 cipherOrder=100 slotParams=(1={%s askpw=any timeout=30})\""
+/*
+ * Read all the existing modules in
+ */
+char **
+legacy_ReadSecmodDB(const char *appName, const char *filename,
+ const char *dbname, char *params, PRBool rw)
+{
+ DBT key,data;
+ int ret;
+ DB *pkcs11db = NULL;
+ char **moduleList = NULL, **newModuleList = NULL;
+ int moduleCount = 1;
+ int useCount = SECMOD_STEP;
+
+ moduleList = (char **) PORT_ZAlloc(useCount*sizeof(char **));
+ if (moduleList == NULL) return NULL;
+
+ pkcs11db = secmod_OpenDB(appName,filename,dbname,PR_TRUE,rw);
+ if (pkcs11db == NULL) goto done;
+
+ /* read and parse the file or data base */
+ ret = (*pkcs11db->seq)(pkcs11db, &key, &data, R_FIRST);
+ if (ret) goto done;
+
+
+ do {
+ char *moduleString;
+ PRBool internal = PR_FALSE;
+ if ((moduleCount+1) >= useCount) {
+ useCount += SECMOD_STEP;
+ newModuleList =
+ (char **)PORT_Realloc(moduleList,useCount*sizeof(char *));
+ if (newModuleList == NULL) goto done;
+ moduleList = newModuleList;
+ PORT_Memset(&moduleList[moduleCount+1],0,
+ sizeof(char *)*SECMOD_STEP);
+ }
+ moduleString = secmod_DecodeData(params,&data,&internal);
+ if (internal) {
+ moduleList[0] = moduleString;
+ } else {
+ moduleList[moduleCount] = moduleString;
+ moduleCount++;
+ }
+ } while ( (*pkcs11db->seq)(pkcs11db, &key, &data, R_NEXT) == 0);
+
+done:
+ if (!moduleList[0]) {
+ char * newparams = secmod_addEscape(params,'"');
+ if (newparams) {
+ moduleList[0] = PR_smprintf(SFTK_DEFAULT_INTERNAL_INIT,newparams,
+ SECMOD_SLOT_FLAGS);
+ PORT_Free(newparams);
+ }
+ }
+ /* deal with trust cert db here */
+
+ if (pkcs11db) {
+ secmod_CloseDB(pkcs11db);
+ } else if (moduleList[0] && rw) {
+ legacy_AddSecmodDB(appName,filename,dbname,moduleList[0], rw) ;
+ }
+ if (!moduleList[0]) {
+ PORT_Free(moduleList);
+ moduleList = NULL;
+ }
+ return moduleList;
+}
+
+SECStatus
+legacy_ReleaseSecmodDBData(const char *appName, const char *filename,
+ const char *dbname, char **moduleSpecList, PRBool rw)
+{
+ if (moduleSpecList) {
+ char **index;
+ for(index = moduleSpecList; *index; index++) {
+ PR_smprintf_free(*index);
+ }
+ PORT_Free(moduleSpecList);
+ }
+ return SECSuccess;
+}
+
+/*
+ * Delete a module from the Data Base
+ */
+SECStatus
+legacy_DeleteSecmodDB(const char *appName, const char *filename,
+ const char *dbname, char *args, PRBool rw)
+{
+ DBT key;
+ SECStatus rv = SECFailure;
+ DB *pkcs11db = NULL;
+ int ret;
+
+ if (!rw) return SECFailure;
+
+ /* make sure we have a db handle */
+ pkcs11db = secmod_OpenDB(appName,filename,dbname,PR_FALSE,PR_FALSE);
+ if (pkcs11db == NULL) {
+ return SECFailure;
+ }
+
+ rv = secmod_MakeKey(&key,args);
+ if (rv != SECSuccess) goto done;
+ rv = SECFailure;
+ ret = (*pkcs11db->del)(pkcs11db, &key, 0);
+ secmod_FreeKey(&key);
+ if (ret != 0) goto done;
+
+
+ ret = (*pkcs11db->sync)(pkcs11db, 0);
+ if (ret == 0) rv = SECSuccess;
+
+done:
+ secmod_CloseDB(pkcs11db);
+ return rv;
+}
+
+/*
+ * Add a module to the Data base
+ */
+SECStatus
+legacy_AddSecmodDB(const char *appName, const char *filename,
+ const char *dbname, char *module, PRBool rw)
+{
+ DBT key,data;
+ SECStatus rv = SECFailure;
+ DB *pkcs11db = NULL;
+ int ret;
+
+
+ if (!rw) return SECFailure;
+
+ /* make sure we have a db handle */
+ pkcs11db = secmod_OpenDB(appName,filename,dbname,PR_FALSE,PR_FALSE);
+ if (pkcs11db == NULL) {
+ return SECFailure;
+ }
+
+ rv = secmod_MakeKey(&key,module);
+ if (rv != SECSuccess) goto done;
+ rv = secmod_EncodeData(&data,module);
+ if (rv != SECSuccess) {
+ secmod_FreeKey(&key);
+ goto done;
+ }
+ rv = SECFailure;
+ ret = (*pkcs11db->put)(pkcs11db, &key, &data, 0);
+ secmod_FreeKey(&key);
+ secmod_FreeData(&data);
+ if (ret != 0) goto done;
+
+ ret = (*pkcs11db->sync)(pkcs11db, 0);
+ if (ret == 0) rv = SECSuccess;
+
+done:
+ secmod_CloseDB(pkcs11db);
+ return rv;
+}