]> git.saurik.com Git - apple/security.git/blobdiff - OSX/libsecurity_ssl/sslViewer/sslServer.cpp
Security-57336.1.9.tar.gz
[apple/security.git] / OSX / libsecurity_ssl / sslViewer / sslServer.cpp
diff --git a/OSX/libsecurity_ssl/sslViewer/sslServer.cpp b/OSX/libsecurity_ssl/sslViewer/sslServer.cpp
new file mode 100644 (file)
index 0000000..c8fe63e
--- /dev/null
@@ -0,0 +1,1061 @@
+/*
+ * Copyright (c) 2008-2014 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+/*
+ * Trivial SSL server example, using SecureTransport / OS X version.
+ *
+ */
+
+#include <Security/SecureTransport.h>
+#include <Security/SecureTransportPriv.h>
+#include "sslAppUtils.h"
+#include "ioSock.h"
+#include "fileIo.h"
+
+#include <Security/SecBase.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <ctype.h>
+#include <sys/param.h>
+
+#include <Security/Security.h>
+#include <Security/SecCertificatePriv.h>
+
+#include <CoreFoundation/CoreFoundation.h>
+#include "printCert.h"
+
+#if NO_SERVER
+#include <securityd/spi.h>
+#endif
+
+/* Set true when PR-3074739 is merged to TOT */
+#define SET_DH_PARAMS_ENABLE           1
+
+/* true when using SSLCopyPeerCertificates() per Radar 3311892 */
+#define USE_COPY_PEER_CERTS            1
+
+/*
+ * Defaults, overridable by user.
+ */
+#define SERVER_MESSAGE  "HTTP/1.0 200 OK\015\012Content-Type: text/html\015\012\015\012" \
+       "<HTML><HEAD><TITLE>SecureTransport Test Server</TITLE></HEAD>" \
+       "<BODY><H2>Secure connection established.</H2>" \
+       "Message from the 'sslServer' sample application.\015\012</BODY>" \
+       "</HTML>\015\012"
+
+/* For ease of debugging, pick a non-privileged port */
+#define DEFAULT_PORT     1200
+// #define DEFAULT_PORT     443
+
+#define DEFAULT_HOST   "localhost"
+
+#define DEFAULT_KC             "certkc"
+
+static void usage(char **argv)
+{
+    printf("Usage: %s [option ...]\n", argv[0]);
+    printf("Options:\n");
+       printf("   P=port      Port to listen on; default is %d\n", DEFAULT_PORT);
+       printf("   k=keychain  Contains server cert and keys.\n");
+       printf("   y=keychain  Encryption-only cert and keys.\n");
+    printf("   e           Allow Expired Certs\n");
+    printf("   r           Allow any root cert\n");
+    printf("   E           Allow Expired Roots\n");
+       printf("   x           Disable Cert Verification\n");
+       printf("   f=fileBase  Write Peer Certs to fileBase*\n");
+       printf("   c           Display peer certs\n");
+       printf("   d           Display received data\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\n");
+       printf("   2           SSLv2 only (default is best fit)\n");
+       printf("   3           SSLv3 only (default is best fit)\n");
+       printf("   t           TLSv1 only (default is best fit)\n");
+       printf("   o           TLSv1, SSLv3 use kSSLProtocol__X__Only\n");
+       printf("   g={prot...} Specify legal protocols; prot = any combo of [23t]\n");
+       printf("   T=[nrsj]    Verify client cert state = "
+                                                       "none/requested/sent/rejected\n");
+       printf("   R           Disable resumable session support\n");
+       printf("   i=timeout   Session cache timeout\n");
+       printf("   u=[nat]     Authentication: n=never; a=always; t=try\n");
+       printf("   b           Non-blocking I/O\n");
+       printf("   a fileNmae  Add fileName to list of trusted roots\n");
+       printf("   A fileName  fileName is ONLY trusted root\n");
+       printf("   U filename  Add filename to acceptable DNList (multiple times OK)\n");
+       printf("   D filename  Diffie-Hellman parameters from filename\n");
+       printf("   z=password  Unlock server keychain with password.\n");
+       printf("   H           Do SecIdentityRef search instead of specific keychain\n");
+       printf("   M           Complete cert chain (default assumes that our identity is root)\n");
+       printf("   4           Disable anonymous ciphers\n");
+       printf("   p           Pause after each phase\n");
+       printf("   l[=loops]   Loop, performing multiple transactions\n");
+       printf("   q           Quiet/diagnostic mode (site names and errors only)\n");
+       printf("   h           Help\n");
+    exit(1);
+}
+
+/* snag a copy of current connection's peer certs so we can
+ * examine them later after the connection is closed */
+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() */
+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
+}
+
+/* print reply received from server */
+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");
+}
+
+static void doPause(const char *prompt) {
+       if(prompt) {
+               printf("%s. ", prompt);
+       }
+       fpurge(stdin);
+       printf("Continue (n/anything)? ");
+       char c = getchar();
+       if(c == 'n') {
+               exit(0);
+       }
+}
+
+/*
+ * Perform one SSL diagnostic server-side session. Returns nonzero on error.
+ * Normally no output to stdout except initial "waiting for connection" 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 sslServe(
+       otSocket                                listenSock,
+       unsigned short                  portNum,
+       SSLProtocol                             tryVersion,                     // only used if acceptedProts NULL
+       const char                              *acceptedProts,
+       CFArrayRef                              serverCerts,            // required
+       char                                    *password,                      // optional
+       CFArrayRef                              encryptServerCerts,     // optional
+       bool                            allowExpired,
+       bool                            allowAnyRoot,
+       bool                            allowExpiredRoot,
+       bool                            disableCertVerify,
+       char                                    *anchorFile,
+       bool                            replaceAnchors,
+       char                                    cipherRestrict,         // '2', 'd'. etc...'\0' for no
+                                                                                               //   restriction
+       SSLAuthenticate                 authenticate,
+       unsigned char                   *dhParams,                      // optional D-H parameters
+       unsigned                                dhParamsLen,
+       CFArrayRef                              acceptableDNList,       // optional
+       bool                            resumableEnable,
+       uint32_t                                        sessionCacheTimeout,// optional
+       bool                            disableAnonCiphers,
+       bool                            silent,                         // no stdout
+       bool                            pause,
+       SSLProtocol                             *negVersion,            // RETURNED
+       SSLCipherSuite                  *negCipher,                     // RETURNED
+       SSLClientCertificateState *certState,           // RETURNED
+       Boolean                                 *sessionWasResumed,     // RETURNED
+       unsigned char                   *sessionID,                     // mallocd by caller, RETURNED
+       size_t                                  *sessionIDLength,       // RETURNED
+       CFArrayRef                              *peerCerts,                     // mallocd & RETURNED
+       char                                    **argv)
+{
+       otSocket                        acceptSock;
+    PeerSpec            peerId;
+    OSStatus            ortn;
+    SSLContextRef       ctx = NULL;
+    size_t              length;
+    uint8_t               rcvBuf[RCV_BUF_SIZE];
+       const char *outMsg = SERVER_MESSAGE;
+
+    *negVersion = kSSLProtocolUnknown;
+    *negCipher = SSL_NULL_WITH_NULL_NULL;
+    *peerCerts = NULL;
+
+       #if IGNORE_SIGPIPE
+       signal(SIGPIPE, sigpipe);
+       #endif
+
+       /* first wait for a connection */
+       if(!silent) {
+               printf("Waiting for client connection on port %u...", portNum);
+               fflush(stdout);
+       }
+       ortn = AcceptClientConnection(listenSock, &acceptSock, &peerId);
+    if(ortn) {
+       printf("AcceptClientConnection returned %d; aborting\n", (int)ortn);
+       return ortn;
+    }
+
+       /*
+        * Set up a SecureTransport session.
+        * First the standard calls.
+        */
+       ortn = SSLNewContext(true, &ctx);
+       if(ortn) {
+               printSslErrStr("SSLNewContext", ortn);
+               goto cleanup;
+       }
+       ortn = SSLSetIOFuncs(ctx, SocketRead, SocketWrite);
+       if(ortn) {
+               printSslErrStr("SSLSetIOFuncs", ortn);
+               goto cleanup;
+       }
+       ortn = SSLSetConnection(ctx, (SSLConnectionRef)acceptSock);
+       if(ortn) {
+               printSslErrStr("SSLSetConnection", ortn);
+               goto cleanup;
+       }
+
+       /* have to do these options befor setting server certs */
+       if(allowExpired) {
+               ortn = SSLSetAllowsExpiredCerts(ctx, true);
+               if(ortn) {
+                       printSslErrStr("SSLSetAllowExpiredCerts", ortn);
+                       goto cleanup;
+               }
+       }
+       if(allowAnyRoot) {
+               ortn = SSLSetAllowsAnyRoot(ctx, true);
+               if(ortn) {
+                       printSslErrStr("SSLSetAllowAnyRoot", ortn);
+                       goto cleanup;
+               }
+       }
+
+       if(anchorFile) {
+               ortn = sslAddTrustedRoot(ctx, anchorFile, replaceAnchors);
+               if(ortn) {
+                       printf("***Error obtaining anchor file %s\n", anchorFile);
+                       goto cleanup;
+               }
+       }
+       if(serverCerts != NULL) {
+               if(anchorFile == NULL) {
+                       /* no specific anchors, so assume we want to trust this one */
+                       ortn = addIdentityAsTrustedRoot(ctx, serverCerts);
+                       if(ortn) {
+                               goto cleanup;
+                       }
+               }
+               ortn = SSLSetCertificate(ctx, serverCerts);
+               if(ortn) {
+                       printSslErrStr("SSLSetCertificate", ortn);
+                       goto cleanup;
+               }
+       }
+       if(encryptServerCerts) {
+               ortn = SSLSetEncryptionCertificate(ctx, encryptServerCerts);
+               if(ortn) {
+                       printSslErrStr("SSLSetEncryptionCertificate", ortn);
+                       goto cleanup;
+               }
+       }
+       if(allowExpiredRoot) {
+               ortn = SSLSetAllowsExpiredRoots(ctx, true);
+               if(ortn) {
+                       printSslErrStr("SSLSetAllowsExpiredRoots", ortn);
+                       goto cleanup;
+               }
+       }
+       if(disableCertVerify) {
+               ortn = SSLSetEnableCertVerify(ctx, false);
+               if(ortn) {
+                       printSslErrStr("SSLSetEnableCertVerify", ortn);
+                       goto cleanup;
+               }
+       }
+
+       /*
+        * SecureTransport options.
+        */
+       if(acceptedProts) {
+               ortn = SSLSetProtocolVersionEnabled(ctx, kSSLProtocolAll, false);
+               if(ortn) {
+                       printSslErrStr("SSLSetProtocolVersionEnabled(all off)", ortn);
+                       goto cleanup;
+               }
+               for(const char *cp = acceptedProts; *cp; cp++) {
+                       SSLProtocol prot = kSSLProtocolUnknown;
+                       switch(*cp) {
+                               case '2':
+                                       prot = kSSLProtocol2;
+                                       break;
+                               case '3':
+                                       prot = kSSLProtocol3;
+                                       break;
+                               case 't':
+                                       prot = kTLSProtocol1;
+                                       break;
+                               default:
+                                       usage(argv);
+                       }
+                       ortn = SSLSetProtocolVersionEnabled(ctx, prot, true);
+                       if(ortn) {
+                               printSslErrStr("SSLSetProtocolVersionEnabled", ortn);
+                               goto cleanup;
+                       }
+               }
+       }
+       else {
+               ortn = SSLSetProtocolVersion(ctx, tryVersion);
+               if(ortn) {
+                       printSslErrStr("SSLSetProtocolVersion", ortn);
+                       goto cleanup;
+               }
+       }
+       if(resumableEnable) {
+               ortn = SSLSetPeerID(ctx, &peerId, sizeof(PeerSpec));
+               if(ortn) {
+                       printSslErrStr("SSLSetPeerID", ortn);
+                       goto cleanup;
+               }
+       }
+       if(cipherRestrict != '\0') {
+               ortn = sslSetCipherRestrictions(ctx, cipherRestrict);
+               if(ortn) {
+                       goto cleanup;
+               }
+       }
+       if(authenticate != kNeverAuthenticate) {
+               ortn = SSLSetClientSideAuthenticate(ctx, authenticate);
+               if(ortn) {
+                       printSslErrStr("SSLSetClientSideAuthenticate", ortn);
+                       goto cleanup;
+               }
+       }
+       if(dhParams) {
+               ortn = SSLSetDiffieHellmanParams(ctx, dhParams, dhParamsLen);
+               if(ortn) {
+                       printSslErrStr("SSLSetDiffieHellmanParams", ortn);
+                       goto cleanup;
+               }
+       }
+       if(sessionCacheTimeout) {
+               ortn = SSLSetSessionCacheTimeout(ctx, sessionCacheTimeout);
+               if(ortn) {
+                       printSslErrStr("SSLSetSessionCacheTimeout", ortn);
+                       goto cleanup;
+               }
+       }
+       if(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 = errSecIO;
+                       goto cleanup;
+               }
+       }
+/* XXX/cs
+       if(acceptableDNList) {
+               ortn = SSLSetCertificateAuthorities(ctx, acceptableDNList, TRUE);
+               if(ortn) {
+                       printSslErrStr("SSLSetCertificateAuthorities", ortn);
+                       goto cleanup;
+               }
+       }
+*/
+       /* end options */
+
+       if(pause) {
+               doPause("SSLContext initialized");
+       }
+
+       /* Perform SSL/TLS handshake */
+    do
+    {   ortn = SSLHandshake(ctx);
+               if((ortn == errSSLWouldBlock) && !silent) {
+                       /* keep UI responsive */
+                       sslOutputDot();
+               }
+    } while (ortn == errSSLWouldBlock);
+
+       /* this works even if handshake failed due to cert chain invalid */
+       copyPeerCerts(ctx, peerCerts);
+
+       SSLGetClientCertificateState(ctx, certState);
+       SSLGetNegotiatedCipher(ctx, negCipher);
+       SSLGetNegotiatedProtocolVersion(ctx, negVersion);
+       *sessionIDLength = MAX_SESSION_ID_LENGTH;
+       SSLGetResumableSessionInfo(ctx, sessionWasResumed, sessionID,
+               sessionIDLength);
+
+       if(!silent) {
+               printf("\n");
+       }
+    if(ortn) {
+       goto cleanup;
+    }
+       if(pause) {
+               doPause("SSLContext handshake complete");
+       }
+
+       /* wait for one complete line or user says they've had enough */
+       while(ortn == errSecSuccess) {
+               length = sizeof(rcvBuf);
+               ortn = SSLRead(ctx, rcvBuf, length, &length);
+               if(length == 0) {
+                       /* keep UI responsive */
+                       sslOutputDot();
+               }
+               else {
+                       /* print what we have */
+                       printf("client request: ");
+                       dumpAscii(rcvBuf, length);
+               }
+               if(pause) {
+                       /* allow user to bail */
+                       char resp;
+
+                       fpurge(stdin);
+                       printf("\nMore client request (y/anything): ");
+                       resp = getchar();
+                       if(resp != 'y') {
+                               break;
+                       }
+               }
+
+               /* poor person's line completion scan */
+               for(unsigned i=0; i<length; i++) {
+                       if((rcvBuf[i] == '\n') || (rcvBuf[i] == '\r')) {
+                               /* a labelled break would be nice here.... */
+                               goto serverResp;
+                       }
+               }
+               if (ortn == errSSLWouldBlock) {
+                       ortn = errSecSuccess;
+               }
+       }
+
+serverResp:
+       if(pause) {
+               doPause("Client GET msg received");
+       }
+
+       /* send out canned response */
+       length = strlen(outMsg);
+       ortn = SSLWrite(ctx, outMsg, length, &length);
+       if(ortn) {
+               printSslErrStr("SSLWrite", ortn);
+       }
+       if(pause) {
+               doPause("Server response sent");
+       }
+cleanup:
+       /*
+        * always do close, even on error - to flush outgoing write queue
+        */
+       OSStatus cerr = SSLClose(ctx);
+       if(ortn == errSecSuccess) {
+               ortn = cerr;
+       }
+       if(acceptSock) {
+               endpointShutdown(acceptSock);
+       }
+       if(ctx) {
+           SSLDisposeContext(ctx);
+       }
+       /* FIXME - dispose of serverCerts */
+       return ortn;
+}
+
+static void showPeerCerts(
+       CFArrayRef                      peerCerts,
+       bool                    verbose)
+{
+       CFIndex numCerts;
+       SecCertificateRef certRef;
+       CFIndex i;
+
+       if(peerCerts == NULL) {
+               return;
+       }
+       numCerts = CFArrayGetCount(peerCerts);
+       for(i=0; i<numCerts; i++) {
+               certRef = (SecCertificateRef)CFArrayGetValueAtIndex(peerCerts, i);
+               printf("\n================== Server Cert %lu ===================\n\n", i);
+               print_cert(certRef, verbose);
+               printf("\n=============== End of Server Cert %lu ===============\n", i);
+       }
+}
+
+static void writePeerCerts(
+       CFArrayRef                      peerCerts,
+       const char                      *fileBase)
+{
+       CFIndex numCerts;
+       SecCertificateRef certRef;
+       CFIndex i;
+       char fileName[100];
+
+       if(peerCerts == NULL) {
+               return;
+       }
+       numCerts = CFArrayGetCount(peerCerts);
+       for(i=0; i<numCerts; i++) {
+               sprintf(fileName, "%s%02d.cer", fileBase, (int)i);
+               certRef = (SecCertificateRef)CFArrayGetValueAtIndex(peerCerts, i);
+               writeFile(fileName, SecCertificateGetBytePtr(certRef),
+                       SecCertificateGetLength(certRef));
+       }
+       printf("...wrote %lu certs to fileBase %s\n", numCerts, fileBase);
+}
+
+static void showSSLResult(
+       SSLProtocol                     tryVersion,
+       char                            *acceptedProts,
+       OSStatus                        err,
+       SSLProtocol                     negVersion,
+       SSLCipherSuite          negCipher,
+       Boolean                         sessionWasResumed,
+       unsigned char           *sessionID,
+       size_t                          sessionIDLength,
+       CFArrayRef                      peerCerts,
+       bool                    displayPeerCerts,
+       SSLClientCertificateState       certState,
+       char                            *fileBase)              // non-NULL: write certs to file
+{
+       CFIndex numPeerCerts;
+
+       printf("\n");
+       if(acceptedProts) {
+               printf("   Allowed SSL versions   : %s\n", acceptedProts);
+       }
+       else {
+               printf("   Attempted  SSL version : %s\n",
+                       sslGetProtocolVersionString(tryVersion));
+       }
+       printf("   Result                 : %s\n", sslGetSSLErrString(err));
+       printf("   Negotiated SSL version : %s\n",
+               sslGetProtocolVersionString(negVersion));
+       printf("   Negotiated CipherSuite : %s\n",
+               sslGetCipherSuiteString(negCipher));
+       if(certState != kSSLClientCertNone) {
+               printf("   Client Cert State      : %s\n",
+                       sslGetClientCertStateString(certState));
+       }
+       printf("   Resumed Session        : ");
+       if(sessionWasResumed) {
+               for(unsigned dex=0; dex<sessionIDLength; dex++) {
+                       printf("%02X ", sessionID[dex]);
+                       if(((dex % 8) == 7) && (dex != (sessionIDLength - 1))) {
+                               printf("\n                            ");
+                       }
+               }
+               printf("\n");
+       }
+       else {
+               printf("NOT RESUMED\n");
+       }
+       if(peerCerts == NULL) {
+               numPeerCerts = 0;
+       }
+       else {
+               numPeerCerts = CFArrayGetCount(peerCerts);
+       }
+       printf("   Number of peer certs : %lu\n", numPeerCerts);
+       if(numPeerCerts != 0) {
+               if(displayPeerCerts) {
+                       showPeerCerts(peerCerts, false);
+               }
+               if(fileBase != NULL) {
+                       writePeerCerts(peerCerts, fileBase);
+               }
+       }
+       printf("\n");
+}
+
+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;
+}
+
+int main(int argc, char **argv)
+{
+       OSStatus            err;
+       int                                     arg;
+       char                            fullFileBase[100];
+       SSLProtocol                     negVersion;
+       SSLCipherSuite          negCipher;
+       Boolean                         sessionWasResumed;
+       unsigned char           sessionID[MAX_SESSION_ID_LENGTH];
+       size_t                          sessionIDLength;
+       CFArrayRef                      peerCerts = NULL;
+       char                            *argp;
+       otSocket                        listenSock;
+       CFArrayRef                      serverCerts = nil;              // required
+       CFArrayRef                      encryptCerts = nil;             // optional
+       SecKeychainRef          serverKc = nil;
+       SecKeychainRef          encryptKc = nil;
+       int                             loopNum;
+       int                                     errCount = 0;
+       SSLClientCertificateState certState;            // obtained from sslServe
+
+       /* user-spec'd parameters */
+       unsigned short          portNum = DEFAULT_PORT;
+       bool                    allowExpired = false;
+       bool                    allowAnyRoot = false;
+       char                            *fileBase = NULL;
+       bool                    displayRxData = false;
+       bool                    displayCerts = false;
+       char                            cipherRestrict = '\0';
+       SSLProtocol                     attemptProt = kTLSProtocol1;
+       bool                    protXOnly = false;      // kSSLProtocol3Only,
+                                                                                               //    kTLSProtocol1Only
+       char                            *acceptedProts = NULL;  // "23t" ==> SSLSetProtocolVersionEnabled
+       bool                    quiet = false;
+       bool                    resumableEnable = true;
+       bool                    pause = false;
+       char                            *keyChainName = NULL;
+       char                            *encryptKeyChainName = NULL;
+       int                                     loops = 1;
+       SSLAuthenticate         authenticate = kNeverAuthenticate;
+       bool                    nonBlocking = false;
+       bool                    allowExpiredRoot = false;
+       bool                    disableCertVerify = false;
+       char                            *anchorFile = NULL;
+       bool                    replaceAnchors = false;
+       bool                    vfyCertState = false;
+       SSLClientCertificateState expectCertState = kSSLClientCertNone;
+       char                            *password = NULL;
+       char                            *dhParamsFile = NULL;
+       unsigned char           *dhParams = NULL;
+       unsigned                        dhParamsLen = 0;
+       bool                    doIdSearch = false;
+       bool                    completeCertChain = false;
+       uint32_t                                sessionCacheTimeout = 0;
+       bool                    disableAnonCiphers = false;
+       CFMutableArrayRef       acceptableDNList = NULL;
+
+       for(arg=1; arg<argc; arg++) {
+               argp = argv[arg];
+               switch(argp[0]) {
+                       case 'P':
+                               portNum = atoi(&argp[2]);
+                               break;
+                       case 'k':
+                               keyChainName = &argp[2];
+                               break;
+                       case 'y':
+                               encryptKeyChainName = &argp[2];
+                               break;
+                       case 'e':
+                               allowExpired = true;
+                               break;
+                       case 'E':
+                               allowExpiredRoot = true;
+                               break;
+                       case 'x':
+                               disableCertVerify = true;
+                               break;
+                       case 'a':
+                               if(++arg == argc)  {
+                                       /* requires another arg */
+                                       usage(argv);
+                               }
+                               anchorFile = argv[arg];
+                               break;
+                       case 'A':
+                               if(++arg == argc)  {
+                                       /* requires another arg */
+                                       usage(argv);
+                               }
+                               anchorFile = argv[arg];
+                               replaceAnchors = true;
+                               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 'r':
+                               allowAnyRoot = true;
+                               break;
+                       case 'd':
+                               displayRxData = true;
+                               break;
+                       case 'c':
+                               displayCerts = true;
+                               break;
+                       case 'f':
+                               fileBase = &argp[2];
+                               break;
+                       case 'C':
+                               cipherRestrict = argp[2];
+                               break;
+                       case '2':
+                               attemptProt = kSSLProtocol2;
+                               break;
+                       case '3':
+                               attemptProt = kSSLProtocol3;
+                               break;
+                       case 't':
+                               attemptProt = kTLSProtocol1;
+                               break;
+                       case 'o':
+                               protXOnly = true;
+                               break;
+                       case 'g':
+                               if(argp[1] != '=') {
+                                       usage(argv);
+                               }
+                               acceptedProts = &argp[2];
+                               break;
+                       case 'R':
+                               resumableEnable = false;
+                               break;
+                       case 'b':
+                               nonBlocking = true;
+                               break;
+                       case 'u':
+                               if(argp[1] != '=') {
+                                       usage(argv);
+                               }
+                               switch(argp[2]) {
+                                       case 'a': authenticate = kAlwaysAuthenticate; break;
+                                       case 'n': authenticate = kNeverAuthenticate; break;
+                                       case 't': authenticate = kTryAuthenticate; break;
+                                       default: usage(argv);
+                               }
+                               break;
+                       case 'D':
+                               if(++arg == argc)  {
+                                       /* requires another arg */
+                                       usage(argv);
+                               }
+                               dhParamsFile = argv[arg];
+                               break;
+                       case 'z':
+                               password = &argp[2];
+                               break;
+                       case 'H':
+                               doIdSearch = true;
+                               break;
+                       case 'M':
+                               completeCertChain = true;
+                               break;
+                       case 'i':
+                               sessionCacheTimeout = atoi(&argp[2]);
+                               break;
+                       case '4':
+                               disableAnonCiphers = true;
+                               break;
+                       case 'p':
+                               pause = true;
+                               break;
+                       case 'q':
+                               quiet = true;
+                               break;
+#if 0
+                       case 'U':
+                               if(++arg == argc)  {
+                                       /* requires another arg */
+                                       usage(argv);
+                               }
+                               if(cspReadFile(argv[arg], &caCert, &caCertLen)) {
+                                       printf("***Error reading file %s. Aborting.\n", argv[arg]);
+                                       exit(1);
+                               }
+                               if(acceptableDNList == NULL) {
+                                       acceptableDNList = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+                               }
+                               certData.Data = caCert;
+                               certData.Length = caCertLen;
+                               ortn = SecCertificateCreateFromData(&certData,
+                                                                                                       CSSM_CERT_X_509v3,
+                                                                                                       CSSM_CERT_ENCODING_DER,
+                                                                                                       &secCert);
+                               if(ortn) {
+                                       cssmPerror("SecCertificateCreateFromData", ortn);
+                                       exit(1);
+                               }
+                               CFArrayAppendValue(acceptableDNList, secCert);
+                               CFRelease(secCert);
+                               break;
+#endif
+                       case 'l':
+                               if(argp[1] == '\0') {
+                                       /* no loop count --> loop forever */
+                                       loops = 0;
+                                       break;
+                               }
+                               else if(argp[1] != '=') {
+                                       usage(argv);
+                               }
+                               loops = atoi(&argp[2]);
+                               break;
+                       default:
+                               usage(argv);
+               }
+       }
+
+#if NO_SERVER
+# if DEBUG
+    securityd_init();
+# endif
+#endif
+
+       /* get server cert and optional encryption cert as CFArrayRef */
+       if(keyChainName) {
+               serverCerts = getSslCerts(keyChainName, false, completeCertChain,
+                       anchorFile, &serverKc);
+               if(serverCerts == nil) {
+                       exit(1);
+               }
+       }
+       else
+#if 0
+    if(doIdSearch) {
+               OSStatus ortn = sslIdentityPicker(NULL, anchorFile, true, NULL, &serverCerts);
+               if(ortn) {
+                       printf("***IdentitySearch failure; aborting.\n");
+                       exit(1);
+               }
+       }
+       if(password) {
+               OSStatus ortn = SecKeychainUnlock(serverKc, strlen(password), password, true);
+               if(ortn) {
+                       printf("SecKeychainUnlock returned %d\n", (int)ortn);
+                       /* oh well */
+               }
+       }
+       if(encryptKeyChainName) {
+               encryptCerts = getSslCerts(encryptKeyChainName, true, completeCertChain,
+                       anchorFile, &encryptKc);
+               if(encryptCerts == nil) {
+                       exit(1);
+               }
+       }
+#endif
+       if(protXOnly) {
+               switch(attemptProt) {
+                       case kTLSProtocol1:
+                               attemptProt = kTLSProtocol1Only;
+                               break;
+                       case kSSLProtocol3:
+                               attemptProt = kSSLProtocol3Only;
+                               break;
+                       default:
+                               break;
+               }
+       }
+#if 0
+       if(dhParamsFile) {
+               int r = cspReadFile(dhParamsFile, &dhParams, &dhParamsLen);
+               if(r) {
+                       printf("***Error reading diffie-hellman params from %s; aborting\n",
+                               dhParamsFile);
+               }
+       }
+#endif
+
+       /* one-time only server port setup */
+       err = ListenForClients(portNum, nonBlocking, &listenSock);
+       if(err) {
+       printf("ListenForClients returned %d; aborting\n", (int)err);
+               exit(1);
+       }
+
+       for(loopNum=1; ; loopNum++) {
+               err = sslServe(listenSock,
+                       portNum,
+                       attemptProt,
+                       acceptedProts,
+                       serverCerts,
+                       password,
+                       encryptCerts,
+                       allowExpired,
+                       allowAnyRoot,
+                       allowExpiredRoot,
+                       disableCertVerify,
+                       anchorFile,
+                       replaceAnchors,
+                       cipherRestrict,
+                       authenticate,
+                       dhParams,
+                       dhParamsLen,
+                       acceptableDNList,
+                       resumableEnable,
+                       sessionCacheTimeout,
+                       disableAnonCiphers,
+                       quiet,
+                       pause,
+                       &negVersion,
+                       &negCipher,
+                       &certState,
+                       &sessionWasResumed,
+                       sessionID,
+                       &sessionIDLength,
+                       &peerCerts,
+                       argv);
+               if(err) {
+                       errCount++;
+               }
+               if(!quiet) {
+                       SSLProtocol tryProt = attemptProt;
+                       showSSLResult(tryProt,
+                               acceptedProts,
+                               err,
+                               negVersion,
+                               negCipher,
+                               sessionWasResumed,
+                               sessionID,
+                               sessionIDLength,
+                               peerCerts,
+                               displayCerts,
+                               certState,
+                               fileBase ? fullFileBase : NULL);
+               }
+               errCount += verifyClientCertState(vfyCertState, expectCertState,
+                       certState);
+               freePeerCerts(peerCerts);
+               if(loops && (loopNum == loops)) {
+                       break;
+               }
+       };
+
+       endpointShutdown(listenSock);
+
+       if(serverKc) {
+               CFRelease(serverKc);
+       }
+       if(encryptKc) {
+               CFRelease(encryptKc);
+       }
+    return errCount;
+
+}
+
+