X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/b1ab9ed8d0e0f1c3b66d7daa8fd5564444c56195..e3d3b979fd185d8303f28a937baa53a187fb8c7d:/libsecurity_ssl/lib/sslRecord.c diff --git a/libsecurity_ssl/lib/sslRecord.c b/libsecurity_ssl/lib/sslRecord.c index f426cf23..35f58180 100644 --- a/libsecurity_ssl/lib/sslRecord.c +++ b/libsecurity_ssl/lib/sslRecord.c @@ -25,20 +25,22 @@ * sslRecord.c - Encryption, decryption and MACing of data */ +#include #include "ssl.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 "SSLRecordInternal.h" #include #include +#include + /* * 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 @@ -46,263 +48,119 @@ */ #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)); } - -