Support output fragmentation of TLS records

This commit is contained in:
Martin Willi
2010-08-31 15:54:37 +02:00
parent f13a03add0
commit ecd98efa9d
5 changed files with 123 additions and 52 deletions
+3 -2
View File
@@ -251,7 +251,7 @@ static status_t process_buf(private_eap_tls_t *this)
{ {
status_t status; status_t status;
status = this->tls->process(this->tls, this->input); status = this->tls->process(this->tls, this->input.ptr, this->input.len);
if (status != NEED_MORE) if (status != NEED_MORE)
{ {
return status; return status;
@@ -260,7 +260,8 @@ static status_t process_buf(private_eap_tls_t *this)
this->inpos = 0; this->inpos = 0;
chunk_free(&this->output); chunk_free(&this->output);
return this->tls->build(this->tls, &this->output); this->output = chunk_alloc(EAP_TLS_FRAGMENT_LEN);
return this->tls->build(this->tls, this->output.ptr, &this->output.len, NULL);
} }
METHOD(eap_method_t, process, status_t, METHOD(eap_method_t, process, status_t,
+3 -2
View File
@@ -254,7 +254,7 @@ static status_t process_buf(private_eap_ttls_t *this)
{ {
status_t status; status_t status;
status = this->tls->process(this->tls, this->input); status = this->tls->process(this->tls, this->input.ptr, this->input.len);
if (status != NEED_MORE) if (status != NEED_MORE)
{ {
return status; return status;
@@ -263,7 +263,8 @@ static status_t process_buf(private_eap_ttls_t *this)
this->inpos = 0; this->inpos = 0;
chunk_free(&this->output); chunk_free(&this->output);
return this->tls->build(this->tls, &this->output); this->output = chunk_alloc(EAP_TTLS_FRAGMENT_LEN);
return this->tls->build(this->tls, this->output.ptr, &this->output.len, NULL);
} }
METHOD(eap_method_t, process, status_t, METHOD(eap_method_t, process, status_t,
+81 -30
View File
@@ -137,6 +137,16 @@ struct private_tls_t {
* Number of bytes read in input buffer * Number of bytes read in input buffer
*/ */
size_t inpos; size_t inpos;
/**
* Allocated output buffer
*/
chunk_t output;
/**
* Number of bytes processed from output buffer
*/
size_t outpos;
}; };
/** /**
@@ -150,23 +160,27 @@ typedef struct __attribute__((packed)) {
} tls_record_t; } tls_record_t;
METHOD(tls_t, process, status_t, METHOD(tls_t, process, status_t,
private_tls_t *this, chunk_t data) private_tls_t *this, void *buf, size_t buflen)
{ {
tls_record_t *record; tls_record_t *record;
status_t status; status_t status;
u_int len; u_int len;
while (data.len > sizeof(tls_record_t)) while (buflen)
{ {
if (this->input.len == 0) if (this->input.len == 0)
{ {
if (buflen < sizeof(tls_record_t))
{
break;
}
while (TRUE) while (TRUE)
{ {
/* try to process records inline */ /* try to process records inline */
record = (tls_record_t*)data.ptr; record = buf;
len = untoh16(&record->length); len = untoh16(&record->length);
if (len + sizeof(tls_record_t) > data.len) if (len + sizeof(tls_record_t) > buflen)
{ /* not a full record, read to buffer */ { /* not a full record, read to buffer */
this->input = chunk_alloc(len + sizeof(tls_record_t)); this->input = chunk_alloc(len + sizeof(tls_record_t));
this->inpos = 0; this->inpos = 0;
@@ -180,16 +194,18 @@ METHOD(tls_t, process, status_t,
{ {
return status; return status;
} }
data = chunk_skip(data, len + sizeof(tls_record_t)); buf += len + sizeof(tls_record_t);
if (data.len == 0) buflen -= len + sizeof(tls_record_t);
if (buflen == 0)
{ {
return NEED_MORE; return NEED_MORE;
} }
} }
} }
len = min(data.len, this->input.len - this->inpos); len = min(buflen, this->input.len - this->inpos);
memcpy(this->input.ptr + this->inpos, data.ptr, len); memcpy(this->input.ptr + this->inpos, buf, len);
data = chunk_skip(data, len); buf += len;
buflen -= len;
this->inpos += len; this->inpos += len;
DBG2(DBG_TLS, "buffering %d bytes, %d bytes of %d byte record received", DBG2(DBG_TLS, "buffering %d bytes, %d bytes of %d byte record received",
len, this->inpos, this->input.len); len, this->inpos, this->input.len);
@@ -210,7 +226,7 @@ METHOD(tls_t, process, status_t,
} }
} }
} }
if (data.len != 0) if (buflen != 0)
{ {
DBG1(DBG_TLS, "received incomplete TLS record header"); DBG1(DBG_TLS, "received incomplete TLS record header");
return FAILED; return FAILED;
@@ -219,34 +235,68 @@ METHOD(tls_t, process, status_t,
} }
METHOD(tls_t, build, status_t, METHOD(tls_t, build, status_t,
private_tls_t *this, chunk_t *data) private_tls_t *this, void *buf, size_t *buflen, size_t *msglen)
{ {
tls_content_type_t type;
tls_record_t record; tls_record_t record;
status_t status; status_t status;
chunk_t data;
size_t len;
*data = chunk_empty; len = *buflen;
while (TRUE) if (this->output.len == 0)
{ {
tls_content_type_t type; /* query upper layers for new records, as many as we can get */
chunk_t body; while (TRUE)
status = this->protection->build(this->protection, &type, &body);
switch (status)
{ {
case INVALID_STATE: status = this->protection->build(this->protection, &type, &data);
return NEED_MORE; switch (status)
case NEED_MORE: {
break; case NEED_MORE:
default: record.type = type;
return status; htoun16(&record.version, this->version);
htoun16(&record.length, data.len);
this->output = chunk_cat("mcm", this->output,
chunk_from_thing(record), data);
DBG2(DBG_TLS, "sending TLS %N record (%d bytes)",
tls_content_type_names, type, data.len);
continue;
case INVALID_STATE:
if (this->output.len == 0)
{
return INVALID_STATE;
}
break;
default:
return status;
}
break;
}
if (msglen)
{
*msglen = this->output.len;
} }
record.type = type;
htoun16(&record.version, this->version);
htoun16(&record.length, body.len);
*data = chunk_cat("mcm", *data, chunk_from_thing(record), body);
DBG2(DBG_TLS, "sending TLS %N record (%u bytes)",
tls_content_type_names, type, sizeof(tls_record_t) + body.len);
} }
else
{
DBG2(DBG_TLS, "sending %d bytes buffered fragment",
min(*buflen, this->output.len - this->outpos));
if (msglen)
{
*msglen = 0;
}
}
len = min(len, this->output.len - this->outpos);
memcpy(buf, this->output.ptr + this->outpos, len);
this->outpos += len;
*buflen = len;
if (this->outpos == this->output.len)
{
chunk_free(&this->output);
this->outpos = 0;
return ALREADY_DONE;
}
return NEED_MORE;
} }
METHOD(tls_t, is_server, bool, METHOD(tls_t, is_server, bool,
@@ -323,6 +373,7 @@ METHOD(tls_t, destroy, void,
this->alert->destroy(this->alert); this->alert->destroy(this->alert);
free(this->input.ptr); free(this->input.ptr);
free(this->output.ptr);
free(this); free(this);
} }
+18 -6
View File
@@ -108,24 +108,36 @@ struct tls_t {
/** /**
* Process one or more TLS records, pass it to upper layers. * Process one or more TLS records, pass it to upper layers.
* *
* @param data TLS record data, including headers * @param buf TLS record data, including headers
* @param buflen number of bytes in buf to process
* @return * @return
* - SUCCESS if TLS negotiation complete * - SUCCESS if TLS negotiation complete
* - FAILED if TLS handshake failed * - FAILED if TLS handshake failed
* - NEED_MORE if more invocations to process/build needed * - NEED_MORE if more invocations to process/build needed
*/ */
status_t (*process)(tls_t *this, chunk_t data); status_t (*process)(tls_t *this, void *buf, size_t buflen);
/** /**
* Query upper layer for TLS record, build protected record. * Query upper layer for one or more TLS records, build fragments.
* *
* @param data allocated data of the built TLS record * The TLS stack automatically fragments the records to the given buffer
* size. Fragmentation is indicated by the reclen ouput parameter and
* the return value. For the first fragment of a TLS record, a non-zero
* record length is returned in reclen. If more fragments follow, NEED_MORE
* is returned. A return value of ALREADY_DONE indicates that the final
* fragment has been returned.
*
* @param buf buffer to write TLS record fragments to
* @param buflen size of buffer, receives bytes written
* @param msglen receives size of all TLS fragments
* @return * @return
* - SUCCESS if TLS negotiation complete * - SUCCESS if TLS negotiation complete
* - FAILED if TLS handshake failed * - FAILED if TLS handshake failed
* - NEED_MORE if more input records required * - INVALID_STATE if more input data required
* - NEED_MORE if more fragments available
* - ALREADY_DONE if the last available fragment returned
*/ */
status_t (*build)(tls_t *this, chunk_t *data); status_t (*build)(tls_t *this, void *buf, size_t *buflen, size_t *msglen);
/** /**
* Check if TLS stack is acting as a server. * Check if TLS stack is acting as a server.
+18 -12
View File
@@ -96,25 +96,31 @@ METHOD(tls_application_t, build, status_t,
*/ */
static bool exchange(private_tls_socket_t *this, bool wr) static bool exchange(private_tls_socket_t *this, bool wr)
{ {
chunk_t data; char buf[1024];
char buf[2048];
ssize_t len; ssize_t len;
int round = 0; int round = 0;
for (round = 0; TRUE; round++) for (round = 0; TRUE; round++)
{ {
if (this->tls->build(this->tls, &data) != NEED_MORE) while (TRUE)
{ {
return FALSE; len = sizeof(buf);
} switch (this->tls->build(this->tls, buf, &len, NULL))
if (data.len)
{
len = write(this->fd, data.ptr, data.len);
free(data.ptr);
if (len != data.len)
{ {
return FALSE; case NEED_MORE:
case ALREADY_DONE:
len = write(this->fd, buf, len);
if (len == -1)
{
return FALSE;
}
continue;
case INVALID_STATE:
break;
default:
return FALSE;
} }
break;
} }
if (wr) if (wr)
{ {
@@ -139,7 +145,7 @@ static bool exchange(private_tls_socket_t *this, bool wr)
{ {
return FALSE; return FALSE;
} }
if (this->tls->process(this->tls, chunk_create(buf, len)) != NEED_MORE) if (this->tls->process(this->tls, buf, len) != NEED_MORE)
{ {
return FALSE; return FALSE;
} }