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