/* * Copyright (c) 1999-2001,2005-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@ */ /* * sslContext.c - SSLContext accessors */ #include "SecureTransport.h" #include "SSLRecordInternal.h" #include "SecureTransportPriv.h" #include "appleSession.h" #include "ssl.h" #include "sslCipherSpecs.h" #include "sslContext.h" #include "sslCrypto.h" #include "sslDebug.h" #include "sslDigests.h" #include "sslKeychain.h" #include "sslMemory.h" #include "sslUtils.h" #include "tlsCallbacks.h" #include #include #include #include #include #include #include #include #include "utilities/SecCFRelease.h" #include "utilities/SecCFWrappers.h" #include #include #if TARGET_OS_IPHONE #include #else #include #include #include #endif static void sslFreeDnList( SSLContext *ctx) { DNListElem *dn, *nextDN; dn = ctx->acceptableDNList; while (dn) { SSLFreeBuffer(&dn->derDN); nextDN = dn->next; sslFree(dn); dn = nextDN; } ctx->acceptableDNList = NULL; } Boolean sslIsSessionActive(const SSLContext *ctx) { assert(ctx != NULL); switch(ctx->state) { case SSL_HdskStateUninit: case SSL_HdskStateGracefulClose: case SSL_HdskStateErrorClose: return false; default: return true; } } /* * Minimum and maximum supported versions */ //#define MINIMUM_STREAM_VERSION SSL_Version_2_0 /* Disabled */ #define MINIMUM_STREAM_VERSION SSL_Version_3_0 #define MAXIMUM_STREAM_VERSION TLS_Version_1_2 #define MINIMUM_DATAGRAM_VERSION DTLS_Version_1_0 /* This should be changed when we start supporting DTLS_Version_1_x */ #define MAXIMUM_DATAGRAM_VERSION DTLS_Version_1_0 #define SSL_ENABLE_ECDSA_SIGN_AUTH 0 #define SSL_ENABLE_RSA_FIXED_ECDH_AUTH 0 #define SSL_ENABLE_ECDSA_FIXED_ECDH_AUTH 0 #define DEFAULT_DTLS_TIMEOUT 1 #define DEFAULT_DTLS_MTU 1400 #define MIN_ALLOWED_DTLS_MTU 64 /* this ensure than there will be no integer underflow when calculating max write size */ int kSplitDefaultValue; static void _SSLContextReadDefault() { /* 0 = disabled, 1 = split every write, 2 = split second and subsequent writes */ /* Enabled by default, this may cause some interop issues, see and */ const int defaultSplitDefaultValue = 2; CFTypeRef value = (CFTypeRef)CFPreferencesCopyValue(CFSTR("SSLWriteSplit"), CFSTR("com.apple.security"), kCFPreferencesAnyUser, kCFPreferencesCurrentHost); if (value) { if (CFGetTypeID(value) == CFBooleanGetTypeID()) kSplitDefaultValue = CFBooleanGetValue((CFBooleanRef)value) ? 1 : 0; else if (CFGetTypeID(value) == CFNumberGetTypeID()) { if (!CFNumberGetValue((CFNumberRef)value, kCFNumberIntType, &kSplitDefaultValue)) kSplitDefaultValue = defaultSplitDefaultValue; } if (kSplitDefaultValue < 0 || kSplitDefaultValue > 2) { kSplitDefaultValue = defaultSplitDefaultValue; } CFRelease(value); } else { kSplitDefaultValue = defaultSplitDefaultValue; } } CFGiblisWithHashFor(SSLContext) OSStatus SSLNewContext (Boolean isServer, SSLContextRef *contextPtr) /* RETURNED */ { if(contextPtr == NULL) { return errSecParam; } *contextPtr = SSLCreateContext(kCFAllocatorDefault, isServer?kSSLServerSide:kSSLClientSide, kSSLStreamType); if (*contextPtr == NULL) return errSecAllocate; return errSecSuccess; } SSLContextRef SSLCreateContext(CFAllocatorRef alloc, SSLProtocolSide protocolSide, SSLConnectionType connectionType) { SSLContextRef ctx; SSLRecordContextRef recCtx; ctx = SSLCreateContextWithRecordFuncs(alloc, protocolSide, connectionType, &SSLRecordLayerInternal); if(ctx==NULL) return NULL; recCtx = SSLCreateInternalRecordLayer(connectionType); if(recCtx==NULL) { CFRelease(ctx); return NULL; } SSLSetRecordContext(ctx, recCtx); return ctx; } SSLContextRef SSLCreateContextWithRecordFuncs(CFAllocatorRef alloc, SSLProtocolSide protocolSide, SSLConnectionType connectionType, const struct SSLRecordFuncs *recFuncs) { OSStatus serr = errSecSuccess; SSLContext *ctx = (SSLContext*) _CFRuntimeCreateInstance(alloc, SSLContextGetTypeID(), sizeof(SSLContext) - sizeof(CFRuntimeBase), NULL); if(ctx == NULL) { return NULL; } /* subsequent errors to errOut: */ memset(((uint8_t*) ctx) + sizeof(CFRuntimeBase), 0, sizeof(SSLContext) - sizeof(CFRuntimeBase)); ctx->hdsk = tls_handshake_create(connectionType==kSSLDatagramType, protocolSide==kSSLServerSide); tls_handshake_set_callbacks(ctx->hdsk, &tls_handshake_callbacks, ctx); ctx->isDTLS = (connectionType==kSSLDatagramType); ctx->state = SSL_HdskStateUninit; ctx->timeout_duration = DEFAULT_DTLS_TIMEOUT; ctx->mtu = DEFAULT_DTLS_MTU; if(ctx->isDTLS) { ctx->minProtocolVersion = MINIMUM_DATAGRAM_VERSION; ctx->maxProtocolVersion = MAXIMUM_DATAGRAM_VERSION; } else { ctx->minProtocolVersion = MINIMUM_STREAM_VERSION; ctx->maxProtocolVersion = MAXIMUM_STREAM_VERSION; } tls_handshake_set_min_protocol_version(ctx->hdsk, ctx->minProtocolVersion); tls_handshake_set_max_protocol_version(ctx->hdsk, ctx->maxProtocolVersion); ctx->negProtocolVersion = SSL_Version_Undetermined; ctx->protocolSide = protocolSide; ctx->recFuncs = recFuncs; /* Initial cert verify state: verify with default system roots */ ctx->enableCertVerify = true; /* Default for RSA blinding is ENABLED */ ctx->rsaBlindingEnable = true; /* Default for sending one-byte app data record is DISABLED */ ctx->oneByteRecordEnable = false; /* Dont enable fallback behavior by default */ ctx->fallbackEnabled = false; /* Consult global system preference for default behavior: * 0 = disabled, 1 = split every write, 2 = split second and subsequent writes * (caller can override by setting kSSLSessionOptionSendOneByteRecord) */ static pthread_once_t sReadDefault = PTHREAD_ONCE_INIT; pthread_once(&sReadDefault, _SSLContextReadDefault); if (kSplitDefaultValue > 0) ctx->oneByteRecordEnable = true; /* default for anonymous ciphers is DISABLED */ ctx->anonCipherEnable = false; ctx->breakOnServerAuth = false; ctx->breakOnCertRequest = false; ctx->breakOnClientAuth = false; ctx->signalServerAuth = false; ctx->signalCertRequest = false; ctx->signalClientAuth = false; ctx->negAuthType = SSLClientAuthNone; /* ditto */ if (serr != errSecSuccess) { CFRelease(ctx); ctx = NULL; } return ctx; } OSStatus SSLNewDatagramContext (Boolean isServer, SSLContextRef *contextPtr) /* RETURNED */ { if (contextPtr == NULL) return errSecParam; *contextPtr = SSLCreateContext(kCFAllocatorDefault, isServer?kSSLServerSide:kSSLClientSide, kSSLDatagramType); if (*contextPtr == NULL) return errSecAllocate; return errSecSuccess; } /* * Dispose of an SSLContext. (private) * This function is invoked after our dispatch queue is safely released, * or directly from SSLDisposeContext if there is no dispatch queue. */ OSStatus SSLDisposeContext (SSLContextRef context) { if(context == NULL) { return errSecParam; } CFRelease(context); return errSecSuccess; } CFStringRef SSLContextCopyDescription(CFTypeRef arg) { SSLContext* ctx = (SSLContext*) arg; if (ctx == NULL) { return NULL; } else { CFStringRef result = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR(""), ctx); return result; } } Boolean SSLContextCompare(CFTypeRef a, CFTypeRef b) { return a == b; } CFHashCode SSLContextHash(CFTypeRef arg) { return (CFHashCode) arg; } void SSLContextDestroy(CFTypeRef arg) { SSLContext* ctx = (SSLContext*) arg; /* destroy the coreTLS handshake object */ tls_handshake_destroy(ctx->hdsk); /* Only destroy if we were using the internal record layer */ if(ctx->recFuncs==&SSLRecordLayerInternal) SSLDestroyInternalRecordLayer(ctx->recCtx); SSLFreeBuffer(&ctx->sessionTicket); SSLFreeBuffer(&ctx->sessionID); SSLFreeBuffer(&ctx->peerID); SSLFreeBuffer(&ctx->resumableSession); SSLFreeBuffer(&ctx->receivedDataBuffer); sslFree(ctx->validCipherSuites); ctx->validCipherSuites = NULL; ctx->numValidCipherSuites = 0; CFReleaseSafe(ctx->acceptableCAs); CFReleaseSafe(ctx->trustedLeafCerts); CFReleaseSafe(ctx->localCertArray); CFReleaseSafe(ctx->encryptCertArray); CFReleaseSafe(ctx->encryptCertArray); CFReleaseSafe(ctx->peerCert); CFReleaseSafe(ctx->trustedCerts); CFReleaseSafe(ctx->peerSecTrust); sslFreePrivKey(&ctx->signingPrivKeyRef); sslFreePrivKey(&ctx->encryptPrivKeyRef); sslFree(ctx->localCert); sslFree(ctx->encryptCert); sslFreeDnList(ctx); SSLFreeBuffer(&ctx->ownVerifyData); SSLFreeBuffer(&ctx->peerVerifyData); SSLFreeBuffer(&ctx->pskIdentity); SSLFreeBuffer(&ctx->pskSharedSecret); SSLFreeBuffer(&ctx->dhParamsEncoded); memset(((uint8_t*) ctx) + sizeof(CFRuntimeBase), 0, sizeof(SSLContext) - sizeof(CFRuntimeBase)); sslCleanupSession(); } /* * Determine the state of an SSL session. */ OSStatus SSLGetSessionState (SSLContextRef context, SSLSessionState *state) /* RETURNED */ { SSLSessionState rtnState = kSSLIdle; if(context == NULL) { return errSecParam; } *state = rtnState; switch(context->state) { case SSL_HdskStateUninit: rtnState = kSSLIdle; break; case SSL_HdskStateGracefulClose: rtnState = kSSLClosed; break; case SSL_HdskStateErrorClose: case SSL_HdskStateNoNotifyClose: rtnState = kSSLAborted; break; case SSL_HdskStateReady: rtnState = kSSLConnected; break; case SSL_HdskStatePending: rtnState = kSSLHandshake; break; } *state = rtnState; return errSecSuccess; } /* * Set options for an SSL session. */ OSStatus SSLSetSessionOption (SSLContextRef context, SSLSessionOption option, Boolean value) { if(context == NULL) { return errSecParam; } if(sslIsSessionActive(context)) { /* can't do this with an active session */ return errSecBadReq; } switch(option) { case kSSLSessionOptionBreakOnServerAuth: context->breakOnServerAuth = value; context->enableCertVerify = !value; break; case kSSLSessionOptionBreakOnCertRequested: context->breakOnCertRequest = value; break; case kSSLSessionOptionBreakOnClientAuth: context->breakOnClientAuth = value; context->enableCertVerify = !value; break; case kSSLSessionOptionSendOneByteRecord: /* Only call the record layer function if the value changed */ if(value != context->oneByteRecordEnable) context->recFuncs->setOption(context->recCtx, kSSLRecordOptionSendOneByteRecord, value); context->oneByteRecordEnable = value; break; case kSSLSessionOptionFalseStart: context->falseStartEnabled = value; break; case kSSLSessionOptionFallback: tls_handshake_set_fallback(context->hdsk, value); context->fallbackEnabled = value; break; default: return errSecParam; } return errSecSuccess; } /* * Determine current value for the specified option in an SSL session. */ OSStatus SSLGetSessionOption (SSLContextRef context, SSLSessionOption option, Boolean *value) { if(context == NULL || value == NULL) { return errSecParam; } switch(option) { case kSSLSessionOptionBreakOnServerAuth: *value = context->breakOnServerAuth; break; case kSSLSessionOptionBreakOnCertRequested: *value = context->breakOnCertRequest; break; case kSSLSessionOptionBreakOnClientAuth: *value = context->breakOnClientAuth; break; case kSSLSessionOptionSendOneByteRecord: *value = context->oneByteRecordEnable; break; case kSSLSessionOptionFalseStart: *value = context->falseStartEnabled; break; default: return errSecParam; } return errSecSuccess; } OSStatus SSLSetRecordContext (SSLContextRef ctx, SSLRecordContextRef recCtx) { if(ctx == NULL) { return errSecParam; } if(sslIsSessionActive(ctx)) { /* can't do this with an active session */ return errSecBadReq; } ctx->recCtx = recCtx; return errSecSuccess; } /* Those two trampolines are used to make the connetion between the record layer IO callbacks and the user provided IO callbacks. Those are currently necessary because the record layer read/write callbacks have different prototypes that the user callbacks advertised in the API. They have different prototypes because the record layer callback have to build in kernelland. This situation is not desirable. So we should figure out a way to get rid of them. */ static int IORead(SSLIOConnectionRef connection, void *data, size_t *dataLength) { OSStatus rc; SSLContextRef ctx = connection; rc = ctx->ioCtx.read(ctx->ioCtx.ioRef, data, dataLength); /* We may need to translate error codes at this layer */ if(rc==errSSLWouldBlock) { rc=errSSLRecordWouldBlock; } return rc; } static int IOWrite(SSLIOConnectionRef connection, const void *data, size_t *dataLength) { OSStatus rc; SSLContextRef ctx = connection; rc = ctx->ioCtx.write(ctx->ioCtx.ioRef, data, dataLength); /* We may need to translate error codes at this layer */ if(rc==errSSLWouldBlock) { rc=errSSLRecordWouldBlock; } return rc; } OSStatus SSLSetIOFuncs (SSLContextRef ctx, SSLReadFunc readFunc, SSLWriteFunc writeFunc) { if(ctx == NULL) { return errSecParam; } if(ctx->recFuncs!=&SSLRecordLayerInternal) { /* Can Only do this with the internal record layer */ check(0); return errSecBadReq; } if(sslIsSessionActive(ctx)) { /* can't do this with an active session */ return errSecBadReq; } ctx->ioCtx.read=readFunc; ctx->ioCtx.write=writeFunc; return SSLSetInternalRecordLayerIOFuncs(ctx->recCtx, IORead, IOWrite); } void SSLSetNPNFunc(SSLContextRef context, SSLNPNFunc npnFunc, void *info) { if (context == NULL) { return; } if (sslIsSessionActive(context)) { return; } context->npnFunc = npnFunc; context->npnFuncInfo = info; if(context->protocolSide==kSSLClientSide) { tls_handshake_set_npn_enable(context->hdsk, npnFunc!=NULL); } } OSStatus SSLSetNPNData(SSLContextRef context, const void *data, size_t length) { if (context == NULL || data == NULL || length == 0) { return errSecParam; } if (length > 255) { return errSecParam; } tls_buffer npn_data; npn_data.data = (uint8_t *)data; npn_data.length = length; return tls_handshake_set_npn_data(context->hdsk, npn_data); } const void * SSLGetNPNData(SSLContextRef context, size_t *length) { if (context == NULL || length == NULL) return NULL; const tls_buffer *npn_data; npn_data = tls_handshake_get_peer_npn_data(context->hdsk); if(npn_data) { *length = npn_data->length; return npn_data->data; } else { return NULL; } } OSStatus SSLSetConnection (SSLContextRef ctx, SSLConnectionRef connection) { if(ctx == NULL) { return errSecParam; } if(ctx->recFuncs!=&SSLRecordLayerInternal) { /* Can Only do this with the internal record layer */ check(0); return errSecBadReq; } if(sslIsSessionActive(ctx)) { /* can't do this with an active session */ return errSecBadReq; } /* Need to keep a copy of it this layer for the Get function */ ctx->ioCtx.ioRef = connection; return SSLSetInternalRecordLayerConnection(ctx->recCtx, ctx); } OSStatus SSLGetConnection (SSLContextRef ctx, SSLConnectionRef *connection) { if((ctx == NULL) || (connection == NULL)) { return errSecParam; } *connection = ctx->ioCtx.ioRef; return errSecSuccess; } OSStatus SSLSetPeerDomainName (SSLContextRef ctx, const char *peerName, size_t peerNameLen) { if(ctx == NULL) { return errSecParam; } if(sslIsSessionActive(ctx)) { /* can't do this with an active session */ return errSecBadReq; } if(ctx->protocolSide == kSSLClientSide) { return tls_handshake_set_peer_hostname(ctx->hdsk, peerName, peerNameLen); } else { return 0; // This should probably return an error, but historically didnt. } } /* * Determine the buffer size needed for SSLGetPeerDomainName(). */ OSStatus SSLGetPeerDomainNameLength (SSLContextRef ctx, size_t *peerNameLen) // RETURNED { if(ctx == NULL) { return errSecParam; } const char *hostname; return tls_handshake_get_peer_hostname(ctx->hdsk, &hostname, peerNameLen); } OSStatus SSLGetPeerDomainName (SSLContextRef ctx, char *peerName, // returned here size_t *peerNameLen) // IN/OUT { const char *hostname; size_t len; int err; if(ctx == NULL) { return errSecParam; } err=tls_handshake_get_peer_hostname(ctx->hdsk, &hostname, &len); if(err) { return err; } else if(*peerNameLenisDTLS) return errSecParam; if((ctx == NULL) || (cookieLen>32)) { return errSecParam; } if(sslIsSessionActive(ctx)) { /* can't do this with an active session */ return errSecBadReq; } /* free possible existing cookie */ if(ctx->dtlsCookie.data) { SSLFreeBuffer(&ctx->dtlsCookie); } /* copy in */ if((err=SSLAllocBuffer(&ctx->dtlsCookie, cookieLen))) return err; memmove(ctx->dtlsCookie.data, cookie, cookieLen); return errSecSuccess; } OSStatus SSLSetMaxDatagramRecordSize (SSLContextRef ctx, size_t maxSize) { if(ctx == NULL) return errSecParam; if(!ctx->isDTLS) return errSecParam; tls_handshake_set_mtu(ctx->hdsk, maxSize); return errSecSuccess; } OSStatus SSLGetMaxDatagramRecordSize (SSLContextRef ctx, size_t *maxSize) { if(ctx == NULL) return errSecParam; if(!ctx->isDTLS) return errSecParam; *maxSize = ctx->mtu; return errSecSuccess; } /* Keys to to math below: A DTLS record looks like this: | header (13 bytes) | fragment | For Null cipher, fragment is clear text as follows: | Contents | Mac | For block cipher, fragment size must be a multiple of the cipher block size, and is the encryption of the following plaintext : | IV (1 block) | content | MAC | padding (0 to 255 bytes) | Padlen (1 byte) | The maximum content length in that case is achieved for 0 padding bytes. */ OSStatus SSLGetDatagramWriteSize (SSLContextRef ctx, size_t *bufSize) { if(ctx == NULL) return errSecParam; if(!ctx->isDTLS) return errSecParam; if(bufSize == NULL) return errSecParam; size_t max_fragment_size = ctx->mtu-13; /* 13 = dtls record header */ #warning SecureTransport: SSLGetDatagramWriteSize is wrong, need hookup with coreTLS #if 0 SSLCipherSpecParams *currCipher = &ctx->selectedCipherSpecParams; size_t blockSize = currCipher->blockSize; size_t macSize = currCipher->macSize; #else size_t blockSize = 16; size_t macSize = 32; #endif if (blockSize > 0) { /* max_fragment_size must be a multiple of blocksize */ max_fragment_size = max_fragment_size & ~(blockSize-1); max_fragment_size -= blockSize; /* 1 block for IV */ max_fragment_size -= 1; /* 1 byte for pad length */ } /* less the mac size */ max_fragment_size -= macSize; /* Thats just a sanity check */ assert(max_fragment_sizemtu); *bufSize = max_fragment_size; return errSecSuccess; } static SSLProtocolVersion SSLProtocolToProtocolVersion(SSLProtocol protocol) { switch (protocol) { case kSSLProtocol2: return SSL_Version_2_0; case kSSLProtocol3: return SSL_Version_3_0; case kTLSProtocol1: return TLS_Version_1_0; case kTLSProtocol11: return TLS_Version_1_1; case kTLSProtocol12: return TLS_Version_1_2; case kDTLSProtocol1: return DTLS_Version_1_0; default: return SSL_Version_Undetermined; } } /* concert between private SSLProtocolVersion and public SSLProtocol */ static SSLProtocol SSLProtocolVersionToProtocol(SSLProtocolVersion version) { switch(version) { case SSL_Version_2_0: return kSSLProtocol2; case SSL_Version_3_0: return kSSLProtocol3; case TLS_Version_1_0: return kTLSProtocol1; case TLS_Version_1_1: return kTLSProtocol11; case TLS_Version_1_2: return kTLSProtocol12; case DTLS_Version_1_0: return kDTLSProtocol1; default: sslErrorLog("SSLProtocolVersionToProtocol: bad prot (%04x)\n", version); /* DROPTHROUGH */ case SSL_Version_Undetermined: return kSSLProtocolUnknown; } } OSStatus SSLSetProtocolVersionMin (SSLContextRef ctx, SSLProtocol minVersion) { if(ctx == NULL) return errSecParam; SSLProtocolVersion version = SSLProtocolToProtocolVersion(minVersion); if (ctx->isDTLS) { if (version > MINIMUM_DATAGRAM_VERSION || version < MAXIMUM_DATAGRAM_VERSION) return errSSLIllegalParam; if (version < ctx->maxProtocolVersion) ctx->maxProtocolVersion = version; } else { if (version < MINIMUM_STREAM_VERSION || version > MAXIMUM_STREAM_VERSION) return errSSLIllegalParam; if (version > ctx->maxProtocolVersion) ctx->maxProtocolVersion = version; } ctx->minProtocolVersion = version; tls_handshake_set_min_protocol_version(ctx->hdsk, ctx->minProtocolVersion); tls_handshake_set_max_protocol_version(ctx->hdsk, ctx->maxProtocolVersion); return errSecSuccess; } OSStatus SSLGetProtocolVersionMin (SSLContextRef ctx, SSLProtocol *minVersion) { if(ctx == NULL) return errSecParam; *minVersion = SSLProtocolVersionToProtocol(ctx->minProtocolVersion); return errSecSuccess; } OSStatus SSLSetProtocolVersionMax (SSLContextRef ctx, SSLProtocol maxVersion) { if(ctx == NULL) return errSecParam; SSLProtocolVersion version = SSLProtocolToProtocolVersion(maxVersion); if (ctx->isDTLS) { if (version > MINIMUM_DATAGRAM_VERSION || version < MAXIMUM_DATAGRAM_VERSION) return errSSLIllegalParam; if (version > ctx->minProtocolVersion) ctx->minProtocolVersion = version; } else { if (version < MINIMUM_STREAM_VERSION || version > MAXIMUM_STREAM_VERSION) return errSSLIllegalParam; if (version < ctx->minProtocolVersion) ctx->minProtocolVersion = version; } ctx->maxProtocolVersion = version; tls_handshake_set_min_protocol_version(ctx->hdsk, ctx->minProtocolVersion); tls_handshake_set_max_protocol_version(ctx->hdsk, ctx->maxProtocolVersion); return errSecSuccess; } OSStatus SSLGetProtocolVersionMax (SSLContextRef ctx, SSLProtocol *maxVersion) { if(ctx == NULL) return errSecParam; *maxVersion = SSLProtocolVersionToProtocol(ctx->maxProtocolVersion); return errSecSuccess; } #define max(x,y) ((x)<(y)?(y):(x)) OSStatus SSLSetProtocolVersionEnabled(SSLContextRef ctx, SSLProtocol protocol, Boolean enable) { if(ctx == NULL) { return errSecParam; } if(sslIsSessionActive(ctx) || ctx->isDTLS) { /* Can't do this with an active session, nor with a DTLS session */ return errSecBadReq; } if (protocol == kSSLProtocolAll) { if (enable) { ctx->minProtocolVersion = MINIMUM_STREAM_VERSION; ctx->maxProtocolVersion = MAXIMUM_STREAM_VERSION; } else { ctx->minProtocolVersion = SSL_Version_Undetermined; ctx->maxProtocolVersion = SSL_Version_Undetermined; } } else { SSLProtocolVersion version = SSLProtocolToProtocolVersion(protocol); if (enable) { if (version < MINIMUM_STREAM_VERSION || version > MAXIMUM_STREAM_VERSION) { return errSecParam; } if (version > ctx->maxProtocolVersion) { ctx->maxProtocolVersion = version; if (ctx->minProtocolVersion == SSL_Version_Undetermined) ctx->minProtocolVersion = version; } if (version < ctx->minProtocolVersion) { ctx->minProtocolVersion = version; } } else { if (version < SSL_Version_2_0 || version > MAXIMUM_STREAM_VERSION) { return errSecParam; } /* Disabling a protocol version now resets the minimum acceptable * version to the next higher version. This means it's no longer * possible to enable a discontiguous set of protocol versions. */ SSLProtocolVersion nextVersion; switch (version) { case SSL_Version_2_0: nextVersion = SSL_Version_3_0; break; case SSL_Version_3_0: nextVersion = TLS_Version_1_0; break; case TLS_Version_1_0: nextVersion = TLS_Version_1_1; break; case TLS_Version_1_1: nextVersion = TLS_Version_1_2; break; case TLS_Version_1_2: default: nextVersion = SSL_Version_Undetermined; break; } ctx->minProtocolVersion = max(ctx->minProtocolVersion, nextVersion); if (ctx->minProtocolVersion > ctx->maxProtocolVersion) { ctx->minProtocolVersion = SSL_Version_Undetermined; ctx->maxProtocolVersion = SSL_Version_Undetermined; } } } tls_handshake_set_min_protocol_version(ctx->hdsk, ctx->minProtocolVersion); tls_handshake_set_max_protocol_version(ctx->hdsk, ctx->maxProtocolVersion); return errSecSuccess; } OSStatus SSLGetProtocolVersionEnabled(SSLContextRef ctx, SSLProtocol protocol, Boolean *enable) /* RETURNED */ { if(ctx == NULL) { return errSecParam; } if(ctx->isDTLS) { /* Can't do this with a DTLS session */ return errSecBadReq; } switch(protocol) { case kSSLProtocol2: case kSSLProtocol3: case kTLSProtocol1: case kTLSProtocol11: case kTLSProtocol12: { SSLProtocolVersion version = SSLProtocolToProtocolVersion(protocol); *enable = (ctx->minProtocolVersion <= version && ctx->maxProtocolVersion >= version); break; } case kSSLProtocolAll: *enable = (ctx->minProtocolVersion <= MINIMUM_STREAM_VERSION && ctx->maxProtocolVersion >= MAXIMUM_STREAM_VERSION); break; default: return errSecParam; } return errSecSuccess; } /* deprecated */ OSStatus SSLSetProtocolVersion (SSLContextRef ctx, SSLProtocol version) { if(ctx == NULL) { return errSecParam; } if(sslIsSessionActive(ctx) || ctx->isDTLS) { /* Can't do this with an active session, nor with a DTLS session */ return errSecBadReq; } switch(version) { case kSSLProtocol3: /* this tells us to do our best, up to 3.0 */ ctx->minProtocolVersion = MINIMUM_STREAM_VERSION; ctx->maxProtocolVersion = SSL_Version_3_0; break; case kSSLProtocol3Only: ctx->minProtocolVersion = SSL_Version_3_0; ctx->maxProtocolVersion = SSL_Version_3_0; break; case kTLSProtocol1: /* this tells us to do our best, up to TLS, but allows 3.0 */ ctx->minProtocolVersion = MINIMUM_STREAM_VERSION; ctx->maxProtocolVersion = TLS_Version_1_0; break; case kTLSProtocol1Only: ctx->minProtocolVersion = TLS_Version_1_0; ctx->maxProtocolVersion = TLS_Version_1_0; break; case kTLSProtocol11: /* This tells us to do our best, up to TLS 1.1, currently also allows 3.0 or TLS 1.0 */ ctx->minProtocolVersion = MINIMUM_STREAM_VERSION; ctx->maxProtocolVersion = TLS_Version_1_1; break; case kTLSProtocol12: case kSSLProtocolAll: case kSSLProtocolUnknown: /* This tells us to do our best, up to TLS 1.2, currently also allows 3.0 or TLS 1.0 or TLS 1.1 */ ctx->minProtocolVersion = MINIMUM_STREAM_VERSION; ctx->maxProtocolVersion = MAXIMUM_STREAM_VERSION; break; default: return errSecParam; } tls_handshake_set_min_protocol_version(ctx->hdsk, ctx->minProtocolVersion); tls_handshake_set_max_protocol_version(ctx->hdsk, ctx->maxProtocolVersion); return errSecSuccess; } /* deprecated */ OSStatus SSLGetProtocolVersion (SSLContextRef ctx, SSLProtocol *protocol) /* RETURNED */ { if(ctx == NULL) { return errSecParam; } /* translate array of booleans to public value; not all combinations * are legal (i.e., meaningful) for this call */ if (ctx->maxProtocolVersion == MAXIMUM_STREAM_VERSION) { if(ctx->minProtocolVersion == MINIMUM_STREAM_VERSION) { /* traditional 'all enabled' */ *protocol = kSSLProtocolAll; return errSecSuccess; } } else if (ctx->maxProtocolVersion == TLS_Version_1_1) { if(ctx->minProtocolVersion == MINIMUM_STREAM_VERSION) { /* traditional 'all enabled' */ *protocol = kTLSProtocol11; return errSecSuccess; } } else if (ctx->maxProtocolVersion == TLS_Version_1_0) { if(ctx->minProtocolVersion == MINIMUM_STREAM_VERSION) { /* TLS1.1 and below enabled */ *protocol = kTLSProtocol1; return errSecSuccess; } else if(ctx->minProtocolVersion == TLS_Version_1_0) { *protocol = kTLSProtocol1Only; } } else if(ctx->maxProtocolVersion == SSL_Version_3_0) { if(ctx->minProtocolVersion == MINIMUM_STREAM_VERSION) { /* Could also return kSSLProtocol3Only since MINIMUM_STREAM_VERSION == SSL_Version_3_0. */ *protocol = kSSLProtocol3; return errSecSuccess; } } return errSecParam; } OSStatus SSLGetNegotiatedProtocolVersion (SSLContextRef ctx, SSLProtocol *protocol) /* RETURNED */ { if(ctx == NULL) { return errSecParam; } *protocol = SSLProtocolVersionToProtocol(ctx->negProtocolVersion); return errSecSuccess; } OSStatus SSLSetEnableCertVerify (SSLContextRef ctx, Boolean enableVerify) { if(ctx == NULL) { return errSecParam; } sslCertDebug("SSLSetEnableCertVerify %s", enableVerify ? "true" : "false"); if(sslIsSessionActive(ctx)) { /* can't do this with an active session */ return errSecBadReq; } ctx->enableCertVerify = enableVerify; return errSecSuccess; } OSStatus SSLGetEnableCertVerify (SSLContextRef ctx, Boolean *enableVerify) { if(ctx == NULL) { return errSecParam; } *enableVerify = ctx->enableCertVerify; return errSecSuccess; } OSStatus SSLSetAllowsExpiredCerts(SSLContextRef ctx, Boolean allowExpired) { if(ctx == NULL) { return errSecParam; } sslCertDebug("SSLSetAllowsExpiredCerts %s", allowExpired ? "true" : "false"); if(sslIsSessionActive(ctx)) { /* can't do this with an active session */ return errSecBadReq; } ctx->allowExpiredCerts = allowExpired; return errSecSuccess; } OSStatus SSLGetAllowsExpiredCerts (SSLContextRef ctx, Boolean *allowExpired) { if(ctx == NULL) { return errSecParam; } *allowExpired = ctx->allowExpiredCerts; return errSecSuccess; } OSStatus SSLSetAllowsExpiredRoots(SSLContextRef ctx, Boolean allowExpired) { if(ctx == NULL) { return errSecParam; } sslCertDebug("SSLSetAllowsExpiredRoots %s", allowExpired ? "true" : "false"); if(sslIsSessionActive(ctx)) { /* can't do this with an active session */ return errSecBadReq; } ctx->allowExpiredRoots = allowExpired; return errSecSuccess; } OSStatus SSLGetAllowsExpiredRoots (SSLContextRef ctx, Boolean *allowExpired) { if(ctx == NULL) { return errSecParam; } *allowExpired = ctx->allowExpiredRoots; return errSecSuccess; } OSStatus SSLSetAllowsAnyRoot( SSLContextRef ctx, Boolean anyRoot) { if(ctx == NULL) { return errSecParam; } sslCertDebug("SSLSetAllowsAnyRoot %s", anyRoot ? "true" : "false"); ctx->allowAnyRoot = anyRoot; return errSecSuccess; } OSStatus SSLGetAllowsAnyRoot( SSLContextRef ctx, Boolean *anyRoot) { if(ctx == NULL) { return errSecParam; } *anyRoot = ctx->allowAnyRoot; return errSecSuccess; } #if !TARGET_OS_IPHONE /* obtain the system roots sets for this app, policy SSL */ static OSStatus sslDefaultSystemRoots( SSLContextRef ctx, CFArrayRef *systemRoots) // created and RETURNED { const char *hostname; size_t len; tls_handshake_get_peer_hostname(ctx->hdsk, &hostname, &len); return SecTrustSettingsCopyQualifiedCerts(&CSSMOID_APPLE_TP_SSL, hostname, (uint32_t)len, (ctx->protocolSide == kSSLServerSide) ? /* server verifies, client encrypts */ CSSM_KEYUSE_VERIFY : CSSM_KEYUSE_ENCRYPT, systemRoots); } #endif /* OS X only */ OSStatus SSLSetTrustedRoots (SSLContextRef ctx, CFArrayRef trustedRoots, Boolean replaceExisting) { #ifdef USE_CDSA_CRYPTO if(ctx == NULL) { return errSecParam; } if(sslIsSessionActive(ctx)) { /* can't do this with an active session */ return errSecBadReq; } if(replaceExisting) { /* trivial case - retain the new, throw out the old. */ if (trustedRoots) CFRetain(trustedRoots); CFReleaseSafe(ctx->trustedCerts); ctx->trustedCerts = trustedRoots; return errSecSuccess; } /* adding new trusted roots - to either our existing set, or the system set */ CFArrayRef existingRoots = NULL; OSStatus ortn; if(ctx->trustedCerts != NULL) { /* we'll release these as we exit */ existingRoots = ctx->trustedCerts; } else { /* get system set for this app, policy SSL */ ortn = sslDefaultSystemRoots(ctx, &existingRoots); if(ortn) { CFReleaseSafe(existingRoots); return ortn; } } /* Create a new root array with caller's roots first */ CFMutableArrayRef newRoots = CFArrayCreateMutableCopy(NULL, 0, trustedRoots); CFRange existRange = { 0, CFArrayGetCount(existingRoots) }; CFArrayAppendArray(newRoots, existingRoots, existRange); CFRelease(existingRoots); ctx->trustedCerts = newRoots; return errSecSuccess; #else if (sslIsSessionActive(ctx)) { /* can't do this with an active session */ return errSecBadReq; } sslCertDebug("SSLSetTrustedRoot numCerts %d replaceExist %s", (int)CFArrayGetCount(trustedRoots), replaceExisting ? "true" : "false"); if (replaceExisting) { ctx->trustedCertsOnly = true; CFReleaseNull(ctx->trustedCerts); } if (ctx->trustedCerts) { CFIndex count = CFArrayGetCount(trustedRoots); CFRange range = { 0, count }; CFArrayAppendArray(ctx->trustedCerts, trustedRoots, range); } else { require(ctx->trustedCerts = CFArrayCreateMutableCopy(kCFAllocatorDefault, 0, trustedRoots), errOut); } return errSecSuccess; errOut: return errSecAllocate; #endif /* !USE_CDSA_CRYPTO */ } OSStatus SSLCopyTrustedRoots (SSLContextRef ctx, CFArrayRef *trustedRoots) /* RETURNED */ { if(ctx == NULL || trustedRoots == NULL) { return errSecParam; } if(ctx->trustedCerts != NULL) { *trustedRoots = ctx->trustedCerts; CFRetain(ctx->trustedCerts); return errSecSuccess; } #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)) /* use default system roots */ return sslDefaultSystemRoots(ctx, trustedRoots); #else *trustedRoots = NULL; return errSecSuccess; #endif } OSStatus SSLSetTrustedLeafCertificates (SSLContextRef ctx, CFArrayRef trustedCerts) { if(ctx == NULL) { return errSecParam; } if(sslIsSessionActive(ctx)) { /* can't do this with an active session */ return errSecBadReq; } if(ctx->trustedLeafCerts) { CFRelease(ctx->trustedLeafCerts); } ctx->trustedLeafCerts = trustedCerts; CFRetain(trustedCerts); return errSecSuccess; } OSStatus SSLCopyTrustedLeafCertificates (SSLContextRef ctx, CFArrayRef *trustedCerts) /* RETURNED */ { if(ctx == NULL) { return errSecParam; } if(ctx->trustedLeafCerts != NULL) { *trustedCerts = ctx->trustedLeafCerts; CFRetain(ctx->trustedCerts); return errSecSuccess; } *trustedCerts = NULL; return errSecSuccess; } OSStatus SSLSetClientSideAuthenticate (SSLContext *ctx, SSLAuthenticate auth) { if(ctx == NULL) { return errSecParam; } if(sslIsSessionActive(ctx)) { /* can't do this with an active session */ return errSecBadReq; } ctx->clientAuth = auth; switch(auth) { case kNeverAuthenticate: tls_handshake_set_client_auth(ctx->hdsk, false); break; case kAlwaysAuthenticate: case kTryAuthenticate: tls_handshake_set_client_auth(ctx->hdsk, true); break; } return errSecSuccess; } OSStatus SSLGetClientSideAuthenticate (SSLContext *ctx, SSLAuthenticate *auth) /* RETURNED */ { if(ctx == NULL || auth == NULL) { return errSecParam; } *auth = ctx->clientAuth; return errSecSuccess; } OSStatus SSLGetClientCertificateState (SSLContextRef ctx, SSLClientCertificateState *clientState) { if(ctx == NULL) { return errSecParam; } *clientState = ctx->clientCertState; return errSecSuccess; } 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 errSecParam; } /* can't do this with an active session */ if(sslIsSessionActive(ctx) && /* kSSLClientCertRequested implies client side */ (ctx->clientCertState != kSSLClientCertRequested)) { return errSecBadReq; } CFReleaseNull(ctx->localCertArray); /* changing the client cert invalidates negotiated auth type */ ctx->negAuthType = SSLClientAuthNone; if(certRefs == NULL) { return errSecSuccess; // we have cleared the cert, as requested } OSStatus ortn = parseIncomingCerts(ctx, certRefs, &ctx->localCert, &ctx->signingPrivKeyRef); if(ortn == errSecSuccess) { ctx->localCertArray = certRefs; CFRetain(certRefs); if(ctx->protocolSide==kSSLClientSide) SSLUpdateNegotiatedClientAuthType(ctx); tls_handshake_set_identity(ctx->hdsk, ctx->localCert, ctx->signingPrivKeyRef); } return ortn; } 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 errSecParam; } if(sslIsSessionActive(ctx)) { /* can't do this with an active session */ return errSecBadReq; } CFReleaseNull(ctx->encryptCertArray); OSStatus ortn = parseIncomingCerts(ctx, certRefs, &ctx->encryptCert, &ctx->encryptPrivKeyRef); if(ortn == errSecSuccess) { ctx->encryptCertArray = certRefs; CFRetain(certRefs); } return ortn; } OSStatus SSLGetCertificate(SSLContextRef ctx, CFArrayRef *certRefs) { if(ctx == NULL) { return errSecParam; } *certRefs = ctx->localCertArray; return errSecSuccess; } OSStatus SSLGetEncryptionCertificate(SSLContextRef ctx, CFArrayRef *certRefs) { if(ctx == NULL) { return errSecParam; } *certRefs = ctx->encryptCertArray; return errSecSuccess; } 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 errSecParam; } if(sslIsSessionActive(ctx) && /* kSSLClientCertRequested implies client side */ (ctx->clientCertState != kSSLClientCertRequested)) { return errSecBadReq; } SSLFreeBuffer(&ctx->peerID); serr = SSLAllocBuffer(&ctx->peerID, peerIDLen); if(serr) { return serr; } tls_handshake_set_resumption(ctx->hdsk, true); memmove(ctx->peerID.data, peerID, peerIDLen); return errSecSuccess; } OSStatus SSLGetPeerID (SSLContextRef ctx, const void **peerID, size_t *peerIDLen) { *peerID = ctx->peerID.data; // may be NULL *peerIDLen = ctx->peerID.length; return errSecSuccess; } OSStatus SSLGetNegotiatedCipher (SSLContextRef ctx, SSLCipherSuite *cipherSuite) { if(ctx == NULL) { return errSecParam; } if(!sslIsSessionActive(ctx)) { return errSecBadReq; } *cipherSuite = (SSLCipherSuite)tls_handshake_get_negotiated_cipherspec(ctx->hdsk); return errSecSuccess; } /* * Add an acceptable distinguished name (client authentication only). */ OSStatus SSLAddDistinguishedName( SSLContextRef ctx, const void *derDN, size_t derDNLen) { DNListElem *dn; OSStatus err; if(ctx == NULL) { return errSecParam; } if(sslIsSessionActive(ctx)) { return errSecBadReq; } dn = (DNListElem *)sslMalloc(sizeof(DNListElem)); if(dn == NULL) { return errSecAllocate; } if ((err = SSLAllocBuffer(&dn->derDN, derDNLen))) return err; memcpy(dn->derDN.data, derDN, derDNLen); dn->next = ctx->acceptableDNList; ctx->acceptableDNList = dn; tls_handshake_set_acceptable_dn_list(ctx->hdsk, dn); return errSecSuccess; } /* single-cert version of SSLSetCertificateAuthorities() */ static OSStatus sslAddCA(SSLContextRef ctx, SecCertificateRef cert) { OSStatus ortn = errSecParam; /* Get subject from certificate. */ #if TARGET_OS_IPHONE CFDataRef subjectName = NULL; subjectName = SecCertificateCopySubjectSequence(cert); require(subjectName, errOut); #else CSSM_DATA_PTR subjectName = NULL; ortn = SecCertificateCopyFirstFieldValue(cert, &CSSMOID_X509V1SubjectNameStd, &subjectName); require_noerr(ortn, errOut); #endif /* add to acceptableCAs as cert, creating array if necessary */ if(ctx->acceptableCAs == NULL) { require(ctx->acceptableCAs = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks), errOut); if(ctx->acceptableCAs == NULL) { return errSecAllocate; } } CFArrayAppendValue(ctx->acceptableCAs, cert); /* then add this cert's subject name to acceptableDNList */ #if TARGET_OS_IPHONE ortn = SSLAddDistinguishedName(ctx, CFDataGetBytePtr(subjectName), CFDataGetLength(subjectName)); #else ortn = SSLAddDistinguishedName(ctx, subjectName->Data, subjectName->Length); #endif errOut: #if TARGET_OS_IPHONE CFReleaseSafe(subjectName); #endif return ortn; } /* * Add a SecCertificateRef, or a CFArray of them, to a server's list * of acceptable Certificate Authorities (CAs) to present to the client * when client authentication is performed. */ OSStatus SSLSetCertificateAuthorities(SSLContextRef ctx, CFTypeRef certificateOrArray, Boolean replaceExisting) { CFTypeID itemType; OSStatus ortn = errSecSuccess; if((ctx == NULL) || sslIsSessionActive(ctx) || (ctx->protocolSide != kSSLServerSide)) { return errSecParam; } if(replaceExisting) { sslFreeDnList(ctx); if(ctx->acceptableCAs) { CFRelease(ctx->acceptableCAs); ctx->acceptableCAs = NULL; } } /* else appending */ itemType = CFGetTypeID(certificateOrArray); if(itemType == SecCertificateGetTypeID()) { /* one cert */ ortn = sslAddCA(ctx, (SecCertificateRef)certificateOrArray); } else if(itemType == CFArrayGetTypeID()) { CFArrayRef cfa = (CFArrayRef)certificateOrArray; CFIndex numCerts = CFArrayGetCount(cfa); CFIndex dex; /* array of certs */ for(dex=0; dexacceptableCAs == NULL) { *certificates = NULL; return errSecSuccess; } *certificates = ctx->acceptableCAs; CFRetain(ctx->acceptableCAs); return errSecSuccess; } /* * Obtain the list of acceptable distinguished names as provided by * a server (if the SSLCotextRef is configured as a client), or as * specified by SSLSetCertificateAuthorities() (if the SSLContextRef * is configured as a server). */ OSStatus SSLCopyDistinguishedNames (SSLContextRef ctx, CFArrayRef *names) { CFMutableArrayRef outArray = NULL; const DNListElem *dn; if((ctx == NULL) || (names == NULL)) { return errSecParam; } if(ctx->protocolSide==kSSLServerSide) { dn = ctx->acceptableDNList; } else { dn = tls_handshake_get_peer_acceptable_dn_list(ctx->hdsk); // ctx->acceptableDNList; } if(dn == NULL) { *names = NULL; return errSecSuccess; } outArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); while (dn) { CFDataRef cfDn = CFDataCreate(NULL, dn->derDN.data, dn->derDN.length); CFArrayAppendValue(outArray, cfDn); CFRelease(cfDn); dn = dn->next; } *names = outArray; return errSecSuccess; } /* * Request peer certificates. Valid anytime, subsequent to * a handshake attempt. * Common code for SSLGetPeerCertificates() and SSLCopyPeerCertificates(). * TODO: the 'legacy' argument is not used anymore. */ static OSStatus sslCopyPeerCertificates (SSLContextRef ctx, CFArrayRef *certs, Boolean legacy) { if(ctx == NULL) { return errSecParam; } if (!ctx->peerCert) { *certs = NULL; return errSecBadReq; } CFArrayRef ca = CFArrayCreateCopy(kCFAllocatorDefault, ctx->peerCert); *certs = ca; if (ca == NULL) { return errSecAllocate; } if (legacy) { CFIndex ix, count = CFArrayGetCount(ca); for (ix = 0; ix < count; ++ix) { CFRetain(CFArrayGetValueAtIndex(ca, ix)); } } return errSecSuccess; } OSStatus SSLCopyPeerCertificates (SSLContextRef ctx, CFArrayRef *certs) { return sslCopyPeerCertificates(ctx, certs, false); } #if !TARGET_OS_IPHONE // Permanently removing from iOS, keep for OSX (deprecated), removed from headers. // Mailsmith Crashes While Getting New Mail Under Mavericks Developer Preview OSStatus SSLGetPeerCertificates (SSLContextRef ctx, CFArrayRef *certs); OSStatus SSLGetPeerCertificates (SSLContextRef ctx, CFArrayRef *certs) { return sslCopyPeerCertificates(ctx, certs, true); } #endif /* * Specify Diffie-Hellman parameters. Optional; if we are configured to allow * for D-H ciphers and a D-H cipher is negotiated, and this function has not * been called, a set of process-wide parameters will be calculated. However * that can take a long time (30 seconds). */ OSStatus SSLSetDiffieHellmanParams( SSLContextRef ctx, const void *dhParams, size_t dhParamsLen) { #if APPLE_DH if(ctx == NULL) { return errSecParam; } if(sslIsSessionActive(ctx)) { return errSecBadReq; } SSLFreeBuffer(&ctx->dhParamsEncoded); OSStatus ortn; ortn = SSLCopyBufferFromData(dhParams, dhParamsLen, &ctx->dhParamsEncoded); return ortn; #endif /* APPLE_DH */ } /* * Return parameter block specified in SSLSetDiffieHellmanParams. * Returned data is not copied and belongs to the SSLContextRef. */ OSStatus SSLGetDiffieHellmanParams( SSLContextRef ctx, const void **dhParams, size_t *dhParamsLen) { #if APPLE_DH if(ctx == NULL) { return errSecParam; } *dhParams = ctx->dhParamsEncoded.data; *dhParamsLen = ctx->dhParamsEncoded.length; return errSecSuccess; #else return errSecUnimplemented; #endif /* APPLE_DH */ } OSStatus SSLSetRsaBlinding( SSLContextRef ctx, Boolean blinding) { if(ctx == NULL) { return errSecParam; } ctx->rsaBlindingEnable = blinding; return errSecSuccess; } OSStatus SSLGetRsaBlinding( SSLContextRef ctx, Boolean *blinding) { if(ctx == NULL) { return errSecParam; } *blinding = ctx->rsaBlindingEnable; return errSecSuccess; } OSStatus SSLCopyPeerTrust( SSLContextRef ctx, SecTrustRef *trust) /* RETURNED */ { OSStatus status = errSecSuccess; if (ctx == NULL || trust == NULL) return errSecParam; /* Create a SecTrustRef if this was a resumed session and we didn't have one yet. */ if (!ctx->peerCert) { ctx->peerCert = tls_get_peer_certs(tls_handshake_get_peer_certificates(ctx->hdsk)); } if (!ctx->peerSecTrust && ctx->peerCert) { status = sslCreateSecTrust(ctx, ctx->peerCert, true, &ctx->peerSecTrust); } *trust = ctx->peerSecTrust; if (ctx->peerSecTrust) CFRetain(ctx->peerSecTrust); return status; } OSStatus SSLGetPeerSecTrust( SSLContextRef ctx, SecTrustRef *trust) /* RETURNED */ { OSStatus status = errSecSuccess; if (ctx == NULL || trust == NULL) return errSecParam; /* Create a SecTrustRef if this was a resumed session and we didn't have one yet. */ if (!ctx->peerSecTrust && ctx->peerCert) { status = sslCreateSecTrust(ctx, ctx->peerCert, true, &ctx->peerSecTrust); } *trust = ctx->peerSecTrust; return status; } 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 errSecParam; } return tls_handshake_internal_master_secret(ctx->hdsk, secret, secretSize); } OSStatus SSLInternalServerRandom( SSLContextRef ctx, void *randBuf, // mallocd by caller, SSL_CLIENT_SRVR_RAND_SIZE size_t *randSize) // in/out { if((ctx == NULL) || (randBuf == NULL) || (randSize == NULL)) { return errSecParam; } return tls_handshake_internal_server_random(ctx->hdsk, randBuf, randSize); } OSStatus SSLInternalClientRandom( SSLContextRef ctx, void *randBuf, // mallocd by caller, SSL_CLIENT_SRVR_RAND_SIZE size_t *randSize) // in/out { if((ctx == NULL) || (randBuf == NULL) || (randSize == NULL)) { return errSecParam; } return tls_handshake_internal_client_random(ctx->hdsk, randBuf, randSize); } /* This is used by EAP 802.1x */ OSStatus SSLGetCipherSizes( SSLContextRef ctx, size_t *digestSize, size_t *symmetricKeySize, size_t *ivSize) { if((ctx == NULL) || (digestSize == NULL) || (symmetricKeySize == NULL) || (ivSize == NULL)) { return errSecParam; } SSLCipherSuite cipher=tls_handshake_get_negotiated_cipherspec(ctx->hdsk); *digestSize = sslCipherSuiteGetMacSize(cipher); *symmetricKeySize = sslCipherSuiteGetSymmetricCipherKeySize(cipher); *ivSize = sslCipherSuiteGetSymmetricCipherBlockIvSize(cipher); return errSecSuccess; } OSStatus SSLGetResumableSessionInfo( SSLContextRef ctx, Boolean *sessionWasResumed, // RETURNED void *sessionID, // RETURNED, mallocd by caller size_t *sessionIDLength) // IN/OUT { if((ctx == NULL) || (sessionWasResumed == NULL) || (sessionID == NULL) || (sessionIDLength == NULL) || (*sessionIDLength < MAX_SESSION_ID_LENGTH)) { return errSecParam; } SSLBuffer localSessionID; bool sessionMatch = tls_handshake_get_session_match(ctx->hdsk, &localSessionID); if(sessionMatch) { *sessionWasResumed = true; if(localSessionID.length > *sessionIDLength) { /* really should never happen - means ID > 32 */ return errSecParam; } if(localSessionID.length) { /* * Note PAC-based session resumption can result in sessionMatch * with no sessionID */ memmove(sessionID, localSessionID.data, localSessionID.length); } *sessionIDLength = localSessionID.length; } else { *sessionWasResumed = false; *sessionIDLength = 0; } return errSecSuccess; } /* * Get/set enable of anonymous ciphers. Default is enabled. */ OSStatus SSLSetAllowAnonymousCiphers( SSLContextRef ctx, Boolean enable) { if(ctx == NULL) { return errSecParam; } if(sslIsSessionActive(ctx)) { return errSecBadReq; } if(ctx->validCipherSuites != NULL) { /* SSLSetEnabledCiphers() has already been called */ return errSecBadReq; } ctx->anonCipherEnable = enable; return errSecSuccess; } OSStatus SSLGetAllowAnonymousCiphers( SSLContextRef ctx, Boolean *enable) { if((ctx == NULL) || (enable == NULL)) { return errSecParam; } if(sslIsSessionActive(ctx)) { return errSecBadReq; } *enable = ctx->anonCipherEnable; return errSecSuccess; } /* * Override the default session cache timeout for a cache entry created for * the current session. */ OSStatus SSLSetSessionCacheTimeout( SSLContextRef ctx, uint32_t timeoutInSeconds) { if(ctx == NULL) { return errSecParam; } ctx->sessionCacheTimeout = timeoutInSeconds; return errSecSuccess; } static void tls_handshake_master_secret_function(const void *arg, /* opaque to coreTLS; app-specific */ void *secret, /* mallocd by caller, SSL_MASTER_SECRET_SIZE */ size_t *secretLength) { SSLContextRef ctx = (SSLContextRef) arg; ctx->masterSecretCallback(ctx, ctx->masterSecretArg, secret, secretLength); } /* * Register a callback for obtaining the master_secret when performing * PAC-based session resumption. */ OSStatus SSLInternalSetMasterSecretFunction( SSLContextRef ctx, SSLInternalMasterSecretFunction mFunc, const void *arg) /* opaque to SecureTransport; app-specific */ { if(ctx == NULL) { return errSecParam; } ctx->masterSecretArg = arg; ctx->masterSecretCallback = mFunc; return tls_handshake_internal_set_master_secret_function(ctx->hdsk, &tls_handshake_master_secret_function, ctx); } /* * Provide an opaque SessionTicket for use in PAC-based session * resumption. Client side only. The provided ticket is sent in * the ClientHello message as a SessionTicket extension. * * We won't reject this on the server side, but server-side support * for PAC-based session resumption is currently enabled for * Development builds only. To fully support this for server side, * besides the rudimentary support that's here for Development builds, * we'd need a getter for the session ticket, so the app code can * access the SessionTicket when its SSLInternalMasterSecretFunction * callback is called. */ OSStatus SSLInternalSetSessionTicket( SSLContextRef ctx, const void *ticket, size_t ticketLength) { if(ctx == NULL) { return errSecParam; } if(sslIsSessionActive(ctx)) { /* can't do this with an active session */ return errSecBadReq; } return tls_handshake_internal_set_session_ticket(ctx->hdsk, ticket, ticketLength); } /* * ECDSA curve accessors. */ /* * Obtain the SSL_ECDSA_NamedCurve negotiated during a handshake. * Returns errSecParam if no ECDH-related ciphersuite was negotiated. */ OSStatus SSLGetNegotiatedCurve( SSLContextRef ctx, SSL_ECDSA_NamedCurve *namedCurve) /* RETURNED */ { if((ctx == NULL) || (namedCurve == NULL)) { return errSecParam; } unsigned int curve = tls_handshake_get_negotiated_curve(ctx->hdsk); if(curve == SSL_Curve_None) { return errSecParam; } *namedCurve = curve; return errSecSuccess; } /* * Obtain the number of currently enabled SSL_ECDSA_NamedCurves. */ OSStatus SSLGetNumberOfECDSACurves( SSLContextRef ctx, unsigned *numCurves) /* RETURNED */ { if((ctx == NULL) || (numCurves == NULL)) { return errSecParam; } *numCurves = ctx->ecdhNumCurves; return errSecSuccess; } /* * Obtain the ordered list of currently enabled SSL_ECDSA_NamedCurves. */ OSStatus SSLGetECDSACurves( SSLContextRef ctx, SSL_ECDSA_NamedCurve *namedCurves, /* RETURNED */ unsigned *numCurves) /* IN/OUT */ { if((ctx == NULL) || (namedCurves == NULL) || (numCurves == NULL)) { return errSecParam; } if(*numCurves < ctx->ecdhNumCurves) { return errSecParam; } memmove(namedCurves, ctx->ecdhCurves, (ctx->ecdhNumCurves * sizeof(SSL_ECDSA_NamedCurve))); *numCurves = ctx->ecdhNumCurves; return errSecSuccess; } /* * Specify ordered list of allowable named curves. */ OSStatus SSLSetECDSACurves( SSLContextRef ctx, const SSL_ECDSA_NamedCurve *namedCurves, unsigned numCurves) { if((ctx == NULL) || (namedCurves == NULL) || (numCurves == 0)) { return errSecParam; } if(sslIsSessionActive(ctx)) { /* can't do this with an active session */ return errSecBadReq; } size_t size = numCurves * sizeof(uint16_t); ctx->ecdhCurves = (uint16_t *)sslMalloc(size); if(ctx->ecdhCurves == NULL) { ctx->ecdhNumCurves = 0; return errSecAllocate; } for (unsigned i=0; iecdhCurves[i] = namedCurves[i]; } ctx->ecdhNumCurves = numCurves; tls_handshake_set_curves(ctx->hdsk, ctx->ecdhCurves, ctx->ecdhNumCurves); return errSecSuccess; } /* * Obtain the number of client authentication mechanisms specified by * the server in its Certificate Request message. * Returns errSecParam if server hasn't sent a Certificate Request message * (i.e., client certificate state is kSSLClientCertNone). */ OSStatus SSLGetNumberOfClientAuthTypes( SSLContextRef ctx, unsigned *numTypes) { if((ctx == NULL) || (ctx->clientCertState == kSSLClientCertNone)) { return errSecParam; } *numTypes = ctx->numAuthTypes; return errSecSuccess; } /* * Obtain the client authentication mechanisms specified by * the server in its Certificate Request message. * Caller allocates returned array and specifies its size (in * SSLClientAuthenticationTypes) in *numType on entry; *numTypes * is the actual size of the returned array on successful return. */ OSStatus SSLGetClientAuthTypes( SSLContextRef ctx, SSLClientAuthenticationType *authTypes, /* RETURNED */ unsigned *numTypes) /* IN/OUT */ { if((ctx == NULL) || (ctx->clientCertState == kSSLClientCertNone)) { return errSecParam; } memmove(authTypes, ctx->clientAuthTypes, ctx->numAuthTypes * sizeof(SSLClientAuthenticationType)); *numTypes = ctx->numAuthTypes; return errSecSuccess; } /* * Obtain the SSLClientAuthenticationType actually performed. * Only valid if client certificate state is kSSLClientCertSent * or kSSLClientCertRejected; returns errSecParam otherwise. */ OSStatus SSLGetNegotiatedClientAuthType( SSLContextRef ctx, SSLClientAuthenticationType *authType) /* RETURNED */ { if(ctx == NULL) { return errSecParam; } *authType = ctx->negAuthType; return errSecSuccess; } /* * Update the negotiated client authentication type. * This function may be called at any time; however, note that * the negotiated authentication type will be SSLClientAuthNone * until both of the following have taken place (in either order): * - a CertificateRequest message from the server has been processed * - a client certificate has been specified * As such, this function (only) needs to be called from (both) * SSLProcessCertificateRequest and SSLSetCertificate. */ OSStatus SSLUpdateNegotiatedClientAuthType( SSLContextRef ctx) { if(ctx == NULL) { return errSecParam; } assert(ctx->protocolSide==kSSLClientSide); /* * See if we have a signing cert that matches one of the * allowed auth types. The x509Requested flag indicates "we * have a cert that we think the server will accept". */ ctx->x509Requested = 0; ctx->negAuthType = SSLClientAuthNone; if(ctx->signingPrivKeyRef != NULL) { CFIndex ourKeyAlg = sslPrivKeyGetAlgorithmID((SecKeyRef)tls_private_key_get_context(ctx->signingPrivKeyRef)); assert(ourKeyAlg==kSecRSAAlgorithmID); /* We don't suport anything else */ unsigned i; for(i=0; inumAuthTypes; i++) { switch(ctx->clientAuthTypes[i]) { case SSLClientAuth_RSASign: if(ourKeyAlg == kSecRSAAlgorithmID) { ctx->x509Requested = 1; ctx->negAuthType = SSLClientAuth_RSASign; } break; #if 0 // The code below is hopelessly broken: ctx->ourSignerAlg is never set #if SSL_ENABLE_ECDSA_SIGN_AUTH case SSLClientAuth_ECDSASign: #endif #if SSL_ENABLE_ECDSA_FIXED_ECDH_AUTH case SSLClientAuth_ECDSAFixedECDH: #endif if((ourKeyAlg == kSecECDSAAlgorithmID) && (ctx->ourSignerAlg == kSecECDSAAlgorithmID)) { ctx->x509Requested = 1; ctx->negAuthType = ctx->clientAuthTypes[i]; } break; #if SSL_ENABLE_RSA_FIXED_ECDH_AUTH case SSLClientAuth_RSAFixedECDH: /* Odd case, we differ from our signer */ if((ourKeyAlg == kSecECDSAAlgorithmID) && (ctx->ourSignerAlg == kSecRSAAlgorithmID)) { ctx->x509Requested = 1; ctx->negAuthType = SSLClientAuth_RSAFixedECDH; } break; #endif #endif default: /* None others supported */ break; } if(ctx->x509Requested) { sslLogNegotiateDebug("===CHOOSING authType %d", (int)ctx->negAuthType); break; } } /* parsing authTypes */ } /* we have a signing key */ tls_handshake_set_client_auth_type(ctx->hdsk, ctx->negAuthType); return errSecSuccess; } OSStatus SSLGetNumberOfSignatureAlgorithms( SSLContextRef ctx, unsigned *numSigAlgs) { if(ctx == NULL){ return errSecParam; } tls_handshake_get_peer_signature_algorithms(ctx->hdsk, numSigAlgs); return errSecSuccess; } _Static_assert(sizeof(SSLSignatureAndHashAlgorithm)==sizeof(tls_signature_and_hash_algorithm), "SSLSignatureAndHashAlgorithm and tls_signature_and_hash_algorithm do not match"); OSStatus SSLGetSignatureAlgorithms( SSLContextRef ctx, SSLSignatureAndHashAlgorithm *sigAlgs, /* RETURNED */ unsigned *numSigAlgs) /* IN/OUT */ { if(ctx == NULL) { return errSecParam; } unsigned numPeerSigAlgs; const tls_signature_and_hash_algorithm *peerAlgs = tls_handshake_get_peer_signature_algorithms(ctx->hdsk, &numPeerSigAlgs); memmove(sigAlgs, peerAlgs, numPeerSigAlgs * sizeof(SSLSignatureAndHashAlgorithm)); *numSigAlgs = numPeerSigAlgs; return errSecSuccess; } /* PSK SPIs */ OSStatus SSLSetPSKSharedSecret(SSLContextRef ctx, const void *secret, size_t secretLen) { if(ctx == NULL) return errSecParam; if(ctx->pskSharedSecret.data) SSLFreeBuffer(&ctx->pskSharedSecret); if(SSLCopyBufferFromData(secret, secretLen, &ctx->pskSharedSecret)) return errSecAllocate; tls_handshake_set_psk_secret(ctx->hdsk, &ctx->pskSharedSecret); return errSecSuccess; } OSStatus SSLSetPSKIdentity(SSLContextRef ctx, const void *pskIdentity, size_t pskIdentityLen) { if((ctx == NULL) || (pskIdentity == NULL) || (pskIdentityLen == 0)) return errSecParam; if(ctx->pskIdentity.data) SSLFreeBuffer(&ctx->pskIdentity); if(SSLCopyBufferFromData(pskIdentity, pskIdentityLen, &ctx->pskIdentity)) return errSecAllocate; tls_handshake_set_psk_identity(ctx->hdsk, &ctx->pskIdentity); return errSecSuccess; } OSStatus SSLGetPSKIdentity(SSLContextRef ctx, const void **pskIdentity, size_t *pskIdentityLen) { if((ctx == NULL) || (pskIdentity == NULL) || (pskIdentityLen == NULL)) return errSecParam; *pskIdentity=ctx->pskIdentity.data; *pskIdentityLen=ctx->pskIdentity.length; return errSecSuccess; } OSStatus SSLInternal_PRF( SSLContext *ctx, const void *vsecret, size_t secretLen, const void *label, // optional, NULL implies that seed contains // the label size_t labelLen, const void *seed, size_t seedLen, void *vout, // mallocd by caller, length >= outLen size_t outLen) { return tls_handshake_internal_prf(ctx->hdsk, vsecret, secretLen, label, labelLen, seed, seedLen, vout, outLen); }