security/nss/cmd/selfserv/selfserv.c

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 /* -r flag is interepreted as follows:
michael@0 6 * 1 -r means request, not require, on initial handshake.
michael@0 7 * 2 -r's mean request and require, on initial handshake.
michael@0 8 * 3 -r's mean request, not require, on second handshake.
michael@0 9 * 4 -r's mean request and require, on second handshake.
michael@0 10 */
michael@0 11 #include <stdio.h>
michael@0 12 #include <string.h>
michael@0 13
michael@0 14 #include "secutil.h"
michael@0 15
michael@0 16 #if defined(XP_UNIX)
michael@0 17 #include <unistd.h>
michael@0 18 #endif
michael@0 19
michael@0 20 #if defined(_WINDOWS)
michael@0 21 #include <process.h> /* for getpid() */
michael@0 22 #endif
michael@0 23
michael@0 24 #include <signal.h>
michael@0 25 #include <stdlib.h>
michael@0 26 #include <errno.h>
michael@0 27 #include <fcntl.h>
michael@0 28 #include <stdarg.h>
michael@0 29
michael@0 30 #include "nspr.h"
michael@0 31 #include "prio.h"
michael@0 32 #include "prerror.h"
michael@0 33 #include "prnetdb.h"
michael@0 34 #include "prclist.h"
michael@0 35 #include "plgetopt.h"
michael@0 36 #include "pk11func.h"
michael@0 37 #include "secitem.h"
michael@0 38 #include "nss.h"
michael@0 39 #include "ssl.h"
michael@0 40 #include "sslproto.h"
michael@0 41 #include "cert.h"
michael@0 42 #include "certt.h"
michael@0 43 #include "ocsp.h"
michael@0 44
michael@0 45 #ifndef PORT_Sprintf
michael@0 46 #define PORT_Sprintf sprintf
michael@0 47 #endif
michael@0 48
michael@0 49 #ifndef PORT_Strstr
michael@0 50 #define PORT_Strstr strstr
michael@0 51 #endif
michael@0 52
michael@0 53 #ifndef PORT_Malloc
michael@0 54 #define PORT_Malloc PR_Malloc
michael@0 55 #endif
michael@0 56
michael@0 57 int NumSidCacheEntries = 1024;
michael@0 58
michael@0 59 static int handle_connection( PRFileDesc *, PRFileDesc *, int );
michael@0 60
michael@0 61 static const char envVarName[] = { SSL_ENV_VAR_NAME };
michael@0 62 static const char inheritableSockName[] = { "SELFSERV_LISTEN_SOCKET" };
michael@0 63
michael@0 64 #define DEFAULT_BULK_TEST 16384
michael@0 65 #define MAX_BULK_TEST 1048576 /* 1 MB */
michael@0 66 static PRBool testBulk;
michael@0 67 static PRUint32 testBulkSize = DEFAULT_BULK_TEST;
michael@0 68 static PRUint32 testBulkTotal;
michael@0 69 static char* testBulkBuf;
michael@0 70 static PRDescIdentity log_layer_id = PR_INVALID_IO_LAYER;
michael@0 71 static PRFileDesc *loggingFD;
michael@0 72 static PRIOMethods loggingMethods;
michael@0 73
michael@0 74 static PRBool logStats;
michael@0 75 static PRBool loggingLayer;
michael@0 76 static int logPeriod = 30;
michael@0 77 static PRUint32 loggerOps;
michael@0 78 static PRUint32 loggerBytes;
michael@0 79 static PRUint32 loggerBytesTCP;
michael@0 80 static PRUint32 bulkSentChunks;
michael@0 81 static enum ocspStaplingModeEnum {
michael@0 82 osm_disabled, /* server doesn't support stapling */
michael@0 83 osm_good, /* supply a signed good status */
michael@0 84 osm_revoked, /* supply a signed revoked status */
michael@0 85 osm_unknown, /* supply a signed unknown status */
michael@0 86 osm_failure, /* supply a unsigned failure status, "try later" */
michael@0 87 osm_badsig, /* supply a good status response with a bad signature */
michael@0 88 osm_corrupted, /* supply a corrupted data block as the status */
michael@0 89 osm_random, /* use a random response for each connection */
michael@0 90 osm_ocsp /* retrieve ocsp status from external ocsp server,
michael@0 91 use empty status if server is unavailable */
michael@0 92 } ocspStaplingMode = osm_disabled;
michael@0 93 typedef enum ocspStaplingModeEnum ocspStaplingModeType;
michael@0 94 static char *ocspStaplingCA = NULL;
michael@0 95 static SECItemArray *certStatus[kt_kea_size] = { NULL };
michael@0 96
michael@0 97 const int ssl2CipherSuites[] = {
michael@0 98 SSL_EN_RC4_128_WITH_MD5, /* A */
michael@0 99 SSL_EN_RC4_128_EXPORT40_WITH_MD5, /* B */
michael@0 100 SSL_EN_RC2_128_CBC_WITH_MD5, /* C */
michael@0 101 SSL_EN_RC2_128_CBC_EXPORT40_WITH_MD5, /* D */
michael@0 102 SSL_EN_DES_64_CBC_WITH_MD5, /* E */
michael@0 103 SSL_EN_DES_192_EDE3_CBC_WITH_MD5, /* F */
michael@0 104 0
michael@0 105 };
michael@0 106
michael@0 107 const int ssl3CipherSuites[] = {
michael@0 108 -1, /* SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA* a */
michael@0 109 -1, /* SSL_FORTEZZA_DMS_WITH_RC4_128_SHA * b */
michael@0 110 TLS_RSA_WITH_RC4_128_MD5, /* c */
michael@0 111 TLS_RSA_WITH_3DES_EDE_CBC_SHA, /* d */
michael@0 112 TLS_RSA_WITH_DES_CBC_SHA, /* e */
michael@0 113 TLS_RSA_EXPORT_WITH_RC4_40_MD5, /* f */
michael@0 114 TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5, /* g */
michael@0 115 -1, /* SSL_FORTEZZA_DMS_WITH_NULL_SHA, * h */
michael@0 116 TLS_RSA_WITH_NULL_MD5, /* i */
michael@0 117 SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA, /* j */
michael@0 118 SSL_RSA_FIPS_WITH_DES_CBC_SHA, /* k */
michael@0 119 TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA, /* l */
michael@0 120 TLS_RSA_EXPORT1024_WITH_RC4_56_SHA, /* m */
michael@0 121 TLS_RSA_WITH_RC4_128_SHA, /* n */
michael@0 122 -1, /* TLS_DHE_DSS_WITH_RC4_128_SHA, * o */
michael@0 123 -1, /* TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, * p */
michael@0 124 -1, /* TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA, * q */
michael@0 125 -1, /* TLS_DHE_RSA_WITH_DES_CBC_SHA, * r */
michael@0 126 -1, /* TLS_DHE_DSS_WITH_DES_CBC_SHA, * s */
michael@0 127 -1, /* TLS_DHE_DSS_WITH_AES_128_CBC_SHA, * t */
michael@0 128 -1, /* TLS_DHE_RSA_WITH_AES_128_CBC_SHA, * u */
michael@0 129 TLS_RSA_WITH_AES_128_CBC_SHA, /* v */
michael@0 130 -1, /* TLS_DHE_DSS_WITH_AES_256_CBC_SHA, * w */
michael@0 131 -1, /* TLS_DHE_RSA_WITH_AES_256_CBC_SHA, * x */
michael@0 132 TLS_RSA_WITH_AES_256_CBC_SHA, /* y */
michael@0 133 TLS_RSA_WITH_NULL_SHA, /* z */
michael@0 134 0
michael@0 135 };
michael@0 136
michael@0 137 /* data and structures for shutdown */
michael@0 138 static int stopping;
michael@0 139
michael@0 140 static PRBool noDelay;
michael@0 141 static int requestCert;
michael@0 142 static int verbose;
michael@0 143 static SECItem bigBuf;
michael@0 144
michael@0 145 static PRThread * acceptorThread;
michael@0 146
michael@0 147 static PRLogModuleInfo *lm;
michael@0 148
michael@0 149 #define PRINTF if (verbose) printf
michael@0 150 #define FPRINTF if (verbose) fprintf
michael@0 151 #define FLUSH if (verbose) { fflush(stdout); fflush(stderr); }
michael@0 152 #define VLOG(arg) PR_LOG(lm,PR_LOG_DEBUG,arg)
michael@0 153
michael@0 154 static void
michael@0 155 PrintUsageHeader(const char *progName)
michael@0 156 {
michael@0 157 fprintf(stderr,
michael@0 158 "Usage: %s -n rsa_nickname -p port [-BDENRbjlmrsuvx] [-w password]\n"
michael@0 159 " [-t threads] [-i pid_file] [-c ciphers] [-Y] [-d dbdir] [-g numblocks]\n"
michael@0 160 " [-f password_file] [-L [seconds]] [-M maxProcs] [-P dbprefix]\n"
michael@0 161 " [-V [min-version]:[max-version]] [-a sni_name]\n"
michael@0 162 " [ T <good|revoked|unknown|badsig|corrupted|none|ocsp>] [-A ca]\n"
michael@0 163 #ifndef NSS_DISABLE_ECC
michael@0 164 " [-C SSLCacheEntries] [-e ec_nickname]\n"
michael@0 165 #else
michael@0 166 " [-C SSLCacheEntries]\n"
michael@0 167 #endif /* NSS_DISABLE_ECC */
michael@0 168 ,progName);
michael@0 169 }
michael@0 170
michael@0 171 static void
michael@0 172 PrintParameterUsage()
michael@0 173 {
michael@0 174 fputs(
michael@0 175 "-V [min]:[max] restricts the set of enabled SSL/TLS protocol versions.\n"
michael@0 176 " All versions are enabled by default.\n"
michael@0 177 " Possible values for min/max: ssl2 ssl3 tls1.0 tls1.1 tls1.2\n"
michael@0 178 " Example: \"-V ssl3:\" enables SSL 3 and newer.\n"
michael@0 179 "-B bypasses the PKCS11 layer for SSL encryption and MACing\n"
michael@0 180 "-q checks for bypassability\n"
michael@0 181 "-D means disable Nagle delays in TCP\n"
michael@0 182 "-E means disable export ciphersuites and SSL step down key gen\n"
michael@0 183 "-R means disable detection of rollback from TLS to SSL3\n"
michael@0 184 "-a configure server for SNI.\n"
michael@0 185 "-k expected name negotiated on server sockets\n"
michael@0 186 "-b means try binding to the port and exit\n"
michael@0 187 "-m means test the model-socket feature of SSL_ImportFD.\n"
michael@0 188 "-r flag is interepreted as follows:\n"
michael@0 189 " 1 -r means request, not require, cert on initial handshake.\n"
michael@0 190 " 2 -r's mean request and require, cert on initial handshake.\n"
michael@0 191 " 3 -r's mean request, not require, cert on second handshake.\n"
michael@0 192 " 4 -r's mean request and require, cert on second handshake.\n"
michael@0 193 "-s means disable SSL socket locking for performance\n"
michael@0 194 "-u means enable Session Ticket extension for TLS.\n"
michael@0 195 "-v means verbose output\n"
michael@0 196 "-x means use export policy.\n"
michael@0 197 "-z means enable compression.\n"
michael@0 198 "-L seconds means log statistics every 'seconds' seconds (default=30).\n"
michael@0 199 "-M maxProcs tells how many processes to run in a multi-process server\n"
michael@0 200 "-N means do NOT use the server session cache. Incompatible with -M.\n"
michael@0 201 "-t threads -- specify the number of threads to use for connections.\n"
michael@0 202 "-i pid_file file to write the process id of selfserve\n"
michael@0 203 "-l means use local threads instead of global threads\n"
michael@0 204 "-g numblocks means test throughput by sending total numblocks chunks\n"
michael@0 205 " of size 16kb to the client, 0 means unlimited (default=0)\n"
michael@0 206 "-j means measure TCP throughput (for use with -g option)\n"
michael@0 207 "-C SSLCacheEntries sets the maximum number of entries in the SSL\n"
michael@0 208 " session cache\n"
michael@0 209 "-T <mode> enable OCSP stapling. Possible modes:\n"
michael@0 210 " none: don't send cert status (default)\n"
michael@0 211 " good, revoked, unknown: Include locally signed response. Requires: -A\n"
michael@0 212 " failure: return a failure response (try later, unsigned)\n"
michael@0 213 " badsig: use a good status but with an invalid signature\n"
michael@0 214 " corrupted: stapled cert status is an invalid block of data\n"
michael@0 215 " random: each connection uses a random status from this list:\n"
michael@0 216 " good, revoked, unknown, failure, badsig, corrupted\n"
michael@0 217 " ocsp: fetch from external OCSP server using AIA, or none\n"
michael@0 218 "-A <ca> Nickname of a CA used to sign a stapled cert status\n"
michael@0 219 "-c Restrict ciphers\n"
michael@0 220 "-Y prints cipher values allowed for parameter -c and exits\n"
michael@0 221 , stderr);
michael@0 222 }
michael@0 223
michael@0 224 static void
michael@0 225 Usage(const char *progName)
michael@0 226 {
michael@0 227 PrintUsageHeader(progName);
michael@0 228 PrintParameterUsage();
michael@0 229 }
michael@0 230
michael@0 231 static void
michael@0 232 PrintCipherUsage(const char *progName)
michael@0 233 {
michael@0 234 PrintUsageHeader(progName);
michael@0 235 fputs(
michael@0 236 "-c ciphers Letter(s) chosen from the following list\n"
michael@0 237 "A SSL2 RC4 128 WITH MD5\n"
michael@0 238 "B SSL2 RC4 128 EXPORT40 WITH MD5\n"
michael@0 239 "C SSL2 RC2 128 CBC WITH MD5\n"
michael@0 240 "D SSL2 RC2 128 CBC EXPORT40 WITH MD5\n"
michael@0 241 "E SSL2 DES 64 CBC WITH MD5\n"
michael@0 242 "F SSL2 DES 192 EDE3 CBC WITH MD5\n"
michael@0 243 "\n"
michael@0 244 "c SSL3 RSA WITH RC4 128 MD5\n"
michael@0 245 "d SSL3 RSA WITH 3DES EDE CBC SHA\n"
michael@0 246 "e SSL3 RSA WITH DES CBC SHA\n"
michael@0 247 "f SSL3 RSA EXPORT WITH RC4 40 MD5\n"
michael@0 248 "g SSL3 RSA EXPORT WITH RC2 CBC 40 MD5\n"
michael@0 249 "i SSL3 RSA WITH NULL MD5\n"
michael@0 250 "j SSL3 RSA FIPS WITH 3DES EDE CBC SHA\n"
michael@0 251 "k SSL3 RSA FIPS WITH DES CBC SHA\n"
michael@0 252 "l SSL3 RSA EXPORT WITH DES CBC SHA\t(new)\n"
michael@0 253 "m SSL3 RSA EXPORT WITH RC4 56 SHA\t(new)\n"
michael@0 254 "n SSL3 RSA WITH RC4 128 SHA\n"
michael@0 255 "v SSL3 RSA WITH AES 128 CBC SHA\n"
michael@0 256 "y SSL3 RSA WITH AES 256 CBC SHA\n"
michael@0 257 "z SSL3 RSA WITH NULL SHA\n"
michael@0 258 "\n"
michael@0 259 ":WXYZ Use cipher with hex code { 0xWX , 0xYZ } in TLS\n"
michael@0 260 , stderr);
michael@0 261 }
michael@0 262
michael@0 263 static const char *
michael@0 264 errWarn(char * funcString)
michael@0 265 {
michael@0 266 PRErrorCode perr = PR_GetError();
michael@0 267 const char * errString = SECU_Strerror(perr);
michael@0 268
michael@0 269 fprintf(stderr, "selfserv: %s returned error %d:\n%s\n",
michael@0 270 funcString, perr, errString);
michael@0 271 return errString;
michael@0 272 }
michael@0 273
michael@0 274 static void
michael@0 275 errExit(char * funcString)
michael@0 276 {
michael@0 277 errWarn(funcString);
michael@0 278 exit(3);
michael@0 279 }
michael@0 280
michael@0 281
michael@0 282 /**************************************************************************
michael@0 283 **
michael@0 284 ** Routines for disabling SSL ciphers.
michael@0 285 **
michael@0 286 **************************************************************************/
michael@0 287
michael@0 288 /* disable all the SSL cipher suites */
michael@0 289 void
michael@0 290 disableAllSSLCiphers(void)
michael@0 291 {
michael@0 292 const PRUint16 *cipherSuites = SSL_ImplementedCiphers;
michael@0 293 int i = SSL_NumImplementedCiphers;
michael@0 294 SECStatus rv;
michael@0 295
michael@0 296 while (--i >= 0) {
michael@0 297 PRUint16 suite = cipherSuites[i];
michael@0 298 rv = SSL_CipherPrefSetDefault(suite, PR_FALSE);
michael@0 299 if (rv != SECSuccess) {
michael@0 300 printf("SSL_CipherPrefSetDefault rejected suite 0x%04x (i = %d)\n",
michael@0 301 suite, i);
michael@0 302 errWarn("SSL_CipherPrefSetDefault");
michael@0 303 }
michael@0 304 }
michael@0 305 }
michael@0 306
michael@0 307 /* disable all the export SSL cipher suites */
michael@0 308 SECStatus
michael@0 309 disableExportSSLCiphers(void)
michael@0 310 {
michael@0 311 const PRUint16 *cipherSuites = SSL_ImplementedCiphers;
michael@0 312 int i = SSL_NumImplementedCiphers;
michael@0 313 SECStatus rv = SECSuccess;
michael@0 314 SSLCipherSuiteInfo info;
michael@0 315
michael@0 316 while (--i >= 0) {
michael@0 317 PRUint16 suite = cipherSuites[i];
michael@0 318 SECStatus status;
michael@0 319 status = SSL_GetCipherSuiteInfo(suite, &info, sizeof info);
michael@0 320 if (status != SECSuccess) {
michael@0 321 printf("SSL_GetCipherSuiteInfo rejected suite 0x%04x (i = %d)\n",
michael@0 322 suite, i);
michael@0 323 errWarn("SSL_GetCipherSuiteInfo");
michael@0 324 rv = SECFailure;
michael@0 325 continue;
michael@0 326 }
michael@0 327 if (info.cipherSuite != suite) {
michael@0 328 printf(
michael@0 329 "SSL_GetCipherSuiteInfo returned wrong suite! Wanted 0x%04x, Got 0x%04x\n",
michael@0 330 suite, i);
michael@0 331 PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
michael@0 332 rv = SECFailure;
michael@0 333 continue;
michael@0 334 }
michael@0 335 /* should check here that info.length >= offsetof isExportable */
michael@0 336 if (info.isExportable) {
michael@0 337 status = SSL_CipherPolicySet(suite, SSL_NOT_ALLOWED);
michael@0 338 if (status != SECSuccess) {
michael@0 339 printf("SSL_CipherPolicySet rejected suite 0x%04x (i = %d)\n",
michael@0 340 suite, i);
michael@0 341 errWarn("SSL_CipherPolicySet");
michael@0 342 rv = SECFailure;
michael@0 343 }
michael@0 344 }
michael@0 345 }
michael@0 346 return rv;
michael@0 347 }
michael@0 348
michael@0 349 static SECStatus
michael@0 350 mySSLAuthCertificate(void *arg, PRFileDesc *fd, PRBool checkSig,
michael@0 351 PRBool isServer)
michael@0 352 {
michael@0 353 SECStatus rv;
michael@0 354 CERTCertificate * peerCert;
michael@0 355
michael@0 356 peerCert = SSL_PeerCertificate(fd);
michael@0 357
michael@0 358 if (peerCert) {
michael@0 359 PRINTF("selfserv: Subject: %s\nselfserv: Issuer : %s\n",
michael@0 360 peerCert->subjectName, peerCert->issuerName);
michael@0 361 CERT_DestroyCertificate(peerCert);
michael@0 362 }
michael@0 363
michael@0 364 rv = SSL_AuthCertificate(arg, fd, checkSig, isServer);
michael@0 365
michael@0 366 if (rv == SECSuccess) {
michael@0 367 PRINTF("selfserv: -- SSL3: Certificate Validated.\n");
michael@0 368 } else {
michael@0 369 int err = PR_GetError();
michael@0 370 FPRINTF(stderr, "selfserv: -- SSL3: Certificate Invalid, err %d.\n%s\n",
michael@0 371 err, SECU_Strerror(err));
michael@0 372 }
michael@0 373 FLUSH;
michael@0 374 return rv;
michael@0 375 }
michael@0 376
michael@0 377 void
michael@0 378 printSSLStatistics()
michael@0 379 {
michael@0 380 SSL3Statistics * ssl3stats = SSL_GetStatistics();
michael@0 381
michael@0 382 printf(
michael@0 383 "selfserv: %ld cache hits; %ld cache misses, %ld cache not reusable\n"
michael@0 384 " %ld stateless resumes, %ld ticket parse failures\n",
michael@0 385 ssl3stats->hch_sid_cache_hits, ssl3stats->hch_sid_cache_misses,
michael@0 386 ssl3stats->hch_sid_cache_not_ok, ssl3stats->hch_sid_stateless_resumes,
michael@0 387 ssl3stats->hch_sid_ticket_parse_failures);
michael@0 388 }
michael@0 389
michael@0 390 void
michael@0 391 printSecurityInfo(PRFileDesc *fd)
michael@0 392 {
michael@0 393 CERTCertificate * cert = NULL;
michael@0 394 SECStatus result;
michael@0 395 SSLChannelInfo channel;
michael@0 396 SSLCipherSuiteInfo suite;
michael@0 397
michael@0 398 if (verbose)
michael@0 399 printSSLStatistics();
michael@0 400
michael@0 401 result = SSL_GetChannelInfo(fd, &channel, sizeof channel);
michael@0 402 if (result == SECSuccess &&
michael@0 403 channel.length == sizeof channel &&
michael@0 404 channel.cipherSuite) {
michael@0 405 result = SSL_GetCipherSuiteInfo(channel.cipherSuite,
michael@0 406 &suite, sizeof suite);
michael@0 407 if (result == SECSuccess) {
michael@0 408 FPRINTF(stderr,
michael@0 409 "selfserv: SSL version %d.%d using %d-bit %s with %d-bit %s MAC\n",
michael@0 410 channel.protocolVersion >> 8, channel.protocolVersion & 0xff,
michael@0 411 suite.effectiveKeyBits, suite.symCipherName,
michael@0 412 suite.macBits, suite.macAlgorithmName);
michael@0 413 FPRINTF(stderr,
michael@0 414 "selfserv: Server Auth: %d-bit %s, Key Exchange: %d-bit %s\n"
michael@0 415 " Compression: %s\n",
michael@0 416 channel.authKeyBits, suite.authAlgorithmName,
michael@0 417 channel.keaKeyBits, suite.keaTypeName,
michael@0 418 channel.compressionMethodName);
michael@0 419 }
michael@0 420 }
michael@0 421 if (verbose) {
michael@0 422 SECItem *hostInfo = SSL_GetNegotiatedHostInfo(fd);
michael@0 423 if (hostInfo) {
michael@0 424 char namePref[] = "selfserv: Negotiated server name: ";
michael@0 425
michael@0 426 fprintf(stderr, "%s", namePref);
michael@0 427 fwrite(hostInfo->data, hostInfo->len, 1, stderr);
michael@0 428 SECITEM_FreeItem(hostInfo, PR_TRUE);
michael@0 429 hostInfo = NULL;
michael@0 430 fprintf(stderr, "\n");
michael@0 431 }
michael@0 432 }
michael@0 433 if (requestCert)
michael@0 434 cert = SSL_PeerCertificate(fd);
michael@0 435 else
michael@0 436 cert = SSL_LocalCertificate(fd);
michael@0 437 if (cert) {
michael@0 438 char * ip = CERT_NameToAscii(&cert->issuer);
michael@0 439 char * sp = CERT_NameToAscii(&cert->subject);
michael@0 440 if (sp) {
michael@0 441 FPRINTF(stderr, "selfserv: subject DN: %s\n", sp);
michael@0 442 PORT_Free(sp);
michael@0 443 }
michael@0 444 if (ip) {
michael@0 445 FPRINTF(stderr, "selfserv: issuer DN: %s\n", ip);
michael@0 446 PORT_Free(ip);
michael@0 447 }
michael@0 448 CERT_DestroyCertificate(cert);
michael@0 449 cert = NULL;
michael@0 450 }
michael@0 451 FLUSH;
michael@0 452 }
michael@0 453
michael@0 454 static int MakeCertOK;
michael@0 455
michael@0 456 static SECStatus
michael@0 457 myBadCertHandler( void *arg, PRFileDesc *fd)
michael@0 458 {
michael@0 459 int err = PR_GetError();
michael@0 460 if (!MakeCertOK)
michael@0 461 fprintf(stderr,
michael@0 462 "selfserv: -- SSL: Client Certificate Invalid, err %d.\n%s\n",
michael@0 463 err, SECU_Strerror(err));
michael@0 464 return (MakeCertOK ? SECSuccess : SECFailure);
michael@0 465 }
michael@0 466
michael@0 467 #define MAX_VIRT_SERVER_NAME_ARRAY_INDEX 10
michael@0 468
michael@0 469 /* Simple SNI socket config function that does not use SSL_ReconfigFD.
michael@0 470 * Only uses one server name but verifies that the names match. */
michael@0 471 PRInt32
michael@0 472 mySSLSNISocketConfig(PRFileDesc *fd, const SECItem *sniNameArr,
michael@0 473 PRUint32 sniNameArrSize, void *arg)
michael@0 474 {
michael@0 475 PRInt32 i = 0;
michael@0 476 const SECItem *current = sniNameArr;
michael@0 477 const char **nameArr = (const char**)arg;
michael@0 478 secuPWData *pwdata;
michael@0 479 CERTCertificate * cert = NULL;
michael@0 480 SECKEYPrivateKey * privKey = NULL;
michael@0 481
michael@0 482 PORT_Assert(fd && sniNameArr);
michael@0 483 if (!fd || !sniNameArr) {
michael@0 484 return SSL_SNI_SEND_ALERT;
michael@0 485 }
michael@0 486
michael@0 487 pwdata = SSL_RevealPinArg(fd);
michael@0 488
michael@0 489 for (;current && i < sniNameArrSize;i++) {
michael@0 490 int j = 0;
michael@0 491 for (;j < MAX_VIRT_SERVER_NAME_ARRAY_INDEX && nameArr[j];j++) {
michael@0 492 if (!PORT_Strncmp(nameArr[j],
michael@0 493 (const char *)current[i].data,
michael@0 494 current[i].len) &&
michael@0 495 PORT_Strlen(nameArr[j]) == current[i].len) {
michael@0 496 const char *nickName = nameArr[j];
michael@0 497 if (j == 0) {
michael@0 498 /* default cert */
michael@0 499 return 0;
michael@0 500 }
michael@0 501 /* if pwdata is NULL, then we would not get the key and
michael@0 502 * return an error status. */
michael@0 503 cert = PK11_FindCertFromNickname(nickName, &pwdata);
michael@0 504 if (cert == NULL) {
michael@0 505 goto loser; /* Send alert */
michael@0 506 }
michael@0 507 privKey = PK11_FindKeyByAnyCert(cert, &pwdata);
michael@0 508 if (privKey == NULL) {
michael@0 509 goto loser; /* Send alert */
michael@0 510 }
michael@0 511 if (SSL_ConfigSecureServer(fd, cert, privKey,
michael@0 512 kt_rsa) != SECSuccess) {
michael@0 513 goto loser; /* Send alert */
michael@0 514 }
michael@0 515 SECKEY_DestroyPrivateKey(privKey);
michael@0 516 CERT_DestroyCertificate(cert);
michael@0 517 return i;
michael@0 518 }
michael@0 519 }
michael@0 520 }
michael@0 521 loser:
michael@0 522 if (privKey) {
michael@0 523 SECKEY_DestroyPrivateKey(privKey);
michael@0 524 }
michael@0 525 if (cert) {
michael@0 526 CERT_DestroyCertificate(cert);
michael@0 527 }
michael@0 528 return SSL_SNI_SEND_ALERT;
michael@0 529 }
michael@0 530
michael@0 531
michael@0 532 /**************************************************************************
michael@0 533 ** Begin thread management routines and data.
michael@0 534 **************************************************************************/
michael@0 535 #define MIN_THREADS 3
michael@0 536 #define DEFAULT_THREADS 8
michael@0 537 #define MAX_THREADS 4096
michael@0 538 #define MAX_PROCS 25
michael@0 539 static int maxThreads = DEFAULT_THREADS;
michael@0 540
michael@0 541
michael@0 542 typedef struct jobStr {
michael@0 543 PRCList link;
michael@0 544 PRFileDesc *tcp_sock;
michael@0 545 PRFileDesc *model_sock;
michael@0 546 int requestCert;
michael@0 547 } JOB;
michael@0 548
michael@0 549 static PZLock * qLock; /* this lock protects all data immediately below */
michael@0 550 static PRLock * lastLoadedCrlLock; /* this lock protects lastLoadedCrl variable */
michael@0 551 static PZCondVar * jobQNotEmptyCv;
michael@0 552 static PZCondVar * freeListNotEmptyCv;
michael@0 553 static PZCondVar * threadCountChangeCv;
michael@0 554 static int threadCount;
michael@0 555 static PRCList jobQ;
michael@0 556 static PRCList freeJobs;
michael@0 557 static JOB *jobTable;
michael@0 558
michael@0 559 SECStatus
michael@0 560 setupJobs(int maxJobs)
michael@0 561 {
michael@0 562 int i;
michael@0 563
michael@0 564 jobTable = (JOB *)PR_Calloc(maxJobs, sizeof(JOB));
michael@0 565 if (!jobTable)
michael@0 566 return SECFailure;
michael@0 567
michael@0 568 PR_INIT_CLIST(&jobQ);
michael@0 569 PR_INIT_CLIST(&freeJobs);
michael@0 570
michael@0 571 for (i = 0; i < maxJobs; ++i) {
michael@0 572 JOB * pJob = jobTable + i;
michael@0 573 PR_APPEND_LINK(&pJob->link, &freeJobs);
michael@0 574 }
michael@0 575 return SECSuccess;
michael@0 576 }
michael@0 577
michael@0 578 typedef int startFn(PRFileDesc *a, PRFileDesc *b, int c);
michael@0 579
michael@0 580 typedef enum { rs_idle = 0, rs_running = 1, rs_zombie = 2 } runState;
michael@0 581
michael@0 582 typedef struct perThreadStr {
michael@0 583 PRFileDesc *a;
michael@0 584 PRFileDesc *b;
michael@0 585 int c;
michael@0 586 int rv;
michael@0 587 startFn * startFunc;
michael@0 588 PRThread * prThread;
michael@0 589 runState state;
michael@0 590 } perThread;
michael@0 591
michael@0 592 static perThread *threads;
michael@0 593
michael@0 594 void
michael@0 595 thread_wrapper(void * arg)
michael@0 596 {
michael@0 597 perThread * slot = (perThread *)arg;
michael@0 598
michael@0 599 slot->rv = (* slot->startFunc)(slot->a, slot->b, slot->c);
michael@0 600
michael@0 601 /* notify the thread exit handler. */
michael@0 602 PZ_Lock(qLock);
michael@0 603 slot->state = rs_zombie;
michael@0 604 --threadCount;
michael@0 605 PZ_NotifyAllCondVar(threadCountChangeCv);
michael@0 606 PZ_Unlock(qLock);
michael@0 607 }
michael@0 608
michael@0 609 int
michael@0 610 jobLoop(PRFileDesc *a, PRFileDesc *b, int c)
michael@0 611 {
michael@0 612 PRCList * myLink = 0;
michael@0 613 JOB * myJob;
michael@0 614
michael@0 615 PZ_Lock(qLock);
michael@0 616 do {
michael@0 617 myLink = 0;
michael@0 618 while (PR_CLIST_IS_EMPTY(&jobQ) && !stopping) {
michael@0 619 PZ_WaitCondVar(jobQNotEmptyCv, PR_INTERVAL_NO_TIMEOUT);
michael@0 620 }
michael@0 621 if (!PR_CLIST_IS_EMPTY(&jobQ)) {
michael@0 622 myLink = PR_LIST_HEAD(&jobQ);
michael@0 623 PR_REMOVE_AND_INIT_LINK(myLink);
michael@0 624 }
michael@0 625 PZ_Unlock(qLock);
michael@0 626 myJob = (JOB *)myLink;
michael@0 627 /* myJob will be null when stopping is true and jobQ is empty */
michael@0 628 if (!myJob)
michael@0 629 break;
michael@0 630 handle_connection( myJob->tcp_sock, myJob->model_sock,
michael@0 631 myJob->requestCert);
michael@0 632 PZ_Lock(qLock);
michael@0 633 PR_APPEND_LINK(myLink, &freeJobs);
michael@0 634 PZ_NotifyCondVar(freeListNotEmptyCv);
michael@0 635 } while (PR_TRUE);
michael@0 636 return 0;
michael@0 637 }
michael@0 638
michael@0 639
michael@0 640 SECStatus
michael@0 641 launch_threads(
michael@0 642 startFn *startFunc,
michael@0 643 PRFileDesc *a,
michael@0 644 PRFileDesc *b,
michael@0 645 int c,
michael@0 646 PRBool local)
michael@0 647 {
michael@0 648 int i;
michael@0 649 SECStatus rv = SECSuccess;
michael@0 650
michael@0 651 /* create the thread management serialization structs */
michael@0 652 qLock = PZ_NewLock(nssILockSelfServ);
michael@0 653 jobQNotEmptyCv = PZ_NewCondVar(qLock);
michael@0 654 freeListNotEmptyCv = PZ_NewCondVar(qLock);
michael@0 655 threadCountChangeCv = PZ_NewCondVar(qLock);
michael@0 656
michael@0 657 /* create monitor for crl reload procedure */
michael@0 658 lastLoadedCrlLock = PR_NewLock();
michael@0 659
michael@0 660 /* allocate the array of thread slots */
michael@0 661 threads = PR_Calloc(maxThreads, sizeof(perThread));
michael@0 662 if ( NULL == threads ) {
michael@0 663 fprintf(stderr, "Oh Drat! Can't allocate the perThread array\n");
michael@0 664 return SECFailure;
michael@0 665 }
michael@0 666 /* 5 is a little extra, intended to keep the jobQ from underflowing.
michael@0 667 ** That is, from going empty while not stopping and clients are still
michael@0 668 ** trying to contact us.
michael@0 669 */
michael@0 670 rv = setupJobs(maxThreads + 5);
michael@0 671 if (rv != SECSuccess)
michael@0 672 return rv;
michael@0 673
michael@0 674 PZ_Lock(qLock);
michael@0 675 for (i = 0; i < maxThreads; ++i) {
michael@0 676 perThread * slot = threads + i;
michael@0 677
michael@0 678 slot->state = rs_running;
michael@0 679 slot->a = a;
michael@0 680 slot->b = b;
michael@0 681 slot->c = c;
michael@0 682 slot->startFunc = startFunc;
michael@0 683 slot->prThread = PR_CreateThread(PR_USER_THREAD,
michael@0 684 thread_wrapper, slot, PR_PRIORITY_NORMAL,
michael@0 685 (PR_TRUE==local)?PR_LOCAL_THREAD:PR_GLOBAL_THREAD,
michael@0 686 PR_UNJOINABLE_THREAD, 0);
michael@0 687 if (slot->prThread == NULL) {
michael@0 688 printf("selfserv: Failed to launch thread!\n");
michael@0 689 slot->state = rs_idle;
michael@0 690 rv = SECFailure;
michael@0 691 break;
michael@0 692 }
michael@0 693
michael@0 694 ++threadCount;
michael@0 695 }
michael@0 696 PZ_Unlock(qLock);
michael@0 697
michael@0 698 return rv;
michael@0 699 }
michael@0 700
michael@0 701 #define DESTROY_CONDVAR(name) if (name) { \
michael@0 702 PZ_DestroyCondVar(name); name = NULL; }
michael@0 703 #define DESTROY_LOCK(name) if (name) { \
michael@0 704 PZ_DestroyLock(name); name = NULL; }
michael@0 705
michael@0 706
michael@0 707 void
michael@0 708 terminateWorkerThreads(void)
michael@0 709 {
michael@0 710 VLOG(("selfserv: server_thead: waiting on stopping"));
michael@0 711 PZ_Lock(qLock);
michael@0 712 PZ_NotifyAllCondVar(jobQNotEmptyCv);
michael@0 713 while (threadCount > 0) {
michael@0 714 PZ_WaitCondVar(threadCountChangeCv, PR_INTERVAL_NO_TIMEOUT);
michael@0 715 }
michael@0 716 /* The worker threads empty the jobQ before they terminate. */
michael@0 717 PORT_Assert(PR_CLIST_IS_EMPTY(&jobQ));
michael@0 718 PZ_Unlock(qLock);
michael@0 719
michael@0 720 DESTROY_CONDVAR(jobQNotEmptyCv);
michael@0 721 DESTROY_CONDVAR(freeListNotEmptyCv);
michael@0 722 DESTROY_CONDVAR(threadCountChangeCv);
michael@0 723
michael@0 724 PR_DestroyLock(lastLoadedCrlLock);
michael@0 725 DESTROY_LOCK(qLock);
michael@0 726 PR_Free(jobTable);
michael@0 727 PR_Free(threads);
michael@0 728 }
michael@0 729
michael@0 730 static void
michael@0 731 logger(void *arg)
michael@0 732 {
michael@0 733 PRFloat64 seconds;
michael@0 734 PRFloat64 opsPerSec;
michael@0 735 PRIntervalTime period;
michael@0 736 PRIntervalTime previousTime;
michael@0 737 PRIntervalTime latestTime;
michael@0 738 PRUint32 previousOps;
michael@0 739 PRUint32 ops;
michael@0 740 PRIntervalTime logPeriodTicks = PR_TicksPerSecond();
michael@0 741 PRFloat64 secondsPerTick = 1.0 / (PRFloat64)logPeriodTicks;
michael@0 742 int iterations = 0;
michael@0 743 int secondsElapsed = 0;
michael@0 744 static PRInt64 totalPeriodBytes = 0;
michael@0 745 static PRInt64 totalPeriodBytesTCP = 0;
michael@0 746
michael@0 747 previousOps = loggerOps;
michael@0 748 previousTime = PR_IntervalNow();
michael@0 749
michael@0 750 for (;;) {
michael@0 751 /* OK, implementing a new sleep algorithm here... always sleep
michael@0 752 * for 1 second but print out info at the user-specified interval.
michael@0 753 * This way, we don't overflow all of our PR_Atomic* functions and
michael@0 754 * we don't have to use locks.
michael@0 755 */
michael@0 756 PR_Sleep(logPeriodTicks);
michael@0 757 secondsElapsed++;
michael@0 758 totalPeriodBytes += PR_ATOMIC_SET(&loggerBytes, 0);
michael@0 759 totalPeriodBytesTCP += PR_ATOMIC_SET(&loggerBytesTCP, 0);
michael@0 760 if (secondsElapsed != logPeriod) {
michael@0 761 continue;
michael@0 762 }
michael@0 763 /* when we reach the user-specified logging interval, print out all
michael@0 764 * data
michael@0 765 */
michael@0 766 secondsElapsed = 0;
michael@0 767 latestTime = PR_IntervalNow();
michael@0 768 ops = loggerOps;
michael@0 769 period = latestTime - previousTime;
michael@0 770 seconds = (PRFloat64) period*secondsPerTick;
michael@0 771 opsPerSec = (ops - previousOps) / seconds;
michael@0 772
michael@0 773 if (testBulk) {
michael@0 774 if (iterations == 0) {
michael@0 775 if (loggingLayer == PR_TRUE) {
michael@0 776 printf("Conn.--------App Data--------TCP Data\n");
michael@0 777 } else {
michael@0 778 printf("Conn.--------App Data\n");
michael@0 779 }
michael@0 780 }
michael@0 781 if (loggingLayer == PR_TRUE) {
michael@0 782 printf("%4.d %5.3f MB/s %5.3f MB/s\n", ops,
michael@0 783 totalPeriodBytes / (seconds * 1048576.0),
michael@0 784 totalPeriodBytesTCP / (seconds * 1048576.0));
michael@0 785 } else {
michael@0 786 printf("%4.d %5.3f MB/s\n", ops,
michael@0 787 totalPeriodBytes / (seconds * 1048576.0));
michael@0 788 }
michael@0 789 totalPeriodBytes = 0;
michael@0 790 totalPeriodBytesTCP = 0;
michael@0 791 /* Print the "legend" every 20 iterations */
michael@0 792 iterations = (iterations + 1) % 20;
michael@0 793 } else {
michael@0 794 printf("%.2f ops/second, %d threads\n", opsPerSec, threadCount);
michael@0 795 }
michael@0 796
michael@0 797 fflush(stdout);
michael@0 798 previousOps = ops;
michael@0 799 previousTime = latestTime;
michael@0 800 if (stopping) {
michael@0 801 break;
michael@0 802 }
michael@0 803 }
michael@0 804 }
michael@0 805
michael@0 806
michael@0 807 /**************************************************************************
michael@0 808 ** End thread management routines.
michael@0 809 **************************************************************************/
michael@0 810
michael@0 811 PRBool useModelSocket = PR_FALSE;
michael@0 812 static SSLVersionRange enabledVersions;
michael@0 813 PRBool enableSSL2 = PR_TRUE;
michael@0 814 PRBool disableRollBack = PR_FALSE;
michael@0 815 PRBool NoReuse = PR_FALSE;
michael@0 816 PRBool hasSidCache = PR_FALSE;
michael@0 817 PRBool disableStepDown = PR_FALSE;
michael@0 818 PRBool bypassPKCS11 = PR_FALSE;
michael@0 819 PRBool disableLocking = PR_FALSE;
michael@0 820 PRBool testbypass = PR_FALSE;
michael@0 821 PRBool enableSessionTickets = PR_FALSE;
michael@0 822 PRBool enableCompression = PR_FALSE;
michael@0 823 PRBool failedToNegotiateName = PR_FALSE;
michael@0 824 static char *virtServerNameArray[MAX_VIRT_SERVER_NAME_ARRAY_INDEX];
michael@0 825 static int virtServerNameIndex = 1;
michael@0 826
michael@0 827
michael@0 828 static const char stopCmd[] = { "GET /stop " };
michael@0 829 static const char getCmd[] = { "GET " };
michael@0 830 static const char EOFmsg[] = { "EOF\r\n\r\n\r\n" };
michael@0 831 static const char outHeader[] = {
michael@0 832 "HTTP/1.0 200 OK\r\n"
michael@0 833 "Server: Generic Web Server\r\n"
michael@0 834 "Date: Tue, 26 Aug 1997 22:10:05 GMT\r\n"
michael@0 835 "Content-type: text/plain\r\n"
michael@0 836 "\r\n"
michael@0 837 };
michael@0 838 static const char crlCacheErr[] = { "CRL ReCache Error: " };
michael@0 839
michael@0 840 PRUint16 cipherlist[100];
michael@0 841 int nciphers;
michael@0 842
michael@0 843 void
michael@0 844 savecipher(int c)
michael@0 845 {
michael@0 846 if (nciphers < sizeof cipherlist / sizeof (cipherlist[0]))
michael@0 847 cipherlist[nciphers++] = (PRUint16)c;
michael@0 848 }
michael@0 849
michael@0 850
michael@0 851 #ifdef FULL_DUPLEX_CAPABLE
michael@0 852
michael@0 853 struct lockedVarsStr {
michael@0 854 PZLock * lock;
michael@0 855 int count;
michael@0 856 int waiters;
michael@0 857 PZCondVar * condVar;
michael@0 858 };
michael@0 859
michael@0 860 typedef struct lockedVarsStr lockedVars;
michael@0 861
michael@0 862 void
michael@0 863 lockedVars_Init( lockedVars * lv)
michael@0 864 {
michael@0 865 lv->count = 0;
michael@0 866 lv->waiters = 0;
michael@0 867 lv->lock = PZ_NewLock(nssILockSelfServ);
michael@0 868 lv->condVar = PZ_NewCondVar(lv->lock);
michael@0 869 }
michael@0 870
michael@0 871 void
michael@0 872 lockedVars_Destroy( lockedVars * lv)
michael@0 873 {
michael@0 874 PZ_DestroyCondVar(lv->condVar);
michael@0 875 lv->condVar = NULL;
michael@0 876
michael@0 877 PZ_DestroyLock(lv->lock);
michael@0 878 lv->lock = NULL;
michael@0 879 }
michael@0 880
michael@0 881 void
michael@0 882 lockedVars_WaitForDone(lockedVars * lv)
michael@0 883 {
michael@0 884 PZ_Lock(lv->lock);
michael@0 885 while (lv->count > 0) {
michael@0 886 PZ_WaitCondVar(lv->condVar, PR_INTERVAL_NO_TIMEOUT);
michael@0 887 }
michael@0 888 PZ_Unlock(lv->lock);
michael@0 889 }
michael@0 890
michael@0 891 int /* returns count */
michael@0 892 lockedVars_AddToCount(lockedVars * lv, int addend)
michael@0 893 {
michael@0 894 int rv;
michael@0 895
michael@0 896 PZ_Lock(lv->lock);
michael@0 897 rv = lv->count += addend;
michael@0 898 if (rv <= 0) {
michael@0 899 PZ_NotifyCondVar(lv->condVar);
michael@0 900 }
michael@0 901 PZ_Unlock(lv->lock);
michael@0 902 return rv;
michael@0 903 }
michael@0 904
michael@0 905 int
michael@0 906 do_writes(
michael@0 907 PRFileDesc * ssl_sock,
michael@0 908 PRFileDesc * model_sock,
michael@0 909 int requestCert
michael@0 910 )
michael@0 911 {
michael@0 912 int sent = 0;
michael@0 913 int count = 0;
michael@0 914 lockedVars * lv = (lockedVars *)model_sock;
michael@0 915
michael@0 916 VLOG(("selfserv: do_writes: starting"));
michael@0 917 while (sent < bigBuf.len) {
michael@0 918
michael@0 919 count = PR_Write(ssl_sock, bigBuf.data + sent, bigBuf.len - sent);
michael@0 920 if (count < 0) {
michael@0 921 errWarn("PR_Write bigBuf");
michael@0 922 break;
michael@0 923 }
michael@0 924 FPRINTF(stderr, "selfserv: PR_Write wrote %d bytes from bigBuf\n", count );
michael@0 925 sent += count;
michael@0 926 }
michael@0 927 if (count >= 0) { /* last write didn't fail. */
michael@0 928 PR_Shutdown(ssl_sock, PR_SHUTDOWN_SEND);
michael@0 929 }
michael@0 930
michael@0 931 /* notify the reader that we're done. */
michael@0 932 lockedVars_AddToCount(lv, -1);
michael@0 933 FLUSH;
michael@0 934 VLOG(("selfserv: do_writes: exiting"));
michael@0 935 return (sent < bigBuf.len) ? SECFailure : SECSuccess;
michael@0 936 }
michael@0 937
michael@0 938 static int
michael@0 939 handle_fdx_connection(
michael@0 940 PRFileDesc * tcp_sock,
michael@0 941 PRFileDesc * model_sock,
michael@0 942 int requestCert
michael@0 943 )
michael@0 944 {
michael@0 945 PRFileDesc * ssl_sock = NULL;
michael@0 946 SECStatus result;
michael@0 947 int firstTime = 1;
michael@0 948 lockedVars lv;
michael@0 949 PRSocketOptionData opt;
michael@0 950 char buf[10240];
michael@0 951
michael@0 952
michael@0 953 VLOG(("selfserv: handle_fdx_connection: starting"));
michael@0 954 opt.option = PR_SockOpt_Nonblocking;
michael@0 955 opt.value.non_blocking = PR_FALSE;
michael@0 956 PR_SetSocketOption(tcp_sock, &opt);
michael@0 957
michael@0 958 if (useModelSocket && model_sock) {
michael@0 959 SECStatus rv;
michael@0 960 ssl_sock = SSL_ImportFD(model_sock, tcp_sock);
michael@0 961 if (!ssl_sock) {
michael@0 962 errWarn("SSL_ImportFD with model");
michael@0 963 goto cleanup;
michael@0 964 }
michael@0 965 rv = SSL_ResetHandshake(ssl_sock, /* asServer */ 1);
michael@0 966 if (rv != SECSuccess) {
michael@0 967 errWarn("SSL_ResetHandshake");
michael@0 968 goto cleanup;
michael@0 969 }
michael@0 970 } else {
michael@0 971 ssl_sock = tcp_sock;
michael@0 972 }
michael@0 973
michael@0 974 lockedVars_Init(&lv);
michael@0 975 lockedVars_AddToCount(&lv, 1);
michael@0 976
michael@0 977 /* Attempt to launch the writer thread. */
michael@0 978 result = launch_thread(do_writes, ssl_sock, (PRFileDesc *)&lv,
michael@0 979 requestCert);
michael@0 980
michael@0 981 if (result == SECSuccess)
michael@0 982 do {
michael@0 983 /* do reads here. */
michael@0 984 int count;
michael@0 985 count = PR_Read(ssl_sock, buf, sizeof buf);
michael@0 986 if (count < 0) {
michael@0 987 errWarn("FDX PR_Read");
michael@0 988 break;
michael@0 989 }
michael@0 990 FPRINTF(stderr, "selfserv: FDX PR_Read read %d bytes.\n", count );
michael@0 991 if (firstTime) {
michael@0 992 firstTime = 0;
michael@0 993 printSecurityInfo(ssl_sock);
michael@0 994 }
michael@0 995 } while (lockedVars_AddToCount(&lv, 0) > 0);
michael@0 996
michael@0 997 /* Wait for writer to finish */
michael@0 998 lockedVars_WaitForDone(&lv);
michael@0 999 lockedVars_Destroy(&lv);
michael@0 1000 FLUSH;
michael@0 1001
michael@0 1002 cleanup:
michael@0 1003 if (ssl_sock) {
michael@0 1004 PR_Close(ssl_sock);
michael@0 1005 } else if (tcp_sock) {
michael@0 1006 PR_Close(tcp_sock);
michael@0 1007 }
michael@0 1008
michael@0 1009 VLOG(("selfserv: handle_fdx_connection: exiting"));
michael@0 1010 return SECSuccess;
michael@0 1011 }
michael@0 1012
michael@0 1013 #endif
michael@0 1014
michael@0 1015 static SECItem *lastLoadedCrl = NULL;
michael@0 1016
michael@0 1017 static SECStatus
michael@0 1018 reload_crl(PRFileDesc *crlFile)
michael@0 1019 {
michael@0 1020 SECItem *crlDer;
michael@0 1021 CERTCertDBHandle *certHandle = CERT_GetDefaultCertDB();
michael@0 1022 SECStatus rv;
michael@0 1023
michael@0 1024 /* Read in the entire file specified with the -f argument */
michael@0 1025 crlDer = PORT_Malloc(sizeof(SECItem));
michael@0 1026 if (!crlDer) {
michael@0 1027 errWarn("Can not allocate memory.");
michael@0 1028 return SECFailure;
michael@0 1029 }
michael@0 1030
michael@0 1031 rv = SECU_ReadDERFromFile(crlDer, crlFile, PR_FALSE, PR_FALSE);
michael@0 1032 if (rv != SECSuccess) {
michael@0 1033 errWarn("Unable to read input file.");
michael@0 1034 PORT_Free(crlDer);
michael@0 1035 return SECFailure;
michael@0 1036 }
michael@0 1037
michael@0 1038 PR_Lock(lastLoadedCrlLock);
michael@0 1039 rv = CERT_CacheCRL(certHandle, crlDer);
michael@0 1040 if (rv == SECSuccess) {
michael@0 1041 SECItem *tempItem = crlDer;
michael@0 1042 if (lastLoadedCrl != NULL) {
michael@0 1043 rv = CERT_UncacheCRL(certHandle, lastLoadedCrl);
michael@0 1044 if (rv != SECSuccess) {
michael@0 1045 errWarn("Unable to uncache crl.");
michael@0 1046 goto loser;
michael@0 1047 }
michael@0 1048 crlDer = lastLoadedCrl;
michael@0 1049 } else {
michael@0 1050 crlDer = NULL;
michael@0 1051 }
michael@0 1052 lastLoadedCrl = tempItem;
michael@0 1053 }
michael@0 1054
michael@0 1055 loser:
michael@0 1056 PR_Unlock(lastLoadedCrlLock);
michael@0 1057 SECITEM_FreeItem(crlDer, PR_TRUE);
michael@0 1058 return rv;
michael@0 1059 }
michael@0 1060
michael@0 1061 void stop_server()
michael@0 1062 {
michael@0 1063 stopping = 1;
michael@0 1064 PR_Interrupt(acceptorThread);
michael@0 1065 PZ_TraceFlush();
michael@0 1066 }
michael@0 1067
michael@0 1068 SECItemArray *
michael@0 1069 makeTryLaterOCSPResponse(PLArenaPool *arena)
michael@0 1070 {
michael@0 1071 SECItemArray *result = NULL;
michael@0 1072 SECItem *ocspResponse = NULL;
michael@0 1073
michael@0 1074 ocspResponse = CERT_CreateEncodedOCSPErrorResponse(arena,
michael@0 1075 SEC_ERROR_OCSP_TRY_SERVER_LATER);
michael@0 1076 if (!ocspResponse)
michael@0 1077 errExit("cannot created ocspResponse");
michael@0 1078
michael@0 1079 result = SECITEM_AllocArray(arena, NULL, 1);
michael@0 1080 if (!result)
michael@0 1081 errExit("cannot allocate multiOcspResponses");
michael@0 1082
michael@0 1083 result->items[0].data = ocspResponse->data;
michael@0 1084 result->items[0].len = ocspResponse->len;
michael@0 1085
michael@0 1086 return result;
michael@0 1087 }
michael@0 1088
michael@0 1089 SECItemArray *
michael@0 1090 makeCorruptedOCSPResponse(PLArenaPool *arena)
michael@0 1091 {
michael@0 1092 SECItemArray *result = NULL;
michael@0 1093 SECItem *ocspResponse = NULL;
michael@0 1094
michael@0 1095 ocspResponse = SECITEM_AllocItem(arena, NULL, 1);
michael@0 1096 if (!ocspResponse)
michael@0 1097 errExit("cannot created ocspResponse");
michael@0 1098
michael@0 1099 result = SECITEM_AllocArray(arena, NULL, 1);
michael@0 1100 if (!result)
michael@0 1101 errExit("cannot allocate multiOcspResponses");
michael@0 1102
michael@0 1103 result->items[0].data = ocspResponse->data;
michael@0 1104 result->items[0].len = ocspResponse->len;
michael@0 1105
michael@0 1106 return result;
michael@0 1107 }
michael@0 1108
michael@0 1109 SECItemArray *
michael@0 1110 makeSignedOCSPResponse(PLArenaPool *arena, ocspStaplingModeType osm,
michael@0 1111 CERTCertificate *cert, secuPWData *pwdata)
michael@0 1112 {
michael@0 1113 SECItemArray *result = NULL;
michael@0 1114 SECItem *ocspResponse = NULL;
michael@0 1115 CERTOCSPSingleResponse **singleResponses;
michael@0 1116 CERTOCSPSingleResponse *sr;
michael@0 1117 CERTOCSPCertID *cid = NULL;
michael@0 1118 CERTCertificate *ca;
michael@0 1119 PRTime now = PR_Now();
michael@0 1120 PRTime nextUpdate;
michael@0 1121
michael@0 1122 PORT_Assert(cert != NULL);
michael@0 1123
michael@0 1124 ca = CERT_FindCertByNickname(CERT_GetDefaultCertDB(), ocspStaplingCA);
michael@0 1125 if (!ca)
michael@0 1126 errExit("cannot find CA");
michael@0 1127
michael@0 1128 cid = CERT_CreateOCSPCertID(cert, now);
michael@0 1129 if (!cid)
michael@0 1130 errExit("cannot created cid");
michael@0 1131
michael@0 1132 nextUpdate = now + 60*60*24 * PR_USEC_PER_SEC; /* plus 1 day */
michael@0 1133
michael@0 1134 switch (osm) {
michael@0 1135 case osm_good:
michael@0 1136 case osm_badsig:
michael@0 1137 sr = CERT_CreateOCSPSingleResponseGood(arena, cid, now,
michael@0 1138 &nextUpdate);
michael@0 1139 break;
michael@0 1140 case osm_unknown:
michael@0 1141 sr = CERT_CreateOCSPSingleResponseUnknown(arena, cid, now,
michael@0 1142 &nextUpdate);
michael@0 1143 break;
michael@0 1144 case osm_revoked:
michael@0 1145 sr = CERT_CreateOCSPSingleResponseRevoked(arena, cid, now,
michael@0 1146 &nextUpdate,
michael@0 1147 now - 60*60*24 * PR_USEC_PER_SEC, /* minus 1 day */
michael@0 1148 NULL);
michael@0 1149 break;
michael@0 1150 default:
michael@0 1151 PORT_Assert(0);
michael@0 1152 break;
michael@0 1153 }
michael@0 1154
michael@0 1155 if (!sr)
michael@0 1156 errExit("cannot create sr");
michael@0 1157
michael@0 1158 /* meaning of value 2: one entry + one end marker */
michael@0 1159 singleResponses = PORT_ArenaNewArray(arena, CERTOCSPSingleResponse*, 2);
michael@0 1160 if (singleResponses == NULL)
michael@0 1161 errExit("cannot allocate singleResponses");
michael@0 1162
michael@0 1163 singleResponses[0] = sr;
michael@0 1164 singleResponses[1] = NULL;
michael@0 1165
michael@0 1166 ocspResponse = CERT_CreateEncodedOCSPSuccessResponse(arena,
michael@0 1167 (osm == osm_badsig) ? NULL : ca,
michael@0 1168 ocspResponderID_byName, now, singleResponses,
michael@0 1169 &pwdata);
michael@0 1170 if (!ocspResponse)
michael@0 1171 errExit("cannot created ocspResponse");
michael@0 1172
michael@0 1173 CERT_DestroyCertificate(ca);
michael@0 1174 ca = NULL;
michael@0 1175
michael@0 1176 result = SECITEM_AllocArray(arena, NULL, 1);
michael@0 1177 if (!result)
michael@0 1178 errExit("cannot allocate multiOcspResponses");
michael@0 1179
michael@0 1180 result->items[0].data = ocspResponse->data;
michael@0 1181 result->items[0].len = ocspResponse->len;
michael@0 1182
michael@0 1183 CERT_DestroyOCSPCertID(cid);
michael@0 1184 cid = NULL;
michael@0 1185
michael@0 1186 return result;
michael@0 1187 }
michael@0 1188
michael@0 1189 void
michael@0 1190 setupCertStatus(PLArenaPool *arena, enum ocspStaplingModeEnum ocspStaplingMode,
michael@0 1191 CERTCertificate *cert, SSLKEAType kea, secuPWData *pwdata)
michael@0 1192 {
michael@0 1193 if (ocspStaplingMode == osm_random) {
michael@0 1194 /* 6 different responses */
michael@0 1195 int r = rand() % 6;
michael@0 1196 switch (r) {
michael@0 1197 case 0: ocspStaplingMode = osm_good; break;
michael@0 1198 case 1: ocspStaplingMode = osm_revoked; break;
michael@0 1199 case 2: ocspStaplingMode = osm_unknown; break;
michael@0 1200 case 3: ocspStaplingMode = osm_badsig; break;
michael@0 1201 case 4: ocspStaplingMode = osm_corrupted; break;
michael@0 1202 case 5: ocspStaplingMode = osm_failure; break;
michael@0 1203 default: PORT_Assert(0); break;
michael@0 1204 }
michael@0 1205 }
michael@0 1206 if (ocspStaplingMode != osm_disabled) {
michael@0 1207 SECItemArray *multiOcspResponses = NULL;
michael@0 1208 switch (ocspStaplingMode) {
michael@0 1209 case osm_good:
michael@0 1210 case osm_revoked:
michael@0 1211 case osm_unknown:
michael@0 1212 case osm_badsig:
michael@0 1213 multiOcspResponses =
michael@0 1214 makeSignedOCSPResponse(arena, ocspStaplingMode, cert,
michael@0 1215 pwdata);
michael@0 1216 break;
michael@0 1217 case osm_corrupted:
michael@0 1218 multiOcspResponses = makeCorruptedOCSPResponse(arena);
michael@0 1219 break;
michael@0 1220 case osm_failure:
michael@0 1221 multiOcspResponses = makeTryLaterOCSPResponse(arena);
michael@0 1222 break;
michael@0 1223 case osm_ocsp:
michael@0 1224 errExit("stapling mode \"ocsp\" not implemented");
michael@0 1225 break;
michael@0 1226 break;
michael@0 1227 default:
michael@0 1228 break;
michael@0 1229 }
michael@0 1230 if (multiOcspResponses) {
michael@0 1231 certStatus[kea] = multiOcspResponses;
michael@0 1232 }
michael@0 1233 }
michael@0 1234 }
michael@0 1235
michael@0 1236 int
michael@0 1237 handle_connection(
michael@0 1238 PRFileDesc *tcp_sock,
michael@0 1239 PRFileDesc *model_sock,
michael@0 1240 int requestCert
michael@0 1241 )
michael@0 1242 {
michael@0 1243 PRFileDesc * ssl_sock = NULL;
michael@0 1244 PRFileDesc * local_file_fd = NULL;
michael@0 1245 char * post;
michael@0 1246 char * pBuf; /* unused space at end of buf */
michael@0 1247 const char * errString;
michael@0 1248 PRStatus status;
michael@0 1249 int bufRem; /* unused bytes at end of buf */
michael@0 1250 int bufDat; /* characters received in buf */
michael@0 1251 int newln = 0; /* # of consecutive newlns */
michael@0 1252 int firstTime = 1;
michael@0 1253 int reqLen;
michael@0 1254 int rv;
michael@0 1255 int numIOVs;
michael@0 1256 PRSocketOptionData opt;
michael@0 1257 PRIOVec iovs[16];
michael@0 1258 char msgBuf[160];
michael@0 1259 char buf[10240];
michael@0 1260 char fileName[513];
michael@0 1261 char proto[128];
michael@0 1262 PRDescIdentity aboveLayer = PR_INVALID_IO_LAYER;
michael@0 1263 SSLKEAType kea;
michael@0 1264
michael@0 1265 pBuf = buf;
michael@0 1266 bufRem = sizeof buf;
michael@0 1267
michael@0 1268 VLOG(("selfserv: handle_connection: starting"));
michael@0 1269 opt.option = PR_SockOpt_Nonblocking;
michael@0 1270 opt.value.non_blocking = PR_FALSE;
michael@0 1271 PR_SetSocketOption(tcp_sock, &opt);
michael@0 1272
michael@0 1273 VLOG(("selfserv: handle_connection: starting\n"));
michael@0 1274 if (useModelSocket && model_sock) {
michael@0 1275 SECStatus rv;
michael@0 1276 ssl_sock = SSL_ImportFD(model_sock, tcp_sock);
michael@0 1277 if (!ssl_sock) {
michael@0 1278 errWarn("SSL_ImportFD with model");
michael@0 1279 goto cleanup;
michael@0 1280 }
michael@0 1281 rv = SSL_ResetHandshake(ssl_sock, /* asServer */ 1);
michael@0 1282 if (rv != SECSuccess) {
michael@0 1283 errWarn("SSL_ResetHandshake");
michael@0 1284 goto cleanup;
michael@0 1285 }
michael@0 1286 } else {
michael@0 1287 ssl_sock = tcp_sock;
michael@0 1288 }
michael@0 1289
michael@0 1290 for (kea = kt_rsa; kea < kt_kea_size; kea++) {
michael@0 1291 if (certStatus[kea] != NULL) {
michael@0 1292 SSL_SetStapledOCSPResponses(ssl_sock, certStatus[kea], kea);
michael@0 1293 }
michael@0 1294 }
michael@0 1295
michael@0 1296 if (loggingLayer) {
michael@0 1297 /* find the layer where our new layer is to be pushed */
michael@0 1298 aboveLayer = PR_GetLayersIdentity(ssl_sock->lower);
michael@0 1299 if (aboveLayer == PR_INVALID_IO_LAYER) {
michael@0 1300 errExit("PRGetUniqueIdentity");
michael@0 1301 }
michael@0 1302 /* create the new layer - this is a very cheap operation */
michael@0 1303 loggingFD = PR_CreateIOLayerStub(log_layer_id, &loggingMethods);
michael@0 1304 if (!loggingFD)
michael@0 1305 errExit("PR_CreateIOLayerStub");
michael@0 1306 /* push the layer below ssl but above TCP */
michael@0 1307 rv = PR_PushIOLayer(ssl_sock, aboveLayer, loggingFD);
michael@0 1308 if (rv != PR_SUCCESS) {
michael@0 1309 errExit("PR_PushIOLayer");
michael@0 1310 }
michael@0 1311 }
michael@0 1312
michael@0 1313 if (noDelay) {
michael@0 1314 opt.option = PR_SockOpt_NoDelay;
michael@0 1315 opt.value.no_delay = PR_TRUE;
michael@0 1316 status = PR_SetSocketOption(ssl_sock, &opt);
michael@0 1317 if (status != PR_SUCCESS) {
michael@0 1318 errWarn("PR_SetSocketOption(PR_SockOpt_NoDelay, PR_TRUE)");
michael@0 1319 if (ssl_sock) {
michael@0 1320 PR_Close(ssl_sock);
michael@0 1321 }
michael@0 1322 return SECFailure;
michael@0 1323 }
michael@0 1324 }
michael@0 1325
michael@0 1326 while (1) {
michael@0 1327 newln = 0;
michael@0 1328 reqLen = 0;
michael@0 1329 rv = PR_Read(ssl_sock, pBuf, bufRem - 1);
michael@0 1330 if (rv == 0 ||
michael@0 1331 (rv < 0 && PR_END_OF_FILE_ERROR == PR_GetError())) {
michael@0 1332 if (verbose)
michael@0 1333 errWarn("HDX PR_Read hit EOF");
michael@0 1334 break;
michael@0 1335 }
michael@0 1336 if (rv < 0) {
michael@0 1337 errWarn("HDX PR_Read");
michael@0 1338 goto cleanup;
michael@0 1339 }
michael@0 1340 /* NULL termination */
michael@0 1341 pBuf[rv] = 0;
michael@0 1342 if (firstTime) {
michael@0 1343 firstTime = 0;
michael@0 1344 printSecurityInfo(ssl_sock);
michael@0 1345 }
michael@0 1346
michael@0 1347 pBuf += rv;
michael@0 1348 bufRem -= rv;
michael@0 1349 bufDat = pBuf - buf;
michael@0 1350 /* Parse the input, starting at the beginning of the buffer.
michael@0 1351 * Stop when we detect two consecutive \n's (or \r\n's)
michael@0 1352 * as this signifies the end of the GET or POST portion.
michael@0 1353 * The posted data follows.
michael@0 1354 */
michael@0 1355 while (reqLen < bufDat && newln < 2) {
michael@0 1356 int octet = buf[reqLen++];
michael@0 1357 if (octet == '\n') {
michael@0 1358 newln++;
michael@0 1359 } else if (octet != '\r') {
michael@0 1360 newln = 0;
michael@0 1361 }
michael@0 1362 }
michael@0 1363
michael@0 1364 /* came to the end of the buffer, or second newln
michael@0 1365 * If we didn't get an empty line (CRLFCRLF) then keep on reading.
michael@0 1366 */
michael@0 1367 if (newln < 2)
michael@0 1368 continue;
michael@0 1369
michael@0 1370 /* we're at the end of the HTTP request.
michael@0 1371 * If the request is a POST, then there will be one more
michael@0 1372 * line of data.
michael@0 1373 * This parsing is a hack, but ok for SSL test purposes.
michael@0 1374 */
michael@0 1375 post = PORT_Strstr(buf, "POST ");
michael@0 1376 if (!post || *post != 'P')
michael@0 1377 break;
michael@0 1378
michael@0 1379 /* It's a post, so look for the next and final CR/LF. */
michael@0 1380 /* We should parse content length here, but ... */
michael@0 1381 while (reqLen < bufDat && newln < 3) {
michael@0 1382 int octet = buf[reqLen++];
michael@0 1383 if (octet == '\n') {
michael@0 1384 newln++;
michael@0 1385 }
michael@0 1386 }
michael@0 1387 if (newln == 3)
michael@0 1388 break;
michael@0 1389 } /* read loop */
michael@0 1390
michael@0 1391 bufDat = pBuf - buf;
michael@0 1392 if (bufDat) do { /* just close if no data */
michael@0 1393 /* Have either (a) a complete get, (b) a complete post, (c) EOF */
michael@0 1394 if (reqLen > 0 && !strncmp(buf, getCmd, sizeof getCmd - 1)) {
michael@0 1395 char * fnBegin = buf + 4;
michael@0 1396 char * fnEnd;
michael@0 1397 PRFileInfo info;
michael@0 1398 /* try to open the file named.
michael@0 1399 * If successful, then write it to the client.
michael@0 1400 */
michael@0 1401 fnEnd = strpbrk(fnBegin, " \r\n");
michael@0 1402 if (fnEnd) {
michael@0 1403 int fnLen = fnEnd - fnBegin;
michael@0 1404 if (fnLen < sizeof fileName) {
michael@0 1405 char *real_fileName = fileName;
michael@0 1406 char *protoEnd = NULL;
michael@0 1407 strncpy(fileName, fnBegin, fnLen);
michael@0 1408 fileName[fnLen] = 0; /* null terminate */
michael@0 1409 if ((protoEnd = strstr(fileName, "://")) != NULL) {
michael@0 1410 int protoLen = PR_MIN(protoEnd - fileName, sizeof(proto) - 1);
michael@0 1411 PL_strncpy(proto, fileName, protoLen);
michael@0 1412 proto[protoLen] = 0;
michael@0 1413 real_fileName= protoEnd + 3;
michael@0 1414 } else {
michael@0 1415 proto[0] = 0;
michael@0 1416 }
michael@0 1417 status = PR_GetFileInfo(real_fileName, &info);
michael@0 1418 if (status == PR_SUCCESS &&
michael@0 1419 info.type == PR_FILE_FILE &&
michael@0 1420 info.size >= 0 ) {
michael@0 1421 local_file_fd = PR_Open(real_fileName, PR_RDONLY, 0);
michael@0 1422 }
michael@0 1423 }
michael@0 1424 }
michael@0 1425 }
michael@0 1426 /* if user has requested client auth in a subsequent handshake,
michael@0 1427 * do it here.
michael@0 1428 */
michael@0 1429 if (requestCert > 2) { /* request cert was 3 or 4 */
michael@0 1430 CERTCertificate * cert = SSL_PeerCertificate(ssl_sock);
michael@0 1431 if (cert) {
michael@0 1432 CERT_DestroyCertificate(cert);
michael@0 1433 } else {
michael@0 1434 rv = SSL_OptionSet(ssl_sock, SSL_REQUEST_CERTIFICATE, 1);
michael@0 1435 if (rv < 0) {
michael@0 1436 errWarn("second SSL_OptionSet SSL_REQUEST_CERTIFICATE");
michael@0 1437 break;
michael@0 1438 }
michael@0 1439 rv = SSL_OptionSet(ssl_sock, SSL_REQUIRE_CERTIFICATE,
michael@0 1440 (requestCert == 4));
michael@0 1441 if (rv < 0) {
michael@0 1442 errWarn("second SSL_OptionSet SSL_REQUIRE_CERTIFICATE");
michael@0 1443 break;
michael@0 1444 }
michael@0 1445 rv = SSL_ReHandshake(ssl_sock, PR_TRUE);
michael@0 1446 if (rv != 0) {
michael@0 1447 errWarn("SSL_ReHandshake");
michael@0 1448 break;
michael@0 1449 }
michael@0 1450 rv = SSL_ForceHandshake(ssl_sock);
michael@0 1451 if (rv < 0) {
michael@0 1452 errWarn("SSL_ForceHandshake");
michael@0 1453 break;
michael@0 1454 }
michael@0 1455 }
michael@0 1456 }
michael@0 1457
michael@0 1458 numIOVs = 0;
michael@0 1459
michael@0 1460 iovs[numIOVs].iov_base = (char *)outHeader;
michael@0 1461 iovs[numIOVs].iov_len = (sizeof(outHeader)) - 1;
michael@0 1462 numIOVs++;
michael@0 1463
michael@0 1464 if (local_file_fd) {
michael@0 1465 PRInt32 bytes;
michael@0 1466 int errLen;
michael@0 1467 if (!PL_strlen(proto) || !PL_strcmp(proto, "file")) {
michael@0 1468 bytes = PR_TransmitFile(ssl_sock, local_file_fd, outHeader,
michael@0 1469 sizeof outHeader - 1,
michael@0 1470 PR_TRANSMITFILE_KEEP_OPEN,
michael@0 1471 PR_INTERVAL_NO_TIMEOUT);
michael@0 1472 if (bytes >= 0) {
michael@0 1473 bytes -= sizeof outHeader - 1;
michael@0 1474 FPRINTF(stderr,
michael@0 1475 "selfserv: PR_TransmitFile wrote %d bytes from %s\n",
michael@0 1476 bytes, fileName);
michael@0 1477 break;
michael@0 1478 }
michael@0 1479 errString = errWarn("PR_TransmitFile");
michael@0 1480 errLen = PORT_Strlen(errString);
michael@0 1481 errLen = PR_MIN(errLen, sizeof msgBuf - 1);
michael@0 1482 PORT_Memcpy(msgBuf, errString, errLen);
michael@0 1483 msgBuf[errLen] = 0;
michael@0 1484
michael@0 1485 iovs[numIOVs].iov_base = msgBuf;
michael@0 1486 iovs[numIOVs].iov_len = PORT_Strlen(msgBuf);
michael@0 1487 numIOVs++;
michael@0 1488 }
michael@0 1489 if (!PL_strcmp(proto, "crl")) {
michael@0 1490 if (reload_crl(local_file_fd) == SECFailure) {
michael@0 1491 errString = errWarn("CERT_CacheCRL");
michael@0 1492 if (!errString)
michael@0 1493 errString = "Unknow error";
michael@0 1494 PR_snprintf(msgBuf, sizeof(msgBuf), "%s%s ",
michael@0 1495 crlCacheErr, errString);
michael@0 1496
michael@0 1497 iovs[numIOVs].iov_base = msgBuf;
michael@0 1498 iovs[numIOVs].iov_len = PORT_Strlen(msgBuf);
michael@0 1499 numIOVs++;
michael@0 1500 } else {
michael@0 1501 FPRINTF(stderr,
michael@0 1502 "selfserv: CRL %s reloaded.\n",
michael@0 1503 fileName);
michael@0 1504 break;
michael@0 1505 }
michael@0 1506 }
michael@0 1507 } else if (reqLen <= 0) { /* hit eof */
michael@0 1508 PORT_Sprintf(msgBuf, "Get or Post incomplete after %d bytes.\r\n",
michael@0 1509 bufDat);
michael@0 1510
michael@0 1511 iovs[numIOVs].iov_base = msgBuf;
michael@0 1512 iovs[numIOVs].iov_len = PORT_Strlen(msgBuf);
michael@0 1513 numIOVs++;
michael@0 1514 } else if (reqLen < bufDat) {
michael@0 1515 PORT_Sprintf(msgBuf, "Discarded %d characters.\r\n",
michael@0 1516 bufDat - reqLen);
michael@0 1517
michael@0 1518 iovs[numIOVs].iov_base = msgBuf;
michael@0 1519 iovs[numIOVs].iov_len = PORT_Strlen(msgBuf);
michael@0 1520 numIOVs++;
michael@0 1521 }
michael@0 1522
michael@0 1523 if (reqLen > 0) {
michael@0 1524 if (verbose > 1)
michael@0 1525 fwrite(buf, 1, reqLen, stdout); /* display it */
michael@0 1526
michael@0 1527 iovs[numIOVs].iov_base = buf;
michael@0 1528 iovs[numIOVs].iov_len = reqLen;
michael@0 1529 numIOVs++;
michael@0 1530 }
michael@0 1531
michael@0 1532 /* Don't add the EOF if we want to test bulk encryption */
michael@0 1533 if (!testBulk) {
michael@0 1534 iovs[numIOVs].iov_base = (char *)EOFmsg;
michael@0 1535 iovs[numIOVs].iov_len = sizeof EOFmsg - 1;
michael@0 1536 numIOVs++;
michael@0 1537 }
michael@0 1538
michael@0 1539 rv = PR_Writev(ssl_sock, iovs, numIOVs, PR_INTERVAL_NO_TIMEOUT);
michael@0 1540 if (rv < 0) {
michael@0 1541 errWarn("PR_Writev");
michael@0 1542 break;
michael@0 1543 }
michael@0 1544
michael@0 1545 /* Send testBulkTotal chunks to the client. Unlimited if 0. */
michael@0 1546 if (testBulk) {
michael@0 1547 while (0 < (rv = PR_Write(ssl_sock, testBulkBuf, testBulkSize))) {
michael@0 1548 PR_ATOMIC_ADD(&loggerBytes, rv);
michael@0 1549 PR_ATOMIC_INCREMENT(&bulkSentChunks);
michael@0 1550 if ((bulkSentChunks > testBulkTotal) && (testBulkTotal != 0))
michael@0 1551 break;
michael@0 1552 }
michael@0 1553
michael@0 1554 /* There was a write error, so close this connection. */
michael@0 1555 if (bulkSentChunks <= testBulkTotal) {
michael@0 1556 errWarn("PR_Write");
michael@0 1557 }
michael@0 1558 PR_ATOMIC_DECREMENT(&loggerOps);
michael@0 1559 break;
michael@0 1560 }
michael@0 1561 } while (0);
michael@0 1562
michael@0 1563 cleanup:
michael@0 1564 if (ssl_sock) {
michael@0 1565 PR_Close(ssl_sock);
michael@0 1566 } else if (tcp_sock) {
michael@0 1567 PR_Close(tcp_sock);
michael@0 1568 }
michael@0 1569 if (local_file_fd)
michael@0 1570 PR_Close(local_file_fd);
michael@0 1571 VLOG(("selfserv: handle_connection: exiting\n"));
michael@0 1572
michael@0 1573 /* do a nice shutdown if asked. */
michael@0 1574 if (!strncmp(buf, stopCmd, sizeof stopCmd - 1)) {
michael@0 1575 VLOG(("selfserv: handle_connection: stop command"));
michael@0 1576 stop_server();
michael@0 1577 }
michael@0 1578 VLOG(("selfserv: handle_connection: exiting"));
michael@0 1579 return SECSuccess; /* success */
michael@0 1580 }
michael@0 1581
michael@0 1582 #ifdef XP_UNIX
michael@0 1583
michael@0 1584 void sigusr1_handler(int sig)
michael@0 1585 {
michael@0 1586 VLOG(("selfserv: sigusr1_handler: stop server"));
michael@0 1587 stop_server();
michael@0 1588 }
michael@0 1589
michael@0 1590 #endif
michael@0 1591
michael@0 1592 SECStatus
michael@0 1593 do_accepts(
michael@0 1594 PRFileDesc *listen_sock,
michael@0 1595 PRFileDesc *model_sock,
michael@0 1596 int requestCert
michael@0 1597 )
michael@0 1598 {
michael@0 1599 PRNetAddr addr;
michael@0 1600 PRErrorCode perr;
michael@0 1601 #ifdef XP_UNIX
michael@0 1602 struct sigaction act;
michael@0 1603 #endif
michael@0 1604
michael@0 1605 VLOG(("selfserv: do_accepts: starting"));
michael@0 1606 PR_SetThreadPriority( PR_GetCurrentThread(), PR_PRIORITY_HIGH);
michael@0 1607
michael@0 1608 acceptorThread = PR_GetCurrentThread();
michael@0 1609 #ifdef XP_UNIX
michael@0 1610 /* set up the signal handler */
michael@0 1611 act.sa_handler = sigusr1_handler;
michael@0 1612 sigemptyset(&act.sa_mask);
michael@0 1613 act.sa_flags = 0;
michael@0 1614 if (sigaction(SIGUSR1, &act, NULL)) {
michael@0 1615 fprintf(stderr, "Error installing signal handler.\n");
michael@0 1616 exit(1);
michael@0 1617 }
michael@0 1618 #endif
michael@0 1619 while (!stopping) {
michael@0 1620 PRFileDesc *tcp_sock;
michael@0 1621 PRCList *myLink;
michael@0 1622
michael@0 1623 FPRINTF(stderr, "\n\n\nselfserv: About to call accept.\n");
michael@0 1624 tcp_sock = PR_Accept(listen_sock, &addr, PR_INTERVAL_NO_TIMEOUT);
michael@0 1625 if (tcp_sock == NULL) {
michael@0 1626 perr = PR_GetError();
michael@0 1627 if ((perr != PR_CONNECT_RESET_ERROR &&
michael@0 1628 perr != PR_PENDING_INTERRUPT_ERROR) || verbose) {
michael@0 1629 errWarn("PR_Accept");
michael@0 1630 }
michael@0 1631 if (perr == PR_CONNECT_RESET_ERROR) {
michael@0 1632 FPRINTF(stderr,
michael@0 1633 "Ignoring PR_CONNECT_RESET_ERROR error - continue\n");
michael@0 1634 continue;
michael@0 1635 }
michael@0 1636 stopping = 1;
michael@0 1637 break;
michael@0 1638 }
michael@0 1639
michael@0 1640 VLOG(("selfserv: do_accept: Got connection\n"));
michael@0 1641
michael@0 1642 if (logStats) {
michael@0 1643 PR_ATOMIC_INCREMENT(&loggerOps);
michael@0 1644 }
michael@0 1645
michael@0 1646 PZ_Lock(qLock);
michael@0 1647 while (PR_CLIST_IS_EMPTY(&freeJobs) && !stopping) {
michael@0 1648 PZ_WaitCondVar(freeListNotEmptyCv, PR_INTERVAL_NO_TIMEOUT);
michael@0 1649 }
michael@0 1650 if (stopping) {
michael@0 1651 PZ_Unlock(qLock);
michael@0 1652 if (tcp_sock) {
michael@0 1653 PR_Close(tcp_sock);
michael@0 1654 }
michael@0 1655 break;
michael@0 1656 }
michael@0 1657 myLink = PR_LIST_HEAD(&freeJobs);
michael@0 1658 PR_REMOVE_AND_INIT_LINK(myLink);
michael@0 1659 /* could release qLock here and reaquire it 7 lines below, but
michael@0 1660 ** why bother for 4 assignment statements?
michael@0 1661 */
michael@0 1662 {
michael@0 1663 JOB * myJob = (JOB *)myLink;
michael@0 1664 myJob->tcp_sock = tcp_sock;
michael@0 1665 myJob->model_sock = model_sock;
michael@0 1666 myJob->requestCert = requestCert;
michael@0 1667 }
michael@0 1668
michael@0 1669 PR_APPEND_LINK(myLink, &jobQ);
michael@0 1670 PZ_NotifyCondVar(jobQNotEmptyCv);
michael@0 1671 PZ_Unlock(qLock);
michael@0 1672 }
michael@0 1673
michael@0 1674 FPRINTF(stderr, "selfserv: Closing listen socket.\n");
michael@0 1675 VLOG(("selfserv: do_accepts: exiting"));
michael@0 1676 if (listen_sock) {
michael@0 1677 PR_Close(listen_sock);
michael@0 1678 }
michael@0 1679 return SECSuccess;
michael@0 1680 }
michael@0 1681
michael@0 1682 PRFileDesc *
michael@0 1683 getBoundListenSocket(unsigned short port)
michael@0 1684 {
michael@0 1685 PRFileDesc * listen_sock;
michael@0 1686 int listenQueueDepth = 5 + (2 * maxThreads);
michael@0 1687 PRStatus prStatus;
michael@0 1688 PRNetAddr addr;
michael@0 1689 PRSocketOptionData opt;
michael@0 1690
michael@0 1691 addr.inet.family = PR_AF_INET;
michael@0 1692 addr.inet.ip = PR_INADDR_ANY;
michael@0 1693 addr.inet.port = PR_htons(port);
michael@0 1694
michael@0 1695 listen_sock = PR_NewTCPSocket();
michael@0 1696 if (listen_sock == NULL) {
michael@0 1697 errExit("PR_NewTCPSocket");
michael@0 1698 }
michael@0 1699
michael@0 1700 opt.option = PR_SockOpt_Nonblocking;
michael@0 1701 opt.value.non_blocking = PR_FALSE;
michael@0 1702 prStatus = PR_SetSocketOption(listen_sock, &opt);
michael@0 1703 if (prStatus < 0) {
michael@0 1704 PR_Close(listen_sock);
michael@0 1705 errExit("PR_SetSocketOption(PR_SockOpt_Nonblocking)");
michael@0 1706 }
michael@0 1707
michael@0 1708 opt.option=PR_SockOpt_Reuseaddr;
michael@0 1709 opt.value.reuse_addr = PR_TRUE;
michael@0 1710 prStatus = PR_SetSocketOption(listen_sock, &opt);
michael@0 1711 if (prStatus < 0) {
michael@0 1712 PR_Close(listen_sock);
michael@0 1713 errExit("PR_SetSocketOption(PR_SockOpt_Reuseaddr)");
michael@0 1714 }
michael@0 1715
michael@0 1716 #ifndef WIN95
michael@0 1717 /* Set PR_SockOpt_Linger because it helps prevent a server bind issue
michael@0 1718 * after clean shutdown . See bug 331413 .
michael@0 1719 * Don't do it in the WIN95 build configuration because clean shutdown is
michael@0 1720 * not implemented, and PR_SockOpt_Linger causes a hang in ssl.sh .
michael@0 1721 * See bug 332348 */
michael@0 1722 opt.option=PR_SockOpt_Linger;
michael@0 1723 opt.value.linger.polarity = PR_TRUE;
michael@0 1724 opt.value.linger.linger = PR_SecondsToInterval(1);
michael@0 1725 prStatus = PR_SetSocketOption(listen_sock, &opt);
michael@0 1726 if (prStatus < 0) {
michael@0 1727 PR_Close(listen_sock);
michael@0 1728 errExit("PR_SetSocketOption(PR_SockOpt_Linger)");
michael@0 1729 }
michael@0 1730 #endif
michael@0 1731
michael@0 1732 prStatus = PR_Bind(listen_sock, &addr);
michael@0 1733 if (prStatus < 0) {
michael@0 1734 PR_Close(listen_sock);
michael@0 1735 errExit("PR_Bind");
michael@0 1736 }
michael@0 1737
michael@0 1738 prStatus = PR_Listen(listen_sock, listenQueueDepth);
michael@0 1739 if (prStatus < 0) {
michael@0 1740 PR_Close(listen_sock);
michael@0 1741 errExit("PR_Listen");
michael@0 1742 }
michael@0 1743 return listen_sock;
michael@0 1744 }
michael@0 1745
michael@0 1746 PRInt32 PR_CALLBACK
michael@0 1747 logWritev (
michael@0 1748 PRFileDesc *fd,
michael@0 1749 const PRIOVec *iov,
michael@0 1750 PRInt32 size,
michael@0 1751 PRIntervalTime timeout )
michael@0 1752 {
michael@0 1753 PRInt32 rv = (fd->lower->methods->writev)(fd->lower, iov, size,
michael@0 1754 timeout);
michael@0 1755 /* Add the amount written, but not if there's an error */
michael@0 1756 if (rv > 0)
michael@0 1757 PR_ATOMIC_ADD(&loggerBytesTCP, rv);
michael@0 1758 return rv;
michael@0 1759 }
michael@0 1760
michael@0 1761 PRInt32 PR_CALLBACK
michael@0 1762 logWrite (
michael@0 1763 PRFileDesc *fd,
michael@0 1764 const void *buf,
michael@0 1765 PRInt32 amount)
michael@0 1766 {
michael@0 1767 PRInt32 rv = (fd->lower->methods->write)(fd->lower, buf, amount);
michael@0 1768 /* Add the amount written, but not if there's an error */
michael@0 1769 if (rv > 0)
michael@0 1770 PR_ATOMIC_ADD(&loggerBytesTCP, rv);
michael@0 1771
michael@0 1772 return rv;
michael@0 1773 }
michael@0 1774
michael@0 1775 PRInt32 PR_CALLBACK
michael@0 1776 logSend (
michael@0 1777 PRFileDesc *fd,
michael@0 1778 const void *buf,
michael@0 1779 PRInt32 amount,
michael@0 1780 PRIntn flags,
michael@0 1781 PRIntervalTime timeout)
michael@0 1782 {
michael@0 1783 PRInt32 rv = (fd->lower->methods->send)(fd->lower, buf, amount,
michael@0 1784 flags, timeout);
michael@0 1785 /* Add the amount written, but not if there's an error */
michael@0 1786 if (rv > 0)
michael@0 1787 PR_ATOMIC_ADD(&loggerBytesTCP, rv);
michael@0 1788 return rv;
michael@0 1789 }
michael@0 1790
michael@0 1791 void initLoggingLayer(void)
michael@0 1792 {
michael@0 1793 /* get a new layer ID */
michael@0 1794 log_layer_id = PR_GetUniqueIdentity("Selfserv Logging");
michael@0 1795 if (log_layer_id == PR_INVALID_IO_LAYER)
michael@0 1796 errExit("PR_GetUniqueIdentity");
michael@0 1797
michael@0 1798 /* setup the default IO methods with my custom write methods */
michael@0 1799 memcpy(&loggingMethods, PR_GetDefaultIOMethods(), sizeof(PRIOMethods));
michael@0 1800 loggingMethods.writev = logWritev;
michael@0 1801 loggingMethods.write = logWrite;
michael@0 1802 loggingMethods.send = logSend;
michael@0 1803 }
michael@0 1804
michael@0 1805 void
michael@0 1806 handshakeCallback(PRFileDesc *fd, void *client_data)
michael@0 1807 {
michael@0 1808 const char *handshakeName = (const char *)client_data;
michael@0 1809 if (handshakeName && !failedToNegotiateName) {
michael@0 1810 SECItem *hostInfo = SSL_GetNegotiatedHostInfo(fd);
michael@0 1811 if (!hostInfo || PORT_Strncmp(handshakeName, (char*)hostInfo->data,
michael@0 1812 hostInfo->len)) {
michael@0 1813 failedToNegotiateName = PR_TRUE;
michael@0 1814 }
michael@0 1815 }
michael@0 1816 }
michael@0 1817
michael@0 1818 void
michael@0 1819 server_main(
michael@0 1820 PRFileDesc * listen_sock,
michael@0 1821 int requestCert,
michael@0 1822 SECKEYPrivateKey ** privKey,
michael@0 1823 CERTCertificate ** cert,
michael@0 1824 const char *expectedHostNameVal)
michael@0 1825 {
michael@0 1826 PRFileDesc *model_sock = NULL;
michael@0 1827 int rv;
michael@0 1828 SSLKEAType kea;
michael@0 1829 SECStatus secStatus;
michael@0 1830
michael@0 1831 if (useModelSocket) {
michael@0 1832 model_sock = PR_NewTCPSocket();
michael@0 1833 if (model_sock == NULL) {
michael@0 1834 errExit("PR_NewTCPSocket on model socket");
michael@0 1835 }
michael@0 1836 model_sock = SSL_ImportFD(NULL, model_sock);
michael@0 1837 if (model_sock == NULL) {
michael@0 1838 errExit("SSL_ImportFD");
michael@0 1839 }
michael@0 1840 } else {
michael@0 1841 model_sock = listen_sock = SSL_ImportFD(NULL, listen_sock);
michael@0 1842 if (listen_sock == NULL) {
michael@0 1843 errExit("SSL_ImportFD");
michael@0 1844 }
michael@0 1845 }
michael@0 1846
michael@0 1847 /* do SSL configuration. */
michael@0 1848 rv = SSL_OptionSet(model_sock, SSL_SECURITY,
michael@0 1849 enableSSL2 || enabledVersions.min != 0);
michael@0 1850 if (rv < 0) {
michael@0 1851 errExit("SSL_OptionSet SSL_SECURITY");
michael@0 1852 }
michael@0 1853
michael@0 1854 rv = SSL_VersionRangeSet(model_sock, &enabledVersions);
michael@0 1855 if (rv != SECSuccess) {
michael@0 1856 errExit("error setting SSL/TLS version range ");
michael@0 1857 }
michael@0 1858
michael@0 1859 rv = SSL_OptionSet(model_sock, SSL_ENABLE_SSL2, enableSSL2);
michael@0 1860 if (rv != SECSuccess) {
michael@0 1861 errExit("error enabling SSLv2 ");
michael@0 1862 }
michael@0 1863
michael@0 1864 rv = SSL_OptionSet(model_sock, SSL_ROLLBACK_DETECTION, !disableRollBack);
michael@0 1865 if (rv != SECSuccess) {
michael@0 1866 errExit("error enabling RollBack detection ");
michael@0 1867 }
michael@0 1868 if (disableStepDown) {
michael@0 1869 rv = SSL_OptionSet(model_sock, SSL_NO_STEP_DOWN, PR_TRUE);
michael@0 1870 if (rv != SECSuccess) {
michael@0 1871 errExit("error disabling SSL StepDown ");
michael@0 1872 }
michael@0 1873 }
michael@0 1874 if (bypassPKCS11) {
michael@0 1875 rv = SSL_OptionSet(model_sock, SSL_BYPASS_PKCS11, PR_TRUE);
michael@0 1876 if (rv != SECSuccess) {
michael@0 1877 errExit("error enabling PKCS11 bypass ");
michael@0 1878 }
michael@0 1879 }
michael@0 1880 if (disableLocking) {
michael@0 1881 rv = SSL_OptionSet(model_sock, SSL_NO_LOCKS, PR_TRUE);
michael@0 1882 if (rv != SECSuccess) {
michael@0 1883 errExit("error disabling SSL socket locking ");
michael@0 1884 }
michael@0 1885 }
michael@0 1886 if (enableSessionTickets) {
michael@0 1887 rv = SSL_OptionSet(model_sock, SSL_ENABLE_SESSION_TICKETS, PR_TRUE);
michael@0 1888 if (rv != SECSuccess) {
michael@0 1889 errExit("error enabling Session Ticket extension ");
michael@0 1890 }
michael@0 1891 }
michael@0 1892
michael@0 1893 if (enableCompression) {
michael@0 1894 rv = SSL_OptionSet(model_sock, SSL_ENABLE_DEFLATE, PR_TRUE);
michael@0 1895 if (rv != SECSuccess) {
michael@0 1896 errExit("error enabling compression ");
michael@0 1897 }
michael@0 1898 }
michael@0 1899
michael@0 1900 if (virtServerNameIndex >1) {
michael@0 1901 rv = SSL_SNISocketConfigHook(model_sock, mySSLSNISocketConfig,
michael@0 1902 (void*)&virtServerNameArray);
michael@0 1903 if (rv != SECSuccess) {
michael@0 1904 errExit("error enabling SNI extension ");
michael@0 1905 }
michael@0 1906 }
michael@0 1907
michael@0 1908 for (kea = kt_rsa; kea < kt_kea_size; kea++) {
michael@0 1909 if (cert[kea] != NULL) {
michael@0 1910 secStatus = SSL_ConfigSecureServer(model_sock,
michael@0 1911 cert[kea], privKey[kea], kea);
michael@0 1912 if (secStatus != SECSuccess)
michael@0 1913 errExit("SSL_ConfigSecureServer");
michael@0 1914 }
michael@0 1915 }
michael@0 1916
michael@0 1917 if (bigBuf.data) { /* doing FDX */
michael@0 1918 rv = SSL_OptionSet(model_sock, SSL_ENABLE_FDX, 1);
michael@0 1919 if (rv < 0) {
michael@0 1920 errExit("SSL_OptionSet SSL_ENABLE_FDX");
michael@0 1921 }
michael@0 1922 }
michael@0 1923
michael@0 1924 if (NoReuse) {
michael@0 1925 rv = SSL_OptionSet(model_sock, SSL_NO_CACHE, 1);
michael@0 1926 if (rv < 0) {
michael@0 1927 errExit("SSL_OptionSet SSL_NO_CACHE");
michael@0 1928 }
michael@0 1929 }
michael@0 1930
michael@0 1931 /* This cipher is not on by default. The Acceptance test
michael@0 1932 * would like it to be. Turn this cipher on.
michael@0 1933 */
michael@0 1934
michael@0 1935 secStatus = SSL_CipherPrefSetDefault( TLS_RSA_WITH_NULL_MD5, PR_TRUE);
michael@0 1936 if ( secStatus != SECSuccess ) {
michael@0 1937 errExit("SSL_CipherPrefSetDefault:TLS_RSA_WITH_NULL_MD5");
michael@0 1938 }
michael@0 1939
michael@0 1940 if (expectedHostNameVal) {
michael@0 1941 SSL_HandshakeCallback(model_sock, handshakeCallback,
michael@0 1942 (void*)expectedHostNameVal);
michael@0 1943 }
michael@0 1944
michael@0 1945 if (requestCert) {
michael@0 1946 SSL_AuthCertificateHook(model_sock, mySSLAuthCertificate,
michael@0 1947 (void *)CERT_GetDefaultCertDB());
michael@0 1948 if (requestCert <= 2) {
michael@0 1949 rv = SSL_OptionSet(model_sock, SSL_REQUEST_CERTIFICATE, 1);
michael@0 1950 if (rv < 0) {
michael@0 1951 errExit("first SSL_OptionSet SSL_REQUEST_CERTIFICATE");
michael@0 1952 }
michael@0 1953 rv = SSL_OptionSet(model_sock, SSL_REQUIRE_CERTIFICATE,
michael@0 1954 (requestCert == 2));
michael@0 1955 if (rv < 0) {
michael@0 1956 errExit("first SSL_OptionSet SSL_REQUIRE_CERTIFICATE");
michael@0 1957 }
michael@0 1958 }
michael@0 1959 }
michael@0 1960
michael@0 1961 if (MakeCertOK)
michael@0 1962 SSL_BadCertHook(model_sock, myBadCertHandler, NULL);
michael@0 1963
michael@0 1964 /* end of ssl configuration. */
michael@0 1965
michael@0 1966
michael@0 1967 /* Now, do the accepting, here in the main thread. */
michael@0 1968 rv = do_accepts(listen_sock, model_sock, requestCert);
michael@0 1969
michael@0 1970 terminateWorkerThreads();
michael@0 1971
michael@0 1972 if (useModelSocket && model_sock) {
michael@0 1973 if (model_sock) {
michael@0 1974 PR_Close(model_sock);
michael@0 1975 }
michael@0 1976 }
michael@0 1977
michael@0 1978 }
michael@0 1979
michael@0 1980 SECStatus
michael@0 1981 readBigFile(const char * fileName)
michael@0 1982 {
michael@0 1983 PRFileInfo info;
michael@0 1984 PRStatus status;
michael@0 1985 SECStatus rv = SECFailure;
michael@0 1986 int count;
michael@0 1987 int hdrLen;
michael@0 1988 PRFileDesc *local_file_fd = NULL;
michael@0 1989
michael@0 1990 status = PR_GetFileInfo(fileName, &info);
michael@0 1991
michael@0 1992 if (status == PR_SUCCESS &&
michael@0 1993 info.type == PR_FILE_FILE &&
michael@0 1994 info.size > 0 &&
michael@0 1995 NULL != (local_file_fd = PR_Open(fileName, PR_RDONLY, 0))) {
michael@0 1996
michael@0 1997 hdrLen = PORT_Strlen(outHeader);
michael@0 1998 bigBuf.len = hdrLen + info.size;
michael@0 1999 bigBuf.data = PORT_Malloc(bigBuf.len + 4095);
michael@0 2000 if (!bigBuf.data) {
michael@0 2001 errWarn("PORT_Malloc");
michael@0 2002 goto done;
michael@0 2003 }
michael@0 2004
michael@0 2005 PORT_Memcpy(bigBuf.data, outHeader, hdrLen);
michael@0 2006
michael@0 2007 count = PR_Read(local_file_fd, bigBuf.data + hdrLen, info.size);
michael@0 2008 if (count != info.size) {
michael@0 2009 errWarn("PR_Read local file");
michael@0 2010 goto done;
michael@0 2011 }
michael@0 2012 rv = SECSuccess;
michael@0 2013 done:
michael@0 2014 if (local_file_fd) {
michael@0 2015 PR_Close(local_file_fd);
michael@0 2016 }
michael@0 2017 }
michael@0 2018 return rv;
michael@0 2019 }
michael@0 2020
michael@0 2021 int numChildren;
michael@0 2022 PRProcess * child[MAX_PROCS];
michael@0 2023
michael@0 2024 PRProcess *
michael@0 2025 haveAChild(int argc, char **argv, PRProcessAttr * attr)
michael@0 2026 {
michael@0 2027 PRProcess * newProcess;
michael@0 2028
michael@0 2029 newProcess = PR_CreateProcess(argv[0], argv, NULL, attr);
michael@0 2030 if (!newProcess) {
michael@0 2031 errWarn("Can't create new process.");
michael@0 2032 } else {
michael@0 2033 child[numChildren++] = newProcess;
michael@0 2034 }
michael@0 2035 return newProcess;
michael@0 2036 }
michael@0 2037
michael@0 2038 void
michael@0 2039 beAGoodParent(int argc, char **argv, int maxProcs, PRFileDesc * listen_sock)
michael@0 2040 {
michael@0 2041 PRProcess * newProcess;
michael@0 2042 PRProcessAttr * attr;
michael@0 2043 int i;
michael@0 2044 PRInt32 exitCode;
michael@0 2045 PRStatus rv;
michael@0 2046
michael@0 2047 rv = PR_SetFDInheritable(listen_sock, PR_TRUE);
michael@0 2048 if (rv != PR_SUCCESS)
michael@0 2049 errExit("PR_SetFDInheritable");
michael@0 2050
michael@0 2051 attr = PR_NewProcessAttr();
michael@0 2052 if (!attr)
michael@0 2053 errExit("PR_NewProcessAttr");
michael@0 2054
michael@0 2055 rv = PR_ProcessAttrSetInheritableFD(attr, listen_sock, inheritableSockName);
michael@0 2056 if (rv != PR_SUCCESS)
michael@0 2057 errExit("PR_ProcessAttrSetInheritableFD");
michael@0 2058
michael@0 2059 for (i = 0; i < maxProcs; ++i) {
michael@0 2060 newProcess = haveAChild(argc, argv, attr);
michael@0 2061 if (!newProcess)
michael@0 2062 break;
michael@0 2063 }
michael@0 2064
michael@0 2065 rv = PR_SetFDInheritable(listen_sock, PR_FALSE);
michael@0 2066 if (rv != PR_SUCCESS)
michael@0 2067 errExit("PR_SetFDInheritable");
michael@0 2068
michael@0 2069 while (numChildren > 0) {
michael@0 2070 newProcess = child[numChildren - 1];
michael@0 2071 PR_WaitProcess(newProcess, &exitCode);
michael@0 2072 fprintf(stderr, "Child %d exited with exit code %x\n",
michael@0 2073 numChildren, exitCode);
michael@0 2074 numChildren--;
michael@0 2075 }
michael@0 2076 exit(0);
michael@0 2077 }
michael@0 2078
michael@0 2079 #define HEXCHAR_TO_INT(c, i) \
michael@0 2080 if (((c) >= '0') && ((c) <= '9')) { \
michael@0 2081 i = (c) - '0'; \
michael@0 2082 } else if (((c) >= 'a') && ((c) <= 'f')) { \
michael@0 2083 i = (c) - 'a' + 10; \
michael@0 2084 } else if (((c) >= 'A') && ((c) <= 'F')) { \
michael@0 2085 i = (c) - 'A' + 10; \
michael@0 2086 } else if ((c) == '\0') { \
michael@0 2087 fprintf(stderr, "Invalid length of cipher string (-c :WXYZ).\n"); \
michael@0 2088 exit(9); \
michael@0 2089 } else { \
michael@0 2090 fprintf(stderr, "Non-hex char in cipher string (-c :WXYZ).\n"); \
michael@0 2091 exit(9); \
michael@0 2092 }
michael@0 2093
michael@0 2094 SECStatus enableOCSPStapling(const char* mode)
michael@0 2095 {
michael@0 2096 if (!strcmp(mode, "good")) {
michael@0 2097 ocspStaplingMode = osm_good;
michael@0 2098 return SECSuccess;
michael@0 2099 }
michael@0 2100 if (!strcmp(mode, "unknown")) {
michael@0 2101 ocspStaplingMode = osm_unknown;
michael@0 2102 return SECSuccess;
michael@0 2103 }
michael@0 2104 if (!strcmp(mode, "revoked")) {
michael@0 2105 ocspStaplingMode = osm_revoked;
michael@0 2106 return SECSuccess;
michael@0 2107 }
michael@0 2108 if (!strcmp(mode, "badsig")) {
michael@0 2109 ocspStaplingMode = osm_badsig;
michael@0 2110 return SECSuccess;
michael@0 2111 }
michael@0 2112 if (!strcmp(mode, "corrupted")) {
michael@0 2113 ocspStaplingMode = osm_corrupted;
michael@0 2114 return SECSuccess;
michael@0 2115 }
michael@0 2116 if (!strcmp(mode, "failure")) {
michael@0 2117 ocspStaplingMode = osm_failure;
michael@0 2118 return SECSuccess;
michael@0 2119 }
michael@0 2120 if (!strcmp(mode, "random")) {
michael@0 2121 ocspStaplingMode = osm_random;
michael@0 2122 return SECSuccess;
michael@0 2123 }
michael@0 2124 if (!strcmp(mode, "ocsp")) {
michael@0 2125 ocspStaplingMode = osm_ocsp;
michael@0 2126 return SECSuccess;
michael@0 2127 }
michael@0 2128 return SECFailure;
michael@0 2129 }
michael@0 2130
michael@0 2131 int
michael@0 2132 main(int argc, char **argv)
michael@0 2133 {
michael@0 2134 char * progName = NULL;
michael@0 2135 char * nickName = NULL;
michael@0 2136 #ifndef NSS_DISABLE_ECC
michael@0 2137 char * ecNickName = NULL;
michael@0 2138 #endif
michael@0 2139 const char * fileName = NULL;
michael@0 2140 char * cipherString= NULL;
michael@0 2141 const char * dir = ".";
michael@0 2142 char * passwd = NULL;
michael@0 2143 char * pwfile = NULL;
michael@0 2144 const char * pidFile = NULL;
michael@0 2145 char * tmp;
michael@0 2146 char * envString;
michael@0 2147 PRFileDesc * listen_sock;
michael@0 2148 CERTCertificate * cert [kt_kea_size] = { NULL };
michael@0 2149 SECKEYPrivateKey * privKey[kt_kea_size] = { NULL };
michael@0 2150 int optionsFound = 0;
michael@0 2151 int maxProcs = 1;
michael@0 2152 unsigned short port = 0;
michael@0 2153 SECStatus rv;
michael@0 2154 PRStatus prStatus;
michael@0 2155 PRBool bindOnly = PR_FALSE;
michael@0 2156 PRBool useExportPolicy = PR_FALSE;
michael@0 2157 PRBool useLocalThreads = PR_FALSE;
michael@0 2158 PLOptState *optstate;
michael@0 2159 PLOptStatus status;
michael@0 2160 PRThread *loggerThread = NULL;
michael@0 2161 PRBool debugCache = PR_FALSE; /* bug 90518 */
michael@0 2162 char emptyString[] = { "" };
michael@0 2163 char* certPrefix = emptyString;
michael@0 2164 PRUint32 protos = 0;
michael@0 2165 SSL3Statistics *ssl3stats;
michael@0 2166 PRUint32 i;
michael@0 2167 secuPWData pwdata = { PW_NONE, 0 };
michael@0 2168 char *expectedHostNameVal = NULL;
michael@0 2169 PLArenaPool *certStatusArena = NULL;
michael@0 2170
michael@0 2171 tmp = strrchr(argv[0], '/');
michael@0 2172 tmp = tmp ? tmp + 1 : argv[0];
michael@0 2173 progName = strrchr(tmp, '\\');
michael@0 2174 progName = progName ? progName + 1 : tmp;
michael@0 2175
michael@0 2176 PR_Init( PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
michael@0 2177 SSL_VersionRangeGetSupported(ssl_variant_stream, &enabledVersions);
michael@0 2178
michael@0 2179 /* please keep this list of options in ASCII collating sequence.
michael@0 2180 ** numbers, then capital letters, then lower case, alphabetical.
michael@0 2181 */
michael@0 2182 optstate = PL_CreateOptState(argc, argv,
michael@0 2183 "2:A:BC:DEL:M:NP:RT:V:Ya:bc:d:e:f:g:hi:jk:lmn:op:qrst:uvw:xyz");
michael@0 2184 while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) {
michael@0 2185 ++optionsFound;
michael@0 2186 switch(optstate->option) {
michael@0 2187 case '2': fileName = optstate->value; break;
michael@0 2188
michael@0 2189 case 'A': ocspStaplingCA = PORT_Strdup(optstate->value); break;
michael@0 2190
michael@0 2191 case 'B': bypassPKCS11 = PR_TRUE; break;
michael@0 2192
michael@0 2193 case 'C': if (optstate->value) NumSidCacheEntries = PORT_Atoi(optstate->value); break;
michael@0 2194
michael@0 2195 case 'D': noDelay = PR_TRUE; break;
michael@0 2196 case 'E': disableStepDown = PR_TRUE; break;
michael@0 2197
michael@0 2198 case 'I': /* reserved for OCSP multi-stapling */ break;
michael@0 2199
michael@0 2200 case 'L':
michael@0 2201 logStats = PR_TRUE;
michael@0 2202 if (optstate->value == NULL) {
michael@0 2203 logPeriod = 30;
michael@0 2204 } else {
michael@0 2205 logPeriod = PORT_Atoi(optstate->value);
michael@0 2206 if (logPeriod <= 0) logPeriod = 30;
michael@0 2207 }
michael@0 2208 break;
michael@0 2209
michael@0 2210 case 'M':
michael@0 2211 maxProcs = PORT_Atoi(optstate->value);
michael@0 2212 if (maxProcs < 1) maxProcs = 1;
michael@0 2213 if (maxProcs > MAX_PROCS) maxProcs = MAX_PROCS;
michael@0 2214 break;
michael@0 2215
michael@0 2216 case 'N': NoReuse = PR_TRUE; break;
michael@0 2217
michael@0 2218 case 'R': disableRollBack = PR_TRUE; break;
michael@0 2219
michael@0 2220 case 'T':
michael@0 2221 if (enableOCSPStapling(optstate->value) != SECSuccess) {
michael@0 2222 fprintf(stderr, "Invalid OCSP stapling mode.\n");
michael@0 2223 fprintf(stderr, "Run '%s -h' for usage information.\n", progName);
michael@0 2224 exit(53);
michael@0 2225 }
michael@0 2226 break;
michael@0 2227
michael@0 2228 case 'V': if (SECU_ParseSSLVersionRangeString(optstate->value,
michael@0 2229 enabledVersions, enableSSL2,
michael@0 2230 &enabledVersions, &enableSSL2) != SECSuccess) {
michael@0 2231 Usage(progName);
michael@0 2232 }
michael@0 2233 break;
michael@0 2234
michael@0 2235 case 'Y': PrintCipherUsage(progName); exit(0); break;
michael@0 2236
michael@0 2237 case 'a': if (virtServerNameIndex >= MAX_VIRT_SERVER_NAME_ARRAY_INDEX) {
michael@0 2238 Usage(progName);
michael@0 2239 }
michael@0 2240 virtServerNameArray[virtServerNameIndex++] =
michael@0 2241 PORT_Strdup(optstate->value); break;
michael@0 2242
michael@0 2243 case 'b': bindOnly = PR_TRUE; break;
michael@0 2244
michael@0 2245 case 'c': cipherString = PORT_Strdup(optstate->value); break;
michael@0 2246
michael@0 2247 case 'd': dir = optstate->value; break;
michael@0 2248
michael@0 2249 #ifndef NSS_DISABLE_ECC
michael@0 2250 case 'e': ecNickName = PORT_Strdup(optstate->value); break;
michael@0 2251 #endif /* NSS_DISABLE_ECC */
michael@0 2252
michael@0 2253 case 'f':
michael@0 2254 pwdata.source = PW_FROMFILE;
michael@0 2255 pwdata.data = pwfile = PORT_Strdup(optstate->value);
michael@0 2256 break;
michael@0 2257
michael@0 2258 case 'g':
michael@0 2259 testBulk = PR_TRUE;
michael@0 2260 testBulkTotal = PORT_Atoi(optstate->value);
michael@0 2261 break;
michael@0 2262
michael@0 2263 case 'h': Usage(progName); exit(0); break;
michael@0 2264
michael@0 2265 case 'i': pidFile = optstate->value; break;
michael@0 2266
michael@0 2267 case 'j':
michael@0 2268 initLoggingLayer();
michael@0 2269 loggingLayer = PR_TRUE;
michael@0 2270 break;
michael@0 2271
michael@0 2272 case 'k': expectedHostNameVal = PORT_Strdup(optstate->value);
michael@0 2273 break;
michael@0 2274
michael@0 2275 case 'l': useLocalThreads = PR_TRUE; break;
michael@0 2276
michael@0 2277 case 'm': useModelSocket = PR_TRUE; break;
michael@0 2278
michael@0 2279 case 'n': nickName = PORT_Strdup(optstate->value);
michael@0 2280 virtServerNameArray[0] = PORT_Strdup(optstate->value);
michael@0 2281 break;
michael@0 2282
michael@0 2283 case 'P': certPrefix = PORT_Strdup(optstate->value); break;
michael@0 2284
michael@0 2285 case 'o': MakeCertOK = 1; break;
michael@0 2286
michael@0 2287 case 'p': port = PORT_Atoi(optstate->value); break;
michael@0 2288
michael@0 2289 case 'q': testbypass = PR_TRUE; break;
michael@0 2290
michael@0 2291 case 'r': ++requestCert; break;
michael@0 2292
michael@0 2293 case 's': disableLocking = PR_TRUE; break;
michael@0 2294
michael@0 2295 case 't':
michael@0 2296 maxThreads = PORT_Atoi(optstate->value);
michael@0 2297 if ( maxThreads > MAX_THREADS ) maxThreads = MAX_THREADS;
michael@0 2298 if ( maxThreads < MIN_THREADS ) maxThreads = MIN_THREADS;
michael@0 2299 break;
michael@0 2300
michael@0 2301 case 'u': enableSessionTickets = PR_TRUE; break;
michael@0 2302
michael@0 2303 case 'v': verbose++; break;
michael@0 2304
michael@0 2305 case 'w':
michael@0 2306 pwdata.source = PW_PLAINTEXT;
michael@0 2307 pwdata.data = passwd = PORT_Strdup(optstate->value);
michael@0 2308 break;
michael@0 2309
michael@0 2310 case 'x': useExportPolicy = PR_TRUE; break;
michael@0 2311
michael@0 2312 case 'y': debugCache = PR_TRUE; break;
michael@0 2313
michael@0 2314 case 'z': enableCompression = PR_TRUE; break;
michael@0 2315
michael@0 2316 default:
michael@0 2317 case '?':
michael@0 2318 fprintf(stderr, "Unrecognized or bad option specified.\n");
michael@0 2319 fprintf(stderr, "Run '%s -h' for usage information.\n", progName);
michael@0 2320 exit(4);
michael@0 2321 break;
michael@0 2322 }
michael@0 2323 }
michael@0 2324 PL_DestroyOptState(optstate);
michael@0 2325 if (status == PL_OPT_BAD) {
michael@0 2326 fprintf(stderr, "Unrecognized or bad option specified.\n");
michael@0 2327 fprintf(stderr, "Run '%s -h' for usage information.\n", progName);
michael@0 2328 exit(5);
michael@0 2329 }
michael@0 2330 if (!optionsFound) {
michael@0 2331 Usage(progName);
michael@0 2332 exit(51);
michael@0 2333 }
michael@0 2334 switch (ocspStaplingMode) {
michael@0 2335 case osm_good:
michael@0 2336 case osm_revoked:
michael@0 2337 case osm_unknown:
michael@0 2338 case osm_random:
michael@0 2339 if (!ocspStaplingCA) {
michael@0 2340 fprintf(stderr, "Selected stapling response requires the -A parameter.\n");
michael@0 2341 fprintf(stderr, "Run '%s -h' for usage information.\n", progName);
michael@0 2342 exit(52);
michael@0 2343 }
michael@0 2344 break;
michael@0 2345 default:
michael@0 2346 break;
michael@0 2347 }
michael@0 2348
michael@0 2349 /* The -b (bindOnly) option is only used by the ssl.sh test
michael@0 2350 * script on Linux to determine whether a previous selfserv
michael@0 2351 * process has fully died and freed the port. (Bug 129701)
michael@0 2352 */
michael@0 2353 if (bindOnly) {
michael@0 2354 listen_sock = getBoundListenSocket(port);
michael@0 2355 if (!listen_sock) {
michael@0 2356 exit(1);
michael@0 2357 }
michael@0 2358 if (listen_sock) {
michael@0 2359 PR_Close(listen_sock);
michael@0 2360 }
michael@0 2361 exit(0);
michael@0 2362 }
michael@0 2363
michael@0 2364 if ((nickName == NULL)
michael@0 2365 #ifndef NSS_DISABLE_ECC
michael@0 2366 && (ecNickName == NULL)
michael@0 2367 #endif
michael@0 2368 ) {
michael@0 2369
michael@0 2370 fprintf(stderr, "Required arg '-n' (rsa nickname) not supplied.\n");
michael@0 2371 fprintf(stderr, "Run '%s -h' for usage information.\n", progName);
michael@0 2372 exit(6);
michael@0 2373 }
michael@0 2374
michael@0 2375 if (port == 0) {
michael@0 2376 fprintf(stderr, "Required argument 'port' must be non-zero value\n");
michael@0 2377 exit(7);
michael@0 2378 }
michael@0 2379
michael@0 2380 if (NoReuse && maxProcs > 1) {
michael@0 2381 fprintf(stderr, "-M and -N options are mutually exclusive.\n");
michael@0 2382 exit(14);
michael@0 2383 }
michael@0 2384
michael@0 2385 if (pidFile) {
michael@0 2386 FILE *tmpfile=fopen(pidFile,"w+");
michael@0 2387
michael@0 2388 if (tmpfile) {
michael@0 2389 fprintf(tmpfile,"%d",getpid());
michael@0 2390 fclose(tmpfile);
michael@0 2391 }
michael@0 2392 }
michael@0 2393
michael@0 2394 /* allocate and initialize app data for bulk encryption testing */
michael@0 2395 if (testBulk) {
michael@0 2396 testBulkBuf = PORT_Malloc(testBulkSize);
michael@0 2397 if (testBulkBuf == NULL)
michael@0 2398 errExit("Out of memory: testBulkBuf");
michael@0 2399 for (i = 0; i < testBulkSize; i++)
michael@0 2400 testBulkBuf[i] = i;
michael@0 2401 }
michael@0 2402
michael@0 2403 envString = getenv(envVarName);
michael@0 2404 tmp = getenv("TMP");
michael@0 2405 if (!tmp)
michael@0 2406 tmp = getenv("TMPDIR");
michael@0 2407 if (!tmp)
michael@0 2408 tmp = getenv("TEMP");
michael@0 2409 if (envString) {
michael@0 2410 /* we're one of the children in a multi-process server. */
michael@0 2411 listen_sock = PR_GetInheritedFD(inheritableSockName);
michael@0 2412 if (!listen_sock)
michael@0 2413 errExit("PR_GetInheritedFD");
michael@0 2414 #ifndef WINNT
michael@0 2415 /* we can't do this on NT because it breaks NSPR and
michael@0 2416 PR_Accept will fail on the socket in the child process if
michael@0 2417 the socket state is change to non inheritable
michael@0 2418 It is however a security issue to leave it accessible,
michael@0 2419 but it is OK for a test server such as selfserv.
michael@0 2420 NSPR should fix it eventually . see bugzilla 101617
michael@0 2421 and 102077
michael@0 2422 */
michael@0 2423 prStatus = PR_SetFDInheritable(listen_sock, PR_FALSE);
michael@0 2424 if (prStatus != PR_SUCCESS)
michael@0 2425 errExit("PR_SetFDInheritable");
michael@0 2426 #endif
michael@0 2427 rv = SSL_InheritMPServerSIDCache(envString);
michael@0 2428 if (rv != SECSuccess)
michael@0 2429 errExit("SSL_InheritMPServerSIDCache");
michael@0 2430 hasSidCache = PR_TRUE;
michael@0 2431 } else if (maxProcs > 1) {
michael@0 2432 /* we're going to be the parent in a multi-process server. */
michael@0 2433 listen_sock = getBoundListenSocket(port);
michael@0 2434 rv = SSL_ConfigMPServerSIDCache(NumSidCacheEntries, 0, 0, tmp);
michael@0 2435 if (rv != SECSuccess)
michael@0 2436 errExit("SSL_ConfigMPServerSIDCache");
michael@0 2437 hasSidCache = PR_TRUE;
michael@0 2438 beAGoodParent(argc, argv, maxProcs, listen_sock);
michael@0 2439 exit(99); /* should never get here */
michael@0 2440 } else {
michael@0 2441 /* we're an ordinary single process server. */
michael@0 2442 listen_sock = getBoundListenSocket(port);
michael@0 2443 prStatus = PR_SetFDInheritable(listen_sock, PR_FALSE);
michael@0 2444 if (prStatus != PR_SUCCESS)
michael@0 2445 errExit("PR_SetFDInheritable");
michael@0 2446 if (!NoReuse) {
michael@0 2447 rv = SSL_ConfigServerSessionIDCache(NumSidCacheEntries,
michael@0 2448 0, 0, tmp);
michael@0 2449 if (rv != SECSuccess)
michael@0 2450 errExit("SSL_ConfigServerSessionIDCache");
michael@0 2451 hasSidCache = PR_TRUE;
michael@0 2452 }
michael@0 2453 }
michael@0 2454
michael@0 2455 lm = PR_NewLogModule("TestCase");
michael@0 2456
michael@0 2457 if (fileName)
michael@0 2458 readBigFile(fileName);
michael@0 2459
michael@0 2460 /* set our password function */
michael@0 2461 PK11_SetPasswordFunc(SECU_GetModulePassword);
michael@0 2462
michael@0 2463 /* Call the NSS initialization routines */
michael@0 2464 rv = NSS_Initialize(dir, certPrefix, certPrefix, SECMOD_DB, NSS_INIT_READONLY);
michael@0 2465 if (rv != SECSuccess) {
michael@0 2466 fputs("NSS_Init failed.\n", stderr);
michael@0 2467 exit(8);
michael@0 2468 }
michael@0 2469
michael@0 2470 /* set the policy bits true for all the cipher suites. */
michael@0 2471 if (useExportPolicy) {
michael@0 2472 NSS_SetExportPolicy();
michael@0 2473 if (disableStepDown) {
michael@0 2474 fputs("selfserv: -x and -E options may not be used together\n",
michael@0 2475 stderr);
michael@0 2476 exit(98);
michael@0 2477 }
michael@0 2478 } else {
michael@0 2479 NSS_SetDomesticPolicy();
michael@0 2480 if (disableStepDown) {
michael@0 2481 rv = disableExportSSLCiphers();
michael@0 2482 if (rv != SECSuccess) {
michael@0 2483 errExit("error disabling export ciphersuites ");
michael@0 2484 }
michael@0 2485 }
michael@0 2486 }
michael@0 2487
michael@0 2488 /* all the SSL2 and SSL3 cipher suites are enabled by default. */
michael@0 2489 if (cipherString) {
michael@0 2490 char *cstringSaved = cipherString;
michael@0 2491 int ndx;
michael@0 2492
michael@0 2493 /* disable all the ciphers, then enable the ones we want. */
michael@0 2494 disableAllSSLCiphers();
michael@0 2495
michael@0 2496 while (0 != (ndx = *cipherString++)) {
michael@0 2497 int cipher;
michael@0 2498
michael@0 2499 if (ndx == ':') {
michael@0 2500 int ctmp;
michael@0 2501
michael@0 2502 cipher = 0;
michael@0 2503 HEXCHAR_TO_INT(*cipherString, ctmp)
michael@0 2504 cipher |= (ctmp << 12);
michael@0 2505 cipherString++;
michael@0 2506 HEXCHAR_TO_INT(*cipherString, ctmp)
michael@0 2507 cipher |= (ctmp << 8);
michael@0 2508 cipherString++;
michael@0 2509 HEXCHAR_TO_INT(*cipherString, ctmp)
michael@0 2510 cipher |= (ctmp << 4);
michael@0 2511 cipherString++;
michael@0 2512 HEXCHAR_TO_INT(*cipherString, ctmp)
michael@0 2513 cipher |= ctmp;
michael@0 2514 cipherString++;
michael@0 2515 } else {
michael@0 2516 const int *cptr;
michael@0 2517
michael@0 2518 if (! isalpha(ndx)) {
michael@0 2519 fprintf(stderr,
michael@0 2520 "Non-alphabetic char in cipher string (-c arg).\n");
michael@0 2521 exit(9);
michael@0 2522 }
michael@0 2523 cptr = islower(ndx) ? ssl3CipherSuites : ssl2CipherSuites;
michael@0 2524 for (ndx &= 0x1f; (cipher = *cptr++) != 0 && --ndx > 0; )
michael@0 2525 /* do nothing */;
michael@0 2526 }
michael@0 2527 if (cipher > 0) {
michael@0 2528 SECStatus status;
michael@0 2529 status = SSL_CipherPrefSetDefault(cipher, SSL_ALLOWED);
michael@0 2530 if (status != SECSuccess)
michael@0 2531 SECU_PrintError(progName, "SSL_CipherPrefSet()");
michael@0 2532 } else {
michael@0 2533 fprintf(stderr,
michael@0 2534 "Invalid cipher specification (-c arg).\n");
michael@0 2535 exit(9);
michael@0 2536 }
michael@0 2537 }
michael@0 2538 PORT_Free(cstringSaved);
michael@0 2539 }
michael@0 2540
michael@0 2541 if (testbypass) {
michael@0 2542 const PRUint16 *cipherSuites = SSL_ImplementedCiphers;
michael@0 2543 int i = SSL_NumImplementedCiphers;
michael@0 2544 PRBool enabled;
michael@0 2545
michael@0 2546 for (i=0; i < SSL_NumImplementedCiphers; i++, cipherSuites++) {
michael@0 2547 if (SSL_CipherPrefGetDefault(*cipherSuites, &enabled) == SECSuccess
michael@0 2548 && enabled)
michael@0 2549 savecipher(*cipherSuites);
michael@0 2550 }
michael@0 2551 protos = 0;
michael@0 2552 if (enabledVersions.min <= SSL_LIBRARY_VERSION_3_0 &&
michael@0 2553 enabledVersions.max >= SSL_LIBRARY_VERSION_3_0) {
michael@0 2554 protos |= SSL_CBP_SSL3;
michael@0 2555 }
michael@0 2556 if (enabledVersions.min <= SSL_LIBRARY_VERSION_TLS_1_0 &&
michael@0 2557 enabledVersions.max >= SSL_LIBRARY_VERSION_TLS_1_0) {
michael@0 2558 protos |= SSL_CBP_TLS1_0;
michael@0 2559 }
michael@0 2560 /* TLS 1.1 has the same SSL Bypass mode requirements as TLS 1.0 */
michael@0 2561 if (enabledVersions.min <= SSL_LIBRARY_VERSION_TLS_1_1 &&
michael@0 2562 enabledVersions.max >= SSL_LIBRARY_VERSION_TLS_1_1) {
michael@0 2563 protos |= SSL_CBP_TLS1_0;
michael@0 2564 }
michael@0 2565 }
michael@0 2566
michael@0 2567 certStatusArena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
michael@0 2568 if (!certStatusArena)
michael@0 2569 errExit("cannot allocate certStatusArena");
michael@0 2570
michael@0 2571 if (nickName) {
michael@0 2572 cert[kt_rsa] = PK11_FindCertFromNickname(nickName, &pwdata);
michael@0 2573 if (cert[kt_rsa] == NULL) {
michael@0 2574 fprintf(stderr, "selfserv: Can't find certificate %s\n", nickName);
michael@0 2575 exit(10);
michael@0 2576 }
michael@0 2577 privKey[kt_rsa] = PK11_FindKeyByAnyCert(cert[kt_rsa], &pwdata);
michael@0 2578 if (privKey[kt_rsa] == NULL) {
michael@0 2579 fprintf(stderr, "selfserv: Can't find Private Key for cert %s\n",
michael@0 2580 nickName);
michael@0 2581 exit(11);
michael@0 2582 }
michael@0 2583 if (testbypass) {
michael@0 2584 PRBool bypassOK;
michael@0 2585 if (SSL_CanBypass(cert[kt_rsa], privKey[kt_rsa], protos, cipherlist,
michael@0 2586 nciphers, &bypassOK, &pwdata) != SECSuccess) {
michael@0 2587 SECU_PrintError(progName, "Bypass test failed %s\n", nickName);
michael@0 2588 exit(14);
michael@0 2589 }
michael@0 2590 fprintf(stderr, "selfserv: %s can%s bypass\n", nickName,
michael@0 2591 bypassOK ? "" : "not");
michael@0 2592 }
michael@0 2593 setupCertStatus(certStatusArena, ocspStaplingMode, cert[kt_rsa], kt_rsa,
michael@0 2594 &pwdata);
michael@0 2595 }
michael@0 2596 #ifndef NSS_DISABLE_ECC
michael@0 2597 if (ecNickName) {
michael@0 2598 cert[kt_ecdh] = PK11_FindCertFromNickname(ecNickName, &pwdata);
michael@0 2599 if (cert[kt_ecdh] == NULL) {
michael@0 2600 fprintf(stderr, "selfserv: Can't find certificate %s\n",
michael@0 2601 ecNickName);
michael@0 2602 exit(13);
michael@0 2603 }
michael@0 2604 privKey[kt_ecdh] = PK11_FindKeyByAnyCert(cert[kt_ecdh], &pwdata);
michael@0 2605 if (privKey[kt_ecdh] == NULL) {
michael@0 2606 fprintf(stderr, "selfserv: Can't find Private Key for cert %s\n",
michael@0 2607 ecNickName);
michael@0 2608 exit(11);
michael@0 2609 }
michael@0 2610 if (testbypass) {
michael@0 2611 PRBool bypassOK;
michael@0 2612 if (SSL_CanBypass(cert[kt_ecdh], privKey[kt_ecdh], protos, cipherlist,
michael@0 2613 nciphers, &bypassOK, &pwdata) != SECSuccess) {
michael@0 2614 SECU_PrintError(progName, "Bypass test failed %s\n", ecNickName);
michael@0 2615 exit(15);
michael@0 2616 }
michael@0 2617 fprintf(stderr, "selfserv: %s can%s bypass\n", ecNickName,
michael@0 2618 bypassOK ? "" : "not");
michael@0 2619 }
michael@0 2620 setupCertStatus(certStatusArena, ocspStaplingMode, cert[kt_ecdh], kt_ecdh,
michael@0 2621 &pwdata);
michael@0 2622 }
michael@0 2623 #endif /* NSS_DISABLE_ECC */
michael@0 2624
michael@0 2625 if (testbypass)
michael@0 2626 goto cleanup;
michael@0 2627
michael@0 2628 /* allocate the array of thread slots, and launch the worker threads. */
michael@0 2629 rv = launch_threads(&jobLoop, 0, 0, requestCert, useLocalThreads);
michael@0 2630
michael@0 2631 if (rv == SECSuccess && logStats) {
michael@0 2632 loggerThread = PR_CreateThread(PR_SYSTEM_THREAD,
michael@0 2633 logger, NULL, PR_PRIORITY_NORMAL,
michael@0 2634 useLocalThreads ? PR_LOCAL_THREAD:PR_GLOBAL_THREAD,
michael@0 2635 PR_JOINABLE_THREAD, 0);
michael@0 2636 if (loggerThread == NULL) {
michael@0 2637 fprintf(stderr, "selfserv: Failed to launch logger thread!\n");
michael@0 2638 rv = SECFailure;
michael@0 2639 }
michael@0 2640 }
michael@0 2641
michael@0 2642 if (rv == SECSuccess) {
michael@0 2643 server_main(listen_sock, requestCert, privKey, cert,
michael@0 2644 expectedHostNameVal);
michael@0 2645 }
michael@0 2646
michael@0 2647 VLOG(("selfserv: server_thread: exiting"));
michael@0 2648
michael@0 2649 cleanup:
michael@0 2650 printSSLStatistics();
michael@0 2651 ssl3stats = SSL_GetStatistics();
michael@0 2652 if (ssl3stats->hch_sid_ticket_parse_failures != 0) {
michael@0 2653 fprintf(stderr, "selfserv: Experienced ticket parse failure(s)\n");
michael@0 2654 exit(1);
michael@0 2655 }
michael@0 2656 if (failedToNegotiateName) {
michael@0 2657 fprintf(stderr, "selfserv: Failed properly negotiate server name\n");
michael@0 2658 exit(1);
michael@0 2659 }
michael@0 2660
michael@0 2661 {
michael@0 2662 int i;
michael@0 2663 for (i=0; i<kt_kea_size; i++) {
michael@0 2664 if (cert[i]) {
michael@0 2665 CERT_DestroyCertificate(cert[i]);
michael@0 2666 }
michael@0 2667 if (privKey[i]) {
michael@0 2668 SECKEY_DestroyPrivateKey(privKey[i]);
michael@0 2669 }
michael@0 2670 }
michael@0 2671 for (i = 0;virtServerNameArray[i];i++) {
michael@0 2672 PORT_Free(virtServerNameArray[i]);
michael@0 2673 }
michael@0 2674 }
michael@0 2675
michael@0 2676 if (debugCache) {
michael@0 2677 nss_DumpCertificateCacheInfo();
michael@0 2678 }
michael@0 2679 if (nickName) {
michael@0 2680 PORT_Free(nickName);
michael@0 2681 }
michael@0 2682 if (expectedHostNameVal) {
michael@0 2683 PORT_Free(expectedHostNameVal);
michael@0 2684 }
michael@0 2685 if (passwd) {
michael@0 2686 PORT_Free(passwd);
michael@0 2687 }
michael@0 2688 if (pwfile) {
michael@0 2689 PORT_Free(pwfile);
michael@0 2690 }
michael@0 2691 if (certPrefix && certPrefix != emptyString) {
michael@0 2692 PORT_Free(certPrefix);
michael@0 2693 }
michael@0 2694 #ifndef NSS_DISABLE_ECC
michael@0 2695 if (ecNickName) {
michael@0 2696 PORT_Free(ecNickName);
michael@0 2697 }
michael@0 2698 #endif
michael@0 2699
michael@0 2700 if (hasSidCache) {
michael@0 2701 SSL_ShutdownServerSessionIDCache();
michael@0 2702 }
michael@0 2703 if (certStatusArena) {
michael@0 2704 PORT_FreeArena(certStatusArena, PR_FALSE);
michael@0 2705 }
michael@0 2706 if (NSS_Shutdown() != SECSuccess) {
michael@0 2707 SECU_PrintError(progName, "NSS_Shutdown");
michael@0 2708 if (loggerThread) {
michael@0 2709 PR_JoinThread(loggerThread);
michael@0 2710 }
michael@0 2711 PR_Cleanup();
michael@0 2712 exit(1);
michael@0 2713 }
michael@0 2714 PR_Cleanup();
michael@0 2715 printf("selfserv: normal termination\n");
michael@0 2716 return 0;
michael@0 2717 }

mercurial