]> git.saurik.com Git - apple/security.git/blobdiff - libsecurity_ssl/lib/sslRecord.c
Security-55471.14.8.tar.gz
[apple/security.git] / libsecurity_ssl / lib / sslRecord.c
index f426cf23ed0f3a8edbc770a1570000b53f2cb3c0..35f58180a894a8675f119f10b09b01630b51a208 100644 (file)
  * sslRecord.c - Encryption, decryption and MACing of data
 */
 
  * sslRecord.c - Encryption, decryption and MACing of data
 */
 
+#include <SecureTransport.h>
 #include "ssl.h"
 #include "ssl.h"
-
 #include "sslRecord.h"
 #include "sslMemory.h"
 #include "sslRecord.h"
 #include "sslMemory.h"
-#include "cryptType.h"
 #include "sslContext.h"
 #include "sslAlertMessage.h"
 #include "sslDebug.h"
 #include "sslUtils.h"
 #include "sslDigests.h"
 #include "sslContext.h"
 #include "sslAlertMessage.h"
 #include "sslDebug.h"
 #include "sslUtils.h"
 #include "sslDigests.h"
+#include "SSLRecordInternal.h"
 
 #include <string.h>
 #include <assert.h>
 
 
 #include <string.h>
 #include <assert.h>
 
+#include <utilities/SecIOFormat.h>
+
 /*
  * Lots of servers fail to provide closure alerts when they disconnect.
  * For now we'll just accept it as long as it occurs on a clean record boundary
 /*
  * Lots of servers fail to provide closure alerts when they disconnect.
  * For now we'll just accept it as long as it occurs on a clean record boundary
  */
 #define SSL_ALLOW_UNNOTICED_DISCONNECT 1
 
  */
 #define SSL_ALLOW_UNNOTICED_DISCONNECT 1
 
