X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/80e2389990082500d76eb566d4946be3e786c3ef..d8f41ccd20de16f8ebe2ccc84d47bf1cb2b26bbb:/SecurityTests/clxutils/sslViewer/SSLViewer.cpp diff --git a/SecurityTests/clxutils/sslViewer/SSLViewer.cpp b/SecurityTests/clxutils/sslViewer/SSLViewer.cpp new file mode 100644 index 00000000..19a94182 --- /dev/null +++ b/SecurityTests/clxutils/sslViewer/SSLViewer.cpp @@ -0,0 +1,1799 @@ +/* + * Copyright (c) 2003-2010 Apple Inc. All Rights Reserved. + * + * SSL viewer tool, SecureTransport / OS X version. + */ + +#include +#include // for SSLGetPeerSecTrust +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#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 + +#define CFRELEASE(cf) if (cf) { CFRelease(cf); cf = NULL; } + +/* true when using SSLCopyPeerCertificates() per Radar 3311892 */ +#define USE_COPY_PEER_CERTS 1 + +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(" d Display received data\n"); + printf(" S Display enabled cipher suites\n"); + printf(" 2 SSLv2 only (default is TLSv1)\n"); + printf(" 3 SSLv3 only w/SSLv2 enabled (default is TLSv1)\n"); + printf(" t TLSv1 only w/SSLv2,SSLv3 enabled (this is the default)\n"); + printf(" L all - TLSv1, SSLv3, SSLv2 (default = TLSv1)\n"); + printf(" o TLSv1, SSLv3 use kSSLProtocol__X__Only\n"); + printf(" g={prot...} Specify legal protocols; prot = any combo of" + " [23t]\n"); + printf(" k=keychain Contains (client|server) 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(" Z fileName fileName is a trusted leaf cert\n"); + printf(" x Disable Cert Verification\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) +{ + 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(" D fileBase Write DNList to fileBase*\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" + " n=RSA/NULL E=ECDHE F=ECDH\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(" i=timeout Session cache timeout\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(" I Interactive client authentication\n"); + printf(" N Log handshake timing\n"); + printf(" 4 Disable anonymous ciphers\n"); + printf(" 7 Pause only after first loop\n"); + exit(1); +} + +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; + char *trustedLeafFile; + bool replaceAnchors; + bool interactiveAuth; + CFArrayRef clientCerts; // optional + CFArrayRef encryptClientCerts; // optional + uint32 sessionCacheTimeout;// optional + bool disableAnonCiphers; + bool showCipherSuites; + 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 + SSLClientAuthenticationType authType; // RETURNED + CFArrayRef dnList; // 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; + +} sslPingArgs; + +#include +static void sigpipe(int sig) +{ + fflush(stdin); + printf("***SIGPIPE***\n"); +} + +/* + * Start up a CFRunLoop. This is needed to field keychain event callbacks, used + * to maintain root cert cache coherency. + */ + +/* first we need something to register so we *have* a run loop */ +static OSStatus kcCacheCallback ( + SecKeychainEvent keychainEvent, + SecKeychainCallbackInfo *info, + void *context) +{ + return noErr; +} + +/* main thread has to wait for this to be set to know a run loop has been set up */ +static int runLoopInitialized = 0; + +/* this is the thread which actually runs the CFRunLoop */ +void *cfRunLoopThread(void *arg) +{ + OSStatus ortn = SecKeychainAddCallback(kcCacheCallback, + kSecTrustSettingsChangedEventMask, NULL); + if(ortn) { + printf("registerCacheCallbacks: SecKeychainAddCallback returned %d", (int32_t)ortn); + /* Not sure how this could ever happen - maybe if there is no run loop active? */ + return NULL; + } + runLoopInitialized = 1; + CFRunLoopRun(); + /* should not be reached */ + printf("\n*** Hey! CFRunLoopRun() exited!***\n"); + return NULL; +} + +static int startCFRunLoop() +{ + pthread_t runLoopThread; + + int result = pthread_create(&runLoopThread, NULL, cfRunLoopThread, NULL); + if(result) { + printf("***pthread_create returned %d, aborting\n", result); + return -1; + } + return 0; +} + +/* + * Snag a copy of current connection's peer certs so we can + * examine them later after the connection is closed. + * SecureTransport actually does the create and retain for us. + */ +static OSStatus copyPeerCerts( + SSLContext *ctx, + CFArrayRef *peerCerts) // mallocd & RETURNED +{ + #if USE_COPY_PEER_CERTS + OSStatus ortn = SSLCopyPeerCertificates(ctx, peerCerts); + #else + OSStatus ortn = SSLGetPeerCertificates(ctx, peerCerts); + #endif + if(ortn) { + printf("***Error obtaining peer certs: %s\n", + sslGetSSLErrString(ortn)); + } + return ortn; +} + +/* free the cert array obtained via SSLGetPeerCertificates() */ +/* necessary due to a buggy SSLGetPeerCertificates which really should + * release its certs after they get added to this array */ +static void freePeerCerts( + CFArrayRef peerCerts) +{ + if(peerCerts == NULL) { + return; + } + + #if USE_COPY_PEER_CERTS + + /* Voila! Problem fixed. */ + CFRelease(peerCerts); + return; + + #else + CFIndex numCerts; + SecCertificateRef certData; + CFIndex i; + + numCerts = CFArrayGetCount(peerCerts); + for(i=0; i", ((unsigned)c) & 0xff); + } + break; + } + + } + printf("\n"); +} + +/* + * 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. + */ + ortn = SSLNewContext(false, &ctx); + if(ortn) { + printSslErrStr("SSLNewContext", ortn); + goto cleanup; + } + ortn = SSLSetIOFuncs(ctx, SocketRead, SocketWrite); + if(ortn) { + printSslErrStr("SSLSetIOFuncs", ortn); + goto cleanup; + } + ortn = SSLSetConnection(ctx, (SSLConnectionRef)sock); + if(ortn) { + printSslErrStr("SSLSetConnection", ortn); + goto cleanup; + } + SSLConnectionRef getConn; + ortn = SSLGetConnection(ctx, &getConn); + if(ortn) { + printSslErrStr("SSLGetConnection", ortn); + goto cleanup; + } + if(getConn != (SSLConnectionRef)sock) { + printf("***SSLGetConnection error\n"); + ortn = paramErr; + 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 = kTLSProtocol1; + 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("SSLSetProtocolVersion", ortn); + goto cleanup; + } + if(getVers != pargs->tryVersion) { + printf("***SSLGetProtocolVersion screwup: try %s get %s\n", + sslGetProtocolVersionString(pargs->tryVersion), + sslGetProtocolVersionString(getVers)); + ortn = paramErr; + 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->trustedLeafFile) { + SecCertificateRef leafCertRef = NULL; + CFMutableArrayRef leafCerts = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + /* sslReadAnchor is a misnomer; it just creates a SecCertificateRef from a file */ + ortn = sslReadAnchor(pargs->trustedLeafFile, &leafCertRef); + if (!ortn) { + CFArrayAppendValue(leafCerts, leafCertRef); + CFRelease(leafCertRef); + ortn = SSLSetTrustedLeafCertificates(ctx, leafCerts); + CFRelease(leafCerts); + } + if(ortn) { + goto cleanup; + } + } + if(pargs->interactiveAuth) { + /* we want to get errSSLServerAuthCompleted from SSLHandshake on server auth completion */ + SSLSetSessionOption(ctx, kSSLSessionOptionBreakOnServerAuth, true); + /* we want to get errSSLClientCertRequested from SSLHandshake on client auth request */ + SSLSetSessionOption(ctx, kSSLSessionOptionBreakOnCertRequested, true); + } + else 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 = ioErr; + goto cleanup; + } + } + if(pargs->encryptClientCerts) { + if(pargs->anchorFile == NULL) { + ortn = addIdentityAsTrustedRoot(ctx, pargs->encryptClientCerts); + if(ortn) { + goto cleanup; + } + } + ortn = SSLSetEncryptionCertificate(ctx, pargs->encryptClientCerts); + if(ortn) { + printSslErrStr("SSLSetEncryptionCertificate", ortn); + goto cleanup; + } + } + if(pargs->sessionCacheTimeout) { + ortn = SSLSetSessionCacheTimeout(ctx, pargs->sessionCacheTimeout); + if(ortn) { + printSslErrStr("SSLSetSessionCacheTimeout", ortn); + goto cleanup; + } + } + if(pargs->disableAnonCiphers) { + ortn = SSLSetAllowAnonymousCiphers(ctx, false); + if(ortn) { + printSslErrStr("SSLSetAllowAnonymousCiphers", ortn); + goto cleanup; + } + /* quickie test of the getter */ + Boolean e; + ortn = SSLGetAllowAnonymousCiphers(ctx, &e); + if(ortn) { + printSslErrStr("SSLGetAllowAnonymousCiphers", ortn); + goto cleanup; + } + if(e) { + printf("***SSLGetAllowAnonymousCiphers() returned true; expected false\n"); + ortn = ioErr; + goto cleanup; + } + } + if(pargs->showCipherSuites) { + sslShowEnabledCipherSuites(ctx); + } + /*** 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(); + } + else if(ortn == errSSLServerAuthCompleted) { + if(pargs->verbose) { + printf("...server authentication completed\n"); + } + } + else if(ortn == errSSLClientCertRequested) { + if(pargs->verbose) { + printf("...received client cert request\n"); + } + /* %%% could prompt interactively here for client cert to use; + * for now, just use the client cert passed on the command line + */ + 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; + } + } + if(pargs->verbose) { + printf("...setting client certificate\n"); + } + 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 = ioErr; + goto cleanup; + } + } + else { + printf("***no client certificate specified!\n"); + } + } + } while (ortn == errSSLWouldBlock || + ortn == errSSLServerAuthCompleted || + ortn == errSSLClientCertRequested); + + 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++; + + /* this works even if handshake failed due to cert chain invalid */ + CFRELEASE(pargs->peerCerts); + if(!pargs->manualCertVerify) { + copyPeerCerts(ctx, &pargs->peerCerts); + } + else { + /* else fetched via SecTrust later */ + pargs->peerCerts = NULL; + } + + ortn = SSLCopyPeerTrust(ctx, &pargs->peerTrust); + if(ortn) { + printf("***SSLCopyPeerTrust error %d\n", (int)ortn); + pargs->peerTrust = NULL; + } + + /* ditto */ + SSLGetClientCertificateState(ctx, &pargs->certState); + SSLGetNegotiatedClientAuthType(ctx, &pargs->authType); + SSLGetNegotiatedCipher(ctx, &pargs->negCipher); + SSLGetNegotiatedProtocolVersion(ctx, &pargs->negVersion); + CFRELEASE(pargs->dnList); + SSLCopyDistinguishedNames(ctx, &pargs->dnList); + pargs->sessionIDLength = MAX_SESSION_ID_LENGTH; + SSLGetResumableSessionInfo(ctx, &pargs->sessionWasResumed, pargs->sessionID, + &pargs->sessionIDLength); + if(pargs->manualCertVerify) { + OSStatus certRtn = sslEvaluateTrust(ctx, pargs->verbose, pargs->silent, + &pargs->peerCerts); + if(certRtn && !ortn ) { + ortn = certRtn; + } + } + + if(ortn) { + if(!pargs->silent) { + printf("\n"); + } + goto cleanup; + } + + if(pargs->verbose) { + printf("...SSL handshake complete\n"); + } + + /* Write our GET request */ + length = strlen(pargs->getMsg); + ortn = SSLWrite(ctx, pargs->getMsg, length, &actLen); + if(ortn) { + printf("***SSLWrite error: %d\n", (int)ortn); + } else if((actLen > 0) && pargs->dumpRxData) { + dumpAscii((uint8_t*)pargs->getMsg, 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 (ortn == noErr) { + 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 == noErr)) { + printf("***Radar 2984932 confirmed***\n"); + } + if (ortn == errSSLWouldBlock) { + /* for this loop, these are identical */ + ortn = noErr; + } + if(ortn == errSSLServerAuthCompleted || + ortn == errSSLClientCertRequested) { + /* should never get these once the handshake is complete */ + printf("***SSLRead returned unexpected handshake error!\n"); + } + + if((actLen > 0) && pargs->dumpRxData) { + dumpAscii(rcvBuf, actLen); + } + if(ortn != noErr) { + /* 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); + CFRELEASE(pargs->dnList); + SSLCopyDistinguishedNames(ctx, &pargs->dnList); + + /* convert normal "shutdown" into zero err rtn */ + if(ortn == errSSLClosedGraceful) { + ortn = noErr; + } + if((ortn == errSSLClosedNoNotify) && !pargs->requireNotify) { + /* relaxed disconnect rules */ + ortn = noErr; + } +cleanup: + /* + * always do close, even on error - to flush outgoing write queue + */ + OSStatus cerr = SSLClose(ctx); + if(ortn == noErr) { + ortn = cerr; + } + if(sock) { + endpointShutdown(sock); + } + if(ctx) { + SSLDisposeContext(ctx); + } + return ortn; +} + +static void showPeerCerts( + CFArrayRef peerCerts, + bool verbose) +{ + CFIndex numCerts; + SecCertificateRef certRef; + OSStatus ortn; + CSSM_DATA certData; + CFIndex i; + + if(peerCerts == NULL) { + return; + } + numCerts = CFArrayGetCount(peerCerts); + for(i=0; i 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; +} + +/* + * Free everything allocated by sslPing in an sslPingArgs. + * Mainly for looping and malloc debugging. + */ +static void freePingArgs( + sslPingArgs *pargs) +{ + freePeerCerts(pargs->peerCerts); + pargs->peerCerts = NULL; + CFRELEASE(pargs->peerTrust); + CFRELEASE(pargs->dnList); + /* more, later, for client retry/identity fetch */ +} + +static SSLProtocol charToProt( + char c, // 2, 3, t + char **argv) +{ + switch(c) { + case '2': + return kSSLProtocol2; + case '3': + return kSSLProtocol3; + case 't': + return kTLSProtocol1; + 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; + SecKeychainRef encryptKc = nil; + sslPingArgs pargs; + + /* user-spec'd parameters */ + char *getPath = (char *)DEFAULT_PATH; + char *fileBase = NULL; + bool displayCerts = false; + bool doSslV2 = false; + bool doSslV3 = false; + bool doTlsV1 = true; + bool protXOnly = false; // kSSLProtocol3Only, kTLSProtocol1Only + bool doProtUnknown = false; + unsigned loopCount = 1; + bool doPause = false; + bool pauseFirstLoop = false; + bool verifyProt = false; + SSLProtocol maxProtocol = kTLSProtocol1; // for verifying negotiated + // protocol + char *acceptedProts = NULL; + char *keyChainName = NULL; + char *encryptKeyChainName = NULL; + char *getMsgSpec = NULL; + bool vfyCertState = false; + SSLClientCertificateState expectCertState; + bool displayHandshakeTimes = false; + bool completeCertChain = false; + char *dnFileBase = NULL; + + /* 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; + +} + +