X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/5dd5f9ec28f304ca377c42fd7f711d6cf12b90e1..5c19dc3ae3bd8e40a9c028b0deddd50ff337692c:/OSX/libsecurity_ssl/lib/sslTransport.c diff --git a/OSX/libsecurity_ssl/lib/sslTransport.c b/OSX/libsecurity_ssl/lib/sslTransport.c new file mode 100644 index 00000000..212e8745 --- /dev/null +++ b/OSX/libsecurity_ssl/lib/sslTransport.c @@ -0,0 +1,538 @@ +/* + * 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@ + */ + +/* + * sslTransport.c - SSL transport layer + */ + +#include "ssl.h" +#include "sslMemory.h" +#include "sslContext.h" +#include "sslRecord.h" +#include "sslDebug.h" +#include "sslCipherSpecs.h" +#include "sslUtils.h" + +#include +#include + +#include + +#ifndef NDEBUG +static inline void sslIoTrace( + SSLContext *ctx, + const char *op, + size_t req, + size_t moved, + OSStatus stat) +{ + sslLogRecordIo("[%p] ===%s: req %4lu moved %4lu status %d", + ctx, op, req, moved, (int)stat); +} +#else +#define sslIoTrace(ctx, op, req, moved, stat) +#endif /* NDEBUG */ + +extern int kSplitDefaultValue; + +static OSStatus SSLProcessProtocolMessage(SSLRecord *rec, SSLContext *ctx); +static OSStatus SSLHandshakeProceed(SSLContext *ctx); +//static OSStatus SSLInitConnection(SSLContext *ctx); + +OSStatus +SSLWrite( + SSLContext *ctx, + const void * data, + size_t dataLength, + size_t *bytesWritten) /* RETURNED */ +{ + OSStatus err; + SSLRecord rec; + size_t dataLen, processed; + + sslLogRecordIo("[%p] SSLWrite top", ctx); + if((ctx == NULL) || (bytesWritten == NULL)) { + return errSecParam; + } + dataLen = dataLength; + processed = 0; /* Initialize in case we return with errSSLWouldBlock */ + *bytesWritten = 0; + + switch(ctx->state) { + case SSL_HdskStateGracefulClose: + err = errSSLClosedGraceful; + goto abort; + case SSL_HdskStateErrorClose: + err = errSSLClosedAbort; + goto abort; + case SSL_HdskStateReady: + break; + case SSL_HdskStateUninit: + /* not ready for I/O, and handshake not in progress */ + sslIoTrace(ctx, "SSLWrite(1)", dataLength, 0, errSecBadReq); + return errSecBadReq; + default: + /* handshake in progress or done. Will call SSLHandshakeProceed below if necessary */ + break; + } + + /* First, we have to wait until the session is ready to send data, + so the encryption keys and such have been established. */ + err = errSecSuccess; + while (!(ctx->writeCipher_ready)) + { if ((err = SSLHandshakeProceed(ctx)) != 0) + goto exit; + } + + /* Attempt to empty the write queue before queueing more data */ + if ((err = SSLServiceWriteQueue(ctx)) != 0) + goto abort; + + processed = 0; + + /* Skip empty writes, fragmentation is done at the coreTLS layer */ + if(dataLen) { + rec.contentType = SSL_RecordTypeAppData; + rec.protocolVersion = ctx->negProtocolVersion; + rec.contents.data = ((uint8_t *)data) + processed; + rec.contents.length = dataLen; + if ((err = SSLWriteRecord(rec, ctx)) != 0) + goto exit; + processed += rec.contents.length; + } + + /* All the data has been advanced to the write queue */ + *bytesWritten = processed; + if ((err = SSLServiceWriteQueue(ctx)) == 0) { + err = errSecSuccess; + } +exit: + switch(err) { + case errSecSuccess: + case errSSLWouldBlock: + case errSSLUnexpectedRecord: + case errSSLServerAuthCompleted: /* == errSSLClientAuthCompleted */ + case errSSLClientCertRequested: + case errSSLClosedGraceful: + break; + default: + sslErrorLog("SSLWrite: going to state errorClose due to err %d\n", + (int)err); + SSLChangeHdskState(ctx, SSL_HdskStateErrorClose); + break; + } +abort: + sslIoTrace(ctx, "SSLWrite(2)", dataLength, *bytesWritten, err); + return err; +} + +OSStatus +SSLRead ( + SSLContext *ctx, + void * data, + size_t dataLength, + size_t *processed) /* RETURNED */ +{ + OSStatus err; + uint8_t *charPtr; + size_t bufSize, remaining, count; + SSLRecord rec; + + sslLogRecordIo("[%p] SSLRead top (dataLength=%ld)", ctx, dataLength); + if((ctx == NULL) || (data == NULL) || (processed == NULL)) { + return errSecParam; + } + bufSize = dataLength; + *processed = 0; /* Initialize in case we return with errSSLWouldBlock */ + +readRetry: + /* first handle cases in which we know we're finished */ + switch(ctx->state) { + case SSL_HdskStateGracefulClose: + err = errSSLClosedGraceful; + goto abort; + case SSL_HdskStateErrorClose: + err = errSSLClosedAbort; + goto abort; + case SSL_HdskStateNoNotifyClose: + err = errSSLClosedNoNotify; + goto abort; + default: + break; + } + + /* First, we have to wait until the session is ready to receive data, + so the encryption keys and such have been established. */ + err = errSecSuccess; + while (ctx->readCipher_ready == 0) { + if ((err = SSLHandshakeProceed(ctx)) != 0) { + goto exit; + } + } + + /* Attempt to service the write queue */ + if ((err = SSLServiceWriteQueue(ctx)) != 0) { + if (err != errSSLWouldBlock) { + goto exit; + } + err = errSecSuccess; /* Write blocking shouldn't stop attempts to read */ + } + + remaining = bufSize; + charPtr = (uint8_t *)data; + if (ctx->receivedDataBuffer.data) + { count = ctx->receivedDataBuffer.length - ctx->receivedDataPos; + if (count > bufSize) + count = bufSize; + memcpy(data, ctx->receivedDataBuffer.data + ctx->receivedDataPos, count); + remaining -= count; + charPtr += count; + *processed += count; + ctx->receivedDataPos += count; + } + + assert(ctx->receivedDataPos <= ctx->receivedDataBuffer.length); + assert(*processed + remaining == bufSize); + assert(charPtr == ((uint8_t *)data) + *processed); + + if (ctx->receivedDataBuffer.data != 0 && + ctx->receivedDataPos >= ctx->receivedDataBuffer.length) + { SSLFreeBuffer(&ctx->receivedDataBuffer); + ctx->receivedDataBuffer.data = 0; + ctx->receivedDataPos = 0; + } + + /* + * This while statement causes a hang when using nonblocking low-level I/O! + while (remaining > 0 && ctx->state != SSL_HdskStateGracefulClose) + ..what we really have to do is just return as soon as we read one + record. A performance hit in the nonblocking case, but that is + the only way this code can work in both modes... + */ + if (remaining > 0 && ctx->state != SSL_HdskStateGracefulClose) + { assert(ctx->receivedDataBuffer.data == 0); + if ((err = SSLReadRecord(&rec, ctx)) != 0) { + goto exit; + } + if (rec.contentType == SSL_RecordTypeAppData || + rec.contentType == SSL_RecordTypeV2_0) + { if (rec.contents.length <= remaining) + { memcpy(charPtr, rec.contents.data, rec.contents.length); + remaining -= rec.contents.length; + charPtr += rec.contents.length; + *processed += rec.contents.length; + { + if ((err = SSLFreeRecord(rec, ctx))) { + goto exit; + } + } + } + else + { memcpy(charPtr, rec.contents.data, remaining); + charPtr += remaining; + *processed += remaining; + ctx->receivedDataBuffer = rec.contents; + ctx->receivedDataPos = remaining; + remaining = 0; + } + } + else { + if ((err = SSLProcessProtocolMessage(&rec, ctx)) != 0) { + /* This may not make much sense, but this is required so that we + process the write queue. This replicate exactly the behavior + before the coreTLS adoption */ + if(err == errSSLClosedGraceful) { + err = SSLClose(ctx); + } else { + goto exit; + } + } + if ((err = SSLFreeRecord(rec, ctx))) { + goto exit; + } + } + } + + err = errSecSuccess; + +exit: + /* test for renegotiate: loop until something useful happens */ + if(((err == errSecSuccess) && (*processed == 0) && dataLength) || (err == errSSLUnexpectedRecord)) { + sslLogNegotiateDebug("SSLRead recursion"); + goto readRetry; + } + /* shut down on serious errors */ + switch(err) { + case errSecSuccess: + case errSSLWouldBlock: + case errSSLUnexpectedRecord: + case errSSLServerAuthCompleted: /* == errSSLClientAuthCompleted */ + case errSSLClientCertRequested: + case errSSLClosedGraceful: + case errSSLClosedNoNotify: + break; + default: + sslErrorLog("SSLRead: going to state errorClose due to err %d\n", + (int)err); + SSLChangeHdskState(ctx, SSL_HdskStateErrorClose); + break; + } +abort: + sslIoTrace(ctx, "SSLRead returns", dataLength, *processed, err); + return err; +} + +#if SSL_DEBUG +#include "sslCrypto.h" +#endif + +OSStatus +SSLHandshake(SSLContext *ctx) +{ + OSStatus err; + + if(ctx == NULL) { + return errSecParam; + } + if (ctx->state == SSL_HdskStateGracefulClose) + return errSSLClosedGraceful; + if (ctx->state == SSL_HdskStateErrorClose) + return errSSLClosedAbort; + + if(ctx->validCipherSuites == NULL) { + /* build list of legal cipherSpecs */ + err = sslBuildCipherSuiteArray(ctx); + if(err) { + return err; + } + } + + err = errSecSuccess; + + if(ctx->isDTLS && ctx->timeout_deadline) { + CFAbsoluteTime current = CFAbsoluteTimeGetCurrent(); + + if (ctx->timeout_deadlinehdsk); + if(err) { + return err; + } + } + } + + while (ctx->readCipher_ready == 0 || ctx->writeCipher_ready == 0) + { + err = SSLHandshakeProceed(ctx); + if((err != 0) && (err != errSSLUnexpectedRecord)) + return err; + } + + /* one more flush at completion of successful handshake */ + if ((err = SSLServiceWriteQueue(ctx)) != 0) { + return err; + } + + return errSecSuccess; +} + +#if (TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR) + +#include + +typedef void (*type_ADClientAddValueForScalarKey)(CFStringRef key, int64_t value); +static type_ADClientAddValueForScalarKey gADClientAddValueForScalarKey = NULL; +static dispatch_once_t gADFunctionPointersSet = 0; +static CFBundleRef gAggdBundleRef = NULL; + +static bool InitializeADFunctionPointers() +{ + if (gADClientAddValueForScalarKey) + { + return true; + } + + dispatch_once(&gADFunctionPointersSet, + ^{ + CFStringRef path_to_aggd_framework = CFSTR("/System/Library/PrivateFrameworks/AggregateDictionary.framework"); + + CFURLRef aggd_url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, path_to_aggd_framework, kCFURLPOSIXPathStyle, true); + + if (NULL != aggd_url) + { + gAggdBundleRef = CFBundleCreate(kCFAllocatorDefault, aggd_url); + if (NULL != gAggdBundleRef) + { + gADClientAddValueForScalarKey = (type_ADClientAddValueForScalarKey) + CFBundleGetFunctionPointerForName(gAggdBundleRef, CFSTR("ADClientAddValueForScalarKey")); + } + CFRelease(aggd_url); + } + }); + + return (gADClientAddValueForScalarKey!=NULL); +} + +static void ad_log_SecureTransport_early_fail(long signature) +{ + if (InitializeADFunctionPointers()) { + + CFStringRef key = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("com.apple.SecureTransport.early_fail.%ld"), signature); + + if(key) + gADClientAddValueForScalarKey(key, 1); + + CFRelease(key); + } +} + +#endif + + +#if (!TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR) + +#include + +static void mt_log_SecureTransport_early_fail(long signature) +{ + char signature_string[16]; + + snprintf(signature_string, sizeof(signature_string), "%ld", signature); + + msgtracer_log_with_keys("com.apple.SecureTransport.early_fail", ASL_LEVEL_NOTICE, + "com.apple.message.signature", signature_string, + "com.apple.message.summarize", "YES", + NULL); +} + +#endif + + +static void log_SecureTransport_early_fail(long signature) +{ +#if (!TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR) + mt_log_SecureTransport_early_fail(signature); +#endif + +#if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR) + ad_log_SecureTransport_early_fail(signature); +#endif +} + +static OSStatus +SSLHandshakeProceed(SSLContext *ctx) +{ + OSStatus err; + + + if(ctx->state==SSL_HdskStateUninit) { + /* If we are the client, we start the negotiation */ + if(ctx->protocolSide == kSSLClientSide) { + err = tls_handshake_negotiate(ctx->hdsk, &ctx->peerID); + if(err) + return err; + } + SSLChangeHdskState(ctx, SSL_HdskStatePending); + } + + if ((err = tls_handshake_continue(ctx->hdsk)) != 0) + return err; + + if ((err = SSLServiceWriteQueue(ctx)) != 0) + return err; + + SSLRecord rec; + + err = SSLReadRecord(&rec, ctx); + + if(!err) { + sslDebugLog("%p going to process a record (rec.len=%zd, ct=%d)\n", ctx, rec.contents.length, rec.contentType); + err = tls_handshake_process(ctx->hdsk, rec.contents, rec.contentType); + sslDebugLog("%p processed a record (rec.len=%zd, ct=%d, err=%d)\n", ctx, rec.contents.length, rec.contentType, (int)err); + SSLFreeRecord(rec, ctx); + } else if(err!=errSSLWouldBlock) { + sslDebugLog("%p Read error err=%d\n\n", ctx, (int)err); + } + + if(ctx->protocolSide == kSSLClientSide && + ctx->dheEnabled == false && + !ctx->serverHelloReceived && + err && err != errSSLWouldBlock) + { + log_SecureTransport_early_fail(err); + } + + return err; +} + +static OSStatus +SSLProcessProtocolMessage(SSLRecord *rec, SSLContext *ctx) +{ + return tls_handshake_process(ctx->hdsk, rec->contents, rec->contentType); +} + +OSStatus +SSLClose(SSLContext *ctx) +{ + OSStatus err = errSecSuccess; + + sslHdskStateDebug("SSLClose"); + if(ctx == NULL) { + return errSecParam; + } + + err = tls_handshake_close(ctx->hdsk); + + if (err == 0) + err = SSLServiceWriteQueue(ctx); + + SSLChangeHdskState(ctx, SSL_HdskStateGracefulClose); + if (err == errSecIO) + err = errSecSuccess; /* Ignore errors related to closed streams */ + return err; +} + +/* + * Determine how much data the client can be guaranteed to + * obtain via SSLRead() without blocking or causing any low-level + * read operations to occur. + * + * Implemented here because the relevant info in SSLContext (receivedDataBuffer + * and receivedDataPos) are only used in this file. + */ +OSStatus +SSLGetBufferedReadSize(SSLContextRef ctx, + size_t *bufSize) /* RETURNED */ +{ + if(ctx == NULL) { + return errSecParam; + } + if(ctx->receivedDataBuffer.data == NULL) { + *bufSize = 0; + } + else { + assert(ctx->receivedDataBuffer.length >= ctx->receivedDataPos); + *bufSize = ctx->receivedDataBuffer.length - ctx->receivedDataPos; + } + return errSecSuccess; +}