зеркало из https://github.com/mozilla/pjs.git
2805 строки
83 KiB
C
2805 строки
83 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 "ctrlconn.h"
|
|
#include "dataconn.h"
|
|
#include "sslconn.h"
|
|
#include "p7cinfo.h"
|
|
#include "p7econn.h"
|
|
#include "p7dconn.h"
|
|
#include "secmime.h"
|
|
#include "hashconn.h"
|
|
#include "certres.h"
|
|
#include "cert.h"
|
|
#include "certdb.h"
|
|
#include "cdbhdl.h"
|
|
#include "servimpl.h"
|
|
#include "newproto.h"
|
|
#include "messages.h"
|
|
#include "serv.h"
|
|
#include "ssmerrs.h"
|
|
#include "minihttp.h"
|
|
#include "secmod.h"
|
|
#include "kgenctxt.h"
|
|
#include "advisor.h"
|
|
#include "processmsg.h"
|
|
#include "signtextres.h"
|
|
#include "p12res.h"
|
|
#include "p12plcy.h"
|
|
#include "secmime.h"
|
|
#include "ciferfam.h"
|
|
#include "profile.h"
|
|
#include "prefs.h"
|
|
#include "ocsp.h"
|
|
#ifdef XP_MAC
|
|
#include "macshell.h"
|
|
#endif
|
|
|
|
/*
|
|
* The structure passed to get an attribute for the control connection,
|
|
* which may require a password prompt.
|
|
*/
|
|
typedef struct GetAttrArgStr {
|
|
SSMResource *res;
|
|
SSMAttributeID attrID;
|
|
SSMResourceAttrType attrType;
|
|
} GetAttrArg;
|
|
|
|
static SSMStatus
|
|
ssmcontrolconnection_encodegetattr_reply(SECItem *msg, SSMStatus rv,
|
|
SSMAttributeValue *value,
|
|
SSMResourceAttrType attrType);
|
|
|
|
|
|
/* The ONLY reason why we can use these macros for both control and
|
|
data connections is that they inherit from the same superclass.
|
|
Do NOT try this at home. */
|
|
#define SSMCONNECTION(c) (&(c)->super)
|
|
#define SSMRESOURCE(c) (&(c)->super.super)
|
|
|
|
/* Special resource id values */
|
|
#define SSM_BASE_RID 0x00000003
|
|
#define SSM_MAX_RID 0x0FFFFFFF
|
|
|
|
|
|
static long ssm_ctrl_count = 0;
|
|
static SSMResourceID ssm_next_ctrlrid = SSM_MAX_RID;
|
|
static char * gUserDir = NULL;
|
|
|
|
static PRMonitor *policySetLock = NULL;
|
|
static PRBool policySet = PR_FALSE;
|
|
|
|
SSMStatus SSM_InitPolicyHandler(void)
|
|
{
|
|
policySetLock = PR_NewMonitor();
|
|
if (policySetLock == NULL) {
|
|
return PR_FAILURE;
|
|
}
|
|
return PR_SUCCESS;
|
|
}
|
|
|
|
|
|
#ifdef TIMEBOMB
|
|
#include "timebomb.h"
|
|
PRBool SSMTimeBombExpired = PR_FALSE;
|
|
#endif
|
|
|
|
|
|
SSMStatus SSMControlConnection_Create(void *arg,
|
|
SSMControlConnection * connection,
|
|
SSMResource **res)
|
|
{
|
|
SSMStatus rv = PR_SUCCESS;
|
|
SSMControlConnection *conn;
|
|
*res = NULL; /* in case we fail */
|
|
|
|
conn = (SSMControlConnection *) PR_CALLOC(sizeof(SSMControlConnection));
|
|
if (!conn) goto loser;
|
|
|
|
SSMRESOURCE(conn)->m_connection = conn;
|
|
rv = SSMControlConnection_Init(conn, SSM_RESTYPE_CONTROL_CONNECTION,
|
|
(PRFileDesc *) arg);
|
|
if (rv != PR_SUCCESS)
|
|
goto loser;
|
|
|
|
SSMControlConnection_Invariant(conn);
|
|
|
|
ssm_ctrl_count++;
|
|
SSM_DEBUG("Control count is now %ld.\n", ssm_ctrl_count);
|
|
|
|
*res = SSMRESOURCE(conn);
|
|
rv = SSM_HashInsert(ctrlConnections, (SSMHashKey)(*res)->m_id,
|
|
(void *)*res);
|
|
if (rv != PR_SUCCESS)
|
|
goto loser;
|
|
|
|
return PR_SUCCESS;
|
|
|
|
loser:
|
|
if (rv == PR_SUCCESS) rv = PR_FAILURE;
|
|
|
|
if (conn)
|
|
{
|
|
SSM_ShutdownResource(SSMRESOURCE(conn), rv); /* force destroy */
|
|
SSM_FreeResource(SSMRESOURCE(conn));
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
#ifdef XP_MAC
|
|
int
|
|
toascii(char c)
|
|
{
|
|
return (int) c;
|
|
}
|
|
#endif
|
|
|
|
SSMStatus SSMControlConnection_GenerateNonce(SSMControlConnection *conn)
|
|
{
|
|
SSMStatus rv = PR_FAILURE;
|
|
SECStatus srv;
|
|
char* buf = NULL;
|
|
char* n = NULL;
|
|
const int NONCE_SIZE = 8;
|
|
int i;
|
|
|
|
conn->m_nonce = NULL; /* in case of failure */
|
|
buf = (char*)PR_CALLOC(NONCE_SIZE);
|
|
if (buf == NULL) {
|
|
goto loser;
|
|
}
|
|
srv = RNG_GenerateGlobalRandomBytes(buf, NONCE_SIZE);
|
|
if (srv != SECSuccess) {
|
|
goto loser;
|
|
}
|
|
|
|
n = PL_strdup("nonce");
|
|
for (i = 0; i < NONCE_SIZE; i++) {
|
|
n = PR_sprintf_append(n, "%d", (int)toascii(buf[i]));
|
|
if (n == NULL) {
|
|
goto loser;
|
|
}
|
|
}
|
|
conn->m_nonce = n;
|
|
rv = PR_SUCCESS;
|
|
loser:
|
|
if (buf != NULL) {
|
|
PR_Free(buf);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
SSMStatus SSMControlConnection_Init(SSMControlConnection *conn,
|
|
SSMResourceType type,
|
|
PRFileDesc *socket)
|
|
{
|
|
SSMStatus rv = PR_SUCCESS;
|
|
PRBool locked = PR_FALSE;
|
|
PRNetAddr dataAddr;
|
|
|
|
rv = SSM_HashCreate(&conn->m_resourceDB);
|
|
if (rv != PR_SUCCESS || !conn->m_resourceDB)
|
|
goto loser;
|
|
/* We need to see if there are any other control connections already
|
|
* established. If there are, then we need to use the next RID
|
|
* as ours so the correct control connection is found for UI events.
|
|
* Before all control connections would get the RID of 3, and that would
|
|
* cause problems.
|
|
*/
|
|
conn->m_lastRID = ssm_next_ctrlrid;
|
|
|
|
conn->m_secAdvisorList = NULL;
|
|
rv = SSM_HashCreate(&conn->m_resourceIdDB);
|
|
if (rv != PR_SUCCESS || conn->m_resourceIdDB == NULL)
|
|
goto loser;
|
|
|
|
rv = SSMConnection_Init(NULL, &conn->super, type);
|
|
if (rv != PR_SUCCESS) goto loser;
|
|
|
|
ssm_next_ctrlrid = conn->super.super.m_id;
|
|
|
|
SSM_LockResource(SSMRESOURCE(conn));
|
|
locked = PR_TRUE;
|
|
|
|
/* Current version. Allow this to drop when the Hello request comes in. */
|
|
conn->m_version = SSM_PROTOCOL_VERSION;
|
|
|
|
/* Generate a nonce. */
|
|
rv = SSMControlConnection_GenerateNonce(conn);
|
|
if (rv != PR_SUCCESS) goto loser;
|
|
SSM_DEBUG("Generated nonce of `%s'.\n",conn->m_nonce);
|
|
|
|
conn->m_controlOutQ = SSM_NewCollection();
|
|
if (!conn->m_controlOutQ)
|
|
goto loser;
|
|
|
|
conn->m_socket = socket;
|
|
|
|
/* Create the data socket */
|
|
conn->m_dataSocket = SSM_OpenPort();
|
|
if (!conn->m_dataSocket) {
|
|
goto loser;
|
|
}
|
|
|
|
/* Get the data port */
|
|
rv = PR_GetSockName(conn->m_dataSocket, &dataAddr);
|
|
if (rv != PR_SUCCESS) {
|
|
goto loser;
|
|
}
|
|
conn->m_dataPort = PR_ntohs(dataAddr.inet.port);
|
|
|
|
SSMCONNECTION(conn)->m_auth_func = SSMControlConnection_Authenticate;
|
|
|
|
/* Creat password handling stuff:temporary and long-term password tables */
|
|
rv = SSM_HashCreate(&conn->m_passwdTable);
|
|
if (rv != PR_SUCCESS || !conn->m_passwdTable)
|
|
goto loser;
|
|
conn->m_passwdLock = PR_NewMonitor();
|
|
if (!conn->m_passwdLock)
|
|
goto loser;
|
|
conn->m_waiting = 0;
|
|
rv = SSM_HashCreate(&conn->m_encrPasswdTable);
|
|
if (rv != PR_SUCCESS || !conn->m_encrPasswdTable)
|
|
goto loser;
|
|
conn->m_encrPasswdLock = PR_NewMonitor();
|
|
if (!conn->m_encrPasswdLock)
|
|
goto loser;
|
|
|
|
/* database for cert look-up by cert ID */
|
|
rv = SSM_HashCreate(&conn->m_certIdDB);
|
|
if (rv != PR_SUCCESS || !conn->m_certIdDB)
|
|
goto loser;
|
|
|
|
conn->m_prefs = PREF_NewPrefs();
|
|
if (conn->m_prefs == NULL) {
|
|
goto loser;
|
|
}
|
|
|
|
conn->m_doesUI = PR_FALSE;
|
|
|
|
/* Spin threads after we set the shutdown function. */
|
|
SSM_DEBUG("spawning read msg thread for %lx.\n", (long) conn);
|
|
|
|
#ifdef ALLOW_STANDALONE
|
|
if (!standalone)
|
|
{
|
|
#endif
|
|
/* Get reference for front end thread */
|
|
SSM_GetResourceReference(SSMRESOURCE(conn));
|
|
conn->m_frontEndThread = SSM_CreateThread(SSMRESOURCE(conn),
|
|
SSM_FrontEndThread);
|
|
if (conn->m_frontEndThread == NULL)
|
|
goto loser;
|
|
|
|
#ifdef ALLOW_STANDALONE
|
|
}
|
|
#endif
|
|
|
|
SSM_UnlockResource(SSMRESOURCE(conn));
|
|
|
|
return PR_SUCCESS;
|
|
|
|
loser:
|
|
if (rv == PR_SUCCESS) rv = PR_FAILURE;
|
|
if (locked)
|
|
SSM_UnlockResource(SSMRESOURCE(conn));
|
|
if (socket)
|
|
{
|
|
PR_Close(socket); /* close this */
|
|
conn->m_socket = NULL;
|
|
}
|
|
if (conn->m_passwdLock)
|
|
PR_DestroyMonitor(conn->m_passwdLock);
|
|
if (conn->m_passwdTable)
|
|
SSM_HashDestroy(conn->m_passwdTable);
|
|
if (conn->m_encrPasswdLock)
|
|
PR_DestroyMonitor(conn->m_encrPasswdLock);
|
|
if (conn->m_encrPasswdTable)
|
|
SSM_HashDestroy(conn->m_encrPasswdTable);
|
|
if (conn->m_certIdDB)
|
|
SSM_HashDestroy(conn->m_certIdDB);
|
|
if (conn->m_prefs)
|
|
PREF_ClosePrefs(conn->m_prefs);
|
|
if (conn->m_resourceDB)
|
|
SSM_HashDestroy(conn->m_resourceDB);
|
|
if (conn->m_resourceIdDB)
|
|
SSM_HashDestroy(conn->m_resourceIdDB);
|
|
|
|
return rv;
|
|
}
|
|
|
|
SSMStatus SSMControlConnection_Shutdown(SSMResource *arg, SSMStatus status)
|
|
{
|
|
SSMStatus rv, trv; /* rv propagates superclass shutdown */
|
|
PRThread *closer = PR_GetCurrentThread();
|
|
|
|
SSMControlConnection *conn = (SSMControlConnection *) arg;
|
|
SSMControlConnection_Invariant(conn);
|
|
|
|
#ifdef TIMEBOMB
|
|
if (SSMTimeBombExpired)
|
|
return;
|
|
#endif
|
|
|
|
SSM_LockResource(arg);
|
|
|
|
/* if the thread calling this routine is a service thread,
|
|
clear its place in the connection object. this is a
|
|
prelude to the wakeup call just below. */
|
|
arg->m_threadCount--; /* decrement if service thread */
|
|
if (closer == conn->m_writeThread)
|
|
conn->m_writeThread = NULL;
|
|
if (closer == conn->m_frontEndThread)
|
|
conn->m_frontEndThread = NULL;
|
|
else
|
|
arg->m_threadCount++; /* not a service thread, restore thread count */
|
|
|
|
/* shut down our base class. */
|
|
rv = SSMConnection_Shutdown(arg, status);
|
|
|
|
/* wake up threads if this is the first time throwing an error */
|
|
if ((arg->m_status != PR_SUCCESS) &&
|
|
(rv != SSM_ERR_ALREADY_SHUT_DOWN))
|
|
{
|
|
SSM_DEBUG("First time aborting control connection.\n");
|
|
SSM_DEBUG("Posting shutdown msgs to queues.\n");
|
|
/* close queues that our threads may be listening on */
|
|
if (conn->m_controlOutQ)
|
|
SSM_SendQMessage(conn->m_controlOutQ,
|
|
SSM_PRIORITY_SHUTDOWN,
|
|
SSM_DATA_PROVIDER_SHUTDOWN,
|
|
0, NULL, PR_TRUE);
|
|
|
|
if (conn->m_writeThread) PR_Interrupt(conn->m_writeThread);
|
|
if (conn->m_frontEndThread) PR_Interrupt(conn->m_frontEndThread);
|
|
}
|
|
|
|
/* If the front end thread is down, close the client socket */
|
|
if ((!conn->m_frontEndThread) && (conn->m_socket))
|
|
{
|
|
/* Got a socket but nothing to work on it with. Close the socket. */
|
|
#ifndef XP_UNIX
|
|
/* Don't close socket with linger on UNIX since the
|
|
* control socket on UNIX is a UNIX domain socket.
|
|
*/
|
|
SSM_DEBUG("Closing control socket with linger.\n");
|
|
trv = SSM_CloseSocketWithLinger(conn->m_socket);
|
|
#else
|
|
SSM_DEBUG("Closing control socket.\n");
|
|
trv = PR_Close(conn->m_socket);
|
|
#endif
|
|
PR_ASSERT(trv == PR_SUCCESS);
|
|
conn->m_socket = NULL; /* don't try closing more than once */
|
|
SSM_DEBUG("Closed control socket (rv == %d).\n",rv);
|
|
}
|
|
|
|
if (SSMRESOURCE(conn)->m_threadCount == 0)
|
|
{
|
|
/* All service threads are down. Shut down NSS. */
|
|
ssm_ShutdownNSS(conn);
|
|
}
|
|
|
|
SSM_UnlockResource(arg);
|
|
return rv;
|
|
}
|
|
|
|
#ifdef XP_MAC
|
|
extern PRBool gShouldQuit;
|
|
#endif
|
|
|
|
SSMStatus SSMControlConnection_Destroy(SSMResource *res,
|
|
PRBool doFree)
|
|
{
|
|
SSMControlConnection *conn = (SSMControlConnection *) res;
|
|
void *value;
|
|
|
|
/* Drain and destroy the queue. */
|
|
if (conn->m_controlOutQ)
|
|
ssm_DrainAndDestroyQueue(&(conn->m_controlOutQ));
|
|
|
|
|
|
/* Free our fields. */
|
|
PR_FREEIF(conn->m_nonce);
|
|
PR_FREEIF(conn->m_profileName);
|
|
PR_FREEIF(conn->m_dirRoot);
|
|
|
|
PR_DestroyMonitor(conn->m_passwdLock);
|
|
PR_DestroyMonitor(conn->m_encrPasswdLock);
|
|
SSM_HashDestroy(conn->m_passwdTable);
|
|
SSM_HashDestroy(conn->m_encrPasswdTable);
|
|
PREF_ClosePrefs(conn->m_prefs);
|
|
|
|
/* log out all pk11 slots for now */
|
|
if (conn->m_pkcs11Init) {
|
|
PK11_LogoutAll();
|
|
}
|
|
|
|
if (conn->m_secAdvisorList)
|
|
SECITEM_ZfreeItem(conn->m_secAdvisorList, PR_TRUE);
|
|
|
|
/* Destroy superclass fields. */
|
|
SSMConnection_Destroy(SSMRESOURCE(conn), PR_FALSE);
|
|
|
|
SSM_HashRemove(ctrlConnections,
|
|
(SSMHashKey)(conn->super.super.m_id), &value);
|
|
|
|
/* If this is the last control connection, quit. */
|
|
--ssm_ctrl_count;
|
|
SSM_DEBUG("Control count is now %ld.\n", ssm_ctrl_count);
|
|
if ((ssm_ctrl_count <= 0)
|
|
#ifdef DEBUG
|
|
&& (PR_GetEnv("NSM_SUPPRESS_EXIT") == NULL)
|
|
#endif
|
|
)
|
|
{
|
|
SSM_DEBUG("Last control connection gone, quitting.\n");
|
|
#ifdef XP_UNIX
|
|
SSM_ReleaseLockFile();
|
|
#endif
|
|
#ifdef XP_MAC
|
|
gShouldQuit = PR_TRUE; // tell primordial thread to quit
|
|
#else
|
|
exit(0);
|
|
#endif
|
|
}
|
|
/* Free the connection object if asked. */
|
|
if (doFree)
|
|
PR_DELETE(conn);
|
|
|
|
return PR_SUCCESS;
|
|
}
|
|
|
|
SSMStatus SSMControlConnection_GetAttrIDs(SSMResource* res,
|
|
SSMAttributeID** ids, PRIntn* count)
|
|
{
|
|
SSMStatus rv;
|
|
|
|
if (res == NULL || ids == NULL || count == NULL) {
|
|
goto loser;
|
|
}
|
|
rv = SSMConnection_GetAttrIDs(res, ids, count);
|
|
if (rv != PR_SUCCESS) {
|
|
goto loser;
|
|
}
|
|
|
|
*ids = (SSMAttributeID *) PR_REALLOC(*ids, (*count + 2)*sizeof(SSMAttributeID));
|
|
if (!*ids) {
|
|
goto loser;
|
|
}
|
|
|
|
(*ids)[*count++] = SSM_FID_DEFAULT_EMAIL_SIGNER_CERT;
|
|
(*ids)[*count++] = SSM_FID_DEFAULT_EMAIL_RECIPIENT_CERT;
|
|
|
|
goto done;
|
|
loser:
|
|
if (rv == PR_SUCCESS) {
|
|
rv = PR_FAILURE;
|
|
}
|
|
done:
|
|
return rv;
|
|
}
|
|
|
|
static void ssmcontrolconnection_getattr_thread(void* inArg)
|
|
{
|
|
GetAttrArg *arg = (GetAttrArg*)inArg;
|
|
SSMResource *res = arg->res;
|
|
SSMAttributeID attrID = arg->attrID;
|
|
SSMResourceAttrType attrType = arg->attrType;
|
|
SSMControlConnection* conn = (SSMControlConnection*)res;
|
|
SSMStatus rv = PR_SUCCESS;
|
|
CERTCertificate *cert = NULL;
|
|
SSMResourceCert *certRes;
|
|
SSMResourceID certID = 0;
|
|
char *certNickname = NULL;
|
|
PRBool locked = PR_FALSE;
|
|
SSMAttributeValue realValue, *value;
|
|
SECItem msg;
|
|
|
|
value = &realValue;
|
|
/* see what it is */
|
|
#ifdef DEBUG
|
|
SSM_RegisterThread("ctrlconn getattr",NULL);
|
|
#endif
|
|
switch(attrID) {
|
|
case SSM_FID_DEFAULT_EMAIL_SIGNER_CERT:
|
|
SSM_LockResource(SSMRESOURCE(conn));
|
|
locked = PR_TRUE;
|
|
rv = PREF_GetStringPref(conn->m_prefs, "security.default_mail_cert",
|
|
&certNickname);
|
|
if (rv != PR_SUCCESS) {
|
|
goto loser;
|
|
}
|
|
|
|
if (certNickname) {
|
|
cert = CERT_FindUserCertByUsage(conn->m_certdb,
|
|
certNickname,
|
|
certUsageEmailSigner,
|
|
PR_FALSE,
|
|
conn);
|
|
if (cert) {
|
|
SSM_CreateResource(SSM_RESTYPE_CERTIFICATE, cert, conn,
|
|
&certID, (SSMResource**)&certRes);
|
|
rv = SSM_ClientGetResourceReference(&certRes->super, &certID);
|
|
SSM_FreeResource(&certRes->super);
|
|
if (rv != SSM_SUCCESS)
|
|
goto loser;
|
|
}
|
|
}
|
|
if (cert == NULL)
|
|
goto loser;
|
|
|
|
value->u.rid = certID;
|
|
value->type = SSM_RID_ATTRIBUTE;
|
|
SSM_UnlockResource(SSMRESOURCE(conn));
|
|
locked = PR_FALSE;
|
|
break;
|
|
|
|
case SSM_FID_DEFAULT_EMAIL_RECIPIENT_CERT:
|
|
SSM_LockResource(SSMRESOURCE(conn));
|
|
locked = PR_TRUE;
|
|
rv = PREF_GetStringPref(conn->m_prefs, "security.default_mail_cert",
|
|
&certNickname);
|
|
if (rv != PR_SUCCESS) {
|
|
goto loser;
|
|
}
|
|
|
|
if (certNickname) {
|
|
cert = CERT_FindUserCertByUsage(conn->m_certdb,
|
|
certNickname,
|
|
certUsageEmailRecipient,
|
|
PR_FALSE,
|
|
conn);
|
|
if (cert) {
|
|
SSM_CreateResource(SSM_RESTYPE_CERTIFICATE, cert, conn,
|
|
&certID, (SSMResource**)&certRes);
|
|
rv = SSM_ClientGetResourceReference(&certRes->super, &certID);
|
|
SSM_FreeResource(&certRes->super);
|
|
if (rv != SSM_SUCCESS)
|
|
goto loser;
|
|
}
|
|
}
|
|
value->u.rid = certID;
|
|
value->type = SSM_RID_ATTRIBUTE;
|
|
SSM_UnlockResource(SSMRESOURCE(conn));
|
|
locked = PR_FALSE;
|
|
break;
|
|
default:
|
|
rv = SSMConnection_GetAttr(res, attrID, attrType, value);
|
|
if (rv != PR_SUCCESS) {
|
|
goto loser;
|
|
}
|
|
}
|
|
if (value->type != attrType) {
|
|
goto loser;
|
|
}
|
|
goto done;
|
|
loser:
|
|
value->type = SSM_NO_ATTRIBUTE;
|
|
if (rv == PR_SUCCESS) {
|
|
rv = PR_FAILURE;
|
|
}
|
|
done:
|
|
if (locked) {
|
|
SSM_UnlockResource(SSMRESOURCE(conn));
|
|
}
|
|
rv = ssmcontrolconnection_encodegetattr_reply(&msg, rv, value,
|
|
attrType);
|
|
if (rv != SSM_SUCCESS) {
|
|
rv = ssmcontrolconnection_encode_err_reply(&msg, rv);
|
|
PR_ASSERT(rv == SSM_SUCCESS);
|
|
}
|
|
ssmcontrolconnection_send_message_to_client(conn, &msg);
|
|
PR_FREEIF(msg.data);
|
|
SSM_FreeResource(res);
|
|
}
|
|
|
|
SSMStatus SSMControlConnection_GetAttr(SSMResource *res, SSMAttributeID attrID,
|
|
SSMResourceAttrType attrType,
|
|
SSMAttributeValue *value)
|
|
{
|
|
GetAttrArg *arg = NULL;
|
|
if (res == NULL || value == NULL) {
|
|
goto loser;
|
|
}
|
|
arg = SSM_NEW(GetAttrArg);
|
|
if (arg == NULL) {
|
|
goto loser;
|
|
}
|
|
arg->res = res;
|
|
arg->attrID = attrID;
|
|
arg->attrType = attrType;
|
|
|
|
if (SSM_CreateAndRegisterThread(PR_USER_THREAD, ssmcontrolconnection_getattr_thread,
|
|
(void *)arg, PR_PRIORITY_NORMAL, PR_LOCAL_THREAD,
|
|
PR_UNJOINABLE_THREAD, 0) == NULL) {
|
|
goto loser;
|
|
}
|
|
return SSM_ERR_DEFER_RESPONSE;
|
|
loser:
|
|
if (arg != NULL) {
|
|
PR_Free(arg);
|
|
}
|
|
return SSM_FAILURE;
|
|
}
|
|
|
|
void
|
|
SSMControlConnection_Invariant(SSMControlConnection *conn)
|
|
{
|
|
if (conn)
|
|
{
|
|
SSMConnection_Invariant(SSMCONNECTION(conn));
|
|
SSM_LockResource(SSMRESOURCE(conn));
|
|
PR_ASSERT(SSM_IsAKindOf(SSMRESOURCE(conn), SSM_RESTYPE_CONTROL_CONNECTION));
|
|
PR_ASSERT(conn->m_controlOutQ != NULL);
|
|
SSM_UnlockResource(SSMRESOURCE(conn));
|
|
}
|
|
}
|
|
|
|
void SSMControlConnection_RecycleItem(SECItem* msg)
|
|
{
|
|
PR_ASSERT(msg != NULL);
|
|
|
|
if (msg->data != NULL) {
|
|
cmt_free(msg->data);
|
|
msg->data = NULL;
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Read msgs from the control connection queue and send them back to client
|
|
*/
|
|
void SSM_WriteCtrlThread(void * arg)
|
|
{
|
|
PRIntn sent, len, type;
|
|
SSMControlConnection * ctrl = NULL;
|
|
SSMStatus rv= PR_FAILURE;
|
|
char * data;
|
|
|
|
#ifdef TIMEBOMB
|
|
if (SSMTimeBombExpired)
|
|
return;
|
|
#endif
|
|
|
|
ctrl = (SSMControlConnection *)arg;
|
|
SSM_RegisterNewThread("ctrl write", (SSMResource*) arg);
|
|
SSM_DEBUG("initializing.\n");
|
|
if (!ctrl)
|
|
{
|
|
rv = (SSMStatus) PR_INVALID_ARGUMENT_ERROR;
|
|
goto loser;
|
|
}
|
|
if (!ctrl->m_socket)
|
|
{
|
|
rv = (SSMStatus) PR_INVALID_ARGUMENT_ERROR;
|
|
goto loser;
|
|
}
|
|
ctrl->m_writeThread = PR_GetCurrentThread();
|
|
|
|
/* wait for SSM_DATA_PROVIDER_OPEN message */
|
|
rv = SSM_RecvQMessage(ctrl->m_controlOutQ,
|
|
SSM_PRIORITY_ANY,
|
|
&type, &len, &data,
|
|
PR_TRUE);
|
|
if (rv != PR_SUCCESS || type != SSM_DATA_PROVIDER_OPEN)
|
|
goto loser;
|
|
SSM_DEBUG("got queue open message.\n");
|
|
|
|
/* look for data in the incoming queue and send it to the client */
|
|
while ((SSMRESOURCE(ctrl)->m_status == PR_SUCCESS) && (rv == PR_SUCCESS))
|
|
{
|
|
rv = SSM_RecvQMessage(ctrl->m_controlOutQ,
|
|
SSM_PRIORITY_ANY,
|
|
&type, &len, &data,
|
|
PR_TRUE);
|
|
if (rv != PR_SUCCESS)
|
|
{
|
|
SSM_DEBUG("Couldn't read or block on outgoing queue (%d).\n",
|
|
rv);
|
|
goto loser;
|
|
}
|
|
switch (type)
|
|
{
|
|
case SSM_DATA_PROVIDER_SHUTDOWN:
|
|
SSM_DEBUG("got queue close message.\n");
|
|
goto loser;
|
|
default:
|
|
{
|
|
CMTMessageHeader header;
|
|
|
|
/* got a regular control message. send it to the client */
|
|
SSM_DEBUG("got message for client (type=%lx,len=%ld).\n",
|
|
type, len);
|
|
header.type = PR_htonl(type);
|
|
header.len = PR_htonl(len);
|
|
|
|
/* Send the message header */
|
|
sent = SSM_WriteThisMany(ctrl->m_socket, &header, sizeof(CMTMessageHeader));
|
|
if (sent != sizeof(CMTMessageHeader)) {
|
|
rv = (SSMStatus) PR_GetError();
|
|
SSM_DEBUG("cannot send message type: %d.\n", rv);
|
|
goto loser;
|
|
}
|
|
|
|
/* Send the message body */
|
|
sent = SSM_WriteThisMany(ctrl->m_socket, data, len);
|
|
if (sent != len)
|
|
{
|
|
rv = (SSMStatus) PR_GetError();
|
|
SSM_DEBUG("cannot send message data: %d.\n", rv);
|
|
goto loser;
|
|
}
|
|
PR_Free(data);
|
|
}
|
|
break;
|
|
} /* end of switch */
|
|
} /* end of while alive loop */
|
|
|
|
loser:
|
|
if (ctrl)
|
|
{
|
|
SSM_DEBUG("Shutting down, rv = %d.\n", rv);
|
|
SSM_ShutdownResource(SSMRESOURCE(ctrl), rv);
|
|
SSM_FreeResource(SSMRESOURCE(ctrl));
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* Check if given cert (arg) already exists in our cert resource db.
|
|
*/
|
|
void SSMControlConnection_CertLookUp(SSMControlConnection * connection,
|
|
void * arg, SSMResource ** res)
|
|
{
|
|
PR_ASSERT(res);
|
|
SSM_HashFind(connection->m_certIdDB, (SSMHashKey) arg, (void **)res);
|
|
if (*res != NULL)
|
|
SSM_GetResourceReference(*res);
|
|
}
|
|
|
|
/* NSS_Init does not open the cert and key db's read/write. Cartman needs
|
|
* them to be opened read/write in order to do key gen's and import
|
|
* certificate chains.
|
|
*
|
|
* For now, we pretty much re-create the code of NSS_Init in this file. If
|
|
* NSS_Init is ever modified so that we can open the db's read/write, then
|
|
* we just call the new function from the function SSM_InitNSS.
|
|
*/
|
|
|
|
static char *
|
|
ssm_certdb_name_cb(void *arg, int dbVersion)
|
|
{
|
|
const char *configdir = (const char*)arg;
|
|
const char *dbver;
|
|
|
|
switch (dbVersion) {
|
|
case 7:
|
|
dbver = "7";
|
|
break;
|
|
case 6:
|
|
dbver = "6";
|
|
break;
|
|
case 5:
|
|
dbver = "5";
|
|
break;
|
|
case 4:
|
|
default:
|
|
dbver = "";
|
|
break;
|
|
}
|
|
|
|
#ifdef XP_MAC
|
|
/* on Mac, :: means parent directory. does non-Mac get // with Seamonkey? */
|
|
return PR_smprintf("%sCertificates%s", configdir, dbver);
|
|
#else
|
|
return PR_smprintf("%s/cert%s.db", configdir, dbver);
|
|
#endif
|
|
}
|
|
|
|
static char *ssm_keydb_name_cb(void *arg, int dbVersion)
|
|
{
|
|
const char *configdir = (const char*)arg;
|
|
const char *dbver;
|
|
|
|
switch (dbVersion) {
|
|
case 3:
|
|
dbver = "3";
|
|
break;
|
|
case 2:
|
|
default:
|
|
dbver = "";
|
|
break;
|
|
}
|
|
#ifdef XP_MAC
|
|
return PR_smprintf("%s:Key Database%s", configdir, dbver);
|
|
#else
|
|
return PR_smprintf("%s/key%s.db", configdir, dbver);
|
|
#endif
|
|
}
|
|
|
|
SECStatus
|
|
ssm_OpenCertDB(const char * configdir,SSMControlConnection *ctrl)
|
|
{
|
|
CERTCertDBHandle *certdb;
|
|
SECStatus status = SECFailure;
|
|
#ifdef ALLOW_STANDALONE
|
|
PRBool readonly = standalone;
|
|
#else
|
|
PRBool readonly = PR_FALSE;
|
|
#endif
|
|
|
|
/* We should not be doing this! */
|
|
if (0) {
|
|
certdb = CERT_GetDefaultCertDB();
|
|
if (certdb) {
|
|
return SECSuccess;
|
|
}
|
|
}
|
|
certdb = PORT_ZNew(CERTCertDBHandle);
|
|
if (certdb == NULL) {
|
|
goto loser;
|
|
}
|
|
status = CERT_OpenCertDB(certdb, readonly, ssm_certdb_name_cb,
|
|
(void*)configdir);
|
|
if (status == SECSuccess) {
|
|
CERT_SetDefaultCertDB(certdb);
|
|
ctrl->m_certdb = certdb;
|
|
} else {
|
|
PR_Free(certdb);
|
|
ctrl->m_certdb = NULL;
|
|
}
|
|
loser:
|
|
return status;
|
|
}
|
|
|
|
SECStatus
|
|
ssm_OpenKeyDB(const char * configdir, SSMControlConnection *ctrl)
|
|
{
|
|
SECKEYKeyDBHandle *keydb;
|
|
#ifdef ALLOW_STANDALONE
|
|
PRBool readonly = standalone;
|
|
#else
|
|
PRBool readonly = PR_FALSE;
|
|
#endif
|
|
|
|
/* we should not be doing this! */
|
|
if (0) {
|
|
keydb = SECKEY_GetDefaultKeyDB();
|
|
if (keydb) {
|
|
return SECSuccess;
|
|
}
|
|
}
|
|
|
|
keydb = SECKEY_OpenKeyDB(readonly, ssm_keydb_name_cb, (void*)configdir);
|
|
if (keydb == NULL) {
|
|
ctrl->m_keydb = NULL;
|
|
return SECFailure;
|
|
}
|
|
SECKEY_SetDefaultKeyDB(keydb);
|
|
ctrl->m_keydb = keydb;
|
|
return SECSuccess;
|
|
}
|
|
|
|
SECStatus
|
|
ssm_OpenSecModDB(const char * configdir)
|
|
{
|
|
static char *secmodname;
|
|
|
|
if (secmodname) {
|
|
return SECSuccess;
|
|
}
|
|
#ifdef XP_UNIX
|
|
secmodname = PR_smprintf("%s/secmodule.db", configdir);
|
|
#elif defined(XP_MAC)
|
|
secmodname = PR_smprintf("%s:Security Modules", configdir);
|
|
#else
|
|
secmodname = PR_smprintf("%s/secmod.db", configdir);
|
|
#endif
|
|
if (secmodname == NULL) {
|
|
return SECFailure;
|
|
}
|
|
SECMOD_init(secmodname);
|
|
return SECSuccess;
|
|
}
|
|
|
|
void
|
|
ssm_ShutdownNSS(SSMControlConnection *ctrl)
|
|
{
|
|
if (ctrl->m_certdb != NULL) {
|
|
CERT_ClosePermCertDB(ctrl->m_certdb);
|
|
}
|
|
if (ctrl->m_keydb != NULL) {
|
|
SECKEY_CloseKeyDB(ctrl->m_keydb);
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
SSMPolicyType
|
|
SSM_ConvertSVRPlcyToSSMPolicy(PRInt32 policy)
|
|
{
|
|
SSMPolicyType retVal;
|
|
switch (policy) {
|
|
#ifdef XP_MAC
|
|
default:
|
|
#endif
|
|
case SVRPLCYDomestic:
|
|
retVal = ssmDomestic;
|
|
break;
|
|
case SVRPLCYExport:
|
|
retVal = ssmExport;
|
|
break;
|
|
case SVRPLCYFrance:
|
|
retVal = ssmFrance;
|
|
break;
|
|
#ifndef XP_MAC
|
|
default:
|
|
retVal = ssmUnknownPolicy;
|
|
#endif
|
|
}
|
|
return retVal;
|
|
}
|
|
#endif
|
|
|
|
void
|
|
SSM_SetP12Export(void)
|
|
{
|
|
SEC_PKCS12EnableCipher(PKCS12_RC4_40, 1);
|
|
SEC_PKCS12EnableCipher(PKCS12_RC4_128, 0);
|
|
SEC_PKCS12EnableCipher(PKCS12_RC2_CBC_40, 1);
|
|
SEC_PKCS12EnableCipher(PKCS12_RC2_CBC_128, 0);
|
|
SEC_PKCS12EnableCipher(PKCS12_DES_56, 0);
|
|
SEC_PKCS12EnableCipher(PKCS12_DES_EDE3_168, 0);
|
|
}
|
|
|
|
void
|
|
SSM_SetSMIMEExport(void)
|
|
{
|
|
SECMIME_EnableCipher(SMIME_RC2_CBC_40, 1);
|
|
#if 0
|
|
SECMIME_EnableCipher(SMIME_RC2_CBC_64, 0);
|
|
SECMIME_EnableCipher(SMIME_RC2_CBC_128, 0);
|
|
SECMIME_EnableCipher(SMIME_RC5PAD_64_16_40, 1);
|
|
SECMIME_EnableCipher(SMIME_RC5PAD_64_16_64, 0);
|
|
SECMIME_EnableCipher(SMIME_RC5PAD_64_16_128, 0);
|
|
SECMIME_EnableCipher(SMIME_DES_CBC_56, 0);
|
|
SECMIME_EnableCipher(SMIME_DES_EDE3_168, 0);
|
|
SECMIME_EnableCipher(SMIME_FORTEZZA, 0);
|
|
#endif
|
|
SECMIME_EnableCipher(CIPHER_FAMILYID_MASK, 0);
|
|
}
|
|
|
|
|
|
SECStatus
|
|
SSM_InitNSS(char* certpath, SSMControlConnection *ctrl, PRInt32 policy)
|
|
{
|
|
SECStatus status;
|
|
SECStatus rv = SECFailure;
|
|
|
|
PR_EnterMonitor(policySetLock);
|
|
if (policySet) {
|
|
SSM_DEBUG("We got another hello request. If this is for "
|
|
"a different user then the first, bad things may happen\n");
|
|
#if 0
|
|
if (reqPolicy != policyType) {
|
|
SSM_DEBUG("Got a hello request with different policy type "
|
|
"than has already been established. "
|
|
"Refusing connection\n");
|
|
rv = SECFailure;
|
|
goto loser;
|
|
}
|
|
#endif
|
|
}
|
|
#if 0
|
|
else if (policyType != reqPolicy) {
|
|
SSM_DEBUG("Initial hello message has different policy type than "
|
|
"the server. Setting policy to Export\n");
|
|
NSS_SetExportPolicy();
|
|
SSM_SetP12Export();
|
|
SSM_SetSMIMEExport();
|
|
policyType = ssmExport;
|
|
}
|
|
#endif
|
|
SSM_DEBUG("First time initializing NSS.\n");
|
|
|
|
|
|
status = ssm_OpenCertDB(certpath, ctrl);
|
|
if (status != SECSuccess) {
|
|
goto loser;
|
|
}
|
|
|
|
status = ssm_OpenKeyDB(certpath, ctrl);
|
|
if (status != SECSuccess) {
|
|
goto loser;
|
|
}
|
|
|
|
status = ssm_OpenSecModDB(certpath);
|
|
if (status != SECSuccess) {
|
|
goto loser;
|
|
}
|
|
ctrl->m_pkcs11Init = PR_TRUE;
|
|
rv = SECSuccess;
|
|
PORT_SetUCS2_ASCIIConversionFunction(SSM_UCS2_ASCIIConversion);
|
|
PORT_SetUCS2_UTF8ConversionFunction(SSM_UCS2_UTF8Conversion);
|
|
policySet = PR_TRUE;
|
|
|
|
/* set default policy strings */
|
|
CERT_SetCAPolicyStringCallback(SSM_GetCAPolicyString, ctrl);
|
|
|
|
|
|
loser:
|
|
if (rv != SECSuccess) {
|
|
ssm_ShutdownNSS(ctrl);
|
|
}
|
|
PR_ExitMonitor(policySetLock);
|
|
return rv;
|
|
}
|
|
/* End the section of code shamelessly copied and modified from NSS_Init*/
|
|
|
|
|
|
static int ssm_ask_pref_to_pk11(int asktype)
|
|
{
|
|
switch (asktype) {
|
|
case 1:
|
|
return -1;
|
|
case 2:
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#ifdef XP_MAC
|
|
extern "C"
|
|
{
|
|
OSErr ConvertMacPathToUnixPath(const char *macPath, char **unixPath);
|
|
}
|
|
#endif
|
|
|
|
/* Set up profile and password information. */
|
|
SSMStatus
|
|
SSMControlConnection_SetupNSS(SSMControlConnection *ctrl, PRInt32 policy)
|
|
{
|
|
SSMStatus rv = PR_SUCCESS;
|
|
SECStatus srv = SECSuccess;
|
|
PRFileInfo info;
|
|
#ifdef XP_MAC
|
|
char *unixPath;
|
|
#endif
|
|
|
|
/* check the profile directory */
|
|
#ifdef XP_UNIX
|
|
/* we expect a non-empty string here: bail if we don't have one */
|
|
if ((PR_GetFileInfo(ctrl->m_dirRoot, &info) != PR_SUCCESS) ||
|
|
(info.type != PR_FILE_DIRECTORY)) {
|
|
SSM_DEBUG("Cannot find a profile in %s.\n", ctrl->m_dirRoot);
|
|
goto loser;
|
|
}
|
|
#elif defined(XP_MAC)
|
|
/* we expect a path that is already Macified. */
|
|
ConvertMacPathToUnixPath(ctrl->m_dirRoot, &unixPath);
|
|
if (!unixPath)
|
|
goto loser;
|
|
if (unixPath[0] != '\0')
|
|
{
|
|
/* profile directory was supplied: check to make sure it exists */
|
|
if ((PR_GetFileInfo(unixPath, &info) != PR_SUCCESS) ||
|
|
(info.type != PR_FILE_DIRECTORY)) {
|
|
SSM_DEBUG("Cannot find a profile in %s.\n", ctrl->m_dirRoot);
|
|
goto loser;
|
|
}
|
|
}
|
|
else
|
|
goto loser; /* for now */
|
|
/* ### mwelch - we're leaking unixPath for now */
|
|
#else
|
|
if (ctrl->m_dirRoot[0] != '\0') {
|
|
/* profile directory was supplied: check to make sure it exists */
|
|
if ((PR_GetFileInfo(ctrl->m_dirRoot, &info) != PR_SUCCESS) ||
|
|
(info.type != PR_FILE_DIRECTORY)) {
|
|
SSM_DEBUG("Cannot find a profile in %s.\n", ctrl->m_dirRoot);
|
|
goto loser;
|
|
}
|
|
}
|
|
else {
|
|
/* profile directory was not supplied: get the "default" directory */
|
|
if (ctrl->m_profileName[0] == '\0') {
|
|
/* this is (almost always) a 3rd party application on win32 that
|
|
* wants to use a "default" profile
|
|
* set it to "Default" and hope for the best
|
|
*/
|
|
ctrl->m_profileName = PL_strdup("Default");
|
|
}
|
|
ctrl->m_dirRoot = SSM_PROF_GetProfileDirectory(ctrl);
|
|
if (ctrl->m_dirRoot == NULL) {
|
|
goto loser;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* Do the libsec initialization */
|
|
|
|
if (gUserDir != NULL) {
|
|
#ifndef WIN32
|
|
if (strcmp(ctrl->m_dirRoot, gUserDir) != 0) {
|
|
goto loser;
|
|
}
|
|
#else
|
|
if (STRNCASECMP(ctrl->m_dirRoot, gUserDir, strlen(gUserDir))) {
|
|
goto loser;
|
|
}
|
|
#endif
|
|
/*
|
|
* This is the best way to get the current key and cert dbs.
|
|
* When NSS fully supports multiple profiles, we'll have to do
|
|
* away with this call.
|
|
*/
|
|
ctrl->m_certdb = CERT_GetDefaultCertDB();
|
|
ctrl->m_keydb = SECKEY_GetDefaultKeyDB();
|
|
} else {
|
|
gUserDir = strdup(ctrl->m_dirRoot);
|
|
/*
|
|
* We only want to initialize NSS once.
|
|
*/
|
|
srv = SSM_InitNSS(ctrl->m_dirRoot, ctrl, policy);
|
|
if (srv != SECSuccess) {
|
|
goto loser;
|
|
}
|
|
}
|
|
|
|
#ifdef ALLOW_STANDALONE
|
|
if (standalone)
|
|
{
|
|
/* Set password callback to be a function which uses a
|
|
preset password. Very insecure, which is why this is
|
|
set in debug code only. */
|
|
PK11_SetPasswordFunc(SSM_StandaloneGetPasswdCallback);
|
|
PK11_SetVerifyPasswordFunc(SSM_StandaloneVerifyPasswdCallback);
|
|
}
|
|
else
|
|
{
|
|
#endif
|
|
/* set passwd callback */
|
|
PK11_SetPasswordFunc(SSM_GetPasswdCallback);
|
|
/* verify passwd */
|
|
PK11_SetVerifyPasswordFunc(SSM_VerifyPasswdCallback);
|
|
#ifdef ALLOW_STANDALONE
|
|
}
|
|
#endif
|
|
|
|
goto done;
|
|
|
|
loser:
|
|
/* Gather the error from wherever it came */
|
|
if (rv == PR_SUCCESS)
|
|
rv = (srv == SECSuccess) ? PR_SUCCESS : PR_FAILURE;
|
|
if (rv == PR_SUCCESS) rv = PR_FAILURE;
|
|
|
|
done:
|
|
return rv;
|
|
}
|
|
|
|
SSMStatus SSMControlConnection_ProcessHello(SSMControlConnection *ctrl, SECItem *msg)
|
|
{
|
|
SSMStatus rv = PR_SUCCESS;
|
|
HelloRequest request;
|
|
HelloReply reply;
|
|
|
|
/* parse client info and store it in the connection object
|
|
*/
|
|
#ifdef NSM_PROTOCOL_STRICT
|
|
/* we should only ever get one hello request */
|
|
PR_ASSERT(ctrl->m_profileName == NULL);
|
|
#endif
|
|
if (CMT_DecodeMessage(HelloRequestTemplate, &request, (CMTItem*)msg) != CMTSuccess) {
|
|
goto loser;
|
|
}
|
|
/* allow earlier client libs to try to talk to us, but not later ones */
|
|
if (request.version > ctrl->m_version) {
|
|
goto loser;
|
|
}
|
|
ctrl->m_doesUI = request.doesUI;
|
|
ctrl->m_profileName = request.profile;
|
|
|
|
if (request.profileDir == NULL) {
|
|
/* it may be an empty string but it should not be NULL */
|
|
goto loser;
|
|
}
|
|
|
|
ctrl->m_dirRoot = request.profileDir;
|
|
SSM_DEBUG("Selected profile directory <%s>.\n", ctrl->m_dirRoot);
|
|
|
|
msg->data = NULL;
|
|
rv = SSMControlConnection_SetupNSS(ctrl, request.policy);
|
|
|
|
/* Create the auto-renewal thread */
|
|
ctrl->m_certRenewalThread = SSM_CreateThread(SSMRESOURCE(ctrl),
|
|
SSM_CertificateRenewalThread);
|
|
goto done;
|
|
|
|
loser:
|
|
if (rv == PR_SUCCESS) rv = PR_FAILURE;
|
|
|
|
done:
|
|
/* construct HELLO_REPLY message
|
|
*/
|
|
SSM_DEBUG("composing Hello reply.\n");
|
|
reply.result = rv;
|
|
reply.sessionID = SSMRESOURCE(ctrl)->m_id;
|
|
reply.version = ctrl->m_version;
|
|
reply.httpPort = SSM_GetHTTPPort();
|
|
reply.nonce.len = strlen(ctrl->m_nonce);
|
|
reply.nonce.data = (unsigned char *) ctrl->m_nonce;
|
|
reply.policy = SSM_GetPolicy();
|
|
reply.stringVersion = SSMVersionString;
|
|
if (rv == PR_SUCCESS) {
|
|
msg->type = (SECItemType) (SSM_HELLO_MESSAGE | SSM_REPLY_OK_MESSAGE);
|
|
}
|
|
else {
|
|
msg->type = (SECItemType) (SSM_HELLO_MESSAGE | SSM_REPLY_ERR_MESSAGE);
|
|
}
|
|
CMT_EncodeMessage(HelloReplyTemplate, (CMTItem*)msg, &reply);
|
|
return rv;
|
|
}
|
|
|
|
|
|
typedef struct {
|
|
char* pref; /* pref key */
|
|
long id; /* cipher ID for NSS */
|
|
} SSMCipherPref;
|
|
|
|
/* cipher suites are listed in the order of decreasing preference in each
|
|
* cipher family
|
|
*/
|
|
SSMCipherPref SSMSSLCiphers[] = {
|
|
/* SSL2 ciphers */
|
|
{"security.ssl2.rc4_128", SSL_EN_RC4_128_WITH_MD5},
|
|
{"security.ssl2.rc2_128", SSL_EN_RC2_128_CBC_WITH_MD5},
|
|
{"security.ssl2.des_ede3_192", SSL_EN_DES_192_EDE3_CBC_WITH_MD5},
|
|
{"security.ssl2.des_64", SSL_EN_DES_64_CBC_WITH_MD5},
|
|
{"security.ssl2.rc4_40", SSL_EN_RC4_128_EXPORT40_WITH_MD5},
|
|
{"security.ssl2.rc2_40", SSL_EN_RC2_128_CBC_EXPORT40_WITH_MD5},
|
|
/* SSL3 ciphers */
|
|
{"security.ssl3.fortezza_fortezza_sha",
|
|
SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA},
|
|
{"security.ssl3.fortezza_rc4_sha", SSL_FORTEZZA_DMS_WITH_RC4_128_SHA},
|
|
{"security.ssl3.rsa_rc4_128_md5", SSL_RSA_WITH_RC4_128_MD5},
|
|
{"security.ssl3.rsa_fips_des_ede3_sha",
|
|
SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA},
|
|
{"security.ssl3.rsa_des_ede3_sha", SSL_RSA_WITH_3DES_EDE_CBC_SHA},
|
|
{"security.ssl3.rsa_fips_des_sha", SSL_RSA_FIPS_WITH_DES_CBC_SHA},
|
|
{"security.ssl3.rsa_des_sha", SSL_RSA_WITH_DES_CBC_SHA},
|
|
{"security.ssl3.rsa_1024_rc4_56_sha",
|
|
TLS_RSA_EXPORT1024_WITH_RC4_56_SHA},
|
|
{"security.ssl3.rsa_1024_des_cbc_sha",
|
|
TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA},
|
|
{"security.ssl3.rsa_rc4_40_md5", SSL_RSA_EXPORT_WITH_RC4_40_MD5},
|
|
{"security.ssl3.rsa_rc2_40_md5", SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5},
|
|
{"security.ssl3.fortezza_null_sha", SSL_FORTEZZA_DMS_WITH_NULL_SHA},
|
|
{"security.ssl3.rsa_null_md5", SSL_RSA_WITH_NULL_MD5},
|
|
{NULL, 0} /* end marker */
|
|
};
|
|
|
|
SSMCipherPref SSMSMIMECiphers[] = {
|
|
/* SMIME bulk ciphers */
|
|
{"security.smime.fortezza", SMIME_FORTEZZA},
|
|
{"security.smime.des_ede3", SMIME_DES_EDE3_168},
|
|
{"security.smime.rc2_128", SMIME_RC2_CBC_128},
|
|
{"security.smime.des", SMIME_DES_CBC_56},
|
|
{"security.smime.rc2_64", SMIME_RC2_CBC_64},
|
|
{"security.smime.rc2_40", SMIME_RC2_CBC_40},
|
|
{NULL, 0} /* end marker */
|
|
};
|
|
|
|
static void ssm_enable_ssl_cipher_prefs(SSMControlConnection* ctrl)
|
|
{
|
|
int i;
|
|
PRBool boolVal = PR_TRUE;
|
|
|
|
for (i = 0; SSMSSLCiphers[i].pref != NULL; i++) {
|
|
if ((PREF_GetBoolPref(ctrl->m_prefs, SSMSSLCiphers[i].pref,
|
|
&boolVal) == PR_SUCCESS) &&
|
|
(boolVal == PR_FALSE)) {
|
|
/* we only have to disable a cipher, not enable one, because
|
|
* prefs only restrict ciphers further over the policies
|
|
*/
|
|
SSL_EnableCipher(SSMSSLCiphers[i].id, boolVal);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ssm_enable_smime_cipher_prefs(SSMControlConnection* ctrl)
|
|
{
|
|
int i;
|
|
PRBool boolVal = PR_TRUE;
|
|
|
|
for (i = 0; SSMSMIMECiphers[i].pref != NULL; i++) {
|
|
if ((PREF_GetBoolPref(ctrl->m_prefs, SSMSMIMECiphers[i].pref,
|
|
&boolVal) == PR_SUCCESS) &&
|
|
(boolVal == PR_FALSE)) {
|
|
SECMIME_EnableCipher(SSMSMIMECiphers[i].id, boolVal);
|
|
}
|
|
}
|
|
}
|
|
|
|
static SSMStatus ssm_enable_security_prefs(SSMControlConnection* ctrl)
|
|
{
|
|
PRBool prefval;
|
|
PRIntn ask;
|
|
PRIntn timeout;
|
|
PK11SlotInfo* slot = NULL;
|
|
PRBool ocspOn;
|
|
char *ocspURL = NULL, *ocspSigner = NULL;
|
|
|
|
PR_ASSERT((ctrl != NULL) && (ctrl->m_prefs != NULL));
|
|
|
|
/* enforce the user's preferences for SSL cipher families */
|
|
if (PREF_GetBoolPref(ctrl->m_prefs, "security.enable_ssl2", &prefval) !=
|
|
PR_SUCCESS) {
|
|
goto loser;
|
|
}
|
|
SSL_EnableDefault(SSL_ENABLE_SSL2, prefval);
|
|
|
|
if (PREF_GetBoolPref(ctrl->m_prefs, "security.enable_ssl3", &prefval) !=
|
|
PR_SUCCESS) {
|
|
goto loser;
|
|
}
|
|
SSL_EnableDefault(SSL_ENABLE_SSL3, prefval);
|
|
|
|
/* set password values */
|
|
if (PREF_GetIntPref(ctrl->m_prefs, "security.ask_for_password", &ask) !=
|
|
PR_SUCCESS) {
|
|
goto loser;
|
|
}
|
|
if (PREF_GetIntPref(ctrl->m_prefs, "security.password_lifetime",
|
|
&timeout) != PR_SUCCESS) {
|
|
goto loser;
|
|
}
|
|
|
|
slot = PK11_GetInternalKeySlot();
|
|
PK11_SetSlotPWValues(slot, ssm_ask_pref_to_pk11((int)ask),
|
|
(int)timeout);
|
|
PK11_FreeSlot(slot);
|
|
|
|
/* disable any additional ciphers that might be marked in prefs */
|
|
ssm_enable_ssl_cipher_prefs(ctrl);
|
|
ssm_enable_smime_cipher_prefs(ctrl);
|
|
|
|
/*
|
|
* Let's take care of OCSP prefs.
|
|
*/
|
|
if (PREF_GetBoolPref(ctrl->m_prefs, "security.OCSP.enabled",
|
|
&ocspOn) != SSM_SUCCESS ||
|
|
!ocspOn) {
|
|
CERT_DisableOCSPChecking(ctrl->m_certdb);
|
|
CERT_DisableOCSPDefaultResponder(ctrl->m_certdb);
|
|
} else {
|
|
/* OCSP should be enabled */
|
|
CERT_EnableOCSPChecking(ctrl->m_certdb);
|
|
/* Do we have a default responder set? */
|
|
if (PREF_GetBoolPref(ctrl->m_prefs,
|
|
"security.OCSP.useDefaultResponder", &ocspOn) == SSM_SUCCESS &&
|
|
ocspOn) {
|
|
/* First let's make sure the default URL and
|
|
* signer have been set.
|
|
*/
|
|
PREF_GetStringPref(ctrl->m_prefs, "security.OCSP.URL", &ocspURL);
|
|
PREF_GetStringPref(ctrl->m_prefs, "security.OCSP.signingCA",
|
|
&ocspSigner);
|
|
if (ocspURL != NULL && ocspSigner != NULL) {
|
|
CERT_SetOCSPDefaultResponder(ctrl->m_certdb, ocspURL,
|
|
ocspSigner);
|
|
CERT_EnableOCSPDefaultResponder(ctrl->m_certdb);
|
|
}
|
|
}
|
|
}
|
|
return PR_SUCCESS;
|
|
loser:
|
|
return PR_FAILURE;
|
|
}
|
|
|
|
SSMStatus SSMControlConnection_ProcessPrefs(SSMControlConnection* ctrl,
|
|
SECItem* msg)
|
|
{
|
|
SSMStatus rv = PR_SUCCESS;
|
|
SetPrefListMessage request;
|
|
SingleNumMessage reply;
|
|
int i;
|
|
PRBool boolval;
|
|
PRIntn intval;
|
|
|
|
SSM_DEBUG("Preferences were passed in from the plugin.\n");
|
|
|
|
/* decode the message */
|
|
if (CMT_DecodeMessage(SetPrefListMessageTemplate, &request,
|
|
(CMTItem*)msg) != CMTSuccess) {
|
|
goto loser;
|
|
}
|
|
|
|
for (i = 0; i < request.length; i++) {
|
|
if (request.list[i].key == NULL) {
|
|
/* misconfigured pref item: look at the next */
|
|
continue;
|
|
}
|
|
|
|
switch (request.list[i].type) {
|
|
case STRING_PREF: /* string type */
|
|
rv = PREF_SetStringPref(ctrl->m_prefs, request.list[i].key,
|
|
request.list[i].value);
|
|
break;
|
|
case BOOL_PREF: /* boolean type */
|
|
if (PL_strcmp(request.list[i].value, "true") == 0) {
|
|
boolval = PR_TRUE;
|
|
}
|
|
else if (PL_strcmp(request.list[i].value, "false") == 0) {
|
|
boolval = PR_FALSE;
|
|
}
|
|
else {
|
|
/* misconfigured */
|
|
break;
|
|
}
|
|
rv = PREF_SetBoolPref(ctrl->m_prefs, request.list[i].key,
|
|
boolval);
|
|
break;
|
|
case INT_PREF: /* integer type */
|
|
intval = atoi(request.list[i].value);
|
|
rv = PREF_SetIntPref(ctrl->m_prefs, request.list[i].key,
|
|
intval);
|
|
break;
|
|
default:
|
|
SSM_DEBUG("We do not understand the pref type.\n");
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* prefs are all stored: now take action to apply prefs */
|
|
rv = ssm_enable_security_prefs(ctrl);
|
|
rv = PR_SUCCESS;
|
|
loser:
|
|
reply.value = rv;
|
|
msg->type =(SECItemType) (SSM_REPLY_OK_MESSAGE | SSM_PREF_ACTION);
|
|
CMT_EncodeMessage(SingleNumMessageTemplate, (CMTItem*)msg, &reply);
|
|
return rv;
|
|
}
|
|
|
|
SSMStatus
|
|
SSMControlConnection_ProcessDataRequest(SSMControlConnection * ctrl,
|
|
SECItem * msg)
|
|
{
|
|
SSMInfoSSL infoSSL;
|
|
SSMInfoP7Encode infoP7Encode;
|
|
SSMInfoP7Decode infoP7Decode;
|
|
SSMHashInitializer infoHash;
|
|
SSMDataConnection *datac;
|
|
void *createArg;
|
|
SSMResourceType connType = SSM_RESTYPE_NULL;
|
|
SSMStatus rv = PR_SUCCESS;
|
|
SSMResourceID dataRID = 0;
|
|
PRInt32 msgtype = msg->type & SSM_SUBTYPE_MASK;
|
|
DataConnectionReply reply;
|
|
|
|
switch (msgtype)
|
|
{
|
|
case SSM_SSL_CONNECTION:
|
|
{
|
|
SSLDataConnectionRequest request;
|
|
|
|
SSM_DEBUG("... specifically, an SSL data request.\n");
|
|
|
|
/* Decode the SSL request message */
|
|
if (CMT_DecodeMessage(SSLDataConnectionRequestTemplate, &request, (CMTItem*)msg) != CMTSuccess) {
|
|
goto loser;
|
|
}
|
|
|
|
(void) memset(&infoSSL, 0, sizeof(SSMInfoSSL));
|
|
infoSSL.isTLS = PR_FALSE;
|
|
infoSSL.flags = request.flags;
|
|
infoSSL.port = request.port;
|
|
infoSSL.hostIP = request.hostIP;
|
|
infoSSL.hostName = request.hostName;
|
|
infoSSL.forceHandshake = request.forceHandshake;
|
|
infoSSL.clientContext = request.clientContext;
|
|
|
|
msg->data = NULL;
|
|
|
|
/* fill in the control connection... */
|
|
infoSSL.parent = ctrl;
|
|
|
|
connType = SSM_RESTYPE_SSL_DATA_CONNECTION;
|
|
createArg = &infoSSL;
|
|
}
|
|
break;
|
|
|
|
case SSM_PKCS7DECODE_STREAM:
|
|
{
|
|
SingleItemMessage request;
|
|
|
|
SSM_DEBUG("PKCS7 Decode Request.\n");
|
|
if (CMT_DecodeMessage(SingleItemMessageTemplate, &request, (CMTItem*)msg) != CMTSuccess) {
|
|
goto loser;
|
|
}
|
|
|
|
connType = SSM_RESTYPE_PKCS7_DECODE_CONNECTION;
|
|
infoP7Decode.ctrl = ctrl;
|
|
infoP7Decode.clientContext = request.item;
|
|
createArg = &infoP7Decode;
|
|
}
|
|
break;
|
|
|
|
case SSM_TLS_CONNECTION:
|
|
case SSM_PROXY_CONNECTION:
|
|
{
|
|
TLSDataConnectionRequest request;
|
|
|
|
SSM_DEBUG("... specifically, a TLS connection request.\n");
|
|
|
|
/* decode the TLS request message */
|
|
if (CMT_DecodeMessage(TLSDataConnectionRequestTemplate, &request,
|
|
(CMTItem*)msg) != CMTSuccess) {
|
|
goto loser;
|
|
}
|
|
|
|
(void)memset(&infoSSL, 0, sizeof(SSMInfoSSL));
|
|
/* notify that this is either an SMTP or an SSL proxy connection, not
|
|
* a regular SSL connection
|
|
*/
|
|
infoSSL.isTLS = PR_TRUE;
|
|
infoSSL.port = request.port;
|
|
infoSSL.hostIP = request.hostIP;
|
|
infoSSL.hostName = request.hostName;
|
|
|
|
msg->data = NULL;
|
|
|
|
/* fill in the control connection... */
|
|
infoSSL.parent = ctrl;
|
|
|
|
connType = SSM_RESTYPE_SSL_DATA_CONNECTION;
|
|
createArg = &infoSSL;
|
|
}
|
|
break;
|
|
|
|
case SSM_PKCS7ENCODE_STREAM:
|
|
{
|
|
PKCS7DataConnectionRequest request;
|
|
|
|
SSM_DEBUG("... specifically, a PKCS#7 Encode request.\n");
|
|
|
|
if (CMT_DecodeMessage(PKCS7DataConnectionRequestTemplate, &request, (CMTItem*)msg) != CMTSuccess) {
|
|
goto loser;
|
|
}
|
|
infoP7Encode.ciRID = request.resID;
|
|
connType = SSM_RESTYPE_PKCS7_ENCODE_CONNECTION;
|
|
infoP7Encode.ctrl = ctrl;
|
|
infoP7Encode.clientContext = request.clientContext;
|
|
createArg = &infoP7Encode;
|
|
}
|
|
break;
|
|
|
|
case SSM_HASH_STREAM:
|
|
{
|
|
SingleNumMessage request;
|
|
|
|
connType = SSM_RESTYPE_HASH_CONNECTION;
|
|
infoHash.m_parent = ctrl;
|
|
if (CMT_DecodeMessage(SingleNumMessageTemplate, &request, (CMTItem*)msg) != CMTSuccess) {
|
|
goto loser;
|
|
}
|
|
infoHash.m_hashtype = (HASH_HashType) request.value;
|
|
msg->data = NULL;
|
|
createArg = &infoHash;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
SSM_DEBUG("Unknown data connection type (%lx).\n",
|
|
(msg->type & SSM_SUBTYPE_MASK));
|
|
}
|
|
|
|
if (connType != SSM_RESTYPE_NULL)
|
|
{
|
|
/* ... then create the data connection */
|
|
SSM_DEBUG("Firing up data connection.\n");
|
|
rv = SSM_CreateResource(connType, createArg, ctrl, &dataRID,
|
|
(SSMResource **) &datac);
|
|
if ((rv != PR_SUCCESS) || (!datac)) {
|
|
goto loser;
|
|
}
|
|
} else {
|
|
rv = (SSMStatus) SSM_ERR_BAD_RESOURCE_TYPE;
|
|
goto loser;
|
|
}
|
|
|
|
/* compose reply message */
|
|
SSM_DEBUG("Composing reply.\n");
|
|
msg->type = (SECItemType) (SSM_DATA_CONNECTION | msgtype | SSM_REPLY_OK_MESSAGE);
|
|
reply.result = rv;
|
|
reply.connID = dataRID;
|
|
reply.port = ctrl->m_dataPort;
|
|
if (CMT_EncodeMessage(DataConnectionReplyTemplate, (CMTItem*)msg, &reply) != CMTSuccess) {
|
|
goto loser;
|
|
}
|
|
if (rv != PR_SUCCESS) goto loser;
|
|
|
|
goto done;
|
|
loser:
|
|
if (msg->data)
|
|
{
|
|
PR_Free(msg->data);
|
|
msg->data = NULL;
|
|
}
|
|
if (rv == PR_SUCCESS) rv = PR_FAILURE;
|
|
reply.result = PR_GetError();
|
|
reply.connID = 0;
|
|
reply.port = 0;
|
|
CMT_EncodeMessage(DataConnectionReplyTemplate, (CMTItem*)msg, &reply);
|
|
done:
|
|
return rv;
|
|
}
|
|
|
|
SSMStatus
|
|
SSMControlConnection_ProcessDupResourceRequest(SSMControlConnection * ctrl,
|
|
SECItem * msg)
|
|
{
|
|
SSMResourceID objID;
|
|
SSMResource *obj = NULL;
|
|
SSMStatus rv = PR_SUCCESS;
|
|
SingleNumMessage request;
|
|
DupResourceReply reply;
|
|
|
|
SSM_DEBUG("Got a Duplicate Resource request.\n");
|
|
/* parse message and get resource/field ID */
|
|
|
|
if (CMT_DecodeMessage(SingleNumMessageTemplate, &request, (CMTItem*)msg) != CMTSuccess) {
|
|
goto loser;
|
|
}
|
|
objID = request.value;
|
|
|
|
/* ### mwelch Should free this here, instead of in the actual
|
|
message parsing code. (4/13/99) */
|
|
msg->data = NULL;
|
|
|
|
SSM_DEBUG("Rsrc ID %ld.\n", objID);
|
|
|
|
rv = SSMControlConnection_GetResource(ctrl, objID, &obj);
|
|
if (rv != PR_SUCCESS)
|
|
goto loser;
|
|
|
|
PR_ASSERT(obj != NULL);
|
|
rv = SSM_ClientGetResourceReference(obj, &objID);
|
|
if (rv != PR_SUCCESS)
|
|
goto loser;
|
|
|
|
SSM_DEBUG("DuplicateResource: result %ld, new rsrc ID %ld.\n",
|
|
(long) rv, (long)obj->m_id);
|
|
goto done;
|
|
loser:
|
|
/* Got an error while getting the reference. This is recoverable,
|
|
because we just report the error back to the client. */
|
|
if (rv == PR_SUCCESS) rv = PR_FAILURE;
|
|
done:
|
|
msg->data = NULL;
|
|
msg->len = 0;
|
|
|
|
/* compose reply message */
|
|
SSM_DEBUG("Composing reply.\n");
|
|
msg->type = (SECItemType) (SSM_RESOURCE_ACTION | SSM_DUPLICATE_RESOURCE
|
|
| ((rv == SSM_SUCCESS) ? SSM_REPLY_OK_MESSAGE : SSM_REPLY_ERR_MESSAGE));
|
|
reply.result = rv;
|
|
reply.resID = (obj ? obj->m_id : 0);
|
|
if (CMT_EncodeMessage(DupResourceReplyTemplate, (CMTItem*)msg, &reply) != CMTSuccess) {
|
|
goto loser;
|
|
}
|
|
rv = PR_SUCCESS;
|
|
if ((msg->data == NULL) || (msg->len == 0)) rv = PR_FAILURE;
|
|
if (obj != NULL)
|
|
SSM_FreeResource(obj);
|
|
|
|
return rv;
|
|
}
|
|
|
|
SSMStatus
|
|
SSMControlConnection_ProcessDestroyRequest(SSMControlConnection * ctrl,
|
|
SECItem * msg)
|
|
{
|
|
SSMStatus rv = PR_SUCCESS;
|
|
DestroyResourceRequest request;
|
|
SingleNumMessage reply;
|
|
|
|
SSM_DEBUG("Got a Destroy Resource request.\n");
|
|
/* parse message and get resource/field ID */
|
|
if (CMT_DecodeMessage(DestroyResourceRequestTemplate, &request, (CMTItem*)msg) != CMTSuccess) {
|
|
goto loser;
|
|
}
|
|
msg->data = NULL;
|
|
|
|
SSM_DEBUG("RID %ld, expected type %ld.\n", request.resID, request.resType);
|
|
|
|
rv = SSM_ClientDestroyResource(ctrl, request.resID, (SSMResourceType) request.resType);
|
|
goto done;
|
|
loser:
|
|
/* Got an error while getting the reference. This is recoverable,
|
|
because we just report the error back to the client. */
|
|
if (rv == PR_SUCCESS) rv = PR_FAILURE;
|
|
done:
|
|
msg->data = NULL;
|
|
msg->len = 0;
|
|
|
|
/* compose reply message */
|
|
SSM_DEBUG("Composing reply.\n");
|
|
msg->type = (SECItemType) (SSM_RESOURCE_ACTION
|
|
| SSM_DESTROY_RESOURCE
|
|
| ((rv == SSM_SUCCESS) ? SSM_REPLY_OK_MESSAGE : SSM_REPLY_ERR_MESSAGE));
|
|
reply.value = rv;
|
|
CMT_EncodeMessage(SingleNumMessageTemplate, (CMTItem*)msg, &reply);
|
|
rv = PR_SUCCESS;
|
|
if ((msg->data == NULL) || (msg->len == 0)) rv = PR_FAILURE;
|
|
|
|
return rv;
|
|
}
|
|
|
|
static SSMStatus
|
|
ssmcontrolconnection_encodegetattr_reply(SECItem *msg, SSMStatus rv,
|
|
SSMAttributeValue *value,
|
|
SSMResourceAttrType attrType)
|
|
{
|
|
GetAttribReply reply;
|
|
|
|
msg->data = NULL;
|
|
msg->len = 0;
|
|
|
|
/* compose reply message */
|
|
SSM_DEBUG("Composing reply.\n");
|
|
msg->type = (SECItemType) (SSM_RESOURCE_ACTION
|
|
| SSM_GET_ATTRIBUTE
|
|
| SSM_REPLY_OK_MESSAGE
|
|
| attrType);
|
|
|
|
reply.result = rv;
|
|
reply.value = *value;
|
|
CMT_EncodeMessage(GetAttribReplyTemplate, (CMTItem*)msg, &reply);
|
|
SSM_DestroyAttrValue(value, PR_FALSE);
|
|
rv = SSM_SUCCESS;
|
|
if ((msg->data == NULL) || (msg->len == 0))
|
|
rv = SSM_FAILURE;
|
|
|
|
return rv;
|
|
}
|
|
|
|
SSMStatus
|
|
SSMControlConnection_ProcessGetAttrRequest(SSMControlConnection * ctrl,
|
|
SECItem * msg)
|
|
{
|
|
SSMResource *obj = NULL;
|
|
SSMResourceAttrType mAttrType;
|
|
SSMStatus rv;
|
|
SSMAttributeValue value = {SSM_NO_ATTRIBUTE};
|
|
GetAttribRequest request;
|
|
|
|
|
|
SSM_DEBUG("Got a Get Attribute request.\n");
|
|
/* parse message and get resource/field ID */
|
|
|
|
if (CMT_DecodeMessage(GetAttribRequestTemplate, &request, (CMTItem*)msg) != CMTSuccess) {
|
|
goto loser;
|
|
}
|
|
|
|
msg->data = NULL;
|
|
|
|
SSM_DEBUG("Rsrc ID %ld, attr ID %ld.\n", request.resID, request.fieldID);
|
|
|
|
if (request.resID == SSM_SESSION_RESOURCE) {
|
|
SSM_GetResourceReference(&ctrl->super.super);
|
|
obj = (SSMResource *)ctrl;
|
|
} else {
|
|
rv = SSMControlConnection_GetResource(ctrl, request.resID, &obj);
|
|
if (rv != PR_SUCCESS)
|
|
goto loser;
|
|
}
|
|
|
|
PR_ASSERT(obj != NULL);
|
|
mAttrType = (SSMResourceAttrType) (msg->type & SSM_SPECIFIC_MASK);
|
|
rv = SSM_GetResAttribute(obj, (SSMAttributeID) request.fieldID, mAttrType,
|
|
&value);
|
|
SSM_DEBUG("GetResAttribute: result %ld, type %lx.\n",
|
|
(long) rv, (long) value.type);
|
|
if (rv == SSM_ERR_DEFER_RESPONSE)
|
|
goto defer;
|
|
if (rv != PR_SUCCESS)
|
|
goto loser;
|
|
|
|
/* Make sure the type returned matches what was asked. */
|
|
if (mAttrType != value.type) {
|
|
rv = SSM_ERR_ATTRIBUTE_TYPE_MISMATCH;
|
|
/* pick default values for resource result that work
|
|
for all three resource replies*/
|
|
SSM_DestroyAttrValue(&value, PR_FALSE);
|
|
value.type = SSM_NUMERIC_ATTRIBUTE;
|
|
value.u.numeric = 0;
|
|
}
|
|
|
|
goto done;
|
|
loser:
|
|
/* Got an error while getting resource and/or attributes. This
|
|
is recoverable, because we just report the error back to the
|
|
client. */
|
|
if (rv == PR_SUCCESS)
|
|
rv = PR_FAILURE;
|
|
/* Create a suitable zero value for any type requested */
|
|
SSM_DestroyAttrValue(&value, PR_FALSE);
|
|
value.type = SSM_NUMERIC_ATTRIBUTE;
|
|
value.u.numeric = 0;
|
|
done:
|
|
rv = ssmcontrolconnection_encodegetattr_reply(msg, rv, &value,
|
|
(SSMResourceAttrType) (msg->type & SSM_SPECIFIC_MASK));
|
|
if (obj != NULL)
|
|
SSM_FreeResource(obj);
|
|
defer:
|
|
return rv;
|
|
}
|
|
|
|
SSMStatus
|
|
SSMControlConnection_ProcessSetAttrRequest(SSMControlConnection * ctrl,
|
|
SECItem * msg)
|
|
{
|
|
SSMStatus rv;
|
|
SSMResource *obj;
|
|
SetAttribRequest request;
|
|
|
|
SSM_DEBUG("Got a Set Attribute Request.\n");
|
|
if (CMT_DecodeMessage(SetAttribRequestTemplate, &request, (CMTItem*)msg) != CMTSuccess) {
|
|
goto loser;
|
|
}
|
|
|
|
PORT_Free(msg->data);
|
|
msg->data = NULL;
|
|
msg->len = 0;
|
|
|
|
SSM_DEBUG("Rsrc ID %ld, attr ID %ld.\n", request.resID, request.fieldID);
|
|
|
|
rv = SSMControlConnection_GetResource(ctrl, request.resID, &obj);
|
|
if (rv != PR_SUCCESS) {
|
|
goto loser;
|
|
}
|
|
PR_ASSERT(obj != NULL);
|
|
rv = SSM_SetResAttribute(obj, (SSMAttributeID)request.fieldID, &request.value);
|
|
SSM_FreeResource(obj);
|
|
SSM_DestroyAttrValue(&request.value, PR_FALSE);
|
|
if (rv != PR_SUCCESS) {
|
|
goto loser;
|
|
}
|
|
msg->type = (SECItemType) (SSM_RESOURCE_ACTION | SSM_SET_ATTRIBUTE |
|
|
SSM_REPLY_OK_MESSAGE | (msg->type & SSM_SPECIFIC_MASK));
|
|
|
|
return PR_SUCCESS;
|
|
loser:
|
|
return PR_FAILURE;
|
|
}
|
|
|
|
SSMStatus
|
|
SSMControlConnection_ProcessCreateRequest(SSMControlConnection * ctrl,
|
|
SECItem * msg)
|
|
{
|
|
SSMStatus rv;
|
|
SSMResourceID rid = 0;
|
|
SSMResource *res = NULL;
|
|
unsigned char *params = NULL;
|
|
CreateResourceRequest request;
|
|
CreateResourceReply reply;
|
|
|
|
SSM_DEBUG("Got a Create Resource Request.\n");
|
|
if (CMT_DecodeMessage(CreateResourceRequestTemplate, &request, (CMTItem*)msg) != CMTSuccess) {
|
|
goto loser;
|
|
}
|
|
|
|
msg->data = NULL;
|
|
msg->len = 0;
|
|
|
|
SSM_DEBUG("Type %ld, param len %ld.\n", request.type, request.params.len);
|
|
|
|
/* Bleah. Switch on the type.
|
|
### mwelch Must replace, since many of these resources can be
|
|
generically created using control connection + single param */
|
|
switch(request.type)
|
|
{
|
|
case SSM_RESTYPE_KEYGEN_CONTEXT:
|
|
{
|
|
SSMKeyGenContextCreateArg arg;
|
|
|
|
arg.parent = ctrl;
|
|
arg.type = SSM_CRMF_KEYGEN;
|
|
arg.param = &request.params;
|
|
SSM_DEBUG("Creating key gen context.\n");
|
|
rv = SSM_CreateResource((SSMResourceType) request.type, &arg,
|
|
ctrl, &rid, &res);
|
|
}
|
|
break;
|
|
case SSM_RESTYPE_SIGNTEXT:
|
|
{
|
|
rv = SSMSignTextResource_Create(request.params.data, ctrl, &res);
|
|
if (rv == PR_SUCCESS) {
|
|
PR_ASSERT(res != NULL);
|
|
rid = res->m_id;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
rv = (SSMStatus) PR_INVALID_ARGUMENT_ERROR;
|
|
break;
|
|
}
|
|
if (rv != PR_SUCCESS && rv != SSM_ERR_DEFER_RESPONSE)
|
|
goto loser;
|
|
PR_ASSERT(res != NULL);
|
|
|
|
if (SSM_ClientGetResourceReference(res, &res->m_id) != PR_SUCCESS)
|
|
goto loser;
|
|
|
|
/* if deferred response don't create reply message */
|
|
if (rv != PR_SUCCESS)
|
|
goto done;
|
|
|
|
msg->type = (SECItemType) (SSM_RESOURCE_ACTION
|
|
| SSM_CREATE_RESOURCE
|
|
| SSM_REPLY_OK_MESSAGE);
|
|
|
|
goto done;
|
|
loser:
|
|
if (rv == PR_SUCCESS)
|
|
rv = PR_FAILURE;
|
|
done:
|
|
if (params)
|
|
PR_Free(params);
|
|
/* Create a reply message here. */
|
|
reply.result = rv;
|
|
reply.resID = rid;
|
|
CMT_EncodeMessage(CreateResourceReplyTemplate,(CMTItem*)msg, &reply);
|
|
return rv;
|
|
}
|
|
|
|
SSMStatus
|
|
SSMControlConnection_ProcessResourceRequest(SSMControlConnection * ctrl,
|
|
SECItem * msg)
|
|
{
|
|
SSMStatus rv = PR_SUCCESS;
|
|
|
|
SSM_DEBUG("Got a resource-related request.\n");
|
|
switch (msg->type & SSM_SUBTYPE_MASK)
|
|
{
|
|
case SSM_GET_ATTRIBUTE:
|
|
rv = SSMControlConnection_ProcessGetAttrRequest(ctrl, msg);
|
|
break;
|
|
case SSM_CONSERVE_RESOURCE:
|
|
rv = SSMControlConnection_ProcessConserveRequest(ctrl, msg);
|
|
break;
|
|
case SSM_DESTROY_RESOURCE:
|
|
rv = SSMControlConnection_ProcessDestroyRequest(ctrl, msg);
|
|
break;
|
|
case SSM_DUPLICATE_RESOURCE:
|
|
rv = SSMControlConnection_ProcessDupResourceRequest(ctrl, msg);
|
|
break;
|
|
case SSM_SET_ATTRIBUTE:
|
|
rv = SSMControlConnection_ProcessSetAttrRequest(ctrl, msg);
|
|
break;
|
|
case SSM_CREATE_RESOURCE:
|
|
rv = SSMControlConnection_ProcessCreateRequest(ctrl, msg);
|
|
break;
|
|
case SSM_TLS_STEPUP:
|
|
rv = SSMControlConnection_ProcessTLSRequest(ctrl, msg);
|
|
break;
|
|
case SSM_PROXY_STEPUP:
|
|
rv = SSMControlConnection_ProcessProxyStepUpRequest(ctrl, msg);
|
|
break;
|
|
default:
|
|
SSM_DEBUG("Unknown resource request (%lx).\n",
|
|
(msg->type & SSM_SUBTYPE_MASK));
|
|
goto loser;
|
|
}
|
|
goto done;
|
|
loser:
|
|
SSM_DEBUG("ProcessResourceRequest: loser hit, rv = %ld.\n", rv);
|
|
if (msg->data)
|
|
{
|
|
PR_Free(msg->data);
|
|
msg->data = NULL;
|
|
msg->len = 0;
|
|
}
|
|
if (rv == PR_SUCCESS) rv = PR_FAILURE;
|
|
done:
|
|
return rv;
|
|
}
|
|
|
|
SSMStatus
|
|
SSMControlConnection_ProcessSigningRequest(SSMControlConnection *ctrl,
|
|
SECItem *msg)
|
|
{
|
|
SSMStatus rv = PR_FAILURE;
|
|
SSMP7ContentInfo *ci;
|
|
SSMResourceID ciRID;
|
|
SEC_PKCS7ContentInfo *cinfo;
|
|
SSMResourceCert *scert, *ecert, *rcert;
|
|
CERTCertificate **rcerts;
|
|
PRInt32 i;
|
|
|
|
/* Handle a Verify Detached Signature message */
|
|
switch(msg->type & SSM_SUBTYPE_MASK)
|
|
{
|
|
case SSM_VERIFY_DETACHED_SIG:
|
|
{
|
|
VerifyDetachedSigRequest request;
|
|
SingleNumMessage reply;
|
|
|
|
SSM_DEBUG("Processing Verify Detached Signature request.\n");
|
|
if (CMT_DecodeMessage(VerifyDetachedSigRequestTemplate, &request, (CMTItem*)msg) != CMTSuccess) {
|
|
rv = PR_FAILURE;
|
|
} else {
|
|
rv = PR_SUCCESS;
|
|
}
|
|
msg->data = NULL;
|
|
|
|
if (rv == PR_SUCCESS)
|
|
{
|
|
/* Get the content info resource, if it exists. */
|
|
rv = SSMControlConnection_GetResource(ctrl, request.pkcs7ContentID,
|
|
(SSMResource **) &ci);
|
|
}
|
|
|
|
if (rv == PR_SUCCESS)
|
|
{
|
|
PR_ASSERT(SSM_IsAKindOf(&ci->super, SSM_RESTYPE_PKCS7_CONTENT_INFO));
|
|
SSM_DEBUG("Found content info (%s at %ld).\n",
|
|
SSM_ResourceClassName(&ci->super),
|
|
ci->super.m_id);
|
|
rv = SSMP7ContentInfo_VerifyDetachedSignature(ci,
|
|
(SECCertUsage) request.certUsage,
|
|
(HASH_HashType) request.hashAlgID,
|
|
(PRBool) request.keepCert,
|
|
(PRIntn) request.hash.len,
|
|
(char *)request.hash.data);
|
|
SSM_DEBUG("VerifyDetachedSig rv = %d.\n", rv);
|
|
}
|
|
msg->type = (SECItemType) (SSM_OBJECT_SIGNING | SSM_VERIFY_DETACHED_SIG
|
|
| SSM_REPLY_OK_MESSAGE);
|
|
if (rv != SSM_SUCCESS) {
|
|
reply.value = PR_GetError();
|
|
} else {
|
|
reply.value = 0;
|
|
}
|
|
CMT_EncodeMessage(SingleNumMessageTemplate, (CMTItem*)msg, &reply);
|
|
|
|
return SSM_SUCCESS;
|
|
}
|
|
break;
|
|
case SSM_CREATE_SIGNED:
|
|
{
|
|
CreateSignedRequest request;
|
|
CreateContentInfoReply reply;
|
|
|
|
SSM_DEBUG("Processing Create Signed request.\n");
|
|
if (CMT_DecodeMessage(CreateSignedRequestTemplate, &request, (CMTItem*)msg) != CMTSuccess) {
|
|
goto create_signed_loser;
|
|
}
|
|
msg->data = NULL;
|
|
rv = SSMControlConnection_GetResource(ctrl, request.scertRID,
|
|
(SSMResource **)&scert);
|
|
if (rv != PR_SUCCESS)
|
|
goto create_signed_loser;
|
|
if (!SSM_IsAKindOf(&scert->super, SSM_RESTYPE_CERTIFICATE))
|
|
goto create_signed_loser;
|
|
rv = SSMControlConnection_GetResource(ctrl, request.ecertRID,
|
|
(SSMResource **)&ecert);
|
|
if (rv != PR_SUCCESS)
|
|
goto create_signed_loser;
|
|
if (!SSM_IsAKindOf(&ecert->super, SSM_RESTYPE_CERTIFICATE))
|
|
goto create_signed_loser;
|
|
cinfo = SECMIME_CreateSigned(scert->cert, ecert->cert, ctrl->m_certdb,
|
|
(SECOidTag) request.dig_alg,
|
|
(SECItem*)&request.digest,
|
|
(SECKEYGetPasswordKey) NULL, NULL);
|
|
if (cinfo == NULL)
|
|
goto create_signed_loser;
|
|
rv = SSM_CreateResource(SSM_RESTYPE_PKCS7_CONTENT_INFO, cinfo,
|
|
ctrl, &ciRID, (SSMResource **)&ci);
|
|
if (rv != PR_SUCCESS)
|
|
goto create_signed_loser;
|
|
|
|
/* Get a client reference */
|
|
rv = SSM_ClientGetResourceReference(&ci->super, &ciRID);
|
|
if (rv != PR_SUCCESS) {
|
|
goto create_signed_loser;
|
|
}
|
|
|
|
msg->type = (SECItemType) (SSM_OBJECT_SIGNING | SSM_CREATE_SIGNED |
|
|
SSM_REPLY_OK_MESSAGE);
|
|
reply.ciRID = ciRID;
|
|
reply.result = PR_SUCCESS;
|
|
reply.errorCode = 0;
|
|
CMT_EncodeMessage(CreateContentInfoReplyTemplate, (CMTItem*)msg, &reply);
|
|
break;
|
|
|
|
create_signed_loser:
|
|
msg->type = (SECItemType) (SSM_OBJECT_SIGNING | SSM_CREATE_SIGNED |
|
|
SSM_REPLY_ERR_MESSAGE);
|
|
reply.ciRID = 0;
|
|
reply.result = PR_FAILURE;
|
|
reply.errorCode = PORT_GetError();
|
|
CMT_EncodeMessage(CreateContentInfoReplyTemplate, (CMTItem*)msg, &reply);
|
|
/* Return success here so that ProcessMessage doesn't just send
|
|
* back a message with only the rv. That's not interesting return
|
|
* value in this case.
|
|
*/
|
|
return PR_SUCCESS;
|
|
}
|
|
case SSM_CREATE_ENCRYPTED:
|
|
{
|
|
CreateEncryptedRequest request;
|
|
CreateContentInfoReply reply;
|
|
|
|
SSM_DEBUG("Processing Create Encrypted request.\n");
|
|
if (CMT_DecodeMessage(CreateEncryptedRequestTemplate, &request, (CMTItem*)msg) != CMTSuccess) {
|
|
goto create_encrypted_loser;
|
|
}
|
|
|
|
msg->data = NULL;
|
|
rv = SSMControlConnection_GetResource(ctrl, request.scertRID,
|
|
(SSMResource **)&scert);
|
|
if (rv != PR_SUCCESS)
|
|
goto create_encrypted_loser;
|
|
if (!SSM_IsAKindOf(&scert->super, SSM_RESTYPE_CERTIFICATE))
|
|
goto create_encrypted_loser;
|
|
|
|
rcerts = (CERTCertificate **) PR_Calloc(request.nrcerts+1, sizeof(CERTCertificate *));
|
|
if (rcerts == NULL)
|
|
goto create_encrypted_loser;
|
|
|
|
for(i = 0; i < request.nrcerts; i++) {
|
|
rv = SSMControlConnection_GetResource(ctrl, request.rcertRIDs[i],
|
|
(SSMResource **)&rcert);
|
|
if (rv != PR_SUCCESS)
|
|
goto create_encrypted_loser;
|
|
if (!SSM_IsAKindOf(&rcert->super, SSM_RESTYPE_CERTIFICATE))
|
|
goto create_encrypted_loser;
|
|
rcerts[i] = CERT_DupCertificate(rcert->cert);
|
|
SSM_FreeResource(&rcert->super);
|
|
}
|
|
|
|
cinfo = SECMIME_CreateEncrypted(scert->cert, rcerts, ctrl->m_certdb,
|
|
NULL, NULL);
|
|
if (cinfo == NULL)
|
|
goto create_encrypted_loser;
|
|
rv = SSM_CreateResource(SSM_RESTYPE_PKCS7_CONTENT_INFO, cinfo,
|
|
ctrl, &ciRID, (SSMResource **)&ci);
|
|
if (rv != PR_SUCCESS)
|
|
goto create_encrypted_loser;
|
|
|
|
/* Get a client reference */
|
|
rv = SSM_ClientGetResourceReference(&ci->super, &ciRID);
|
|
if (rv != PR_SUCCESS) {
|
|
goto create_encrypted_loser;
|
|
}
|
|
|
|
msg->type = (SECItemType) (SSM_OBJECT_SIGNING | SSM_CREATE_ENCRYPTED |
|
|
SSM_REPLY_OK_MESSAGE);
|
|
reply.ciRID = ciRID;
|
|
reply.result = PR_SUCCESS;
|
|
CMT_EncodeMessage(CreateContentInfoReplyTemplate, (CMTItem*)msg, &reply);
|
|
break;
|
|
|
|
/*
|
|
goto create_encrypted_done;
|
|
*/
|
|
|
|
create_encrypted_loser:
|
|
msg->type = (SECItemType) (SSM_OBJECT_SIGNING | SSM_CREATE_ENCRYPTED |
|
|
SSM_REPLY_ERR_MESSAGE);
|
|
reply.ciRID = 0;
|
|
reply.result = PR_FAILURE;
|
|
CMT_EncodeMessage(CreateContentInfoReplyTemplate, (CMTItem*)msg, &reply);
|
|
|
|
/*
|
|
create_encrypted_done:
|
|
*/
|
|
if (rcerts != NULL) {
|
|
for(i = 0; i < request.nrcerts; i++) {
|
|
if (rcerts[i] != NULL)
|
|
CERT_DestroyCertificate(rcerts[i]);
|
|
}
|
|
PR_Free(rcerts);
|
|
}
|
|
}
|
|
return PR_FAILURE;
|
|
|
|
default:
|
|
{
|
|
SingleNumMessage reply;
|
|
|
|
SSM_DEBUG("Unknown signing-related request (%lx).\n", msg->type & SSM_SUBTYPE_MASK);
|
|
reply.value = SSM_ERR_BAD_REQUEST;
|
|
CMT_EncodeMessage(SingleNumMessageTemplate, (CMTItem*)msg, &reply);
|
|
}
|
|
break;
|
|
}
|
|
return PR_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* Called by the ReadCtrlThread, processes messages for control connection
|
|
* and places response messages in the queue
|
|
*/
|
|
SSMStatus SSMControlConnection_ProcessMessage(SSMControlConnection * ctrl, SECItem * msg)
|
|
{
|
|
SSMStatus rv = PR_FAILURE;
|
|
SECStatus secstatus = SECFailure;
|
|
|
|
SSM_DEBUG("SSMControlConnection_ProcessMessage called.\n");
|
|
if (!ctrl || !msg)
|
|
{
|
|
PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
|
|
goto loser;
|
|
}
|
|
|
|
if ((msg->type & SSM_CATEGORY_MASK)!= SSM_REQUEST_MESSAGE)
|
|
{
|
|
/* post CARTMAN_ERROR here */
|
|
goto loser;
|
|
}
|
|
|
|
/* action depending on the message type and data */
|
|
switch (msg->type & SSM_TYPE_MASK)
|
|
{
|
|
case SSM_HELLO_MESSAGE:
|
|
SSM_DEBUG("we have a Hello request.\n");
|
|
rv = SSMControlConnection_ProcessHello(ctrl, msg);
|
|
if (rv != PR_SUCCESS) goto hello_loser;
|
|
break;
|
|
|
|
case SSM_DATA_CONNECTION:
|
|
/* differentiate between different data connection types */
|
|
SSM_DEBUG("we have a data connection request.\n");
|
|
rv = SSMControlConnection_ProcessDataRequest(ctrl,msg);
|
|
break;
|
|
|
|
case SSM_RESOURCE_ACTION:
|
|
rv = SSMControlConnection_ProcessResourceRequest(ctrl,msg);
|
|
break;
|
|
case SSM_CERT_ACTION:
|
|
rv = SSMControlConnection_ProcessCertRequest(ctrl, msg);
|
|
break;
|
|
case SSM_KEYGEN_TAG:
|
|
rv = SSMControlConnection_ProcessKeygenTag(ctrl, msg);
|
|
break;
|
|
case SSM_OBJECT_SIGNING:
|
|
/* Handle an object signing related message */
|
|
rv = SSMControlConnection_ProcessSigningRequest(ctrl,msg);
|
|
break;
|
|
case SSM_PKCS11_ACTION:
|
|
rv = SSMControlConnection_ProcessPKCS11Request(ctrl, msg);
|
|
break;
|
|
case SSM_LOCALIZED_TEXT:
|
|
rv = SSMControlConnection_ProcessLocalizedTextRequest(ctrl, msg);
|
|
break;
|
|
case SSM_CRMF_ACTION:
|
|
rv = SSMControlConnection_ProcessCRMFRequest(ctrl, msg);
|
|
break;
|
|
case SSM_FORMSIGN_ACTION:
|
|
rv = SSMControlConnection_ProcessFormSigningRequest(ctrl, msg);
|
|
break;
|
|
case SSM_SECURITY_ADVISOR:
|
|
rv = SSMControlConnection_ProcessSecurityAdvsiorRequest(ctrl, msg);
|
|
break;
|
|
case SSM_SEC_CFG_ACTION:
|
|
rv = SSMControlConnection_ProcessSecCfgRequest(ctrl, msg);
|
|
break;
|
|
case SSM_PREF_ACTION:
|
|
rv = SSMControlConnection_ProcessPrefs(ctrl, msg);
|
|
break;
|
|
case SSM_MISC_ACTION:
|
|
rv = SSMControlConnection_ProcessMiscRequest(ctrl, msg);
|
|
break;
|
|
default:
|
|
/* just send it back to the client or post an error
|
|
*/
|
|
SSM_DEBUG("Unknown request type (%lx).\n", msg->type & SSM_TYPE_MASK);
|
|
break;
|
|
}
|
|
if (rv == SSM_ERR_DEFER_RESPONSE) {
|
|
/* If asked to defer response, don't send anything back.
|
|
Just indicate that we did what we needed to do. */
|
|
rv = PR_SUCCESS;
|
|
goto done;
|
|
}
|
|
if (rv != PR_SUCCESS) {
|
|
rv = ssmcontrolconnection_encode_err_reply(msg,rv);
|
|
}
|
|
|
|
hello_loser:
|
|
ssmcontrolconnection_send_message_to_client(ctrl, msg);
|
|
SSMControlConnection_RecycleItem(msg);
|
|
msg = NULL; /* so that exception handling doesn't try to free it */
|
|
|
|
SSM_DEBUG("SSMControlConnection_ProcessMessage returning rv == %ld.\n", rv);
|
|
goto done;
|
|
|
|
loser:
|
|
if (rv == PR_SUCCESS) rv = (SSMStatus) secstatus;
|
|
if (rv == PR_SUCCESS) rv = PR_FAILURE;
|
|
SSM_DEBUG("FAILURE in SSMControlConnection_ProcessMessage, rv = %ld.\n", rv);
|
|
|
|
/* Attempt to pack a generic error message. ### mwelch todo */
|
|
|
|
done:
|
|
if (msg) SSMControlConnection_RecycleItem(msg);
|
|
return rv;
|
|
}
|
|
|
|
SSMStatus
|
|
ssmcontrolconnection_send_message_to_client(SSMControlConnection *ctrl,
|
|
SECItem *msg)
|
|
{
|
|
SSM_DEBUG("queueing reply: type %lx, len %ld.\n", msg->type, msg->len);
|
|
SSM_SendQMessage(ctrl->m_controlOutQ,
|
|
SSM_PRIORITY_NORMAL,
|
|
msg->type, msg->len,
|
|
(char *)msg->data, PR_TRUE);
|
|
|
|
return SSM_SUCCESS;
|
|
}
|
|
|
|
SSMStatus
|
|
ssmcontrolconnection_encode_err_reply(SECItem *msg, SSMStatus rv)
|
|
{
|
|
SingleNumMessage reply;
|
|
|
|
msg->type = (SECItemType) SSM_REPLY_ERR_MESSAGE;
|
|
reply.value = rv;
|
|
CMT_EncodeMessage(SingleNumMessageTemplate, (CMTItem*)msg, &reply);
|
|
if (!msg->data || msg->len == 0){
|
|
rv = SSM_FAILURE;
|
|
} else {
|
|
rv = SSM_SUCCESS;
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
SSMStatus
|
|
SSMControlConnection_Authenticate(SSMConnection *arg, char *nonce)
|
|
{
|
|
SSMStatus rv = SSM_FAILURE;
|
|
SSMControlConnection *ctrl = (SSMControlConnection *) arg;
|
|
|
|
if (!SSM_IsAKindOf(SSMRESOURCE(ctrl), SSM_RESTYPE_CONTROL_CONNECTION))
|
|
goto loser;
|
|
|
|
/* Compare the nonce against what we have. */
|
|
if (!strncmp(ctrl->m_nonce, nonce, strlen(ctrl->m_nonce)))
|
|
rv = SSM_SUCCESS;
|
|
|
|
loser:
|
|
return rv;
|
|
}
|
|
|
|
SSMStatus
|
|
SSMControlConnection_SendUIEvent(SSMControlConnection *conn,
|
|
char *command,
|
|
char *baseRef,
|
|
SSMResource *target, /* can pass NULL */
|
|
char *otherParams /* can pass NULL */,
|
|
CMTItem * clientContext /* can pass NULL */)
|
|
{
|
|
char *url;
|
|
SECItem event;
|
|
SSMStatus rv = PR_SUCCESS;
|
|
PRUint32 width, height;
|
|
SSMResourceID rid = 0;
|
|
UIEvent reply;
|
|
|
|
if (!conn->m_doesUI) {
|
|
return SSM_FAILURE;
|
|
}
|
|
|
|
if ((!target) && (conn))
|
|
target = &conn->super.super;
|
|
PR_ASSERT(target);
|
|
|
|
if (target)
|
|
rid = ((SSMResource *) target)->m_id;
|
|
|
|
/* Construct a URL out of the parameters we've been given. */
|
|
rv = SSM_GenerateURL(conn, command, baseRef, target, otherParams,
|
|
&width, &height, &url);
|
|
if (rv != PR_SUCCESS) {
|
|
goto loser;
|
|
}
|
|
/* Generate the actual message to send to the client. */
|
|
reply.resourceID = rid;
|
|
reply.width = width;
|
|
reply.height = height;
|
|
reply.isModal = CM_TRUE;
|
|
reply.url = url;
|
|
if (clientContext) {
|
|
reply.clientContext = *clientContext;
|
|
} else {
|
|
reply.clientContext.len = 0;
|
|
reply.clientContext.data = NULL;
|
|
}
|
|
if (CMT_EncodeMessage(UIEventTemplate, (CMTItem*)&event, &reply) != CMTSuccess) {
|
|
goto loser;
|
|
}
|
|
|
|
/* Post the message on the outgoing control channel. */
|
|
rv = SSM_SendQMessage(conn->m_controlOutQ, SSM_PRIORITY_NORMAL,
|
|
SSM_EVENT_MESSAGE | SSM_UI_EVENT,
|
|
(int) event.len, (char *) event.data, PR_FALSE);
|
|
if (rv != PR_SUCCESS) goto loser;
|
|
|
|
goto done;
|
|
loser:
|
|
if (rv == PR_SUCCESS) rv = PR_FAILURE;
|
|
done:
|
|
PR_FREEIF(event.data);
|
|
return rv;
|
|
}
|
|
|
|
static SSMStatus SSMControlConnection_SavePref(SSMControlConnection* ctrl,
|
|
char* key, char* value,
|
|
PRIntn type)
|
|
{
|
|
SSMStatus rv = PR_FAILURE;
|
|
SetPrefElement item = {0};
|
|
SetPrefListMessage request = {0};
|
|
CMTItem message = {0};
|
|
|
|
/* pack the request */
|
|
item.key = PL_strdup(key);
|
|
item.type = type;
|
|
if (value != NULL) {
|
|
item.value = PL_strdup(value);
|
|
}
|
|
else if (type == STRING_PREF) {
|
|
item.value = NULL; /* this is legal */
|
|
}
|
|
else {
|
|
goto loser;
|
|
}
|
|
|
|
request.length = 1;
|
|
request.list = &item;
|
|
|
|
message.type = SSM_EVENT_MESSAGE | SSM_SAVE_PREF_EVENT;
|
|
if (CMT_EncodeMessage(SetPrefListMessageTemplate, &message,
|
|
&request) != CMTSuccess) {
|
|
goto loser;
|
|
}
|
|
|
|
/* send the message through the control out queue */
|
|
SSM_SendQMessage(ctrl->m_controlOutQ, SSM_PRIORITY_NORMAL,
|
|
message.type, message.len, (char*)message.data,
|
|
PR_TRUE);
|
|
|
|
rv = PR_SUCCESS;
|
|
loser:
|
|
PR_FREEIF(item.key);
|
|
PR_FREEIF(item.value);
|
|
return rv;
|
|
}
|
|
|
|
SSMStatus SSMControlConnection_SaveStringPref(SSMControlConnection* ctrl,
|
|
char* key, char* value)
|
|
{
|
|
SSMStatus rv = PR_SUCCESS;
|
|
|
|
if ((ctrl == NULL) || (ctrl->m_prefs == NULL) || (key == NULL)) {
|
|
return PR_FAILURE;
|
|
}
|
|
|
|
if (PREF_StringPrefChanged(ctrl->m_prefs, key, value)) {
|
|
rv = PREF_SetStringPref(ctrl->m_prefs, key, value);
|
|
if (rv != PR_SUCCESS) {
|
|
return rv;
|
|
}
|
|
rv = SSMControlConnection_SavePref(ctrl, key, value, STRING_PREF);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
SSMStatus SSMControlConnection_SaveBoolPref(SSMControlConnection* ctrl,
|
|
char* key, PRBool value)
|
|
{
|
|
SSMStatus rv = PR_SUCCESS;
|
|
|
|
if ((ctrl == NULL) || (ctrl->m_prefs == NULL) || (key == NULL)) {
|
|
return PR_FAILURE;
|
|
}
|
|
|
|
if (PREF_BoolPrefChanged(ctrl->m_prefs, key, value)) {
|
|
rv = PREF_SetBoolPref(ctrl->m_prefs, key, value);
|
|
if (rv != PR_SUCCESS) {
|
|
return rv;
|
|
}
|
|
if (value) {
|
|
rv = SSMControlConnection_SavePref(ctrl, key, "true", BOOL_PREF);
|
|
}
|
|
else {
|
|
rv = SSMControlConnection_SavePref(ctrl, key, "false", BOOL_PREF);
|
|
}
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
SSMStatus SSMControlConnection_SaveIntPref(SSMControlConnection* ctrl,
|
|
char* key, PRIntn value)
|
|
{
|
|
SSMStatus rv = PR_SUCCESS;
|
|
|
|
if ((ctrl == NULL) || (ctrl->m_prefs == NULL) || (key == NULL)) {
|
|
return PR_FAILURE;
|
|
}
|
|
|
|
if (PREF_IntPrefChanged(ctrl->m_prefs, key, value)) {
|
|
char* tmp = NULL;
|
|
|
|
rv = PREF_SetIntPref(ctrl->m_prefs, key, value);
|
|
if (rv != PR_SUCCESS) {
|
|
return rv;
|
|
}
|
|
tmp = PR_smprintf("%d", value);
|
|
rv = SSMControlConnection_SavePref(ctrl, key, tmp, INT_PREF);
|
|
PR_FREEIF(tmp);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
extern SSMControlConnection * SSM_PARENT_CONN (SSMConnection * x);
|
|
|
|
void SSM_LockPasswdTable(SSMConnection * conn)
|
|
{
|
|
SSMControlConnection * control = SSM_PARENT_CONN(conn);
|
|
PR_EnterMonitor(control->m_passwdLock);
|
|
}
|
|
|
|
SSMStatus SSM_UnlockPasswdTable(SSMConnection * conn)
|
|
{
|
|
SSMControlConnection * control = SSM_PARENT_CONN(conn);
|
|
return PR_ExitMonitor(control->m_passwdLock);
|
|
}
|
|
|
|
SSMStatus SSM_WaitPasswdTable(SSMConnection * conn)
|
|
{
|
|
SSMControlConnection * control = SSM_PARENT_CONN(conn);
|
|
|
|
return PR_Wait(control->m_passwdLock, SSM_PASSWORD_WAIT_TIME);
|
|
}
|
|
|
|
SSMStatus SSM_NotifyAllPasswdTable(SSMConnection * conn)
|
|
{
|
|
SSMControlConnection * control = SSM_PARENT_CONN(conn);
|
|
return PR_NotifyAll(control->m_passwdLock);
|
|
}
|
|
|
|
/* Find the next unallocated resource ID */
|
|
SSMResourceID
|
|
SSMControlConnection_GenerateResourceID(SSMControlConnection *conn)
|
|
{
|
|
void *junk;
|
|
SSMResourceID rid;
|
|
|
|
rid = conn->m_lastRID;
|
|
do {
|
|
rid++;
|
|
if(rid > SSM_MAX_RID)
|
|
rid = SSM_BASE_RID;
|
|
SSM_HashFind(conn->m_resourceDB, rid, &junk);
|
|
} while(junk != NULL);
|
|
conn->m_lastRID = rid;
|
|
return rid;
|
|
}
|
|
|
|
SSMStatus
|
|
SSM_GetControlConnection(SSMResourceID rid, SSMControlConnection **res)
|
|
{
|
|
return SSM_HashFind(ctrlConnections, rid, (void **)res);
|
|
}
|
|
|
|
SSMStatus
|
|
SSMControlConnection_AddResource(SSMResource *res, SSMResourceID rid)
|
|
{
|
|
SSMStatus rv;
|
|
|
|
rv = SSM_HashInsert(res->m_connection->m_resourceDB, rid, res);
|
|
if (rv != PR_SUCCESS)
|
|
return rv;
|
|
rv = SSM_HashInsert(res->m_connection->m_resourceIdDB, (SSMHashKey)res,
|
|
(void *)rid);
|
|
return rv;
|
|
}
|
|
|
|
SSMStatus
|
|
SSMControlConnection_GetResource(SSMControlConnection * connection,
|
|
SSMResourceID rid, SSMResource **res)
|
|
{
|
|
SSMStatus rv = PR_FAILURE;
|
|
if (connection->m_resourceDB) {
|
|
rv = SSM_HashFind(connection->m_resourceDB, rid, (void **) res);
|
|
if (*res)
|
|
SSM_GetResourceReference(*res);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
SSMStatus
|
|
SSMControlConnection_GetGlobalResourceID(SSMControlConnection * connection,
|
|
SSMResource * res, SSMResourceID *rid)
|
|
{
|
|
return SSM_HashFind(connection->m_resourceIdDB, (SSMHashKey)res,
|
|
(void **) rid);
|
|
}
|
|
|
|
SSMStatus
|
|
SSMControlConnection_FormSubmitHandler(SSMResource * res, HTTPRequest * req)
|
|
{
|
|
SSMStatus rv;
|
|
char* tmpStr = NULL;
|
|
SSMSSLDataConnection* conn;
|
|
|
|
conn = (SSMSSLDataConnection*)res;
|
|
|
|
/* 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_HTTPCloseAndSleep(req);
|
|
if (rv != SSM_SUCCESS)
|
|
SSM_DEBUG("Errors closing window in FormSubmitHandler: %d\n", rv);
|
|
|
|
|
|
if (!res->m_formName)
|
|
goto loser;
|
|
if (PL_strcmp(res->m_formName, "choose_cert_by_usage") == 0)
|
|
rv = SSM_ChooseCertUsageHandler(req);
|
|
else /* other cases where this could be used will go here */
|
|
goto loser;
|
|
return rv;
|
|
loser:
|
|
SSM_DEBUG("FormSubmit handler is called with no valid formName\n");
|
|
SSM_NotifyUIEvent(res);
|
|
return SSM_FAILURE;
|
|
}
|
|
|
|
typedef struct DefaultCertLookupArgStr {
|
|
char *defaultEmailCert;
|
|
SSMControlConnection *conn;
|
|
PRBool isOwnThread;
|
|
CERTCertificate *cert;
|
|
SSMStatus rv;
|
|
} DefaultCertLookupArg;
|
|
|
|
static void
|
|
ssm_lookup_default_cert(void *arg)
|
|
{
|
|
DefaultCertLookupArg *lookupArg = (DefaultCertLookupArg*)arg;
|
|
char *defaultEmailCert = lookupArg->defaultEmailCert;
|
|
SSMControlConnection *conn = lookupArg->conn;
|
|
CERTCertificate *tmp;
|
|
CERTCertificate *cert = lookupArg->cert;
|
|
|
|
|
|
#ifdef DEBUG
|
|
if (lookupArg->isOwnThread) {
|
|
SSM_RegisterThread("email cert lookup", NULL);
|
|
}
|
|
#endif
|
|
SSM_DEBUG("Looking up the cert %s", defaultEmailCert);
|
|
tmp = CERT_FindUserCertByUsage(conn->m_certdb,
|
|
defaultEmailCert,
|
|
certUsageEmailSigner,
|
|
PR_FALSE,
|
|
conn);
|
|
if (tmp == NULL) {
|
|
lookupArg->rv =
|
|
SSMControlConnection_SaveStringPref(conn,
|
|
"security.default_mail_cert",
|
|
cert->nickname);
|
|
|
|
} else {
|
|
CERT_DestroyCertificate(tmp);
|
|
lookupArg->rv = SSM_FAILURE;
|
|
}
|
|
SSM_FreeResource(&conn->super.super);
|
|
if (lookupArg->isOwnThread) {
|
|
PR_Free(arg);
|
|
}
|
|
CERT_DestroyCertificate(cert);
|
|
}
|
|
|
|
SSMStatus
|
|
SSM_UseAsDefaultEmailIfNoneSet(SSMControlConnection *conn,
|
|
CERTCertificate *cert,
|
|
PRBool onFrontEndThread)
|
|
{
|
|
SSMStatus rv = SSM_FAILURE;
|
|
char *defaultEmailCert;
|
|
|
|
if (cert->emailAddr) {
|
|
rv = PREF_GetStringPref(conn->m_prefs, "security.default_mail_cert",
|
|
&defaultEmailCert);
|
|
if (rv != SSM_SUCCESS || defaultEmailCert == NULL) {
|
|
rv = SSMControlConnection_SaveStringPref(conn,
|
|
"security.default_mail_cert",
|
|
cert->nickname);
|
|
} else {
|
|
DefaultCertLookupArg *arg = SSM_ZNEW(DefaultCertLookupArg);
|
|
|
|
SSM_GetResourceReference(&conn->super.super);
|
|
PR_ASSERT(arg);
|
|
if (arg == NULL) {
|
|
goto loser;
|
|
}
|
|
arg->defaultEmailCert = defaultEmailCert;
|
|
arg->conn = conn;
|
|
arg->cert = CERT_DupCertificate(cert);
|
|
if (strchr(defaultEmailCert, ':') != NULL && onFrontEndThread) {
|
|
/* ARGH!! The default email cert is on an external token
|
|
* this may require an authentication event, so in order
|
|
* to prevent dead-locking the front end thread, we'll
|
|
* do the look up on a separate thread.
|
|
*/
|
|
arg->isOwnThread = PR_TRUE;
|
|
SSM_CreateAndRegisterThread(PR_USER_THREAD, ssm_lookup_default_cert,
|
|
(void *)arg,
|
|
PR_PRIORITY_NORMAL, PR_LOCAL_THREAD,
|
|
PR_UNJOINABLE_THREAD, 0);
|
|
/*
|
|
* We don't know if the cert was made default, so we'll return
|
|
* failure in this case.
|
|
*/
|
|
rv = SSM_FAILURE;
|
|
} else {
|
|
/*
|
|
* It's in the internal database, so just lookup
|
|
* on this same thread.
|
|
*/
|
|
arg->isOwnThread = PR_FALSE;
|
|
ssm_lookup_default_cert((void*)arg);
|
|
rv = arg->rv;
|
|
PR_Free(arg);
|
|
}
|
|
|
|
}
|
|
}
|
|
return rv;
|
|
loser:
|
|
return SSM_FAILURE;
|
|
}
|