vfyserv.c (16751B)
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 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 * 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 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 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 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 PORT_Free(hostName); 344 exitErr("PR_GetHostByName"); 345 } 346 347 rv = PR_EnumerateHostEnt(0, &hostEntry, port, &addr); 348 if (rv < 0) { 349 PORT_Free(hostName); 350 exitErr("PR_EnumerateHostEnt"); 351 } 352 353 secStatus = launch_thread(&threadMGR, do_connects, &addr, 1); 354 if (secStatus != SECSuccess) { 355 PORT_Free(hostName); 356 exitErr("launch_thread"); 357 } 358 359 if (connections > 1) { 360 /* wait for the first connection to terminate, then launch the rest. */ 361 reap_threads(&threadMGR); 362 /* Start up the connections */ 363 for (i = 2; i <= connections; ++i) { 364 secStatus = launch_thread(&threadMGR, do_connects, &addr, i); 365 if (secStatus != SECSuccess) { 366 errWarn("launch_thread"); 367 } 368 } 369 } 370 371 reap_threads(&threadMGR); 372 destroy_thread_data(&threadMGR); 373 } 374 375 #define HEXCHAR_TO_INT(c, i) \ 376 if (((c) >= '0') && ((c) <= '9')) { \ 377 i = (c) - '0'; \ 378 } else if (((c) >= 'a') && ((c) <= 'f')) { \ 379 i = (c) - 'a' + 10; \ 380 } else if (((c) >= 'A') && ((c) <= 'F')) { \ 381 i = (c) - 'A' + 10; \ 382 } else { \ 383 Usage(progName); \ 384 } 385 386 int 387 main(int argc, char **argv) 388 { 389 char *certDir = NULL; 390 char *progName = NULL; 391 int connections = 1; 392 char *cipherString = NULL; 393 char *respUrl = NULL; 394 char *respCertName = NULL; 395 SECStatus secStatus; 396 PLOptState *optstate; 397 PLOptStatus status; 398 PRBool doOcspCheck = PR_FALSE; 399 400 /* Call the NSPR initialization routines */ 401 PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1); 402 403 progName = PORT_Strdup(argv[0]); 404 405 hostName = NULL; 406 optstate = PL_CreateOptState(argc, argv, "C:cd:f:l:n:p:ot:w:"); 407 while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) { 408 switch (optstate->option) { 409 case 'C': 410 cipherString = PL_strdup(optstate->value); 411 break; 412 case 'c': 413 dumpChain = PR_TRUE; 414 break; 415 case 'd': 416 certDir = PL_strdup(optstate->value); 417 break; 418 case 'l': 419 respUrl = PL_strdup(optstate->value); 420 break; 421 case 'p': 422 port = PORT_Atoi(optstate->value); 423 break; 424 case 'o': 425 doOcspCheck = PR_TRUE; 426 break; 427 case 't': 428 respCertName = PL_strdup(optstate->value); 429 break; 430 case 'w': 431 pwdata.source = PW_PLAINTEXT; 432 pwdata.data = PORT_Strdup(optstate->value); 433 break; 434 435 case 'f': 436 pwdata.source = PW_FROMFILE; 437 pwdata.data = PORT_Strdup(optstate->value); 438 break; 439 case '\0': 440 hostName = PL_strdup(optstate->value); 441 break; 442 default: 443 Usage(progName); 444 } 445 } 446 PL_DestroyOptState(optstate); 447 optstate = NULL; 448 449 if (port == 0) { 450 port = 443; 451 } 452 453 if (port == 0 || hostName == NULL) 454 Usage(progName); 455 456 if (doOcspCheck && 457 ((respCertName != NULL && respUrl == NULL) || 458 (respUrl != NULL && respCertName == NULL))) { 459 SECU_PrintError(progName, "options -l <url> and -t " 460 "<responder> must be used together"); 461 Usage(progName); 462 } 463 464 PK11_SetPasswordFunc(SECU_GetModulePassword); 465 466 /* Initialize the NSS libraries. */ 467 if (certDir) { 468 secStatus = NSS_Init(certDir); 469 PR_Free(certDir); 470 certDir = NULL; 471 } else { 472 secStatus = NSS_NoDB_Init(NULL); 473 474 /* load the builtins */ 475 SECMOD_AddNewModule("Builtins", 476 DLL_PREFIX "nssckbi." DLL_SUFFIX, 0, 0); 477 } 478 if (secStatus != SECSuccess) { 479 exitErr("NSS_Init"); 480 } 481 SECU_RegisterDynamicOids(); 482 483 if (doOcspCheck == PR_TRUE) { 484 SECStatus rv; 485 CERTCertDBHandle *handle = CERT_GetDefaultCertDB(); 486 if (handle == NULL) { 487 SECU_PrintError(progName, "problem getting certdb handle"); 488 goto cleanup; 489 } 490 491 rv = CERT_EnableOCSPChecking(handle); 492 if (rv != SECSuccess) { 493 SECU_PrintError(progName, "error enabling OCSP checking"); 494 goto cleanup; 495 } 496 497 if (respUrl != NULL) { 498 rv = CERT_SetOCSPDefaultResponder(handle, respUrl, 499 respCertName); 500 if (rv != SECSuccess) { 501 SECU_PrintError(progName, 502 "error setting default responder"); 503 goto cleanup; 504 } 505 506 rv = CERT_EnableOCSPDefaultResponder(handle); 507 if (rv != SECSuccess) { 508 SECU_PrintError(progName, 509 "error enabling default responder"); 510 goto cleanup; 511 } 512 } 513 } 514 515 /* All cipher suites except RSA_NULL_MD5 are enabled by 516 * Domestic Policy. */ 517 NSS_SetDomesticPolicy(); 518 SSL_CipherPrefSetDefault(TLS_RSA_WITH_NULL_MD5, PR_TRUE); 519 520 /* all the SSL2 and SSL3 cipher suites are enabled by default. */ 521 if (cipherString) { 522 int ndx; 523 524 /* disable all the ciphers, then enable the ones we want. */ 525 disableAllSSLCiphers(); 526 527 while (0 != (ndx = *cipherString++)) { 528 int cipher = 0; 529 530 if (ndx == ':') { 531 int ctmp = 0; 532 533 HEXCHAR_TO_INT(*cipherString, ctmp) 534 cipher |= (ctmp << 12); 535 cipherString++; 536 HEXCHAR_TO_INT(*cipherString, ctmp) 537 cipher |= (ctmp << 8); 538 cipherString++; 539 HEXCHAR_TO_INT(*cipherString, ctmp) 540 cipher |= (ctmp << 4); 541 cipherString++; 542 HEXCHAR_TO_INT(*cipherString, ctmp) 543 cipher |= ctmp; 544 cipherString++; 545 } else { 546 if (!isalpha((unsigned char)ndx)) 547 Usage(progName); 548 ndx = tolower((unsigned char)ndx) - 'a'; 549 if (ndx < numSSL3CipherSuites) { 550 cipher = ssl3CipherSuites[ndx]; 551 } 552 } 553 if (cipher > 0) { 554 SECStatus rv = SSL_CipherPrefSetDefault(cipher, PR_TRUE); 555 if (rv != SECSuccess) { 556 SECU_PrintError(progName, 557 "error setting cipher default preference"); 558 goto cleanup; 559 } 560 } else { 561 Usage(progName); 562 } 563 } 564 } 565 566 client_main(connections); 567 568 cleanup: 569 if (doOcspCheck) { 570 CERTCertDBHandle *handle = CERT_GetDefaultCertDB(); 571 CERT_DisableOCSPDefaultResponder(handle); 572 CERT_DisableOCSPChecking(handle); 573 } 574 575 if (NSS_Shutdown() != SECSuccess) { 576 exit(1); 577 } 578 579 PR_Cleanup(); 580 PORT_Free(progName); 581 return 0; 582 }