summaryrefslogtreecommitdiff
path: root/security/nss/cmd/selfserv/selfserv.c
diff options
context:
space:
mode:
Diffstat (limited to 'security/nss/cmd/selfserv/selfserv.c')
-rw-r--r--security/nss/cmd/selfserv/selfserv.c2712
1 files changed, 0 insertions, 2712 deletions
diff --git a/security/nss/cmd/selfserv/selfserv.c b/security/nss/cmd/selfserv/selfserv.c
deleted file mode 100644
index e9d5dc3e2..000000000
--- a/security/nss/cmd/selfserv/selfserv.c
+++ /dev/null
@@ -1,2712 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-/* -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
-
-#include <signal.h>
-#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"
-#include "cert.h"
-#include "certt.h"
-#include "ocsp.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
-
-int NumSidCacheEntries = 1024;
-
-static int handle_connection( PRFileDesc *, PRFileDesc *, int );
-
-static const char envVarName[] = { SSL_ENV_VAR_NAME };
-static const char inheritableSockName[] = { "SELFSERV_LISTEN_SOCKET" };
-
-#define DEFAULT_BULK_TEST 16384
-#define MAX_BULK_TEST 1048576 /* 1 MB */
-static PRBool testBulk;
-static PRUint32 testBulkSize = DEFAULT_BULK_TEST;
-static PRUint32 testBulkTotal;
-static char* testBulkBuf;
-static PRDescIdentity log_layer_id = PR_INVALID_IO_LAYER;
-static PRFileDesc *loggingFD;
-static PRIOMethods loggingMethods;
-
-static PRBool logStats;
-static PRBool loggingLayer;
-static int logPeriod = 30;
-static PRUint32 loggerOps;
-static PRUint32 loggerBytes;
-static PRUint32 loggerBytesTCP;
-static PRUint32 bulkSentChunks;
-static enum ocspStaplingModeEnum {
- osm_disabled, /* server doesn't support stapling */
- osm_good, /* supply a signed good status */
- osm_revoked, /* supply a signed revoked status */
- osm_unknown, /* supply a signed unknown status */
- osm_failure, /* supply a unsigned failure status, "try later" */
- osm_badsig, /* supply a good status response with a bad signature */
- osm_corrupted, /* supply a corrupted data block as the status */
- osm_random, /* use a random response for each connection */
- osm_ocsp /* retrieve ocsp status from external ocsp server,
- use empty status if server is unavailable */
-} ocspStaplingMode = osm_disabled;
-typedef enum ocspStaplingModeEnum ocspStaplingModeType;
-static char *ocspStaplingCA = NULL;
-CERTCertificate * certForStatusWeakReference = NULL;
-
-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[] = {
- -1, /* SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA* a */
- -1, /* 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 */
- -1, /* 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 */
- -1, /* TLS_DHE_DSS_WITH_RC4_128_SHA, * o */
- -1, /* SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA, * p */
- -1, /* SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA, * q */
- -1, /* SSL_DHE_RSA_WITH_DES_CBC_SHA, * r */
- -1, /* SSL_DHE_DSS_WITH_DES_CBC_SHA, * s */
- -1, /* TLS_DHE_DSS_WITH_AES_128_CBC_SHA, * t */
- -1, /* TLS_DHE_RSA_WITH_AES_128_CBC_SHA, * u */
- TLS_RSA_WITH_AES_128_CBC_SHA, /* v */
- -1, /* TLS_DHE_DSS_WITH_AES_256_CBC_SHA, * w */
- -1, /* 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;
-
-#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
-PrintUsageHeader(const char *progName)
-{
- fprintf(stderr,
-"Usage: %s -n rsa_nickname -p port [-BDENRbjlmrsuvx] [-w password]\n"
-" [-t threads] [-i pid_file] [-c ciphers] [-Y] [-d dbdir] [-g numblocks]\n"
-" [-f password_file] [-L [seconds]] [-M maxProcs] [-P dbprefix]\n"
-" [-V [min-version]:[max-version]] [-a sni_name]\n"
-" [ T <good|revoked|unknown|badsig|corrupted|none|ocsp>] [-A ca]\n"
-#ifdef NSS_ENABLE_ECC
-" [-C SSLCacheEntries] [-e ec_nickname]\n"
-#else
-" [-C SSLCacheEntries]\n"
-#endif /* NSS_ENABLE_ECC */
- ,progName);
-}
-
-static void
-PrintParameterUsage()
-{
- fputs(
-"-V [min]:[max] restricts the set of enabled SSL/TLS protocol versions.\n"
-" All versions are enabled by default.\n"
-" Possible values for min/max: ssl2 ssl3 tls1.0 tls1.1\n"
-" Example: \"-V ssl3:\" enables SSL 3 and newer.\n"
-"-B bypasses the PKCS11 layer for SSL encryption and MACing\n"
-"-q checks for bypassability\n"
-"-D means disable Nagle delays in TCP\n"
-"-E means disable export ciphersuites and SSL step down key gen\n"
-"-R means disable detection of rollback from TLS to SSL3\n"
-"-a configure server for SNI.\n"
-"-k expected name negotiated on server sockets\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"
-"-s means disable SSL socket locking for performance\n"
-"-u means enable Session Ticket extension for TLS.\n"
-"-v means verbose output\n"
-"-x means use export policy.\n"
-"-z means enable compression.\n"
-"-L seconds means log statistics every 'seconds' seconds (default=30).\n"
-"-M maxProcs tells how many processes to run in a multi-process server\n"
-"-N means do NOT use the server session cache. Incompatible with -M.\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"
-"-l means use local threads instead of global threads\n"
-"-g numblocks means test throughput by sending total numblocks chunks\n"
-" of size 16kb to the client, 0 means unlimited (default=0)\n"
-"-j means measure TCP throughput (for use with -g option)\n"
-"-C SSLCacheEntries sets the maximum number of entries in the SSL\n"
-" session cache\n"
-"-T <mode> enable OCSP stapling. Possible modes:\n"
-" none: don't send cert status (default)\n"
-" good, revoked, unknown: Include locally signed response. Requires: -A\n"
-" failure: return a failure response (try later, unsigned)\n"
-" badsig: use a good status but with an invalid signature\n"
-" corrupted: stapled cert status is an invalid block of data\n"
-" random: each connection uses a random status from this list:\n"
-" good, revoked, unknown, failure, badsig, corrupted\n"
-" ocsp: fetch from external OCSP server using AIA, or none\n"
-"-A <ca> Nickname of a CA used to sign a stapled cert status\n"
-"-c Restrict ciphers\n"
-"-Y prints cipher values allowed for parameter -c and exits\n"
- , stderr);
-}
-
-static void
-Usage(const char *progName)
-{
- PrintUsageHeader(progName);
- PrintParameterUsage();
-}
-
-static void
-PrintCipherUsage(const char *progName)
-{
- PrintUsageHeader(progName);
- fputs(
-"-c ciphers Letter(s) chosen from the following list\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"
-"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"
-"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"
-"\n"
-":WXYZ Use cipher with hex code { 0xWX , 0xYZ } in TLS\n"
- , stderr);
-}
-
-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.
-**
-**************************************************************************/
-
-/* disable all the SSL cipher suites */
-void
-disableAllSSLCiphers(void)
-{
- const PRUint16 *cipherSuites = SSL_ImplementedCiphers;
- int i = SSL_NumImplementedCiphers;
- SECStatus rv;
-
- while (--i >= 0) {
- PRUint16 suite = cipherSuites[i];
- rv = SSL_CipherPrefSetDefault(suite, PR_FALSE);
- if (rv != SECSuccess) {
- printf("SSL_CipherPrefSetDefault rejected suite 0x%04x (i = %d)\n",
- suite, i);
- errWarn("SSL_CipherPrefSetDefault");
- }
- }
-}
-
-/* disable all the export SSL cipher suites */
-SECStatus
-disableExportSSLCiphers(void)
-{
- const PRUint16 *cipherSuites = SSL_ImplementedCiphers;
- int i = SSL_NumImplementedCiphers;
- SECStatus rv = SECSuccess;
- SSLCipherSuiteInfo info;
-
- while (--i >= 0) {
- PRUint16 suite = cipherSuites[i];
- SECStatus status;
- status = SSL_GetCipherSuiteInfo(suite, &info, sizeof info);
- if (status != SECSuccess) {
- printf("SSL_GetCipherSuiteInfo rejected suite 0x%04x (i = %d)\n",
- suite, i);
- errWarn("SSL_GetCipherSuiteInfo");
- rv = SECFailure;
- continue;
- }
- if (info.cipherSuite != suite) {
- printf(
-"SSL_GetCipherSuiteInfo returned wrong suite! Wanted 0x%04x, Got 0x%04x\n",
- suite, i);
- PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
- rv = SECFailure;
- continue;
- }
- /* should check here that info.length >= offsetof isExportable */
- if (info.isExportable) {
- status = SSL_CipherPolicySet(suite, SSL_NOT_ALLOWED);
- if (status != SECSuccess) {
- printf("SSL_CipherPolicySet rejected suite 0x%04x (i = %d)\n",
- suite, i);
- errWarn("SSL_CipherPolicySet");
- rv = SECFailure;
- }
- }
- }
- return rv;
-}
-
-static SECStatus
-mySSLAuthCertificate(void *arg, PRFileDesc *fd, PRBool checkSig,
- PRBool isServer)
-{
- SECStatus rv;
- CERTCertificate * peerCert;
-
- peerCert = SSL_PeerCertificate(fd);
-
- if (peerCert) {
- PRINTF("selfserv: Subject: %s\nselfserv: Issuer : %s\n",
- peerCert->subjectName, peerCert->issuerName);
- CERT_DestroyCertificate(peerCert);
- }
-
- 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));
- }
- FLUSH;
- return rv;
-}
-
-void
-printSSLStatistics()
-{
- SSL3Statistics * ssl3stats = SSL_GetStatistics();
-
- printf(
- "selfserv: %ld cache hits; %ld cache misses, %ld cache not reusable\n"
- " %ld stateless resumes, %ld ticket parse failures\n",
- ssl3stats->hch_sid_cache_hits, ssl3stats->hch_sid_cache_misses,
- ssl3stats->hch_sid_cache_not_ok, ssl3stats->hch_sid_stateless_resumes,
- ssl3stats->hch_sid_ticket_parse_failures);
-}
-
-void
-printSecurityInfo(PRFileDesc *fd)
-{
- CERTCertificate * cert = NULL;
- SECStatus result;
- SSLChannelInfo channel;
- SSLCipherSuiteInfo suite;
-
- if (verbose)
- printSSLStatistics();
-
- 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"
- " Compression: %s\n",
- channel.authKeyBits, suite.authAlgorithmName,
- channel.keaKeyBits, suite.keaTypeName,
- channel.compressionMethodName);
- }
- }
- if (verbose) {
- SECItem *hostInfo = SSL_GetNegotiatedHostInfo(fd);
- if (hostInfo) {
- char namePref[] = "selfserv: Negotiated server name: ";
-
- fprintf(stderr, "%s", namePref);
- fwrite(hostInfo->data, hostInfo->len, 1, stderr);
- SECITEM_FreeItem(hostInfo, PR_TRUE);
- hostInfo = NULL;
- fprintf(stderr, "\n");
- }
- }
- 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);
- PORT_Free(sp);
- }
- if (ip) {
- FPRINTF(stderr, "selfserv: issuer DN: %s\n", ip);
- PORT_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);
-}
-
-#define MAX_VIRT_SERVER_NAME_ARRAY_INDEX 10
-
-/* Simple SNI socket config function that does not use SSL_ReconfigFD.
- * Only uses one server name but verifies that the names match. */
-PRInt32
-mySSLSNISocketConfig(PRFileDesc *fd, const SECItem *sniNameArr,
- PRUint32 sniNameArrSize, void *arg)
-{
- PRInt32 i = 0;
- const SECItem *current = sniNameArr;
- const char **nameArr = (const char**)arg;
- const secuPWData *pwdata;
- CERTCertificate * cert = NULL;
- SECKEYPrivateKey * privKey = NULL;
-
- PORT_Assert(fd && sniNameArr);
- if (!fd || !sniNameArr) {
- return SSL_SNI_SEND_ALERT;
- }
-
- pwdata = SSL_RevealPinArg(fd);
-
- for (;current && i < sniNameArrSize;i++) {
- int j = 0;
- for (;j < MAX_VIRT_SERVER_NAME_ARRAY_INDEX && nameArr[j];j++) {
- if (!PORT_Strncmp(nameArr[j],
- (const char *)current[i].data,
- current[i].len) &&
- PORT_Strlen(nameArr[j]) == current[i].len) {
- const char *nickName = nameArr[j];
- if (j == 0) {
- /* default cert */
- return 0;
- }
- /* if pwdata is NULL, then we would not get the key and
- * return an error status. */
- cert = PK11_FindCertFromNickname(nickName, &pwdata);
- if (cert == NULL) {
- goto loser; /* Send alert */
- }
- privKey = PK11_FindKeyByAnyCert(cert, &pwdata);
- if (privKey == NULL) {
- goto loser; /* Send alert */
- }
- if (SSL_ConfigSecureServer(fd, cert, privKey,
- kt_rsa) != SECSuccess) {
- goto loser; /* Send alert */
- }
- SECKEY_DestroyPrivateKey(privKey);
- CERT_DestroyCertificate(cert);
- return i;
- }
- }
- }
-loser:
- if (privKey) {
- SECKEY_DestroyPrivateKey(privKey);
- }
- if (cert) {
- CERT_DestroyCertificate(cert);
- }
- return SSL_SNI_SEND_ALERT;
-}
-
-
-/**************************************************************************
-** Begin thread management routines and data.
-**************************************************************************/
-#define MIN_THREADS 3
-#define DEFAULT_THREADS 8
-#define MAX_THREADS 4096
-#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 PRLock * lastLoadedCrlLock; /* this lock protects lastLoadedCrl variable */
-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);
-
- /* create monitor for crl reload procedure */
- lastLoadedCrlLock = PR_NewLock();
-
- /* 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);
- }
- /* The worker threads empty the jobQ before they terminate. */
- PORT_Assert(PR_CLIST_IS_EMPTY(&jobQ));
- PZ_Unlock(qLock);
-
- DESTROY_CONDVAR(jobQNotEmptyCv);
- DESTROY_CONDVAR(freeListNotEmptyCv);
- DESTROY_CONDVAR(threadCountChangeCv);
-
- PR_DestroyLock(lastLoadedCrlLock);
- 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_TicksPerSecond();
- PRFloat64 secondsPerTick = 1.0 / (PRFloat64)logPeriodTicks;
- int iterations = 0;
- int secondsElapsed = 0;
- static PRInt64 totalPeriodBytes = 0;
- static PRInt64 totalPeriodBytesTCP = 0;
-
- previousOps = loggerOps;
- previousTime = PR_IntervalNow();
-
- for (;;) {
- /* OK, implementing a new sleep algorithm here... always sleep
- * for 1 second but print out info at the user-specified interval.
- * This way, we don't overflow all of our PR_Atomic* functions and
- * we don't have to use locks.
- */
- PR_Sleep(logPeriodTicks);
- secondsElapsed++;
- totalPeriodBytes += PR_ATOMIC_SET(&loggerBytes, 0);
- totalPeriodBytesTCP += PR_ATOMIC_SET(&loggerBytesTCP, 0);
- if (secondsElapsed != logPeriod) {
- continue;
- }
- /* when we reach the user-specified logging interval, print out all
- * data
- */
- secondsElapsed = 0;
- latestTime = PR_IntervalNow();
- ops = loggerOps;
- period = latestTime - previousTime;
- seconds = (PRFloat64) period*secondsPerTick;
- opsPerSec = (ops - previousOps) / seconds;
-
- if (testBulk) {
- if (iterations == 0) {
- if (loggingLayer == PR_TRUE) {
- printf("Conn.--------App Data--------TCP Data\n");
- } else {
- printf("Conn.--------App Data\n");
- }
- }
- if (loggingLayer == PR_TRUE) {
- printf("%4.d %5.3f MB/s %5.3f MB/s\n", ops,
- totalPeriodBytes / (seconds * 1048576.0),
- totalPeriodBytesTCP / (seconds * 1048576.0));
- } else {
- printf("%4.d %5.3f MB/s\n", ops,
- totalPeriodBytes / (seconds * 1048576.0));
- }
- totalPeriodBytes = 0;
- totalPeriodBytesTCP = 0;
- /* Print the "legend" every 20 iterations */
- iterations = (iterations + 1) % 20;
- } else {
- printf("%.2f ops/second, %d threads\n", opsPerSec, threadCount);
- }
-
- fflush(stdout);
- previousOps = ops;
- previousTime = latestTime;
- if (stopping) {
- break;
- }
- }
-}
-
-
-/**************************************************************************
-** End thread management routines.
-**************************************************************************/
-
-PRBool useModelSocket = PR_FALSE;
-static SSLVersionRange enabledVersions;
-PRBool enableSSL2 = PR_TRUE;
-PRBool disableRollBack = PR_FALSE;
-PRBool NoReuse = PR_FALSE;
-PRBool hasSidCache = PR_FALSE;
-PRBool disableStepDown = PR_FALSE;
-PRBool bypassPKCS11 = PR_FALSE;
-PRBool disableLocking = PR_FALSE;
-PRBool testbypass = PR_FALSE;
-PRBool enableSessionTickets = PR_FALSE;
-PRBool enableCompression = PR_FALSE;
-PRBool failedToNegotiateName = PR_FALSE;
-static char *virtServerNameArray[MAX_VIRT_SERVER_NAME_ARRAY_INDEX];
-static int virtServerNameIndex = 1;
-
-
-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"
-};
-static const char crlCacheErr[] = { "CRL ReCache Error: " };
-
-PRUint16 cipherlist[100];
-int nciphers;
-
-void
-savecipher(int c)
-{
- if (nciphers < sizeof cipherlist / sizeof (cipherlist[0]))
- cipherlist[nciphers++] = (PRUint16)c;
-}
-
-
-#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 if (tcp_sock) {
- PR_Close(tcp_sock);
- }
-
- VLOG(("selfserv: handle_fdx_connection: exiting"));
- return SECSuccess;
-}
-
-#endif
-
-static SECItem *lastLoadedCrl = NULL;
-
-static SECStatus
-reload_crl(PRFileDesc *crlFile)
-{
- SECItem *crlDer;
- CERTCertDBHandle *certHandle = CERT_GetDefaultCertDB();
- SECStatus rv;
-
- /* Read in the entire file specified with the -f argument */
- crlDer = PORT_Malloc(sizeof(SECItem));
- if (!crlDer) {
- errWarn("Can not allocate memory.");
- return SECFailure;
- }
-
- rv = SECU_ReadDERFromFile(crlDer, crlFile, PR_FALSE);
- if (rv != SECSuccess) {
- errWarn("Unable to read input file.");
- PORT_Free(crlDer);
- return SECFailure;
- }
-
- PR_Lock(lastLoadedCrlLock);
- rv = CERT_CacheCRL(certHandle, crlDer);
- if (rv == SECSuccess) {
- SECItem *tempItem = crlDer;
- if (lastLoadedCrl != NULL) {
- rv = CERT_UncacheCRL(certHandle, lastLoadedCrl);
- if (rv != SECSuccess) {
- errWarn("Unable to uncache crl.");
- goto loser;
- }
- crlDer = lastLoadedCrl;
- } else {
- crlDer = NULL;
- }
- lastLoadedCrl = tempItem;
- }
-
- loser:
- PR_Unlock(lastLoadedCrlLock);
- SECITEM_FreeItem(crlDer, PR_TRUE);
- return rv;
-}
-
-void stop_server()
-{
- stopping = 1;
- PR_Interrupt(acceptorThread);
- PZ_TraceFlush();
-}
-
-SECItemArray *
-makeTryLaterOCSPResponse(PRArenaPool *arena)
-{
- SECItemArray *result = NULL;
- SECItem *ocspResponse = NULL;
-
- ocspResponse = CERT_CreateEncodedOCSPErrorResponse(arena,
- SEC_ERROR_OCSP_TRY_SERVER_LATER);
- if (!ocspResponse)
- errExit("cannot created ocspResponse");
-
- result = SECITEM_AllocArray(arena, NULL, 1);
- if (!result)
- errExit("cannot allocate multiOcspResponses");
-
- result->items[0].data = ocspResponse->data;
- result->items[0].len = ocspResponse->len;
-
- return result;
-}
-
-SECItemArray *
-makeCorruptedOCSPResponse(PRArenaPool *arena)
-{
- SECItemArray *result = NULL;
- SECItem *ocspResponse = NULL;
-
- ocspResponse = SECITEM_AllocItem(arena, NULL, 1);
- if (!ocspResponse)
- errExit("cannot created ocspResponse");
-
- result = SECITEM_AllocArray(arena, NULL, 1);
- if (!result)
- errExit("cannot allocate multiOcspResponses");
-
- result->items[0].data = ocspResponse->data;
- result->items[0].len = ocspResponse->len;
-
- return result;
-}
-
-SECItemArray *
-makeSignedOCSPResponse(PRArenaPool *arena, ocspStaplingModeType osm,
- PRFileDesc *model_sock, CERTCertificate *cert)
-{
- SECItemArray *result = NULL;
- SECItem *ocspResponse = NULL;
- CERTOCSPSingleResponse **singleResponses;
- CERTOCSPSingleResponse *sr;
- CERTOCSPCertID *cid = NULL;
- CERTCertificate *ca;
- PRTime now = PR_Now();
- PRTime nextUpdate;
- const secuPWData *pwdata;
-
- PORT_Assert(model_sock != NULL && cert != NULL);
-
- ca = CERT_FindCertByNickname(CERT_GetDefaultCertDB(), ocspStaplingCA);
- if (!ca)
- errExit("cannot find CA");
-
- cid = CERT_CreateOCSPCertID(cert, now);
- if (!cid)
- errExit("cannot created cid");
-
- nextUpdate = now + 60*60*24 * PR_USEC_PER_SEC; /* plus 1 day */
-
- switch (osm) {
- case osm_good:
- case osm_badsig:
- sr = CERT_CreateOCSPSingleResponseGood(arena, cid, now,
- &nextUpdate);
- break;
- case osm_unknown:
- sr = CERT_CreateOCSPSingleResponseUnknown(arena, cid, now,
- &nextUpdate);
- break;
- case osm_revoked:
- sr = CERT_CreateOCSPSingleResponseRevoked(arena, cid, now,
- &nextUpdate,
- now - 60*60*24 * PR_USEC_PER_SEC, /* minus 1 day */
- NULL);
- break;
- default:
- PORT_Assert(0);
- break;
- }
-
- if (!sr)
- errExit("cannot create sr");
-
- /* meaning of value 2: one entry + one end marker */
- singleResponses = PORT_ArenaNewArray(arena, CERTOCSPSingleResponse*, 2);
- if (singleResponses == NULL)
- errExit("cannot allocate singleResponses");
-
- singleResponses[0] = sr;
- singleResponses[1] = NULL;
-
- pwdata = SSL_RevealPinArg(model_sock);
-
- ocspResponse = CERT_CreateEncodedOCSPSuccessResponse(arena,
- (osm == osm_badsig) ? NULL : ca,
- ocspResponderID_byName, now, singleResponses,
- &pwdata);
- if (!ocspResponse)
- errExit("cannot created ocspResponse");
-
- CERT_DestroyCertificate(ca);
- ca = NULL;
-
- result = SECITEM_AllocArray(arena, NULL, 1);
- if (!result)
- errExit("cannot allocate multiOcspResponses");
-
- result->items[0].data = ocspResponse->data;
- result->items[0].len = ocspResponse->len;
-
- CERT_DestroyOCSPCertID(cid);
- cid = NULL;
-
- return result;
-}
-
-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];
- char proto[128];
- PRDescIdentity aboveLayer = PR_INVALID_IO_LAYER;
- PRArenaPool *arena = NULL;
- ocspStaplingModeType osm;
-
- pBuf = buf;
- bufRem = 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;
- }
-
- arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
- if (!arena)
- errExit("cannot allocate arena");
-
- osm = ocspStaplingMode;
- if (osm == osm_random) {
- /* 6 different responses */
- int r = rand() % 6;
- switch (r) {
- case 0: osm = osm_good; break;
- case 1: osm = osm_revoked; break;
- case 2: osm = osm_unknown; break;
- case 3: osm = osm_badsig; break;
- case 4: osm = osm_corrupted; break;
- case 5: osm = osm_failure; break;
- default: PORT_Assert(0); break;
- }
- }
- if (osm != osm_disabled) {
- SECItemArray *multiOcspResponses = NULL;
- switch (osm) {
- case osm_good:
- case osm_revoked:
- case osm_unknown:
- case osm_badsig:
- multiOcspResponses =
- makeSignedOCSPResponse(arena, osm, ssl_sock,
- certForStatusWeakReference);
- break;
- case osm_corrupted:
- multiOcspResponses = makeCorruptedOCSPResponse(arena);
- break;
- case osm_failure:
- multiOcspResponses = makeTryLaterOCSPResponse(arena);
- break;
- case osm_ocsp:
- errExit("stapling mode \"ocsp\" not implemented");
- break;
- break;
- default:
- break;
- }
-
- if (multiOcspResponses) {
- SSL_SetStapledOCSPResponses(ssl_sock, multiOcspResponses,
- PR_FALSE /* no ownership transfer */);
- }
- }
-
- PORT_FreeArena(arena, PR_FALSE);
- arena = NULL;
-
- if (loggingLayer) {
- /* find the layer where our new layer is to be pushed */
- aboveLayer = PR_GetLayersIdentity(ssl_sock->lower);
- if (aboveLayer == PR_INVALID_IO_LAYER) {
- errExit("PRGetUniqueIdentity");
- }
- /* create the new layer - this is a very cheap operation */
- loggingFD = PR_CreateIOLayerStub(log_layer_id, &loggingMethods);
- if (!loggingFD)
- errExit("PR_CreateIOLayerStub");
- /* push the layer below ssl but above TCP */
- rv = PR_PushIOLayer(ssl_sock, aboveLayer, loggingFD);
- if (rv != PR_SUCCESS) {
- errExit("PR_PushIOLayer");
- }
- }
-
- 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)");
- if (ssl_sock) {
- PR_Close(ssl_sock);
- }
- return SECFailure;
- }
- }
-
- while (1) {
- newln = 0;
- reqLen = 0;
- rv = PR_Read(ssl_sock, pBuf, bufRem - 1);
- 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;
- }
- /* NULL termination */
- pBuf[rv] = 0;
- 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 successful, then write it to the client.
- */
- fnEnd = strpbrk(fnBegin, " \r\n");
- if (fnEnd) {
- int fnLen = fnEnd - fnBegin;
- if (fnLen < sizeof fileName) {
- char *real_fileName = fileName;
- char *protoEnd = NULL;
- strncpy(fileName, fnBegin, fnLen);
- fileName[fnLen] = 0; /* null terminate */
- if ((protoEnd = strstr(fileName, "://")) != NULL) {
- int protoLen = PR_MIN(protoEnd - fileName, sizeof(proto) - 1);
- PL_strncpy(proto, fileName, protoLen);
- proto[protoLen] = 0;
- real_fileName= protoEnd + 3;
- } else {
- proto[0] = 0;
- }
- status = PR_GetFileInfo(real_fileName, &info);
- if (status == PR_SUCCESS &&
- info.type == PR_FILE_FILE &&
- info.size >= 0 ) {
- local_file_fd = PR_Open(real_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;
- if (!PL_strlen(proto) || !PL_strcmp(proto, "file")) {
- 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);
- errLen = PR_MIN(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++;
- }
- if (!PL_strcmp(proto, "crl")) {
- if (reload_crl(local_file_fd) == SECFailure) {
- errString = errWarn("CERT_CacheCRL");
- if (!errString)
- errString = "Unknow error";
- PR_snprintf(msgBuf, sizeof(msgBuf), "%s%s ",
- crlCacheErr, errString);
-
- iovs[numIOVs].iov_base = msgBuf;
- iovs[numIOVs].iov_len = PORT_Strlen(msgBuf);
- numIOVs++;
- } else {
- FPRINTF(stderr,
- "selfserv: CRL %s reloaded.\n",
- fileName);
- break;
- }
- }
- } 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++;
- }
-
- /* Don't add the EOF if we want to test bulk encryption */
- if (!testBulk) {
- 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;
- }
-
- /* Send testBulkTotal chunks to the client. Unlimited if 0. */
- if (testBulk) {
- while (0 < (rv = PR_Write(ssl_sock, testBulkBuf, testBulkSize))) {
- PR_ATOMIC_ADD(&loggerBytes, rv);
- PR_ATOMIC_INCREMENT(&bulkSentChunks);
- if ((bulkSentChunks > testBulkTotal) && (testBulkTotal != 0))
- break;
- }
-
- /* There was a write error, so close this connection. */
- if (bulkSentChunks <= testBulkTotal) {
- errWarn("PR_Write");
- }
- PR_ATOMIC_DECREMENT(&loggerOps);
- break;
- }
- } while (0);
-
-cleanup:
- if (ssl_sock) {
- PR_Close(ssl_sock);
- } else if (tcp_sock) {
- PR_Close(tcp_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)) {
- VLOG(("selfserv: handle_connection: stop command"));
- stop_server();
- }
- VLOG(("selfserv: handle_connection: exiting"));
- return SECSuccess; /* success */
-}
-
-#ifdef XP_UNIX
-
-void sigusr1_handler(int sig)
-{
- VLOG(("selfserv: sigusr1_handler: stop server"));
- stop_server();
-}
-
-#endif
-
-SECStatus
-do_accepts(
- PRFileDesc *listen_sock,
- PRFileDesc *model_sock,
- int requestCert
- )
-{
- PRNetAddr addr;
- PRErrorCode perr;
-#ifdef XP_UNIX
- struct sigaction act;
-#endif
-
- VLOG(("selfserv: do_accepts: starting"));
- PR_SetThreadPriority( PR_GetCurrentThread(), PR_PRIORITY_HIGH);
-
- acceptorThread = PR_GetCurrentThread();
-#ifdef XP_UNIX
- /* set up the signal handler */
- act.sa_handler = sigusr1_handler;
- sigemptyset(&act.sa_mask);
- act.sa_flags = 0;
- if (sigaction(SIGUSR1, &act, NULL)) {
- fprintf(stderr, "Error installing signal handler.\n");
- exit(1);
- }
-#endif
- 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) {
- PR_ATOMIC_INCREMENT(&loggerOps);
- }
-
- PZ_Lock(qLock);
- while (PR_CLIST_IS_EMPTY(&freeJobs) && !stopping) {
- PZ_WaitCondVar(freeListNotEmptyCv, PR_INTERVAL_NO_TIMEOUT);
- }
- if (stopping) {
- PZ_Unlock(qLock);
- if (tcp_sock) {
- 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"));
- if (listen_sock) {
- 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) {
- PR_Close(listen_sock);
- 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) {
- PR_Close(listen_sock);
- errExit("PR_SetSocketOption(PR_SockOpt_Reuseaddr)");
- }
-
-#ifndef WIN95
- /* Set PR_SockOpt_Linger because it helps prevent a server bind issue
- * after clean shutdown . See bug 331413 .
- * Don't do it in the WIN95 build configuration because clean shutdown is
- * not implemented, and PR_SockOpt_Linger causes a hang in ssl.sh .
- * See bug 332348 */
- opt.option=PR_SockOpt_Linger;
- opt.value.linger.polarity = PR_TRUE;
- opt.value.linger.linger = PR_SecondsToInterval(1);
- prStatus = PR_SetSocketOption(listen_sock, &opt);
- if (prStatus < 0) {
- PR_Close(listen_sock);
- errExit("PR_SetSocketOption(PR_SockOpt_Linger)");
- }
-#endif
-
- prStatus = PR_Bind(listen_sock, &addr);
- if (prStatus < 0) {
- PR_Close(listen_sock);
- errExit("PR_Bind");
- }
-
- prStatus = PR_Listen(listen_sock, listenQueueDepth);
- if (prStatus < 0) {
- PR_Close(listen_sock);
- errExit("PR_Listen");
- }
- return listen_sock;
-}
-
-PRInt32 PR_CALLBACK
-logWritev (
- PRFileDesc *fd,
- const PRIOVec *iov,
- PRInt32 size,
- PRIntervalTime timeout )
-{
- PRInt32 rv = (fd->lower->methods->writev)(fd->lower, iov, size,
- timeout);
- /* Add the amount written, but not if there's an error */
- if (rv > 0)
- PR_ATOMIC_ADD(&loggerBytesTCP, rv);
- return rv;
-}
-
-PRInt32 PR_CALLBACK
-logWrite (
- PRFileDesc *fd,
- const void *buf,
- PRInt32 amount)
-{
- PRInt32 rv = (fd->lower->methods->write)(fd->lower, buf, amount);
- /* Add the amount written, but not if there's an error */
- if (rv > 0)
- PR_ATOMIC_ADD(&loggerBytesTCP, rv);
-
- return rv;
-}
-
-PRInt32 PR_CALLBACK
-logSend (
- PRFileDesc *fd,
- const void *buf,
- PRInt32 amount,
- PRIntn flags,
- PRIntervalTime timeout)
-{
- PRInt32 rv = (fd->lower->methods->send)(fd->lower, buf, amount,
- flags, timeout);
- /* Add the amount written, but not if there's an error */
- if (rv > 0)
- PR_ATOMIC_ADD(&loggerBytesTCP, rv);
- return rv;
-}
-
-void initLoggingLayer(void)
-{
- /* get a new layer ID */
- log_layer_id = PR_GetUniqueIdentity("Selfserv Logging");
- if (log_layer_id == PR_INVALID_IO_LAYER)
- errExit("PR_GetUniqueIdentity");
-
- /* setup the default IO methods with my custom write methods */
- memcpy(&loggingMethods, PR_GetDefaultIOMethods(), sizeof(PRIOMethods));
- loggingMethods.writev = logWritev;
- loggingMethods.write = logWrite;
- loggingMethods.send = logSend;
-}
-
-void
-handshakeCallback(PRFileDesc *fd, void *client_data)
-{
- const char *handshakeName = (const char *)client_data;
- if (handshakeName && !failedToNegotiateName) {
- SECItem *hostInfo = SSL_GetNegotiatedHostInfo(fd);
- if (!hostInfo || PORT_Strncmp(handshakeName, (char*)hostInfo->data,
- hostInfo->len)) {
- failedToNegotiateName = PR_TRUE;
- }
- }
-}
-
-void
-server_main(
- PRFileDesc * listen_sock,
- int requestCert,
- SECKEYPrivateKey ** privKey,
- CERTCertificate ** cert,
- const char *expectedHostNameVal)
-{
- 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. */
- rv = SSL_OptionSet(model_sock, SSL_SECURITY,
- enableSSL2 || enabledVersions.min != 0);
- if (rv < 0) {
- errExit("SSL_OptionSet SSL_SECURITY");
- }
-
- rv = SSL_VersionRangeSet(model_sock, &enabledVersions);
- if (rv != SECSuccess) {
- errExit("error setting SSL/TLS version range ");
- }
-
- rv = SSL_OptionSet(model_sock, SSL_ENABLE_SSL2, enableSSL2);
- if (rv != SECSuccess) {
- errExit("error enabling SSLv2 ");
- }
-
- rv = SSL_OptionSet(model_sock, SSL_ROLLBACK_DETECTION, !disableRollBack);
- if (rv != SECSuccess) {
- errExit("error enabling RollBack detection ");
- }
- if (disableStepDown) {
- rv = SSL_OptionSet(model_sock, SSL_NO_STEP_DOWN, PR_TRUE);
- if (rv != SECSuccess) {
- errExit("error disabling SSL StepDown ");
- }
- }
- if (bypassPKCS11) {
- rv = SSL_OptionSet(model_sock, SSL_BYPASS_PKCS11, PR_TRUE);
- if (rv != SECSuccess) {
- errExit("error enabling PKCS11 bypass ");
- }
- }
- if (disableLocking) {
- rv = SSL_OptionSet(model_sock, SSL_NO_LOCKS, PR_TRUE);
- if (rv != SECSuccess) {
- errExit("error disabling SSL socket locking ");
- }
- }
- if (enableSessionTickets) {
- rv = SSL_OptionSet(model_sock, SSL_ENABLE_SESSION_TICKETS, PR_TRUE);
- if (rv != SECSuccess) {
- errExit("error enabling Session Ticket extension ");
- }
- }
-
- if (enableCompression) {
- rv = SSL_OptionSet(model_sock, SSL_ENABLE_DEFLATE, PR_TRUE);
- if (rv != SECSuccess) {
- errExit("error enabling compression ");
- }
- }
-
- if (virtServerNameIndex >1) {
- rv = SSL_SNISocketConfigHook(model_sock, mySSLSNISocketConfig,
- (void*)&virtServerNameArray);
- if (rv != SECSuccess) {
- errExit("error enabling SNI extension ");
- }
- }
-
- for (kea = kt_rsa; kea < kt_kea_size; kea++) {
- if (cert[kea] != NULL) {
- if (!certForStatusWeakReference)
- certForStatusWeakReference = cert[kea];
-
- 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");
- }
- }
-
- if (NoReuse) {
- rv = SSL_OptionSet(model_sock, SSL_NO_CACHE, 1);
- if (rv < 0) {
- errExit("SSL_OptionSet SSL_NO_CACHE");
- }
- }
-
- /* 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 (expectedHostNameVal) {
- SSL_HandshakeCallback(model_sock, handshakeCallback,
- (void*)expectedHostNameVal);
- }
-
- 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) {
- if (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:
- if (local_file_fd) {
- 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);
-}
-
-#define HEXCHAR_TO_INT(c, i) \
- if (((c) >= '0') && ((c) <= '9')) { \
- i = (c) - '0'; \
- } else if (((c) >= 'a') && ((c) <= 'f')) { \
- i = (c) - 'a' + 10; \
- } else if (((c) >= 'A') && ((c) <= 'F')) { \
- i = (c) - 'A' + 10; \
- } else if ((c) == '\0') { \
- fprintf(stderr, "Invalid length of cipher string (-c :WXYZ).\n"); \
- exit(9); \
- } else { \
- fprintf(stderr, "Non-hex char in cipher string (-c :WXYZ).\n"); \
- exit(9); \
- }
-
-SECStatus enableOCSPStapling(const char* mode)
-{
- if (!strcmp(mode, "good")) {
- ocspStaplingMode = osm_good;
- return SECSuccess;
- }
- if (!strcmp(mode, "unknown")) {
- ocspStaplingMode = osm_unknown;
- return SECSuccess;
- }
- if (!strcmp(mode, "revoked")) {
- ocspStaplingMode = osm_revoked;
- return SECSuccess;
- }
- if (!strcmp(mode, "badsig")) {
- ocspStaplingMode = osm_badsig;
- return SECSuccess;
- }
- if (!strcmp(mode, "corrupted")) {
- ocspStaplingMode = osm_corrupted;
- return SECSuccess;
- }
- if (!strcmp(mode, "failure")) {
- ocspStaplingMode = osm_failure;
- return SECSuccess;
- }
- if (!strcmp(mode, "random")) {
- ocspStaplingMode = osm_random;
- return SECSuccess;
- }
- if (!strcmp(mode, "ocsp")) {
- ocspStaplingMode = osm_ocsp;
- return SECSuccess;
- }
- return SECFailure;
-}
-
-int
-main(int argc, char **argv)
-{
- char * progName = NULL;
- char * nickName = NULL;
-#ifdef NSS_ENABLE_ECC
- char * ecNickName = NULL;
-#endif
- const char * fileName = NULL;
- char * cipherString= NULL;
- const char * dir = ".";
- char * passwd = NULL;
- char * pwfile = 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 = NULL;
- PRBool debugCache = PR_FALSE; /* bug 90518 */
- char emptyString[] = { "" };
- char* certPrefix = emptyString;
- PRUint32 protos = 0;
- SSL3Statistics *ssl3stats;
- PRUint32 i;
- secuPWData pwdata = { PW_NONE, 0 };
- char *expectedHostNameVal = NULL;
-
- 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);
- SSL_VersionRangeGetSupported(ssl_variant_stream, &enabledVersions);
-
- /* please keep this list of options in ASCII collating sequence.
- ** numbers, then capital letters, then lower case, alphabetical.
- */
- optstate = PL_CreateOptState(argc, argv,
- "2:A:BC:DEL:M:NP:RT:V:Ya:bc:d:e:f:g:hi:jk:lmn:op:qrst:uvw:xyz");
- while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) {
- ++optionsFound;
- switch(optstate->option) {
- case '2': fileName = optstate->value; break;
-
- case 'A': ocspStaplingCA = PORT_Strdup(optstate->value); break;
-
- case 'B': bypassPKCS11 = PR_TRUE; break;
-
- case 'C': if (optstate->value) NumSidCacheEntries = PORT_Atoi(optstate->value); break;
-
- case 'D': noDelay = PR_TRUE; break;
- case 'E': disableStepDown = PR_TRUE; break;
-
- case 'I': /* reserved for OCSP multi-stapling */ break;
-
- case 'L':
- logStats = PR_TRUE;
- if (optstate->value == NULL) {
- logPeriod = 30;
- } else {
- 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 'N': NoReuse = PR_TRUE; break;
-
- case 'R': disableRollBack = PR_TRUE; break;
-
- case 'T':
- if (enableOCSPStapling(optstate->value) != SECSuccess) {
- fprintf(stderr, "Invalid OCSP stapling mode.\n");
- fprintf(stderr, "Run '%s -h' for usage information.\n", progName);
- exit(53);
- }
- break;
-
- case 'V': if (SECU_ParseSSLVersionRangeString(optstate->value,
- enabledVersions, enableSSL2,
- &enabledVersions, &enableSSL2) != SECSuccess) {
- Usage(progName);
- }
- break;
-
- case 'Y': PrintCipherUsage(progName); exit(0); break;
-
- case 'a': if (virtServerNameIndex >= MAX_VIRT_SERVER_NAME_ARRAY_INDEX) {
- Usage(progName);
- }
- virtServerNameArray[virtServerNameIndex++] =
- PORT_Strdup(optstate->value); break;
-
- case 'b': bindOnly = PR_TRUE; break;
-
- case 'c': cipherString = PORT_Strdup(optstate->value); break;
-
- case 'd': dir = optstate->value; break;
-
-#ifdef NSS_ENABLE_ECC
- case 'e': ecNickName = PORT_Strdup(optstate->value); break;
-#endif /* NSS_ENABLE_ECC */
-
- case 'f':
- pwdata.source = PW_FROMFILE;
- pwdata.data = pwfile = PORT_Strdup(optstate->value);
- break;
-
- case 'g':
- testBulk = PR_TRUE;
- testBulkTotal = PORT_Atoi(optstate->value);
- break;
-
- case 'h': Usage(progName); exit(0); break;
-
- case 'i': pidFile = optstate->value; break;
-
- case 'j':
- initLoggingLayer();
- loggingLayer = PR_TRUE;
- break;
-
- case 'k': expectedHostNameVal = PORT_Strdup(optstate->value);
- break;
-
- case 'l': useLocalThreads = PR_TRUE; break;
-
- case 'm': useModelSocket = PR_TRUE; break;
-
- case 'n': nickName = PORT_Strdup(optstate->value);
- virtServerNameArray[0] = PORT_Strdup(optstate->value);
- break;
-
- case 'P': certPrefix = PORT_Strdup(optstate->value); break;
-
- case 'o': MakeCertOK = 1; break;
-
- case 'p': port = PORT_Atoi(optstate->value); break;
-
- case 'q': testbypass = PR_TRUE; break;
-
- case 'r': ++requestCert; break;
-
- case 's': disableLocking = PR_TRUE; break;
-
- case 't':
- maxThreads = PORT_Atoi(optstate->value);
- if ( maxThreads > MAX_THREADS ) maxThreads = MAX_THREADS;
- if ( maxThreads < MIN_THREADS ) maxThreads = MIN_THREADS;
- break;
-
- case 'u': enableSessionTickets = PR_TRUE; break;
-
- case 'v': verbose++; break;
-
- case 'w':
- pwdata.source = PW_PLAINTEXT;
- pwdata.data = passwd = PORT_Strdup(optstate->value);
- break;
-
- case 'x': useExportPolicy = PR_TRUE; break;
-
- case 'y': debugCache = PR_TRUE; break;
-
- case 'z': enableCompression = 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);
- }
- switch (ocspStaplingMode) {
- case osm_good:
- case osm_revoked:
- case osm_unknown:
- case osm_random:
- if (!ocspStaplingCA) {
- fprintf(stderr, "Selected stapling response requires the -A parameter.\n");
- fprintf(stderr, "Run '%s -h' for usage information.\n", progName);
- exit(52);
- }
- break;
- default:
- break;
- }
-
- /* 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);
- }
- if (listen_sock) {
- PR_Close(listen_sock);
- }
- exit(0);
- }
-
- if ((nickName == NULL)
- #ifdef NSS_ENABLE_ECC
- && (ecNickName == NULL)
- #endif
- ) {
-
- 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 (NoReuse && maxProcs > 1) {
- fprintf(stderr, "-M and -N options are mutually exclusive.\n");
- exit(14);
- }
-
- if (pidFile) {
- FILE *tmpfile=fopen(pidFile,"w+");
-
- if (tmpfile) {
- fprintf(tmpfile,"%d",getpid());
- fclose(tmpfile);
- }
- }
-
- /* allocate and initialize app data for bulk encryption testing */
- if (testBulk) {
- testBulkBuf = PORT_Malloc(testBulkSize);
- if (testBulkBuf == NULL)
- errExit("Out of memory: testBulkBuf");
- for (i = 0; i < testBulkSize; i++)
- testBulkBuf[i] = i;
- }
-
- 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
- rv = SSL_InheritMPServerSIDCache(envString);
- if (rv != SECSuccess)
- errExit("SSL_InheritMPServerSIDCache");
- hasSidCache = PR_TRUE;
- } else if (maxProcs > 1) {
- /* we're going to be the parent in a multi-process server. */
- listen_sock = getBoundListenSocket(port);
- rv = SSL_ConfigMPServerSIDCache(NumSidCacheEntries, 0, 0, tmp);
- if (rv != SECSuccess)
- errExit("SSL_ConfigMPServerSIDCache");
- hasSidCache = PR_TRUE;
- 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");
- if (!NoReuse) {
- rv = SSL_ConfigServerSessionIDCache(NumSidCacheEntries,
- 0, 0, tmp);
- if (rv != SECSuccess)
- errExit("SSL_ConfigServerSessionIDCache");
- hasSidCache = PR_TRUE;
- }
- }
-
- lm = PR_NewLogModule("TestCase");
-
- if (fileName)
- readBigFile(fileName);
-
- /* set our password function */
- PK11_SetPasswordFunc(SECU_GetModulePassword);
-
- /* Call the NSS initialization routines */
- rv = NSS_Initialize(dir, certPrefix, certPrefix, SECMOD_DB, NSS_INIT_READONLY);
- 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();
- if (disableStepDown) {
- fputs("selfserv: -x and -E options may not be used together\n",
- stderr);
- exit(98);
- }
- } else {
- NSS_SetDomesticPolicy();
- if (disableStepDown) {
- rv = disableExportSSLCiphers();
- if (rv != SECSuccess) {
- errExit("error disabling export ciphersuites ");
- }
- }
- }
-
- /* all the SSL2 and SSL3 cipher suites are enabled by default. */
- if (cipherString) {
- char *cstringSaved = cipherString;
- int ndx;
-
- /* disable all the ciphers, then enable the ones we want. */
- disableAllSSLCiphers();
-
- while (0 != (ndx = *cipherString++)) {
- int cipher;
-
- if (ndx == ':') {
- int ctmp;
-
- cipher = 0;
- HEXCHAR_TO_INT(*cipherString, ctmp)
- cipher |= (ctmp << 12);
- cipherString++;
- HEXCHAR_TO_INT(*cipherString, ctmp)
- cipher |= (ctmp << 8);
- cipherString++;
- HEXCHAR_TO_INT(*cipherString, ctmp)
- cipher |= (ctmp << 4);
- cipherString++;
- HEXCHAR_TO_INT(*cipherString, ctmp)
- cipher |= ctmp;
- cipherString++;
- } else {
- const int *cptr;
-
- 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 > 0) {
- SECStatus status;
- status = SSL_CipherPrefSetDefault(cipher, SSL_ALLOWED);
- if (status != SECSuccess)
- SECU_PrintError(progName, "SSL_CipherPrefSet()");
- } else {
- fprintf(stderr,
- "Invalid cipher specification (-c arg).\n");
- exit(9);
- }
- }
- PORT_Free(cstringSaved);
- }
-
- if (testbypass) {
- const PRUint16 *cipherSuites = SSL_ImplementedCiphers;
- int i = SSL_NumImplementedCiphers;
- PRBool enabled;
-
- for (i=0; i < SSL_NumImplementedCiphers; i++, cipherSuites++) {
- if (SSL_CipherPrefGetDefault(*cipherSuites, &enabled) == SECSuccess
- && enabled)
- savecipher(*cipherSuites);
- }
- protos = 0;
- if (enabledVersions.min <= SSL_LIBRARY_VERSION_3_0 &&
- enabledVersions.max >= SSL_LIBRARY_VERSION_3_0) {
- protos |= SSL_CBP_SSL3;
- }
- if (enabledVersions.min <= SSL_LIBRARY_VERSION_TLS_1_0 &&
- enabledVersions.max >= SSL_LIBRARY_VERSION_TLS_1_0) {
- protos |= SSL_CBP_TLS1_0;
- }
- /* TLS 1.1 has the same SSL Bypass mode requirements as TLS 1.0 */
- if (enabledVersions.min <= SSL_LIBRARY_VERSION_TLS_1_1 &&
- enabledVersions.max >= SSL_LIBRARY_VERSION_TLS_1_1) {
- protos |= SSL_CBP_TLS1_0;
- }
- }
-
- if (nickName) {
- cert[kt_rsa] = PK11_FindCertFromNickname(nickName, &pwdata);
- 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], &pwdata);
- if (privKey[kt_rsa] == NULL) {
- fprintf(stderr, "selfserv: Can't find Private Key for cert %s\n",
- nickName);
- exit(11);
- }
- if (testbypass) {
- PRBool bypassOK;
- if (SSL_CanBypass(cert[kt_rsa], privKey[kt_rsa], protos, cipherlist,
- nciphers, &bypassOK, &pwdata) != SECSuccess) {
- SECU_PrintError(progName, "Bypass test failed %s\n", nickName);
- exit(14);
- }
- fprintf(stderr, "selfserv: %s can%s bypass\n", nickName,
- bypassOK ? "" : "not");
- }
- }
-#ifdef NSS_ENABLE_ECC
- if (ecNickName) {
- cert[kt_ecdh] = PK11_FindCertFromNickname(ecNickName, &pwdata);
- if (cert[kt_ecdh] == NULL) {
- fprintf(stderr, "selfserv: Can't find certificate %s\n",
- ecNickName);
- exit(13);
- }
- privKey[kt_ecdh] = PK11_FindKeyByAnyCert(cert[kt_ecdh], &pwdata);
- if (privKey[kt_ecdh] == NULL) {
- fprintf(stderr, "selfserv: Can't find Private Key for cert %s\n",
- ecNickName);
- exit(11);
- }
- if (testbypass) {
- PRBool bypassOK;
- if (SSL_CanBypass(cert[kt_ecdh], privKey[kt_ecdh], protos, cipherlist,
- nciphers, &bypassOK, &pwdata) != SECSuccess) {
- SECU_PrintError(progName, "Bypass test failed %s\n", ecNickName);
- exit(15);
- }
- fprintf(stderr, "selfserv: %s can%s bypass\n", ecNickName,
- bypassOK ? "" : "not");
- }
- }
-#endif /* NSS_ENABLE_ECC */
-
- if (testbypass)
- goto cleanup;
-
-/* 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_JOINABLE_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,
- expectedHostNameVal);
- }
-
- VLOG(("selfserv: server_thread: exiting"));
-
-cleanup:
- printSSLStatistics();
- ssl3stats = SSL_GetStatistics();
- if (ssl3stats->hch_sid_ticket_parse_failures != 0) {
- fprintf(stderr, "selfserv: Experienced ticket parse failure(s)\n");
- exit(1);
- }
- if (failedToNegotiateName) {
- fprintf(stderr, "selfserv: Failed properly negotiate server name\n");
- exit(1);
- }
-
- {
- int i;
- for (i=0; i<kt_kea_size; i++) {
- if (cert[i]) {
- CERT_DestroyCertificate(cert[i]);
- }
- if (privKey[i]) {
- SECKEY_DestroyPrivateKey(privKey[i]);
- }
- }
- for (i = 0;virtServerNameArray[i];i++) {
- PORT_Free(virtServerNameArray[i]);
- }
- }
-
- if (debugCache) {
- nss_DumpCertificateCacheInfo();
- }
- if (nickName) {
- PORT_Free(nickName);
- }
- if (expectedHostNameVal) {
- PORT_Free(expectedHostNameVal);
- }
- if (passwd) {
- PORT_Free(passwd);
- }
- if (pwfile) {
- PORT_Free(pwfile);
- }
- if (certPrefix && certPrefix != emptyString) {
- PORT_Free(certPrefix);
- }
- #ifdef NSS_ENABLE_ECC
- if (ecNickName) {
- PORT_Free(ecNickName);
- }
- #endif
-
- if (hasSidCache) {
- SSL_ShutdownServerSessionIDCache();
- }
- if (NSS_Shutdown() != SECSuccess) {
- SECU_PrintError(progName, "NSS_Shutdown");
- if (loggerThread) {
- PR_JoinThread(loggerThread);
- }
- PR_Cleanup();
- exit(1);
- }
- PR_Cleanup();
- printf("selfserv: normal termination\n");
- return 0;
-}
-