/*
* These functions keep track of the card state so we don't have to keep
* querying the card.
*/
#include "sec.h"
#include "sslimpl.h"
#include "fortezza.h"
#include "fortdlgs.h"
#include "ssl.h"
#include "certdb.h"
#include "rsa.h" /* for big nums defines */
#include "prtime.h"
#ifdef USE_NSPR_MT
#include "nspr/prmon.h"
#endif
#if defined(SERVER_BUILD) && defined(XP_UNIX)
#undef PRIVATE
#include <sys/mman.h>
#endif
extern int SEC_ERROR_IO;
extern int XP_SEC_FORTEZZA_BAD_CARD;
FortezzaGlobal fortezzaGlobal = { 0 };
static void FortezzaRegisterFree(FortezzaPCMCIASocket *psock,FortezzaKey *key);
static int FortezzaFindCertIndexByNickname(FortezzaPCMCIASocket *p,char *c);
/*
* These arrays are index by FortezzaCryptType. Changing the enum would
* require changes to these arrays. Internal means save in the card's internal
* storage. External means save to an external data area.
*/
unsigned int CI_External[] = {
CI_ENCRYPT_EXT_TYPE,
CI_DECRYPT_EXT_TYPE,
CI_HASH_EXT_TYPE
};
/* return true if the cert for this bit is set in the cert flags */
static PRBool
FortezzaCertBits(unsigned char *bits, int cert) {
int bitIndex = cert >> 3; /* cert div 8 */
int bitLoc = 0x80 >> (cert & 0x7); /* cert mod 8 */
return ((bits[bitIndex] & bitLoc) == bitLoc);
}
/* add a cert to the card */
static void
FortezzaSetCertBits(unsigned char *bits, int cert) {
int bitIndex = cert >> 3; /* cert div 8 */
int bitLoc = 0x80 >> (cert & 0x7); /* cert mod 8 */
bits[bitIndex] |= bitLoc;
}
/* remove a cert from the card */
static void
FortezzaClrCertBits(unsigned char *bits, int cert) {
int bitIndex = cert >> 3; /* cert div 8 */
int bitLoc = 0x80 >> (cert & 0x7); /* cert mod 8 */
bits[bitIndex] &= ~bitLoc;
}
/* Card Responses */
/* Library Errors */
static char *errorStr[] = {
"CI_SRVR_ERROR",
"CI_LIB_ALRDY_INIT",
"CI_INVALID_FUNCTION",
"Unknown",
"Unknown",
"Unknown",
"Unknown",
"CI_NOT_A_CRYPTO_CARD",
"CI_BAD_TUPLES",
"CI_SOCKET_NOT_OPENED",
"CI_NO_SOCKET",
"CI_SOCKET_IN_USE",
"CI_INV_SOCKET_INDEX",
"Unknown",
"Unknown",
"CI_BAD_ADDR",
"CI_BAD_IOSEEK",
"CI_BAD_FLUSH",
"CI_BAD_WRITE",
"CI_BAD_SEEK",
"CI_BAD_READ",
"CI_BAD_IOCTL",
"CI_BAD_CARD",
"Unknown",
"Unknown",
"Unknown",
"Unknown",
"Unknown",
"Unknown",
"CI_NO_SCTSRV",
"CI_NO_CRDSRV",
"CI_NO_DRIVER",
"CI_NO_CARD",
"Unknown",
"Unknown",
"Unknown",
"Unknown",
"Unknown",
"Unknown",
"CI_OUT_OF_RESOURCES",
"CI_BAD_PARAMETER",
"CI_NO_EXECUTE",
"CI_NO_ENCRYPT",
"CI_NO_DECRYPT",
"CI_BAD_SIZE",
"CI_NULL_PTR",
"CI_OUT_OF_MEMORY",
"CI_TIME_OUT",
"CI_CARD_IN_USE",
"CI_CARD_NOT_READY",
"CI_LIB_NOT_INIT",
"CI_ERROR",
"CI_OK",
"CI_FAIL",
"CI_CHECKWORD_FAIL",
"CI_INV_TYPE",
"CI_INV_MODE",
"CI_INV_KEY_INDEX",
"CI_INV_CERT_INDEX",
"CI_INV_SIZE",
"CI_INV_HEADER",
"CI_INV_STATE",
"CI_EXEC_FAIL",
"CI_NO_KEY",
"CI_NO_IV",
"CI_NO_X",
"Unknown",
"CI_NO_SAVE",
"CI_REG_IN_USE",
"CI_INV_COMMAND",
"CI_INV_POINTER",
"CI_BAD_CLOCK",
"CI_NO_DSA_PARMS",
};
static int errMax = sizeof(errorStr)/sizeof(errorStr[0]);
#define ERROFF 52
/*
* Invalidate all the keys and context structs associated with a card that
* has gone bad, or has been removed.
*
* Fortezza Error Must be called with CI_lock (to protect cx->state).
* On servers FortezzaError logs error conditions.
*/
static void
FortezzaError(int error,FortezzaContext *cx,FortezzaPCMCIASocket *psock) {
FortezzaContext *current;
CI_PERSON *personalityList;
SECItem *certList;
CERTCertificate *rootCert;
HSESSION hs = psock->maci_session;
int i;
/*
* card errors. often caused by bad data sent from
* our peer. Only reset our connection, not our card.
*/
if (error != CI_OK) PORT_SetError(SEC_ERROR_IO);
if (error > 0) {
if (fortezzaGlobal.fortezzaAlert != NULL) {
fortezzaGlobal.fortezzaAlert(fortezzaGlobal.fortezzaAlertArg,
PR_FALSE,"Fortezza Card Error : %d (%s)\n", error,
(error + ERROFF)<errMax ? errorStr[error+ERROFF]
: "unknown");
}
/* SSL needs to free it */
if (cx) cx->state = BadCard;
return;
}
/* now handle library errors. These include errors about card
* insertion and what not. If these happen mark the whole socket
* as down */
if (error < 0) {
if (fortezzaGlobal.fortezzaAlert != NULL) {
fortezzaGlobal.fortezzaAlert(fortezzaGlobal.fortezzaAlertArg,
PR_FALSE,"Fortezza Library Error : %d (%s)\n", error,
(error +ERROFF) > 0 ? errorStr[error+ERROFF]
: "unkown");
}
}
/* negative errors are library errors */
/* tell everyone that we're gone */
FORTEZZALOCK(psock->stateLock,psock->stateLockInit);
psock->state = NoCard;
PORT_Memset(psock->serial,0,sizeof(psock->serial));
psock->load = 0;
FORTEZZAUNLOCK(psock->stateLock);
if (fortezzaGlobal.defaultSocket == psock) {
fortezzaGlobal.defaultSocket = NULL;
}
MACI_Close(hs,CI_POWER_DOWN_FLAG,psock->socketId);
/* ok, first we invalid all the keys */
FORTEZZAPLOCK(psock->keyLock);
for (i=0; i < psock->registerCount; i++) {
if (psock->registers[i]) {
FortezzaRegisterFree(psock,psock->registers[i]);
}
psock->registers[i] = NULL;
}
psock->registerCount = 0;
FORTEZZAPUNLOCK(psock->keyLock);
/* now dettach each context and mark them dead , as the
* ssl sockets they are attached to disappear, they will
* get freed */
FORTEZZALOCK(psock->listLock,psock->listLockInit);
for (current = psock->head; current; current = current->next) {
current->state = BadCard;
}
psock->head=NULL;
FORTEZZAUNLOCK(psock->listLock);
/* we kill the cached certs and personalities */
FORTEZZALOCK(psock->certLock,psock->certLockInit);
certList = psock->certificateList;
personalityList = psock->personalityList;
rootCert = psock->rootCert;
psock->certificateList = NULL;
psock->personalityList = NULL;
psock->rootCert = NULL;
psock->certificateCount = 0;
FORTEZZAUNLOCK(psock->certLock);
if (rootCert) CERT_DestroyCertificate(rootCert);
if (personalityList) PORT_Free(personalityList);
if (certList) PORT_Free(certList);
/* finally we decode the error */
if (error != CI_OK) {
/* clear out XP_WouldBlock */
PORT_SetError(0); /* need to get and error code & map it*/
}
}
/*
* Save the current crypto state.
*
* Environment: Caller must hold CI_LOCK();
*
*/
static SECStatus
FortezzaSaveState(FortezzaPCMCIASocket *psock,FortezzaContext *cx) {
int error = CI_OK;
HSESSION hs = psock->maci_session;
/*
* It looks like we need our personality set no matter what...
* so let's just set it
*/
error = MACI_Save(hs,CI_External[cx->type],cx->save);
if (error != CI_OK) goto loser;
cx->state = Saved;
return SECSuccess;
loser:
FortezzaError(error,cx,psock);
return SECFailure;
}
/*
* restore a saved state. This routine is a lot simplier than it's
* previous incarnation, which would keep track of which state is current.
* It turns out that sneazing will trash your state, so we need to save
* and restore our states early rather than delay.
*
* Environment: Caller must hold CI_LOCK();
*
*/
static SECStatus
FortezzaRestoreState(FortezzaPCMCIASocket *psock,FortezzaContext *cx) {
int error = CI_OK;
HSESSION hs = psock->maci_session;
FortezzaIV bogus_iv;
if (cx->theKey == NULL) return SECFailure;
/*
* It looks like we need our personality set no matter what...
* so let's just set it
*/
error = MACI_SetPersonality(hs,cx->certificate);
if (error != CI_OK) goto loser;
/*
* We need to restore our state based on
* where it is. It can be in 1 of 5 places:
* 3) Saved - the state is saved in the context data structure.
* 4) Init - no state has been started yet.
*/
switch (cx->state) {
case ServerInit:
/* This is on purely to check Key escrow on server->client
* write */
error = MACI_LoadIV(hs,cx->iv);
if (error != CI_OK) {
break;
}
if (cx->type == Encrypt) {
/* OK, now make Spyrus cards happy */
error = MACI_SetKey(hs,cx->theKey->keyRegister);
if (error != CI_OK) break;
error = MACI_GenerateIV(hs,cx->save);
}
break;
case Saved:
/*
* Sigh, the cards need to have some state bits set because
* save and restore don't necessarily save all the state.
* Instead of fixing the cards, they decided to change the
* protocol :(.
*/
if (cx->type == Encrypt) {
/* OK, now make Spyrus cards happy */
error = MACI_SetKey(hs,cx->theKey->keyRegister);
if (error != CI_OK) break;
error = MACI_GenerateIV(hs,bogus_iv);
if (error != CI_OK) break;
} else if (cx->type == Decrypt) {
error = MACI_LoadIV(hs,cx->iv);
if (error != CI_OK) break;
}
error = MACI_Restore(hs,CI_External[cx->type],cx->save);
break;
case Init:
error = MACI_LoadIV(hs,cx->iv);
break;
default:
/* Shouldn't happen, set the error to something
* else that shouldn't happen as well.. */
error = CI_INV_POINTER;
break;
}
if (error != CI_OK) goto loser;
return SECSuccess;
loser:
FortezzaError(error,cx,psock);
return SECFailure;
}
/*
* pull more keys into the into the keyList.
* The keyListLock must be held with this routine is called
* NOTE: There is currently not Garbage collection. Once keys are allocated
* they stay forever. Also NOTE: on the server (with shared memory) there is
* no way to implement garbage collection. Once we touch a page of memory,
* We can't un-touch it without freeing all of the shared memory segment.
*/
static FortezzaKey *FortezzaGetMoreKeys(unsigned char *mem_segment) {
unsigned int count = (NEW_KEY_COUNT+1)*sizeof(FortezzaKey);
unsigned char *maddr;
int i;
#if defined(SERVER_BUILD) && defined(XP_UNIX)
unsigned int len;
len = (unsigned int) *(unsigned long *)mem_segment;;
maddr = mem_segment + len;
if (len + count >= FORTEZZA_MAX_SHARED_MEM) {
return NULL;
}
*(unsigned long *)mem_segment = (unsigned long)(len + count);
#else
FORTEZZAPUNLOCK(fortezzaGlobal.keyListLock);
maddr = PORT_Alloc(count);
if (maddr == NULL) {
return NULL;
}
FORTEZZAPLOCK(fortezzaGlobal.keyListLock);
#endif
for (i=0; i < NEW_KEY_COUNT; i++) {
FortezzaKey *key = (FortezzaKey *)maddr;
maddr += sizeof(FortezzaKey);
key->next = fortezzaGlobal.keyTop;
key->id = 0;
fortezzaGlobal.keyTop = key;
}
return (FortezzaKey *)maddr;
}
/*
* Allocate a new key.
* On multi-process servers key's need to be allocated out of shared memory.
* Since we need to allocate keys off lists anyway, we use the same structure
* for allocating keys for clients. Note: there is no garbage collection.
* Once memory has been put to getting keys, it will never be freed.
*/
FortezzaKey *FortezzaKeyAlloc(FortezzaKEAContext *cx) {
FortezzaKey *key;
unsigned long id;
FORTEZZAPLOCK(fortezzaGlobal.keyListLock);
/* get a uniq non-zero keyid... depends on the fact that old keys
* will get freed before an unsigned long will wrap... only Servers
* actuall care about these */
id = (*fortezzaGlobal.keyid)++;
if (id == 0) id = (*fortezzaGlobal.keyid)++;
/* now get a key off the list */
key = fortezzaGlobal.keyTop;
if (key) {
fortezzaGlobal.keyTop = key->next;
key->next = NULL;
} else {
/* no key on the list, get more keys into the list */
key = FortezzaGetMoreKeys(fortezzaGlobal.memaddr);
}
FORTEZZAPUNLOCK(fortezzaGlobal.keyListLock);
if (key) {
key->keyRegister = KeyNotLoaded;
key->hitCount = 0;
key->keyType = NOKEY;
key->ref_count = 1;
key->socket = cx->socket;
key->id = id;
key->cached = PR_FALSE;
}
return key;
}
/*
* Tek's have extra fields to initialize. Most of the work is done by
* FortezzaKeyAlloc.
*/
FortezzaKey *FortezzaTEKAlloc(FortezzaKEAContext *cx) {
FortezzaKey *tek;
tek = FortezzaKeyAlloc(cx);
if (tek) {
tek->keyType = TEK;
tek->keyData.tek.destroy_certificate = 0;
}
return tek;
}
/*
* There are only a fixed number (usually 10) of key registers on the
* fortezza card. We manage these registers based on an LRU sheme.
* Each key structure carries enough information to reload itself if
* necessary. FortezzaRegisterAlloc finds a free slot, or if there is
* no free slot, throw out the oldest key.
*
* NOTE: maci is also managing the keys, those keys we don't know anything
* about. That's ok because there aren't likely going to be very many others
* using the server's cards at the same time as the server!
*
* Environment: Caller must hold CI_LOCK();
*/
static int
FortezzaRegisterAlloc(FortezzaPCMCIASocket *psock,
FortezzaContext *cx,FortezzaKey *key) {
int i;
uint32 minHitCount= 0xffffffff; /* MAX INT */
FortezzaKeyRegister *registers;
HSESSION hs = psock->maci_session;
FortezzaTEK *tek;
int candidate = 1;
int tek_reg = 0;
int error;
/* paranoia */
if (key == NULL) return KeyNotLoaded;
/* register '0' is reserved, so we start by looking at register '1' */
FORTEZZAPLOCK(psock->keyLock);
registers = psock->registers;
if (registers == NULL) {
FORTEZZAPUNLOCK(psock->keyLock);
return KeyNotLoaded;
}
for (i=1; i < psock->registerCount; i++) {
/* Ok, we found an emply slot */
if (registers[i] == NULL) {
candidate = i;
break;
}
/* If this one is the oldest, he may be the one we toss */
if (registers[i]->hitCount < minHitCount) {
minHitCount = registers[i]->hitCount;
candidate = i;
}
}
if (registers[candidate] != NULL) {
registers[candidate]->keyRegister = KeyNotLoaded;
registers[candidate] = NULL;
}
/* the key is loaded, update our data structures to reflect reality */
key->keyRegister = candidate;
registers[candidate] = key;
/* definately have touched this key */
key->hitCount = psock->hitCount++;
FORTEZZAPUNLOCK(psock->keyLock);
MACI_DeleteKey(hs,candidate);
switch (key->keyType) {
case TEKMEK:
/* First make sure the tek is loaded */
tek_reg = key->keyData.tek_mek.wrappingTEK->keyRegister;
if (tek_reg == KeyNotLoaded) {
tek_reg = FortezzaRegisterAlloc(psock,cx,
key->keyData.tek_mek.wrappingTEK);
}
/* now unwrap the key */
error = MACI_UnwrapKey(hs,tek_reg,candidate,
key->keyData.tek_mek.TEK_M);
if (error != CI_OK) {
break;
}
/* now convert it to a Ks wrapped mek */
FortezzaTEKFree(key->keyData.tek_mek.wrappingTEK);
key->keyType = MEK;
error = MACI_WrapKey(hs,0,candidate,key->keyData.mek.Ks_M);
break;
case MEK:
error = MACI_UnwrapKey(hs,0,candidate,key->keyData.mek.Ks_M);
break;
case TEK:
tek= &key->keyData.tek;
error = MACI_SetPersonality(hs,tek->certificate);
if (error == CI_OK) {
error = MACI_GenerateTEK(hs,tek->flags,candidate,
tek->R_a,tek->R_b,tek->Y_bSize,tek->Y_b);
}
break;
/* get a slot to Generate or load an MEK into */
case NOKEY:
error = CI_OK;
break;
default:
error = -1;
break;
}
if (error != CI_OK) {
/* this clears out all the key structures */
FortezzaError(error,cx,psock);
return KeyNotLoaded;
}
return candidate;
}
/*
* When we're through with a key, free up the slot.
*
* Enviroment: Caller must hold psock->keylock.
*/
static void
FortezzaRegisterFree(FortezzaPCMCIASocket *psock,FortezzaKey *key) {
int reg = key->keyRegister;
key->keyRegister = KeyNotLoaded;
if (reg != KeyNotLoaded) {
if (reg < psock->registerCount) {
PORT_Assert(psock->registers[reg] == key);
psock->registers[reg] = NULL;
}
}
}
/*
* pre-encrypted files use this. It takes a K_s wrapped key (from some file)
* and rewappes it with the session tek.
*/
SECStatus
FortezzaRewrapKey(FortezzaKEAContext *kea_cx, FortezzaSymetric inkey,
FortezzaSymetric wrapped_key)
{
FortezzaPCMCIASocket *psock;
int key_reg, tek_reg, error;
FortezzaKey *key, *tek;
HSESSION hs;
psock = (FortezzaPCMCIASocket *)kea_cx->socket;
if (psock == NULL) goto loser;
hs = psock->maci_session;
tek = (FortezzaKey *)kea_cx->tek;
if (tek == NULL) goto loser;
/* allocate a key slot to use for unwrapping and wrapping */
key = FortezzaKeyAlloc(kea_cx);
if (key == NULL) goto loser;
CI_LOCK(psock);
key_reg = FortezzaRegisterAlloc(psock, NULL, key);
if (key_reg == KeyNotLoaded) goto loser;
FortezzaRestoreKey(psock, NULL, tek);
tek_reg = tek->keyRegister;
if (tek_reg == KeyNotLoaded) goto loser;
/* unwrap the local Ks wrapped MEK */
error = MACI_UnwrapKey(hs,0, key_reg, inkey);
/* rewrap the MEK with the session TEK */
error = MACI_WrapKey(hs,tek_reg, key_reg, wrapped_key);
CI_UNLOCK(psock);
FortezzaFreeKey(key);
if (error != CI_OK) {
FortezzaError(error, NULL, psock);
return(SECFailure);
}
return(SECSuccess);
loser:
CI_UNLOCK(psock);
return SECFailure;
}
/*
* restore the current key to a key register and set it.
* Question, do I really need to send the whole context just to restore
* a single key? Does it hurt?
*
* Environment: Caller must hold CI_LOCK()
*/
SECStatus
FortezzaRestoreKey(FortezzaPCMCIASocket *psock,FortezzaContext *cx,
FortezzaKey *key) {
int keyRegister;
int error;
/* first make sure we have our key in place */
keyRegister = key->keyRegister;
if (keyRegister == KeyNotLoaded) {
keyRegister = FortezzaRegisterAlloc(psock,cx,key);
/* FortezzaRegisterAlloc has already loaded our key.
* if there was an error, he has already called
* the fortezza error routine.
*/
if (keyRegister == KeyNotLoaded) {
goto loser;
}
}
/* it looks like the key setting can be trashed pretty easily,
* so for now we always just set the Key */
error= MACI_SetKey(psock->maci_session,keyRegister);
if (error != CI_OK) {
FortezzaError(error,cx,psock);
goto loser;
}
/* we've touched the key ... question is 32 bits long enough */
key->hitCount = psock->hitCount++;
return SECSuccess;
loser:
return SECFailure;
}
/*
* this does an encrypt, decrypt, or hash depending on the value of in
* the context.
*/
SECStatus Fortezza_Op(FortezzaContext *cx, unsigned char *out, unsigned *part,
unsigned int maxOut, unsigned char *in, unsigned inLen)
{
FortezzaPCMCIASocket *psock;
int error = CI_OK;
unsigned tmpSize = 0;
unsigned char *tmp = NULL;
unsigned char *loopin,*loopout;
unsigned left;
PRBool encrypt_hack = PR_FALSE;
SECStatus rv;
HSESSION hs;
static unsigned char pad[] =
{ 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef };
/*
* CI_LOCK locks the whole CI_Library. Since is is such a
* coarse grain lock, we use smaller grain locks on some of
* the key data structures. To prevent dead locks, you cannot
* call CI_LOCK() while you are already holding a finer grain
* lock.
*/
/* the card has been removed on this stream, no way to
* recover. Just keep yelling until the upper level
* code gives up and frees us.
*/
if ((cx->state == BadCard) || (cx->state == NotInit)) {
if (part) *part = 0;
return SECFailure;
}
psock = cx->socket;
if (psock->state == NoCard) {
if (part) *part = 0;
return SECFailure;
}
hs = psock->maci_session;
CI_LOCK(psock);
#ifdef notdef
/*
* Always force a select!
*/
error = MACI_Select(hs,psock->socketId);
if (error != CI_OK) {
FortezzaError(error,cx,psock);
goto loser;
}
#endif
/*
* restore the key if necessary
*/
if (cx->type != Hash) {
if (FortezzaRestoreKey(psock,cx,cx->theKey) != SECSuccess) {
goto loser;
}
}
/* update the key hit */
cx->theKey->hitCount = psock->hitCount++;
/* restore the state */
if (cx->state == ServerInit) {
encrypt_hack = PR_TRUE;
}
if (FortezzaRestoreState(psock,cx) != SECSuccess) {
goto loser;
}
/*
* sigh, save & restore is not working properly, so we let the
* first 8 bytes of the block get trashed.
*/
if (encrypt_hack) {
tmpSize = inLen+8;
tmp = PORT_Alloc(tmpSize);
if (tmp == NULL) { goto loser; }
}
/* OK, we're ready to encrypt, decrypt or hash */
switch (cx->type) {
case Encrypt:
if (encrypt_hack) {
PORT_Memcpy(tmp,pad,8);
PORT_Memcpy(tmp+8,in,inLen);
inLen += 8;
loopin = tmp;
loopout = out;
} else {
loopin = in;
loopout = out;
}
left = inLen;
while((left > 0) && (error == CI_OK)) {
/* Hack, should look at card 's ramsize value */
unsigned current = left > 0x8000 ? 0x8000 : left;
error = MACI_Encrypt(hs,current,loopin,loopout);
loopin += current;
loopout += current;
left -= current;
}
break;
case Decrypt:
loopout = encrypt_hack ? tmp : out;
loopin = in;
left = inLen;
while((left > 0) && (error == CI_OK)) {
/* Hack, should look at card 's ramsize value */
unsigned current = left > 0x8000 ? 0x8000 : left;
error = MACI_Decrypt(hs,current,loopin,loopout);
loopin += current;
loopout += current;
left -= current;
}
if (encrypt_hack) {
PORT_Assert(inLen >= 8);
if (inLen < 8) inLen = 8;
inLen -= 8;
PORT_Memcpy(out,tmp+8,inLen);
}
break;
case Hash:
/* if buffer is supplied, we return the hashed value */
if (out) {
error = MACI_GetHash(hs,inLen,in,out);
inLen = 20;
} else {
error = MACI_Hash(hs,inLen,in);
}
break;
default:
error = CI_ERROR;
break;
}
/* tmp had plain text, zero it on freeing */
if (encrypt_hack) {
PORT_ZFree(tmp,tmpSize);
}
if (error != CI_OK) {
FortezzaError(error,cx,psock);
goto loser;
}
rv = FortezzaSaveState(psock,cx);
if (rv < 0) goto loser;
CI_UNLOCK(psock);
if (part) *part = inLen;
return SECSuccess;
loser:
CI_UNLOCK(psock);
if (part) *part = 0;
return SECFailure;
}
/*
* We need a different routine to handle the premaster secret, as the premaster
* secret does not use the trick of allowing the first 8 bytes to get trashed.
* Actually we can now do without this, and use fortezza_op, but there are more
* important things to clean up...
*/
SECStatus FortezzaCryptPremaster(FortezzaCryptType type,
FortezzaKEAContext *cx,FortezzaKey *fk, FortezzaIV iv, unsigned char *in,
unsigned char *out, int inLen) {
FortezzaPCMCIASocket *psock;
HSESSION hs;
int error;
/*
* CI_LOCK locks the whole CI_Library. Since is is such a
* coarse grain lock, we use smaller grain locks on some of
* the key data structures. To prevent dead locks, you cannot
* call CI_LOCK() while you are already holding a finer grain
* lock.
*/
PORT_Assert(cx->socketId == cx->socket->socketId) ;
psock = cx->socket;
hs = psock->maci_session;
CI_LOCK(psock);
if (fk->keyType != TEK) goto loser;
error = MACI_SetPersonality(hs,fk->keyData.tek.certificate);
/*
* restore the key
*/
if (FortezzaRestoreKey(psock,NULL,fk) != SECSuccess) {
goto loser;
}
/* OK, we're ready to encrypt, decrypt or hash */
switch (type) {
case Encrypt:
error = MACI_GenerateIV(hs,iv);
if (error == CI_OK) {
error = MACI_Encrypt(hs,inLen,in,out);
}
break;
case Decrypt:
error = MACI_LoadIV(hs,iv);
if (error == CI_OK) {
error = MACI_Decrypt(hs,inLen,in,out);
}
break;
default:
error = CI_ERROR;
break;
}
if (error != CI_OK) {
FortezzaError(error,NULL,psock);
goto loser;
}
CI_UNLOCK(psock);
return SECSuccess;
loser:
CI_UNLOCK(psock);
return SECFailure;
}
/*
* create a new context and link it into the card's socket structure
*/
FortezzaContext *
FortezzaCreateContext(FortezzaKEAContext *template, FortezzaCryptType type) {
FortezzaPCMCIASocket *psock = template->socket;
FortezzaContext *cx;
PORT_Assert(template->socketId == template->socket->socketId) ;
cx = PORT_ZAlloc(sizeof(FortezzaContext));
cx->state = NotInit;
cx->type = type;
cx->next = NULL;
cx->theKey = NULL;
cx->certificate = template->certificate;
cx->socketId = template->socketId;
FORTEZZALOCK(psock->listLock,psock->listLockInit);
psock->load++;
cx->socket = psock;
if (psock->head) {
cx->next = psock->head->next;
}
psock->head = cx;
FORTEZZAUNLOCK(psock->listLock);
return cx;
}
/*
* KEA contexts are used by external code to represent sockets. It also is
* used by SSL to store Key exchange specific contexts like the current
* session's tek, or the Server Randon number.
*/
FortezzaKEAContext *
FortezzaCreateKEAContext(FortezzaPCMCIASocket *psock) {
FortezzaKEAContext *template = &psock->template;
FortezzaKEAContext *cx;
cx = PORT_ZAlloc(sizeof(FortezzaKEAContext));
if (cx) {
cx->certificate = template->certificate;
cx->socket = psock;
cx->socketId = psock->socketId;
cx->tek = NULL;
}
return cx;
}
/*
* get a context from a fortezza socket id
*/
FortezzaKEAContext *
FortezzaCreateKEAContextFromSocket(int socket) {
FortezzaPCMCIASocket *psock;
if ((socket <= 0) || (socket > fortezzaGlobal.socketCount)) {
return NULL;
}
psock = &fortezzaGlobal.sockets[socket-1];
return(FortezzaCreateKEAContext(psock));
}
/* Destroy a kea context */
void
FortezzaDestroyKEAContext(FortezzaKEAContext *kcx,PRBool freeit) {
PORT_Assert(kcx->socketId == kcx->socket->socketId) ;
if (kcx->tek) {
FortezzaTEKFree(kcx->tek);
kcx->tek = NULL;
}
if (freeit) {
PORT_Free(kcx);
}
return;
}
/* Destroy a crypto context */
void
FortezzaDestroyContext(FortezzaContext *cx,PRBool freeit) {
FortezzaPCMCIASocket *psock = cx->socket;
FortezzaContext **last,*current;
cx->state = BadCard;
if (cx->theKey) {
FortezzaFreeKey(cx->theKey);
}
FORTEZZALOCK(psock->listLock,psock->listLockInit);
/* remove from the list */
for (last = &psock->head, current=*last; *last;
last = ¤t->next,current = *last) {
if (current == cx) {
*last = current->next;
if (psock->load > 0) psock->load--;
break;
}
}
FORTEZZAUNLOCK(psock->listLock);
if (freeit) PORT_Free(cx);
return;
}
/*
* set the current context key with tha given key.
* (NOTE: context must not have a key already set! );
*/
SECStatus
FortezzaSetKey(FortezzaContext *cx, FortezzaKey *key) {
FortezzaPCMCIASocket *psock = cx->socket;
PORT_Assert(cx->theKey == NULL);
cx->theKey = key;
FORTEZZAPLOCK(psock->keyLock);
key->ref_count++;
FORTEZZAPUNLOCK(psock->keyLock);
return SECSuccess;
}
/*
* Generate a new MEK for this context and return it wrapped with the tek
*/
SECStatus
FortezzaGenerateKeys(FortezzaContext *cx, FortezzaKey *tek,
FortezzaSymetric wrapped_key) {
FortezzaPCMCIASocket *psock = cx->socket;
HSESSION hs = psock->maci_session;
int error,key_reg,tek_reg;
CI_LOCK(psock);
/* Get a Free Key Register to store the Key in */
PORT_Assert(cx->theKey == NULL);
cx->theKey = FortezzaKeyAlloc(&psock->template);
if (cx->theKey == NULL) { goto loser; }
key_reg = FortezzaRegisterAlloc(psock,cx,cx->theKey);
if (key_reg == KeyNotLoaded) { goto loser; }
/* make sure the tek is loaded */
tek_reg = tek->keyRegister;
if (tek_reg == KeyNotLoaded) {
tek_reg = FortezzaRegisterAlloc(psock,cx,tek);
if (tek_reg == KeyNotLoaded) { goto loser; }
}
/* generate a new MEK */
error = MACI_GenerateMEK(hs,key_reg,0);
if (error != CI_OK) { goto process_loser; }
/* Wrap that puppy with the tek for transport over the net */
error = MACI_WrapKey(hs,tek_reg,key_reg,wrapped_key);
if (error != CI_OK) { goto process_loser; }
/* Wrap that puppy with Ks for local storage */
error = MACI_WrapKey(hs,0,key_reg,cx->theKey->keyData.mek.Ks_M);
if (error != CI_OK) { goto process_loser; }
cx->theKey->keyType = MEK;
CI_UNLOCK(psock);
return SECSuccess;
process_loser:
FortezzaError(error,cx,psock);
loser:
CI_UNLOCK(psock);
return SECFailure;
}
/*
* load a tek wrapped MEK into this context
*/
SECStatus
FortezzaUnwrapKey(FortezzaContext *cx, FortezzaKey *tek,
FortezzaSymetric wrapped_key) {
/*
* delay the unwrapping
* of the key until we load the context. This will limit the
* number of CI_LOCK calls needed on the server side to just
* '2' to create a connection (once to generate the server random
* and once to decrypt the pre-master secret). We already
* delay load IV to context loading time. NOTE: to do this we
* need to store the TEK in the FortezzaKey structure, and
* add a reference count to TEK.
*/
FortezzaPCMCIASocket *psock = cx->socket;
PORT_Assert(cx->theKey == NULL);
cx->theKey = FortezzaKeyAlloc(&cx->socket->template);
if (cx->theKey == NULL) return SECFailure;
if (tek != NULL) {
cx->theKey->keyType = TEKMEK;
cx->theKey->keyData.tek_mek.wrappingTEK = tek;
FORTEZZAPLOCK(psock->keyLock);
tek->ref_count++;
FORTEZZAPUNLOCK(psock->keyLock);
PORT_Memcpy(cx->theKey->keyData.tek_mek.TEK_M,wrapped_key,
sizeof(FortezzaSymetric));
} else {
cx->theKey->keyType = MEK;
PORT_Memcpy(cx->theKey->keyData.mek.Ks_M,wrapped_key,
sizeof(FortezzaSymetric));
}
return SECSuccess;
}
/*
* Generate a new iv for the key in the context.
* This also changes the context state from NoInit to OnLine
*/
SECStatus
FortezzaGenerateIV(FortezzaContext *cx, FortezzaIV iv) {
FortezzaPCMCIASocket *psock = cx->socket;
HSESSION hs = psock->maci_session;
int error;
SECStatus rv = SECSuccess;
CI_LOCK(psock);
/* load the key */
rv = FortezzaRestoreKey(psock,cx,cx->theKey);
if (rv < 0) goto loser;
/* build the IV */
error = MACI_GenerateIV(hs,iv);
if (error != CI_OK) {
FortezzaError(error,cx,psock);
goto loser;
}
PORT_Memcpy(cx->iv,iv,sizeof(FortezzaIV));
if (cx->type == Decrypt) {
cx->state = ServerInit;
} else {
rv = FortezzaSaveState(psock,cx);
cx->state = Saved;
}
if (rv < 0) goto loser;
CI_UNLOCK(psock);
return SECSuccess;
loser:
CI_UNLOCK(psock);
return SECFailure;
}
/*
* load's iv into the save area and sets the state to Init.
* iv gets loaded when we decide to start an encrypt or decrypt
*/
SECStatus
FortezzaLoadIV(FortezzaContext *cx, FortezzaIV iv) {
PORT_Memcpy(cx->iv,iv,sizeof(FortezzaIV));
if (cx->type == Encrypt) {
cx->state = ServerInit;
} else {
cx->state = Init;
}
return SECSuccess;
}
/*
* force a reloaded decrypt context into ServerInit state.
*/
SECStatus
FortezzaSetServerInit(FortezzaContext *cx) {
PORT_Assert(cx->type == Decrypt);
cx->state = ServerInit;
return SECSuccess;
}
SECStatus
FortezzaLoadSave(FortezzaContext *cx, FortezzaCryptSave save) {
PORT_Memcpy(cx->save,save,sizeof(FortezzaCryptSave));
cx->state = Saved;
return SECSuccess;
}
/*
* look up all the personalities that we have valid keys for
* and return them in an array. (which needs to be freed below).
*/
SECStatus
FortezzaLookupPersonality(FortezzaPCMCIASocket *psock,
char ***personalities, int *found,int **index) {
char **parray;
int next = 0;
int count,i;
count =psock->certificateCount;
/* define parray to manage the triple indirection better...*/
(*personalities) = parray = (char **)
PORT_ZAlloc((count+1)*sizeof(char *));
if (parray == NULL) {
return SECFailure;
}
if (index) {
(*index) = (int *) PORT_ZAlloc(count * sizeof(int));
if (*index == NULL) {
PORT_Free(parray);
return SECFailure;
}
}
for (i=1; i < count; i++) {
int cert;
CI_CERT_STR name;
FORTEZZALOCK(psock->certLock,psock->certLockInit);
/* deal with the race condition */
if ( i >= psock->certificateCount) {
FORTEZZAUNLOCK(psock->certLock);
break;
}
cert = (int)psock->personalityList[i].CertificateIndex;
if (cert >= psock->certificateCount) {
FORTEZZAUNLOCK(psock->certLock);
continue;
}
if (!FortezzaCertBits(psock->certFlags, cert)) {
FORTEZZAUNLOCK(psock->certLock);
continue;
}
PORT_Memcpy(name,psock->personalityList[i].CertLabel,
sizeof(CI_CERT_STR));
FORTEZZAUNLOCK(psock->certLock);
/* Only use certificates we have keys for */
if (name[2] != 'K' && name[3] != 'S' ) {
continue;
}
parray[next] = PORT_Alloc(sizeof(CI_CERT_STR));
if (parray[next] == NULL) {
FortezzaFreePersonality(parray);
if (index) PORT_Free(*index);
return SECFailure;
}
PORT_Memcpy(parray[next],&name[8], sizeof(CI_CERT_STR)-8);
parray[next][sizeof(CI_CERT_STR)-8] = 0;
if (index) (*index)[next] = cert;
next++;
}
*found = next;
return SECSuccess;
}
/* Free a list of personalities found by lookup personalities */
void
FortezzaFreePersonality(char **personalities) {
char **parray = personalities;
while (*parray) PORT_Free(*parray++);
PORT_Free(personalities);
}
/*
* Find the "best" certificate in the card to use. If there is any
* doubt, and we are on a client, ask the user.
*/
SECStatus
FortezzaSelectCertificate(SSLSocket *ss,FortezzaPCMCIASocket *psock,
int *certificate) {
SSLSecurityInfo *sec = NULL;
SECStatus rv = SECSuccess;
char **personalities;
int *index;
int count = 0;
if (ss) sec = ss->sec;
rv = FortezzaLookupPersonality(psock,&personalities,&count,&index);
if (rv < 0) return rv;
switch (count) {
case 0:
/* No certificates, we fail */
*certificate= 0;
rv = SECFailure;
break;
case 1:
/* Only one certificate, we use it */
*certificate = index[0];
break;
default:
/* more than one and we're a client */
if (sec && sec->fortezzaCertificateSelect) {
int cert;
rv = (*sec->fortezzaCertificateSelect)
(sec->fortezzaCertificateArg,ss->fd,personalities,count,&cert);
if (rv < 0) break;
/* make sure we weren't a victim of FortezzaError. */
if (index[cert-1] > psock->certificateCount) {
*certificate = -1;
rv = SECFailure;
break;
}
*certificate=index[cert-1];
/* more than one and we're a server with a specified cert */
} else if (fortezzaGlobal.serverCertificate) {
*certificate = fortezzaGlobal.serverCertificate;
} else {
/* we're on the server, and no cert has been selected, use
* the first valid one */
*certificate=index[0];
}
break;
}
FortezzaFreePersonality(personalities);
PORT_Free(index);
return rv;
}
/*
* check the pin. This returns 3 different types of values:
* 1) SECSuccess... we have the correct pin.
* 2) SECWouldBlock... we have the incorrect pin, but ths card is still
* functioning, try again with another pin.
* 3) SECFailure... The card is out to lunch (probably because we tried to
* many invalid pin attempts.
*/
SECStatus
FortezzaPinCheck(FortezzaPCMCIASocket *psock,char *pin) {
int error;
HSESSION hs=psock->maci_session;
CI_LOCK(psock);
error = MACI_CheckPIN(hs,CI_USER_PIN,(unsigned char *)pin);
CI_UNLOCK(psock);
if (error != CI_OK) {
/* failed because incorrect pin, try again */
if (error == CI_FAIL) {
return SECWouldBlock;
}
/* failed because of library error or card problem */
FortezzaError(error,NULL,psock);
return SECFailure;
}
return SECSuccess;
}
/*
* Fortezza certificates are 2048 bytes on the card, but most of those
* bytes are '0'. We parse the starting sequence header to get the
* true certificate length.
*/
static int
FortezzaCertLength(unsigned char *DerData) {
int length = 0;
unsigned char *dp = (unsigned char *)DerData;
unsigned char tag = *dp++;
int consumed;
if (tag & 0x1f != 0x10 /* SEQUENCE */) {
return 0;
}
length = *dp++;
if (length & 0x80) {
int len_count = length & 0x7f;
length = 0;
while (len_count-- > 0) {
length = (length << 8) | *dp++;
}
}
consumed = dp - (unsigned char *)DerData;
length += consumed;
/* Don't blow up on invalide certs */
if (length > 2048) {
length=2048;
} else if (length < 0) {
length = 0;
}
return length;
}
/*
* load up the certificates from the card into memory.
*/
SECStatus
FortezzaCertInit(FortezzaPCMCIASocket *psock) {
SECItem *certListFree, *certificateList = NULL;
CI_PERSON *personListFree, *personalityList = NULL;
CI_STATUS status;
CERTCertificate *rootCert = NULL, *rootCertFree;
HSESSION hs=psock->maci_session;
CERTCertTrust trust;
char *name;
int error, i;
SECStatus rv;
/* now we read in the certificate list and personality list. Why
* didn't we when we initted the psock structure?? simple.. we needed
* the pin to read these...
*/
CI_LOCK(psock);
/* then we get the current card state */
error = MACI_GetStatus(hs,&status);
if (error != CI_OK) { goto loser; }
/* finally we read in the personalities */
personalityList = (CI_PERSON *) PORT_ZAlloc
(sizeof(CI_PERSON)*status.CertificateCount);
if (personalityList == NULL) { goto loser; }
error = MACI_GetPersonalityList(hs,status.CertificateCount,
personalityList);
if (error != CI_OK) { goto loser; }
certificateList = (SECItem *) PORT_ZAlloc
(sizeof(SECItem)*status.CertificateCount);
if (certificateList == NULL) { goto loser; }
for (i = 0; i < status.CertificateCount; i++) {
if (FortezzaCertBits(psock->certFlags, i)) {
certificateList[i].data = (unsigned char *)PORT_ZAlloc(2048);
if (certificateList[i].data == NULL) { goto loser; }
error = MACI_GetCertificate(hs,i,certificateList[i].data);
if (error != CI_OK) { goto loser; }
certificateList[i].len=FortezzaCertLength(certificateList[i].data);
} else {
certificateList[i].data = NULL;
certificateList[i].len = 0;
}
}
CI_UNLOCK(psock);
/* Store the Root Cert at trusted */
/* create the root certificate */
rootCert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(),
&certificateList[0], NULL, PR_FALSE, PR_TRUE);
if (rootCert == NULL) goto loser;
/* make sure it's valid (self signed) */
rv = CERT_VerifySignedData(&rootCert->signatureWrap, rootCert);
if (rv < 0) goto loser;
/* make it trusted */
/* this code was swiped from certdlgs.c -- session option */
if (!rootCert->isperm) {
name = CERT_GetCommonName(&rootCert->subject);
if (name == NULL) goto loser;
/*
* Should we make NS_TRUSTED_CA? Probably not we really
* didn't look at this cert, and it could be a rougue
* card.. Also, should we set emailFlags? rjr
*/
trust.sslFlags = (CERTDB_VALID_CA | CERTDB_TRUSTED_CA );
/* CERTDB_NS_TRUSTED_CA ;*/
trust.emailFlags = 0;
trust.paymentFlags = 0;
rv = CERT_AddTempCertToPerm(rootCert,name,&trust);
PORT_Free(name);
if (rv < 0) goto loser;
}
FORTEZZALOCK(psock->certLock,psock->certLockInit);
certListFree = psock->certificateList;
personListFree = psock->personalityList;
rootCertFree = psock->rootCert;
psock->certificateList = certificateList;
psock->personalityList = personalityList;
psock->rootCert = rootCert;
psock->certificateCount = status.CertificateCount;
FORTEZZAUNLOCK(psock->certLock);
if (rootCertFree) CERT_DestroyCertificate(rootCertFree);
if (certListFree) PORT_Free(certListFree);
if (personListFree) PORT_Free(personListFree);
return SECSuccess;
loser:
if (rootCert) CERT_DestroyCertificate(rootCert);
if (personalityList) PORT_Free(personalityList);
if (certificateList) PORT_Free(certificateList);
CI_UNLOCK(psock);
return SECFailure;
}
/*
* initialize the pcmcia cards
*
* This routine is called to initialize a fortezza structure.
*
*/
static void
FortezzaSocketInit(FortezzaPCMCIASocket *psock) {
int error,i;
CI_STATUS status;
CI_PERSON *personalityList;
SECItem *certList;
CERTCertificate *rootCert;
FortezzaKeyRegister *registers;
HSESSION hs=psock->maci_session;
if (psock->state != NoCard) {
return;
}
/* first we open the socket */
CI_LOCKA(psock); /* just do the local locking... */
error = MACI_Open(hs,0,psock->socketId);
#ifdef XP_WIN
/* for some reason Window's likes to shutdown the library on us
* if this happens we need to reopen the library.. */
if (error == CI_LIB_NOT_INIT) {
int count;
error = MACI_Initialize(&count);
if (error == CI_OK) {
error = MACI_Open(hs,0,psock->socketId);
}
}
#endif
if (error != CI_OK) { goto loser; }
/* then we get the current card state */
error = MACI_GetStatus(hs,&status);
if (error != CI_OK) { goto loser; }
CI_UNLOCKA(psock);
FORTEZZALOCK(psock->stateLock,psock->stateLockInit);
/* check the state, it may have changed since we were called
* (someone may have called init between the time we checked
* and now).
*/
if (psock->state != NoCard) {
/* someone has come in and initialized the card for us already. */
FORTEZZAUNLOCK(psock->stateLock);
return;
}
/* update the serial number */
PORT_Memcpy(psock->serial,status.SerialNumber,sizeof(psock->serial));
psock->hitCount = 0;
psock->load = 0;
/* set the state to reflect our init values */
PORT_Memcpy(psock->certFlags,status.CertificateFlags,
sizeof(status.CertificateFlags));
switch (status.CurrentState) {
case CI_USER_INITIALIZED:
psock->state = NeedPin;
FORTEZZAUNLOCK(psock->stateLock);
FORTEZZALOCK(psock->certLock,psock->certLockInit);
personalityList = psock->personalityList;
certList = psock->certificateList;
rootCert= psock->rootCert;
psock->certificateCount = 0;
psock->certificateList = NULL;
psock->personalityList = NULL;
psock->rootCert = NULL;
FORTEZZAUNLOCK(psock->certLock);
if (rootCert) CERT_DestroyCertificate(rootCert);
if (personalityList) PORT_Free(personalityList);
if (certList) PORT_Free(certList);
break;
case CI_STANDBY:
case CI_READY:
psock->state = NeedCert;
FORTEZZAUNLOCK(psock->stateLock);
if (FortezzaCertInit(psock) != SECSuccess) {
/* recover */
FORTEZZALOCK(psock->certLock,psock->certLockInit);
personalityList = psock->personalityList;
certList = psock->certificateList;
rootCert = psock->rootCert;
psock->certificateCount = 0;
psock->certificateList = NULL;
psock->personalityList = NULL;
psock->rootCert = NULL;
FORTEZZAUNLOCK(psock->certLock);
if (rootCert) CERT_DestroyCertificate(rootCert);
if (personalityList) PORT_Free(personalityList);
if (certList) PORT_Free(certList);
}
break;
default:
FORTEZZAUNLOCK(psock->stateLock);
if (fortezzaGlobal.fortezzaAlert != NULL) {
fortezzaGlobal.fortezzaAlert(
fortezzaGlobal.fortezzaAlertArg,
PR_TRUE,XP_GetString(XP_SEC_FORTEZZA_BAD_CARD),
psock->socketId,NULL);
}
psock->state = NoCard; /* redundant */
return;
}
FORTEZZAPLOCK(psock->keyLock);
psock->registerCount = status.KeyRegisterCount;
registers = psock->registers;
for (i=0;i < status.KeyRegisterCount;i++) {
registers[i] = NULL;
}
FORTEZZAPUNLOCK(psock->keyLock);
return;
loser:
psock->state = NoCard;
CI_UNLOCKA(psock);
return;
}
/*
* Create shared memory creates a memory region that can be addressed
* any this process and any of it's decendants. This memory region
* is used to manage the keys of the fortezza card. Other process access to
* these keys are managed by MACI. If the application of OS does not create
* additional processes, normal malloc may be used. (Thread or OK).
*/
static unsigned char *FortezzaSharedMemory(int len, unsigned char **addr){
#if defined(SERVER_BUILD) && defined(XP_UNIX)
/* we use mmap instead of sys V shared memory so the system
* can clean up after us (sys v shared memory stays around *FOREVER*
* mmap'ed unlinked files disappear as soon as all references leave
* them.
* system V shared memory, we should use mmap.
* NOTE: Most systems can't extend their shared memory segments
* on the fly (sigh), so we preallocate a *LARGE* shareed memory
* segment. We don't actually touch that memory in case the system
* uses late swap allocation.. we won't actually be using
* any resources associated with that memory. Note also that this
* backing store is saved in the specified TMP filesystem */
int fd;
char *tmp;
char *tmpdir=getenv("TMPDIR");
#define TMP_NAME "fortezza_keys"
if (tmpdir == NULL) {
tmpdir = "/tmp";
}
tmp = PORT_Alloc(PORT_Strlen(tmpdir)+PORT_Strlen(TMP_NAME)+30);
sprintf(tmp,"%s/%s%d",tmpdir,TMP_NAME,getpid());
fd = open(tmp,O_CREAT|O_RDWR|O_EXCL,0600);
if (fd < 0) {
PORT_Free(tmp);
return NULL;
}
/* Only we need it.. make it go way when we're through */
unlink(tmp);
PORT_Free(tmp);
/* make the file big enough */
lseek(fd,FORTEZZA_MAX_SHARED_MEM-1, SEEK_SET);
write(fd,"\0",1);
*addr = mmap(NULL,FORTEZZA_MAX_SHARED_MEM,
PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
if (*addr == NULL) {
return NULL;
}
*(unsigned long *)(*addr) = len + sizeof(unsigned long);
return (*addr+sizeof(unsigned long));
#else
return PORT_Alloc(len);
#endif
}
/*
* initialize the global fields
*/
void
FortezzaGlobalInit(void) {
if (fortezzaGlobal.socketCount == 0) {
int socketCount,i;
int error;
unsigned char * mem_addr;
fortezzaGlobal.state = GlobalOK;
fortezzaGlobal.sslList = NULL; /* paranoia */
error = MACI_Initialize(&socketCount);
if (error != CI_OK) {
return;
}
fortezzaGlobal.timeout = LL_ZERO;/* by default, set no timeout value */
fortezzaGlobal.sockets = (FortezzaPCMCIASocket *)
PORT_ZAlloc(sizeof(FortezzaPCMCIASocket)*socketCount);
if (fortezzaGlobal.sockets == NULL) {
return;
}
fortezzaGlobal.socketCount = socketCount;
fortezzaGlobal.defaultSocket = NULL; /* used by clients */
/*
* CI_Lock currently does not really lock the library,
* but the current library is single threaded, so we need
* to protect the library calls with a lock. That's what
* this lock is for...
*/
FORTEZZAINITLOCK(fortezzaGlobal.libLock,
fortezzaGlobal.libLockInit);
FORTEZZAINITLOCK(fortezzaGlobal.paramLock,
fortezzaGlobal.paramLockInit);
fortezzaGlobal.fortezzaMarkSocket = NULL;
fortezzaGlobal.fortezzaAlert = NULL;
fortezzaGlobal.fortezzaAlertArg = NULL;
fortezzaGlobal.mask = 0xffffffff;
/*
* Create shared memory for key allocation
* XXX The following really should be an NSPR call!!
*/
mem_addr= FortezzaSharedMemory( fortezzaGlobal.socketCount*
((FORTEZZA_MAX_REG*sizeof(FortezzaKey *))
+sizeof(FortezzaPlock))+
sizeof(FortezzaPlock)+sizeof(unsigned long) +
FORTEZZA_INIT_KEYS*sizeof(FortezzaKey),
&fortezzaGlobal.memaddr);
if (mem_addr == NULL) {
fortezzaGlobal.socketCount = 0;
PORT_Free(fortezzaGlobal.sockets);
return;
}
/*
* initalize the global shared memory fields
*/
/* Key id is to handle some races in sslnonce. It makes sure the
* key we have is the key we started with. */
fortezzaGlobal.keyid = (unsigned long *)mem_addr;
mem_addr += sizeof(unsigned long);
*(fortezzaGlobal.keyid) = 1;
/* keyList is the lock to avoid contention on the list of free keys */
fortezzaGlobal.keyListLock = (FortezzaPlock *)mem_addr;
mem_addr += sizeof(FortezzaPlock);
FORTEZZAINITPLOCK(fortezzaGlobal.keyListLock);
for (i=0; i < socketCount; i++) {
FortezzaPCMCIASocket *psock=&fortezzaGlobal.sockets[i];
psock->socketId = i+1;
/*
* look at all these "baby"-locks. The rule:
* 1) you can't grab CI_LOCK if you already hold
* a baby-lock (you can grab a baby-lock if you
* already hold CI_LOCK).
* 2) you can't grab more than one baby-lock at a
* time.
* 3) Don't make CI_Calls, UI-calls, or other
* potentially long calls with holding a baby-lock;
* See the other comments in this file for why we
* bother with these finer grain locks.
*/
/*FORTEZZAINITLOCK(psock->keyLock,psock->keyLockInit);*/
/* the key data structures are shared by all processes.. so we
* have to lock them among all processes */
psock->keyLock = (FortezzaPlock *)mem_addr;
mem_addr += sizeof(FortezzaPlock);
FORTEZZAINITPLOCK(psock->keyLock);
FORTEZZAINITLOCK(psock->listLock,psock->listLockInit);
FORTEZZAINITLOCK(psock->stateLock,psock->stateLockInit);
FORTEZZAINITLOCK(psock->certLock,psock->certLockInit);
FORTEZZAINITLOCK(psock->libLock,psock->libLockInit);
error = MACI_GetSessionID(&psock->maci_session);
psock->state = NoCard;
/*registers allocation is shared globally among all the processes*/
psock->registers = (FortezzaKey **)mem_addr;
mem_addr += FORTEZZA_MAX_REG*sizeof(FortezzaKey *);
psock->head = NULL;
psock->certificateList = NULL;
psock->personalityList = NULL;
psock->rootCert = NULL;
psock->certificateCount = 0;
psock->template.socket = psock;
/* paranoia to help verify stuff */
psock->template.socketId = psock->socketId;
psock->template.certificate = -1;
FortezzaSocketInit(psock);
}
/*
* OK now build the list of keys.
*/
fortezzaGlobal.keyTop = NULL;
for (i=0; i < FORTEZZA_INIT_KEYS; i++) {
FortezzaKey *next = (FortezzaKey *)mem_addr;
mem_addr += sizeof(FortezzaKey);
next->next = fortezzaGlobal.keyTop;
fortezzaGlobal.keyTop = next;
}
}
return;
}
/* log out of all the fortezza cards */
void
FortezzaLogout() {
int i;
for (i=0; i < fortezzaGlobal.socketCount; i++) {
FortezzaPCMCIASocket *psock=&fortezzaGlobal.sockets[i];
/* reset all the cards */
FortezzaError(CI_OK,NULL,psock);
}
}
/*
* initialize the pcmcia cards
*
* This routine is called to initialize a fortezza structure.
*
*/
static PRBool
FortezzaSocketVerify(FortezzaPCMCIASocket *psock) {
int error;
CI_STATUS status;
HSESSION hs = psock->maci_session;
PRBool ret;
/*
* CI_LOCK locks the whole CI_Library. Since is is such a
* coarse grain lock, we use smaller grain locks on some of
* the key data structures. To prevent dead locks, you cannot
* call CI_LOCK() while you are already holding a finer grain
* lock.
*/
PORT_Memset(&status,0,sizeof(CI_STATUS));
CI_LOCK(psock);
/* What we are trying to detect if the card
* has popped in or out. If it has, select should fail, and we
* go off and clear off any existing context's then re-init the
* socket. However it looks like select succedes even if there
* is no card, so we issue a CI_GetStatus call. This better fail if
* there is no card.
*/
error = MACI_GetStatus(hs,&status);
if ((error != CI_OK) || (status.CurrentSocket != psock->socketId) ||
(PORT_Memcmp(psock->serial,
status.SerialNumber,sizeof(psock->serial))!=0)) {
/* If this is the default card, then it has changed */
if (fortezzaGlobal.defaultSocket == psock) {
fortezzaGlobal.defaultSocket = NULL;
}
FortezzaError(CI_OK,NULL,psock); /* don't throw an alert */
CI_UNLOCK(psock);
FortezzaSocketInit(psock);
return (psock->state != NoCard);
}
CI_UNLOCK(psock);
FORTEZZALOCK(psock->stateLock,psock->stateLockInit);
/* if we're not already in ready state, someone
* is dealing with getting us there...
*/
if ((psock->state != Ready) && (psock->state != NeedCert)) {
FORTEZZAUNLOCK(psock->stateLock);
return PR_TRUE;
}
ret = PR_TRUE;
switch (status.CurrentState) {
case CI_USER_INITIALIZED:
/* some one pulled the card, we need a new pin */
psock->state = NeedPin;
break;
case CI_STANDBY:
case CI_READY:
/* The card is there, we don't set the ready state because
* we may be in NeedCert state.. Since the card is initialized,
* psock->state must already be correct. */
/*psock->state = Ready; */
break;
default:
if (fortezzaGlobal.fortezzaAlert != NULL) {
fortezzaGlobal.fortezzaAlert(
fortezzaGlobal.fortezzaAlertArg,
PR_TRUE,XP_GetString(XP_SEC_FORTEZZA_BAD_CARD),
psock->socketId,NULL);
}
psock->state = NoCard; /* redundant */
ret = PR_FALSE;
break;
}
if (ret && !LL_IS_ZERO(fortezzaGlobal.timeout) &&
(psock->state != NeedPin)) {
/* time out fortezza cards */
int64 timeout;
int64 now = PR_NowS();
LL_ADD(timeout,psock->lasttime,fortezzaGlobal.timeout);
if (LL_CMP(now, > , timeout)) {
psock->state = NeedPin;
}
}
FORTEZZAUNLOCK(psock->stateLock);
return ret;
}
/*
* get a list of sockets and cards on the system
*/
SECStatus
FortezzaLookupCard(FortezzaCardInfo **info, uint32 *mask, int *count,
int *found,FortezzaPCMCIASocket **min, FortezzaPCMCIASocket **last,
PRBool isServer) {
int i;
FortezzaPCMCIASocket *last_sock = NULL;
FortezzaPCMCIASocket *min_sock = NULL;
uint32 minload = 0xffffffff /* MAXUINT */;
*mask = 0;
*found = 0;
PORT_Assert(fortezzaGlobal.socketCount <= 32);
if (info) {
*info = PORT_Alloc(sizeof(FortezzaCardInfo)
*fortezzaGlobal.socketCount);
if (*info == NULL) return SECFailure;
}
if (count != NULL) {
*count = fortezzaGlobal.socketCount;
}
for (i=0; i < fortezzaGlobal.socketCount; i++) {
FortezzaPCMCIASocket *psock = &fortezzaGlobal.sockets[i];
/* only tweak the cards in the mask */
if ((fortezzaGlobal.mask & (1 << i)) == 0) {
if (psock->state != NoCard) {
/* close the current socket down */
FortezzaError(CI_OK,NULL,psock);
}
continue;
}
/* look for and existing card */
if (psock->state != NoCard) {
/* don't verify on servers, let the connection fail if
* the fortezza card was pulled out! */
if (isServer || FortezzaSocketVerify(psock)) {
last_sock = psock;
(*mask) |= ((uint32)1 << i);
(*found)++;
if ((unsigned)psock->load < minload) {
minload = psock->load;
min_sock = psock;
}
} else {
/* card has been moved */
if (fortezzaGlobal.defaultSocket == psock) {
fortezzaGlobal.defaultSocket = NULL;
}
}
} else {
/* try to init one */
FortezzaSocketInit(psock);
if (psock->state != NoCard) {
last_sock = psock;
(*mask) |= (1 << i);
(*found)++;
if ((unsigned)psock->load < minload) {
minload = psock->load;
min_sock = psock;
}
}
}
/* add vendor? */
if (info) {
PORT_Memcpy((*info)[i].serial,psock->serial,
sizeof(psock->serial));
(*info)[i].state = psock->state;
}
}
if (last) *last = last_sock;
if (min) *min = min_sock;
return SECSuccess;
}
/*
* this routine finds the first available fortezza card.
* In the future this routine could get a lot smarter, and look for
* user selected cards, and/or spread out load over multiple cards on
* a server.
*/
static SECStatus
FortezzaFindCard(SSLSocket *ss, FortezzaPCMCIASocket **ppsock) {
SSLSecurityInfo *sec = NULL;
int rv;
uint32 cards = 0;
int count=0;
FortezzaPCMCIASocket *last_sock = NULL;
FortezzaPCMCIASocket *min_sock = NULL;
FortezzaCardInfo *info = NULL;
PRBool isServer = PR_TRUE;
if (ss) {
sec=ss->sec;
if (sec->fortezzaCardSelect != NULL) isServer=PR_FALSE;
}
/*
* we're waiting for a popup to complete
*/
if (fortezzaGlobal.state == GlobalOpen) {
if (fortezzaGlobal.fortezzaMarkSocket != NULL) {
fortezzaGlobal.fortezzaMarkSocket(ss);
}
return SECWouldBlock;
}
/*
* the popup was cancelled, continue processing without the
* fortezza card.
*/
if (fortezzaGlobal.state == GlobalSkip) {
return SECFailure;
}
/* hack -- not thread safe... */
if (sec) fortezzaGlobal.fortezzaAlertArg = sec->fortezzaAlertArg;
rv = FortezzaLookupCard(isServer? NULL : &info,&cards,NULL,
&count,&min_sock,&last_sock,isServer);
if (rv < 0) return SECFailure;
switch (count) {
case 0:
*ppsock = NULL;
if (info) PORT_Free(info);
return SECFailure; /* none found */
case 1:
/* only one card available, no need to ask the user
* anything */
*ppsock = last_sock;
fortezzaGlobal.defaultSocket = last_sock;
if (info) PORT_Free(info);
return SECSuccess;
}
/* default fall-through */
if (sec && sec->fortezzaCardSelect) {
int psockID;
SECStatus rv;
/* if we have already selected a socket, and we haven't
* added new sockets or removed the selected socket ..
* use the selected socket */
if (fortezzaGlobal.defaultSocket) {
*ppsock = fortezzaGlobal.defaultSocket;
if (info) PORT_Free(info);
return SECSuccess;
}
rv=sec->fortezzaCardSelect
(sec->fortezzaCardArg,ss->fd,info,cards,&psockID);
if (info) PORT_Free(info);
if (rv == SECSuccess) {
PORT_Assert((psockID > 0)
&&(psockID <= fortezzaGlobal.socketCount));
*ppsock = &fortezzaGlobal.sockets[psockID-1];
fortezzaGlobal.defaultSocket = *ppsock;
}
return rv;
}
if (info) PORT_Free(info);
/* for the server we grab the least busy card */
*ppsock = min_sock;
fortezzaGlobal.defaultSocket = min_sock;
return SECSuccess;
}
/*
* this routine finds the first available fortezza card.
* In the future this routine will get a lot smarter, and look for
* user selected cards, and/or spread out load over multiple cards on
* a server.
*/
static SECStatus
FortezzaFindOpenCard(FortezzaPCMCIASocket **ppsock) {
int i;
unsigned long cards;
FortezzaPCMCIASocket *last_sock = NULL;
FortezzaPCMCIASocket *min_sock = NULL;
unsigned long minload = 0xffffffff /* MAXUINT */;
int count=0;
PORT_Assert(fortezzaGlobal.socketCount <= 32);
for (i=0; i < fortezzaGlobal.socketCount; i++) {
FortezzaPCMCIASocket *psock = &fortezzaGlobal.sockets[i];
/* look for and existing card */
/* Any card that's been initialized with a pin will work */
if ((psock->state == Ready) || (psock->state == NeedCert)) {
last_sock = psock;
cards |= (1 << i);
count++;
if ((unsigned)psock->load < minload) {
minload = psock->load;
min_sock = psock;
}
}
}
switch (count) {
case 0:
*ppsock = NULL;
return SECFailure; /* none found */
case 1:
/* only one card available, no need to ask the user
* anything */
*ppsock = last_sock;
return SECSuccess;
}
/* default fall-through */
/* for the server we grab the least busy card */
*ppsock = min_sock;
return SECSuccess;
}
/*
* the goal with FortezzaOpenCard is to get through the an already opened
* adapter without having to do a CI_LOCK or a CI_ call. This is most
* important in the server, which will never call CI_LOCK in open (all
* the fortezza control on the server happen "off-line".
*/
static SECStatus
FortezzaOpenCard(SSLSocket *ss,FortezzaKEAContext **cx,
FortezzaPCMCIASocket *psock,PRBool needCert) {
SSLSecurityInfo *sec = NULL;
if (ss) sec = ss->sec;
FORTEZZALOCK(psock->stateLock,psock->stateLockInit);
if (psock->state == NoCard) {
FORTEZZAUNLOCK(psock->stateLock);
return SECFailure;
}
/* see if we can get out of here without issuing a
* CI command! */
psock->lasttime = PR_NowS();
if (psock->state == Ready) {
FORTEZZAUNLOCK(psock->stateLock);
if (cx) *cx = FortezzaCreateKEAContext(psock);
return SECSuccess;
}
/*
* Nope, we are going to need to get the pin.
* So now we tell everyone else interested in openning this socket
* go go away
*/
if (sec && sec->fortezzaGetPin) {
SECStatus rv;
FORTEZZAUNLOCK(psock->stateLock);
/* does *NOT* return SECWouldBlock! */
if (psock->state == NeedPin) {
rv = (sec->fortezzaGetPin)(sec->fortezzaPinArg,psock);
} else {
rv = SECSuccess;
}
if (rv == SECSuccess) {
/* assumes assignment to int is atomic ! */
psock->state = NeedCert;
*cx = FortezzaCreateKEAContext(psock);
FORTEZZAWAKEUP(psock->stateLock);
rv = FortezzaCertInit(psock);
if (rv < 0) return rv;
if (needCert) {
rv = FortezzaSelectCertificate(ss,psock,
&psock->template.certificate);
(*cx)->certificate = psock->template.certificate;
if (rv == SECSuccess) psock->state = Ready;
}
return rv;
}
psock->state = NoCard;
FORTEZZAWAKEUP(psock->stateLock);
return rv;
}
psock->state = NoCard; /* don't keep putting up dialogs right away */
/* no need to wakeup here, no one could have been put to sleep on
* this path */
FORTEZZAUNLOCK(psock->stateLock);
return SECFailure;
}
/*
* the goal with FortezzaOpenCard is to get through the an already opened
* adapter without having to do a CI_LOCK or a CI_ call. This is most
* important in the server, which will never call CI_LOCK in open (all
* the fortezza control on the server happen "off-line".
*/
SECStatus
FortezzaOpen(SSLSocket *ss,FortezzaKEAContext **cx) {
FortezzaPCMCIASocket *psock;
SECStatus rv;
rv = FortezzaFindCard(ss,&psock);
if (rv == SECSuccess) {
rv=FortezzaOpenCard(ss,cx,psock,PR_TRUE);
}
return (rv);
}
/*
* Servers are configured once on startup. Dynamic changing of cards
* does not happen in the server case! If a card goes off line, it's offline
* for the rest to the life of the server.
*/
SECStatus
FortezzaConfigureServer(FortezzaPinFunc getPin,uint32 mask, char *cert_name,
FortezzaAlert func, void *arg, int *error) {
char *password;
FortezzaPCMCIASocket *psock;
SECStatus rv = SECFailure;
FortezzaKEAContext *cx = NULL;
SECKEYPrivateKey *key;
uint32 cards = 0;
int i,count=0;
int first = 0;
*error = FORTEZZA_BADCARD;
fortezzaGlobal.mask = mask;
fortezzaGlobal.fortezzaAlert = func;
fortezzaGlobal.fortezzaAlertArg = arg;
rv = FortezzaLookupCard(NULL,&cards,NULL,&count,NULL,NULL,PR_TRUE);
password = NULL;
fortezzaGlobal.mask = cards;
if (rv == SECSuccess) {
/* initialize all the cards the server is going to use */
for (i=0; i < fortezzaGlobal.socketCount; i++) {
/* skip cards slots that have been reserved */
if ((cards & (1<<i)) == 0) {
continue;
}
psock = &fortezzaGlobal.sockets[i];
/* pin initialize all the cards */
if (password == NULL) {
/* prompt for the pin of the first card */
do {
if (password) PORT_ZFree(password, PORT_Strlen(password));
if ((*getPin)(&password) == NULL) {
*error = FORTEZZA_BADPASSWD;
return SECFailure;
}
if ((password == NULL) || (*password == '\0')) {
rv = SECFailure;
break;
}
rv = FortezzaPinCheck(psock,password);
} while (rv == SECWouldBlock);
if (rv < 0) {
*error = FORTEZZA_BADPASSWD;
if (password) PORT_ZFree(password, PORT_Strlen(password));
return rv;
}
} else {
/* now do a pin check on the subsequent cards. If they
* fail, just disable them */
rv = FortezzaPinCheck(psock,password);
if (rv < 0) {
fortezzaGlobal.mask &= ~(1<<i);
FortezzaError(CI_OK,NULL,psock);
func(arg,PR_TRUE,
"Card in socket %d failed pin check.. disabling\n",
i+1,NULL);
continue;
}
}
psock->state = Ready; /* assumes single threaded at init time */
rv=FortezzaOpenCard(NULL,&cx,psock,PR_TRUE);
if (rv < 0) {
fortezzaGlobal.mask &= ~(1<<i);
FortezzaError(CI_OK,NULL,psock);
continue;
}
rv = FortezzaCertInit(psock);
if (rv < 0) {
fortezzaGlobal.mask &= ~(1<<i);
FortezzaError(CI_OK,NULL,psock);
FortezzaDestroyKEAContext(cx,PR_TRUE);
continue;
}
/* use the first one to initialize the fortezza keys structure */
if (first == 0) {
CERTCertificate *certificate = NULL;
/* now look up the certificate by name */
if (cert_name) {
psock->template.certificate =
FortezzaFindCertIndexByNickname(psock,cert_name);
} else psock->template.certificate = -1;
if (psock->template.certificate < 0) {
rv = FortezzaSelectCertificate(NULL,psock,
&psock->template.certificate);
}
if (rv < 0) {
fortezzaGlobal.mask &= ~(1<<i);
FortezzaError(CI_OK,NULL,psock);
FortezzaDestroyKEAContext(cx,PR_TRUE);
continue;
}
cx->certificate = psock->template.certificate;
rv=FortezzaGetCertList(cx,
&ssl3_fortezza_server_cert_chain,&key,&certificate);
if (rv < 0) {
fortezzaGlobal.mask &= ~(1<<i);
FortezzaError(CI_OK,NULL,psock);
FortezzaDestroyKEAContext(cx,PR_TRUE);
continue;
}
CERT_DestroyCertificate(certificate);
first++;
SSL3_SetFortezzaKeys(key);
fortezzaGlobal.serverCertificate=psock->template.certificate;
} else {
psock->template.certificate = fortezzaGlobal.serverCertificate;
}
FortezzaDestroyKEAContext(cx,PR_TRUE);
}
}
if (password) PORT_ZFree(password, PORT_Strlen(password));
rv = SECFailure;
if (first) {
*error = FORTEZZA_OK;
rv = SECSuccess;
}
return (rv);
}
/*
* turn the 4 byte BCD index in the personality label to an
* integer we can use to look up the parent
*/
static int
FortezzaDecodeIndex(unsigned char *label) {
int i,c;
int value = 0;
label +=4;
for (i=0; i < 4; i++) {
c = label[i];
if (c < '0' || c > '9') return -1;
value = value*10 + label[i] - '0';
}
return value;
}
/*
* return configuration and status info
*/
SECStatus
FortezzaGetConfig(FortezzaPCMCIASocket *psock,CI_CONFIG *config,
CI_STATUS *status) {
int error;
HSESSION hs;
if (psock == NULL || (psock->state == NoCard)) goto loser;
hs = psock->maci_session;
CI_LOCK(psock);
error = MACI_GetConfiguration(hs,config);
if (error != CI_OK) {
goto loser;
CI_UNLOCK(psock);
}
error = MACI_GetStatus(hs,status);
if (error != CI_OK) {
PORT_Memset(status, 0, sizeof(CI_STATUS));
}
CI_UNLOCK(psock);
return SECSuccess;
loser:
PORT_Memset(config, 0, sizeof(CI_CONFIG));
PORT_Memset(status, 0, sizeof(CI_STATUS));
return SECFailure;
}
/*
* do the actual lookup... used below.
*/
static CERTCertificate *
FortezzaCertLookUp(FortezzaPCMCIASocket *psock, int certID, int *nextCert) {
int i;
char nickname[25];
FORTEZZALOCK(psock->certLock,psock->certLockInit);
if (certID > psock->certificateCount) {
goto loser;
}
if (psock->certificateList == NULL) {
goto loser;
}
if (psock->certificateList[certID].data == NULL) {
goto loser;
}
/*
* Look up 1) our parent certificate and 2) our nickname.
* We get this from the personalityList record. The personality
* List record has a CertificateIndex and a Label. The certificate
* index tells which certificate this belongs to, and the Label
* is a string with the following format:
* Bytes 0-3 Certificate type
* Bytes 4-7 BCD encoded parent certificate index
* Bytes 8-31 Name of the certificate (nickname)
*/
if (certID != 0) {
for (i=0; i < psock->certificateCount; i++) {
/* here's the personality that matches our cert */
if (psock->personalityList[i].CertificateIndex == certID) {
/* copy the name field */
PORT_Memcpy(nickname,&psock->personalityList[i].CertLabel[8],24);
nickname[24] = 0;
/* grab the parent cert */
*nextCert =
FortezzaDecodeIndex(psock->personalityList[i].CertLabel);
break;
}
}
} else {
i = 0;
*nextCert = -1;
nickname[0] = 0;
}
if (i == psock->certificateCount) {
FORTEZZAUNLOCK(psock->certLock);
/*should do something more drastic, like invalidate the card!*/
return NULL;
}
FORTEZZAUNLOCK(psock->certLock);
/* look up the next cert and add it to the chain (special
* case the first try) */
return CERT_NewTempCertificate(CERT_GetDefaultCertDB(),
&psock->certificateList[certID],nickname, PR_FALSE, PR_TRUE);
loser:
FORTEZZAUNLOCK(psock->certLock);
return NULL;
}
/*
* grab a certificate from the "card" based on socketID, serial number, and
* certificate index. Future enhancements may be to search all to sockets
* for the serial number? It wouldn't be to much code, but will it ever be
* useful? Fortezza Certificates really come in chains. You pass the whole
* chain around (except the root, which everyone has..).
*/
CERTCertificate *
FortezzaGetCertificate(int sockID,int certID,unsigned char *serial) {
FortezzaPCMCIASocket *psock;
unsigned char nickname[25];
int nextCert;
CERTCertificate *current = NULL, *certificate = NULL;
SSL3CertNode *certs = NULL, *cnode;
PRArenaPool *arena = NULL;
if ((sockID <= 0) || (sockID > fortezzaGlobal.socketCount)) {
return NULL;
}
psock = &fortezzaGlobal.sockets[sockID-1];
nickname[0] = 0;
FORTEZZALOCK(psock->stateLock,psock->stateLockInit);
if (PORT_Memcmp(psock->serial,serial,sizeof(psock->serial)) != 0) {
FORTEZZAUNLOCK(psock->stateLock);
return NULL; /* Well maybe we should go looking for it? */
}
FORTEZZAUNLOCK(psock->stateLock);
/* ok we have a valid card, and we have a valid serial, lets
* go look up the certificate...
*/
certificate = FortezzaCertLookUp(psock,certID,&nextCert);
current = certificate;
if (certificate == NULL) return NULL;
/*
* a fortezza certificate by itself is not meaningful
* we need a chain of certificates. NOTE: multiple users certificates
* can all point to the same LA or CA certificates. We send all but
* the root certificate, since all fortezza cards must have the same
* root certificate in their card.
*/
for (certID = nextCert; certID != -1 &&
certificate->subjectPublicKeyInfo.algorithm.parameters.len == 0;
certID = nextCert) {
if (arena == NULL) {
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if (arena == NULL) goto loser;
}
/* unashamedly plagerized from ssl3con.c
* we keep track of the certs in a link list because we need to
* walk the array backwards to fill in the PQG parameters at the
* end... */
cnode = PORT_ArenaAlloc(arena, sizeof(SSL3CertNode));
if (cnode == NULL) goto loser;
cnode->cert = FortezzaCertLookUp(psock,certID,&nextCert);
if (cnode->cert == NULL) goto loser;
cnode->next = certs;
certs = cnode;
}
/* make each cert independent from each other */
if (certs) {
FortezzaPQGUpdate(certs,certificate);
/* destroy the other certs */
for (cnode = certs; cnode != NULL; cnode = cnode->next) {
CERT_DestroyCertificate(cnode->cert);
}
PORT_FreeArena(arena, PR_FALSE);
}
return certificate;
loser:
if (certificate) { CERT_DestroyCertificate(certificate); }
if (certs) {
for (cnode = certs; cnode != NULL; cnode = cnode->next) {
CERT_DestroyCertificate(cnode->cert);
}
}
if (arena) { PORT_FreeArena(arena, PR_FALSE); }
return NULL;
}
/*
* Grab A cert by it's nickname
*/
static int FortezzaFindCertIndexByNickname(
FortezzaPCMCIASocket *psock,char *certname) {
int len = PORT_Strlen(certname);
char **personalities;
int *indexes;
int count,i;
int found = -1;
SECStatus rv;
if (psock == NULL) {
return -1;
}
rv = FortezzaLookupPersonality(psock,&personalities,&count,&indexes);
if (rv < 0 ) return -1;
for (i=0; i < count ; i++) {
if (XP_STRNCMP(personalities[i],certname,len) == 0) {
found = indexes[i];
break;
}
}
FortezzaFreePersonality(personalities);
PORT_Free(indexes);
return found;
}
/*
* look up a cert based on the nickname in the personality list field
*/
CERTCertificate * FortezzaFindCertByNickname(char *certname) {
FortezzaPCMCIASocket *psock = fortezzaGlobal.defaultSocket;
int found;
found = FortezzaFindCertIndexByNickname(psock,certname);
if (found < 0) {
return NULL;
}
return FortezzaGetCertificate(psock->socketId,found,psock->serial);
}
/*
* build an SECKEYPrivateKey from a fortezza context.
*/
SECKEYPrivateKey *
FortezzaGetPrivateKey(FortezzaKEAContext *cx) {
PRArenaPool *arena;
SECKEYPrivateKey *privkey;
PORT_Assert(cx->socketId == cx->socket->socketId) ;
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if (arena == NULL) {
return NULL;
}
privkey = (SECKEYPrivateKey *)PORT_ArenaZAlloc(arena,sizeof(SECKEYPrivateKey));
if (privkey == NULL) {
PORT_FreeArena(arena, PR_FALSE);
return NULL;
}
privkey->arena = arena;
privkey->keyType = fortezzaKeys;
privkey->fortezzaKeyData.certificate = cx->certificate;
PORT_Memcpy(privkey->fortezzaKeyData.serial,
cx->socket->serial,sizeof(cx->socket->serial));
privkey->fortezzaKeyData.socket = cx->socket->socketId;
return privkey;
}
/*
* build a certificate list and a certificat DER list from a
* context (get the key as well).
*/
SECStatus
FortezzaGetCertList(FortezzaKEAContext *cx,CERTCertificateList **chain,
SECKEYPrivateKey **key, CERTCertificate **certificate) {
FortezzaPCMCIASocket *psock = cx->socket;
unsigned char nickname[25];
int nextCert;
int certID = cx->certificate;
SSL3CertNode *certs = NULL, *cnode;
PRArenaPool *arena = NULL;
PORT_Assert(cx->socketId == cx->socket->socketId) ;
*key = NULL;
*chain = NULL;
*certificate = NULL;
nickname[0] = 0;
/* ok we have a valid card, and we have a valid serial, lets
* go look up the certificate...
*/
*certificate = FortezzaCertLookUp(psock,certID,&nextCert);
if (*certificate == NULL) return SECFailure;
/*
* a fortezza certificate by itself is not meaningful
* we need a chain of certificates.
*/
for (certID = nextCert; certID != 0 ; certID = nextCert) {
if (arena == NULL) {
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if (arena == NULL) goto loser;
}
/* unashamedly plagerized from ssl3con.c
* we keep track of the certs in a link list because we need to
* walk the array backwards to fill in the PQG parameters at the
* end... */
cnode = PORT_ArenaAlloc(arena, sizeof(SSL3CertNode));
if (cnode == NULL) goto loser;
cnode->cert = FortezzaCertLookUp(psock,certID,&nextCert);
if (cnode->cert == NULL) goto loser;
cnode->next = certs;
certs = cnode;
}
/* make each cert independent from each other */
if (certs) {
FortezzaPQGUpdate(certs,*certificate);
}
*chain = CERT_CertChainFromCert(CERT_GetDefaultCertDB(),*certificate);
/* destroy the certs now */
if (certs) {
for (cnode = certs; cnode != NULL; cnode = cnode->next) {
CERT_DestroyCertificate(cnode->cert);
}
PORT_FreeArena(arena, PR_FALSE);
}
*key = FortezzaGetPrivateKey(cx);
if ((key != NULL) && (*chain != NULL)) {
return SECSuccess;
}
if (*key) { SECKEY_DestroyPrivateKey(*key); }
if (*chain) { CERT_DestroyCertificateList(*chain); }
CERT_DestroyCertificate(*certificate);
*certificate = NULL;
*key = NULL;
*chain = NULL;
return SECFailure;
loser:
if (*certificate) { CERT_DestroyCertificate(*certificate); }
if (certs) {
for (cnode = certs; cnode != NULL; cnode = cnode->next) {
CERT_DestroyCertificate(cnode->cert);
}
}
if (arena) { PORT_FreeArena(arena, PR_FALSE); }
return SECFailure;
}
/*
* Not all Fortezza certificates carry their PQG parameters.
* Users inherit their parameters from their "parent" certificate.
* When we get into the bowels of key extraction code, you don't have
* any information about what the parameters are, which we need to set
* in our public key structure. If we ignore this step, we won't be able
* to check parameter mis-matches when verifying signatures.
*/
SECStatus
FortezzaPQGUpdate(SSL3CertNode *certs,CERTCertificate *tipCert) {
int tag;
SECOidData *oid;
SECItem parameters;
SSL3CertNode *cnode;
CERTSubjectPublicKeyInfo *spki;
/* we only need to do this update on fortezza certificates */
oid = SECOID_FindOID(&tipCert->subjectPublicKeyInfo.algorithm.algorithm);
if (oid == NULL) return SECSuccess; /* or should we fail? */
tag = oid->offset;
if ( (tag != SEC_OID_MISSI_KEA_DSS_OLD) &&
(tag != SEC_OID_MISSI_DSS_OLD) && (tag != SEC_OID_MISSI_KEA_DSS)
&& (tag != SEC_OID_MISSI_DSS) ) {
/* this is not a fortezza certificate, we're done */
return SECSuccess;
}
/* initialize the paramters from the global root parameters,
* This requires that we have loaded at least on fortezza card once
* sometime in the past. If we haven't, we shouldn't be gettting
* MISSI formated certificates! This also assumes a single
* fortezza root for all cards!
*
* NOTE: this locking is broken. parameters.data may get freed
* out from under us. It works for now because 1) the client
* is not multi-threaded, and 2) the server never frees
* it's parameters.data structure? */
parameters.data = NULL;
parameters.len = 0;
FORTEZZAUNLOCK(fortezzaGlobal.paramLock);
for (cnode = certs; cnode != NULL; cnode = cnode->next) {
CERTCertificate *cert = cnode->cert;
spki=&cert->subjectPublicKeyInfo;
if (spki->algorithm.parameters.len == 0) {
SECStatus rv;
if (parameters.len != 0) {
rv = SECITEM_CopyItem(cert->arena,
&spki->algorithm.parameters, ¶meters);
if (rv < 0) return rv;
}
} else {
parameters.data = spki->algorithm.parameters.data;
parameters.len = spki->algorithm.parameters.len;
}
}
spki=&tipCert->subjectPublicKeyInfo;
if (parameters.len != 0 && spki->algorithm.parameters.len == 0) {
SECStatus rv = SECITEM_CopyItem(tipCert->arena,
&spki->algorithm.parameters, ¶meters);
if (rv < 0) return rv;
}
return SECSuccess;
}
/*
* make sure the PQG parameters for any two certs match
*/
int
FortezzaComparePQG(CERTCertificate *cert1, CERTCertificate *cert2) {
CERTSubjectPublicKeyInfo *spki1 = &cert1->subjectPublicKeyInfo;
CERTSubjectPublicKeyInfo *spki2 = &cert1->subjectPublicKeyInfo;
return SECITEM_CompareItem(&spki1->algorithm.parameters,
&spki2->algorithm.parameters);
}
static int
FortezzaFindBlankPersonality(FortezzaPCMCIASocket *psock) {
int i,count = psock->certificateCount;
int certID = -1;
for (i=0; i < count; i++) {
FORTEZZALOCK(psock->certLock,psock->certLockInit);
/* deal with the race condition */
if ( i >= psock->certificateCount) {
FORTEZZAUNLOCK(psock->certLock);
break;
}
if (!FortezzaCertBits(psock->certFlags, i)) {
certID = i;
FortezzaSetCertBits(psock->certFlags, i);
FORTEZZAUNLOCK(psock->certLock);
}
}
return certID;
}
/*
* Generate a new key based on the PQG parameters of the server's certificate
*/
SECStatus
FortezzaGenerateNewPersonality(FortezzaKEAContext *cx, int *certIndex,
FortezzaPublic y, CERTCertificate *cert) {
SECKEYPublicKey *key=NULL;
FortezzaPCMCIASocket *psock = cx->socket;
HSESSION hs = psock->maci_session;
int error;
PORT_Assert(cx->socketId == cx->socket->socketId) ;
/*
* Extracting the key is the easiest way to find the pqg
* parameters.
*/
key = CERT_ExtractPublicKey(&cert->subjectPublicKeyInfo);
if (key == NULL) {
return SECFailure;
}
/*
* RestoreState saves the current state as well as restoring our
* state. For signatures we have no state to restore... we just do
* it. That's why we pass it a NULL. We call restore state so that
* we don't trash any running encryption/decryption.
*/
CI_LOCK(psock);
*certIndex = FortezzaFindBlankPersonality(psock);
if (*certIndex < 0) { goto loser; }
error = MACI_GenerateX(hs,*certIndex,CI_KEA_TYPE,key->fortezzaKeyData.p.len,
key->fortezzaKeyData.q.len,key->fortezzaKeyData.p.data,
key->fortezzaKeyData.q.data,key->fortezzaKeyData.g.data,
sizeof(FortezzaPublicKey), y);
if (error != CI_OK) { goto loser; }
/* Load a fake certificate??! */
CI_UNLOCK(psock);
SECKEY_DestroyPublicKey(key);
return SECSuccess;
loser:
CI_UNLOCK(psock);
SECKEY_DestroyPublicKey(key);
return SECFailure;
}
/*
* Delete a generated personality
*/
SECStatus FortezzaDeletePersonality(FortezzaKEAContext *cx,int cert) {
FortezzaPCMCIASocket *psock = cx->socket;
HSESSION hs = psock->maci_session;
PORT_Assert(cx->socketId == cx->socket->socketId) ;
CI_LOCK(psock);
/*FortezzaRestoreState(psock,NULL);*/
MACI_DeleteCertificate(hs,cert);
CI_UNLOCK(psock);
FORTEZZALOCK(psock->certLock,psock->certLockInit);
FortezzaClrCertBits(psock->certFlags, cert);
FORTEZZAUNLOCK(psock->certLock);
return SECSuccess;
}
/*
* use the card to Sign something
*/
SECStatus
FortezzaSign(unsigned char *digest,unsigned char *signature, SECKEYPrivateKey *key) {
FortezzaPCMCIASocket *psock;
HSESSION hs;
int error;
int sockID = key->fortezzaKeyData.socket;
PORT_Assert( key->keyType == fortezzaKeys );
/* paranoia */
if ((sockID <= 0) || (sockID > fortezzaGlobal.socketCount)) {
return SECFailure;
}
psock = &fortezzaGlobal.sockets[key->fortezzaKeyData.socket-1];
hs = psock->maci_session;
CI_LOCK(psock);
if (psock->state == NoCard) {
goto loser;
}
if (PORT_Memcmp(psock->serial,key->fortezzaKeyData.serial,
sizeof(psock->serial)) != 0) {
goto loser;
}
/*
* RestoreState saves the current state as well as restoring our
* state. For signatures we have no state to restore... we just do
* it. That's why we pass it a NULL. We call restore state so that
* we don't trash any running encryption/decryption.
*/
#ifdef notdef
error = MACI_Select(hs,psock->socketId);
if (error != CI_OK) { goto loser; }
#endif
error = MACI_SetPersonality(hs,key->fortezzaKeyData.certificate);
if (error != CI_OK) { goto loser; }
error = MACI_Sign(hs,digest,signature);
if (error != CI_OK) { goto loser; }
CI_UNLOCK(psock);
return SECSuccess;
loser:
CI_UNLOCK(psock);
return SECFailure;
}
/*
* General DSA signature verify routine. This routine will work with
* any DSA signatures, as long as the SECKEYPublicKey has the fortezza
* DSA key material in them.
*/
#ifndef FORTEZZA_SOFTWARE
#define DSA_WORDS 11
#define MAX_DSA_KEY 65
PRBool
DSAVerify(unsigned char *digest,unsigned char *signature, SECKEYPublicKey *key) {
FortezzaPCMCIASocket *psock;
HSESSION hs;
int error;
SECStatus rv;
rv = FortezzaFindOpenCard(&psock);
if (rv < 0) return PR_FALSE;
PORT_Assert( key->keyType == fortezzaKeys );
hs = psock->maci_session;
CI_LOCK(psock);
if (psock->state == NoCard) {
goto loser;
}
/*
* RestoreState saves the current state as well as restoring our
* state. For signatures we have no state to restore... we just do
* it. That's why we pass it a NULL. We call restore state so that
* we don't trash any running encryption/decryption.
*/
#ifdef notdef
error = MACI_Select(hs,psock->socketId);
if (error != CI_OK) { goto loser; }
#endif
error = MACI_LoadDSAParameters(hs,key->fortezzaKeyData.p.len,
key->fortezzaKeyData.q.len,key->fortezzaKeyData.p.data,
key->fortezzaKeyData.q.data,key->fortezzaKeyData.g.data);
if (error != CI_OK) { goto loser; }
error = MACI_VerifySignature(hs,digest,
key->fortezzaKeyData.DSSKey.len,
key->fortezzaKeyData.DSSKey.data,signature);
if (error != CI_OK) { goto loser; }
CI_UNLOCK(psock);
return PR_TRUE;
loser:
CI_UNLOCK(psock);
return PR_FALSE;
}
#endif
/*
* set up the fortezza hooks. This code was plagerized from sslauth.c
*/
int
Fortezza_CardSelectHook(int s, FortezzaCardSelect func, void *arg)
{
SSLSocket *ss;
int rv;
ss = ssl_FindSocket(s);
if (!ss) {
SSL_DBG(("%d: SSL[%d]: bad socket in AuthCertificateHook",
SSL_GETPID(), s));
return -1;
}
if ((rv = ssl_CreateSecurityInfo(ss)) != 0) {
return(rv);
}
ss->sec->fortezzaCardSelect = func;
ss->sec->fortezzaCardArg = arg;
return(0);
}
/*
* Error Logging or client notification.
*/
int
Fortezza_AlertHook(int s, FortezzaAlert func, void *arg)
{
SSLSocket *ss;
int rv;
ss = ssl_FindSocket(s);
if (!ss) {
SSL_DBG(("%d: SSL[%d]: bad socket in AuthCertificateHook",
SSL_GETPID(), s));
return -1;
}
if ((rv = ssl_CreateSecurityInfo(ss)) != 0) {
return(rv);
}
ss->sec->fortezzaAlert = func;
ss->sec->fortezzaAlertArg = arg;
if (fortezzaGlobal.fortezzaAlert == NULL) {
fortezzaGlobal.fortezzaAlert = func;
fortezzaGlobal.fortezzaAlertArg = arg;
}
return(0);
}
/*
* finding a new certificate
*/
int
Fortezza_CertificateSelectHook(int s, FortezzaCertificateSelect func, void *arg)
{
SSLSocket *ss;
int rv;
ss = ssl_FindSocket(s);
if (!ss) {
SSL_DBG(("%d: SSL[%d]: bad socket in AuthCertificateHook",
SSL_GETPID(), s));
return -1;
}
if ((rv = ssl_CreateSecurityInfo(ss)) != 0) {
return(rv);
}
ss->sec->fortezzaCertificateSelect = func;
ss->sec->fortezzaCertificateArg = arg;
return(0);
}
/* Getting a pin */
int
Fortezza_GetPinHook(int s, FortezzaGetPin func, void *arg)
{
SSLSocket *ss;
int rv;
ss = ssl_FindSocket(s);
if (!ss) {
SSL_DBG(("%d: SSL[%d]: bad socket in AuthCertificateHook",
SSL_GETPID(), s));
return -1;
}
if ((rv = ssl_CreateSecurityInfo(ss)) != 0) {
return(rv);
}
ss->sec->fortezzaGetPin = func;
ss->sec->fortezzaPinArg = arg;
return(0);
}
/*
* template for unloading Fortezza pqg parameters
*/
DERTemplate FortezzaPreParamTemplate[] = {
{ DER_BOOLEAN|DER_CONSTRUCTED|DER_CONTEXT_SPECIFIC, 0, NULL,
sizeof(SECItem) },
{ 0 },
};
DERTemplate FortezzaParameterTemplate[] = {
{ DER_SEQUENCE, 0, NULL, sizeof(SECKEYPublicKey) },
{ DER_OCTET_STRING, offsetof(SECKEYPublicKey,fortezzaKeyData.p), },
{ DER_OCTET_STRING, offsetof(SECKEYPublicKey,fortezzaKeyData.q), },
{ DER_OCTET_STRING, offsetof(SECKEYPublicKey,fortezzaKeyData.g), },
{ 0 },
};
/*
* NOTE: this only decodes KEA_DSS type keys, not DSS only type keys.
*/
SECStatus
FortezzaDecodeCertKey(PRArenaPool *arena, SECKEYPublicKey *pubk, SECItem *rawkey,
SECItem *params) {
unsigned char *rawptr = rawkey->data;
unsigned char *end = rawkey->data + rawkey->len;
unsigned char *clearptr;
SECItem preParams;
SECStatus rv;
/* first march down and decode the raw key data */
/* version */
pubk->fortezzaKeyData.KEAversion = *rawptr++;
if (*rawptr++ != 0x01) {
return SECFailure;
}
/* KMID */
PORT_Memcpy(pubk->fortezzaKeyData.KMID,rawptr,
sizeof(pubk->fortezzaKeyData.KMID));
rawptr += sizeof(pubk->fortezzaKeyData.KMID);
/* clearance (the string up to the first byte with the hi-bit on */
clearptr = rawptr;
while ((rawptr < end) && (*rawptr++ & 0x80));
if (rawptr >= end) { return SECFailure; }
pubk->fortezzaKeyData.clearance.len = rawptr - clearptr;
pubk->fortezzaKeyData.clearance.data =
PORT_ArenaZAlloc(arena,pubk->fortezzaKeyData.clearance.len);
if (pubk->fortezzaKeyData.clearance.data == NULL) {
return SECFailure;
}
PORT_Memcpy(pubk->fortezzaKeyData.clearance.data,clearptr,
pubk->fortezzaKeyData.clearance.len);
/* KEAPrivilege (the string up to the first byte with the hi-bit on */
clearptr = rawptr;
while ((rawptr < end) && (*rawptr++ & 0x80));
if (rawptr >= end) { return SECFailure; }
pubk->fortezzaKeyData.KEApriviledge.len = rawptr - clearptr;
pubk->fortezzaKeyData.KEApriviledge.data =
PORT_ArenaZAlloc(arena,pubk->fortezzaKeyData.KEApriviledge.len);
if (pubk->fortezzaKeyData.KEApriviledge.data == NULL) {
return SECFailure;
}
PORT_Memcpy(pubk->fortezzaKeyData.KEApriviledge.data,clearptr,
pubk->fortezzaKeyData.KEApriviledge.len);
/* now copy the key. The next to bytes are the key length, and the
* key follows */
pubk->fortezzaKeyData.KEAKey.len = (*rawptr << 8) | rawptr[1];
rawptr += 2;
if (rawptr+pubk->fortezzaKeyData.KEAKey.len > end) { return SECFailure; }
pubk->fortezzaKeyData.KEAKey.data =
PORT_ArenaZAlloc(arena,pubk->fortezzaKeyData.KEAKey.len);
if (pubk->fortezzaKeyData.KEAKey.data == NULL) {
return SECFailure;
}
PORT_Memcpy(pubk->fortezzaKeyData.KEAKey.data,rawptr,
pubk->fortezzaKeyData.KEAKey.len);
rawptr += pubk->fortezzaKeyData.KEAKey.len;
/* shared key */
if (rawptr >= end) {
pubk->fortezzaKeyData.DSSKey.len = pubk->fortezzaKeyData.KEAKey.len;
/* this depends on the fact that we are going to get freed with an
* ArenaFree call. We cannot free DSSKey and KEAKey separately */
pubk->fortezzaKeyData.DSSKey.data=
pubk->fortezzaKeyData.KEAKey.data;
pubk->fortezzaKeyData.DSSpriviledge.len =
pubk->fortezzaKeyData.KEApriviledge.len;
pubk->fortezzaKeyData.DSSpriviledge.data =
pubk->fortezzaKeyData.DSSpriviledge.data;
goto done;
}
/* DSS Version is next */
pubk->fortezzaKeyData.DSSversion = *rawptr++;
if (*rawptr++ != 2) {
return SECFailure;
}
/* DSSPrivilege (the string up to the first byte with the hi-bit on */
clearptr = rawptr;
while ((rawptr < end) && (*rawptr++ & 0x80));
if (rawptr >= end) { return SECFailure; }
pubk->fortezzaKeyData.DSSpriviledge.len = rawptr - clearptr;
pubk->fortezzaKeyData.DSSpriviledge.data =
PORT_ArenaZAlloc(arena,pubk->fortezzaKeyData.DSSpriviledge.len);
if (pubk->fortezzaKeyData.DSSpriviledge.data == NULL) {
return SECFailure;
}
PORT_Memcpy(pubk->fortezzaKeyData.DSSpriviledge.data,clearptr,
pubk->fortezzaKeyData.DSSpriviledge.len);
/* finally copy the DSS key. The next to bytes are the key length,
* and the key follows */
pubk->fortezzaKeyData.DSSKey.len = (*rawptr << 8) | rawptr[1];
rawptr += 2;
if (rawptr+pubk->fortezzaKeyData.DSSKey.len > end){ return SECFailure; }
pubk->fortezzaKeyData.DSSKey.data =
PORT_ArenaZAlloc(arena,pubk->fortezzaKeyData.DSSKey.len);
if (pubk->fortezzaKeyData.DSSKey.data == NULL) {
return SECFailure;
}
PORT_Memcpy(pubk->fortezzaKeyData.DSSKey.data,rawptr,
pubk->fortezzaKeyData.DSSKey.len);
/* ok, now we decode the parameters */
done:
rv = DER_Decode(arena, &preParams, FortezzaPreParamTemplate, params);
if (rv < 0) return rv;
return DER_Decode(arena,pubk, FortezzaParameterTemplate, &preParams);
}
/*
* decode PQG Parameters.
*/
SECStatus
FortezzaBuildParams(SECItem *params,SECKEYPublicKey *key) {
return DER_Encode(NULL, params, FortezzaParameterTemplate, key);
}
/*
* create a der encoded version of a public key
*/
SECStatus
FortezzaEncodeCertKey(PRArenaPool *arena,SECItem *encodeKey,SECKEYPublicKey *pubk) {
unsigned char *rawptr;
encodeKey->len = 2 /* versions */ +
sizeof(pubk->fortezzaKeyData.KMID) +
pubk->fortezzaKeyData.clearance.len +
pubk->fortezzaKeyData.KEApriviledge.len +
pubk->fortezzaKeyData.DSSpriviledge.len +
pubk->fortezzaKeyData.KEAKey.len + 2 /* KEA Length */ +
pubk->fortezzaKeyData.DSSKey.len + 2 /* DSS Length */;
rawptr = (unsigned char *)PORT_ArenaAlloc(arena,encodeKey->len);
if (rawptr == NULL) {
return SECFailure;
}
encodeKey->data = rawptr;
/* march down and encode the raw key data */
/* version */
*rawptr++ = pubk->fortezzaKeyData.KEAversion;
/* KMID */
PORT_Memcpy(rawptr,pubk->fortezzaKeyData.KMID,
sizeof(pubk->fortezzaKeyData.KMID));
rawptr += sizeof(pubk->fortezzaKeyData.KMID);
/* clearance (assumes the high bit is already on) */
PORT_Memcpy(rawptr,pubk->fortezzaKeyData.clearance.data,
pubk->fortezzaKeyData.clearance.len);
rawptr += pubk->fortezzaKeyData.clearance.len;
/* KEAPrivilege */
PORT_Memcpy(rawptr,pubk->fortezzaKeyData.KEApriviledge.data,
pubk->fortezzaKeyData.KEApriviledge.len);
rawptr += pubk->fortezzaKeyData.KEApriviledge.len;
/* now copy the key. */
*rawptr++=(unsigned char)((pubk->fortezzaKeyData.KEAKey.len>>8)&0xff);
*rawptr++ = (unsigned char)(pubk->fortezzaKeyData.KEAKey.len & 0xff);
PORT_Memcpy(rawptr,pubk->fortezzaKeyData.KEAKey.data,
pubk->fortezzaKeyData.KEAKey.len);
rawptr += pubk->fortezzaKeyData.KEAKey.len;
/* DSS Version is next */
*rawptr++=pubk->fortezzaKeyData.DSSversion;
/* DSSPrivilege */
PORT_Memcpy(rawptr,pubk->fortezzaKeyData.DSSpriviledge.data,
pubk->fortezzaKeyData.DSSpriviledge.len);
rawptr += pubk->fortezzaKeyData.DSSpriviledge.len;
/* finally copy the DSS key. */
*rawptr++=(unsigned char)((pubk->fortezzaKeyData.DSSKey.len>>8)&0xff);
*rawptr++ = (unsigned char)(pubk->fortezzaKeyData.DSSKey.len & 0xff);
PORT_Memcpy(rawptr,pubk->fortezzaKeyData.DSSKey.data,
pubk->fortezzaKeyData.DSSKey.len);
return SECSuccess;
}
/*
* Generate a Key Exchange Random number.
*/
SECStatus
FortezzaGenerateRa(FortezzaKEAContext *cx,unsigned char *ra) {
int error;
PORT_Assert(cx->socketId == cx->socket->socketId) ;
CI_LOCK(cx->socket);
error = MACI_SetPersonality(cx->socket->maci_session,cx->certificate);
if (error != CI_OK) goto loser;
error = MACI_GenerateRa(cx->socket->maci_session,ra);
if (error != CI_OK) goto loser;
CI_UNLOCK(cx->socket);
return SECSuccess;
loser:
FortezzaError(error,NULL,cx->socket);
CI_UNLOCK(cx->socket);
return SECFailure;
}
/*
* When we're through with a tek, free up the slot...
* It's the same as FortezzaFreeKey, except it makes sure the key is a tek.
* this is becaus fortezzaFreeKey call it, an this will prevent infinite
* recursion.
*/
void
FortezzaTEKFree(FortezzaKey *tek) {
/* avoid recursion... especially in the case of TEKMEK! */
if (tek->keyType != TEK) return;
FortezzaFreeKey(tek);
return;
}
/*
* Free a key and everything associated with it.
*/
void
FortezzaFreeKey(FortezzaKey *key) {
FortezzaPCMCIASocket *psock = key->socket;
FORTEZZAPLOCK(psock->keyLock);
if (key->ref_count == 1) {
/* get rid of any register references */
key->id = 0;
FortezzaRegisterFree(psock,key);
FORTEZZAPUNLOCK(psock->keyLock);
/* handle key specific stuff */
switch (key->keyType) {
case TEKMEK:
FortezzaTEKFree(key->keyData.tek_mek.wrappingTEK);
break;
case TEK:
if (key->keyData.tek.destroy_certificate) {
FortezzaDeletePersonality(&psock->template,
key->keyData.tek.certificate);
}
break;
default:
break;
}
/* OK, now free the key */
FORTEZZAPLOCK(fortezzaGlobal.keyListLock);
key->next = fortezzaGlobal.keyTop;
fortezzaGlobal.keyTop = key;
FORTEZZAPUNLOCK(fortezzaGlobal.keyListLock);
/* PORT_Free(key); */
return;
}
FORTEZZAPUNLOCK(psock->keyLock);
return;
}
#ifdef SUNOS4
/* Sigh make maci happy */
#undef strerror
char *strerror(unsigned e) {
return sys_errlist[((unsigned)(e) < sys_nerr) ? e : 0];
}
#endif
unsigned long FortezzaKeyID(FortezzaKey *key) { return key->id; }
void FortezzaKeyLock(int socket) {
FortezzaPCMCIASocket *psock;
if ((socket <= 0) || (socket > fortezzaGlobal.socketCount)) {
return;
}
psock = &fortezzaGlobal.sockets[socket-1];
FORTEZZAPLOCK(psock->keyLock);
}
void FortezzaKeyUnlock(int socket) {
FortezzaPCMCIASocket *psock;
if ((socket <= 0) || (socket > fortezzaGlobal.socketCount)) {
return;
}
psock = &fortezzaGlobal.sockets[socket-1];
FORTEZZAPUNLOCK(psock->keyLock);
}
/* reference the key (already have the lock) */
void FortezzaKeyReferenceLocked(FortezzaKey *key) { key->ref_count++; }
/* normal Fortezza key Reference */
void FortezzaKeyReference(FortezzaKey *key) {
FortezzaPCMCIASocket *psock = key->socket;
FORTEZZAPLOCK(psock->keyLock);
key->ref_count++;
FORTEZZAPUNLOCK(psock->keyLock);
}
/* Cached keys may have a race which causes them to free twice.
* Since a key is unique to a session, we know it can only be
* cached once, so only ree it if it was cached.
*/
void FortezzaFreeCachedKey(FortezzaKey *key,unsigned long id) {
FortezzaPCMCIASocket *psock = key->socket;
FORTEZZAPLOCK(psock->keyLock);
if ((key->id == id) && (key->cached)) {
key->cached = PR_FALSE;
FORTEZZAPUNLOCK(psock->keyLock);
FortezzaFreeKey(key);
}
FORTEZZAPUNLOCK(psock->keyLock);
return;
}
/*
* mark a key as cached before we save it.
*/
void FortezzaCachedKey(FortezzaKey *key) {
FortezzaPCMCIASocket *psock = key->socket;
FORTEZZAPLOCK(psock->keyLock);
key->cached = PR_TRUE;
key->ref_count++;
FORTEZZAPUNLOCK(psock->keyLock);
}
/*
* set the timeout value (called from FE's
*/
void
FortezzaSetTimeout(int time) {
/* time comes in in minutes, we store it as seconds */
#ifdef HAVE_LONG_LONG
LL_MUL(fortezzaGlobal.timeout,(int64)time,(int64)60);
#else
LL_MUL32(fortezzaGlobal.timeout,time,60);
#endif
}
#ifdef FORTEZZA_NEED_GROSS_LOCK
/*
* again this should be NSPR routines. It needs.
* if you don't have any real mutexes, we use system V
* semaphores (slow). Only Sunos uses them.
*/
#include <sys/ipc.h>
#include <sys/sem.h>
/*
* first we initialize the semaphore....
*/
static int fsemid = 0;
FortezzaPlock *
FortezzaInitPlock(void) {
static int init= 0;
FortezzaPlock *lock;
lock = (FortezzaPlock *) PORT_Alloc(sizeof(FortezzaPlock));
if (lock == NULL) return NULL;
/* get our semaphore if we haven't got it yet */
if (init == 0) {
union semun arg;
int i;
fsemid=semget(IPC_PRIVATE, 10, 0600);
if (fsemid < 0) {
PORT_Free(lock);
return NULL;
}
/* initialize the semaphores to 1 */
arg.val = 1;
for (i=0; i < 10; i++) {
semctl(fsemid,i,SETVAL,&arg);
}
}
/* initialize the Plock structure */
lock->semid = fsemid;
if (init < 10) {
lock->sem_num = init++;
} else {
PORT_Free(lock);
lock= NULL;
}
return lock;
}
int
FortezzaSetPLock(FortezzaPlock *lock,int x) {
struct sembuf sops;
PORT_Assert(lock != NULL);
if (lock == NULL) return -1;
sops.sem_num = lock->sem_num;
sops.sem_op = x;
sops.sem_flg = SEM_UNDO;
return semop(lock->semid,&sops,1);
}
void
FortezzaDeletePlocks() {
if (fsemid > 0) semctl(fsemid,0,IPC_RMID,NULL);
}
#endif
/*
* When we have semaphores we need to clean then up before
* we die. This cleanup function is tied to the watchdog deamon.
*/
void
fortezza_cleanup_init(void) {
#ifdef FORTEZZA_NEED_GROSS_LOCK
on_exit(FortezzaDeletePlocks,NULL);
#endif
}