diff options
Diffstat (limited to 'security/nss/lib/softoken/dbmshim.c')
-rw-r--r-- | security/nss/lib/softoken/dbmshim.c | 664 |
1 files changed, 0 insertions, 664 deletions
diff --git a/security/nss/lib/softoken/dbmshim.c b/security/nss/lib/softoken/dbmshim.c deleted file mode 100644 index 04c291d7f..000000000 --- a/security/nss/lib/softoken/dbmshim.c +++ /dev/null @@ -1,664 +0,0 @@ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is the Netscape security libraries. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1994-2000 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -/* - * Berkeley DB 1.85 Shim code to handle blobs. - * - * $Id$ - */ -#include "mcom_db.h" -#include "secitem.h" -#include "secder.h" -#include "prprf.h" -#include "cdbhdl.h" - -/* Call to SFTK_FreeSlot below */ - -#include "pcertt.h" -#include "secasn1.h" -#include "secerr.h" -#include "nssb64.h" -#include "blapi.h" -#include "sechash.h" - -#include "pkcs11i.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 (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; -} |