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