gecko-dev/security/psm/server/main.c

659 строки
18 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 "serv.h"
#include "secport.h"
#include "dataconn.h"
#include "ctrlconn.h"
#include "minihttp.h"
#include "ciferfam.h"
#include "secmime.h"
#include "messages.h"
#include "textgen.h"
#include "oldfunc.h"
#include "nss.h"
#include "p12plcy.h"
#include "nlslayer.h"
#include "softoken.h"
void SSM_InitLogging(void);
#ifdef TIMEBOMB
#include "timebomb.h"
#endif
#ifdef WIN32
#include <wtypes.h>
#include <winreg.h>
#include <winerror.h>
#endif
#ifdef XP_UNIX
#include <signal.h>
#include <unistd.h>
#ifndef SIG_ERR
#define SIG_ERR -1
#endif /*SIG_ERR*/
#endif /*XP_UNIX*/
#define POLICY_TYPE_INDEX 0
SSMCollection * connections = NULL;
SSMHashTable * tokenList = NULL;
PRMonitor * tokenLock = NULL;
SSMHashTable * ctrlConnections = NULL;
SSMPolicyType policyType = ssmDomestic;
/*
* The following is a quick write of enabling various ciphers. This code is
* essentially moved from the server core code. Eventually we will need to
* place this data and functionality in a more modular way.
*/
#define SSM_POLICY_END 0 /* end of table */
#define SSM_POLICY_SSL 1 /* SSL ciphersuites: not really used */
#define SSM_POLICY_PK12 2 /* PKCS #12 ciphersuites */
#define SSM_POLICY_SMIME 3 /* S/MIME ciphersuites */
typedef struct {
PRInt32 policy;
PRInt32 key;
PRInt32 value;
} SSMPolicyEntry;
static SSMPolicyEntry ssmPK12PolicyTable[] =
{
{SSM_POLICY_PK12, PKCS12_RC4_40, (PRInt32)PR_TRUE},
{SSM_POLICY_PK12, PKCS12_RC4_128, (PRInt32)PR_TRUE},
{SSM_POLICY_PK12, PKCS12_RC2_CBC_40, (PRInt32)PR_TRUE},
{SSM_POLICY_PK12, PKCS12_RC2_CBC_128, (PRInt32)PR_TRUE},
{SSM_POLICY_PK12, PKCS12_DES_56, (PRInt32)PR_TRUE},
{SSM_POLICY_PK12, PKCS12_DES_EDE3_168, (PRInt32)PR_TRUE},
{SSM_POLICY_END, 0, 0}
};
static SSMPolicyEntry ssmSMIMEPolicyTable[] =
{
{SSM_POLICY_SMIME, SMIME_RC2_CBC_40, (PRInt32)PR_TRUE},
{SSM_POLICY_SMIME, SMIME_RC2_CBC_64, (PRInt32)PR_TRUE},
{SSM_POLICY_SMIME, SMIME_RC2_CBC_128, (PRInt32)PR_TRUE},
{SSM_POLICY_SMIME, SMIME_DES_CBC_56, (PRInt32)PR_TRUE},
{SSM_POLICY_SMIME, SMIME_DES_EDE3_168, (PRInt32)PR_TRUE},
{SSM_POLICY_SMIME, SMIME_RC5PAD_64_16_40, (PRInt32)PR_TRUE},
{SSM_POLICY_SMIME, SMIME_RC5PAD_64_16_64, (PRInt32)PR_TRUE},
{SSM_POLICY_SMIME, SMIME_RC5PAD_64_16_128, (PRInt32)PR_TRUE},
{SSM_POLICY_END, 0, 0}
};
static SSMStatus SSM_InstallPK12Policy(void)
{
const SSMPolicyEntry* entry = ssmPK12PolicyTable;
int i;
for (i = 0; entry[i].policy != SSM_POLICY_END; i++) {
if (SEC_PKCS12EnableCipher(entry[i].key, entry[i].value) !=
SECSuccess) {
return SSM_FAILURE;
}
}
return SSM_SUCCESS;
}
static SSMStatus SSM_InstallSMIMEPolicy(void)
{
const SSMPolicyEntry* entry = ssmSMIMEPolicyTable;
int i;
for (i = 0; entry[i].policy != SSM_POLICY_END; i++) {
if (SECMIME_SetPolicy(entry[i].key, entry[i].value) != SECSuccess) {
return SSM_FAILURE;
}
}
return SSM_SUCCESS;
}
/* XXX sjlee: we don't need to do a similar thing for SSL as we can call an
* NSS function to do it
*/
#if 0
/*
* This function is required by svrplcy to set the
* utility policy. This will tell us what kind of
* policy we are running.
*/
SECStatus Utility_SetPolicy(long which, int policy)
{
policyType = ssmDomestic;
return SECSuccess;
}
#endif
void SSM_SetPolicy(void)
{
#if 0
SVRPLCY_InstallSSLPolicy();
SVRPLCY_InstallPK12Policy();
SVRPLCY_InstallSMIMEPolicy();
SVRPLCY_InstallUtilityPolicy();
#else
/* Always domestic policy now */
NSS_SetDomesticPolicy();
SSM_InstallPK12Policy();
SSM_InstallSMIMEPolicy();
#endif
}
SSMPolicyType
SSM_GetPolicy(void)
{
return policyType;
}
static void
enable_SMIME_cipher_prefs(void)
{
SSMPolicyType policy;
policy = SSM_GetPolicy();
switch (policy)
{
case ssmDomestic:
SECMIME_EnableCipher(SMIME_DES_EDE3_168, 1);
SECMIME_EnableCipher(SMIME_RC2_CBC_128, 1);
SECMIME_EnableCipher(SMIME_RC2_CBC_64, 1);
SECMIME_EnableCipher(SMIME_DES_CBC_56, 1);
#if 0
SECMIME_EnableCipher(SMIME_RC5PAD_64_16_128, 1);
SECMIME_EnableCipher(SMIME_RC5PAD_64_16_64, 1);
SECMIME_EnableCipher(SMIME_FORTEZZA, 1);
#endif
case ssmExport:
SECMIME_EnableCipher(SMIME_RC2_CBC_40, 1);
#if 0
SECMIME_EnableCipher(SMIME_RC5PAD_64_16_40, 1);
#endif
case ssmFrance:
default:
break;
}
/* now tell secmime that we've sent it the last preference */
SECMIME_EnableCipher(CIPHER_FAMILYID_MASK, 0);
}
#define SHORT_PK11_STRING 33
#define LONG_PK11_STRING 65
static char*
ssm_ConverToLength(char *origString, PRUint32 newLen)
{
char *newString;
PRUint32 origLen;
PRUint32 copyLen;
newString = SSM_NEW_ARRAY(char,newLen+1);
if (newString == NULL) {
return origString;
}
origLen = PL_strlen(origString);
copyLen = (origLen > newLen) ? newLen : origLen;
memcpy(newString, origString, copyLen);
memset(newString+copyLen, ' ' ,newLen - copyLen);
newString[newLen]='\0';
PR_Free(origString);
return newString;
}
SECStatus
ssm_InitializePKCS11Strings(void)
{
char *manufacturerID = NULL;
char *libraryDescription = NULL;
char *tokenDescription = NULL;
char *privateTokenDescription = NULL;
char *slotDescription = NULL;
char *privateSlotDescription = NULL;
char *fipsSlotDescription = NULL;
char *fipsPrivateSlotDescription = NULL;
SSMTextGenContext *cx;
SSMStatus rv;
rv = SSMTextGen_NewTopLevelContext(NULL, &cx);
if (rv != SSM_SUCCESS) {
goto loser;
}
rv = SSM_FindUTF8StringInBundles(cx, "manufacturerID", &manufacturerID);
if (rv != SSM_SUCCESS) {
goto loser;
}
if (PL_strlen(manufacturerID) != SHORT_PK11_STRING) {
manufacturerID = ssm_ConverToLength(manufacturerID, SHORT_PK11_STRING);
}
rv = SSM_FindUTF8StringInBundles(cx, "libraryDescription",
&libraryDescription);
if (rv != SSM_SUCCESS) {
goto loser;
}
if (PL_strlen(libraryDescription) != SHORT_PK11_STRING) {
libraryDescription = ssm_ConverToLength(libraryDescription,
SHORT_PK11_STRING);
}
rv = SSM_FindUTF8StringInBundles(cx, "tokenDescription",
&tokenDescription);
if (rv != SSM_SUCCESS) {
goto loser;
}
if (PL_strlen(tokenDescription) != SHORT_PK11_STRING) {
tokenDescription = ssm_ConverToLength(tokenDescription,
SHORT_PK11_STRING);
}
rv = SSM_FindUTF8StringInBundles(cx, "privateTokenDescription",
&privateTokenDescription);
if (rv != SSM_SUCCESS) {
goto loser;
}
if (PL_strlen(privateTokenDescription) != SHORT_PK11_STRING) {
privateTokenDescription = ssm_ConverToLength(privateTokenDescription,
SHORT_PK11_STRING);
}
rv = SSM_FindUTF8StringInBundles(cx, "slotDescription", &slotDescription);
if (rv != SSM_SUCCESS) {
goto loser;
}
if (PL_strlen(slotDescription) != LONG_PK11_STRING) {
slotDescription = ssm_ConverToLength(slotDescription,
LONG_PK11_STRING);
}
rv = SSM_FindUTF8StringInBundles(cx, "privateSlotDescription",
&privateSlotDescription);
if (rv != SSM_SUCCESS) {
goto loser;
}
if (PL_strlen(privateSlotDescription) != LONG_PK11_STRING) {
privateSlotDescription = ssm_ConverToLength(privateSlotDescription,
LONG_PK11_STRING);
}
rv = SSM_FindUTF8StringInBundles(cx, "fipsSlotDescription",
&fipsSlotDescription);
if (rv != SSM_SUCCESS) {
goto loser;
}
if (PL_strlen(fipsSlotDescription) != LONG_PK11_STRING) {
fipsSlotDescription = ssm_ConverToLength(fipsSlotDescription,
LONG_PK11_STRING);
}
rv = SSM_FindUTF8StringInBundles(cx, "fipsPrivateSlotDescription",
&fipsPrivateSlotDescription);
if (rv != SSM_SUCCESS) {
goto loser;
}
if (PL_strlen(fipsPrivateSlotDescription) != LONG_PK11_STRING) {
fipsPrivateSlotDescription =
ssm_ConverToLength(fipsPrivateSlotDescription, LONG_PK11_STRING);
}
if (cx != NULL) {
SSMTextGen_DestroyContext(cx);
}
PK11_ConfigurePKCS11(manufacturerID, libraryDescription, tokenDescription,
privateTokenDescription, slotDescription,
privateSlotDescription, fipsSlotDescription,
fipsPrivateSlotDescription, 0, 0);
return SECSuccess;
loser:
PR_FREEIF(manufacturerID);
PR_FREEIF(libraryDescription);
PR_FREEIF(tokenDescription);
PR_FREEIF(privateTokenDescription);
PR_FREEIF(slotDescription);
PR_FREEIF(privateSlotDescription);
if (cx != NULL) {
SSMTextGen_DestroyContext(cx);
}
return SECFailure;
}
#ifdef XP_UNIX
#define CATCH_SIGNAL_DEFAULT(SIGNAL) \
if (((int)signal(SIGNAL, psm_signal_handler_default)) == ((int)SIG_ERR)) \
goto loser;
#define CATCH_SIGNAL_IGNORE(SIGNAL) \
if (((int)signal(SIGNAL, psm_signal_handler_ignore)) == ((int)SIG_ERR)) \
goto loser;
static void
psm_signal_handler_default(int sig)
{
#ifdef DEBUG
printf ("Trapping the signal %d\n", sig);
#endif
SSM_ReleaseLockFile();
kill(getpid(),SIGKILL);
}
static void
psm_signal_handler_ignore(int sig)
{
#ifdef DEBUG
printf ("Ignoring the signal %d\n", sig);
#endif
signal(sig,psm_signal_handler_ignore);
}
static SSMStatus
psm_catch_signals(void)
{
CATCH_SIGNAL_IGNORE(SIGHUP);
CATCH_SIGNAL_DEFAULT(SIGINT);
CATCH_SIGNAL_DEFAULT(SIGQUIT);
CATCH_SIGNAL_DEFAULT(SIGILL);
CATCH_SIGNAL_DEFAULT(SIGTRAP);
CATCH_SIGNAL_DEFAULT(SIGABRT);
CATCH_SIGNAL_DEFAULT(SIGIOT);
#ifdef SIGEMT
CATCH_SIGNAL_DEFAULT(SIGEMT);
#endif
CATCH_SIGNAL_DEFAULT(SIGFPE);
CATCH_SIGNAL_DEFAULT(SIGBUS);
CATCH_SIGNAL_DEFAULT(SIGSEGV);
#ifdef SIGSYS
CATCH_SIGNAL_DEFAULT(SIGSYS);
#endif
CATCH_SIGNAL_IGNORE(SIGPIPE);
CATCH_SIGNAL_DEFAULT(SIGTERM);
#ifndef LINUX
CATCH_SIGNAL_DEFAULT(SIGUSR1);
#endif
CATCH_SIGNAL_DEFAULT(SIGUSR2);
#ifdef SIGXCPU
CATCH_SIGNAL_DEFAULT(SIGXCPU);
#endif
#ifdef SIGDANGER
CATCH_SIGNAL_DEFAULT(SIGDANGER);
#endif
return SSM_SUCCESS;
loser:
return SSM_FAILURE;
}
#endif
#if defined(XP_PC) && !defined(DEBUG)
int PASCAL WinMain(HINSTANCE hInst, HINSTANCE hPrevInst,
LPSTR lpszLine, int nShow)
#elif defined(XP_MAC)
/*
We run RunMacPSM in a separate thread off the main thread.
This is because we can't do any blocking I/O routines on the main
thread, because Mac NSPR doesn't own the original thread used to
run the app.
*/
static void* glue_component = NULL;
void RunMacPSM(void *arg)
#else
int main(int argc, char ** argv)
#endif
{
#if (defined(XP_PC) && !defined(DEBUG)) || (defined(XP_MAC))
/* substitute argc and argv for NSPR */
int argc = 0;
char *argv[] = {"", NULL};
#endif
PRIntn result = 0;
#ifdef XP_MAC
glue_component = arg;
#endif
#ifdef DEBUG
PR_STDIO_INIT();
#endif
#ifdef DEBUG
/* Initialize logging. */
SSM_InitLogging();
#endif
#ifdef TIMEBOMB
SSM_CheckTimebomb();
#endif
#ifdef XP_UNIX
if (psm_catch_signals() != SSM_SUCCESS) {
SSM_DEBUG("Couldn't set signal handlers. Quitting\n");
exit(1);
}
#endif
SSM_SetPolicy();
enable_SMIME_cipher_prefs();
if (SSM_GetPolicy() == ssmDomestic) {
SSM_EnableHighGradeKeyGen();
}
/* Initialize NLS layer */
if (nlsInit() != PR_TRUE) {
SSM_DEBUG("Failed to initialize the NLS layer\n");
exit(1);
}
/* Initialize global list of control connections. */
connections = SSM_NewCollection();
if (connections == NULL)
{
SSM_DEBUG("Can't initialize! (%ld)\n", (long) result);
exit(1);
}
/* Initialize global list of tokens */
result = SSM_HashCreate(&tokenList);
if (result != PR_SUCCESS || !tokenList) {
SSM_DEBUG("Can't initialize - tokenList \n");
exit(result);
}
tokenLock = PR_NewMonitor();
if (!tokenLock) {
SSM_DEBUG("Can't initialize - tokenLock\n");
exit(1);
}
/* Initialize hash table of control connections */
result = SSM_HashCreate(&ctrlConnections);
if (result != PR_SUCCESS || !ctrlConnections) {
SSM_DEBUG("Can't initialize global table for control connections \n");
exit(result);
}
/* Initialize resource table */
SSM_ResourceInit();
if (SSM_InitPolicyHandler() != PR_SUCCESS) {
SSM_DEBUG("Couldn't initialize the Policy Handler.\n");
exit (1);
}
/* initialize random number generator */
SSM_DEBUG("Initializing random number generator.\n");
RNG_RNGInit();
RNG_SystemInfoForRNG();
/*
* All the ciphers except SSL_RSA_WITH_NULL_MD5 are on by default.
* Enable encryption, enable NULL cipher.
*/
#ifdef XP_MAC
result = mainLoop(argc, argv);
#else
result = PR_Initialize(mainLoop, argc, argv, 0);
#endif
#ifdef DEBUG
printf("main: Finishing (%ld)\n", (long) result);
#endif
#ifdef XP_UNIX
SSM_ReleaseLockFile();
#endif
#ifndef XP_MAC
return result;
#endif
}
#ifdef XP_MAC
void *
psm_malloc(unsigned long numbytes)
{
return NewPtrClear(numbytes);
}
void
psm_free(void *ptr)
{
DisposePtr((char *) ptr);
}
#endif
PRIntn mainLoop(PRIntn argc, char ** argv)
{
PRFileDesc *socket, *respsocket;
PRNetAddr clientaddr;
SSMControlConnection *curconnect;
SSMResourceID ctrlID;
SSMStatus status = PR_FAILURE;
PRBool alive = PR_TRUE;
/* Register ourselves so that logs, etc can identify us */
SSM_RegisterThread("main", NULL);
/* Open NLS stuff */
SSM_DEBUG("Initializing NLS.\n");
#ifdef XP_MAC
SSM_InitNLS(":ui:");
#else
SSM_InitNLS("ui");
#endif
ssm_InitializePKCS11Strings();
/* Initialize the protocol */
#ifdef XP_MAC
CMT_Init(malloc, free);
#else
CMT_Init(PR_Malloc, PR_Free);
#endif
/* Open the HTTP listener */
SSM_DEBUG("Opening HTTP thread.\n");
status = SSM_InitHTTP();
if (status != SSM_SUCCESS)
{
SSM_DEBUG("Couldn't open web port. Exiting.\n");
goto loser;
}
/* open a port for control connections, with well-known port# */
socket = SSM_OpenControlPort();
if (!socket)
{
SSM_DEBUG("Couldn't open control port. Exiting.\n");
goto loser;
}
#ifdef XP_MAC
if (PR_CEnterMonitor(glue_component) != NULL) {
PR_CNotify(glue_component);
PR_CExitMonitor(glue_component);
}
#endif
while (alive)
{
/* wait until there is incoming request */
respsocket = PR_Accept(socket, &clientaddr, PR_INTERVAL_NO_TIMEOUT);
/* while (respsocket == NULL)
{
if (PR_GetError() != PR_WOULD_BLOCK_ERROR)
goto loser;
PR_Sleep(CARTMAN_SPINTIME);
#ifdef DEBUG
printf("master: Still ready for client connection on port %d\n", CARTMAN_PORT); fflush(stdout);
#endif
respsocket = PR_Accept(socket, &clientaddr,
PR_SecondsToInterval(2));
} */
if (!respsocket) {
/* accept failed: abort */
status = PR_GetError();
SSM_DEBUG("Error %d accepting control connection. Exiting.\n",
status);
goto loser;
}
if (SSM_SocketPeerCheck(respsocket, PR_TRUE)) {
SSM_DEBUG("creating control connection.\n");
status = SSM_CreateResource(SSM_RESTYPE_CONTROL_CONNECTION,
respsocket,
NULL,
&ctrlID,
(SSMResource **) &curconnect);
if (status != PR_SUCCESS)
break;
PR_ASSERT(RESOURCE_CLASS(curconnect) == SSM_RESTYPE_CONTROL_CONNECTION);
}
else {
/* connection did not come from localhost: shut down the
* connection and continue to loop
*/
SSM_DEBUG("Connection attempt from a non-local host!\n");
status = PR_Shutdown(respsocket, PR_SHUTDOWN_BOTH);
respsocket = NULL;
}
} /* end while(true) */
loser:
/* Shut down the HTTP thread. */
if (httpListenThread != NULL){
PR_Interrupt(httpListenThread);
}
exit(1);
/* ### mwelch - should we return meaningful error code? */
return 0;
}