-/* ReadSSLRecord
- *  Attempt to read & decrypt an SSL record.
- */
-OSStatus
-SSLReadRecord(SSLRecord *rec, SSLContext *ctx)
-{   OSStatus        err;
-    size_t          len, contentLen;
-    UInt8           *charPtr;
-    SSLBuffer       readData, cipherFragment;
-    size_t          head=5;
-    int             skipit=0;
 
 
-#if ENABLE_DTLS
-    if(ctx->isDTLS)
-        head+=8;
-#endif
-
-    if (!ctx->partialReadBuffer.data || ctx->partialReadBuffer.length < head)
-    {   if (ctx->partialReadBuffer.data)
-            if ((err = SSLFreeBuffer(&ctx->partialReadBuffer, ctx)) != 0)
-            {   SSLFatalSessionAlert(SSL_AlertInternalError, ctx);
-                return err;
-            }
-        if ((err = SSLAllocBuffer(&ctx->partialReadBuffer,
-                               DEFAULT_BUFFER_SIZE, ctx)) != 0)
-        {   SSLFatalSessionAlert(SSL_AlertInternalError, ctx);
-            return err;
-        }
-    }
-
-    if (ctx->negProtocolVersion == SSL_Version_Undetermined) {
-        if (ctx->amountRead < 1)
-        {   readData.length = 1 - ctx->amountRead;
-            readData.data = ctx->partialReadBuffer.data + ctx->amountRead;
-            len = readData.length;
-            err = sslIoRead(readData, &len, ctx);
-            if(err != 0)
-            {   if (err == errSSLWouldBlock) {
-                                       ctx->amountRead += len;
-                                       return err;
-                               }
-                else {
-                                       /* abort */
-                                       err = errSSLClosedAbort;
-                                       if((ctx->protocolSide == kSSLClientSide) &&
-                                          (ctx->amountRead == 0) &&
-                                          (len == 0)) {
-                                               /*
-                                                * Detect "server refused to even try to negotiate"
-                                                * error, when the server drops the connection before
-                                                * sending a single byte.
-                                                */
-                                               switch(ctx->state) {
-                                                       case SSL_HdskStateServerHello:
-                                                       case SSL_HdskStateServerHelloUnknownVersion:
-                                                               sslHdskStateDebug("Server dropped initial connection\n");
-                                                               err = errSSLConnectionRefused;
-                                                               break;
-                                                       default:
-                                                               break;
-                                               }
-                                       }
-                    SSLFatalSessionAlert(SSL_AlertCloseNotify, ctx);
-                                       return err;
-                               }
-            }
-            ctx->amountRead += len;
-        }
-    }
-
-    if (ctx->amountRead < head)
-    {   readData.length = head - ctx->amountRead;
-        readData.data = ctx->partialReadBuffer.data + ctx->amountRead;
-        len = readData.length;
-        err = sslIoRead(readData, &len, ctx);
-        if(err != 0)
-        {
-                       switch(err) {
-                               case errSSLWouldBlock:
-                                       ctx->amountRead += len;
-                                       break;
-                               #if     SSL_ALLOW_UNNOTICED_DISCONNECT
-                               case errSSLClosedGraceful:
-                                       /* legal if we're on record boundary and we've gotten past
-                                        * the handshake */
-                                       if((ctx->amountRead == 0) &&                            /* nothing pending */
-                                          (len == 0) &&                                                        /* nothing new */
-                                          (ctx->state == SSL_HdskStateClientReady)) {  /* handshake done */
-                                           /*
-                                                * This means that the server has disconnected without
-                                                * sending a closure alert notice. This is technically
-                                                * illegal per the SSL3 spec, but about half of the
-                                                * servers out there do it, so we report it as a separate
-                                                * error which most clients - including (currently)
-                                                * URLAccess - ignore by treating it the same as
-                                                * a errSSLClosedGraceful error. Paranoid
-                                                * clients can detect it and handle it however they
-                                                * want to.
-                                                */
-                                               SSLChangeHdskState(ctx, SSL_HdskStateNoNotifyClose);
-                                               err = errSSLClosedNoNotify;
-                                               break;
-                                       }
-                                       else {
-                                               /* illegal disconnect */
-                                               err = errSSLClosedAbort;
-                                               /* and drop thru to default: fatal alert */
-                                       }
-                               #endif  /* SSL_ALLOW_UNNOTICED_DISCONNECT */
-                               default:
-                                       SSLFatalSessionAlert(SSL_AlertCloseNotify, ctx);
-                                       break;
-                               }
-            return err;
-        }
-        ctx->amountRead += len;
-    }
-
-    assert(ctx->amountRead >= head);
-
-    charPtr = ctx->partialReadBuffer.data;
-    rec->contentType = *charPtr++;
-    if (rec->contentType < SSL_RecordTypeV3_Smallest ||
-        rec->contentType > SSL_RecordTypeV3_Largest)
-        return errSSLProtocol;
-
-    rec->protocolVersion = (SSLProtocolVersion)SSLDecodeInt(charPtr, 2);
-    charPtr += 2;
-
-#if ENABLE_DTLS
-    if(rec->protocolVersion == DTLS_Version_1_0)
-    {
-        sslUint64 seqNum;
-        SSLDecodeUInt64(charPtr, 8, &seqNum);
-        charPtr += 8;
-        sslLogRecordIo("Read DTLS Record %08x_%08x (seq is: %08x_%08x)",
-               seqNum.high, seqNum.low,
-               ctx->readCipher.sequenceNum.high,ctx->readCipher.sequenceNum.low);
-
-        /* if the epoch of the record is different of current read cipher, just drop it */
-        if((seqNum.high>>8)!=(ctx->readCipher.sequenceNum.high>>8)) {
-            skipit=1;
-        } else {
-            ctx->readCipher.sequenceNum.high=seqNum.high;
-            ctx->readCipher.sequenceNum.low=seqNum.low;
-        }
-    }
-#endif
-
-    contentLen = SSLDecodeInt(charPtr, 2);
-    charPtr += 2;
-    if (contentLen > (16384 + 2048))    /* Maximum legal length of an
-                                                                                * SSLCipherText payload */
-    {   SSLFatalSessionAlert(SSL_AlertRecordOverflow, ctx);
-        return errSSLProtocol;
+static OSStatus errorTranslate(int recordErr)
+{
+    switch(recordErr) {
+        case errSecSuccess:
+            return errSecSuccess;
+        case errSSLRecordInternal:
+            return errSSLInternal;
+        case errSSLRecordWouldBlock:
+            return errSSLWouldBlock;
+        case errSSLRecordProtocol:
+            return errSSLProtocol;
+        case errSSLRecordNegotiation:
+            return errSSLNegotiation;
+        case errSSLRecordClosedAbort:
+            return errSSLClosedAbort;
+        case errSSLRecordConnectionRefused:
+            return errSSLConnectionRefused;
+        case errSSLRecordDecryptionFail:
+            return errSSLDecryptionFail;
+        case errSSLRecordBadRecordMac:
+            return errSSLBadRecordMac;
+        case errSSLRecordRecordOverflow:
+            return errSSLRecordOverflow;
+        case errSSLRecordUnexpectedRecord:
+            return errSSLUnexpectedRecord;
+        default:
+            sslErrorLog("unknown error code returned in sslErrorTranslate: %d\n", recordErr);
+            return recordErr;
     }
     }
+}
 
 
-    /* Dont check this if we are going to drop an old packet:
-      we dont know if the digestSize is the correct one */
-       if (!skipit && contentLen < ctx->readCipher.macRef->hash->digestSize)
-       {
-               SSLFatalSessionAlert(SSL_AlertInternalError, ctx);
-               return errSSLClosedAbort;
-       }
+/* SSLWriteRecord
+ *  Attempt to encrypt and queue an SSL record.
+ */
+OSStatus
+SSLWriteRecord(SSLRecord rec, SSLContext *ctx)
+{
+    OSStatus    err;
 
 
-    if (ctx->partialReadBuffer.length < head + contentLen)
-    {   if ((err = SSLReallocBuffer(&ctx->partialReadBuffer, head + contentLen, ctx)) != 0)
-        {   SSLFatalSessionAlert(SSL_AlertInternalError, ctx);
-            return err;
-        }
-    }
+    err=errorTranslate(ctx->recFuncs->write(ctx->recCtx, rec));
 
 
-    if (ctx->amountRead < head + contentLen)
-    {   readData.length = head + contentLen - ctx->amountRead;
-        readData.data = ctx->partialReadBuffer.data + ctx->amountRead;
-        len = readData.length;
-        err = sslIoRead(readData, &len, ctx);
-        if(err != 0)
-        {   if (err == errSSLWouldBlock)
-                ctx->amountRead += len;
-            else
-                SSLFatalSessionAlert(SSL_AlertCloseNotify, ctx);
-            return err;
-        }
-        ctx->amountRead += len;
+    switch(err) {
+        case errSecSuccess:
+            break;
+        default:
+            sslErrorLog("unexpected error code returned in SSLWriteRecord: %d\n", (int)err);
+            break;
     }
 
     }
 
-    assert(ctx->amountRead >= head + contentLen);
-
-    cipherFragment.data = ctx->partialReadBuffer.data + head;
-    cipherFragment.length = contentLen;
-
-    ctx->amountRead = 0;        /* We've used all the data in the cache */
+    return err;
+}
 
 
-    /* We dont decrypt if we were told to skip this record */
-    if(skipit) {
-        DTLSRetransmit(ctx);
-        return errSSLWouldBlock;
-    }
-       /*
-        * Decrypt the payload & check the MAC, modifying the length of the
-        * buffer to indicate the amount of plaintext data after adjusting
-        * for the block size and removing the MAC (this function generates
-        * its own alerts).
-        */
-       assert(ctx->sslTslCalls != NULL);
-    if ((err = ctx->sslTslCalls->decryptRecord(rec->contentType,
-                       &cipherFragment, ctx)) != 0)
-        return err;
+/* SSLFreeRecord
+ *  Free a record returned by SSLReadRecord.
+ */
+OSStatus
+SSLFreeRecord(SSLRecord rec, SSLContext *ctx)
+{
+    return ctx->recFuncs->free(ctx->recCtx, rec);
+}
 
 
-       /*
-        * We appear to have sucessfully received a record; increment the
-        * sequence number
-        */
-    IncrementUInt64(&ctx->readCipher.sequenceNum);
+/* SSLReadRecord
+ *  Attempt to read & decrypt an SSL record.
+ *  Record content should be freed using SSLFreeRecord
+ */
+OSStatus
+SSLReadRecord(SSLRecord *rec, SSLContext *ctx)
+{   OSStatus        err;
 
 
-       /* Allocate a buffer to return the plaintext in and return it */
-    if ((err = SSLAllocBuffer(&rec->contents, cipherFragment.length, ctx)) != 0)
-    {   SSLFatalSessionAlert(SSL_AlertInternalError, ctx);
-        return err;
+    err=errorTranslate(ctx->recFuncs->read(ctx->recCtx, rec));
+
+    switch(err) {
+        case errSecSuccess:
+        case errSSLWouldBlock:
+            break;
+        case errSSLUnexpectedRecord:
+            DTLSRetransmit(ctx);
+            break;
+        case errSSLDecryptionFail:
+        case errSSLBadRecordMac:
+            /* We never send a Decryption Failed alert, instead we send the BadRecordMac alert */
+            /* This is TLS 1.1 compliant - Do it for all protocols versions. */
+            /* Except for DTLS where we do not send any alert. */
+            if(ctx->isDTLS) {
+                /* This will ensure we try to read again before returning to the caller
+                   We do NOT want to use errSSLWouldBlock here, as this should only indicate
+                   the IO read callback status */
+                err=errSSLUnexpectedRecord;
+            } else {
+                SSLFatalSessionAlert(SSL_AlertBadRecordMac, ctx);
+            }
+            break;
+        case errSSLInternal:
+            SSLFatalSessionAlert(SSL_AlertInternalError, ctx);
+            break;
+        case errSSLRecordOverflow:
+            SSLFatalSessionAlert(SSL_AlertRecordOverflow, ctx);
+            break;
+        case errSSLClosedAbort:
+        case errSSLConnectionRefused:
+            SSLFatalSessionAlert(SSL_AlertCloseNotify, ctx);
+            break;
+        default:
+            sslErrorLog("unknown error code returned in SSLReadRecord: %d\n", (int)err);
+            SSLFatalSessionAlert(SSL_AlertCloseNotify, ctx);
+            break;
     }
     }
-    memcpy(rec->contents.data, cipherFragment.data, cipherFragment.length);
-
 
 
-    return noErr;
+    return err;
 }
 
 }
 
