diff options
author | ian.mcgreer%sun.com <devnull@localhost> | 2003-07-09 22:25:00 +0000 |
---|---|---|
committer | ian.mcgreer%sun.com <devnull@localhost> | 2003-07-09 22:25:00 +0000 |
commit | 8e26d776b15a227eb11de0a7cc31e552ea07d02d (patch) | |
tree | 14ee1818753953e615398b82aa111c82e51a90af | |
parent | 5243f56515d2406157ad3bc04ed7de583aa5ffa1 (diff) | |
download | nss-hg-8e26d776b15a227eb11de0a7cc31e552ea07d02d.tar.gz |
introduce cert8.db on sun branch
-rw-r--r-- | security/nss/lib/certdb/cdbhdl.h | 4 | ||||
-rw-r--r-- | security/nss/lib/certdb/certdb.h | 6 | ||||
-rw-r--r-- | security/nss/lib/certdb/dbmshim.c | 671 | ||||
-rw-r--r-- | security/nss/lib/certdb/manifest.mn | 1 | ||||
-rw-r--r-- | security/nss/lib/certdb/pcertdb.c | 195 | ||||
-rw-r--r-- | security/nss/lib/nss/nssinit.c | 3 |
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; |