diff options
Diffstat (limited to 'security/nss/cmd/selfserv/selfserv.c')
-rw-r--r-- | security/nss/cmd/selfserv/selfserv.c | 1712 |
1 files changed, 1712 insertions, 0 deletions
diff --git a/security/nss/cmd/selfserv/selfserv.c b/security/nss/cmd/selfserv/selfserv.c new file mode 100644 index 000000000..ea0797073 --- /dev/null +++ b/security/nss/cmd/selfserv/selfserv.c @@ -0,0 +1,1712 @@ +/* + * 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. + */ + +/* -r flag is interepreted as follows: + * 1 -r means request, not require, on initial handshake. + * 2 -r's mean request and require, on initial handshake. + * 3 -r's mean request, not require, on second handshake. + * 4 -r's mean request and require, on second handshake. + */ +#include <stdio.h> +#include <string.h> + +#include "secutil.h" + +#if defined(XP_UNIX) +#include <unistd.h> +#endif + +#if defined(_WINDOWS) +#include <process.h> /* for getpid() */ +#endif + +#ifdef XP_OS2_VACPP +#include <Process.h> /* for getpid() */ +#endif + +#include <stdlib.h> +#include <errno.h> +#include <fcntl.h> +#include <stdarg.h> + +#include "nspr.h" +#include "prio.h" +#include "prerror.h" +#include "prnetdb.h" +#include "prclist.h" +#include "plgetopt.h" +#include "pk11func.h" +#include "secitem.h" +#include "nss.h" +#include "ssl.h" +#include "sslproto.h" + +#ifndef PORT_Sprintf +#define PORT_Sprintf sprintf +#endif + +#ifndef PORT_Strstr +#define PORT_Strstr strstr +#endif + +#ifndef PORT_Malloc +#define PORT_Malloc PR_Malloc +#endif + +#define NUM_SID_CACHE_ENTRIES 1024 + +static int handle_connection( PRFileDesc *, PRFileDesc *, int ); + +static const char envVarName[] = { SSL_ENV_VAR_NAME }; +static const char inheritableSockName[] = { "SELFSERV_LISTEN_SOCKET" }; + +static PRBool logStats = PR_FALSE; +static int logPeriod = 30; +static PRUint32 loggerOps = 0; + +const int ssl2CipherSuites[] = { + SSL_EN_RC4_128_WITH_MD5, /* A */ + SSL_EN_RC4_128_EXPORT40_WITH_MD5, /* B */ + SSL_EN_RC2_128_CBC_WITH_MD5, /* C */ + SSL_EN_RC2_128_CBC_EXPORT40_WITH_MD5, /* D */ + SSL_EN_DES_64_CBC_WITH_MD5, /* E */ + SSL_EN_DES_192_EDE3_CBC_WITH_MD5, /* F */ + 0 +}; + +const int ssl3CipherSuites[] = { + SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA, /* a */ + SSL_FORTEZZA_DMS_WITH_RC4_128_SHA, /* b */ + SSL_RSA_WITH_RC4_128_MD5, /* c */ + SSL_RSA_WITH_3DES_EDE_CBC_SHA, /* d */ + SSL_RSA_WITH_DES_CBC_SHA, /* e */ + SSL_RSA_EXPORT_WITH_RC4_40_MD5, /* f */ + SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5, /* g */ + SSL_FORTEZZA_DMS_WITH_NULL_SHA, /* h */ + SSL_RSA_WITH_NULL_MD5, /* i */ + SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA, /* j */ + SSL_RSA_FIPS_WITH_DES_CBC_SHA, /* k */ + TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA, /* l */ + TLS_RSA_EXPORT1024_WITH_RC4_56_SHA, /* m */ + SSL_RSA_WITH_RC4_128_SHA, /* n */ + TLS_DHE_DSS_WITH_RC4_128_SHA, /* o */ + SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA, /* p */ + SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA, /* q */ + SSL_DHE_RSA_WITH_DES_CBC_SHA, /* r */ + SSL_DHE_DSS_WITH_DES_CBC_SHA, /* s */ + TLS_DHE_DSS_WITH_AES_128_CBC_SHA, /* t */ + TLS_DHE_RSA_WITH_AES_128_CBC_SHA, /* u */ + TLS_RSA_WITH_AES_128_CBC_SHA, /* v */ + TLS_DHE_DSS_WITH_AES_256_CBC_SHA, /* w */ + TLS_DHE_RSA_WITH_AES_256_CBC_SHA, /* x */ + TLS_RSA_WITH_AES_256_CBC_SHA, /* y */ + SSL_RSA_WITH_NULL_SHA, /* z */ + 0 +}; + +/* data and structures for shutdown */ +static int stopping; + +static PRBool noDelay; +static int requestCert; +static int verbose; +static SECItem bigBuf; + +static PRThread * acceptorThread; + +static PRLogModuleInfo *lm; + +/* Add custom password handler because SECU_GetModulePassword + * makes automation of this program next to impossible. + */ + +char * +ownPasswd(PK11SlotInfo *info, PRBool retry, void *arg) +{ + char * passwd = NULL; + + if ( (!retry) && arg ) { + passwd = PL_strdup((char *)arg); + } + + return passwd; +} + +#define PRINTF if (verbose) printf +#define FPRINTF if (verbose) fprintf +#define FLUSH if (verbose) { fflush(stdout); fflush(stderr); } +#define VLOG(arg) PR_LOG(lm,PR_LOG_DEBUG,arg) + +static void +Usage(const char *progName) +{ + fprintf(stderr, + +"Usage: %s -n rsa_nickname -p port [-3DRTbmrvx] [-w password] [-t threads]\n" +" [-i pid_file] [-c ciphers] [-d dbdir] [-f fortezza_nickname] \n" +" [-M maxProcs] [-l]\n" +"-3 means disable SSL v3\n" +"-D means disable Nagle delays in TCP\n" +"-T means disable TLS\n" +"-R means disable detection of rollback from TLS to SSL3\n" +"-b means try binding to the port and exit\n" +"-m means test the model-socket feature of SSL_ImportFD.\n" +"-r flag is interepreted as follows:\n" +" 1 -r means request, not require, cert on initial handshake.\n" +" 2 -r's mean request and require, cert on initial handshake.\n" +" 3 -r's mean request, not require, cert on second handshake.\n" +" 4 -r's mean request and require, cert on second handshake.\n" +"-v means verbose output\n" +"-x means use export policy.\n" +"-M maxProcs tells how many processes to run in a multi-process server\n" +"-t threads -- specify the number of threads to use for connections.\n" +"-i pid_file file to write the process id of selfserve\n" +"-c ciphers Letter(s) chosen from the following list\n" +"-l means use local threads instead of global threads\n" +"A SSL2 RC4 128 WITH MD5\n" +"B SSL2 RC4 128 EXPORT40 WITH MD5\n" +"C SSL2 RC2 128 CBC WITH MD5\n" +"D SSL2 RC2 128 CBC EXPORT40 WITH MD5\n" +"E SSL2 DES 64 CBC WITH MD5\n" +"F SSL2 DES 192 EDE3 CBC WITH MD5\n" +"\n" +"a SSL3 FORTEZZA DMS WITH FORTEZZA CBC SHA\n" +"b SSL3 FORTEZZA DMS WITH RC4 128 SHA\n" +"c SSL3 RSA WITH RC4 128 MD5\n" +"d SSL3 RSA WITH 3DES EDE CBC SHA\n" +"e SSL3 RSA WITH DES CBC SHA\n" +"f SSL3 RSA EXPORT WITH RC4 40 MD5\n" +"g SSL3 RSA EXPORT WITH RC2 CBC 40 MD5\n" +"h SSL3 FORTEZZA DMS WITH NULL SHA\n" +"i SSL3 RSA WITH NULL MD5\n" +"j SSL3 RSA FIPS WITH 3DES EDE CBC SHA\n" +"k SSL3 RSA FIPS WITH DES CBC SHA\n" +"l SSL3 RSA EXPORT WITH DES CBC SHA\t(new)\n" +"m SSL3 RSA EXPORT WITH RC4 56 SHA\t(new)\n" +"n SSL3 RSA WITH RC4 128 SHA\n" +"v SSL3 RSA WITH AES 128 CBC SHA\n" +"y SSL3 RSA WITH AES 256 CBC SHA\n" +"z SSL3 RSA WITH NULL SHA\n" + ,progName); +} + +static const char * +errWarn(char * funcString) +{ + PRErrorCode perr = PR_GetError(); + const char * errString = SECU_Strerror(perr); + + fprintf(stderr, "selfserv: %s returned error %d:\n%s\n", + funcString, perr, errString); + return errString; +} + +static void +errExit(char * funcString) +{ + errWarn(funcString); + exit(3); +} + + +/************************************************************************** +** +** Routines for disabling SSL ciphers. +** +**************************************************************************/ + +void +disableAllSSLCiphers(void) +{ + const PRUint16 *cipherSuites = SSL_ImplementedCiphers; + int i = SSL_NumImplementedCiphers; + SECStatus rv; + + /* disable all the SSL3 cipher suites */ + while (--i >= 0) { + PRUint16 suite = cipherSuites[i]; + rv = SSL_CipherPrefSetDefault(suite, PR_FALSE); + if (rv != SECSuccess) { + printf("SSL_CipherPrefSetDefault didn't like value 0x%04x (i = %d)\n", + suite, i); + errWarn("SSL_CipherPrefSetDefault"); + exit(2); + } + } +} + +static SECStatus +mySSLAuthCertificate(void *arg, PRFileDesc *fd, PRBool checkSig, + PRBool isServer) +{ + SECStatus rv; + CERTCertificate * peerCert; + + peerCert = SSL_PeerCertificate(fd); + + PRINTF("selfserv: Subject: %s\nselfserv: Issuer : %s\n", + peerCert->subjectName, peerCert->issuerName); + + rv = SSL_AuthCertificate(arg, fd, checkSig, isServer); + + if (rv == SECSuccess) { + PRINTF("selfserv: -- SSL3: Certificate Validated.\n"); + } else { + int err = PR_GetError(); + FPRINTF(stderr, "selfserv: -- SSL3: Certificate Invalid, err %d.\n%s\n", + err, SECU_Strerror(err)); + } + CERT_DestroyCertificate(peerCert); + FLUSH; + return rv; +} + +void +printSecurityInfo(PRFileDesc *fd) +{ + CERTCertificate * cert = NULL; + SSL3Statistics * ssl3stats = SSL_GetStatistics(); + SECStatus result; + SSLChannelInfo channel; + SSLCipherSuiteInfo suite; + + PRINTF( + "selfserv: %ld cache hits; %ld cache misses, %ld cache not reusable\n", + ssl3stats->hch_sid_cache_hits, ssl3stats->hch_sid_cache_misses, + ssl3stats->hch_sid_cache_not_ok); + + result = SSL_GetChannelInfo(fd, &channel, sizeof channel); + if (result == SECSuccess && + channel.length == sizeof channel && + channel.cipherSuite) { + result = SSL_GetCipherSuiteInfo(channel.cipherSuite, + &suite, sizeof suite); + if (result == SECSuccess) { + FPRINTF(stderr, + "selfserv: SSL version %d.%d using %d-bit %s with %d-bit %s MAC\n", + channel.protocolVersion >> 8, channel.protocolVersion & 0xff, + suite.effectiveKeyBits, suite.symCipherName, + suite.macBits, suite.macAlgorithmName); + FPRINTF(stderr, + "selfserv: Server Auth: %d-bit %s, Key Exchange: %d-bit %s\n", + channel.authKeyBits, suite.authAlgorithmName, + channel.keaKeyBits, suite.keaTypeName); + } + } + if (requestCert) + cert = SSL_PeerCertificate(fd); + else + cert = SSL_LocalCertificate(fd); + if (cert) { + char * ip = CERT_NameToAscii(&cert->issuer); + char * sp = CERT_NameToAscii(&cert->subject); + if (sp) { + FPRINTF(stderr, "selfserv: subject DN: %s\n", sp); + PR_Free(sp); + } + if (ip) { + FPRINTF(stderr, "selfserv: issuer DN: %s\n", ip); + PR_Free(ip); + } + CERT_DestroyCertificate(cert); + cert = NULL; + } + FLUSH; +} + +static int MakeCertOK; + +static SECStatus +myBadCertHandler( void *arg, PRFileDesc *fd) +{ + int err = PR_GetError(); + if (!MakeCertOK) + fprintf(stderr, + "selfserv: -- SSL: Client Certificate Invalid, err %d.\n%s\n", + err, SECU_Strerror(err)); + return (MakeCertOK ? SECSuccess : SECFailure); +} + +/************************************************************************** +** Begin thread management routines and data. +**************************************************************************/ +#define MIN_THREADS 3 +#define DEFAULT_THREADS 8 +#define MAX_THREADS 128 +#define MAX_PROCS 25 +static int maxThreads = DEFAULT_THREADS; + + +typedef struct jobStr { + PRCList link; + PRFileDesc *tcp_sock; + PRFileDesc *model_sock; + int requestCert; +} JOB; + +static PZLock * qLock; /* this lock protects all data immediately below */ +static PZCondVar * jobQNotEmptyCv; +static PZCondVar * freeListNotEmptyCv; +static PZCondVar * threadCountChangeCv; +static int threadCount; +static PRCList jobQ; +static PRCList freeJobs; +static JOB *jobTable; + +SECStatus +setupJobs(int maxJobs) +{ + int i; + + jobTable = (JOB *)PR_Calloc(maxJobs, sizeof(JOB)); + if (!jobTable) + return SECFailure; + + PR_INIT_CLIST(&jobQ); + PR_INIT_CLIST(&freeJobs); + + for (i = 0; i < maxJobs; ++i) { + JOB * pJob = jobTable + i; + PR_APPEND_LINK(&pJob->link, &freeJobs); + } + return SECSuccess; +} + +typedef int startFn(PRFileDesc *a, PRFileDesc *b, int c); + +typedef enum { rs_idle = 0, rs_running = 1, rs_zombie = 2 } runState; + +typedef struct perThreadStr { + PRFileDesc *a; + PRFileDesc *b; + int c; + int rv; + startFn * startFunc; + PRThread * prThread; + runState state; +} perThread; + +static perThread *threads; + +void +thread_wrapper(void * arg) +{ + perThread * slot = (perThread *)arg; + + slot->rv = (* slot->startFunc)(slot->a, slot->b, slot->c); + + /* notify the thread exit handler. */ + PZ_Lock(qLock); + slot->state = rs_zombie; + --threadCount; + PZ_NotifyAllCondVar(threadCountChangeCv); + PZ_Unlock(qLock); +} + +int +jobLoop(PRFileDesc *a, PRFileDesc *b, int c) +{ + PRCList * myLink = 0; + JOB * myJob; + + PZ_Lock(qLock); + do { + myLink = 0; + while (PR_CLIST_IS_EMPTY(&jobQ) && !stopping) { + PZ_WaitCondVar(jobQNotEmptyCv, PR_INTERVAL_NO_TIMEOUT); + } + if (!PR_CLIST_IS_EMPTY(&jobQ)) { + myLink = PR_LIST_HEAD(&jobQ); + PR_REMOVE_AND_INIT_LINK(myLink); + } + PZ_Unlock(qLock); + myJob = (JOB *)myLink; + /* myJob will be null when stopping is true and jobQ is empty */ + if (!myJob) + break; + handle_connection( myJob->tcp_sock, myJob->model_sock, + myJob->requestCert); + PZ_Lock(qLock); + PR_APPEND_LINK(myLink, &freeJobs); + PZ_NotifyCondVar(freeListNotEmptyCv); + } while (PR_TRUE); + return 0; +} + + +SECStatus +launch_threads( + startFn *startFunc, + PRFileDesc *a, + PRFileDesc *b, + int c, + PRBool local) +{ + int i; + SECStatus rv = SECSuccess; + + /* create the thread management serialization structs */ + qLock = PZ_NewLock(nssILockSelfServ); + jobQNotEmptyCv = PZ_NewCondVar(qLock); + freeListNotEmptyCv = PZ_NewCondVar(qLock); + threadCountChangeCv = PZ_NewCondVar(qLock); + + /* allocate the array of thread slots */ + threads = PR_Calloc(maxThreads, sizeof(perThread)); + if ( NULL == threads ) { + fprintf(stderr, "Oh Drat! Can't allocate the perThread array\n"); + return SECFailure; + } + /* 5 is a little extra, intended to keep the jobQ from underflowing. + ** That is, from going empty while not stopping and clients are still + ** trying to contact us. + */ + rv = setupJobs(maxThreads + 5); + if (rv != SECSuccess) + return rv; + + PZ_Lock(qLock); + for (i = 0; i < maxThreads; ++i) { + perThread * slot = threads + i; + + slot->state = rs_running; + slot->a = a; + slot->b = b; + slot->c = c; + slot->startFunc = startFunc; + slot->prThread = PR_CreateThread(PR_USER_THREAD, + thread_wrapper, slot, PR_PRIORITY_NORMAL, + (PR_TRUE==local)?PR_LOCAL_THREAD:PR_GLOBAL_THREAD, + PR_UNJOINABLE_THREAD, 0); + if (slot->prThread == NULL) { + printf("selfserv: Failed to launch thread!\n"); + slot->state = rs_idle; + rv = SECFailure; + break; + } + + ++threadCount; + } + PZ_Unlock(qLock); + + return rv; +} + +#define DESTROY_CONDVAR(name) if (name) { \ + PZ_DestroyCondVar(name); name = NULL; } +#define DESTROY_LOCK(name) if (name) { \ + PZ_DestroyLock(name); name = NULL; } + + +void +terminateWorkerThreads(void) +{ + VLOG(("selfserv: server_thead: waiting on stopping")); + PZ_Lock(qLock); + PZ_NotifyAllCondVar(jobQNotEmptyCv); + while (threadCount > 0) { + PZ_WaitCondVar(threadCountChangeCv, PR_INTERVAL_NO_TIMEOUT); + } + PZ_Unlock(qLock); + + DESTROY_CONDVAR(jobQNotEmptyCv); + DESTROY_CONDVAR(freeListNotEmptyCv); + DESTROY_CONDVAR(threadCountChangeCv); + + DESTROY_LOCK(qLock); + PR_Free(jobTable); + PR_Free(threads); +} + +static void +logger(void *arg) +{ + PRFloat64 seconds; + PRFloat64 opsPerSec; + PRIntervalTime period; + PRIntervalTime previousTime; + PRIntervalTime latestTime; + PRUint32 previousOps; + PRUint32 ops; + PRIntervalTime logPeriodTicks = PR_SecondsToInterval(logPeriod); + PRFloat64 secondsPerTick = 1.0 / (PRFloat64)PR_TicksPerSecond(); + + previousOps = loggerOps; + previousTime = PR_IntervalNow(); + + for (;;) { + PR_Sleep(logPeriodTicks); + latestTime = PR_IntervalNow(); + ops = loggerOps; + period = latestTime - previousTime; + seconds = (PRFloat64) period*secondsPerTick; + opsPerSec = (ops - previousOps) / seconds; + printf("%.2f ops/second, %d threads\n", opsPerSec, threadCount); + fflush(stdout); + previousOps = ops; + previousTime = latestTime; + } +} + + +/************************************************************************** +** End thread management routines. +**************************************************************************/ + +PRBool useModelSocket = PR_FALSE; +PRBool disableSSL3 = PR_FALSE; +PRBool disableTLS = PR_FALSE; +PRBool disableRollBack = PR_FALSE; + +static const char stopCmd[] = { "GET /stop " }; +static const char getCmd[] = { "GET " }; +static const char EOFmsg[] = { "EOF\r\n\r\n\r\n" }; +static const char outHeader[] = { + "HTTP/1.0 200 OK\r\n" + "Server: Generic Web Server\r\n" + "Date: Tue, 26 Aug 1997 22:10:05 GMT\r\n" + "Content-type: text/plain\r\n" + "\r\n" +}; + +#ifdef FULL_DUPLEX_CAPABLE + +struct lockedVarsStr { + PZLock * lock; + int count; + int waiters; + PZCondVar * condVar; +}; + +typedef struct lockedVarsStr lockedVars; + +void +lockedVars_Init( lockedVars * lv) +{ + lv->count = 0; + lv->waiters = 0; + lv->lock = PZ_NewLock(nssILockSelfServ); + lv->condVar = PZ_NewCondVar(lv->lock); +} + +void +lockedVars_Destroy( lockedVars * lv) +{ + PZ_DestroyCondVar(lv->condVar); + lv->condVar = NULL; + + PZ_DestroyLock(lv->lock); + lv->lock = NULL; +} + +void +lockedVars_WaitForDone(lockedVars * lv) +{ + PZ_Lock(lv->lock); + while (lv->count > 0) { + PZ_WaitCondVar(lv->condVar, PR_INTERVAL_NO_TIMEOUT); + } + PZ_Unlock(lv->lock); +} + +int /* returns count */ +lockedVars_AddToCount(lockedVars * lv, int addend) +{ + int rv; + + PZ_Lock(lv->lock); + rv = lv->count += addend; + if (rv <= 0) { + PZ_NotifyCondVar(lv->condVar); + } + PZ_Unlock(lv->lock); + return rv; +} + +int +do_writes( + PRFileDesc * ssl_sock, + PRFileDesc * model_sock, + int requestCert + ) +{ + int sent = 0; + int count = 0; + lockedVars * lv = (lockedVars *)model_sock; + + VLOG(("selfserv: do_writes: starting")); + while (sent < bigBuf.len) { + + count = PR_Write(ssl_sock, bigBuf.data + sent, bigBuf.len - sent); + if (count < 0) { + errWarn("PR_Write bigBuf"); + break; + } + FPRINTF(stderr, "selfserv: PR_Write wrote %d bytes from bigBuf\n", count ); + sent += count; + } + if (count >= 0) { /* last write didn't fail. */ + PR_Shutdown(ssl_sock, PR_SHUTDOWN_SEND); + } + + /* notify the reader that we're done. */ + lockedVars_AddToCount(lv, -1); + FLUSH; + VLOG(("selfserv: do_writes: exiting")); + return (sent < bigBuf.len) ? SECFailure : SECSuccess; +} + +static int +handle_fdx_connection( + PRFileDesc * tcp_sock, + PRFileDesc * model_sock, + int requestCert + ) +{ + PRFileDesc * ssl_sock = NULL; + SECStatus result; + int firstTime = 1; + lockedVars lv; + PRSocketOptionData opt; + char buf[10240]; + + + VLOG(("selfserv: handle_fdx_connection: starting")); + opt.option = PR_SockOpt_Nonblocking; + opt.value.non_blocking = PR_FALSE; + PR_SetSocketOption(tcp_sock, &opt); + + if (useModelSocket && model_sock) { + SECStatus rv; + ssl_sock = SSL_ImportFD(model_sock, tcp_sock); + if (!ssl_sock) { + errWarn("SSL_ImportFD with model"); + goto cleanup; + } + rv = SSL_ResetHandshake(ssl_sock, /* asServer */ 1); + if (rv != SECSuccess) { + errWarn("SSL_ResetHandshake"); + goto cleanup; + } + } else { + ssl_sock = tcp_sock; + } + + lockedVars_Init(&lv); + lockedVars_AddToCount(&lv, 1); + + /* Attempt to launch the writer thread. */ + result = launch_thread(do_writes, ssl_sock, (PRFileDesc *)&lv, + requestCert); + + if (result == SECSuccess) + do { + /* do reads here. */ + int count; + count = PR_Read(ssl_sock, buf, sizeof buf); + if (count < 0) { + errWarn("FDX PR_Read"); + break; + } + FPRINTF(stderr, "selfserv: FDX PR_Read read %d bytes.\n", count ); + if (firstTime) { + firstTime = 0; + printSecurityInfo(ssl_sock); + } + } while (lockedVars_AddToCount(&lv, 0) > 0); + + /* Wait for writer to finish */ + lockedVars_WaitForDone(&lv); + lockedVars_Destroy(&lv); + FLUSH; + +cleanup: + if (ssl_sock) + PR_Close(ssl_sock); + else + PR_Close(tcp_sock); + + VLOG(("selfserv: handle_fdx_connection: exiting")); + return SECSuccess; +} + +#endif + +int +handle_connection( + PRFileDesc *tcp_sock, + PRFileDesc *model_sock, + int requestCert + ) +{ + PRFileDesc * ssl_sock = NULL; + PRFileDesc * local_file_fd = NULL; + char * post; + char * pBuf; /* unused space at end of buf */ + const char * errString; + PRStatus status; + int bufRem; /* unused bytes at end of buf */ + int bufDat; /* characters received in buf */ + int newln = 0; /* # of consecutive newlns */ + int firstTime = 1; + int reqLen; + int rv; + int numIOVs; + PRSocketOptionData opt; + PRIOVec iovs[16]; + char msgBuf[160]; + char buf[10240]; + char fileName[513]; + + pBuf = buf; + bufRem = sizeof buf; + memset(buf, 0, sizeof buf); + + VLOG(("selfserv: handle_connection: starting")); + opt.option = PR_SockOpt_Nonblocking; + opt.value.non_blocking = PR_FALSE; + PR_SetSocketOption(tcp_sock, &opt); + + VLOG(("selfserv: handle_connection: starting\n")); + if (useModelSocket && model_sock) { + SECStatus rv; + ssl_sock = SSL_ImportFD(model_sock, tcp_sock); + if (!ssl_sock) { + errWarn("SSL_ImportFD with model"); + goto cleanup; + } + rv = SSL_ResetHandshake(ssl_sock, /* asServer */ 1); + if (rv != SECSuccess) { + errWarn("SSL_ResetHandshake"); + goto cleanup; + } + } else { + ssl_sock = tcp_sock; + } + + if (noDelay) { + opt.option = PR_SockOpt_NoDelay; + opt.value.no_delay = PR_TRUE; + status = PR_SetSocketOption(ssl_sock, &opt); + if (status != PR_SUCCESS) { + errWarn("PR_SetSocketOption(PR_SockOpt_NoDelay, PR_TRUE)"); + PR_Close(ssl_sock); + return SECFailure; + } + } + + while (1) { + newln = 0; + reqLen = 0; + rv = PR_Read(ssl_sock, pBuf, bufRem); + if (rv == 0 || + (rv < 0 && PR_END_OF_FILE_ERROR == PR_GetError())) { + if (verbose) + errWarn("HDX PR_Read hit EOF"); + break; + } + if (rv < 0) { + errWarn("HDX PR_Read"); + goto cleanup; + } + if (firstTime) { + firstTime = 0; + printSecurityInfo(ssl_sock); + } + + pBuf += rv; + bufRem -= rv; + bufDat = pBuf - buf; + /* Parse the input, starting at the beginning of the buffer. + * Stop when we detect two consecutive \n's (or \r\n's) + * as this signifies the end of the GET or POST portion. + * The posted data follows. + */ + while (reqLen < bufDat && newln < 2) { + int octet = buf[reqLen++]; + if (octet == '\n') { + newln++; + } else if (octet != '\r') { + newln = 0; + } + } + + /* came to the end of the buffer, or second newln + * If we didn't get an empty line (CRLFCRLF) then keep on reading. + */ + if (newln < 2) + continue; + + /* we're at the end of the HTTP request. + * If the request is a POST, then there will be one more + * line of data. + * This parsing is a hack, but ok for SSL test purposes. + */ + post = PORT_Strstr(buf, "POST "); + if (!post || *post != 'P') + break; + + /* It's a post, so look for the next and final CR/LF. */ + /* We should parse content length here, but ... */ + while (reqLen < bufDat && newln < 3) { + int octet = buf[reqLen++]; + if (octet == '\n') { + newln++; + } + } + if (newln == 3) + break; + } /* read loop */ + + bufDat = pBuf - buf; + if (bufDat) do { /* just close if no data */ + /* Have either (a) a complete get, (b) a complete post, (c) EOF */ + if (reqLen > 0 && !strncmp(buf, getCmd, sizeof getCmd - 1)) { + char * fnBegin = buf + 4; + char * fnEnd; + PRFileInfo info; + /* try to open the file named. + * If succesful, then write it to the client. + */ + fnEnd = strpbrk(fnBegin, " \r\n"); + if (fnEnd) { + int fnLen = fnEnd - fnBegin; + if (fnLen < sizeof fileName) { + strncpy(fileName, fnBegin, fnLen); + fileName[fnLen] = 0; /* null terminate */ + status = PR_GetFileInfo(fileName, &info); + if (status == PR_SUCCESS && + info.type == PR_FILE_FILE && + info.size >= 0 ) { + local_file_fd = PR_Open(fileName, PR_RDONLY, 0); + } + } + } + } + /* if user has requested client auth in a subsequent handshake, + * do it here. + */ + if (requestCert > 2) { /* request cert was 3 or 4 */ + CERTCertificate * cert = SSL_PeerCertificate(ssl_sock); + if (cert) { + CERT_DestroyCertificate(cert); + } else { + rv = SSL_OptionSet(ssl_sock, SSL_REQUEST_CERTIFICATE, 1); + if (rv < 0) { + errWarn("second SSL_OptionSet SSL_REQUEST_CERTIFICATE"); + break; + } + rv = SSL_OptionSet(ssl_sock, SSL_REQUIRE_CERTIFICATE, + (requestCert == 4)); + if (rv < 0) { + errWarn("second SSL_OptionSet SSL_REQUIRE_CERTIFICATE"); + break; + } + rv = SSL_ReHandshake(ssl_sock, PR_TRUE); + if (rv != 0) { + errWarn("SSL_ReHandshake"); + break; + } + rv = SSL_ForceHandshake(ssl_sock); + if (rv < 0) { + errWarn("SSL_ForceHandshake"); + break; + } + } + } + + numIOVs = 0; + + iovs[numIOVs].iov_base = (char *)outHeader; + iovs[numIOVs].iov_len = (sizeof(outHeader)) - 1; + numIOVs++; + + if (local_file_fd) { + PRInt32 bytes; + int errLen; + bytes = PR_TransmitFile(ssl_sock, local_file_fd, outHeader, + sizeof outHeader - 1, + PR_TRANSMITFILE_KEEP_OPEN, + PR_INTERVAL_NO_TIMEOUT); + if (bytes >= 0) { + bytes -= sizeof outHeader - 1; + FPRINTF(stderr, + "selfserv: PR_TransmitFile wrote %d bytes from %s\n", + bytes, fileName); + break; + } + errString = errWarn("PR_TransmitFile"); + errLen = PORT_Strlen(errString); + if (errLen > sizeof msgBuf - 1) + errLen = sizeof msgBuf - 1; + PORT_Memcpy(msgBuf, errString, errLen); + msgBuf[errLen] = 0; + + iovs[numIOVs].iov_base = msgBuf; + iovs[numIOVs].iov_len = PORT_Strlen(msgBuf); + numIOVs++; + } else if (reqLen <= 0) { /* hit eof */ + PORT_Sprintf(msgBuf, "Get or Post incomplete after %d bytes.\r\n", + bufDat); + + iovs[numIOVs].iov_base = msgBuf; + iovs[numIOVs].iov_len = PORT_Strlen(msgBuf); + numIOVs++; + } else if (reqLen < bufDat) { + PORT_Sprintf(msgBuf, "Discarded %d characters.\r\n", + bufDat - reqLen); + + iovs[numIOVs].iov_base = msgBuf; + iovs[numIOVs].iov_len = PORT_Strlen(msgBuf); + numIOVs++; + } + + if (reqLen > 0) { + if (verbose > 1) + fwrite(buf, 1, reqLen, stdout); /* display it */ + + iovs[numIOVs].iov_base = buf; + iovs[numIOVs].iov_len = reqLen; + numIOVs++; + +/* printSecurityInfo(ssl_sock); */ + } + + iovs[numIOVs].iov_base = (char *)EOFmsg; + iovs[numIOVs].iov_len = sizeof EOFmsg - 1; + numIOVs++; + + rv = PR_Writev(ssl_sock, iovs, numIOVs, PR_INTERVAL_NO_TIMEOUT); + if (rv < 0) { + errWarn("PR_Writev"); + break; + } + } while (0); + +cleanup: + PR_Close(ssl_sock); + if (local_file_fd) + PR_Close(local_file_fd); + VLOG(("selfserv: handle_connection: exiting\n")); + + /* do a nice shutdown if asked. */ + if (!strncmp(buf, stopCmd, sizeof stopCmd - 1)) { + stopping = 1; + VLOG(("selfserv: handle_connection: stop command")); + PR_Interrupt(acceptorThread); + PZ_TraceFlush(); + } + VLOG(("selfserv: handle_connection: exiting")); + return SECSuccess; /* success */ +} + +SECStatus +do_accepts( + PRFileDesc *listen_sock, + PRFileDesc *model_sock, + int requestCert + ) +{ + PRNetAddr addr; + PRErrorCode perr; + + VLOG(("selfserv: do_accepts: starting")); + PR_SetThreadPriority( PR_GetCurrentThread(), PR_PRIORITY_HIGH); + + acceptorThread = PR_GetCurrentThread(); + while (!stopping) { + PRFileDesc *tcp_sock; + PRCList *myLink; + + FPRINTF(stderr, "\n\n\nselfserv: About to call accept.\n"); + tcp_sock = PR_Accept(listen_sock, &addr, PR_INTERVAL_NO_TIMEOUT); + if (tcp_sock == NULL) { + perr = PR_GetError(); + if ((perr != PR_CONNECT_RESET_ERROR && + perr != PR_PENDING_INTERRUPT_ERROR) || verbose) { + errWarn("PR_Accept"); + } + if (perr == PR_CONNECT_RESET_ERROR) { + FPRINTF(stderr, + "Ignoring PR_CONNECT_RESET_ERROR error - continue\n"); + continue; + } + stopping = 1; + break; + } + + VLOG(("selfserv: do_accept: Got connection\n")); + + if (logStats) { + loggerOps++; + } + + PZ_Lock(qLock); + while (PR_CLIST_IS_EMPTY(&freeJobs) && !stopping) { + PZ_WaitCondVar(freeListNotEmptyCv, PR_INTERVAL_NO_TIMEOUT); + } + if (stopping) { + PZ_Unlock(qLock); + PR_Close(tcp_sock); + break; + } + myLink = PR_LIST_HEAD(&freeJobs); + PR_REMOVE_AND_INIT_LINK(myLink); + /* could release qLock here and reaquire it 7 lines below, but + ** why bother for 4 assignment statements? + */ + { + JOB * myJob = (JOB *)myLink; + myJob->tcp_sock = tcp_sock; + myJob->model_sock = model_sock; + myJob->requestCert = requestCert; + } + + PR_APPEND_LINK(myLink, &jobQ); + PZ_NotifyCondVar(jobQNotEmptyCv); + PZ_Unlock(qLock); + } + + FPRINTF(stderr, "selfserv: Closing listen socket.\n"); + VLOG(("selfserv: do_accepts: exiting")); + PR_Close(listen_sock); + return SECSuccess; +} + +PRFileDesc * +getBoundListenSocket(unsigned short port) +{ + PRFileDesc * listen_sock; + int listenQueueDepth = 5 + (2 * maxThreads); + PRStatus prStatus; + PRNetAddr addr; + PRSocketOptionData opt; + + addr.inet.family = PR_AF_INET; + addr.inet.ip = PR_INADDR_ANY; + addr.inet.port = PR_htons(port); + + listen_sock = PR_NewTCPSocket(); + if (listen_sock == NULL) { + errExit("PR_NewTCPSocket"); + } + + opt.option = PR_SockOpt_Nonblocking; + opt.value.non_blocking = PR_FALSE; + prStatus = PR_SetSocketOption(listen_sock, &opt); + if (prStatus < 0) { + errExit("PR_SetSocketOption(PR_SockOpt_Nonblocking)"); + } + + opt.option=PR_SockOpt_Reuseaddr; + opt.value.reuse_addr = PR_TRUE; + prStatus = PR_SetSocketOption(listen_sock, &opt); + if (prStatus < 0) { + errExit("PR_SetSocketOption(PR_SockOpt_Reuseaddr)"); + } + + prStatus = PR_Bind(listen_sock, &addr); + if (prStatus < 0) { + errExit("PR_Bind"); + } + + prStatus = PR_Listen(listen_sock, listenQueueDepth); + if (prStatus < 0) { + errExit("PR_Listen"); + } + return listen_sock; +} + +void +server_main( + PRFileDesc * listen_sock, + int requestCert, + SECKEYPrivateKey ** privKey, + CERTCertificate ** cert) +{ + PRFileDesc *model_sock = NULL; + int rv; + SSLKEAType kea; + SECStatus secStatus; + + if (useModelSocket) { + model_sock = PR_NewTCPSocket(); + if (model_sock == NULL) { + errExit("PR_NewTCPSocket on model socket"); + } + model_sock = SSL_ImportFD(NULL, model_sock); + if (model_sock == NULL) { + errExit("SSL_ImportFD"); + } + } else { + model_sock = listen_sock = SSL_ImportFD(NULL, listen_sock); + if (listen_sock == NULL) { + errExit("SSL_ImportFD"); + } + } + + /* do SSL configuration. */ + /* all suites except RSA_NULL_MD5 are enabled by default */ + +#if 0 + /* This is supposed to be true by default. + ** Setting it explicitly should not be necessary. + ** Let's test and make sure that's true. + */ + rv = SSL_OptionSet(model_sock, SSL_SECURITY, 1); + if (rv < 0) { + errExit("SSL_OptionSet SSL_SECURITY"); + } +#endif + + rv = SSL_OptionSet(model_sock, SSL_ENABLE_SSL3, !disableSSL3); + if (rv != SECSuccess) { + errExit("error enabling SSLv3 "); + } + + rv = SSL_OptionSet(model_sock, SSL_ENABLE_TLS, !disableTLS); + if (rv != SECSuccess) { + errExit("error enabling TLS "); + } + + rv = SSL_OptionSet(model_sock, SSL_ROLLBACK_DETECTION, !disableRollBack); + if (rv != SECSuccess) { + errExit("error enabling RollBack detection "); + } + + for (kea = kt_rsa; kea < kt_kea_size; kea++) { + if (cert[kea] != NULL) { + secStatus = SSL_ConfigSecureServer(model_sock, + cert[kea], privKey[kea], kea); + if (secStatus != SECSuccess) + errExit("SSL_ConfigSecureServer"); + } + } + + if (bigBuf.data) { /* doing FDX */ + rv = SSL_OptionSet(model_sock, SSL_ENABLE_FDX, 1); + if (rv < 0) { + errExit("SSL_OptionSet SSL_ENABLE_FDX"); + } + } + + /* This cipher is not on by default. The Acceptance test + * would like it to be. Turn this cipher on. + */ + + secStatus = SSL_CipherPrefSetDefault( SSL_RSA_WITH_NULL_MD5, PR_TRUE); + if ( secStatus != SECSuccess ) { + errExit("SSL_CipherPrefSetDefault:SSL_RSA_WITH_NULL_MD5"); + } + + + if (requestCert) { + SSL_AuthCertificateHook(model_sock, mySSLAuthCertificate, + (void *)CERT_GetDefaultCertDB()); + if (requestCert <= 2) { + rv = SSL_OptionSet(model_sock, SSL_REQUEST_CERTIFICATE, 1); + if (rv < 0) { + errExit("first SSL_OptionSet SSL_REQUEST_CERTIFICATE"); + } + rv = SSL_OptionSet(model_sock, SSL_REQUIRE_CERTIFICATE, + (requestCert == 2)); + if (rv < 0) { + errExit("first SSL_OptionSet SSL_REQUIRE_CERTIFICATE"); + } + } + } + + if (MakeCertOK) + SSL_BadCertHook(model_sock, myBadCertHandler, NULL); + + /* end of ssl configuration. */ + + + /* Now, do the accepting, here in the main thread. */ + rv = do_accepts(listen_sock, model_sock, requestCert); + + terminateWorkerThreads(); + + if (useModelSocket && model_sock) { + PR_Close(model_sock); + } + +} + +SECStatus +readBigFile(const char * fileName) +{ + PRFileInfo info; + PRStatus status; + SECStatus rv = SECFailure; + int count; + int hdrLen; + PRFileDesc *local_file_fd = NULL; + + status = PR_GetFileInfo(fileName, &info); + + if (status == PR_SUCCESS && + info.type == PR_FILE_FILE && + info.size > 0 && + NULL != (local_file_fd = PR_Open(fileName, PR_RDONLY, 0))) { + + hdrLen = PORT_Strlen(outHeader); + bigBuf.len = hdrLen + info.size; + bigBuf.data = PORT_Malloc(bigBuf.len + 4095); + if (!bigBuf.data) { + errWarn("PORT_Malloc"); + goto done; + } + + PORT_Memcpy(bigBuf.data, outHeader, hdrLen); + + count = PR_Read(local_file_fd, bigBuf.data + hdrLen, info.size); + if (count != info.size) { + errWarn("PR_Read local file"); + goto done; + } + rv = SECSuccess; +done: + PR_Close(local_file_fd); + } + return rv; +} + +int numChildren; +PRProcess * child[MAX_PROCS]; + +PRProcess * +haveAChild(int argc, char **argv, PRProcessAttr * attr) +{ + PRProcess * newProcess; + + newProcess = PR_CreateProcess(argv[0], argv, NULL, attr); + if (!newProcess) { + errWarn("Can't create new process."); + } else { + child[numChildren++] = newProcess; + } + return newProcess; +} + +void +beAGoodParent(int argc, char **argv, int maxProcs, PRFileDesc * listen_sock) +{ + PRProcess * newProcess; + PRProcessAttr * attr; + int i; + PRInt32 exitCode; + PRStatus rv; + + rv = PR_SetFDInheritable(listen_sock, PR_TRUE); + if (rv != PR_SUCCESS) + errExit("PR_SetFDInheritable"); + + attr = PR_NewProcessAttr(); + if (!attr) + errExit("PR_NewProcessAttr"); + + rv = PR_ProcessAttrSetInheritableFD(attr, listen_sock, inheritableSockName); + if (rv != PR_SUCCESS) + errExit("PR_ProcessAttrSetInheritableFD"); + + for (i = 0; i < maxProcs; ++i) { + newProcess = haveAChild(argc, argv, attr); + if (!newProcess) + break; + } + + rv = PR_SetFDInheritable(listen_sock, PR_FALSE); + if (rv != PR_SUCCESS) + errExit("PR_SetFDInheritable"); + + while (numChildren > 0) { + newProcess = child[numChildren - 1]; + PR_WaitProcess(newProcess, &exitCode); + fprintf(stderr, "Child %d exited with exit code %x\n", + numChildren, exitCode); + numChildren--; + } + exit(0); +} + +#ifdef DEBUG_nelsonb + +#if defined(XP_UNIX) || defined(XP_OS2) || defined(XP_BEOS) +#define SSL_GETPID getpid +#elif defined(_WIN32_WCE) +#define SSL_GETPID GetCurrentProcessId +#elif defined(WIN32) +extern int __cdecl _getpid(void); +#define SSL_GETPID _getpid +#else +#define SSL_GETPID() 0 +#endif + +void +WaitForDebugger(void) +{ + + int waiting = 12; + int myPid = SSL_GETPID(); + PRIntervalTime nrval = PR_SecondsToInterval(5); + + while (waiting) { + printf("child %d is waiting to be debugged!\n", myPid); + PR_Sleep(nrval); + --waiting; + } +} +#endif + +int +main(int argc, char **argv) +{ + char * progName = NULL; + char * nickName = NULL; + char * fNickName = NULL; + const char * fileName = NULL; + char * cipherString= NULL; + const char * dir = "."; + char * passwd = NULL; + const char * pidFile = NULL; + char * tmp; + char * envString; + PRFileDesc * listen_sock; + CERTCertificate * cert [kt_kea_size] = { NULL }; + SECKEYPrivateKey * privKey[kt_kea_size] = { NULL }; + int optionsFound = 0; + int maxProcs = 1; + unsigned short port = 0; + SECStatus rv; + PRStatus prStatus; + PRBool bindOnly = PR_FALSE; + PRBool useExportPolicy = PR_FALSE; + PRBool useLocalThreads = PR_FALSE; + PLOptState *optstate; + PLOptStatus status; + PRThread *loggerThread; + PRBool debugCache = PR_FALSE; /* bug 90518 */ + + + tmp = strrchr(argv[0], '/'); + tmp = tmp ? tmp + 1 : argv[0]; + progName = strrchr(tmp, '\\'); + progName = progName ? progName + 1 : tmp; + + PR_Init( PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1); + + /* please keep this list of options in ASCII collating sequence. + ** numbers, then capital letters, then lower case, alphabetical. + */ + optstate = PL_CreateOptState(argc, argv, + "2:3DL:M:RTbc:d:f:hi:lmn:op:rt:vw:xy"); + while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) { + ++optionsFound; + switch(optstate->option) { + case '2': fileName = optstate->value; break; + + case '3': disableSSL3 = PR_TRUE; break; + + case 'D': noDelay = PR_TRUE; break; + + case 'L': + logStats = PR_TRUE; + logPeriod = PORT_Atoi(optstate->value); + if (logPeriod < 0) logPeriod = 30; + break; + + case 'M': + maxProcs = PORT_Atoi(optstate->value); + if (maxProcs < 1) maxProcs = 1; + if (maxProcs > MAX_PROCS) maxProcs = MAX_PROCS; + break; + + case 'R': disableRollBack = PR_TRUE; break; + + case 'T': disableTLS = PR_TRUE; break; + + case 'b': bindOnly = PR_TRUE; break; + + case 'c': cipherString = strdup(optstate->value); break; + + case 'd': dir = optstate->value; break; + + case 'f': fNickName = strdup(optstate->value); break; + + case 'h': Usage(progName); exit(0); break; + + case 'i': pidFile = optstate->value; break; + + case 'l': useLocalThreads = PR_TRUE; break; + + case 'm': useModelSocket = PR_TRUE; break; + + case 'n': nickName = strdup(optstate->value); break; + + case 'o': MakeCertOK = 1; break; + + case 'p': port = PORT_Atoi(optstate->value); break; + + case 'r': ++requestCert; break; + + case 't': + maxThreads = PORT_Atoi(optstate->value); + if ( maxThreads > MAX_THREADS ) maxThreads = MAX_THREADS; + if ( maxThreads < MIN_THREADS ) maxThreads = MIN_THREADS; + break; + + case 'v': verbose++; break; + + case 'w': passwd = strdup(optstate->value); break; + + case 'x': useExportPolicy = PR_TRUE; break; + + case 'y': debugCache = PR_TRUE; break; + + default: + case '?': + fprintf(stderr, "Unrecognized or bad option specified.\n"); + fprintf(stderr, "Run '%s -h' for usage information.\n", progName); + exit(4); + break; + } + } + PL_DestroyOptState(optstate); + if (status == PL_OPT_BAD) { + fprintf(stderr, "Unrecognized or bad option specified.\n"); + fprintf(stderr, "Run '%s -h' for usage information.\n", progName); + exit(5); + } + if (!optionsFound) { + Usage(progName); + exit(51); + } + + /* The -b (bindOnly) option is only used by the ssl.sh test + * script on Linux to determine whether a previous selfserv + * process has fully died and freed the port. (Bug 129701) + */ + if (bindOnly) { + listen_sock = getBoundListenSocket(port); + if (!listen_sock) { + exit(1); + } + PR_Close(listen_sock); + exit(0); + } + + if ((nickName == NULL) && (fNickName == NULL)) { + fprintf(stderr, "Required arg '-n' (rsa nickname) not supplied.\n"); + fprintf(stderr, "Run '%s -h' for usage information.\n", progName); + exit(6); + } + + if (port == 0) { + fprintf(stderr, "Required argument 'port' must be non-zero value\n"); + exit(7); + } + + if (pidFile) { + FILE *tmpfile=fopen(pidFile,"w+"); + + if (tmpfile) { + fprintf(tmpfile,"%d",getpid()); + fclose(tmpfile); + } + } + + envString = getenv(envVarName); + tmp = getenv("TMP"); + if (!tmp) + tmp = getenv("TMPDIR"); + if (!tmp) + tmp = getenv("TEMP"); + if (envString) { + /* we're one of the children in a multi-process server. */ + listen_sock = PR_GetInheritedFD(inheritableSockName); + if (!listen_sock) + errExit("PR_GetInheritedFD"); +#ifndef WINNT + /* we can't do this on NT because it breaks NSPR and + PR_Accept will fail on the socket in the child process if + the socket state is change to non inheritable + It is however a security issue to leave it accessible, + but it is OK for a test server such as selfserv. + NSPR should fix it eventually . see bugzilla 101617 + and 102077 + */ + prStatus = PR_SetFDInheritable(listen_sock, PR_FALSE); + if (prStatus != PR_SUCCESS) + errExit("PR_SetFDInheritable"); +#endif +#ifdef DEBUG_nelsonb + WaitForDebugger(); +#endif + rv = SSL_InheritMPServerSIDCache(envString); + if (rv != SECSuccess) + errExit("SSL_InheritMPServerSIDCache"); + } else if (maxProcs > 1) { + /* we're going to be the parent in a multi-process server. */ + listen_sock = getBoundListenSocket(port); + rv = SSL_ConfigMPServerSIDCache(NUM_SID_CACHE_ENTRIES, 0, 0, tmp); + if (rv != SECSuccess) + errExit("SSL_ConfigMPServerSIDCache"); + beAGoodParent(argc, argv, maxProcs, listen_sock); + exit(99); /* should never get here */ + } else { + /* we're an ordinary single process server. */ + listen_sock = getBoundListenSocket(port); + prStatus = PR_SetFDInheritable(listen_sock, PR_FALSE); + if (prStatus != PR_SUCCESS) + errExit("PR_SetFDInheritable"); + rv = SSL_ConfigServerSessionIDCache(NUM_SID_CACHE_ENTRIES, 0, 0, tmp); + if (rv != SECSuccess) + errExit("SSL_ConfigServerSessionIDCache"); + } + + lm = PR_NewLogModule("TestCase"); + + if (fileName) + readBigFile(fileName); + + /* set our password function */ + PK11_SetPasswordFunc( passwd ? ownPasswd : SECU_GetModulePassword); + + /* Call the libsec initialization routines */ + rv = NSS_Init(dir); + if (rv != SECSuccess) { + fputs("NSS_Init failed.\n", stderr); + exit(8); + } + + /* set the policy bits true for all the cipher suites. */ + if (useExportPolicy) + NSS_SetExportPolicy(); + else + NSS_SetDomesticPolicy(); + + /* all the SSL2 and SSL3 cipher suites are enabled by default. */ + if (cipherString) { + int ndx; + + /* disable all the ciphers, then enable the ones we want. */ + disableAllSSLCiphers(); + + while (0 != (ndx = *cipherString++)) { + const int *cptr; + int cipher; + + if (! isalpha(ndx)) { + fprintf(stderr, + "Non-alphabetic char in cipher string (-c arg).\n"); + exit(9); + } + cptr = islower(ndx) ? ssl3CipherSuites : ssl2CipherSuites; + for (ndx &= 0x1f; (cipher = *cptr++) != 0 && --ndx > 0; ) + /* do nothing */; + if (cipher) { + SECStatus status; + status = SSL_CipherPrefSetDefault(cipher, SSL_ALLOWED); + if (status != SECSuccess) + SECU_PrintError(progName, "SSL_CipherPrefSet()"); + } + } + } + + if (nickName) { + cert[kt_rsa] = PK11_FindCertFromNickname(nickName, passwd); + if (cert[kt_rsa] == NULL) { + fprintf(stderr, "selfserv: Can't find certificate %s\n", nickName); + exit(10); + } + privKey[kt_rsa] = PK11_FindKeyByAnyCert(cert[kt_rsa], passwd); + if (privKey[kt_rsa] == NULL) { + fprintf(stderr, "selfserv: Can't find Private Key for cert %s\n", + nickName); + exit(11); + } + } + if (fNickName) { + cert[kt_fortezza] = PK11_FindCertFromNickname(fNickName, NULL); + if (cert[kt_fortezza] == NULL) { + fprintf(stderr, "selfserv: Can't find certificate %s\n", fNickName); + exit(12); + } + privKey[kt_fortezza] = PK11_FindKeyByAnyCert(cert[kt_fortezza], NULL); + } + + /* allocate the array of thread slots, and launch the worker threads. */ + rv = launch_threads(&jobLoop, 0, 0, requestCert, useLocalThreads); + + if (rv == SECSuccess && logStats) { + loggerThread = PR_CreateThread(PR_SYSTEM_THREAD, + logger, NULL, PR_PRIORITY_NORMAL, + useLocalThreads ? PR_LOCAL_THREAD:PR_GLOBAL_THREAD, + PR_UNJOINABLE_THREAD, 0); + if (loggerThread == NULL) { + fprintf(stderr, "selfserv: Failed to launch logger thread!\n"); + rv = SECFailure; + } + } + + if (rv == SECSuccess) { + server_main(listen_sock, requestCert, privKey, cert); + } + + VLOG(("selfserv: server_thread: exiting")); + + { + int i; + for (i=0; i<kt_kea_size; i++) { + if (cert[i]) { + CERT_DestroyCertificate(cert[i]); + } + if (privKey[i]) { + SECKEY_DestroyPrivateKey(privKey[i]); + } + } + } + + if (debugCache) { + nss_DumpCertificateCacheInfo(); + } + + free(nickName); + free(passwd); + + NSS_Shutdown(); + PR_Cleanup(); + printf("selfserv: normal termination\n"); + return 0; +} |