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