# HG changeset patch # User Andre Heinecke # 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, 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 +#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 + * + * 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 +#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 + +#ifdef __DBINTERFACE_PRIVATE + +#ifdef HAVE_SYS_CDEFS_H +#include +#else +#include "cdefs.h" +#endif + +#ifdef HAVE_SYS_BYTEORDER_H +#include +#endif + +#if defined(__linux) || defined(__BEOS__) +#include +#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 +#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 +#endif + +/* Digital Unix */ +#ifdef __osf__ +#include +#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 +#endif + +#ifdef __QNX__ +#ifdef __QNXNTO__ +#include +#else +#define LITTLE_ENDIAN 1234 +#define BIG_ENDIAN 4321 +#define BYTE_ORDER LITTLE_ENDIAN +#endif +#endif + +#ifdef SNI +/* #include */ +#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 + +#if defined(_WINDOWS) || defined(XP_OS2) +#include +#include + +#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 +#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 +#include +#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 + +/* + * 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 + +/* + * 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 . */ +#define MAX(_a,_b) ((_a)<(_b)?(_b):(_a)) +#endif +#ifndef MIN /* Usually found in . */ +#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 on an SGI */ +#if defined(XP_WIN32) || defined(_WIN32) +/* 32-bit stuff here */ +#include +#include +#ifdef __MINGW32__ +#include +#include +#else +#include +#include +#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 +#include +#include + + + +/* 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 +#else +#include +#endif + +#include +#include +#include +#include + +#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 +#include +#include +#include + +#include +#include + +/*#ifndef __EMX__ +#include +#endif */ + +#define INCL_DOSFILEMGR +#define INCL_DOSERRORS +#include + +#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 */ + 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@garfield), + * August 1897 + * + * Extended by Peter Lim (lim@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 +#else +#if defined(__IBMC__) || defined(__IBMCPP__) || defined(XP_W32_MSVC) +#include +#ifdef MAXPATHLEN + #undef MAXPATHLEN +#endif +#define MAXPATHLEN (FILENAME_MAX*4) +#define MAXNAMLEN FILENAME_MAX + +#else +#include +#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 +#endif + +#include +#include +#include +#include + +#ifdef DEBUG +#include +#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 +#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 +#ifndef macintosh +#include +#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 +#endif + +#if defined(macintosh) +#include +#endif + +#include +#include +#if defined(_WIN32) || defined(_WINDOWS) +#include +#endif +#include +#include +#include +#include + +#if !defined(_WIN32) && !defined(_WINDOWS) && !defined(macintosh) +#include +#endif + +#include + +#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 +#endif + +#if !defined(macintosh) +#ifdef XP_OS2 +#include +#endif +#include +#endif + +#if defined(macintosh) +#include +#include +#endif + +#include +#include +#include +#include +#include + +#if !defined(_WIN32) && !defined(_WINDOWS) && !defined(macintosh) +#include +#endif +#if defined(_WIN32) || defined(_WINDOWS) +#include +#endif + +#include + +#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@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 +#endif + +#include +#include +#include +#include +#include + +#ifdef DEBUG +#include +#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 +#else +#include "cdefs.h" +#endif +#include + +/* + * 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 +#else +#include +#include +#endif +#include +#include +#include +#include +#include "mcom_db.h" + +#ifndef _WINDOWS +#include +#endif + +#ifdef _WINDOWS +#include +#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 +#include +#include + +#ifdef HAVE_SYS_CDEFS_H +#include +#else +#include "cdefs.h" +#endif + +#include "prtypes.h" + +#include + +#ifdef __STDC__ +#include +#else +#include +#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 + +#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@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 + +#include +#ifdef STDC_HEADERS +#include +#else +#include +#endif + +#ifdef HAVE_MEMORY_H +#include +#endif +#include +#include +#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, ¶m, + 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, ¶m, NSSLOWPasswordParamTemplate, + &edi.algorithm.parameters); + if (rv != SECSuccess) { + goto loser; + } + rv = SECITEM_CopyItem(NULL, salt, ¶m.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, + ¬Before->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, + ¬After->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, ¬BeforeA, ¬AfterA); + if ( rv != SECSuccess ) { + return(PR_FALSE); + } + + rv = nsslowcert_GetCertTimes(certb, ¬BeforeB, ¬AfterB); + 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 + +#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,¶m,&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; +}