зеркало из https://github.com/mozilla/pjs.git
1557 строки
41 KiB
C
1557 строки
41 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 "sslconn.h"
|
|
#include "ssldlgs.h"
|
|
#include "ctrlconn.h"
|
|
#include "textgen.h"
|
|
#include "resource.h"
|
|
#include "certres.h"
|
|
#include "minihttp.h"
|
|
|
|
#include "cert.h"
|
|
#include "certt.h"
|
|
#include "certdb.h"
|
|
#include "sechash.h"
|
|
#include "nlsutil.h"
|
|
#include "nlslayer.h"
|
|
|
|
|
|
#define SSMRESOURCE(sslconn) (&(sslconn)->super.super.super)
|
|
#define SSMCONTROLCONNECTION(sslconn) ((SSMControlConnection*)(sslconn->super.super.m_parent))
|
|
|
|
|
|
/* private functions */
|
|
SSMStatus ssm_client_auth_get_utf8_cert_list(SSMSSLDataConnection* conn,
|
|
char* fmt,
|
|
char** result);
|
|
SSMStatus ssm_http_client_auth_handle_ok_button(HTTPRequest* req);
|
|
SSMStatus ssm_http_client_auth_handle_cancel_button(HTTPRequest* req);
|
|
SECStatus SSM_SSLServerCertResetTrust(CERTCertificate* cert,
|
|
SSMBadServerCertAccept accept);
|
|
static char* ssm_default_server_nickname(CERTCertificate* cert);
|
|
SSMStatus ssm_http_server_auth_handle_ok_button(HTTPRequest* req);
|
|
SSMStatus ssm_http_server_auth_handle_cancel_button(HTTPRequest* req);
|
|
SSMStatus ssm_http_unknown_issuer_step1_handle_next_button(HTTPRequest* req);
|
|
|
|
|
|
SSMStatus SSM_SSLMakeBadClientAuthDialog(SSMSSLDataConnection* conn)
|
|
{
|
|
SSMStatus rv;
|
|
|
|
SSM_DEBUG("Client authentication failed.\n");
|
|
|
|
PR_ASSERT(conn != NULL);
|
|
|
|
SSM_LockResource(SSMRESOURCE(conn));
|
|
conn->m_UIInfo.UICompleted = PR_FALSE;
|
|
|
|
/* fire up the UI */
|
|
rv = SSMControlConnection_SendUIEvent(SSMCONTROLCONNECTION(conn), "get",
|
|
"bad_client_auth",
|
|
SSMRESOURCE(conn), NULL,&SSMRESOURCE(conn)->m_clientContext);
|
|
if (rv != SSM_SUCCESS) {
|
|
goto loser;
|
|
}
|
|
|
|
/* wait until the UI event is complete */
|
|
while (!conn->m_UIInfo.UICompleted) {
|
|
SSM_WaitResource(SSMRESOURCE(conn), PR_INTERVAL_NO_TIMEOUT);
|
|
}
|
|
|
|
loser:
|
|
conn->m_UIInfo.UICompleted = PR_FALSE;
|
|
SSM_UnlockResource(SSMRESOURCE(conn));
|
|
|
|
return rv;
|
|
}
|
|
|
|
SSMStatus SSM_FormatCert(CERTCertificate* cert, char* fmt,
|
|
char** result)
|
|
{
|
|
char* hostname = NULL;
|
|
char* orgname = NULL;
|
|
char* issuer = NULL;
|
|
char * notBefore = NULL, * notAfter = NULL;
|
|
SSMStatus rv = SSM_SUCCESS;
|
|
|
|
|
|
/* check arguments */
|
|
if (cert == NULL || fmt == NULL || result == NULL) {
|
|
PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
|
|
goto loser;
|
|
}
|
|
|
|
/* retrieve cert information */
|
|
hostname = CERT_GetCommonName(&cert->subject);
|
|
if (hostname == NULL) {
|
|
goto loser;
|
|
}
|
|
|
|
orgname = CERT_GetOrgName(&cert->subject);
|
|
if (orgname == NULL) {
|
|
goto loser;
|
|
}
|
|
|
|
issuer = CERT_GetOrgName(&cert->issuer);
|
|
if (issuer == NULL)
|
|
{
|
|
goto loser;
|
|
}
|
|
/* ### sjlee: should issuer be a localizable string? */
|
|
|
|
notBefore = DER_UTCDayToAscii(&cert->validity.notBefore);
|
|
if (!notBefore)
|
|
notBefore = PL_strdup("No date found");
|
|
notAfter = DER_UTCDayToAscii(&cert->validity.notAfter);
|
|
if (!notAfter)
|
|
notAfter = PL_strdup("No date found");
|
|
|
|
*result = PR_smprintf(fmt, hostname, notBefore, notAfter, issuer, orgname);
|
|
SSM_DebugUTF8String("wrapped cert", *result);
|
|
goto done;
|
|
loser:
|
|
SSM_DEBUG("Formatting cert failed.\n");
|
|
if (rv == SSM_SUCCESS) {
|
|
rv = SSM_FAILURE;
|
|
}
|
|
done:
|
|
if (hostname != NULL) {
|
|
PR_Free(hostname);
|
|
}
|
|
if (orgname != NULL) {
|
|
PR_Free(orgname);
|
|
}
|
|
if (issuer != NULL) {
|
|
PR_Free(issuer);
|
|
}
|
|
|
|
PR_FREEIF(notBefore);
|
|
PR_FREEIF(notAfter);
|
|
return rv;
|
|
}
|
|
|
|
|
|
SSMStatus SSM_ServerCertKeywordHandler(SSMTextGenContext* cx)
|
|
{
|
|
SSMResource* target = NULL;
|
|
SSMSSLDataConnection* sslconn = NULL;
|
|
SSMStatus rv;
|
|
char* pattern = NULL;
|
|
char* key = NULL;
|
|
char* formatKey = NULL;
|
|
char* simpleStr = NULL;
|
|
char* prettyStr = NULL;
|
|
CERTCertificate* serverCert = NULL;
|
|
const PRIntn CERT_FORMAT = (PRIntn)0;
|
|
const PRIntn CERT_WRAPPER = (PRIntn)1;
|
|
const PRIntn CERT_WRAPPER_NO_COMMENT = (PRIntn)2;
|
|
PRIntn wrapper;
|
|
|
|
/* check arguments */
|
|
/* ### sjlee: might as well make this a helper function because most
|
|
* keyword handlers will use this checking
|
|
*/
|
|
PR_ASSERT(cx != NULL);
|
|
PR_ASSERT(cx->m_request != NULL);
|
|
PR_ASSERT(cx->m_params != NULL);
|
|
PR_ASSERT(cx->m_result != NULL);
|
|
if (cx == NULL || cx->m_request == NULL || cx->m_params == NULL ||
|
|
cx->m_result == NULL) {
|
|
PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
|
|
goto loser;
|
|
}
|
|
|
|
simpleStr = "simple";
|
|
prettyStr = "pretty";
|
|
|
|
/* retrieve the server cert */
|
|
target = SSMTextGen_GetTargetObject(cx);
|
|
PR_ASSERT(SSM_IsA(target, SSM_RESTYPE_SSL_DATA_CONNECTION));
|
|
sslconn = (SSMSSLDataConnection*)target;
|
|
|
|
serverCert = SSL_PeerCertificate(sslconn->socketSSL);
|
|
if (serverCert == NULL) {
|
|
/* couldn't get the server cert: what do I do? */
|
|
goto loser;
|
|
}
|
|
|
|
/* form the Format object */
|
|
/* first, find the keys */
|
|
formatKey = (char *) SSM_At(cx->m_params, CERT_FORMAT);
|
|
|
|
/* only show comments field if there are comments */
|
|
if (CERT_GetCertCommentString(serverCert))
|
|
wrapper = CERT_WRAPPER;
|
|
else
|
|
wrapper = CERT_WRAPPER_NO_COMMENT;
|
|
key = (char *) SSM_At(cx->m_params, wrapper);
|
|
|
|
/* second, grab and expand the keyword objects */
|
|
rv = SSM_GetAndExpandTextKeyedByString(cx, key, &pattern);
|
|
if (rv != SSM_SUCCESS) {
|
|
goto loser;
|
|
}
|
|
SSM_DebugUTF8String("server cert info pattern", pattern);
|
|
|
|
/* Now let's figure out if we're doing pretty print or simple print. */
|
|
PR_FREEIF(cx->m_result);
|
|
if (formatKey[0] == 's') {
|
|
rv = SSM_FormatCert(serverCert, pattern, &cx->m_result);
|
|
} else if (formatKey[0] == 'p') {
|
|
rv = SSM_PrettyFormatCert(serverCert, pattern, &cx->m_result);
|
|
} else {
|
|
SSM_DEBUG("cannot understand the format key.\n");
|
|
rv = SSM_FAILURE;
|
|
}
|
|
if (rv != SSM_SUCCESS) {
|
|
goto loser;
|
|
}
|
|
goto done;
|
|
loser:
|
|
if (rv == SSM_SUCCESS) {
|
|
rv = SSM_FAILURE;
|
|
}
|
|
done:
|
|
if (serverCert != NULL) {
|
|
CERT_DestroyCertificate(serverCert);
|
|
}
|
|
PR_FREEIF(pattern);
|
|
return rv;
|
|
}
|
|
|
|
SSMStatus SSM_HTTPBadClientAuthButtonHandler(HTTPRequest* req)
|
|
{
|
|
SSMStatus rv;
|
|
SSMSSLDataConnection* conn;
|
|
char* tmpStr = NULL;
|
|
|
|
PR_ASSERT(req->target != NULL);
|
|
conn = (SSMSSLDataConnection*)(req->target);
|
|
|
|
/* make sure you got the right baseRef */
|
|
rv = SSM_HTTPParamValue(req, "baseRef", &tmpStr);
|
|
if (rv != SSM_SUCCESS ||
|
|
PL_strcmp(tmpStr, "windowclose_doclose_js") != 0) {
|
|
goto loser;
|
|
}
|
|
|
|
return ssm_http_client_auth_handle_cancel_button(req);
|
|
loser:
|
|
/* set the predicate to true and unblock the SSL thread */
|
|
SSM_LockResource(req->target);
|
|
conn->m_UIInfo.UICompleted = PR_TRUE;
|
|
conn->m_UIInfo.chosen = -1;
|
|
SSM_NotifyResource(req->target);
|
|
SSM_UnlockResource(req->target);
|
|
|
|
if (rv == SSM_SUCCESS) {
|
|
rv = SSM_FAILURE;
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
|
|
SSMStatus SSM_SSLMakeClientAuthDialog(SSMSSLDataConnection* conn)
|
|
{
|
|
SSMStatus rv;
|
|
|
|
/* enter the SSL monitor */
|
|
SSM_LockResource(SSMRESOURCE(conn));
|
|
conn->m_UIInfo.UICompleted = PR_FALSE;
|
|
|
|
/* fire up the UI */
|
|
rv = SSMControlConnection_SendUIEvent(SSMCONTROLCONNECTION(conn), "get",
|
|
"client_auth", SSMRESOURCE(conn),
|
|
NULL, &SSMRESOURCE(conn)->m_clientContext);
|
|
if (rv != SSM_SUCCESS) {
|
|
goto loser;
|
|
}
|
|
|
|
while (!conn->m_UIInfo.UICompleted) {
|
|
SSM_WaitResource(SSMRESOURCE(conn), PR_INTERVAL_NO_TIMEOUT);
|
|
}
|
|
|
|
if (conn->m_UIInfo.chosen < 0) {
|
|
/* "cancel" button clicked: bring up the no cert dialog? */
|
|
if (rv == SSM_SUCCESS) {
|
|
rv = SSM_ERR_USER_CANCEL;
|
|
}
|
|
}
|
|
/* the user selected a nickname; let's exit in orderly fashion */
|
|
|
|
loser:
|
|
/* reset this bit to facilitate future UI events */
|
|
conn->m_UIInfo.UICompleted = PR_FALSE;
|
|
SSM_UnlockResource(SSMRESOURCE(conn));
|
|
|
|
return rv;
|
|
}
|
|
|
|
|
|
SSMStatus SSM_ClientAuthCertListKeywordHandler(SSMTextGenContext* cx)
|
|
{
|
|
SSMStatus rv = SSM_SUCCESS;
|
|
SSMResource* target = NULL;
|
|
SSMSSLDataConnection* conn;
|
|
char* prefix = NULL;
|
|
char* wrapper = NULL;
|
|
char* suffix = NULL;
|
|
char* tmpStr = NULL;
|
|
char* prefixExp = NULL;
|
|
char* fmt = NULL;
|
|
char* certList = NULL;
|
|
const PRIntn CERT_LIST_PREFIX = (PRIntn)0;
|
|
const PRIntn CERT_LIST_WRAPPER = (PRIntn)1;
|
|
const PRIntn CERT_LIST_SUFFIX = (PRIntn)2;
|
|
const PRIntn CERT_LIST_PARAM_COUNT = (PRIntn)3;
|
|
|
|
/*
|
|
* make sure cx, cx->m_request, cx->m_params, cx->m_result are not
|
|
* NULL
|
|
*/
|
|
PR_ASSERT(cx != NULL && cx->m_request != NULL && cx->m_params != NULL &&
|
|
cx->m_result != NULL);
|
|
if (cx == NULL || cx->m_request == NULL || cx->m_params == NULL ||
|
|
cx->m_result == NULL) {
|
|
PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
|
|
goto loser;
|
|
}
|
|
|
|
if (SSM_Count(cx->m_params) != CERT_LIST_PARAM_COUNT) {
|
|
SSM_HTTPReportSpecificError(cx->m_request, "_client_auth_certList: ",
|
|
"Incorrect number of parameters "
|
|
" (%d supplied, %d needed).\n",
|
|
SSM_Count(cx->m_params),
|
|
CERT_LIST_PARAM_COUNT);
|
|
goto loser;
|
|
}
|
|
|
|
/* get the connection object */
|
|
target = SSMTextGen_GetTargetObject(cx);
|
|
PR_ASSERT(target != NULL);
|
|
conn = (SSMSSLDataConnection*)target;
|
|
|
|
/* form the MessageFormat object */
|
|
/* find arguments (prefix, wrapper, and suffix) */
|
|
prefix = (char *) SSM_At(cx->m_params, CERT_LIST_PREFIX);
|
|
wrapper = (char *) SSM_At(cx->m_params, CERT_LIST_WRAPPER);
|
|
suffix = (char *) SSM_At(cx->m_params, CERT_LIST_SUFFIX);
|
|
PR_ASSERT(prefix != NULL && wrapper != NULL && suffix != NULL);
|
|
|
|
/* grab the prefix and expand it */
|
|
rv = SSM_GetAndExpandTextKeyedByString(cx, prefix, &prefixExp);
|
|
if (rv != SSM_SUCCESS) {
|
|
goto loser;
|
|
}
|
|
|
|
SSM_DebugUTF8String("client cert list prefix", prefixExp);
|
|
|
|
/* grab the wrapper */
|
|
rv = SSM_GetAndExpandTextKeyedByString(cx, wrapper, &fmt);
|
|
if (rv != SSM_SUCCESS) {
|
|
goto loser;
|
|
}
|
|
SSM_DebugUTF8String("client cert list wrapper", fmt);
|
|
|
|
/* form the wrapped cert list UnicodeString */
|
|
rv = ssm_client_auth_get_utf8_cert_list(conn, fmt, &certList);
|
|
if (rv != SSM_SUCCESS) {
|
|
goto loser;
|
|
}
|
|
|
|
/* grab the suffix and expand it */
|
|
rv = SSM_GetAndExpandTextKeyedByString(cx, suffix, &tmpStr);
|
|
if (rv != SSM_SUCCESS) {
|
|
goto loser;
|
|
}
|
|
SSM_DebugUTF8String("client cert list suffix", tmpStr);
|
|
|
|
cx->m_result = PR_smprintf("%s%s%s", prefixExp, certList, tmpStr);
|
|
goto done;
|
|
loser:
|
|
if (rv == SSM_SUCCESS) {
|
|
rv = SSM_FAILURE;
|
|
}
|
|
done:
|
|
PR_FREEIF(tmpStr);
|
|
PR_FREEIF(prefixExp);
|
|
PR_FREEIF(fmt);
|
|
PR_FREEIF(certList);
|
|
return rv;
|
|
}
|
|
|
|
|
|
/*
|
|
* Function: SSMStatus ssm_client_auth_get_utf8_cert_list()
|
|
* Purpose: forms the cert list UnicodeString
|
|
*
|
|
* Arguments and return values:
|
|
* - conn: SSL connection object
|
|
* - fmt: cert wrapper UnicodeString format
|
|
* - result: resulting UnicodeString
|
|
*
|
|
* Note: if we include the expired certs, we need to append to the end as
|
|
* well
|
|
*/
|
|
SSMStatus ssm_client_auth_get_utf8_cert_list(SSMSSLDataConnection* conn,
|
|
char* fmt,
|
|
char** result)
|
|
{
|
|
SSMStatus rv = SSM_SUCCESS;
|
|
char* tmpStr = NULL;
|
|
char* finalStr = NULL;
|
|
int i;
|
|
|
|
PR_ASSERT(conn != NULL && fmt != NULL && result != NULL);
|
|
/* in case we fail */
|
|
*result = NULL;
|
|
finalStr = PL_strdup("");
|
|
/* concatenate the string using the nicknames */
|
|
for (i = 0; i < conn->m_UIInfo.numFilteredCerts; i++) {
|
|
tmpStr = PR_smprintf(fmt, i, conn->m_UIInfo.certNicknames[i]);
|
|
if (SSM_ConcatenateUTF8String(&finalStr, tmpStr) != SSM_SUCCESS) {
|
|
goto loser;
|
|
}
|
|
PR_Free(tmpStr);
|
|
tmpStr = NULL;
|
|
}
|
|
|
|
SSM_DebugUTF8String("client auth: final cert list", finalStr);
|
|
*result = finalStr;
|
|
return SSM_SUCCESS;
|
|
loser:
|
|
SSM_DEBUG("client auth: wrapping cert list failed.\n");
|
|
if (rv == SSM_SUCCESS) {
|
|
rv = SSM_FAILURE;
|
|
}
|
|
PR_FREEIF(finalStr);
|
|
PR_FREEIF(tmpStr);
|
|
return rv;
|
|
}
|
|
|
|
|
|
SSMStatus SSM_ClientAuthCertSelectionButtonHandler(HTTPRequest* req)
|
|
{
|
|
SSMStatus rv;
|
|
SSMResource* res;
|
|
SSMSSLDataConnection* conn;
|
|
|
|
res = (SSMResource*)(req->target);
|
|
conn = (SSMSSLDataConnection*)(req->target);
|
|
|
|
|
|
if (res->m_buttonType == SSM_BUTTON_OK) {
|
|
/* "OK" button was clicked */
|
|
rv = ssm_http_client_auth_handle_ok_button(req);
|
|
goto done;
|
|
}
|
|
else if (res->m_buttonType == SSM_BUTTON_CANCEL) {
|
|
/* "Cancel" button was clicked */
|
|
rv = ssm_http_client_auth_handle_cancel_button(req);
|
|
goto done;
|
|
}
|
|
else {
|
|
SSM_DEBUG("Don't know which button was clicked.\n");
|
|
rv = SSM_FAILURE;
|
|
}
|
|
SSM_LockResource(res);
|
|
conn->m_UIInfo.chosen = -1;
|
|
conn->m_UIInfo.UICompleted = PR_TRUE;
|
|
SSM_NotifyResource(res);
|
|
SSM_UnlockResource(res);
|
|
done:
|
|
/* this actually includes the error cases returned from individual
|
|
* button handlers
|
|
*/
|
|
return rv;
|
|
}
|
|
|
|
|
|
SSMStatus SSM_HTTPClientAuthButtonHandler(HTTPRequest* req)
|
|
{
|
|
SSMStatus rv;
|
|
SSMSSLDataConnection* conn;
|
|
char* tmpStr = NULL;
|
|
|
|
PR_ASSERT(req->target != NULL);
|
|
conn = (SSMSSLDataConnection*)(req->target);
|
|
|
|
/* make sure you got the right baseRef */
|
|
rv = SSM_HTTPParamValue(req, "baseRef", &tmpStr);
|
|
if (rv != SSM_SUCCESS ||
|
|
PL_strcmp(tmpStr, "windowclose_doclose_js") != 0) {
|
|
goto loser;
|
|
}
|
|
|
|
/* ### sjlee: this is a very ugly way to differentiate among different
|
|
* buttons
|
|
*/
|
|
rv = SSM_HTTPParamValue(req, "do_ok", &tmpStr);
|
|
if (rv == SSM_SUCCESS) {
|
|
/* "OK" button was clicked */
|
|
rv = ssm_http_client_auth_handle_ok_button(req);
|
|
goto done;
|
|
}
|
|
|
|
rv = SSM_HTTPParamValue(req, "do_cancel", &tmpStr);
|
|
if (rv == SSM_SUCCESS) {
|
|
/* "Cancel" button was clicked */
|
|
rv = ssm_http_client_auth_handle_cancel_button(req);
|
|
goto done;
|
|
}
|
|
|
|
rv = SSM_HTTPParamValue(req, "do_help", &tmpStr);
|
|
if (rv == SSM_SUCCESS) {
|
|
/* "help" button was clicked */
|
|
/* just use cancel handler for now */
|
|
rv = ssm_http_client_auth_handle_cancel_button(req);
|
|
goto done;
|
|
}
|
|
loser:
|
|
if (rv == SSM_SUCCESS) {
|
|
SSM_DEBUG("Don't know which button was clicked.\n");
|
|
rv = SSM_FAILURE;
|
|
}
|
|
SSM_LockResource(req->target);
|
|
conn->m_UIInfo.chosen = -1;
|
|
conn->m_UIInfo.UICompleted = PR_TRUE;
|
|
SSM_NotifyResource(req->target);
|
|
SSM_UnlockResource(req->target);
|
|
done:
|
|
/* this actually includes the error cases returned from individual
|
|
* button handlers
|
|
*/
|
|
return rv;
|
|
}
|
|
|
|
|
|
SSMStatus ssm_http_client_auth_handle_ok_button(HTTPRequest* req)
|
|
{
|
|
SSMStatus rv;
|
|
SSMSSLDataConnection* conn;
|
|
char* chosen = NULL;
|
|
int i;
|
|
|
|
conn = (SSMSSLDataConnection*)(req->target);
|
|
|
|
SSM_LockResource(req->target);
|
|
/* retrieve the chosen nickname */
|
|
rv = SSM_HTTPParamValue(req, "chosen", &chosen);
|
|
if (rv != SSM_SUCCESS) {
|
|
goto loser;
|
|
}
|
|
PR_ASSERT(chosen != NULL);
|
|
PR_sscanf(chosen, "%ld", &i);
|
|
PR_ASSERT(i >= 0 && i < conn->m_UIInfo.numFilteredCerts);
|
|
|
|
rv = SSM_HTTPCloseAndSleep(req);
|
|
if (rv != SSM_SUCCESS) {
|
|
goto loser;
|
|
}
|
|
|
|
/* set the predicate to true and unblock the SSL thread */
|
|
conn->m_UIInfo.chosen = i;
|
|
conn->m_UIInfo.UICompleted = PR_TRUE;
|
|
SSM_NotifyResource(req->target);
|
|
SSM_UnlockResource(req->target);
|
|
|
|
return rv; /* SSM_SUCCESS */
|
|
loser:
|
|
/* still we want to unblock the SSL thread: it will see NULL nickname */
|
|
conn->m_UIInfo.chosen = -1;
|
|
conn->m_UIInfo.UICompleted = PR_TRUE;
|
|
SSM_NotifyResource(req->target);
|
|
SSM_UnlockResource(req->target);
|
|
|
|
return rv;
|
|
}
|
|
|
|
SSMStatus ssm_http_client_auth_handle_cancel_button(HTTPRequest* req)
|
|
{
|
|
SSMStatus rv;
|
|
SSMSSLDataConnection* conn;
|
|
|
|
conn = (SSMSSLDataConnection*)(req->target);
|
|
|
|
SSM_LockResource(req->target);
|
|
/* close the window */
|
|
rv = SSM_HTTPCloseAndSleep(req);
|
|
if (rv != SSM_SUCCESS) {
|
|
goto loser;
|
|
}
|
|
|
|
loser:
|
|
/* set the predicate to true and unblock the SSL thread */
|
|
conn->m_UIInfo.chosen = -1;
|
|
conn->m_UIInfo.UICompleted = PR_TRUE;
|
|
SSM_NotifyResource(req->target);
|
|
SSM_UnlockResource(req->target);
|
|
|
|
return rv;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Function: SECStatus SSM_SSLMakeCertExpiredDialog()
|
|
* Purpose: dispatch the UI event to create the server cert expired
|
|
* dialog
|
|
* Arguments and return values
|
|
* - cert: server cert we are dealing with
|
|
* - conn: SSL connection object
|
|
* - returns: SECSuccess if successful *and* the user decides to trust
|
|
* the cert; appropriate error code otherwise
|
|
*/
|
|
SECStatus SSM_SSLMakeCertExpiredDialog(CERTCertificate* cert,
|
|
SSMSSLDataConnection* conn)
|
|
{
|
|
SECStatus rv;
|
|
int64 now;
|
|
int64 notBefore;
|
|
int64 notAfter;
|
|
char* baseRef = NULL;
|
|
|
|
/* compare the time and determine which dialog box to bring up */
|
|
now = PR_Now();
|
|
rv = CERT_GetCertTimes(cert, ¬Before, ¬After);
|
|
if (rv != SECSuccess) {
|
|
return rv;
|
|
}
|
|
|
|
if (LL_CMP(now, >, notAfter)) {
|
|
baseRef = PL_strdup("bad_server_cert_expired");
|
|
}
|
|
else {
|
|
baseRef = PL_strdup("bad_server_cert_not_yet_valid");
|
|
}
|
|
if (baseRef == NULL) {
|
|
return SECFailure;
|
|
}
|
|
|
|
SSM_LockResource(SSMRESOURCE(conn));
|
|
conn->m_UIInfo.UICompleted = PR_FALSE;
|
|
conn->m_UIInfo.trustBadServerCert = BSCA_NO;
|
|
|
|
/* fire up the UI */
|
|
if (SSMControlConnection_SendUIEvent(SSMCONTROLCONNECTION(conn), "get",
|
|
baseRef, SSMRESOURCE(conn), NULL,&SSMRESOURCE(conn)->m_clientContext) != SSM_SUCCESS) {
|
|
rv = SECFailure;
|
|
goto loser;
|
|
}
|
|
|
|
/* wait until the UI event is complete */
|
|
while (!conn->m_UIInfo.UICompleted) {
|
|
SSM_WaitResource(SSMRESOURCE(conn), PR_INTERVAL_NO_TIMEOUT);
|
|
}
|
|
|
|
if (conn->m_UIInfo.trustBadServerCert == BSCA_NO) {
|
|
/* user did not want to continue. Cancel here. */
|
|
if (rv == SECSuccess) {
|
|
rv = SECFailure;
|
|
}
|
|
goto loser;
|
|
}
|
|
|
|
/* ### sjlee: ugliness warning! accessing cert fields directly */
|
|
cert->timeOK = PR_TRUE;
|
|
|
|
#if 0 /* Setting timeOK suffices. Setting trust flags is unnecessary. */
|
|
rv = SSM_SSLServerCertResetTrust(cert, conn->m_UIInfo.trustBadServerCert);
|
|
if (rv != SECSuccess) {
|
|
goto loser;
|
|
}
|
|
#endif
|
|
|
|
loser:
|
|
conn->m_UIInfo.trustBadServerCert = BSCA_NO;
|
|
conn->m_UIInfo.UICompleted = PR_FALSE;
|
|
SSM_UnlockResource(SSMRESOURCE(conn));
|
|
|
|
if (baseRef != NULL) {
|
|
PR_Free(baseRef);
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
|
|
SECStatus SSM_SSLServerCertResetTrust(CERTCertificate* cert,
|
|
SSMBadServerCertAccept accept)
|
|
{
|
|
char* nickname = NULL;
|
|
CERTCertTrust trust;
|
|
CERTCertTrust* trustPtr = NULL;
|
|
SECStatus rv;
|
|
|
|
if (accept == BSCA_NO) {
|
|
/* this usually shouldn't happen */
|
|
goto loser;
|
|
}
|
|
else if (accept == BSCA_PERMANENT) {
|
|
memset((void*)&trust, 0, sizeof(trust));
|
|
/*trust.sslFlags = CERTDB_VALID_PEER | CERTDB_TRUSTED;*/
|
|
/* this resets the SSL flags CERTDB_VALID_PEER | CERTDB_TRUSTED */
|
|
rv = CERT_DecodeTrustString(&trust, "P");
|
|
if (rv != SECSuccess) {
|
|
goto loser;
|
|
}
|
|
|
|
/* XXX not neccessary? */
|
|
/*
|
|
if (mystate->postwarn) {
|
|
trust.sslFlags |= CERTDB_SEND_WARN;
|
|
rv = CERT_DecodeTrustString(&trust, "w");
|
|
}
|
|
*/
|
|
|
|
nickname = ssm_default_server_nickname(cert);
|
|
if (nickname == NULL) {
|
|
goto loser;
|
|
}
|
|
|
|
/* add a temporary certificate to the permanent database */
|
|
rv = CERT_AddTempCertToPerm(cert, nickname, &trust);
|
|
if (nickname != NULL) {
|
|
PR_Free(nickname);
|
|
}
|
|
if (rv != SECSuccess) {
|
|
goto loser;
|
|
}
|
|
|
|
} else if (accept == BSCA_SESSION) {
|
|
/* bump the ref count on the cert so that it stays around for
|
|
* the entire session
|
|
*/
|
|
/* ### sjlee: ugliness warning: accessing fields directly */
|
|
cert->keepSession = PR_TRUE;
|
|
|
|
if (cert->trust != NULL) {
|
|
trustPtr = cert->trust;
|
|
}
|
|
else {
|
|
trustPtr = (CERTCertTrust*)PORT_ArenaZAlloc(cert->arena,
|
|
sizeof(CERTCertTrust));
|
|
cert->trust = trustPtr;
|
|
}
|
|
if (trustPtr == NULL) {
|
|
goto loser;
|
|
}
|
|
|
|
/* set the trust value */
|
|
/*trustptr->sslFlags |= (CERTDB_VALID_PEER | CERTDB_TRUSTED);*/
|
|
/* reset the trust SSL bit to CERTDB_VALID_PEER | CERTDB_TRUSTED */
|
|
rv = CERT_DecodeTrustString(trustPtr, "P");
|
|
if (rv != SECSuccess) {
|
|
goto loser;
|
|
}
|
|
/*
|
|
if (mystate->postwarn) {
|
|
trustptr->sslFlags |= CERTDB_SEND_WARN;
|
|
rv = CERT_DecodeTrustString(trustptr, "w");
|
|
}
|
|
*/
|
|
}
|
|
|
|
return rv;
|
|
loser:
|
|
if (rv == SECSuccess) {
|
|
rv = SECFailure;
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
|
|
/* This is also called in oldfunc.c when creating default nickname for
|
|
* a new certificate.
|
|
*/
|
|
void _ssm_compress_spaces(char* psrc)
|
|
{
|
|
char* pdst;
|
|
char c;
|
|
PRBool lastspace = PR_FALSE;
|
|
|
|
if (psrc == NULL) {
|
|
return;
|
|
}
|
|
|
|
pdst = psrc;
|
|
|
|
while (((c = *psrc) != 0) && isspace(c)) {
|
|
psrc++;
|
|
/* swallow leading spaces */
|
|
}
|
|
|
|
|
|
while ((c = *psrc++) != 0) {
|
|
if (isspace(c)) {
|
|
if (!lastspace) {
|
|
*pdst++ = ' ';
|
|
}
|
|
lastspace = PR_TRUE;
|
|
} else {
|
|
*pdst++ = c;
|
|
lastspace = PR_FALSE;
|
|
}
|
|
}
|
|
*pdst = '\0';
|
|
|
|
/* if the last character is a space, then remove it too */
|
|
pdst--;
|
|
c = *pdst;
|
|
if (isspace(c)) {
|
|
*pdst = '\0';
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* Function: char* ssm_default_server_nickname()
|
|
* Purpose: creates a default nickname for a bad server cert in case the
|
|
* user wanted to continue
|
|
* Arguments and return values
|
|
* - cert: cert in question
|
|
* - returns: the composed nickname; NULL if failure
|
|
*
|
|
* Note: shamelessly copied from the Nova code
|
|
*/
|
|
static char* ssm_default_server_nickname(CERTCertificate* cert)
|
|
{
|
|
char* nickname = NULL;
|
|
int count;
|
|
PRBool conflict;
|
|
char* servername = NULL;
|
|
|
|
servername = CERT_GetCommonName(&cert->subject);
|
|
if (servername == NULL) {
|
|
goto loser;
|
|
}
|
|
|
|
count = 1;
|
|
while (1) {
|
|
if (count == 1) {
|
|
nickname = PR_smprintf("%s", servername);
|
|
}
|
|
else {
|
|
nickname = PR_smprintf("%s #%d", servername, count);
|
|
}
|
|
if (nickname == NULL) {
|
|
goto loser;
|
|
}
|
|
|
|
_ssm_compress_spaces(nickname);
|
|
|
|
conflict = SEC_CertNicknameConflict(nickname, &cert->derSubject,
|
|
cert->dbhandle);
|
|
if (conflict == PR_SUCCESS) {
|
|
goto done;
|
|
}
|
|
|
|
/* free the nickname */
|
|
PORT_Free(nickname);
|
|
|
|
count++;
|
|
}
|
|
|
|
loser:
|
|
if (nickname != NULL) {
|
|
PR_Free(nickname);
|
|
nickname = NULL;
|
|
}
|
|
done:
|
|
if (servername != NULL) {
|
|
PR_Free(servername);
|
|
}
|
|
|
|
return nickname;
|
|
}
|
|
|
|
|
|
SSMStatus SSM_CurrentTimeKeywordHandler(SSMTextGenContext* cx)
|
|
{
|
|
SSMStatus rv;
|
|
char* wrapper = NULL;
|
|
char* key = NULL;
|
|
void* dfmt = NULL;
|
|
char* utf8Now = NULL;
|
|
int64 now;
|
|
const PRIntn TIME_WRAPPER = (PRIntn)0;
|
|
/* we have one keyword */
|
|
|
|
/* check arguments */
|
|
/* ### sjlee: might as well make this a helper function because most
|
|
* keyword handlers will use this checking
|
|
*/
|
|
PR_ASSERT(cx != NULL);
|
|
/* PR_ASSERT(cx->m_request != NULL);*/
|
|
PR_ASSERT(cx->m_params != NULL);
|
|
PR_ASSERT(cx->m_result != NULL);
|
|
if (cx == NULL || cx->m_params == NULL || cx->m_result == NULL) {
|
|
PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
|
|
goto loser;
|
|
}
|
|
|
|
/* form the DateFormat object */
|
|
dfmt = nlsNewDateFormat();
|
|
if (!dfmt) {
|
|
goto loser;
|
|
}
|
|
|
|
/* form the MessageFormat object */
|
|
/* first, find the key (wrapper) */
|
|
key = (char *) SSM_At(cx->m_params, TIME_WRAPPER);
|
|
|
|
/* second, grab and expand the key word object */
|
|
rv = SSM_GetAndExpandTextKeyedByString(cx, key, &wrapper);
|
|
if (rv != SSM_SUCCESS) {
|
|
goto loser;
|
|
}
|
|
SSM_DebugUTF8String("current time wrapper", wrapper);
|
|
now = PR_Now();
|
|
utf8Now = nslPRTimeToUTF8String(dfmt, now);
|
|
if (!utf8Now) {
|
|
goto loser;
|
|
}
|
|
|
|
SSMTextGen_UTF8StringClear(&cx->m_result);
|
|
|
|
PR_FREEIF(cx->m_result);
|
|
cx->m_result = PR_smprintf(wrapper, utf8Now);
|
|
SSM_DebugUTF8String("result of wrapping", cx->m_result);
|
|
goto done;
|
|
loser:
|
|
if (rv == SSM_SUCCESS) {
|
|
rv = SSM_FAILURE;
|
|
}
|
|
done:
|
|
PR_FREEIF(wrapper);
|
|
if (dfmt != NULL) {
|
|
nlsFreeDateFormat(dfmt);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
|
|
|
|
/* this function handles the button clicks for all server auth failure dialogs
|
|
* except the unknown issuers case
|
|
*/
|
|
SSMStatus SSM_ServerAuthFailureButtonHandler(HTTPRequest* req)
|
|
{
|
|
SSMStatus rv;
|
|
SSMResource* res;
|
|
SSMSSLDataConnection* conn;
|
|
|
|
res = (SSMResource*)(req->target);
|
|
conn = (SSMSSLDataConnection*)(req->target);
|
|
|
|
|
|
if (res->m_buttonType == SSM_BUTTON_OK) {
|
|
/* "OK" button was clicked */
|
|
rv = ssm_http_server_auth_handle_ok_button(req);
|
|
goto done;
|
|
}
|
|
else if (res->m_buttonType == SSM_BUTTON_CANCEL) {
|
|
/* "Cancel" button was clicked */
|
|
rv = ssm_http_server_auth_handle_cancel_button(req);
|
|
goto done;
|
|
}
|
|
else {
|
|
SSM_DEBUG("Don't know which button was clicked.\n");
|
|
rv = SSM_FAILURE;
|
|
}
|
|
SSM_LockResource(res);
|
|
conn->m_UIInfo.UICompleted = PR_TRUE;
|
|
/* the default course of action if something goes wrong is not to trust */
|
|
conn->m_UIInfo.trustBadServerCert = BSCA_NO;
|
|
SSM_NotifyResource(res);
|
|
SSM_UnlockResource(res);
|
|
done:
|
|
/* this actually includes the error cases returned from individual
|
|
* button handlers
|
|
*/
|
|
return rv;
|
|
}
|
|
|
|
|
|
SSMStatus ssm_http_server_auth_handle_ok_button(HTTPRequest* req)
|
|
{
|
|
SSMStatus rv;
|
|
SSMSSLDataConnection* conn;
|
|
char* accept = NULL;
|
|
|
|
conn = (SSMSSLDataConnection*)(req->target);
|
|
|
|
SSM_LockResource(req->target);
|
|
/* if the user wanted to go ahead, accept="session" */
|
|
rv = SSM_HTTPParamValue(req, "accept", &accept);
|
|
if (rv != SSM_SUCCESS) {
|
|
/* either the user did not want to continue or something went wrong */
|
|
conn->m_UIInfo.trustBadServerCert = BSCA_NO;
|
|
goto loser;
|
|
}
|
|
PR_ASSERT(accept != NULL);
|
|
if (PL_strcmp(accept, "session") == 0) {
|
|
conn->m_UIInfo.trustBadServerCert = BSCA_SESSION;
|
|
}
|
|
else if (PL_strcmp(accept, "permanent") == 0) {
|
|
conn->m_UIInfo.trustBadServerCert = BSCA_PERMANENT;
|
|
}
|
|
else {
|
|
/* either something went wrong or the user did not want to continue */
|
|
conn->m_UIInfo.trustBadServerCert = BSCA_NO;
|
|
}
|
|
|
|
loser:
|
|
/* close the window */
|
|
rv = SSM_HTTPCloseAndSleep(req);
|
|
|
|
/* set the predicate to true and unblock the SSL thread */
|
|
conn->m_UIInfo.UICompleted = PR_TRUE;
|
|
SSM_NotifyResource(req->target);
|
|
SSM_UnlockResource(req->target);
|
|
|
|
return rv;
|
|
}
|
|
|
|
|
|
SSMStatus ssm_http_server_auth_handle_cancel_button(HTTPRequest* req)
|
|
{
|
|
SSMStatus rv;
|
|
SSMSSLDataConnection* conn;
|
|
|
|
conn = (SSMSSLDataConnection*)(req->target);
|
|
|
|
SSM_LockResource(req->target);
|
|
|
|
/* close the window */
|
|
rv = SSM_HTTPCloseAndSleep(req);
|
|
|
|
/* leave the cert untrusted */
|
|
conn->m_UIInfo.trustBadServerCert = BSCA_NO;
|
|
conn->m_UIInfo.UICompleted = PR_TRUE;
|
|
SSM_NotifyResource(req->target);
|
|
SSM_UnlockResource(req->target);
|
|
|
|
return rv;
|
|
}
|
|
|
|
|
|
SSMStatus SSM_ServerAuthDomainNameKeywordHandler(SSMTextGenContext* cx)
|
|
{
|
|
SSMResource* target = NULL;
|
|
SSMSSLDataConnection* sslconn = NULL;
|
|
SSMStatus rv;
|
|
char* pattern = NULL;
|
|
char* key = NULL;
|
|
CERTCertificate* serverCert = NULL;
|
|
char* hostname = NULL;
|
|
char* URLHostname = NULL;
|
|
const PRIntn DOMAIN_NAME_FORMAT = (PRIntn)0;
|
|
/* we have one keyword */
|
|
|
|
/* check arguments */
|
|
/* ### sjlee: might as well make this a helper function because most
|
|
* keyword handlers will use this checking
|
|
*/
|
|
PR_ASSERT(cx != NULL);
|
|
PR_ASSERT(cx->m_request != NULL);
|
|
PR_ASSERT(cx->m_params != NULL);
|
|
PR_ASSERT(cx->m_result != NULL);
|
|
if (cx == NULL || cx->m_request == NULL || cx->m_params == NULL ||
|
|
cx->m_result == NULL) {
|
|
PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
|
|
goto loser;
|
|
}
|
|
|
|
/* retrieve the server cert */
|
|
target = SSMTextGen_GetTargetObject(cx);
|
|
PR_ASSERT(SSM_IsA(target, SSM_RESTYPE_SSL_DATA_CONNECTION));
|
|
sslconn = (SSMSSLDataConnection*)target;
|
|
|
|
serverCert = SSL_PeerCertificate(sslconn->socketSSL);
|
|
if (serverCert == NULL) {
|
|
/* couldn't get the server cert: what do I do? */
|
|
goto loser;
|
|
}
|
|
|
|
/* get the hostname from the cert */
|
|
hostname = CERT_GetCommonName(&serverCert->subject);
|
|
if (hostname == NULL) {
|
|
goto loser;
|
|
}
|
|
|
|
/* get the URL hostname from the socket */
|
|
URLHostname = SSL_RevealURL(sslconn->socketSSL);
|
|
if (URLHostname == NULL) {
|
|
goto loser;
|
|
}
|
|
|
|
/* form the MessageFormat object */
|
|
/* first, find the key (format argument) */
|
|
key = (char *) SSM_At(cx->m_params, DOMAIN_NAME_FORMAT);
|
|
|
|
/* second, grab and expand the key word object */
|
|
rv = SSM_GetAndExpandTextKeyedByString(cx, key, &pattern);
|
|
if (rv != SSM_SUCCESS) {
|
|
goto loser;
|
|
}
|
|
SSM_DebugUTF8String("domain name string pattern", pattern);
|
|
|
|
SSMTextGen_UTF8StringClear(&cx->m_result);
|
|
|
|
PR_FREEIF(cx->m_result);
|
|
cx->m_result = PR_smprintf(pattern, URLHostname, hostname);
|
|
if (cx->m_result == NULL) {
|
|
goto loser;
|
|
}
|
|
SSM_DebugUTF8String("wrapped domain name string", cx->m_result);
|
|
|
|
goto done;
|
|
loser:
|
|
if (rv == SSM_SUCCESS) {
|
|
rv = SSM_FAILURE;
|
|
}
|
|
done:
|
|
if (serverCert != NULL) {
|
|
CERT_DestroyCertificate(serverCert);
|
|
}
|
|
if (hostname != NULL) {
|
|
PR_Free(hostname);
|
|
}
|
|
if (URLHostname != NULL) {
|
|
PR_Free(URLHostname);
|
|
}
|
|
PR_FREEIF(pattern);
|
|
return rv;
|
|
}
|
|
|
|
|
|
/*
|
|
* Function: SECStatus SSM_SSLMakeCertBadDomainDialog()
|
|
* Purpose: dispatch the UI event to create the server cert domain name
|
|
* mismatch dialog
|
|
* Arguments and return values
|
|
* - cert: server cert we are dealing with
|
|
* - conn: SSL connection object
|
|
* - returns: SECSuccess if successful *and* the user decides to trust
|
|
* the cert; appropriate error code otherwise
|
|
*/
|
|
SECStatus SSM_SSLMakeCertBadDomainDialog(CERTCertificate* cert,
|
|
SSMSSLDataConnection* conn)
|
|
{
|
|
char * sslHostname = NULL;
|
|
SECStatus rv = SECSuccess;
|
|
|
|
SSM_LockResource(SSMRESOURCE(conn));
|
|
conn->m_UIInfo.UICompleted = PR_FALSE;
|
|
conn->m_UIInfo.trustBadServerCert = BSCA_NO;
|
|
|
|
/* fire up the UI */
|
|
if (SSMControlConnection_SendUIEvent(SSMCONTROLCONNECTION(conn), "get",
|
|
"bad_server_cert_domain",
|
|
SSMRESOURCE(conn), NULL, &SSMRESOURCE(conn)->m_clientContext) != SSM_SUCCESS) {
|
|
rv = SECFailure;
|
|
goto loser;
|
|
}
|
|
|
|
/* wait until the UI event is complete */
|
|
while (!conn->m_UIInfo.UICompleted) {
|
|
SSM_WaitResource(SSMRESOURCE(conn), PR_INTERVAL_NO_TIMEOUT);
|
|
}
|
|
|
|
if (conn->m_UIInfo.trustBadServerCert == BSCA_NO) {
|
|
/* user did not want to continue. Cancel here. */
|
|
if (rv == SECSuccess) {
|
|
rv = SECFailure;
|
|
}
|
|
goto loser;
|
|
}
|
|
|
|
sslHostname = SSL_RevealURL(conn->socketSSL);
|
|
if (!sslHostname)
|
|
goto loser;
|
|
rv = CERT_AddOKDomainName(cert, sslHostname);
|
|
PORT_Free(sslHostname);
|
|
|
|
#if 0 /* this is not neccessary, and is wrong (in this case) */
|
|
rv = SSM_SSLServerCertResetTrust(cert, conn->m_UIInfo.trustBadServerCert);
|
|
#endif
|
|
if (rv != SECSuccess) {
|
|
goto loser;
|
|
}
|
|
|
|
loser:
|
|
conn->m_UIInfo.trustBadServerCert = BSCA_NO;
|
|
conn->m_UIInfo.UICompleted = PR_FALSE;
|
|
SSM_UnlockResource(SSMRESOURCE(conn));
|
|
|
|
return rv;
|
|
}
|
|
|
|
SSMStatus SSM_PrettyFormatCert(CERTCertificate* cert, char* fmt,
|
|
char** result)
|
|
{
|
|
SSMStatus rv = SSM_SUCCESS;
|
|
char * displayName = NULL, *location=NULL, *state = NULL, *country = NULL;
|
|
char * emailaddr = NULL, * orgName = NULL, *unitName = NULL;
|
|
char* issuer = NULL;
|
|
char* serialNumber = NULL;
|
|
char * notBefore = NULL;
|
|
char * notAfter = NULL;
|
|
unsigned char fingerprint[16];
|
|
SECItem fpItem;
|
|
char* fpStr = NULL;
|
|
char* commentString = NULL;
|
|
|
|
/* check arguments */
|
|
if (cert == NULL || fmt == NULL || result == NULL) {
|
|
PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
|
|
goto loser;
|
|
}
|
|
|
|
/* retrieve cert information */
|
|
|
|
displayName = CERT_GetCommonName(&cert->subject);
|
|
emailaddr = CERT_GetCertEmailAddress(&cert->subject);
|
|
orgName = CERT_GetOrgName(&cert->subject);
|
|
unitName = CERT_GetOrgUnitName(&cert->subject);
|
|
if (!displayName)
|
|
displayName = PL_strdup("");
|
|
if (!emailaddr)
|
|
emailaddr = PL_strdup("");
|
|
if (!orgName)
|
|
orgName = PL_strdup("");
|
|
if (!unitName)
|
|
unitName = PL_strdup("");
|
|
|
|
location = CERT_GetLocalityName(&cert->subject);
|
|
if (!location)
|
|
location = PL_strdup("");
|
|
state = CERT_GetStateName(&cert->subject);
|
|
if (!state)
|
|
state = PL_strdup("");
|
|
country = CERT_GetCountryName(&cert->subject);
|
|
if (!country)
|
|
country = PL_strdup("");
|
|
|
|
issuer = CERT_GetOrgName(&cert->issuer);
|
|
if (issuer == NULL)
|
|
issuer = PL_strdup("");
|
|
|
|
serialNumber = CERT_Hexify(&cert->serialNumber, 1);
|
|
if (serialNumber == NULL) {
|
|
serialNumber = PL_strdup("");
|
|
}
|
|
|
|
notBefore = DER_UTCDayToAscii(&cert->validity.notBefore);
|
|
if (!notBefore)
|
|
notBefore = PL_strdup("");
|
|
notAfter = DER_UTCDayToAscii(&cert->validity.notAfter);
|
|
if (!notAfter)
|
|
notAfter = PL_strdup("");
|
|
|
|
MD5_HashBuf(fingerprint, cert->derCert.data, cert->derCert.len);
|
|
|
|
fpItem.data = fingerprint;
|
|
fpItem.len = sizeof(fingerprint);
|
|
|
|
fpStr = CERT_Hexify(&fpItem, 1);
|
|
if (fpStr == NULL) {
|
|
fpStr = PL_strdup("");
|
|
}
|
|
|
|
commentString = CERT_GetCertCommentString(cert);
|
|
if (commentString == NULL) {
|
|
commentString = PL_strdup(" ");
|
|
}
|
|
/* comments can be NULL */
|
|
|
|
*result = PR_smprintf(fmt, displayName, emailaddr, unitName, orgName,
|
|
location, state, country, issuer, serialNumber,
|
|
notBefore, notAfter, fpStr, commentString);
|
|
|
|
if (*result == NULL) {
|
|
goto loser;
|
|
}
|
|
SSM_DebugUTF8String("wrapped view cert string", *result);
|
|
goto done;
|
|
loser:
|
|
SSM_DEBUG("Pretty formatting cert failed.\n");
|
|
if (rv == SSM_SUCCESS) {
|
|
rv = SSM_FAILURE;
|
|
}
|
|
done:
|
|
if (issuer != NULL) {
|
|
PR_Free(issuer);
|
|
}
|
|
if (serialNumber != NULL) {
|
|
PR_Free(serialNumber);
|
|
}
|
|
if (fpStr != NULL) {
|
|
PR_Free(fpStr);
|
|
}
|
|
if (commentString != NULL) {
|
|
PR_Free(commentString);
|
|
}
|
|
PR_FREEIF(notBefore);
|
|
PR_FREEIF(notAfter);
|
|
PR_FREEIF(displayName);
|
|
PR_FREEIF(emailaddr);
|
|
PR_FREEIF(orgName);
|
|
PR_FREEIF(unitName);
|
|
return rv;
|
|
}
|
|
|
|
|
|
SSMStatus SSM_VerifyServerCertKeywordHandler(SSMTextGenContext* cx)
|
|
{
|
|
SSMStatus rv;
|
|
SSMResource* target = NULL;
|
|
SSMSSLDataConnection* sslconn = NULL;
|
|
CERTCertDBHandle* handle = NULL;
|
|
CERTCertificate* serverCert = NULL;
|
|
char* nickname = NULL;
|
|
char* pattern = NULL;
|
|
|
|
PR_ASSERT(cx != NULL);
|
|
PR_ASSERT(cx->m_request != NULL);
|
|
PR_ASSERT(cx->m_params != NULL);
|
|
PR_ASSERT(cx->m_result != NULL);
|
|
|
|
/* retrieve the server cert */
|
|
target = SSMTextGen_GetTargetObject(cx);
|
|
PR_ASSERT(SSM_IsA(target, SSM_RESTYPE_SSL_DATA_CONNECTION));
|
|
sslconn = (SSMSSLDataConnection*)target;
|
|
handle = SSMCONTROLCONNECTION(sslconn)->m_certdb;
|
|
|
|
serverCert = SSL_PeerCertificate(sslconn->socketSSL);
|
|
if (serverCert == NULL) {
|
|
goto loser;
|
|
}
|
|
|
|
nickname = CERT_GetNickName(serverCert, handle, serverCert->arena);
|
|
if (nickname == NULL) {
|
|
/* nickname was not found: that's still OK, let's do this */
|
|
nickname = PL_strdup("Unknown");
|
|
if (nickname == NULL) {
|
|
goto loser;
|
|
}
|
|
}
|
|
/* don't free it! */
|
|
/* if we want to verify the cert, we would do something like this...
|
|
srv = CERT_VerifyCertNow(ctrlconn->m_certdb, cert, PR_TRUE, certSSLServer,
|
|
conn);
|
|
*/
|
|
SSMTextGen_UTF8StringClear(&cx->m_result);
|
|
rv = SSM_GetAndExpandTextKeyedByString(cx, "not_verified_text",
|
|
&cx->m_result);
|
|
if (rv != SSM_SUCCESS) {
|
|
goto loser;
|
|
}
|
|
SSM_DebugUTF8String("wrapped verification string %s", cx->m_result);
|
|
goto done;
|
|
loser:
|
|
if (rv == SSM_SUCCESS) {
|
|
rv = SSM_FAILURE;
|
|
}
|
|
done:
|
|
if (serverCert != NULL) {
|
|
CERT_DestroyCertificate(serverCert);
|
|
}
|
|
if (nickname != NULL) {
|
|
PR_Free(nickname);
|
|
}
|
|
PR_FREEIF(pattern);
|
|
return rv;
|
|
}
|
|
|
|
|
|
/*
|
|
* Function: SECStatus SSM_SSLMakeUnknownIssuerDialog()
|
|
* Purpose: dispatch the UI event to create the unknown issuer dialog
|
|
* Arguments and return values
|
|
* - cert: server cert we are dealing with
|
|
* - conn: SSL connection object
|
|
* - returns: SECSuccess if successful *and* the user decides to trust
|
|
* the cert; appropriate error code otherwise
|
|
*/
|
|
SECStatus SSM_SSLMakeUnknownIssuerDialog(CERTCertificate* cert,
|
|
SSMSSLDataConnection* conn)
|
|
{
|
|
SECStatus rv = SECSuccess;
|
|
|
|
SSM_LockResource(SSMRESOURCE(conn));
|
|
conn->m_UIInfo.UICompleted = PR_FALSE;
|
|
conn->m_UIInfo.trustBadServerCert = BSCA_NO;
|
|
|
|
/* fire up the UI */
|
|
if (SSMControlConnection_SendUIEvent(SSMCONTROLCONNECTION(conn), "get",
|
|
"bad_server_cert_unknown_issuer1",
|
|
SSMRESOURCE(conn), NULL, &SSMRESOURCE(conn)->m_clientContext) !=
|
|
SSM_SUCCESS) {
|
|
rv = SECFailure;
|
|
goto loser;
|
|
}
|
|
|
|
/* wait until the UI event is complete */
|
|
while (!conn->m_UIInfo.UICompleted) {
|
|
SSM_WaitResource(SSMRESOURCE(conn), PR_INTERVAL_NO_TIMEOUT);
|
|
}
|
|
|
|
if (conn->m_UIInfo.trustBadServerCert == BSCA_NO) {
|
|
/* user did not want to continue. Cancel here. */
|
|
if (rv == SECSuccess) {
|
|
rv = SECFailure;
|
|
}
|
|
goto loser;
|
|
}
|
|
|
|
/* reset the trust bit for the session and continue */
|
|
rv = SSM_SSLServerCertResetTrust(cert, conn->m_UIInfo.trustBadServerCert);
|
|
if (rv != SECSuccess) {
|
|
goto loser;
|
|
}
|
|
|
|
loser:
|
|
conn->m_UIInfo.trustBadServerCert = BSCA_NO;
|
|
conn->m_UIInfo.UICompleted = PR_FALSE;
|
|
SSM_UnlockResource(SSMRESOURCE(conn));
|
|
|
|
return rv;
|
|
}
|
|
|
|
|
|
SSMStatus SSM_HTTPUnknownIssuerStep1ButtonHandler(HTTPRequest* req)
|
|
{
|
|
SSMStatus rv;
|
|
SSMSSLDataConnection* conn;
|
|
char* tmpStr = NULL;
|
|
|
|
PR_ASSERT(req->target != NULL);
|
|
conn = (SSMSSLDataConnection*)(req->target);
|
|
|
|
/* make sure you got the right baseRef */
|
|
rv = SSM_HTTPParamValue(req, "baseRef", &tmpStr);
|
|
if (rv != SSM_SUCCESS ||
|
|
PL_strcmp(tmpStr, "windowclose_doclose_js") != 0) {
|
|
goto loser;
|
|
}
|
|
|
|
rv = SSM_HTTPParamValue(req, "do_cancel", &tmpStr);
|
|
if (rv == SSM_SUCCESS) {
|
|
/* cancel button was clicked */
|
|
req->target->m_buttonType = SSM_BUTTON_CANCEL;
|
|
}
|
|
else {
|
|
rv = SSM_HTTPParamValue(req, "do_next", &tmpStr);
|
|
if (rv == SSM_SUCCESS) {
|
|
/* next button was clicked */
|
|
req->target->m_buttonType = SSM_BUTTON_OK; /* close enough */
|
|
}
|
|
}
|
|
if (rv != SSM_SUCCESS) {
|
|
rv = SSM_ERR_NO_BUTTON;
|
|
goto loser;
|
|
}
|
|
|
|
switch (req->target->m_buttonType)
|
|
{
|
|
case SSM_BUTTON_CANCEL:
|
|
rv = ssm_http_server_auth_handle_cancel_button(req);
|
|
break;
|
|
case SSM_BUTTON_OK:
|
|
rv = ssm_http_unknown_issuer_step1_handle_next_button(req);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return rv; /* error code will be properly set */
|
|
loser:
|
|
/* set the predicate to true and unblock the SSL thread */
|
|
SSM_LockResource(req->target);
|
|
conn->m_UIInfo.UICompleted = PR_TRUE;
|
|
conn->m_UIInfo.trustBadServerCert = BSCA_NO;
|
|
SSM_NotifyResource(req->target);
|
|
SSM_UnlockResource(req->target);
|
|
|
|
if (rv == SSM_SUCCESS) {
|
|
rv = SSM_FAILURE;
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
|
|
SSMStatus ssm_http_unknown_issuer_step1_handle_next_button(HTTPRequest* req)
|
|
{
|
|
SSMStatus rv;
|
|
SSMSSLDataConnection* conn;
|
|
|
|
conn = (SSMSSLDataConnection*)(req->target);
|
|
|
|
SSM_LockResource(req->target);
|
|
|
|
/* do away with the first dialog */
|
|
rv = SSM_HTTPCloseAndSleep(req);
|
|
if (rv != SSM_SUCCESS) {
|
|
goto loser;
|
|
}
|
|
|
|
/* fire up the next dialog */
|
|
rv = SSMControlConnection_SendUIEvent(SSMCONTROLCONNECTION(conn), "get",
|
|
"bad_server_cert_unknown_issuer2",
|
|
SSMRESOURCE(conn), NULL, &SSMRESOURCE(conn)->m_clientContext);
|
|
if (rv != SSM_SUCCESS) {
|
|
goto loser;
|
|
}
|
|
|
|
conn->m_UIInfo.UICompleted = PR_FALSE;
|
|
/* the above is redundant but for peace of mind */
|
|
SSM_UnlockResource(req->target);
|
|
return rv; /* SSM_SUCCESS */
|
|
loser:
|
|
/* still we want to unblock the SSL thread: the connection will fail */
|
|
conn->m_UIInfo.trustBadServerCert = BSCA_NO;
|
|
conn->m_UIInfo.UICompleted = PR_TRUE;
|
|
SSM_NotifyResource(req->target);
|
|
SSM_UnlockResource(req->target);
|
|
|
|
return rv;
|
|
}
|
|
|
|
|