#include "ssl.h"
#ifndef _SSLREC_H_
#include "sslrec.h"
#endif
#ifndef _SSLALLOC_H_
#include "sslalloc.h"
#endif
#ifndef _CRYPTTYPE_H_
#include "cryptType.h"
#endif
#ifndef _SSLCTX_H_
#include "sslctx.h"
#endif
#ifndef _SSLALERT_H_
#include "sslalert.h"
#endif
#ifndef _SSL_DEBUG_H_
#include "sslDebug.h"
#endif
#ifndef _SSL2_H_
#include "ssl2.h"
#endif
#ifndef _SSLUTIL_H_
#include "sslutil.h"
#endif
#ifdef _APPLE_CDSA_
#ifndef _APPLE_GLUE_H_
#include "appleGlue.h"
#endif
#endif
#include <string.h>
#define SSL_ALLOW_UNNOTICED_DISCONNECT 1
static SSLErr DecryptSSLRecord(UInt8 type, SSLBuffer *payload, SSLContext *ctx);
static SSLErr VerifyMAC(UInt8 type, SSLBuffer data, UInt8 *compareMAC, SSLContext *ctx);
static SSLErr ComputeMAC(UInt8 type, SSLBuffer data, SSLBuffer mac, sslUint64 seqNo, SSLBuffer secret, const HashReference *macHash, SSLContext *ctx);
static UInt8* SSLEncodeUInt64(UInt8 *p, sslUint64 value);
SSLErr
SSLReadRecord(SSLRecord *rec, SSLContext *ctx)
{ SSLErr err;
UInt32 len, contentLen;
UInt8 *progress;
SSLBuffer readData, cipherFragment;
if (!ctx->partialReadBuffer.data || ctx->partialReadBuffer.length < 5)
{ if (ctx->partialReadBuffer.data)
if ((err = SSLFreeBuffer(&ctx->partialReadBuffer, &ctx->sysCtx)) != 0)
{ SSLFatalSessionAlert(alert_close_notify, ctx);
return ERR(err);
}
if ((err = SSLAllocBuffer(&ctx->partialReadBuffer, DEFAULT_BUFFER_SIZE, &ctx->sysCtx)) != 0)
{ SSLFatalSessionAlert(alert_close_notify, ctx);
return ERR(err);
}
}
if (ctx->negProtocolVersion == SSL_Version_Undetermined ||
ctx->negProtocolVersion == SSL_Version_3_0_With_2_0_Hello)
if (ctx->amountRead < 1)
{ readData.length = 1 - ctx->amountRead;
readData.data = ctx->partialReadBuffer.data + ctx->amountRead;
len = readData.length;
#ifdef _APPLE_CDSA_
err = sslIoRead(readData, &len, ctx);
if(err != 0)
#else
if (ERR(err = ctx->ioCtx.read(readData, &len, ctx->ioCtx.ioRef)) != 0)
#endif
{ if (err == SSLWouldBlockErr)
ctx->amountRead += len;
else
SSLFatalSessionAlert(alert_close_notify, ctx);
return err;
}
ctx->amountRead += len;
}
switch (ctx->negProtocolVersion)
{ case SSL_Version_Undetermined:
case SSL_Version_3_0_With_2_0_Hello:
if (ctx->partialReadBuffer.data[0] < SSL_smallest_3_0_type ||
ctx->partialReadBuffer.data[0] > SSL_largest_3_0_type)
return SSL2ReadRecord(rec, ctx);
else
break;
case SSL_Version_2_0:
return SSL2ReadRecord(rec, ctx);
default:
break;
}
if (ctx->amountRead < 5)
{ readData.length = 5 - ctx->amountRead;
readData.data = ctx->partialReadBuffer.data + ctx->amountRead;
len = readData.length;
#ifdef _APPLE_CDSA_
err = sslIoRead(readData, &len, ctx);
if(err != 0)
#else
if (ERR(err = ctx->ioCtx.read(readData, &len, ctx->ioCtx.ioRef)) != 0)
#endif
{
switch(err) {
case SSLWouldBlockErr:
ctx->amountRead += len;
break;
#if SSL_ALLOW_UNNOTICED_DISCONNECT
case SSLConnectionClosedGraceful:
if((ctx->amountRead == 0) &&
(len == 0) &&
(ctx->state == HandshakeClientReady)) {
SSLChangeHdskState(ctx, SSLNoNotifyClose);
err = SSLConnectionClosedNoNotify;
break;
}
else {
err = SSLConnectionClosedError;
}
#endif
default:
SSLFatalSessionAlert(alert_close_notify, ctx);
break;
}
return err;
}
ctx->amountRead += len;
}
CASSERT(ctx->amountRead >= 5);
progress = ctx->partialReadBuffer.data;
rec->contentType = *progress++;
if (rec->contentType < SSL_smallest_3_0_type ||
rec->contentType > SSL_largest_3_0_type)
return ERR(SSLProtocolErr);
rec->protocolVersion = (SSLProtocolVersion)SSLDecodeInt(progress, 2);
progress += 2;
contentLen = SSLDecodeInt(progress, 2);
progress += 2;
if (contentLen > (16384 + 2048))
{ SSLFatalSessionAlert(alert_unexpected_message, ctx);
return ERR(SSLProtocolErr);
}
if (ctx->partialReadBuffer.length < 5 + contentLen)
{ if ((err = SSLReallocBuffer(&ctx->partialReadBuffer, 5 + contentLen, &ctx->sysCtx)) != 0)
{ SSLFatalSessionAlert(alert_close_notify, ctx);
return ERR(err);
}
}
if (ctx->amountRead < 5 + contentLen)
{ readData.length = 5 + contentLen - ctx->amountRead;
readData.data = ctx->partialReadBuffer.data + ctx->amountRead;
len = readData.length;
#ifdef _APPLE_CDSA_
err = sslIoRead(readData, &len, ctx);
if(err != 0)
#else
if (ERR(err = ctx->ioCtx.read(readData, &len, ctx->ioCtx.ioRef)) != 0)
#endif
{ if (err == SSLWouldBlockErr)
ctx->amountRead += len;
else
SSLFatalSessionAlert(alert_close_notify, ctx);
return err;
}
ctx->amountRead += len;
}
CASSERT(ctx->amountRead >= 5 + contentLen);
cipherFragment.data = ctx->partialReadBuffer.data + 5;
cipherFragment.length = contentLen;
if ((err = DecryptSSLRecord(rec->contentType, &cipherFragment, ctx)) != 0)
return err;
IncrementUInt64(&ctx->readCipher.sequenceNum);
if ((err = SSLAllocBuffer(&rec->contents, cipherFragment.length, &ctx->sysCtx)) != 0)
{ SSLFatalSessionAlert(alert_close_notify, ctx);
return ERR(err);
}
memcpy(rec->contents.data, cipherFragment.data, cipherFragment.length);
ctx->amountRead = 0;
return SSLNoErr;
}
SSLErr
SSLWriteRecord(SSLRecord rec, SSLContext *ctx)
{ SSLErr err;
int padding = 0, i;
WaitingRecord *out, *queue;
SSLBuffer buf, payload, secret, mac;
UInt8 *progress;
UInt16 payloadSize,blockSize;
if (rec.protocolVersion == SSL_Version_2_0)
return SSL2WriteRecord(rec, ctx);
CASSERT(rec.protocolVersion == SSL_Version_3_0);
CASSERT(rec.contents.length <= 16384);
out = 0;
if ((err = SSLAllocBuffer(&buf, sizeof(WaitingRecord), &ctx->sysCtx)) != 0)
return ERR(err);
out = (WaitingRecord*)buf.data;
out->next = 0;
out->sent = 0;
payloadSize = (UInt16) (rec.contents.length + ctx->writeCipher.hash->digestSize);
blockSize = ctx->writeCipher.symCipher->blockSize;
if (blockSize > 0)
{ padding = blockSize - (payloadSize % blockSize) - 1;
payloadSize += padding + 1;
}
out->data.data = 0;
if ((err = SSLAllocBuffer(&out->data, 5 + payloadSize, &ctx->sysCtx)) != 0)
goto fail;
progress = out->data.data;
*(progress++) = rec.contentType;
progress = SSLEncodeInt(progress, rec.protocolVersion, 2);
progress = SSLEncodeInt(progress, payloadSize, 2);
memcpy(progress, rec.contents.data, rec.contents.length);
payload.data = progress;
payload.length = rec.contents.length;
progress += rec.contents.length;
mac.data = progress;
mac.length = ctx->writeCipher.hash->digestSize;
progress += mac.length;
if (mac.length > 0)
{ secret.data = ctx->writeCipher.macSecret;
secret.length = ctx->writeCipher.hash->digestSize;
if ((err = ComputeMAC(rec.contentType, payload, mac, ctx->writeCipher.sequenceNum, secret, ctx->writeCipher.hash, ctx)) != 0)
goto fail;
}
payload.length = payloadSize;
if (ctx->writeCipher.symCipher->blockSize > 0)
for (i = 1; i <= padding + 1; ++i)
payload.data[payload.length - i] = padding;
DUMP_BUFFER_NAME("cleartext data", payload);
if ((err = ctx->writeCipher.symCipher->encrypt(payload,
payload,
&ctx->writeCipher,
ctx)) != 0)
goto fail;
DUMP_BUFFER_NAME("encrypted data", payload);
if (ctx->recordWriteQueue == 0)
ctx->recordWriteQueue = out;
else
{ queue = ctx->recordWriteQueue;
while (queue->next != 0)
queue = queue->next;
queue->next = out;
}
IncrementUInt64(&ctx->writeCipher.sequenceNum);
return SSLNoErr;
fail:
SSLFreeBuffer(&out->data, &ctx->sysCtx);
buf.data = (UInt8*)out;
buf.length = sizeof(WaitingRecord);
SSLFreeBuffer(&buf, &ctx->sysCtx);
return ERR(err);
}
static SSLErr
DecryptSSLRecord(UInt8 type, SSLBuffer *payload, SSLContext *ctx)
{ SSLErr err;
SSLBuffer content;
if ((ctx->readCipher.symCipher->blockSize > 0) &&
((payload->length % ctx->readCipher.symCipher->blockSize) != 0))
{ SSLFatalSessionAlert(alert_unexpected_message, ctx);
return ERR(SSLProtocolErr);
}
DUMP_BUFFER_NAME("encrypted data", (*payload));
if ((err = ctx->readCipher.symCipher->decrypt(*payload,
*payload,
&ctx->readCipher,
ctx)) != 0)
{ SSLFatalSessionAlert(alert_close_notify, ctx);
return ERR(err);
}
DUMP_BUFFER_NAME("decrypted data", (*payload));
content.data = payload->data;
content.length = payload->length - ctx->readCipher.hash->digestSize;
if (ctx->readCipher.symCipher->blockSize > 0)
{
if (payload->data[payload->length - 1] >= ctx->readCipher.symCipher->blockSize)
{ SSLFatalSessionAlert(alert_unexpected_message, ctx);
errorLog1("DecryptSSLRecord: bad padding length (%d)\n",
(unsigned)payload->data[payload->length - 1]);
return ERR(SSLProtocolErr);
}
content.length -= 1 + payload->data[payload->length - 1];
}
if (ctx->readCipher.hash->digestSize > 0)
if ((err = VerifyMAC(type, content, payload->data + content.length, ctx)) != 0)
{ SSLFatalSessionAlert(alert_bad_record_mac, ctx);
return ERR(err);
}
*payload = content;
return SSLNoErr;
}
static UInt8*
SSLEncodeUInt64(UInt8 *p, sslUint64 value)
{ p = SSLEncodeInt(p, value.high, 4);
return SSLEncodeInt(p, value.low, 4);
}
static SSLErr
VerifyMAC(UInt8 type, SSLBuffer data, UInt8 *compareMAC, SSLContext *ctx)
{ SSLErr err;
UInt8 macData[MAX_DIGEST_SIZE];
SSLBuffer secret, mac;
secret.data = ctx->readCipher.macSecret;
secret.length = ctx->readCipher.hash->digestSize;
mac.data = macData;
mac.length = ctx->readCipher.hash->digestSize;
if ((err = ComputeMAC(type, data, mac, ctx->readCipher.sequenceNum, secret, ctx->readCipher.hash, ctx)) != 0)
return ERR(err);
if ((memcmp(mac.data, compareMAC, mac.length)) != 0) {
errorLog0("VerifyMAC: Mac verify failure\n");
return ERR(SSLProtocolErr);
}
return SSLNoErr;
}
static SSLErr
ComputeMAC(UInt8 type, SSLBuffer data, SSLBuffer mac, sslUint64 seqNo, SSLBuffer secret,
const HashReference *macHash, SSLContext *ctx)
{ SSLErr err;
UInt8 innerDigestData[MAX_DIGEST_SIZE];
UInt8 scratchData[11], *progress;
SSLBuffer digest,digestCtx,scratch;
CASSERT(macHash->macPadSize <= MAX_MAC_PADDING);
CASSERT(macHash->digestSize <= MAX_DIGEST_SIZE);
CASSERT(SSLMACPad1[0] == 0x36 && SSLMACPad2[0] == 0x5C);
digestCtx.data = 0;
if ((err = SSLAllocBuffer(&digestCtx, macHash->contextSize, &ctx->sysCtx)) != 0)
goto exit;
if ((err = macHash->init(digestCtx)) != 0)
goto exit;
if ((err = macHash->update(digestCtx, secret)) != 0)
goto exit;
scratch.data = SSLMACPad1;
scratch.length = macHash->macPadSize;
if ((err = macHash->update(digestCtx, scratch)) != 0)
goto exit;
progress = scratchData;
progress = SSLEncodeUInt64(progress, seqNo);
*progress++ = type;
progress = SSLEncodeInt(progress, data.length, 2);
scratch.data = scratchData;
scratch.length = 11;
CASSERT(progress = scratchData+11);
if ((err = macHash->update(digestCtx, scratch)) != 0)
goto exit;
if ((err = macHash->update(digestCtx, data)) != 0)
goto exit;
digest.data = innerDigestData;
digest.length = macHash->digestSize;
if ((err = macHash->final(digestCtx, digest)) != 0)
goto exit;
if ((err = macHash->init(digestCtx)) != 0)
goto exit;
if ((err = macHash->update(digestCtx, secret)) != 0)
goto exit;
scratch.data = SSLMACPad2;
scratch.length = macHash->macPadSize;
if ((err = macHash->update(digestCtx, scratch)) != 0)
goto exit;
if ((err = macHash->update(digestCtx, digest)) != 0)
goto exit;
if ((err = macHash->final(digestCtx, mac)) != 0)
goto exit;
err = SSLNoErr;
exit:
SSLFreeBuffer(&digestCtx, &ctx->sysCtx);
return ERR(err);
}