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