[PATCH 1 of 2] Add DBM and legacydb support

Wald Commits scm-commit at wald.intevation.org
Tue Aug 5 18:58:09 CEST 2014


# HG changeset patch
# User Andre Heinecke <andre.heinecke at intevation.de>
# Date 1407256322 -7200
# Node ID 150b7211354542850cca0fdecaee4d7b1161a41f
# Parent  a945361df361ebd06a9c2663685fe1d6e485b13e
Add DBM and legacydb support

diff -r a945361df361 -r 150b72113545 CMakeLists.txt
--- a/CMakeLists.txt	Wed Jul 30 16:20:44 2014 +0200
+++ b/CMakeLists.txt	Tue Aug 05 18:32:02 2014 +0200
@@ -30,6 +30,7 @@
    ${CMAKE_CURRENT_SOURCE_DIR}/nss/lib/softoken
    ${CMAKE_CURRENT_SOURCE_DIR}/nss/lib/ssl
    ${CMAKE_CURRENT_SOURCE_DIR}/nss/lib/util
+   ${CMAKE_CURRENT_SOURCE_DIR}/nss/lib/dbm/include
 )
 
 include_directories(${NSS_INCLUDE_DIRS})
@@ -38,7 +39,7 @@
 set (COMMON_DEFINITIONS "${COMMON_DEFINITIONS} -DWIN95 -D_PR_GLOBAL_THREADS_ONLY -D_X86_ -UWINNT")
 set (COMMON_DEFINITIONS "${COMMON_DEFINITIONS} -DNSPR_STATIC")
 
-set (NSS_DEFINITIONS "${COMMON_DEFINITIONS} -DMP_API_COMPATIBLE -DNSS_DISABLE_DBM -DNSS_STATIC")
+set (NSS_DEFINITIONS "${COMMON_DEFINITIONS} -DMP_API_COMPATIBLE -DNSS_STATIC")
 set (NSS_DEFINITIONS "${NSS_DEFINITIONS} -DNSS_USE_STATIC_LIBS -DRIJNDAEL_INCLUDE_TABLES -DSHLIB_VERSION=\\\"3\\\"" )
 set (NSS_DEFINITIONS "${NSS_DEFINITIONS} -DSOFTOKEN_SHLIB_VERSION=\\\"3\\\" -DUSE_UTIL_DIRECTLY")
 set (NSS_DEFINITIONS "${NSS_DEFINITIONS} -DNSS_DISABLE_ROOT_CERTS -DNSS_DISABLE_LIBPKIX -DSHLIB_SUFFIX=\\\"dll\\\"")
@@ -202,6 +203,18 @@
    nss/lib/softoken/sftkpwd.c
    nss/lib/softoken/softkver.c
    nss/lib/softoken/tlsprf.c
+   nss/lib/softoken/legacydb/dbmshim.c
+   nss/lib/softoken/legacydb/keydb.c
+   nss/lib/softoken/legacydb/lgattr.c
+   nss/lib/softoken/legacydb/lgcreate.c
+   nss/lib/softoken/legacydb/lgdestroy.c
+   nss/lib/softoken/legacydb/lgfind.c
+   nss/lib/softoken/legacydb/lginit.c
+   nss/lib/softoken/legacydb/lgutil.c
+   nss/lib/softoken/legacydb/lowcert.c
+   nss/lib/softoken/legacydb/lowkey.c
+   nss/lib/softoken/legacydb/pcertdb.c
+   nss/lib/softoken/legacydb/pk11db.c
    nss/lib/util/derdec.c
    nss/lib/util/derenc.c
    nss/lib/util/dersubr.c
@@ -229,6 +242,11 @@
    nss/lib/util/utilpars.c
    nss/lib/nss/nssver.c
    nss/lib/freebl/win_rand.c
+   nss/lib/dbm/src/db.c
+   nss/lib/dbm/src/dirent.c
+   nss/lib/dbm/src/memmove.c
+   nss/lib/dbm/src/mktemp.c
+   nss/lib/dbm/src/hash.c
    sqlite/sqlite3.c
    )
 
@@ -249,6 +267,7 @@
    nspr/pr/src/io/prsocket.c
    nspr/pr/src/io/pripv6.c
    nspr/pr/src/io/prio.c
+   nspr/pr/src/io/prdir.c
    nspr/pr/src/linking/prlink.c
    nspr/pr/src/malloc/prmalloc.c
    nspr/pr/src/malloc/prmem.c
diff -r a945361df361 -r 150b72113545 nss/lib/dbm/Makefile
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nss/lib/dbm/Makefile	Tue Aug 05 18:32:02 2014 +0200
@@ -0,0 +1,52 @@
+#! gmake
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#######################################################################
+# (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).      #
+#######################################################################
+
+
+
+#######################################################################
+# (5) Execute "global" rules. (OPTIONAL)                              #
+#######################################################################
+
+include $(CORE_DEPTH)/coreconf/rules.mk
+
+#######################################################################
+# (6) Execute "component" rules. (OPTIONAL)                           #
+#######################################################################
+
+
+
+#######################################################################
+# (7) Execute "local" rules. (OPTIONAL).                              #
+#######################################################################
+
+coreconf_hack:
+	cd ../coreconf; gmake
+	gmake import
+
+RelEng_bld: coreconf_hack
+	gmake
diff -r a945361df361 -r 150b72113545 nss/lib/dbm/config/config.mk
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nss/lib/dbm/config/config.mk	Tue Aug 05 18:32:02 2014 +0200
@@ -0,0 +1,39 @@
+#! gmake
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#
+# These macros are defined by mozilla's configure script.
+# We define them manually here.
+#
+
+DEFINES += -DSTDC_HEADERS -DHAVE_STRERROR
+
+#
+# Most platforms have snprintf, so it's simpler to list the exceptions.
+#
+HAVE_SNPRINTF = 1
+#
+# OSF1 V4.0D doesn't have snprintf but V5.0A does.
+#
+ifeq ($(OS_TARGET)$(OS_RELEASE),OSF1V4.0D)
+HAVE_SNPRINTF =
+endif
+ifdef HAVE_SNPRINTF
+DEFINES += -DHAVE_SNPRINTF
+endif
+
+ifeq (,$(filter-out IRIX Linux,$(OS_TARGET)))
+DEFINES += -DHAVE_SYS_CDEFS_H
+endif
+
+ifeq (,$(filter-out DGUX NCR ReliantUNIX SCO_SV SCOOS UNIXWARE,$(OS_TARGET)))
+DEFINES += -DHAVE_SYS_BYTEORDER_H
+endif
+
+#
+# None of the platforms that we are interested in need to
+# define HAVE_MEMORY_H.
+#
diff -r a945361df361 -r 150b72113545 nss/lib/dbm/include/Makefile
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nss/lib/dbm/include/Makefile	Tue Aug 05 18:32:02 2014 +0200
@@ -0,0 +1,47 @@
+#! gmake
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#######################################################################
+# (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).      #
+#######################################################################
+
+
+
+#######################################################################
+# (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 -r a945361df361 -r 150b72113545 nss/lib/dbm/include/cdefs.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nss/lib/dbm/include/cdefs.h	Tue Aug 05 18:32:02 2014 +0200
@@ -0,0 +1,126 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Berkeley Software Design, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. ***REMOVED*** - see 
+ *    ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *	@(#)cdefs.h	8.7 (Berkeley) 1/21/94
+ */
+
+#ifndef	_CDEFS_H_
+#define	_CDEFS_H_
+
+#if defined(__cplusplus)
+#define	__BEGIN_DECLS	extern "C" {
+#define	__END_DECLS	}
+#else
+#define	__BEGIN_DECLS
+#define	__END_DECLS
+#endif
+
+/*
+ * The __CONCAT macro is used to concatenate parts of symbol names, e.g.
+ * with "#define OLD(foo) __CONCAT(old,foo)", OLD(foo) produces oldfoo.
+ * The __CONCAT macro is a bit tricky -- make sure you don't put spaces
+ * in between its arguments.  __CONCAT can also concatenate double-quoted
+ * strings produced by the __STRING macro, but this only works with ANSI C.
+ */
+#if defined(__STDC__) || defined(__cplusplus) || defined(_WINDOWS) || defined(XP_OS2)
+#define	__P(protos)	protos		/* full-blown ANSI C */
+#define	__CONCAT(x,y)	x ## y
+#define	__STRING(x)	#x
+
+/* On HP-UX 11.00, <sys/stdsyms.h> defines __const. */
+#ifndef __const
+#define	__const		const		/* define reserved names to standard */
+#endif  /* __const */
+#define	__signed	signed
+#define	__volatile	volatile
+#ifndef _WINDOWS
+#if defined(__cplusplus)
+#define	__inline	inline		/* convert to C++ keyword */
+#else
+#if !defined(__GNUC__) && !defined(__MWERKS__)
+#define	__inline			/* delete GCC keyword */
+#endif /* !__GNUC__ */
+#endif /* !__cplusplus */
+#endif /* !_WINDOWS */
+
+#else	/* !(__STDC__ || __cplusplus) */
+#define	__P(protos)	()		/* traditional C preprocessor */
+#define	__CONCAT(x,y)	x/**/y
+#define	__STRING(x)	"x"
+
+#ifndef __GNUC__
+#define	__const				/* delete pseudo-ANSI C keywords */
+#define	__inline
+#define	__signed
+#define	__volatile
+/*
+ * In non-ANSI C environments, new programs will want ANSI-only C keywords
+ * deleted from the program and old programs will want them left alone.
+ * When using a compiler other than gcc, programs using the ANSI C keywords
+ * const, inline etc. as normal identifiers should define -DNO_ANSI_KEYWORDS.
+ * When using "gcc -traditional", we assume that this is the intent; if
+ * __GNUC__ is defined but __STDC__ is not, we leave the new keywords alone.
+ */
+#ifndef	NO_ANSI_KEYWORDS
+#define	const				/* delete ANSI C keywords */
+#define	inline
+#define	signed
+#define	volatile
+#endif
+#endif	/* !__GNUC__ */
+#endif	/* !(__STDC__ || __cplusplus) */
+
+/*
+ * GCC1 and some versions of GCC2 declare dead (non-returning) and
+ * pure (no side effects) functions using "volatile" and "const";
+ * unfortunately, these then cause warnings under "-ansi -pedantic".
+ * GCC2 uses a new, peculiar __attribute__((attrs)) style.  All of
+ * these work for GNU C++ (modulo a slight glitch in the C++ grammar
+ * in the distribution version of 2.5.5).
+ */
+#if !defined(__GNUC__) || __GNUC__ < 2 || __GNUC_MINOR__ < 5
+#define	__attribute__(x)	/* delete __attribute__ if non-gcc or gcc1 */
+#if defined(__GNUC__) && !defined(__STRICT_ANSI__)
+#define	__dead		__volatile
+#define	__pure		__const
+#endif
+#endif
+
+/* Delete pseudo-keywords wherever they are not available or needed. */
+#ifndef __dead
+#define	__dead
+#define	__pure
+#endif
+
+#endif /* !_CDEFS_H_ */
diff -r a945361df361 -r 150b72113545 nss/lib/dbm/include/extern.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nss/lib/dbm/include/extern.h	Tue Aug 05 18:32:02 2014 +0200
@@ -0,0 +1,63 @@
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. ***REMOVED*** - see 
+ *    ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *	@(#)extern.h	8.4 (Berkeley) 6/16/94
+ */
+
+BUFHEAD	*__add_ovflpage (HTAB *, BUFHEAD *);
+int	 __addel (HTAB *, BUFHEAD *, const DBT *, const DBT *);
+int	 __big_delete (HTAB *, BUFHEAD *);
+int	 __big_insert (HTAB *, BUFHEAD *, const DBT *, const DBT *);
+int	 __big_keydata (HTAB *, BUFHEAD *, DBT *, DBT *, int);
+int	 __big_return (HTAB *, BUFHEAD *, int, DBT *, int);
+int	 __big_split (HTAB *, BUFHEAD *, BUFHEAD *, BUFHEAD *,
+		uint32, uint32, SPLIT_RETURN *);
+int	 __buf_free (HTAB *, int, int);
+void	 __buf_init (HTAB *, int);
+uint32	 __call_hash (HTAB *, char *, size_t);
+int	 __delpair (HTAB *, BUFHEAD *, int);
+int	 __expand_table (HTAB *);
+int	 __find_bigpair (HTAB *, BUFHEAD *, int, char *, int);
+uint16	 __find_last_page (HTAB *, BUFHEAD **);
+void	 __free_ovflpage (HTAB *, BUFHEAD *);
+BUFHEAD	*__get_buf (HTAB *, uint32, BUFHEAD *, int);
+int	 __get_page (HTAB *, char *, uint32, int, int, int);
+int	 __ibitmap (HTAB *, int, int, int);
+uint32	 __log2 (uint32);
+int	 __put_page (HTAB *, char *, uint32, int, int);
+void	 __reclaim_buf (HTAB *, BUFHEAD *);
+int	 __split_page (HTAB *, uint32, uint32);
+
+/* Default hash routine. */
+extern uint32 (*__default_hash) (const void *, size_t);
+
+#ifdef HASH_STATISTICS
+extern int hash_accesses, hash_collisions, hash_expansions, hash_overflows;
+#endif
diff -r a945361df361 -r 150b72113545 nss/lib/dbm/include/hash.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nss/lib/dbm/include/hash.h	Tue Aug 05 18:32:02 2014 +0200
@@ -0,0 +1,335 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Margo Seltzer.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. ***REMOVED*** - see 
+ *    ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *	@(#)hash.h	8.3 (Berkeley) 5/31/94
+ */
+
+/* Operations */
+
+#include <stdio.h>
+#include "mcom_db.h"
+typedef enum {
+	HASH_GET, HASH_PUT, HASH_PUTNEW, HASH_DELETE, HASH_FIRST, HASH_NEXT
+} ACTION;
+
+/* Buffer Management structures */
+typedef struct _bufhead BUFHEAD;
+
+struct _bufhead {
+	BUFHEAD		*prev;		/* LRU links */
+	BUFHEAD		*next;		/* LRU links */
+	BUFHEAD		*ovfl;		/* Overflow page buffer header */
+	uint32	 	addr;		/* Address of this page */
+	char		*page;		/* Actual page data */
+	char     	is_disk;
+	char	 	flags;
+#define	BUF_MOD		0x0001
+#define BUF_DISK	0x0002
+#define	BUF_BUCKET	0x0004
+#define	BUF_PIN		0x0008
+};
+
+#define IS_BUCKET(X)	((X) & BUF_BUCKET)
+
+typedef BUFHEAD **SEGMENT;
+
+typedef int DBFILE_PTR;
+#define NO_FILE -1
+#ifdef macintosh
+#define DBFILE_OPEN(path, flag,mode) open((path), flag)
+#define EXISTS(path)
+#else
+#define DBFILE_OPEN(path, flag,mode) open((path), (flag), (mode))
+#endif
+/* Hash Table Information */
+typedef struct hashhdr {		/* Disk resident portion */
+	int32		magic;		/* Magic NO for hash tables */
+	int32		version;	/* Version ID */
+	uint32		lorder;		/* Byte Order */
+	int32		bsize;		/* Bucket/Page Size */
+	int32		bshift;		/* Bucket shift */
+	int32		dsize;		/* Directory Size */
+	int32		ssize;		/* Segment Size */
+	int32		sshift;		/* Segment shift */
+	int32		ovfl_point;	/* Where overflow pages are being 
+					 * allocated */
+	int32		last_freed;	/* Last overflow page freed */
+	int32		max_bucket;	/* ID of Maximum bucket in use */
+	int32		high_mask;	/* Mask to modulo into entire table */
+	int32		low_mask;	/* Mask to modulo into lower half of 
+					 * table */
+	int32		ffactor;	/* Fill factor */
+	int32		nkeys;		/* Number of keys in hash table */
+	int32		hdrpages;	/* Size of table header */
+	uint32		h_charkey;	/* value of hash(CHARKEY) */
+#define NCACHED	32			/* number of bit maps and spare 
+					 * points */
+	int32		spares[NCACHED];/* spare pages for overflow */
+	uint16		bitmaps[NCACHED];	/* address of overflow page 
+						 * bitmaps */
+} HASHHDR;
+
+typedef struct htab	 {		/* Memory resident data structure */
+	HASHHDR 	hdr;		/* Header */
+	int		nsegs;		/* Number of allocated segments */
+	int		exsegs;		/* Number of extra allocated 
+					 * segments */
+	uint32			/* Hash function */
+	    (*hash)(const void *, size_t);
+	int		flags;		/* Flag values */
+	DBFILE_PTR	fp;		/* File pointer */
+	char            *filename;
+	char		*tmp_buf;	/* Temporary Buffer for BIG data */
+	char		*tmp_key;	/* Temporary Buffer for BIG keys */
+	BUFHEAD 	*cpage;		/* Current page */
+	int		cbucket;	/* Current bucket */
+	int		cndx;		/* Index of next item on cpage */
+	int		dbmerrno;		/* Error Number -- for DBM 
+					 * compatability */
+	int		new_file;	/* Indicates if fd is backing store 
+					 * or no */
+	int		save_file;	/* Indicates whether we need to flush 
+					 * file at
+					 * exit */
+	uint32		*mapp[NCACHED];	/* Pointers to page maps */
+	int		nmaps;		/* Initial number of bitmaps */
+	int		nbufs;		/* Number of buffers left to 
+					 * allocate */
+	BUFHEAD 	bufhead;	/* Header of buffer lru list */
+	SEGMENT 	*dir;		/* Hash Bucket directory */
+	off_t		file_size;	/* in bytes */
+	char		is_temp;	/* unlink file on close */
+	char		updateEOF;	/* force EOF update on flush */
+} HTAB;
+
+/*
+ * Constants
+ */
+#define DATABASE_CORRUPTED_ERROR -999   /* big ugly abort, delete database */
+#define	OLD_MAX_BSIZE		65536		/* 2^16 */
+#define MAX_BSIZE       	32l*1024l         /* 2^15 */
+#define MIN_BUFFERS		6
+#define MINHDRSIZE		512
+#define DEF_BUFSIZE		65536l		/* 64 K */
+#define DEF_BUCKET_SIZE		4096
+#define DEF_BUCKET_SHIFT	12		/* log2(BUCKET) */
+#define DEF_SEGSIZE		256
+#define DEF_SEGSIZE_SHIFT	8		/* log2(SEGSIZE)	 */
+#define DEF_DIRSIZE		256
+#define DEF_FFACTOR		65536l
+#define MIN_FFACTOR		4
+#define SPLTMAX			8
+#define CHARKEY			"%$sniglet^&"
+#define NUMKEY			1038583l
+#define BYTE_SHIFT		3
+#define INT_TO_BYTE		2
+#define INT_BYTE_SHIFT		5
+#define ALL_SET			((uint32)0xFFFFFFFF)
+#define ALL_CLEAR		0
+
+#define PTROF(X)	((ptrdiff_t)(X) == BUF_DISK ? 0 : (X))
+#define ISDISK(X)	((X) ? ((ptrdiff_t)(X) == BUF_DISK ? BUF_DISK \
+				: (X)->is_disk) : 0)
+
+#define BITS_PER_MAP	32
+
+/* Given the address of the beginning of a big map, clear/set the nth bit */
+#define CLRBIT(A, N)	((A)[(N)/BITS_PER_MAP] &= ~(1<<((N)%BITS_PER_MAP)))
+#define SETBIT(A, N)	((A)[(N)/BITS_PER_MAP] |= (1<<((N)%BITS_PER_MAP)))
+#define ISSET(A, N)	((A)[(N)/BITS_PER_MAP] & (1<<((N)%BITS_PER_MAP)))
+
+/* Overflow management */
+/*
+ * Overflow page numbers are allocated per split point.  At each doubling of
+ * the table, we can allocate extra pages.  So, an overflow page number has
+ * the top 5 bits indicate which split point and the lower 11 bits indicate
+ * which page at that split point is indicated (pages within split points are
+ * numberered starting with 1).
+ */
+
+#define SPLITSHIFT	11
+#define SPLITMASK	0x7FF
+#define SPLITNUM(N)	(((uint32)(N)) >> SPLITSHIFT)
+#define OPAGENUM(N)	((N) & SPLITMASK)
+#define	OADDR_OF(S,O)	((uint32)((uint32)(S) << SPLITSHIFT) + (O))
+
+#define BUCKET_TO_PAGE(B) \
+	(B) + hashp->HDRPAGES + ((B) ? hashp->SPARES[__log2((uint32)((B)+1))-1] : 0)
+#define OADDR_TO_PAGE(B) 	\
+	BUCKET_TO_PAGE ( (1 << SPLITNUM((B))) -1 ) + OPAGENUM((B));
+
+/*
+ * page.h contains a detailed description of the page format.
+ *
+ * Normally, keys and data are accessed from offset tables in the top of
+ * each page which point to the beginning of the key and data.  There are
+ * four flag values which may be stored in these offset tables which indicate
+ * the following:
+ *
+ *
+ * OVFLPAGE	Rather than a key data pair, this pair contains
+ *		the address of an overflow page.  The format of
+ *		the pair is:
+ *		    OVERFLOW_PAGE_NUMBER OVFLPAGE
+ *
+ * PARTIAL_KEY	This must be the first key/data pair on a page
+ *		and implies that page contains only a partial key.
+ *		That is, the key is too big to fit on a single page
+ *		so it starts on this page and continues on the next.
+ *		The format of the page is:
+ *		    KEY_OFF PARTIAL_KEY OVFL_PAGENO OVFLPAGE
+ *		
+ *		    KEY_OFF -- offset of the beginning of the key
+ *		    PARTIAL_KEY -- 1
+ *		    OVFL_PAGENO - page number of the next overflow page
+ *		    OVFLPAGE -- 0
+ *
+ * FULL_KEY	This must be the first key/data pair on the page.  It
+ *		is used in two cases.
+ *
+ *		Case 1:
+ *		    There is a complete key on the page but no data
+ *		    (because it wouldn't fit).  The next page contains
+ *		    the data.
+ *
+ *		    Page format it:
+ *		    KEY_OFF FULL_KEY OVFL_PAGENO OVFL_PAGE
+ *
+ *		    KEY_OFF -- offset of the beginning of the key
+ *		    FULL_KEY -- 2
+ *		    OVFL_PAGENO - page number of the next overflow page
+ *		    OVFLPAGE -- 0
+ *
+ *		Case 2:
+ *		    This page contains no key, but part of a large
+ *		    data field, which is continued on the next page.
+ *
+ *		    Page format it:
+ *		    DATA_OFF FULL_KEY OVFL_PAGENO OVFL_PAGE
+ *
+ *		    KEY_OFF -- offset of the beginning of the data on
+ *				this page
+ *		    FULL_KEY -- 2
+ *		    OVFL_PAGENO - page number of the next overflow page
+ *		    OVFLPAGE -- 0
+ *
+ * FULL_KEY_DATA 
+ *		This must be the first key/data pair on the page.
+ *		There are two cases:
+ *
+ *		Case 1:
+ *		    This page contains a key and the beginning of the
+ *		    data field, but the data field is continued on the
+ *		    next page.
+ *
+ *		    Page format is:
+ *		    KEY_OFF FULL_KEY_DATA OVFL_PAGENO DATA_OFF
+ *
+ *		    KEY_OFF -- offset of the beginning of the key
+ *		    FULL_KEY_DATA -- 3
+ *		    OVFL_PAGENO - page number of the next overflow page
+ *		    DATA_OFF -- offset of the beginning of the data
+ *
+ *		Case 2:
+ *		    This page contains the last page of a big data pair.
+ *		    There is no key, only the  tail end of the data
+ *		    on this page.
+ *
+ *		    Page format is:
+ *		    DATA_OFF FULL_KEY_DATA <OVFL_PAGENO> <OVFLPAGE>
+ *
+ *		    DATA_OFF -- offset of the beginning of the data on
+ *				this page
+ *		    FULL_KEY_DATA -- 3
+ *		    OVFL_PAGENO - page number of the next overflow page
+ *		    OVFLPAGE -- 0
+ *
+ *		    OVFL_PAGENO and OVFLPAGE are optional (they are
+ *		    not present if there is no next page).
+ */
+
+#define OVFLPAGE	0
+#define PARTIAL_KEY	1
+#define FULL_KEY	2
+#define FULL_KEY_DATA	3
+#define	REAL_KEY	4
+
+/* Short hands for accessing structure */
+#undef BSIZE
+#define BSIZE		hdr.bsize
+#undef BSHIFT
+#define BSHIFT		hdr.bshift
+#define DSIZE		hdr.dsize
+#define SGSIZE		hdr.ssize
+#define SSHIFT		hdr.sshift
+#define LORDER		hdr.lorder
+#define OVFL_POINT	hdr.ovfl_point
+#define	LAST_FREED	hdr.last_freed
+#define MAX_BUCKET	hdr.max_bucket
+#define FFACTOR		hdr.ffactor
+#define HIGH_MASK	hdr.high_mask
+#define LOW_MASK	hdr.low_mask
+#define NKEYS		hdr.nkeys
+#define HDRPAGES	hdr.hdrpages
+#define SPARES		hdr.spares
+#define BITMAPS		hdr.bitmaps
+#define VERSION		hdr.version
+#define MAGIC		hdr.magic
+#define NEXT_FREE	hdr.next_free
+#define H_CHARKEY	hdr.h_charkey
+
+extern uint32 (*__default_hash) (const void *, size_t);
+void __buf_init(HTAB *hashp, int32 nbytes);
+int __big_delete(HTAB *hashp, BUFHEAD *bufp);
+BUFHEAD * __get_buf(HTAB *hashp, uint32 addr, BUFHEAD *prev_bp, int newpage);
+uint32 __call_hash(HTAB *hashp, char *k, size_t len);
+#include "page.h"
+extern int __big_split(HTAB *hashp, BUFHEAD *op,BUFHEAD *np,
+BUFHEAD *big_keyp,uint32 addr,uint32   obucket, SPLIT_RETURN *ret);
+void __free_ovflpage(HTAB *hashp, BUFHEAD *obufp);
+BUFHEAD * __add_ovflpage(HTAB *hashp, BUFHEAD *bufp);
+int __big_insert(HTAB *hashp, BUFHEAD *bufp, const DBT *key, const DBT *val);
+int __expand_table(HTAB *hashp);
+uint32 __log2(uint32 num);
+void __reclaim_buf(HTAB *hashp, BUFHEAD *bp);
+int __get_page(HTAB *hashp, char * p, uint32 bucket, int is_bucket, int is_disk, int is_bitmap);
+int __put_page(HTAB *hashp, char *p, uint32 bucket, int is_bucket, int is_bitmap);
+int __ibitmap(HTAB *hashp, int pnum, int nbits, int ndx);
+int __buf_free(HTAB *hashp, int do_free, int to_disk);
+int __find_bigpair(HTAB *hashp, BUFHEAD *bufp, int ndx, char *key, int size);
+uint16 __find_last_page(HTAB *hashp, BUFHEAD **bpp);
+int __addel(HTAB *hashp, BUFHEAD *bufp, const DBT *key, const DBT * val);
+int __big_return(HTAB *hashp, BUFHEAD *bufp, int ndx, DBT *val, int set_current);
+int __delpair(HTAB *hashp, BUFHEAD *bufp, int ndx);
+int __big_keydata(HTAB *hashp, BUFHEAD *bufp, DBT *key, DBT *val, int set);
+int __split_page(HTAB *hashp, uint32 obucket, uint32 nbucket);
diff -r a945361df361 -r 150b72113545 nss/lib/dbm/include/hsearch.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nss/lib/dbm/include/hsearch.h	Tue Aug 05 18:32:02 2014 +0200
@@ -0,0 +1,49 @@
+/*-
+ * Copyright (c) 1990, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Margo Seltzer.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. ***REMOVED*** - see 
+ *    ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *	@(#)search.h	8.1 (Berkeley) 6/4/93
+ */
+
+/* Backward compatibility to hsearch interface. */
+typedef struct entry {
+	char *key;
+	char *data;
+} ENTRY;
+
+typedef enum {
+	FIND, ENTER
+} ACTION;
+
+int	 hcreate (unsigned int);
+void	 hdestroy (void);
+ENTRY	*hsearch (ENTRY, ACTION);
diff -r a945361df361 -r 150b72113545 nss/lib/dbm/include/manifest.mn
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nss/lib/dbm/include/manifest.mn	Tue Aug 05 18:32:02 2014 +0200
@@ -0,0 +1,25 @@
+#! gmake
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+CORE_DEPTH = ../../..
+
+MODULE = dbm
+
+EXPORTS =	cdefs.h   \
+		mcom_db.h \
+		ncompat.h \
+		winfile.h \
+		$(NULL)
+
+PRIVATE_EXPORTS =	hsearch.h \
+			page.h    \
+			extern.h  \
+			queue.h   \
+			hash.h    \
+			mpool.h   \
+			search.h  \
+			$(NULL)
+
diff -r a945361df361 -r 150b72113545 nss/lib/dbm/include/mcom_db.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nss/lib/dbm/include/mcom_db.h	Tue Aug 05 18:32:02 2014 +0200
@@ -0,0 +1,420 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*- 
+ * Copyright (c) 1990, 1993, 1994
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. ***REMOVED*** - see 
+ *    ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *	@(#)db.h	8.7 (Berkeley) 6/16/94
+ */
+
+#ifndef _DB_H_
+#define	_DB_H_
+
+#ifndef macintosh
+#include <sys/types.h>
+#endif
+#include "prtypes.h"
+
+#if !defined(XP_BEOS) && !defined(XP_OS2) && !defined(XP_UNIX) || defined(NTO)
+typedef PRUintn uint;
+#endif
+typedef PRUint8 uint8;
+typedef PRUint16 uint16;
+/* On AIX 5.2, sys/inttypes.h (which is included by sys/types.h)
+ * defines the types int8, int16, int32, and int64. */
+#if !defined(AIX)
+typedef PRInt32 int32;
+#endif
+typedef PRUint32 uint32;
+
+#include <limits.h>
+
+#ifdef __DBINTERFACE_PRIVATE
+
+#ifdef HAVE_SYS_CDEFS_H
+#include <sys/cdefs.h>
+#else
+#include "cdefs.h"
+#endif
+
+#ifdef HAVE_SYS_BYTEORDER_H
+#include <sys/byteorder.h>
+#endif
+
+#if defined(__linux) || defined(__BEOS__)
+#include <endian.h>
+#ifndef BYTE_ORDER
+#define BYTE_ORDER    __BYTE_ORDER
+#define BIG_ENDIAN    __BIG_ENDIAN
+#define LITTLE_ENDIAN __LITTLE_ENDIAN
+#endif
+#endif /* __linux */
+
+#ifdef __sgi
+#define BYTE_ORDER BIG_ENDIAN
+#define BIG_ENDIAN      4321
+#define LITTLE_ENDIAN   1234            /* LSB first: i386, vax, all NT risc */
+#endif
+
+#ifdef __sun
+#define BIG_ENDIAN      4321
+#define LITTLE_ENDIAN   1234            /* LSB first: i386, vax, all NT risc */
+
+#ifndef __SVR4
+/* compat.h is only in 4.1.3 machines. - dp */
+#include <compat.h>
+#endif
+
+/* XXX - dp
+ * Need to find a general way of defining endian-ness in SunOS 5.3
+ * SunOS 5.4 defines _BIG_ENDIAN and _LITTLE_ENDIAN
+ * SunOS 5.3 does nothing like this.
+ */
+
+#ifndef BYTE_ORDER
+
+#if defined(_BIG_ENDIAN)
+#define BYTE_ORDER BIG_ENDIAN
+#elif defined(_LITTLE_ENDIAN)
+#define BYTE_ORDER LITTLE_ENDIAN
+#elif !defined(__SVR4)
+/* 4.1.3 is always BIG_ENDIAN as it was released only on sparc platforms. */
+#define BYTE_ORDER BIG_ENDIAN
+#elif !defined(vax) && !defined(ntohl) && !defined(lint) && !defined(i386)
+/* 5.3 big endian. Copied this above line from sys/byteorder.h */
+/* Now we are in a 5.3 SunOS rather non 5.4 or above SunOS  */
+#define BYTE_ORDER BIG_ENDIAN
+#else
+#define BYTE_ORDER LITTLE_ENDIAN
+#endif
+
+#endif /* !BYTE_ORDER */
+#endif /* __sun */
+
+#if defined(__hpux) || defined(__hppa)
+#define BYTE_ORDER BIG_ENDIAN
+#define BIG_ENDIAN      4321
+#define LITTLE_ENDIAN   1234            /* LSB first: i386, vax, all NT risc */
+#endif
+
+#if defined(AIXV3) || defined(AIX)
+/* BYTE_ORDER, LITTLE_ENDIAN, BIG_ENDIAN are all defined here */
+#include <sys/machine.h>
+#endif
+
+/* Digital Unix */
+#ifdef __osf__
+#include <machine/endian.h>
+#endif
+
+#ifdef __alpha
+#ifndef WIN32
+#else
+/* Alpha NT */
+#define BYTE_ORDER LITTLE_ENDIAN
+#define BIG_ENDIAN      4321
+#define LITTLE_ENDIAN   1234 
+#endif
+#endif
+
+#ifdef NCR
+#include <sys/endian.h>
+#endif
+
+#ifdef __QNX__
+#ifdef __QNXNTO__
+#include <sys/param.h>
+#else
+#define LITTLE_ENDIAN	1234
+#define BIG_ENDIAN	4321
+#define BYTE_ORDER	LITTLE_ENDIAN
+#endif
+#endif
+
+#ifdef SNI
+/* #include <sys/hetero.h> */
+#define BYTE_ORDER BIG_ENDIAN
+#define BIG_ENDIAN      4321
+#define LITTLE_ENDIAN   1234
+#endif
+
+#ifdef _WINDOWS
+#ifdef BYTE_ORDER
+#undef BYTE_ORDER
+#endif
+
+#define BYTE_ORDER LITTLE_ENDIAN
+#define LITTLE_ENDIAN   1234            /* LSB first: i386, vax, all NT risc */
+#define BIG_ENDIAN      4321
+#endif
+
+#ifdef macintosh
+#define BIG_ENDIAN 4321
+#define LITTLE_ENDIAN 1234
+#define BYTE_ORDER BIG_ENDIAN
+#endif
+
+#endif  /* __DBINTERFACE_PRIVATE */
+
+#ifdef SCO
+#define MAXPATHLEN 	1024              
+#endif
+
+#include <fcntl.h>
+
+#if defined(_WINDOWS) || defined(XP_OS2)
+#include <stdio.h>
+#include <io.h>
+
+#ifndef XP_OS2 
+#define MAXPATHLEN 	1024               
+#endif
+
+#define	EFTYPE		EINVAL		/* POSIX 1003.1 format errno. */
+
+#ifndef	STDERR_FILENO
+#define	STDIN_FILENO	0		/* ANSI C #defines */
+#define	STDOUT_FILENO	1
+#define	STDERR_FILENO	2
+#endif
+
+#ifndef O_ACCMODE			/* POSIX 1003.1 access mode mask. */
+#define	O_ACCMODE	(O_RDONLY|O_WRONLY|O_RDWR)
+#endif
+#endif
+
+#ifdef macintosh
+#include <stdio.h>
+#include "xp_mcom.h"
+#define O_ACCMODE       3       /* Mask for file access modes */
+#define EFTYPE 2000
+PR_BEGIN_EXTERN_C
+int mkstemp(const char *path);
+PR_END_EXTERN_C
+#endif	/* MACINTOSH */
+
+#if !defined(_WINDOWS) && !defined(macintosh)
+#include <sys/stat.h>
+#include <errno.h>
+#endif
+
+/* define EFTYPE since most don't */
+#ifndef EFTYPE
+#define EFTYPE      EINVAL      /* POSIX 1003.1 format errno. */
+#endif
+
+#define	RET_ERROR	-1		/* Return values. */
+#define	RET_SUCCESS	 0
+#define	RET_SPECIAL	 1
+
+#define	MAX_PAGE_NUMBER	0xffffffff	/* >= # of pages in a file */
+
+#ifndef __sgi
+typedef uint32	pgno_t;
+#endif
+
+#define	MAX_PAGE_OFFSET	65535		/* >= # of bytes in a page */
+typedef uint16	indx_t;
+#define	MAX_REC_NUMBER	0xffffffff	/* >= # of records in a tree */
+typedef uint32	recno_t;
+
+/* Key/data structure -- a Data-Base Thang. */
+typedef struct {
+	void	*data;			/* data */
+	size_t	 size;			/* data length */
+} DBT;
+
+/* Routine flags. */
+#define	R_CURSOR	1		/* del, put, seq */
+#define	__R_UNUSED	2		/* UNUSED */
+#define	R_FIRST		3		/* seq */
+#define	R_IAFTER	4		/* put (RECNO) */
+#define	R_IBEFORE	5		/* put (RECNO) */
+#define	R_LAST		6		/* seq (BTREE, RECNO) */
+#define	R_NEXT		7		/* seq */
+#define	R_NOOVERWRITE	8		/* put */
+#define	R_PREV		9		/* seq (BTREE, RECNO) */
+#define	R_SETCURSOR	10		/* put (RECNO) */
+#define	R_RECNOSYNC	11		/* sync (RECNO) */
+
+typedef enum { DB_BTREE, DB_HASH, DB_RECNO } DBTYPE;
+
+typedef enum { LockOutDatabase, UnlockDatabase } DBLockFlagEnum;
+
+/*
+ * !!!
+ * The following flags are included in the dbopen(3) call as part of the
+ * open(2) flags.  In order to avoid conflicts with the open flags, start
+ * at the top of the 16 or 32-bit number space and work our way down.  If
+ * the open flags were significantly expanded in the future, it could be
+ * a problem.  Wish I'd left another flags word in the dbopen call.
+ *
+ * !!!
+ * None of this stuff is implemented yet.  The only reason that it's here
+ * is so that the access methods can skip copying the key/data pair when
+ * the DB_LOCK flag isn't set.
+ */
+#if UINT_MAX > 65535
+#define	DB_LOCK		0x20000000	/* Do locking. */
+#define	DB_SHMEM	0x40000000	/* Use shared memory. */
+#define	DB_TXN		0x80000000	/* Do transactions. */
+#else
+#define	DB_LOCK		    0x2000	/* Do locking. */
+#define	DB_SHMEM	    0x4000	/* Use shared memory. */
+#define	DB_TXN		    0x8000	/* Do transactions. */
+#endif
+
+/* Access method description structure. */
+typedef struct __db {
+	DBTYPE type;			/* Underlying db type. */
+	int (*close)	(struct __db *);
+	int (*del)	(const struct __db *, const DBT *, uint);
+	int (*get)	(const struct __db *, const DBT *, DBT *, uint);
+	int (*put)	(const struct __db *, DBT *, const DBT *, uint);
+	int (*seq)	(const struct __db *, DBT *, DBT *, uint);
+	int (*sync)	(const struct __db *, uint);
+	void *internal;			/* Access method private. */
+	int (*fd)	(const struct __db *);
+} DB;
+
+#define	BTREEMAGIC	0x053162
+#define	BTREEVERSION	3
+
+/* Structure used to pass parameters to the btree routines. */
+typedef struct {
+#define	R_DUP		0x01	/* duplicate keys */
+	uint32	flags;
+	uint	cachesize;	/* bytes to cache */
+	int	maxkeypage;	/* maximum keys per page */
+	int	minkeypage;	/* minimum keys per page */
+	uint	psize;		/* page size */
+	int	(*compare)	/* comparison function */
+	    (const DBT *, const DBT *);
+	size_t	(*prefix)	/* prefix function */
+	    (const DBT *, const DBT *);
+	int	lorder;		/* byte order */
+} BTREEINFO;
+
+#define	HASHMAGIC	0x061561
+#define	HASHVERSION	2
+
+/* Structure used to pass parameters to the hashing routines. */
+typedef struct {
+	uint	bsize;		/* bucket size */
+	uint	ffactor;	/* fill factor */
+	uint	nelem;		/* number of elements */
+	uint	cachesize;	/* bytes to cache */
+	uint32		/* hash function */
+		(*hash) (const void *, size_t);
+	int	lorder;		/* byte order */
+} HASHINFO;
+
+/* Structure used to pass parameters to the record routines. */
+typedef struct {
+#define	R_FIXEDLEN	0x01	/* fixed-length records */
+#define	R_NOKEY		0x02	/* key not required */
+#define	R_SNAPSHOT	0x04	/* snapshot the input */
+	uint32	flags;
+	uint	cachesize;	/* bytes to cache */
+	uint	psize;		/* page size */
+	int	lorder;		/* byte order */
+	size_t	reclen;		/* record length (fixed-length records) */
+	uint8	bval;		/* delimiting byte (variable-length records */
+	char	*bfname;	/* btree file name */ 
+} RECNOINFO;
+
+#ifdef __DBINTERFACE_PRIVATE
+/*
+ * Little endian <==> big endian 32-bit swap macros.
+ *	M_32_SWAP	swap a memory location
+ *	P_32_SWAP	swap a referenced memory location
+ *	P_32_COPY	swap from one location to another
+ */
+#define	M_32_SWAP(a) {							\
+	uint32 _tmp = a;						\
+	((char *)&a)[0] = ((char *)&_tmp)[3];				\
+	((char *)&a)[1] = ((char *)&_tmp)[2];				\
+	((char *)&a)[2] = ((char *)&_tmp)[1];				\
+	((char *)&a)[3] = ((char *)&_tmp)[0];				\
+}
+#define	P_32_SWAP(a) {							\
+	uint32 _tmp = *(uint32 *)a;				\
+	((char *)a)[0] = ((char *)&_tmp)[3];				\
+	((char *)a)[1] = ((char *)&_tmp)[2];				\
+	((char *)a)[2] = ((char *)&_tmp)[1];				\
+	((char *)a)[3] = ((char *)&_tmp)[0];				\
+}
+#define	P_32_COPY(a, b) {						\
+	((char *)&(b))[0] = ((char *)&(a))[3];				\
+	((char *)&(b))[1] = ((char *)&(a))[2];				\
+	((char *)&(b))[2] = ((char *)&(a))[1];				\
+	((char *)&(b))[3] = ((char *)&(a))[0];				\
+}
+
+/*
+ * Little endian <==> big endian 16-bit swap macros.
+ *	M_16_SWAP	swap a memory location
+ *	P_16_SWAP	swap a referenced memory location
+ *	P_16_COPY	swap from one location to another
+ */
+#define	M_16_SWAP(a) {							\
+	uint16 _tmp = a;						\
+	((char *)&a)[0] = ((char *)&_tmp)[1];				\
+	((char *)&a)[1] = ((char *)&_tmp)[0];				\
+}
+#define	P_16_SWAP(a) {							\
+	uint16 _tmp = *(uint16 *)a;				\
+	((char *)a)[0] = ((char *)&_tmp)[1];				\
+	((char *)a)[1] = ((char *)&_tmp)[0];				\
+}
+#define	P_16_COPY(a, b) {						\
+	((char *)&(b))[0] = ((char *)&(a))[1];				\
+	((char *)&(b))[1] = ((char *)&(a))[0];				\
+}
+#endif
+
+PR_BEGIN_EXTERN_C
+
+extern DB *
+dbopen (const char *, int, int, DBTYPE, const void *);
+
+/* set or unset a global lock flag to disable the
+ * opening of any DBM file
+ */
+void dbSetOrClearDBLock(DBLockFlagEnum type);
+
+#ifdef __DBINTERFACE_PRIVATE
+DB	*__bt_open (const char *, int, int, const BTREEINFO *, int);
+DB	*__hash_open (const char *, int, int, const HASHINFO *, int);
+DB	*__rec_open (const char *, int, int, const RECNOINFO *, int);
+void	 __dbpanic (DB *dbp);
+#endif
+
+PR_END_EXTERN_C
+
+#endif /* !_DB_H_ */
diff -r a945361df361 -r 150b72113545 nss/lib/dbm/include/mpool.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nss/lib/dbm/include/mpool.h	Tue Aug 05 18:32:02 2014 +0200
@@ -0,0 +1,97 @@
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. ***REMOVED*** - see 
+ *    ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *	@(#)mpool.h	8.2 (Berkeley) 7/14/94
+ */
+
+#include <sys/queue.h>
+
+/*
+ * The memory pool scheme is a simple one.  Each in-memory page is referenced
+ * by a bucket which is threaded in up to two of three ways.  All active pages
+ * are threaded on a hash chain (hashed by page number) and an lru chain.
+ * Inactive pages are threaded on a free chain.  Each reference to a memory
+ * pool is handed an opaque MPOOL cookie which stores all of this information.
+ */
+#define	HASHSIZE	128
+#define	HASHKEY(pgno)	((pgno - 1) % HASHSIZE)
+
+/* The BKT structures are the elements of the queues. */
+typedef struct _bkt {
+	CIRCLEQ_ENTRY(_bkt) hq;		/* hash queue */
+	CIRCLEQ_ENTRY(_bkt) q;		/* lru queue */
+	void    *page;			/* page */
+	pgno_t   pgno;			/* page number */
+
+#define	MPOOL_DIRTY	0x01		/* page needs to be written */
+#define	MPOOL_PINNED	0x02		/* page is pinned into memory */
+	uint8 flags;			/* flags */
+} BKT;
+
+typedef struct MPOOL {
+	CIRCLEQ_HEAD(_lqh, _bkt) lqh;	/* lru queue head */
+					/* hash queue array */
+	CIRCLEQ_HEAD(_hqh, _bkt) hqh[HASHSIZE];
+	pgno_t	curcache;		/* current number of cached pages */
+	pgno_t	maxcache;		/* max number of cached pages */
+	pgno_t	npages;			/* number of pages in the file */
+	uint32	pagesize;		/* file page size */
+	int	fd;			/* file descriptor */
+					/* page in conversion routine */
+	void    (*pgin) (void *, pgno_t, void *);
+					/* page out conversion routine */
+	void    (*pgout) (void *, pgno_t, void *);
+	void	*pgcookie;		/* cookie for page in/out routines */
+#ifdef STATISTICS
+	uint32	cachehit;
+	uint32	cachemiss;
+	uint32	pagealloc;
+	uint32	pageflush;
+	uint32	pageget;
+	uint32	pagenew;
+	uint32	pageput;
+	uint32	pageread;
+	uint32	pagewrite;
+#endif
+} MPOOL;
+
+__BEGIN_DECLS
+MPOOL	*mpool_open (void *, int, pgno_t, pgno_t);
+void	 mpool_filter (MPOOL *, void (*)(void *, pgno_t, void *),
+	    void (*)(void *, pgno_t, void *), void *);
+void	*mpool_new (MPOOL *, pgno_t *);
+void	*mpool_get (MPOOL *, pgno_t, uint);
+int	 mpool_put (MPOOL *, void *, uint);
+int	 mpool_sync (MPOOL *);
+int	 mpool_close (MPOOL *);
+#ifdef STATISTICS
+void	 mpool_stat (MPOOL *);
+#endif
+__END_DECLS
diff -r a945361df361 -r 150b72113545 nss/lib/dbm/include/ncompat.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nss/lib/dbm/include/ncompat.h	Tue Aug 05 18:32:02 2014 +0200
@@ -0,0 +1,230 @@
+/*-
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. ***REMOVED*** - see 
+ *    ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *	@(#)compat.h	8.13 (Berkeley) 2/21/94
+ */
+
+#ifndef	_COMPAT_H_
+#define	_COMPAT_H_
+
+#include <sys/types.h>
+
+/*
+ * If your system doesn't typedef u_long, u_short, or u_char, change
+ * the 0 to a 1.
+ */
+#if 0
+typedef unsigned char	u_char;		/* 4.[34]BSD names. */
+typedef unsigned int	u_int;
+typedef unsigned long	u_long;
+typedef unsigned short	u_short;
+#endif
+
+/* If your system doesn't typedef size_t, change the 0 to a 1. */
+#if 0
+typedef unsigned int	size_t;		/* POSIX, 4.[34]BSD names. */
+#endif
+
+/* If your system doesn't typedef ssize_t, change the 0 to a 1. */
+#if 0
+typedef	int		ssize_t;	/* POSIX names. */
+#endif
+
+/*
+ * If your system doesn't have the POSIX type for a signal mask,
+ * change the 0 to a 1.
+ */
+#if 0					/* POSIX 1003.1 signal mask type. */
+typedef unsigned int	sigset_t;
+#endif
+
+/*
+ * If your system's vsprintf returns a char *, not an int,
+ * change the 0 to a 1.
+ */
+#if defined (__sun) && !defined(__SVR4) /* SUNOS */
+#define	VSPRINTF_CHARSTAR
+#endif
+/*
+ * If you don't have POSIX 1003.1 signals, the signal code surrounding the 
+ * temporary file creation is intended to block all of the possible signals
+ * long enough to create the file and unlink it.  All of this stuff is
+ * intended to use old-style BSD calls to fake POSIX 1003.1 calls.
+ */
+#ifdef	NO_POSIX_SIGNALS
+#define	sigemptyset(set)	(*(set) = 0)
+#define	sigfillset(set)		(*(set) = ~(sigset_t)0, 0)
+#define	sigaddset(set,signo)	(*(set) |= sigmask(signo), 0)
+#define	sigdelset(set,signo)	(*(set) &= ~sigmask(signo), 0)
+#define	sigismember(set,signo)	((*(set) & sigmask(signo)) != 0)
+
+#define	SIG_BLOCK	1
+#define	SIG_UNBLOCK	2
+#define	SIG_SETMASK	3
+
+static int __sigtemp;		/* For the use of sigprocmask */
+
+/* Repeated test of oset != NULL is to avoid "*0". */
+#define	sigprocmask(how, set, oset)					\
+	((__sigtemp =							\
+	(((how) == SIG_BLOCK) ?						\
+		sigblock(0) | *(set) :					\
+	(((how) == SIG_UNBLOCK) ?					\
+		sigblock(0) & ~(*(set)) :				\
+	((how) == SIG_SETMASK ?						\
+		*(set) : sigblock(0))))),				\
+	((oset) ? (*(oset ? oset : set) = sigsetmask(__sigtemp)) :	\
+		sigsetmask(__sigtemp)), 0)
+#endif
+
+/*
+ * If your system doesn't have an include file with the appropriate
+ * byte order set, make sure you specify the correct one.
+ */
+#ifndef BYTE_ORDER
+#define	LITTLE_ENDIAN	1234		/* LSB first: i386, vax */
+#define	BIG_ENDIAN	4321		/* MSB first: 68000, ibm, net */
+#define	BYTE_ORDER	BIG_ENDIAN	/* Set for your system. */
+#endif
+
+#if defined(SYSV) || defined(SYSTEM5) || defined(__sun)
+#define	index(a, b)		strchr(a, b)
+#define	rindex(a, b)		strrchr(a, b)
+#define	bzero(a, b)		memset(a, 0, b)
+#define	bcmp(a, b, n)		memcmp(a, b, n)
+#define	bcopy(a, b, n)		memmove(b, a, n)
+#endif
+
+#if defined(BSD) || defined(BSD4_3)
+#define	strchr(a, b)		index(a, b)
+#define	strrchr(a, b)		rindex(a, b)
+#define	memcmp(a, b, n)		bcmp(a, b, n)
+#define	memmove(a, b, n)	bcopy(b, a, n)
+#endif
+
+/*
+ * 32-bit machine.  The db routines are theoretically independent of
+ * the size of u_shorts and u_longs, but I don't know that anyone has
+ * ever actually tried it.  At a minimum, change the following #define's
+ * if you are trying to compile on a different type of system.
+ */
+#ifndef USHRT_MAX
+#define	USHRT_MAX		0xFFFF
+#define	ULONG_MAX		0xFFFFFFFF
+#endif
+
+#ifndef O_ACCMODE			/* POSIX 1003.1 access mode mask. */
+#define	O_ACCMODE	(O_RDONLY|O_WRONLY|O_RDWR)
+#endif
+
+#ifndef	_POSIX2_RE_DUP_MAX		/* POSIX 1003.2 RE limit. */
+#define	_POSIX2_RE_DUP_MAX	255
+#endif
+
+/*
+ * If you can't provide lock values in the open(2) call.  Note, this
+ * allows races to happen.
+ */
+#ifndef O_EXLOCK			/* 4.4BSD extension. */
+#define	O_EXLOCK	0
+#endif
+
+#ifndef O_SHLOCK			/* 4.4BSD extension. */
+#define	O_SHLOCK	0
+#endif
+
+#ifndef EFTYPE
+#define	EFTYPE		EINVAL		/* POSIX 1003.1 format errno. */
+#endif
+
+#ifndef	WCOREDUMP			/* 4.4BSD extension */
+#define	WCOREDUMP(a)	0
+#endif
+
+#ifndef	STDERR_FILENO
+#define	STDIN_FILENO	0		/* ANSI C #defines */
+#define	STDOUT_FILENO	1
+#define	STDERR_FILENO	2
+#endif
+
+#ifndef SEEK_END
+#define	SEEK_SET	0		/* POSIX 1003.1 seek values */
+#define	SEEK_CUR	1
+#define	SEEK_END	2
+#endif
+
+#ifndef _POSIX_VDISABLE			/* POSIX 1003.1 disabling char. */
+#define	_POSIX_VDISABLE	0		/* Some systems used 0. */
+#endif
+
+#ifndef	TCSASOFT			/* 4.4BSD extension. */
+#define	TCSASOFT	0
+#endif
+
+#ifndef _POSIX2_RE_DUP_MAX		/* POSIX 1003.2 values. */
+#define	_POSIX2_RE_DUP_MAX	255
+#endif
+
+#ifndef NULL				/* ANSI C #defines NULL everywhere. */
+#define	NULL		0
+#endif
+
+#ifndef	MAX				/* Usually found in <sys/param.h>. */
+#define	MAX(_a,_b)	((_a)<(_b)?(_b):(_a))
+#endif
+#ifndef	MIN				/* Usually found in <sys/param.h>. */
+#define	MIN(_a,_b)	((_a)<(_b)?(_a):(_b))
+#endif
+
+/* Default file permissions. */
+#ifndef DEFFILEMODE			/* 4.4BSD extension. */
+#define	DEFFILEMODE	(S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)
+#endif
+
+#ifndef __sun
+#ifndef S_ISDIR				/* POSIX 1003.1 file type tests. */
+#define	S_ISDIR(m)	((m & 0170000) == 0040000)	/* directory */
+#define	S_ISCHR(m)	((m & 0170000) == 0020000)	/* char special */
+#define	S_ISBLK(m)	((m & 0170000) == 0060000)	/* block special */
+#define	S_ISREG(m)	((m & 0170000) == 0100000)	/* regular file */
+#define	S_ISFIFO(m)	((m & 0170000) == 0010000)	/* fifo */
+#endif
+#ifndef S_ISLNK				/* BSD POSIX 1003.1 extensions */
+#define	S_ISLNK(m)	((m & 0170000) == 0120000)	/* symbolic link */
+#define	S_ISSOCK(m)	((m & 0170000) == 0140000)	/* socket */
+#endif
+#endif /* __sun */
+
+/* The type of a va_list. */
+#ifndef _BSD_VA_LIST_			/* 4.4BSD #define. */
+#define	_BSD_VA_LIST_	char *
+#endif
+
+#endif /* !_COMPAT_H_ */
diff -r a945361df361 -r 150b72113545 nss/lib/dbm/include/page.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nss/lib/dbm/include/page.h	Tue Aug 05 18:32:02 2014 +0200
@@ -0,0 +1,94 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Margo Seltzer.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. ***REMOVED*** - see 
+ *    ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *	@(#)page.h	8.2 (Berkeley) 5/31/94
+ */
+
+/*
+ * Definitions for hashing page file format.
+ */
+
+/*
+ * routines dealing with a data page
+ *
+ * page format:
+ *	+------------------------------+
+ * p	| n | keyoff | datoff | keyoff |
+ * 	+------------+--------+--------+
+ *	| datoff | free  |  ptr  | --> |
+ *	+--------+---------------------+
+ *	|	 F R E E A R E A       |
+ *	+--------------+---------------+
+ *	|  <---- - - - | data          |
+ *	+--------+-----+----+----------+
+ *	|  key   | data     | key      |
+ *	+--------+----------+----------+
+ *
+ * Pointer to the free space is always:  p[p[0] + 2]
+ * Amount of free space on the page is:  p[p[0] + 1]
+ */
+
+/*
+ * How many bytes required for this pair?
+ *	2 shorts in the table at the top of the page + room for the
+ *	key and room for the data
+ *
+ * We prohibit entering a pair on a page unless there is also room to append
+ * an overflow page. The reason for this it that you can get in a situation
+ * where a single key/data pair fits on a page, but you can't append an
+ * overflow page and later you'd have to split the key/data and handle like
+ * a big pair.
+ * You might as well do this up front.
+ */
+#ifndef PAGE_H
+#define PAGE_H
+
+#define	PAIRSIZE(K,D)	(2*sizeof(uint16) + (K)->size + (D)->size)
+#define BIGOVERHEAD	(4*sizeof(uint16))
+#define KEYSIZE(K)	(4*sizeof(uint16) + (K)->size);
+#define OVFLSIZE	(2*sizeof(uint16))
+#define FREESPACE(P)	((P)[(P)[0]+1])
+#define	OFFSET(P)	((P)[(P)[0]+2])
+#define PAIRFITS(P,K,D) \
+	(((P)[2] >= REAL_KEY) && \
+	    (PAIRSIZE((K),(D)) + OVFLSIZE) <= FREESPACE((P)))
+#define PAGE_META(N)	(((N)+3) * sizeof(uint16))
+
+typedef struct {
+	BUFHEAD *newp;
+	BUFHEAD *oldp;
+	BUFHEAD *nextp;
+	uint16 next_addr;
+}       SPLIT_RETURN;
+#endif
+
diff -r a945361df361 -r 150b72113545 nss/lib/dbm/include/queue.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nss/lib/dbm/include/queue.h	Tue Aug 05 18:32:02 2014 +0200
@@ -0,0 +1,243 @@
+/* 
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. ***REMOVED*** - see 
+ *    ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *	@(#)queue.h	8.3 (Berkeley) 12/13/93
+ */
+
+#ifndef	_QUEUE_H_
+#define	_QUEUE_H_
+
+/*
+ * This file defines three types of data structures: lists, tail queues,
+ * and circular queues.
+ *
+ * A list is headed by a single forward pointer (or an array of forward
+ * pointers for a hash table header). The elements are doubly linked
+ * so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list after
+ * an existing element or at the head of the list. A list may only be
+ * traversed in the forward direction.
+ *
+ * A tail queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list after
+ * an existing element, at the head of the list, or at the end of the
+ * list. A tail queue may only be traversed in the forward direction.
+ *
+ * A circle queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or after
+ * an existing element, at the head of the list, or at the end of the list.
+ * A circle queue may be traversed in either direction, but has a more
+ * complex end of list detection.
+ *
+ * For details on the use of these macros, see the queue(3) manual page.
+ */
+
+/*
+ * List definitions.
+ */
+#define LIST_HEAD(name, type)						\
+struct name {								\
+	struct type *lh_first;	/* first element */			\
+}
+
+#define LIST_ENTRY(type)						\
+struct {								\
+	struct type *le_next;	/* next element */			\
+	struct type **le_prev;	/* address of previous next element */	\
+}
+
+/*
+ * List functions.
+ */
+#define	LIST_INIT(head) {						\
+	(head)->lh_first = NULL;					\
+}
+
+#define LIST_INSERT_AFTER(listelm, elm, field) {			\
+	if (((elm)->field.le_next = (listelm)->field.le_next) != NULL)	\
+		(listelm)->field.le_next->field.le_prev =		\
+		    &(elm)->field.le_next;				\
+	(listelm)->field.le_next = (elm);				\
+	(elm)->field.le_prev = &(listelm)->field.le_next;		\
+}
+
+#define LIST_INSERT_HEAD(head, elm, field) {				\
+	if (((elm)->field.le_next = (head)->lh_first) != NULL)		\
+		(head)->lh_first->field.le_prev = &(elm)->field.le_next;\
+	(head)->lh_first = (elm);					\
+	(elm)->field.le_prev = &(head)->lh_first;			\
+}
+
+#define LIST_REMOVE(elm, field) {					\
+	if ((elm)->field.le_next != NULL)				\
+		(elm)->field.le_next->field.le_prev = 			\
+		    (elm)->field.le_prev;				\
+	*(elm)->field.le_prev = (elm)->field.le_next;			\
+}
+
+/*
+ * Tail queue definitions.
+ */
+#define TAILQ_HEAD(name, type)						\
+struct name {								\
+	struct type *tqh_first;	/* first element */			\
+	struct type **tqh_last;	/* addr of last next element */		\
+}
+
+#define TAILQ_ENTRY(type)						\
+struct {								\
+	struct type *tqe_next;	/* next element */			\
+	struct type **tqe_prev;	/* address of previous next element */	\
+}
+
+/*
+ * Tail queue functions.
+ */
+#define	TAILQ_INIT(head) {						\
+	(head)->tqh_first = NULL;					\
+	(head)->tqh_last = &(head)->tqh_first;				\
+}
+
+#define TAILQ_INSERT_HEAD(head, elm, field) {				\
+	if (((elm)->field.tqe_next = (head)->tqh_first) != NULL)	\
+		(elm)->field.tqe_next->field.tqe_prev =			\
+		    &(elm)->field.tqe_next;				\
+	else								\
+		(head)->tqh_last = &(elm)->field.tqe_next;		\
+	(head)->tqh_first = (elm);					\
+	(elm)->field.tqe_prev = &(head)->tqh_first;			\
+}
+
+#define TAILQ_INSERT_TAIL(head, elm, field) {				\
+	(elm)->field.tqe_next = NULL;					\
+	(elm)->field.tqe_prev = (head)->tqh_last;			\
+	*(head)->tqh_last = (elm);					\
+	(head)->tqh_last = &(elm)->field.tqe_next;			\
+}
+
+#define TAILQ_INSERT_AFTER(head, listelm, elm, field) {			\
+	if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
+		(elm)->field.tqe_next->field.tqe_prev = 		\
+		    &(elm)->field.tqe_next;				\
+	else								\
+		(head)->tqh_last = &(elm)->field.tqe_next;		\
+	(listelm)->field.tqe_next = (elm);				\
+	(elm)->field.tqe_prev = &(listelm)->field.tqe_next;		\
+}
+
+#define TAILQ_REMOVE(head, elm, field) {				\
+	if (((elm)->field.tqe_next) != NULL)				\
+		(elm)->field.tqe_next->field.tqe_prev = 		\
+		    (elm)->field.tqe_prev;				\
+	else								\
+		(head)->tqh_last = (elm)->field.tqe_prev;		\
+	*(elm)->field.tqe_prev = (elm)->field.tqe_next;			\
+}
+
+/*
+ * Circular queue definitions.
+ */
+#define CIRCLEQ_HEAD(name, type)					\
+struct name {								\
+	struct type *cqh_first;		/* first element */		\
+	struct type *cqh_last;		/* last element */		\
+}
+
+#define CIRCLEQ_ENTRY(type)						\
+struct {								\
+	struct type *cqe_next;		/* next element */		\
+	struct type *cqe_prev;		/* previous element */		\
+}
+
+/*
+ * Circular queue functions.
+ */
+#define	CIRCLEQ_INIT(head) {						\
+	(head)->cqh_first = (void *)(head);				\
+	(head)->cqh_last = (void *)(head);				\
+}
+
+#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) {		\
+	(elm)->field.cqe_next = (listelm)->field.cqe_next;		\
+	(elm)->field.cqe_prev = (listelm);				\
+	if ((listelm)->field.cqe_next == (void *)(head))		\
+		(head)->cqh_last = (elm);				\
+	else								\
+		(listelm)->field.cqe_next->field.cqe_prev = (elm);	\
+	(listelm)->field.cqe_next = (elm);				\
+}
+
+#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) {		\
+	(elm)->field.cqe_next = (listelm);				\
+	(elm)->field.cqe_prev = (listelm)->field.cqe_prev;		\
+	if ((listelm)->field.cqe_prev == (void *)(head))		\
+		(head)->cqh_first = (elm);				\
+	else								\
+		(listelm)->field.cqe_prev->field.cqe_next = (elm);	\
+	(listelm)->field.cqe_prev = (elm);				\
+}
+
+#define CIRCLEQ_INSERT_HEAD(head, elm, field) {				\
+	(elm)->field.cqe_next = (head)->cqh_first;			\
+	(elm)->field.cqe_prev = (void *)(head);				\
+	if ((head)->cqh_last == (void *)(head))				\
+		(head)->cqh_last = (elm);				\
+	else								\
+		(head)->cqh_first->field.cqe_prev = (elm);		\
+	(head)->cqh_first = (elm);					\
+}
+
+#define CIRCLEQ_INSERT_TAIL(head, elm, field) {				\
+	(elm)->field.cqe_next = (void *)(head);				\
+	(elm)->field.cqe_prev = (head)->cqh_last;			\
+	if ((head)->cqh_first == (void *)(head))			\
+		(head)->cqh_first = (elm);				\
+	else								\
+		(head)->cqh_last->field.cqe_next = (elm);		\
+	(head)->cqh_last = (elm);					\
+}
+
+#define	CIRCLEQ_REMOVE(head, elm, field) {				\
+	if ((elm)->field.cqe_next == (void *)(head))			\
+		(head)->cqh_last = (elm)->field.cqe_prev;		\
+	else								\
+		(elm)->field.cqe_next->field.cqe_prev =			\
+		    (elm)->field.cqe_prev;				\
+	if ((elm)->field.cqe_prev == (void *)(head))			\
+		(head)->cqh_first = (elm)->field.cqe_next;		\
+	else								\
+		(elm)->field.cqe_prev->field.cqe_next =			\
+		    (elm)->field.cqe_next;				\
+}
+#endif	/* !_QUEUE_H_ */
diff -r a945361df361 -r 150b72113545 nss/lib/dbm/include/search.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nss/lib/dbm/include/search.h	Tue Aug 05 18:32:02 2014 +0200
@@ -0,0 +1,49 @@
+/*-
+ * Copyright (c) 1990, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Margo Seltzer.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. ***REMOVED*** - see 
+ *    ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *	@(#)search.h	8.1 (Berkeley) 6/4/93
+ */
+
+/* Backward compatibility to hsearch interface. */
+typedef struct entry {
+	char *key;
+	char *data;
+} ENTRY;
+
+typedef enum {
+	FIND, ENTER
+} ACTION;
+
+int	 hcreate (unsigned int);
+void	 hdestroy (void);
+ENTRY	*hsearch (ENTRY, ACTION);
diff -r a945361df361 -r 150b72113545 nss/lib/dbm/include/winfile.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nss/lib/dbm/include/winfile.h	Tue Aug 05 18:32:02 2014 +0200
@@ -0,0 +1,106 @@
+
+/* ---------------------------------------------------------------------------
+    Stuff to fake unix file I/O on windows boxes
+    ------------------------------------------------------------------------*/
+
+#ifndef WINFILE_H
+#define WINFILE_H
+
+#ifdef _WINDOWS
+/* hacked out of <dirent.h> on an SGI */
+#if defined(XP_WIN32) || defined(_WIN32)
+/* 32-bit stuff here */
+#include <windows.h>
+#include <stdlib.h>
+#ifdef __MINGW32__
+#include <sys/types.h>
+#include <sys/stat.h>
+#else
+#include <sys\types.h>
+#include <sys\stat.h>
+#endif
+
+typedef struct DIR_Struct {
+    void            * directoryPtr;
+    WIN32_FIND_DATA   data;
+} DIR;
+
+#define _ST_FSTYPSZ 16
+
+#if !defined(__BORLANDC__) && !defined(__GNUC__)
+ typedef unsigned long mode_t;
+ typedef          long uid_t;
+ typedef          long gid_t;
+ typedef          long off_t;
+ typedef unsigned long nlink_t;
+#endif 
+
+typedef struct timestruc {
+    time_t  tv_sec;         /* seconds */
+    long    tv_nsec;        /* and nanoseconds */
+} timestruc_t;
+
+
+struct dirent {                                 /* data from readdir() */
+        ino_t           d_ino;                  /* inode number of entry */
+        off_t           d_off;                  /* offset of disk direntory entry */
+        unsigned short  d_reclen;               /* length of this record */
+        char            d_name[_MAX_FNAME];     /* name of file */
+};
+
+#if !defined(__BORLANDC__) && !defined (__GNUC__)
+#define S_ISDIR(s)  ((s) & _S_IFDIR)
+#endif
+
+#else /* _WIN32 */
+/* 16-bit windows stuff */
+
+#include <sys\types.h>
+#include <sys\stat.h>
+#include <dos.h>
+
+
+
+/*	Getting cocky to support multiple file systems */
+typedef struct	dirStruct_tag	{
+	struct _find_t	file_data;
+	char			c_checkdrive;
+} dirStruct;
+
+typedef struct DIR_Struct {
+    void            * directoryPtr;
+    dirStruct         data;
+} DIR;
+
+#define _ST_FSTYPSZ 16
+typedef unsigned long mode_t;
+typedef          long uid_t;
+typedef          long gid_t;
+typedef          long off_t;
+typedef unsigned long nlink_t;
+
+typedef struct timestruc {
+    time_t  tv_sec;         /* seconds */
+    long    tv_nsec;        /* and nanoseconds */
+} timestruc_t;
+
+struct dirent {                             /* data from readdir() */
+        ino_t           d_ino;              /* inode number of entry */
+        off_t           d_off;              /* offset of disk direntory entry */
+        unsigned short  d_reclen;           /* length of this record */
+#ifdef XP_WIN32
+        char            d_name[_MAX_FNAME]; /* name of file */
+#else
+        char            d_name[20]; /* name of file */
+#endif
+};
+
+#define S_ISDIR(s)  ((s) & _S_IFDIR)
+
+#endif /* 16-bit windows */
+
+#define CONST const
+
+#endif /* _WINDOWS */
+
+#endif /* WINFILE_H */
diff -r a945361df361 -r 150b72113545 nss/lib/dbm/manifest.mn
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nss/lib/dbm/manifest.mn	Tue Aug 05 18:32:02 2014 +0200
@@ -0,0 +1,17 @@
+#! gmake
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+CORE_DEPTH = ../..
+
+MODULE = dbm
+
+IMPORTS = nspr20/v4.4.1
+
+RELEASE = dbm
+
+DIRS =  include \
+        src     \
+	$(NULL)
diff -r a945361df361 -r 150b72113545 nss/lib/dbm/src/.hash.c.swp
Binary file nss/lib/dbm/src/.hash.c.swp has changed
diff -r a945361df361 -r 150b72113545 nss/lib/dbm/src/Makefile
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nss/lib/dbm/src/Makefile	Tue Aug 05 18:32:02 2014 +0200
@@ -0,0 +1,48 @@
+#! gmake
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#######################################################################
+# (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)       #
+#######################################################################
+
+include $(CORE_DEPTH)/lib/dbm/config/config.mk
+
+#######################################################################
+# (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).                              #
+#######################################################################
+
+
+
diff -r a945361df361 -r 150b72113545 nss/lib/dbm/src/config.mk
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nss/lib/dbm/src/config.mk	Tue Aug 05 18:32:02 2014 +0200
@@ -0,0 +1,33 @@
+#! gmake
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DEFINES += -DMEMMOVE -D__DBINTERFACE_PRIVATE
+
+#
+#  Currently, override TARGETS variable so that only static libraries
+#  are specifed as dependencies within rules.mk.
+#
+
+TARGETS        = $(LIBRARY)
+SHARED_LIBRARY =
+IMPORT_LIBRARY =
+PURE_LIBRARY   =
+PROGRAM        =
+
+ifdef SHARED_LIBRARY
+	ifeq (,$(filter-out WIN%,$(OS_TARGET)))
+		DLLBASE=/BASE:0x30000000
+		RES=$(OBJDIR)/dbm.res
+		RESNAME=../include/dbm.rc
+	endif
+	ifeq ($(DLL_SUFFIX),dll)
+		DEFINES += -D_DLL
+	endif
+endif
+
+ifeq ($(OS_TARGET),AIX)
+	OS_LIBS += -lc_r
+endif
diff -r a945361df361 -r 150b72113545 nss/lib/dbm/src/db.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nss/lib/dbm/src/db.c	Tue Aug 05 18:32:02 2014 +0200
@@ -0,0 +1,136 @@
+/*-
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. ***REMOVED*** - see 
+ *    ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)db.c	8.4 (Berkeley) 2/21/94";
+#endif /* LIBC_SCCS and not lint */
+
+#ifndef __DBINTERFACE_PRIVATE
+#define __DBINTERFACE_PRIVATE
+#endif
+#ifdef macintosh
+#include <unix.h>
+#else
+#include <sys/types.h>
+#endif
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stddef.h>
+#include <stdio.h>
+
+#include "mcom_db.h"
+
+/* a global flag that locks closed all databases */
+int all_databases_locked_closed = 0;
+
+/* set or unset a global lock flag to disable the
+ * opening of any DBM file
+ */
+void 
+dbSetOrClearDBLock(DBLockFlagEnum type)
+{
+	if(type == LockOutDatabase)
+		all_databases_locked_closed = 1;
+	else
+		all_databases_locked_closed = 0;
+}
+
+DB *
+dbopen(const char *fname, int flags,int mode, DBTYPE type, const void *openinfo)
+{
+
+	/* lock out all file databases.  Let in-memory databases through
+	 */
+	if(all_databases_locked_closed && fname)
+	  {
+		errno = EINVAL;
+		return(NULL);
+	  }
+
+#define	DB_FLAGS	(DB_LOCK | DB_SHMEM | DB_TXN)
+
+
+#if 0  /* most systems don't have EXLOCK and SHLOCK */
+#define	USE_OPEN_FLAGS							\
+	(O_CREAT | O_EXCL | O_EXLOCK | O_NONBLOCK | O_RDONLY |		\
+	 O_RDWR | O_SHLOCK | O_TRUNC)
+#else
+#define	USE_OPEN_FLAGS							\
+	(O_CREAT | O_EXCL  | O_RDONLY |		\
+	 O_RDWR | O_TRUNC)
+#endif
+
+	if ((flags & ~(USE_OPEN_FLAGS | DB_FLAGS)) == 0)
+		switch (type) {
+/* we don't need btree and recno right now */	
+#if 0
+		case DB_BTREE:
+			return (__bt_open(fname, flags & USE_OPEN_FLAGS,
+			    mode, openinfo, flags & DB_FLAGS));
+		case DB_RECNO:
+			return (__rec_open(fname, flags & USE_OPEN_FLAGS,
+			    mode, openinfo, flags & DB_FLAGS));
+#endif
+
+		case DB_HASH:
+			return (__hash_open(fname, flags & USE_OPEN_FLAGS,
+			    mode, (const HASHINFO *)openinfo, flags & DB_FLAGS));
+		default:
+			break;
+		}
+	errno = EINVAL;
+	return (NULL);
+}
+
+static int
+__dberr()
+{
+	return (RET_ERROR);
+}
+
+/*
+ * __DBPANIC -- Stop.
+ *
+ * Parameters:
+ *	dbp:	pointer to the DB structure.
+ */
+void
+__dbpanic(DB *dbp)
+{
+	/* The only thing that can succeed is a close. */
+	dbp->del = (int (*)(const struct __db *, const DBT *, uint))__dberr;
+	dbp->fd = (int (*)(const struct __db *))__dberr;
+	dbp->get = (int (*)(const struct __db *, const DBT *, DBT *, uint))__dberr;
+	dbp->put = (int (*)(const struct __db *, DBT *, const DBT *, uint))__dberr;
+	dbp->seq = (int (*)(const struct __db *, DBT *, DBT *, uint))__dberr;
+	dbp->sync = (int (*)(const struct __db *, uint))__dberr;
+}
diff -r a945361df361 -r 150b72113545 nss/lib/dbm/src/dirent.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nss/lib/dbm/src/dirent.c	Tue Aug 05 18:32:02 2014 +0200
@@ -0,0 +1,348 @@
+#ifdef OS2
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include <dirent.h>
+#include <errno.h>
+
+/*#ifndef __EMX__ 
+#include <libx.h>
+#endif */
+
+#define INCL_DOSFILEMGR
+#define INCL_DOSERRORS
+#include <os2.h>
+
+#if OS2 >= 2
+# define FFBUF	FILEFINDBUF3
+# define Word	ULONG
+  /*
+   * LS20 recommends a request count of 100, but according to the
+   * APAR text it does not lead to missing files, just to funny
+   * numbers of returned entries.
+   *
+   * LS30 HPFS386 requires a count greater than 2, or some files
+   * are missing (those starting with a character less that '.').
+   *
+   * Novell loses entries which overflow the buffer. In previous
+   * versions of dirent2, this could have lead to missing files
+   * when the average length of 100 directory entries was 40 bytes
+   * or more (quite unlikely for files on a Novell server).
+   *
+   * Conclusion: Make sure that the entries all fit into the buffer
+   * and that the buffer is large enough for more than 2 entries
+   * (each entry is at most 300 bytes long). And ignore the LS20
+   * effect.
+   */
+# define Count	25
+# define BufSz	(25 * (sizeof(FILEFINDBUF3)+1))
+#else
+# define FFBUF	FILEFINDBUF
+# define Word	USHORT
+# define BufSz	1024
+# define Count	3
+#endif
+
+#if defined(__IBMC__) || defined(__IBMCPP__)
+  #define error(rc) _doserrno = rc, errno = EOS2ERR
+#elif defined(MICROSOFT)
+  #define error(rc) _doserrno = rc, errno = 255
+#else
+  #define error(rc) errno = 255
+#endif
+
+struct _dirdescr {
+	HDIR		handle;		/* DosFindFirst handle */
+	char		fstype;		/* filesystem type */
+	Word		count;		/* valid entries in <ffbuf> */
+	long		number;		/* absolute number of next entry */
+	int		index;		/* relative number of next entry */
+	FFBUF *		next;		/* pointer to next entry */
+	char		name[MAXPATHLEN+3]; /* directory name */
+	unsigned	attrmask;	/* attribute mask for seekdir */
+	struct dirent	entry;		/* buffer for directory entry */
+	BYTE		ffbuf[BufSz];
+};
+
+/*
+ * Return first char of filesystem type, or 0 if unknown.
+ */
+static char
+getFSType(const char *path)
+{
+	static char cache[1+26];
+	char drive[3], info[512];
+	Word unit, infolen;
+	char r;
+
+	if (isalpha(path[0]) && path[1] == ':') {
+		unit = toupper(path[0]) - '@';
+		path += 2;
+	} else {
+		ULONG driveMap;
+#if OS2 >= 2
+		if (DosQueryCurrentDisk(&unit, &driveMap))
+#else
+		if (DosQCurDisk(&unit, &driveMap))
+#endif
+			return 0;
+	}
+
+	if ((path[0] == '\\' || path[0] == '/')
+	 && (path[1] == '\\' || path[1] == '/'))
+		return 0;
+
+	if (cache [unit])
+		return cache [unit];
+
+	drive[0] = '@' + unit;
+	drive[1] = ':';
+	drive[2] = '\0';
+	infolen = sizeof info;
+#if OS2 >= 2
+	if (DosQueryFSAttach(drive, 0, FSAIL_QUERYNAME, (PVOID)info, &infolen))
+		return 0;
+	if (infolen >= sizeof(FSQBUFFER2)) {
+		FSQBUFFER2 *p = (FSQBUFFER2 *)info;
+		r = p->szFSDName[p->cbName];
+	} else
+#else
+	if (DosQFSAttach((PSZ)drive, 0, FSAIL_QUERYNAME, (PVOID)info, &infolen, 0))
+		return 0;
+	if (infolen >= 9) {
+		char *p = info + sizeof(USHORT);
+		p += sizeof(USHORT) + *(USHORT *)p + 1 + sizeof(USHORT);
+		r = *p;
+	} else
+#endif
+		r = 0;
+	return cache [unit] = r;
+}
+
+char *
+abs_path(const char *name, char *buffer, int len)
+{
+	char buf[4];
+	if (isalpha(name[0]) && name[1] == ':' && name[2] == '\0') {
+		buf[0] = name[0];
+		buf[1] = name[1];
+		buf[2] = '.';
+		buf[3] = '\0';
+		name = buf;
+	}
+#if OS2 >= 2
+	if (DosQueryPathInfo((PSZ)name, FIL_QUERYFULLNAME, buffer, len))
+#else
+	if (DosQPathInfo((PSZ)name, FIL_QUERYFULLNAME, (PBYTE)buffer, len, 0L))
+#endif
+		return NULL;
+	return buffer;
+}
+
+DIR *
+openxdir(const char *path, unsigned att_mask)
+{
+	DIR *dir;
+	char name[MAXPATHLEN+3];
+	Word rc;
+
+	dir = malloc(sizeof(DIR));
+	if (dir == NULL) {
+		errno = ENOMEM;
+		return NULL;
+	}
+
+	strncpy(name, path, MAXPATHLEN);
+	name[MAXPATHLEN] = '\0';
+	switch (name[strlen(name)-1]) {
+	default:
+		strcat(name, "\\");
+	case '\\':
+	case '/':
+	case ':':
+		;
+	}
+	strcat(name, ".");
+	if (!abs_path(name, dir->name, MAXPATHLEN+1))
+		strcpy(dir->name, name);
+	if (dir->name[strlen(dir->name)-1] == '\\')
+		strcat(dir->name, "*");
+	else
+		strcat(dir->name, "\\*");
+
+	dir->fstype = getFSType(dir->name);
+	dir->attrmask = att_mask | A_DIR;
+
+	dir->handle = HDIR_CREATE;
+	dir->count = 100;
+#if OS2 >= 2
+	rc = DosFindFirst(dir->name, &dir->handle, dir->attrmask,
+		dir->ffbuf, sizeof dir->ffbuf, &dir->count, FIL_STANDARD);
+#else
+	rc = DosFindFirst((PSZ)dir->name, &dir->handle, dir->attrmask,
+		(PFILEFINDBUF)dir->ffbuf, sizeof dir->ffbuf, &dir->count, 0);
+#endif
+	switch (rc) {
+	default:
+		free(dir);
+		error(rc);
+		return NULL;
+	case NO_ERROR:
+	case ERROR_NO_MORE_FILES:
+		;
+	}
+
+	dir->number = 0;
+	dir->index = 0;
+	dir->next = (FFBUF *)dir->ffbuf;
+
+	return (DIR *)dir;
+}
+
+DIR *
+opendir(const char *pathname)
+{
+	return openxdir(pathname, 0);
+}
+
+struct dirent *
+readdir(DIR *dir)
+{
+	static int dummy_ino = 2;
+
+	if (dir->index == dir->count) {
+		Word rc;
+		dir->count = 100;
+#if OS2 >= 2
+		rc = DosFindNext(dir->handle, dir->ffbuf,
+			sizeof dir->ffbuf, &dir->count);
+#else
+		rc = DosFindNext(dir->handle, (PFILEFINDBUF)dir->ffbuf,
+			sizeof dir->ffbuf, &dir->count);
+#endif
+		if (rc) {
+			error(rc);
+			return NULL;
+		}
+
+		dir->index = 0;
+		dir->next = (FFBUF *)dir->ffbuf;
+	}
+
+	if (dir->index == dir->count)
+		return NULL;
+
+	memcpy(dir->entry.d_name, dir->next->achName, dir->next->cchName);
+	dir->entry.d_name[dir->next->cchName] = '\0';
+	dir->entry.d_ino = dummy_ino++;
+	dir->entry.d_reclen = dir->next->cchName;
+	dir->entry.d_namlen = dir->next->cchName;
+	dir->entry.d_size = dir->next->cbFile;
+	dir->entry.d_attribute = dir->next->attrFile;
+	dir->entry.d_time = *(USHORT *)&dir->next->ftimeLastWrite;
+	dir->entry.d_date = *(USHORT *)&dir->next->fdateLastWrite;
+
+	switch (dir->fstype) {
+	case 'F': /* FAT */
+	case 'C': /* CDFS */
+		if (dir->next->attrFile & FILE_DIRECTORY)
+			strupr(dir->entry.d_name);
+		else
+			strlwr(dir->entry.d_name);
+	}
+
+#if OS2 >= 2
+	dir->next = (FFBUF *)((BYTE *)dir->next + dir->next->oNextEntryOffset);
+#else
+	dir->next = (FFBUF *)((BYTE *)dir->next->achName + dir->next->cchName + 1);
+#endif
+	++dir->number;
+	++dir->index;
+
+	return &dir->entry;
+}
+
+long
+telldir(DIR *dir)
+{
+	return dir->number;
+}
+
+void
+seekdir(DIR *dir, long off)
+{
+	if (dir->number > off) {
+		char name[MAXPATHLEN+2];
+		Word rc;
+
+		DosFindClose(dir->handle);
+
+		strcpy(name, dir->name);
+		strcat(name, "*");
+
+		dir->handle = HDIR_CREATE;
+		dir->count = 32767;
+#if OS2 >= 2
+		rc = DosFindFirst(name, &dir->handle, dir->attrmask,
+			dir->ffbuf, sizeof dir->ffbuf, &dir->count, FIL_STANDARD);
+#else
+		rc = DosFindFirst((PSZ)name, &dir->handle, dir->attrmask,
+			(PFILEFINDBUF)dir->ffbuf, sizeof dir->ffbuf, &dir->count, 0);
+#endif
+		switch (rc) {
+		default:
+			error(rc);
+			return;
+		case NO_ERROR:
+		case ERROR_NO_MORE_FILES:
+			;
+		}
+
+		dir->number = 0;
+		dir->index = 0;
+		dir->next = (FFBUF *)dir->ffbuf;
+	}
+
+	while (dir->number < off && readdir(dir))
+		;
+}
+
+void
+closedir(DIR *dir)
+{
+	DosFindClose(dir->handle);
+	free(dir);
+}
+
+/*****************************************************************************/
+
+#ifdef TEST
+
+main(int argc, char **argv)
+{
+	int i;
+	DIR *dir;
+	struct dirent *ep;
+
+	for (i = 1; i < argc; ++i) {
+		dir = opendir(argv[i]);
+		if (!dir)
+			continue;
+		while (ep = readdir(dir))
+			if (strchr("\\/:", argv[i] [strlen(argv[i]) - 1]))
+				printf("%s%s\n", argv[i], ep->d_name);
+			else
+				printf("%s/%s\n", argv[i], ep->d_name);
+		closedir(dir);
+	}
+
+	return 0;
+}
+
+#endif
+
+#endif /* OS2 */
+
diff -r a945361df361 -r 150b72113545 nss/lib/dbm/src/dirent.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nss/lib/dbm/src/dirent.h	Tue Aug 05 18:32:02 2014 +0200
@@ -0,0 +1,97 @@
+#ifndef __DIRENT_H__
+#define __DIRENT_H__
+/*
+ * @(#)msd_dir.h 1.4 87/11/06   Public Domain.
+ *
+ *  A public domain implementation of BSD directory routines for
+ *  MS-DOS.  Written by Michael Rendell ({uunet,utai}michael at garfield),
+ *  August 1897
+ *
+ *  Extended by Peter Lim (lim at mullian.oz) to overcome some MS DOS quirks
+ *  and returns 2 more pieces of information - file size & attribute.
+ *  Plus a little reshuffling of some #define's positions    December 1987
+ *
+ *  Some modifications by Martin Junius                      02-14-89
+ *
+ *	AK900712
+ *	AK910410	abs_path - make absolute path
+ *
+ */
+
+#ifdef __EMX__
+#include <sys/param.h>
+#else
+#if defined(__IBMC__) || defined(__IBMCPP__) || defined(XP_W32_MSVC)
+#include <stdio.h>
+#ifdef MAXPATHLEN
+	#undef MAXPATHLEN
+#endif
+#define MAXPATHLEN (FILENAME_MAX*4)
+#define MAXNAMLEN FILENAME_MAX
+
+#else
+#include <param.h>
+#endif
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* attribute stuff */
+#ifndef A_RONLY
+# define A_RONLY   0x01
+# define A_HIDDEN  0x02
+# define A_SYSTEM  0x04
+# define A_LABEL   0x08
+# define A_DIR     0x10
+# define A_ARCHIVE 0x20
+#endif
+
+struct dirent {
+#if defined(OS2) || defined(WIN32)        /* use the layout of EMX to avoid trouble */
+    int            d_ino;                 /* Dummy */
+    int            d_reclen;		  /* Dummy, same as d_namlen */
+    int            d_namlen;              /* length of name */
+    char           d_name[MAXNAMLEN + 1];
+    unsigned long  d_size;
+    unsigned short d_attribute;           /* attributes (see above) */
+    unsigned short d_time;                /* modification time */
+    unsigned short d_date;                /* modification date */
+#else
+    char	   d_name[MAXNAMLEN + 1]; /* garentee null termination */
+    char	   d_attribute;		  /* .. extension .. */
+    unsigned long  d_size;		  /* .. extension .. */
+#endif
+};
+
+typedef struct _dirdescr DIR;
+/* the structs do not have to be defined here */
+
+extern DIR		*opendir(const char *);
+extern DIR		*openxdir(const char *, unsigned);
+extern struct dirent	*readdir(DIR *);
+extern void		seekdir(DIR *, long);
+extern long		telldir(DIR *);
+extern void 		closedir(DIR *);
+#define			rewinddir(dirp) seekdir(dirp, 0L)
+
+extern char *		abs_path(const char *name, char *buffer, int len);
+
+#ifndef S_IFMT
+#define S_IFMT ( S_IFDIR | S_IFREG )
+#endif
+
+#ifndef S_ISDIR
+#define S_ISDIR( m )                    (((m) & S_IFMT) == S_IFDIR)
+#endif
+
+#ifndef S_ISREG
+#define S_ISREG( m )                    (((m) & S_IFMT) == S_IFREG)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff -r a945361df361 -r 150b72113545 nss/lib/dbm/src/h_bigkey.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nss/lib/dbm/src/h_bigkey.c	Tue Aug 05 18:32:02 2014 +0200
@@ -0,0 +1,709 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Margo Seltzer.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. ***REMOVED*** - see 
+ *    ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)hash_bigkey.c	8.3 (Berkeley) 5/31/94";
+#endif /* LIBC_SCCS and not lint */
+
+/*
+ * PACKAGE: hash
+ * DESCRIPTION:
+ *	Big key/data handling for the hashing package.
+ *
+ * ROUTINES:
+ * External
+ *	__big_keydata
+ *	__big_split
+ *	__big_insert
+ *	__big_return
+ *	__big_delete
+ *	__find_last_page
+ * Internal
+ *	collect_key
+ *	collect_data
+ */
+
+#if !defined(_WIN32) && !defined(_WINDOWS) && !defined(macintosh)
+#include <sys/param.h>
+#endif
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef DEBUG
+#include <assert.h>
+#endif
+
+#include "mcom_db.h"
+#include "hash.h"
+#include "page.h"
+/* #include "extern.h" */
+
+static int collect_key __P((HTAB *, BUFHEAD *, int, DBT *, int));
+static int collect_data __P((HTAB *, BUFHEAD *, int, int));
+
+/*
+ * Big_insert
+ *
+ * You need to do an insert and the key/data pair is too big
+ *
+ * Returns:
+ * 0 ==> OK
+ *-1 ==> ERROR
+ */
+extern int
+__big_insert(HTAB *hashp, BUFHEAD *bufp, const DBT *key, const DBT *val)
+{
+	register uint16 *p;
+	uint key_size, n, val_size;
+	uint16 space, move_bytes, off;
+	char *cp, *key_data, *val_data;
+
+	cp = bufp->page;		/* Character pointer of p. */
+	p = (uint16 *)cp;
+
+	key_data = (char *)key->data;
+	key_size = key->size;
+	val_data = (char *)val->data;
+	val_size = val->size;
+
+	/* First move the Key */
+	for (space = FREESPACE(p) - BIGOVERHEAD; key_size;
+	    space = FREESPACE(p) - BIGOVERHEAD) {
+		move_bytes = PR_MIN(space, key_size);
+		off = OFFSET(p) - move_bytes;
+		memmove(cp + off, key_data, move_bytes);
+		key_size -= move_bytes;
+		key_data += move_bytes;
+		n = p[0];
+		p[++n] = off;
+		p[0] = ++n;
+		FREESPACE(p) = off - PAGE_META(n);
+		OFFSET(p) = off;
+		p[n] = PARTIAL_KEY;
+		bufp = __add_ovflpage(hashp, bufp);
+		if (!bufp)
+			return (-1);
+		n = p[0];
+		if (!key_size) {
+			if (FREESPACE(p)) {
+				move_bytes = PR_MIN(FREESPACE(p), val_size);
+				off = OFFSET(p) - move_bytes;
+				p[n] = off;
+				memmove(cp + off, val_data, move_bytes);
+				val_data += move_bytes;
+				val_size -= move_bytes;
+				p[n - 2] = FULL_KEY_DATA;
+				FREESPACE(p) = FREESPACE(p) - move_bytes;
+				OFFSET(p) = off;
+			} else
+				p[n - 2] = FULL_KEY;
+		}
+		p = (uint16 *)bufp->page;
+		cp = bufp->page;
+		bufp->flags |= BUF_MOD;
+	}
+
+	/* Now move the data */
+	for (space = FREESPACE(p) - BIGOVERHEAD; val_size;
+	    space = FREESPACE(p) - BIGOVERHEAD) {
+		move_bytes = PR_MIN(space, val_size);
+		/*
+		 * Here's the hack to make sure that if the data ends on the
+		 * same page as the key ends, FREESPACE is at least one.
+		 */
+		if (space == val_size && val_size == val->size)
+			move_bytes--;
+		off = OFFSET(p) - move_bytes;
+		memmove(cp + off, val_data, move_bytes);
+		val_size -= move_bytes;
+		val_data += move_bytes;
+		n = p[0];
+		p[++n] = off;
+		p[0] = ++n;
+		FREESPACE(p) = off - PAGE_META(n);
+		OFFSET(p) = off;
+		if (val_size) {
+			p[n] = FULL_KEY;
+			bufp = __add_ovflpage(hashp, bufp);
+			if (!bufp)
+				return (-1);
+			cp = bufp->page;
+			p = (uint16 *)cp;
+		} else
+			p[n] = FULL_KEY_DATA;
+		bufp->flags |= BUF_MOD;
+	}
+	return (0);
+}
+
+/*
+ * Called when bufp's page  contains a partial key (index should be 1)
+ *
+ * All pages in the big key/data pair except bufp are freed.  We cannot
+ * free bufp because the page pointing to it is lost and we can't get rid
+ * of its pointer.
+ *
+ * Returns:
+ * 0 => OK
+ *-1 => ERROR
+ */
+extern int
+__big_delete(HTAB *hashp, BUFHEAD *bufp)
+{
+	register BUFHEAD *last_bfp, *rbufp;
+	uint16 *bp, pageno;
+	int key_done, n;
+
+	rbufp = bufp;
+	last_bfp = NULL;
+	bp = (uint16 *)bufp->page;
+	pageno = 0;
+	key_done = 0;
+
+	while (!key_done || (bp[2] != FULL_KEY_DATA)) {
+		if (bp[2] == FULL_KEY || bp[2] == FULL_KEY_DATA)
+			key_done = 1;
+
+		/*
+		 * If there is freespace left on a FULL_KEY_DATA page, then
+		 * the data is short and fits entirely on this page, and this
+		 * is the last page.
+		 */
+		if (bp[2] == FULL_KEY_DATA && FREESPACE(bp))
+			break;
+		pageno = bp[bp[0] - 1];
+		rbufp->flags |= BUF_MOD;
+		rbufp = __get_buf(hashp, pageno, rbufp, 0);
+		if (last_bfp)
+			__free_ovflpage(hashp, last_bfp);
+		last_bfp = rbufp;
+		if (!rbufp)
+			return (-1);		/* Error. */
+		bp = (uint16 *)rbufp->page;
+	}
+
+	/*
+	 * If we get here then rbufp points to the last page of the big
+	 * key/data pair.  Bufp points to the first one -- it should now be
+	 * empty pointing to the next page after this pair.  Can't free it
+	 * because we don't have the page pointing to it.
+	 */
+
+	/* This is information from the last page of the pair. */
+	n = bp[0];
+	pageno = bp[n - 1];
+
+	/* Now, bp is the first page of the pair. */
+	bp = (uint16 *)bufp->page;
+	if (n > 2) {
+		/* There is an overflow page. */
+		bp[1] = pageno;
+		bp[2] = OVFLPAGE;
+		bufp->ovfl = rbufp->ovfl;
+	} else
+		/* This is the last page. */
+		bufp->ovfl = NULL;
+	n -= 2;
+	bp[0] = n;
+	FREESPACE(bp) = hashp->BSIZE - PAGE_META(n);
+	OFFSET(bp) = hashp->BSIZE - 1;
+
+	bufp->flags |= BUF_MOD;
+	if (rbufp)
+		__free_ovflpage(hashp, rbufp);
+	if (last_bfp != rbufp)
+		__free_ovflpage(hashp, last_bfp);
+
+	hashp->NKEYS--;
+	return (0);
+}
+/*
+ * Returns:
+ *  0 = key not found
+ * -1 = get next overflow page
+ * -2 means key not found and this is big key/data
+ * -3 error
+ */
+extern int
+__find_bigpair(HTAB *hashp, BUFHEAD *bufp, int ndx, char *key, int size)
+{
+	register uint16 *bp;
+	register char *p;
+	int ksize;
+	uint16 bytes;
+	char *kkey;
+
+	bp = (uint16 *)bufp->page;
+	p = bufp->page;
+	ksize = size;
+	kkey = key;
+
+	for (bytes = hashp->BSIZE - bp[ndx];
+	    bytes <= size && bp[ndx + 1] == PARTIAL_KEY;
+	    bytes = hashp->BSIZE - bp[ndx]) {
+		if (memcmp(p + bp[ndx], kkey, bytes))
+			return (-2);
+		kkey += bytes;
+		ksize -= bytes;
+		bufp = __get_buf(hashp, bp[ndx + 2], bufp, 0);
+		if (!bufp)
+			return (-3);
+		p = bufp->page;
+		bp = (uint16 *)p;
+		ndx = 1;
+	}
+
+	if (bytes != ksize || memcmp(p + bp[ndx], kkey, bytes)) {
+#ifdef HASH_STATISTICS
+		++hash_collisions;
+#endif
+		return (-2);
+	} else
+		return (ndx);
+}
+
+/*
+ * Given the buffer pointer of the first overflow page of a big pair,
+ * find the end of the big pair
+ *
+ * This will set bpp to the buffer header of the last page of the big pair.
+ * It will return the pageno of the overflow page following the last page
+ * of the pair; 0 if there isn't any (i.e. big pair is the last key in the
+ * bucket)
+ */
+extern uint16
+__find_last_page(HTAB *hashp, BUFHEAD **bpp)
+{
+	BUFHEAD *bufp;
+	uint16 *bp, pageno;
+	uint n;
+
+	bufp = *bpp;
+	bp = (uint16 *)bufp->page;
+	for (;;) {
+		n = bp[0];
+
+		/*
+		 * This is the last page if: the tag is FULL_KEY_DATA and
+		 * either only 2 entries OVFLPAGE marker is explicit there
+		 * is freespace on the page.
+		 */
+		if (bp[2] == FULL_KEY_DATA &&
+		    ((n == 2) || (bp[n] == OVFLPAGE) || (FREESPACE(bp))))
+			break;
+
+		/* LJM bound the size of n to reasonable limits
+		 */
+		if(n > hashp->BSIZE/sizeof(uint16))
+			return(0);
+
+		pageno = bp[n - 1];
+		bufp = __get_buf(hashp, pageno, bufp, 0);
+		if (!bufp)
+			return (0);	/* Need to indicate an error! */
+		bp = (uint16 *)bufp->page;
+	}
+
+	*bpp = bufp;
+	if (bp[0] > 2)
+		return (bp[3]);
+	else
+		return (0);
+}
+
+/*
+ * Return the data for the key/data pair that begins on this page at this
+ * index (index should always be 1).
+ */
+extern int
+__big_return(
+	HTAB *hashp,
+	BUFHEAD *bufp,
+	int ndx,
+	DBT *val,
+	int set_current)
+{
+	BUFHEAD *save_p;
+	uint16 *bp, len, off, save_addr;
+	char *tp;
+	int save_flags;
+
+	bp = (uint16 *)bufp->page;
+	while (bp[ndx + 1] == PARTIAL_KEY) {
+		bufp = __get_buf(hashp, bp[bp[0] - 1], bufp, 0);
+		if (!bufp)
+			return (-1);
+		bp = (uint16 *)bufp->page;
+		ndx = 1;
+	}
+
+	if (bp[ndx + 1] == FULL_KEY) {
+		bufp = __get_buf(hashp, bp[bp[0] - 1], bufp, 0);
+		if (!bufp)
+			return (-1);
+		bp = (uint16 *)bufp->page;
+		save_p = bufp;
+		save_addr = save_p->addr;
+		off = bp[1];
+		len = 0;
+	} else
+		if (!FREESPACE(bp)) {
+			/*
+			 * This is a hack.  We can't distinguish between
+			 * FULL_KEY_DATA that contains complete data or
+			 * incomplete data, so we require that if the data
+			 * is complete, there is at least 1 byte of free
+			 * space left.
+			 */
+			off = bp[bp[0]];
+			len = bp[1] - off;
+			save_p = bufp;
+			save_addr = bufp->addr;
+			bufp = __get_buf(hashp, bp[bp[0] - 1], bufp, 0);
+			if (!bufp)
+				return (-1);
+			bp = (uint16 *)bufp->page;
+		} else {
+			/* The data is all on one page. */
+			tp = (char *)bp;
+			off = bp[bp[0]];
+			val->data = (uint8 *)tp + off;
+			val->size = bp[1] - off;
+			if (set_current) {
+				if (bp[0] == 2) {	/* No more buckets in
+							 * chain */
+					hashp->cpage = NULL;
+					hashp->cbucket++;
+					hashp->cndx = 1;
+				} else {
+					hashp->cpage = __get_buf(hashp,
+					    bp[bp[0] - 1], bufp, 0);
+					if (!hashp->cpage)
+						return (-1);
+					hashp->cndx = 1;
+					if (!((uint16 *)
+					    hashp->cpage->page)[0]) {
+						hashp->cbucket++;
+						hashp->cpage = NULL;
+					}
+				}
+			}
+			return (0);
+		}
+
+	/* pin our saved buf so that we don't lose if 
+	 * we run out of buffers */
+ 	save_flags = save_p->flags;
+	save_p->flags |= BUF_PIN;
+	val->size = collect_data(hashp, bufp, (int)len, set_current);
+	save_p->flags = save_flags;
+	if (val->size == (size_t)-1)
+		return (-1);
+	if (save_p->addr != save_addr) {
+		/* We are pretty short on buffers. */
+		errno = EINVAL;			/* OUT OF BUFFERS */
+		return (-1);
+	}
+	memmove(hashp->tmp_buf, (save_p->page) + off, len);
+	val->data = (uint8 *)hashp->tmp_buf;
+	return (0);
+}
+
+
+/*
+ * Count how big the total datasize is by looping through the pages.  Then
+ * allocate a buffer and copy the data in the second loop. NOTE: Our caller
+ * may already have a bp which it is holding onto. The caller is
+ * responsible for copying that bp into our temp buffer. 'len' is how much
+ * space to reserve for that buffer.
+ */
+static int
+collect_data(
+	HTAB *hashp,
+	BUFHEAD *bufp,
+	int len, int set)
+{
+	register uint16 *bp;
+	BUFHEAD *save_bufp;
+	int save_flags;
+	int mylen, totlen;
+
+	/*
+	 * save the input buf head because we need to walk the list twice.
+	 * pin it to make sure it doesn't leave the buffer pool. 
+	 * This has the effect of growing the buffer pool if necessary.
+	 */
+	save_bufp = bufp;
+	save_flags = save_bufp->flags;
+	save_bufp->flags |= BUF_PIN;
+
+	/* read the length of the buffer */
+	for (totlen = len; bufp ; bufp = __get_buf(hashp, bp[bp[0]-1], bufp, 0)) {
+		bp = (uint16 *)bufp->page;
+		mylen = hashp->BSIZE - bp[1];
+
+		/* if mylen ever goes negative it means that the
+		 * page is screwed up.
+		 */
+		if (mylen < 0) {
+			save_bufp->flags = save_flags;
+			return (-1);
+ 		}
+		totlen += mylen;
+		if (bp[2] == FULL_KEY_DATA) {		/* End of Data */
+			break;
+		}
+	}
+
+ 	if (!bufp) {
+		save_bufp->flags = save_flags;
+		return (-1);
+	}
+
+	/* allocate a temp buf */
+	if (hashp->tmp_buf)
+		free(hashp->tmp_buf);
+	if ((hashp->tmp_buf = (char *)malloc((size_t)totlen)) == NULL) {
+		save_bufp->flags = save_flags;
+		return (-1);
+ 	}
+
+	/* copy the buffers back into temp buf */
+	for (bufp = save_bufp; bufp ;
+				bufp = __get_buf(hashp, bp[bp[0]-1], bufp, 0)) {
+		bp = (uint16 *)bufp->page;
+		mylen = hashp->BSIZE - bp[1];
+		memmove(&hashp->tmp_buf[len], (bufp->page) + bp[1], (size_t)mylen);
+		len += mylen;
+		if (bp[2] == FULL_KEY_DATA) {
+			break;
+		}
+	}
+
+	/* 'clear' the pin flags */
+	save_bufp->flags = save_flags;
+
+	/* update the database cursor */
+	if (set) {
+		hashp->cndx = 1;
+		if (bp[0] == 2) {	/* No more buckets in chain */
+			hashp->cpage = NULL;
+			hashp->cbucket++;
+		} else {
+			hashp->cpage = __get_buf(hashp, bp[bp[0] - 1], bufp, 0);
+			if (!hashp->cpage)
+				return (-1);
+			else if (!((uint16 *)hashp->cpage->page)[0]) {
+				hashp->cbucket++;
+				hashp->cpage = NULL;
+			}
+		}
+	}
+	return (totlen);
+}
+
+/*
+ * Fill in the key and data for this big pair.
+ */
+extern int
+__big_keydata(
+	HTAB *hashp, 
+	BUFHEAD *bufp, 
+	DBT *key, DBT *val,
+	int set)
+{
+	key->size = collect_key(hashp, bufp, 0, val, set);
+	if (key->size == (size_t)-1)
+		return (-1);
+	key->data = (uint8 *)hashp->tmp_key;
+	return (0);
+}
+
+/*
+ * Count how big the total key size is by recursing through the pages.  Then
+ * collect the data, allocate a buffer and copy the key as you recurse up.
+ */
+static int
+collect_key(
+	HTAB *hashp,
+	BUFHEAD *bufp,
+	int len,
+	DBT *val,
+	int set)
+{
+	BUFHEAD *xbp;
+	char *p;
+	int mylen, totlen;
+	uint16 *bp, save_addr;
+
+	p = bufp->page;
+	bp = (uint16 *)p;
+	mylen = hashp->BSIZE - bp[1];
+
+	save_addr = bufp->addr;
+	totlen = len + mylen;
+	if (bp[2] == FULL_KEY || bp[2] == FULL_KEY_DATA) {    /* End of Key. */
+		if (hashp->tmp_key != NULL)
+			free(hashp->tmp_key);
+		if ((hashp->tmp_key = (char *)malloc((size_t)totlen)) == NULL)
+			return (-1);
+		if (__big_return(hashp, bufp, 1, val, set))
+			return (-1);
+	} else {
+		xbp = __get_buf(hashp, bp[bp[0] - 1], bufp, 0);
+		if (!xbp || ((totlen =
+		    collect_key(hashp, xbp, totlen, val, set)) < 1))
+			return (-1);
+	}
+	if (bufp->addr != save_addr) {
+		errno = EINVAL;		/* MIS -- OUT OF BUFFERS */
+		return (-1);
+	}
+	memmove(&hashp->tmp_key[len], (bufp->page) + bp[1], (size_t)mylen);
+	return (totlen);
+}
+
+/*
+ * Returns:
+ *  0 => OK
+ * -1 => error
+ */
+extern int
+__big_split(
+	HTAB *hashp,
+	BUFHEAD *op,	/* Pointer to where to put keys that go in old bucket */
+	BUFHEAD *np,	/* Pointer to new bucket page */
+			/* Pointer to first page containing the big key/data */
+	BUFHEAD *big_keyp,
+	uint32 addr,	/* Address of big_keyp */
+	uint32   obucket,/* Old Bucket */
+	SPLIT_RETURN *ret)
+{
+	register BUFHEAD *tmpp;
+	register uint16 *tp;
+	BUFHEAD *bp;
+	DBT key, val;
+	uint32 change;
+	uint16 free_space, n, off;
+
+	bp = big_keyp;
+
+	/* Now figure out where the big key/data goes */
+	if (__big_keydata(hashp, big_keyp, &key, &val, 0))
+		return (-1);
+	change = (__call_hash(hashp,(char*) key.data, key.size) != obucket);
+
+	if ((ret->next_addr = __find_last_page(hashp, &big_keyp))) {
+		if (!(ret->nextp =
+		    __get_buf(hashp, ret->next_addr, big_keyp, 0)))
+			return (-1);;
+	} else
+		ret->nextp = NULL;
+
+	/* Now make one of np/op point to the big key/data pair */
+#ifdef DEBUG
+	assert(np->ovfl == NULL);
+#endif
+	if (change)
+		tmpp = np;
+	else
+		tmpp = op;
+
+	tmpp->flags |= BUF_MOD;
+#ifdef DEBUG1
+	(void)fprintf(stderr,
+	    "BIG_SPLIT: %d->ovfl was %d is now %d\n", tmpp->addr,
+	    (tmpp->ovfl ? tmpp->ovfl->addr : 0), (bp ? bp->addr : 0));
+#endif
+	tmpp->ovfl = bp;	/* one of op/np point to big_keyp */
+	tp = (uint16 *)tmpp->page;
+
+
+#if 0  /* this get's tripped on database corrupted error */
+	assert(FREESPACE(tp) >= OVFLSIZE);
+#endif
+	if(FREESPACE(tp) < OVFLSIZE)
+		return(DATABASE_CORRUPTED_ERROR);
+
+	n = tp[0];
+	off = OFFSET(tp);
+	free_space = FREESPACE(tp);
+	tp[++n] = (uint16)addr;
+	tp[++n] = OVFLPAGE;
+	tp[0] = n;
+	OFFSET(tp) = off;
+	FREESPACE(tp) = free_space - OVFLSIZE;
+
+	/*
+	 * Finally, set the new and old return values. BIG_KEYP contains a
+	 * pointer to the last page of the big key_data pair. Make sure that
+	 * big_keyp has no following page (2 elements) or create an empty
+	 * following page.
+	 */
+
+	ret->newp = np;
+	ret->oldp = op;
+
+	tp = (uint16 *)big_keyp->page;
+	big_keyp->flags |= BUF_MOD;
+	if (tp[0] > 2) {
+		/*
+		 * There may be either one or two offsets on this page.  If
+		 * there is one, then the overflow page is linked on normally
+		 * and tp[4] is OVFLPAGE.  If there are two, tp[4] contains
+		 * the second offset and needs to get stuffed in after the
+		 * next overflow page is added.
+		 */
+		n = tp[4];
+		free_space = FREESPACE(tp);
+		off = OFFSET(tp);
+		tp[0] -= 2;
+		FREESPACE(tp) = free_space + OVFLSIZE;
+		OFFSET(tp) = off;
+		tmpp = __add_ovflpage(hashp, big_keyp);
+		if (!tmpp)
+			return (-1);
+		tp[4] = n;
+	} else
+		tmpp = big_keyp;
+
+	if (change)
+		ret->newp = tmpp;
+	else
+		ret->oldp = tmpp;
+	return (0);
+}
diff -r a945361df361 -r 150b72113545 nss/lib/dbm/src/h_func.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nss/lib/dbm/src/h_func.c	Tue Aug 05 18:32:02 2014 +0200
@@ -0,0 +1,207 @@
+/*-
+ * Copyright (c) 1990, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Margo Seltzer.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. ***REMOVED*** - see 
+ *    ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)hash_func.c	8.2 (Berkeley) 2/21/94";
+#endif /* LIBC_SCCS and not lint */
+
+#ifndef macintosh
+#include <sys/types.h>
+#endif
+#include "mcom_db.h"
+#include "hash.h"
+#include "page.h"
+/* #include "extern.h" */
+
+#if 0
+static uint32 hash1 __P((const void *, size_t));
+static uint32 hash2 __P((const void *, size_t));
+static uint32 hash3 __P((const void *, size_t));
+#endif
+static uint32 hash4 __P((const void *, size_t));
+
+/* Global default hash function */
+uint32 (*__default_hash) __P((const void *, size_t)) = hash4;
+
+/*
+ * HASH FUNCTIONS
+ *
+ * Assume that we've already split the bucket to which this key hashes,
+ * calculate that bucket, and check that in fact we did already split it.
+ *
+ * This came from ejb's hsearch.
+ */
+
+#define PRIME1		37
+#define PRIME2		1048583
+
+#if 0
+static uint32
+hash1(const void *keyarg, register size_t len)
+{
+	register const uint8 *key;
+	register uint32 h;
+
+	/* Convert string to integer */
+	for (key = (const uint8 *)keyarg, h = 0; len--;)
+		h = h * PRIME1 ^ (*key++ - ' ');
+	h %= PRIME2;
+	return (h);
+}
+
+/*
+ * Phong's linear congruential hash
+ */
+#define dcharhash(h, c)	((h) = 0x63c63cd9*(h) + 0x9c39c33d + (c))
+
+static uint32
+hash2(const void *keyarg, size_t len)
+{
+	register const uint8 *e, *key;
+	register uint32 h;
+	register uint8 c;
+
+	key = (const uint8 *)keyarg;
+	e = key + len;
+	for (h = 0; key != e;) {
+		c = *key++;
+		if (!c && key > e)
+			break;
+		dcharhash(h, c);
+	}
+	return (h);
+}
+
+/*
+ * This is INCREDIBLY ugly, but fast.  We break the string up into 8 byte
+ * units.  On the first time through the loop we get the "leftover bytes"
+ * (strlen % 8).  On every other iteration, we perform 8 HASHC's so we handle
+ * all 8 bytes.  Essentially, this saves us 7 cmp & branch instructions.  If
+ * this routine is heavily used enough, it's worth the ugly coding.
+ *
+ * OZ's original sdbm hash
+ */
+static uint32
+hash3(const void *keyarg, register size_t len)
+{
+	register const uint8 *key;
+	register size_t loop;
+	register uint32 h;
+
+#define HASHC   h = *key++ + 65599 * h
+
+	h = 0;
+	key = (const uint8 *)keyarg;
+	if (len > 0) {
+		loop = (len + 8 - 1) >> 3;
+
+		switch (len & (8 - 1)) {
+		case 0:
+			do {
+				HASHC;
+				/* FALLTHROUGH */
+		case 7:
+				HASHC;
+				/* FALLTHROUGH */
+		case 6:
+				HASHC;
+				/* FALLTHROUGH */
+		case 5:
+				HASHC;
+				/* FALLTHROUGH */
+		case 4:
+				HASHC;
+				/* FALLTHROUGH */
+		case 3:
+				HASHC;
+				/* FALLTHROUGH */
+		case 2:
+				HASHC;
+				/* FALLTHROUGH */
+		case 1:
+				HASHC;
+			} while (--loop);
+		}
+	}
+	return (h);
+}
+#endif /* 0 */
+
+/* Hash function from Chris Torek. */
+static uint32
+hash4(const void *keyarg, register size_t len)
+{
+	register const uint8 *key;
+	register size_t loop;
+	register uint32 h;
+
+#define HASH4a   h = (h << 5) - h + *key++;
+#define HASH4b   h = (h << 5) + h + *key++;
+#define HASH4 HASH4b
+
+	h = 0;
+	key = (const uint8 *)keyarg;
+	if (len > 0) {
+		loop = (len + 8 - 1) >> 3;
+
+		switch (len & (8 - 1)) {
+		case 0:
+			do {
+				HASH4;
+				/* FALLTHROUGH */
+		case 7:
+				HASH4;
+				/* FALLTHROUGH */
+		case 6:
+				HASH4;
+				/* FALLTHROUGH */
+		case 5:
+				HASH4;
+				/* FALLTHROUGH */
+		case 4:
+				HASH4;
+				/* FALLTHROUGH */
+		case 3:
+				HASH4;
+				/* FALLTHROUGH */
+		case 2:
+				HASH4;
+				/* FALLTHROUGH */
+		case 1:
+				HASH4;
+			} while (--loop);
+		}
+	}
+	return (h);
+}
diff -r a945361df361 -r 150b72113545 nss/lib/dbm/src/h_log2.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nss/lib/dbm/src/h_log2.c	Tue Aug 05 18:32:02 2014 +0200
@@ -0,0 +1,52 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Margo Seltzer.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. ***REMOVED*** - see 
+ *    ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)hash_log2.c	8.2 (Berkeley) 5/31/94";
+#endif /* LIBC_SCCS and not lint */
+
+#include <stdio.h>
+#ifndef macintosh
+#include <sys/types.h>
+#endif
+#include "mcom_db.h"
+
+uint32 __log2(uint32 num)
+{
+	register uint32 i, limit;
+
+	limit = 1;
+	for (i = 0; limit < num; limit = limit << 1, i++) {}
+	return (i);
+}
diff -r a945361df361 -r 150b72113545 nss/lib/dbm/src/h_page.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nss/lib/dbm/src/h_page.c	Tue Aug 05 18:32:02 2014 +0200
@@ -0,0 +1,1286 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Margo Seltzer.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. ***REMOVED*** - see 
+ *    ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(unix)
+#define MY_LSEEK lseek
+#else
+#define MY_LSEEK new_lseek
+extern long new_lseek(int fd, long pos, int start);
+#endif
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)hash_page.c	8.7 (Berkeley) 8/16/94";
+#endif /* LIBC_SCCS and not lint */
+
+/*
+ * PACKAGE:  hashing
+ *
+ * DESCRIPTION:
+ *	Page manipulation for hashing package.
+ *
+ * ROUTINES:
+ *
+ * External
+ *	__get_page
+ *	__add_ovflpage
+ * Internal
+ *	overflow_page
+ *	open_temp
+ */
+#ifndef macintosh
+#include <sys/types.h>
+#endif
+
+#if defined(macintosh)
+#include <unistd.h>
+#endif
+
+#include <errno.h>
+#include <fcntl.h>
+#if defined(_WIN32) || defined(_WINDOWS) 
+#include <io.h>
+#endif
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if !defined(_WIN32) && !defined(_WINDOWS) && !defined(macintosh)
+#include <unistd.h>
+#endif
+
+#include <assert.h>
+
+#include "mcom_db.h"
+#include "hash.h"
+#include "page.h"
+/* #include "extern.h" */
+
+extern int mkstempflags(char *path, int extraFlags);
+
+static uint32	*fetch_bitmap __P((HTAB *, uint32));
+static uint32	 first_free __P((uint32));
+static int	 open_temp __P((HTAB *));
+static uint16	 overflow_page __P((HTAB *));
+static void	 squeeze_key __P((uint16 *, const DBT *, const DBT *));
+static int	 ugly_split
+		    __P((HTAB *, uint32, BUFHEAD *, BUFHEAD *, int, int));
+
+#define	PAGE_INIT(P) { \
+	((uint16 *)(P))[0] = 0; \
+	((uint16 *)(P))[1] = hashp->BSIZE - 3 * sizeof(uint16); \
+	((uint16 *)(P))[2] = hashp->BSIZE; \
+}
+
+/* implement a new lseek using lseek that
+ * writes zero's when extending a file
+ * beyond the end.
+ */
+long new_lseek(int fd, long offset, int origin)
+{
+ 	long cur_pos=0;
+	long end_pos=0;
+	long seek_pos=0;
+
+	if(origin == SEEK_CUR)
+      {	
+      	if(offset < 1)							  
+	    	return(lseek(fd, offset, SEEK_CUR));
+
+		cur_pos = lseek(fd, 0, SEEK_CUR);
+
+		if(cur_pos < 0)
+			return(cur_pos);
+	  }
+										 
+	end_pos = lseek(fd, 0, SEEK_END);
+	if(end_pos < 0)
+		return(end_pos);
+
+	if(origin == SEEK_SET)
+		seek_pos = offset;
+	else if(origin == SEEK_CUR)
+		seek_pos = cur_pos + offset;
+	else if(origin == SEEK_END)
+		seek_pos = end_pos + offset;
+ 	else
+	  {
+	  	assert(0);
+		return(-1);
+	  }
+
+ 	/* the seek position desired is before the
+	 * end of the file.  We don't need
+	 * to do anything special except the seek.
+	 */
+ 	if(seek_pos <= end_pos)
+ 		return(lseek(fd, seek_pos, SEEK_SET));
+ 		
+ 	  /* the seek position is beyond the end of the
+ 	   * file.  Write zero's to the end.
+ 	   *
+	   * we are already at the end of the file so
+	   * we just need to "write()" zeros for the
+	   * difference between seek_pos-end_pos and
+	   * then seek to the position to finish
+	   * the call
+ 	   */
+ 	  { 
+ 	 	char buffer[1024];
+	   	long len = seek_pos-end_pos;
+	   	memset(&buffer, 0, 1024);
+	   	while(len > 0)
+	      {
+	        write(fd, (char*)&buffer, (size_t)(1024 > len ? len : 1024));
+		    len -= 1024;
+		  }
+		return(lseek(fd, seek_pos, SEEK_SET));
+	  }		
+
+}
+
+/*
+ * This is called AFTER we have verified that there is room on the page for
+ * the pair (PAIRFITS has returned true) so we go right ahead and start moving
+ * stuff on.
+ */
+static void
+putpair(char *p, const DBT *key, DBT * val)
+{
+	register uint16 *bp, n, off;
+
+	bp = (uint16 *)p;
+
+	/* Enter the key first. */
+	n = bp[0];
+
+	off = OFFSET(bp) - key->size;
+	memmove(p + off, key->data, key->size);
+	bp[++n] = off;
+
+	/* Now the data. */
+	off -= val->size;
+	memmove(p + off, val->data, val->size);
+	bp[++n] = off;
+
+	/* Adjust page info. */
+	bp[0] = n;
+	bp[n + 1] = off - ((n + 3) * sizeof(uint16));
+	bp[n + 2] = off;
+}
+
+/*
+ * Returns:
+ *	 0 OK
+ *	-1 error
+ */
+extern int
+__delpair(HTAB *hashp, BUFHEAD *bufp, int ndx)
+{
+	register uint16 *bp, newoff;
+	register int n;
+	uint16 pairlen;
+
+	bp = (uint16 *)bufp->page;
+	n = bp[0];
+
+	if (bp[ndx + 1] < REAL_KEY)
+		return (__big_delete(hashp, bufp));
+	if (ndx != 1)
+		newoff = bp[ndx - 1];
+	else
+		newoff = hashp->BSIZE;
+	pairlen = newoff - bp[ndx + 1];
+
+	if (ndx != (n - 1)) {
+		/* Hard Case -- need to shuffle keys */
+		register int i;
+		register char *src = bufp->page + (int)OFFSET(bp);
+		uint32 dst_offset = (uint32)OFFSET(bp) + (uint32)pairlen;
+		register char *dst = bufp->page + dst_offset;
+		uint32 length = bp[ndx + 1] - OFFSET(bp);
+
+		/*
+		 * +-----------+XXX+---------+XXX+---------+---------> +infinity
+		 * |           |             |             |
+		 * 0           src_offset    dst_offset    BSIZE
+		 *
+		 * Dst_offset is > src_offset, so if src_offset were bad, dst_offset
+		 * would be too, therefore we check only dst_offset.
+		 *
+		 * If dst_offset is >= BSIZE, either OFFSET(bp), or pairlen, or both
+		 * is corrupted.
+		 *
+		 * Once we know dst_offset is < BSIZE, we can subtract it from BSIZE
+		 * to get an upper bound on length.
+		 */
+		if(dst_offset > (uint32)hashp->BSIZE)
+			return(DATABASE_CORRUPTED_ERROR);
+
+		if(length > (uint32)(hashp->BSIZE - dst_offset))
+			return(DATABASE_CORRUPTED_ERROR);
+
+		memmove(dst, src, length);
+
+		/* Now adjust the pointers */
+		for (i = ndx + 2; i <= n; i += 2) {
+			if (bp[i + 1] == OVFLPAGE) {
+				bp[i - 2] = bp[i];
+				bp[i - 1] = bp[i + 1];
+			} else {
+				bp[i - 2] = bp[i] + pairlen;
+				bp[i - 1] = bp[i + 1] + pairlen;
+			}
+		}
+	}
+	/* Finally adjust the page data */
+	bp[n] = OFFSET(bp) + pairlen;
+	bp[n - 1] = bp[n + 1] + pairlen + 2 * sizeof(uint16);
+	bp[0] = n - 2;
+	hashp->NKEYS--;
+
+	bufp->flags |= BUF_MOD;
+	return (0);
+}
+/*
+ * Returns:
+ *	 0 ==> OK
+ *	-1 ==> Error
+ */
+extern int
+__split_page(HTAB *hashp, uint32 obucket, uint32 nbucket)
+{
+	register BUFHEAD *new_bufp, *old_bufp;
+	register uint16 *ino;
+	register uint16 *tmp_uint16_array;
+	register char *np;
+	DBT key, val;
+    uint16 n, ndx;
+	int retval;
+	uint16 copyto, diff, moved;
+	size_t off;
+	char *op;
+
+	copyto = (uint16)hashp->BSIZE;
+	off = (uint16)hashp->BSIZE;
+	old_bufp = __get_buf(hashp, obucket, NULL, 0);
+	if (old_bufp == NULL)
+		return (-1);
+	new_bufp = __get_buf(hashp, nbucket, NULL, 0);
+	if (new_bufp == NULL)
+		return (-1);
+
+	old_bufp->flags |= (BUF_MOD | BUF_PIN);
+	new_bufp->flags |= (BUF_MOD | BUF_PIN);
+
+	ino = (uint16 *)(op = old_bufp->page);
+	np = new_bufp->page;
+
+	moved = 0;
+
+	for (n = 1, ndx = 1; n < ino[0]; n += 2) {
+		if (ino[n + 1] < REAL_KEY) {
+			retval = ugly_split(hashp, obucket, old_bufp, new_bufp,
+			    (int)copyto, (int)moved);
+			old_bufp->flags &= ~BUF_PIN;
+			new_bufp->flags &= ~BUF_PIN;
+			return (retval);
+
+		}
+		key.data = (uint8 *)op + ino[n];
+
+		/* check here for ino[n] being greater than
+		 * off.  If it is then the database has
+		 * been corrupted.
+		 */
+		if(ino[n] > off)
+			return(DATABASE_CORRUPTED_ERROR);
+
+		key.size = off - ino[n];
+
+#ifdef DEBUG
+		/* make sure the size is positive */
+		assert(((int)key.size) > -1);
+#endif
+
+		if (__call_hash(hashp, (char *)key.data, key.size) == obucket) {
+			/* Don't switch page */
+			diff = copyto - off;
+			if (diff) {
+				copyto = ino[n + 1] + diff;
+				memmove(op + copyto, op + ino[n + 1],
+				    off - ino[n + 1]);
+				ino[ndx] = copyto + ino[n] - ino[n + 1];
+				ino[ndx + 1] = copyto;
+			} else
+				copyto = ino[n + 1];
+			ndx += 2;
+		} else {
+			/* Switch page */
+			val.data = (uint8 *)op + ino[n + 1];
+			val.size = ino[n] - ino[n + 1];
+
+			/* if the pair doesn't fit something is horribly
+			 * wrong.  LJM
+			 */
+			tmp_uint16_array = (uint16*)np;
+			if(!PAIRFITS(tmp_uint16_array, &key, &val))
+				return(DATABASE_CORRUPTED_ERROR);
+
+			putpair(np, &key, &val);
+			moved += 2;
+		}
+
+		off = ino[n + 1];
+	}
+
+	/* Now clean up the page */
+	ino[0] -= moved;
+	FREESPACE(ino) = copyto - sizeof(uint16) * (ino[0] + 3);
+	OFFSET(ino) = copyto;
+
+#ifdef DEBUG3
+	(void)fprintf(stderr, "split %d/%d\n",
+	    ((uint16 *)np)[0] / 2,
+	    ((uint16 *)op)[0] / 2);
+#endif
+	/* unpin both pages */
+	old_bufp->flags &= ~BUF_PIN;
+	new_bufp->flags &= ~BUF_PIN;
+	return (0);
+}
+
+/*
+ * Called when we encounter an overflow or big key/data page during split
+ * handling.  This is special cased since we have to begin checking whether
+ * the key/data pairs fit on their respective pages and because we may need
+ * overflow pages for both the old and new pages.
+ *
+ * The first page might be a page with regular key/data pairs in which case
+ * we have a regular overflow condition and just need to go on to the next
+ * page or it might be a big key/data pair in which case we need to fix the
+ * big key/data pair.
+ *
+ * Returns:
+ *	 0 ==> success
+ *	-1 ==> failure
+ */
+
+/* the maximum number of loops we will allow UGLY split to chew
+ * on before we assume the database is corrupted and throw it
+ * away.
+ */
+#define MAX_UGLY_SPLIT_LOOPS 10000
+
+static int
+ugly_split(HTAB *hashp, uint32 obucket, BUFHEAD *old_bufp,
+ BUFHEAD *new_bufp,/* Same as __split_page. */ int copyto, int moved)
+	/* int copyto;	 First byte on page which contains key/data values. */
+	/* int moved;	 Number of pairs moved to new page. */
+{
+	register BUFHEAD *bufp;	/* Buffer header for ino */
+	register uint16 *ino;	/* Page keys come off of */
+	register uint16 *np;	/* New page */
+	register uint16 *op;	/* Page keys go on to if they aren't moving */
+    uint32 loop_detection=0;
+
+	BUFHEAD *last_bfp;	/* Last buf header OVFL needing to be freed */
+	DBT key, val;
+	SPLIT_RETURN ret;
+	uint16 n, off, ov_addr, scopyto;
+	char *cino;		/* Character value of ino */
+	int status;
+
+	bufp = old_bufp;
+	ino = (uint16 *)old_bufp->page;
+	np = (uint16 *)new_bufp->page;
+	op = (uint16 *)old_bufp->page;
+	last_bfp = NULL;
+	scopyto = (uint16)copyto;	/* ANSI */
+
+	n = ino[0] - 1;
+	while (n < ino[0]) {
+
+
+        /* this function goes nuts sometimes and never returns. 
+         * I havent found the problem yet but I need a solution
+         * so if we loop too often we assume a database curruption error
+         * :LJM
+         */
+        loop_detection++;
+
+        if(loop_detection > MAX_UGLY_SPLIT_LOOPS)
+            return DATABASE_CORRUPTED_ERROR;
+
+		if (ino[2] < REAL_KEY && ino[2] != OVFLPAGE) {
+			if ((status = __big_split(hashp, old_bufp,
+			    new_bufp, bufp, bufp->addr, obucket, &ret)))
+				return (status);
+			old_bufp = ret.oldp;
+			if (!old_bufp)
+				return (-1);
+			op = (uint16 *)old_bufp->page;
+			new_bufp = ret.newp;
+			if (!new_bufp)
+				return (-1);
+			np = (uint16 *)new_bufp->page;
+			bufp = ret.nextp;
+			if (!bufp)
+				return (0);
+			cino = (char *)bufp->page;
+			ino = (uint16 *)cino;
+			last_bfp = ret.nextp;
+		} else if (ino[n + 1] == OVFLPAGE) {
+			ov_addr = ino[n];
+			/*
+			 * Fix up the old page -- the extra 2 are the fields
+			 * which contained the overflow information.
+			 */
+			ino[0] -= (moved + 2);
+			FREESPACE(ino) =
+			    scopyto - sizeof(uint16) * (ino[0] + 3);
+			OFFSET(ino) = scopyto;
+
+			bufp = __get_buf(hashp, ov_addr, bufp, 0);
+			if (!bufp)
+				return (-1);
+
+			ino = (uint16 *)bufp->page;
+			n = 1;
+			scopyto = hashp->BSIZE;
+			moved = 0;
+
+			if (last_bfp)
+				__free_ovflpage(hashp, last_bfp);
+			last_bfp = bufp;
+		}
+		/* Move regular sized pairs of there are any */
+		off = hashp->BSIZE;
+		for (n = 1; (n < ino[0]) && (ino[n + 1] >= REAL_KEY); n += 2) {
+			cino = (char *)ino;
+			key.data = (uint8 *)cino + ino[n];
+			key.size = off - ino[n];
+			val.data = (uint8 *)cino + ino[n + 1];
+			val.size = ino[n] - ino[n + 1];
+			off = ino[n + 1];
+
+			if (__call_hash(hashp, (char*)key.data, key.size) == obucket) {
+				/* Keep on old page */
+				if (PAIRFITS(op, (&key), (&val)))
+					putpair((char *)op, &key, &val);
+				else {
+					old_bufp =
+					    __add_ovflpage(hashp, old_bufp);
+					if (!old_bufp)
+						return (-1);
+					op = (uint16 *)old_bufp->page;
+					putpair((char *)op, &key, &val);
+				}
+				old_bufp->flags |= BUF_MOD;
+			} else {
+				/* Move to new page */
+				if (PAIRFITS(np, (&key), (&val)))
+					putpair((char *)np, &key, &val);
+				else {
+					new_bufp =
+					    __add_ovflpage(hashp, new_bufp);
+					if (!new_bufp)
+						return (-1);
+					np = (uint16 *)new_bufp->page;
+					putpair((char *)np, &key, &val);
+				}
+				new_bufp->flags |= BUF_MOD;
+			}
+		}
+	}
+	if (last_bfp)
+		__free_ovflpage(hashp, last_bfp);
+	return (0);
+}
+
+/*
+ * Add the given pair to the page
+ *
+ * Returns:
+ *	0 ==> OK
+ *	1 ==> failure
+ */
+extern int
+__addel(HTAB *hashp, BUFHEAD *bufp, const DBT *key, const DBT * val)
+{
+	register uint16 *bp, *sop;
+	int do_expand;
+
+	bp = (uint16 *)bufp->page;
+	do_expand = 0;
+	while (bp[0] && (bp[2] < REAL_KEY || bp[bp[0]] < REAL_KEY))
+		/* Exception case */
+		if (bp[2] == FULL_KEY_DATA && bp[0] == 2)
+			/* This is the last page of a big key/data pair
+			   and we need to add another page */
+			break;
+		else if (bp[2] < REAL_KEY && bp[bp[0]] != OVFLPAGE) {
+			bufp = __get_buf(hashp, bp[bp[0] - 1], bufp, 0);
+			if (!bufp)
+			  {
+#ifdef DEBUG
+				assert(0);
+#endif
+				return (-1);
+			  }
+			bp = (uint16 *)bufp->page;
+		} else
+			/* Try to squeeze key on this page */
+			if (FREESPACE(bp) > PAIRSIZE(key, val)) {
+			  {
+				squeeze_key(bp, key, val);
+
+				/* LJM: I added this because I think it was
+				 * left out on accident.
+				 * if this isn't incremented nkeys will not
+				 * be the actual number of keys in the db.
+				 */
+				hashp->NKEYS++;
+				return (0);
+			  }
+			} else {
+				bufp = __get_buf(hashp, bp[bp[0] - 1], bufp, 0);
+				if (!bufp)
+			      {
+#ifdef DEBUG
+				    assert(0);
+#endif
+					return (-1);
+				  }
+				bp = (uint16 *)bufp->page;
+			}
+
+	if (PAIRFITS(bp, key, val))
+		putpair(bufp->page, key, (DBT *)val);
+	else {
+		do_expand = 1;
+		bufp = __add_ovflpage(hashp, bufp);
+		if (!bufp)
+	      {
+#ifdef DEBUG
+		    assert(0);
+#endif
+			return (-1);
+		  }
+		sop = (uint16 *)bufp->page;
+
+		if (PAIRFITS(sop, key, val))
+			putpair((char *)sop, key, (DBT *)val);
+		else
+			if (__big_insert(hashp, bufp, key, val))
+	          {
+#ifdef DEBUG
+		        assert(0);
+#endif
+			    return (-1);
+		      }
+	}
+	bufp->flags |= BUF_MOD;
+	/*
+	 * If the average number of keys per bucket exceeds the fill factor,
+	 * expand the table.
+	 */
+	hashp->NKEYS++;
+	if (do_expand ||
+	    (hashp->NKEYS / (hashp->MAX_BUCKET + 1) > hashp->FFACTOR))
+		return (__expand_table(hashp));
+	return (0);
+}
+
+/*
+ *
+ * Returns:
+ *	pointer on success
+ *	NULL on error
+ */
+extern BUFHEAD *
+__add_ovflpage(HTAB *hashp, BUFHEAD *bufp)
+{
+	register uint16 *sp;
+	uint16 ndx, ovfl_num;
+#ifdef DEBUG1
+	int tmp1, tmp2;
+#endif
+	sp = (uint16 *)bufp->page;
+
+	/* Check if we are dynamically determining the fill factor */
+	if (hashp->FFACTOR == DEF_FFACTOR) {
+		hashp->FFACTOR = sp[0] >> 1;
+		if (hashp->FFACTOR < MIN_FFACTOR)
+			hashp->FFACTOR = MIN_FFACTOR;
+	}
+	bufp->flags |= BUF_MOD;
+	ovfl_num = overflow_page(hashp);
+#ifdef DEBUG1
+	tmp1 = bufp->addr;
+	tmp2 = bufp->ovfl ? bufp->ovfl->addr : 0;
+#endif
+	if (!ovfl_num || !(bufp->ovfl = __get_buf(hashp, ovfl_num, bufp, 1)))
+		return (NULL);
+	bufp->ovfl->flags |= BUF_MOD;
+#ifdef DEBUG1
+	(void)fprintf(stderr, "ADDOVFLPAGE: %d->ovfl was %d is now %d\n",
+	    tmp1, tmp2, bufp->ovfl->addr);
+#endif
+	ndx = sp[0];
+	/*
+	 * Since a pair is allocated on a page only if there's room to add
+	 * an overflow page, we know that the OVFL information will fit on
+	 * the page.
+	 */
+	sp[ndx + 4] = OFFSET(sp);
+	sp[ndx + 3] = FREESPACE(sp) - OVFLSIZE;
+	sp[ndx + 1] = ovfl_num;
+	sp[ndx + 2] = OVFLPAGE;
+	sp[0] = ndx + 2;
+#ifdef HASH_STATISTICS
+	hash_overflows++;
+#endif
+	return (bufp->ovfl);
+}
+
+/*
+ * Returns:
+ *	 0 indicates SUCCESS
+ *	-1 indicates FAILURE
+ */
+extern int
+__get_page(HTAB *hashp,
+	char * p,
+	uint32 bucket, 
+	int is_bucket, 
+	int is_disk, 
+	int is_bitmap)
+{
+	register int fd, page;
+	size_t size;
+	int rsize;
+	uint16 *bp;
+
+	fd = hashp->fp;
+	size = hashp->BSIZE;
+
+	if ((fd == -1) || !is_disk) {
+		PAGE_INIT(p);
+		return (0);
+	}
+	if (is_bucket)
+		page = BUCKET_TO_PAGE(bucket);
+	else
+		page = OADDR_TO_PAGE(bucket);
+	if ((MY_LSEEK(fd, (off_t)page << hashp->BSHIFT, SEEK_SET) == -1) ||
+	    ((rsize = read(fd, p, size)) == -1))
+		return (-1);
+
+	bp = (uint16 *)p;
+	if (!rsize)
+		bp[0] = 0;	/* We hit the EOF, so initialize a new page */
+	else
+		if ((unsigned)rsize != size) {
+			errno = EFTYPE;
+			return (-1);
+		}
+
+	if (!is_bitmap && !bp[0]) {
+		PAGE_INIT(p);
+	} else {
+
+#ifdef DEBUG
+		if(BYTE_ORDER == LITTLE_ENDIAN)
+		  {
+			int is_little_endian;
+			is_little_endian = BYTE_ORDER;
+		  }
+		else if(BYTE_ORDER == BIG_ENDIAN)
+		  {
+			int is_big_endian;
+			is_big_endian = BYTE_ORDER;
+		  }
+		else
+		  {
+			assert(0);
+		  }
+#endif
+
+		if (hashp->LORDER != BYTE_ORDER) {
+			register int i, max;
+
+			if (is_bitmap) {
+				max = hashp->BSIZE >> 2; /* divide by 4 */
+				for (i = 0; i < max; i++)
+					M_32_SWAP(((int *)p)[i]);
+			} else {
+				M_16_SWAP(bp[0]);
+				max = bp[0] + 2;
+
+	    		/* bound the size of max by
+	     		 * the maximum number of entries
+	     		 * in the array
+	     		 */
+				if((unsigned)max > (size / sizeof(uint16)))
+					return(DATABASE_CORRUPTED_ERROR);
+
+				/* do the byte order swap
+				 */
+				for (i = 1; i <= max; i++)
+					M_16_SWAP(bp[i]);
+			}
+		}
+
+		/* check the validity of the page here
+		 * (after doing byte order swaping if necessary)
+		 */
+		if(!is_bitmap && bp[0] != 0)
+		  {
+			uint16 num_keys = bp[0];
+			uint16 offset;
+			uint16 i;
+
+			/* bp[0] is supposed to be the number of
+			 * entries currently in the page.  If
+			 * bp[0] is too large (larger than the whole
+			 * page) then the page is corrupted
+			 */
+			if(bp[0] > (size / sizeof(uint16)))
+				return(DATABASE_CORRUPTED_ERROR);
+			
+			/* bound free space */
+			if(FREESPACE(bp) > size)
+				return(DATABASE_CORRUPTED_ERROR);
+		
+			/* check each key and data offset to make
+ 			 * sure they are all within bounds they
+ 			 * should all be less than the previous
+ 			 * offset as well.
+ 			 */
+			offset = size;
+			for(i=1 ; i <= num_keys; i+=2)
+  			  {
+				/* ignore overflow pages etc. */
+				if(bp[i+1] >= REAL_KEY)
+	  			  {
+						
+					if(bp[i] > offset || bp[i+1] > bp[i])			
+						return(DATABASE_CORRUPTED_ERROR);
+			
+					offset = bp[i+1];
+	  			  }
+				else
+	  			  {
+					/* there are no other valid keys after
+		 			 * seeing a non REAL_KEY
+		 			 */
+					break;
+	  			  }
+  			  }
+		}
+	}
+	return (0);
+}
+
+/*
+ * Write page p to disk
+ *
+ * Returns:
+ *	 0 ==> OK
+ *	-1 ==>failure
+ */
+extern int
+__put_page(HTAB *hashp, char *p, uint32 bucket, int is_bucket, int is_bitmap)
+{
+	register int fd, page;
+	size_t size;
+	int wsize;
+	off_t offset;
+
+	size = hashp->BSIZE;
+	if ((hashp->fp == -1) && open_temp(hashp))
+		return (-1);
+	fd = hashp->fp;
+
+	if (hashp->LORDER != BYTE_ORDER) {
+		register int i;
+		register int max;
+
+		if (is_bitmap) {
+			max = hashp->BSIZE >> 2;	/* divide by 4 */
+			for (i = 0; i < max; i++)
+				M_32_SWAP(((int *)p)[i]);
+		} else {
+			max = ((uint16 *)p)[0] + 2;
+
+            /* bound the size of max by
+             * the maximum number of entries
+             * in the array
+             */
+            if((unsigned)max > (size / sizeof(uint16)))
+                return(DATABASE_CORRUPTED_ERROR);
+
+			for (i = 0; i <= max; i++)
+				M_16_SWAP(((uint16 *)p)[i]);
+
+		}
+	}
+
+	if (is_bucket)
+		page = BUCKET_TO_PAGE(bucket);
+	else
+		page = OADDR_TO_PAGE(bucket);
+	offset = (off_t)page << hashp->BSHIFT;
+	if ((MY_LSEEK(fd, offset, SEEK_SET) == -1) ||
+	    ((wsize = write(fd, p, size)) == -1))
+		/* Errno is set */
+		return (-1);
+	if ((unsigned)wsize != size) {
+		errno = EFTYPE;
+		return (-1);
+	}
+#if defined(_WIN32) || defined(_WINDOWS) 
+	if (offset + size > hashp->file_size) {
+		hashp->updateEOF = 1;
+	}
+#endif
+	/* put the page back the way it was so that it isn't byteswapped
+	 * if it remains in memory - LJM
+	 */
+	if (hashp->LORDER != BYTE_ORDER) {
+		register int i;
+		register int max;
+
+		if (is_bitmap) {
+			max = hashp->BSIZE >> 2;	/* divide by 4 */
+			for (i = 0; i < max; i++)
+				M_32_SWAP(((int *)p)[i]);
+		} else {
+    		uint16 *bp = (uint16 *)p;
+
+			M_16_SWAP(bp[0]);
+			max = bp[0] + 2;
+
+			/* no need to bound the size if max again
+			 * since it was done already above
+			 */
+
+			/* do the byte order re-swap
+			 */
+			for (i = 1; i <= max; i++)
+				M_16_SWAP(bp[i]);
+		}
+	}
+
+	return (0);
+}
+
+#define BYTE_MASK	((1 << INT_BYTE_SHIFT) -1)
+/*
+ * Initialize a new bitmap page.  Bitmap pages are left in memory
+ * once they are read in.
+ */
+extern int
+__ibitmap(HTAB *hashp, int pnum, int nbits, int ndx)
+{
+	uint32 *ip;
+	size_t clearbytes, clearints;
+
+	if ((ip = (uint32 *)malloc((size_t)hashp->BSIZE)) == NULL)
+		return (1);
+	hashp->nmaps++;
+	clearints = ((nbits - 1) >> INT_BYTE_SHIFT) + 1;
+	clearbytes = clearints << INT_TO_BYTE;
+	(void)memset((char *)ip, 0, clearbytes);
+	(void)memset(((char *)ip) + clearbytes, 0xFF,
+	    hashp->BSIZE - clearbytes);
+	ip[clearints - 1] = ALL_SET << (nbits & BYTE_MASK);
+	SETBIT(ip, 0);
+	hashp->BITMAPS[ndx] = (uint16)pnum;
+	hashp->mapp[ndx] = ip;
+	return (0);
+}
+
+static uint32
+first_free(uint32 map)
+{
+	register uint32 i, mask;
+
+	mask = 0x1;
+	for (i = 0; i < BITS_PER_MAP; i++) {
+		if (!(mask & map))
+			return (i);
+		mask = mask << 1;
+	}
+	return (i);
+}
+
+static uint16
+overflow_page(HTAB *hashp)
+{
+	register uint32 *freep=NULL;
+	register int max_free, offset, splitnum;
+	uint16 addr;
+	uint32 i;
+	int bit, first_page, free_bit, free_page, in_use_bits, j;
+#ifdef DEBUG2
+	int tmp1, tmp2;
+#endif
+	splitnum = hashp->OVFL_POINT;
+	max_free = hashp->SPARES[splitnum];
+
+	free_page = (max_free - 1) >> (hashp->BSHIFT + BYTE_SHIFT);
+	free_bit = (max_free - 1) & ((hashp->BSIZE << BYTE_SHIFT) - 1);
+
+	/* Look through all the free maps to find the first free block */
+	first_page = hashp->LAST_FREED >>(hashp->BSHIFT + BYTE_SHIFT);
+	for ( i = first_page; i <= (unsigned)free_page; i++ ) {
+		if (!(freep = (uint32 *)hashp->mapp[i]) &&
+		    !(freep = fetch_bitmap(hashp, i)))
+			return (0);
+		if (i == (unsigned)free_page)
+			in_use_bits = free_bit;
+		else
+			in_use_bits = (hashp->BSIZE << BYTE_SHIFT) - 1;
+		
+		if (i == (unsigned)first_page) {
+			bit = hashp->LAST_FREED &
+			    ((hashp->BSIZE << BYTE_SHIFT) - 1);
+			j = bit / BITS_PER_MAP;
+			bit = bit & ~(BITS_PER_MAP - 1);
+		} else {
+			bit = 0;
+			j = 0;
+		}
+		for (; bit <= in_use_bits; j++, bit += BITS_PER_MAP)
+			if (freep[j] != ALL_SET)
+				goto found;
+	}
+
+	/* No Free Page Found */
+	hashp->LAST_FREED = hashp->SPARES[splitnum];
+	hashp->SPARES[splitnum]++;
+	offset = hashp->SPARES[splitnum] -
+	    (splitnum ? hashp->SPARES[splitnum - 1] : 0);
+
+#define	OVMSG	"HASH: Out of overflow pages.  Increase page size\n"
+	if (offset > SPLITMASK) {
+		if (++splitnum >= NCACHED) {
+#ifndef macintosh
+			(void)write(STDERR_FILENO, OVMSG, sizeof(OVMSG) - 1);
+#endif
+			return (0);
+		}
+		hashp->OVFL_POINT = splitnum;
+		hashp->SPARES[splitnum] = hashp->SPARES[splitnum-1];
+		hashp->SPARES[splitnum-1]--;
+		offset = 1;
+	}
+
+	/* Check if we need to allocate a new bitmap page */
+	if (free_bit == (hashp->BSIZE << BYTE_SHIFT) - 1) {
+		free_page++;
+		if (free_page >= NCACHED) {
+#ifndef macintosh
+			(void)write(STDERR_FILENO, OVMSG, sizeof(OVMSG) - 1);
+#endif
+			return (0);
+		}
+		/*
+		 * This is tricky.  The 1 indicates that you want the new page
+		 * allocated with 1 clear bit.  Actually, you are going to
+		 * allocate 2 pages from this map.  The first is going to be
+		 * the map page, the second is the overflow page we were
+		 * looking for.  The init_bitmap routine automatically, sets
+		 * the first bit of itself to indicate that the bitmap itself
+		 * is in use.  We would explicitly set the second bit, but
+		 * don't have to if we tell init_bitmap not to leave it clear
+		 * in the first place.
+		 */
+		if (__ibitmap(hashp,
+		    (int)OADDR_OF(splitnum, offset), 1, free_page))
+			return (0);
+		hashp->SPARES[splitnum]++;
+#ifdef DEBUG2
+		free_bit = 2;
+#endif
+		offset++;
+		if (offset > SPLITMASK) {
+			if (++splitnum >= NCACHED) {
+#ifndef macintosh
+				(void)write(STDERR_FILENO, OVMSG,
+				    sizeof(OVMSG) - 1);
+#endif
+				return (0);
+			}
+			hashp->OVFL_POINT = splitnum;
+			hashp->SPARES[splitnum] = hashp->SPARES[splitnum-1];
+			hashp->SPARES[splitnum-1]--;
+			offset = 0;
+		}
+	} else {
+		/*
+		 * Free_bit addresses the last used bit.  Bump it to address
+		 * the first available bit.
+		 */
+		free_bit++;
+		SETBIT(freep, free_bit);
+	}
+
+	/* Calculate address of the new overflow page */
+	addr = OADDR_OF(splitnum, offset);
+#ifdef DEBUG2
+	(void)fprintf(stderr, "OVERFLOW_PAGE: ADDR: %d BIT: %d PAGE %d\n",
+	    addr, free_bit, free_page);
+#endif
+	return (addr);
+
+found:
+	bit = bit + first_free(freep[j]);
+	SETBIT(freep, bit);
+#ifdef DEBUG2
+	tmp1 = bit;
+	tmp2 = i;
+#endif
+	/*
+	 * Bits are addressed starting with 0, but overflow pages are addressed
+	 * beginning at 1. Bit is a bit addressnumber, so we need to increment
+	 * it to convert it to a page number.
+	 */
+	bit = 1 + bit + (i * (hashp->BSIZE << BYTE_SHIFT));
+	if (bit >= hashp->LAST_FREED)
+		hashp->LAST_FREED = bit - 1;
+
+	/* Calculate the split number for this page */
+	for (i = 0; (i < (unsigned)splitnum) && (bit > hashp->SPARES[i]); i++) {}
+	offset = (i ? bit - hashp->SPARES[i - 1] : bit);
+	if (offset >= SPLITMASK)
+		return (0);	/* Out of overflow pages */
+	addr = OADDR_OF(i, offset);
+#ifdef DEBUG2
+	(void)fprintf(stderr, "OVERFLOW_PAGE: ADDR: %d BIT: %d PAGE %d\n",
+	    addr, tmp1, tmp2);
+#endif
+
+	/* Allocate and return the overflow page */
+	return (addr);
+}
+
+/*
+ * Mark this overflow page as free.
+ */
+extern void
+__free_ovflpage(HTAB *hashp, BUFHEAD *obufp)
+{
+	uint16 addr;
+	uint32 *freep;
+	uint32 bit_address, free_page, free_bit;
+	uint16 ndx;
+
+	if(!obufp || !obufp->addr)
+	    return;
+
+	addr = obufp->addr;
+#ifdef DEBUG1
+	(void)fprintf(stderr, "Freeing %d\n", addr);
+#endif
+	ndx = (((uint16)addr) >> SPLITSHIFT);
+	bit_address =
+	    (ndx ? hashp->SPARES[ndx - 1] : 0) + (addr & SPLITMASK) - 1;
+	if (bit_address < (uint32)hashp->LAST_FREED)
+		hashp->LAST_FREED = bit_address;
+	free_page = (bit_address >> (hashp->BSHIFT + BYTE_SHIFT));
+	free_bit = bit_address & ((hashp->BSIZE << BYTE_SHIFT) - 1);
+
+	if (!(freep = hashp->mapp[free_page])) 
+		freep = fetch_bitmap(hashp, free_page);
+
+#ifdef DEBUG
+	/*
+	 * This had better never happen.  It means we tried to read a bitmap
+	 * that has already had overflow pages allocated off it, and we
+	 * failed to read it from the file.
+	 */
+	if (!freep)
+	  {
+		assert(0);
+		return;
+	  }
+#endif
+	CLRBIT(freep, free_bit);
+#ifdef DEBUG2
+	(void)fprintf(stderr, "FREE_OVFLPAGE: ADDR: %d BIT: %d PAGE %d\n",
+	    obufp->addr, free_bit, free_page);
+#endif
+	__reclaim_buf(hashp, obufp);
+}
+
+/*
+ * Returns:
+ *	 0 success
+ *	-1 failure
+ */
+static int
+open_temp(HTAB *hashp)
+{
+#ifdef XP_OS2
+ 	hashp->fp = mkstemp(NULL);
+#else
+#if !defined(_WIN32) && !defined(_WINDOWS) && !defined(macintosh)
+	sigset_t set, oset;
+#endif
+#if !defined(macintosh)
+	char * tmpdir;
+	size_t len;
+	char last;
+#endif
+	static const char namestr[] = "/_hashXXXXXX";
+	char filename[1024];
+
+#if !defined(_WIN32) && !defined(_WINDOWS) && !defined(macintosh)
+	/* Block signals; make sure file goes away at process exit. */
+	(void)sigfillset(&set);
+	(void)sigprocmask(SIG_BLOCK, &set, &oset);
+#endif
+
+	filename[0] = 0;
+#if defined(macintosh)
+	strcat(filename, namestr + 1);
+#else
+	tmpdir = getenv("TMP");
+	if (!tmpdir)
+		tmpdir = getenv("TMPDIR");
+	if (!tmpdir)
+		tmpdir = getenv("TEMP");
+	if (!tmpdir)
+		tmpdir = ".";
+	len = strlen(tmpdir);
+	if (len && len < (sizeof filename - sizeof namestr)) {
+		strcpy(filename, tmpdir);
+	}
+	len = strlen(filename);
+	last = tmpdir[len - 1];
+	strcat(filename, (last == '/' || last == '\\') ? namestr + 1 : namestr);
+#endif
+
+#if defined(_WIN32) || defined(_WINDOWS)
+	if ((hashp->fp = mkstempflags(filename, _O_BINARY|_O_TEMPORARY)) != -1) {
+		if (hashp->filename) {
+			free(hashp->filename);
+		}
+		hashp->filename = strdup(filename);
+		hashp->is_temp = 1;
+	}
+#else
+	if ((hashp->fp = mkstemp(filename)) != -1) {
+		(void)unlink(filename);
+#if !defined(macintosh)
+		(void)fcntl(hashp->fp, F_SETFD, 1);
+#endif									  
+	}
+#endif
+
+#if !defined(_WIN32) && !defined(_WINDOWS) && !defined(macintosh)
+	(void)sigprocmask(SIG_SETMASK, &oset, (sigset_t *)NULL);
+#endif
+#endif  /* !OS2 */
+	return (hashp->fp != -1 ? 0 : -1);
+}
+
+/*
+ * We have to know that the key will fit, but the last entry on the page is
+ * an overflow pair, so we need to shift things.
+ */
+static void
+squeeze_key(uint16 *sp, const DBT * key, const DBT * val)
+{
+	register char *p;
+	uint16 free_space, n, off, pageno;
+
+	p = (char *)sp;
+	n = sp[0];
+	free_space = FREESPACE(sp);
+	off = OFFSET(sp);
+
+	pageno = sp[n - 1];
+	off -= key->size;
+	sp[n - 1] = off;
+	memmove(p + off, key->data, key->size);
+	off -= val->size;
+	sp[n] = off;
+	memmove(p + off, val->data, val->size);
+	sp[0] = n + 2;
+	sp[n + 1] = pageno;
+	sp[n + 2] = OVFLPAGE;
+	FREESPACE(sp) = free_space - PAIRSIZE(key, val);
+	OFFSET(sp) = off;
+}
+
+static uint32 *
+fetch_bitmap(HTAB *hashp, uint32 ndx)
+{
+	if (ndx >= (unsigned)hashp->nmaps)
+		return (NULL);
+	if ((hashp->mapp[ndx] = (uint32 *)malloc((size_t)hashp->BSIZE)) == NULL)
+		return (NULL);
+	if (__get_page(hashp,
+	    (char *)hashp->mapp[ndx], hashp->BITMAPS[ndx], 0, 1, 1)) {
+		free(hashp->mapp[ndx]);
+		hashp->mapp[ndx] = NULL; /* NEW: 9-11-95 */
+		return (NULL);
+	}                 
+	return (hashp->mapp[ndx]);
+}
+
+#ifdef DEBUG4
+int
+print_chain(int addr)
+{
+	BUFHEAD *bufp;
+	short *bp, oaddr;
+
+	(void)fprintf(stderr, "%d ", addr);
+	bufp = __get_buf(hashp, addr, NULL, 0);
+	bp = (short *)bufp->page;
+	while (bp[0] && ((bp[bp[0]] == OVFLPAGE) ||
+		((bp[0] > 2) && bp[2] < REAL_KEY))) {
+		oaddr = bp[bp[0] - 1];
+		(void)fprintf(stderr, "%d ", (int)oaddr);
+		bufp = __get_buf(hashp, (int)oaddr, bufp, 0);
+		bp = (short *)bufp->page;
+	}
+	(void)fprintf(stderr, "\n");
+}
+#endif
diff -r a945361df361 -r 150b72113545 nss/lib/dbm/src/hash.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nss/lib/dbm/src/hash.c	Tue Aug 05 18:32:02 2014 +0200
@@ -0,0 +1,1175 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Margo Seltzer.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. ***REMOVED*** - see 
+ *    ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)hash.c	8.9 (Berkeley) 6/16/94";
+#endif /* LIBC_SCCS and not lint */
+
+#if !defined(_WIN32) && !defined(_WINDOWS) && !defined(macintosh)
+#include <sys/param.h>
+#endif
+
+#if !defined(macintosh)
+#ifdef XP_OS2
+#include <sys/types.h>
+#endif
+#include <sys/stat.h>
+#endif
+
+#if defined(macintosh)
+#include <unix.h>
+#include <unistd.h>
+#endif
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if !defined(_WIN32) && !defined(_WINDOWS) && !defined(macintosh)
+#include <unistd.h>
+#endif
+#if defined(_WIN32) || defined(_WINDOWS) 
+#include <windows.h>
+#endif
+
+#include <assert.h>
+
+#include "mcom_db.h"
+#include "hash.h"
+#include "page.h"
+
+/*
+#include "extern.h"
+*/
+static int   alloc_segs __P((HTAB *, int));
+static int   flush_meta __P((HTAB *));
+static int   hash_access __P((HTAB *, ACTION, DBT *, DBT *));
+static int   hash_close __P((DB *));
+static int   hash_delete __P((const DB *, const DBT *, uint));
+static int   hash_fd __P((const DB *));
+static int   hash_get __P((const DB *, const DBT *, DBT *, uint));
+static int   hash_put __P((const DB *, DBT *, const DBT *, uint));
+static void *hash_realloc __P((SEGMENT **, size_t, size_t));
+static int   hash_seq __P((const DB *, DBT *, DBT *, uint));
+static int   hash_sync __P((const DB *, uint));
+static int   hdestroy __P((HTAB *));
+static HTAB *init_hash __P((HTAB *, const char *, HASHINFO *));
+static int   init_htab __P((HTAB *, int));
+#if BYTE_ORDER == LITTLE_ENDIAN
+static void  swap_header __P((HTAB *));
+static void  swap_header_copy __P((HASHHDR *, HASHHDR *));
+#endif
+
+/* Fast arithmetic, relying on powers of 2, */
+#define MOD(x, y)		((x) & ((y) - 1))
+
+#define RETURN_ERROR(ERR, LOC)	{ save_errno = ERR; goto LOC; }
+
+/* Return values */
+#define	SUCCESS	 (0)
+#define	DBM_ERROR	(-1)
+#define	ABNORMAL (1)
+
+#ifdef HASH_STATISTICS
+int hash_accesses, hash_collisions, hash_expansions, hash_overflows;
+#endif
+
+/* A new Lou (montulli at mozilla.com) routine.
+ *
+ * The database is screwed.  
+ *
+ * This closes the file, flushing buffers as appropriate.
+ */
+static void
+__remove_database(DB *dbp)
+{
+	HTAB *hashp = (HTAB *)dbp->internal;
+
+	assert(0);
+
+	if (!hashp)
+		return;
+	hdestroy(hashp);
+	dbp->internal = NULL; 
+}
+
+/************************** INTERFACE ROUTINES ***************************/
+/* OPEN/CLOSE */
+
+
+extern DB *
+__hash_open(const char *file, int flags, int mode, const HASHINFO *info, int dflags)
+{
+	HTAB *hashp=NULL;
+	struct stat statbuf;
+	DB *dbp;
+	int bpages, hdrsize, new_table, nsegs, save_errno;
+
+	if ((flags & O_ACCMODE) == O_WRONLY) {
+		errno = EINVAL;
+		return NULL;
+	}
+
+	/* zero the statbuffer so that
+	 * we can check it for a non-zero
+	 * date to see if stat succeeded
+	 */
+	memset(&statbuf, 0, sizeof(struct stat));
+
+	if (!(hashp = (HTAB *)calloc(1, sizeof(HTAB)))) {
+		errno = ENOMEM;
+		return NULL;
+	}
+	hashp->fp = NO_FILE;
+	if(file)
+		hashp->filename = strdup(file);
+
+	/*
+	 * Even if user wants write only, we need to be able to read
+	 * the actual file, so we need to open it read/write. But, the
+	 * field in the hashp structure needs to be accurate so that
+	 * we can check accesses.
+	 */
+	hashp->flags = flags;
+
+	new_table = 0;
+	if (!file || (flags & O_TRUNC) 	|| (stat(file, &statbuf)  && (errno == ENOENT))) 
+	{
+		if (errno == ENOENT)
+			errno = 0; /* Just in case someone looks at errno */
+		new_table = 1;
+	}
+	else if(statbuf.st_mtime && statbuf.st_size == 0)
+	{
+		/* check for a zero length file and delete it
+	 	 * if it exists
+	 	 */
+		new_table = 1;
+	}
+	hashp->file_size = statbuf.st_size;
+
+	if (file) {				 
+#if defined(_WIN32) || defined(_WINDOWS) || defined (macintosh)  || defined(XP_OS2)
+		if ((hashp->fp = DBFILE_OPEN(file, flags | O_BINARY, mode)) == -1)
+			RETURN_ERROR(errno, error1);
+#else
+		if ((hashp->fp = open(file, flags, mode)) == -1)
+			RETURN_ERROR(errno, error1);
+		(void)fcntl(hashp->fp, F_SETFD, 1);
+#endif
+	}
+	if (new_table) {
+		if (!init_hash(hashp, file, (HASHINFO *)info))
+			RETURN_ERROR(errno, error1);
+	} else {
+		/* Table already exists */
+		if (info && info->hash)
+			hashp->hash = info->hash;
+		else
+			hashp->hash = __default_hash;
+
+		hdrsize = read(hashp->fp, (char *)&hashp->hdr, sizeof(HASHHDR));
+		if (hdrsize == -1)
+			RETURN_ERROR(errno, error1);
+		if (hdrsize != sizeof(HASHHDR))
+			RETURN_ERROR(EFTYPE, error1);
+#if BYTE_ORDER == LITTLE_ENDIAN
+		swap_header(hashp);
+#endif
+		/* Verify file type, versions and hash function */
+		if (hashp->MAGIC != HASHMAGIC)
+			RETURN_ERROR(EFTYPE, error1);
+#define	OLDHASHVERSION	1
+		if (hashp->VERSION != HASHVERSION &&
+		    hashp->VERSION != OLDHASHVERSION)
+			RETURN_ERROR(EFTYPE, error1);
+		if (hashp->hash(CHARKEY, sizeof(CHARKEY)) != hashp->H_CHARKEY)
+			RETURN_ERROR(EFTYPE, error1);
+		if (hashp->NKEYS < 0) /* Old bad database. */
+			RETURN_ERROR(EFTYPE, error1);
+
+		/*
+		 * Figure out how many segments we need.  Max_Bucket is the
+		 * maximum bucket number, so the number of buckets is
+		 * max_bucket + 1.
+		 */
+		nsegs = (hashp->MAX_BUCKET + 1 + hashp->SGSIZE - 1) /
+			 hashp->SGSIZE;
+		hashp->nsegs = 0;
+		if (alloc_segs(hashp, nsegs))
+			/* If alloc_segs fails, errno will have been set.  */
+			RETURN_ERROR(errno, error1);
+		/* Read in bitmaps */
+		bpages = (hashp->SPARES[hashp->OVFL_POINT] +
+		    (hashp->BSIZE << BYTE_SHIFT) - 1) >>
+		    (hashp->BSHIFT + BYTE_SHIFT);
+
+		hashp->nmaps = bpages;
+		(void)memset(&hashp->mapp[0], 0, bpages * sizeof(uint32 *));
+	}
+
+	/* Initialize Buffer Manager */
+	if (info && info->cachesize)
+		__buf_init(hashp, (int32) info->cachesize);
+	else
+		__buf_init(hashp, DEF_BUFSIZE);
+
+	hashp->new_file = new_table;
+#ifdef macintosh
+	hashp->save_file = file && !(hashp->flags & O_RDONLY);
+#else
+	hashp->save_file = file && (hashp->flags & O_RDWR);
+#endif
+	hashp->cbucket = -1;
+	if (!(dbp = (DB *)malloc(sizeof(DB)))) {
+		RETURN_ERROR(ENOMEM, error1);
+	}
+	dbp->internal = hashp;
+	dbp->close = hash_close;
+	dbp->del = hash_delete;
+	dbp->fd = hash_fd;
+	dbp->get = hash_get;
+	dbp->put = hash_put;
+	dbp->seq = hash_seq;
+	dbp->sync = hash_sync;
+	dbp->type = DB_HASH;
+
+#ifdef HASH_STATISTICS
+	hash_overflows = hash_accesses = hash_collisions = hash_expansions = 0;
+#endif
+	return (dbp);
+
+error1:
+	hdestroy(hashp);
+	errno = save_errno;
+	return (NULL);
+}
+
+static int
+hash_close(DB *dbp)
+{
+	HTAB *hashp;
+	int retval;
+
+	if (!dbp)
+		return (DBM_ERROR);
+
+	hashp = (HTAB *)dbp->internal;
+	if(!hashp)
+		return (DBM_ERROR);
+
+	retval = hdestroy(hashp);
+	free(dbp);
+	return (retval);
+}
+
+static int hash_fd(const DB *dbp)
+{
+	HTAB *hashp;
+
+	if (!dbp)
+		return (DBM_ERROR);
+
+	hashp = (HTAB *)dbp->internal;
+	if(!hashp)
+		return (DBM_ERROR);
+
+	if (hashp->fp == -1) {
+		errno = ENOENT;
+		return (-1);
+	}
+	return (hashp->fp);
+}
+
+/************************** LOCAL CREATION ROUTINES **********************/
+static HTAB *
+init_hash(HTAB *hashp, const char *file, HASHINFO *info)
+{
+	struct stat statbuf;
+	int nelem;
+
+	nelem = 1;
+	hashp->NKEYS = 0;
+	hashp->LORDER = BYTE_ORDER;
+	hashp->BSIZE = DEF_BUCKET_SIZE;
+	hashp->BSHIFT = DEF_BUCKET_SHIFT;
+	hashp->SGSIZE = DEF_SEGSIZE;
+	hashp->SSHIFT = DEF_SEGSIZE_SHIFT;
+	hashp->DSIZE = DEF_DIRSIZE;
+	hashp->FFACTOR = DEF_FFACTOR;
+	hashp->hash = __default_hash;
+	memset(hashp->SPARES, 0, sizeof(hashp->SPARES));
+	memset(hashp->BITMAPS, 0, sizeof (hashp->BITMAPS));
+
+	/* Fix bucket size to be optimal for file system */
+	if (file != NULL) {
+		if (stat(file, &statbuf))
+			return (NULL);
+
+#if !defined(_WIN32) && !defined(_WINDOWS) && !defined(macintosh) && !defined(XP_OS2)
+#if defined(__QNX__) && !defined(__QNXNTO__)
+		hashp->BSIZE = 512; /* preferred blk size on qnx4 */
+#else
+		hashp->BSIZE = statbuf.st_blksize;
+#endif
+
+		/* new code added by Lou to reduce block
+		 * size down below MAX_BSIZE
+		 */
+		if (hashp->BSIZE > MAX_BSIZE)
+			hashp->BSIZE = MAX_BSIZE;
+#endif
+		hashp->BSHIFT = __log2((uint32)hashp->BSIZE);
+	}
+
+	if (info) {
+		if (info->bsize) {
+			/* Round pagesize up to power of 2 */
+			hashp->BSHIFT = __log2(info->bsize);
+			hashp->BSIZE = 1 << hashp->BSHIFT;
+			if (hashp->BSIZE > MAX_BSIZE) {
+				errno = EINVAL;
+				return (NULL);
+			}
+		}
+		if (info->ffactor)
+			hashp->FFACTOR = info->ffactor;
+		if (info->hash)
+			hashp->hash = info->hash;
+		if (info->nelem)
+			nelem = info->nelem;
+		if (info->lorder) {
+			if (info->lorder != BIG_ENDIAN &&
+			    info->lorder != LITTLE_ENDIAN) {
+				errno = EINVAL;
+				return (NULL);
+			}
+			hashp->LORDER = info->lorder;
+		}
+	}
+	/* init_htab sets errno if it fails */
+	if (init_htab(hashp, nelem))
+		return (NULL);
+	else
+		return (hashp);
+}
+/*
+ * This calls alloc_segs which may run out of memory.  Alloc_segs will 
+ * set errno, so we just pass the error information along.
+ *
+ * Returns 0 on No Error
+ */
+static int
+init_htab(HTAB *hashp, int nelem)
+{
+	register int nbuckets, nsegs;
+	int l2;
+
+	/*
+	 * Divide number of elements by the fill factor and determine a
+	 * desired number of buckets.  Allocate space for the next greater
+	 * power of two number of buckets.
+	 */
+	nelem = (nelem - 1) / hashp->FFACTOR + 1;
+
+	l2 = __log2((uint32)PR_MAX(nelem, 2));
+	nbuckets = 1 << l2;
+
+	hashp->SPARES[l2] = l2 + 1;
+	hashp->SPARES[l2 + 1] = l2 + 1;
+	hashp->OVFL_POINT = l2;
+	hashp->LAST_FREED = 2;
+
+	/* First bitmap page is at: splitpoint l2 page offset 1 */
+	if (__ibitmap(hashp, (int)OADDR_OF(l2, 1), l2 + 1, 0))
+		return (-1);
+
+	hashp->MAX_BUCKET = hashp->LOW_MASK = nbuckets - 1;
+	hashp->HIGH_MASK = (nbuckets << 1) - 1;
+	hashp->HDRPAGES = ((PR_MAX(sizeof(HASHHDR), MINHDRSIZE) - 1) >>
+	    hashp->BSHIFT) + 1;
+
+	nsegs = (nbuckets - 1) / hashp->SGSIZE + 1;
+	nsegs = 1 << __log2((uint32)nsegs);
+
+	if (nsegs > hashp->DSIZE)
+		hashp->DSIZE = nsegs;
+	return (alloc_segs(hashp, nsegs));
+}
+
+/********************** DESTROY/CLOSE ROUTINES ************************/
+
+/*
+ * Flushes any changes to the file if necessary and destroys the hashp
+ * structure, freeing all allocated space.
+ */
+static int
+hdestroy(HTAB *hashp)
+{
+	int i, save_errno;
+
+	save_errno = 0;
+
+#ifdef HASH_STATISTICS
+	(void)fprintf(stderr, "hdestroy: accesses %ld collisions %ld\n",
+	    hash_accesses, hash_collisions);
+	(void)fprintf(stderr, "hdestroy: expansions %ld\n",
+	    hash_expansions);
+	(void)fprintf(stderr, "hdestroy: overflows %ld\n",
+	    hash_overflows);
+	(void)fprintf(stderr, "keys %ld maxp %d segmentcount %d\n",
+	    hashp->NKEYS, hashp->MAX_BUCKET, hashp->nsegs);
+
+	for (i = 0; i < NCACHED; i++)
+		(void)fprintf(stderr,
+		    "spares[%d] = %d\n", i, hashp->SPARES[i]);
+#endif
+	/*
+	 * Call on buffer manager to free buffers, and if required,
+	 * write them to disk.
+	 */
+	if (__buf_free(hashp, 1, hashp->save_file))
+		save_errno = errno;
+	if (hashp->dir) {
+		free(*hashp->dir);	/* Free initial segments */
+		/* Free extra segments */
+		while (hashp->exsegs--)
+			free(hashp->dir[--hashp->nsegs]);
+		free(hashp->dir);
+	}
+	if (flush_meta(hashp) && !save_errno)
+		save_errno = errno;
+	/* Free Bigmaps */
+	for (i = 0; i < hashp->nmaps; i++)
+		if (hashp->mapp[i])
+			free(hashp->mapp[i]);
+
+	if (hashp->fp != -1)
+		(void)close(hashp->fp);
+
+	if(hashp->filename) {
+#if defined(_WIN32) || defined(_WINDOWS) || defined(XP_OS2)
+		if (hashp->is_temp)
+			(void)unlink(hashp->filename);
+#endif
+		free(hashp->filename);
+	}
+	if (hashp->tmp_buf)
+	    free(hashp->tmp_buf);
+	if (hashp->tmp_key)
+	    free(hashp->tmp_key);
+	free(hashp);
+	if (save_errno) {
+		errno = save_errno;
+		return (DBM_ERROR);
+	}
+	return (SUCCESS);
+}
+
+#if defined(_WIN32) || defined(_WINDOWS) 
+/*
+ * Close and reopen file to force file length update on windows. 
+ *
+ * Returns:
+ *	 0 == OK
+ *	-1 DBM_ERROR
+ */
+static int
+update_EOF(HTAB *hashp)
+{
+#if defined(DBM_REOPEN_ON_FLUSH)
+	char *      file       = hashp->filename;
+	off_t       file_size;
+	int         flags;
+	int         mode       = -1;
+	struct stat statbuf;
+
+	memset(&statbuf, 0, sizeof statbuf);
+
+	/* make sure we won't lose the file by closing it. */
+	if (!file || (stat(file, &statbuf)  && (errno == ENOENT)))  {
+		/* pretend we did it. */
+		return 0;
+	}
+
+	(void)close(hashp->fp);
+
+	flags = hashp->flags & ~(O_TRUNC | O_CREAT | O_EXCL);
+
+	if ((hashp->fp = DBFILE_OPEN(file, flags | O_BINARY, mode)) == -1)
+		return -1;
+	file_size = lseek(hashp->fp, (off_t)0, SEEK_END);
+	if (file_size == -1) 
+		return -1;
+	hashp->file_size = file_size;
+	return 0;
+#else
+	int    fd        = hashp->fp;
+	off_t  file_size = lseek(fd, (off_t)0, SEEK_END);
+	HANDLE handle    = (HANDLE)_get_osfhandle(fd);
+	BOOL   cool      = FlushFileBuffers(handle);
+#ifdef DEBUG3
+	if (!cool) {
+		DWORD err = GetLastError();
+		(void)fprintf(stderr,
+			"FlushFileBuffers failed, last error = %d, 0x%08x\n",
+			err, err);
+	}
+#endif
+	if (file_size == -1) 
+		return -1;
+	hashp->file_size = file_size;
+	return cool ? 0 : -1;
+#endif
+}
+#endif
+
+/*
+ * Write modified pages to disk
+ *
+ * Returns:
+ *	 0 == OK
+ *	-1 DBM_ERROR
+ */
+static int
+hash_sync(const DB *dbp, uint flags)
+{
+	HTAB *hashp;
+
+	if (flags != 0) {
+		errno = EINVAL;
+		return (DBM_ERROR);
+	}
+
+	if (!dbp)
+		return (DBM_ERROR);
+
+	hashp = (HTAB *)dbp->internal;
+	if(!hashp)
+		return (DBM_ERROR);
+
+	if (!hashp->save_file)
+		return (0);
+	if (__buf_free(hashp, 0, 1) || flush_meta(hashp))
+		return (DBM_ERROR);
+#if defined(_WIN32) || defined(_WINDOWS) 
+	if (hashp->updateEOF && hashp->filename && !hashp->is_temp) {
+		int status = update_EOF(hashp);
+		hashp->updateEOF = 0;
+		if (status)
+			return status;
+	}
+#endif
+	hashp->new_file = 0;
+	return (0);
+}
+
+/*
+ * Returns:
+ *	 0 == OK
+ *	-1 indicates that errno should be set
+ */
+static int
+flush_meta(HTAB *hashp)
+{
+	HASHHDR *whdrp;
+#if BYTE_ORDER == LITTLE_ENDIAN
+	HASHHDR whdr;
+#endif
+	int fp, i, wsize;
+
+	if (!hashp->save_file)
+		return (0);
+	hashp->MAGIC = HASHMAGIC;
+	hashp->VERSION = HASHVERSION;
+	hashp->H_CHARKEY = hashp->hash(CHARKEY, sizeof(CHARKEY));
+
+	fp = hashp->fp;
+	whdrp = &hashp->hdr;
+#if BYTE_ORDER == LITTLE_ENDIAN
+	whdrp = &whdr;
+	swap_header_copy(&hashp->hdr, whdrp);
+#endif
+	if ((lseek(fp, (off_t)0, SEEK_SET) == -1) ||
+	    ((wsize = write(fp, (char*)whdrp, sizeof(HASHHDR))) == -1))
+		return (-1);
+	else
+		if (wsize != sizeof(HASHHDR)) {
+			errno = EFTYPE;
+			hashp->dbmerrno = errno;
+			return (-1);
+		}
+	for (i = 0; i < NCACHED; i++)
+		if (hashp->mapp[i])
+			if (__put_page(hashp, (char *)hashp->mapp[i],
+				hashp->BITMAPS[i], 0, 1))
+				return (-1);
+	return (0);
+}
+
+/*******************************SEARCH ROUTINES *****************************/
+/*
+ * All the access routines return
+ *
+ * Returns:
+ *	 0 on SUCCESS
+ *	 1 to indicate an external DBM_ERROR (i.e. key not found, etc)
+ *	-1 to indicate an internal DBM_ERROR (i.e. out of memory, etc)
+ */
+static int
+hash_get(
+	const DB *dbp,
+	const DBT *key,
+	DBT *data,
+	uint flag)
+{
+	HTAB *hashp;
+	int rv;
+
+	hashp = (HTAB *)dbp->internal;
+	if (!hashp)
+		return (DBM_ERROR);
+
+	if (flag) {
+		hashp->dbmerrno = errno = EINVAL;
+		return (DBM_ERROR);
+	}
+
+	rv = hash_access(hashp, HASH_GET, (DBT *)key, data);
+
+	if(rv == DATABASE_CORRUPTED_ERROR)
+	  {
+#if defined(unix) && defined(DEBUG)
+		printf("\n\nDBM Database has been corrupted, tell Lou...\n\n");
+#endif
+	  	__remove_database((DB *)dbp);
+	  }
+
+	return(rv);
+}
+
+static int
+hash_put(
+	const DB *dbp,
+	DBT *key,
+	const DBT *data,
+	uint flag)
+{
+	HTAB *hashp;
+	int rv;
+
+	hashp = (HTAB *)dbp->internal;
+	if (!hashp)
+		return (DBM_ERROR);
+
+	if (flag && flag != R_NOOVERWRITE) {
+		hashp->dbmerrno = errno = EINVAL;
+		return (DBM_ERROR);
+	}
+	if ((hashp->flags & O_ACCMODE) == O_RDONLY) {
+		hashp->dbmerrno = errno = EPERM;
+		return (DBM_ERROR);
+	}
+
+	rv =  hash_access(hashp, flag == R_NOOVERWRITE ?
+	    HASH_PUTNEW : HASH_PUT, (DBT *)key, (DBT *)data);
+
+	if(rv == DATABASE_CORRUPTED_ERROR)
+	  {
+#if defined(unix) && defined(DEBUG)
+		printf("\n\nDBM Database has been corrupted, tell Lou...\n\n");
+#endif
+	  	__remove_database((DB *)dbp);
+	  }
+
+	return(rv);
+}
+
+static int
+hash_delete(
+	const DB *dbp,
+	const DBT *key,
+	uint flag)		/* Ignored */
+{
+	HTAB *hashp;
+	int rv;
+
+	hashp = (HTAB *)dbp->internal;
+	if (!hashp)
+		return (DBM_ERROR);
+
+	if (flag && flag != R_CURSOR) {
+		hashp->dbmerrno = errno = EINVAL;
+		return (DBM_ERROR);
+	}
+	if ((hashp->flags & O_ACCMODE) == O_RDONLY) {
+		hashp->dbmerrno = errno = EPERM;
+		return (DBM_ERROR);
+	}
+	rv = hash_access(hashp, HASH_DELETE, (DBT *)key, NULL);
+
+	if(rv == DATABASE_CORRUPTED_ERROR)
+	  {
+#if defined(unix) && defined(DEBUG)
+		printf("\n\nDBM Database has been corrupted, tell Lou...\n\n");
+#endif
+	  	__remove_database((DB *)dbp);
+	  }
+
+	return(rv);
+}
+
+#define MAX_OVERFLOW_HASH_ACCESS_LOOPS 2000
+/*
+ * Assume that hashp has been set in wrapper routine.
+ */
+static int
+hash_access(
+	HTAB *hashp,
+	ACTION action,
+	DBT *key, DBT *val)
+{
+	register BUFHEAD *rbufp;
+	BUFHEAD *bufp, *save_bufp;
+	register uint16 *bp;
+	register long n, ndx, off;
+	register size_t size;
+	register char *kp;
+	uint16 pageno;
+	uint32 ovfl_loop_count=0;
+    int32 last_overflow_page_no = -1;
+
+#ifdef HASH_STATISTICS
+	hash_accesses++;
+#endif
+
+	off = hashp->BSIZE;
+	size = key->size;
+	kp = (char *)key->data;
+	rbufp = __get_buf(hashp, __call_hash(hashp, kp, size), NULL, 0);
+	if (!rbufp)
+		return (DATABASE_CORRUPTED_ERROR);
+	save_bufp = rbufp;
+
+	/* Pin the bucket chain */
+	rbufp->flags |= BUF_PIN;
+	for (bp = (uint16 *)rbufp->page, n = *bp++, ndx = 1; ndx < n;)
+	{
+
+		if (bp[1] >= REAL_KEY) {
+			/* Real key/data pair */
+			if (size == (unsigned long)(off - *bp) &&
+			    memcmp(kp, rbufp->page + *bp, size) == 0)
+				goto found;
+			off = bp[1];
+#ifdef HASH_STATISTICS
+			hash_collisions++;
+#endif
+			bp += 2;
+			ndx += 2;
+		} else if (bp[1] == OVFLPAGE) {
+
+            /* database corruption: overflow loop detection */
+            if(last_overflow_page_no == (int32)*bp)
+    			return (DATABASE_CORRUPTED_ERROR);
+
+            last_overflow_page_no = *bp;
+
+			rbufp = __get_buf(hashp, *bp, rbufp, 0);
+			if (!rbufp) {
+				save_bufp->flags &= ~BUF_PIN;
+				return (DBM_ERROR);
+			}
+
+            ovfl_loop_count++;
+            if(ovfl_loop_count > MAX_OVERFLOW_HASH_ACCESS_LOOPS)
+    			return (DATABASE_CORRUPTED_ERROR);
+
+			/* FOR LOOP INIT */
+			bp = (uint16 *)rbufp->page;
+			n = *bp++;
+			ndx = 1;
+			off = hashp->BSIZE;
+		} else if (bp[1] < REAL_KEY) {
+			if ((ndx =
+			    __find_bigpair(hashp, rbufp, ndx, kp, (int)size)) > 0)
+				goto found;
+			if (ndx == -2) {
+				bufp = rbufp;
+				if (!(pageno =
+				    __find_last_page(hashp, &bufp))) {
+					ndx = 0;
+					rbufp = bufp;
+					break;	/* FOR */
+				}
+				rbufp = __get_buf(hashp, pageno, bufp, 0);
+				if (!rbufp) {
+					save_bufp->flags &= ~BUF_PIN;
+					return (DBM_ERROR);
+				}
+				/* FOR LOOP INIT */
+				bp = (uint16 *)rbufp->page;
+				n = *bp++;
+				ndx = 1;
+				off = hashp->BSIZE;
+			} else {
+				save_bufp->flags &= ~BUF_PIN;
+				return (DBM_ERROR);
+
+			}
+		}
+	}
+
+	/* Not found */
+	switch (action) {
+	case HASH_PUT:
+	case HASH_PUTNEW:
+		if (__addel(hashp, rbufp, key, val)) {
+			save_bufp->flags &= ~BUF_PIN;
+			return (DBM_ERROR);
+		} else {
+			save_bufp->flags &= ~BUF_PIN;
+			return (SUCCESS);
+		}
+	case HASH_GET:
+	case HASH_DELETE:
+	default:
+		save_bufp->flags &= ~BUF_PIN;
+		return (ABNORMAL);
+	}
+
+found:
+	switch (action) {
+	case HASH_PUTNEW:
+		save_bufp->flags &= ~BUF_PIN;
+		return (ABNORMAL);
+	case HASH_GET:
+		bp = (uint16 *)rbufp->page;
+		if (bp[ndx + 1] < REAL_KEY) {
+			if (__big_return(hashp, rbufp, ndx, val, 0))
+				return (DBM_ERROR);
+		} else {
+			val->data = (uint8 *)rbufp->page + (int)bp[ndx + 1];
+			val->size = bp[ndx] - bp[ndx + 1];
+		}
+		break;
+	case HASH_PUT:
+		if ((__delpair(hashp, rbufp, ndx)) ||
+		    (__addel(hashp, rbufp, key, val))) {
+			save_bufp->flags &= ~BUF_PIN;
+			return (DBM_ERROR);
+		}
+		break;
+	case HASH_DELETE:
+		if (__delpair(hashp, rbufp, ndx))
+			return (DBM_ERROR);
+		break;
+	default:
+		abort();
+	}
+	save_bufp->flags &= ~BUF_PIN;
+	return (SUCCESS);
+}
+
+static int
+hash_seq(
+	const DB *dbp,
+	DBT *key, DBT *data,
+	uint flag)
+{
+	register uint32 bucket;
+	register BUFHEAD *bufp;
+	HTAB *hashp;
+	uint16 *bp, ndx;
+
+	hashp = (HTAB *)dbp->internal;
+	if (!hashp)
+		return (DBM_ERROR);
+
+	if (flag && flag != R_FIRST && flag != R_NEXT) {
+		hashp->dbmerrno = errno = EINVAL;
+		return (DBM_ERROR);
+	}
+#ifdef HASH_STATISTICS
+	hash_accesses++;
+#endif
+	if ((hashp->cbucket < 0) || (flag == R_FIRST)) {
+		hashp->cbucket = 0;
+		hashp->cndx = 1;
+		hashp->cpage = NULL;
+	}
+
+	for (bp = NULL; !bp || !bp[0]; ) {
+		if (!(bufp = hashp->cpage)) {
+			for (bucket = hashp->cbucket;
+			    bucket <= (uint32)hashp->MAX_BUCKET;
+			    bucket++, hashp->cndx = 1) {
+				bufp = __get_buf(hashp, bucket, NULL, 0);
+				if (!bufp)
+					return (DBM_ERROR);
+				hashp->cpage = bufp;
+				bp = (uint16 *)bufp->page;
+				if (bp[0])
+					break;
+			}
+			hashp->cbucket = bucket;
+			if (hashp->cbucket > hashp->MAX_BUCKET) {
+				hashp->cbucket = -1;
+				return (ABNORMAL);
+			}
+		} else
+			bp = (uint16 *)hashp->cpage->page;
+
+#ifdef DEBUG
+		assert(bp);
+		assert(bufp);
+#endif
+		while (bp[hashp->cndx + 1] == OVFLPAGE) {
+			bufp = hashp->cpage =
+			    __get_buf(hashp, bp[hashp->cndx], bufp, 0);
+			if (!bufp)
+				return (DBM_ERROR);
+			bp = (uint16 *)(bufp->page);
+			hashp->cndx = 1;
+		}
+		if (!bp[0]) {
+			hashp->cpage = NULL;
+			++hashp->cbucket;
+		}
+	}
+	ndx = hashp->cndx;
+	if (bp[ndx + 1] < REAL_KEY) {
+		if (__big_keydata(hashp, bufp, key, data, 1))
+			return (DBM_ERROR);
+	} else {
+		key->data = (uint8 *)hashp->cpage->page + bp[ndx];
+		key->size = (ndx > 1 ? bp[ndx - 1] : hashp->BSIZE) - bp[ndx];
+		data->data = (uint8 *)hashp->cpage->page + bp[ndx + 1];
+		data->size = bp[ndx] - bp[ndx + 1];
+		ndx += 2;
+		if (ndx > bp[0]) {
+			hashp->cpage = NULL;
+			hashp->cbucket++;
+			hashp->cndx = 1;
+		} else
+			hashp->cndx = ndx;
+	}
+	return (SUCCESS);
+}
+
+/********************************* UTILITIES ************************/
+
+/*
+ * Returns:
+ *	 0 ==> OK
+ *	-1 ==> Error
+ */
+extern int
+__expand_table(HTAB *hashp)
+{
+	uint32 old_bucket, new_bucket;
+	int new_segnum, spare_ndx;
+	size_t dirsize;
+
+#ifdef HASH_STATISTICS
+	hash_expansions++;
+#endif
+	new_bucket = ++hashp->MAX_BUCKET;
+	old_bucket = (hashp->MAX_BUCKET & hashp->LOW_MASK);
+
+	new_segnum = new_bucket >> hashp->SSHIFT;
+
+	/* Check if we need a new segment */
+	if (new_segnum >= hashp->nsegs) {
+		/* Check if we need to expand directory */
+		if (new_segnum >= hashp->DSIZE) {
+			/* Reallocate directory */
+			dirsize = hashp->DSIZE * sizeof(SEGMENT *);
+			if (!hash_realloc(&hashp->dir, dirsize, dirsize << 1))
+				return (-1);
+			hashp->DSIZE = dirsize << 1;
+		}
+		if ((hashp->dir[new_segnum] =
+		    (SEGMENT)calloc((size_t)hashp->SGSIZE, sizeof(SEGMENT))) == NULL)
+			return (-1);
+		hashp->exsegs++;
+		hashp->nsegs++;
+	}
+	/*
+	 * If the split point is increasing (MAX_BUCKET's log base 2
+	 * * increases), we need to copy the current contents of the spare
+	 * split bucket to the next bucket.
+	 */
+	spare_ndx = __log2((uint32)(hashp->MAX_BUCKET + 1));
+	if (spare_ndx > hashp->OVFL_POINT) {
+		hashp->SPARES[spare_ndx] = hashp->SPARES[hashp->OVFL_POINT];
+		hashp->OVFL_POINT = spare_ndx;
+	}
+
+	if (new_bucket > (uint32)hashp->HIGH_MASK) {
+		/* Starting a new doubling */
+		hashp->LOW_MASK = hashp->HIGH_MASK;
+		hashp->HIGH_MASK = new_bucket | hashp->LOW_MASK;
+	}
+	/* Relocate records to the new bucket */
+	return (__split_page(hashp, old_bucket, new_bucket));
+}
+
+/*
+ * If realloc guarantees that the pointer is not destroyed if the realloc
+ * fails, then this routine can go away.
+ */
+static void *
+hash_realloc(
+	SEGMENT **p_ptr,
+	size_t oldsize, size_t newsize)
+{
+	register void *p;
+
+	if ((p = malloc(newsize))) {
+		memmove(p, *p_ptr, oldsize);
+		memset((char *)p + oldsize, 0, newsize - oldsize);
+		free(*p_ptr);
+		*p_ptr = (SEGMENT *)p;
+	}
+	return (p);
+}
+
+extern uint32
+__call_hash(HTAB *hashp, char *k, size_t len)
+{
+	uint32 n, bucket;
+
+	n = hashp->hash(k, len);
+	bucket = n & hashp->HIGH_MASK;
+	if (bucket > (uint32)hashp->MAX_BUCKET)
+		bucket = bucket & hashp->LOW_MASK;
+	return (bucket);
+}
+
+/*
+ * Allocate segment table.  On error, set errno.
+ *
+ * Returns 0 on success
+ */
+static int
+alloc_segs(
+	HTAB *hashp,
+	int nsegs)
+{
+	register int i;
+	register SEGMENT store;
+
+	if ((hashp->dir =
+	    (SEGMENT *)calloc((size_t)hashp->DSIZE, sizeof(SEGMENT *))) == NULL) {
+		errno = ENOMEM;
+		return (-1);
+	}
+	/* Allocate segments */
+	if ((store =
+	    (SEGMENT)calloc((size_t)nsegs << hashp->SSHIFT, sizeof(SEGMENT))) == NULL) {
+		errno = ENOMEM;
+		return (-1);
+	}
+	for (i = 0; i < nsegs; i++, hashp->nsegs++)
+		hashp->dir[i] = &store[i << hashp->SSHIFT];
+	return (0);
+}
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+/*
+ * Hashp->hdr needs to be byteswapped.
+ */
+static void
+swap_header_copy(
+	HASHHDR *srcp, HASHHDR *destp)
+{
+	int i;
+
+	P_32_COPY(srcp->magic, destp->magic);
+	P_32_COPY(srcp->version, destp->version);
+	P_32_COPY(srcp->lorder, destp->lorder);
+	P_32_COPY(srcp->bsize, destp->bsize);
+	P_32_COPY(srcp->bshift, destp->bshift);
+	P_32_COPY(srcp->dsize, destp->dsize);
+	P_32_COPY(srcp->ssize, destp->ssize);
+	P_32_COPY(srcp->sshift, destp->sshift);
+	P_32_COPY(srcp->ovfl_point, destp->ovfl_point);
+	P_32_COPY(srcp->last_freed, destp->last_freed);
+	P_32_COPY(srcp->max_bucket, destp->max_bucket);
+	P_32_COPY(srcp->high_mask, destp->high_mask);
+	P_32_COPY(srcp->low_mask, destp->low_mask);
+	P_32_COPY(srcp->ffactor, destp->ffactor);
+	P_32_COPY(srcp->nkeys, destp->nkeys);
+	P_32_COPY(srcp->hdrpages, destp->hdrpages);
+	P_32_COPY(srcp->h_charkey, destp->h_charkey);
+	for (i = 0; i < NCACHED; i++) {
+		P_32_COPY(srcp->spares[i], destp->spares[i]);
+		P_16_COPY(srcp->bitmaps[i], destp->bitmaps[i]);
+	}
+}
+
+static void
+swap_header(HTAB *hashp)
+{
+	HASHHDR *hdrp;
+	int i;
+
+	hdrp = &hashp->hdr;
+
+	M_32_SWAP(hdrp->magic);
+	M_32_SWAP(hdrp->version);
+	M_32_SWAP(hdrp->lorder);
+	M_32_SWAP(hdrp->bsize);
+	M_32_SWAP(hdrp->bshift);
+	M_32_SWAP(hdrp->dsize);
+	M_32_SWAP(hdrp->ssize);
+	M_32_SWAP(hdrp->sshift);
+	M_32_SWAP(hdrp->ovfl_point);
+	M_32_SWAP(hdrp->last_freed);
+	M_32_SWAP(hdrp->max_bucket);
+	M_32_SWAP(hdrp->high_mask);
+	M_32_SWAP(hdrp->low_mask);
+	M_32_SWAP(hdrp->ffactor);
+	M_32_SWAP(hdrp->nkeys);
+	M_32_SWAP(hdrp->hdrpages);
+	M_32_SWAP(hdrp->h_charkey);
+	for (i = 0; i < NCACHED; i++) {
+		M_32_SWAP(hdrp->spares[i]);
+		M_16_SWAP(hdrp->bitmaps[i]);
+	}
+}
+#endif
diff -r a945361df361 -r 150b72113545 nss/lib/dbm/src/hash_buf.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nss/lib/dbm/src/hash_buf.c	Tue Aug 05 18:32:02 2014 +0200
@@ -0,0 +1,410 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Margo Seltzer.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. ***REMOVED*** - see 
+ *    ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)hash_buf.c	8.5 (Berkeley) 7/15/94";
+#endif /* LIBC_SCCS and not lint */
+
+/*
+ * PACKAGE: hash
+ *
+ * DESCRIPTION:
+ *	Contains buffer management
+ *
+ * ROUTINES:
+ * External
+ *	__buf_init
+ *	__get_buf
+ *	__buf_free
+ *	__reclaim_buf
+ * Internal
+ *	newbuf
+ */
+#if !defined(_WIN32) && !defined(_WINDOWS) && !defined(macintosh)
+#include <sys/param.h>
+#endif
+
+#include <errno.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef DEBUG
+#include <assert.h>
+#endif
+
+#include "mcom_db.h"
+#include "hash.h"
+#include "page.h"
+/* #include "extern.h" */
+
+static BUFHEAD *newbuf __P((HTAB *, uint32, BUFHEAD *));
+
+/* Unlink B from its place in the lru */
+#define BUF_REMOVE(B) { \
+	(B)->prev->next = (B)->next; \
+	(B)->next->prev = (B)->prev; \
+}
+
+/* Insert B after P */
+#define BUF_INSERT(B, P) { \
+	(B)->next = (P)->next; \
+	(B)->prev = (P); \
+	(P)->next = (B); \
+	(B)->next->prev = (B); \
+}
+
+#define	MRU	hashp->bufhead.next
+#define	LRU	hashp->bufhead.prev
+
+#define MRU_INSERT(B)	BUF_INSERT((B), &hashp->bufhead)
+#define LRU_INSERT(B)	BUF_INSERT((B), LRU)
+
+/*
+ * We are looking for a buffer with address "addr".  If prev_bp is NULL, then
+ * address is a bucket index.  If prev_bp is not NULL, then it points to the
+ * page previous to an overflow page that we are trying to find.
+ *
+ * CAVEAT:  The buffer header accessed via prev_bp's ovfl field may no longer
+ * be valid.  Therefore, you must always verify that its address matches the
+ * address you are seeking.
+ */
+extern BUFHEAD *
+__get_buf(HTAB *hashp, uint32 addr, BUFHEAD *prev_bp, int newpage)
+/* If prev_bp set, indicates a new overflow page. */
+{
+	register BUFHEAD *bp;
+	register uint32 is_disk_mask;
+	register int is_disk, segment_ndx = 0;
+	SEGMENT segp = 0;
+
+	is_disk = 0;
+	is_disk_mask = 0;
+	if (prev_bp) {
+		bp = prev_bp->ovfl;
+		if (!bp || (bp->addr != addr))
+			bp = NULL;
+		if (!newpage)
+			is_disk = BUF_DISK;
+	} else {
+		/* Grab buffer out of directory */
+		segment_ndx = addr & (hashp->SGSIZE - 1);
+
+		/* valid segment ensured by __call_hash() */
+		segp = hashp->dir[addr >> hashp->SSHIFT];
+#ifdef DEBUG
+		assert(segp != NULL);
+#endif  
+
+		bp = PTROF(segp[segment_ndx]);
+
+		is_disk_mask = ISDISK(segp[segment_ndx]);
+		is_disk = is_disk_mask || !hashp->new_file;
+	}
+
+	if (!bp) {
+		bp = newbuf(hashp, addr, prev_bp);
+		if (!bp)
+			return(NULL);
+		if(__get_page(hashp, bp->page, addr, !prev_bp, is_disk, 0))
+		  {
+			/* free bp and its page */
+			if(prev_bp)
+			  {
+				/* if prev_bp is set then the new page that
+				 * failed is hooked onto prev_bp as an overflow page.
+				 * if we don't remove the pointer to the bad page
+				 * we may try and access it later and we will die
+				 * horribly because it will have already been
+				 * free'd and overwritten with bogus data.
+				 */
+				prev_bp->ovfl = NULL;
+			  }
+			BUF_REMOVE(bp);
+			free(bp->page);
+			free(bp);
+			return (NULL);
+		  }
+
+		if (!prev_bp)			
+		  {
+#if 0
+			/* 16 bit windows and mac can't handle the
+			 * oring of the is disk flag.
+		 	 */			
+			segp[segment_ndx] =
+			    (BUFHEAD *)((ptrdiff_t)bp | is_disk_mask);
+#else   
+			/* set the is_disk thing inside the structure
+			 */
+			bp->is_disk = is_disk_mask;
+			segp[segment_ndx] = bp;
+#endif
+		  }
+	} else {  
+		BUF_REMOVE(bp);
+		MRU_INSERT(bp);                 
+	}
+	return (bp);
+}
+
+/*
+ * We need a buffer for this page. Either allocate one, or evict a resident
+ * one (if we have as many buffers as we're allowed) and put this one in.
+ *
+ * If newbuf finds an error (returning NULL), it also sets errno.
+ */
+static BUFHEAD *
+newbuf(HTAB *hashp, uint32 addr, BUFHEAD *prev_bp)
+{
+	register BUFHEAD *bp;		/* The buffer we're going to use */
+	register BUFHEAD *xbp;		/* Temp pointer */
+	register BUFHEAD *next_xbp;
+	SEGMENT segp;
+	int segment_ndx;
+	uint16 oaddr, *shortp;
+
+	oaddr = 0;
+	bp = LRU;
+	/*
+	 * If LRU buffer is pinned, the buffer pool is too small. We need to
+	 * allocate more buffers.
+	 */
+	if (hashp->nbufs || (bp->flags & BUF_PIN)) {
+		/* Allocate a new one */
+		if ((bp = (BUFHEAD *)malloc(sizeof(BUFHEAD))) == NULL)
+			return (NULL);
+
+		/* this memset is supposedly unnecessary but lets add
+		 * it anyways.
+		 */
+		memset(bp, 0xff, sizeof(BUFHEAD));
+
+		if ((bp->page = (char *)malloc((size_t)hashp->BSIZE)) == NULL) {
+			free(bp);
+			return (NULL);
+		}
+
+		/* this memset is supposedly unnecessary but lets add
+		 * it anyways.
+		 */
+		memset(bp->page, 0xff, (size_t)hashp->BSIZE);
+
+		if (hashp->nbufs)
+			hashp->nbufs--;
+	} else {
+		/* Kick someone out */
+		BUF_REMOVE(bp);
+		/*
+		 * If this is an overflow page with addr 0, it's already been
+		 * flushed back in an overflow chain and initialized.
+		 */
+		if ((bp->addr != 0) || (bp->flags & BUF_BUCKET)) {
+			/*
+			 * Set oaddr before __put_page so that you get it
+			 * before bytes are swapped.
+			 */
+			shortp = (uint16 *)bp->page;
+			if (shortp[0])
+			  {
+				if(shortp[0] > (hashp->BSIZE / sizeof(uint16)))
+				  {
+					return(NULL);
+				  }
+				oaddr = shortp[shortp[0] - 1];
+			  }
+			if ((bp->flags & BUF_MOD) && __put_page(hashp, bp->page,
+			    bp->addr, (int)IS_BUCKET(bp->flags), 0))
+				return (NULL);
+			/*
+			 * Update the pointer to this page (i.e. invalidate it).
+			 *
+			 * If this is a new file (i.e. we created it at open
+			 * time), make sure that we mark pages which have been
+			 * written to disk so we retrieve them from disk later,
+			 * rather than allocating new pages.
+			 */
+			if (IS_BUCKET(bp->flags)) {
+				segment_ndx = bp->addr & (hashp->SGSIZE - 1);
+				segp = hashp->dir[bp->addr >> hashp->SSHIFT];
+#ifdef DEBUG
+				assert(segp != NULL);
+#endif
+
+				if (hashp->new_file &&
+				    ((bp->flags & BUF_MOD) ||
+				    ISDISK(segp[segment_ndx])))
+					segp[segment_ndx] = (BUFHEAD *)BUF_DISK;
+				else
+					segp[segment_ndx] = NULL;
+			}
+			/*
+			 * Since overflow pages can only be access by means of
+			 * their bucket, free overflow pages associated with
+			 * this bucket.
+			 */
+			for (xbp = bp; xbp->ovfl;) {
+				next_xbp = xbp->ovfl;
+				xbp->ovfl = 0;
+				xbp = next_xbp;
+
+				/* leave pinned pages alone, we are still using
+				 * them. */
+				if (xbp->flags & BUF_PIN) {
+					continue;
+				}
+
+				/* Check that ovfl pointer is up date. */
+				if (IS_BUCKET(xbp->flags) ||
+				    (oaddr != xbp->addr))
+					break;
+
+				shortp = (uint16 *)xbp->page;
+				if (shortp[0])
+				  {
+					/* LJM is the number of reported
+					 * pages way too much?
+					 */
+					if(shortp[0] > hashp->BSIZE/sizeof(uint16))
+						return NULL;
+					/* set before __put_page */
+					oaddr = shortp[shortp[0] - 1];
+				  }
+				if ((xbp->flags & BUF_MOD) && __put_page(hashp,
+				    xbp->page, xbp->addr, 0, 0))
+					return (NULL);
+				xbp->addr = 0;
+				xbp->flags = 0;
+				BUF_REMOVE(xbp);
+				LRU_INSERT(xbp);
+			}
+		}
+	}
+
+	/* Now assign this buffer */
+	bp->addr = addr;
+#ifdef DEBUG1
+	(void)fprintf(stderr, "NEWBUF1: %d->ovfl was %d is now %d\n",
+	    bp->addr, (bp->ovfl ? bp->ovfl->addr : 0), 0);
+#endif
+	bp->ovfl = NULL;
+	if (prev_bp) {
+		/*
+		 * If prev_bp is set, this is an overflow page, hook it in to
+		 * the buffer overflow links.
+		 */
+#ifdef DEBUG1
+		(void)fprintf(stderr, "NEWBUF2: %d->ovfl was %d is now %d\n",
+		    prev_bp->addr, (prev_bp->ovfl ? bp->ovfl->addr : 0),
+		    (bp ? bp->addr : 0));
+#endif
+		prev_bp->ovfl = bp;
+		bp->flags = 0;
+	} else
+		bp->flags = BUF_BUCKET;
+	MRU_INSERT(bp);
+	return (bp);
+}
+
+extern void __buf_init(HTAB *hashp, int32 nbytes)
+{
+	BUFHEAD *bfp;
+	int npages;
+
+	bfp = &(hashp->bufhead);
+	npages = (nbytes + hashp->BSIZE - 1) >> hashp->BSHIFT;
+	npages = PR_MAX(npages, MIN_BUFFERS);
+
+	hashp->nbufs = npages;
+	bfp->next = bfp;
+	bfp->prev = bfp;
+	/*
+	 * This space is calloc'd so these are already null.
+	 *
+	 * bfp->ovfl = NULL;
+	 * bfp->flags = 0;
+	 * bfp->page = NULL;
+	 * bfp->addr = 0;
+	 */
+}
+
+extern int
+__buf_free(HTAB *hashp, int do_free, int to_disk)
+{
+	BUFHEAD *bp;
+	int status = -1;
+
+	/* Need to make sure that buffer manager has been initialized */
+	if (!LRU)
+		return (0);
+	for (bp = LRU; bp != &hashp->bufhead;) {
+		/* Check that the buffer is valid */
+		if (bp->addr || IS_BUCKET(bp->flags)) {
+			if (to_disk && (bp->flags & BUF_MOD) &&
+			    (status = __put_page(hashp, bp->page,
+			    bp->addr, IS_BUCKET(bp->flags), 0))) {
+			  
+				if (do_free) {
+					if (bp->page)
+						free(bp->page);
+					BUF_REMOVE(bp);
+					free(bp);
+				}
+				
+				return (status);
+			}
+		}
+		/* Check if we are freeing stuff */
+		if (do_free) {
+			if (bp->page)
+				free(bp->page);
+			BUF_REMOVE(bp);
+			free(bp);
+			bp = LRU;
+		} else
+			bp = bp->prev;
+	}
+	return (0);
+}
+
+extern void
+__reclaim_buf(HTAB *hashp, BUFHEAD *bp)
+{
+	bp->ovfl = 0;
+	bp->addr = 0;
+	bp->flags = 0;
+	BUF_REMOVE(bp);
+	LRU_INSERT(bp);
+}
diff -r a945361df361 -r 150b72113545 nss/lib/dbm/src/manifest.mn
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nss/lib/dbm/src/manifest.mn	Tue Aug 05 18:32:02 2014 +0200
@@ -0,0 +1,28 @@
+#! gmake
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+CORE_DEPTH = ../../..
+
+MODULE = dbm
+
+#
+# memmove.c, snprintf.c, and strerror.c are not in CSRCS because
+# the Standard C Library has memmove and strerror and DBM is not
+# using snprintf.
+#
+
+CSRCS = db.c	   \
+	h_bigkey.c \
+	h_func.c   \
+	h_log2.c   \
+	h_page.c   \
+	hash.c	   \
+	hash_buf.c \
+	mktemp.c   \
+	dirent.c   \
+	$(NULL)
+
+LIBRARY_NAME = dbm
diff -r a945361df361 -r 150b72113545 nss/lib/dbm/src/memmove.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nss/lib/dbm/src/memmove.c	Tue Aug 05 18:32:02 2014 +0200
@@ -0,0 +1,146 @@
+#if defined(__sun) && !defined(__SVR4)
+/*-
+ * Copyright (c) 1990, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. ***REMOVED*** - see 
+ *    ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)bcopy.c	8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+#ifdef HAVE_SYS_CDEFS_H
+#include <sys/cdefs.h>
+#else
+#include "cdefs.h"
+#endif
+#include <string.h>
+
+/*
+ * sizeof(word) MUST BE A POWER OF TWO
+ * SO THAT wmask BELOW IS ALL ONES
+ */
+typedef	int word;		/* "word" used for optimal copy speed */
+
+#define	wsize	sizeof(word)
+#define	wmask	(wsize - 1)
+
+/*
+ * Copy a block of memory, handling overlap.
+ * This is the routine that actually implements
+ * (the portable versions of) bcopy, memcpy, and memmove.
+ */
+#ifdef MEMCOPY
+void *
+memcpy(dst0, src0, length)
+#else
+#ifdef MEMMOVE
+void *
+memmove(dst0, src0, length)
+#else
+void
+bcopy(src0, dst0, length)
+#endif
+#endif
+	void *dst0;
+	const void *src0;
+	register size_t length;
+{
+	register char *dst = dst0;
+	register const char *src = src0;
+	register size_t t;
+
+	if (length == 0 || dst == src)		/* nothing to do */
+		goto done;
+
+	/*
+	 * Macros: loop-t-times; and loop-t-times, t>0
+	 */
+#define	TLOOP(s) if (t) TLOOP1(s)
+#define	TLOOP1(s) do { s; } while (--t)
+
+	if ((unsigned long)dst < (unsigned long)src) {
+		/*
+		 * Copy forward.
+		 */
+		t = (int)src;	/* only need low bits */
+		if ((t | (int)dst) & wmask) {
+			/*
+			 * Try to align operands.  This cannot be done
+			 * unless the low bits match.
+			 */
+			if ((t ^ (int)dst) & wmask || length < wsize)
+				t = length;
+			else
+				t = wsize - (t & wmask);
+			length -= t;
+			TLOOP1(*dst++ = *src++);
+		}
+		/*
+		 * Copy whole words, then mop up any trailing bytes.
+		 */
+		t = length / wsize;
+		TLOOP(*(word *)dst = *(word *)src; src += wsize; dst += wsize);
+		t = length & wmask;
+		TLOOP(*dst++ = *src++);
+	} else {
+		/*
+		 * Copy backwards.  Otherwise essentially the same.
+		 * Alignment works as before, except that it takes
+		 * (t&wmask) bytes to align, not wsize-(t&wmask).
+		 */
+		src += length;
+		dst += length;
+		t = (int)src;
+		if ((t | (int)dst) & wmask) {
+			if ((t ^ (int)dst) & wmask || length <= wsize)
+				t = length;
+			else
+				t &= wmask;
+			length -= t;
+			TLOOP1(*--dst = *--src);
+		}
+		t = length / wsize;
+		TLOOP(src -= wsize; dst -= wsize; *(word *)dst = *(word *)src);
+		t = length & wmask;
+		TLOOP(*--dst = *--src);
+	}
+done:
+#if defined(MEMCOPY) || defined(MEMMOVE)
+	return (dst0);
+#else
+	return;
+#endif
+}
+#endif /* no __sgi */
+
+/* Some compilers don't like an empty source file. */
+static int dummy = 0;
diff -r a945361df361 -r 150b72113545 nss/lib/dbm/src/mktemp.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nss/lib/dbm/src/mktemp.c	Tue Aug 05 18:32:02 2014 +0200
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 1987, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. ***REMOVED*** - see 
+ *    ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)mktemp.c	8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+#ifdef macintosh
+#include <unix.h>
+#else
+#include <sys/types.h>
+#include <sys/stat.h>
+#endif
+#include <fcntl.h>
+#include <errno.h>
+#include <stdio.h>
+#include <ctype.h>
+#include "mcom_db.h"
+
+#ifndef _WINDOWS
+#include <unistd.h>
+#endif
+
+#ifdef _WINDOWS
+#include <process.h>
+#include "winfile.h"
+#endif
+
+static int _gettemp(char *path, register int *doopen, int extraFlags);
+
+int
+mkstemp(char *path)
+{
+#ifdef XP_OS2
+	FILE *temp = tmpfile();
+
+	return (temp ? fileno(temp) : -1);
+#else
+	int fd;
+
+	return (_gettemp(path, &fd, 0) ? fd : -1);
+#endif
+}
+
+int
+mkstempflags(char *path, int extraFlags)
+{
+	int fd;
+
+	return (_gettemp(path, &fd, extraFlags) ? fd : -1);
+}
+
+/* NB: This routine modifies its input string, and does not always restore it.
+** returns 1 on success, 0 on failure.
+*/
+static int 
+_gettemp(char *path, register int *doopen, int extraFlags)
+{    
+	register char *start, *trv;
+	struct stat sbuf;
+	unsigned int pid;
+
+	pid = getpid();
+	for (trv = path; *trv; ++trv);		/* extra X's get set to 0's */
+	while (*--trv == 'X') {
+		*trv = (pid % 10) + '0';
+		pid /= 10;
+	}
+
+	/*
+	 * check the target directory; if you have six X's and it
+	 * doesn't exist this runs for a *very* long time.
+	 */
+	for (start = trv + 1;; --trv) {
+		char saved;
+		if (trv <= path)
+			break;
+		saved = *trv;
+		if (saved == '/' || saved == '\\') {
+			int rv;
+			*trv = '\0';
+			rv = stat(path, &sbuf);
+			*trv = saved;
+			if (rv)
+				return(0);
+			if (!S_ISDIR(sbuf.st_mode)) {
+				errno = ENOTDIR;
+				return(0);
+			}
+			break;
+		}
+	}
+
+	for (;;) {
+		if (doopen) {
+			if ((*doopen =
+			    open(path, O_CREAT|O_EXCL|O_RDWR|extraFlags, 0600)) >= 0)
+				return(1);
+			if (errno != EEXIST)
+				return(0);
+		}
+		else if (stat(path, &sbuf))
+			return(errno == ENOENT ? 1 : 0);
+
+		/* tricky little algorithm for backward compatibility */
+		for (trv = start;;) {
+			if (!*trv)
+				return(0);
+			if (*trv == 'z')
+				*trv++ = 'a';
+			else {
+				if (isdigit(*trv))
+					*trv = 'a';
+				else
+					++*trv;
+				break;
+			}
+		}
+	}
+	/*NOTREACHED*/
+}
diff -r a945361df361 -r 150b72113545 nss/lib/dbm/src/snprintf.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nss/lib/dbm/src/snprintf.c	Tue Aug 05 18:32:02 2014 +0200
@@ -0,0 +1,73 @@
+#ifndef HAVE_SNPRINTF
+
+#include <sys/types.h>
+#include <stddef.h>
+#include <stdio.h>
+
+#ifdef HAVE_SYS_CDEFS_H
+#include <sys/cdefs.h>
+#else
+#include "cdefs.h"
+#endif
+
+#include "prtypes.h"
+
+#include <ncompat.h>
+
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+int
+#ifdef __STDC__
+snprintf(char *str, size_t n, const char *fmt, ...)
+#else
+snprintf(str, n, fmt, va_alist)
+	char *str;
+	size_t n;
+	const char *fmt;
+	va_dcl
+#endif
+{
+	va_list ap;
+#ifdef VSPRINTF_CHARSTAR
+	char *rp;
+#else
+	int rval;
+#endif
+#ifdef __STDC__
+	va_start(ap, fmt);
+#else
+	va_start(ap);
+#endif
+#ifdef VSPRINTF_CHARSTAR
+	rp = vsprintf(str, fmt, ap);
+	va_end(ap);
+	return (strlen(rp));
+#else
+	rval = vsprintf(str, fmt, ap);
+	va_end(ap);
+	return (rval);
+#endif
+}
+
+int
+vsnprintf(str, n, fmt, ap)
+	char *str;
+	size_t n;
+	const char *fmt;
+	va_list ap;
+{
+#ifdef VSPRINTF_CHARSTAR
+	return (strlen(vsprintf(str, fmt, ap)));
+#else
+	return (vsprintf(str, fmt, ap));
+#endif
+}
+
+#endif /* HAVE_SNPRINTF */
+
+/* Some compilers don't like an empty source file. */
+static int dummy = 0;
diff -r a945361df361 -r 150b72113545 nss/lib/dbm/src/strerror.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nss/lib/dbm/src/strerror.c	Tue Aug 05 18:32:02 2014 +0200
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 1988, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. ***REMOVED*** - see 
+ *    ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)strerror.c	8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+#include <string.h>
+
+#ifdef _DLL
+#define sys_nerr    (*_sys_nerr_dll)
+#endif
+
+#ifndef HAVE_STRERROR
+#ifndef _AFXDLL
+char *
+strerror(num)
+	int num;
+{
+	extern int sys_nerr;
+	extern char *sys_errlist[];
+#define	UPREFIX	"Unknown error: "
+	static char ebuf[40] = UPREFIX;		/* 64-bit number + slop */
+	register unsigned int errnum;
+	register char *p, *t;
+	char tmp[40];
+
+	errnum = num;				/* convert to unsigned */
+	if (errnum < sys_nerr)
+		return(sys_errlist[errnum]);
+
+	/* Do this by hand, so we don't include stdio(3). */
+	t = tmp;
+	do {
+		*t++ = "0123456789"[errnum % 10];
+	} while (errnum /= 10);
+	for (p = ebuf + sizeof(UPREFIX) - 1;;) {
+		*p++ = *--t;
+		if (t <= tmp)
+			break;
+	}
+	return(ebuf);
+}
+
+#endif
+#endif /* !HAVE_STRERROR */
diff -r a945361df361 -r 150b72113545 nss/lib/dbm/tests/Makefile
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nss/lib/dbm/tests/Makefile	Tue Aug 05 18:32:02 2014 +0200
@@ -0,0 +1,41 @@
+#! gmake
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+DEPTH		= ../../..
+CORE_DEPTH	= ../../..
+
+VPATH		= $(CORE_DEPTH)/../dbm/tests
+
+MODULE		= dbm
+
+CSRCS		= lots.c
+
+PROGRAM		= lots
+
+include $(DEPTH)/coreconf/config.mk
+
+include $(DEPTH)/dbm/config/config.mk
+
+ifeq (,$(filter-out WIN%,$(OS_TARGET))) 
+LIBDBM		= ../src/$(PLATFORM)/dbm$(STATIC_LIB_SUFFIX)
+else
+LIBDBM		= ../src/$(PLATFORM)/libdbm$(STATIC_LIB_SUFFIX)
+endif
+
+INCLUDES	+= -I$(CORE_DEPTH)/../dbm/include
+
+LDFLAGS		= $(LDOPTS) $(LIBDBM)
+
+include $(DEPTH)/coreconf/rules.mk
+
+lots.pure: lots
+	purify $(CC) -o lots.pure $(CFLAGS) $(OBJS) $(MYLIBS)
+
+crash: crash.o $(MYLIBS)
+	$(CC) -o crash $(CFLAGS) $^
+
+crash.pure: crash.o $(MYLIBS)
+	purify $(CC) -o crash.pure $(CFLAGS) $^
+
diff -r a945361df361 -r 150b72113545 nss/lib/dbm/tests/dbmtest.pkg
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nss/lib/dbm/tests/dbmtest.pkg	Tue Aug 05 18:32:02 2014 +0200
@@ -0,0 +1,2 @@
+[gecko-tests]
+dist/bin/lots at BINS@
diff -r a945361df361 -r 150b72113545 nss/lib/dbm/tests/lots.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nss/lib/dbm/tests/lots.c	Tue Aug 05 18:32:02 2014 +0200
@@ -0,0 +1,606 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* use sequental numbers printed to strings
+ * to store lots and lots of entries in the
+ * database.
+ *
+ * Start with 100 entries, put them and then
+ * read them out.  Then delete the first
+ * half and verify that all of the first half
+ * is gone and then verify that the second
+ * half is still there.
+ * Then add the first half back and verify
+ * again.  Then delete the middle third
+ * and verify again.
+ * Then increase the size by 1000 and do
+ * the whole add delete thing again.
+ *
+ * The data for each object is the number string translated
+ * to hex and replicated a random number of times.  The
+ * number of times that the data is replicated is the first
+ * int32 in the data.
+ */
+
+#include <stdio.h>
+
+#include <stdlib.h>
+#ifdef STDC_HEADERS
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+#ifdef HAVE_MEMORY_H
+#include <memory.h>
+#endif
+#include <string.h>
+#include <assert.h>
+#include "mcom_db.h"
+
+DB *database=0;
+int MsgPriority=5;
+
+#if defined(_WINDOWS) && !defined(WIN32)
+#define int32 long
+#define uint32 unsigned long
+#else
+#define int32 int
+#define uint32 unsigned int
+#endif
+
+typedef enum {
+USE_LARGE_KEY,
+USE_SMALL_KEY
+} key_type_enum;
+
+#define TraceMe(priority, msg) 		\
+	do {							\
+		if(priority <= MsgPriority)	\
+		  {							\
+			ReportStatus msg;		\
+		  }							\
+	} while(0)
+
+int
+ReportStatus(char *string, ...)
+{
+    va_list args;
+
+#ifdef STDC_HEADERS
+    va_start(args, string);
+#else
+    va_start(args);
+#endif
+    vfprintf(stderr, string, args);
+    va_end(args);
+
+	fprintf (stderr, "\n");
+
+	return(0);
+}
+
+int
+ReportError(char *string, ...)
+{
+    va_list args;
+
+#ifdef STDC_HEADERS
+    va_start(args, string);
+#else
+    va_start(args);
+#endif
+	fprintf (stderr, "\n	");
+    vfprintf(stderr, string, args);
+	fprintf (stderr, "\n");
+    va_end(args);
+
+	return(0);
+}
+
+DBT * MakeLargeKey(int32 num)
+{
+	int32 low_bits;
+	static DBT rv;
+	static char *string_rv=0;
+	int rep_char;
+	size_t size;
+
+	if(string_rv)
+		free(string_rv);
+
+	/* generate a really large text key derived from
+	 * an int32
+	 */
+	low_bits = (num % 10000) + 1;
+
+	/* get the repeat char from the low 26 */
+	rep_char = (char) ((low_bits % 26) + 'a');
+
+	/* malloc a string low_bits wide */
+	size = low_bits*sizeof(char);
+	string_rv = (char *)malloc(size);
+
+	memset(string_rv, rep_char, size);
+
+	rv.data = string_rv;
+	rv.size = size;
+
+	return(&rv);
+}
+
+DBT * MakeSmallKey(int32 num)
+{
+	static DBT rv;
+	static char data_string[64];
+
+	rv.data = data_string;
+
+	sprintf(data_string, "%ld", (long)num);
+	rv.size = strlen(data_string);
+
+	return(&rv);
+
+}
+
+DBT * GenKey(int32 num, key_type_enum key_type)
+{
+	DBT *key;
+
+	switch(key_type)
+	  {
+		case USE_LARGE_KEY:
+			key = MakeLargeKey(num);
+			break;
+		case USE_SMALL_KEY:
+			key = MakeSmallKey(num);
+			break;
+		default:
+			abort();
+			break;
+	  }
+
+	return(key);
+}
+
+int
+SeqDatabase()
+{
+	int status;
+	DBT key, data;
+
+	ReportStatus("SEQuencing through database...");
+
+	/* seq through the whole database */
+    if(!(status = (*database->seq)(database, &key, &data, R_FIRST)))
+	  {
+        while(!(status = (database->seq) (database, &key, &data, R_NEXT)))
+			; /* null body */
+	  }
+
+	if(status < 0)
+		ReportError("Error seq'ing database");
+
+	return(status);
+}
+
+int 
+VerifyData(DBT *data, int32 num, key_type_enum key_type)
+{
+	int32 count, compare_num;
+	size_t size;
+	int32 *int32_array;
+
+	/* The first int32 is count 
+	 * The other n entries should
+	 * all equal num
+	 */
+	if(data->size < sizeof(int32))
+	  {
+		ReportError("Data size corrupted");
+		return -1;
+	  }
+
+	memcpy(&count, data->data, sizeof(int32));
+
+	size = sizeof(int32)*(count+1);
+
+	if(size != data->size)
+	  {
+		ReportError("Data size corrupted");
+		return -1;
+	  }
+
+	int32_array = (int32*)data->data;
+
+	for(;count > 0; count--)
+	  {
+		memcpy(&compare_num, &int32_array[count], sizeof(int32));
+
+		if(compare_num != num)
+	      {
+		    ReportError("Data corrupted");
+		    return -1;
+	      }
+	  }
+
+	return(0);
+}
+
+
+/* verify that a range of number strings exist
+ * or don't exist. And that the data is valid
+ */
+#define SHOULD_EXIST 1
+#define SHOULD_NOT_EXIST 0
+int
+VerifyRange(int32 low, int32 high, int32 should_exist, key_type_enum key_type)
+{
+	DBT *key, data;
+	int32 num;
+	int status;
+
+	TraceMe(1, ("Verifying: %ld to %ld, using %s keys", 
+		    low, high, key_type == USE_SMALL_KEY ? "SMALL" : "LARGE"));
+
+	for(num = low; num <= high; num++)
+	  {
+
+		key = GenKey(num, key_type);
+
+		status = (*database->get)(database, key, &data, 0);
+
+		if(status == 0)
+		  {
+			/* got the item */
+			if(!should_exist)
+			  {
+				ReportError("Item exists but shouldn't: %ld", num);
+			  }
+			else
+			  {
+			    /* else verify the data */
+			    VerifyData(&data, num, key_type);
+			  }
+		  }
+		else if(status > 0)
+		  {
+			/* item not found */
+			if(should_exist)
+			  {
+				ReportError("Item not found but should be: %ld", num);
+			  }
+		  }
+		else
+		  {
+			/* database error */
+			ReportError("Database error");
+			return(-1);
+		  }
+			
+	  }
+
+	TraceMe(1, ("Correctly verified: %ld to %ld", low, high));
+
+	return(0);
+
+}
+
+DBT *
+GenData(int32 num)
+{
+	int32 n;
+	static DBT *data=0;
+	int32 *int32_array;
+	size_t size;
+
+	if(!data)
+	  {
+		data = (DBT*)malloc(sizeof(DBT));
+		data->size = 0;
+		data->data = 0;
+	  }
+	else if(data->data)
+	  {
+		free(data->data);
+	  }
+
+	n = rand();
+
+	n = n % 512;  /* bound to a 2K size */
+
+	
+	size = sizeof(int32)*(n+1);
+	int32_array = (int32 *) malloc(size);
+
+	memcpy(&int32_array[0], &n, sizeof(int32));
+
+	for(; n > 0; n--)
+	  {
+		memcpy(&int32_array[n], &num, sizeof(int32));
+	  }
+
+	data->data = (void*)int32_array;
+	data->size = size;
+
+	return(data);
+}
+
+#define ADD_RANGE 1
+#define DELETE_RANGE 2
+
+int
+AddOrDelRange(int32 low, int32 high, int action, key_type_enum key_type)
+{
+	DBT *key, *data;
+#if 0 /* only do this if your really analy checking the puts */
+	DBT tmp_data;
+#endif 
+	int32 num;
+	int status;
+
+	if(action != ADD_RANGE && action != DELETE_RANGE)
+		assert(0);
+
+	if(action == ADD_RANGE)
+	  {
+		TraceMe(1, ("Adding: %ld to %ld: %s keys", low, high,
+		    	key_type == USE_SMALL_KEY ? "SMALL" : "LARGE"));
+	  }
+	else
+	  {
+		TraceMe(1, ("Deleting: %ld to %ld: %s keys", low, high,
+		    	key_type == USE_SMALL_KEY ? "SMALL" : "LARGE"));
+	  }
+
+	for(num = low; num <= high; num++)
+	  {
+
+		key = GenKey(num, key_type);
+
+		if(action == ADD_RANGE)
+		  {
+			data = GenData(num);
+			status = (*database->put)(database, key, data, 0);
+		  }
+		else
+		  {
+			status = (*database->del)(database, key, 0);
+		  }
+
+		if(status < 0)
+		  {
+			ReportError("Database error %s item: %ld",
+							action == ADD_RANGE ? "ADDING" : "DELETING", 
+							num);
+		  }
+		else if(status > 0)
+		  {
+			ReportError("Could not %s item: %ld", 
+							action == ADD_RANGE ? "ADD" : "DELETE", 
+							num);
+		  }
+		else if(action == ADD_RANGE)
+		  {
+#define SYNC_EVERY_TIME
+#ifdef SYNC_EVERY_TIME
+			status = (*database->sync)(database, 0);
+			if(status != 0)
+				ReportError("Database error syncing after add");
+#endif
+
+#if 0 /* only do this if your really analy checking the puts */
+	 
+			/* make sure we can still get it
+			 */
+			status = (*database->get)(database, key, &tmp_data, 0);
+
+			if(status != 0)
+			  {
+				ReportError("Database error checking item just added: %d",
+							num);
+			  }
+			else
+			  {
+				/* now verify that none of the ones we already
+				 * put in have disappeared
+				 */
+				VerifyRange(low, num, SHOULD_EXIST, key_type);
+			  }
+#endif
+			
+		  }
+	  }
+
+
+	if(action == ADD_RANGE)
+	  {
+		TraceMe(1, ("Successfully added: %ld to %ld", low, high));
+	  }
+	else
+	  {
+		TraceMe(1, ("Successfully deleted: %ld to %ld", low, high));
+	  }
+
+	return(0);
+}
+
+int
+TestRange(int32 low, int32 range, key_type_enum key_type)
+{
+	int status; int32 low_of_range1, high_of_range1; int32 low_of_range2, high_of_range2;
+	int32 low_of_range3, high_of_range3;
+
+	status = AddOrDelRange(low, low+range, ADD_RANGE, key_type);
+	status = VerifyRange(low, low+range, SHOULD_EXIST, key_type);
+
+	TraceMe(1, ("Finished with sub test 1"));
+
+	SeqDatabase();
+
+	low_of_range1 = low;
+	high_of_range1 = low+(range/2);
+	low_of_range2 = high_of_range1+1;
+	high_of_range2 = low+range;
+	status = AddOrDelRange(low_of_range1, high_of_range1, DELETE_RANGE, key_type);
+	status = VerifyRange(low_of_range1, high_of_range1, SHOULD_NOT_EXIST, key_type);
+	status = VerifyRange(low_of_range2, low_of_range2, SHOULD_EXIST, key_type);
+
+	TraceMe(1, ("Finished with sub test 2"));
+
+	SeqDatabase();
+
+	status = AddOrDelRange(low_of_range1, high_of_range1, ADD_RANGE, key_type);
+	/* the whole thing should exist now */
+	status = VerifyRange(low, low+range, SHOULD_EXIST, key_type);
+
+	TraceMe(1, ("Finished with sub test 3"));
+
+	SeqDatabase();
+
+	status = AddOrDelRange(low_of_range2, high_of_range2, DELETE_RANGE, key_type);
+	status = VerifyRange(low_of_range1, high_of_range1, SHOULD_EXIST, key_type);
+	status = VerifyRange(low_of_range2, high_of_range2, SHOULD_NOT_EXIST, key_type);
+
+	TraceMe(1, ("Finished with sub test 4"));
+
+	SeqDatabase();
+
+	status = AddOrDelRange(low_of_range2, high_of_range2, ADD_RANGE, key_type);
+	/* the whole thing should exist now */
+	status = VerifyRange(low, low+range, SHOULD_EXIST, key_type);
+
+	TraceMe(1, ("Finished with sub test 5"));
+
+	SeqDatabase();
+
+	low_of_range1 = low;
+	high_of_range1 = low+(range/3);
+	low_of_range2 = high_of_range1+1;
+	high_of_range2 = high_of_range1+(range/3);
+	low_of_range3 = high_of_range2+1;
+	high_of_range3 = low+range;
+	/* delete range 2 */
+	status = AddOrDelRange(low_of_range2, high_of_range2, DELETE_RANGE, key_type);
+	status = VerifyRange(low_of_range1, high_of_range1, SHOULD_EXIST, key_type);
+	status = VerifyRange(low_of_range2, low_of_range2, SHOULD_NOT_EXIST, key_type);
+	status = VerifyRange(low_of_range3, low_of_range2, SHOULD_EXIST, key_type);
+
+	TraceMe(1, ("Finished with sub test 6"));
+
+	SeqDatabase();
+
+	status = AddOrDelRange(low_of_range2, high_of_range2, ADD_RANGE, key_type);
+	/* the whole thing should exist now */
+	status = VerifyRange(low, low+range, SHOULD_EXIST, key_type);
+
+	TraceMe(1, ("Finished with sub test 7"));
+
+	return(0);
+}
+
+#define START_RANGE 109876
+int
+main(int argc, char **argv)
+{
+	int32 i, j=0;
+    int quick_exit = 0;
+    int large_keys = 0;
+    HASHINFO hash_info = {
+        16*1024,
+        0,
+        0,
+        0,
+        0,
+        0};
+
+
+    if(argc > 1)
+      {
+        while(argc > 1)
+	  {
+            if(!strcmp(argv[argc-1], "-quick"))
+                quick_exit = 1;
+            else if(!strcmp(argv[argc-1], "-large"))
+			  {
+                large_keys = 1;
+			  }
+            argc--;
+          }
+      }
+
+	database = dbopen("test.db", O_RDWR | O_CREAT, 0644, DB_HASH, &hash_info);
+
+	if(!database)
+	  {
+		ReportError("Could not open database");
+#ifdef unix
+		perror("");
+#endif
+		exit(1);
+	  }
+
+	if(quick_exit)
+	  {
+		if(large_keys)
+			TestRange(START_RANGE, 200, USE_LARGE_KEY);
+		else
+			TestRange(START_RANGE, 200, USE_SMALL_KEY);
+
+		(*database->sync)(database, 0);
+		(*database->close)(database);
+        exit(0);
+	  }
+
+	for(i=100; i < 10000000; i+=200)
+	  {
+		if(1 || j)
+		  {
+			TestRange(START_RANGE, i, USE_LARGE_KEY);
+			j = 0;
+		  }
+		else
+		  {
+			TestRange(START_RANGE, i, USE_SMALL_KEY);
+			j = 1;
+		  }
+
+		if(1 == rand() % 3)
+		  {
+			(*database->sync)(database, 0);
+		  }
+		
+		if(1 == rand() % 3)
+	 	  {
+			/* close and reopen */
+			(*database->close)(database);
+			database = dbopen("test.db", O_RDWR | O_CREAT, 0644, DB_HASH, 0);
+			if(!database)
+	  		{
+				ReportError("Could not reopen database");
+#ifdef unix
+				perror("");
+#endif
+				exit(1);
+	  		}
+	 	  }
+		else
+		  {
+			/* reopen database without closeing the other */
+			database = dbopen("test.db", O_RDWR | O_CREAT, 0644, DB_HASH, 0);
+			if(!database)
+	  		{
+				ReportError("Could not reopen database "
+							"after not closing the other");
+#ifdef unix
+				perror("");
+#endif
+				exit(1);
+	  		}
+	 	  }
+	  }
+
+	return(0);
+}
diff -r a945361df361 -r 150b72113545 nss/lib/softoken/legacydb/Makefile
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nss/lib/softoken/legacydb/Makefile	Tue Aug 05 18:32:02 2014 +0200
@@ -0,0 +1,50 @@
+#! gmake
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#######################################################################
+# (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
+
+# indicates dependency on freebl static lib
+$(SHARED_LIBRARY): $(CRYPTOLIB)
diff -r a945361df361 -r 150b72113545 nss/lib/softoken/legacydb/cdbhdl.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nss/lib/softoken/legacydb/cdbhdl.h	Tue Aug 05 18:32:02 2014 +0200
@@ -0,0 +1,51 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+/*
+ * cdbhdl.h - certificate database handle
+ *   private to the certdb module
+ */
+#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 -r a945361df361 -r 150b72113545 nss/lib/softoken/legacydb/config.mk
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nss/lib/softoken/legacydb/config.mk	Tue Aug 05 18:32:02 2014 +0200
@@ -0,0 +1,57 @@
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+# $(PROGRAM) has explicit dependencies on $(EXTRA_LIBS)
+CRYPTOLIB=$(DIST)/lib/$(LIB_PREFIX)freebl.$(LIB_SUFFIX)
+
+EXTRA_LIBS += \
+	$(CRYPTOLIB) \
+	$(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$(DIST)/lib \
+	-L$(NSSUTIL_LIB_DIR) \
+	-lnssutil3 \
+	-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 \
+	$(DIST)/lib/nssutil3.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$(DIST)/lib \
+	-L$(NSSUTIL_LIB_DIR) \
+	-lnssutil3 \
+	-L$(NSPR_LIB_DIR) \
+	-lplc4 \
+	-lplds4 \
+	-lnspr4 \
+	$(NULL)
+endif
diff -r a945361df361 -r 150b72113545 nss/lib/softoken/legacydb/dbmshim.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nss/lib/softoken/legacydb/dbmshim.c	Tue Aug 05 18:32:02 2014 +0200
@@ -0,0 +1,613 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * Berkeley DB 1.85 Shim code to handle blobs.
+ */
+#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 -r a945361df361 -r 150b72113545 nss/lib/softoken/legacydb/keydb.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nss/lib/softoken/legacydb/keydb.c	Tue Aug 05 18:32:02 2014 +0200
@@ -0,0 +1,2267 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "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 "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
+
+SEC_ASN1_MKSUB(SECOID_AlgorithmIDTemplate)
+
+const SEC_ASN1Template nsslowkey_EncryptedPrivateKeyInfoTemplate[] = {
+    { SEC_ASN1_SEQUENCE,
+	0, NULL, sizeof(NSSLOWKEYEncryptedPrivateKeyInfo) },
+    { SEC_ASN1_INLINE | SEC_ASN1_XTRN,
+	offsetof(NSSLOWKEYEncryptedPrivateKeyInfo,algorithm),
+	SEC_ASN1_SUB(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);
+
+/*
+ * 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;
+    handle->lock = PZ_NewLock(nssILockKeyDB);
+
+    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);
+	}
+	if (handle->lock != NULL) {
+	    SKIP_AFTER_FORK(PZ_DestroyLock(handle->lock));
+	}
+	    
+	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;
+#ifndef NSS_DISABLE_ECC
+      case NSSLOWKEYECKey:
+	namekey.data = pubkey->u.ec.publicValue.data;
+	namekey.size = pubkey->u.ec.publicValue.len;
+	break;
+#endif /* NSS_DISABLE_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);
+	}
+    }
+    lg_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 | SEC_ASN1_XTRN,
+        offsetof(LGEncryptedDataInfo,algorithm),
+        SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
+    { SEC_ASN1_OCTET_STRING,
+        offsetof(LGEncryptedDataInfo,encryptedData) },
+    { 0 }
+};
+
+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.type = siBuffer;  /* encode as signed integer */
+    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;
+    param.iter.type = siBuffer;  /* decode as signed integer */
+
+    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;
+    }
+    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,NSSLOWKEYPasswordEntry *entry)
+{
+    DBT checkkey; /*, checkdata; */
+    NSSLOWKEYDBKey *dbkey = NULL;
+    SECItem   *global_salt = NULL; 
+    SECItem   *item = NULL; 
+    SECItem   entryData, oid;
+    SECItem   none = { siBuffer, NULL, 0 };
+    SECStatus rv = SECFailure;
+    SECOidTag algorithm;
+
+    if (handle == NULL) {
+	/* PORT_SetError */
+	return(SECFailure);
+    }
+
+    global_salt = GetKeyDBGlobalSalt(handle);
+    if (!global_salt) {
+	global_salt = &none;
+    }
+    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.type = siBuffer;
+    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 != &none) {
+	SECITEM_FreeItem(global_salt,PR_TRUE);
+    }
+    return rv;
+}
+
+/*
+ * check to see if the user has a password
+ */
+static SECStatus
+nsslowkey_PutPWCheckEntry(NSSLOWKEYDBHandle *handle,NSSLOWKEYPasswordEntry *entry)
+{
+    DBT checkkey;
+    NSSLOWKEYDBKey *dbkey = NULL;
+    SECItem   *item = NULL; 
+    SECItem   salt; 
+    SECOidTag algid;
+    SECStatus rv = SECFailure;
+    PLArenaPool *arena;
+    int ret;
+
+    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;
+    }
+    ret = keydb_Sync(handle, 0);
+    if ( ret ) {
+	rv = SECFailure;
+	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;
+#ifndef NSS_DISABLE_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:
+        lg_prepare_low_rsa_priv_key_for_asn1(pk);
+	dummy = SEC_ASN1EncodeItem(temparena, &(pki->privateKey), pk, 
+				   lg_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:
+        lg_prepare_low_dsa_priv_key_for_asn1(pk);
+	dummy = SEC_ASN1EncodeItem(temparena, &(pki->privateKey), pk,
+				   lg_nsslowkey_DSAPrivateKeyTemplate);
+	if (dummy == NULL) {
+	    rv = SECFailure;
+	    goto loser;
+	}
+	
+        lg_prepare_low_pqg_params_for_asn1(&pk->u.dsa.params);
+	dummy = SEC_ASN1EncodeItem(temparena, NULL, &pk->u.dsa.params,
+				   lg_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:
+        lg_prepare_low_dh_priv_key_for_asn1(pk);
+	dummy = SEC_ASN1EncodeItem(temparena, &(pki->privateKey), pk,
+				   lg_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;
+#ifndef NSS_DISABLE_ECC
+      case NSSLOWKEYECKey:
+	lg_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,
+				   lg_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_DISABLE_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, 
+	lg_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;
+#ifndef NSS_DISABLE_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, 
+	    lg_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;
+		lg_prepare_low_rsa_priv_key_for_asn1(pk);
+                if (SECSuccess != SECITEM_CopyItem(permarena, &newPrivateKey,
+                    &pki->privateKey) ) break;
+		rv = SEC_QuickDERDecodeItem(permarena, pk,
+					lg_nsslowkey_RSAPrivateKeyTemplate,
+					&newPrivateKey);
+		if (rv == SECSuccess) {
+		    break;
+		}
+		/* Try decoding with the alternative template, but only allow
+		 * a zero-length modulus for a secret key object.
+		 * See bug 715073.
+		 */
+		rv = SEC_QuickDERDecodeItem(permarena, pk,
+					lg_nsslowkey_RSAPrivateKeyTemplate2,
+					&newPrivateKey);
+		/* A publicExponent of 0 is the defining property of a secret
+		 * key disguised as an RSA key. When decoding with the
+		 * alternative template, only accept a secret key with an
+		 * improperly encoded modulus and a publicExponent of 0.
+		 */
+		if (rv == SECSuccess) {
+		    if (pk->u.rsa.modulus.len == 2 &&
+			pk->u.rsa.modulus.data[0] == SEC_ASN1_INTEGER &&
+			pk->u.rsa.modulus.data[1] == 0 &&
+			pk->u.rsa.publicExponent.len == 1 &&
+			pk->u.rsa.publicExponent.data[0] == 0) {
+			/* Fix the zero-length integer by setting it to 0. */
+			pk->u.rsa.modulus.data = pk->u.rsa.publicExponent.data;
+			pk->u.rsa.modulus.len = pk->u.rsa.publicExponent.len;
+		    } else {
+			PORT_SetError(SEC_ERROR_BAD_DER);
+			rv = SECFailure;
+		    }
+		}
+		break;
+	      case SEC_OID_ANSIX9_DSA_SIGNATURE:
+		pk->keyType = NSSLOWKEYDSAKey;
+		lg_prepare_low_dsa_priv_key_for_asn1(pk);
+                if (SECSuccess != SECITEM_CopyItem(permarena, &newPrivateKey,
+                    &pki->privateKey) ) break;
+		rv = SEC_QuickDERDecodeItem(permarena, pk,
+					lg_nsslowkey_DSAPrivateKeyTemplate,
+					&newPrivateKey);
+		if (rv != SECSuccess)
+		    goto loser;
+		lg_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,
+					lg_nsslowkey_PQGParamsTemplate,
+					&newAlgParms);
+		break;
+	      case SEC_OID_X942_DIFFIE_HELMAN_KEY:
+		pk->keyType = NSSLOWKEYDHKey;
+		lg_prepare_low_dh_priv_key_for_asn1(pk);
+                if (SECSuccess != SECITEM_CopyItem(permarena, &newPrivateKey,
+                    &pki->privateKey) ) break;
+		rv = SEC_QuickDERDecodeItem(permarena, pk,
+					lg_nsslowkey_DHPrivateKeyTemplate,
+					&newPrivateKey);
+		break;
+#ifndef NSS_DISABLE_ECC
+	      case SEC_OID_ANSIX962_EC_PUBLIC_KEY:
+		pk->keyType = NSSLOWKEYECKey;
+		lg_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,
+					lg_nsslowkey_ECPrivateKeyTemplate,
+					&newPrivateKey);
+		if (rv != SECSuccess)
+		    goto loser;
+
+		lg_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_DISABLE_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) {
+	lg_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);
+    SKIP_AFTER_FORK(PZ_Lock(kdbLock));
+
+    (* db->close)(db);
+    
+    SKIP_AFTER_FORK(prstat = PZ_Unlock(kdbLock));
+
+    return;
+}
+
+/*
+ * SDB Entry Points for the Key DB 
+ */
+
+CK_RV
+lg_GetMetaData(SDB *sdb, const char *id, SECItem *item1, SECItem *item2)
+{
+    NSSLOWKEYDBHandle *keydb;
+    NSSLOWKEYPasswordEntry entry;
+    SECStatus rv;
+
+    keydb = lg_getKeyDB(sdb);
+    if (keydb == NULL) {
+        return CKR_TOKEN_WRITE_PROTECTED;
+    }
+    if (PORT_Strcmp(id,"password") != 0) {
+	/* shouldn't happen */
+	return CKR_GENERAL_ERROR; /* no extra data stored */
+    }
+    rv = nsslowkey_GetPWCheckEntry(keydb, &entry);
+    if (rv != SECSuccess) {
+        return CKR_GENERAL_ERROR;
+    }
+    item1->len = entry.salt.len;
+    PORT_Memcpy(item1->data, entry.salt.data, item1->len);
+    item2->len = entry.value.len;
+    PORT_Memcpy(item2->data, entry.value.data, item2->len);
+    return CKR_OK;
+}
+
+CK_RV
+lg_PutMetaData(SDB *sdb, const char *id, 
+	       const SECItem *item1, const SECItem *item2)
+{
+    NSSLOWKEYDBHandle *keydb;
+    NSSLOWKEYPasswordEntry entry;
+    SECStatus rv;
+
+    keydb = lg_getKeyDB(sdb);
+    if (keydb == NULL) {
+        return CKR_TOKEN_WRITE_PROTECTED;
+    }
+    if (PORT_Strcmp(id,"password") != 0) {
+	/* shouldn't happen */
+	return CKR_GENERAL_ERROR; /* no extra data stored */
+    }
+    entry.salt = *item1;
+    entry.value = *item2;
+    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 -r a945361df361 -r 150b72113545 nss/lib/softoken/legacydb/keydbi.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nss/lib/softoken/legacydb/keydbi.h	Tue Aug 05 18:32:02 2014 +0200
@@ -0,0 +1,53 @@
+/*
+ * private.h - Private data structures for the software token library
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#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 -r a945361df361 -r 150b72113545 nss/lib/softoken/legacydb/lgattr.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nss/lib/softoken/legacydb/lgattr.c	Tue Aug 05 18:32:02 2014 +0200
@@ -0,0 +1,1790 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+/*
+ * 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"
+#include "secasn1.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_NSS_TRUST, CKO_NSS_CRL, CKO_NSS_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;
+    obj->dbKey.data = NULL;
+    obj->dbKey.len = 0;
+    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_NSS_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_NSS_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_NSS_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_NSS_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 = lg_nsslowkey_ConvertToPublicKey(privKey);
+    lg_nsslowkey_DestroyPrivateKey(privKey);
+    obj->objectInfo = (void *) pubKey;
+    obj->infoFree = (LGFreeFunc) lg_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) lg_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;
+#ifndef NSS_DISABLE_ECC
+    case NSSLOWKEYECKey:
+	    pubItem = &pubKey->u.ec.publicValue;
+	    break;
+#endif /* NSS_DISABLE_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);
+}
+
+#ifndef NSS_DISABLE_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:
+	if (getenv("NSS_USE_DECODED_CKA_EC_POINT")) {
+	    return lg_CopyAttributeSigned(attribute, type,
+					key->u.ec.publicValue.data,
+					key->u.ec.publicValue.len);
+	} else {
+	    SECItem *pubValue = SEC_ASN1EncodeItem(NULL, NULL, 
+					&(key->u.ec.publicValue), 
+					SEC_ASN1_GET(SEC_OctetStringTemplate));
+	    CK_RV crv;
+	    if (!pubValue) {
+		return CKR_HOST_MEMORY;
+	    }
+	    crv = lg_CopyAttributeSigned(attribute, type,
+					pubValue->data,
+					pubValue->len);
+	    SECITEM_FreeItem(pubValue, PR_TRUE);
+	    return crv;
+	}
+    default:
+	break;
+    }
+    return lg_invalidAttribute(attribute);
+}
+#endif /* NSS_DISABLE_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_SUBJECT:
+	   return LG_CLONE_ATTR(attribute,type,lg_StaticNullAttr);
+    case CKA_START_DATE:
+    case CKA_END_DATE:
+	   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_GetPublicKey(obj);
+    if (key == NULL) {
+	if (type == CKA_ID) {
+	   return LG_CLONE_ATTR(attribute,type,lg_StaticNullAttr);
+	}
+	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);
+#ifndef NSS_DISABLE_ECC
+    case NSSLOWKEYECKey:
+	return lg_FindECPublicKeyAttribute(key,type,attribute);
+#endif /* NSS_DISABLE_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:
+    case CKA_LOCAL:
+	return LG_CLONE_ATTR(attribute,type,lg_StaticTrueAttr);
+    case CKA_NEVER_EXTRACTABLE:
+	return LG_CLONE_ATTR(attribute,type,lg_StaticFalseAttr);
+    case CKA_START_DATE:
+    case CKA_END_DATE:
+	   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;
+    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);
+}
+
+#ifndef NSS_DISABLE_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_DISABLE_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:
+    case CKA_LOCAL:
+	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_START_DATE:
+    case CKA_END_DATE:
+	   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);
+#ifndef NSS_DISABLE_ECC
+    case NSSLOWKEYECKey:
+	return lg_FindECPrivateKeyAttribute(key,type,attribute,obj->sdb);
+#endif /* NSS_DISABLE_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_NSS_EMAIL:
+	return lg_CopyAttribute(attribute,type,obj->dbKey.data,
+						obj->dbKey.len-1);
+    case CKA_NSS_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_NSS_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_NSS_TRUSTED_DELEGATOR);
+	}
+	if (trustFlags & CERTDB_TRUSTED) {
+	    return lg_ULongAttribute(attribute, type, CKT_NSS_TRUSTED);
+	}
+	if (trustFlags & CERTDB_MUST_VERIFY) {
+	    return lg_ULongAttribute(attribute, type, 
+				     CKT_NSS_MUST_VERIFY_TRUST);
+	}
+	if (trustFlags & CERTDB_TRUSTED_UNKNOWN) {
+	    return lg_ULongAttribute(attribute, type, CKT_NSS_TRUST_UNKNOWN);
+	}
+	if (trustFlags & CERTDB_VALID_CA) {
+	    return lg_ULongAttribute(attribute, type, CKT_NSS_VALID_DELEGATOR);
+	}
+	if (trustFlags & CERTDB_TERMINAL_RECORD) {
+	    return lg_ULongAttribute(attribute, type, CKT_NSS_NOT_TRUSTED);
+	}
+	return lg_ULongAttribute(attribute, type, CKT_NSS_TRUST_UNKNOWN);
+    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_NSS_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_NSS_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_NSS_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_NSS_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) {
+	    lg_nsslowkey_DestroyPublicKey(pubKey);
+	    break;
+	}
+	SHA1_HashBuf(hash,item->data,item->len);
+	/* item is imbedded in pubKey, just free the key */
+	lg_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_NSS_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_NSS_CRL:
+	return lg_FindCrlAttribute(obj,type,attribute);
+    case CKO_NSS_TRUST:
+	return lg_FindTrustAttribute(obj,type,attribute);
+    case CKO_NSS_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;
+    unsigned 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);
+    unsigned 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_NSS_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) ||
+	(type == CKA_LOCAL) || (type == CKA_NEVER_EXTRACTABLE) ||
+	(type == CKA_ALWAYS_SENSITIVE)) {
+	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_UNWRAP:
+    case CKA_SIGN:
+    case CKA_DERIVE:
+    case CKA_SIGN_RECOVER:
+    case CKA_DECRYPT:
+	/* ignore attempts to change restrict these.
+	 * legacyDB ignore these flags and always presents all of them 
+	 * that are valid as true. 
+	 * NOTE: We only get here if the current value and the new value do
+	 * not match. */
+	if (*(char *)value == 0) {
+	    crv = CKR_OK;
+	}
+	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 = PR_TRUE;
+	crv = CKR_OK;
+	break;
+    default:
+	crv = CKR_ATTRIBUTE_READ_ONLY;
+	break;
+    }
+done:
+    if (nickname) {
+	PORT_Free(nickname);
+    }
+    return crv;
+}
+
+static CK_RV
+lg_SetPublicKeyAttribute(LGObjectCache *obj, CK_ATTRIBUTE_TYPE type, 
+			const void *value, unsigned int len, 
+			PRBool *writePrivate)
+{
+    /* 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) || (type == CKA_LABEL)) {
+	return CKR_OK;
+    }
+    return  CKR_ATTRIBUTE_READ_ONLY;
+}
+
+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;
+
+    if (attr->type == CKA_LABEL) {
+	return CKR_OK;
+    }
+
+    crv = lg_GetULongAttribute(attr->type, attr, 1, &trust);
+    if (crv != CKR_OK) {
+	return crv;
+    }
+    flags = lg_MapTrust(trust, (PRBool) (attr->type == CKA_TRUST_CLIENT_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;
+
+    if ((attr->type == CKA_NETSCAPE_DB) && (obj->objclass == CKO_PRIVATE_KEY)) {
+	*writePrivate = PR_TRUE;
+	return CKR_OK;
+    }
+
+    /* 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_NSS_CRL:
+	/* change URL */
+	break;
+    case CKO_NSS_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;
+    case CKO_PUBLIC_KEY:
+	crv = lg_SetPublicKeyAttribute(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;
+    unsigned 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_StoreKeyByPublicKeyAlg(lg_getKeyDB(sdb), privKey, 
+		&obj->dbKey, label, sdb, PR_TRUE );
+	}
+	if (rv != SECSuccess) {
+	    crv = CKR_DEVICE_ERROR;
+	}
+    }
+
+    lg_DestroyObjectCache(obj);
+    return crvCollect;
+}
diff -r a945361df361 -r 150b72113545 nss/lib/softoken/legacydb/lgcreate.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nss/lib/softoken/legacydb/lgcreate.c	Tue Aug 05 18:32:02 2014 +0200
@@ -0,0 +1,981 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include "secitem.h"
+#include "pkcs11.h"
+#include "lgdb.h" 
+#include "pcert.h"
+#include "lowkeyi.h"
+#include "blapi.h"
+#include "secder.h"
+#include "secasn1.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;
+    CK_RV crv;
+    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 */
+    crv = lg_GetULongAttribute(CKA_CERTIFICATE_TYPE, templ, count, &type);
+    if (crv != CKR_OK) {
+	return crv;
+    }
+
+    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_NSS_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_NSS_TRUSTED:
+	return CERTDB_TERMINAL_RECORD|CERTDB_TRUSTED;
+    case CKT_NSS_TRUSTED_DELEGATOR:
+	return CERTDB_VALID_CA|trustCA;
+    case CKT_NSS_MUST_VERIFY_TRUST:
+	return CERTDB_MUST_VERIFY;
+    case CKT_NSS_NOT_TRUSTED:
+	return CERTDB_TERMINAL_RECORD;
+    case CKT_NSS_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_NSS_TRUST_UNKNOWN;
+    CK_TRUST clientTrust = CKT_NSS_TRUST_UNKNOWN;
+    CK_TRUST emailTrust = CKT_NSS_TRUST_UNKNOWN;
+    CK_TRUST signTrust = CKT_NSS_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_NSS_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_NSS_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_NSS_URL,templ,count);
+    isKRL = lg_isTrue(CKA_NSS_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 = CKR_OK;
+    NSSLOWKEYPrivateKey *priv;
+    SECItem pubKeySpace = {siBuffer, NULL, 0};
+    SECItem *pubKey;
+#ifndef NSS_DISABLE_ECC
+    SECItem pubKey2Space = {siBuffer, NULL, 0};
+    PLArenaPool *arena = NULL;
+#endif /* NSS_DISABLE_ECC */
+    NSSLOWKEYDBHandle *keyHandle = NULL;
+	
+
+    switch (key_type) {
+    case CKK_RSA:
+	pubKeyAttr = CKA_MODULUS;
+	break;
+#ifndef NSS_DISABLE_ECC
+    case CKK_EC:
+	pubKeyAttr = CKA_EC_POINT;
+	break;
+#endif /* NSS_DISABLE_ECC */
+    case CKK_DSA:
+    case CKK_DH:
+	break;
+    default:
+	return CKR_ATTRIBUTE_VALUE_INVALID;
+    }
+
+
+    pubKey = &pubKeySpace;
+    crv = lg_Attribute2SSecItem(NULL,pubKeyAttr,templ,count,pubKey);
+    if (crv != CKR_OK) return crv;
+
+#ifndef NSS_DISABLE_ECC
+    if (key_type == CKK_EC) {
+	SECStatus rv;
+	/*
+	 * for ECC, use the decoded key first.
+	 */
+	arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+	if (arena == NULL) {
+	    crv = CKR_HOST_MEMORY;
+	    goto done;
+	}
+	rv= SEC_QuickDERDecodeItem(arena, &pubKey2Space, 
+				   SEC_ASN1_GET(SEC_OctetStringTemplate), 
+				   pubKey);
+	if (rv != SECSuccess) {
+	    /* decode didn't work, just try the pubKey */
+	    PORT_FreeArena(arena, PR_FALSE);
+	    arena = NULL;
+	} else {
+	    /* try the decoded pub key first */
+	    pubKey = &pubKey2Space;
+	}
+    }
+#endif /* NSS_DISABLE_ECC */
+
+    PORT_Assert(pubKey->data);
+    if (pubKey->data == NULL) {
+	crv = CKR_ATTRIBUTE_VALUE_INVALID;
+	goto done;
+    }
+    keyHandle = lg_getKeyDB(sdb);
+    if (keyHandle == NULL) {
+	crv = CKR_TOKEN_WRITE_PROTECTED;
+	goto done;
+    }
+    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*/);
+#ifndef NSS_DISABLE_ECC
+    if (priv == NULL && pubKey == &pubKey2Space) {
+	/* no match on the decoded key, match the original pubkey */
+	pubKey = &pubKeySpace;
+    	priv = nsslowkey_FindKeyByPublicKey(keyHandle, pubKey, 
+					    sdb /*password*/);
+    }
+#endif
+    if (priv == NULL) {
+	/* the legacy database can only 'store' public keys which already
+	 * have their corresponding private keys in the database */
+	crv = CKR_ATTRIBUTE_VALUE_INVALID;
+	goto done;
+    }
+    lg_nsslowkey_DestroyPrivateKey(priv);
+    crv = CKR_OK;
+
+    *handle = lg_mkHandle(sdb, pubKey, LG_TOKEN_TYPE_PUB);
+
+done:
+    PORT_Free(pubKeySpace.data);
+#ifndef NSS_DISABLE_ECC
+    if (arena) 
+	PORT_FreeArena(arena, PR_FALSE);
+#endif
+
+    return crv;
+}
+
+/* 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;
+
+#ifndef NSS_DISABLE_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_DISABLE_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);
+    lg_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 */
+    privKey->u.rsa.modulus.data =
+		(unsigned char *) PORT_ArenaAlloc(arena, pubkey->len);
+    if (privKey->u.rsa.modulus.data == NULL) {
+	crv = CKR_HOST_MEMORY;
+	goto loser;
+    }
+    privKey->u.rsa.modulus.len = pubkey->len;
+    PORT_Memcpy(privKey->u.rsa.modulus.data, pubkey->data, pubkey->len);
+
+    /* The public exponent is set to 0 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) lg_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_NSS_TRUST:
+	crv = lg_createTrustObject(sdb,handle,templ,count);
+	break;
+    case CKO_NSS_CRL:
+	crv = lg_createCrlObject(sdb,handle,templ,count);
+	break;
+    case CKO_NSS_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 -r a945361df361 -r 150b72113545 nss/lib/softoken/legacydb/lgdb.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nss/lib/softoken/legacydb/lgdb.h	Tue Aug 05 18:32:02 2014 +0200
@@ -0,0 +1,177 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+/*
+ * 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:"
+
+
+/* path stuff (was machine dependent) used by dbinit.c and pk11db.c */
+#define PATH_SEPARATOR "/"
+#define SECMOD_DB "secmod.db"
+#define CERT_DB_FMT "%scert%s.db"
+#define KEY_DB_FMT "%skey%s.db"
+
+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 lg_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_GetMetaData(SDB *sdb, const char *id, SECItem *item1, SECItem *item2);
+CK_RV lg_PutMetaData(SDB *sdb, const char *id, 
+			const SECItem *item1, const SECItem *item2);
+
+SEC_END_PROTOS
+
+#ifndef XP_UNIX
+
+#define NO_FORK_CHECK
+
+#endif
+
+#ifndef NO_FORK_CHECK
+
+extern PRBool lg_parentForkedAfterC_Initialize;
+#define SKIP_AFTER_FORK(x) if (!lg_parentForkedAfterC_Initialize) x
+
+#else
+
+#define SKIP_AFTER_FORK(x) x
+
+#endif /* NO_FORK_CHECK */
+
+#endif /* _LGDB_H_ */
+
diff -r a945361df361 -r 150b72113545 nss/lib/softoken/legacydb/lgdestroy.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nss/lib/softoken/legacydb/lgdestroy.c	Tue Aug 05 18:32:02 2014 +0200
@@ -0,0 +1,111 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+/*
+ * 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 -r a945361df361 -r 150b72113545 nss/lib/softoken/legacydb/lgfind.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nss/lib/softoken/legacydb/lgfind.c	Tue Aug 05 18:32:02 2014 +0200
@@ -0,0 +1,915 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include "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 && keyData->id->data) {
+	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);
+		lg_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 ) {
+	lg_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;
+	    }
+    	    lg_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;
+
+    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;
+		break;
+	    }
+	    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;
+		break;
+	    }
+	    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;
+		break;
+	    }
+	    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_CERTIFICATE_TYPE,&pTemplate[i],
+								1,&certType);
+	    if (crv != CKR_OK) {
+		classFlags = 0;
+		break;
+	    }
+	    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;
+		break;
+	    }
+	    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 = (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));
+    } 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 -r a945361df361 -r 150b72113545 nss/lib/softoken/legacydb/lginit.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nss/lib/softoken/legacydb/lginit.c	Tue Aug 05 18:32:02 2014 +0200
@@ -0,0 +1,664 @@
+/*
+ * NSS utility functions
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "lowkeyi.h"
+#include "pcert.h"
+#include "keydbi.h"
+#include "lgdb.h"
+#include "secoid.h"
+#include "prenv.h"
+#include "softkver.h"
+
+/* Library identity and versioning */
+
+#if defined(DEBUG)
+#define _DEBUG_STRING " (debug)"
+#else
+#define _DEBUG_STRING ""
+#endif
+
+/*
+ * Version information for the 'ident' and 'what commands
+ *
+ * NOTE: the first component of the concatenated rcsid string
+ * must not end in a '$' to prevent rcs keyword substitution.
+ */
+const char __nss_dbm_rcsid[] = "$Header: NSS " SOFTOKEN_VERSION _DEBUG_STRING
+        "  " __DATE__ " " __TIME__ " $";
+const char __nss_dbm_sccsid[] = "@(#)NSS " SOFTOKEN_VERSION _DEBUG_STRING
+        "  " __DATE__ " " __TIME__;
+
+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;
+    char *disableUnload = NULL;
+
+    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 */
+    disableUnload = PR_GetEnv("NSS_DISABLE_UNLOAD");
+    if (!disableUnload) {
+        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;
+    SKIP_AFTER_FORK(PR_Lock(lgdb_p->dbLock));
+}
+
+void
+lg_DBUnlock(SDB *sdb) 
+{
+    LGPrivate *lgdb_p = (LGPrivate *)sdb->private;
+    SKIP_AFTER_FORK(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;
+}
+
+PRBool lg_parentForkedAfterC_Initialize;
+
+void lg_SetForkState(PRBool forked)
+{
+    lg_parentForkedAfterC_Initialize = forked;
+}
+
+CK_RV
+lg_Close(SDB *sdb)
+{
+    LGPrivate *lgdb_p = (LGPrivate *)sdb->private;
+    lg_ClearTokenKeyHashTable(sdb);
+    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) {
+	    SKIP_AFTER_FORK(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;
+}
+
+PRIntn
+lg_CompareValues(const void *v1, const void *v2)
+{
+    PLHashNumber value1 = (PLHashNumber) v1;
+    PLHashNumber value2 = (PLHashNumber) v2;
+    return (value1 == value2);
+}
+
+/*
+ * 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, lg_CompareValues,
+			SECITEM_HashCompare, NULL, 0);
+    if (lgdb_p->hashTable == NULL) {
+	goto loser;
+    }
+
+    sdb->private = lgdb_p;
+    sdb->version = 0;
+    /*sdb->sdb_type = SDB_LEGACY; */
+    sdb->sdb_flags = flags;
+    sdb->app_private = NULL;
+    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_GetMetaData = lg_GetMetaData;
+    sdb->sdb_PutMetaData = lg_PutMetaData;
+    sdb->sdb_Begin = lg_Begin;
+    sdb->sdb_Commit = lg_Commit;
+    sdb->sdb_Abort = lg_Abort;
+    sdb->sdb_Reset = lg_Reset;
+    sdb->sdb_Close = lg_Close;
+    sdb->sdb_SetForkState = lg_SetForkState;
+
+    *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;
+
+}
+
+/*
+ * 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;
+    SECStatus rv;
+    PRBool readOnly = (flags == SDB_RDONLY)? PR_TRUE: PR_FALSE;
+    volatile char c; /* force a reference that won't get optimized away */
+
+    c = __nss_dbm_rcsid[0] + __nss_dbm_sccsid[0];
+
+    rv = SECOID_Init();
+    if (SECSuccess != rv) {
+        return CKR_DEVICE_ERROR;
+    }
+    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(PRBool forked)
+{
+    lg_SetForkState(forked);
+    nsslowcert_DestroyFreeLists();
+    nsslowcert_DestroyGlobalLocks();
+    SECOID_Shutdown();
+    lg_SetForkState(PR_FALSE);
+    return CKR_OK;
+}
+
diff -r a945361df361 -r 150b72113545 nss/lib/softoken/legacydb/lgutil.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nss/lib/softoken/legacydb/lgutil.c	Tue Aug 05 18:32:02 2014 +0200
@@ -0,0 +1,391 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include "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 )
+{
+    unsigned 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 != 4) {
+	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_DBUnlock(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 -r a945361df361 -r 150b72113545 nss/lib/softoken/legacydb/lowcert.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nss/lib/softoken/legacydb/lowcert.c	Tue Aug 05 18:32:02 2014 +0200
@@ -0,0 +1,826 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * Certificate handling code
+ */
+
+#include "seccomon.h"
+#include "secder.h"
+#include "nssilock.h"
+#include "lowkeyi.h"
+#include "secasn1.h"
+#include "secoid.h"
+#include "secerr.h"
+#include "pcert.h"
+
+SEC_ASN1_MKSUB(SECOID_AlgorithmIDTemplate)
+
+static const SEC_ASN1Template nsslowcert_SubjectPublicKeyInfoTemplate[] = {
+    { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(NSSLOWCERTSubjectPublicKeyInfo) },
+    { SEC_ASN1_INLINE | SEC_ASN1_XTRN,
+        offsetof(NSSLOWCERTSubjectPublicKeyInfo,algorithm),
+        SEC_ASN1_SUB(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;
+
+    /* need at least a tag and a 1 byte length */
+    if (length < 2) {
+	return NULL;
+    }
+
+    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;
+
+	if (len_count+used_length > length) {
+	   return NULL;
+	}
+
+	*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);
+	/* derSN->data  doesn't need to be checked because if it fails so will
+	 * serial->data below. The only difference between the two calls is
+	 * whether or not the tags are included in the returned buffer */
+    }
+    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);
+	    /* if the DER is bad, we should fail. Previously we accepted
+	     * bad DER here and treated the extension as missin */
+	    if (extensions->data == NULL ||
+	       (extensions->data - buf) + extensions->len != buf_length) 
+                return SECFailure;
+            buf = extensions->data;
+            buf_length = extensions->len; 
+            /* now parse the SEQUENCE holding the extensions. */
+            dummy = nsslowcert_dataStart(buf,buf_length,&dummylen,PR_FALSE,NULL);
+            if (dummy == NULL ||
+               (dummy - buf) + dummylen != buf_length)
+                return SECFailure;
+            buf_length -= (dummy - buf);
+            buf = dummy;
+            /* Now parse the extensions inside this sequence */
+	}
+	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(PLArenaPool *arena,
+			      SECItem *issuer, SECItem *sn, SECItem *key)
+{
+    unsigned int len = sn->len + issuer->len;
+
+    if (!arena) {
+        PORT_SetError(SEC_ERROR_INVALID_ARGS);
+	goto loser;
+    }
+    if (len > NSS_MAX_LEGACY_DB_KEY_SIZE) {
+	PORT_SetError(SEC_ERROR_INPUT_LEN);
+	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);
+
+    if (rv != SECSuccess) {
+	goto loser;
+    }
+
+    /* 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(PLArenaPool *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;
+    PLArenaPool *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;
+#ifndef NSS_DISABLE_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_DISABLE_ECC */
+      default:
+        rv = SECFailure;
+        break;
+    }
+
+    lg_nsslowkey_DestroyPublicKey (pubk);
+    return NULL;
+}
+
diff -r a945361df361 -r 150b72113545 nss/lib/softoken/legacydb/lowkey.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nss/lib/softoken/legacydb/lowkey.c	Tue Aug 05 18:32:02 2014 +0200
@@ -0,0 +1,410 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include "lowkeyi.h" 
+#include "secoid.h" 
+#include "secitem.h"
+#include "secder.h" 
+#include "secasn1.h"
+#include "secerr.h" 
+
+SEC_ASN1_MKSUB(SEC_AnyTemplate)
+SEC_ASN1_MKSUB(SEC_BitStringTemplate)
+SEC_ASN1_MKSUB(SEC_ObjectIDTemplate)
+SEC_ASN1_MKSUB(SECOID_AlgorithmIDTemplate)
+
+static const SEC_ASN1Template nsslowkey_AttributeTemplate[] = {
+    { SEC_ASN1_SEQUENCE, 
+	0, NULL, sizeof(NSSLOWKEYAttribute) },
+    { SEC_ASN1_OBJECT_ID, offsetof(NSSLOWKEYAttribute, attrType) },
+    { SEC_ASN1_SET_OF | SEC_ASN1_XTRN, offsetof(NSSLOWKEYAttribute, attrValue), 
+	SEC_ASN1_SUB(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 lg_nsslowkey_PrivateKeyInfoTemplate[] = {
+    { SEC_ASN1_SEQUENCE,
+	0, NULL, sizeof(NSSLOWKEYPrivateKeyInfo) },
+    { SEC_ASN1_INTEGER,
+	offsetof(NSSLOWKEYPrivateKeyInfo,version) },
+    { SEC_ASN1_INLINE | SEC_ASN1_XTRN,
+	offsetof(NSSLOWKEYPrivateKeyInfo,algorithm),
+	SEC_ASN1_SUB(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 lg_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 lg_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 }                                                                     
+};                                                                            
+
+/*
+ * Allows u.rsa.modulus to be zero length for secret keys with an empty
+ * CKA_ID incorrectly generated in NSS 3.13.3 or earlier.  Only used for
+ * decoding.  See bug 715073.
+ */
+const SEC_ASN1Template lg_nsslowkey_RSAPrivateKeyTemplate2[] = {
+    { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(NSSLOWKEYPrivateKey) },
+    { SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPrivateKey,u.rsa.version) },
+    { SEC_ASN1_ANY, 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 lg_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 lg_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, }
+};
+
+#ifndef NSS_DISABLE_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 lg_nsslowkey_ECParamsTemplate.
+ */
+const SEC_ASN1Template lg_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 lg_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 lg_nsslowkey_ECParamsTemplate
+     */
+#if 1
+    { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED |
+      SEC_ASN1_EXPLICIT | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0, 
+      offsetof(NSSLOWKEYPrivateKey,u.ec.ecParams.curveOID), 
+      SEC_ASN1_SUB(SEC_ObjectIDTemplate) }, 
+#else
+    { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED |
+      SEC_ASN1_EXPLICIT | SEC_ASN1_CONTEXT_SPECIFIC | 0, 
+      offsetof(NSSLOWKEYPrivateKey,u.ec.ecParams), 
+      lg_nsslowkey_ECParamsTemplate }, 
+#endif
+    { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED |
+      SEC_ASN1_EXPLICIT | SEC_ASN1_CONTEXT_SPECIFIC |
+      SEC_ASN1_XTRN | 1, 
+      offsetof(NSSLOWKEYPrivateKey,u.ec.publicValue),
+      SEC_ASN1_SUB(SEC_BitStringTemplate) }, 
+    { 0, }
+};
+
+
+/*
+ * smaller version of EC_FillParams. In this code, we only need
+ * oid and DER data.
+ */
+SECStatus
+LGEC_FillParams(PLArenaPool *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(PLArenaPool *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_DISABLE_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
+lg_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
+lg_prepare_low_pqg_params_for_asn1(PQGParams *params)
+{
+    params->prime.type = siUnsignedInteger;
+    params->subPrime.type = siUnsignedInteger;
+    params->base.type = siUnsignedInteger;
+}
+
+void
+lg_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
+lg_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;
+}
+
+#ifndef NSS_DISABLE_ECC
+void
+lg_prepare_low_ecparams_for_asn1(ECParams *params)
+{
+    params->DEREncoding.type = siUnsignedInteger;
+    params->curveOID.type = siUnsignedInteger;
+}
+
+void
+lg_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_DISABLE_ECC */
+
+void
+lg_nsslowkey_DestroyPrivateKey(NSSLOWKEYPrivateKey *privk)
+{
+    if (privk && privk->arena) {
+	PORT_FreeArena(privk->arena, PR_TRUE);
+    }
+}
+
+void
+lg_nsslowkey_DestroyPublicKey(NSSLOWKEYPublicKey *pubk)
+{
+    if (pubk && pubk->arena) {
+	PORT_FreeArena(pubk->arena, PR_FALSE);
+    }
+}
+
+NSSLOWKEYPublicKey *
+lg_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;
+#ifndef NSS_DISABLE_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_DISABLE_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 -r a945361df361 -r 150b72113545 nss/lib/softoken/legacydb/lowkeyi.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nss/lib/softoken/legacydb/lowkeyi.h	Tue Aug 05 18:32:02 2014 +0200
@@ -0,0 +1,152 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#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 lg_prepare_low_rsa_priv_key_for_asn1(NSSLOWKEYPrivateKey *key);
+extern void lg_prepare_low_pqg_params_for_asn1(PQGParams *params);
+extern void lg_prepare_low_dsa_priv_key_for_asn1(NSSLOWKEYPrivateKey *key);
+extern void lg_prepare_low_dh_priv_key_for_asn1(NSSLOWKEYPrivateKey *key);
+#ifndef NSS_DISABLE_ECC
+extern void lg_prepare_low_ec_priv_key_for_asn1(NSSLOWKEYPrivateKey *key);
+extern void lg_prepare_low_ecparams_for_asn1(ECParams *params);
+#endif /* NSS_DISABLE_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 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 lg_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 lg_nsslowkey_DestroyPublicKey(NSSLOWKEYPublicKey *key);
+
+
+/*
+** Convert a low private key "privateKey" into a public low key
+*/
+extern NSSLOWKEYPublicKey 
+	*lg_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);
+
+#ifndef NSS_DISABLE_ECC
+/*
+ * smaller version of EC_FillParams. In this code, we only need
+ * oid and DER data.
+ */
+SECStatus LGEC_FillParams(PLArenaPool *arena, const SECItem *encodedParams,
+    ECParams *params);
+
+/* Copy all of the fields from srcParams into dstParams */
+SECStatus LGEC_CopyParams(PLArenaPool *arena, ECParams *dstParams,
+	      const ECParams *srcParams);
+#endif
+SEC_END_PROTOS
+
+#endif /* _LOWKEYI_H_ */
diff -r a945361df361 -r 150b72113545 nss/lib/softoken/legacydb/lowkeyti.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nss/lib/softoken/legacydb/lowkeyti.h	Tue Aug 05 18:32:02 2014 +0200
@@ -0,0 +1,137 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#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 lg_nsslowkey_PQGParamsTemplate[];
+extern const SEC_ASN1Template lg_nsslowkey_RSAPrivateKeyTemplate[];
+extern const SEC_ASN1Template lg_nsslowkey_RSAPrivateKeyTemplate2[];
+extern const SEC_ASN1Template lg_nsslowkey_DSAPrivateKeyTemplate[];
+extern const SEC_ASN1Template lg_nsslowkey_DHPrivateKeyTemplate[];
+extern const SEC_ASN1Template lg_nsslowkey_DHPrivateKeyExportTemplate[];
+#ifndef NSS_DISABLE_ECC
+#define NSSLOWKEY_EC_PRIVATE_KEY_VERSION   1  /* as per SECG 1 C.4 */
+extern const SEC_ASN1Template lg_nsslowkey_ECParamsTemplate[];
+extern const SEC_ASN1Template lg_nsslowkey_ECPrivateKeyTemplate[];
+#endif /* NSS_DISABLE_ECC */
+
+extern const SEC_ASN1Template lg_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;
+
+
+typedef struct NSSLOWKEYPasswordEntryStr NSSLOWKEYPasswordEntry;
+struct NSSLOWKEYPasswordEntryStr {
+    SECItem salt;
+    SECItem value;
+    unsigned char data[128];
+};
+
+
+#endif	/* _LOWKEYTI_H_ */
diff -r a945361df361 -r 150b72113545 nss/lib/softoken/legacydb/manifest.mn
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nss/lib/softoken/legacydb/manifest.mn	Tue Aug 05 18:32:02 2014 +0200
@@ -0,0 +1,31 @@
+# 
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+CORE_DEPTH = ../../..
+
+MODULE = nss
+
+REQUIRES = dbm 
+
+LIBRARY_NAME = nssdbm
+LIBRARY_VERSION = 3
+MAPFILE = $(OBJDIR)/nssdbm.def
+
+DEFINES += -DSHLIB_SUFFIX=\"$(DLL_SUFFIX)\" -DSHLIB_PREFIX=\"$(DLL_PREFIX)\"
+
+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)
+
diff -r a945361df361 -r 150b72113545 nss/lib/softoken/legacydb/nssdbm.def
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nss/lib/softoken/legacydb/nssdbm.def	Tue Aug 05 18:32:02 2014 +0200
@@ -0,0 +1,31 @@
+;+#
+;+# This Source Code Form is subject to the terms of the Mozilla Public
+;+# License, v. 2.0. If a copy of the MPL was not distributed with this
+;+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+;+#
+;+# 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 ";-"
+;+NSSDBM_3.12 {       # NSS 3.12 release
+;+    global:
+LIBRARY nssdbm3 ;-
+EXPORTS ;-
+legacy_Open;
+legacy_Shutdown;
+legacy_ReadSecmodDB;
+legacy_ReleaseSecmodDBData;
+legacy_AddSecmodDB;
+legacy_DeleteSecmodDB;
+legacy_SetCryptFunctions;
+;+    local:
+;+       *;
+;+};
diff -r a945361df361 -r 150b72113545 nss/lib/softoken/legacydb/nssdbm.rc
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nss/lib/softoken/legacydb/nssdbm.rc	Tue Aug 05 18:32:02 2014 +0200
@@ -0,0 +1,68 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "../softkver.h"
+#include <winver.h>
+
+#define MY_LIBNAME "nssdbm"
+#define MY_FILEDESCRIPTION "Legacy Database Driver"
+
+#define STRINGIZE(x) #x
+#define STRINGIZE2(x) STRINGIZE(x)
+#define SOFTOKEN_VMAJOR_STR STRINGIZE2(SOFTOKEN_VMAJOR)
+
+#ifdef _DEBUG
+#define MY_DEBUG_STR " (debug)"
+#define MY_FILEFLAGS_1 VS_FF_DEBUG
+#else
+#define MY_DEBUG_STR ""
+#define MY_FILEFLAGS_1 0x0L
+#endif
+#if SOFTOKEN_BETA
+#define MY_FILEFLAGS_2 MY_FILEFLAGS_1|VS_FF_PRERELEASE
+#else
+#define MY_FILEFLAGS_2 MY_FILEFLAGS_1
+#endif
+
+#ifdef WINNT
+#define MY_FILEOS VOS_NT_WINDOWS32
+#else
+#define MY_FILEOS VOS__WINDOWS32
+#endif
+
+#define MY_INTERNAL_NAME MY_LIBNAME SOFTOKEN_VMAJOR_STR
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version-information resource
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION SOFTOKEN_VMAJOR,SOFTOKEN_VMINOR,SOFTOKEN_VPATCH,SOFTOKEN_VBUILD
+ PRODUCTVERSION SOFTOKEN_VMAJOR,SOFTOKEN_VMINOR,SOFTOKEN_VPATCH,SOFTOKEN_VBUILD
+ FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
+ FILEFLAGS MY_FILEFLAGS_2
+ FILEOS MY_FILEOS
+ FILETYPE VFT_DLL
+ FILESUBTYPE 0x0L // not used
+
+BEGIN
+    BLOCK "StringFileInfo"
+    BEGIN
+        BLOCK "040904B0" // Lang=US English, CharSet=Unicode
+        BEGIN
+            VALUE "CompanyName", "Mozilla Foundation\0"
+            VALUE "FileDescription", MY_FILEDESCRIPTION MY_DEBUG_STR "\0"
+            VALUE "FileVersion", SOFTOKEN_VERSION "\0"
+            VALUE "InternalName", MY_INTERNAL_NAME "\0"
+            VALUE "OriginalFilename", MY_INTERNAL_NAME ".dll\0"
+            VALUE "ProductName", "Network Security Services\0"
+            VALUE "ProductVersion", SOFTOKEN_VERSION "\0"
+        END
+    END
+    BLOCK "VarFileInfo"
+    BEGIN
+        VALUE "Translation", 0x409, 1200
+    END
+END
diff -r a945361df361 -r 150b72113545 nss/lib/softoken/legacydb/pcert.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nss/lib/softoken/legacydb/pcert.h	Tue Aug 05 18:32:02 2014 +0200
@@ -0,0 +1,229 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#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(PLArenaPool *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 -r a945361df361 -r 150b72113545 nss/lib/softoken/legacydb/pcertdb.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nss/lib/softoken/legacydb/pcertdb.c	Tue Aug 05 18:32:02 2014 +0200
@@ -0,0 +1,5357 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * Permanent Certificate database handling code 
+ */
+#include "lowkeyti.h"
+#include "pcert.h"
+#include "mcom_db.h"
+#include "pcert.h"
+#include "secitem.h"
+#include "secder.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) {
+	dbLock = PZ_NewLock(nssILockCertDB);
+	PORT_Assert(dbLock != NULL);
+    }
+}
+
+SECStatus
+nsslowcert_InitLocks(void)
+{
+    if (freeListLock == NULL) {
+	freeListLock = PZ_NewLock(nssILockRefLock);
+	if (freeListLock == NULL) {
+	    return SECFailure;
+	}
+    }
+    if (certRefCountLock == NULL) {
+	certRefCountLock = PZ_NewLock(nssILockRefLock);
+	if (certRefCountLock == NULL) {
+	    return SECFailure;
+	}
+    }
+    if (certTrustLock == NULL ) {
+	certTrustLock = PZ_NewLock(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);
+    
+    SKIP_AFTER_FORK(PZ_Lock(freeListLock));
+    return;
+}
+
+/*
+ * Free the cert reference count lock
+ */
+static void
+nsslowcert_UnlockFreeList(void)
+{
+    PRStatus prstat = PR_SUCCESS;
+
+    PORT_Assert(freeListLock != NULL);
+    
+    SKIP_AFTER_FORK(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 = PR_SUCCESS;
+
+    PORT_Assert(dbLock != NULL);
+    SKIP_AFTER_FORK(PZ_Lock(dbLock));
+
+    (* db->close)(db);
+    
+    SKIP_AFTER_FORK(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)
+{
+    PLArenaPool *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, PLArenaPool *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, PLArenaPool *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] = (PRUint8)( entry->trust.sslFlags >> 8 );
+    buf[1] = (PRUint8)( entry->trust.sslFlags      );
+    buf[2] = (PRUint8)( entry->trust.emailFlags >> 8 );
+    buf[3] = (PRUint8)( entry->trust.emailFlags      );
+    buf[4] = (PRUint8)( entry->trust.objectSigningFlags >> 8 );
+    buf[5] = (PRUint8)( entry->trust.objectSigningFlags      );
+    buf[6] = (PRUint8)( entry->derCert.len >> 8 );
+    buf[7] = (PRUint8)( entry->derCert.len      );
+    buf[8] = (PRUint8)( nnlen >> 8 );
+    buf[9] = (PRUint8)( nnlen      );
+    
+    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, PLArenaPool *arena, SECItem *dbkey)
+{
+    unsigned int len = certKey->len + SEC_DB_KEY_HEADER_LEN;
+    if (len > NSS_MAX_LEGACY_DB_KEY_SIZE)
+	goto loser;
+    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, PLArenaPool *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;
+    if (dbkey->len > NSS_MAX_LEGACY_DB_KEY_SIZE)
+	goto loser;
+    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] );
+    lenoff = dbentry->len - ( entry->derCert.len + nnlen + headerlen );
+    if ( lenoff ) {
+	if ( lenoff < 0 || (lenoff & 0xffff) != 0 ) {
+	    PORT_SetError(SEC_ERROR_BAD_DATABASE);
+	    goto loser;
+	}
+	/* The cert size exceeded 64KB.  Reconstruct the correct length. */
+	entry->derCert.len += lenoff;
+    }
+    
+    /* 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;
+    PLArenaPool *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;
+    PLArenaPool *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;
+    PLArenaPool *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;
+    }
+
+    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, PLArenaPool *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] = (PRUint8)( entry->derCrl.len >> 8 );
+    buf[1] = (PRUint8)( entry->derCrl.len      );
+    buf[2] = (PRUint8)( nnlen >> 8 );
+    buf[3] = (PRUint8)( nnlen      );
+    
+    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 urlLen;
+    int lenDiff;
+
+    /* 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] );
+    urlLen =            ( ( dbentry->data[2] << 8 ) | dbentry->data[3] );
+    lenDiff = dbentry->len - 
+			(entry->derCrl.len + urlLen + DB_CRL_ENTRY_HEADER_LEN);
+    if (lenDiff) {
+    	if (lenDiff < 0 || (lenDiff & 0xffff) != 0) {
+	    PORT_SetError(SEC_ERROR_BAD_DATABASE);
+	    goto loser;
+	}    
+	/* CRL entry is greater than 64 K. Hack to make this continue to work */
+	entry->derCrl.len += lenDiff;
+    }
+    
+    /* copy the der CRL */
+    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 (urlLen != 0) {
+	entry->url = (char *)PORT_ArenaAlloc(entry->common.arena, urlLen);
+	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],
+	      urlLen);
+    }
+    
+    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;
+    PLArenaPool *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;
+    PLArenaPool *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;
+    PLArenaPool *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)
+{
+    PLArenaPool *arena = NULL;
+    PLArenaPool *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, PLArenaPool *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] = (PRUint8)( entry->subjectName.len >> 8 );
+    buf[1] = (PRUint8)( entry->subjectName.len      );
+    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, PLArenaPool *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;
+    if (dbkey->len > NSS_MAX_LEGACY_DB_KEY_SIZE)
+	goto loser;
+    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)
+{
+    int lenDiff;
+
+    /* 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] );
+    lenDiff = dbentry->len - 
+	      (entry->subjectName.len + DB_NICKNAME_ENTRY_HEADER_LEN);
+    if (lenDiff) {
+	if (lenDiff < 0 || (lenDiff & 0xffff) != 0 ) { 
+	    PORT_SetError(SEC_ERROR_BAD_DATABASE);
+	    goto loser;
+	}
+	/* The entry size exceeded 64KB.  Reconstruct the correct length. */
+	entry->subjectName.len += lenDiff;
+    }
+
+    /* 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)
+{
+    PLArenaPool *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)
+{
+    PLArenaPool *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)
+{
+    PLArenaPool *arena = NULL;
+    PLArenaPool *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;
+    PLArenaPool *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, PLArenaPool *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] = (PRUint8)( entry->subjectName.len >> 8 );
+    buf[1] = (PRUint8)( entry->subjectName.len      );
+    buf[2] = (PRUint8)( entry->smimeOptions.len >> 8 );
+    buf[3] = (PRUint8)( entry->smimeOptions.len      );
+    buf[4] = (PRUint8)( entry->optionsDate.len >> 8 );
+    buf[5] = (PRUint8)( entry->optionsDate.len      );
+
+    /* 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, PLArenaPool *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;
+    if (dbkey->len > NSS_MAX_LEGACY_DB_KEY_SIZE)
+	goto loser;
+    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)
+{
+    int lenDiff;
+
+    /* 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] );
+    lenDiff = dbentry->len - (entry->subjectName.len + 
+                              entry->smimeOptions.len + 
+			      entry->optionsDate.len + 
+			      DB_SMIME_ENTRY_HEADER_LEN);
+    if (lenDiff) {
+	if (lenDiff < 0 || (lenDiff & 0xffff) != 0 ) { 
+	    PORT_SetError(SEC_ERROR_BAD_DATABASE);
+	    goto loser;
+	}
+	/* The entry size exceeded 64KB.  Reconstruct the correct length. */
+	entry->subjectName.len += lenDiff;
+    }
+
+    /* 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)
+{
+    PLArenaPool *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)
+{
+    PLArenaPool *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)
+{
+    PLArenaPool *arena = NULL;
+    PLArenaPool *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;
+    PLArenaPool *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, PLArenaPool *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 = entry->certKeys;
+    SECItem *keyIDs   = entry->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++ ) {
+	if (keyIDs[i].len   > 0xffff ||
+	   (certKeys[i].len > 0xffff)) {
+    	    PORT_SetError(SEC_ERROR_INPUT_LEN);
+	    goto loser;
+	}
+	len += certKeys[i].len;
+	len += 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] = (PRUint8)( ncerts >> 8 );
+    buf[1] = (PRUint8)( ncerts      );
+    buf[2] = (PRUint8)( nnlen >> 8 );
+    buf[3] = (PRUint8)( nnlen      );
+    /* v7 email field is NULL in v8 */
+    buf[4] = 0;
+    buf[5] = 0;
+
+    PORT_Memcpy(&buf[DB_SUBJECT_ENTRY_HEADER_LEN], entry->nickname, nnlen);
+    tmpbuf = &buf[keyidoff];   
+    for ( i = 0; i < ncerts; i++ ) {
+	tmpbuf[0] = (PRUint8)( certKeys[i].len >> 8 );
+	tmpbuf[1] = (PRUint8)( certKeys[i].len      );
+	tmpbuf += 2;
+    }
+    for ( i = 0; i < ncerts; i++ ) {
+	tmpbuf[0] = (PRUint8)( keyIDs[i].len >> 8 );
+	tmpbuf[1] = (PRUint8)( keyIDs[i].len      );
+	tmpbuf += 2;
+    }
+    
+    for ( i = 0; i < ncerts; i++ ) {
+	PORT_Memcpy(tmpbuf, certKeys[i].data, certKeys[i].len);
+	tmpbuf += certKeys[i].len;
+    }
+    for ( i = 0; i < ncerts; i++ ) {
+	PORT_Memcpy(tmpbuf, keyIDs[i].data, keyIDs[i].len);
+	tmpbuf += keyIDs[i].len;
+    }
+
+    if (entry->emailAddrs) {
+	tmpbuf[0] = (PRUint8)( entry->nemailAddrs >> 8 );
+	tmpbuf[1] = (PRUint8)( entry->nemailAddrs      );
+	tmpbuf += 2;
+	for (i=0; i < entry->nemailAddrs; i++) {
+	    int nameLen = PORT_Strlen(entry->emailAddrs[i]) + 1;
+	    tmpbuf[0] = (PRUint8)( nameLen >> 8 );
+	    tmpbuf[1] = (PRUint8)( nameLen      );
+	    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, PLArenaPool *arena,
+		   SECItem *dbkey)
+{
+    dbkey->len = derSubject->len + SEC_DB_KEY_HEADER_LEN;
+    if (dbkey->len > NSS_MAX_LEGACY_DB_KEY_SIZE)
+	goto loser;
+    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)
+{
+    PLArenaPool *arena     = entry->common.arena;
+    unsigned char *tmpbuf;
+    unsigned char *end;
+    void        *mark      = PORT_ArenaMark(arena);
+    unsigned int eaddrlen;
+    unsigned int i;
+    unsigned int keyidoff;
+    unsigned int len;
+    unsigned int ncerts    = 0;
+    unsigned int nnlen;
+    SECStatus rv;
+
+    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] );
+    keyidoff = DB_SUBJECT_ENTRY_HEADER_LEN + nnlen + eaddrlen;
+    len = keyidoff + (4 * ncerts);
+    if ( dbentry->len < len) {
+	PORT_SetError(SEC_ERROR_BAD_DATABASE);
+	goto loser;
+    }
+    
+    entry->certKeys = PORT_ArenaNewArray(arena, SECItem, ncerts);
+    entry->keyIDs   = PORT_ArenaNewArray(arena, 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 = PORT_ArenaNewArray(arena, char *, 2);
+	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.
+     */
+    tmpbuf = &dbentry->data[keyidoff];
+    for ( i = 0; i < ncerts; i++ ) {
+        unsigned int itemlen = ( tmpbuf[0] << 8 ) | tmpbuf[1];
+        entry->certKeys[i].len = itemlen;
+        len += itemlen;
+        tmpbuf += 2;
+    }
+    for ( i = 0; i < ncerts; i++ ) {
+        unsigned int itemlen = ( tmpbuf[0] << 8 ) | tmpbuf[1] ;
+        entry->keyIDs[i].len = itemlen;
+        len += itemlen;
+        tmpbuf += 2;
+    }
+
+    /* is encoded entry large enough ? */
+    if ( len > dbentry->len ){
+	PORT_SetError(SEC_ERROR_BAD_DATABASE);
+	goto loser;
+    }
+
+    for ( i = 0; i < ncerts; i++ ) {
+	unsigned int kLen = entry->certKeys[i].len;
+	entry->certKeys[i].data = (unsigned char *)PORT_ArenaAlloc(arena, kLen);
+	if ( entry->certKeys[i].data == NULL ) {
+	    PORT_SetError(SEC_ERROR_NO_MEMORY);
+	    goto loser;
+	}
+	PORT_Memcpy(entry->certKeys[i].data, tmpbuf, kLen);
+	tmpbuf += kLen;
+    }
+    for ( i = 0; i < ncerts; i++ ) {
+	unsigned int iLen = entry->keyIDs[i].len;
+	entry->keyIDs[i].data = (unsigned char *)PORT_ArenaAlloc(arena, iLen);
+	if ( entry->keyIDs[i].data == NULL ) {
+	    PORT_SetError(SEC_ERROR_NO_MEMORY);
+	    goto loser;
+	}
+	PORT_Memcpy(entry->keyIDs[i].data, tmpbuf, iLen);
+	tmpbuf += iLen;
+    }
+
+    end = dbentry->data + dbentry->len;
+    if ((eaddrlen == 0) && (end - tmpbuf > 1)) {
+	/* read in the additional email addresses */
+	entry->nemailAddrs = (((unsigned int)tmpbuf[0]) << 8) | tmpbuf[1];
+	tmpbuf += 2;
+	if (end - tmpbuf < 2 * (int)entry->nemailAddrs)
+	    goto loser;
+	entry->emailAddrs = PORT_ArenaNewArray(arena, char *, entry->nemailAddrs);
+	if (entry->emailAddrs == NULL) {
+	    PORT_SetError(SEC_ERROR_NO_MEMORY);
+	    goto loser;
+	}
+	for (i=0; i < entry->nemailAddrs; i++) {
+	    int nameLen;
+	    if (end - tmpbuf < 2) {
+		goto loser;
+	    }
+	    nameLen = (((int)tmpbuf[0]) << 8) | tmpbuf[1];
+	    tmpbuf += 2;
+	    if (end - tmpbuf < nameLen) {
+		goto loser;
+	    }
+	    entry->emailAddrs[i] = PORT_ArenaAlloc(arena,nameLen);
+	    if (entry->emailAddrs == NULL) {
+	        PORT_SetError(SEC_ERROR_NO_MEMORY);
+	        goto loser;
+	    }
+	    PORT_Memcpy(entry->emailAddrs[i], tmpbuf, nameLen);
+	    tmpbuf += nameLen;
+	}
+	if (tmpbuf != end) 
+	    goto loser;
+    }
+    PORT_ArenaUnmark(arena, mark);
+    return(SECSuccess);
+
+loser:
+    PORT_ArenaRelease(arena, mark); /* discard above allocations */
+    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)
+{
+    PLArenaPool *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;
+    PLArenaPool *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)
+{
+    PLArenaPool *arena = NULL;
+    PLArenaPool *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;
+    PLArenaPool *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)
+{
+    PLArenaPool *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)
+{
+    PLArenaPool *arena = NULL;
+    PLArenaPool *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;
+    PLArenaPool *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;
+    PLArenaPool *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;
+    PLArenaPool *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 = SECSuccess;
+    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);
+    }
+    /* here, ret is zero and rv is SECSuccess.  
+     * Below here, ret is a count of successful calls to the callback function.
+     */
+    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 ) {
+		++ret;
+	    }
+	}
+    } while ( certdb_Seq(handle->permCertDB, &key, &data, R_NEXT) == 0 );
+    /* If any callbacks succeeded, or no calls to callbacks were made, 
+     * then report success.  Otherwise, report failure.
+     */
+    return (ret ? SECSuccess : rv);
+}
+/*
+ * 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;
+    PLArenaPool *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;
+	}
+	PORT_Free(handle);
+    }
+    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:
+    if (handle->dbMon) {
+        PZ_DestroyMonitor(handle->dbMon);
+        handle->dbMon = NULL;
+    }
+    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)
+{
+    PLArenaPool *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;
+    PLArenaPool *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)
+{
+    if (freeListLock == NULL) {
+	return;
+    }
+    DestroyCertEntryFreeList();
+    DestroyTrustFreeList();
+    DestroyCertFreeList();
+    SKIP_AFTER_FORK(PZ_DestroyLock(freeListLock));
+    freeListLock = NULL;
+}
+
+void
+nsslowcert_DestroyGlobalLocks(void)
+{
+    if (dbLock) {
+	SKIP_AFTER_FORK(PZ_DestroyLock(dbLock));
+	dbLock = NULL;
+    }
+    if (certRefCountLock) {
+	SKIP_AFTER_FORK(PZ_DestroyLock(certRefCountLock));
+	certRefCountLock = NULL;
+    }
+    if (certTrustLock) {
+	SKIP_AFTER_FORK(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 -r a945361df361 -r 150b72113545 nss/lib/softoken/legacydb/pcertt.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nss/lib/softoken/legacydb/pcertt.h	Tue Aug 05 18:32:02 2014 +0200
@@ -0,0 +1,418 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+/*
+ * certt.h - public data structures for the certificate library
+ */
+#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 {
+    PLArenaPool *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 {
+    PLArenaPool *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 */
+
+#define NSS_MAX_LEGACY_DB_KEY_SIZE (60 * 1024)
+
+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;
+    PLArenaPool *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_TERMINAL_RECORD	(1u<<0)
+#define CERTDB_TRUSTED		(1u<<1)
+#define CERTDB_SEND_WARN	(1u<<2)
+#define CERTDB_VALID_CA		(1u<<3)
+#define CERTDB_TRUSTED_CA	(1u<<4) /* trusted for issuing server certs */
+#define CERTDB_NS_TRUSTED_CA	(1u<<5)
+#define CERTDB_USER		(1u<<6)
+#define CERTDB_TRUSTED_CLIENT_CA (1u<<7) /* trusted for issuing client certs */
+#define CERTDB_INVISIBLE_CA	(1u<<8) /* don't show in UI */
+#define CERTDB_GOVT_APPROVED_CA	(1u<<9) /* can do strong crypto in export ver */
+#define CERTDB_MUST_VERIFY	(1u<<10) /* explicitly don't trust this cert */
+#define CERTDB_TRUSTED_UNKNOWN	(1u<<11) /* accept trust from another source */
+
+/* bits not affected by the CKO_NETSCAPE_TRUST object */
+#define CERTDB_PRESERVE_TRUST_BITS (CERTDB_USER | \
+        CERTDB_NS_TRUSTED_CA | CERTDB_VALID_CA | CERTDB_INVISIBLE_CA | \
+                                        CERTDB_GOVT_APPROVED_CA)
+
+#endif /* _PCERTT_H_ */
diff -r a945361df361 -r 150b72113545 nss/lib/softoken/legacydb/pk11db.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nss/lib/softoken/legacydb/pk11db.c	Tue Aug 05 18:32:02 2014 +0200
@@ -0,0 +1,717 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+/* 
+ *  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 "lgdb.h"
+#include "mcom_db.h"
+#include "secerr.h"
+#include "utilpars.h"
+
+#define FREE_CLEAR(p) if (p) { PORT_Free(p); p = NULL; }
+
+/* Construct a database key for a given module */
+static SECStatus lgdb_MakeKey(DBT *key, char * module) {
+    int len = 0;
+    char *commonName;
+
+    commonName = NSSUTIL_ArgGetParamValue("name",module);
+    if (commonName == NULL) {
+	commonName = NSSUTIL_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 
+lgdb_FreeKey(DBT *key) 
+{
+    if (key->data) {
+	PORT_Free(key->data);
+    }
+    key->data = NULL;
+    key->size = 0;
+}
+
+typedef struct lgdbDataStr lgdbData;
+typedef struct lgdbSlotDataStr lgdbSlotData;
+struct lgdbDataStr {
+    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 lgdbSlotDataStr {
+    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 LGDB_DB_VERSION_MAJOR 0
+#define LGDB_DB_VERSION_MINOR 6
+#define LGDB_DB_EXT1_VERSION_MAJOR 0
+#define LGDB_DB_EXT1_VERSION_MINOR 6
+#define LGDB_DB_NOUI_VERSION_MAJOR 0
+#define LGDB_DB_NOUI_VERSION_MINOR 4
+
+#define LGDB_PUTSHORT(dest,src) \
+	(dest)[1] = (unsigned char) ((src)&0xff); \
+	(dest)[0] = (unsigned char) (((src) >> 8) & 0xff);
+#define LGDB_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 LGDB_GETSHORT(src) \
+	((unsigned short) (((src)[0] << 8) | (src)[1]))
+#define LGDB_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 
+lgdb_EncodeData(DBT *data, char * module) 
+{
+    lgdbData *encoded = NULL;
+    lgdbSlotData *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;
+    struct NSSUTILPreSlotInfoStr *slotInfo = NULL;
+    SECStatus rv = SECFailure;
+
+    rv = NSSUTIL_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 = NSSUTIL_ArgGetParamValue("slotParams",nss); 
+    slotInfo = NSSUTIL_ArgParseSlotInfo(NULL,slotParams,&count);
+    if (slotParams) PORT_Free(slotParams);
+
+    if (count && slotInfo == NULL) {
+	/* set error */
+	goto loser;
+    }
+
+    dataLen = sizeof(lgdbData) + len + len2 + len3 + sizeof(unsigned short) +
+				 count*sizeof(lgdbSlotData);
+
+    data->data = (unsigned char *) PORT_ZAlloc(dataLen);
+    encoded = (lgdbData *)data->data;
+    dataPtr = (unsigned char *) data->data;
+    data->size = dataLen;
+
+    if (encoded == NULL) {
+	/* set error */
+	goto loser;
+    }
+
+    encoded->major = LGDB_DB_VERSION_MAJOR;
+    encoded->minor = LGDB_DB_VERSION_MINOR;
+    encoded->internal = (unsigned char) 
+			(NSSUTIL_ArgHasFlag("flags","internal",nss) ? 1 : 0);
+    encoded->fips = (unsigned char) 
+			(NSSUTIL_ArgHasFlag("flags","FIPS",nss) ? 1 : 0);
+    encoded->isModuleDB = (unsigned char) 
+			(NSSUTIL_ArgHasFlag("flags","isModuleDB",nss) ? 1 : 0);
+    encoded->isModuleDBOnly = (unsigned char) 
+		    (NSSUTIL_ArgHasFlag("flags","isModuleDBOnly",nss) ? 1 : 0);
+    encoded->isCritical = (unsigned char) 
+			(NSSUTIL_ArgHasFlag("flags","critical",nss) ? 1 : 0);
+
+    order = NSSUTIL_ArgReadLong("trustOrder", nss,
+				 NSSUTIL_DEFAULT_TRUST_ORDER, NULL);
+    LGDB_PUTLONG(encoded->trustOrder,order);
+    order = NSSUTIL_ArgReadLong("cipherOrder", nss, 
+				NSSUTIL_DEFAULT_CIPHER_ORDER, NULL);
+    LGDB_PUTLONG(encoded->cipherOrder,order);
+
+   
+    ciphers = NSSUTIL_ArgGetParamValue("ciphers",nss); 
+    NSSUTIL_ArgParseCipherFlags(&ssl[0], ciphers);
+    LGDB_PUTLONG(encoded->ssl,ssl[0]);
+    LGDB_PUTLONG(&encoded->ssl[4],ssl[1]);
+    if (ciphers) PORT_Free(ciphers);
+
+    offset = (unsigned short) offsetof(lgdbData, names);
+    LGDB_PUTSHORT(encoded->nameStart,offset);
+    offset = offset + len + len2 + len3 + 3*sizeof(unsigned short);
+    LGDB_PUTSHORT(encoded->slotOffset,offset);
+
+
+    LGDB_PUTSHORT(&dataPtr[offset],((unsigned short)count));
+    slot = (lgdbSlotData *)(dataPtr+offset+sizeof(unsigned short));
+
+    offset = 0;
+    LGDB_PUTSHORT(encoded->names,len);
+    offset += sizeof(unsigned short);
+    PORT_Memcpy(&encoded->names[offset],commonName,len);
+    offset += len;
+
+
+    LGDB_PUTSHORT(&encoded->names[offset],len2);
+    offset += sizeof(unsigned short);
+    if (len2) PORT_Memcpy(&encoded->names[offset],dllName,len2);
+    offset += len2;
+
+    LGDB_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++) {
+	    LGDB_PUTLONG(slot[i].slotID, slotInfo[i].slotID);
+	    LGDB_PUTLONG(slot[i].defaultFlags,
+					slotInfo[i].defaultFlags);
+	    LGDB_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 
+lgdb_FreeData(DBT *data)
+{
+    if (data->data) {
+	PORT_Free(data->data);
+    }
+}
+
+static void
+lgdb_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 *
+lgdb_DecodeData(char *defParams, DBT *data, PRBool *retInternal)
+{
+    lgdbData *encoded;
+    lgdbSlotData *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	= NSSUTIL_DEFAULT_TRUST_ORDER;
+    unsigned long cipherOrder	= NSSUTIL_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 lgdbData struct. 
+    ** It may be an old or new version.  Check the length for each. 
+    */
+
+    CHECK_SIZE( offsetof(lgdbData, trustOrder[0]) );
+
+    encoded = (lgdbData *)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 == LGDB_DB_NOUI_VERSION_MAJOR) &&
+ 	(encoded->minor <= LGDB_DB_NOUI_VERSION_MINOR)) {
+	isOldVersion = PR_TRUE;
+    }
+    if ((encoded->major == LGDB_DB_EXT1_VERSION_MAJOR) &&
+	(encoded->minor >= LGDB_DB_EXT1_VERSION_MINOR)) {
+	CHECK_SIZE( sizeof(lgdbData));
+	trustOrder     = LGDB_GETLONG(encoded->trustOrder);
+	cipherOrder    = LGDB_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 = LGDB_GETLONG(encoded->ssl);
+    ssl1 = LGDB_GETLONG(encoded->ssl + 4);
+
+    slotOffset  = LGDB_GETSHORT(encoded->slotOffset);
+    namesOffset = LGDB_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   = LGDB_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 = LGDB_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 = LGDB_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 ];
+    **     lgdbSlotData [ slotCount ];
+    ** {
+    */
+
+    CHECK_SIZE( slotOffset + 2 );
+    slotCount = LGDB_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(lgdbSlotData)) {
+	    goto db_loser;
+	}
+    }
+
+    CHECK_SIZE( (slotOffset + 2 + slotCount * sizeof(lgdbSlotData)));
+    slots = (lgdbSlotData *) ((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       = LGDB_GETLONG(slots->slotID);
+	defaultFlags = LGDB_GETLONG(slots->defaultFlags);
+	timeout      = LGDB_GETLONG(slots->timeout);
+	hasRootCerts = slots->hasRootCerts;
+	if (isOldVersion && internal && (slotID != 2)) {
+	    unsigned long internalFlags=
+	         NSSUTIL_ArgParseSlotFlags("slotFlags", 
+					NSSUTIL_DEFAULT_SFTKN_FLAGS);
+	    defaultFlags |= internalFlags;
+	}
+	if (hasRootCerts && !extended) {
+	    trustOrder = 100;
+	}
+
+	slotStrings[i] = NSSUTIL_MkSlotString(slotID, defaultFlags, timeout, 
+	                                   (unsigned char)slots->askpw, 
+	                                   hasRootCerts, hasRootTrust);
+	if (slotStrings[i] == NULL) {
+	    lgdb_FreeSlotStrings(slotStrings,i);
+	    goto loser;
+	}
+    }
+
+    nss = NSSUTIL_MkNSSString(slotStrings, slotCount, internal, isFIPS, 
+		     isModuleDB, isModuleDBOnly, internal, trustOrder, 
+		     cipherOrder, ssl0, ssl1);
+    lgdb_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 = NSSUTIL_MkModuleSpec(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 *
+lgdb_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 
+lgdb_CloseDB(DB *pkcs11db) 
+{
+     (*pkcs11db->close)(pkcs11db);
+}
+
+
+SECStatus legacy_AddSecmodDB(const char *appName, const char *filename, 
+			const char *dbname, char *module, PRBool rw);
+
+#define LGDB_STEP 10
+/*
+ * 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 = LGDB_STEP;
+
+    moduleList = (char **) PORT_ZAlloc(useCount*sizeof(char **));
+    if (moduleList == NULL) return NULL;
+
+    pkcs11db = lgdb_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 += LGDB_STEP;
+	    newModuleList =
+		(char **)PORT_Realloc(moduleList,useCount*sizeof(char *));
+	    if (newModuleList == NULL) goto done;
+	    moduleList = newModuleList;
+	    PORT_Memset(&moduleList[moduleCount+1],0,
+						sizeof(char *)*LGDB_STEP);
+	}
+	moduleString = lgdb_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 = NSSUTIL_Quote(params,'"');
+	if (newparams) {
+	    moduleList[0] = PR_smprintf(
+		NSSUTIL_DEFAULT_INTERNAL_INIT1 "%s" 
+		NSSUTIL_DEFAULT_INTERNAL_INIT2 "%s"
+		NSSUTIL_DEFAULT_INTERNAL_INIT3,
+		newparams, NSSUTIL_DEFAULT_SFTKN_FLAGS);
+	    PORT_Free(newparams);
+	}
+    }
+    /* deal with trust cert db here */
+
+    if (pkcs11db) {
+	lgdb_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 = lgdb_OpenDB(appName,filename,dbname,PR_FALSE,PR_FALSE);
+    if (pkcs11db == NULL) {
+	return SECFailure;
+    }
+
+    rv = lgdb_MakeKey(&key,args);
+    if (rv != SECSuccess) goto done;
+    rv = SECFailure;
+    ret = (*pkcs11db->del)(pkcs11db, &key, 0);
+    lgdb_FreeKey(&key);
+    if (ret != 0) goto done;
+
+
+    ret = (*pkcs11db->sync)(pkcs11db, 0);
+    if (ret == 0) rv = SECSuccess;
+
+done:
+    lgdb_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 = lgdb_OpenDB(appName,filename,dbname,PR_FALSE,PR_FALSE);
+    if (pkcs11db == NULL) {
+	return SECFailure;
+    }
+
+    rv = lgdb_MakeKey(&key,module);
+    if (rv != SECSuccess) goto done;
+    rv = lgdb_EncodeData(&data,module);
+    if (rv != SECSuccess) {
+	lgdb_FreeKey(&key);
+	goto done;
+    }
+    rv = SECFailure;
+    ret = (*pkcs11db->put)(pkcs11db, &key, &data, 0);
+    lgdb_FreeKey(&key);
+    lgdb_FreeData(&data);
+    if (ret != 0) goto done;
+
+    ret = (*pkcs11db->sync)(pkcs11db, 0);
+    if (ret == 0) rv = SECSuccess;
+
+done:
+    lgdb_CloseDB(pkcs11db);
+    return rv;
+}


More information about the Trustbridge-commits mailing list