michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* -r flag is interepreted as follows: michael@0: * 1 -r means request, not require, on initial handshake. michael@0: * 2 -r's mean request and require, on initial handshake. michael@0: * 3 -r's mean request, not require, on second handshake. michael@0: * 4 -r's mean request and require, on second handshake. michael@0: */ michael@0: #include michael@0: #include michael@0: michael@0: #include "secutil.h" michael@0: michael@0: #if defined(XP_UNIX) michael@0: #include michael@0: #endif michael@0: michael@0: #if defined(_WINDOWS) michael@0: #include /* for getpid() */ michael@0: #endif michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #include "nspr.h" michael@0: #include "prio.h" michael@0: #include "prerror.h" michael@0: #include "prnetdb.h" michael@0: #include "prclist.h" michael@0: #include "plgetopt.h" michael@0: #include "pk11func.h" michael@0: #include "secitem.h" michael@0: #include "nss.h" michael@0: #include "ssl.h" michael@0: #include "sslproto.h" michael@0: #include "cert.h" michael@0: #include "certt.h" michael@0: #include "ocsp.h" michael@0: michael@0: #ifndef PORT_Sprintf michael@0: #define PORT_Sprintf sprintf michael@0: #endif michael@0: michael@0: #ifndef PORT_Strstr michael@0: #define PORT_Strstr strstr michael@0: #endif michael@0: michael@0: #ifndef PORT_Malloc michael@0: #define PORT_Malloc PR_Malloc michael@0: #endif michael@0: michael@0: int NumSidCacheEntries = 1024; michael@0: michael@0: static int handle_connection( PRFileDesc *, PRFileDesc *, int ); michael@0: michael@0: static const char envVarName[] = { SSL_ENV_VAR_NAME }; michael@0: static const char inheritableSockName[] = { "SELFSERV_LISTEN_SOCKET" }; michael@0: michael@0: #define DEFAULT_BULK_TEST 16384 michael@0: #define MAX_BULK_TEST 1048576 /* 1 MB */ michael@0: static PRBool testBulk; michael@0: static PRUint32 testBulkSize = DEFAULT_BULK_TEST; michael@0: static PRUint32 testBulkTotal; michael@0: static char* testBulkBuf; michael@0: static PRDescIdentity log_layer_id = PR_INVALID_IO_LAYER; michael@0: static PRFileDesc *loggingFD; michael@0: static PRIOMethods loggingMethods; michael@0: michael@0: static PRBool logStats; michael@0: static PRBool loggingLayer; michael@0: static int logPeriod = 30; michael@0: static PRUint32 loggerOps; michael@0: static PRUint32 loggerBytes; michael@0: static PRUint32 loggerBytesTCP; michael@0: static PRUint32 bulkSentChunks; michael@0: static enum ocspStaplingModeEnum { michael@0: osm_disabled, /* server doesn't support stapling */ michael@0: osm_good, /* supply a signed good status */ michael@0: osm_revoked, /* supply a signed revoked status */ michael@0: osm_unknown, /* supply a signed unknown status */ michael@0: osm_failure, /* supply a unsigned failure status, "try later" */ michael@0: osm_badsig, /* supply a good status response with a bad signature */ michael@0: osm_corrupted, /* supply a corrupted data block as the status */ michael@0: osm_random, /* use a random response for each connection */ michael@0: osm_ocsp /* retrieve ocsp status from external ocsp server, michael@0: use empty status if server is unavailable */ michael@0: } ocspStaplingMode = osm_disabled; michael@0: typedef enum ocspStaplingModeEnum ocspStaplingModeType; michael@0: static char *ocspStaplingCA = NULL; michael@0: static SECItemArray *certStatus[kt_kea_size] = { NULL }; michael@0: michael@0: const int ssl2CipherSuites[] = { michael@0: SSL_EN_RC4_128_WITH_MD5, /* A */ michael@0: SSL_EN_RC4_128_EXPORT40_WITH_MD5, /* B */ michael@0: SSL_EN_RC2_128_CBC_WITH_MD5, /* C */ michael@0: SSL_EN_RC2_128_CBC_EXPORT40_WITH_MD5, /* D */ michael@0: SSL_EN_DES_64_CBC_WITH_MD5, /* E */ michael@0: SSL_EN_DES_192_EDE3_CBC_WITH_MD5, /* F */ michael@0: 0 michael@0: }; michael@0: michael@0: const int ssl3CipherSuites[] = { michael@0: -1, /* SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA* a */ michael@0: -1, /* SSL_FORTEZZA_DMS_WITH_RC4_128_SHA * b */ michael@0: TLS_RSA_WITH_RC4_128_MD5, /* c */ michael@0: TLS_RSA_WITH_3DES_EDE_CBC_SHA, /* d */ michael@0: TLS_RSA_WITH_DES_CBC_SHA, /* e */ michael@0: TLS_RSA_EXPORT_WITH_RC4_40_MD5, /* f */ michael@0: TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5, /* g */ michael@0: -1, /* SSL_FORTEZZA_DMS_WITH_NULL_SHA, * h */ michael@0: TLS_RSA_WITH_NULL_MD5, /* i */ michael@0: SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA, /* j */ michael@0: SSL_RSA_FIPS_WITH_DES_CBC_SHA, /* k */ michael@0: TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA, /* l */ michael@0: TLS_RSA_EXPORT1024_WITH_RC4_56_SHA, /* m */ michael@0: TLS_RSA_WITH_RC4_128_SHA, /* n */ michael@0: -1, /* TLS_DHE_DSS_WITH_RC4_128_SHA, * o */ michael@0: -1, /* TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, * p */ michael@0: -1, /* TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA, * q */ michael@0: -1, /* TLS_DHE_RSA_WITH_DES_CBC_SHA, * r */ michael@0: -1, /* TLS_DHE_DSS_WITH_DES_CBC_SHA, * s */ michael@0: -1, /* TLS_DHE_DSS_WITH_AES_128_CBC_SHA, * t */ michael@0: -1, /* TLS_DHE_RSA_WITH_AES_128_CBC_SHA, * u */ michael@0: TLS_RSA_WITH_AES_128_CBC_SHA, /* v */ michael@0: -1, /* TLS_DHE_DSS_WITH_AES_256_CBC_SHA, * w */ michael@0: -1, /* TLS_DHE_RSA_WITH_AES_256_CBC_SHA, * x */ michael@0: TLS_RSA_WITH_AES_256_CBC_SHA, /* y */ michael@0: TLS_RSA_WITH_NULL_SHA, /* z */ michael@0: 0 michael@0: }; michael@0: michael@0: /* data and structures for shutdown */ michael@0: static int stopping; michael@0: michael@0: static PRBool noDelay; michael@0: static int requestCert; michael@0: static int verbose; michael@0: static SECItem bigBuf; michael@0: michael@0: static PRThread * acceptorThread; michael@0: michael@0: static PRLogModuleInfo *lm; michael@0: michael@0: #define PRINTF if (verbose) printf michael@0: #define FPRINTF if (verbose) fprintf michael@0: #define FLUSH if (verbose) { fflush(stdout); fflush(stderr); } michael@0: #define VLOG(arg) PR_LOG(lm,PR_LOG_DEBUG,arg) michael@0: michael@0: static void michael@0: PrintUsageHeader(const char *progName) michael@0: { michael@0: fprintf(stderr, michael@0: "Usage: %s -n rsa_nickname -p port [-BDENRbjlmrsuvx] [-w password]\n" michael@0: " [-t threads] [-i pid_file] [-c ciphers] [-Y] [-d dbdir] [-g numblocks]\n" michael@0: " [-f password_file] [-L [seconds]] [-M maxProcs] [-P dbprefix]\n" michael@0: " [-V [min-version]:[max-version]] [-a sni_name]\n" michael@0: " [ T ] [-A ca]\n" michael@0: #ifndef NSS_DISABLE_ECC michael@0: " [-C SSLCacheEntries] [-e ec_nickname]\n" michael@0: #else michael@0: " [-C SSLCacheEntries]\n" michael@0: #endif /* NSS_DISABLE_ECC */ michael@0: ,progName); michael@0: } michael@0: michael@0: static void michael@0: PrintParameterUsage() michael@0: { michael@0: fputs( michael@0: "-V [min]:[max] restricts the set of enabled SSL/TLS protocol versions.\n" michael@0: " All versions are enabled by default.\n" michael@0: " Possible values for min/max: ssl2 ssl3 tls1.0 tls1.1 tls1.2\n" michael@0: " Example: \"-V ssl3:\" enables SSL 3 and newer.\n" michael@0: "-B bypasses the PKCS11 layer for SSL encryption and MACing\n" michael@0: "-q checks for bypassability\n" michael@0: "-D means disable Nagle delays in TCP\n" michael@0: "-E means disable export ciphersuites and SSL step down key gen\n" michael@0: "-R means disable detection of rollback from TLS to SSL3\n" michael@0: "-a configure server for SNI.\n" michael@0: "-k expected name negotiated on server sockets\n" michael@0: "-b means try binding to the port and exit\n" michael@0: "-m means test the model-socket feature of SSL_ImportFD.\n" michael@0: "-r flag is interepreted as follows:\n" michael@0: " 1 -r means request, not require, cert on initial handshake.\n" michael@0: " 2 -r's mean request and require, cert on initial handshake.\n" michael@0: " 3 -r's mean request, not require, cert on second handshake.\n" michael@0: " 4 -r's mean request and require, cert on second handshake.\n" michael@0: "-s means disable SSL socket locking for performance\n" michael@0: "-u means enable Session Ticket extension for TLS.\n" michael@0: "-v means verbose output\n" michael@0: "-x means use export policy.\n" michael@0: "-z means enable compression.\n" michael@0: "-L seconds means log statistics every 'seconds' seconds (default=30).\n" michael@0: "-M maxProcs tells how many processes to run in a multi-process server\n" michael@0: "-N means do NOT use the server session cache. Incompatible with -M.\n" michael@0: "-t threads -- specify the number of threads to use for connections.\n" michael@0: "-i pid_file file to write the process id of selfserve\n" michael@0: "-l means use local threads instead of global threads\n" michael@0: "-g numblocks means test throughput by sending total numblocks chunks\n" michael@0: " of size 16kb to the client, 0 means unlimited (default=0)\n" michael@0: "-j means measure TCP throughput (for use with -g option)\n" michael@0: "-C SSLCacheEntries sets the maximum number of entries in the SSL\n" michael@0: " session cache\n" michael@0: "-T enable OCSP stapling. Possible modes:\n" michael@0: " none: don't send cert status (default)\n" michael@0: " good, revoked, unknown: Include locally signed response. Requires: -A\n" michael@0: " failure: return a failure response (try later, unsigned)\n" michael@0: " badsig: use a good status but with an invalid signature\n" michael@0: " corrupted: stapled cert status is an invalid block of data\n" michael@0: " random: each connection uses a random status from this list:\n" michael@0: " good, revoked, unknown, failure, badsig, corrupted\n" michael@0: " ocsp: fetch from external OCSP server using AIA, or none\n" michael@0: "-A Nickname of a CA used to sign a stapled cert status\n" michael@0: "-c Restrict ciphers\n" michael@0: "-Y prints cipher values allowed for parameter -c and exits\n" michael@0: , stderr); michael@0: } michael@0: michael@0: static void michael@0: Usage(const char *progName) michael@0: { michael@0: PrintUsageHeader(progName); michael@0: PrintParameterUsage(); michael@0: } michael@0: michael@0: static void michael@0: PrintCipherUsage(const char *progName) michael@0: { michael@0: PrintUsageHeader(progName); michael@0: fputs( michael@0: "-c ciphers Letter(s) chosen from the following list\n" michael@0: "A SSL2 RC4 128 WITH MD5\n" michael@0: "B SSL2 RC4 128 EXPORT40 WITH MD5\n" michael@0: "C SSL2 RC2 128 CBC WITH MD5\n" michael@0: "D SSL2 RC2 128 CBC EXPORT40 WITH MD5\n" michael@0: "E SSL2 DES 64 CBC WITH MD5\n" michael@0: "F SSL2 DES 192 EDE3 CBC WITH MD5\n" michael@0: "\n" michael@0: "c SSL3 RSA WITH RC4 128 MD5\n" michael@0: "d SSL3 RSA WITH 3DES EDE CBC SHA\n" michael@0: "e SSL3 RSA WITH DES CBC SHA\n" michael@0: "f SSL3 RSA EXPORT WITH RC4 40 MD5\n" michael@0: "g SSL3 RSA EXPORT WITH RC2 CBC 40 MD5\n" michael@0: "i SSL3 RSA WITH NULL MD5\n" michael@0: "j SSL3 RSA FIPS WITH 3DES EDE CBC SHA\n" michael@0: "k SSL3 RSA FIPS WITH DES CBC SHA\n" michael@0: "l SSL3 RSA EXPORT WITH DES CBC SHA\t(new)\n" michael@0: "m SSL3 RSA EXPORT WITH RC4 56 SHA\t(new)\n" michael@0: "n SSL3 RSA WITH RC4 128 SHA\n" michael@0: "v SSL3 RSA WITH AES 128 CBC SHA\n" michael@0: "y SSL3 RSA WITH AES 256 CBC SHA\n" michael@0: "z SSL3 RSA WITH NULL SHA\n" michael@0: "\n" michael@0: ":WXYZ Use cipher with hex code { 0xWX , 0xYZ } in TLS\n" michael@0: , stderr); michael@0: } michael@0: michael@0: static const char * michael@0: errWarn(char * funcString) michael@0: { michael@0: PRErrorCode perr = PR_GetError(); michael@0: const char * errString = SECU_Strerror(perr); michael@0: michael@0: fprintf(stderr, "selfserv: %s returned error %d:\n%s\n", michael@0: funcString, perr, errString); michael@0: return errString; michael@0: } michael@0: michael@0: static void michael@0: errExit(char * funcString) michael@0: { michael@0: errWarn(funcString); michael@0: exit(3); michael@0: } michael@0: michael@0: michael@0: /************************************************************************** michael@0: ** michael@0: ** Routines for disabling SSL ciphers. michael@0: ** michael@0: **************************************************************************/ michael@0: michael@0: /* disable all the SSL cipher suites */ michael@0: void michael@0: disableAllSSLCiphers(void) michael@0: { michael@0: const PRUint16 *cipherSuites = SSL_ImplementedCiphers; michael@0: int i = SSL_NumImplementedCiphers; michael@0: SECStatus rv; michael@0: michael@0: while (--i >= 0) { michael@0: PRUint16 suite = cipherSuites[i]; michael@0: rv = SSL_CipherPrefSetDefault(suite, PR_FALSE); michael@0: if (rv != SECSuccess) { michael@0: printf("SSL_CipherPrefSetDefault rejected suite 0x%04x (i = %d)\n", michael@0: suite, i); michael@0: errWarn("SSL_CipherPrefSetDefault"); michael@0: } michael@0: } michael@0: } michael@0: michael@0: /* disable all the export SSL cipher suites */ michael@0: SECStatus michael@0: disableExportSSLCiphers(void) michael@0: { michael@0: const PRUint16 *cipherSuites = SSL_ImplementedCiphers; michael@0: int i = SSL_NumImplementedCiphers; michael@0: SECStatus rv = SECSuccess; michael@0: SSLCipherSuiteInfo info; michael@0: michael@0: while (--i >= 0) { michael@0: PRUint16 suite = cipherSuites[i]; michael@0: SECStatus status; michael@0: status = SSL_GetCipherSuiteInfo(suite, &info, sizeof info); michael@0: if (status != SECSuccess) { michael@0: printf("SSL_GetCipherSuiteInfo rejected suite 0x%04x (i = %d)\n", michael@0: suite, i); michael@0: errWarn("SSL_GetCipherSuiteInfo"); michael@0: rv = SECFailure; michael@0: continue; michael@0: } michael@0: if (info.cipherSuite != suite) { michael@0: printf( michael@0: "SSL_GetCipherSuiteInfo returned wrong suite! Wanted 0x%04x, Got 0x%04x\n", michael@0: suite, i); michael@0: PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); michael@0: rv = SECFailure; michael@0: continue; michael@0: } michael@0: /* should check here that info.length >= offsetof isExportable */ michael@0: if (info.isExportable) { michael@0: status = SSL_CipherPolicySet(suite, SSL_NOT_ALLOWED); michael@0: if (status != SECSuccess) { michael@0: printf("SSL_CipherPolicySet rejected suite 0x%04x (i = %d)\n", michael@0: suite, i); michael@0: errWarn("SSL_CipherPolicySet"); michael@0: rv = SECFailure; michael@0: } michael@0: } michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: static SECStatus michael@0: mySSLAuthCertificate(void *arg, PRFileDesc *fd, PRBool checkSig, michael@0: PRBool isServer) michael@0: { michael@0: SECStatus rv; michael@0: CERTCertificate * peerCert; michael@0: michael@0: peerCert = SSL_PeerCertificate(fd); michael@0: michael@0: if (peerCert) { michael@0: PRINTF("selfserv: Subject: %s\nselfserv: Issuer : %s\n", michael@0: peerCert->subjectName, peerCert->issuerName); michael@0: CERT_DestroyCertificate(peerCert); michael@0: } michael@0: michael@0: rv = SSL_AuthCertificate(arg, fd, checkSig, isServer); michael@0: michael@0: if (rv == SECSuccess) { michael@0: PRINTF("selfserv: -- SSL3: Certificate Validated.\n"); michael@0: } else { michael@0: int err = PR_GetError(); michael@0: FPRINTF(stderr, "selfserv: -- SSL3: Certificate Invalid, err %d.\n%s\n", michael@0: err, SECU_Strerror(err)); michael@0: } michael@0: FLUSH; michael@0: return rv; michael@0: } michael@0: michael@0: void michael@0: printSSLStatistics() michael@0: { michael@0: SSL3Statistics * ssl3stats = SSL_GetStatistics(); michael@0: michael@0: printf( michael@0: "selfserv: %ld cache hits; %ld cache misses, %ld cache not reusable\n" michael@0: " %ld stateless resumes, %ld ticket parse failures\n", michael@0: ssl3stats->hch_sid_cache_hits, ssl3stats->hch_sid_cache_misses, michael@0: ssl3stats->hch_sid_cache_not_ok, ssl3stats->hch_sid_stateless_resumes, michael@0: ssl3stats->hch_sid_ticket_parse_failures); michael@0: } michael@0: michael@0: void michael@0: printSecurityInfo(PRFileDesc *fd) michael@0: { michael@0: CERTCertificate * cert = NULL; michael@0: SECStatus result; michael@0: SSLChannelInfo channel; michael@0: SSLCipherSuiteInfo suite; michael@0: michael@0: if (verbose) michael@0: printSSLStatistics(); michael@0: michael@0: result = SSL_GetChannelInfo(fd, &channel, sizeof channel); michael@0: if (result == SECSuccess && michael@0: channel.length == sizeof channel && michael@0: channel.cipherSuite) { michael@0: result = SSL_GetCipherSuiteInfo(channel.cipherSuite, michael@0: &suite, sizeof suite); michael@0: if (result == SECSuccess) { michael@0: FPRINTF(stderr, michael@0: "selfserv: SSL version %d.%d using %d-bit %s with %d-bit %s MAC\n", michael@0: channel.protocolVersion >> 8, channel.protocolVersion & 0xff, michael@0: suite.effectiveKeyBits, suite.symCipherName, michael@0: suite.macBits, suite.macAlgorithmName); michael@0: FPRINTF(stderr, michael@0: "selfserv: Server Auth: %d-bit %s, Key Exchange: %d-bit %s\n" michael@0: " Compression: %s\n", michael@0: channel.authKeyBits, suite.authAlgorithmName, michael@0: channel.keaKeyBits, suite.keaTypeName, michael@0: channel.compressionMethodName); michael@0: } michael@0: } michael@0: if (verbose) { michael@0: SECItem *hostInfo = SSL_GetNegotiatedHostInfo(fd); michael@0: if (hostInfo) { michael@0: char namePref[] = "selfserv: Negotiated server name: "; michael@0: michael@0: fprintf(stderr, "%s", namePref); michael@0: fwrite(hostInfo->data, hostInfo->len, 1, stderr); michael@0: SECITEM_FreeItem(hostInfo, PR_TRUE); michael@0: hostInfo = NULL; michael@0: fprintf(stderr, "\n"); michael@0: } michael@0: } michael@0: if (requestCert) michael@0: cert = SSL_PeerCertificate(fd); michael@0: else michael@0: cert = SSL_LocalCertificate(fd); michael@0: if (cert) { michael@0: char * ip = CERT_NameToAscii(&cert->issuer); michael@0: char * sp = CERT_NameToAscii(&cert->subject); michael@0: if (sp) { michael@0: FPRINTF(stderr, "selfserv: subject DN: %s\n", sp); michael@0: PORT_Free(sp); michael@0: } michael@0: if (ip) { michael@0: FPRINTF(stderr, "selfserv: issuer DN: %s\n", ip); michael@0: PORT_Free(ip); michael@0: } michael@0: CERT_DestroyCertificate(cert); michael@0: cert = NULL; michael@0: } michael@0: FLUSH; michael@0: } michael@0: michael@0: static int MakeCertOK; michael@0: michael@0: static SECStatus michael@0: myBadCertHandler( void *arg, PRFileDesc *fd) michael@0: { michael@0: int err = PR_GetError(); michael@0: if (!MakeCertOK) michael@0: fprintf(stderr, michael@0: "selfserv: -- SSL: Client Certificate Invalid, err %d.\n%s\n", michael@0: err, SECU_Strerror(err)); michael@0: return (MakeCertOK ? SECSuccess : SECFailure); michael@0: } michael@0: michael@0: #define MAX_VIRT_SERVER_NAME_ARRAY_INDEX 10 michael@0: michael@0: /* Simple SNI socket config function that does not use SSL_ReconfigFD. michael@0: * Only uses one server name but verifies that the names match. */ michael@0: PRInt32 michael@0: mySSLSNISocketConfig(PRFileDesc *fd, const SECItem *sniNameArr, michael@0: PRUint32 sniNameArrSize, void *arg) michael@0: { michael@0: PRInt32 i = 0; michael@0: const SECItem *current = sniNameArr; michael@0: const char **nameArr = (const char**)arg; michael@0: secuPWData *pwdata; michael@0: CERTCertificate * cert = NULL; michael@0: SECKEYPrivateKey * privKey = NULL; michael@0: michael@0: PORT_Assert(fd && sniNameArr); michael@0: if (!fd || !sniNameArr) { michael@0: return SSL_SNI_SEND_ALERT; michael@0: } michael@0: michael@0: pwdata = SSL_RevealPinArg(fd); michael@0: michael@0: for (;current && i < sniNameArrSize;i++) { michael@0: int j = 0; michael@0: for (;j < MAX_VIRT_SERVER_NAME_ARRAY_INDEX && nameArr[j];j++) { michael@0: if (!PORT_Strncmp(nameArr[j], michael@0: (const char *)current[i].data, michael@0: current[i].len) && michael@0: PORT_Strlen(nameArr[j]) == current[i].len) { michael@0: const char *nickName = nameArr[j]; michael@0: if (j == 0) { michael@0: /* default cert */ michael@0: return 0; michael@0: } michael@0: /* if pwdata is NULL, then we would not get the key and michael@0: * return an error status. */ michael@0: cert = PK11_FindCertFromNickname(nickName, &pwdata); michael@0: if (cert == NULL) { michael@0: goto loser; /* Send alert */ michael@0: } michael@0: privKey = PK11_FindKeyByAnyCert(cert, &pwdata); michael@0: if (privKey == NULL) { michael@0: goto loser; /* Send alert */ michael@0: } michael@0: if (SSL_ConfigSecureServer(fd, cert, privKey, michael@0: kt_rsa) != SECSuccess) { michael@0: goto loser; /* Send alert */ michael@0: } michael@0: SECKEY_DestroyPrivateKey(privKey); michael@0: CERT_DestroyCertificate(cert); michael@0: return i; michael@0: } michael@0: } michael@0: } michael@0: loser: michael@0: if (privKey) { michael@0: SECKEY_DestroyPrivateKey(privKey); michael@0: } michael@0: if (cert) { michael@0: CERT_DestroyCertificate(cert); michael@0: } michael@0: return SSL_SNI_SEND_ALERT; michael@0: } michael@0: michael@0: michael@0: /************************************************************************** michael@0: ** Begin thread management routines and data. michael@0: **************************************************************************/ michael@0: #define MIN_THREADS 3 michael@0: #define DEFAULT_THREADS 8 michael@0: #define MAX_THREADS 4096 michael@0: #define MAX_PROCS 25 michael@0: static int maxThreads = DEFAULT_THREADS; michael@0: michael@0: michael@0: typedef struct jobStr { michael@0: PRCList link; michael@0: PRFileDesc *tcp_sock; michael@0: PRFileDesc *model_sock; michael@0: int requestCert; michael@0: } JOB; michael@0: michael@0: static PZLock * qLock; /* this lock protects all data immediately below */ michael@0: static PRLock * lastLoadedCrlLock; /* this lock protects lastLoadedCrl variable */ michael@0: static PZCondVar * jobQNotEmptyCv; michael@0: static PZCondVar * freeListNotEmptyCv; michael@0: static PZCondVar * threadCountChangeCv; michael@0: static int threadCount; michael@0: static PRCList jobQ; michael@0: static PRCList freeJobs; michael@0: static JOB *jobTable; michael@0: michael@0: SECStatus michael@0: setupJobs(int maxJobs) michael@0: { michael@0: int i; michael@0: michael@0: jobTable = (JOB *)PR_Calloc(maxJobs, sizeof(JOB)); michael@0: if (!jobTable) michael@0: return SECFailure; michael@0: michael@0: PR_INIT_CLIST(&jobQ); michael@0: PR_INIT_CLIST(&freeJobs); michael@0: michael@0: for (i = 0; i < maxJobs; ++i) { michael@0: JOB * pJob = jobTable + i; michael@0: PR_APPEND_LINK(&pJob->link, &freeJobs); michael@0: } michael@0: return SECSuccess; michael@0: } michael@0: michael@0: typedef int startFn(PRFileDesc *a, PRFileDesc *b, int c); michael@0: michael@0: typedef enum { rs_idle = 0, rs_running = 1, rs_zombie = 2 } runState; michael@0: michael@0: typedef struct perThreadStr { michael@0: PRFileDesc *a; michael@0: PRFileDesc *b; michael@0: int c; michael@0: int rv; michael@0: startFn * startFunc; michael@0: PRThread * prThread; michael@0: runState state; michael@0: } perThread; michael@0: michael@0: static perThread *threads; michael@0: michael@0: void michael@0: thread_wrapper(void * arg) michael@0: { michael@0: perThread * slot = (perThread *)arg; michael@0: michael@0: slot->rv = (* slot->startFunc)(slot->a, slot->b, slot->c); michael@0: michael@0: /* notify the thread exit handler. */ michael@0: PZ_Lock(qLock); michael@0: slot->state = rs_zombie; michael@0: --threadCount; michael@0: PZ_NotifyAllCondVar(threadCountChangeCv); michael@0: PZ_Unlock(qLock); michael@0: } michael@0: michael@0: int michael@0: jobLoop(PRFileDesc *a, PRFileDesc *b, int c) michael@0: { michael@0: PRCList * myLink = 0; michael@0: JOB * myJob; michael@0: michael@0: PZ_Lock(qLock); michael@0: do { michael@0: myLink = 0; michael@0: while (PR_CLIST_IS_EMPTY(&jobQ) && !stopping) { michael@0: PZ_WaitCondVar(jobQNotEmptyCv, PR_INTERVAL_NO_TIMEOUT); michael@0: } michael@0: if (!PR_CLIST_IS_EMPTY(&jobQ)) { michael@0: myLink = PR_LIST_HEAD(&jobQ); michael@0: PR_REMOVE_AND_INIT_LINK(myLink); michael@0: } michael@0: PZ_Unlock(qLock); michael@0: myJob = (JOB *)myLink; michael@0: /* myJob will be null when stopping is true and jobQ is empty */ michael@0: if (!myJob) michael@0: break; michael@0: handle_connection( myJob->tcp_sock, myJob->model_sock, michael@0: myJob->requestCert); michael@0: PZ_Lock(qLock); michael@0: PR_APPEND_LINK(myLink, &freeJobs); michael@0: PZ_NotifyCondVar(freeListNotEmptyCv); michael@0: } while (PR_TRUE); michael@0: return 0; michael@0: } michael@0: michael@0: michael@0: SECStatus michael@0: launch_threads( michael@0: startFn *startFunc, michael@0: PRFileDesc *a, michael@0: PRFileDesc *b, michael@0: int c, michael@0: PRBool local) michael@0: { michael@0: int i; michael@0: SECStatus rv = SECSuccess; michael@0: michael@0: /* create the thread management serialization structs */ michael@0: qLock = PZ_NewLock(nssILockSelfServ); michael@0: jobQNotEmptyCv = PZ_NewCondVar(qLock); michael@0: freeListNotEmptyCv = PZ_NewCondVar(qLock); michael@0: threadCountChangeCv = PZ_NewCondVar(qLock); michael@0: michael@0: /* create monitor for crl reload procedure */ michael@0: lastLoadedCrlLock = PR_NewLock(); michael@0: michael@0: /* allocate the array of thread slots */ michael@0: threads = PR_Calloc(maxThreads, sizeof(perThread)); michael@0: if ( NULL == threads ) { michael@0: fprintf(stderr, "Oh Drat! Can't allocate the perThread array\n"); michael@0: return SECFailure; michael@0: } michael@0: /* 5 is a little extra, intended to keep the jobQ from underflowing. michael@0: ** That is, from going empty while not stopping and clients are still michael@0: ** trying to contact us. michael@0: */ michael@0: rv = setupJobs(maxThreads + 5); michael@0: if (rv != SECSuccess) michael@0: return rv; michael@0: michael@0: PZ_Lock(qLock); michael@0: for (i = 0; i < maxThreads; ++i) { michael@0: perThread * slot = threads + i; michael@0: michael@0: slot->state = rs_running; michael@0: slot->a = a; michael@0: slot->b = b; michael@0: slot->c = c; michael@0: slot->startFunc = startFunc; michael@0: slot->prThread = PR_CreateThread(PR_USER_THREAD, michael@0: thread_wrapper, slot, PR_PRIORITY_NORMAL, michael@0: (PR_TRUE==local)?PR_LOCAL_THREAD:PR_GLOBAL_THREAD, michael@0: PR_UNJOINABLE_THREAD, 0); michael@0: if (slot->prThread == NULL) { michael@0: printf("selfserv: Failed to launch thread!\n"); michael@0: slot->state = rs_idle; michael@0: rv = SECFailure; michael@0: break; michael@0: } michael@0: michael@0: ++threadCount; michael@0: } michael@0: PZ_Unlock(qLock); michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: #define DESTROY_CONDVAR(name) if (name) { \ michael@0: PZ_DestroyCondVar(name); name = NULL; } michael@0: #define DESTROY_LOCK(name) if (name) { \ michael@0: PZ_DestroyLock(name); name = NULL; } michael@0: michael@0: michael@0: void michael@0: terminateWorkerThreads(void) michael@0: { michael@0: VLOG(("selfserv: server_thead: waiting on stopping")); michael@0: PZ_Lock(qLock); michael@0: PZ_NotifyAllCondVar(jobQNotEmptyCv); michael@0: while (threadCount > 0) { michael@0: PZ_WaitCondVar(threadCountChangeCv, PR_INTERVAL_NO_TIMEOUT); michael@0: } michael@0: /* The worker threads empty the jobQ before they terminate. */ michael@0: PORT_Assert(PR_CLIST_IS_EMPTY(&jobQ)); michael@0: PZ_Unlock(qLock); michael@0: michael@0: DESTROY_CONDVAR(jobQNotEmptyCv); michael@0: DESTROY_CONDVAR(freeListNotEmptyCv); michael@0: DESTROY_CONDVAR(threadCountChangeCv); michael@0: michael@0: PR_DestroyLock(lastLoadedCrlLock); michael@0: DESTROY_LOCK(qLock); michael@0: PR_Free(jobTable); michael@0: PR_Free(threads); michael@0: } michael@0: michael@0: static void michael@0: logger(void *arg) michael@0: { michael@0: PRFloat64 seconds; michael@0: PRFloat64 opsPerSec; michael@0: PRIntervalTime period; michael@0: PRIntervalTime previousTime; michael@0: PRIntervalTime latestTime; michael@0: PRUint32 previousOps; michael@0: PRUint32 ops; michael@0: PRIntervalTime logPeriodTicks = PR_TicksPerSecond(); michael@0: PRFloat64 secondsPerTick = 1.0 / (PRFloat64)logPeriodTicks; michael@0: int iterations = 0; michael@0: int secondsElapsed = 0; michael@0: static PRInt64 totalPeriodBytes = 0; michael@0: static PRInt64 totalPeriodBytesTCP = 0; michael@0: michael@0: previousOps = loggerOps; michael@0: previousTime = PR_IntervalNow(); michael@0: michael@0: for (;;) { michael@0: /* OK, implementing a new sleep algorithm here... always sleep michael@0: * for 1 second but print out info at the user-specified interval. michael@0: * This way, we don't overflow all of our PR_Atomic* functions and michael@0: * we don't have to use locks. michael@0: */ michael@0: PR_Sleep(logPeriodTicks); michael@0: secondsElapsed++; michael@0: totalPeriodBytes += PR_ATOMIC_SET(&loggerBytes, 0); michael@0: totalPeriodBytesTCP += PR_ATOMIC_SET(&loggerBytesTCP, 0); michael@0: if (secondsElapsed != logPeriod) { michael@0: continue; michael@0: } michael@0: /* when we reach the user-specified logging interval, print out all michael@0: * data michael@0: */ michael@0: secondsElapsed = 0; michael@0: latestTime = PR_IntervalNow(); michael@0: ops = loggerOps; michael@0: period = latestTime - previousTime; michael@0: seconds = (PRFloat64) period*secondsPerTick; michael@0: opsPerSec = (ops - previousOps) / seconds; michael@0: michael@0: if (testBulk) { michael@0: if (iterations == 0) { michael@0: if (loggingLayer == PR_TRUE) { michael@0: printf("Conn.--------App Data--------TCP Data\n"); michael@0: } else { michael@0: printf("Conn.--------App Data\n"); michael@0: } michael@0: } michael@0: if (loggingLayer == PR_TRUE) { michael@0: printf("%4.d %5.3f MB/s %5.3f MB/s\n", ops, michael@0: totalPeriodBytes / (seconds * 1048576.0), michael@0: totalPeriodBytesTCP / (seconds * 1048576.0)); michael@0: } else { michael@0: printf("%4.d %5.3f MB/s\n", ops, michael@0: totalPeriodBytes / (seconds * 1048576.0)); michael@0: } michael@0: totalPeriodBytes = 0; michael@0: totalPeriodBytesTCP = 0; michael@0: /* Print the "legend" every 20 iterations */ michael@0: iterations = (iterations + 1) % 20; michael@0: } else { michael@0: printf("%.2f ops/second, %d threads\n", opsPerSec, threadCount); michael@0: } michael@0: michael@0: fflush(stdout); michael@0: previousOps = ops; michael@0: previousTime = latestTime; michael@0: if (stopping) { michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: michael@0: michael@0: /************************************************************************** michael@0: ** End thread management routines. michael@0: **************************************************************************/ michael@0: michael@0: PRBool useModelSocket = PR_FALSE; michael@0: static SSLVersionRange enabledVersions; michael@0: PRBool enableSSL2 = PR_TRUE; michael@0: PRBool disableRollBack = PR_FALSE; michael@0: PRBool NoReuse = PR_FALSE; michael@0: PRBool hasSidCache = PR_FALSE; michael@0: PRBool disableStepDown = PR_FALSE; michael@0: PRBool bypassPKCS11 = PR_FALSE; michael@0: PRBool disableLocking = PR_FALSE; michael@0: PRBool testbypass = PR_FALSE; michael@0: PRBool enableSessionTickets = PR_FALSE; michael@0: PRBool enableCompression = PR_FALSE; michael@0: PRBool failedToNegotiateName = PR_FALSE; michael@0: static char *virtServerNameArray[MAX_VIRT_SERVER_NAME_ARRAY_INDEX]; michael@0: static int virtServerNameIndex = 1; michael@0: michael@0: michael@0: static const char stopCmd[] = { "GET /stop " }; michael@0: static const char getCmd[] = { "GET " }; michael@0: static const char EOFmsg[] = { "EOF\r\n\r\n\r\n" }; michael@0: static const char outHeader[] = { michael@0: "HTTP/1.0 200 OK\r\n" michael@0: "Server: Generic Web Server\r\n" michael@0: "Date: Tue, 26 Aug 1997 22:10:05 GMT\r\n" michael@0: "Content-type: text/plain\r\n" michael@0: "\r\n" michael@0: }; michael@0: static const char crlCacheErr[] = { "CRL ReCache Error: " }; michael@0: michael@0: PRUint16 cipherlist[100]; michael@0: int nciphers; michael@0: michael@0: void michael@0: savecipher(int c) michael@0: { michael@0: if (nciphers < sizeof cipherlist / sizeof (cipherlist[0])) michael@0: cipherlist[nciphers++] = (PRUint16)c; michael@0: } michael@0: michael@0: michael@0: #ifdef FULL_DUPLEX_CAPABLE michael@0: michael@0: struct lockedVarsStr { michael@0: PZLock * lock; michael@0: int count; michael@0: int waiters; michael@0: PZCondVar * condVar; michael@0: }; michael@0: michael@0: typedef struct lockedVarsStr lockedVars; michael@0: michael@0: void michael@0: lockedVars_Init( lockedVars * lv) michael@0: { michael@0: lv->count = 0; michael@0: lv->waiters = 0; michael@0: lv->lock = PZ_NewLock(nssILockSelfServ); michael@0: lv->condVar = PZ_NewCondVar(lv->lock); michael@0: } michael@0: michael@0: void michael@0: lockedVars_Destroy( lockedVars * lv) michael@0: { michael@0: PZ_DestroyCondVar(lv->condVar); michael@0: lv->condVar = NULL; michael@0: michael@0: PZ_DestroyLock(lv->lock); michael@0: lv->lock = NULL; michael@0: } michael@0: michael@0: void michael@0: lockedVars_WaitForDone(lockedVars * lv) michael@0: { michael@0: PZ_Lock(lv->lock); michael@0: while (lv->count > 0) { michael@0: PZ_WaitCondVar(lv->condVar, PR_INTERVAL_NO_TIMEOUT); michael@0: } michael@0: PZ_Unlock(lv->lock); michael@0: } michael@0: michael@0: int /* returns count */ michael@0: lockedVars_AddToCount(lockedVars * lv, int addend) michael@0: { michael@0: int rv; michael@0: michael@0: PZ_Lock(lv->lock); michael@0: rv = lv->count += addend; michael@0: if (rv <= 0) { michael@0: PZ_NotifyCondVar(lv->condVar); michael@0: } michael@0: PZ_Unlock(lv->lock); michael@0: return rv; michael@0: } michael@0: michael@0: int michael@0: do_writes( michael@0: PRFileDesc * ssl_sock, michael@0: PRFileDesc * model_sock, michael@0: int requestCert michael@0: ) michael@0: { michael@0: int sent = 0; michael@0: int count = 0; michael@0: lockedVars * lv = (lockedVars *)model_sock; michael@0: michael@0: VLOG(("selfserv: do_writes: starting")); michael@0: while (sent < bigBuf.len) { michael@0: michael@0: count = PR_Write(ssl_sock, bigBuf.data + sent, bigBuf.len - sent); michael@0: if (count < 0) { michael@0: errWarn("PR_Write bigBuf"); michael@0: break; michael@0: } michael@0: FPRINTF(stderr, "selfserv: PR_Write wrote %d bytes from bigBuf\n", count ); michael@0: sent += count; michael@0: } michael@0: if (count >= 0) { /* last write didn't fail. */ michael@0: PR_Shutdown(ssl_sock, PR_SHUTDOWN_SEND); michael@0: } michael@0: michael@0: /* notify the reader that we're done. */ michael@0: lockedVars_AddToCount(lv, -1); michael@0: FLUSH; michael@0: VLOG(("selfserv: do_writes: exiting")); michael@0: return (sent < bigBuf.len) ? SECFailure : SECSuccess; michael@0: } michael@0: michael@0: static int michael@0: handle_fdx_connection( michael@0: PRFileDesc * tcp_sock, michael@0: PRFileDesc * model_sock, michael@0: int requestCert michael@0: ) michael@0: { michael@0: PRFileDesc * ssl_sock = NULL; michael@0: SECStatus result; michael@0: int firstTime = 1; michael@0: lockedVars lv; michael@0: PRSocketOptionData opt; michael@0: char buf[10240]; michael@0: michael@0: michael@0: VLOG(("selfserv: handle_fdx_connection: starting")); michael@0: opt.option = PR_SockOpt_Nonblocking; michael@0: opt.value.non_blocking = PR_FALSE; michael@0: PR_SetSocketOption(tcp_sock, &opt); michael@0: michael@0: if (useModelSocket && model_sock) { michael@0: SECStatus rv; michael@0: ssl_sock = SSL_ImportFD(model_sock, tcp_sock); michael@0: if (!ssl_sock) { michael@0: errWarn("SSL_ImportFD with model"); michael@0: goto cleanup; michael@0: } michael@0: rv = SSL_ResetHandshake(ssl_sock, /* asServer */ 1); michael@0: if (rv != SECSuccess) { michael@0: errWarn("SSL_ResetHandshake"); michael@0: goto cleanup; michael@0: } michael@0: } else { michael@0: ssl_sock = tcp_sock; michael@0: } michael@0: michael@0: lockedVars_Init(&lv); michael@0: lockedVars_AddToCount(&lv, 1); michael@0: michael@0: /* Attempt to launch the writer thread. */ michael@0: result = launch_thread(do_writes, ssl_sock, (PRFileDesc *)&lv, michael@0: requestCert); michael@0: michael@0: if (result == SECSuccess) michael@0: do { michael@0: /* do reads here. */ michael@0: int count; michael@0: count = PR_Read(ssl_sock, buf, sizeof buf); michael@0: if (count < 0) { michael@0: errWarn("FDX PR_Read"); michael@0: break; michael@0: } michael@0: FPRINTF(stderr, "selfserv: FDX PR_Read read %d bytes.\n", count ); michael@0: if (firstTime) { michael@0: firstTime = 0; michael@0: printSecurityInfo(ssl_sock); michael@0: } michael@0: } while (lockedVars_AddToCount(&lv, 0) > 0); michael@0: michael@0: /* Wait for writer to finish */ michael@0: lockedVars_WaitForDone(&lv); michael@0: lockedVars_Destroy(&lv); michael@0: FLUSH; michael@0: michael@0: cleanup: michael@0: if (ssl_sock) { michael@0: PR_Close(ssl_sock); michael@0: } else if (tcp_sock) { michael@0: PR_Close(tcp_sock); michael@0: } michael@0: michael@0: VLOG(("selfserv: handle_fdx_connection: exiting")); michael@0: return SECSuccess; michael@0: } michael@0: michael@0: #endif michael@0: michael@0: static SECItem *lastLoadedCrl = NULL; michael@0: michael@0: static SECStatus michael@0: reload_crl(PRFileDesc *crlFile) michael@0: { michael@0: SECItem *crlDer; michael@0: CERTCertDBHandle *certHandle = CERT_GetDefaultCertDB(); michael@0: SECStatus rv; michael@0: michael@0: /* Read in the entire file specified with the -f argument */ michael@0: crlDer = PORT_Malloc(sizeof(SECItem)); michael@0: if (!crlDer) { michael@0: errWarn("Can not allocate memory."); michael@0: return SECFailure; michael@0: } michael@0: michael@0: rv = SECU_ReadDERFromFile(crlDer, crlFile, PR_FALSE, PR_FALSE); michael@0: if (rv != SECSuccess) { michael@0: errWarn("Unable to read input file."); michael@0: PORT_Free(crlDer); michael@0: return SECFailure; michael@0: } michael@0: michael@0: PR_Lock(lastLoadedCrlLock); michael@0: rv = CERT_CacheCRL(certHandle, crlDer); michael@0: if (rv == SECSuccess) { michael@0: SECItem *tempItem = crlDer; michael@0: if (lastLoadedCrl != NULL) { michael@0: rv = CERT_UncacheCRL(certHandle, lastLoadedCrl); michael@0: if (rv != SECSuccess) { michael@0: errWarn("Unable to uncache crl."); michael@0: goto loser; michael@0: } michael@0: crlDer = lastLoadedCrl; michael@0: } else { michael@0: crlDer = NULL; michael@0: } michael@0: lastLoadedCrl = tempItem; michael@0: } michael@0: michael@0: loser: michael@0: PR_Unlock(lastLoadedCrlLock); michael@0: SECITEM_FreeItem(crlDer, PR_TRUE); michael@0: return rv; michael@0: } michael@0: michael@0: void stop_server() michael@0: { michael@0: stopping = 1; michael@0: PR_Interrupt(acceptorThread); michael@0: PZ_TraceFlush(); michael@0: } michael@0: michael@0: SECItemArray * michael@0: makeTryLaterOCSPResponse(PLArenaPool *arena) michael@0: { michael@0: SECItemArray *result = NULL; michael@0: SECItem *ocspResponse = NULL; michael@0: michael@0: ocspResponse = CERT_CreateEncodedOCSPErrorResponse(arena, michael@0: SEC_ERROR_OCSP_TRY_SERVER_LATER); michael@0: if (!ocspResponse) michael@0: errExit("cannot created ocspResponse"); michael@0: michael@0: result = SECITEM_AllocArray(arena, NULL, 1); michael@0: if (!result) michael@0: errExit("cannot allocate multiOcspResponses"); michael@0: michael@0: result->items[0].data = ocspResponse->data; michael@0: result->items[0].len = ocspResponse->len; michael@0: michael@0: return result; michael@0: } michael@0: michael@0: SECItemArray * michael@0: makeCorruptedOCSPResponse(PLArenaPool *arena) michael@0: { michael@0: SECItemArray *result = NULL; michael@0: SECItem *ocspResponse = NULL; michael@0: michael@0: ocspResponse = SECITEM_AllocItem(arena, NULL, 1); michael@0: if (!ocspResponse) michael@0: errExit("cannot created ocspResponse"); michael@0: michael@0: result = SECITEM_AllocArray(arena, NULL, 1); michael@0: if (!result) michael@0: errExit("cannot allocate multiOcspResponses"); michael@0: michael@0: result->items[0].data = ocspResponse->data; michael@0: result->items[0].len = ocspResponse->len; michael@0: michael@0: return result; michael@0: } michael@0: michael@0: SECItemArray * michael@0: makeSignedOCSPResponse(PLArenaPool *arena, ocspStaplingModeType osm, michael@0: CERTCertificate *cert, secuPWData *pwdata) michael@0: { michael@0: SECItemArray *result = NULL; michael@0: SECItem *ocspResponse = NULL; michael@0: CERTOCSPSingleResponse **singleResponses; michael@0: CERTOCSPSingleResponse *sr; michael@0: CERTOCSPCertID *cid = NULL; michael@0: CERTCertificate *ca; michael@0: PRTime now = PR_Now(); michael@0: PRTime nextUpdate; michael@0: michael@0: PORT_Assert(cert != NULL); michael@0: michael@0: ca = CERT_FindCertByNickname(CERT_GetDefaultCertDB(), ocspStaplingCA); michael@0: if (!ca) michael@0: errExit("cannot find CA"); michael@0: michael@0: cid = CERT_CreateOCSPCertID(cert, now); michael@0: if (!cid) michael@0: errExit("cannot created cid"); michael@0: michael@0: nextUpdate = now + 60*60*24 * PR_USEC_PER_SEC; /* plus 1 day */ michael@0: michael@0: switch (osm) { michael@0: case osm_good: michael@0: case osm_badsig: michael@0: sr = CERT_CreateOCSPSingleResponseGood(arena, cid, now, michael@0: &nextUpdate); michael@0: break; michael@0: case osm_unknown: michael@0: sr = CERT_CreateOCSPSingleResponseUnknown(arena, cid, now, michael@0: &nextUpdate); michael@0: break; michael@0: case osm_revoked: michael@0: sr = CERT_CreateOCSPSingleResponseRevoked(arena, cid, now, michael@0: &nextUpdate, michael@0: now - 60*60*24 * PR_USEC_PER_SEC, /* minus 1 day */ michael@0: NULL); michael@0: break; michael@0: default: michael@0: PORT_Assert(0); michael@0: break; michael@0: } michael@0: michael@0: if (!sr) michael@0: errExit("cannot create sr"); michael@0: michael@0: /* meaning of value 2: one entry + one end marker */ michael@0: singleResponses = PORT_ArenaNewArray(arena, CERTOCSPSingleResponse*, 2); michael@0: if (singleResponses == NULL) michael@0: errExit("cannot allocate singleResponses"); michael@0: michael@0: singleResponses[0] = sr; michael@0: singleResponses[1] = NULL; michael@0: michael@0: ocspResponse = CERT_CreateEncodedOCSPSuccessResponse(arena, michael@0: (osm == osm_badsig) ? NULL : ca, michael@0: ocspResponderID_byName, now, singleResponses, michael@0: &pwdata); michael@0: if (!ocspResponse) michael@0: errExit("cannot created ocspResponse"); michael@0: michael@0: CERT_DestroyCertificate(ca); michael@0: ca = NULL; michael@0: michael@0: result = SECITEM_AllocArray(arena, NULL, 1); michael@0: if (!result) michael@0: errExit("cannot allocate multiOcspResponses"); michael@0: michael@0: result->items[0].data = ocspResponse->data; michael@0: result->items[0].len = ocspResponse->len; michael@0: michael@0: CERT_DestroyOCSPCertID(cid); michael@0: cid = NULL; michael@0: michael@0: return result; michael@0: } michael@0: michael@0: void michael@0: setupCertStatus(PLArenaPool *arena, enum ocspStaplingModeEnum ocspStaplingMode, michael@0: CERTCertificate *cert, SSLKEAType kea, secuPWData *pwdata) michael@0: { michael@0: if (ocspStaplingMode == osm_random) { michael@0: /* 6 different responses */ michael@0: int r = rand() % 6; michael@0: switch (r) { michael@0: case 0: ocspStaplingMode = osm_good; break; michael@0: case 1: ocspStaplingMode = osm_revoked; break; michael@0: case 2: ocspStaplingMode = osm_unknown; break; michael@0: case 3: ocspStaplingMode = osm_badsig; break; michael@0: case 4: ocspStaplingMode = osm_corrupted; break; michael@0: case 5: ocspStaplingMode = osm_failure; break; michael@0: default: PORT_Assert(0); break; michael@0: } michael@0: } michael@0: if (ocspStaplingMode != osm_disabled) { michael@0: SECItemArray *multiOcspResponses = NULL; michael@0: switch (ocspStaplingMode) { michael@0: case osm_good: michael@0: case osm_revoked: michael@0: case osm_unknown: michael@0: case osm_badsig: michael@0: multiOcspResponses = michael@0: makeSignedOCSPResponse(arena, ocspStaplingMode, cert, michael@0: pwdata); michael@0: break; michael@0: case osm_corrupted: michael@0: multiOcspResponses = makeCorruptedOCSPResponse(arena); michael@0: break; michael@0: case osm_failure: michael@0: multiOcspResponses = makeTryLaterOCSPResponse(arena); michael@0: break; michael@0: case osm_ocsp: michael@0: errExit("stapling mode \"ocsp\" not implemented"); michael@0: break; michael@0: break; michael@0: default: michael@0: break; michael@0: } michael@0: if (multiOcspResponses) { michael@0: certStatus[kea] = multiOcspResponses; michael@0: } michael@0: } michael@0: } michael@0: michael@0: int michael@0: handle_connection( michael@0: PRFileDesc *tcp_sock, michael@0: PRFileDesc *model_sock, michael@0: int requestCert michael@0: ) michael@0: { michael@0: PRFileDesc * ssl_sock = NULL; michael@0: PRFileDesc * local_file_fd = NULL; michael@0: char * post; michael@0: char * pBuf; /* unused space at end of buf */ michael@0: const char * errString; michael@0: PRStatus status; michael@0: int bufRem; /* unused bytes at end of buf */ michael@0: int bufDat; /* characters received in buf */ michael@0: int newln = 0; /* # of consecutive newlns */ michael@0: int firstTime = 1; michael@0: int reqLen; michael@0: int rv; michael@0: int numIOVs; michael@0: PRSocketOptionData opt; michael@0: PRIOVec iovs[16]; michael@0: char msgBuf[160]; michael@0: char buf[10240]; michael@0: char fileName[513]; michael@0: char proto[128]; michael@0: PRDescIdentity aboveLayer = PR_INVALID_IO_LAYER; michael@0: SSLKEAType kea; michael@0: michael@0: pBuf = buf; michael@0: bufRem = sizeof buf; michael@0: michael@0: VLOG(("selfserv: handle_connection: starting")); michael@0: opt.option = PR_SockOpt_Nonblocking; michael@0: opt.value.non_blocking = PR_FALSE; michael@0: PR_SetSocketOption(tcp_sock, &opt); michael@0: michael@0: VLOG(("selfserv: handle_connection: starting\n")); michael@0: if (useModelSocket && model_sock) { michael@0: SECStatus rv; michael@0: ssl_sock = SSL_ImportFD(model_sock, tcp_sock); michael@0: if (!ssl_sock) { michael@0: errWarn("SSL_ImportFD with model"); michael@0: goto cleanup; michael@0: } michael@0: rv = SSL_ResetHandshake(ssl_sock, /* asServer */ 1); michael@0: if (rv != SECSuccess) { michael@0: errWarn("SSL_ResetHandshake"); michael@0: goto cleanup; michael@0: } michael@0: } else { michael@0: ssl_sock = tcp_sock; michael@0: } michael@0: michael@0: for (kea = kt_rsa; kea < kt_kea_size; kea++) { michael@0: if (certStatus[kea] != NULL) { michael@0: SSL_SetStapledOCSPResponses(ssl_sock, certStatus[kea], kea); michael@0: } michael@0: } michael@0: michael@0: if (loggingLayer) { michael@0: /* find the layer where our new layer is to be pushed */ michael@0: aboveLayer = PR_GetLayersIdentity(ssl_sock->lower); michael@0: if (aboveLayer == PR_INVALID_IO_LAYER) { michael@0: errExit("PRGetUniqueIdentity"); michael@0: } michael@0: /* create the new layer - this is a very cheap operation */ michael@0: loggingFD = PR_CreateIOLayerStub(log_layer_id, &loggingMethods); michael@0: if (!loggingFD) michael@0: errExit("PR_CreateIOLayerStub"); michael@0: /* push the layer below ssl but above TCP */ michael@0: rv = PR_PushIOLayer(ssl_sock, aboveLayer, loggingFD); michael@0: if (rv != PR_SUCCESS) { michael@0: errExit("PR_PushIOLayer"); michael@0: } michael@0: } michael@0: michael@0: if (noDelay) { michael@0: opt.option = PR_SockOpt_NoDelay; michael@0: opt.value.no_delay = PR_TRUE; michael@0: status = PR_SetSocketOption(ssl_sock, &opt); michael@0: if (status != PR_SUCCESS) { michael@0: errWarn("PR_SetSocketOption(PR_SockOpt_NoDelay, PR_TRUE)"); michael@0: if (ssl_sock) { michael@0: PR_Close(ssl_sock); michael@0: } michael@0: return SECFailure; michael@0: } michael@0: } michael@0: michael@0: while (1) { michael@0: newln = 0; michael@0: reqLen = 0; michael@0: rv = PR_Read(ssl_sock, pBuf, bufRem - 1); michael@0: if (rv == 0 || michael@0: (rv < 0 && PR_END_OF_FILE_ERROR == PR_GetError())) { michael@0: if (verbose) michael@0: errWarn("HDX PR_Read hit EOF"); michael@0: break; michael@0: } michael@0: if (rv < 0) { michael@0: errWarn("HDX PR_Read"); michael@0: goto cleanup; michael@0: } michael@0: /* NULL termination */ michael@0: pBuf[rv] = 0; michael@0: if (firstTime) { michael@0: firstTime = 0; michael@0: printSecurityInfo(ssl_sock); michael@0: } michael@0: michael@0: pBuf += rv; michael@0: bufRem -= rv; michael@0: bufDat = pBuf - buf; michael@0: /* Parse the input, starting at the beginning of the buffer. michael@0: * Stop when we detect two consecutive \n's (or \r\n's) michael@0: * as this signifies the end of the GET or POST portion. michael@0: * The posted data follows. michael@0: */ michael@0: while (reqLen < bufDat && newln < 2) { michael@0: int octet = buf[reqLen++]; michael@0: if (octet == '\n') { michael@0: newln++; michael@0: } else if (octet != '\r') { michael@0: newln = 0; michael@0: } michael@0: } michael@0: michael@0: /* came to the end of the buffer, or second newln michael@0: * If we didn't get an empty line (CRLFCRLF) then keep on reading. michael@0: */ michael@0: if (newln < 2) michael@0: continue; michael@0: michael@0: /* we're at the end of the HTTP request. michael@0: * If the request is a POST, then there will be one more michael@0: * line of data. michael@0: * This parsing is a hack, but ok for SSL test purposes. michael@0: */ michael@0: post = PORT_Strstr(buf, "POST "); michael@0: if (!post || *post != 'P') michael@0: break; michael@0: michael@0: /* It's a post, so look for the next and final CR/LF. */ michael@0: /* We should parse content length here, but ... */ michael@0: while (reqLen < bufDat && newln < 3) { michael@0: int octet = buf[reqLen++]; michael@0: if (octet == '\n') { michael@0: newln++; michael@0: } michael@0: } michael@0: if (newln == 3) michael@0: break; michael@0: } /* read loop */ michael@0: michael@0: bufDat = pBuf - buf; michael@0: if (bufDat) do { /* just close if no data */ michael@0: /* Have either (a) a complete get, (b) a complete post, (c) EOF */ michael@0: if (reqLen > 0 && !strncmp(buf, getCmd, sizeof getCmd - 1)) { michael@0: char * fnBegin = buf + 4; michael@0: char * fnEnd; michael@0: PRFileInfo info; michael@0: /* try to open the file named. michael@0: * If successful, then write it to the client. michael@0: */ michael@0: fnEnd = strpbrk(fnBegin, " \r\n"); michael@0: if (fnEnd) { michael@0: int fnLen = fnEnd - fnBegin; michael@0: if (fnLen < sizeof fileName) { michael@0: char *real_fileName = fileName; michael@0: char *protoEnd = NULL; michael@0: strncpy(fileName, fnBegin, fnLen); michael@0: fileName[fnLen] = 0; /* null terminate */ michael@0: if ((protoEnd = strstr(fileName, "://")) != NULL) { michael@0: int protoLen = PR_MIN(protoEnd - fileName, sizeof(proto) - 1); michael@0: PL_strncpy(proto, fileName, protoLen); michael@0: proto[protoLen] = 0; michael@0: real_fileName= protoEnd + 3; michael@0: } else { michael@0: proto[0] = 0; michael@0: } michael@0: status = PR_GetFileInfo(real_fileName, &info); michael@0: if (status == PR_SUCCESS && michael@0: info.type == PR_FILE_FILE && michael@0: info.size >= 0 ) { michael@0: local_file_fd = PR_Open(real_fileName, PR_RDONLY, 0); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: /* if user has requested client auth in a subsequent handshake, michael@0: * do it here. michael@0: */ michael@0: if (requestCert > 2) { /* request cert was 3 or 4 */ michael@0: CERTCertificate * cert = SSL_PeerCertificate(ssl_sock); michael@0: if (cert) { michael@0: CERT_DestroyCertificate(cert); michael@0: } else { michael@0: rv = SSL_OptionSet(ssl_sock, SSL_REQUEST_CERTIFICATE, 1); michael@0: if (rv < 0) { michael@0: errWarn("second SSL_OptionSet SSL_REQUEST_CERTIFICATE"); michael@0: break; michael@0: } michael@0: rv = SSL_OptionSet(ssl_sock, SSL_REQUIRE_CERTIFICATE, michael@0: (requestCert == 4)); michael@0: if (rv < 0) { michael@0: errWarn("second SSL_OptionSet SSL_REQUIRE_CERTIFICATE"); michael@0: break; michael@0: } michael@0: rv = SSL_ReHandshake(ssl_sock, PR_TRUE); michael@0: if (rv != 0) { michael@0: errWarn("SSL_ReHandshake"); michael@0: break; michael@0: } michael@0: rv = SSL_ForceHandshake(ssl_sock); michael@0: if (rv < 0) { michael@0: errWarn("SSL_ForceHandshake"); michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: michael@0: numIOVs = 0; michael@0: michael@0: iovs[numIOVs].iov_base = (char *)outHeader; michael@0: iovs[numIOVs].iov_len = (sizeof(outHeader)) - 1; michael@0: numIOVs++; michael@0: michael@0: if (local_file_fd) { michael@0: PRInt32 bytes; michael@0: int errLen; michael@0: if (!PL_strlen(proto) || !PL_strcmp(proto, "file")) { michael@0: bytes = PR_TransmitFile(ssl_sock, local_file_fd, outHeader, michael@0: sizeof outHeader - 1, michael@0: PR_TRANSMITFILE_KEEP_OPEN, michael@0: PR_INTERVAL_NO_TIMEOUT); michael@0: if (bytes >= 0) { michael@0: bytes -= sizeof outHeader - 1; michael@0: FPRINTF(stderr, michael@0: "selfserv: PR_TransmitFile wrote %d bytes from %s\n", michael@0: bytes, fileName); michael@0: break; michael@0: } michael@0: errString = errWarn("PR_TransmitFile"); michael@0: errLen = PORT_Strlen(errString); michael@0: errLen = PR_MIN(errLen, sizeof msgBuf - 1); michael@0: PORT_Memcpy(msgBuf, errString, errLen); michael@0: msgBuf[errLen] = 0; michael@0: michael@0: iovs[numIOVs].iov_base = msgBuf; michael@0: iovs[numIOVs].iov_len = PORT_Strlen(msgBuf); michael@0: numIOVs++; michael@0: } michael@0: if (!PL_strcmp(proto, "crl")) { michael@0: if (reload_crl(local_file_fd) == SECFailure) { michael@0: errString = errWarn("CERT_CacheCRL"); michael@0: if (!errString) michael@0: errString = "Unknow error"; michael@0: PR_snprintf(msgBuf, sizeof(msgBuf), "%s%s ", michael@0: crlCacheErr, errString); michael@0: michael@0: iovs[numIOVs].iov_base = msgBuf; michael@0: iovs[numIOVs].iov_len = PORT_Strlen(msgBuf); michael@0: numIOVs++; michael@0: } else { michael@0: FPRINTF(stderr, michael@0: "selfserv: CRL %s reloaded.\n", michael@0: fileName); michael@0: break; michael@0: } michael@0: } michael@0: } else if (reqLen <= 0) { /* hit eof */ michael@0: PORT_Sprintf(msgBuf, "Get or Post incomplete after %d bytes.\r\n", michael@0: bufDat); michael@0: michael@0: iovs[numIOVs].iov_base = msgBuf; michael@0: iovs[numIOVs].iov_len = PORT_Strlen(msgBuf); michael@0: numIOVs++; michael@0: } else if (reqLen < bufDat) { michael@0: PORT_Sprintf(msgBuf, "Discarded %d characters.\r\n", michael@0: bufDat - reqLen); michael@0: michael@0: iovs[numIOVs].iov_base = msgBuf; michael@0: iovs[numIOVs].iov_len = PORT_Strlen(msgBuf); michael@0: numIOVs++; michael@0: } michael@0: michael@0: if (reqLen > 0) { michael@0: if (verbose > 1) michael@0: fwrite(buf, 1, reqLen, stdout); /* display it */ michael@0: michael@0: iovs[numIOVs].iov_base = buf; michael@0: iovs[numIOVs].iov_len = reqLen; michael@0: numIOVs++; michael@0: } michael@0: michael@0: /* Don't add the EOF if we want to test bulk encryption */ michael@0: if (!testBulk) { michael@0: iovs[numIOVs].iov_base = (char *)EOFmsg; michael@0: iovs[numIOVs].iov_len = sizeof EOFmsg - 1; michael@0: numIOVs++; michael@0: } michael@0: michael@0: rv = PR_Writev(ssl_sock, iovs, numIOVs, PR_INTERVAL_NO_TIMEOUT); michael@0: if (rv < 0) { michael@0: errWarn("PR_Writev"); michael@0: break; michael@0: } michael@0: michael@0: /* Send testBulkTotal chunks to the client. Unlimited if 0. */ michael@0: if (testBulk) { michael@0: while (0 < (rv = PR_Write(ssl_sock, testBulkBuf, testBulkSize))) { michael@0: PR_ATOMIC_ADD(&loggerBytes, rv); michael@0: PR_ATOMIC_INCREMENT(&bulkSentChunks); michael@0: if ((bulkSentChunks > testBulkTotal) && (testBulkTotal != 0)) michael@0: break; michael@0: } michael@0: michael@0: /* There was a write error, so close this connection. */ michael@0: if (bulkSentChunks <= testBulkTotal) { michael@0: errWarn("PR_Write"); michael@0: } michael@0: PR_ATOMIC_DECREMENT(&loggerOps); michael@0: break; michael@0: } michael@0: } while (0); michael@0: michael@0: cleanup: michael@0: if (ssl_sock) { michael@0: PR_Close(ssl_sock); michael@0: } else if (tcp_sock) { michael@0: PR_Close(tcp_sock); michael@0: } michael@0: if (local_file_fd) michael@0: PR_Close(local_file_fd); michael@0: VLOG(("selfserv: handle_connection: exiting\n")); michael@0: michael@0: /* do a nice shutdown if asked. */ michael@0: if (!strncmp(buf, stopCmd, sizeof stopCmd - 1)) { michael@0: VLOG(("selfserv: handle_connection: stop command")); michael@0: stop_server(); michael@0: } michael@0: VLOG(("selfserv: handle_connection: exiting")); michael@0: return SECSuccess; /* success */ michael@0: } michael@0: michael@0: #ifdef XP_UNIX michael@0: michael@0: void sigusr1_handler(int sig) michael@0: { michael@0: VLOG(("selfserv: sigusr1_handler: stop server")); michael@0: stop_server(); michael@0: } michael@0: michael@0: #endif michael@0: michael@0: SECStatus michael@0: do_accepts( michael@0: PRFileDesc *listen_sock, michael@0: PRFileDesc *model_sock, michael@0: int requestCert michael@0: ) michael@0: { michael@0: PRNetAddr addr; michael@0: PRErrorCode perr; michael@0: #ifdef XP_UNIX michael@0: struct sigaction act; michael@0: #endif michael@0: michael@0: VLOG(("selfserv: do_accepts: starting")); michael@0: PR_SetThreadPriority( PR_GetCurrentThread(), PR_PRIORITY_HIGH); michael@0: michael@0: acceptorThread = PR_GetCurrentThread(); michael@0: #ifdef XP_UNIX michael@0: /* set up the signal handler */ michael@0: act.sa_handler = sigusr1_handler; michael@0: sigemptyset(&act.sa_mask); michael@0: act.sa_flags = 0; michael@0: if (sigaction(SIGUSR1, &act, NULL)) { michael@0: fprintf(stderr, "Error installing signal handler.\n"); michael@0: exit(1); michael@0: } michael@0: #endif michael@0: while (!stopping) { michael@0: PRFileDesc *tcp_sock; michael@0: PRCList *myLink; michael@0: michael@0: FPRINTF(stderr, "\n\n\nselfserv: About to call accept.\n"); michael@0: tcp_sock = PR_Accept(listen_sock, &addr, PR_INTERVAL_NO_TIMEOUT); michael@0: if (tcp_sock == NULL) { michael@0: perr = PR_GetError(); michael@0: if ((perr != PR_CONNECT_RESET_ERROR && michael@0: perr != PR_PENDING_INTERRUPT_ERROR) || verbose) { michael@0: errWarn("PR_Accept"); michael@0: } michael@0: if (perr == PR_CONNECT_RESET_ERROR) { michael@0: FPRINTF(stderr, michael@0: "Ignoring PR_CONNECT_RESET_ERROR error - continue\n"); michael@0: continue; michael@0: } michael@0: stopping = 1; michael@0: break; michael@0: } michael@0: michael@0: VLOG(("selfserv: do_accept: Got connection\n")); michael@0: michael@0: if (logStats) { michael@0: PR_ATOMIC_INCREMENT(&loggerOps); michael@0: } michael@0: michael@0: PZ_Lock(qLock); michael@0: while (PR_CLIST_IS_EMPTY(&freeJobs) && !stopping) { michael@0: PZ_WaitCondVar(freeListNotEmptyCv, PR_INTERVAL_NO_TIMEOUT); michael@0: } michael@0: if (stopping) { michael@0: PZ_Unlock(qLock); michael@0: if (tcp_sock) { michael@0: PR_Close(tcp_sock); michael@0: } michael@0: break; michael@0: } michael@0: myLink = PR_LIST_HEAD(&freeJobs); michael@0: PR_REMOVE_AND_INIT_LINK(myLink); michael@0: /* could release qLock here and reaquire it 7 lines below, but michael@0: ** why bother for 4 assignment statements? michael@0: */ michael@0: { michael@0: JOB * myJob = (JOB *)myLink; michael@0: myJob->tcp_sock = tcp_sock; michael@0: myJob->model_sock = model_sock; michael@0: myJob->requestCert = requestCert; michael@0: } michael@0: michael@0: PR_APPEND_LINK(myLink, &jobQ); michael@0: PZ_NotifyCondVar(jobQNotEmptyCv); michael@0: PZ_Unlock(qLock); michael@0: } michael@0: michael@0: FPRINTF(stderr, "selfserv: Closing listen socket.\n"); michael@0: VLOG(("selfserv: do_accepts: exiting")); michael@0: if (listen_sock) { michael@0: PR_Close(listen_sock); michael@0: } michael@0: return SECSuccess; michael@0: } michael@0: michael@0: PRFileDesc * michael@0: getBoundListenSocket(unsigned short port) michael@0: { michael@0: PRFileDesc * listen_sock; michael@0: int listenQueueDepth = 5 + (2 * maxThreads); michael@0: PRStatus prStatus; michael@0: PRNetAddr addr; michael@0: PRSocketOptionData opt; michael@0: michael@0: addr.inet.family = PR_AF_INET; michael@0: addr.inet.ip = PR_INADDR_ANY; michael@0: addr.inet.port = PR_htons(port); michael@0: michael@0: listen_sock = PR_NewTCPSocket(); michael@0: if (listen_sock == NULL) { michael@0: errExit("PR_NewTCPSocket"); michael@0: } michael@0: michael@0: opt.option = PR_SockOpt_Nonblocking; michael@0: opt.value.non_blocking = PR_FALSE; michael@0: prStatus = PR_SetSocketOption(listen_sock, &opt); michael@0: if (prStatus < 0) { michael@0: PR_Close(listen_sock); michael@0: errExit("PR_SetSocketOption(PR_SockOpt_Nonblocking)"); michael@0: } michael@0: michael@0: opt.option=PR_SockOpt_Reuseaddr; michael@0: opt.value.reuse_addr = PR_TRUE; michael@0: prStatus = PR_SetSocketOption(listen_sock, &opt); michael@0: if (prStatus < 0) { michael@0: PR_Close(listen_sock); michael@0: errExit("PR_SetSocketOption(PR_SockOpt_Reuseaddr)"); michael@0: } michael@0: michael@0: #ifndef WIN95 michael@0: /* Set PR_SockOpt_Linger because it helps prevent a server bind issue michael@0: * after clean shutdown . See bug 331413 . michael@0: * Don't do it in the WIN95 build configuration because clean shutdown is michael@0: * not implemented, and PR_SockOpt_Linger causes a hang in ssl.sh . michael@0: * See bug 332348 */ michael@0: opt.option=PR_SockOpt_Linger; michael@0: opt.value.linger.polarity = PR_TRUE; michael@0: opt.value.linger.linger = PR_SecondsToInterval(1); michael@0: prStatus = PR_SetSocketOption(listen_sock, &opt); michael@0: if (prStatus < 0) { michael@0: PR_Close(listen_sock); michael@0: errExit("PR_SetSocketOption(PR_SockOpt_Linger)"); michael@0: } michael@0: #endif michael@0: michael@0: prStatus = PR_Bind(listen_sock, &addr); michael@0: if (prStatus < 0) { michael@0: PR_Close(listen_sock); michael@0: errExit("PR_Bind"); michael@0: } michael@0: michael@0: prStatus = PR_Listen(listen_sock, listenQueueDepth); michael@0: if (prStatus < 0) { michael@0: PR_Close(listen_sock); michael@0: errExit("PR_Listen"); michael@0: } michael@0: return listen_sock; michael@0: } michael@0: michael@0: PRInt32 PR_CALLBACK michael@0: logWritev ( michael@0: PRFileDesc *fd, michael@0: const PRIOVec *iov, michael@0: PRInt32 size, michael@0: PRIntervalTime timeout ) michael@0: { michael@0: PRInt32 rv = (fd->lower->methods->writev)(fd->lower, iov, size, michael@0: timeout); michael@0: /* Add the amount written, but not if there's an error */ michael@0: if (rv > 0) michael@0: PR_ATOMIC_ADD(&loggerBytesTCP, rv); michael@0: return rv; michael@0: } michael@0: michael@0: PRInt32 PR_CALLBACK michael@0: logWrite ( michael@0: PRFileDesc *fd, michael@0: const void *buf, michael@0: PRInt32 amount) michael@0: { michael@0: PRInt32 rv = (fd->lower->methods->write)(fd->lower, buf, amount); michael@0: /* Add the amount written, but not if there's an error */ michael@0: if (rv > 0) michael@0: PR_ATOMIC_ADD(&loggerBytesTCP, rv); michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: PRInt32 PR_CALLBACK michael@0: logSend ( michael@0: PRFileDesc *fd, michael@0: const void *buf, michael@0: PRInt32 amount, michael@0: PRIntn flags, michael@0: PRIntervalTime timeout) michael@0: { michael@0: PRInt32 rv = (fd->lower->methods->send)(fd->lower, buf, amount, michael@0: flags, timeout); michael@0: /* Add the amount written, but not if there's an error */ michael@0: if (rv > 0) michael@0: PR_ATOMIC_ADD(&loggerBytesTCP, rv); michael@0: return rv; michael@0: } michael@0: michael@0: void initLoggingLayer(void) michael@0: { michael@0: /* get a new layer ID */ michael@0: log_layer_id = PR_GetUniqueIdentity("Selfserv Logging"); michael@0: if (log_layer_id == PR_INVALID_IO_LAYER) michael@0: errExit("PR_GetUniqueIdentity"); michael@0: michael@0: /* setup the default IO methods with my custom write methods */ michael@0: memcpy(&loggingMethods, PR_GetDefaultIOMethods(), sizeof(PRIOMethods)); michael@0: loggingMethods.writev = logWritev; michael@0: loggingMethods.write = logWrite; michael@0: loggingMethods.send = logSend; michael@0: } michael@0: michael@0: void michael@0: handshakeCallback(PRFileDesc *fd, void *client_data) michael@0: { michael@0: const char *handshakeName = (const char *)client_data; michael@0: if (handshakeName && !failedToNegotiateName) { michael@0: SECItem *hostInfo = SSL_GetNegotiatedHostInfo(fd); michael@0: if (!hostInfo || PORT_Strncmp(handshakeName, (char*)hostInfo->data, michael@0: hostInfo->len)) { michael@0: failedToNegotiateName = PR_TRUE; michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: server_main( michael@0: PRFileDesc * listen_sock, michael@0: int requestCert, michael@0: SECKEYPrivateKey ** privKey, michael@0: CERTCertificate ** cert, michael@0: const char *expectedHostNameVal) michael@0: { michael@0: PRFileDesc *model_sock = NULL; michael@0: int rv; michael@0: SSLKEAType kea; michael@0: SECStatus secStatus; michael@0: michael@0: if (useModelSocket) { michael@0: model_sock = PR_NewTCPSocket(); michael@0: if (model_sock == NULL) { michael@0: errExit("PR_NewTCPSocket on model socket"); michael@0: } michael@0: model_sock = SSL_ImportFD(NULL, model_sock); michael@0: if (model_sock == NULL) { michael@0: errExit("SSL_ImportFD"); michael@0: } michael@0: } else { michael@0: model_sock = listen_sock = SSL_ImportFD(NULL, listen_sock); michael@0: if (listen_sock == NULL) { michael@0: errExit("SSL_ImportFD"); michael@0: } michael@0: } michael@0: michael@0: /* do SSL configuration. */ michael@0: rv = SSL_OptionSet(model_sock, SSL_SECURITY, michael@0: enableSSL2 || enabledVersions.min != 0); michael@0: if (rv < 0) { michael@0: errExit("SSL_OptionSet SSL_SECURITY"); michael@0: } michael@0: michael@0: rv = SSL_VersionRangeSet(model_sock, &enabledVersions); michael@0: if (rv != SECSuccess) { michael@0: errExit("error setting SSL/TLS version range "); michael@0: } michael@0: michael@0: rv = SSL_OptionSet(model_sock, SSL_ENABLE_SSL2, enableSSL2); michael@0: if (rv != SECSuccess) { michael@0: errExit("error enabling SSLv2 "); michael@0: } michael@0: michael@0: rv = SSL_OptionSet(model_sock, SSL_ROLLBACK_DETECTION, !disableRollBack); michael@0: if (rv != SECSuccess) { michael@0: errExit("error enabling RollBack detection "); michael@0: } michael@0: if (disableStepDown) { michael@0: rv = SSL_OptionSet(model_sock, SSL_NO_STEP_DOWN, PR_TRUE); michael@0: if (rv != SECSuccess) { michael@0: errExit("error disabling SSL StepDown "); michael@0: } michael@0: } michael@0: if (bypassPKCS11) { michael@0: rv = SSL_OptionSet(model_sock, SSL_BYPASS_PKCS11, PR_TRUE); michael@0: if (rv != SECSuccess) { michael@0: errExit("error enabling PKCS11 bypass "); michael@0: } michael@0: } michael@0: if (disableLocking) { michael@0: rv = SSL_OptionSet(model_sock, SSL_NO_LOCKS, PR_TRUE); michael@0: if (rv != SECSuccess) { michael@0: errExit("error disabling SSL socket locking "); michael@0: } michael@0: } michael@0: if (enableSessionTickets) { michael@0: rv = SSL_OptionSet(model_sock, SSL_ENABLE_SESSION_TICKETS, PR_TRUE); michael@0: if (rv != SECSuccess) { michael@0: errExit("error enabling Session Ticket extension "); michael@0: } michael@0: } michael@0: michael@0: if (enableCompression) { michael@0: rv = SSL_OptionSet(model_sock, SSL_ENABLE_DEFLATE, PR_TRUE); michael@0: if (rv != SECSuccess) { michael@0: errExit("error enabling compression "); michael@0: } michael@0: } michael@0: michael@0: if (virtServerNameIndex >1) { michael@0: rv = SSL_SNISocketConfigHook(model_sock, mySSLSNISocketConfig, michael@0: (void*)&virtServerNameArray); michael@0: if (rv != SECSuccess) { michael@0: errExit("error enabling SNI extension "); michael@0: } michael@0: } michael@0: michael@0: for (kea = kt_rsa; kea < kt_kea_size; kea++) { michael@0: if (cert[kea] != NULL) { michael@0: secStatus = SSL_ConfigSecureServer(model_sock, michael@0: cert[kea], privKey[kea], kea); michael@0: if (secStatus != SECSuccess) michael@0: errExit("SSL_ConfigSecureServer"); michael@0: } michael@0: } michael@0: michael@0: if (bigBuf.data) { /* doing FDX */ michael@0: rv = SSL_OptionSet(model_sock, SSL_ENABLE_FDX, 1); michael@0: if (rv < 0) { michael@0: errExit("SSL_OptionSet SSL_ENABLE_FDX"); michael@0: } michael@0: } michael@0: michael@0: if (NoReuse) { michael@0: rv = SSL_OptionSet(model_sock, SSL_NO_CACHE, 1); michael@0: if (rv < 0) { michael@0: errExit("SSL_OptionSet SSL_NO_CACHE"); michael@0: } michael@0: } michael@0: michael@0: /* This cipher is not on by default. The Acceptance test michael@0: * would like it to be. Turn this cipher on. michael@0: */ michael@0: michael@0: secStatus = SSL_CipherPrefSetDefault( TLS_RSA_WITH_NULL_MD5, PR_TRUE); michael@0: if ( secStatus != SECSuccess ) { michael@0: errExit("SSL_CipherPrefSetDefault:TLS_RSA_WITH_NULL_MD5"); michael@0: } michael@0: michael@0: if (expectedHostNameVal) { michael@0: SSL_HandshakeCallback(model_sock, handshakeCallback, michael@0: (void*)expectedHostNameVal); michael@0: } michael@0: michael@0: if (requestCert) { michael@0: SSL_AuthCertificateHook(model_sock, mySSLAuthCertificate, michael@0: (void *)CERT_GetDefaultCertDB()); michael@0: if (requestCert <= 2) { michael@0: rv = SSL_OptionSet(model_sock, SSL_REQUEST_CERTIFICATE, 1); michael@0: if (rv < 0) { michael@0: errExit("first SSL_OptionSet SSL_REQUEST_CERTIFICATE"); michael@0: } michael@0: rv = SSL_OptionSet(model_sock, SSL_REQUIRE_CERTIFICATE, michael@0: (requestCert == 2)); michael@0: if (rv < 0) { michael@0: errExit("first SSL_OptionSet SSL_REQUIRE_CERTIFICATE"); michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (MakeCertOK) michael@0: SSL_BadCertHook(model_sock, myBadCertHandler, NULL); michael@0: michael@0: /* end of ssl configuration. */ michael@0: michael@0: michael@0: /* Now, do the accepting, here in the main thread. */ michael@0: rv = do_accepts(listen_sock, model_sock, requestCert); michael@0: michael@0: terminateWorkerThreads(); michael@0: michael@0: if (useModelSocket && model_sock) { michael@0: if (model_sock) { michael@0: PR_Close(model_sock); michael@0: } michael@0: } michael@0: michael@0: } michael@0: michael@0: SECStatus michael@0: readBigFile(const char * fileName) michael@0: { michael@0: PRFileInfo info; michael@0: PRStatus status; michael@0: SECStatus rv = SECFailure; michael@0: int count; michael@0: int hdrLen; michael@0: PRFileDesc *local_file_fd = NULL; michael@0: michael@0: status = PR_GetFileInfo(fileName, &info); michael@0: michael@0: if (status == PR_SUCCESS && michael@0: info.type == PR_FILE_FILE && michael@0: info.size > 0 && michael@0: NULL != (local_file_fd = PR_Open(fileName, PR_RDONLY, 0))) { michael@0: michael@0: hdrLen = PORT_Strlen(outHeader); michael@0: bigBuf.len = hdrLen + info.size; michael@0: bigBuf.data = PORT_Malloc(bigBuf.len + 4095); michael@0: if (!bigBuf.data) { michael@0: errWarn("PORT_Malloc"); michael@0: goto done; michael@0: } michael@0: michael@0: PORT_Memcpy(bigBuf.data, outHeader, hdrLen); michael@0: michael@0: count = PR_Read(local_file_fd, bigBuf.data + hdrLen, info.size); michael@0: if (count != info.size) { michael@0: errWarn("PR_Read local file"); michael@0: goto done; michael@0: } michael@0: rv = SECSuccess; michael@0: done: michael@0: if (local_file_fd) { michael@0: PR_Close(local_file_fd); michael@0: } michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: int numChildren; michael@0: PRProcess * child[MAX_PROCS]; michael@0: michael@0: PRProcess * michael@0: haveAChild(int argc, char **argv, PRProcessAttr * attr) michael@0: { michael@0: PRProcess * newProcess; michael@0: michael@0: newProcess = PR_CreateProcess(argv[0], argv, NULL, attr); michael@0: if (!newProcess) { michael@0: errWarn("Can't create new process."); michael@0: } else { michael@0: child[numChildren++] = newProcess; michael@0: } michael@0: return newProcess; michael@0: } michael@0: michael@0: void michael@0: beAGoodParent(int argc, char **argv, int maxProcs, PRFileDesc * listen_sock) michael@0: { michael@0: PRProcess * newProcess; michael@0: PRProcessAttr * attr; michael@0: int i; michael@0: PRInt32 exitCode; michael@0: PRStatus rv; michael@0: michael@0: rv = PR_SetFDInheritable(listen_sock, PR_TRUE); michael@0: if (rv != PR_SUCCESS) michael@0: errExit("PR_SetFDInheritable"); michael@0: michael@0: attr = PR_NewProcessAttr(); michael@0: if (!attr) michael@0: errExit("PR_NewProcessAttr"); michael@0: michael@0: rv = PR_ProcessAttrSetInheritableFD(attr, listen_sock, inheritableSockName); michael@0: if (rv != PR_SUCCESS) michael@0: errExit("PR_ProcessAttrSetInheritableFD"); michael@0: michael@0: for (i = 0; i < maxProcs; ++i) { michael@0: newProcess = haveAChild(argc, argv, attr); michael@0: if (!newProcess) michael@0: break; michael@0: } michael@0: michael@0: rv = PR_SetFDInheritable(listen_sock, PR_FALSE); michael@0: if (rv != PR_SUCCESS) michael@0: errExit("PR_SetFDInheritable"); michael@0: michael@0: while (numChildren > 0) { michael@0: newProcess = child[numChildren - 1]; michael@0: PR_WaitProcess(newProcess, &exitCode); michael@0: fprintf(stderr, "Child %d exited with exit code %x\n", michael@0: numChildren, exitCode); michael@0: numChildren--; michael@0: } michael@0: exit(0); michael@0: } michael@0: michael@0: #define HEXCHAR_TO_INT(c, i) \ michael@0: if (((c) >= '0') && ((c) <= '9')) { \ michael@0: i = (c) - '0'; \ michael@0: } else if (((c) >= 'a') && ((c) <= 'f')) { \ michael@0: i = (c) - 'a' + 10; \ michael@0: } else if (((c) >= 'A') && ((c) <= 'F')) { \ michael@0: i = (c) - 'A' + 10; \ michael@0: } else if ((c) == '\0') { \ michael@0: fprintf(stderr, "Invalid length of cipher string (-c :WXYZ).\n"); \ michael@0: exit(9); \ michael@0: } else { \ michael@0: fprintf(stderr, "Non-hex char in cipher string (-c :WXYZ).\n"); \ michael@0: exit(9); \ michael@0: } michael@0: michael@0: SECStatus enableOCSPStapling(const char* mode) michael@0: { michael@0: if (!strcmp(mode, "good")) { michael@0: ocspStaplingMode = osm_good; michael@0: return SECSuccess; michael@0: } michael@0: if (!strcmp(mode, "unknown")) { michael@0: ocspStaplingMode = osm_unknown; michael@0: return SECSuccess; michael@0: } michael@0: if (!strcmp(mode, "revoked")) { michael@0: ocspStaplingMode = osm_revoked; michael@0: return SECSuccess; michael@0: } michael@0: if (!strcmp(mode, "badsig")) { michael@0: ocspStaplingMode = osm_badsig; michael@0: return SECSuccess; michael@0: } michael@0: if (!strcmp(mode, "corrupted")) { michael@0: ocspStaplingMode = osm_corrupted; michael@0: return SECSuccess; michael@0: } michael@0: if (!strcmp(mode, "failure")) { michael@0: ocspStaplingMode = osm_failure; michael@0: return SECSuccess; michael@0: } michael@0: if (!strcmp(mode, "random")) { michael@0: ocspStaplingMode = osm_random; michael@0: return SECSuccess; michael@0: } michael@0: if (!strcmp(mode, "ocsp")) { michael@0: ocspStaplingMode = osm_ocsp; michael@0: return SECSuccess; michael@0: } michael@0: return SECFailure; michael@0: } michael@0: michael@0: int michael@0: main(int argc, char **argv) michael@0: { michael@0: char * progName = NULL; michael@0: char * nickName = NULL; michael@0: #ifndef NSS_DISABLE_ECC michael@0: char * ecNickName = NULL; michael@0: #endif michael@0: const char * fileName = NULL; michael@0: char * cipherString= NULL; michael@0: const char * dir = "."; michael@0: char * passwd = NULL; michael@0: char * pwfile = NULL; michael@0: const char * pidFile = NULL; michael@0: char * tmp; michael@0: char * envString; michael@0: PRFileDesc * listen_sock; michael@0: CERTCertificate * cert [kt_kea_size] = { NULL }; michael@0: SECKEYPrivateKey * privKey[kt_kea_size] = { NULL }; michael@0: int optionsFound = 0; michael@0: int maxProcs = 1; michael@0: unsigned short port = 0; michael@0: SECStatus rv; michael@0: PRStatus prStatus; michael@0: PRBool bindOnly = PR_FALSE; michael@0: PRBool useExportPolicy = PR_FALSE; michael@0: PRBool useLocalThreads = PR_FALSE; michael@0: PLOptState *optstate; michael@0: PLOptStatus status; michael@0: PRThread *loggerThread = NULL; michael@0: PRBool debugCache = PR_FALSE; /* bug 90518 */ michael@0: char emptyString[] = { "" }; michael@0: char* certPrefix = emptyString; michael@0: PRUint32 protos = 0; michael@0: SSL3Statistics *ssl3stats; michael@0: PRUint32 i; michael@0: secuPWData pwdata = { PW_NONE, 0 }; michael@0: char *expectedHostNameVal = NULL; michael@0: PLArenaPool *certStatusArena = NULL; michael@0: michael@0: tmp = strrchr(argv[0], '/'); michael@0: tmp = tmp ? tmp + 1 : argv[0]; michael@0: progName = strrchr(tmp, '\\'); michael@0: progName = progName ? progName + 1 : tmp; michael@0: michael@0: PR_Init( PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1); michael@0: SSL_VersionRangeGetSupported(ssl_variant_stream, &enabledVersions); michael@0: michael@0: /* please keep this list of options in ASCII collating sequence. michael@0: ** numbers, then capital letters, then lower case, alphabetical. michael@0: */ michael@0: optstate = PL_CreateOptState(argc, argv, michael@0: "2:A:BC:DEL:M:NP:RT:V:Ya:bc:d:e:f:g:hi:jk:lmn:op:qrst:uvw:xyz"); michael@0: while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) { michael@0: ++optionsFound; michael@0: switch(optstate->option) { michael@0: case '2': fileName = optstate->value; break; michael@0: michael@0: case 'A': ocspStaplingCA = PORT_Strdup(optstate->value); break; michael@0: michael@0: case 'B': bypassPKCS11 = PR_TRUE; break; michael@0: michael@0: case 'C': if (optstate->value) NumSidCacheEntries = PORT_Atoi(optstate->value); break; michael@0: michael@0: case 'D': noDelay = PR_TRUE; break; michael@0: case 'E': disableStepDown = PR_TRUE; break; michael@0: michael@0: case 'I': /* reserved for OCSP multi-stapling */ break; michael@0: michael@0: case 'L': michael@0: logStats = PR_TRUE; michael@0: if (optstate->value == NULL) { michael@0: logPeriod = 30; michael@0: } else { michael@0: logPeriod = PORT_Atoi(optstate->value); michael@0: if (logPeriod <= 0) logPeriod = 30; michael@0: } michael@0: break; michael@0: michael@0: case 'M': michael@0: maxProcs = PORT_Atoi(optstate->value); michael@0: if (maxProcs < 1) maxProcs = 1; michael@0: if (maxProcs > MAX_PROCS) maxProcs = MAX_PROCS; michael@0: break; michael@0: michael@0: case 'N': NoReuse = PR_TRUE; break; michael@0: michael@0: case 'R': disableRollBack = PR_TRUE; break; michael@0: michael@0: case 'T': michael@0: if (enableOCSPStapling(optstate->value) != SECSuccess) { michael@0: fprintf(stderr, "Invalid OCSP stapling mode.\n"); michael@0: fprintf(stderr, "Run '%s -h' for usage information.\n", progName); michael@0: exit(53); michael@0: } michael@0: break; michael@0: michael@0: case 'V': if (SECU_ParseSSLVersionRangeString(optstate->value, michael@0: enabledVersions, enableSSL2, michael@0: &enabledVersions, &enableSSL2) != SECSuccess) { michael@0: Usage(progName); michael@0: } michael@0: break; michael@0: michael@0: case 'Y': PrintCipherUsage(progName); exit(0); break; michael@0: michael@0: case 'a': if (virtServerNameIndex >= MAX_VIRT_SERVER_NAME_ARRAY_INDEX) { michael@0: Usage(progName); michael@0: } michael@0: virtServerNameArray[virtServerNameIndex++] = michael@0: PORT_Strdup(optstate->value); break; michael@0: michael@0: case 'b': bindOnly = PR_TRUE; break; michael@0: michael@0: case 'c': cipherString = PORT_Strdup(optstate->value); break; michael@0: michael@0: case 'd': dir = optstate->value; break; michael@0: michael@0: #ifndef NSS_DISABLE_ECC michael@0: case 'e': ecNickName = PORT_Strdup(optstate->value); break; michael@0: #endif /* NSS_DISABLE_ECC */ michael@0: michael@0: case 'f': michael@0: pwdata.source = PW_FROMFILE; michael@0: pwdata.data = pwfile = PORT_Strdup(optstate->value); michael@0: break; michael@0: michael@0: case 'g': michael@0: testBulk = PR_TRUE; michael@0: testBulkTotal = PORT_Atoi(optstate->value); michael@0: break; michael@0: michael@0: case 'h': Usage(progName); exit(0); break; michael@0: michael@0: case 'i': pidFile = optstate->value; break; michael@0: michael@0: case 'j': michael@0: initLoggingLayer(); michael@0: loggingLayer = PR_TRUE; michael@0: break; michael@0: michael@0: case 'k': expectedHostNameVal = PORT_Strdup(optstate->value); michael@0: break; michael@0: michael@0: case 'l': useLocalThreads = PR_TRUE; break; michael@0: michael@0: case 'm': useModelSocket = PR_TRUE; break; michael@0: michael@0: case 'n': nickName = PORT_Strdup(optstate->value); michael@0: virtServerNameArray[0] = PORT_Strdup(optstate->value); michael@0: break; michael@0: michael@0: case 'P': certPrefix = PORT_Strdup(optstate->value); break; michael@0: michael@0: case 'o': MakeCertOK = 1; break; michael@0: michael@0: case 'p': port = PORT_Atoi(optstate->value); break; michael@0: michael@0: case 'q': testbypass = PR_TRUE; break; michael@0: michael@0: case 'r': ++requestCert; break; michael@0: michael@0: case 's': disableLocking = PR_TRUE; break; michael@0: michael@0: case 't': michael@0: maxThreads = PORT_Atoi(optstate->value); michael@0: if ( maxThreads > MAX_THREADS ) maxThreads = MAX_THREADS; michael@0: if ( maxThreads < MIN_THREADS ) maxThreads = MIN_THREADS; michael@0: break; michael@0: michael@0: case 'u': enableSessionTickets = PR_TRUE; break; michael@0: michael@0: case 'v': verbose++; break; michael@0: michael@0: case 'w': michael@0: pwdata.source = PW_PLAINTEXT; michael@0: pwdata.data = passwd = PORT_Strdup(optstate->value); michael@0: break; michael@0: michael@0: case 'x': useExportPolicy = PR_TRUE; break; michael@0: michael@0: case 'y': debugCache = PR_TRUE; break; michael@0: michael@0: case 'z': enableCompression = PR_TRUE; break; michael@0: michael@0: default: michael@0: case '?': michael@0: fprintf(stderr, "Unrecognized or bad option specified.\n"); michael@0: fprintf(stderr, "Run '%s -h' for usage information.\n", progName); michael@0: exit(4); michael@0: break; michael@0: } michael@0: } michael@0: PL_DestroyOptState(optstate); michael@0: if (status == PL_OPT_BAD) { michael@0: fprintf(stderr, "Unrecognized or bad option specified.\n"); michael@0: fprintf(stderr, "Run '%s -h' for usage information.\n", progName); michael@0: exit(5); michael@0: } michael@0: if (!optionsFound) { michael@0: Usage(progName); michael@0: exit(51); michael@0: } michael@0: switch (ocspStaplingMode) { michael@0: case osm_good: michael@0: case osm_revoked: michael@0: case osm_unknown: michael@0: case osm_random: michael@0: if (!ocspStaplingCA) { michael@0: fprintf(stderr, "Selected stapling response requires the -A parameter.\n"); michael@0: fprintf(stderr, "Run '%s -h' for usage information.\n", progName); michael@0: exit(52); michael@0: } michael@0: break; michael@0: default: michael@0: break; michael@0: } michael@0: michael@0: /* The -b (bindOnly) option is only used by the ssl.sh test michael@0: * script on Linux to determine whether a previous selfserv michael@0: * process has fully died and freed the port. (Bug 129701) michael@0: */ michael@0: if (bindOnly) { michael@0: listen_sock = getBoundListenSocket(port); michael@0: if (!listen_sock) { michael@0: exit(1); michael@0: } michael@0: if (listen_sock) { michael@0: PR_Close(listen_sock); michael@0: } michael@0: exit(0); michael@0: } michael@0: michael@0: if ((nickName == NULL) michael@0: #ifndef NSS_DISABLE_ECC michael@0: && (ecNickName == NULL) michael@0: #endif michael@0: ) { michael@0: michael@0: fprintf(stderr, "Required arg '-n' (rsa nickname) not supplied.\n"); michael@0: fprintf(stderr, "Run '%s -h' for usage information.\n", progName); michael@0: exit(6); michael@0: } michael@0: michael@0: if (port == 0) { michael@0: fprintf(stderr, "Required argument 'port' must be non-zero value\n"); michael@0: exit(7); michael@0: } michael@0: michael@0: if (NoReuse && maxProcs > 1) { michael@0: fprintf(stderr, "-M and -N options are mutually exclusive.\n"); michael@0: exit(14); michael@0: } michael@0: michael@0: if (pidFile) { michael@0: FILE *tmpfile=fopen(pidFile,"w+"); michael@0: michael@0: if (tmpfile) { michael@0: fprintf(tmpfile,"%d",getpid()); michael@0: fclose(tmpfile); michael@0: } michael@0: } michael@0: michael@0: /* allocate and initialize app data for bulk encryption testing */ michael@0: if (testBulk) { michael@0: testBulkBuf = PORT_Malloc(testBulkSize); michael@0: if (testBulkBuf == NULL) michael@0: errExit("Out of memory: testBulkBuf"); michael@0: for (i = 0; i < testBulkSize; i++) michael@0: testBulkBuf[i] = i; michael@0: } michael@0: michael@0: envString = getenv(envVarName); michael@0: tmp = getenv("TMP"); michael@0: if (!tmp) michael@0: tmp = getenv("TMPDIR"); michael@0: if (!tmp) michael@0: tmp = getenv("TEMP"); michael@0: if (envString) { michael@0: /* we're one of the children in a multi-process server. */ michael@0: listen_sock = PR_GetInheritedFD(inheritableSockName); michael@0: if (!listen_sock) michael@0: errExit("PR_GetInheritedFD"); michael@0: #ifndef WINNT michael@0: /* we can't do this on NT because it breaks NSPR and michael@0: PR_Accept will fail on the socket in the child process if michael@0: the socket state is change to non inheritable michael@0: It is however a security issue to leave it accessible, michael@0: but it is OK for a test server such as selfserv. michael@0: NSPR should fix it eventually . see bugzilla 101617 michael@0: and 102077 michael@0: */ michael@0: prStatus = PR_SetFDInheritable(listen_sock, PR_FALSE); michael@0: if (prStatus != PR_SUCCESS) michael@0: errExit("PR_SetFDInheritable"); michael@0: #endif michael@0: rv = SSL_InheritMPServerSIDCache(envString); michael@0: if (rv != SECSuccess) michael@0: errExit("SSL_InheritMPServerSIDCache"); michael@0: hasSidCache = PR_TRUE; michael@0: } else if (maxProcs > 1) { michael@0: /* we're going to be the parent in a multi-process server. */ michael@0: listen_sock = getBoundListenSocket(port); michael@0: rv = SSL_ConfigMPServerSIDCache(NumSidCacheEntries, 0, 0, tmp); michael@0: if (rv != SECSuccess) michael@0: errExit("SSL_ConfigMPServerSIDCache"); michael@0: hasSidCache = PR_TRUE; michael@0: beAGoodParent(argc, argv, maxProcs, listen_sock); michael@0: exit(99); /* should never get here */ michael@0: } else { michael@0: /* we're an ordinary single process server. */ michael@0: listen_sock = getBoundListenSocket(port); michael@0: prStatus = PR_SetFDInheritable(listen_sock, PR_FALSE); michael@0: if (prStatus != PR_SUCCESS) michael@0: errExit("PR_SetFDInheritable"); michael@0: if (!NoReuse) { michael@0: rv = SSL_ConfigServerSessionIDCache(NumSidCacheEntries, michael@0: 0, 0, tmp); michael@0: if (rv != SECSuccess) michael@0: errExit("SSL_ConfigServerSessionIDCache"); michael@0: hasSidCache = PR_TRUE; michael@0: } michael@0: } michael@0: michael@0: lm = PR_NewLogModule("TestCase"); michael@0: michael@0: if (fileName) michael@0: readBigFile(fileName); michael@0: michael@0: /* set our password function */ michael@0: PK11_SetPasswordFunc(SECU_GetModulePassword); michael@0: michael@0: /* Call the NSS initialization routines */ michael@0: rv = NSS_Initialize(dir, certPrefix, certPrefix, SECMOD_DB, NSS_INIT_READONLY); michael@0: if (rv != SECSuccess) { michael@0: fputs("NSS_Init failed.\n", stderr); michael@0: exit(8); michael@0: } michael@0: michael@0: /* set the policy bits true for all the cipher suites. */ michael@0: if (useExportPolicy) { michael@0: NSS_SetExportPolicy(); michael@0: if (disableStepDown) { michael@0: fputs("selfserv: -x and -E options may not be used together\n", michael@0: stderr); michael@0: exit(98); michael@0: } michael@0: } else { michael@0: NSS_SetDomesticPolicy(); michael@0: if (disableStepDown) { michael@0: rv = disableExportSSLCiphers(); michael@0: if (rv != SECSuccess) { michael@0: errExit("error disabling export ciphersuites "); michael@0: } michael@0: } michael@0: } michael@0: michael@0: /* all the SSL2 and SSL3 cipher suites are enabled by default. */ michael@0: if (cipherString) { michael@0: char *cstringSaved = cipherString; michael@0: int ndx; michael@0: michael@0: /* disable all the ciphers, then enable the ones we want. */ michael@0: disableAllSSLCiphers(); michael@0: michael@0: while (0 != (ndx = *cipherString++)) { michael@0: int cipher; michael@0: michael@0: if (ndx == ':') { michael@0: int ctmp; michael@0: michael@0: cipher = 0; michael@0: HEXCHAR_TO_INT(*cipherString, ctmp) michael@0: cipher |= (ctmp << 12); michael@0: cipherString++; michael@0: HEXCHAR_TO_INT(*cipherString, ctmp) michael@0: cipher |= (ctmp << 8); michael@0: cipherString++; michael@0: HEXCHAR_TO_INT(*cipherString, ctmp) michael@0: cipher |= (ctmp << 4); michael@0: cipherString++; michael@0: HEXCHAR_TO_INT(*cipherString, ctmp) michael@0: cipher |= ctmp; michael@0: cipherString++; michael@0: } else { michael@0: const int *cptr; michael@0: michael@0: if (! isalpha(ndx)) { michael@0: fprintf(stderr, michael@0: "Non-alphabetic char in cipher string (-c arg).\n"); michael@0: exit(9); michael@0: } michael@0: cptr = islower(ndx) ? ssl3CipherSuites : ssl2CipherSuites; michael@0: for (ndx &= 0x1f; (cipher = *cptr++) != 0 && --ndx > 0; ) michael@0: /* do nothing */; michael@0: } michael@0: if (cipher > 0) { michael@0: SECStatus status; michael@0: status = SSL_CipherPrefSetDefault(cipher, SSL_ALLOWED); michael@0: if (status != SECSuccess) michael@0: SECU_PrintError(progName, "SSL_CipherPrefSet()"); michael@0: } else { michael@0: fprintf(stderr, michael@0: "Invalid cipher specification (-c arg).\n"); michael@0: exit(9); michael@0: } michael@0: } michael@0: PORT_Free(cstringSaved); michael@0: } michael@0: michael@0: if (testbypass) { michael@0: const PRUint16 *cipherSuites = SSL_ImplementedCiphers; michael@0: int i = SSL_NumImplementedCiphers; michael@0: PRBool enabled; michael@0: michael@0: for (i=0; i < SSL_NumImplementedCiphers; i++, cipherSuites++) { michael@0: if (SSL_CipherPrefGetDefault(*cipherSuites, &enabled) == SECSuccess michael@0: && enabled) michael@0: savecipher(*cipherSuites); michael@0: } michael@0: protos = 0; michael@0: if (enabledVersions.min <= SSL_LIBRARY_VERSION_3_0 && michael@0: enabledVersions.max >= SSL_LIBRARY_VERSION_3_0) { michael@0: protos |= SSL_CBP_SSL3; michael@0: } michael@0: if (enabledVersions.min <= SSL_LIBRARY_VERSION_TLS_1_0 && michael@0: enabledVersions.max >= SSL_LIBRARY_VERSION_TLS_1_0) { michael@0: protos |= SSL_CBP_TLS1_0; michael@0: } michael@0: /* TLS 1.1 has the same SSL Bypass mode requirements as TLS 1.0 */ michael@0: if (enabledVersions.min <= SSL_LIBRARY_VERSION_TLS_1_1 && michael@0: enabledVersions.max >= SSL_LIBRARY_VERSION_TLS_1_1) { michael@0: protos |= SSL_CBP_TLS1_0; michael@0: } michael@0: } michael@0: michael@0: certStatusArena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); michael@0: if (!certStatusArena) michael@0: errExit("cannot allocate certStatusArena"); michael@0: michael@0: if (nickName) { michael@0: cert[kt_rsa] = PK11_FindCertFromNickname(nickName, &pwdata); michael@0: if (cert[kt_rsa] == NULL) { michael@0: fprintf(stderr, "selfserv: Can't find certificate %s\n", nickName); michael@0: exit(10); michael@0: } michael@0: privKey[kt_rsa] = PK11_FindKeyByAnyCert(cert[kt_rsa], &pwdata); michael@0: if (privKey[kt_rsa] == NULL) { michael@0: fprintf(stderr, "selfserv: Can't find Private Key for cert %s\n", michael@0: nickName); michael@0: exit(11); michael@0: } michael@0: if (testbypass) { michael@0: PRBool bypassOK; michael@0: if (SSL_CanBypass(cert[kt_rsa], privKey[kt_rsa], protos, cipherlist, michael@0: nciphers, &bypassOK, &pwdata) != SECSuccess) { michael@0: SECU_PrintError(progName, "Bypass test failed %s\n", nickName); michael@0: exit(14); michael@0: } michael@0: fprintf(stderr, "selfserv: %s can%s bypass\n", nickName, michael@0: bypassOK ? "" : "not"); michael@0: } michael@0: setupCertStatus(certStatusArena, ocspStaplingMode, cert[kt_rsa], kt_rsa, michael@0: &pwdata); michael@0: } michael@0: #ifndef NSS_DISABLE_ECC michael@0: if (ecNickName) { michael@0: cert[kt_ecdh] = PK11_FindCertFromNickname(ecNickName, &pwdata); michael@0: if (cert[kt_ecdh] == NULL) { michael@0: fprintf(stderr, "selfserv: Can't find certificate %s\n", michael@0: ecNickName); michael@0: exit(13); michael@0: } michael@0: privKey[kt_ecdh] = PK11_FindKeyByAnyCert(cert[kt_ecdh], &pwdata); michael@0: if (privKey[kt_ecdh] == NULL) { michael@0: fprintf(stderr, "selfserv: Can't find Private Key for cert %s\n", michael@0: ecNickName); michael@0: exit(11); michael@0: } michael@0: if (testbypass) { michael@0: PRBool bypassOK; michael@0: if (SSL_CanBypass(cert[kt_ecdh], privKey[kt_ecdh], protos, cipherlist, michael@0: nciphers, &bypassOK, &pwdata) != SECSuccess) { michael@0: SECU_PrintError(progName, "Bypass test failed %s\n", ecNickName); michael@0: exit(15); michael@0: } michael@0: fprintf(stderr, "selfserv: %s can%s bypass\n", ecNickName, michael@0: bypassOK ? "" : "not"); michael@0: } michael@0: setupCertStatus(certStatusArena, ocspStaplingMode, cert[kt_ecdh], kt_ecdh, michael@0: &pwdata); michael@0: } michael@0: #endif /* NSS_DISABLE_ECC */ michael@0: michael@0: if (testbypass) michael@0: goto cleanup; michael@0: michael@0: /* allocate the array of thread slots, and launch the worker threads. */ michael@0: rv = launch_threads(&jobLoop, 0, 0, requestCert, useLocalThreads); michael@0: michael@0: if (rv == SECSuccess && logStats) { michael@0: loggerThread = PR_CreateThread(PR_SYSTEM_THREAD, michael@0: logger, NULL, PR_PRIORITY_NORMAL, michael@0: useLocalThreads ? PR_LOCAL_THREAD:PR_GLOBAL_THREAD, michael@0: PR_JOINABLE_THREAD, 0); michael@0: if (loggerThread == NULL) { michael@0: fprintf(stderr, "selfserv: Failed to launch logger thread!\n"); michael@0: rv = SECFailure; michael@0: } michael@0: } michael@0: michael@0: if (rv == SECSuccess) { michael@0: server_main(listen_sock, requestCert, privKey, cert, michael@0: expectedHostNameVal); michael@0: } michael@0: michael@0: VLOG(("selfserv: server_thread: exiting")); michael@0: michael@0: cleanup: michael@0: printSSLStatistics(); michael@0: ssl3stats = SSL_GetStatistics(); michael@0: if (ssl3stats->hch_sid_ticket_parse_failures != 0) { michael@0: fprintf(stderr, "selfserv: Experienced ticket parse failure(s)\n"); michael@0: exit(1); michael@0: } michael@0: if (failedToNegotiateName) { michael@0: fprintf(stderr, "selfserv: Failed properly negotiate server name\n"); michael@0: exit(1); michael@0: } michael@0: michael@0: { michael@0: int i; michael@0: for (i=0; i