summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorian.mcgreer%sun.com <devnull@localhost>2003-07-09 22:25:00 +0000
committerian.mcgreer%sun.com <devnull@localhost>2003-07-09 22:25:00 +0000
commit8e26d776b15a227eb11de0a7cc31e552ea07d02d (patch)
tree14ee1818753953e615398b82aa111c82e51a90af
parent5243f56515d2406157ad3bc04ed7de583aa5ffa1 (diff)
downloadnss-hg-8e26d776b15a227eb11de0a7cc31e552ea07d02d.tar.gz
introduce cert8.db on sun branch
-rw-r--r--security/nss/lib/certdb/cdbhdl.h4
-rw-r--r--security/nss/lib/certdb/certdb.h6
-rw-r--r--security/nss/lib/certdb/dbmshim.c671
-rw-r--r--security/nss/lib/certdb/manifest.mn1
-rw-r--r--security/nss/lib/certdb/pcertdb.c195
-rw-r--r--security/nss/lib/nss/nssinit.c3
6 files changed, 870 insertions, 10 deletions
diff --git a/security/nss/lib/certdb/cdbhdl.h b/security/nss/lib/certdb/cdbhdl.h
index c195eb308..6a9f11409 100644
--- a/security/nss/lib/certdb/cdbhdl.h
+++ b/security/nss/lib/certdb/cdbhdl.h
@@ -54,4 +54,8 @@ struct CERTCertDBHandleStr {
PZMonitor *dbMon;
};
+DB *
+dbsopen(const char *dbname, int flags, int mode, DBTYPE type,
+ const void *userData);
+
#endif
diff --git a/security/nss/lib/certdb/certdb.h b/security/nss/lib/certdb/certdb.h
index 2d75ee8b5..7da572099 100644
--- a/security/nss/lib/certdb/certdb.h
+++ b/security/nss/lib/certdb/certdb.h
@@ -41,7 +41,8 @@
*/
/* version number of certificate database */
-#define CERT_DB_FILE_VERSION 7
+#define CERT_DB_FILE_VERSION 8
+#define CERT_DB_V7_FILE_VERSION 7
#ifdef USE_NS_ROOTS
#define CERT_DB_CONTENT_VERSION 28
#else
@@ -69,7 +70,8 @@ typedef enum {
certDBEntryTypeRevocation = 4,
certDBEntryTypeKeyRevocation = 5,
certDBEntryTypeSMimeProfile = 6,
- certDBEntryTypeContentVersion = 7
+ certDBEntryTypeContentVersion = 7,
+ certDBEntryTypeBlob = 8
} certDBEntryType;
typedef struct {
diff --git a/security/nss/lib/certdb/dbmshim.c b/security/nss/lib/certdb/dbmshim.c
new file mode 100644
index 000000000..b469a80f4
--- /dev/null
+++ b/security/nss/lib/certdb/dbmshim.c
@@ -0,0 +1,671 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1994-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL. If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ */
+
+/*
+ * Berkeley DB 1.85 Shim code to handle blobs.
+ *
+ * $Id$
+ */
+
+#include "prerror.h"
+#include "prprf.h"
+#include "cert.h"
+#include "mcom_db.h"
+#include "certdb.h"
+#include "secitem.h"
+#include "secder.h"
+
+/* Call to PK11_FreeSlot below */
+
+#include "secasn1.h"
+#include "secerr.h"
+#include "nssb64.h"
+#include "blapi.h"
+#include "sechash.h"
+
+#ifdef macintosh
+#define PATH_SEPARATOR ":"
+#else
+#define PATH_SEPARATOR "/"
+#endif
+
+#define NO_RDONLY O_RDONLY
+#define NO_RDWR O_RDWR
+#define NO_CREATE (O_RDWR | O_CREAT | O_TRUNC)
+
+/*
+ * 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 (addr) {
+ if (mapfile) {
+ PORT_Assert(len != -1);
+ PR_MemUnmap(addr,len);
+ } else {
+ PORT_Free(addr);
+ }
+ }
+ 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 && dbsp->blobdir) {
+ PORT_Free(dbsp->blobdir);
+ }
+ if (dbsp) {
+ PORT_Free(dbsp);
+ }
+ return NULL;
+}
diff --git a/security/nss/lib/certdb/manifest.mn b/security/nss/lib/certdb/manifest.mn
index 1cb6ff4b7..578faeea4 100644
--- a/security/nss/lib/certdb/manifest.mn
+++ b/security/nss/lib/certdb/manifest.mn
@@ -56,6 +56,7 @@ CSRCS = \
$(CERTINIT) \
certxutl.c \
crl.c \
+ dbmshim.c \
genname.c \
pcertdb.c \
polcyxtn.c \
diff --git a/security/nss/lib/certdb/pcertdb.c b/security/nss/lib/certdb/pcertdb.c
index 5bef6128c..28d672dce 100644
--- a/security/nss/lib/certdb/pcertdb.c
+++ b/security/nss/lib/certdb/pcertdb.c
@@ -238,7 +238,11 @@ ReadDBEntry(CERTCertDBHandle *handle, certDBEntryCommon *entry,
goto loser;
}
buf = (unsigned char *)data.data;
- if ( buf[0] != (unsigned char)CERT_DB_FILE_VERSION ) {
+ /* version 7 uses the shame scheme, we may be using a v7 db if we
+ * opened the dbs 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;
}
@@ -445,6 +449,7 @@ DecodeDBCertEntry(certDBEntryCert *entry, SECItem *dbentry)
lenoff = 3;
break;
case 7:
+ case 8:
headerlen = DB_CERT_ENTRY_HEADER_LEN;
lenoff = 6;
break;
@@ -3774,6 +3779,144 @@ loser:
return(NULL);
}
+/* forward declaration */
+static SECStatus
+UpdateV7DB(CERTCertDBHandle *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(CERTCertDBHandle *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(CERTCertDBHandle *handle, DB *updatedb)
+{
+ DBT key, data;
+ int ret;
+ CERTCertificate *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:
+ /*default: */
+ break;
+
+ case certDBEntryTypeCert:
+ /* decode Entry */
+ certEntry.common.version = (unsigned int)dataBuf[0];
+ certEntry.common.type = entryType;
+ certEntry.common.flags = (unsigned int)dataBuf[2];
+ certEntry.common.arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (certEntry.common.arena == NULL) {
+ break;
+ }
+ rv = DecodeDBCertEntry(&certEntry,&dbEntry);
+ if (rv != SECSuccess) {
+ break;
+ }
+ /* should we check for existing duplicates? */
+ cert = CERT_DecodeDERCertificate(&certEntry.derCert, PR_FALSE,
+ certEntry.nickname);
+ if (cert) {
+ AddCertToPermDB(handle, cert, certEntry.nickname,
+ &certEntry.trust);
+ CERT_DestroyCertificate(cert);
+ }
+ /* free data allocated by the decode */
+ PORT_FreeArena(certEntry.common.arena, PR_FALSE);
+ certEntry.common.arena = NULL;
+ 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;
+ }
+
+ rv = WriteDBCrlEntry(handle, &crlEntry);
+ if (rv != SECSuccess) {
+ break;
+ }
+ /* free data allocated by the decode */
+ PORT_FreeArena(crlEntry.common.arena, PR_FALSE);
+ crlEntry.common.arena = NULL;
+ break;
+
+ case certDBEntryTypeSMimeProfile:
+ 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);
+ rv = DecodeDBSMimeEntry(&smimeEntry,&dbEntry,(char *)dbKey.data);
+ /* decode entry */
+ WriteDBSMimeEntry(handle, &smimeEntry);
+ PORT_FreeArena(smimeEntry.common.arena, PR_FALSE);
+ smimeEntry.common.arena = NULL;
+ break;
+ }
+ } 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 */
+ 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.
@@ -4261,7 +4404,7 @@ SEC_OpenPermCertDB(CERTCertDBHandle *handle, PRBool readOnly,
/*
* first open the permanent file based database.
*/
- handle->permCertDB = dbopen( certdbname, openflags, 0600, DB_HASH, 0 );
+ handle->permCertDB = dbsopen( certdbname, openflags, 0600, DB_HASH, 0 );
/* check for correct version number */
if ( handle->permCertDB ) {
@@ -4284,14 +4427,34 @@ SEC_OpenPermCertDB(CERTCertDBHandle *handle, PRBool readOnly,
/* if first open fails, try to create a new DB */
if ( handle->permCertDB == NULL ) {
- /* don't create if readonly */
if ( readOnly ) {
- goto loser;
+ /* if opening read-only and cert8.db does not exist,
+ * use cert7.db
+ */
+ tmpname = (* namecb)(cbarg, 7); /* get v7 db name */
+ if (!tmpname) {
+ goto loser;
+ }
+ handle->permCertDB = dbopen(tmpname, O_RDONLY, 0600, DB_HASH, 0);
+ PORT_Free(tmpname);
+ if (!handle->permCertDB) {
+ goto loser;
+ }
+ versionEntry = ReadDBVersionEntry(handle);
+ if ( versionEntry == NULL ) {
+ /* no version number */
+ goto loser;
+ } else if ( versionEntry->common.version != 7 ) {
+ DestroyDBEntry((certDBEntry *)versionEntry);
+ goto loser;
+ }
+ PORT_Free(certdbname);
+ return SECSuccess;
}
-
- handle->permCertDB = dbopen(certdbname,
- O_RDWR | O_CREAT | O_TRUNC,
- 0600, DB_HASH, 0);
+ /* create a new database */
+ handle->permCertDB = dbsopen(certdbname,
+ O_RDWR | O_CREAT | O_TRUNC,
+ 0600, DB_HASH, 0);
/* if create fails then we lose */
if ( handle->permCertDB == 0 ) {
@@ -4312,6 +4475,21 @@ SEC_OpenPermCertDB(CERTCertDBHandle *handle, PRBool readOnly,
}
/* try to upgrade old db here */
+ tmpname = (* namecb)(cbarg, 7); /* get v7 db name */
+ if ( tmpname ) {
+ updatedb = dbopen( tmpname, O_RDONLY, 0600, DB_HASH, 0 );
+ PORT_Free(tmpname);
+ if ( updatedb ) {
+ rv = UpdateV7DB(handle, updatedb);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+ updated = PR_TRUE;
+ goto update_finished;
+ }
+ }
+
+ /* try to upgrade old db here */
tmpname = (* namecb)(cbarg, 6); /* get v6 db name */
if ( tmpname ) {
updatedb = dbopen( tmpname, O_RDONLY, 0600, DB_HASH, 0 );
@@ -4358,6 +4536,7 @@ SEC_OpenPermCertDB(CERTCertDBHandle *handle, PRBool readOnly,
}
}
}
+update_finished:
/* initialize the database with our well known certificates
* or in the case of update, just fall down to CERT_AddNewCerts()
diff --git a/security/nss/lib/nss/nssinit.c b/security/nss/lib/nss/nssinit.c
index 8ee45528d..849e33bb0 100644
--- a/security/nss/lib/nss/nssinit.c
+++ b/security/nss/lib/nss/nssinit.c
@@ -72,6 +72,9 @@ nss_certdb_name_cb(void *arg, int dbVersion)
const char *dbver;
switch (dbVersion) {
+ case 8:
+ dbver = "8";
+ break;
case 7:
dbver = "7";
break;