Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
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/. */
5 /****************************************************************************
6 * SSL client program that tests a server for proper operation of SSL2, *
7 * SSL3, and TLS. Test propder certificate installation. *
8 * *
9 * This code was modified from the SSLSample code also kept in the NSS *
10 * directory. *
11 ****************************************************************************/
13 #include <stdio.h>
14 #include <string.h>
16 #if defined(XP_UNIX)
17 #include <unistd.h>
18 #endif
20 #include "prerror.h"
22 #include "pk11func.h"
23 #include "secmod.h"
24 #include "secitem.h"
27 #include <stdlib.h>
28 #include <errno.h>
29 #include <fcntl.h>
30 #include <stdarg.h>
32 #include "nspr.h"
33 #include "plgetopt.h"
34 #include "prio.h"
35 #include "prnetdb.h"
36 #include "nss.h"
37 #include "secutil.h"
38 #include "ocsp.h"
40 #include "vfyserv.h"
42 #define RD_BUF_SIZE (60 * 1024)
44 extern int ssl2CipherSuites[];
45 extern int ssl3CipherSuites[];
47 GlobalThreadMgr threadMGR;
48 char *certNickname = NULL;
49 char *hostName = NULL;
50 secuPWData pwdata = { PW_NONE, 0 };
51 unsigned short port = 0;
52 PRBool dumpChain;
54 static void
55 Usage(const char *progName)
56 {
57 PRFileDesc *pr_stderr;
59 pr_stderr = PR_STDERR;
61 PR_fprintf(pr_stderr, "Usage:\n"
62 " %s [-c ] [-o] [-p port] [-d dbdir] [-w password] [-f pwfile]\n"
63 " \t\t[-C cipher(s)] [-l <url> -t <nickname> ] hostname",
64 progName);
65 PR_fprintf (pr_stderr, "\nWhere:\n");
66 PR_fprintf (pr_stderr,
67 " %-13s dump server cert chain into files\n",
68 "-c");
69 PR_fprintf (pr_stderr,
70 " %-13s perform server cert OCSP check\n",
71 "-o");
72 PR_fprintf (pr_stderr,
73 " %-13s server port to be used\n",
74 "-p");
75 PR_fprintf (pr_stderr,
76 " %-13s use security databases in \"dbdir\"\n",
77 "-d dbdir");
78 PR_fprintf (pr_stderr,
79 " %-13s key database password\n",
80 "-w password");
81 PR_fprintf (pr_stderr,
82 " %-13s token password file\n",
83 "-f pwfile");
84 PR_fprintf (pr_stderr,
85 " %-13s communication cipher list\n",
86 "-C cipher(s)");
87 PR_fprintf (pr_stderr,
88 " %-13s OCSP responder location. This location is used to\n"
89 " %-13s check status of a server certificate. If not \n"
90 " %-13s specified, location will be taken from the AIA\n"
91 " %-13s server certificate extension.\n",
92 "-l url", "", "", "");
93 PR_fprintf (pr_stderr,
94 " %-13s OCSP Trusted Responder Cert nickname\n\n",
95 "-t nickname");
97 exit(1);
98 }
100 PRFileDesc *
101 setupSSLSocket(PRNetAddr *addr)
102 {
103 PRFileDesc *tcpSocket;
104 PRFileDesc *sslSocket;
105 PRSocketOptionData socketOption;
106 PRStatus prStatus;
107 SECStatus secStatus;
110 tcpSocket = PR_NewTCPSocket();
111 if (tcpSocket == NULL) {
112 errWarn("PR_NewTCPSocket");
113 }
115 /* Make the socket blocking. */
116 socketOption.option = PR_SockOpt_Nonblocking;
117 socketOption.value.non_blocking = PR_FALSE;
119 prStatus = PR_SetSocketOption(tcpSocket, &socketOption);
120 if (prStatus != PR_SUCCESS) {
121 errWarn("PR_SetSocketOption");
122 goto loser;
123 }
126 /* Import the socket into the SSL layer. */
127 sslSocket = SSL_ImportFD(NULL, tcpSocket);
128 if (!sslSocket) {
129 errWarn("SSL_ImportFD");
130 goto loser;
131 }
133 /* Set configuration options. */
134 secStatus = SSL_OptionSet(sslSocket, SSL_SECURITY, PR_TRUE);
135 if (secStatus != SECSuccess) {
136 errWarn("SSL_OptionSet:SSL_SECURITY");
137 goto loser;
138 }
140 secStatus = SSL_OptionSet(sslSocket, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE);
141 if (secStatus != SECSuccess) {
142 errWarn("SSL_OptionSet:SSL_HANDSHAKE_AS_CLIENT");
143 goto loser;
144 }
146 /* Set SSL callback routines. */
147 secStatus = SSL_GetClientAuthDataHook(sslSocket,
148 (SSLGetClientAuthData)myGetClientAuthData,
149 (void *)certNickname);
150 if (secStatus != SECSuccess) {
151 errWarn("SSL_GetClientAuthDataHook");
152 goto loser;
153 }
155 secStatus = SSL_AuthCertificateHook(sslSocket,
156 (SSLAuthCertificate)myAuthCertificate,
157 (void *)CERT_GetDefaultCertDB());
158 if (secStatus != SECSuccess) {
159 errWarn("SSL_AuthCertificateHook");
160 goto loser;
161 }
163 secStatus = SSL_BadCertHook(sslSocket,
164 (SSLBadCertHandler)myBadCertHandler, NULL);
165 if (secStatus != SECSuccess) {
166 errWarn("SSL_BadCertHook");
167 goto loser;
168 }
170 secStatus = SSL_HandshakeCallback(sslSocket,
171 myHandshakeCallback,
172 NULL);
173 if (secStatus != SECSuccess) {
174 errWarn("SSL_HandshakeCallback");
175 goto loser;
176 }
178 return sslSocket;
180 loser:
182 PR_Close(tcpSocket);
183 return NULL;
184 }
187 const char requestString[] = {"GET /testfile HTTP/1.0\r\n\r\n" };
189 SECStatus
190 handle_connection(PRFileDesc *sslSocket, int connection)
191 {
192 int countRead = 0;
193 PRInt32 numBytes;
194 char *readBuffer;
196 readBuffer = PORT_Alloc(RD_BUF_SIZE);
197 if (!readBuffer) {
198 exitErr("PORT_Alloc");
199 }
201 /* compose the http request here. */
203 numBytes = PR_Write(sslSocket, requestString, strlen(requestString));
204 if (numBytes <= 0) {
205 errWarn("PR_Write");
206 PR_Free(readBuffer);
207 readBuffer = NULL;
208 return SECFailure;
209 }
211 /* read until EOF */
212 while (PR_TRUE) {
213 numBytes = PR_Read(sslSocket, readBuffer, RD_BUF_SIZE);
214 if (numBytes == 0) {
215 break; /* EOF */
216 }
217 if (numBytes < 0) {
218 errWarn("PR_Read");
219 break;
220 }
221 countRead += numBytes;
222 }
224 printSecurityInfo(stderr, sslSocket);
226 PR_Free(readBuffer);
227 readBuffer = NULL;
229 /* Caller closes the socket. */
231 fprintf(stderr,
232 "***** Connection %d read %d bytes total.\n",
233 connection, countRead);
235 return SECSuccess; /* success */
236 }
238 #define BYTE(n,i) (((i)>>((n)*8))&0xff)
240 /* one copy of this function is launched in a separate thread for each
241 ** connection to be made.
242 */
243 SECStatus
244 do_connects(void *a, int connection)
245 {
246 PRNetAddr *addr = (PRNetAddr *)a;
247 PRFileDesc *sslSocket;
248 PRHostEnt hostEntry;
249 char buffer[PR_NETDB_BUF_SIZE];
250 PRStatus prStatus;
251 PRIntn hostenum;
252 PRInt32 ip;
253 SECStatus secStatus;
255 /* Set up SSL secure socket. */
256 sslSocket = setupSSLSocket(addr);
257 if (sslSocket == NULL) {
258 errWarn("setupSSLSocket");
259 return SECFailure;
260 }
262 secStatus = SSL_SetPKCS11PinArg(sslSocket, &pwdata);
263 if (secStatus != SECSuccess) {
264 errWarn("SSL_SetPKCS11PinArg");
265 return secStatus;
266 }
268 secStatus = SSL_SetURL(sslSocket, hostName);
269 if (secStatus != SECSuccess) {
270 errWarn("SSL_SetURL");
271 return secStatus;
272 }
274 /* Prepare and setup network connection. */
275 prStatus = PR_GetHostByName(hostName, buffer, sizeof(buffer), &hostEntry);
276 if (prStatus != PR_SUCCESS) {
277 errWarn("PR_GetHostByName");
278 return SECFailure;
279 }
281 hostenum = PR_EnumerateHostEnt(0, &hostEntry, port, addr);
282 if (hostenum == -1) {
283 errWarn("PR_EnumerateHostEnt");
284 return SECFailure;
285 }
287 ip = PR_ntohl(addr->inet.ip);
288 fprintf(stderr,
289 "Connecting to host %s (addr %d.%d.%d.%d) on port %d\n",
290 hostName, BYTE(3,ip), BYTE(2,ip), BYTE(1,ip),
291 BYTE(0,ip), PR_ntohs(addr->inet.port));
293 prStatus = PR_Connect(sslSocket, addr, PR_INTERVAL_NO_TIMEOUT);
294 if (prStatus != PR_SUCCESS) {
295 errWarn("PR_Connect");
296 return SECFailure;
297 }
299 /* Established SSL connection, ready to send data. */
300 #if 0
301 secStatus = SSL_ForceHandshake(sslSocket);
302 if (secStatus != SECSuccess) {
303 errWarn("SSL_ForceHandshake");
304 return secStatus;
305 }
306 #endif
308 secStatus = SSL_ResetHandshake(sslSocket, /* asServer */ PR_FALSE);
309 if (secStatus != SECSuccess) {
310 errWarn("SSL_ResetHandshake");
311 prStatus = PR_Close(sslSocket);
312 if (prStatus != PR_SUCCESS) {
313 errWarn("PR_Close");
314 }
315 return secStatus;
316 }
318 secStatus = handle_connection(sslSocket, connection);
319 if (secStatus != SECSuccess) {
320 /* error already printed out in handle_connection */
321 /* errWarn("handle_connection"); */
322 prStatus = PR_Close(sslSocket);
323 if (prStatus != PR_SUCCESS) {
324 errWarn("PR_Close");
325 }
326 return secStatus;
327 }
329 PR_Close(sslSocket);
330 return SECSuccess;
331 }
333 void
334 client_main(unsigned short port,
335 int connections,
336 const char * hostName)
337 {
338 int i;
339 SECStatus secStatus;
340 PRStatus prStatus;
341 PRInt32 rv;
342 PRNetAddr addr;
343 PRHostEnt hostEntry;
344 char buffer[PR_NETDB_BUF_SIZE];
346 /* Setup network connection. */
347 prStatus = PR_GetHostByName(hostName, buffer, sizeof(buffer), &hostEntry);
348 if (prStatus != PR_SUCCESS) {
349 exitErr("PR_GetHostByName");
350 }
352 rv = PR_EnumerateHostEnt(0, &hostEntry, port, &addr);
353 if (rv < 0) {
354 exitErr("PR_EnumerateHostEnt");
355 }
357 secStatus = launch_thread(&threadMGR, do_connects, &addr, 1);
358 if (secStatus != SECSuccess) {
359 exitErr("launch_thread");
360 }
362 if (connections > 1) {
363 /* wait for the first connection to terminate, then launch the rest. */
364 reap_threads(&threadMGR);
365 /* Start up the connections */
366 for (i = 2; i <= connections; ++i) {
367 secStatus = launch_thread(&threadMGR, do_connects, &addr, i);
368 if (secStatus != SECSuccess) {
369 errWarn("launch_thread");
370 }
371 }
372 }
374 reap_threads(&threadMGR);
375 destroy_thread_data(&threadMGR);
376 }
378 #define HEXCHAR_TO_INT(c, i) \
379 if (((c) >= '0') && ((c) <= '9')) { \
380 i = (c) - '0'; \
381 } else if (((c) >= 'a') && ((c) <= 'f')) { \
382 i = (c) - 'a' + 10; \
383 } else if (((c) >= 'A') && ((c) <= 'F')) { \
384 i = (c) - 'A' + 10; \
385 } else { \
386 Usage(progName); \
387 }
389 int
390 main(int argc, char **argv)
391 {
392 char * certDir = NULL;
393 char * progName = NULL;
394 int connections = 1;
395 char * cipherString = NULL;
396 char * respUrl = NULL;
397 char * respCertName = NULL;
398 SECStatus secStatus;
399 PLOptState * optstate;
400 PLOptStatus status;
401 PRBool doOcspCheck = PR_FALSE;
403 /* Call the NSPR initialization routines */
404 PR_Init( PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
406 progName = PORT_Strdup(argv[0]);
408 hostName = NULL;
409 optstate = PL_CreateOptState(argc, argv, "C:cd:f:l:n:p:ot:w:");
410 while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) {
411 switch(optstate->option) {
412 case 'C' : cipherString = PL_strdup(optstate->value); break;
413 case 'c' : dumpChain = PR_TRUE; break;
414 case 'd' : certDir = PL_strdup(optstate->value); break;
415 case 'l' : respUrl = PL_strdup(optstate->value); break;
416 case 'p' : port = PORT_Atoi(optstate->value); break;
417 case 'o' : doOcspCheck = PR_TRUE; break;
418 case 't' : respCertName = PL_strdup(optstate->value); break;
419 case 'w':
420 pwdata.source = PW_PLAINTEXT;
421 pwdata.data = PORT_Strdup(optstate->value);
422 break;
424 case 'f':
425 pwdata.source = PW_FROMFILE;
426 pwdata.data = PORT_Strdup(optstate->value);
427 break;
428 case '\0': hostName = PL_strdup(optstate->value); break;
429 default : Usage(progName);
430 }
431 }
433 if (port == 0) {
434 port = 443;
435 }
437 if (port == 0 || hostName == NULL)
438 Usage(progName);
440 if (doOcspCheck &&
441 ((respCertName != NULL && respUrl == NULL) ||
442 (respUrl != NULL && respCertName == NULL))) {
443 SECU_PrintError (progName, "options -l <url> and -t "
444 "<responder> must be used together");
445 Usage(progName);
446 }
448 PK11_SetPasswordFunc(SECU_GetModulePassword);
450 /* Initialize the NSS libraries. */
451 if (certDir) {
452 secStatus = NSS_Init(certDir);
453 } else {
454 secStatus = NSS_NoDB_Init(NULL);
456 /* load the builtins */
457 SECMOD_AddNewModule("Builtins",
458 DLL_PREFIX"nssckbi."DLL_SUFFIX, 0, 0);
459 }
460 if (secStatus != SECSuccess) {
461 exitErr("NSS_Init");
462 }
463 SECU_RegisterDynamicOids();
465 if (doOcspCheck == PR_TRUE) {
466 SECStatus rv;
467 CERTCertDBHandle *handle = CERT_GetDefaultCertDB();
468 if (handle == NULL) {
469 SECU_PrintError (progName, "problem getting certdb handle");
470 goto cleanup;
471 }
473 rv = CERT_EnableOCSPChecking (handle);
474 if (rv != SECSuccess) {
475 SECU_PrintError (progName, "error enabling OCSP checking");
476 goto cleanup;
477 }
479 if (respUrl != NULL) {
480 rv = CERT_SetOCSPDefaultResponder (handle, respUrl,
481 respCertName);
482 if (rv != SECSuccess) {
483 SECU_PrintError (progName,
484 "error setting default responder");
485 goto cleanup;
486 }
488 rv = CERT_EnableOCSPDefaultResponder (handle);
489 if (rv != SECSuccess) {
490 SECU_PrintError (progName,
491 "error enabling default responder");
492 goto cleanup;
493 }
494 }
495 }
497 /* All cipher suites except RSA_NULL_MD5 are enabled by
498 * Domestic Policy. */
499 NSS_SetDomesticPolicy();
500 SSL_CipherPrefSetDefault(TLS_RSA_WITH_NULL_MD5, PR_TRUE);
502 /* all the SSL2 and SSL3 cipher suites are enabled by default. */
503 if (cipherString) {
504 int ndx;
506 /* disable all the ciphers, then enable the ones we want. */
507 disableAllSSLCiphers();
509 while (0 != (ndx = *cipherString++)) {
510 int cipher;
512 if (ndx == ':') {
513 int ctmp;
515 cipher = 0;
516 HEXCHAR_TO_INT(*cipherString, ctmp)
517 cipher |= (ctmp << 12);
518 cipherString++;
519 HEXCHAR_TO_INT(*cipherString, ctmp)
520 cipher |= (ctmp << 8);
521 cipherString++;
522 HEXCHAR_TO_INT(*cipherString, ctmp)
523 cipher |= (ctmp << 4);
524 cipherString++;
525 HEXCHAR_TO_INT(*cipherString, ctmp)
526 cipher |= ctmp;
527 cipherString++;
528 } else {
529 const int *cptr;
530 if (! isalpha(ndx))
531 Usage(progName);
532 cptr = islower(ndx) ? ssl3CipherSuites : ssl2CipherSuites;
533 for (ndx &= 0x1f; (cipher = *cptr++) != 0 && --ndx > 0; )
534 /* do nothing */;
535 }
536 if (cipher > 0) {
537 SSL_CipherPrefSetDefault(cipher, PR_TRUE);
538 } else {
539 Usage(progName);
540 }
541 }
542 }
544 client_main(port, connections, hostName);
546 cleanup:
547 if (doOcspCheck) {
548 CERTCertDBHandle *handle = CERT_GetDefaultCertDB();
549 CERT_DisableOCSPDefaultResponder(handle);
550 CERT_DisableOCSPChecking (handle);
551 }
553 if (NSS_Shutdown() != SECSuccess) {
554 exit(1);
555 }
557 PR_Cleanup();
558 PORT_Free(progName);
559 return 0;
560 }