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