SSLViewer.c
1 /* 2 * Copyright (c) 2006-2013, 2015 Apple Inc. All Rights Reserved. 3 * 4 * SSL viewer tool 5 */ 6 7 #include <CoreFoundation/CoreFoundation.h> 8 #include <Security/Security.h> 9 #include <Security/SecureTransport.h> 10 #include <Security/SecureTransportPriv.h> 11 #include <Security/SecTrustPriv.h> 12 #include <Security/SecPolicyPriv.h> 13 14 #include <stdio.h> 15 #include <stdlib.h> 16 #include <string.h> 17 #include <signal.h> 18 #include <time.h> 19 #include <ctype.h> 20 21 #include "sslAppUtils.h" 22 #include "ioSock.h" 23 #include "utilities/fileIo.h" 24 #include "utilities/SecCFWrappers.h" 25 #include "utilities/SecIOFormat.h" 26 #include "SecurityTool/sharedTool/print_cert.h" 27 28 #define DEFAULT_GETMSG "GET" 29 #define DEFAULT_PATH "/" 30 #define DEFAULT_GET_SUFFIX "HTTP/1.0\r\n\r\n" 31 32 #define DEFAULT_HOST "www.amazon.com" 33 #define DEFAULT_PORT 443 34 35 static const int _maxFileStringSize = 100; 36 37 static void usageNorm(char **argv) 38 { 39 printf("Usage: %s [hostname|-] [path] [option ...]\n", argv[0]); 40 printf(" %s hostname [path] [option ...]\n", argv[0]); 41 printf("Specifying '-' for hostname, or no args, uses default of %s.\n", 42 DEFAULT_HOST); 43 printf("Optional path argument must start with leading '/'.\n"); 44 printf("Options:\n"); 45 printf(" e Allow Expired Certs\n"); 46 printf(" E Allow Expired Roots\n"); 47 printf(" r Allow any root cert\n"); 48 printf(" c Display peer certs\n"); 49 printf(" c c Display peer SecTrust\n"); 50 printf(" d Display received data\n"); 51 printf(" 2 SSLv2 only (default is TLSv1)\n"); 52 printf(" 3 SSLv3 only (default is TLSv1)\n"); 53 printf(" t TLSv1\n"); 54 printf(" %% TLSv1.1 only\n"); 55 printf(" ^ TLSv1.2 only\n"); 56 printf(" L all - TLSv1.2, TLSv1.1, TLSv1.0, SSLv3, SSLv2 (default = TLSv1.2)\n"); 57 printf(" g={prot...} Specify legal protocols; prot = any combo of" 58 " [23t]\n"); 59 printf(" k=keychain Contains cert and keys. Optional.\n"); 60 printf(" l=loopCount Perform loopCount ops (default = 1)\n"); 61 printf(" P=port Default = %d\n", DEFAULT_PORT); 62 printf(" p Pause after each loop\n"); 63 printf(" q Quiet/diagnostic mode (site names and errors only)\n"); 64 printf(" a fileName Add fileName to list of trusted roots\n"); 65 printf(" A fileName fileName is ONLY trusted root\n"); 66 printf(" x Disable Cert Verification\n"); 67 printf(" Z string ALPN setting\n"); 68 printf(" z=password Unlock client keychain with password.\n"); 69 printf(" 8 Complete cert chains (default is out cert is a root)\n"); 70 printf(" s Silent\n"); 71 printf(" V Verbose\n"); 72 printf(" h Help\n"); 73 printf(" hv More, verbose help\n"); 74 } 75 76 static void usageVerbose(char **argv) __attribute__((noreturn)); 77 static void usageVerbose(char **argv) 78 { 79 usageNorm(argv); 80 printf("Obscure Usage:\n"); 81 printf(" u kSSLProtocolUnknown only (TLSv1)\n"); 82 printf(" M Manual cert verification via " 83 "SecTrustEvaluate\n"); 84 printf(" f fileBase Write Peer Certs to fileBase*\n"); 85 printf(" o TLSv1, SSLv3 use kSSLProtocol__X__Only\n"); 86 printf(" C=cipherSuite (e=40-bit d=DES D=40-bit DES 3=3DES 4=RC4 " 87 "$=40-bit RC4\n" 88 " 2=RC2 a=AES128 A=AES256 h=DH H=Anon DH r=DHE/RSA s=DH/DSS\n"); 89 printf(" y=keychain Encryption-only cert and keys. Optional.\n"); 90 printf(" K Keep connected until server disconnects\n"); 91 printf(" n Require closure notify message in TLSv1, " 92 "SSLv3 mode (implies K)\n"); 93 printf(" R Disable resumable session support\n"); 94 printf(" b Non-blocking I/O\n"); 95 printf(" v Verify negotiated protocol equals attempted\n"); 96 printf(" m=[23t] Max protocol supported as specified; implies " 97 "v\n"); 98 printf(" T=[nrsj] Verify client cert state = " 99 "none/requested/sent/rejected\n"); 100 printf(" H allow hostname spoofing\n"); 101 printf(" F=vfyHost Verify certs with specified host name\n"); 102 printf(" G=getMsg Specify entire GET, POST, etc.\n"); 103 printf(" N Log handshake timing\n"); 104 printf(" 7 Pause only after first loop\n"); 105 exit(1); 106 } 107 108 static void usage(char **argv) __attribute__((noreturn)); 109 static void usage(char **argv) 110 { 111 usageNorm(argv); 112 exit(1); 113 } 114 115 /* 116 * Arguments to top-level sslPing() 117 */ 118 typedef struct { 119 SSLProtocol tryVersion; // only used if acceptedProts NULL 120 // uses SSLSetProtocolVersion 121 char *acceptedProts; // optional, any combo of {2,3,t} 122 // uses SSLSetProtocolVersionEnabled 123 const char *hostName; // e.g., "www.amazon.com" 124 const char *vfyHostName; // use this for cert vfy if non-NULL, 125 // else use hostName 126 unsigned short port; 127 const char *getMsg; // e.g., 128 // "GET / HTTP/1.0\r\n\r\n" 129 bool allowExpired; 130 bool allowAnyRoot; 131 bool allowExpiredRoot; 132 bool disableCertVerify; 133 bool manualCertVerify; 134 bool dumpRxData; // display server data 135 char cipherRestrict; // '2', 'd'. etc...; '\0' for 136 // no restriction 137 bool keepConnected; 138 bool requireNotify; // require closure notify 139 // in V3 mode 140 bool resumableEnable; 141 bool allowHostnameSpoof; 142 bool nonBlocking; 143 char *anchorFile; 144 bool replaceAnchors; 145 CFArrayRef clientCerts; // optional 146 bool quiet; // minimal stdout 147 bool silent; // no stdout 148 bool verbose; 149 SSLProtocol negVersion; // RETURNED 150 SSLCipherSuite negCipher; // RETURNED 151 CFArrayRef peerCerts; // mallocd & RETURNED 152 SecTrustRef peerTrust; // RETURNED 153 SSLClientCertificateState certState; // RETURNED 154 char *password; // optional to open clientCerts 155 char **argv; 156 Boolean sessionWasResumed; 157 unsigned char sessionID[MAX_SESSION_ID_LENGTH]; 158 size_t sessionIDLength; 159 CFAbsoluteTime handshakeTimeOp; // time for this op 160 CFAbsoluteTime handshakeTimeFirst; // time for FIRST op, not averaged 161 CFAbsoluteTime handshakeTimeTotal; // time for all ops except first 162 unsigned numHandshakes; 163 164 CFMutableArrayRef alpnNames; 165 CFMutableArrayRef policies; 166 167 } sslPingArgs; 168 169 static void 170 sigpipe(int sig) 171 { 172 fflush(stdin); 173 printf("***SIGPIPE***\n"); 174 } 175 176 /* 177 * Manually evaluate session's SecTrustRef. 178 */ 179 180 static OSStatus sslEvaluateTrust( 181 SSLContextRef ctx, 182 sslPingArgs *pargs, 183 CFArrayRef *peerCerts) // fetched and retained 184 { 185 OSStatus ortn = errSecSuccess; 186 SecTrustRef secTrust = NULL; 187 188 ortn = SSLGetPeerSecTrust(ctx, &secTrust); 189 if(ortn) { 190 printf("\n***Error obtaining peer SecTrustRef: %s\n", 191 sslGetSSLErrString(ortn)); 192 return ortn; 193 } 194 if(secTrust == NULL) { 195 /* this is the normal case for resumed sessions, in which 196 * no cert evaluation is performed */ 197 if(!pargs->silent) { 198 printf("...No SecTrust available - this is a resumed session, right?\n"); 199 } 200 return errSecSuccess; 201 } 202 203 204 if (pargs->policies) { 205 SecTrustSetPolicies(secTrust, pargs->policies); 206 } 207 208 SecTrustResultType secTrustResult; 209 ortn = SecTrustGetTrustResult(secTrust, &secTrustResult); // implicitly does trust evaluate 210 if(ortn) { 211 printf("\n***Error on SecTrustEvaluate: %d\n", (int)ortn); 212 return ortn; 213 } 214 if(pargs->verbose) { 215 const char *res = NULL; 216 switch(secTrustResult) { 217 case kSecTrustResultInvalid: 218 res = "kSecTrustResultInvalid"; break; 219 case kSecTrustResultProceed: 220 res = "kSecTrustResultProceed"; break; 221 case kSecTrustResultDeny: 222 res = "kSecTrustResultDeny"; break; 223 case kSecTrustResultUnspecified: 224 res = "kSecTrustResultUnspecified"; break; 225 case kSecTrustResultRecoverableTrustFailure: 226 res = "kSecTrustResultRecoverableTrustFailure"; break; 227 case kSecTrustResultFatalTrustFailure: 228 res = "kSecTrustResultFatalTrustFailure"; break; 229 case kSecTrustResultOtherError: 230 res = "kSecTrustResultOtherError"; break; 231 default: 232 res = "UNKNOWN"; break; 233 } 234 printf("\nSecTrustEvaluate(): secTrustResult %s\n", res); 235 } 236 237 switch(secTrustResult) { 238 case kSecTrustResultUnspecified: 239 /* cert chain valid, no special UserTrust assignments */ 240 case kSecTrustResultProceed: 241 /* cert chain valid AND user explicitly trusts this */ 242 break; 243 default: 244 printf("\n***SecTrustEvaluate reported secTrustResult %d\n", 245 (int)secTrustResult); 246 ortn = errSSLXCertChainInvalid; 247 break; 248 } 249 250 *peerCerts = NULL; 251 252 #ifdef USE_CDSA_CRYPTO 253 /* one more thing - get peer certs in the form of an evidence chain */ 254 CSSM_TP_APPLE_EVIDENCE_INFO *dummyEv; 255 OSStatus thisRtn = SecTrustGetResult(secTrust, &secTrustResult, 256 peerCerts, &dummyEv); 257 if(thisRtn) { 258 printSslErrStr("SecTrustGetResult", thisRtn); 259 } 260 else { 261 /* workaround for the fact that SSLGetPeerCertificates() 262 * leaves a retain count on each element in the returned array, 263 * requiring us to do a release on each cert. 264 */ 265 CFIndex numCerts = CFArrayGetCount(*peerCerts); 266 for(CFIndex dex=0; dex<numCerts; dex++) { 267 CFRetain(CFArrayGetValueAtIndex(*peerCerts, dex)); 268 } 269 } 270 #endif 271 return ortn; 272 } 273 274 /* print reply received from server, safely */ 275 static void dumpAscii( 276 uint8_t *rcvBuf, 277 size_t len) 278 { 279 char *cp = (char *)rcvBuf; 280 uint32_t i; 281 char c; 282 283 for(i=0; i<len; i++) { 284 c = *cp++; 285 if(c == '\0') { 286 break; 287 } 288 switch(c) { 289 case '\n': 290 printf("\\n"); 291 break; 292 case '\r': 293 printf("\\r"); 294 break; 295 default: 296 if(isprint(c) && (c != '\n')) { 297 printf("%c", c); 298 } 299 else { 300 printf("<%02X>", ((unsigned)c) & 0xff); 301 } 302 break; 303 } 304 305 } 306 printf("\n"); 307 } 308 309 static void 310 alpnFunc(SSLContextRef ctx, 311 void *info, 312 const void *alpnData, 313 size_t alpnDataLength) 314 { 315 printf("[selected ALPN]"); 316 } 317 318 #pragma clang diagnostic push 319 #pragma clang diagnostic ignored "-Wdeprecated-declarations" 320 321 /* 322 * Perform one SSL diagnostic session. Returns nonzero on error. Normally no 323 * output to stdout except initial "connecting to" message, unless there 324 * is a really screwed up error (i.e., something not directly related 325 * to the SSL connection). 326 */ 327 #define RCV_BUF_SIZE 256 328 329 static OSStatus sslPing( 330 sslPingArgs *pargs) 331 { 332 PeerSpec peerId; 333 otSocket sock = 0; 334 OSStatus ortn; 335 SSLContextRef ctx = NULL; 336 size_t length; 337 size_t actLen; 338 uint8_t rcvBuf[RCV_BUF_SIZE]; 339 CFAbsoluteTime startHandshake; 340 CFAbsoluteTime endHandshake; 341 342 pargs->negVersion = kSSLProtocolUnknown; 343 pargs->negCipher = SSL_NULL_WITH_NULL_NULL; 344 pargs->peerCerts = NULL; 345 346 /* first make sure requested server is there */ 347 ortn = MakeServerConnection(pargs->hostName, pargs->port, pargs->nonBlocking, 348 &sock, &peerId); 349 if(ortn) { 350 printf("MakeServerConnection returned %d; aborting\n", (int)ortn); 351 return ortn; 352 } 353 if(pargs->verbose) { 354 printf("...connected to server; starting SecureTransport\n"); 355 } 356 357 /* 358 * Set up a SecureTransport session. 359 * First the standard calls. 360 */ 361 ctx = SSLCreateContext(kCFAllocatorDefault, kSSLClientSide, kSSLStreamType); 362 if(ctx == NULL) { 363 printf("SSLCreateContext\n"); 364 goto cleanup; 365 } 366 ortn = SSLSetIOFuncs(ctx, SocketRead, SocketWrite); 367 if(ortn) { 368 printSslErrStr("SSLSetIOFuncs", ortn); 369 goto cleanup; 370 } 371 ortn = SSLSetConnection(ctx, (SSLConnectionRef)(intptr_t)sock); 372 if(ortn) { 373 printSslErrStr("SSLSetConnection", ortn); 374 goto cleanup; 375 } 376 SSLConnectionRef getConn; 377 ortn = SSLGetConnection(ctx, &getConn); 378 if(ortn) { 379 printSslErrStr("SSLGetConnection", ortn); 380 goto cleanup; 381 } 382 if(getConn != (SSLConnectionRef)(intptr_t)sock) { 383 printf("***SSLGetConnection error\n"); 384 ortn = errSecParam; 385 goto cleanup; 386 } 387 if(!pargs->allowHostnameSpoof) { 388 /* if this isn't set, it isn't checked by AppleX509TP */ 389 const char *vfyHost = pargs->hostName; 390 if(pargs->vfyHostName) { 391 /* generally means we're expecting an error */ 392 vfyHost = pargs->vfyHostName; 393 } 394 ortn = SSLSetPeerDomainName(ctx, vfyHost, strlen(vfyHost)); 395 if(ortn) { 396 printSslErrStr("SSLSetPeerDomainName", ortn); 397 goto cleanup; 398 } 399 } 400 401 /* 402 * SecureTransport options. 403 */ 404 if(pargs->acceptedProts) { 405 if(ortn) { 406 printSslErrStr("SSLSetProtocolVersionEnabled(all off)", ortn); 407 goto cleanup; 408 } 409 for(const char *cp = pargs->acceptedProts; *cp; cp++) { 410 switch(*cp) { 411 case '2': 412 ortn = SSLSetProtocolVersionMax(ctx, kSSLProtocol2); 413 break; 414 case '3': 415 ortn = SSLSetProtocolVersionMax(ctx, kSSLProtocol3); 416 break; 417 case 't': 418 ortn = SSLSetProtocolVersionMax(ctx, kTLSProtocol12); 419 break; 420 default: 421 usage(pargs->argv); 422 } 423 if(ortn) { 424 printSslErrStr("SSLSetProtocolVersionMax", ortn); 425 goto cleanup; 426 } 427 } 428 } else { 429 ortn = SSLSetProtocolVersionMax(ctx, pargs->tryVersion); 430 if(ortn) { 431 printSslErrStr("SSLSetProtocolVersionMax", ortn); 432 goto cleanup; 433 } 434 } 435 436 if(pargs->resumableEnable) { 437 const void *rtnId = NULL; 438 size_t rtnIdLen = 0; 439 440 ortn = SSLSetPeerID(ctx, &peerId, sizeof(PeerSpec)); 441 if(ortn) { 442 printSslErrStr("SSLSetPeerID", ortn); 443 goto cleanup; 444 } 445 /* quick test of the get fcn */ 446 ortn = SSLGetPeerID(ctx, &rtnId, &rtnIdLen); 447 if(ortn) { 448 printSslErrStr("SSLGetPeerID", ortn); 449 goto cleanup; 450 } 451 if((rtnId == NULL) || (rtnIdLen != sizeof(PeerSpec))) { 452 printf("***SSLGetPeerID screwup\n"); 453 } 454 else if(memcmp(&peerId, rtnId, rtnIdLen) != 0) { 455 printf("***SSLGetPeerID data mismatch\n"); 456 } 457 } 458 if(pargs->allowExpired) { 459 ortn = SSLSetAllowsExpiredCerts(ctx, true); 460 if(ortn) { 461 printSslErrStr("SSLSetAllowExpiredCerts", ortn); 462 goto cleanup; 463 } 464 } 465 if(pargs->allowExpiredRoot) { 466 ortn = SSLSetAllowsExpiredRoots(ctx, true); 467 if(ortn) { 468 printSslErrStr("SSLSetAllowsExpiredRoots", ortn); 469 goto cleanup; 470 } 471 } 472 if(pargs->disableCertVerify) { 473 ortn = SSLSetEnableCertVerify(ctx, false); 474 if(ortn) { 475 printSslErrStr("SSLSetEnableCertVerify", ortn); 476 goto cleanup; 477 } 478 } 479 if(pargs->allowAnyRoot) { 480 ortn = SSLSetAllowsAnyRoot(ctx, true); 481 if(ortn) { 482 printSslErrStr("SSLSetAllowAnyRoot", ortn); 483 goto cleanup; 484 } 485 } 486 if(pargs->cipherRestrict != '\0') { 487 ortn = sslSetCipherRestrictions(ctx, pargs->cipherRestrict); 488 if(ortn) { 489 goto cleanup; 490 } 491 } 492 if(pargs->anchorFile) { 493 ortn = sslAddTrustedRoot(ctx, pargs->anchorFile, pargs->replaceAnchors); 494 if(ortn) { 495 printf("***Error obtaining anchor file %s\n", pargs->anchorFile); 496 goto cleanup; 497 } 498 } 499 if(pargs->clientCerts) { 500 CFArrayRef dummy; 501 if(pargs->anchorFile == NULL) { 502 /* assume this is a root we want to implicitly trust */ 503 ortn = addIdentityAsTrustedRoot(ctx, pargs->clientCerts); 504 if(ortn) { 505 goto cleanup; 506 } 507 } 508 ortn = SSLSetCertificate(ctx, pargs->clientCerts); 509 if(ortn) { 510 printSslErrStr("SSLSetCertificate", ortn); 511 goto cleanup; 512 } 513 /* quickie test for this new function */ 514 ortn = SSLGetCertificate(ctx, &dummy); 515 if(ortn) { 516 printSslErrStr("SSLGetCertificate", ortn); 517 goto cleanup; 518 } 519 if(dummy != pargs->clientCerts) { 520 printf("***SSLGetCertificate error\n"); 521 ortn = errSecIO; 522 goto cleanup; 523 } 524 } 525 if (pargs->alpnNames) { 526 CFMutableDataRef alpn = CFDataCreateMutable(NULL, 0); 527 528 CFArrayForEach(pargs->alpnNames, ^(const void *value) { 529 CFDataRef data = (CFDataRef)value; 530 uint8_t len = CFDataGetLength(data); 531 CFDataAppendBytes(alpn, (const UInt8 *)&len, sizeof(len)); 532 CFDataAppend(alpn, data); 533 }); 534 535 SSLSetALPNData(ctx, CFDataGetBytePtr(alpn), CFDataGetLength(alpn)); 536 SSLSetALPNFunc(ctx, alpnFunc, (void *)NULL); 537 CFRelease(alpn); 538 } 539 540 /*** end options ***/ 541 542 if(pargs->verbose) { 543 printf("...starting SSL handshake\n"); 544 } 545 startHandshake = CFAbsoluteTimeGetCurrent(); 546 547 do 548 { ortn = SSLHandshake(ctx); 549 if((ortn == errSSLWouldBlock) && !pargs->silent) { 550 /* keep UI responsive */ 551 sslOutputDot(); 552 } 553 } while (ortn == errSSLWouldBlock); 554 555 endHandshake = CFAbsoluteTimeGetCurrent(); 556 pargs->handshakeTimeOp = endHandshake - startHandshake; 557 if(pargs->numHandshakes == 0) { 558 /* special case, this one is always way longer */ 559 pargs->handshakeTimeFirst = pargs->handshakeTimeOp; 560 } 561 else { 562 /* normal running total */ 563 pargs->handshakeTimeTotal += pargs->handshakeTimeOp; 564 } 565 pargs->numHandshakes++; 566 567 ortn = SSLCopyPeerTrust(ctx, &pargs->peerTrust); 568 if(ortn) { 569 printf("***SSLCopyPeerTrust error %" PRIdOSStatus "\n", ortn); 570 pargs->peerTrust = NULL; 571 } 572 573 /* ditto */ 574 SSLGetClientCertificateState(ctx, &pargs->certState); 575 SSLGetNegotiatedCipher(ctx, &pargs->negCipher); 576 SSLGetNegotiatedProtocolVersion(ctx, &pargs->negVersion); 577 pargs->sessionIDLength = MAX_SESSION_ID_LENGTH; 578 ortn = SSLGetResumableSessionInfo(ctx, &pargs->sessionWasResumed, pargs->sessionID, &pargs->sessionIDLength); 579 if(!ortn) { 580 OSStatus certRtn = sslEvaluateTrust(ctx, pargs, &pargs->peerCerts); 581 582 if (certRtn && !pargs->manualCertVerify) { 583 SSLCopyPeerCertificates(ctx, &pargs->peerCerts); 584 certRtn = 0; 585 } 586 587 if(certRtn && !ortn ) { 588 ortn = certRtn; 589 } 590 } 591 592 if(ortn) { 593 if(!pargs->silent) { 594 printf("\n"); 595 } 596 goto cleanup; 597 } 598 599 if(pargs->verbose) { 600 printf("...SSL handshake complete\n"); 601 } 602 length = strlen(pargs->getMsg); 603 (void) SSLWrite(ctx, pargs->getMsg, length, &actLen); 604 605 /* 606 * Try to snag RCV_BUF_SIZE bytes. Exit if (!keepConnected and we get any data 607 * at all), or (keepConnected and err != (none, wouldBlock)). 608 */ 609 while (1) { 610 actLen = 0; 611 if(pargs->dumpRxData) { 612 size_t avail = 0; 613 614 ortn = SSLGetBufferedReadSize(ctx, &avail); 615 if(ortn) { 616 printf("***SSLGetBufferedReadSize error\n"); 617 break; 618 } 619 if(avail != 0) { 620 printf("\n%d bytes available: ", (int)avail); 621 } 622 } 623 ortn = SSLRead(ctx, rcvBuf, RCV_BUF_SIZE, &actLen); 624 if((actLen == 0) && !pargs->silent) { 625 sslOutputDot(); 626 } 627 if((actLen == 0) && (ortn == errSecSuccess)) { 628 printf("***Radar 2984932 confirmed***\n"); 629 } 630 if (ortn == errSSLWouldBlock) { 631 /* for this loop, these are identical */ 632 ortn = errSecSuccess; 633 } 634 if((actLen > 0) && pargs->dumpRxData) { 635 dumpAscii(rcvBuf, actLen); 636 } 637 if(ortn != errSecSuccess) { 638 /* connection closed by server or by error */ 639 break; 640 } 641 if(!pargs->keepConnected && (actLen > 0)) { 642 /* good enough, we connected */ 643 break; 644 } 645 } 646 if(!pargs->silent) { 647 printf("\n"); 648 } 649 650 /* snag these again in case of renegotiate */ 651 SSLGetClientCertificateState(ctx, &pargs->certState); 652 SSLGetNegotiatedCipher(ctx, &pargs->negCipher); 653 SSLGetNegotiatedProtocolVersion(ctx, &pargs->negVersion); 654 655 /* convert normal "shutdown" into zero err rtn */ 656 if(ortn == errSSLClosedGraceful) { 657 ortn = errSecSuccess; 658 } 659 if((ortn == errSSLClosedNoNotify) && !pargs->requireNotify) { 660 /* relaxed disconnect rules */ 661 ortn = errSecSuccess; 662 } 663 cleanup: ; 664 /* 665 * always do close, even on error - to flush outgoing write queue 666 */ 667 OSStatus cerr = errSecParam; 668 if (ctx) { 669 cerr = SSLClose(ctx); 670 } 671 if(ortn == errSecSuccess) { 672 ortn = cerr; 673 } 674 if(sock) { 675 endpointShutdown(sock); 676 } 677 if(ctx) { 678 CFRelease(ctx); 679 } 680 return ortn; 681 } 682 683 #if TARGET_OS_IPHONE 684 685 static void add_key(const void *key, const void *value, void *context) { 686 CFArrayAppendValue((CFMutableArrayRef)context, key); 687 } 688 689 690 static void showInfo(CFDictionaryRef info) { 691 CFIndex dict_count, key_ix, key_count; 692 CFMutableArrayRef keys = NULL; 693 CFIndex maxWidth = 20; /* Maybe precompute this or grab from context? */ 694 695 dict_count = CFDictionaryGetCount(info); 696 keys = CFArrayCreateMutable(kCFAllocatorDefault, dict_count, 697 &kCFTypeArrayCallBacks); 698 CFDictionaryApplyFunction(info, add_key, keys); 699 key_count = CFArrayGetCount(keys); 700 CFArraySortValues(keys, CFRangeMake(0, key_count), 701 (CFComparatorFunction)CFStringCompare, 0); 702 703 for (key_ix = 0; key_ix < key_count; ++key_ix) { 704 CFStringRef key = (CFStringRef)CFArrayGetValueAtIndex(keys, key_ix); 705 CFTypeRef value = CFDictionaryGetValue(info, key); 706 CFMutableStringRef line = CFStringCreateMutable(NULL, 0); 707 708 CFStringAppend(line, key); 709 CFIndex jx; 710 for (jx = CFStringGetLength(key); 711 jx < maxWidth; ++jx) { 712 CFStringAppend(line, CFSTR(" ")); 713 } 714 CFStringAppend(line, CFSTR(" : ")); 715 if (CFStringGetTypeID() == CFGetTypeID(value)) { 716 CFStringAppend(line, (CFStringRef)value); 717 } else if (CFDateGetTypeID() == CFGetTypeID(value)) { 718 CFLocaleRef lc = CFLocaleCopyCurrent(); 719 CFDateFormatterRef df = CFDateFormatterCreate(NULL, lc, 720 kCFDateFormatterFullStyle, kCFDateFormatterFullStyle); 721 CFDateRef date = (CFDateRef)value; 722 CFStringRef ds = CFDateFormatterCreateStringWithDate(NULL, df, 723 date); 724 CFStringAppend(line, ds); 725 CFRelease(ds); 726 CFRelease(df); 727 CFRelease(lc); 728 } else if (CFURLGetTypeID() == CFGetTypeID(value)) { 729 CFURLRef url = (CFURLRef)value; 730 CFStringAppend(line, CFSTR("<")); 731 CFStringAppend(line, CFURLGetString(url)); 732 CFStringAppend(line, CFSTR(">")); 733 } else if (CFDataGetTypeID() == CFGetTypeID(value)) { 734 CFDataRef v_d = (CFDataRef)value; 735 CFStringRef v_s = CFStringCreateFromExternalRepresentation( 736 kCFAllocatorDefault, v_d, kCFStringEncodingUTF8); 737 if (v_s) { 738 CFStringAppend(line, CFSTR("/")); 739 CFStringAppend(line, v_s); 740 CFStringAppend(line, CFSTR("/ ")); 741 CFRelease(v_s); 742 } 743 const uint8_t *bytes = CFDataGetBytePtr(v_d); 744 CFIndex len = CFDataGetLength(v_d); 745 for (jx = 0; jx < len; ++jx) { 746 CFStringAppendFormat(line, NULL, CFSTR("%.02X"), bytes[jx]); 747 } 748 } else { 749 CFStringAppendFormat(line, NULL, CFSTR("%@"), value); 750 } 751 CFStringWriteToFileWithNewline(line, stdout); 752 CFRelease(line); 753 } 754 CFRelease(keys); 755 } 756 #endif 757 758 #pragma clang diagnostic pop 759 760 static void showPeerTrust(SecTrustRef peerTrust, bool verbose) { 761 762 if(peerTrust == NULL) { 763 return; 764 } 765 #if TARGET_OS_IPHONE 766 CFIndex numCerts; 767 CFIndex i; 768 769 printf("\n=============== Peer Trust Properties ===============\n"); 770 CFArrayRef plist = SecTrustCopyProperties(peerTrust); 771 if (plist) { 772 print_plist(plist); 773 CFRelease(plist); 774 } 775 776 printf("\n================== Peer Trust Info ==================\n"); 777 CFDictionaryRef info = SecTrustCopyInfo(peerTrust); 778 if (info && CFDictionaryGetCount(info)) { 779 showInfo(info); 780 } 781 if (info) { 782 CFRelease(info); 783 } 784 785 numCerts = SecTrustGetCertificateCount(peerTrust); 786 for(i=0; i<numCerts; i++) { 787 plist = SecTrustCopySummaryPropertiesAtIndex(peerTrust, i); 788 printf("\n============= Peer Trust Cert %lu Summary =============\n\n", i); 789 print_plist(plist); 790 if (plist) { 791 CFRelease(plist); 792 } 793 printf("\n============= Peer Trust Cert %lu Details =============\n\n", i); 794 plist = SecTrustCopyDetailedPropertiesAtIndex(peerTrust, i); 795 print_plist(plist); 796 if (plist) { 797 CFRelease(plist); 798 } 799 printf("\n============= End of Peer Trust Cert %lu ==============\n", i); 800 } 801 #endif 802 } 803 804 static void showPeerCerts( 805 CFArrayRef __unused peerCerts, 806 bool __unused verbose) 807 { 808 #if 0 809 CFIndex numCerts; 810 SecCertificateRef certRef; 811 CFIndex i; 812 813 if(peerCerts == NULL) { 814 return; 815 } 816 numCerts = CFArrayGetCount(peerCerts); 817 for(i=0; i<numCerts; i++) { 818 certRef = (SecCertificateRef)CFArrayGetValueAtIndex(peerCerts, i); 819 printf("\n==================== Peer Cert %lu ====================\n\n", i); 820 print_cert(certRef, verbose); 821 printf("\n================ End of Peer Cert %lu =================\n", i); 822 } 823 #endif 824 } 825 826 static void writePeerCerts( 827 CFArrayRef peerCerts, 828 const char *fileBase) 829 { 830 CFIndex numCerts; 831 SecCertificateRef certRef; 832 CFIndex i; 833 char fileName[_maxFileStringSize]; 834 835 if(peerCerts == NULL) { 836 return; 837 } 838 numCerts = CFArrayGetCount(peerCerts); 839 for(i=0; i<numCerts; i++) { 840 snprintf(fileName, _maxFileStringSize, "%s%02d.cer", fileBase, (int)i); 841 certRef = (SecCertificateRef)CFArrayGetValueAtIndex(peerCerts, i); 842 CFDataRef derCert = SecCertificateCopyData(certRef); 843 if (derCert) { 844 writeFileSizet(fileName, CFDataGetBytePtr(derCert), 845 CFDataGetLength(derCert)); 846 CFRelease(derCert); 847 } 848 } 849 printf("...wrote %lu certs to fileBase %s\n", numCerts, fileBase); 850 } 851 852 /* 853 * Show result of an sslPing(). 854 * Assumes the following from sslPingArgs: 855 * 856 * verbose 857 * tryVersion 858 * acceptedProts 859 * negVersion 860 * negCipher 861 * peerCerts 862 * certState 863 * sessionWasResumed 864 * sessionID 865 * sessionIDLength 866 * handshakeTime 867 */ 868 static void showSSLResult( 869 const sslPingArgs *pargs, 870 OSStatus err, 871 int displayPeerCerts, 872 char *fileBase) // non-NULL: write certs to file 873 { 874 CFIndex numPeerCerts; 875 876 printf("\n"); 877 878 if(pargs->acceptedProts) { 879 printf(" Allowed SSL versions : %s\n", pargs->acceptedProts); 880 } 881 else { 882 printf(" Attempted SSL version : %s\n", 883 sslGetProtocolVersionString(pargs->tryVersion)); 884 } 885 886 printf(" Result : %s\n", sslGetSSLErrString(err)); 887 printf(" Negotiated SSL version : %s\n", 888 sslGetProtocolVersionString(pargs->negVersion)); 889 printf(" Negotiated CipherSuite : %s\n", 890 sslGetCipherSuiteString(pargs->negCipher)); 891 #pragma clang diagnostic push 892 #pragma clang diagnostic ignored "-Wdeprecated-declarations" 893 if(pargs->certState != kSSLClientCertNone) { 894 printf(" Client Cert State : %s\n", 895 sslGetClientCertStateString(pargs->certState)); 896 } 897 #pragma clang diagnostic pop 898 if(pargs->verbose) { 899 printf(" Resumed Session : "); 900 if(pargs->sessionWasResumed) { 901 for(unsigned dex=0; dex<pargs->sessionIDLength; dex++) { 902 printf("%02X ", pargs->sessionID[dex]); 903 if(((dex % 8) == 7) && (dex != (pargs->sessionIDLength - 1))) { 904 printf("\n "); 905 } 906 } 907 printf("\n"); 908 } 909 else { 910 printf("NOT RESUMED\n"); 911 } 912 printf(" Handshake time : %f seconds\n", pargs->handshakeTimeOp); 913 } 914 if(pargs->peerCerts == NULL) { 915 numPeerCerts = 0; 916 } 917 else { 918 numPeerCerts = CFArrayGetCount(pargs->peerCerts); 919 } 920 printf(" Number of server certs : %lu\n", numPeerCerts); 921 if(numPeerCerts != 0) { 922 if (displayPeerCerts == 1) { 923 showPeerCerts(pargs->peerCerts, false); 924 } else if (displayPeerCerts == 2) { 925 showPeerTrust(pargs->peerTrust, false); 926 } 927 if(fileBase != NULL) { 928 writePeerCerts(pargs->peerCerts, fileBase); 929 } 930 } 931 932 printf("\n"); 933 } 934 935 static int verifyProtocol( 936 bool verifyProt, 937 SSLProtocol maxProtocol, 938 SSLProtocol reqProtocol, 939 SSLProtocol negProtocol) 940 { 941 if(!verifyProt) { 942 return 0; 943 } 944 if(reqProtocol > maxProtocol) { 945 /* known not to support this attempt, relax */ 946 reqProtocol = maxProtocol; 947 } 948 if(reqProtocol != negProtocol) { 949 printf("***Expected protocol %s; negotiated %s\n", 950 sslGetProtocolVersionString(reqProtocol), 951 sslGetProtocolVersionString(negProtocol)); 952 return 1; 953 } 954 else { 955 return 0; 956 } 957 } 958 959 static int verifyClientCertState( 960 bool verifyCertState, 961 SSLClientCertificateState expectState, 962 SSLClientCertificateState gotState) 963 { 964 if(!verifyCertState) { 965 return 0; 966 } 967 if(expectState == gotState) { 968 return 0; 969 } 970 printf("***Expected clientCertState %s; got %s\n", 971 sslGetClientCertStateString(expectState), 972 sslGetClientCertStateString(gotState)); 973 return 1; 974 } 975 976 static SSLProtocol charToProt( 977 char c, // 2, 3, t 978 char **argv) 979 { 980 #pragma clang diagnostic push 981 #pragma clang diagnostic ignored "-Wdeprecated-declarations" 982 switch(c) { 983 case '2': 984 return kSSLProtocol2; 985 case '3': 986 return kSSLProtocol3; 987 case 't': 988 return kTLSProtocol12; 989 default: 990 usage(argv); 991 } 992 #pragma clang diagnostic pop 993 } 994 995 int main(int argc, char **argv) 996 { 997 OSStatus err; 998 int arg; 999 char *argp; 1000 char getMsg[300]; 1001 char fullFileBase[_maxFileStringSize]; 1002 int ourRtn = 0; // exit status - sum of all errors 1003 unsigned loop; 1004 SecKeychainRef serverKc = nil; 1005 sslPingArgs pargs; 1006 1007 /* user-spec'd parameters */ 1008 const char *getPath = DEFAULT_PATH; 1009 char *fileBase = NULL; 1010 int displayCerts = 0; 1011 bool doSslV2 = false; 1012 bool doSslV3 = false; 1013 bool doTlsV1 = true; 1014 bool doTlsV11 = false; 1015 bool doTlsV12 = false; 1016 bool protXOnly = false; // kSSLProtocol3Only, kTLSProtocol1Only 1017 bool doProtUnknown = false; 1018 unsigned loopCount = 1; 1019 bool doPause = false; 1020 bool pauseFirstLoop = false; 1021 bool verifyProt = false; 1022 char *acceptedProts = NULL; 1023 char *keyChainName = NULL; 1024 char *getMsgSpec = NULL; 1025 bool vfyCertState = false; 1026 bool displayHandshakeTimes = false; 1027 bool completeCertChain = false; 1028 #pragma clang diagnostic push 1029 #pragma clang diagnostic ignored "-Wdeprecated-declarations" 1030 SSLClientCertificateState expectCertState = kSSLClientCertNone; 1031 SSLProtocol maxProtocol = kTLSProtocol12; // for verifying negotiated protocol 1032 #pragma clang diagnostic pop 1033 1034 /* special case - one arg of "h" or "-h" or "hv" */ 1035 if(argc == 2) { 1036 if((strcmp(argv[1], "h") == 0) || (strcmp(argv[1], "-h") == 0)) { 1037 usage(argv); 1038 } 1039 if(strcmp(argv[1], "hv") == 0) { 1040 usageVerbose(argv); 1041 } 1042 } 1043 1044 /* set up defaults */ 1045 memset(&pargs, 0, sizeof(sslPingArgs)); 1046 pargs.hostName = DEFAULT_HOST; 1047 pargs.port = DEFAULT_PORT; 1048 pargs.resumableEnable = true; 1049 pargs.argv = argv; 1050 1051 for(arg=1; arg<argc; arg++) { 1052 argp = argv[arg]; 1053 if(arg == 1) { 1054 /* first arg, is always hostname; '-' means default */ 1055 if(argp[0] != '-') { 1056 pargs.hostName = argp; 1057 } 1058 continue; 1059 } 1060 if(argp[0] == '/') { 1061 /* path always starts with leading slash */ 1062 getPath = argp; 1063 continue; 1064 } 1065 /* options */ 1066 switch(argp[0]) { 1067 case 'Z': { 1068 if(++arg == argc) { 1069 /* requires another arg */ 1070 usage(argv); 1071 } 1072 if (pargs.alpnNames == NULL) { 1073 pargs.alpnNames = CFArrayCreateMutableForCFTypes(NULL); 1074 } 1075 1076 CFDataRef alpn = CFDataCreate(NULL, (const UInt8 *)argv[arg], strlen(argv[arg])); 1077 CFArrayAppendValue(pargs.alpnNames, alpn); 1078 CFReleaseNull(alpn); 1079 break; 1080 } 1081 case 'W': 1082 case 'w': { 1083 CFDictionaryRef context = NULL; 1084 1085 if(++arg == argc) { 1086 /* requires another arg */ 1087 usage(argv); 1088 } 1089 1090 if (argp[0] == 'W') { 1091 context = CFDictionaryCreateForCFTypes(NULL, 1092 CFSTR("AppleServerAuthenticationAllowUATAPN"), kCFBooleanTrue, 1093 CFSTR("AppleServerAuthenticationAllowUATIDS"), kCFBooleanTrue, 1094 CFSTR("AppleServerAuthenticationAllowUATGS"), kCFBooleanTrue, 1095 NULL); 1096 } 1097 const char *verifyName = pargs.hostName; 1098 1099 if (pargs.policies == NULL) { 1100 pargs.policies = CFArrayCreateMutableForCFTypes(NULL); 1101 } 1102 1103 if (pargs.vfyHostName) 1104 verifyName = pargs.vfyHostName; 1105 1106 SecPolicyRef policy = NULL; 1107 CFStringRef hostname = CFStringCreateWithCString(NULL, verifyName, kCFStringEncodingUTF8); 1108 1109 if (strcasecmp(argv[arg], "PushLegacy") == 0) { 1110 policy = SecPolicyCreateApplePushServiceLegacy(hostname); 1111 } else if (strcasecmp(argv[arg], "Push") == 0) { 1112 policy = SecPolicyCreateApplePushService(hostname, context); 1113 } else if (strcasecmp(argv[arg], "IDS") == 0) { 1114 policy = SecPolicyCreateAppleIDSServiceContext(hostname, context); 1115 } else if (strcasecmp(argv[arg], "GS") == 0) { 1116 policy = SecPolicyCreateAppleGSService(hostname, context); 1117 } else { 1118 printf("unknown policy: %s", argv[arg]); 1119 CFReleaseNull(hostname); 1120 CFReleaseNull(context); 1121 usage(argv); 1122 } 1123 1124 if (policy) { 1125 CFArrayAppendValue(pargs.policies, policy); 1126 } 1127 1128 CFReleaseNull(policy); 1129 CFReleaseNull(hostname); 1130 CFReleaseNull(context); 1131 1132 break; 1133 } 1134 case 'e': 1135 pargs.allowExpired = true; 1136 break; 1137 case 'E': 1138 pargs.allowExpiredRoot = true; 1139 break; 1140 case 'x': 1141 pargs.disableCertVerify = true; 1142 break; 1143 case 'M': 1144 pargs.disableCertVerify = true; // implied 1145 pargs.manualCertVerify = true; 1146 break; 1147 case 'a': 1148 if(++arg == argc) { 1149 /* requires another arg */ 1150 usage(argv); 1151 } 1152 pargs.anchorFile = argv[arg]; 1153 break; 1154 case 'A': 1155 if(++arg == argc) { 1156 /* requires another arg */ 1157 usage(argv); 1158 } 1159 pargs.anchorFile = argv[arg]; 1160 pargs.replaceAnchors = true; 1161 break; 1162 case 'r': 1163 pargs.allowAnyRoot = true; 1164 break; 1165 case 'd': 1166 pargs.dumpRxData = true; 1167 break; 1168 case 'c': 1169 displayCerts++; 1170 break; 1171 case 'f': 1172 if(++arg == argc) { 1173 /* requires another arg */ 1174 usage(argv); 1175 } 1176 fileBase = argv[arg]; 1177 break; 1178 case 'C': 1179 pargs.cipherRestrict = argp[2]; 1180 break; 1181 case '2': 1182 doSslV3 = doTlsV1 = doTlsV11 = false; 1183 doSslV2 = true; 1184 break; 1185 case '3': 1186 doSslV2 = doTlsV1 = doTlsV11 = doTlsV12 = false; 1187 doSslV3 = true; 1188 break; 1189 case 't': 1190 doSslV2 = doSslV3 = doTlsV11 = doTlsV12 = false; 1191 doTlsV1 = true; 1192 break; 1193 case '%': 1194 doSslV2 = doSslV3 = doTlsV1 = false; 1195 doTlsV11 = true; 1196 break; 1197 case '^': 1198 doSslV2 = doSslV3 = doTlsV1 = false; 1199 doTlsV12 = true; 1200 break; 1201 case 'L': 1202 doSslV2 = doSslV3 = doTlsV1 = doTlsV11 = doTlsV12 = true; 1203 break; 1204 case 'o': 1205 protXOnly = true; 1206 break; 1207 case 'u': 1208 doSslV2 = doSslV3 = doTlsV1 = doTlsV11 = doTlsV12 = false; 1209 doProtUnknown = true; 1210 break; 1211 case 'K': 1212 pargs.keepConnected = true; 1213 break; 1214 case 'n': 1215 pargs.requireNotify = true; 1216 pargs.keepConnected = true; 1217 break; 1218 case 'R': 1219 pargs.resumableEnable = false; 1220 break; 1221 case 'b': 1222 pargs.nonBlocking = true; 1223 break; 1224 case 'v': 1225 verifyProt = true; 1226 break; 1227 case 'm': 1228 if(argp[1] != '=') { 1229 usage(argv); 1230 } 1231 verifyProt = true; // implied 1232 maxProtocol = charToProt(argp[2], argv); 1233 break; 1234 case 'g': 1235 if(argp[1] != '=') { 1236 usage(argv); 1237 } 1238 acceptedProts = argv[arg]; 1239 doSslV3 = doSslV2 = doTlsV1 = doTlsV11 = doTlsV12 = false; 1240 break; 1241 case 'l': 1242 if(++arg == argc) { 1243 /* requires another arg */ 1244 usage(argv); 1245 } 1246 int parsedLoopCount = atoi(argv[arg]); 1247 if (parsedLoopCount <= 0) { 1248 printf("***bad loopCount\n"); 1249 usage(argv); 1250 } 1251 loopCount = (unsigned) parsedLoopCount; 1252 break; 1253 case 'P': 1254 if(++arg == argc) { 1255 /* requires another arg */ 1256 usage(argv); 1257 } 1258 pargs.port = atoi(argv[arg]); 1259 break; 1260 case 'H': 1261 pargs.allowHostnameSpoof = true; 1262 break; 1263 case 'F': 1264 if(++arg == argc) { 1265 /* requires another arg */ 1266 usage(argv); 1267 } 1268 pargs.vfyHostName = argv[arg]; 1269 break; 1270 case 'k': 1271 if(++arg == argc) { 1272 /* requires another arg */ 1273 usage(argv); 1274 } 1275 keyChainName = &argp[2]; 1276 break; 1277 case 'G': 1278 getMsgSpec = &argp[2]; 1279 break; 1280 case 'T': 1281 if(argp[1] != '=') { 1282 usage(argv); 1283 } 1284 vfyCertState = true; 1285 #pragma clang diagnostic push 1286 #pragma clang diagnostic ignored "-Wdeprecated-declarations" 1287 switch(argp[2]) { 1288 case 'n': 1289 expectCertState = kSSLClientCertNone; 1290 break; 1291 case 'r': 1292 expectCertState = kSSLClientCertRequested; 1293 break; 1294 case 's': 1295 expectCertState = kSSLClientCertSent; 1296 break; 1297 case 'j': 1298 expectCertState = kSSLClientCertRejected; 1299 break; 1300 default: 1301 usage(argv); 1302 } 1303 #pragma clang diagnostic pop 1304 break; 1305 case 'z': 1306 pargs.password = &argp[2]; 1307 break; 1308 case 'p': 1309 doPause = true; 1310 break; 1311 case '7': 1312 pauseFirstLoop = true; 1313 break; 1314 case 'q': 1315 pargs.quiet = true; 1316 break; 1317 case 'V': 1318 pargs.verbose = true; 1319 break; 1320 case 's': 1321 pargs.silent = pargs.quiet = true; 1322 break; 1323 case 'N': 1324 displayHandshakeTimes = true; 1325 break; 1326 case '8': 1327 completeCertChain = true; 1328 break; 1329 case 'h': 1330 if(pargs.verbose || (argp[1] == 'v')) { 1331 usageVerbose(argv); 1332 } 1333 else { 1334 usage(argv); 1335 } 1336 default: 1337 usage(argv); 1338 } 1339 } 1340 if(getMsgSpec) { 1341 pargs.getMsg = getMsgSpec; 1342 } 1343 else { 1344 sprintf(getMsg, "%s %s %s", 1345 DEFAULT_GETMSG, getPath, DEFAULT_GET_SUFFIX); 1346 pargs.getMsg = getMsg; 1347 } 1348 1349 /* get client cert and optional encryption cert as CFArrayRef */ 1350 if(keyChainName) { 1351 pargs.clientCerts = getSslCerts(keyChainName, false, completeCertChain, 1352 pargs.anchorFile, &serverKc); 1353 if(pargs.clientCerts == nil) { 1354 exit(1); 1355 } 1356 #ifdef USE_CDSA_CRYPTO 1357 if(pargs.password) { 1358 OSStatus ortn = SecKeychainUnlock(serverKc, 1359 strlen(pargs.password), pargs.password, true); 1360 if(ortn) { 1361 printf("SecKeychainUnlock returned %d\n", (int)ortn); 1362 /* oh well */ 1363 } 1364 } 1365 #endif 1366 } 1367 1368 { 1369 struct sigaction sa; 1370 memset(&sa, 0, sizeof(sa)); 1371 sa.sa_flags = SA_RESTART; 1372 sa.sa_handler = sigpipe; 1373 sigaction(SIGPIPE, &sa, NULL); 1374 } 1375 1376 #pragma clang diagnostic push 1377 #pragma clang diagnostic ignored "-Wdeprecated-declarations" 1378 for(loop=0; loop<loopCount; loop++) { 1379 /* 1380 * One pass for each protocol version, skipping any explicit version if 1381 * an attempt at a higher version and succeeded in doing so successfully fell 1382 * back. 1383 */ 1384 if(doTlsV12) { 1385 pargs.tryVersion = kTLSProtocol12; 1386 pargs.acceptedProts = NULL; 1387 if(!pargs.silent) { 1388 printf("Connecting to host %s with TLS V1.2...", pargs.hostName); 1389 } 1390 fflush(stdout); 1391 err = sslPing(&pargs); 1392 if(err) { 1393 ourRtn++; 1394 } 1395 if(!pargs.quiet) { 1396 if(fileBase) { 1397 snprintf(fullFileBase, _maxFileStringSize, "%s_v3.1", fileBase); 1398 } 1399 showSSLResult(&pargs, 1400 err, 1401 displayCerts, 1402 fileBase ? fullFileBase : NULL); 1403 } 1404 CFReleaseNull(pargs.peerCerts); 1405 if(!err) { 1406 /* deal with fallbacks, skipping redundant tests */ 1407 switch(pargs.negVersion) { 1408 case kTLSProtocol11: 1409 doTlsV11 =false; 1410 break; 1411 case kTLSProtocol1: 1412 doTlsV11 =false; 1413 doTlsV1 =false; 1414 break; 1415 case kSSLProtocol3: 1416 doTlsV11 =false; 1417 doTlsV1 =false; 1418 doSslV3 = false; 1419 break; 1420 case kSSLProtocol2: 1421 doTlsV11 =false; 1422 doTlsV1 =false; 1423 doSslV3 = false; 1424 doSslV2 = false; 1425 break; 1426 default: 1427 break; 1428 } 1429 ourRtn += verifyProtocol(verifyProt, maxProtocol, kTLSProtocol12, 1430 pargs.negVersion); 1431 } 1432 /* note we do this regardless since the client state might be 1433 * the cause of a failure */ 1434 ourRtn += verifyClientCertState(vfyCertState, expectCertState, 1435 pargs.certState); 1436 } 1437 if(doTlsV11) { 1438 pargs.tryVersion = kTLSProtocol11; 1439 pargs.acceptedProts = NULL; 1440 if(!pargs.silent) { 1441 printf("Connecting to host %s with TLS V1.1...", pargs.hostName); 1442 } 1443 fflush(stdout); 1444 err = sslPing(&pargs); 1445 if(err) { 1446 ourRtn++; 1447 } 1448 if(!pargs.quiet) { 1449 if(fileBase) { 1450 snprintf(fullFileBase, _maxFileStringSize, "%s_v3.1", fileBase); 1451 } 1452 showSSLResult(&pargs, 1453 err, 1454 displayCerts, 1455 fileBase ? fullFileBase : NULL); 1456 } 1457 CFReleaseNull(pargs.peerCerts); 1458 if(!err) { 1459 /* deal with fallbacks, skipping redundant tests */ 1460 switch(pargs.negVersion) { 1461 case kTLSProtocol1: 1462 doTlsV1 =false; 1463 break; 1464 case kSSLProtocol3: 1465 doTlsV1 =false; 1466 doSslV3 = false; 1467 break; 1468 case kSSLProtocol2: 1469 doTlsV1 =false; 1470 doSslV3 = false; 1471 doSslV2 = false; 1472 break; 1473 default: 1474 break; 1475 } 1476 ourRtn += verifyProtocol(verifyProt, maxProtocol, kTLSProtocol11, 1477 pargs.negVersion); 1478 } 1479 /* note we do this regardless since the client state might be 1480 * the cause of a failure */ 1481 ourRtn += verifyClientCertState(vfyCertState, expectCertState, 1482 pargs.certState); 1483 } 1484 if(doTlsV1) { 1485 pargs.tryVersion = 1486 protXOnly ? kTLSProtocol1Only : kTLSProtocol1; 1487 pargs.acceptedProts = NULL; 1488 if(!pargs.silent) { 1489 printf("Connecting to host %s with TLS V1...", pargs.hostName); 1490 } 1491 fflush(stdout); 1492 err = sslPing(&pargs); 1493 if(err) { 1494 ourRtn++; 1495 } 1496 if(!pargs.quiet) { 1497 if(fileBase) { 1498 snprintf(fullFileBase, _maxFileStringSize, "%s_v3.1", fileBase); 1499 } 1500 showSSLResult(&pargs, 1501 err, 1502 displayCerts, 1503 fileBase ? fullFileBase : NULL); 1504 } 1505 CFReleaseNull(pargs.peerCerts); 1506 if(!err) { 1507 /* deal with fallbacks, skipping redundant tests */ 1508 switch(pargs.negVersion) { 1509 case kSSLProtocol3: 1510 doSslV3 = false; 1511 break; 1512 case kSSLProtocol2: 1513 doSslV3 = false; 1514 doSslV2 = false; 1515 break; 1516 default: 1517 break; 1518 } 1519 ourRtn += verifyProtocol(verifyProt, maxProtocol, kTLSProtocol1, 1520 pargs.negVersion); 1521 } 1522 /* note we do this regardless since the client state might be 1523 * the cause of a failure */ 1524 ourRtn += verifyClientCertState(vfyCertState, expectCertState, 1525 pargs.certState); 1526 } 1527 if(doSslV3) { 1528 pargs.tryVersion = protXOnly ? kSSLProtocol3Only : kSSLProtocol3; 1529 pargs.acceptedProts = NULL; 1530 if(!pargs.silent) { 1531 printf("Connecting to host %s with SSL V3...", pargs.hostName); 1532 } 1533 fflush(stdout); 1534 err = sslPing(&pargs); 1535 if(err) { 1536 ourRtn++; 1537 } 1538 if(!pargs.quiet) { 1539 if(fileBase) { 1540 snprintf(fullFileBase, _maxFileStringSize, "%s_v3.0", fileBase); 1541 } 1542 showSSLResult(&pargs, 1543 err, 1544 displayCerts, 1545 fileBase ? fullFileBase : NULL); 1546 } 1547 CFReleaseNull(pargs.peerCerts); 1548 if(!err) { 1549 /* deal with fallbacks, skipping redundant tests */ 1550 switch(pargs.negVersion) { 1551 case kSSLProtocol2: 1552 doSslV2 = false; 1553 break; 1554 default: 1555 break; 1556 } 1557 ourRtn += verifyProtocol(verifyProt, maxProtocol, kSSLProtocol3, 1558 pargs.negVersion); 1559 } 1560 /* note we do this regardless since the client state might be 1561 * the cause of a failure */ 1562 ourRtn += verifyClientCertState(vfyCertState, expectCertState, 1563 pargs.certState); 1564 } 1565 1566 if(doSslV2) { 1567 if(fileBase) { 1568 snprintf(fullFileBase, _maxFileStringSize, "%s_v2", fileBase); 1569 } 1570 if(!pargs.silent) { 1571 printf("Connecting to host %s with SSL V2...", pargs.hostName); 1572 } 1573 fflush(stdout); 1574 pargs.tryVersion = kSSLProtocol2; 1575 pargs.acceptedProts = NULL; 1576 err = sslPing(&pargs); 1577 if(err) { 1578 ourRtn++; 1579 } 1580 if(!pargs.quiet) { 1581 if(fileBase) { 1582 snprintf(fullFileBase, _maxFileStringSize, "%s_v2", fileBase); 1583 } 1584 showSSLResult(&pargs, 1585 err, 1586 displayCerts, 1587 fileBase ? fullFileBase : NULL); 1588 } 1589 CFReleaseNull(pargs.peerCerts); 1590 if(!err) { 1591 ourRtn += verifyProtocol(verifyProt, maxProtocol, kSSLProtocol2, 1592 pargs.negVersion); 1593 } 1594 /* note we do this regardless since the client state might be 1595 * the cause of a failure */ 1596 ourRtn += verifyClientCertState(vfyCertState, expectCertState, 1597 pargs.certState); 1598 } 1599 if(doProtUnknown) { 1600 if(!pargs.silent) { 1601 printf("Connecting to host %s with kSSLProtocolUnknown...", 1602 pargs.hostName); 1603 } 1604 fflush(stdout); 1605 pargs.tryVersion = kSSLProtocolUnknown; 1606 pargs.acceptedProts = NULL; 1607 err = sslPing(&pargs); 1608 if(err) { 1609 ourRtn++; 1610 } 1611 if(!pargs.quiet) { 1612 if(fileBase) { 1613 snprintf(fullFileBase, _maxFileStringSize, "%s_def", fileBase); 1614 } 1615 showSSLResult(&pargs, 1616 err, 1617 displayCerts, 1618 fileBase ? fullFileBase : NULL); 1619 } 1620 CFReleaseNull(pargs.peerCerts); 1621 } 1622 if(acceptedProts != NULL) { 1623 pargs.acceptedProts = acceptedProts; 1624 pargs.tryVersion = kSSLProtocolUnknown; // not used 1625 if(!pargs.silent) { 1626 printf("Connecting to host %s with acceptedProts %s...", 1627 pargs.hostName, pargs.acceptedProts); 1628 } 1629 fflush(stdout); 1630 err = sslPing(&pargs); 1631 if(err) { 1632 ourRtn++; 1633 } 1634 if(!pargs.quiet) { 1635 if(fileBase) { 1636 snprintf(fullFileBase, _maxFileStringSize, "%s_def", fileBase); 1637 } 1638 showSSLResult(&pargs, 1639 err, 1640 displayCerts, 1641 fileBase ? fullFileBase : NULL); 1642 } 1643 CFReleaseNull(pargs.peerCerts); 1644 } 1645 if(doPause || 1646 (pauseFirstLoop && 1647 /* pause after first, before last to grab trace */ 1648 ((loop == 0) || (loop == loopCount - 1)) 1649 ) 1650 ) { 1651 char resp; 1652 fpurge(stdin); 1653 printf("a to abort, c to continue: "); 1654 resp = (char) getchar(); 1655 if(resp == 'a') { 1656 break; 1657 } 1658 } 1659 } /* main loop */ 1660 #pragma clang diagnostic pop 1661 1662 if(displayHandshakeTimes) { 1663 CFAbsoluteTime totalTime; 1664 unsigned numHandshakes; 1665 if(pargs.numHandshakes == 1) { 1666 /* just display the first one */ 1667 totalTime = pargs.handshakeTimeFirst; 1668 numHandshakes = 1; 1669 } 1670 else { 1671 /* skip the first one */ 1672 totalTime = pargs.handshakeTimeTotal; 1673 numHandshakes = pargs.numHandshakes - 1; 1674 } 1675 if(numHandshakes != 0) { 1676 printf(" %u handshakes in %f seconds; %f seconds per handshake\n", 1677 numHandshakes, totalTime, 1678 (totalTime / numHandshakes)); 1679 } 1680 } 1681 1682 if(ourRtn) { 1683 printf("===%s exiting with %d %s for host %s\n", argv[0], ourRtn, 1684 (ourRtn > 1) ? "errors" : "error", pargs.hostName); 1685 } 1686 return ourRtn; 1687 1688 }