+++ /dev/null
-/*
- * Copyright (c) 2003-2010 Apple Inc. All Rights Reserved.
- *
- * SSL viewer tool, SecureTransport / OS X version.
- */
-
-#include <Security/SecureTransport.h>
-#include <Security/SecureTransportPriv.h> // for SSLGetPeerSecTrust
-#include <Security/SecCertificate.h>
-#include <clAppUtils/sslAppUtils.h>
-#include <clAppUtils/ioSock.h>
-#include <security_cdsa_utils/cuPrintCert.h>
-#include <utilLib/fileIo.h>
-
-#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <ctype.h>
-#include <CoreFoundation/CoreFoundation.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
-
-#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 <signal.h>
-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<numCerts; i++) {
- certData = (SecCertificateRef)CFArrayGetValueAtIndex(peerCerts, i);
- CFRelease(certData);
- }
- CFRelease(peerCerts);
- #endif /* USE_COPY_PEER_CERTS */
-}
-
-/*
- * Manually evaluate session's SecTrustRef.
- */
-#define SSL_SEC_TRUST 1
-
-static OSStatus sslEvaluateTrust(
- SSLContext *ctx,
- bool verbose,
- bool silent,
- CFArrayRef *peerCerts) // fetched and retained
-{
- OSStatus ortn;
- SecTrustRef secTrust = NULL;
-
- #if SSL_SEC_TRUST
- ortn = SSLGetPeerSecTrust(ctx, &secTrust);
- #else
- ortn = unimpErr;
- #endif
- 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(!silent) {
- printf("...No SecTrust available - this is a resumed session, right?\n");
- }
- return noErr;
- }
- SecTrustResultType secTrustResult;
- ortn = SecTrustEvaluate(secTrust, &secTrustResult);
- if(ortn) {
- printf("\n***Error on SecTrustEvaluate: %d\n", (int)ortn);
- return ortn;
- }
- if(verbose) {
- const char *res = NULL;
- switch(secTrustResult) {
- case kSecTrustResultInvalid:
- res = "kSecTrustResultInvalid"; break;
- case kSecTrustResultProceed:
- res = "kSecTrustResultProceed"; break;
- case kSecTrustResultConfirm:
- 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;
-
- /* 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);
- }
- #if !USE_COPY_PEER_CERTS
- 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 /* !USE_COPY_PEER_CERTS */
- return ortn;
-}
-
-static void sslShowEnabledCipherSuites(
- SSLContextRef ctx)
-{
- OSStatus status;
- SSLCipherSuite *ciphers;
- size_t numCiphers, totalCiphers;
- const char *c;
- unsigned int i;
-
- status = SSLGetNumberSupportedCiphers(ctx, &totalCiphers);
- status = SSLGetNumberEnabledCiphers(ctx, &numCiphers);
- ciphers = (SSLCipherSuite *)malloc(sizeof(SSLCipherSuite) * numCiphers);
- status = SSLGetEnabledCiphers(ctx, ciphers, &numCiphers);
-
- printf(" Total enabled ciphers : %ld of %ld\n", numCiphers, totalCiphers);
-
- for(i=0; i<numCiphers; i++) {
- switch(ciphers[i]) {
- case SSL_NULL_WITH_NULL_NULL: c="SSL_NULL_WITH_NULL_NULL"; break;
- case SSL_RSA_WITH_NULL_MD5: c="SSL_RSA_WITH_NULL_MD5"; break;
- case SSL_RSA_WITH_NULL_SHA: c="SSL_RSA_WITH_NULL_SHA"; break;
- case SSL_RSA_EXPORT_WITH_RC4_40_MD5: c="SSL_RSA_EXPORT_WITH_RC4_40_MD5"; break;
- case SSL_RSA_WITH_RC4_128_MD5: c="SSL_RSA_WITH_RC4_128_MD5"; break;
- case SSL_RSA_WITH_RC4_128_SHA: c="SSL_RSA_WITH_RC4_128_SHA"; break;
- case SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5: c="SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5"; break;
- case SSL_RSA_WITH_IDEA_CBC_SHA: c="SSL_RSA_WITH_IDEA_CBC_SHA"; break;
- case SSL_RSA_EXPORT_WITH_DES40_CBC_SHA: c="SSL_RSA_EXPORT_WITH_DES40_CBC_SHA"; break;
- case SSL_RSA_WITH_DES_CBC_SHA: c="SSL_RSA_WITH_DES_CBC_SHA"; break;
- case SSL_RSA_WITH_3DES_EDE_CBC_SHA: c="SSL_RSA_WITH_3DES_EDE_CBC_SHA"; break;
- case SSL_DH_DSS_EXPORT_WITH_DES40_CBC_SHA: c="SSL_DH_DSS_EXPORT_WITH_DES40_CBC_SHA"; break;
- case SSL_DH_DSS_WITH_DES_CBC_SHA: c="SSL_DH_DSS_WITH_DES_CBC_SHA"; break;
- case SSL_DH_DSS_WITH_3DES_EDE_CBC_SHA: c="SSL_DH_DSS_WITH_3DES_EDE_CBC_SHA"; break;
- case SSL_DH_RSA_EXPORT_WITH_DES40_CBC_SHA: c="SSL_DH_RSA_EXPORT_WITH_DES40_CBC_SHA"; break;
- case SSL_DH_RSA_WITH_DES_CBC_SHA: c="SSL_DH_RSA_WITH_DES_CBC_SHA"; break;
- case SSL_DH_RSA_WITH_3DES_EDE_CBC_SHA: c="SSL_DH_RSA_WITH_3DES_EDE_CBC_SHA"; break;
- case SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA: c="SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA"; break;
- case SSL_DHE_DSS_WITH_DES_CBC_SHA: c="SSL_DHE_DSS_WITH_DES_CBC_SHA"; break;
- case SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA: c="SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA"; break;
- case SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA: c="SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA"; break;
- case SSL_DHE_RSA_WITH_DES_CBC_SHA: c="SSL_DHE_RSA_WITH_DES_CBC_SHA"; break;
- case SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA: c="SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA"; break;
- case SSL_DH_anon_EXPORT_WITH_RC4_40_MD5: c="SSL_DH_anon_EXPORT_WITH_RC4_40_MD5"; break;
- case SSL_DH_anon_WITH_RC4_128_MD5: c="SSL_DH_anon_WITH_RC4_128_MD5"; break;
- case SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA: c="SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA"; break;
- case SSL_DH_anon_WITH_DES_CBC_SHA: c="SSL_DH_anon_WITH_DES_CBC_SHA"; break;
- case SSL_DH_anon_WITH_3DES_EDE_CBC_SHA: c="SSL_DH_anon_WITH_3DES_EDE_CBC_SHA"; break;
- case SSL_FORTEZZA_DMS_WITH_NULL_SHA: c="SSL_FORTEZZA_DMS_WITH_NULL_SHA"; break;
- case SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA:c="SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA"; break;
-
- /* TLS addenda using AES, per RFC 3268 */
- case TLS_RSA_WITH_AES_128_CBC_SHA: c="TLS_RSA_WITH_AES_128_CBC_SHA"; break;
- case TLS_DH_DSS_WITH_AES_128_CBC_SHA: c="TLS_DH_DSS_WITH_AES_128_CBC_SHA"; break;
- case TLS_DH_RSA_WITH_AES_128_CBC_SHA: c="TLS_DH_RSA_WITH_AES_128_CBC_SHA"; break;
- case TLS_DHE_DSS_WITH_AES_128_CBC_SHA: c="TLS_DHE_DSS_WITH_AES_128_CBC_SHA"; break;
- case TLS_DHE_RSA_WITH_AES_128_CBC_SHA: c="TLS_DHE_RSA_WITH_AES_128_CBC_SHA"; break;
- case TLS_DH_anon_WITH_AES_128_CBC_SHA: c="TLS_DH_anon_WITH_AES_128_CBC_SHA"; break;
- case TLS_RSA_WITH_AES_256_CBC_SHA: c="TLS_RSA_WITH_AES_256_CBC_SHA"; break;
- case TLS_DH_DSS_WITH_AES_256_CBC_SHA: c="TLS_DH_DSS_WITH_AES_256_CBC_SHA"; break;
- case TLS_DH_RSA_WITH_AES_256_CBC_SHA: c="TLS_DH_RSA_WITH_AES_256_CBC_SHA"; break;
- case TLS_DHE_DSS_WITH_AES_256_CBC_SHA: c="TLS_DHE_DSS_WITH_AES_256_CBC_SHA"; break;
- case TLS_DHE_RSA_WITH_AES_256_CBC_SHA: c="TLS_DHE_RSA_WITH_AES_256_CBC_SHA"; break;
- case TLS_DH_anon_WITH_AES_256_CBC_SHA: c="TLS_DH_anon_WITH_AES_256_CBC_SHA"; break;
-
- /* ECDSA addenda, RFC 4492 */
- case TLS_ECDH_ECDSA_WITH_NULL_SHA: c="TLS_ECDH_ECDSA_WITH_NULL_SHA"; break;
- case TLS_ECDH_ECDSA_WITH_RC4_128_SHA: c="TLS_ECDH_ECDSA_WITH_RC4_128_SHA"; break;
- case TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA: c="TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA"; break;
- case TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA: c="TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA"; break;
- case TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA: c="TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA"; break;
- case TLS_ECDHE_ECDSA_WITH_NULL_SHA: c="TLS_ECDHE_ECDSA_WITH_NULL_SHA"; break;
- case TLS_ECDHE_ECDSA_WITH_RC4_128_SHA: c="TLS_ECDHE_ECDSA_WITH_RC4_128_SHA"; break;
- case TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA: c="TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA"; break;
- case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: c="TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA"; break;
- case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: c="TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA"; break;
- case TLS_ECDH_RSA_WITH_NULL_SHA: c="TLS_ECDH_RSA_WITH_NULL_SHA"; break;
- case TLS_ECDH_RSA_WITH_RC4_128_SHA: c="TLS_ECDH_RSA_WITH_RC4_128_SHA"; break;
- case TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA: c="TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA"; break;
- case TLS_ECDH_RSA_WITH_AES_128_CBC_SHA: c="TLS_ECDH_RSA_WITH_AES_128_CBC_SHA"; break;
- case TLS_ECDH_RSA_WITH_AES_256_CBC_SHA: c="TLS_ECDH_RSA_WITH_AES_256_CBC_SHA"; break;
- case TLS_ECDHE_RSA_WITH_NULL_SHA: c="TLS_ECDHE_RSA_WITH_NULL_SHA"; break;
- case TLS_ECDHE_RSA_WITH_RC4_128_SHA: c="TLS_ECDHE_RSA_WITH_RC4_128_SHA"; break;
- case TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA: c="TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA"; break;
- case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: c="TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA"; break;
- case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: c="TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA"; break;
- case TLS_ECDH_anon_WITH_NULL_SHA: c="TLS_ECDH_anon_WITH_NULL_SHA"; break;
- case TLS_ECDH_anon_WITH_RC4_128_SHA: c="TLS_ECDH_anon_WITH_RC4_128_SHA"; break;
- case TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA: c="TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA"; break;
- case TLS_ECDH_anon_WITH_AES_128_CBC_SHA: c="TLS_ECDH_anon_WITH_AES_128_CBC_SHA"; break;
- case TLS_ECDH_anon_WITH_AES_256_CBC_SHA: c="TLS_ECDH_anon_WITH_AES_256_CBC_SHA"; break;
-
- /*
- * Tags for SSL 2 cipher kinds which are not specified
- * for SSL 3.
- */
- case SSL_RSA_WITH_RC2_CBC_MD5: c="SSL_RSA_WITH_RC2_CBC_MD5"; break;
- case SSL_RSA_WITH_IDEA_CBC_MD5: c="SSL_RSA_WITH_IDEA_CBC_MD5"; break;
- case SSL_RSA_WITH_DES_CBC_MD5: c="SSL_RSA_WITH_DES_CBC_MD5"; break;
- case SSL_RSA_WITH_3DES_EDE_CBC_MD5: c="SSL_RSA_WITH_3DES_EDE_CBC_MD5"; break;
- case SSL_NO_SUCH_CIPHERSUITE:
- default:
- c="SSL_NO_SUCH_CIPHERSUITE"; break;
- }
- printf(" %s\n", c);
- fflush(stdout);
- }
- free(ciphers);
-}
-
-/* print reply received from server, safely */
-static void dumpAscii(
- uint8_t *rcvBuf,
- uint32_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");
-}
-
-/*
- * 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<numCerts; i++) {
- certRef = (SecCertificateRef)CFArrayGetValueAtIndex(peerCerts, i);
- ortn = SecCertificateGetData(certRef, &certData);
- if(ortn) {
- printf("***SecCertificateGetData returned %d\n", (int)ortn);
- continue;
- }
- printf("\n================== Server Cert %lu ===================\n\n", i);
- printCert(certData.Data, certData.Length, verbose);
- printf("\n=============== End of Server Cert %lu ===============\n", i);
- }
-}
-
-static void writePeerCerts(
- CFArrayRef peerCerts,
- const char *fileBase)
-{
- CFIndex numCerts;
- SecCertificateRef certRef;
- OSStatus ortn;
- CSSM_DATA certData;
- 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);
- ortn = SecCertificateGetData(certRef, &certData);
- if(ortn) {
- printf("***SecCertificateGetData returned %d\n", (int)ortn);
- continue;
- }
- cspWriteFile(fileName, certData.Data, certData.Length);
- }
- printf("...wrote %lu certs to fileBase %s\n", numCerts, fileBase);
-}
-
-static void writeDnList(
- CFArrayRef dnList,
- const char *fileBase)
-{
- CFIndex numDns;
- CFDataRef cfDn;
- CFIndex i;
- char fileName[100];
-
- if(dnList == NULL) {
- return;
- }
- numDns = CFArrayGetCount(dnList);
- for(i=0; i<numDns; i++) {
- sprintf(fileName, "%s%02d.der", fileBase, (int)i);
- cfDn = (CFDataRef)CFArrayGetValueAtIndex(dnList, i);
- cspWriteFile(fileName, CFDataGetBytePtr(cfDn), CFDataGetLength(cfDn));
- }
- printf("...wrote %lu RDNs to fileBase %s\n", numDns, fileBase);
-}
-
-/*
- * Show result of an sslPing().
- * Assumes the following from sslPingArgs:
- *
- * verbose
- * tryVersion
- * acceptedProts
- * negVersion
- * negCipher
- * peerCerts
- * certState
- * authType
- * sessionWasResumed
- * sessionID
- * sessionIDLength
- * handshakeTime
- */
-static void showSSLResult(
- const sslPingArgs &pargs,
- OSStatus err,
- bool displayPeerCerts,
- const char *fileBase, // non-NULL: write certs to file
- const char *dnFileBase) // non-NULL: write DNList 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));
- printf(" Client Auth Type : %s\n",
- sslGetClientAuthTypeString(pargs.authType));
- }
- 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) {
- showPeerCerts(pargs.peerCerts, false);
- }
- if(fileBase != NULL) {
- writePeerCerts(pargs.peerCerts, fileBase);
- }
- }
- if(dnFileBase != NULL) {
- writeDnList(pargs.dnList, dnFileBase);
- }
-
- 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;
-}
-
-/*
- * 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<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 '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 'I':
- pargs.interactiveAuth = 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 'Z':
- if(++arg == argc) {
- /* requires another arg */
- usage(argv);
- }
- pargs.trustedLeafFile = argv[arg];
- break;
- case 'r':
- pargs.allowAnyRoot = true;
- break;
- case 'd':
- pargs.dumpRxData = true;
- break;
- case 'c':
- displayCerts = true;
- break;
- case 'f':
- if(++arg == argc) {
- /* requires another arg */
- usage(argv);
- }
- fileBase = argv[arg];
- break;
- case 'C':
- pargs.cipherRestrict = argp[2];
- break;
- case 'S':
- pargs.showCipherSuites = true;
- break;
- case '2':
- doSslV3 = doTlsV1 = false;
- doSslV2 = true;
- break;
- case '3':
- doSslV2 = doTlsV1 = false;
- doSslV3 = true;
- break;
- case 't':
- /* currently the default... */
- doSslV2 = doSslV3 = false;
- doTlsV1 = true;
- break;
- case 'L':
- doSslV2 = doSslV3 = doTlsV1 = true;
- break;
- case 'o':
- protXOnly = true;
- break;
- case 'u':
- doSslV2 = doSslV3 = doTlsV1 = 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 = &argp[2];
- doSslV3 = doSslV2 = doTlsV1 = false;
- break;
- case 'l':
- loopCount = atoi(&argp[2]);
- if(loopCount == 0) {
- printf("***bad loopCount\n");
- usage(argv);
- }
- break;
- case 'P':
- pargs.port = atoi(&argp[2]);
- break;
- case 'H':
- pargs.allowHostnameSpoof = true;
- break;
- case 'F':
- pargs.vfyHostName = &argp[2];
- break;
- case 'k':
- keyChainName = &argp[2];
- break;
- case 'y':
- encryptKeyChainName = &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 'i':
- pargs.sessionCacheTimeout = atoi(&argp[2]);
- break;
- case '4':
- pargs.disableAnonCiphers = true;
- break;
- case 'D':
- if(++arg == argc) {
- /* requires another arg */
- usage(argv);
- }
- dnFileBase = argv[arg];
- break;
- case 'h':
- if(pargs.verbose || (argp[1] == 'v')) {
- usageVerbose(argv);
- }
- else {
- usage(argv);
- }
- default:
- usage(argv);
- }
- }
- 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);
- }
- if(pargs.password) {
- OSStatus ortn = SecKeychainUnlock(serverKc,
- strlen(pargs.password), pargs.password, true);
- if(ortn) {
- printf("SecKeychainUnlock returned %d\n", (int)ortn);
- /* oh well */
- }
- }
- }
- if(encryptKeyChainName) {
- pargs.encryptClientCerts = getSslCerts(encryptKeyChainName, true,
- completeCertChain, pargs.anchorFile, &encryptKc);
- if(pargs.encryptClientCerts == nil) {
- exit(1);
- }
- }
- signal(SIGPIPE, sigpipe);
-
- if(loopCount != 0) {
- /* prepare to handle KC callbacks for root cert cache invalidation */
- if(startCFRunLoop()) {
- exit(1);
- }
-
- /* give that thread a chance right now */
- while(!runLoopInitialized) {
- usleep(1000);
- };
- }
-
- if(doPause) {
- char resp;
- fpurge(stdin);
- printf("Before main loop. Hit a to abort, c to continue: ");
- resp = getchar();
- if(resp == 'a') {
- exit(1);
- }
- }
-
- 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(doTlsV1) {
- pargs.tryVersion =
- protXOnly ? kTLSProtocol1Only : kTLSProtocol1;
- pargs.acceptedProts = NULL;
- if(!pargs.silent) {
- printf("Connecting to host %s with TLS V1...\n", 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,
- dnFileBase);
- }
- freePingArgs(&pargs);
- 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...\n", 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,
- dnFileBase);
- }
- freePingArgs(&pargs);
- 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...\n", 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,
- dnFileBase);
- }
- freePingArgs(&pargs);
- 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...\n",
- 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,
- dnFileBase);
- }
- freePingArgs(&pargs);
- }
- if(acceptedProts != NULL) {
- pargs.acceptedProts = acceptedProts;
- pargs.tryVersion = kSSLProtocolUnknown; // not used
- if(!pargs.silent) {
- printf("Connecting to host %s with acceptedProts %s...\n",
- 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,
- dnFileBase);
- }
- freePingArgs(&pargs);
- }
- 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));
- }
- }
- printCertShutdown();
- if(ourRtn) {
- printf("===%s exiting with %d %s for host %s\n", argv[0], ourRtn,
- (ourRtn > 1) ? "errors" : "error", pargs.hostName);
- }
- return ourRtn;
-
-}
-
-