summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorwchang0222%aol.com <devnull@localhost>2004-04-09 23:34:00 +0000
committerwchang0222%aol.com <devnull@localhost>2004-04-09 23:34:00 +0000
commit65ce80950b1ad5303ca0a22f3b490105f2041800 (patch)
tree96ad23c0d061d519ad8bb249669e7426f19e2fa3
parent81f997497d81398146f00f8b7d0f379bb4b87b06 (diff)
downloadnss-hg-65ce80950b1ad5303ca0a22f3b490105f2041800.tar.gz
Bug 238914. r=wchang sr=misterSSL a=chofmann for mozilla 1.7 final.
Agressive checks on database data before we actually reference it. This should prevent crashes because of invalid databases. Tag: NSS_3_9_BRANCH
-rw-r--r--security/nss/lib/softoken/pk11db.c240
1 files changed, 176 insertions, 64 deletions
diff --git a/security/nss/lib/softoken/pk11db.c b/security/nss/lib/softoken/pk11db.c
index 220e897ba..d9d003fa1 100644
--- a/security/nss/lib/softoken/pk11db.c
+++ b/security/nss/lib/softoken/pk11db.c
@@ -40,6 +40,7 @@
#include "pkcs11i.h"
#include "mcom_db.h"
#include "cdbhdl.h"
+#include "secerr.h"
#define FREE_CLEAR(p) if (p) { PORT_Free(p); p = NULL; }
@@ -510,6 +511,18 @@ secmod_FreeData(DBT *data)
}
}
+static void
+secmod_FreeSlotStrings(char **slotStrings, int count)
+{
+ int i;
+
+ for (i=0; i < count; i++) {
+ if (slotStrings[i]) {
+ PR_smprintf_free(slotStrings[i]);
+ }
+ }
+}
+
/*
* build a module from the data base entry.
*/
@@ -518,128 +531,227 @@ secmod_DecodeData(char *defParams, DBT *data, PRBool *retInternal)
{
secmodData *encoded;
secmodSlotData *slots;
- char *commonName = NULL,*dllName = NULL,*parameters = NULL;
+ PLArenaPool *arena;
+ char *commonName = NULL;
+ char *dllName = NULL;
+ char *parameters = NULL;
+ char *nss;
+ char *moduleSpec;
+ char **slotStrings = NULL;
unsigned char *names;
- unsigned short len;
unsigned long slotCount;
- unsigned short offset;
- PRBool isOldVersion = PR_FALSE;
- PRBool internal, isFIPS, isModuleDB=PR_FALSE, isModuleDBOnly=PR_FALSE;
- PRBool extended=PR_FALSE;
- PRBool hasRootCerts=PR_FALSE,hasRootTrust=PR_FALSE;
- unsigned long trustOrder=PK11_DEFAULT_TRUST_ORDER,
- cipherOrder=PK11_DEFAULT_CIPHER_ORDER;
- unsigned long ssl0=0, ssl1=0;
- char **slotStrings = NULL;
- unsigned long slotID,defaultFlags,timeout;
- char *nss,*moduleSpec;
+ unsigned long ssl0 =0;
+ unsigned long ssl1 =0;
+ unsigned long slotID;
+ unsigned long defaultFlags;
+ unsigned long timeout;
+ unsigned long trustOrder =PK11_DEFAULT_TRUST_ORDER;
+ unsigned long cipherOrder =PK11_DEFAULT_CIPHER_ORDER;
+ unsigned short len;
+ unsigned short namesOffset = 0; /* start of the names block */
+ unsigned short 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;
- PLArenaPool *arena;
arena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
- if (arena == NULL) return NULL;
+ 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 secmodData struct.
+ ** It may be an old or new version. Check the length fo each.
+ */
+
+ CHECK_SIZE( offsetof(secmodData, trustOrder[0]) );
encoded = (secmodData *)data->data;
- names = (unsigned char *)data->data;
- offset = SECMOD_GETSHORT(encoded->slotOffset);
- slots = (secmodSlotData *) (names + offset + 2);
- slotCount = SECMOD_GETSHORT(names + offset);
- names += SECMOD_GETSHORT(encoded->nameStart);
- * retInternal = internal = (encoded->internal != 0) ? PR_TRUE: PR_FALSE;
- isFIPS = (encoded->fips != 0) ? PR_TRUE: PR_FALSE;
- len = SECMOD_GETSHORT(names);
+ 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 == SECMOD_DB_NOUI_VERSION_MAJOR) &&
(encoded->minor <= SECMOD_DB_NOUI_VERSION_MINOR)) {
isOldVersion = PR_TRUE;
}
-
if ((encoded->major == SECMOD_DB_EXT1_VERSION_MAJOR) &&
(encoded->minor >= SECMOD_DB_EXT1_VERSION_MINOR)) {
- trustOrder = SECMOD_GETLONG(encoded->trustOrder);
- cipherOrder = SECMOD_GETLONG(encoded->cipherOrder);
- isModuleDB = (encoded->isModuleDB != 0) ? PR_TRUE: PR_FALSE;
+ CHECK_SIZE( sizeof(secmodData));
+ trustOrder = SECMOD_GETLONG(encoded->trustOrder);
+ cipherOrder = SECMOD_GETLONG(encoded->cipherOrder);
+ isModuleDB = (encoded->isModuleDB != 0) ? PR_TRUE: PR_FALSE;
isModuleDBOnly = (encoded->isModuleDBOnly != 0) ? PR_TRUE: PR_FALSE;
- extended = PR_TRUE;
+ extended = PR_TRUE;
}
-
if (internal && !extended) {
trustOrder = 0;
cipherOrder = 100;
}
+ /* decode SSL cipher enable flags */
+ ssl0 = SECMOD_GETLONG(encoded->ssl);
+ ssl1 = SECMOD_GETLONG(encoded->ssl + 4);
+
+ slotOffset = SECMOD_GETSHORT(encoded->slotOffset);
+ namesOffset = SECMOD_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 = SECMOD_GETSHORT(names+namesRunningOffset);
- /* decode the common name */
+ CHECK_SIZE( namesRunningOffset + 2 + len);
commonName = (char*)PORT_ArenaAlloc(arena,len+1);
- if (commonName == NULL) {
- PORT_FreeArena(arena,PR_TRUE);
- return NULL;
- }
- PORT_Memcpy(commonName,&names[2],len);
+ if (commonName == NULL)
+ goto loser;
+ PORT_Memcpy(commonName, names + namesRunningOffset + 2, len);
commonName[len] = 0;
+ namesRunningOffset += len + 2;
- /* decode the DLL name */
- names += len+2;
- len = SECMOD_GETSHORT(names);
+ /* copy the module's shared library file name. */
+ CHECK_SIZE( namesRunningOffset + 2);
+ len = SECMOD_GETSHORT(names + namesRunningOffset);
if (len) {
+ CHECK_SIZE( namesRunningOffset + 2 + len);
dllName = (char*)PORT_ArenaAlloc(arena,len + 1);
- if (dllName == NULL) {
- PORT_FreeArena(arena,PR_TRUE);
- return NULL;
- }
- PORT_Memcpy(dllName,&names[2],len);
+ 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) {
- names += len+2;
- len = SECMOD_GETSHORT(names);
+ CHECK_SIZE( namesRunningOffset + 2);
+ len = SECMOD_GETSHORT(names+namesRunningOffset);
if (len) {
+ CHECK_SIZE( namesRunningOffset + 2 + len );
parameters = (char*)PORT_ArenaAlloc(arena,len + 1);
- if (parameters == NULL) {
- PORT_FreeArena(arena,PR_TRUE);
- return NULL;
- }
- PORT_Memcpy(parameters,&names[2],len);
+ if (parameters == NULL)
+ goto loser;
+ PORT_Memcpy(parameters,names + namesRunningOffset + 2, len);
parameters[len] = 0;
}
+ namesRunningOffset += len + 2;
+ }
+
+ /*
+ * Consistancy 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;
+ }
}
- if (internal) {
- parameters = PORT_ArenaStrdup(arena,defParams);
+
+ /* ------------------------------------------------------------------
+ ** Part 3, process the slot table.
+ ** This part has this structure:
+ ** struct {
+ ** BYTE slotCount [ 2 ];
+ ** secmodSlotData [ slotCount ];
+ ** {
+ */
+
+ CHECK_SIZE( slotOffset + 2 );
+ slotCount = SECMOD_GETSHORT((unsigned char *)data->data + slotOffset);
+
+ /*
+ * Consistancy check: Part2. 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(secmodSlotData)) {
+ goto db_loser;
+ }
}
- /* decode SSL cipher enable flags */
- ssl0 = SECMOD_GETLONG(encoded->ssl);
- ssl1 = SECMOD_GETLONG(&encoded->ssl[4]);
+ CHECK_SIZE( (slotOffset + 2 + slotCount * sizeof(secmodSlotData)));
+ slots = (secmodSlotData *) ((unsigned char *)data->data + slotOffset + 2);
/* slotCount; */
- slotStrings = (char **)PORT_ArenaAlloc(arena, slotCount * sizeof(char *));
- for (i=0; i < (int) slotCount; i++) {
- slotID = SECMOD_GETLONG(slots[i].slotID);
- defaultFlags = SECMOD_GETLONG(slots[i].defaultFlags);
+ slotStrings = (char **)PORT_ArenaZAlloc(arena, slotCount * sizeof(char *));
+ for (i=0; i < (int) slotCount; i++, slots++) {
+ PRBool hasRootCerts =PR_FALSE;
+ PRBool hasRootTrust =PR_FALSE;
+ slotID = SECMOD_GETLONG(slots->slotID);
+ defaultFlags = SECMOD_GETLONG(slots->defaultFlags);
+ timeout = SECMOD_GETLONG(slots->timeout);
+ hasRootCerts = slots->hasRootCerts;
if (isOldVersion && internal && (slotID != 2)) {
unsigned long internalFlags=
pk11_argSlotFlags("slotFlags",SECMOD_SLOT_FLAGS);
defaultFlags |= internalFlags;
}
- timeout = SECMOD_GETLONG(slots[i].timeout);
- hasRootCerts = slots[i].hasRootCerts;
if (hasRootCerts && !extended) {
trustOrder = 100;
}
slotStrings[i] = pk11_mkSlotString(slotID, defaultFlags, timeout,
- (unsigned char)slots[i].askpw,
+ (unsigned char)slots->askpw,
hasRootCerts, hasRootTrust);
+ if (slotStrings[i] == NULL) {
+ secmod_FreeSlotStrings(slotStrings,i);
+ goto loser;
+ }
}
nss = pk11_mkNSS(slotStrings, slotCount, internal, isFIPS, isModuleDB,
- isModuleDBOnly, internal, trustOrder, cipherOrder, ssl0, ssl1);
+ isModuleDBOnly, internal, trustOrder, cipherOrder,
+ ssl0, ssl1);
+ secmod_FreeSlotStrings(slotStrings,slotCount);
+ if (nss == NULL)
+ goto loser;
moduleSpec = pk11_mkNewModuleSpec(dllName,commonName,parameters,nss);
PR_smprintf_free(nss);
PORT_FreeArena(arena,PR_TRUE);
+ return moduleSpec;
- return (moduleSpec);
+db_loser:
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+loser:
+ PORT_FreeArena(arena,PR_TRUE);
+ return NULL;
}