javi%netscape.com ad78f2d7f0 Whomping my changes for imporoved cert UI and cert lookup over LDAP onto
mozilla.

Make P12 unicode conversion switch bytes to Network Byte Order.  Mozilla
localization doesn't do that for us.


git-svn-id: svn://10.0.0.236/trunk@67334 18797224-902f-48f8-a5cc-f745e15eee43
2000-04-27 00:03:33 +00:00

962 lines
29 KiB
C

/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is the Netscape security libraries.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1994-2000 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU General Public License Version 2 or later (the
* "GPL"), in which case the provisions of the GPL are applicable
* instead of those above. If you wish to allow use of your
* version of this file only under the terms of the GPL and not to
* allow others to use your version of this file under the MPL,
* indicate your decision by deleting the provisions above and
* replace them with the notice and other provisions required by
* the GPL. If you do not delete the provisions above, a recipient
* may use your version of this file under either the MPL or the
* GPL.
*/
#include "p12res.h"
#include "minihttp.h"
#include "pk11func.h"
#include "secmod.h"
#include "p12.h"
#include "p12plcy.h"
#include "secerr.h"
#include "newproto.h"
#include "messages.h"
#include "advisor.h"
#include "nlslayer.h"
#define SSMRESOURCE(object) (&object->super)
#define PKCS12_IN_BUFFER_SIZE 2048
static SSMStatus
ssmpkcs12context_createpkcs12file(SSMPKCS12Context *cx,
PRBool forceAuthenticate,
CERTCertificate **certArr,
PRIntn numCerts);
SECStatus
SSM_UnicodeConversion(SECItem *dest, SECItem *src,
PRBool toUnicode, PRBool swapBytes)
{
unsigned int allocLen;
if(!dest || !src) {
return SECFailure;
}
allocLen = ((toUnicode) ? (src->len << 2) : src->len);
if (allocLen == 0) {
/* empty string: we need to pad it by 2 bytes */
allocLen = 2;
}
dest->data = SSM_ZNEW_ARRAY(unsigned char, allocLen);
if(!SSM_UCS2_ASCIIConversion(toUnicode, src->data, src->len,
dest->data, allocLen, &dest->len,
swapBytes)) {
PR_Free(dest->data);
dest->data = NULL;
return SECFailure;
}
return SECSuccess;
}
SSMStatus
SSMPKCS12Context_Create(void *arg, SSMControlConnection *ctrl,
SSMResource **res)
{
SSMPKCS12Context *cxt = NULL;
SSMPKCS12CreateArg *createArg = (SSMPKCS12CreateArg*)arg;
SSMStatus rv;
cxt = SSM_ZNEW(SSMPKCS12Context);
if (cxt == NULL) {
return SSM_ERR_OUT_OF_MEMORY;
}
rv = SSMPKCS12Context_Init(ctrl,cxt,SSM_RESTYPE_PKCS12_CONTEXT ,
createArg->isExportContext);
if (rv != PR_SUCCESS) {
goto loser;
}
*res = SSMRESOURCE(cxt);
return PR_SUCCESS;
loser:
if (cxt != NULL) {
SSM_FreeResource(SSMRESOURCE(cxt));
}
*res = NULL;
return rv;
}
SSMStatus
SSMPKCS12Context_Init(SSMControlConnection *ctrl, SSMPKCS12Context *res,
SSMResourceType type, PRBool isExportContext)
{
res->m_isExportContext = isExportContext;
res->m_password = NULL;
res->m_inputProcessed = PR_FALSE;
res->m_file = NULL;
res->m_digestFile = NULL;
res->m_error = PR_FALSE;
return SSMResource_Init(ctrl, SSMRESOURCE(res), type);
}
SSMStatus
SSMPKCS12Context_Destroy(SSMResource *res, PRBool doFree)
{
SSMPKCS12Context *cxt = (SSMPKCS12Context*)res;
SSMResource_Destroy(res, PR_FALSE);
if (cxt->m_password != NULL) {
PR_Free(cxt->m_password);
cxt->m_password = NULL;
}
if (cxt->m_cert != NULL) {
CERT_DestroyCertificate(cxt->m_cert);
cxt->m_cert = NULL;
}
if (doFree) {
PR_Free(res);
}
return PR_SUCCESS;
}
static SSMStatus
SSMPKCS12Context_HandlePasswordRequest(SSMResource *res,
HTTPRequest *req)
{
char *password, *confirmPassword;
SSMPKCS12Context *p12Cxt = (SSMPKCS12Context*)res;
SSMStatus rv = SSM_FAILURE;
/* Let's get the password out of the dialog. */
if (res->m_buttonType != SSM_BUTTON_OK) {
goto loser;
}
rv = SSM_HTTPParamValue(req, "passwd", &password);
if (rv != SSM_SUCCESS) {
goto loser;
}
rv = SSM_HTTPParamValue(req, "confirmPasswd", &confirmPassword);
if (rv != SSM_SUCCESS) {
goto loser;
}
if (strcmp(password, confirmPassword) != 0) {
/* Should re-prompt, but for now we fail. */
rv = SSM_FAILURE;
goto loser;
}
p12Cxt->m_password = PL_strdup(password);
goto done;
loser:
p12Cxt->m_password = NULL;
done:
SSM_LockResource(res);
SSM_NotifyResource(res);
SSM_UnlockResource(res);
SSM_HTTPDefaultCommandHandler(req);
p12Cxt->m_inputProcessed = PR_TRUE;
return rv;
}
SSMStatus
SSMPKCS12Context_FormSubmitHandler(SSMResource *res, HTTPRequest *req)
{
char *formName;
SSMStatus rv=SSM_FAILURE;
rv = SSM_HTTPParamValue(req, "formName", &formName);
if (rv != SSM_SUCCESS) {
goto loser;
}
if (!strcmp(formName, "cert_backup_form")) {
rv = SSMPKCS12Context_HandlePasswordRequest(res, req);
} else if (!strcmp(formName, "set_db_password")) {
rv = SSM_SetDBPasswordHandler(req);
} else {
goto loser;
}
return rv;
loser:
SSM_HTTPDefaultCommandHandler(req);
return SSM_FAILURE;
}
static void
ssmpkcs12context_writetoexportfile(void *arg, const char *buf,
unsigned long len)
{
SSMPKCS12Context *p12Cxt = (SSMPKCS12Context*)arg;
PRInt32 bytesWritten;
if (p12Cxt == NULL) {
return;
}
if (p12Cxt->m_file == NULL) {
p12Cxt->m_error = PR_TRUE;
return;
}
bytesWritten = PR_Write(p12Cxt->m_file, buf, len);
if (bytesWritten != len) {
p12Cxt->m_error = PR_TRUE;
}
}
SSMStatus
SSMPKCS12Context_CreatePKCS12FileForMultipleCerts(SSMPKCS12Context *p12Cxt,
PRBool forceAuthenticate,
CERTCertificate **certArr,
PRIntn numCerts)
{
return ssmpkcs12context_createpkcs12file(p12Cxt, forceAuthenticate,
certArr, numCerts);
}
SSMStatus
SSMPKCS12Context_CreatePKCS12File(SSMPKCS12Context *cxt,
PRBool forceAuthenticate)
{
return ssmpkcs12context_createpkcs12file(cxt, forceAuthenticate,
&cxt->m_cert, 1);
}
static SSMStatus
ssmpkcs12context_createpkcs12file(SSMPKCS12Context *cxt,
PRBool forceAuthenticate,
CERTCertificate **certArr,
PRIntn numCerts)
{
SEC_PKCS12ExportContext *p12ecx = NULL;
SEC_PKCS12SafeInfo *keySafe = NULL, *certSafe = NULL;
SECItem pwitem = { siBuffer, NULL, 0 };
PK11SlotInfo *slot = NULL;
PK11SlotInfo *slotToUse = NULL;
SSMControlConnection *ctrl;
SSMStatus rv=SSM_FAILURE;
int i;
if (cxt == NULL || certArr == NULL || numCerts == 0) {
return SSM_ERR_BAD_REQUEST;
}
/*
* We're about to send the UI event requesting the password to use
* when encrypting
*/
SSM_LockResource(&cxt->super);
cxt->m_inputProcessed = PR_FALSE;
rv = SSMControlConnection_SendUIEvent(SSMRESOURCE(cxt)->m_connection,
"get", "cert_backup",
SSMRESOURCE(cxt),
(numCerts > 1) ? "multipleCerts=1" :
NULL,
&SSMRESOURCE(cxt)->m_clientContext);
if (rv != SSM_SUCCESS) {
SSM_UnlockResource(SSMRESOURCE(cxt));
goto loser;
}
/*
* Wait until the form is submitted to proceed. We'll get notified.
*/
SSM_WaitResource(SSMRESOURCE(cxt), PR_INTERVAL_NO_TIMEOUT);
SSM_UnlockResource(SSMRESOURCE(cxt));
if (cxt->m_password == NULL ||
cxt->super.m_buttonType == SSM_BUTTON_CANCEL) {
rv = SSM_ERR_NO_PASSWORD;
goto loser;
}
/* Wait for the dialog box to go down so that when it disappears,
* the window doesn't take away the password prompt.
*/
PR_Sleep(PR_TicksPerSecond());
ctrl = SSMRESOURCE(cxt)->m_connection;
pwitem.data = (unsigned char *) cxt->m_password;
pwitem.len = strlen(cxt->m_password);
PK11_FindObjectForCert(certArr[0], ctrl, &slot);
if (slot == NULL) {
rv = SSM_FAILURE;
goto loser;
}
slotToUse = slot;
if (forceAuthenticate && PK11_NeedLogin(slot)) {
PK11_Logout(slot);
}
if (PK11_Authenticate(slot, PR_TRUE, ctrl) != SECSuccess) {
rv = SSM_ERR_BAD_DB_PASSWORD;
goto loser;
}
p12ecx = SEC_PKCS12CreateExportContext(NULL, NULL, slot, ctrl);
if (p12ecx == NULL) {
rv = SSM_FAILURE;
goto loser;
}
if (SEC_PKCS12AddPasswordIntegrity(p12ecx, &pwitem, SEC_OID_SHA1)
!= SECSuccess) {
rv = SSM_ERR_BAD_PASSWORD;
goto loser;
}
for (i=0; i <numCerts; i++) {
PK11_FindObjectForCert(certArr[i], ctrl, &slot);
if (slot != slotToUse) {
continue;
}
keySafe = SEC_PKCS12CreateUnencryptedSafe(p12ecx);
if (!SEC_PKCS12IsEncryptionAllowed() || PK11_IsFIPS()) {
certSafe = keySafe;
} else {
certSafe = SEC_PKCS12CreatePasswordPrivSafe(p12ecx, &pwitem,
SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC);
}
if (certSafe == NULL || keySafe == NULL) {
rv = SSM_FAILURE;
goto loser;
}
if (SEC_PKCS12AddCertAndKey(p12ecx, certSafe, NULL, certArr[i],
SSMRESOURCE(cxt)->m_connection->m_certdb,
keySafe, NULL, PR_TRUE, &pwitem,
SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_3KEY_TRIPLE_DES_CBC)
!= SECSuccess) {
rv = SSM_FAILURE;
goto loser;
}
}
/* Done with the password, free it */
PR_Free(cxt->m_password);
cxt->m_password = NULL;
rv = SSM_RequestFilePathFromUser(SSMRESOURCE(cxt),
"pkcs12_export_file_prompt",
"*.p12",
PR_FALSE);
if (rv != SSM_SUCCESS || cxt->super.m_fileName == NULL) {
rv = SSM_ERR_BAD_FILENAME;
goto loser;
}
cxt->m_file = PR_Open (cxt->super.m_fileName,
PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE,
0600);
if (cxt->m_file == NULL) {
rv = SSM_ERR_BAD_FILENAME;
goto loser;
}
if (SEC_PKCS12Encode(p12ecx, ssmpkcs12context_writetoexportfile, cxt)
!= SECSuccess) {
rv = SSM_FAILURE;
goto loser;
}
PR_Close(cxt->m_file);
if (slotToUse) {
PK11_FreeSlot(slotToUse);
}
SEC_PKCS12DestroyExportContext(p12ecx);
return SSM_SUCCESS;
loser:
if (p12ecx != NULL) {
SEC_PKCS12DestroyExportContext(p12ecx);
}
if (slot && cxt->m_cert && (slot != cxt->m_cert->slot)) {
PK11_FreeSlot(slot);
}
PR_FREEIF(cxt->m_password);
cxt->m_password = NULL;
return rv;
}
/* This function converts ASCII strings to UCS2 strings in Network Byte Order.
** The "swapBytes" argument is ignored.
** The PKCS#12 code only makes it true on Little Endian systems,
** where it was intended to force the output into NBO.
*/
PRBool
SSM_UCS2_ASCIIConversion(PRBool toUnicode,
unsigned char *inBuf,
unsigned int inBufLen,
unsigned char *outBuf,
unsigned int maxOutBufLen,
unsigned int *outBufLen,
PRBool swapBytes)
{
if (!inBuf || !outBuf || !outBufLen) {
return PR_FALSE;
}
if (toUnicode) {
PRBool rv;
#ifdef DEBUG
unsigned int outLen;
int i;
fprintf(stderr,"\n---ssm_ConvertAsciiToUCS2---\nInput: inBuf= ");
for (i = 0; i < inBufLen; i++) {
fprintf(stderr, "%c", inBuf[i]);
}
fprintf(stderr,"\ninBufLen=%d\n", inBufLen);
#endif
rv = nlsASCIIToUnicode(inBuf, inBufLen,
outBuf, maxOutBufLen, outBufLen);
if (swapBytes) {
unsigned int i;
char tmp;
for (i=0; i<outBufLen; i+=2) {
tmp = outBuf[i];
outBuf[i] = outBuf[i+1];
outBuf[i+1] = tmp;
}
}
#ifdef DEBUG
outLen = *outBufLen;
fprintf(stderr,"output: outBuf= ");
for(i = 0; i < outLen; i++) {
fprintf(stderr, "%c ", outBuf[i]);
}
fprintf(stderr,"\noutBuf= ");
for(i = 0; i < outLen; i++) {
fprintf(stderr,"%2x ", outBuf[i]);
}
fprintf(stderr,"\noutLen = %d\n", outLen);
#endif /* DEBUG */
return rv;
}
PR_ASSERT(PR_FALSE); /* not supported yet */
return PR_FALSE;
}
PRBool
SSM_UCS2_UTF8Conversion(PRBool toUnicode, unsigned char *inBuf,
unsigned int inBufLen,unsigned char *outBuf,
unsigned int maxOutBufLen, unsigned int *outBufLen)
{
PRBool retval;
#ifdef DEBUG
unsigned int i;
#endif
if(!inBuf || !outBuf || !outBufLen) {
return PR_FALSE;
}
*outBufLen = 0;
#ifdef DEBUG
fprintf(stderr,"---UCS2_UTF8Conversion (%s) ---\nInput: \n",
(toUnicode?"to UCS2":"to UTF8"));
for(i=0; i< inBufLen; i++) {
fprintf(stderr,"%c", (char) inBuf[i]);
}
fprintf(stderr,"\n");
for(i=0; i< inBufLen; i++) {
fprintf(stderr,"%2x ", (char) inBuf[i]);
}
fprintf(stderr,"\n");
#endif
if(toUnicode) {
retval = nlsUTF8ToUnicode(inBuf, inBufLen, outBuf, maxOutBufLen,
outBufLen);
} else {
retval = nlsUnicodeToUTF8(inBuf, inBufLen, outBuf, maxOutBufLen,
outBufLen);
}
#ifdef DEBUG
fprintf(stderr,"Output: \n");
for(i=0; i< *outBufLen; i++) {
fprintf(stderr,"%c", (char) outBuf[i]);
}
fprintf(stderr,"\n");
for(i=0; i< *outBufLen; i++) {
fprintf(stderr,"%2x ", (char) outBuf[i]);
}
fprintf(stderr,"\n\n");
#endif
return retval;
}
static SECStatus
ssmpkcs12context_digestopen(void *arg, PRBool readData)
{
char *tmpFileName=NULL;
char filePathSep;
SSMPKCS12Context *cxt = (SSMPKCS12Context *)arg;
#if defined(XP_UNIX)
filePathSep = '/';
#elif defined(WIN32)
filePathSep = '\\';
#elif defined(XP_MAC)
filePathSep = ':';
#else
#error Tell me what the file path separator is of this platform.
#endif
tmpFileName = PR_smprintf("%s%c%s",
SSMRESOURCE(cxt)->m_connection->m_dirRoot,
filePathSep,
".nsm_p12_tmp");
if (tmpFileName == NULL) {
return SECFailure;
}
if (readData) {
cxt->m_digestFile = PR_Open(tmpFileName,
PR_RDONLY, 0400);
} else {
cxt->m_digestFile = PR_Open(tmpFileName,
PR_CREATE_FILE | PR_RDWR | PR_TRUNCATE,
0600);
}
cxt->m_tempFilePath = tmpFileName;
if (cxt->m_digestFile == NULL) {
cxt->m_error = PR_TRUE;
return SECFailure;
}
return SECSuccess;
}
static SECStatus
ssmpkcs12context_digestclose(void *arg, PRBool removeFile)
{
SSMPKCS12Context *cxt = (SSMPKCS12Context*)arg;
if (cxt == NULL || cxt->m_digestFile == NULL) {
return SECFailure;
}
PR_Close(cxt->m_digestFile);
cxt->m_digestFile = NULL;
if (removeFile) {
PR_Delete(cxt->m_tempFilePath);
PR_Free(cxt->m_tempFilePath);
cxt->m_tempFilePath = NULL;
}
return SECSuccess;
}
static int
ssmpkcs12context_digestread(void *arg, unsigned char *buf, unsigned long len)
{
SSMPKCS12Context *cxt = (SSMPKCS12Context*)arg;
if (cxt == NULL || cxt->m_digestFile == NULL) {
return -1;
}
if (buf == NULL || len == 0) {
return -1;
}
return PR_Read(cxt->m_digestFile, buf, len);
}
static int
ssmpkcs12context_digestwrite(void *arg, unsigned char *buf, unsigned long len)
{
SSMPKCS12Context *cxt = (SSMPKCS12Context *)arg;
if (cxt == NULL || cxt->m_digestFile == NULL) {
return -1;
}
if (buf == NULL || len == 0) {
return -1;
}
return PR_Write(cxt->m_digestFile, buf, len);
}
SECItem*
SSM_NicknameCollisionCallback(SECItem *old_nick, PRBool *cancel,
void *wincx)
{
/* We don't handle this yet */
*cancel = PR_TRUE;
return NULL;
}
static PK11SlotInfo*
SSMPKCS12Context_ChooseSlotForImport(SSMPKCS12Context *cxt,
PK11SlotList *slotList)
{
char mech[20];
SSMStatus rv;
PR_snprintf(mech, 20, "mech=%d&task=import&unused=unused", CKM_RSA_PKCS);
SSM_LockUIEvent(&cxt->super);
rv = SSMControlConnection_SendUIEvent(cxt->super.m_connection,
"get",
"select_token",
&cxt->super,
mech, &SSMRESOURCE(cxt)->m_clientContext);
if (rv != SSM_SUCCESS) {
SSM_UnlockResource(&cxt->super);
return NULL;
}
SSM_WaitUIEvent(&cxt->super, PR_INTERVAL_NO_TIMEOUT);
/* Wait so damn window goes away without swallowing up
* the password prompt that will come up next.
*/
PR_Sleep(PR_TicksPerSecond());
return (PK11SlotInfo*)cxt->super.m_uiData;
}
static PK11SlotInfo*
SSMPKCS12Context_GetSlotForImport(SSMPKCS12Context *cxt)
{
PK11SlotList *slotList;
PK11SlotInfo *slot = NULL;
slotList = PK11_GetAllTokens(CKM_RSA_PKCS, PR_TRUE, PR_TRUE,
cxt->super.m_connection);
if (slotList == NULL || slotList->head == NULL) {
/* Couldn't find a slot, let's try the internal slot
* and see what happens
*/
slot = PK11_GetInternalKeySlot();
} else if (slotList->head->next == NULL) {
/*
* Only one slot, return it.
*/
slot = PK11_ReferenceSlot(slotList->head->slot);
} else {
slot = SSMPKCS12Context_ChooseSlotForImport(cxt, slotList);
}
if (slotList)
PK11_FreeSlotList(slotList);
return slot;
}
SSMStatus
SSMPKCS12Context_RestoreCertFromPKCS12File(SSMPKCS12Context *cxt)
{
SSMStatus rv;
SECItem passwdReq;
char *prompt=NULL;
PK11SlotInfo *slot=NULL;
SEC_PKCS12DecoderContext *p12dcx=NULL;
PRBool swapUnicode = PR_FALSE;
unsigned char *buf=NULL;
SECItem pwItem = { siBuffer, NULL, 0 }, uniPwItem = { siBuffer, NULL, 0 };
SECStatus srv;
CERTCertList *certList=NULL;
CERTCertListNode *node=NULL;
PromptRequest request;
if (cxt == NULL || cxt->m_isExportContext) {
return SSM_FAILURE;
}
#ifdef IS_LITTLE_ENDIAN
swapUnicode = PR_TRUE;
#endif
rv = SSM_RequestFilePathFromUser(SSMRESOURCE(cxt),
"pkcs12_import_file_prompt",
"*.p12",
PR_TRUE);
if (rv != SSM_SUCCESS || cxt->super.m_fileName == NULL) {
rv = SSM_ERR_BAD_FILENAME;
goto loser;
}
prompt = SSM_GetCharFromKey("pkcs12_request_password_prompt",
"ISO-8859-1");
if (prompt == NULL) {
rv = SSM_FAILURE;
goto loser;
}
request.resID = SSMRESOURCE(cxt)->m_id;
request.prompt = prompt;
request.clientContext = SSMRESOURCE(cxt)->m_clientContext;
if (CMT_EncodeMessage(PromptRequestTemplate, (CMTItem*)&passwdReq, &request) != CMTSuccess) {
rv = SSM_FAILURE;
goto loser;
}
passwdReq.type = (SECItemType) (SSM_EVENT_MESSAGE | SSM_PROMPT_EVENT);
SSM_LockResource(SSMRESOURCE(cxt));
cxt->m_password = NULL;
rv = SSM_SendQMessage(SSMRESOURCE(cxt)->m_connection->m_controlOutQ,
20,
passwdReq.type,
passwdReq.len,
(char*)passwdReq.data,
PR_TRUE);
SSM_WaitResource(SSMRESOURCE(cxt), SSM_PASSWORD_WAIT_TIME);
SSM_UnlockResource(SSMRESOURCE(cxt));
if (cxt->m_password == NULL) {
rv = SSM_ERR_NO_PASSWORD;
goto loser;
}
cxt->m_file = PR_Open(SSMRESOURCE(cxt)->m_fileName,
PR_RDONLY, 0400);
if (cxt->m_file == NULL) {
rv = SSM_ERR_BAD_FILENAME;
goto loser;
}
slot = SSMPKCS12Context_GetSlotForImport(cxt);
if (slot == NULL) {
goto loser;
}
if (PK11_NeedLogin(slot)) {
/* we should log out only if the slot needs login */
PK11_Logout(slot);
}
/* User has not initialize DB, ask for a password. */
if (PK11_NeedUserInit(slot)) {
rv = SSM_SetUserPassword(slot, SSMRESOURCE(cxt));
if (rv != SSM_SUCCESS) {
rv = SSM_ERR_NEED_USER_INIT_DB;
goto loser;
}
}
if (PK11_Authenticate(slot, PR_FALSE, SSMRESOURCE(cxt)->m_connection)
!= SECSuccess) {
rv = SSM_ERR_BAD_DB_PASSWORD;
goto loser;
}
pwItem.data = (unsigned char *) cxt->m_password;
pwItem.len = strlen(cxt->m_password);
if (SSM_UnicodeConversion(&uniPwItem, &pwItem, PR_TRUE,
swapUnicode) != SECSuccess) {
rv = SSM_ERR_BAD_PASSWORD;
goto loser;
}
p12dcx = SEC_PKCS12DecoderStart(&uniPwItem, slot,
SSMRESOURCE(cxt)->m_connection,
ssmpkcs12context_digestopen,
ssmpkcs12context_digestclose,
ssmpkcs12context_digestread,
ssmpkcs12context_digestwrite,
cxt);
if (p12dcx == NULL) {
rv = SSM_FAILURE;
goto loser;
}
buf = SSM_NEW_ARRAY(unsigned char, PKCS12_IN_BUFFER_SIZE);
if (buf == NULL) {
rv = SSM_ERR_OUT_OF_MEMORY;
goto loser;
}
cxt->m_file = PR_Open(SSMRESOURCE(cxt)->m_fileName, PR_RDONLY, 0400);
if (cxt->m_file == NULL) {
rv = SSM_ERR_BAD_FILENAME;
goto loser;
}
while (PR_TRUE) {
int readLen = PR_Read(cxt->m_file, buf, PKCS12_IN_BUFFER_SIZE);
if (readLen < 0) {
rv = SSM_FAILURE;
goto loser;
}
srv = SEC_PKCS12DecoderUpdate(p12dcx, buf, readLen);
if (srv != SECSuccess || readLen != PKCS12_IN_BUFFER_SIZE) {
break;
}
}
if (srv != SECSuccess) {
rv = SSM_ERR_CANNOT_DECODE;
goto loser;
}
if (SEC_PKCS12DecoderVerify(p12dcx) != SECSuccess) {
rv = SSM_FAILURE;
goto loser;
}
if (SEC_PKCS12DecoderValidateBags(p12dcx, SSM_NicknameCollisionCallback)
!= SECSuccess) {
if (PORT_GetError() == SEC_ERROR_PKCS12_DUPLICATE_DATA) {
rv = SSM_PKCS12_CERT_ALREADY_EXISTS;
} else {
rv = SSM_FAILURE;
}
goto loser;
}
if (SEC_PKCS12DecoderImportBags(p12dcx) != SECSuccess) {
rv = SSM_FAILURE;
goto loser;
}
PR_Close(cxt->m_file);
cxt->m_file = NULL;
PR_Free(prompt);
PK11_FreeSlot(slot);
certList = SEC_PKCS12DecoderGetCerts(p12dcx);
if (certList != NULL) {
for (node = CERT_LIST_HEAD(certList); !CERT_LIST_END(node, certList);
node = CERT_LIST_NEXT(node)) {
if ((node->cert->trust) &&
(node->cert->trust->emailFlags & CERTDB_USER) &&
CERT_VerifyCertNow(cxt->super.m_connection->m_certdb,
node->cert, PR_TRUE, certUsageEmailSigner,
cxt) == SSM_SUCCESS) {
rv = SSM_UseAsDefaultEmailIfNoneSet(cxt->super.m_connection,
node->cert, PR_FALSE);
if (rv == SSM_SUCCESS) {
/* We just made this cert the default new cert */
rv = SSM_ERR_NEW_DEF_MAIL_CERT;
break;
}
}
}
CERT_DestroyCertList(certList);
certList = NULL;
}
SEC_PKCS12DecoderFinish(p12dcx);
return (rv == SSM_ERR_NEW_DEF_MAIL_CERT) ? rv : SSM_SUCCESS;
loser:
if (cxt->m_file != NULL) {
PR_Close(cxt->m_file);
cxt->m_file = NULL;
}
if (prompt != NULL) {
PR_Free(prompt);
}
if (slot != NULL) {
PK11_FreeSlot(slot);
}
if (p12dcx != NULL) {
SEC_PKCS12DecoderFinish(p12dcx);
}
if (buf != NULL) {
PR_Free(buf);
}
cxt->m_error = PR_TRUE;
return rv;
}
SSMStatus
SSMPKCS12Context_ProcessPromptReply(SSMResource *res,
char *reply)
{
SSMPKCS12Context *cxt = (SSMPKCS12Context*)res;
if (!SSM_IsAKindOf(res, SSM_RESTYPE_PKCS12_CONTEXT)) {
return PR_FAILURE;
}
cxt->m_password = reply?PL_strdup(reply):NULL;
SSM_LockResource(res);
SSM_NotifyResource(res);
SSM_UnlockResource(res);
return PR_SUCCESS;
}
SSMStatus
SSMPKCS12Context_Print(SSMResource *res,
char *fmt,
PRIntn numParams,
char **value,
char **resultStr)
{
char mechStr[48];
SSMStatus rv = SSM_FAILURE;
PR_ASSERT(resultStr != NULL);
if (resultStr == NULL) {
rv = SSM_FAILURE;
goto done;
}
if (numParams) {
rv = SSMResource_Print(res, fmt, numParams, value, resultStr);
goto done;
}
PR_snprintf(mechStr, 48, "%d", CKM_RSA_PKCS);
*resultStr = PR_smprintf(fmt, res->m_id, mechStr, "");
rv = (*resultStr == NULL) ? SSM_FAILURE : SSM_SUCCESS;
done:
return rv;
}
void SSMPKCS12Context_BackupMultipleCertsThread(void *arg)
{
SSMPKCS12Context *p12Cxt = (SSMPKCS12Context*)arg;
SSMStatus rv;
SSMControlConnection *connection = p12Cxt->super.m_connection;
PRIntn i;
SSM_RegisterThread("PKCS12", NULL);
SSM_DEBUG("About to backup some certs.\n");
rv = SSMControlConnection_SendUIEvent(connection,
"get", "backup_new_cert",
&p12Cxt->super, NULL,
&p12Cxt->super.m_clientContext);
PR_ASSERT(SSMRESOURCE(p12Cxt)->m_buttonType == SSM_BUTTON_NONE);
if (rv == SSM_SUCCESS) {
while (SSMRESOURCE(p12Cxt)->m_buttonType == SSM_BUTTON_NONE) {
SSM_WaitForOKCancelEvent(SSMRESOURCE(p12Cxt),
PR_INTERVAL_NO_TIMEOUT);
}
}
/*
* Eventhough we tell Nova to use a context it provided to us,
* it still tries to use the top-most window to bring up the
* next dialog. Meaning I still have to insert this freakin'
* sleep.
*
* XXX -javi
*/
PR_Sleep(PR_TicksPerSecond());
#if 0
/*
* Disable this for now, until we figure out how we're really gonna
* support putting multiple cert/key pairs in one P12 file.
*/
SSMPKCS12Context_CreatePKCS12FileForMultipleCerts(p12Cxt, PR_FALSE,
p12Cxt->arg->certs,
p12Cxt->arg->numCerts);
#endif
for (i=0; i<p12Cxt->arg->numCerts;i++) {
p12Cxt->m_cert = p12Cxt->arg->certs[i];
SSMPKCS12Context_CreatePKCS12File(p12Cxt, PR_FALSE);
/*
* ARGH!! If we do more than one, then Communicator crashes
* because it tries to use a window that no longer exists as
* the base for the next window.
*/
PR_Sleep(PR_TicksPerSecond());
}
PR_Free(p12Cxt->arg->certs);
PR_Free(p12Cxt->arg);
p12Cxt->arg = NULL;
p12Cxt->m_thread = NULL;
SSM_FreeResource(&p12Cxt->super);
SSM_DEBUG("Done backing up certs.\n");
}
SSMStatus
SSM_WarnPKCS12Incompatibility(SSMTextGenContext *cx)
{
SSMStatus rv;
char *value;
rv = SSM_HTTPParamValue(cx->m_request, "multipleCerts", &value);
PR_FREEIF(cx->m_result);
cx->m_result = NULL;
if (rv == SSM_SUCCESS) {
rv = SSM_FindUTF8StringInBundles(cx, "pkcs12_incompatible_warn",
&cx->m_result);
if (rv != SSM_SUCCESS) {
cx->m_result = PL_strdup("");
}
} else {
cx->m_result = PL_strdup("");
}
return SSM_SUCCESS;
}