/* -*- 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; }