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