netscape-revival
security/lib/nav/secmoz.c
/*
* Security stuff specific to the client (mozilla).
*
* Copyright © 1995 Netscape Communications Corporation, all rights reserved.
*
* $Id: secmoz.c,v 1.99.2.14 1997/05/24 00:23:41 jwz Exp $
*/
#include "xp.h"
#include "cert.h"
#include "secder.h"
#include "secitem.h"
#include "key.h"
#include "crypto.h"
#include "base64.h"
#include "net.h"
#include "secnav.h"
#include "ssl.h"
#include "secmime.h" /* for SECMIME_ funcs */
#ifdef XP_MAC
#include "certdb.h"
#include "sslimpl.h"
#else
#include "../cert/certdb.h"
#include "../ssl/sslimpl.h"
#endif
#include "htmldlgs.h"
#include "sslproto.h"
#include "sslerr.h"
#include "fe_proto.h"
#include "xpgetstr.h"
#include "prefapi.h"
#include "xp_file.h"
#include "secport.h"
#include "secasn1.h"
#include "pk11func.h"
#include "secmod.h"
#include "secrng.h"
#include "pkcs12.h"
#include "p12plcy.h"
#ifdef JAVA
#include "java.h"
#endif
#ifdef FORTEZZA
#include "fortdlgs.h"
#endif
extern int XP_CONFIG_NO_CIPHERS_STRINGS;
extern int XP_CONFIG_MAYBE_CIPHERS_STRINGS;
extern int XP_CONFIG_SMIME_CIPHERS_STRINGS;
extern int XP_CONFIG_SSL2_CIPHERS_STRINGS;
extern int XP_CONFIG_SSL3_CIPHERS_STRINGS;
extern int XP_CONFCIPHERS_TITLE_STRING;
extern int XP_HIGH_GRADE;
extern int XP_MEDIUM_GRADE;
extern int XP_LOW_GRADE;
extern int XP_VIEW_CERT_POLICY;
extern int XP_CHECK_CERT_STATUS;
extern int XP_SSL2_RC4_128;
extern int XP_SSL2_RC2_128;
extern int XP_SSL2_DES_192_EDE3;
extern int XP_SSL2_DES_64;
extern int XP_SSL2_RC4_40;
extern int XP_SSL2_RC2_40;
extern int XP_SSL3_RSA_RC4_128_MD5;
extern int XP_SSL3_RSA_3DES_SHA;
extern int XP_SSL3_RSA_DES_SHA;
extern int XP_SSL3_RSA_RC4_40_MD5;
extern int XP_SSL3_RSA_RC2_40_MD5;
extern int XP_SSL3_RSA_NULL_MD5;
#ifdef FORTEZZA
extern int XP_SSL3_FORTEZZA_SHA;
extern int XP_SSL3_FORTEZZA_RC4_SHA;
extern int XP_SSL3_FORTEZZA_NULL_SHA;
#endif
extern int SEC_ERROR_NO_MEMORY;
extern int XP_SEC_ENTER_EXPORT_PWD;
extern int XP_SEC_ENTER_IMPORT_PWD;
extern int XP_SEC_PROMPT_NICKNAME_COLLISION;
extern int SEC_ERROR_BAD_EXPORT_ALGORITHM;
extern int SEC_ERROR_IMPORTING_CERTIFICATES;
extern int SEC_ERROR_EXPORTING_CERTIFICATES;
extern int XP_PKCS12_IMPORT_FILE_PROMPT;
extern int XP_PKCS12_EXPORT_FILE_PROMPT;
extern int SEC_ERROR_PKCS12_DECODING_PFX;
extern int SEC_ERROR_PKCS12_INVALID_MAC;
extern int SEC_ERROR_PKCS12_UNSUPPORTED_MAC_ALGORITHM;
extern int SEC_ERROR_PKCS12_UNSUPPORTED_TRANSPORT_MODE;
extern int SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE;
extern int SEC_ERROR_PKCS12_UNSUPPORTED_PBE_ALGORITHM;
extern int SEC_ERROR_PKCS12_UNSUPPORTED_VERSION;
extern int SEC_ERROR_PKCS12_PRIVACY_PASSWORD_INCORRECT;
extern int SEC_ERROR_PKCS12_CERT_COLLISION;
extern int SEC_ERROR_PKCS12_DUPLICATE_DATA;
extern int SEC_ERROR_USER_CANCELLED;
extern int XP_PKCS12_SUCCESSFUL_IMPORT;
extern int XP_PKCS12_SUCCESSFUL_EXPORT;
extern int SEC_ERROR_PKCS12_UNABLE_TO_LOCATE_OBJECT_BY_NAME;
extern int SEC_ERROR_PKCS12_UNABLE_TO_EXPORT_KEY;
extern int SEC_ERROR_PKCS12_UNABLE_TO_READ;
extern int SEC_ERROR_PKCS12_UNABLE_TO_WRITE;
extern int SEC_ERROR_PKCS12_KEY_DATABASE_NOT_INITIALIZED;
extern int SEC_PK11_MANUFACTURER;
extern int SEC_PK11_LIBARARY;
extern int SEC_PK11_TOKEN;
extern int SEC_PK11_PRIV_TOKEN;
extern int SEC_PK11_SLOT;
extern int SEC_PK11_PRIV_SLOT;
extern int SEC_PK11_FIPS_SLOT;
extern int SEC_PK11_FIPS_PRIV_SLOT;
extern int XP_SEC_REENTER_TO_CONFIRM_PWD;
extern int XP_SEC_BAD_CONFIRM_EXPORT_PWD;
void SEC_Init(void);
/* What personal cert does the user want to be presented by default? */
struct {
enum { ASK, AUTO, STR } mode;
char *chosen;
} UserCertChoice = {
ASK,
(char *) 0
};
/*
* Representation of a key size. The name is how it is displayed
* to the user for selection purposes. XXX The attributes need to
* be implemented; they should describe appropriate (or inappropriate)
* uses of the key size. (For example, a 1024-bit key can be used in
* exported environments for payment purposes only. This should be
* marked in the attributes so when the keygen is being done for a
* payment protocol it can choose 1024-bit keys but keygens for other
* purposes cannot choose 1024.) I think.
*/
typedef struct SECKeySizeChoiceInfoStr {
char *name;
int size;
unsigned int attributes;
} SECKeySizeChoiceInfo;
/*
* All possible key size choices.
*/
static SECKeySizeChoiceInfo SECKeySizeChoiceList[] = {
{ (char *)0, 1024, 0 },
{ (char *)0, 768, 0 },
{ (char *)0, 512, 0 },
{ (char *)0, 0, 0 }
};
static SECStatus enable_SSL_cipher_prefs(void);
static SECStatus enable_SSL_cipher_policies(void);
static SECStatus enable_SMIME_cipher_prefs(void);
static SECStatus enable_SMIME_cipher_policies(void);
static SECStatus enable_PKCS12_cipher_policies(void);
typedef struct _CipherConfigState CipherConfigState;
struct _CipherConfigState
{
int cipher;
char *name;
char *desc;
int version;
PRBool enabled;
};
/* ---------------- executable code follows ----------------------- */
static PQGParams *
decode_pqg_params(char *str)
{
char *buf;
unsigned int len;
PRArenaPool *arena;
PQGParams *params;
SECStatus status;
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if (arena == NULL)
return NULL;
params = PORT_ArenaZAlloc(arena, sizeof(PQGParams));
if (params == NULL)
goto loser;
params->arena = arena;
buf = (char *)ATOB_AsciiToData(str, &len);
if ((buf == NULL) || (len == 0))
goto loser;
status = SEC_ASN1Decode(arena, params, SECKEY_PQGParamsTemplate, buf, len);
if (status != SECSuccess)
goto loser;
return params;
loser:
if (arena != NULL)
PORT_FreeArena(arena, PR_FALSE);
return NULL;
}
static int
pqg_prime_bits(char *str)
{
PQGParams *params = NULL;
int primeBits = 0, i;
params = decode_pqg_params(str);
if (params == NULL)
goto done; /* lose */
for (i = 0; params->prime.data[i] == 0; i++)
/* empty */;
primeBits = (params->prime.len - i) * 8;
done:
if (params != NULL)
PQG_DestroyParams(params);
return primeBits;
}
/*
* Should return only the choices available depending on export, etc.
* AND the first item in the list should be the chosen default.
* (Later, the specification parameter should also have control over
* limiting the choices or specifying the default.)
*/
char **
SECNAV_GetKeyChoiceList(char *type, char *pqgString)
{
char ** list;
char * pqg;
char * end;
int i;
int j;
int len;
int primeBits;
len = sizeof (SECKeySizeChoiceList) / sizeof (SECKeySizeChoiceInfo);
/* XXX Help! When do I free this??? */
list = (char**)PORT_Alloc (len * sizeof (char **));
if (pqgString != NULL)
pqgString = PORT_Strdup(pqgString);
if (type && (PORT_Strcasecmp(type, "dsa") == 0)) {
if (pqgString == NULL) {
list[0] = NULL;
return list;
}
pqg = pqgString;
j = 0;
do {
end = PORT_Strchr(pqg, ',');
if (end != NULL)
*end = '\0';
primeBits = pqg_prime_bits(pqg);
for (i = 0; SECKeySizeChoiceList[i].size != 0; i++) {
if (SECKeySizeChoiceList[i].size == primeBits) {
list[j++] = SECKeySizeChoiceList[i].name;
break;
}
}
pqg = end + 1;
} while (end != NULL);
} else {
int maxKeyBits = SECNAV_getMaxKeyGenBits();
j = 0;
for (i = 0; SECKeySizeChoiceList[i].size != 0; i++) {
if (SECKeySizeChoiceList[i].size <= maxKeyBits)
list[j++] = SECKeySizeChoiceList[i].name;
}
}
list[j] = NULL;
if (pqgString != NULL)
PORT_Free(pqgString);
return list;
}
/*
* Based on the choice specified, generate a public/private key pair,
* recording the pair in the key database.
*/
PRBool
SECNAV_GenKeyFromChoice(void *proto_win, LO_Element *form,
char *choiceString, char *challenge,
char *typeString, char *pqgString,
char **pValue, PRBool *pDone)
{
SECKeySizeChoiceInfo *choice;
SECStatus rv;
char *end, *str;
PQGParams *pqg = NULL;
int primeBits;
PRBool found_match = PR_FALSE;
KeyType type;
choice = SECKeySizeChoiceList;
while (PORT_Strcmp(choice->name, choiceString) != 0)
choice++;
if (typeString == NULL || (PORT_Strcasecmp(typeString, "rsa") == 0)) {
type = rsaKey;
} else if (PORT_Strcasecmp(typeString, "dsa") == 0) {
type = dsaKey;
} else {
return PR_FALSE;
}
if (pqgString != NULL)
pqgString = PORT_Strdup(pqgString);
if (type == dsaKey) {
if (pqgString == NULL)
return PR_FALSE;
str = pqgString;
do {
end = PORT_Strchr(str, ',');
if (end != NULL)
*end = '\0';
primeBits = pqg_prime_bits(str);
if (choice->size == primeBits)
goto found_match;
str = end + 1;
} while (end != NULL);
PORT_Free(pqgString);
return PR_FALSE;
found_match:
pqg = decode_pqg_params(str);
}
/*
* XXX Should we check that *pValue is NULL when it comes in?
* If so, we should we do if it is not -- just free it?
*/
*pValue = NULL;
/* XXX - who owns challenge string?? */
rv = SEC_MakeKeyGenDialog(proto_win, form, choice->size, challenge,
type, pqg, pValue, pDone);
if (pqgString != NULL)
PORT_Free(pqgString);
return PR_TRUE;
}
char *
SECNAV_GetHostFromURL(char *url)
{
char *hostname;
char *colon;
/* if no colon, then it must be only the hostname */
colon = PORT_Strchr(url, ':');
if ( colon == NULL ) {
hostname = PORT_Strdup(url);
return(hostname);
}
hostname = NET_ParseURL(url, GET_HOST_PART);
/* Sigh, netlib include the :portnumber as part of the "hostname" */
colon = PORT_Strchr(hostname, ':');
if ( colon != NULL ) {
*colon = '\0';
}
return(hostname);
}
void
SECNAV_Posting(int fd)
{
SSLSocket *ss;
ss = ssl_FindSocket(fd);
if ( ss && ss->sec ) {
ss->sec->app_operation = SSLAppOpPost;
}
return;
}
void
SECNAV_HTTPHead(int fd)
{
SSLSocket *ss;
ss = ssl_FindSocket(fd);
if ( ss && ss->sec ) {
ss->sec->app_operation = SSLAppOpHeader;
}
return;
}
/*
* Callback from SSL for checking certificate of the server
*/
int
SECNAV_ConfirmServerCert(void *arg, int fd, PRBool checkSig, PRBool isServer)
{
SECStatus rv;
SSLSocket *ss;
char *hostname;
CERTCertificate *cert;
if ( isServer ) {
return((int)SECFailure);
}
ss = ssl_FindSocket(fd);
if ( ss && ss->sec ) {
rv = CERT_VerifyCertNow(CERT_GetDefaultCertDB(), ss->sec->peerCert,
checkSig, certUsageSSLServer, arg);
if ( rv == SECSuccess ) { /* cert is OK */
/* now check the name field in the cert */
if ( ss->sec->url ) {
hostname = SECNAV_GetHostFromURL(ss->sec->url);
if ( hostname ) {
rv = CERT_VerifyCertName(ss->sec->peerCert, hostname);
PORT_Free(hostname);
}
}
}
if ( rv == SECSuccess ) {
if ( ( ss->sec->app_operation == SSLAppOpPost ) &&
( ss->sec->post_ok != PR_TRUE ) ) {
cert = ss->sec->peerCert;
/* generate a warning if we are doing a post and the user
* has requested it.
*/
if ( cert->trust &&
( cert->trust->sslFlags & CERTDB_SEND_WARN ) ) {
PORT_SetError(SSL_ERROR_POST_WARNING);
rv = SECFailure;
}
}
}
} else {
rv = SECFailure;
}
return((int)rv);
}
void mydestroy(void *cert, PRBool dummy)
{
CERT_DestroyCertificate((CERTCertificate *)cert);
}
static SECStatus
SECCmpCertChainWCANames(CERTCertificate *cert, CERTDistNames *caNames)
{
int j;
SECItem *caname;
SECItem issuerName;
SECItem compatIssuerName;
int headerlen;
uint32 contentlen;
SECStatus rv;
CERTCertificate *curcert, *oldcert;
int depth;
depth=0;
curcert = CERT_DupCertificate(cert);
while( curcert ) {
issuerName = cert->derIssuer;
/* compute an alternate issuer name for compatibility with 2.0
* enterprise server, which send the CA names without
* the outer layer of DER hearder
*/
rv = DER_Lengths(&issuerName, &headerlen, &contentlen);
if ( rv == SECSuccess ) {
compatIssuerName.data = &issuerName.data[headerlen];
compatIssuerName.len = issuerName.len - headerlen;
} else {
compatIssuerName.data = NULL;
compatIssuerName.len = 0;
}
for (j = 0; j < caNames->nnames; j++) {
caname = &caNames->names[j];
if (SECITEM_CompareItem(&issuerName, caname) == SECEqual) {
rv = SECSuccess;
CERT_DestroyCertificate(curcert);
goto done;
} else if (SECITEM_CompareItem(&compatIssuerName, caname) == SECEqual) {
rv = SECSuccess;
CERT_DestroyCertificate(curcert);
goto done;
}
}
if ( ( depth <= 20 ) &&
( SECITEM_CompareItem(&curcert->derIssuer, &curcert->derSubject)
!= SECEqual ) ) {
oldcert = curcert;
curcert = CERT_FindCertByName(curcert->dbhandle,
&curcert->derIssuer);
CERT_DestroyCertificate(oldcert);
depth++;
} else {
CERT_DestroyCertificate(curcert);
curcert = NULL;
}
}
rv = SECFailure;
done:
return(rv);
}
int
SECNAV_GetClientAuthData(void *arg,
int fd,
CERTDistNames *caNames,
CERTCertificate **pRetCert,
SECKEYPrivateKey **pRetKey)
{
CERTCertificate *cert;
void *proto_win;
SECKEYPrivateKey *privkey;
int ret;
SSLSocket *ss;
PRBool isFortezza = PR_FALSE;
proto_win = (void *)arg;
ss = ssl_FindSocket(fd);
/* in the case of fortezza we already know the cert (we always
* need the cert, so we grab it early..
*/
if (ss && ss->ssl3) {
#ifdef notdef
if (ss->ssl3->hs.kea == kea_fortezza) {
isFortezza = PR_TRUE; /* client cert must be fortezza */
}
#endif
/* Diffie-Helman is the other we need to worry about */
/* Allow DSA and RSA to work interchangably */
}
PORT_Assert(!isFortezza);
switch (UserCertChoice.mode) {
case STR:
cert = PK11_FindCertFromNickname(UserCertChoice.chosen,proto_win);
if ( !cert ) {
ret = SECNAV_MakeClientAuthDialog(proto_win, fd, caNames);
break;
}
privkey = PK11_FindKeyByAnyCert(cert,proto_win);
if ( !privkey ) {
CERT_DestroyCertificate(cert);
ret = SECNAV_MakeClientAuthDialog(proto_win, fd, caNames);
break;
}
*pRetCert = cert;
*pRetKey = privkey;
ret = 0;
break;
case AUTO:
{
int i;
CERTCertNicknames *names = NULL;
CERTCertificate *cert;
SECStatus rv;
names = CERT_GetCertNicknames(CERT_GetDefaultCertDB(),
SEC_CERT_NICKNAMES_USER,proto_win);
if (names != NULL) {
for (i = 0; i < names->numnicknames; i++) {
cert = PK11_FindCertFromNickname(names->nicknames[i],proto_win),
rv = SECCmpCertChainWCANames(cert, caNames);
if ( rv == SECSuccess ) {
*pRetCert = cert;
*pRetKey=PK11_FindKeyByAnyCert(cert, proto_win);
CERT_FreeNicknames(names);
return(0);
} else {
CERT_DestroyCertificate(cert);
}
}
CERT_FreeNicknames(names);
}
}
/* NOTE - fall through */
default:
ret = SECNAV_MakeClientAuthDialog(proto_win, fd, caNames);
break;
}
/* since we are handling this asynchronously we can ignore the
* cert and key return values. They will be set when we restart
* the connection.
*/
return(ret);
}
SECStatus RNG_RNGInit(); /* XXX move when we move RNG_RNGInit to pkcs11.c */
static char *
certDBNameCallback(void *arg, int dbVersion)
{
char *fnarg;
switch ( dbVersion ) {
case 7:
fnarg = "7";
break;
case 6:
fnarg = "6";
break;
case 5:
fnarg = "5";
break;
case 4:
default:
fnarg = NULL;
break;
}
return(WH_FileName(fnarg, xpCertDB));
}
static char *
keyDBNameCallback(void *arg, int dbVersion)
{
char *fnarg;
switch ( dbVersion ) {
case 3:
fnarg = "3";
break;
case 2:
default:
fnarg = NULL;
break;
}
return(WH_FileName(fnarg, xpKeyDB));
}
int SECNAV_AskPrefToPK11(int);
char * PR_CALLBACK
SECNAV_GetCAPolicyString(char *org, unsigned long noticeNumber, void *arg)
{
char *prefname;
char *charvalue = NULL;
int ret;
int len;
char *tmporg, *saveorg;
int c;
/* get space for a new copy of the org string */
len = PORT_Strlen(org);
saveorg = tmporg = (char *)PORT_Alloc(len + 1);
if ( saveorg == NULL ) {
return(NULL);
}
/* only use characters that are alpha */
while ( ( c = *org ) != '\0' ) {
if ( isalpha(c) ) {
*tmporg = c;
tmporg++;
}
org++;
}
*tmporg = '\0';
prefname = PR_smprintf("security.canotices.%s.notice%ld",
saveorg, noticeNumber);
PORT_Free(saveorg);
if ( prefname != NULL ) {
ret = PREF_CopyCharPref(prefname, &charvalue);
PORT_Free(prefname);
}
return(charvalue);
}
/*
* Initialize the security library for mozilla
*/
void
SECNAV_Init(void)
{
CERTCertDBHandle * cdb_handle;
SECKEYKeyDBHandle * kdb_handle;
char * keydbname;
char * secmodule;
int i;
int ret;
SECStatus rv;
XP_Bool prefval;
int32 ask,timeout;
PK11SlotInfo *slot;
char * prefchar = NULL;
/*
* Deal with non-hard-coded strings before other initialization.
*/
i = 0;
SECKeySizeChoiceList[i++].name = PORT_Strdup(XP_GetString(XP_HIGH_GRADE));
SECKeySizeChoiceList[i++].name = PORT_Strdup(XP_GetString(XP_MEDIUM_GRADE));
SECKeySizeChoiceList[i++].name = PORT_Strdup(XP_GetString(XP_LOW_GRADE));
/*
* Initialize the certificate database.
*/
cdb_handle = (CERTCertDBHandle *)PORT_ZAlloc(sizeof(CERTCertDBHandle));
if (cdb_handle != NULL) {
rv = CERT_OpenCertDB(cdb_handle, PR_FALSE, certDBNameCallback, NULL);
if (rv != SECSuccess) {
/* that failed; try getting a "volatile" database */
rv = CERT_OpenVolatileCertDB(cdb_handle);
}
if (rv == SECSuccess) {
CERT_SetDefaultCertDB(cdb_handle);
}
}
/*
* Initialize the private key database.
*/
/* in the "ideal world"(tm), the Key database would be initialized in
* the Software PKCS#11 module. Unfortunately there's not enough of
* an interface to pass all the info to the PCKS#11 module, so go ahead
* and initialize here...
*/
RNG_RNGInit(); /* #$@$#@ Guess what SECKEY_OpenKeyDB calls if there is
* no keyDB? You got it Get Random Data... just one more
* reason to want to move this call into pkcs11.c
*/
kdb_handle = SECKEY_OpenKeyDB(PR_FALSE, keyDBNameCallback, NULL);
if (kdb_handle != NULL) {
SECKEY_SetDefaultKeyDB(kdb_handle);
}
/*
* Configure the strings for pkcs11.
*/
{
char *man= PORT_Strdup(XP_GetString(SEC_PK11_MANUFACTURER));
char *libdes = PORT_Strdup(XP_GetString(SEC_PK11_LIBARARY));
char *tokdes = PORT_Strdup(XP_GetString(SEC_PK11_TOKEN));
char *ptokdes = PORT_Strdup(XP_GetString(SEC_PK11_PRIV_TOKEN));
char *slotdes = PORT_Strdup(XP_GetString(SEC_PK11_SLOT));
char *pslotdes = PORT_Strdup(XP_GetString(SEC_PK11_PRIV_SLOT));
char *fslotdes = PORT_Strdup(XP_GetString(SEC_PK11_FIPS_SLOT));
char *fpslotdes = PORT_Strdup(XP_GetString(SEC_PK11_FIPS_PRIV_SLOT));
PK11_ConfigurePKCS11(man,libdes,tokdes,ptokdes,slotdes,pslotdes,
fslotdes,fpslotdes);
}
/*
* set our default password function
*/
PK11_SetPasswordFunc(SECNAV_PromptPassword);
/*
* OK, now we initialize the PKCS11 subsystems
*/
secmodule = WH_FileName(NULL,xpSecModuleDB);
SECMOD_init(secmodule);
ret = PREF_GetIntPref("security.ask_for_password",&ask);
ret = PREF_GetIntPref("security.password_lifetime",&timeout);
slot = PK11_GetInternalKeySlot();
PK11_SetSlotPWValues(slot,SECNAV_AskPrefToPK11((int)ask),(int)timeout);
PK11_FreeSlot(slot);
/*
* Initialize the common parts of the security library.
*/
SEC_Init();
/* read export policy bits */
SECNAV_PolicyInit();
/* tell SSL what algs it is allowed to do by policy */
enable_SSL_cipher_policies();
/* tell SMIME what algs it is allowed to do by policy */
enable_SMIME_cipher_policies();
/* tell PKCS12 what algs it is allowed to do by policy */
enable_PKCS12_cipher_policies();
/* read preferences corresponding to policy bits */
SECNAV_PrefsRefresh();
/*
* Enable/Disable SSL based on user prefs
*/
ret = PREF_GetBoolPref("security.enable_ssl2", &prefval);
SSL_EnableDefault(SSL_ENABLE_SSL2, prefval);
ret = PREF_GetBoolPref("security.enable_ssl3", &prefval);
SSL_EnableDefault(SSL_ENABLE_SSL3, prefval);
ret = PREF_CopyCharPref("security.default_personal_cert", &prefchar);
SECNAV_SetDefUserCertList(prefchar);
/*
* Initialize the cipher preferences specified by the user.
*/
enable_SSL_cipher_prefs();
enable_SMIME_cipher_prefs();
SECNAV_DefaultEmailCertInit();
CERT_SetCAPolicyStringCallback(SECNAV_GetCAPolicyString, NULL);
}
/*
* Shutdown the security library.
*
* Currently closes the certificate and key databases.
*/
void
SECNAV_Shutdown(void)
{
CERTCertDBHandle *cdb_handle;
SECKEYKeyDBHandle *kdb_handle;
cdb_handle = CERT_GetDefaultCertDB();
if (cdb_handle != NULL) {
CERT_ClosePermCertDB(cdb_handle);
}
kdb_handle = SECKEY_GetDefaultKeyDB();
if (kdb_handle != NULL) {
SECKEY_CloseKeyDB(kdb_handle);
}
}
int
SECNAV_SetupSecureSocket(int fd, char *url, MWContext *cx)
{
int status;
if ( cx == NULL ) {
goto loser;
}
#ifndef AKBAR
#ifdef JAVA
/* XXX - if this is a java context, then we lose for now, since we can't
* figure out how to get the correct context at this point.
*/
if ( cx->type == MWContextJava ) {
cx = NSN_JavaContextToRealContext(cx);
}
#endif
#endif /* !AKBAR */
/* pass URL to ssl code */
status = SSL_SetURL(fd, (char *)url);
if (status) {
goto loser;
}
/* set certificate validation hook */
status = SSL_AuthCertificateHook(fd, SECNAV_ConfirmServerCert,
(void *)cx);
if (status) {
goto loser;
}
status = SSL_GetClientAuthDataHook(fd, SECNAV_GetClientAuthData,
(void *)cx);
if (status) {
goto loser;
}
status = SSL_BadCertHook(fd, SECNAV_HandleBadCert, (void *)cx);
if (status) {
goto loser;
}
#ifdef FORTEZZA
status = Fortezza_AlertHook(fd, SECMOZ_FortezzaAlert, (void *)cx);
if (status) {
goto loser;
}
#endif
return(0);
loser:
return(-1);
}
void
SECNAV_NewUserCertificate(MWContext *cx)
{
URL_Struct *fetchurl;
fetchurl = NET_CreateURLStruct(SEC_CLIENT_CERT_PAGE, NET_DONT_RELOAD);
/* don't set the window target if the context is null. This is for
* the Mac, which doesn't have an MWContext for preferences.
*/
if ( cx != NULL ) {
StrAllocCopy(fetchurl->window_target, "_CA_PAGE");
}
FE_GetURL(cx,fetchurl);
return;
}
/*
* Password obfuscation, etc.
*/
static unsigned char secmoz_tmmdi[] = { /* tmmdi == They Made Me Do It */
0xd0, /* jsw, karlton */
0x86, /* repka, paquin */
0x9c, /* freier, elgamal */
0xde, /* jonm, bobj */
0xc6, /* jevering, kent */
0xee, /* fur, sharoni */
0xeb, /* ari, sk */
0x3e /* terry, atotic */
};
char *
SECNAV_MungeString(const char *unmunged_string)
{
RC4Context *rc4;
unsigned char *munged_data;
unsigned int inlen, outlen, maxlen;
char *munged_string;
SECStatus rv;
/*
* The encrypted version of NULL is NULL.
*/
if (unmunged_string == NULL)
return NULL;
inlen = PORT_Strlen(unmunged_string);
/*
* Similarly, the encrypted version of "" is "".
* But, since our caller will want to free any non-null return value,
* we actually have to allocate and copy this (no)thing.
*/
if (inlen == 0)
return PORT_Strdup(unmunged_string);
maxlen = inlen + 64; /* XXX slop */
munged_data = (unsigned char *) PORT_Alloc(maxlen);
if (munged_data == NULL)
return NULL;
rc4 = RC4_CreateContext(secmoz_tmmdi, sizeof(secmoz_tmmdi));
if (rc4 == NULL) {
PORT_Free(munged_data);
return NULL;
}
rv = RC4_Encrypt(rc4, munged_data, &outlen, maxlen,
(const unsigned char *)unmunged_string, inlen);
RC4_DestroyContext(rc4, PR_TRUE);
if (rv != SECSuccess) {
PORT_Free(munged_data);
return NULL;
}
munged_string = BTOA_DataToAscii(munged_data, outlen);
PORT_Free(munged_data);
return munged_string;
}
char *
SECNAV_UnMungeString(const char *munged_string)
{
RC4Context *rc4;
unsigned char *munged_data;
unsigned int inlen, outlen, maxlen;
char *unmunged_string;
SECStatus rv;
/*
* The decrypted version of NULL is NULL.
*/
if (munged_string == NULL)
return NULL;
/*
* Similarly, the decrypted version of "" is "".
* But, since our caller will want to free any non-null return value,
* we actually have to allocate and copy this (no)thing.
*/
if (*munged_string == '\0')
return PORT_Strdup(munged_string);
munged_data = ATOB_AsciiToData(munged_string, &inlen);
if (munged_data == NULL)
return NULL;
maxlen = inlen + 64; /* XXX slop */
unmunged_string = (char *) PORT_Alloc(maxlen);
if (unmunged_string == NULL) {
PORT_Free(munged_data);
return NULL;
}
--maxlen; /* reserve 1 for null terminator */
rc4 = RC4_CreateContext(secmoz_tmmdi, sizeof(secmoz_tmmdi));
if (rc4 == NULL) {
PORT_Free(munged_data);
PORT_Free(unmunged_string);
return NULL;
}
rv = RC4_Decrypt(rc4, (unsigned char *)unmunged_string, &outlen, maxlen,
munged_data, inlen);
RC4_DestroyContext(rc4, PR_TRUE);
PORT_Free(munged_data);
if (rv != SECSuccess) {
PORT_Free(unmunged_string);
return NULL;
}
unmunged_string[outlen] = '\0';
return unmunged_string;
}
/*
* extract the database key from a certificate and base64 encode it
* to turn it into a string
*/
char *
secmoz_CertToKeyString(CERTCertificate *cert)
{
char *keystring;
keystring = BTOA_DataToAscii(cert->certKey.data, cert->certKey.len);
return(keystring);
}
/*
* Convert a URL with a base64 encoded cert database key in it to the
* certificate that the key references.
*/
CERTCertificate *
secmoz_URLToCert(char *url)
{
char *keystring;
SECItem dbkey;
CERTCertificate *cert;
unsigned int size;
/* base 64 encoded database key is after : */
keystring = PORT_Strchr(url, ':');
if ( keystring == NULL ) {
return NULL;
}
keystring++;
if ( *keystring == '\0' ) {
return NULL;
}
/* do the base64 decode */
dbkey.data = ATOB_AsciiToData(keystring, &size);
dbkey.len = (size_t)size;
if ( dbkey.data == NULL ) {
return NULL;
}
/* look up the cert */
cert = CERT_FindCertByKey(CERT_GetDefaultCertDB(), &dbkey);
return(cert);
}
/*
* make a string of ascii hex digits from the certificate serial number
* this is used for CA URLs
*/
char *
secmoz_GetCertSNString(CERTCertificate *cert)
{
SECItem *snitem;
char *snstring, *str;
int len;
unsigned char *data;
char t;
snitem = &cert->serialNumber;
len = snitem->len;
data = snitem->data;
str = snstring = PORT_Alloc(len * 2 + 1);
if ( snstring ) {
/* skip leading zeros */
while ( ( len > 1 ) && ( *data == 0 ) ) {
data++;
len--;
}
/* convert to string */
while (len) {
/* first digit */
t = (char)(*data >> 4);
*str++ = (t < 10 ) ? (t+'0') : (t - 10 + 'a');
/* second digit */
t = (char)(*data & 0xf);
*str++ = (t < 10 ) ? (t+'0') : (t - 10 + 'a');
data++;
len--;
}
*str = '\0';
}
return(snstring);
}
static void
fetchURL(void *closure)
{
URL_Struct *fetchurl;
MWContext *cx;
fetchurl = (URL_Struct *)closure;
cx = (MWContext *)fetchurl->sec_private;
FE_GetURL(cx, fetchurl);
return;
}
static void
secmoz_HandleInternalCertRevokeURL(URL_Struct *url, MWContext *cx,
CERTCertificate *cert)
{
URL_Struct *fetchurl = NULL;
char *serialnumber = NULL;
char *fetchstring = NULL;
char *urlstring = NULL;
serialnumber = secmoz_GetCertSNString(cert);
if ( serialnumber == NULL ) {
goto loser;
}
urlstring = CERT_FindCertURLExtension(cert,
SEC_OID_NS_CERT_EXT_REVOCATION_URL,
SEC_OID_NS_CERT_EXT_CA_REVOCATION_URL);
if ( urlstring == NULL ) {
goto loser;
}
fetchstring = (char *)PORT_Alloc(PORT_Strlen(serialnumber) +
PORT_Strlen(urlstring) + 1);
if ( fetchstring == NULL ) {
goto loser;
}
/* copy the base url data */
PORT_Strcpy(fetchstring, urlstring);
/* copy the serial number */
XP_STRCAT(fetchstring, serialnumber);
fetchurl = NET_CreateURLStruct(fetchstring, NET_DONT_RELOAD);
fetchurl->sec_private = (void *)cx;
(void)FE_SetTimeout(fetchURL, (void *)fetchurl, 0);
goto done;
loser:
if ( fetchurl ) {
NET_FreeURLStruct(fetchurl);
}
done:
if ( urlstring ) {
PORT_Free(urlstring);
}
if ( serialnumber ) {
PORT_Free(serialnumber);
}
if ( fetchstring ) {
PORT_Free(fetchstring);
}
return;
}
static void
secmoz_HandleInternalCertPolicyURL(URL_Struct *url, MWContext *cx,
CERTCertificate *cert)
{
URL_Struct *fetchurl = NULL;
char *fetchstring = NULL;
char *urlstring = NULL;
urlstring = CERT_FindCertURLExtension(cert,
SEC_OID_NS_CERT_EXT_CA_POLICY_URL,
SEC_OID_NS_CERT_EXT_CA_POLICY_URL);
if ( urlstring == NULL ) {
goto loser;
}
fetchstring = (char *)PORT_Alloc(PORT_Strlen(urlstring) + 1);
if ( fetchstring == NULL ) {
goto loser;
}
/* copy the base url data */
PORT_Strcpy(fetchstring, urlstring);
fetchurl = NET_CreateURLStruct(fetchstring, NET_DONT_RELOAD);
StrAllocCopy(fetchurl->window_target, "_CA_POLICY");
fetchurl->sec_private = (void *)cx;
(void)FE_SetTimeout(fetchURL, (void *)fetchurl, 0);
goto done;
loser:
if ( fetchurl ) {
NET_FreeURLStruct(fetchurl);
}
done:
if ( urlstring ) {
PORT_Free(urlstring);
}
if ( fetchstring ) {
PORT_Free(fetchstring);
}
return;
}
static void
secmoz_HandleInternalCertURL(URL_Struct *url, MWContext *cx)
{
CERTCertificate *cert;
cert = secmoz_URLToCert(url->address);
if ( cert == NULL ) {
return;
}
if(!strncasecomp(url->address,"internal-security-cert-revoke",29)) {
secmoz_HandleInternalCertRevokeURL(url, cx, cert);
} else if(!strncasecomp(url->address,"internal-security-cert-policy",29)) {
secmoz_HandleInternalCertPolicyURL(url, cx, cert);
}
return;
}
void
SECNAV_HandleInternalSecURL(URL_Struct *url, MWContext *cx)
{
if(!strncasecomp(url->address,"internal-security-cert-",23)) {
secmoz_HandleInternalCertURL(url, cx);
}
/* XXX - what to do if no match? */
return;
}
#define POLICY_START_STRING "<form action=\"internal-security-cert-policy:"
#define POLICY_MID_STRING "\" method=post><input type=submit value=\""
#define POLICY_END_STRING "\"></form>"
char *
SECMOZ_MakeCertPolicyButtonString(CERTCertificate *cert)
{
char *ret;
int len;
char *keystring;
char *urlstring;
char *intlstring;
/* make sure that the extension is there */
urlstring = CERT_FindCertURLExtension(cert,
SEC_OID_NS_CERT_EXT_CA_POLICY_URL,
SEC_OID_NS_CERT_EXT_CA_POLICY_URL);
if ( urlstring ) {
/* extension is there, but we don't need the string right now */
PORT_Free(urlstring);
} else {
/* extension isn't there, so give up */
return(NULL);
}
/* make the keystring */
keystring = secmoz_CertToKeyString(cert);
if ( keystring == NULL ) {
return NULL;
}
intlstring = XP_GetString(XP_VIEW_CERT_POLICY);
len = PORT_Strlen(keystring) + PORT_Strlen(intlstring) +
sizeof(POLICY_START_STRING) + sizeof(POLICY_MID_STRING) +
sizeof(POLICY_END_STRING);
ret = (char *)PORT_Alloc(len);
/* make the full url */
if ( ret ) {
PORT_Strcpy(ret, POLICY_START_STRING);
XP_STRCAT(ret, keystring);
XP_STRCAT(ret, POLICY_MID_STRING);
XP_STRCAT(ret, intlstring);
XP_STRCAT(ret, POLICY_END_STRING);
}
PORT_Free(keystring);
return(ret);
}
#define REVOKE_START_STRING "<form action=\"internal-security-cert-revoke:"
#define REVOKE_MID_STRING "\" method=post><input type=submit value=\""
#define REVOKE_END_STRING "\"></form>"
char *
SECMOZ_MakeCertRevokeButtonString(CERTCertificate *cert)
{
char *ret;
int len;
char *keystring;
char *urlstring;
char *intlstring;
/* make sure that the extension is there */
urlstring = CERT_FindCertURLExtension(cert,
SEC_OID_NS_CERT_EXT_REVOCATION_URL,
SEC_OID_NS_CERT_EXT_CA_REVOCATION_URL);
if ( urlstring ) {
/* extension is there, but we don't need the string right now */
PORT_Free(urlstring);
} else {
/* extension isn't there, so give up */
return(NULL);
}
/* make the keystring */
keystring = secmoz_CertToKeyString(cert);
if ( keystring == NULL ) {
return NULL;
}
intlstring = XP_GetString(XP_CHECK_CERT_STATUS);
len = PORT_Strlen(keystring) + PORT_Strlen(intlstring) +
sizeof(REVOKE_START_STRING) + sizeof(REVOKE_MID_STRING) +
sizeof(REVOKE_END_STRING);
ret = (char *)PORT_Alloc(len);
/* make the full url */
if ( ret ) {
PORT_Strcpy(ret, REVOKE_START_STRING);
XP_STRCAT(ret, keystring);
XP_STRCAT(ret, REVOKE_MID_STRING);
XP_STRCAT(ret, intlstring);
XP_STRCAT(ret, REVOKE_END_STRING);
}
PORT_Free(keystring);
return(ret);
}
char *
SECNAV_MakeCertButtonString(CERTCertificate *cert)
{
char *str = NULL;
char *extstring;
extstring = SECMOZ_MakeCertRevokeButtonString(cert);
if ( extstring ) {
StrAllocCat(str, extstring);
PORT_Free(extstring);
}
extstring = SECMOZ_MakeCertPolicyButtonString(cert);
if ( extstring ) {
if ( str ) {
StrAllocCat(str, "</td><td>");
}
StrAllocCat(str, extstring);
PORT_Free(extstring);
}
if ( str ) {
extstring = NULL;
StrAllocCat(extstring, "<table><tr><td>");
StrAllocCat(extstring, str);
StrAllocCat(extstring, "</td></tr></table>");
PORT_Free(str);
}
return(extstring);
}
/* ----------------------------------------------------------------------
** stuff for cipher preferences dialogs
*/
static SECStatus
enable_SSL_cipher_prefs(void)
{
SECNAVPolicyFind *snpf;
long protocolID;
snpf = SECNAV_FindInit(CIPHER_FAMILY_SSL, PR_FALSE, PR_FALSE);
if (!snpf)
return SECFailure;
while (0 <= (protocolID = SECNAV_FindNext(snpf))) {
int pref = SECNAV_AlgPreferred(protocolID);
SSL_EnableCipher(protocolID, pref);
}
SECNAV_FindEnd(snpf);
return SECSuccess;
}
static SECStatus
enable_SSL_cipher_policies(void)
{
SECNAVPolicyFind *snpf;
long protocolID;
snpf = SECNAV_FindInit(CIPHER_FAMILY_SSL, PR_FALSE, PR_FALSE);
if (!snpf)
return SECFailure;
while (0 <= (protocolID = SECNAV_FindNext(snpf))) {
int allowed = SECNAV_AlgAllowed(protocolID);
SSL_SetPolicy(protocolID, allowed);
}
SECNAV_FindEnd(snpf);
return SECSuccess;
}
static SECStatus
enable_SMIME_cipher_prefs(void)
{
SECNAVPolicyFind *snpf;
long protocolID;
snpf = SECNAV_FindInit(CIPHER_FAMILY_SMIME, PR_FALSE, PR_FALSE);
if (!snpf)
return SECFailure;
while (0 <= (protocolID = SECNAV_FindNext(snpf))) {
int pref = SECNAV_AlgPreferred(protocolID);
SECMIME_EnableCipher(protocolID, pref);
}
SECNAV_FindEnd(snpf);
/* now tell secmime that we've sent it the last preference */
SECMIME_EnableCipher(CIPHER_FAMILYID_MASK, 0);
return SECSuccess;
}
static SECStatus
enable_SMIME_cipher_policies(void)
{
SECNAVPolicyFind *snpf;
long protocolID;
snpf = SECNAV_FindInit(CIPHER_FAMILY_SMIME, PR_FALSE, PR_FALSE);
if (!snpf)
return SECFailure;
while (0 <= (protocolID = SECNAV_FindNext(snpf))) {
int allowed = SECNAV_AlgAllowed(protocolID);
SECMIME_SetPolicy(protocolID, allowed);
}
SECNAV_FindEnd(snpf);
return SECSuccess;
}
static SECStatus
enable_PKCS12_cipher_policies(void)
{
SECNAVPolicyFind *snpf;
long protocolID;
snpf = SECNAV_FindInit(CIPHER_FAMILY_PKCS12, PR_FALSE, PR_FALSE);
if (!snpf)
return SECFailure;
while (0 <= (protocolID = SECNAV_FindNext(snpf))) {
int allowed = SECNAV_AlgAllowed(protocolID);
SEC_PKCS12EnableCipher(protocolID, allowed);
}
SECNAV_FindEnd(snpf);
return SECSuccess;
}
PRBool
ciphers_dialog_handler(XPDialogState *dialog_state, char **argv,
int argc, unsigned int button)
{
SECNAVPolicyFind *snpf;
char * family;
long protocolID;
int count = 0;
if (button != XP_DIALOG_OK_BUTTON)
goto loser;
switch ((int)dialog_state->arg) {
case SSL_LIBRARY_VERSION_2: family = CIPHER_FAMILY_SSL2; break;
case SSL_LIBRARY_VERSION_3_0: family = CIPHER_FAMILY_SSL3; break;
case SMIME_LIBRARY_VERSION_1_0: family = CIPHER_FAMILY_SMIME; break;
default: goto loser;
}
/* get only the ciphers permitted by policy */
snpf = SECNAV_FindInit(family, PR_TRUE, PR_FALSE);
if (!snpf)
goto loser;
while (0 <= (protocolID = SECNAV_FindNext(snpf))) {
char * value;
const char * protoName;
PRBool boolVal;
protoName = SECNAV_GetProtoName(protocolID);
value = XP_FindValueInArgs((char *)protoName, argv, argc);
/* if the checkbox was checked, then value will "on".
* if the checkbox was unchecked, then value will be NULL, not "off".
*/
boolVal = ((value != NULL) && !PORT_Strcasecmp(value, "on"));
SECNAV_PrefBoolUpdate(protocolID, boolVal);
++count;
}
SECNAV_FindEnd(snpf);
if (count) {
PREF_SavePrefFile();
if (((int)dialog_state->arg) == SMIME_LIBRARY_VERSION_1_0)
enable_SMIME_cipher_prefs();
else
enable_SSL_cipher_prefs();
}
loser:
return PR_FALSE; /* tells caller not to leave window on screen */
}
static XPDialogInfo ciphersDialog = {
XP_DIALOG_OK_BUTTON | XP_DIALOG_CANCEL_BUTTON,
ciphers_dialog_handler,
550,
400,
};
void
SECNAV_MakeCiphersDialog(void *proto_win, int version)
{
XPDialogStrings * strings;
char * family;
char * toggles = NULL;
const char * conditional = NULL;
SECNAVPolicyFind * snpf;
long protocolID;
switch (version) {
case SSL_LIBRARY_VERSION_2:
strings = XP_GetDialogStrings(XP_CONFIG_SSL2_CIPHERS_STRINGS);
family = CIPHER_FAMILY_SSL2;
break;
case SSL_LIBRARY_VERSION_3_0:
strings = XP_GetDialogStrings(XP_CONFIG_SSL3_CIPHERS_STRINGS);
family = CIPHER_FAMILY_SSL3;
break;
case SMIME_LIBRARY_VERSION_1_0:
strings = XP_GetDialogStrings(XP_CONFIG_SMIME_CIPHERS_STRINGS);
family = CIPHER_FAMILY_SMIME;
break;
default:
goto loser;
}
if (strings == NULL)
return;
XP_CopyDialogString(strings, 0, ""); /* "" hack, sorry */
/* get only the ciphers permitted by policy */
snpf = SECNAV_FindInit(family, PR_TRUE, PR_FALSE);
if (!snpf)
goto loser;
while (0 <= (protocolID = SECNAV_FindNext(snpf))) {
PRBool allowed = SECNAV_AlgAllowed(protocolID);
StrAllocCat(toggles, "<li><input type=checkbox name=");
StrAllocCat(toggles, SECNAV_GetProtoName(protocolID));
if (SECNAV_AlgPreferred(protocolID)) {
StrAllocCat(toggles, " checked");
}
StrAllocCat(toggles, "> ");
StrAllocCat(toggles, SECNAV_GetProtoDesc(protocolID));
if (allowed != PR_TRUE && allowed != PR_FALSE) {
if (conditional == NULL)
conditional = XP_GetString(XP_CONFIG_MAYBE_CIPHERS_STRINGS);
if (conditional != NULL)
StrAllocCat(toggles, conditional);
}
}
SECNAV_FindEnd(snpf);
if (toggles) {
XP_CopyDialogString(strings, 1, toggles);
PORT_Free(toggles);
} else {
/* there were no Ciphers permitted by Policy. */
conditional = XP_GetString(XP_CONFIG_NO_CIPHERS_STRINGS);
if (conditional != NULL)
XP_CopyDialogString(strings, 1, conditional);
}
XP_MakeHTMLDialog(proto_win, &ciphersDialog, XP_CONFCIPHERS_TITLE_STRING,
strings, (void *)version);
loser:
XP_FreeDialogStrings(strings);
return;
}
/* --------------------------------------------------------------------- */
extern int SA_ASK_SA_EVERY_TIME_LABEL;
extern int SA_SELECT_AUTO_LABEL;
CERTCertNicknames *
SECNAV_GetDefUserCertList(CERTCertDBHandle *handle,void *wincx)
{
CERTCertNicknames *names;
names = CERT_GetCertNicknames(handle, SEC_CERT_NICKNAMES_USER,wincx);
if (names) {
char *askUser, *pickAuto;
char **newnames;
askUser = XP_GetString(SA_ASK_SA_EVERY_TIME_LABEL);
pickAuto = XP_GetString(SA_SELECT_AUTO_LABEL);
newnames = PORT_ArenaGrow(names->arena, names->nicknames,
names->numnicknames * sizeof(char *),
names->numnicknames * sizeof(char *) + 2);
if (newnames) {
/* Move first two certs to the end of the list, making
sure to avoid a UMR in case numnicknames is 1. */
names->nicknames = newnames;
if (names->numnicknames > 1) {
names->nicknames[names->numnicknames] =
names->nicknames[0];
names->nicknames[names->numnicknames + 1] =
names->nicknames[1];
} else if (names->numnicknames == 1) {
names->nicknames[2] = names->nicknames[0];
}
names->nicknames[0] = askUser;
names->nicknames[1] = pickAuto;
names->numnicknames += 2;
names->totallen += PORT_Strlen(askUser) + PORT_Strlen(pickAuto);
} else {
CERT_FreeNicknames(names);
names = NULL;
}
}
return (names);
}
SECStatus
SECNAV_SetDefUserCertList(char *chosen)
{
char *askUser, *pickAuto;
askUser = XP_GetString(SA_ASK_SA_EVERY_TIME_LABEL);
pickAuto = XP_GetString(SA_SELECT_AUTO_LABEL);
if (UserCertChoice.chosen)
PORT_Free(UserCertChoice.chosen);
if ( chosen == NULL ) {
UserCertChoice.chosen = NULL;
UserCertChoice.mode = ASK;
} else {
UserCertChoice.chosen = PORT_Strdup(chosen);
if (PORT_Strcmp(chosen, askUser) == 0) {
UserCertChoice.mode = ASK;
} else if (PORT_Strcmp(chosen, pickAuto) == 0) {
UserCertChoice.mode = AUTO;
} else {
UserCertChoice.mode = STR;
}
}
return (PR_TRUE);
}
PRBool
SECNAV_SecurityDialog(MWContext *context, int state)
{
XP_Bool value, *valueptr, savevalue;
int ret;
char *prefname = NULL;
PRBool rv;
valueptr = &value;
switch (state) {
case SD_INSECURE_POST_FROM_INSECURE_DOC:
prefname = "security.warn_submit_insecure";
break;
case SD_ENTERING_SECURE_SPACE:
prefname = "security.warn_entering_secure";
break;
case SD_LEAVING_SECURE_SPACE:
prefname = "security.warn_leaving_secure";
break;
case SD_INSECURE_DOCS_WITHIN_SECURE_DOCS_NOT_SHOWN:
prefname = "security.viewing_mixed";
break;
case SD_INSECURE_POST_FROM_SECURE_DOC:
case SD_REDIRECTION_TO_INSECURE_DOC:
case SD_REDIRECTION_TO_SECURE_SITE:
valueptr = 0;
break;
}
if ( prefname ) {
ret = PREF_GetBoolPref(prefname, &value);
savevalue = value;
if ( !value ) {
return(PR_TRUE);
}
}
ret = (int)FE_SecurityDialog(context, state
#ifndef AKBAR
, valueptr
#endif /* AKBAR */
);
rv = (PRBool)ret;
if ( prefname && ( value != savevalue ) ) {
ret = PREF_SetBoolPref(prefname, value);
ret = PREF_SavePrefFile();
}
return(rv);
}
void SECNAV_Logout(void) {
PK11_LogoutAll();
SSL_ClearSessionCache();
}
/* additions for importing and exporting PKCS 12 files */
struct secnav_ExportFileInfo{
char *filename; /* export filename */
XP_File file_ptr; /* pointer to file */
PRBool error; /* error occurred? */
SECItem *data; /* info to write */
};
static void
secnav_DestroyExportFileInfo(struct secnav_ExportFileInfo **exp_ptr)
{
if((*exp_ptr)->data != NULL) {
SECITEM_ZfreeItem((*exp_ptr)->data, PR_FALSE);
}
if((*exp_ptr)->filename != NULL) {
PORT_Free((*exp_ptr)->filename);
}
if((*exp_ptr)->file_ptr != NULL) {
XP_FileClose((*exp_ptr)->file_ptr);
}
PORT_Free(*exp_ptr);
}
static PRBool
secnav_OpenExportFile(MWContext *ctxt,
struct secnav_ExportFileInfo *exp_info,
PRBool file_read)
{
if(exp_info == NULL)
return PR_FALSE;
if(file_read == PR_TRUE) {
#ifdef XP_MAC
/* XXX We are using xpMailFolder here because on the MAC, xpTemporary
* modifies the filename, while xpMailFolder does not. This is
* a hack to accomplish the desired functionality and should be
* replaced with something cleaner later.
*/
exp_info->file_ptr = XP_FileOpen(exp_info->filename,
xpMailFolder,
XP_FILE_READ_BIN);
#else
exp_info->file_ptr = XP_FileOpen(exp_info->filename,
xpTemporary,
XP_FILE_READ_BIN);
#endif
} else {
exp_info->file_ptr = XP_FileOpen(exp_info->filename,
xpPKCS12File,
XP_FILE_WRITE_BIN);
}
if(!exp_info->file_ptr) {
exp_info->error = PR_TRUE;
PORT_SetError(SEC_ERROR_NO_MEMORY);
return PR_FALSE;
}
return PR_TRUE;
}
static PRBool
secnav_WriteExportFile(MWContext *ctxt,
struct secnav_ExportFileInfo *exp_info)
{
int write_len;
write_len = XP_FileWrite(exp_info->data->data,
(int32)exp_info->data->len,
exp_info->file_ptr);
XP_FileClose(exp_info->file_ptr);
exp_info->file_ptr = NULL;
if(write_len != (int)exp_info->data->len) {
/* error occured -- remove file */
XP_FileRemove(exp_info->filename, xpPKCS12File);
PORT_SetError(SEC_ERROR_PKCS12_UNABLE_TO_WRITE);
exp_info->error = PR_TRUE;
return PR_FALSE;
}
exp_info->error = PR_FALSE;
return PR_TRUE;
}
static void
simple_copy(MWContext *context,
char *newFile,
void *closure)
{
if(newFile == NULL) {
return;
}
PORT_Strcpy((char *)closure, newFile);
return;
}
PRBool
secnav_ReadExportFile(MWContext *ctxt,
struct secnav_ExportFileInfo *exp_info)
{
XP_StatStruct exp_stat;
PRBool ret_val = PR_TRUE;
int read_len;
char testBuf[2];
/* get information about the file */
#ifdef XP_MAC
if(!XP_Stat(exp_info->filename, &exp_stat, xpMailFolder)) {
#else
if(!XP_Stat(exp_info->filename, &exp_stat, xpTemporary)) {
#endif
/* rewind the file */
XP_FileSeek(exp_info->file_ptr, 0L, SEEK_SET);
/* is the file at least 2 bytes -- even an empty PFX file
* will contain a header byte and a length byte
*/
if(exp_stat.st_size >= 2) {
XP_FileRead(testBuf, 2, exp_info->file_ptr);
/* check that the first two bytes are appropriate for the
* size and type of the file -- filter out people trying
* to import NETSCAPE.exe
*/
if(SEC_PKCS12ValidData(testBuf, 2, (long int)exp_stat.st_size)) {
/* reset the file pointer to the begining, allocate space
* and read in the file
*/
XP_FileSeek(exp_info->file_ptr, 0L, SEEK_SET);
exp_info->data = (SECItem *)PORT_Alloc(sizeof(SECItem));
if(exp_info->data != NULL) {
exp_info->data->data =
(unsigned char *)PORT_Alloc(exp_stat.st_size);
if(exp_info->data->data != NULL) {
exp_info->data->len = exp_stat.st_size;
read_len = XP_FileRead(exp_info->data->data,
(int32)exp_info->data->len,
exp_info->file_ptr);
if(read_len != (int)exp_info->data->len) {
PORT_SetError(SEC_ERROR_PKCS12_UNABLE_TO_READ);
ret_val = PR_FALSE;
}
}
} else {
PORT_SetError(SEC_ERROR_PKCS12_DECODING_PFX);
ret_val = PR_FALSE;
}
} else {
PORT_SetError(SEC_ERROR_PKCS12_DECODING_PFX);
ret_val = PR_FALSE;
}
} else {
PORT_SetError(SEC_ERROR_PKCS12_UNABLE_TO_READ);
ret_val = PR_FALSE;
}
}
XP_FileClose(exp_info->file_ptr);
exp_info->file_ptr = NULL;
if(ret_val == PR_FALSE) {
exp_info->error = PR_TRUE;
} else {
exp_info->error = PR_FALSE;
}
return ret_val;
}
SECItem *
SECNAV_ReadExportFile(void *ctxt)
{
PRBool ret_val = PR_FALSE;
struct secnav_ExportFileInfo *exp_info = NULL;
SECItem *ret_item = NULL;
int fe_ret = 0;
exp_info = PORT_ZAlloc(sizeof(struct secnav_ExportFileInfo));
if(exp_info == NULL)
goto loser;
exp_info->data = NULL;
exp_info->error = PR_FALSE;
exp_info->filename = (char *)PORT_ZAlloc(2048); /* XXX different length? */
if(exp_info->filename == NULL) {
goto loser;
}
fe_ret = FE_PromptForFileName(ctxt,
XP_GetString(XP_PKCS12_IMPORT_FILE_PROMPT),
"*.p12", /* filename */
PR_TRUE, /* file need not exist */
FALSE, /* do not allow directories */
simple_copy, /* callback */
(void *)exp_info->filename); /* closure */
/* properly check for cancel. On UNIX/Win32 fe_ret is negative
* when cancel is pressed. On the MAC, it is not, in all cases
* a zero length string indicates cancel
*/
if((fe_ret >= 0) && (PORT_Strlen(exp_info->filename) > 0)) {
secnav_OpenExportFile((MWContext *)ctxt, exp_info, PR_TRUE);
if(exp_info->error == PR_FALSE) {
ret_val = secnav_ReadExportFile((MWContext *)ctxt, exp_info);
if(exp_info->error == PR_FALSE) {
ret_item = SECITEM_DupItem(exp_info->data);
if(ret_item == NULL) {
ret_val = PR_FALSE;
exp_info->error = PR_TRUE;
}
}
}
else {
ret_val = PR_FALSE;
}
} else {
ret_val = PR_FALSE;
}
/* let stuff fall through */
loser:
if((ret_val == PR_FALSE) || (fe_ret < 0)) {
if((fe_ret < 0) || (PORT_Strlen(exp_info->filename) == 0)) {
PORT_SetError(SEC_ERROR_USER_CANCELLED);
}
if(ret_item != NULL) {
SECITEM_ZfreeItem(ret_item, PR_TRUE);
ret_item = NULL;
}
}
secnav_DestroyExportFileInfo(&exp_info);
return ret_item;
}
PRBool
SECNAV_WriteExportFile(void *ctxt, SECItem *data)
{
PRBool ret_val = PR_FALSE;
struct secnav_ExportFileInfo *exp_info = NULL;
int fe_ret;
if(data == NULL)
return PR_FALSE;
exp_info = PORT_ZAlloc(sizeof(struct secnav_ExportFileInfo));
if(exp_info == NULL)
goto loser;
exp_info->data = data;
exp_info->error = PR_FALSE;
exp_info->filename = (char *)PORT_ZAlloc(2048); /* XXX different length? */
if(exp_info->filename == NULL) {
goto loser;
}
fe_ret = FE_PromptForFileName(ctxt,
XP_GetString(XP_PKCS12_EXPORT_FILE_PROMPT),
"*.p12", /* filename */
FALSE, /* file need not exist */
FALSE, /* do not allow directories */
simple_copy, /* callback */
(void *)exp_info->filename); /* closure */
/* properly catch a cancel press. on mac fe_ret is non-negative
* when cancel is pressed. on unix/win32 it is positive. so,
* we check for a 0 length filename as well as checking fe_ret.
*/
if((fe_ret >= 0) && (PORT_Strlen(exp_info->filename) > 0)) {
secnav_OpenExportFile((MWContext *)ctxt, exp_info, PR_FALSE);
if(exp_info->error == PR_FALSE) {
ret_val = secnav_WriteExportFile((MWContext *)ctxt, exp_info);
}
else {
ret_val = PR_FALSE;
}
} else {
PORT_SetError(SEC_ERROR_USER_CANCELLED);
ret_val = PR_FALSE;
}
/* let stuff fall through */
loser:
secnav_DestroyExportFileInfo(&exp_info);
return ret_val;
}
SECItem *SECNAV_GetExportPassword(void *arg)
{
char *pw, *confirm_pw;
MWContext *cx;
SECItem *pwitem;
SECNAVPKCS12PwdPromptArg *pwd_arg;
int prompt_val;
if(arg == NULL) {
return NULL;
}
pwd_arg = (SECNAVPKCS12PwdPromptArg *)arg;
cx = (MWContext *)pwd_arg->wincx;
switch(pwd_arg->prompt_type) {
case pwdGenericExport:
prompt_val = XP_SEC_ENTER_EXPORT_PWD;
break;
case pwdGenericImport:
prompt_val = XP_SEC_ENTER_IMPORT_PWD;
break;
default:
/* handle other prompts when they are needed... */
return NULL;
}
while(1)
{
/* get the password */
pw = FE_PromptPassword(cx, XP_GetString(prompt_val));
if(pw == NULL) {
PORT_SetError(SEC_ERROR_USER_CANCELLED);
goto loser;
}
/* on export, we want to reconfirm passwords */
if(pwd_arg->prompt_type == pwdGenericExport) {
confirm_pw = FE_PromptPassword(cx,
XP_GetString(XP_SEC_REENTER_TO_CONFIRM_PWD));
if(confirm_pw == NULL) {
PORT_Free(pw);
PORT_SetError(SEC_ERROR_USER_CANCELLED);
goto loser;
}
if(PORT_Strcmp(pw, confirm_pw)) {
prompt_val = XP_SEC_BAD_CONFIRM_EXPORT_PWD;
PORT_Free(confirm_pw);
PORT_Free(pw);
continue;
} else {
PORT_Free(confirm_pw);
}
}
pwitem = (SECItem *)PORT_Alloc(sizeof(SECItem));
if(pwitem == NULL)
{
PORT_SetError(SEC_ERROR_NO_MEMORY);
SECNAV_ZeroPassword(pw);
PORT_Free(pw);
goto loser;
}
pwitem->data = PORT_Alloc(PORT_Strlen(pw) + 1);
if(pwitem != NULL)
{
PORT_Memcpy(pwitem->data, pw, PORT_Strlen(pw));
pwitem->len = PORT_Strlen(pw);
pwitem->data[PORT_Strlen(pw)] = 0;
}
else
{
PORT_SetError(SEC_ERROR_NO_MEMORY);
SECNAV_ZeroPassword(pw);
PORT_Free(pw);
PORT_Free(pwitem);
goto loser;
}
return pwitem;
}
PORT_Assert(0); /* should never get here */
loser:
return NULL;
}
SECItem *
SECNAV_NicknameCollisionCallback(SECItem *old_nick,
PRBool *cancel,
void *wincx)
{
char *nick = NULL;
SECItem *ret_nick = NULL;
MWContext *cx;
if((old_nick == NULL) || (cancel == NULL)){
return NULL;
}
cx = wincx;
nick = FE_Prompt(cx,
XP_GetString(XP_SEC_PROMPT_NICKNAME_COLLISION),
(char *)old_nick->data);
if(nick == NULL) {
*cancel = PR_TRUE;
return NULL;
}
if(PORT_Strcmp((char *)old_nick->data, nick)) {
ret_nick = (SECItem *)PORT_ZAlloc(sizeof(SECItem));
if(ret_nick == NULL) {
PORT_Free(nick);
return NULL;
} else {
ret_nick->data = (unsigned char *)nick;
ret_nick->len = PORT_Strlen(nick);
}
} else {
PORT_Free(nick);
return NULL;
}
return ret_nick;
}
/* Returns a string, a comma separated list of the country codes in all
* the "user certs". Or returns NULL if no user certs exists.
*/
char *
SECNAV_GetUserCertCountries(void)
{
return NULL; /* XXX FIX ME - jsw, please fill this in. */
}
static void
secnav_DoPKCS12ImportErrors(void *proto_win)
{
int error_value;
error_value = PORT_GetError();
if((error_value == SEC_ERROR_PKCS12_DECODING_PFX) ||
(error_value == SEC_ERROR_PKCS12_INVALID_MAC) ||
(error_value == SEC_ERROR_PKCS12_UNSUPPORTED_MAC_ALGORITHM) ||
(error_value == SEC_ERROR_PKCS12_UNSUPPORTED_TRANSPORT_MODE) ||
(error_value == SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE) ||
(error_value == SEC_ERROR_PKCS12_UNSUPPORTED_PBE_ALGORITHM) ||
(error_value == SEC_ERROR_PKCS12_UNSUPPORTED_VERSION) ||
(error_value == SEC_ERROR_PKCS12_PRIVACY_PASSWORD_INCORRECT) ||
(error_value == SEC_ERROR_PKCS12_CERT_COLLISION) ||
(error_value == SEC_ERROR_PKCS12_DUPLICATE_DATA) ||
(error_value == SEC_ERROR_BAD_EXPORT_ALGORITHM) ||
(error_value == SEC_ERROR_PKCS12_UNABLE_TO_READ)) {
FE_Alert(proto_win, XP_GetString(error_value));
} else if(error_value == SEC_ERROR_USER_CANCELLED) {
;
} else {
FE_Alert(proto_win, XP_GetString(SEC_ERROR_IMPORTING_CERTIFICATES));
}
}
typedef struct {
void *state;
void *dlgstate;
} PKCS12RedrawCallbackArg;
typedef void (*refreshCallback)(void *);
SECStatus
SECNAV_ImportPKCS12Object(void *arg, PRBool cancel)
{
SECNAVPKCS12ImportArg *import_arg = NULL;
PKCS12RedrawCallbackArg *cbarg = NULL;
SECItem *pfx = NULL;
SECStatus rv;
refreshCallback callback;
PK11SlotInfo *slot = NULL;
if(arg == NULL) {
return SECFailure;
}
import_arg = (SECNAVPKCS12ImportArg *)arg;
slot = (PK11SlotInfo *)import_arg->slot_ptr;
if(cancel == PR_TRUE) {
PORT_SetError(SEC_ERROR_USER_CANCELLED);
goto loser;
}
if(PK11_NeedLogin(slot) && !PK11_IsLoggedIn(slot)) {
SECStatus rv = PK11_DoPassword(slot, import_arg->proto_win);
if(rv != SECSuccess) {
goto loser;
}
}
cbarg = (PKCS12RedrawCallbackArg *)PORT_Alloc(sizeof(PKCS12RedrawCallbackArg));
if(cbarg) {
cbarg->dlgstate = import_arg->dlg_state;
cbarg->state = import_arg->sa_state;
pfx = SECNAV_ReadExportFile((void *)import_arg->proto_win);
if(pfx != NULL) {
SECNAVPKCS12PwdPromptArg pwd_arg;
pwd_arg.prompt_type = pwdGenericImport;
pwd_arg.wincx = import_arg->proto_win;
rv = SEC_PKCS12PutPFX(pfx,
SECNAV_GetExportPassword,
(void *)&pwd_arg,
SECNAV_NicknameCollisionCallback,
(PK11SlotInfo *)import_arg->slot_ptr,
import_arg->proto_win);
SECITEM_ZfreeItem(pfx, PR_TRUE);
if(rv != SECSuccess) {
secnav_DoPKCS12ImportErrors(import_arg->proto_win);
goto loser;
} else {
callback = (refreshCallback)import_arg->redrawCallback;
(*callback)(cbarg);
FE_Alert(import_arg->proto_win,
XP_GetString(XP_PKCS12_SUCCESSFUL_IMPORT));
cbarg = NULL;
rv = SECSuccess;
}
} else {
secnav_DoPKCS12ImportErrors(import_arg->proto_win);
rv = SECFailure;
}
} else {
rv = SECFailure;
}
if(cbarg != NULL) {
PORT_Free(cbarg);
}
PK11_FreeSlot((PK11SlotInfo *)import_arg->slot_ptr);
PORT_Free(import_arg);
return rv;
loser:
if(cbarg != NULL) {
PORT_Free(cbarg);
}
if(import_arg != NULL) {
if(import_arg->slot_ptr != NULL) {
PK11_FreeSlot((PK11SlotInfo *)import_arg->slot_ptr);
}
PORT_Free(import_arg);
}
return SECFailure;
}
static void
secnav_DoPKCS12ExportErrors(void *proto_win)
{
int error_value;
error_value = PORT_GetError();
if((error_value == SEC_ERROR_PKCS12_UNABLE_TO_EXPORT_KEY) ||
(error_value == SEC_ERROR_PKCS12_UNABLE_TO_LOCATE_OBJECT_BY_NAME) ||
(error_value == SEC_ERROR_PKCS12_UNABLE_TO_WRITE)) {
FE_Alert(proto_win, XP_GetString(error_value));
} else if(error_value == SEC_ERROR_USER_CANCELLED) {
;
} else {
FE_Alert(proto_win, XP_GetString(SEC_ERROR_EXPORTING_CERTIFICATES));
}
}
void
SECNAV_ExportPKCS12Object(CERTCertificate *cert, void *proto_win,
void *arg)
{
char *cert_nick[2];
CERTCertificate *certs[2];
PK11SlotInfo *slot;
SECItem *pfx;
SECNAVPKCS12PwdPromptArg pwd_arg;
if((cert == NULL) || (arg == NULL)) {
/* XXX set error ? */
return;
}
cert_nick[0] = (char *)arg;
cert_nick[1] = NULL;
certs[0] = cert;
certs[1] = NULL;
pwd_arg.prompt_type = pwdGenericExport;
pwd_arg.wincx = proto_win;
if(cert->slot == NULL) {
slot = PK11_GetInternalKeySlot();
} else {
slot = cert->slot;
}
if(slot && PK11_NeedLogin(slot) && !PK11_IsLoggedIn(slot)) {
SECStatus rv = PK11_DoPassword(slot, proto_win);
PK11_FreeSlot(slot);
if(rv != SECSuccess) {
PORT_SetError(SEC_ERROR_USER_CANCELLED);
goto process_errors;
}
}
pfx = SEC_PKCS12GetPFX(cert_nick, certs,
PR_TRUE, /* shroud keys */
SECNAV_GetExportPassword,
(void *)&pwd_arg,
SEC_OID_PKCS12_OFFLINE_TRANSPORT_MODE,
proto_win);
if(pfx != NULL) {
PRBool write_success;
write_success = SECNAV_WriteExportFile(proto_win, pfx);
if(write_success == PR_TRUE) {
FE_Alert(proto_win, XP_GetString(XP_PKCS12_SUCCESSFUL_EXPORT));
} else {
secnav_DoPKCS12ExportErrors(proto_win);
}
goto done;
}
process_errors:
secnav_DoPKCS12ExportErrors(proto_win);
done:
return;
}