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