Wed, 31 Dec 2014 06:55:50 +0100
Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2
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 | } |