--- /dev/null
+/*
+ * Copyright (c) 2006-2013, 2015 Apple Inc. All Rights Reserved.
+ *
+ * SSL viewer tool
+ */
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <Security/Security.h>
+#include <Security/SecureTransport.h>
+#include <Security/SecureTransportPriv.h>
+#include <Security/SecTrustPriv.h>
+#include <Security/SecPolicyPriv.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <time.h>
+#include <ctype.h>
+
+#include "sslAppUtils.h"
+#include "ioSock.h"
+#include "utilities/fileIo.h"
+#include "utilities/SecCFWrappers.h"
+#include "utilities/SecIOFormat.h"
+#include "SecurityTool/print_cert.h"
+
+#define DEFAULT_GETMSG "GET"
+#define DEFAULT_PATH "/"
+#define DEFAULT_GET_SUFFIX "HTTP/1.0\r\n\r\n"
+
+#define DEFAULT_HOST "www.amazon.com"
+#define DEFAULT_PORT 443
+
+
+static void usageNorm(char **argv)
+{
+ printf("Usage: %s [hostname|-] [path] [option ...]\n", argv[0]);
+ printf(" %s hostname [path] [option ...]\n", argv[0]);
+ printf("Specifying '-' for hostname, or no args, uses default of %s.\n",
+ DEFAULT_HOST);
+ printf("Optional path argument must start with leading '/'.\n");
+ printf("Options:\n");
+ printf(" e Allow Expired Certs\n");
+ printf(" E Allow Expired Roots\n");
+ printf(" r Allow any root cert\n");
+ printf(" c Display peer certs\n");
+ printf(" c c Display peer SecTrust\n");
+ printf(" d Display received data\n");
+ printf(" 2 SSLv2 only (default is TLSv1)\n");
+ printf(" 3 SSLv3 only (default is TLSv1)\n");
+ printf(" t TLSv1\n");
+ printf(" %% TLSv1.1 only\n");
+ printf(" ^ TLSv1.2 only\n");
+ printf(" L all - TLSv1.2, TLSv1.1, TLSv1.0, SSLv3, SSLv2 (default = TLSv1.2)\n");
+ printf(" g={prot...} Specify legal protocols; prot = any combo of"
+ " [23t]\n");
+ printf(" k=keychain Contains cert and keys. Optional.\n");
+ printf(" l=loopCount Perform loopCount ops (default = 1)\n");
+ printf(" P=port Default = %d\n", DEFAULT_PORT);
+ printf(" p Pause after each loop\n");
+ printf(" q Quiet/diagnostic mode (site names and errors only)\n");
+ printf(" a fileName Add fileName to list of trusted roots\n");
+ printf(" A fileName fileName is ONLY trusted root\n");
+ printf(" x Disable Cert Verification\n");
+ printf(" Z string ALPN setting\n");
+ printf(" z=password Unlock client keychain with password.\n");
+ printf(" 8 Complete cert chains (default is out cert is a root)\n");
+ printf(" s Silent\n");
+ printf(" V Verbose\n");
+ printf(" h Help\n");
+ printf(" hv More, verbose help\n");
+}
+
+static void usageVerbose(char **argv) __attribute__((noreturn));
+static void usageVerbose(char **argv)
+{
+ usageNorm(argv);
+ printf("Obscure Usage:\n");
+ printf(" u kSSLProtocolUnknown only (TLSv1)\n");
+ printf(" M Manual cert verification via "
+ "SecTrustEvaluate\n");
+ printf(" f fileBase Write Peer Certs to fileBase*\n");
+ printf(" o TLSv1, SSLv3 use kSSLProtocol__X__Only\n");
+ printf(" C=cipherSuite (e=40-bit d=DES D=40-bit DES 3=3DES 4=RC4 "
+ "$=40-bit RC4\n"
+ " 2=RC2 a=AES128 A=AES256 h=DH H=Anon DH r=DHE/RSA s=DH/DSS\n");
+ printf(" y=keychain Encryption-only cert and keys. Optional.\n");
+ printf(" K Keep connected until server disconnects\n");
+ printf(" n Require closure notify message in TLSv1, "
+ "SSLv3 mode (implies K)\n");
+ printf(" R Disable resumable session support\n");
+ printf(" b Non-blocking I/O\n");
+ printf(" v Verify negotiated protocol equals attempted\n");
+ printf(" m=[23t] Max protocol supported as specified; implies "
+ "v\n");
+ printf(" T=[nrsj] Verify client cert state = "
+ "none/requested/sent/rejected\n");
+ printf(" H allow hostname spoofing\n");
+ printf(" F=vfyHost Verify certs with specified host name\n");
+ printf(" G=getMsg Specify entire GET, POST, etc.\n");
+ printf(" N Log handshake timing\n");
+ printf(" 7 Pause only after first loop\n");
+ exit(1);
+}
+
+static void usage(char **argv) __attribute__((noreturn));
+static void usage(char **argv)
+{
+ usageNorm(argv);
+ exit(1);
+}
+
+/*
+ * Arguments to top-level sslPing()
+ */
+typedef struct {
+ SSLProtocol tryVersion; // only used if acceptedProts NULL
+ // uses SSLSetProtocolVersion
+ char *acceptedProts; // optional, any combo of {2,3,t}
+ // uses SSLSetProtocolVersionEnabled
+ const char *hostName; // e.g., "www.amazon.com"
+ const char *vfyHostName; // use this for cert vfy if non-NULL,
+ // else use hostName
+ unsigned short port;
+ const char *getMsg; // e.g.,
+ // "GET / HTTP/1.0\r\n\r\n"
+ bool allowExpired;
+ bool allowAnyRoot;
+ bool allowExpiredRoot;
+ bool disableCertVerify;
+ bool manualCertVerify;
+ bool dumpRxData; // display server data
+ char cipherRestrict; // '2', 'd'. etc...; '\0' for
+ // no restriction
+ bool keepConnected;
+ bool requireNotify; // require closure notify
+ // in V3 mode
+ bool resumableEnable;
+ bool allowHostnameSpoof;
+ bool nonBlocking;
+ char *anchorFile;
+ bool replaceAnchors;
+ CFArrayRef clientCerts; // optional
+ bool quiet; // minimal stdout
+ bool silent; // no stdout
+ bool verbose;
+ SSLProtocol negVersion; // RETURNED
+ SSLCipherSuite negCipher; // RETURNED
+ CFArrayRef peerCerts; // mallocd & RETURNED
+ SecTrustRef peerTrust; // RETURNED
+ SSLClientCertificateState certState; // RETURNED
+ char *password; // optional to open clientCerts
+ char **argv;
+ Boolean sessionWasResumed;
+ unsigned char sessionID[MAX_SESSION_ID_LENGTH];
+ size_t sessionIDLength;
+ CFAbsoluteTime handshakeTimeOp; // time for this op
+ CFAbsoluteTime handshakeTimeFirst; // time for FIRST op, not averaged
+ CFAbsoluteTime handshakeTimeTotal; // time for all ops except first
+ unsigned numHandshakes;
+
+ CFMutableArrayRef alpnNames;
+ CFMutableArrayRef policies;
+
+} sslPingArgs;
+
+static void
+sigpipe(int sig)
+{
+ fflush(stdin);
+ printf("***SIGPIPE***\n");
+}
+
+/*
+ * Manually evaluate session's SecTrustRef.
+ */
+
+static OSStatus sslEvaluateTrust(
+ SSLContextRef ctx,
+ sslPingArgs *pargs,
+ CFArrayRef *peerCerts) // fetched and retained
+{
+ OSStatus ortn = errSecSuccess;
+ SecTrustRef secTrust = NULL;
+
+ ortn = SSLGetPeerSecTrust(ctx, &secTrust);
+ if(ortn) {
+ printf("\n***Error obtaining peer SecTrustRef: %s\n",
+ sslGetSSLErrString(ortn));
+ return ortn;
+ }
+ if(secTrust == NULL) {
+ /* this is the normal case for resumed sessions, in which
+ * no cert evaluation is performed */
+ if(!pargs->silent) {
+ printf("...No SecTrust available - this is a resumed session, right?\n");
+ }
+ return errSecSuccess;
+ }
+
+
+ if (pargs->policies) {
+ SecTrustSetPolicies(secTrust, pargs->policies);
+ }
+
+ SecTrustResultType secTrustResult;
+ ortn = SecTrustEvaluate(secTrust, &secTrustResult);
+ if(ortn) {
+ printf("\n***Error on SecTrustEvaluate: %d\n", (int)ortn);
+ return ortn;
+ }
+ if(pargs->verbose) {
+ const char *res = NULL;
+ switch(secTrustResult) {
+ case kSecTrustResultInvalid:
+ res = "kSecTrustResultInvalid"; break;
+ case kSecTrustResultProceed:
+ res = "kSecTrustResultProceed"; break;
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+ case kSecTrustResultConfirm:
+#pragma clang diagnostic pop
+ res = "kSecTrustResultConfirm"; break;
+ case kSecTrustResultDeny:
+ res = "kSecTrustResultDeny"; break;
+ case kSecTrustResultUnspecified:
+ res = "kSecTrustResultUnspecified"; break;
+ case kSecTrustResultRecoverableTrustFailure:
+ res = "kSecTrustResultRecoverableTrustFailure"; break;
+ case kSecTrustResultFatalTrustFailure:
+ res = "kSecTrustResultFatalTrustFailure"; break;
+ case kSecTrustResultOtherError:
+ res = "kSecTrustResultOtherError"; break;
+ default:
+ res = "UNKNOWN"; break;
+ }
+ printf("\nSecTrustEvaluate(): secTrustResult %s\n", res);
+ }
+
+ switch(secTrustResult) {
+ case kSecTrustResultUnspecified:
+ /* cert chain valid, no special UserTrust assignments */
+ case kSecTrustResultProceed:
+ /* cert chain valid AND user explicitly trusts this */
+ break;
+ default:
+ printf("\n***SecTrustEvaluate reported secTrustResult %d\n",
+ (int)secTrustResult);
+ ortn = errSSLXCertChainInvalid;
+ break;
+ }
+
+ *peerCerts = NULL;
+
+#ifdef USE_CDSA_CRYPTO
+ /* one more thing - get peer certs in the form of an evidence chain */
+ CSSM_TP_APPLE_EVIDENCE_INFO *dummyEv;
+ OSStatus thisRtn = SecTrustGetResult(secTrust, &secTrustResult,
+ peerCerts, &dummyEv);
+ if(thisRtn) {
+ printSslErrStr("SecTrustGetResult", thisRtn);
+ }
+ else {
+ /* workaround for the fact that SSLGetPeerCertificates()
+ * leaves a retain count on each element in the returned array,
+ * requiring us to do a release on each cert.
+ */
+ CFIndex numCerts = CFArrayGetCount(*peerCerts);
+ for(CFIndex dex=0; dex<numCerts; dex++) {
+ CFRetain(CFArrayGetValueAtIndex(*peerCerts, dex));
+ }
+ }
+#endif
+ return ortn;
+}
+
+/* print reply received from server, safely */
+static void dumpAscii(
+ uint8_t *rcvBuf,
+ size_t len)
+{
+ char *cp = (char *)rcvBuf;
+ uint32_t i;
+ char c;
+
+ for(i=0; i<len; i++) {
+ c = *cp++;
+ if(c == '\0') {
+ break;
+ }
+ switch(c) {
+ case '\n':
+ printf("\\n");
+ break;
+ case '\r':
+ printf("\\r");
+ break;
+ default:
+ if(isprint(c) && (c != '\n')) {
+ printf("%c", c);
+ }
+ else {
+ printf("<%02X>", ((unsigned)c) & 0xff);
+ }
+ break;
+ }
+
+ }
+ printf("\n");
+}
+
+static void
+alpnFunc(SSLContextRef ctx,
+ void *info,
+ const void *alpnData,
+ size_t alpnDataLength)
+{
+ printf("[selected ALPN]");
+}
+
+
+/*
+ * Perform one SSL diagnostic session. Returns nonzero on error. Normally no
+ * output to stdout except initial "connecting to" message, unless there
+ * is a really screwed up error (i.e., something not directly related
+ * to the SSL connection).
+ */
+#define RCV_BUF_SIZE 256
+
+static OSStatus sslPing(
+ sslPingArgs *pargs)
+{
+ PeerSpec peerId;
+ otSocket sock = 0;
+ OSStatus ortn;
+ SSLContextRef ctx = NULL;
+ size_t length;
+ size_t actLen;
+ uint8_t rcvBuf[RCV_BUF_SIZE];
+ CFAbsoluteTime startHandshake;
+ CFAbsoluteTime endHandshake;
+
+ pargs->negVersion = kSSLProtocolUnknown;
+ pargs->negCipher = SSL_NULL_WITH_NULL_NULL;
+ pargs->peerCerts = NULL;
+
+ /* first make sure requested server is there */
+ ortn = MakeServerConnection(pargs->hostName, pargs->port, pargs->nonBlocking,
+ &sock, &peerId);
+ if(ortn) {
+ printf("MakeServerConnection returned %d; aborting\n", (int)ortn);
+ return ortn;
+ }
+ if(pargs->verbose) {
+ printf("...connected to server; starting SecureTransport\n");
+ }
+
+ /*
+ * Set up a SecureTransport session.
+ * First the standard calls.
+ */
+ ctx = SSLCreateContext(kCFAllocatorDefault, kSSLClientSide, kSSLStreamType);
+ if(ctx == NULL) {
+ printf("SSLCreateContext\n");
+ goto cleanup;
+ }
+ ortn = SSLSetIOFuncs(ctx, SocketRead, SocketWrite);
+ if(ortn) {
+ printSslErrStr("SSLSetIOFuncs", ortn);
+ goto cleanup;
+ }
+ ortn = SSLSetConnection(ctx, (SSLConnectionRef)(intptr_t)sock);
+ if(ortn) {
+ printSslErrStr("SSLSetConnection", ortn);
+ goto cleanup;
+ }
+ SSLConnectionRef getConn;
+ ortn = SSLGetConnection(ctx, &getConn);
+ if(ortn) {
+ printSslErrStr("SSLGetConnection", ortn);
+ goto cleanup;
+ }
+ if(getConn != (SSLConnectionRef)(intptr_t)sock) {
+ printf("***SSLGetConnection error\n");
+ ortn = errSecParam;
+ goto cleanup;
+ }
+ if(!pargs->allowHostnameSpoof) {
+ /* if this isn't set, it isn't checked by AppleX509TP */
+ const char *vfyHost = pargs->hostName;
+ if(pargs->vfyHostName) {
+ /* generally means we're expecting an error */
+ vfyHost = pargs->vfyHostName;
+ }
+ ortn = SSLSetPeerDomainName(ctx, vfyHost, strlen(vfyHost));
+ if(ortn) {
+ printSslErrStr("SSLSetPeerDomainName", ortn);
+ goto cleanup;
+ }
+ }
+
+ /*
+ * SecureTransport options.
+ */
+ if(pargs->acceptedProts) {
+ ortn = SSLSetProtocolVersionEnabled(ctx, kSSLProtocolAll, false);
+ if(ortn) {
+ printSslErrStr("SSLSetProtocolVersionEnabled(all off)", ortn);
+ goto cleanup;
+ }
+ for(const char *cp = pargs->acceptedProts; *cp; cp++) {
+ SSLProtocol prot;
+ switch(*cp) {
+ case '2':
+ prot = kSSLProtocol2;
+ break;
+ case '3':
+ prot = kSSLProtocol3;
+ break;
+ case 't':
+ prot = kTLSProtocol12;
+ break;
+ default:
+ usage(pargs->argv);
+ }
+ ortn = SSLSetProtocolVersionEnabled(ctx, prot, true);
+ if(ortn) {
+ printSslErrStr("SSLSetProtocolVersionEnabled", ortn);
+ goto cleanup;
+ }
+ }
+ }
+ else {
+ ortn = SSLSetProtocolVersion(ctx, pargs->tryVersion);
+ if(ortn) {
+ printSslErrStr("SSLSetProtocolVersion", ortn);
+ goto cleanup;
+ }
+ SSLProtocol getVers;
+ ortn = SSLGetProtocolVersion(ctx, &getVers);
+ if(ortn) {
+ printSslErrStr("SSLGetProtocolVersion", ortn);
+ goto cleanup;
+ }
+ if(getVers != pargs->tryVersion) {
+ printf("***SSLGetProtocolVersion screwup: try %s get %s\n",
+ sslGetProtocolVersionString(pargs->tryVersion),
+ sslGetProtocolVersionString(getVers));
+ ortn = errSecParam;
+ goto cleanup;
+ }
+ }
+ if(pargs->resumableEnable) {
+ const void *rtnId = NULL;
+ size_t rtnIdLen = 0;
+
+ ortn = SSLSetPeerID(ctx, &peerId, sizeof(PeerSpec));
+ if(ortn) {
+ printSslErrStr("SSLSetPeerID", ortn);
+ goto cleanup;
+ }
+ /* quick test of the get fcn */
+ ortn = SSLGetPeerID(ctx, &rtnId, &rtnIdLen);
+ if(ortn) {
+ printSslErrStr("SSLGetPeerID", ortn);
+ goto cleanup;
+ }
+ if((rtnId == NULL) || (rtnIdLen != sizeof(PeerSpec))) {
+ printf("***SSLGetPeerID screwup\n");
+ }
+ else if(memcmp(&peerId, rtnId, rtnIdLen) != 0) {
+ printf("***SSLGetPeerID data mismatch\n");
+ }
+ }
+ if(pargs->allowExpired) {
+ ortn = SSLSetAllowsExpiredCerts(ctx, true);
+ if(ortn) {
+ printSslErrStr("SSLSetAllowExpiredCerts", ortn);
+ goto cleanup;
+ }
+ }
+ if(pargs->allowExpiredRoot) {
+ ortn = SSLSetAllowsExpiredRoots(ctx, true);
+ if(ortn) {
+ printSslErrStr("SSLSetAllowsExpiredRoots", ortn);
+ goto cleanup;
+ }
+ }
+ if(pargs->disableCertVerify) {
+ ortn = SSLSetEnableCertVerify(ctx, false);
+ if(ortn) {
+ printSslErrStr("SSLSetEnableCertVerify", ortn);
+ goto cleanup;
+ }
+ }
+ if(pargs->allowAnyRoot) {
+ ortn = SSLSetAllowsAnyRoot(ctx, true);
+ if(ortn) {
+ printSslErrStr("SSLSetAllowAnyRoot", ortn);
+ goto cleanup;
+ }
+ }
+ if(pargs->cipherRestrict != '\0') {
+ ortn = sslSetCipherRestrictions(ctx, pargs->cipherRestrict);
+ if(ortn) {
+ goto cleanup;
+ }
+ }
+ if(pargs->anchorFile) {
+ ortn = sslAddTrustedRoot(ctx, pargs->anchorFile, pargs->replaceAnchors);
+ if(ortn) {
+ printf("***Error obtaining anchor file %s\n", pargs->anchorFile);
+ goto cleanup;
+ }
+ }
+ if(pargs->clientCerts) {
+ CFArrayRef dummy;
+ if(pargs->anchorFile == NULL) {
+ /* assume this is a root we want to implicitly trust */
+ ortn = addIdentityAsTrustedRoot(ctx, pargs->clientCerts);
+ if(ortn) {
+ goto cleanup;
+ }
+ }
+ ortn = SSLSetCertificate(ctx, pargs->clientCerts);
+ if(ortn) {
+ printSslErrStr("SSLSetCertificate", ortn);
+ goto cleanup;
+ }
+ /* quickie test for this new function */
+ ortn = SSLGetCertificate(ctx, &dummy);
+ if(ortn) {
+ printSslErrStr("SSLGetCertificate", ortn);
+ goto cleanup;
+ }
+ if(dummy != pargs->clientCerts) {
+ printf("***SSLGetCertificate error\n");
+ ortn = errSecIO;
+ goto cleanup;
+ }
+ }
+ if (pargs->alpnNames) {
+ CFMutableDataRef alpn = CFDataCreateMutable(NULL, 0);
+
+ CFArrayForEach(pargs->alpnNames, ^(const void *value) {
+ CFDataRef data = (CFDataRef)value;
+ uint8_t len = CFDataGetLength(data);
+ CFDataAppendBytes(alpn, (const UInt8 *)&len, sizeof(len));
+ CFDataAppend(alpn, data);
+ });
+
+ SSLSetALPNData(ctx, CFDataGetBytePtr(alpn), CFDataGetLength(alpn));
+ SSLSetALPNFunc(ctx, alpnFunc, (void *)NULL);
+ CFRelease(alpn);
+ }
+
+ /*** end options ***/
+
+ if(pargs->verbose) {
+ printf("...starting SSL handshake\n");
+ }
+ startHandshake = CFAbsoluteTimeGetCurrent();
+
+ do
+ { ortn = SSLHandshake(ctx);
+ if((ortn == errSSLWouldBlock) && !pargs->silent) {
+ /* keep UI responsive */
+ sslOutputDot();
+ }
+ } while (ortn == errSSLWouldBlock);
+
+ endHandshake = CFAbsoluteTimeGetCurrent();
+ pargs->handshakeTimeOp = endHandshake - startHandshake;
+ if(pargs->numHandshakes == 0) {
+ /* special case, this one is always way longer */
+ pargs->handshakeTimeFirst = pargs->handshakeTimeOp;
+ }
+ else {
+ /* normal running total */
+ pargs->handshakeTimeTotal += pargs->handshakeTimeOp;
+ }
+ pargs->numHandshakes++;
+
+ ortn = SSLCopyPeerTrust(ctx, &pargs->peerTrust);
+ if(ortn) {
+ printf("***SSLCopyPeerTrust error %" PRIdOSStatus "\n", ortn);
+ pargs->peerTrust = NULL;
+ }
+
+ /* ditto */
+ SSLGetClientCertificateState(ctx, &pargs->certState);
+ SSLGetNegotiatedCipher(ctx, &pargs->negCipher);
+ SSLGetNegotiatedProtocolVersion(ctx, &pargs->negVersion);
+ pargs->sessionIDLength = MAX_SESSION_ID_LENGTH;
+ SSLGetResumableSessionInfo(ctx, &pargs->sessionWasResumed, pargs->sessionID,
+ &pargs->sessionIDLength);
+
+ {
+ OSStatus certRtn = sslEvaluateTrust(ctx, pargs, &pargs->peerCerts);
+
+ if (certRtn && !pargs->manualCertVerify) {
+ SSLCopyPeerCertificates(ctx, &pargs->peerCerts);
+ certRtn = 0;
+ }
+
+ if(certRtn && !ortn ) {
+ ortn = certRtn;
+ }
+ }
+
+ if(ortn) {
+ if(!pargs->silent) {
+ printf("\n");
+ }
+ goto cleanup;
+ }
+
+ if(pargs->verbose) {
+ printf("...SSL handshake complete\n");
+ }
+ length = strlen(pargs->getMsg);
+ (void) SSLWrite(ctx, pargs->getMsg, length, &actLen);
+
+ /*
+ * Try to snag RCV_BUF_SIZE bytes. Exit if (!keepConnected and we get any data
+ * at all), or (keepConnected and err != (none, wouldBlock)).
+ */
+ while (1) {
+ actLen = 0;
+ if(pargs->dumpRxData) {
+ size_t avail = 0;
+
+ ortn = SSLGetBufferedReadSize(ctx, &avail);
+ if(ortn) {
+ printf("***SSLGetBufferedReadSize error\n");
+ break;
+ }
+ if(avail != 0) {
+ printf("\n%d bytes available: ", (int)avail);
+ }
+ }
+ ortn = SSLRead(ctx, rcvBuf, RCV_BUF_SIZE, &actLen);
+ if((actLen == 0) && !pargs->silent) {
+ sslOutputDot();
+ }
+ if((actLen == 0) && (ortn == errSecSuccess)) {
+ printf("***Radar 2984932 confirmed***\n");
+ }
+ if (ortn == errSSLWouldBlock) {
+ /* for this loop, these are identical */
+ ortn = errSecSuccess;
+ }
+ if((actLen > 0) && pargs->dumpRxData) {
+ dumpAscii(rcvBuf, actLen);
+ }
+ if(ortn != errSecSuccess) {
+ /* connection closed by server or by error */
+ break;
+ }
+ if(!pargs->keepConnected && (actLen > 0)) {
+ /* good enough, we connected */
+ break;
+ }
+ }
+ if(!pargs->silent) {
+ printf("\n");
+ }
+
+ /* snag these again in case of renegotiate */
+ SSLGetClientCertificateState(ctx, &pargs->certState);
+ SSLGetNegotiatedCipher(ctx, &pargs->negCipher);
+ SSLGetNegotiatedProtocolVersion(ctx, &pargs->negVersion);
+
+ /* convert normal "shutdown" into zero err rtn */
+ if(ortn == errSSLClosedGraceful) {
+ ortn = errSecSuccess;
+ }
+ if((ortn == errSSLClosedNoNotify) && !pargs->requireNotify) {
+ /* relaxed disconnect rules */
+ ortn = errSecSuccess;
+ }
+cleanup: ;
+ /*
+ * always do close, even on error - to flush outgoing write queue
+ */
+ OSStatus cerr = SSLClose(ctx);
+ if(ortn == errSecSuccess) {
+ ortn = cerr;
+ }
+ if(sock) {
+ endpointShutdown(sock);
+ }
+ if(ctx) {
+ CFRelease(ctx);
+ }
+ return ortn;
+}
+
+#if TARGET_OS_IPHONE
+
+static void add_key(const void *key, const void *value, void *context) {
+ CFArrayAppendValue((CFMutableArrayRef)context, key);
+}
+
+
+static void showInfo(CFDictionaryRef info) {
+ CFIndex dict_count, key_ix, key_count;
+ CFMutableArrayRef keys = NULL;
+ CFIndex maxWidth = 20; /* Maybe precompute this or grab from context? */
+
+ dict_count = CFDictionaryGetCount(info);
+ keys = CFArrayCreateMutable(kCFAllocatorDefault, dict_count,
+ &kCFTypeArrayCallBacks);
+ CFDictionaryApplyFunction(info, add_key, keys);
+ key_count = CFArrayGetCount(keys);
+ CFArraySortValues(keys, CFRangeMake(0, key_count),
+ (CFComparatorFunction)CFStringCompare, 0);
+
+ for (key_ix = 0; key_ix < key_count; ++key_ix) {
+ CFStringRef key = (CFStringRef)CFArrayGetValueAtIndex(keys, key_ix);
+ CFTypeRef value = CFDictionaryGetValue(info, key);
+ CFMutableStringRef line = CFStringCreateMutable(NULL, 0);
+
+ CFStringAppend(line, key);
+ CFIndex jx;
+ for (jx = CFStringGetLength(key);
+ jx < maxWidth; ++jx) {
+ CFStringAppend(line, CFSTR(" "));
+ }
+ CFStringAppend(line, CFSTR(" : "));
+ if (CFStringGetTypeID() == CFGetTypeID(value)) {
+ CFStringAppend(line, (CFStringRef)value);
+ } else if (CFDateGetTypeID() == CFGetTypeID(value)) {
+ CFLocaleRef lc = CFLocaleCopyCurrent();
+ CFDateFormatterRef df = CFDateFormatterCreate(NULL, lc,
+ kCFDateFormatterFullStyle, kCFDateFormatterFullStyle);
+ CFDateRef date = (CFDateRef)value;
+ CFStringRef ds = CFDateFormatterCreateStringWithDate(NULL, df,
+ date);
+ CFStringAppend(line, ds);
+ CFRelease(ds);
+ CFRelease(df);
+ CFRelease(lc);
+ } else if (CFURLGetTypeID() == CFGetTypeID(value)) {
+ CFURLRef url = (CFURLRef)value;
+ CFStringAppend(line, CFSTR("<"));
+ CFStringAppend(line, CFURLGetString(url));
+ CFStringAppend(line, CFSTR(">"));
+ } else if (CFDataGetTypeID() == CFGetTypeID(value)) {
+ CFDataRef v_d = (CFDataRef)value;
+ CFStringRef v_s = CFStringCreateFromExternalRepresentation(
+ kCFAllocatorDefault, v_d, kCFStringEncodingUTF8);
+ if (v_s) {
+ CFStringAppend(line, CFSTR("/"));
+ CFStringAppend(line, v_s);
+ CFStringAppend(line, CFSTR("/ "));
+ CFRelease(v_s);
+ }
+ const uint8_t *bytes = CFDataGetBytePtr(v_d);
+ CFIndex len = CFDataGetLength(v_d);
+ for (jx = 0; jx < len; ++jx) {
+ CFStringAppendFormat(line, NULL, CFSTR("%.02X"), bytes[jx]);
+ }
+ } else {
+ CFStringAppendFormat(line, NULL, CFSTR("%@"), value);
+ }
+ CFStringWriteToFileWithNewline(line, stdout);
+ CFRelease(line);
+ }
+ CFRelease(keys);
+}
+#endif
+
+static void showPeerTrust(SecTrustRef peerTrust, bool verbose) {
+
+ if(peerTrust == NULL) {
+ return;
+ }
+#if TARGET_OS_IPHONE
+ CFIndex numCerts;
+ CFIndex i;
+
+ printf("\n=============== Peer Trust Properties ===============\n");
+ CFArrayRef plist = SecTrustCopyProperties(peerTrust);
+ if (plist) {
+ print_plist(plist);
+ CFRelease(plist);
+ }
+
+ printf("\n================== Peer Trust Info ==================\n");
+ CFDictionaryRef info = SecTrustCopyInfo(peerTrust);
+ if (info && CFDictionaryGetCount(info)) {
+ showInfo(info);
+ }
+ if (info)
+ CFRelease(info);
+
+ numCerts = SecTrustGetCertificateCount(peerTrust);
+ for(i=0; i<numCerts; i++) {
+ plist = SecTrustCopySummaryPropertiesAtIndex(peerTrust, i);
+ printf("\n============= Peer Trust Cert %lu Summary =============\n\n", i);
+ print_plist(plist);
+ if (plist)
+ CFRelease(plist);
+ printf("\n============= Peer Trust Cert %lu Details =============\n\n", i);
+ plist = SecTrustCopyDetailedPropertiesAtIndex(peerTrust, i);
+ print_plist(plist);
+ if (plist)
+ CFRelease(plist);
+ printf("\n============= End of Peer Trust Cert %lu ==============\n", i);
+ }
+#endif
+}
+
+static void showPeerCerts(
+ CFArrayRef __unused peerCerts,
+ bool __unused verbose)
+{
+#if 0
+ CFIndex numCerts;
+ SecCertificateRef certRef;
+ CFIndex i;
+
+ if(peerCerts == NULL) {
+ return;
+ }
+ numCerts = CFArrayGetCount(peerCerts);
+ for(i=0; i<numCerts; i++) {
+ certRef = (SecCertificateRef)CFArrayGetValueAtIndex(peerCerts, i);
+ printf("\n==================== Peer Cert %lu ====================\n\n", i);
+ print_cert(certRef, verbose);
+ printf("\n================ End of Peer Cert %lu =================\n", i);
+ }
+#endif
+}
+
+static void writePeerCerts(
+ CFArrayRef peerCerts,
+ const char *fileBase)
+{
+ CFIndex numCerts;
+ SecCertificateRef certRef;
+ CFIndex i;
+ char fileName[100];
+
+ if(peerCerts == NULL) {
+ return;
+ }
+ numCerts = CFArrayGetCount(peerCerts);
+ for(i=0; i<numCerts; i++) {
+ sprintf(fileName, "%s%02d.cer", fileBase, (int)i);
+ certRef = (SecCertificateRef)CFArrayGetValueAtIndex(peerCerts, i);
+ CFDataRef derCert = SecCertificateCopyData(certRef);
+ if (derCert) {
+ writeFile(fileName, CFDataGetBytePtr(derCert),
+ CFDataGetLength(derCert));
+ CFRelease(derCert);
+ }
+ }
+ printf("...wrote %lu certs to fileBase %s\n", numCerts, fileBase);
+}
+
+/*
+ * Show result of an sslPing().
+ * Assumes the following from sslPingArgs:
+ *
+ * verbose
+ * tryVersion
+ * acceptedProts
+ * negVersion
+ * negCipher
+ * peerCerts
+ * certState
+ * sessionWasResumed
+ * sessionID
+ * sessionIDLength
+ * handshakeTime
+ */
+static void showSSLResult(
+ const sslPingArgs *pargs,
+ OSStatus err,
+ int displayPeerCerts,
+ char *fileBase) // non-NULL: write certs to file
+{
+ CFIndex numPeerCerts;
+
+ printf("\n");
+
+ if(pargs->acceptedProts) {
+ printf(" Allowed SSL versions : %s\n", pargs->acceptedProts);
+ }
+ else {
+ printf(" Attempted SSL version : %s\n",
+ sslGetProtocolVersionString(pargs->tryVersion));
+ }
+
+ printf(" Result : %s\n", sslGetSSLErrString(err));
+ printf(" Negotiated SSL version : %s\n",
+ sslGetProtocolVersionString(pargs->negVersion));
+ printf(" Negotiated CipherSuite : %s\n",
+ sslGetCipherSuiteString(pargs->negCipher));
+ if(pargs->certState != kSSLClientCertNone) {
+ printf(" Client Cert State : %s\n",
+ sslGetClientCertStateString(pargs->certState));
+ }
+ if(pargs->verbose) {
+ printf(" Resumed Session : ");
+ if(pargs->sessionWasResumed) {
+ for(unsigned dex=0; dex<pargs->sessionIDLength; dex++) {
+ printf("%02X ", pargs->sessionID[dex]);
+ if(((dex % 8) == 7) && (dex != (pargs->sessionIDLength - 1))) {
+ printf("\n ");
+ }
+ }
+ printf("\n");
+ }
+ else {
+ printf("NOT RESUMED\n");
+ }
+ printf(" Handshake time : %f seconds\n", pargs->handshakeTimeOp);
+ }
+ if(pargs->peerCerts == NULL) {
+ numPeerCerts = 0;
+ }
+ else {
+ numPeerCerts = CFArrayGetCount(pargs->peerCerts);
+ }
+ printf(" Number of server certs : %lu\n", numPeerCerts);
+ if(numPeerCerts != 0) {
+ if (displayPeerCerts == 1) {
+ showPeerCerts(pargs->peerCerts, false);
+ } else if (displayPeerCerts == 2) {
+ showPeerTrust(pargs->peerTrust, false);
+ }
+ if(fileBase != NULL) {
+ writePeerCerts(pargs->peerCerts, fileBase);
+ }
+ }
+
+ printf("\n");
+}
+
+static int verifyProtocol(
+ bool verifyProt,
+ SSLProtocol maxProtocol,
+ SSLProtocol reqProtocol,
+ SSLProtocol negProtocol)
+{
+ if(!verifyProt) {
+ return 0;
+ }
+ if(reqProtocol > maxProtocol) {
+ /* known not to support this attempt, relax */
+ reqProtocol = maxProtocol;
+ }
+ if(reqProtocol != negProtocol) {
+ printf("***Expected protocol %s; negotiated %s\n",
+ sslGetProtocolVersionString(reqProtocol),
+ sslGetProtocolVersionString(negProtocol));
+ return 1;
+ }
+ else {
+ return 0;
+ }
+}
+
+static int verifyClientCertState(
+ bool verifyCertState,
+ SSLClientCertificateState expectState,
+ SSLClientCertificateState gotState)
+{
+ if(!verifyCertState) {
+ return 0;
+ }
+ if(expectState == gotState) {
+ return 0;
+ }
+ printf("***Expected clientCertState %s; got %s\n",
+ sslGetClientCertStateString(expectState),
+ sslGetClientCertStateString(gotState));
+ return 1;
+}
+
+static SSLProtocol charToProt(
+ char c, // 2, 3, t
+ char **argv)
+{
+ switch(c) {
+ case '2':
+ return kSSLProtocol2;
+ case '3':
+ return kSSLProtocol3;
+ case 't':
+ return kTLSProtocol12;
+ default:
+ usage(argv);
+ }
+ /* NOT REACHED */
+ return kSSLProtocolUnknown;
+}
+
+int main(int argc, char **argv)
+{
+ OSStatus err;
+ int arg;
+ char *argp;
+ char getMsg[300];
+ char fullFileBase[100];
+ int ourRtn = 0; // exit status - sum of all errors
+ unsigned loop;
+ SecKeychainRef serverKc = nil;
+ sslPingArgs pargs;
+
+ /* user-spec'd parameters */
+ const char *getPath = DEFAULT_PATH;
+ char *fileBase = NULL;
+ int displayCerts = 0;
+ bool doSslV2 = false;
+ bool doSslV3 = false;
+ bool doTlsV1 = true;
+ bool doTlsV11 = false;
+ bool doTlsV12 = false;
+ bool protXOnly = false; // kSSLProtocol3Only, kTLSProtocol1Only
+ bool doProtUnknown = false;
+ unsigned loopCount = 1;
+ bool doPause = false;
+ bool pauseFirstLoop = false;
+ bool verifyProt = false;
+ SSLProtocol maxProtocol = kTLSProtocol12; // for verifying negotiated
+ // protocol
+ char *acceptedProts = NULL;
+ char *keyChainName = NULL;
+ char *getMsgSpec = NULL;
+ bool vfyCertState = false;
+ SSLClientCertificateState expectCertState = kSSLClientCertNone;
+ bool displayHandshakeTimes = false;
+ bool completeCertChain = false;
+
+ /* special case - one arg of "h" or "-h" or "hv" */
+ if(argc == 2) {
+ if((strcmp(argv[1], "h") == 0) || (strcmp(argv[1], "-h") == 0)) {
+ usage(argv);
+ }
+ if(strcmp(argv[1], "hv") == 0) {
+ usageVerbose(argv);
+ }
+ }
+
+ /* set up defaults */
+ memset(&pargs, 0, sizeof(sslPingArgs));
+ pargs.hostName = DEFAULT_HOST;
+ pargs.port = DEFAULT_PORT;
+ pargs.resumableEnable = true;
+ pargs.argv = argv;
+
+ for(arg=1; arg<argc; arg++) {
+ argp = argv[arg];
+ if(arg == 1) {
+ /* first arg, is always hostname; '-' means default */
+ if(argp[0] != '-') {
+ pargs.hostName = argp;
+ }
+ continue;
+ }
+ if(argp[0] == '/') {
+ /* path always starts with leading slash */
+ getPath = argp;
+ continue;
+ }
+ /* options */
+ switch(argp[0]) {
+ case 'Z': {
+ if(++arg == argc) {
+ /* requires another arg */
+ usage(argv);
+ }
+ if (pargs.alpnNames == NULL) {
+ pargs.alpnNames = CFArrayCreateMutableForCFTypes(NULL);
+ }
+
+ CFDataRef alpn = CFDataCreate(NULL, (const UInt8 *)argv[arg], strlen(argv[arg]));
+ CFArrayAppendValue(pargs.alpnNames, alpn);
+ CFReleaseNull(alpn);
+ break;
+ }
+ case 'W':
+ case 'w': {
+ CFDictionaryRef context = NULL;
+
+ if(++arg == argc) {
+ /* requires another arg */
+ usage(argv);
+ }
+
+ if (argp[0] == 'W') {
+ context = CFDictionaryCreateForCFTypes(NULL,
+ CFSTR("AppleServerAuthenticationAllowUATAPN"), kCFBooleanTrue,
+ CFSTR("AppleServerAuthenticationAllowUATIDS"), kCFBooleanTrue,
+ CFSTR("AppleServerAuthenticationAllowUATGS"), kCFBooleanTrue,
+ NULL);
+ }
+ const char *verifyName = pargs.hostName;
+
+ if (pargs.policies == NULL) {
+ pargs.policies = CFArrayCreateMutableForCFTypes(NULL);
+ }
+
+ if (pargs.vfyHostName)
+ verifyName = pargs.vfyHostName;
+
+ SecPolicyRef policy = NULL;
+ CFStringRef hostname = CFStringCreateWithCString(NULL, verifyName, kCFStringEncodingUTF8);
+
+ if (strcasecmp(argv[arg], "PushLegacy") == 0) {
+ policy = SecPolicyCreateApplePushServiceLegacy(hostname);
+ } else if (strcasecmp(argv[arg], "Push") == 0) {
+ policy = SecPolicyCreateApplePushService(hostname, context);
+ } else if (strcasecmp(argv[arg], "IDS") == 0) {
+ policy = SecPolicyCreateAppleIDSServiceContext(hostname, context);
+ } else if (strcasecmp(argv[arg], "GS") == 0) {
+ policy = SecPolicyCreateAppleGSService(hostname, context);
+ } else {
+ printf("unknown policy: %s", argv[arg]);
+ CFReleaseNull(hostname);
+ CFReleaseNull(context);
+ usage(argv);
+ }
+
+ if (policy) {
+ CFArrayAppendValue(pargs.policies, policy);
+ }
+
+ CFReleaseNull(policy);
+ CFReleaseNull(hostname);
+ CFReleaseNull(context);
+
+ break;
+ }
+ case 'e':
+ pargs.allowExpired = true;
+ break;
+ case 'E':
+ pargs.allowExpiredRoot = true;
+ break;
+ case 'x':
+ pargs.disableCertVerify = true;
+ break;
+ case 'M':
+ pargs.disableCertVerify = true; // implied
+ pargs.manualCertVerify = true;
+ break;
+ case 'a':
+ if(++arg == argc) {
+ /* requires another arg */
+ usage(argv);
+ }
+ pargs.anchorFile = argv[arg];
+ break;
+ case 'A':
+ if(++arg == argc) {
+ /* requires another arg */
+ usage(argv);
+ }
+ pargs.anchorFile = argv[arg];
+ pargs.replaceAnchors = true;
+ break;
+ case 'r':
+ pargs.allowAnyRoot = true;
+ break;
+ case 'd':
+ pargs.dumpRxData = true;
+ break;
+ case 'c':
+ displayCerts++;
+ break;
+ case 'f':
+ if(++arg == argc) {
+ /* requires another arg */
+ usage(argv);
+ }
+ fileBase = argv[arg];
+ break;
+ case 'C':
+ pargs.cipherRestrict = argp[2];
+ break;
+ case '2':
+ doSslV3 = doTlsV1 = doTlsV11 = false;
+ doSslV2 = true;
+ break;
+ case '3':
+ doSslV2 = doTlsV1 = doTlsV11 = doTlsV12 = false;
+ doSslV3 = true;
+ break;
+ case 't':
+ doSslV2 = doSslV3 = doTlsV11 = doTlsV12 = false;
+ doTlsV1 = true;
+ break;
+ case '%':
+ doSslV2 = doSslV3 = doTlsV1 = false;
+ doTlsV11 = true;
+ break;
+ case '^':
+ doSslV2 = doSslV3 = doTlsV1 = doTlsV12 = false;
+ doTlsV12 = true;
+ break;
+ case 'L':
+ doSslV2 = doSslV3 = doTlsV1 = doTlsV11 = doTlsV12 = true;
+ break;
+ case 'o':
+ protXOnly = true;
+ break;
+ case 'u':
+ doSslV2 = doSslV3 = doTlsV1 = doTlsV11 = doTlsV12 = false;
+ doProtUnknown = true;
+ break;
+ case 'K':
+ pargs.keepConnected = true;
+ break;
+ case 'n':
+ pargs.requireNotify = true;
+ pargs.keepConnected = true;
+ break;
+ case 'R':
+ pargs.resumableEnable = false;
+ break;
+ case 'b':
+ pargs.nonBlocking = true;
+ break;
+ case 'v':
+ verifyProt = true;
+ break;
+ case 'm':
+ if(argp[1] != '=') {
+ usage(argv);
+ }
+ verifyProt = true; // implied
+ maxProtocol = charToProt(argp[2], argv);
+ break;
+ case 'g':
+ if(argp[1] != '=') {
+ usage(argv);
+ }
+ acceptedProts = argv[arg];
+ doSslV3 = doSslV2 = doTlsV1 = doTlsV11 = doTlsV12 = false;
+ break;
+ case 'l':
+ if(++arg == argc) {
+ /* requires another arg */
+ usage(argv);
+ }
+ loopCount = atoi(argv[arg]);
+ if(loopCount == 0) {
+ printf("***bad loopCount\n");
+ usage(argv);
+ }
+ break;
+ case 'P':
+ if(++arg == argc) {
+ /* requires another arg */
+ usage(argv);
+ }
+ pargs.port = atoi(argv[arg]);
+ break;
+ case 'H':
+ pargs.allowHostnameSpoof = true;
+ break;
+ case 'F':
+ if(++arg == argc) {
+ /* requires another arg */
+ usage(argv);
+ }
+ pargs.vfyHostName = argv[arg];
+ break;
+ case 'k':
+ if(++arg == argc) {
+ /* requires another arg */
+ usage(argv);
+ }
+ keyChainName = &argp[2];
+ break;
+ case 'G':
+ getMsgSpec = &argp[2];
+ break;
+ case 'T':
+ if(argp[1] != '=') {
+ usage(argv);
+ }
+ vfyCertState = true;
+ switch(argp[2]) {
+ case 'n':
+ expectCertState = kSSLClientCertNone;
+ break;
+ case 'r':
+ expectCertState = kSSLClientCertRequested;
+ break;
+ case 's':
+ expectCertState = kSSLClientCertSent;
+ break;
+ case 'j':
+ expectCertState = kSSLClientCertRejected;
+ break;
+ default:
+ usage(argv);
+ }
+ break;
+ case 'z':
+ pargs.password = &argp[2];
+ break;
+ case 'p':
+ doPause = true;
+ break;
+ case '7':
+ pauseFirstLoop = true;
+ break;
+ case 'q':
+ pargs.quiet = true;
+ break;
+ case 'V':
+ pargs.verbose = true;
+ break;
+ case 's':
+ pargs.silent = pargs.quiet = true;
+ break;
+ case 'N':
+ displayHandshakeTimes = true;
+ break;
+ case '8':
+ completeCertChain = true;
+ break;
+ case 'h':
+ if(pargs.verbose || (argp[1] == 'v')) {
+ usageVerbose(argv);
+ }
+ else {
+ usage(argv);
+ }
+ break;
+ default:
+ usage(argv);
+ break;
+ }
+ }
+ if(getMsgSpec) {
+ pargs.getMsg = getMsgSpec;
+ }
+ else {
+ sprintf(getMsg, "%s %s %s",
+ DEFAULT_GETMSG, getPath, DEFAULT_GET_SUFFIX);
+ pargs.getMsg = getMsg;
+ }
+
+ /* get client cert and optional encryption cert as CFArrayRef */
+ if(keyChainName) {
+ pargs.clientCerts = getSslCerts(keyChainName, false, completeCertChain,
+ pargs.anchorFile, &serverKc);
+ if(pargs.clientCerts == nil) {
+ exit(1);
+ }
+#ifdef USE_CDSA_CRYPTO
+ if(pargs.password) {
+ OSStatus ortn = SecKeychainUnlock(serverKc,
+ strlen(pargs.password), pargs.password, true);
+ if(ortn) {
+ printf("SecKeychainUnlock returned %d\n", (int)ortn);
+ /* oh well */
+ }
+ }
+#endif
+ }
+
+ {
+ struct sigaction sa;
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_flags = SA_RESTART;
+ sa.sa_handler = sigpipe;
+ sigaction(SIGPIPE, &sa, NULL);
+ }
+
+ for(loop=0; loop<loopCount; loop++) {
+ /*
+ * One pass for each protocol version, skipping any explicit version if
+ * an attempt at a higher version and succeeded in doing so successfully fell
+ * back.
+ */
+ if(doTlsV12) {
+ pargs.tryVersion = kTLSProtocol12;
+ pargs.acceptedProts = NULL;
+ if(!pargs.silent) {
+ printf("Connecting to host %s with TLS V1.2...", pargs.hostName);
+ }
+ fflush(stdout);
+ err = sslPing(&pargs);
+ if(err) {
+ ourRtn++;
+ }
+ if(!pargs.quiet) {
+ if(fileBase) {
+ sprintf(fullFileBase, "%s_v3.1", fileBase);
+ }
+ showSSLResult(&pargs,
+ err,
+ displayCerts,
+ fileBase ? fullFileBase : NULL);
+ }
+ CFReleaseNull(pargs.peerCerts);
+ if(!err) {
+ /* deal with fallbacks, skipping redundant tests */
+ switch(pargs.negVersion) {
+ case kTLSProtocol11:
+ doTlsV11 =false;
+ break;
+ case kTLSProtocol1:
+ doTlsV11 =false;
+ doTlsV1 =false;
+ break;
+ case kSSLProtocol3:
+ doTlsV11 =false;
+ doTlsV1 =false;
+ doSslV3 = false;
+ break;
+ case kSSLProtocol2:
+ doTlsV11 =false;
+ doTlsV1 =false;
+ doSslV3 = false;
+ doSslV2 = false;
+ break;
+ default:
+ break;
+ }
+ ourRtn += verifyProtocol(verifyProt, maxProtocol, kTLSProtocol12,
+ pargs.negVersion);
+ }
+ /* note we do this regardless since the client state might be
+ * the cause of a failure */
+ ourRtn += verifyClientCertState(vfyCertState, expectCertState,
+ pargs.certState);
+ }
+ if(doTlsV11) {
+ pargs.tryVersion = kTLSProtocol11;
+ pargs.acceptedProts = NULL;
+ if(!pargs.silent) {
+ printf("Connecting to host %s with TLS V1.1...", pargs.hostName);
+ }
+ fflush(stdout);
+ err = sslPing(&pargs);
+ if(err) {
+ ourRtn++;
+ }
+ if(!pargs.quiet) {
+ if(fileBase) {
+ sprintf(fullFileBase, "%s_v3.1", fileBase);
+ }
+ showSSLResult(&pargs,
+ err,
+ displayCerts,
+ fileBase ? fullFileBase : NULL);
+ }
+ CFReleaseNull(pargs.peerCerts);
+ if(!err) {
+ /* deal with fallbacks, skipping redundant tests */
+ switch(pargs.negVersion) {
+ case kTLSProtocol1:
+ doTlsV1 =false;
+ break;
+ case kSSLProtocol3:
+ doTlsV1 =false;
+ doSslV3 = false;
+ break;
+ case kSSLProtocol2:
+ doTlsV1 =false;
+ doSslV3 = false;
+ doSslV2 = false;
+ break;
+ default:
+ break;
+ }
+ ourRtn += verifyProtocol(verifyProt, maxProtocol, kTLSProtocol11,
+ pargs.negVersion);
+ }
+ /* note we do this regardless since the client state might be
+ * the cause of a failure */
+ ourRtn += verifyClientCertState(vfyCertState, expectCertState,
+ pargs.certState);
+ }
+ if(doTlsV1) {
+ pargs.tryVersion =
+ protXOnly ? kTLSProtocol1Only : kTLSProtocol1;
+ pargs.acceptedProts = NULL;
+ if(!pargs.silent) {
+ printf("Connecting to host %s with TLS V1...", pargs.hostName);
+ }
+ fflush(stdout);
+ err = sslPing(&pargs);
+ if(err) {
+ ourRtn++;
+ }
+ if(!pargs.quiet) {
+ if(fileBase) {
+ sprintf(fullFileBase, "%s_v3.1", fileBase);
+ }
+ showSSLResult(&pargs,
+ err,
+ displayCerts,
+ fileBase ? fullFileBase : NULL);
+ }
+ CFReleaseNull(pargs.peerCerts);
+ if(!err) {
+ /* deal with fallbacks, skipping redundant tests */
+ switch(pargs.negVersion) {
+ case kSSLProtocol3:
+ doSslV3 = false;
+ break;
+ case kSSLProtocol2:
+ doSslV3 = false;
+ doSslV2 = false;
+ break;
+ default:
+ break;
+ }
+ ourRtn += verifyProtocol(verifyProt, maxProtocol, kTLSProtocol1,
+ pargs.negVersion);
+ }
+ /* note we do this regardless since the client state might be
+ * the cause of a failure */
+ ourRtn += verifyClientCertState(vfyCertState, expectCertState,
+ pargs.certState);
+ }
+ if(doSslV3) {
+ pargs.tryVersion = protXOnly ? kSSLProtocol3Only : kSSLProtocol3;
+ pargs.acceptedProts = NULL;
+ if(!pargs.silent) {
+ printf("Connecting to host %s with SSL V3...", pargs.hostName);
+ }
+ fflush(stdout);
+ err = sslPing(&pargs);
+ if(err) {
+ ourRtn++;
+ }
+ if(!pargs.quiet) {
+ if(fileBase) {
+ sprintf(fullFileBase, "%s_v3.0", fileBase);
+ }
+ showSSLResult(&pargs,
+ err,
+ displayCerts,
+ fileBase ? fullFileBase : NULL);
+ }
+ CFReleaseNull(pargs.peerCerts);
+ if(!err) {
+ /* deal with fallbacks, skipping redundant tests */
+ switch(pargs.negVersion) {
+ case kSSLProtocol2:
+ doSslV2 = false;
+ break;
+ default:
+ break;
+ }
+ ourRtn += verifyProtocol(verifyProt, maxProtocol, kSSLProtocol3,
+ pargs.negVersion);
+ }
+ /* note we do this regardless since the client state might be
+ * the cause of a failure */
+ ourRtn += verifyClientCertState(vfyCertState, expectCertState,
+ pargs.certState);
+ }
+
+ if(doSslV2) {
+ if(fileBase) {
+ sprintf(fullFileBase, "%s_v2", fileBase);
+ }
+ if(!pargs.silent) {
+ printf("Connecting to host %s with SSL V2...", pargs.hostName);
+ }
+ fflush(stdout);
+ pargs.tryVersion = kSSLProtocol2;
+ pargs.acceptedProts = NULL;
+ err = sslPing(&pargs);
+ if(err) {
+ ourRtn++;
+ }
+ if(!pargs.quiet) {
+ if(fileBase) {
+ sprintf(fullFileBase, "%s_v2", fileBase);
+ }
+ showSSLResult(&pargs,
+ err,
+ displayCerts,
+ fileBase ? fullFileBase : NULL);
+ }
+ CFReleaseNull(pargs.peerCerts);
+ if(!err) {
+ ourRtn += verifyProtocol(verifyProt, maxProtocol, kSSLProtocol2,
+ pargs.negVersion);
+ }
+ /* note we do this regardless since the client state might be
+ * the cause of a failure */
+ ourRtn += verifyClientCertState(vfyCertState, expectCertState,
+ pargs.certState);
+ }
+ if(doProtUnknown) {
+ if(!pargs.silent) {
+ printf("Connecting to host %s with kSSLProtocolUnknown...",
+ pargs.hostName);
+ }
+ fflush(stdout);
+ pargs.tryVersion = kSSLProtocolUnknown;
+ pargs.acceptedProts = NULL;
+ err = sslPing(&pargs);
+ if(err) {
+ ourRtn++;
+ }
+ if(!pargs.quiet) {
+ if(fileBase) {
+ sprintf(fullFileBase, "%s_def", fileBase);
+ }
+ showSSLResult(&pargs,
+ err,
+ displayCerts,
+ fileBase ? fullFileBase : NULL);
+ }
+ CFReleaseNull(pargs.peerCerts);
+ }
+ if(acceptedProts != NULL) {
+ pargs.acceptedProts = acceptedProts;
+ pargs.tryVersion = kSSLProtocolUnknown; // not used
+ if(!pargs.silent) {
+ printf("Connecting to host %s with acceptedProts %s...",
+ pargs.hostName, pargs.acceptedProts);
+ }
+ fflush(stdout);
+ err = sslPing(&pargs);
+ if(err) {
+ ourRtn++;
+ }
+ if(!pargs.quiet) {
+ if(fileBase) {
+ sprintf(fullFileBase, "%s_def", fileBase);
+ }
+ showSSLResult(&pargs,
+ err,
+ displayCerts,
+ fileBase ? fullFileBase : NULL);
+ }
+ CFReleaseNull(pargs.peerCerts);
+ }
+ if(doPause ||
+ (pauseFirstLoop &&
+ /* pause after first, before last to grab trace */
+ ((loop == 0) || (loop == loopCount - 1))
+ )
+ ) {
+ char resp;
+ fpurge(stdin);
+ printf("a to abort, c to continue: ");
+ resp = getchar();
+ if(resp == 'a') {
+ break;
+ }
+ }
+ } /* main loop */
+ if(displayHandshakeTimes) {
+ CFAbsoluteTime totalTime;
+ unsigned numHandshakes;
+ if(pargs.numHandshakes == 1) {
+ /* just display the first one */
+ totalTime = pargs.handshakeTimeFirst;
+ numHandshakes = 1;
+ }
+ else {
+ /* skip the first one */
+ totalTime = pargs.handshakeTimeTotal;
+ numHandshakes = pargs.numHandshakes - 1;
+ }
+ if(numHandshakes != 0) {
+ printf(" %u handshakes in %f seconds; %f seconds per handshake\n",
+ numHandshakes, totalTime,
+ (totalTime / numHandshakes));
+ }
+ }
+
+ if(ourRtn) {
+ printf("===%s exiting with %d %s for host %s\n", argv[0], ourRtn,
+ (ourRtn > 1) ? "errors" : "error", pargs.hostName);
+ }
+ return ourRtn;
+
+}
+
+