#include "ssl.h"
#include "sslMemory.h"
#include "sslContext.h"
#include "sslRecord.h"
#include "sslAlertMessage.h"
#include "sslSession.h"
#include "sslDebug.h"
#include "sslCipherSpecs.h"
#include "sslUtils.h"
#include <assert.h>
#include <string.h>
#include <utilities/SecIOFormat.h>
#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
extern int kSplitDefaultValue;
static OSStatus SSLProcessProtocolMessage(SSLRecord *rec, SSLContext *ctx);
static OSStatus SSLHandshakeProceed(SSLContext *ctx);
OSStatus
SSLWrite(
SSLContext *ctx,
const void * data,
size_t dataLength,
size_t *bytesWritten)
{
OSStatus err;
SSLRecord rec;
size_t dataLen, processed;
sslLogRecordIo("[%p] SSLWrite top", ctx);
if((ctx == NULL) || (bytesWritten == NULL)) {
return errSecParam;
}
dataLen = dataLength;
processed = 0;
*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:
sslIoTrace(ctx, "SSLWrite(1)", dataLength, 0, errSecBadReq);
return errSecBadReq;
default:
break;
}
err = errSecSuccess;
while (!(ctx->writeCipher_ready))
{ if ((err = SSLHandshakeProceed(ctx)) != 0)
goto exit;
}
if ((err = SSLServiceWriteQueue(ctx)) != 0)
goto abort;
processed = 0;
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;
}
*bytesWritten = processed;
if ((err = SSLServiceWriteQueue(ctx)) == 0) {
err = errSecSuccess;
}
exit:
switch(err) {
case errSecSuccess:
case errSSLWouldBlock:
case errSSLUnexpectedRecord:
case errSSLServerAuthCompleted:
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)
{
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;
readRetry:
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;
}
err = errSecSuccess;
while (ctx->readCipher_ready == 0) {
if ((err = SSLHandshakeProceed(ctx)) != 0) {
goto exit;
}
}
if ((err = SSLServiceWriteQueue(ctx)) != 0) {
if (err != errSSLWouldBlock) {
goto exit;
}
err = errSecSuccess;
}
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;
}
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) {
if(err == errSSLClosedGraceful) {
err = SSLClose(ctx);
} else {
goto exit;
}
}
if ((err = SSLFreeRecord(rec, ctx))) {
goto exit;
}
}
}
err = errSecSuccess;
exit:
if(((err == errSecSuccess) && (*processed == 0) && dataLength) || (err == errSSLUnexpectedRecord)) {
sslLogNegotiateDebug("SSLRead recursion");
goto readRetry;
}
switch(err) {
case errSecSuccess:
case errSSLWouldBlock:
case errSSLUnexpectedRecord:
case errSSLServerAuthCompleted:
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) {
err = sslBuildCipherSuiteArray(ctx);
if(err) {
return err;
}
}
err = errSecSuccess;
if(ctx->isDTLS && ctx->timeout_deadline) {
CFAbsoluteTime current = CFAbsoluteTimeGetCurrent();
if (ctx->timeout_deadline<current) {
sslDebugLog("%p, retransmition deadline expired\n", ctx);
err = tls_handshake_retransmit_timer_expired(ctx->hdsk);
if(err) {
return err;
}
}
}
while (ctx->readCipher_ready == 0 || ctx->writeCipher_ready == 0)
{
err = SSLHandshakeProceed(ctx);
if((err != 0) && (err != errSSLUnexpectedRecord))
return err;
}
if ((err = SSLServiceWriteQueue(ctx)) != 0) {
return err;
}
return errSecSuccess;
}
static OSStatus
SSLHandshakeProceed(SSLContext *ctx)
{
OSStatus err;
if(ctx->state==SSL_HdskStateUninit) {
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);
}
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;
return err;
}
OSStatus
SSLGetBufferedReadSize(SSLContextRef ctx,
size_t *bufSize)
{
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;
}