]> git.saurik.com Git - apple/security.git/blobdiff - SecureTransport/sslContext.cpp
Security-54.1.3.tar.gz
[apple/security.git] / SecureTransport / sslContext.cpp
diff --git a/SecureTransport/sslContext.cpp b/SecureTransport/sslContext.cpp
new file mode 100644 (file)
index 0000000..203bc53
--- /dev/null
@@ -0,0 +1,1033 @@
+/*
+ * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved.
+ * 
+ * The contents of this file constitute Original Code as defined in and are
+ * subject to the Apple Public Source License Version 1.2 (the 'License').
+ * You may not use this file except in compliance with the License. Please obtain
+ * a copy of the License at http://www.apple.com/publicsource and read it before
+ * using this file.
+ * 
+ * This 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.
+ */
+
+
+/*
+       File:           sslContext.cpp
+
+       Contains:       SSLContext accessors
+
+       Written by:     Doug Mitchell
+
+       Copyright: (c) 1999 by Apple Computer, Inc., all rights reserved.
+
+*/
+
+#include "ssl.h"
+#include "sslContext.h"
+#include "sslMemory.h"
+#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
+#include "sslDigests.h"
+#include "sslDebug.h"
+#include "appleCdsa.h"
+#include "sslKeychain.h"
+#include "sslUtils.h"
+#include "cipherSpecs.h"
+#include "appleSession.h"
+#include <string.h>
+#include <Security/SecCertificate.h>
+#include <Security/SecTrust.h>
+
+static void sslFreeDnList(
+       SSLContext *ctx)
+{
+    DNListElem      *dn, *nextDN;
+    
+    dn = ctx->acceptableDNList;
+    while (dn)
+    {   
+       SSLFreeBuffer(dn->derDN, ctx);
+        nextDN = dn->next;
+        sslFree(dn);
+        dn = nextDN;
+    }
+    ctx->acceptableDNList = NULL;
+}
+
+static OSStatus sslFreeTrustedRoots(
+       SSLContext *ctx)
+{
+       unsigned i;
+       
+       assert(ctx != NULL);
+       if((ctx->numTrustedCerts == 0) || (ctx->trustedCerts == NULL)) {
+               /* they really should both be zero, right? */
+               assert((ctx->numTrustedCerts == 0) && (ctx->trustedCerts == NULL));
+       }
+       else {
+               for(i=0; i<ctx->numTrustedCerts; i++) {
+                       stFreeCssmData(&ctx->trustedCerts[i], CSSM_FALSE);
+               }
+               sslFree(ctx->trustedCerts);
+       }
+       ctx->numTrustedCerts = 0;
+       ctx->trustedCerts = NULL;
+       sslFreeDnList(ctx);
+       return noErr;
+}
+
+/*
+ * Default attempted version. 
+ */
+#define DEFAULT_MAX_VERSION            TLS_Version_1_0 
+
+OSStatus
+SSLNewContext                          (Boolean                        isServer,
+                                                        SSLContextRef          *contextPtr)    /* RETURNED */
+{
+       SSLContext      *ctx;
+       OSStatus        serr;
+               
+       if(contextPtr == NULL) {
+               return paramErr;
+       }
+       *contextPtr = NULL;
+       ctx = (SSLContext *)sslMalloc(sizeof(SSLContext));
+       if(ctx == NULL) {
+               return memFullErr;
+       }
+       /* subsequent errors to errOut: */
+       
+    memset(ctx, 0, sizeof(SSLContext));
+    ctx->state = SSL_HdskStateUninit;
+    ctx->clientCertState = kSSLClientCertNone;
+       
+    /* different defaults for client and server ... */
+    if(isServer) {
+       ctx->protocolSide = SSL_ServerSide;
+       ctx->reqProtocolVersion = DEFAULT_MAX_VERSION;
+    }
+    else {
+       ctx->protocolSide = SSL_ClientSide;
+       ctx->reqProtocolVersion = SSL_Version_Undetermined;
+    }
+    ctx->negProtocolVersion = SSL_Version_Undetermined;
+       ctx->maxProtocolVersion = DEFAULT_MAX_VERSION;
+       /* Default value so we can send and receive hello msgs */
+       ctx->sslTslCalls = &Ssl3Callouts;
+       
+    /* Initialize the cipher state to NULL_WITH_NULL_NULL */
+    ctx->selectedCipherSpec    = &SSL_NULL_WITH_NULL_NULL_CipherSpec;
+    ctx->selectedCipher        = ctx->selectedCipherSpec->cipherSpec;
+    ctx->writeCipher.macRef    = ctx->selectedCipherSpec->macAlgorithm;
+    ctx->readCipher.macRef     = ctx->selectedCipherSpec->macAlgorithm;
+    ctx->readCipher.symCipher  = ctx->selectedCipherSpec->cipher;
+    ctx->writeCipher.symCipher = ctx->selectedCipherSpec->cipher;
+       
+       /* these two are invariant */
+    ctx->writeCipher.encrypting = 1;
+    ctx->writePending.encrypting = 1;
+       
+    /* this gets init'd on first call to SSLHandshake() */
+    ctx->validCipherSpecs = NULL;
+    ctx->numValidCipherSpecs = 0;
+    
+       ctx->peerDomainName = NULL;
+       ctx->peerDomainNameLen = 0;
+
+       /* attach to CSP, CL, TP */
+       serr = attachToAll(ctx);
+       if(serr) {
+               goto errOut;
+       }
+       
+       /* Initial cert verify state: verify with default system roots */
+       ctx->enableCertVerify = true;
+       
+       /* snag root certs from Keychain, tolerate error */
+       addBuiltInCerts(ctx);
+       
+    *contextPtr = ctx;
+    return noErr;
+    
+errOut:
+       sslFree(ctx);
+       return serr;
+}
+
+
+/*
+ * Dispose of an SSLContext.
+ */
+OSStatus
+SSLDisposeContext                              (SSLContext                     *ctx)
+{   
+       WaitingRecord   *wait, *next;
+    SSLBuffer       buf;
+    
+    if(ctx == NULL) {
+       return paramErr;
+    }
+    sslDeleteCertificateChain(ctx->localCert, ctx);
+    sslDeleteCertificateChain(ctx->encryptCert, ctx);
+    sslDeleteCertificateChain(ctx->peerCert, ctx);
+    ctx->localCert = ctx->encryptCert = ctx->peerCert = NULL;
+    SSLFreeBuffer(ctx->partialReadBuffer, ctx);
+    
+    wait = ctx->recordWriteQueue;
+    while (wait)
+    {   SSLFreeBuffer(wait->data, ctx);
+        next = wait->next;
+        buf.data = (uint8*)wait;
+        buf.length = sizeof(WaitingRecord);
+        SSLFreeBuffer(buf, ctx);
+        wait = next;
+    }
+    
+    SSLFreeBuffer(ctx->dhPeerPublic, ctx);
+    SSLFreeBuffer(ctx->dhExchangePublic, ctx);
+    SSLFreeBuffer(ctx->dhPrivate, ctx);
+    
+       CloseHash(SSLHashSHA1, ctx->shaState, ctx);
+       CloseHash(SSLHashMD5,  ctx->md5State, ctx);
+    
+    SSLFreeBuffer(ctx->sessionID, ctx);
+    SSLFreeBuffer(ctx->peerID, ctx);
+    SSLFreeBuffer(ctx->resumableSession, ctx);
+    SSLFreeBuffer(ctx->preMasterSecret, ctx);
+    SSLFreeBuffer(ctx->partialReadBuffer, ctx);
+    SSLFreeBuffer(ctx->fragmentedMessageCache, ctx);
+    SSLFreeBuffer(ctx->receivedDataBuffer, ctx);
+
+       if(ctx->peerDomainName) {
+               sslFree(ctx->peerDomainName);
+               ctx->peerDomainName = NULL;
+               ctx->peerDomainNameLen = 0;
+       }
+    SSLDisposeCipherSuite(&ctx->readCipher, ctx);
+    SSLDisposeCipherSuite(&ctx->writeCipher, ctx);
+    SSLDisposeCipherSuite(&ctx->readPending, ctx);
+    SSLDisposeCipherSuite(&ctx->writePending, ctx);
+
+       sslFree(ctx->validCipherSpecs);
+       ctx->validCipherSpecs = NULL;
+       ctx->numValidCipherSpecs = 0;
+       
+       /*
+        * NOTE: currently, all public keys come from the CL via CSSM_CL_CertGetKeyInfo.
+        * We really don't know what CSP the CL used to generate a public key (in fact,
+        * it uses the raw CSP only to get LogicalKeySizeInBits, but we can't know
+        * that). Thus using e.g. signingKeyCsp (or any other CSP) to free 
+        * signingPubKey is not tecnically accurate. However, our public keys 
+        * are all raw keys, and all Apple CSPs dispose of raw keys in the same
+        * way.
+        */
+       sslFreeKey(ctx->signingKeyCsp, &ctx->signingPubKey, NULL);
+       sslFreeKey(ctx->encryptKeyCsp, &ctx->encryptPubKey, NULL);
+       sslFreeKey(ctx->peerPubKeyCsp, &ctx->peerPubKey, NULL);
+       
+       sslFreeTrustedRoots(ctx);
+       
+       detachFromAll(ctx);
+           
+    memset(ctx, 0, sizeof(SSLContext));
+    sslFree(ctx);
+       sslCleanupSession();
+       return noErr;
+}
+
+/*
+ * Determine the state of an SSL session.
+ */
+OSStatus 
+SSLGetSessionState                     (SSLContextRef          context,
+                                                        SSLSessionState        *state)         /* RETURNED */
+{
+       SSLSessionState rtnState = kSSLIdle;
+       
+       if(context == NULL) {
+               return paramErr;
+       }
+       *state = rtnState;
+       switch(context->state) {
+               case SSL_HdskStateUninit:
+               case SSL_HdskStateServerUninit:
+               case SSL_HdskStateClientUninit:
+                       rtnState = kSSLIdle;
+                       break;
+               case SSL_HdskStateGracefulClose:
+                       rtnState = kSSLClosed;
+                       break;
+               case SSL_HdskStateErrorClose:
+               case SSL_HdskStateNoNotifyClose:
+                       rtnState = kSSLAborted;
+                       break;
+               case SSL2_HdskStateServerReady:
+               case SSL2_HdskStateClientReady:
+                       rtnState = kSSLConnected;
+                       break;
+               default:
+                       assert((context->state >= SSL_HdskStateServerHello) &&
+                               (context->state <= SSL2_HdskStateServerFinished));
+                       rtnState = kSSLHandshake;
+                       break;
+                       
+       }
+       *state = rtnState;
+       return noErr;
+}
+
+OSStatus 
+SSLSetIOFuncs                          (SSLContextRef          ctx, 
+                                                        SSLReadFunc            read,
+                                                        SSLWriteFunc           write)
+{
+       if(ctx == NULL) {
+               return paramErr;
+       }
+       if(sslIsSessionActive(ctx)) {
+               /* can't do this with an active session */
+               return badReqErr;
+       }
+       ctx->ioCtx.read = read;
+       ctx->ioCtx.write = write;
+       return noErr;
+}
+
+OSStatus
+SSLSetConnection                       (SSLContextRef          ctx,
+                                                        SSLConnectionRef       connection)
+{
+       if(ctx == NULL) {
+               return paramErr;
+       }
+       if(sslIsSessionActive(ctx)) {
+               /* can't do this with an active session */
+               return badReqErr;
+       }
+       ctx->ioCtx.ioRef = connection;
+    return noErr;
+}
+
+OSStatus
+SSLSetPeerDomainName           (SSLContextRef          ctx,
+                                                        const char                     *peerName,
+                                                        size_t                         peerNameLen)
+{
+       if(ctx == NULL) {
+               return paramErr;
+       }
+       if(sslIsSessionActive(ctx)) {
+               /* can't do this with an active session */
+               return badReqErr;
+       }
+       
+       /* free possible existing name */
+       if(ctx->peerDomainName) {
+               sslFree(ctx->peerDomainName);
+       }
+       
+       /* copy in */
+       ctx->peerDomainName = (char *)sslMalloc(peerNameLen);
+       if(ctx->peerDomainName == NULL) {
+               return memFullErr;
+       }
+       memmove(ctx->peerDomainName, peerName, peerNameLen);
+       ctx->peerDomainNameLen = peerNameLen;
+       return noErr;
+}
+               
+/*
+ * Determine the buffer size needed for SSLGetPeerDomainName().
+ */
+OSStatus 
+SSLGetPeerDomainNameLength     (SSLContextRef          ctx,
+                                                        size_t                         *peerNameLen)   // RETURNED
+{
+       if(ctx == NULL) {
+               return paramErr;
+       }
+       *peerNameLen = ctx->peerDomainNameLen;
+       return noErr;
+}
+
+OSStatus 
+SSLGetPeerDomainName           (SSLContextRef          ctx,
+                                                        char                           *peerName,              // returned here
+                                                        size_t                         *peerNameLen)   // IN/OUT
+{
+       if(ctx == NULL) {
+               return paramErr;
+       }
+       if(*peerNameLen < ctx->peerDomainNameLen) {
+               return errSSLBufferOverflow;
+       }
+       memmove(peerName, ctx->peerDomainName, ctx->peerDomainNameLen);
+       *peerNameLen = ctx->peerDomainNameLen;
+       return noErr;
+}
+
+OSStatus 
+SSLSetProtocolVersion          (SSLContextRef          ctx,
+                                                        SSLProtocol            version)
+{   
+       SSLProtocolVersion      versInt;
+       SSLProtocolVersion      versMax;
+       
+       if(ctx == NULL) {
+               return paramErr;
+       }
+       if(sslIsSessionActive(ctx)) {
+               /* can't do this with an active session */
+               return badReqErr;
+       }
+
+       /* convert external representation to private */
+       switch(version) {
+               case kSSLProtocolUnknown:
+                       versInt = SSL_Version_Undetermined;
+                       versMax = DEFAULT_MAX_VERSION;
+                       break;
+               case kSSLProtocol2:
+                       versInt = versMax = SSL_Version_2_0;
+                       break;
+               case kSSLProtocol3:
+                       /* this tells us to do our best but allows 2.0 */
+                       versInt = SSL_Version_Undetermined;
+                       versMax = SSL_Version_3_0;
+                       break;
+               case kSSLProtocol3Only:
+                       versInt = SSL_Version_3_0_Only;
+                       versMax = SSL_Version_3_0;
+                       break;
+               case kTLSProtocol1:
+                       /* this tells us to do our best but allows 2.0 */
+                       versInt = SSL_Version_Undetermined;
+                       versMax = TLS_Version_1_0;
+                       break;
+               case kTLSProtocol1Only:
+                       versInt = TLS_Version_1_0_Only;
+                       versMax = TLS_Version_1_0;
+                       break;
+               default:
+                       return paramErr;
+       }
+       ctx->reqProtocolVersion = ctx->negProtocolVersion = versInt;
+       ctx->maxProtocolVersion = versMax;
+    return noErr;
+}
+
+static SSLProtocol convertProtToExtern(SSLProtocolVersion prot)
+{
+       switch(prot) {
+               case SSL_Version_Undetermined:
+                       return kSSLProtocolUnknown;
+               case SSL_Version_3_0_Only:
+                       return kSSLProtocol3Only;
+               case SSL_Version_2_0:
+                       return kSSLProtocol2;
+               case SSL_Version_3_0:
+                       return kSSLProtocol3;
+               case TLS_Version_1_0_Only:
+                       return kTLSProtocol1Only;
+               case TLS_Version_1_0:
+                       return kTLSProtocol1;
+               /* this can happen in an intermediate state while negotiation
+                * is active...right? */
+               case SSL_Version_3_0_With_2_0_Hello:
+                       return kSSLProtocolUnknown;
+               default:
+                       sslErrorLog("convertProtToExtern: bad prot\n");
+                       return kSSLProtocolUnknown;
+       }
+       /* not reached but make compiler happy */
+       return kSSLProtocolUnknown;
+}
+
+OSStatus 
+SSLGetProtocolVersion          (SSLContextRef          ctx,
+                                                        SSLProtocol            *protocol)              /* RETURNED */
+{
+       if(ctx == NULL) {
+               return paramErr;
+       }
+       *protocol = convertProtToExtern(ctx->reqProtocolVersion);
+       return noErr;
+}
+
+OSStatus 
+SSLGetNegotiatedProtocolVersion                (SSLContextRef          ctx,
+                                                                        SSLProtocol            *protocol) /* RETURNED */
+{
+       if(ctx == NULL) {
+               return paramErr;
+       }
+       *protocol = convertProtToExtern(ctx->negProtocolVersion);
+       return noErr;
+}
+
+OSStatus
+SSLSetEnableCertVerify         (SSLContextRef          ctx,
+                                                        Boolean                        enableVerify)
+{
+       if(ctx == NULL) {
+               return paramErr;
+       }
+       if(sslIsSessionActive(ctx)) {
+               /* can't do this with an active session */
+               return badReqErr;
+       }
+       ctx->enableCertVerify = enableVerify;
+       return noErr;
+}
+
+OSStatus 
+SSLGetEnableCertVerify         (SSLContextRef          ctx,
+                                                       Boolean                         *enableVerify)
+{
+       if(ctx == NULL) {
+               return paramErr;
+       }
+       *enableVerify = ctx->enableCertVerify;
+       return noErr;
+}
+
+OSStatus 
+SSLSetAllowsExpiredCerts(SSLContextRef         ctx,
+                                                Boolean                        allowExpired)
+{
+       if(ctx == NULL) {
+               return paramErr;
+       }
+       if(sslIsSessionActive(ctx)) {
+               /* can't do this with an active session */
+               return badReqErr;
+       }
+       ctx->allowExpiredCerts = allowExpired;
+       return noErr;
+}
+
+OSStatus
+SSLGetAllowsExpiredCerts       (SSLContextRef          ctx,
+                                                        Boolean                        *allowExpired)
+{
+       if(ctx == NULL) {
+               return paramErr;
+       }
+       *allowExpired = ctx->allowExpiredCerts;
+       return noErr;
+}
+
+OSStatus 
+SSLSetAllowsExpiredRoots(SSLContextRef         ctx,
+                                                Boolean                        allowExpired)
+{
+       if(ctx == NULL) {
+               return paramErr;
+       }
+       if(sslIsSessionActive(ctx)) {
+               /* can't do this with an active session */
+               return badReqErr;
+       }
+       ctx->allowExpiredRoots = allowExpired;
+       return noErr;
+}
+
+OSStatus
+SSLGetAllowsExpiredRoots       (SSLContextRef          ctx,
+                                                        Boolean                        *allowExpired)
+{
+       if(ctx == NULL) {
+               return paramErr;
+       }
+       *allowExpired = ctx->allowExpiredRoots;
+       return noErr;
+}
+
+OSStatus SSLSetAllowsAnyRoot(
+       SSLContextRef   ctx,
+       Boolean                 anyRoot)
+{
+       if(ctx == NULL) {
+               return paramErr;
+       }
+       ctx->allowAnyRoot = anyRoot;
+       return noErr;
+}
+
+OSStatus
+SSLGetAllowsAnyRoot(
+       SSLContextRef   ctx,
+       Boolean                 *anyRoot)
+{
+       if(ctx == NULL) {
+               return paramErr;
+       }
+       *anyRoot = ctx->allowAnyRoot;
+       return noErr;
+}
+
+OSStatus 
+SSLSetTrustedRoots                     (SSLContextRef          ctx,
+                                                        CFArrayRef             trustedRoots,
+                                                        Boolean                        replaceExisting)
+{
+       unsigned                        dex;
+       unsigned                        outDex;
+       unsigned                        numIncoming;
+       uint32                          numCerts;
+       CSSM_DATA_PTR           newRoots = NULL;
+       const CSSM_DATA         *existAnchors = NULL;
+       uint32                          numExistAnchors = 0;
+       OSStatus                        ortn = noErr;
+       
+       if(ctx == NULL) {
+               return paramErr;
+       }
+       if(sslIsSessionActive(ctx)) {
+               /* can't do this with an active session */
+               return badReqErr;
+       }
+       numCerts = numIncoming = CFArrayGetCount(trustedRoots);
+       if(!replaceExisting) {
+               if(ctx->trustedCerts != NULL) {
+                       /* adding to existing store */
+                       existAnchors = ctx->trustedCerts;
+                       numExistAnchors = ctx->numTrustedCerts;
+               }
+               else {
+                       /* adding to system roots */
+                       ortn = SecTrustGetCSSMAnchorCertificates(&existAnchors,
+                               &numExistAnchors);
+                       if(ortn) {
+                               /* should never happen */
+                               return ortn;
+                       }
+               }
+               numCerts += numExistAnchors;
+       }
+       newRoots = (CSSM_DATA_PTR)sslMalloc(numCerts * sizeof(CSSM_DATA));
+       memset(newRoots, 0, numCerts * sizeof(CSSM_DATA));
+       
+       /* Caller's certs first */
+       for(dex=0, outDex=0; dex<numIncoming; dex++, outDex++) {
+               CSSM_DATA certData;
+               SecCertificateRef secCert = (SecCertificateRef)
+                       CFArrayGetValueAtIndex(trustedRoots, dex);
+               
+               if(CFGetTypeID(secCert) != SecCertificateGetTypeID()) {
+                       /* elements of trustedRoots must be SecCertificateRefs */
+                       ortn = paramErr;
+                       goto abort;
+               }
+               ortn = SecCertificateGetData(secCert, &certData);
+               if(ortn) {
+                       goto abort;
+               }
+               stSetUpCssmData(&newRoots[outDex], certData.Length);
+               memmove(newRoots[outDex].Data, certData.Data, certData.Length);
+       }
+       
+       /* now existing roots - either ours, or the system's */
+       for(dex=0; dex<numExistAnchors; dex++, outDex++) {
+               stSetUpCssmData(&newRoots[outDex], existAnchors[dex].Length);
+               memmove(newRoots[outDex].Data, existAnchors[dex].Data, 
+                       existAnchors[dex].Length);
+       }
+       
+       /* success - replace context values */
+       sslFreeTrustedRoots(ctx);
+       ctx->numTrustedCerts = numCerts;
+       ctx->trustedCerts = newRoots;
+       return noErr;
+       
+abort:
+       sslFree(newRoots);
+       return ortn;
+}
+
+OSStatus 
+SSLGetTrustedRoots                     (SSLContextRef          ctx,
+                                                        CFArrayRef             *trustedRoots)  /* RETURNED */
+{
+       uint32                          numCerts;
+       const CSSM_DATA         *certs;
+       CFMutableArrayRef       certArray;
+       unsigned                        dex;
+       SecCertificateRef       secCert;
+       OSStatus                        ortn;
+       
+       if(ctx == NULL) {
+               return paramErr;
+       }
+       if(ctx->trustedCerts != NULL) {
+               /* use ours */
+               certs = ctx->trustedCerts;
+               numCerts = ctx->numTrustedCerts;
+       }
+       else {
+               /* use default system roots */
+               OSStatus ortn = SecTrustGetCSSMAnchorCertificates(&certs,
+                       &numCerts);
+               if(ortn) {
+                       /* should never happen */
+                       return ortn;
+               }
+       }
+       
+       certArray = CFArrayCreateMutable(kCFAllocatorDefault,
+               (CFIndex)numCerts, &kCFTypeArrayCallBacks);
+       if(certArray == NULL) {
+               return memFullErr;      
+       }
+       for(dex=0; dex<numCerts; dex++) {
+               ortn = SecCertificateCreateFromData(&certs[dex],
+                       CSSM_CERT_X_509v3,
+                       CSSM_CERT_ENCODING_DER,
+                       &secCert);
+               if(ortn) {
+                       CFRelease(certArray);
+                       return ortn;
+               }
+               CFArrayAppendValue(certArray, secCert);
+       }
+       *trustedRoots = certArray;
+       return noErr;
+}
+
+OSStatus
+SSLSetClientSideAuthenticate   (SSLContext                     *ctx,
+                                                                SSLAuthenticate        auth)
+{
+       if(ctx == NULL) {
+               return paramErr;
+       }
+       if(sslIsSessionActive(ctx)) {
+               /* can't do this with an active session */
+               return badReqErr;
+       }
+       ctx->clientAuth = auth;
+       switch(auth) {
+               case kNeverAuthenticate:
+                       ctx->tryClientAuth = false;
+                       break;
+               case kAlwaysAuthenticate:
+               case kTryAuthenticate:
+                       ctx->tryClientAuth = true;
+                       break;
+       }
+       return noErr;
+}
+
+OSStatus 
+SSLGetClientCertificateState   (SSLContextRef                          ctx,
+                                                                SSLClientCertificateState      *clientState)
+{
+       if(ctx == NULL) {
+               return paramErr;
+       }
+       *clientState = ctx->clientCertState;
+       return noErr;
+}
+
+OSStatus
+SSLSetCertificate                      (SSLContextRef          ctx,
+                                                        CFArrayRef                     certRefs)
+{
+       /*
+        * -- free localCerts if we have any
+        * -- Get raw cert data, convert to ctx->localCert
+        * -- get pub, priv keys from certRef[0]
+        * -- validate cert chain
+        */
+       if(ctx == NULL) {
+               return paramErr;
+       }
+       if(sslIsSessionActive(ctx)) {
+               /* can't do this with an active session */
+               return badReqErr;
+       }
+       return parseIncomingCerts(ctx,
+               certRefs,
+               &ctx->localCert,
+               &ctx->signingPubKey,
+               &ctx->signingPrivKey,
+               &ctx->signingKeyCsp
+               #if ST_KC_KEYS_NEED_REF
+               ,
+               &ctx->signingKeyRef
+               #else
+               );
+               #endif
+}
+
+OSStatus
+SSLSetEncryptionCertificate    (SSLContextRef          ctx,
+                                                        CFArrayRef                     certRefs)
+{
+       /*
+        * -- free encryptCert if we have any
+        * -- Get raw cert data, convert to ctx->encryptCert
+        * -- get pub, priv keys from certRef[0]
+        * -- validate cert chain
+        */
+       if(ctx == NULL) {
+               return paramErr;
+       }
+       if(sslIsSessionActive(ctx)) {
+               /* can't do this with an active session */
+               return badReqErr;
+       }
+       return parseIncomingCerts(ctx,
+               certRefs,
+               &ctx->encryptCert,
+               &ctx->encryptPubKey,
+               &ctx->encryptPrivKey,
+               &ctx->encryptKeyCsp
+               #if     ST_KC_KEYS_NEED_REF
+               ,
+               &ctx->encryptKeyRef);
+               #else
+               );
+               #endif
+}
+
+#if            ST_MANAGES_TRUSTED_ROOTS
+
+/*
+ * Add (optional, additional) trusted root certs.
+ */
+OSStatus
+SSLSetTrustedRootCertKC                (SSLContextRef          ctx,
+                                                        KCRef                          keyChainRef,
+                                                        Boolean                        deleteExisting)
+{
+       /*
+        * -- free trustedCerts if deleteExisting
+        * -- Get raw cert data, add to ctx->trustedCerts
+        * -- verify that each of these is a valid (self-verifying)
+        *    root cert
+        * -- add each subject name to acceptableDNList
+        */
+       if((ctx == NULL) || (keyChainRef == nil)) {
+               return paramErr;
+       }
+       if(sslIsSessionActive(ctx)) {
+               /* can't do this with an active session */
+               return badReqErr;
+       }
+       if(deleteExisting) {
+               sslFreeTrustedRoots(ctx);
+       }
+       return parseTrustedKeychain(ctx, keyChainRef);
+}
+
+OSStatus 
+SSLSetNewRootKC                                (SSLContextRef          ctx,
+                                                        KCRef                          keyChainRef,
+                                                        void                           *accessCreds)
+{
+       if((ctx == NULL) || (keyChainRef == nil)) {
+               return paramErr;
+       }
+       if(sslIsSessionActive(ctx)) {
+               /* can't do this with an active session */
+               return badReqErr;
+       }
+       if(ctx->newRootCertKc != NULL) {
+               /* can't do this multiple times */
+               return badReqErr;
+       }
+       ctx->newRootCertKc = keyChainRef;
+       ctx->accessCreds = accessCreds;
+       return noErr;
+}
+#endif /* ST_MANAGES_TRUSTED_ROOTS */
+
+OSStatus 
+SSLSetPeerID                           (SSLContext             *ctx, 
+                                                        const void             *peerID,
+                                                        size_t                         peerIDLen)
+{
+       OSStatus serr;
+       
+       /* copy peerId to context->peerId */
+       if((ctx == NULL) || 
+          (peerID == NULL) ||
+          (peerIDLen == 0)) {
+               return paramErr;
+       }
+       if(sslIsSessionActive(ctx)) {
+               /* can't do this with an active session */
+               return badReqErr;
+       }
+       SSLFreeBuffer(ctx->peerID, ctx);
+       serr = SSLAllocBuffer(ctx->peerID, peerIDLen, ctx);
+       if(serr) {
+               return serr;
+       }
+       memmove(ctx->peerID.data, peerID, peerIDLen);
+       return noErr;
+}
+
+OSStatus
+SSLGetPeerID                           (SSLContextRef          ctx, 
+                                                        const void             **peerID,
+                                                        size_t                         *peerIDLen)
+{
+       *peerID = ctx->peerID.data;                     // may be NULL
+       *peerIDLen = ctx->peerID.length;
+       return noErr;
+}
+
+OSStatus 
+SSLGetNegotiatedCipher         (SSLContextRef          ctx,
+                                                        SSLCipherSuite         *cipherSuite)
+{
+       if(ctx == NULL) {
+               return paramErr;
+       }
+       if(!sslIsSessionActive(ctx)) {
+               return badReqErr;
+       }
+       *cipherSuite = (SSLCipherSuite)ctx->selectedCipher;
+       return noErr;
+}
+
+/*
+ * Add an acceptable distinguished name (client authentication only).
+ */
+OSStatus
+SSLAddDistinguishedName( 
+       SSLContextRef ctx, 
+       const void *derDN,
+       size_t derDNLen)
+{   
+    DNListElem      *dn;
+    OSStatus        err;
+    
+       dn = (DNListElem *)sslMalloc(sizeof(DNListElem));
+       if(dn == NULL) {
+               return memFullErr;
+       }
+    if ((err = SSLAllocBuffer(dn->derDN, derDNLen, ctx)) != 0)
+        return err;
+    memcpy(dn->derDN.data, derDN, derDNLen);
+    dn->next = ctx->acceptableDNList;
+    ctx->acceptableDNList = dn;
+    return noErr;
+}
+
+/*
+ * Request peer certificates. Valid anytime, subsequent to
+ * a handshake attempt.
+ */    
+OSStatus 
+SSLGetPeerCertificates         (SSLContextRef          ctx, 
+                                                        CFArrayRef                     *certs)
+{
+       uint32                          numCerts;
+       CFMutableArrayRef       ca;
+       CFIndex                         i;
+       SecCertificateRef       cfd;
+       OSStatus                        ortn;
+       CSSM_DATA                       certData;
+       SSLCertificate          *scert;
+       
+       if(ctx == NULL) {
+               return paramErr;
+       }
+       *certs = NULL;
+       
+       /* 
+        * Copy peerCert, a chain of SSLCertificates, to a CFArray of 
+        * CFDataRefs, each of which is one DER-encoded cert.
+        */
+       numCerts = SSLGetCertificateChainLength(ctx->peerCert);
+       if(numCerts == 0) {
+               return noErr;
+       }
+       ca = CFArrayCreateMutable(kCFAllocatorDefault,
+               (CFIndex)numCerts, &kCFTypeArrayCallBacks);
+       if(ca == NULL) {
+               return memFullErr;      
+       }
+       
+       /*
+        * Caller gets leaf cert first, the opposite of the way we store them.
+        */
+       scert = ctx->peerCert;
+       for(i=0; (unsigned)i<numCerts; i++) {
+               assert(scert != NULL);          /* else SSLGetCertificateChainLength 
+                                                                        * broken */
+               SSLBUF_TO_CSSM(&scert->derCert, &certData);
+               ortn = SecCertificateCreateFromData(&certData,
+                       CSSM_CERT_X_509v3,
+                       CSSM_CERT_ENCODING_DER,
+                       &cfd);
+               if(ortn) {
+                       CFRelease(ca);
+                       return ortn;
+               }
+               /* insert at head of array */
+               CFArrayInsertValueAtIndex(ca, 0, cfd);
+               scert = scert->next;
+       }
+       *certs = ca;
+       return noErr;
+}                                                                                                                       
+   
+OSStatus SSLInternalMasterSecret(
+   SSLContextRef ctx,
+   void *secret,        // mallocd by caller, SSL_MASTER_SECRET_SIZE 
+   size_t *secretSize)  // in/out   
+{
+       if((ctx == NULL) || (secret == NULL) || (secretSize == NULL)) {
+               return paramErr;
+       }
+       if(*secretSize < SSL_MASTER_SECRET_SIZE) {
+               return paramErr;
+       }
+       memmove(secret, ctx->masterSecret, SSL_MASTER_SECRET_SIZE);
+       *secretSize = SSL_MASTER_SECRET_SIZE;
+       return noErr;
+}
+
+OSStatus SSLInternalServerRandom(
+   SSLContextRef ctx,
+   void *rand,                         // mallocd by caller, SSL_CLIENT_SRVR_RAND_SIZE
+   size_t *randSize)   // in/out   
+{
+       if((ctx == NULL) || (rand == NULL) || (randSize == NULL)) {
+               return paramErr;
+       }
+       if(*randSize < SSL_CLIENT_SRVR_RAND_SIZE) {
+               return paramErr;
+       }
+       memmove(rand, ctx->serverRandom, SSL_CLIENT_SRVR_RAND_SIZE);
+       *randSize = SSL_CLIENT_SRVR_RAND_SIZE;
+       return noErr;
+}
+
+OSStatus SSLInternalClientRandom(
+   SSLContextRef ctx,
+   void *rand,                 // mallocd by caller, SSL_CLIENT_SRVR_RAND_SIZE
+   size_t *randSize)   // in/out   
+{
+       if((ctx == NULL) || (rand == NULL) || (randSize == NULL)) {
+               return paramErr;
+       }
+       if(*randSize < SSL_CLIENT_SRVR_RAND_SIZE) {
+               return paramErr;
+       }
+       memmove(rand, ctx->clientRandom, SSL_CLIENT_SRVR_RAND_SIZE);
+       *randSize = SSL_CLIENT_SRVR_RAND_SIZE;
+       return noErr;
+}
+
+
+