]> git.saurik.com Git - apple/security.git/blobdiff - OSX/libsecurity_ssl/lib/sslTransport.c
Security-58286.20.16.tar.gz
[apple/security.git] / OSX / libsecurity_ssl / lib / sslTransport.c
index 8bd50bcae09fd217b8a38d22f518989caeab0f05..616b9d492eb371280b44fb70fced61b4a7dfad07 100644 (file)
 #include "sslRecord.h"
 #include "sslDebug.h"
 #include "sslCipherSpecs.h"
 #include "sslRecord.h"
 #include "sslDebug.h"
 #include "sslCipherSpecs.h"
-#include "sslUtils.h"
 
 #include <assert.h>
 #include <string.h>
 
 #include <utilities/SecIOFormat.h>
 
 #include <assert.h>
 #include <string.h>
 
 #include <utilities/SecIOFormat.h>
+#include <utilities/SecCFWrappers.h>
+
+#include <CommonCrypto/CommonDigest.h>
+#include <Security/SecCertificatePriv.h>
 
 #ifndef        NDEBUG
 static inline void sslIoTrace(
 
 #ifndef        NDEBUG
 static inline void sslIoTrace(
@@ -57,7 +60,7 @@ extern int kSplitDefaultValue;
 
 static OSStatus SSLProcessProtocolMessage(SSLRecord *rec, SSLContext *ctx);
 static OSStatus SSLHandshakeProceed(SSLContext *ctx);
 
 static OSStatus SSLProcessProtocolMessage(SSLRecord *rec, SSLContext *ctx);
 static OSStatus SSLHandshakeProceed(SSLContext *ctx);
-//static OSStatus SSLInitConnection(SSLContext *ctx);
+static OSStatus SSLSendAlert(SSLContext *ctx, tls_alert_level_t level, tls_alert_t description);
 
 OSStatus 
 SSLWrite(
 
 OSStatus 
 SSLWrite(
@@ -75,7 +78,6 @@ SSLWrite(
        return errSecParam;
     }
     dataLen = dataLength;
        return errSecParam;
     }
     dataLen = dataLength;
-    processed = 0;        /* Initialize in case we return with errSSLWouldBlock */
     *bytesWritten = 0;
 
     switch(ctx->state) {
     *bytesWritten = 0;
 
     switch(ctx->state) {
@@ -83,6 +85,7 @@ SSLWrite(
                err = errSSLClosedGraceful;
                        goto abort;
         case SSL_HdskStateErrorClose:
                err = errSSLClosedGraceful;
                        goto abort;
         case SSL_HdskStateErrorClose:
+        case SSL_HdskStateOutOfBandError:
                err = errSSLClosedAbort;
                        goto abort;
            case SSL_HdskStateReady:
                err = errSSLClosedAbort;
                        goto abort;
            case SSL_HdskStateReady:
@@ -98,7 +101,6 @@ SSLWrite(
 
     /* First, we have to wait until the session is ready to send data,
         so the encryption keys and such have been established. */
 
     /* 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;
     while (!(ctx->writeCipher_ready))
     {   if ((err = SSLHandshakeProceed(ctx)) != 0)
             goto exit;
@@ -113,7 +115,6 @@ SSLWrite(
     /* Skip empty writes, fragmentation is done at the coreTLS layer */
     if(dataLen) {
         rec.contentType = SSL_RecordTypeAppData;
     /* 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)
         rec.contents.data = ((uint8_t *)data) + processed;
         rec.contents.length = dataLen;
         if ((err = SSLWriteRecord(rec, ctx)) != 0)
@@ -133,13 +134,14 @@ exit:
         case errSSLUnexpectedRecord:
                case errSSLServerAuthCompleted: /* == errSSLClientAuthCompleted */
                case errSSLClientCertRequested:
         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;
+        case errSSLClientHelloReceived:
+        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);
     }
 abort:
        sslIoTrace(ctx, "SSLWrite(2)", dataLength, *bytesWritten, err);
@@ -172,6 +174,7 @@ readRetry:
                        err = errSSLClosedGraceful;
                        goto abort;
                case SSL_HdskStateErrorClose:
                        err = errSSLClosedGraceful;
                        goto abort;
                case SSL_HdskStateErrorClose:
+        case SSL_HdskStateOutOfBandError:
                        err = errSSLClosedAbort;
                        goto abort;
                case SSL_HdskStateNoNotifyClose:
                        err = errSSLClosedAbort;
                        goto abort;
                case SSL_HdskStateNoNotifyClose:
@@ -183,25 +186,31 @@ readRetry:
 
     /* First, we have to wait until the session is ready to receive data,
         so the encryption keys and such have been established. */
 
     /* 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;
                }
     }
 
     while (ctx->readCipher_ready == 0) {
                if ((err = SSLHandshakeProceed(ctx)) != 0) {
             goto exit;
                }
     }
 
+    /* Need this to handle the case were SSLRead returned
+       errSSLClientHelloReceived as readCipher_ready is not set yet in that case */
+    if ((err = tls_handshake_continue(ctx->hdsk)) != 0)
+        return err;
+
     /* Attempt to service the write queue */
     if ((err = SSLServiceWriteQueue(ctx)) != 0) {
                if (err != errSSLWouldBlock) {
             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;
     }
 
     remaining = bufSize;
     charPtr = (uint8_t *)data;
+
+    /* If we have data in the buffer, use that first */
     if (ctx->receivedDataBuffer.data)
     if (ctx->receivedDataBuffer.data)
-    {   count = ctx->receivedDataBuffer.length - ctx->receivedDataPos;
+    {
+        count = ctx->receivedDataBuffer.length - ctx->receivedDataPos;
         if (count > bufSize)
             count = bufSize;
         memcpy(data, ctx->receivedDataBuffer.data + ctx->receivedDataPos, count);
         if (count > bufSize)
             count = bufSize;
         memcpy(data, ctx->receivedDataBuffer.data + ctx->receivedDataPos, count);
@@ -217,29 +226,27 @@ readRetry:
 
     if (ctx->receivedDataBuffer.data != 0 &&
         ctx->receivedDataPos >= ctx->receivedDataBuffer.length)
 
     if (ctx->receivedDataBuffer.data != 0 &&
         ctx->receivedDataPos >= ctx->receivedDataBuffer.length)
-    {   SSLFreeBuffer(&ctx->receivedDataBuffer);
+    {
+        SSLFreeBuffer(&ctx->receivedDataBuffer);
         ctx->receivedDataBuffer.data = 0;
         ctx->receivedDataPos = 0;
     }
 
        /*
         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 we didnt fill up the users buffer, get some more data
         */
     if (remaining > 0 && ctx->state != SSL_HdskStateGracefulClose)
         */
     if (remaining > 0 && ctx->state != SSL_HdskStateGracefulClose)
-    {   assert(ctx->receivedDataBuffer.data == 0);
+    {
+        assert(ctx->receivedDataBuffer.data == 0);
         if ((err = SSLReadRecord(&rec, ctx)) != 0) {
             goto exit;
         }
         if (rec.contentType == SSL_RecordTypeAppData ||
             rec.contentType == SSL_RecordTypeV2_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;
+        {
+            if (rec.contents.length <= remaining)
+            {   /* Copy all we got in the user's buffer */
+                memcpy(charPtr, rec.contents.data, rec.contents.length);
                 *processed += rec.contents.length;
                 {
                     if ((err = SSLFreeRecord(rec, ctx))) {
                 *processed += rec.contents.length;
                 {
                     if ((err = SSLFreeRecord(rec, ctx))) {
@@ -248,12 +255,11 @@ readRetry:
                 }
             }
             else
                 }
             }
             else
-            {   memcpy(charPtr, rec.contents.data, remaining);
-                charPtr += remaining;
+            {   /* Copy what we can in the user's buffer, keep the rest for next SSLRead. */
+                memcpy(charPtr, rec.contents.data, remaining);
                 *processed += remaining;
                 ctx->receivedDataBuffer = rec.contents;
                 ctx->receivedDataPos = remaining;
                 *processed += remaining;
                 ctx->receivedDataBuffer = rec.contents;
                 ctx->receivedDataPos = remaining;
-                remaining = 0;
             }
         }
         else {
             }
         }
         else {
@@ -262,7 +268,7 @@ readRetry:
                  process the write queue. This replicate exactly the behavior 
                  before the coreTLS adoption */
                 if(err == errSSLClosedGraceful) {
                  process the write queue. This replicate exactly the behavior 
                  before the coreTLS adoption */
                 if(err == errSSLClosedGraceful) {
-                    err = SSLClose(ctx);
+                    SSLClose(ctx);
                 } else {
                     goto exit;
                 }
                 } else {
                     goto exit;
                 }
@@ -282,20 +288,21 @@ exit:
                goto readRetry;
        }
        /* shut down on serious errors */
                goto readRetry;
        }
        /* shut down on serious errors */
-       switch(err) {
-               case errSecSuccess:
-               case errSSLWouldBlock:
+    switch(err) {
+        case errSecSuccess:
+        case errSSLWouldBlock:
         case errSSLUnexpectedRecord:
         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;
+        case errSSLServerAuthCompleted: /* == errSSLClientAuthCompleted */
+        case errSSLClientCertRequested:
+        case errSSLClientHelloReceived:
+        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);
     }
 abort:
        sslIoTrace(ctx, "SSLRead returns", dataLength, *processed, err);
@@ -306,6 +313,90 @@ abort:
 #include "sslCrypto.h"
 #endif
 
 #include "sslCrypto.h"
 #endif
 
+
+
+static void get_extended_peer_id(SSLContext *ctx, tls_buffer *extended_peer_id)
+{
+    uint8_t md[CC_SHA256_DIGEST_LENGTH];
+    __block CC_SHA256_CTX hash_ctx;
+
+    CC_SHA256_Init(&hash_ctx);
+
+    CC_SHA256_Update(&hash_ctx, &ctx->allowAnyRoot, sizeof(ctx->allowAnyRoot));
+
+#if !TARGET_OS_IPHONE
+    if(ctx->trustedLeafCerts) {
+        CFArrayForEach(ctx->trustedLeafCerts, ^(const void *value) {
+            SecCertificateRef cert = (SecCertificateRef) value;
+            CC_SHA256_Update(&hash_ctx, SecCertificateGetBytePtr(cert), (CC_LONG)SecCertificateGetLength(cert));
+        });
+    }
+#endif
+
+    CC_SHA256_Update(&hash_ctx, &ctx->trustedCertsOnly, sizeof(ctx->trustedCertsOnly));
+
+
+    if(ctx->trustedCerts) {
+        CFArrayForEach(ctx->trustedCerts, ^(const void *value) {
+            SecCertificateRef cert = (SecCertificateRef) value;
+            CC_SHA256_Update(&hash_ctx, SecCertificateGetBytePtr(cert), (CC_LONG)SecCertificateGetLength(cert));
+        });
+    }
+
+    CC_SHA256_Final(md, &hash_ctx);
+
+    extended_peer_id->length = ctx->peerID.length + sizeof(md);
+    extended_peer_id->data = sslMalloc(extended_peer_id->length);
+    memcpy(extended_peer_id->data, ctx->peerID.data, ctx->peerID.length);
+    memcpy(extended_peer_id->data+ctx->peerID.length, md, sizeof(md));
+}
+
+/* Send the initial client hello */
+static OSStatus
+SSLHandshakeStart(SSLContext *ctx)
+{
+    int err;
+    tls_buffer extended_peer_id;
+    get_extended_peer_id(ctx, &extended_peer_id);
+    err = tls_handshake_negotiate(ctx->hdsk, &extended_peer_id);
+    free(extended_peer_id.data);
+    if(err)
+        return err;
+
+    ctx->readCipher_ready = 0;
+    ctx->writeCipher_ready = 0;
+    SSLChangeHdskState(ctx, SSL_HdskStatePending);
+
+    return noErr;
+}
+
+OSStatus
+SSLReHandshake(SSLContext *ctx)
+{
+    if(ctx == NULL) {
+        return errSecParam;
+    }
+
+    switch (ctx->state) {
+        case SSL_HdskStateGracefulClose:
+            return errSSLClosedGraceful;
+        case SSL_HdskStateErrorClose:
+        case SSL_HdskStateOutOfBandError:
+            return errSSLClosedAbort;
+        case SSL_HdskStatePending:
+            return errSecBadReq;
+        default:
+            break;
+    }
+
+    /* If we are the client, we start the negotiation */
+    if(ctx->protocolSide == kSSLClientSide) {
+        return SSLHandshakeStart(ctx);
+    } else {
+        return tls_handshake_request_renegotiation(ctx->hdsk);
+    }
+}
+
 OSStatus
 SSLHandshake(SSLContext *ctx)
 {
 OSStatus
 SSLHandshake(SSLContext *ctx)
 {
@@ -314,12 +405,15 @@ SSLHandshake(SSLContext *ctx)
        if(ctx == NULL) {
                return errSecParam;
        }
        if(ctx == NULL) {
                return errSecParam;
        }
-    if (ctx->state == SSL_HdskStateGracefulClose)
-        return errSSLClosedGraceful;
-    if (ctx->state == SSL_HdskStateErrorClose)
-        return errSSLClosedAbort;
 
 
-    err = errSecSuccess;
+    switch (ctx->state) {
+        case SSL_HdskStateGracefulClose:
+            return errSSLClosedGraceful;
+        case SSL_HdskStateErrorClose:
+            return errSSLClosedAbort;
+        default:
+            break;
+    }
 
     if(ctx->isDTLS && ctx->timeout_deadline) {
         CFAbsoluteTime current = CFAbsoluteTimeGetCurrent();
 
     if(ctx->isDTLS && ctx->timeout_deadline) {
         CFAbsoluteTime current = CFAbsoluteTimeGetCurrent();
@@ -333,12 +427,44 @@ SSLHandshake(SSLContext *ctx)
         }
     }
 
         }
     }
 
-    while (ctx->readCipher_ready == 0 || ctx->writeCipher_ready == 0)
-    {
+    /* Initial Client Hello */
+    if(ctx->state==SSL_HdskStateUninit) {
+        /* If we are the client, we start the negotiation */
+        if(ctx->protocolSide == kSSLClientSide) {
+            err = SSLHandshakeStart(ctx);
+            if(err) {
+                return err;
+            }
+        }
+        SSLChangeHdskState(ctx, SSL_HdskStatePending);
+    }
+
+    /* If an out-of-band error occurred, handle it here and then terminate
+     the connection as needed. */
+    if (ctx->state == SSL_HdskStateOutOfBandError) {
+        bool shouldClose = true;
+        switch (ctx->outOfBandError) {
+            case errSecCertificateExpired:
+                SSLSendAlert(ctx, tls_handshake_alert_level_fatal, tls_handshake_alert_CertExpired);
+                break;
+            case errSecCertificateRevoked:
+                SSLSendAlert(ctx, tls_handshake_alert_level_fatal, tls_handshake_alert_CertRevoked);
+                break;
+            default:
+                shouldClose = false;
+                break;
+        }
+
+        if (shouldClose) {
+            return SSLClose(ctx);
+        }
+    }
+
+    do {
         err = SSLHandshakeProceed(ctx);
         if((err != 0) && (err != errSSLUnexpectedRecord))
             return err;
         err = SSLHandshakeProceed(ctx);
         if((err != 0) && (err != errSSLUnexpectedRecord))
             return err;
-    }
+    } while (ctx->readCipher_ready == 0 || ctx->writeCipher_ready == 0);
 
        /* one more flush at completion of successful handshake */
     if ((err = SSLServiceWriteQueue(ctx)) != 0) {
 
        /* one more flush at completion of successful handshake */
     if ((err = SSLServiceWriteQueue(ctx)) != 0) {
@@ -350,50 +476,14 @@ SSLHandshake(SSLContext *ctx)
 
 #if (TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR)
 
 
 #if (TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR)
 
-#include <AggregateDictionary/ADClient.h>
-
-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);
-}
+#include "SecADWrapper.h"
 
 static void ad_log_SecureTransport_early_fail(long signature)
 {
 
 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);
+    CFStringRef key = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("com.apple.SecureTransport.early_fail.%ld"), signature);
 
 
+    if (key) {
+        SecADAddValueForScalarKey(key, 1);
         CFRelease(key);
     }
 }
         CFRelease(key);
     }
 }
@@ -431,22 +521,12 @@ static void log_SecureTransport_early_fail(long signature)
 #endif
 }
 
 #endif
 }
 
+
 static OSStatus
 SSLHandshakeProceed(SSLContext *ctx)
 {
     OSStatus  err;
 
 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 = tls_handshake_continue(ctx->hdsk)) != 0)
         return err;
 
@@ -499,11 +579,30 @@ SSLClose(SSLContext *ctx)
         err = SSLServiceWriteQueue(ctx);
 
     SSLChangeHdskState(ctx, SSL_HdskStateGracefulClose);
         err = SSLServiceWriteQueue(ctx);
 
     SSLChangeHdskState(ctx, SSL_HdskStateGracefulClose);
-    if (err == errSecIO)
+    if (err == errSecIO) 
         err = errSecSuccess;     /* Ignore errors related to closed streams */
     return err;
 }
 
         err = errSecSuccess;     /* Ignore errors related to closed streams */
     return err;
 }
 
+static OSStatus
+SSLSendAlert(SSLContext *ctx, tls_alert_level_t alertLevel, tls_alert_t alert)
+{
+    sslHdskStateDebug("SSLSendAlert");
+    if (ctx == NULL) {
+        return errSecParam;
+    }
+
+    return tls_handshake_send_alert(ctx->hdsk, alertLevel, alert);
+}
+
+OSStatus
+SSLSetError(SSLContext *ctx, OSStatus error)
+{
+    ctx->state = SSL_HdskStateOutOfBandError;
+    ctx->outOfBandError = error;
+    return errSecSuccess;
+}
+
 /*
  * Determine how much data the client can be guaranteed to
  * obtain via SSLRead() without blocking or causing any low-level
 /*
  * Determine how much data the client can be guaranteed to
  * obtain via SSLRead() without blocking or causing any low-level