X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/5dd5f9ec28f304ca377c42fd7f711d6cf12b90e1..5c19dc3ae3bd8e40a9c028b0deddd50ff337692c:/sslViewer/SSLViewer.c diff --git a/sslViewer/SSLViewer.c b/sslViewer/SSLViewer.c new file mode 100644 index 00000000..e00d6e9f --- /dev/null +++ b/sslViewer/SSLViewer.c @@ -0,0 +1,1690 @@ +/* + * Copyright (c) 2006-2013, 2015 Apple Inc. All Rights Reserved. + * + * SSL viewer tool + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#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", ((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; iacceptedProts) { + 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; dexsessionIDLength; 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 1) ? "errors" : "error", pargs.hostName); + } + return ourRtn; + +} + +