-/* common for sslv3 and tlsv1, except for the computeMac callout */
-OSStatus SSLVerifyMac(
-       UInt8 type,
-       SSLBuffer *data,
-       UInt8 *compareMAC,
-       SSLContext *ctx)
+OSStatus SSLServiceWriteQueue(SSLContext *ctx)
 {
 {
-       OSStatus        err;
-    UInt8           macData[SSL_MAX_DIGEST_LEN];
-    SSLBuffer       secret, mac;
-
-    secret.data = ctx->readCipher.macSecret;
-    secret.length = ctx->readCipher.macRef->hash->digestSize;
-    mac.data = macData;
-    mac.length = ctx->readCipher.macRef->hash->digestSize;
-
-       assert(ctx->sslTslCalls != NULL);
-    if ((err = ctx->sslTslCalls->computeMac(type,
-                       *data,
-                       mac,
-                       &ctx->readCipher,
-                       ctx->readCipher.sequenceNum,
-                       ctx)) != 0)
-        return err;
-
-    if ((memcmp(mac.data, compareMAC, mac.length)) != 0) {
-               sslErrorLog("ssl3VerifyMac: Mac verify failure\n");
-        return errSSLProtocol;
-    }
-    return noErr;
+    return errorTranslate(ctx->recFuncs->serviceWriteQueue(ctx->recCtx));
 }
 }
-
-