Implemented TLS Alert handling

This commit is contained in:
Martin Willi
2010-08-23 15:13:37 +02:00
parent 908e752201
commit e6f3ef1330
15 changed files with 643 additions and 80 deletions
+115 -9
View File
@@ -21,6 +21,18 @@
typedef struct private_tls_fragmentation_t private_tls_fragmentation_t;
/**
* Alert state
*/
typedef enum {
/* no alert received/sent */
ALERT_NONE,
/* currently sending an alert */
ALERT_SENDING,
/* alert sent and out */
ALERT_SENT,
} alert_state_t;
/**
* Private data of an tls_fragmentation_t object.
*/
@@ -36,6 +48,16 @@ struct private_tls_fragmentation_t {
*/
tls_handshake_t *handshake;
/**
* TLS alert handler
*/
tls_alert_t *alert;
/**
* State of alert handling
*/
alert_state_t state;
/**
* Handshake input buffer
*/
@@ -72,6 +94,23 @@ struct private_tls_fragmentation_t {
*/
#define MAX_TLS_HANDSHAKE_LEN 65536
/**
* Process a TLS alert
*/
static status_t process_alert(private_tls_fragmentation_t *this,
tls_reader_t *reader)
{
u_int8_t level, description;
if (!reader->read_uint8(reader, &level) ||
!reader->read_uint8(reader, &description))
{
this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR);
return NEED_MORE;
}
return this->alert->process(this->alert, level, description);
}
/**
* Process TLS handshake protocol data
*/
@@ -89,7 +128,8 @@ static status_t process_handshake(private_tls_fragmentation_t *this,
if (reader->remaining(reader) > MAX_TLS_FRAGMENT_LEN)
{
DBG1(DBG_TLS, "TLS fragment has invalid length");
return FAILED;
this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR);
return NEED_MORE;
}
if (this->input.len == 0)
@@ -97,13 +137,16 @@ static status_t process_handshake(private_tls_fragmentation_t *this,
if (!reader->read_uint8(reader, &type) ||
!reader->read_uint24(reader, &len))
{
return FAILED;
DBG1(DBG_TLS, "TLS handshake header invalid");
this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR);
return NEED_MORE;
}
this->type = type;
if (len > MAX_TLS_HANDSHAKE_LEN)
{
DBG1(DBG_TLS, "TLS handshake message exceeds maximum length");
return FAILED;
this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR);
return NEED_MORE;
}
chunk_free(&this->input);
this->inpos = 0;
@@ -116,7 +159,9 @@ static status_t process_handshake(private_tls_fragmentation_t *this,
len = min(this->input.len - this->inpos, reader->remaining(reader));
if (!reader->read_data(reader, len, &data))
{
return FAILED;
DBG1(DBG_TLS, "TLS fragment has invalid length");
this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR);
return NEED_MORE;
}
memcpy(this->input.ptr + this->inpos, data.ptr, len);
this->inpos += len;
@@ -151,12 +196,14 @@ static status_t process_application(private_tls_fragmentation_t *this,
if (reader->remaining(reader) > MAX_TLS_FRAGMENT_LEN)
{
DBG1(DBG_TLS, "TLS fragment has invalid length");
return FAILED;
this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR);
return NEED_MORE;
}
status = this->application->process(this->application, reader);
if (status != NEED_MORE)
{
return status;
this->alert->add(this->alert, TLS_FATAL, TLS_CLOSE_NOTIFY);
return NEED_MORE;
}
}
return NEED_MORE;
@@ -168,6 +215,15 @@ METHOD(tls_fragmentation_t, process, status_t,
tls_reader_t *reader;
status_t status;
switch (this->state)
{
case ALERT_SENDING:
case ALERT_SENT:
/* don't accept more input, fatal error ocurred */
return NEED_MORE;
case ALERT_NONE:
break;
}
reader = tls_reader_create(data);
switch (type)
{
@@ -180,8 +236,7 @@ METHOD(tls_fragmentation_t, process, status_t,
status = FAILED;
break;
case TLS_ALERT:
/* TODO: handle Alert */
status = FAILED;
status = process_alert(this, reader);
break;
case TLS_HANDSHAKE:
status = process_handshake(this, reader);
@@ -198,6 +253,29 @@ METHOD(tls_fragmentation_t, process, status_t,
return status;
}
/**
* Check if alerts are pending
*/
static bool check_alerts(private_tls_fragmentation_t *this, chunk_t *data)
{
tls_alert_level_t level;
tls_alert_desc_t desc;
tls_writer_t *writer;
if (this->alert->get(this->alert, &level, &desc))
{
writer = tls_writer_create(2);
writer->write_uint8(writer, level);
writer->write_uint8(writer, desc);
*data = chunk_clone(writer->get_buf(writer));
writer->destroy(writer);
return TRUE;
}
return FALSE;
}
METHOD(tls_fragmentation_t, build, status_t,
private_tls_fragmentation_t *this, tls_content_type_t *type, chunk_t *data)
{
@@ -206,6 +284,22 @@ METHOD(tls_fragmentation_t, build, status_t,
tls_writer_t *writer, *msg;
status_t status = INVALID_STATE;
switch (this->state)
{
case ALERT_SENDING:
this->state = ALERT_SENT;
return INVALID_STATE;
case ALERT_SENT:
return FAILED;
case ALERT_NONE:
break;
}
if (check_alerts(this, data))
{
this->state = ALERT_SENDING;
*type = TLS_ALERT;
return NEED_MORE;
}
if (this->handshake->cipherspec_changed(this->handshake))
{
*type = TLS_CHANGE_CIPHER_SPEC;
@@ -227,6 +321,16 @@ METHOD(tls_fragmentation_t, build, status_t,
*type = TLS_APPLICATION_DATA;
this->output = chunk_clone(msg->get_buf(msg));
}
else if (status != NEED_MORE)
{
this->alert->add(this->alert, TLS_FATAL, TLS_CLOSE_NOTIFY);
if (check_alerts(this, data))
{
this->state = ALERT_SENDING;
*type = TLS_ALERT;
return NEED_MORE;
}
}
}
}
else
@@ -290,7 +394,7 @@ METHOD(tls_fragmentation_t, destroy, void,
* See header
*/
tls_fragmentation_t *tls_fragmentation_create(tls_handshake_t *handshake,
tls_application_t *application)
tls_alert_t *alert, tls_application_t *application)
{
private_tls_fragmentation_t *this;
@@ -301,6 +405,8 @@ tls_fragmentation_t *tls_fragmentation_create(tls_handshake_t *handshake,
.destroy = _destroy,
},
.handshake = handshake,
.alert = alert,
.state = ALERT_NONE,
.application = application,
);