TLS: Clean up TLS record layer processing

Return number of user input bytes from tlsv1_record_receive() to
move this detail into the proper record layer processing. In addition,
ignore unknown content types at record layer and allow processing to
continue after warning level TLS alerts to provide minimal workaround
for closure alerts.

Signed-hostap: Jouni Malinen <j@w1.fi>
This commit is contained in:
Jouni Malinen 2011-11-05 13:04:02 +02:00
parent edc95487aa
commit c4a3480826
3 changed files with 140 additions and 60 deletions

View file

@ -147,6 +147,7 @@ u8 * tlsv1_client_handshake(struct tlsv1_client *conn,
u8 *msg = NULL, *in_msg, *in_pos, *in_end, alert, ct;
size_t in_msg_len;
int no_appl_data;
int used;
if (conn->state == CLIENT_HELLO) {
if (in_len)
@ -166,13 +167,21 @@ u8 * tlsv1_client_handshake(struct tlsv1_client *conn,
/* Each received packet may include multiple records */
while (pos < end) {
in_msg_len = in_len;
if (tlsv1_record_receive(&conn->rl, pos, end - pos,
in_msg, &in_msg_len, &alert)) {
used = tlsv1_record_receive(&conn->rl, pos, end - pos,
in_msg, &in_msg_len, &alert);
if (used < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Processing received "
"record failed");
tls_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
goto failed;
}
if (used == 0) {
/* need more data */
wpa_printf(MSG_DEBUG, "TLSv1: Partial processing not "
"yet supported");
tls_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
goto failed;
}
ct = pos[0];
in_pos = in_msg;
@ -190,7 +199,7 @@ u8 * tlsv1_client_handshake(struct tlsv1_client *conn,
in_pos += in_msg_len;
}
pos += TLS_RECORD_HEADER_LEN + WPA_GET_BE16(pos + 3);
pos += used;
}
os_free(in_msg);
@ -266,8 +275,8 @@ int tlsv1_client_decrypt(struct tlsv1_client *conn,
u8 *out_data, size_t out_len)
{
const u8 *in_end, *pos;
int res;
u8 alert, *out_end, *out_pos;
int used;
u8 alert, *out_end, *out_pos, ct;
size_t olen;
pos = in_data;
@ -276,23 +285,53 @@ int tlsv1_client_decrypt(struct tlsv1_client *conn,
out_end = out_data + out_len;
while (pos < in_end) {
if (pos[0] != TLS_CONTENT_TYPE_APPLICATION_DATA) {
wpa_printf(MSG_DEBUG, "TLSv1: Unexpected content type "
"0x%x", pos[0]);
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_UNEXPECTED_MESSAGE);
return -1;
}
ct = pos[0];
olen = out_end - out_pos;
res = tlsv1_record_receive(&conn->rl, pos, in_end - pos,
out_pos, &olen, &alert);
if (res < 0) {
used = tlsv1_record_receive(&conn->rl, pos, in_end - pos,
out_pos, &olen, &alert);
if (used < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Record layer processing "
"failed");
tls_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
return -1;
}
if (used == 0) {
/* need more data */
wpa_printf(MSG_DEBUG, "TLSv1: Partial processing not "
"yet supported");
tls_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
return -1;
}
if (ct == TLS_CONTENT_TYPE_ALERT) {
if (olen < 2) {
wpa_printf(MSG_DEBUG, "TLSv1: Alert "
"underflow");
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_DECODE_ERROR);
return -1;
}
wpa_printf(MSG_DEBUG, "TLSv1: Received alert %d:%d",
out_pos[0], out_pos[1]);
if (out_pos[0] == TLS_ALERT_LEVEL_WARNING) {
/* Continue processing */
pos += used;
continue;
}
tls_alert(conn, TLS_ALERT_LEVEL_FATAL, out_pos[1]);
return -1;
}
if (ct != TLS_CONTENT_TYPE_APPLICATION_DATA) {
wpa_printf(MSG_DEBUG, "TLSv1: Unexpected content type "
"0x%x when decrypting application data",
pos[0]);
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_UNEXPECTED_MESSAGE);
return -1;
}
out_pos += olen;
if (out_pos > out_end) {
wpa_printf(MSG_DEBUG, "TLSv1: Buffer not large enough "
@ -302,7 +341,7 @@ int tlsv1_client_decrypt(struct tlsv1_client *conn,
return -1;
}
pos += TLS_RECORD_HEADER_LEN + WPA_GET_BE16(pos + 3);
pos += used;
}
return out_pos - out_data;

View file

@ -271,7 +271,8 @@ int tlsv1_record_send(struct tlsv1_record_layer *rl, u8 content_type, u8 *buf,
* @out_len: Set to maximum out_data length by caller; used to return the
* length of the used data
* @alert: Buffer for returning an alert value on failure
* Returns: 0 on success, -1 on failure
* Returns: Number of bytes used from in_data on success, 0 if record was not
* complete (more data needed), or -1 on failure
*
* This function decrypts the received message, verifies HMAC and TLS record
* layer header.
@ -285,30 +286,21 @@ int tlsv1_record_receive(struct tlsv1_record_layer *rl,
struct crypto_hash *hmac;
u8 len[2], hash[100];
int force_mac_error = 0;
wpa_hexdump(MSG_MSGDUMP, "TLSv1: Record Layer - Received",
in_data, in_len);
u8 ct;
if (in_len < TLS_RECORD_HEADER_LEN) {
wpa_printf(MSG_DEBUG, "TLSv1: Too short record (in_len=%lu)",
wpa_printf(MSG_DEBUG, "TLSv1: Too short record (in_len=%lu) - "
"need more data",
(unsigned long) in_len);
*alert = TLS_ALERT_DECODE_ERROR;
return -1;
wpa_hexdump(MSG_MSGDUMP, "TLSv1: Record Layer - Received",
in_data, in_len);
return 0;
}
ct = in_data[0];
rlen = WPA_GET_BE16(in_data + 3);
wpa_printf(MSG_DEBUG, "TLSv1: Received content type %d version %d.%d "
"length %d", in_data[0], in_data[1], in_data[2],
WPA_GET_BE16(in_data + 3));
if (in_data[0] != TLS_CONTENT_TYPE_HANDSHAKE &&
in_data[0] != TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC &&
in_data[0] != TLS_CONTENT_TYPE_ALERT &&
in_data[0] != TLS_CONTENT_TYPE_APPLICATION_DATA) {
wpa_printf(MSG_DEBUG, "TLSv1: Unexpected content type 0x%x",
in_data[0]);
*alert = TLS_ALERT_UNEXPECTED_MESSAGE;
return -1;
}
"length %d", ct, in_data[1], in_data[2], (int) rlen);
/*
* TLS v1.0 and v1.1 RFCs were not exactly clear on the use of the
@ -322,8 +314,6 @@ int tlsv1_record_receive(struct tlsv1_record_layer *rl,
return -1;
}
rlen = WPA_GET_BE16(in_data + 3);
/* TLSCiphertext must not be more than 2^14+2048 bytes */
if (TLS_RECORD_HEADER_LEN + rlen > 18432) {
wpa_printf(MSG_DEBUG, "TLSv1: Record overflow (len=%lu)",
@ -339,7 +329,19 @@ int tlsv1_record_receive(struct tlsv1_record_layer *rl,
wpa_printf(MSG_DEBUG, "TLSv1: Not all record data included "
"(rlen=%lu > in_len=%lu)",
(unsigned long) rlen, (unsigned long) in_len);
*alert = TLS_ALERT_DECODE_ERROR;
return 0;
}
wpa_hexdump(MSG_MSGDUMP, "TLSv1: Record Layer - Received",
in_data, rlen);
if (ct != TLS_CONTENT_TYPE_HANDSHAKE &&
ct != TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC &&
ct != TLS_CONTENT_TYPE_ALERT &&
ct != TLS_CONTENT_TYPE_APPLICATION_DATA) {
wpa_printf(MSG_DEBUG, "TLSv1: Ignore record with unknown "
"content type 0x%x", ct);
*alert = TLS_ALERT_UNEXPECTED_MESSAGE;
return -1;
}
@ -417,13 +419,13 @@ int tlsv1_record_receive(struct tlsv1_record_layer *rl,
}
plen -= padlen + 1;
wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Record Layer - "
"Decrypted data with IV and padding "
"removed", out_data, plen);
}
check_mac:
wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Record Layer - Decrypted "
"data with IV and padding removed",
out_data, plen);
if (plen < rl->hash_size) {
wpa_printf(MSG_DEBUG, "TLSv1: Too short record; no "
"hash value");
@ -481,5 +483,5 @@ int tlsv1_record_receive(struct tlsv1_record_layer *rl,
inc_byte_array(rl->read_seq_num, TLS_SEQ_NUM_LEN);
return 0;
return TLS_RECORD_HEADER_LEN + rlen;
}

View file

@ -115,6 +115,7 @@ u8 * tlsv1_server_handshake(struct tlsv1_server *conn,
const u8 *pos, *end;
u8 *msg = NULL, *in_msg, *in_pos, *in_end, alert, ct;
size_t in_msg_len;
int used;
if (in_data == NULL || in_len == 0) {
wpa_printf(MSG_DEBUG, "TLSv1: No input data to server");
@ -130,13 +131,21 @@ u8 * tlsv1_server_handshake(struct tlsv1_server *conn,
/* Each received packet may include multiple records */
while (pos < end) {
in_msg_len = in_len;
if (tlsv1_record_receive(&conn->rl, pos, end - pos,
in_msg, &in_msg_len, &alert)) {
used = tlsv1_record_receive(&conn->rl, pos, end - pos,
in_msg, &in_msg_len, &alert);
if (used < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Processing received "
"record failed");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
goto failed;
}
if (used == 0) {
/* need more data */
wpa_printf(MSG_DEBUG, "TLSv1: Partial processing not "
"yet supported");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
goto failed;
}
ct = pos[0];
in_pos = in_msg;
@ -152,7 +161,7 @@ u8 * tlsv1_server_handshake(struct tlsv1_server *conn,
in_pos += in_msg_len;
}
pos += TLS_RECORD_HEADER_LEN + WPA_GET_BE16(pos + 3);
pos += used;
}
os_free(in_msg);
@ -230,8 +239,8 @@ int tlsv1_server_decrypt(struct tlsv1_server *conn,
u8 *out_data, size_t out_len)
{
const u8 *in_end, *pos;
int res;
u8 alert, *out_end, *out_pos;
int used;
u8 alert, *out_end, *out_pos, ct;
size_t olen;
pos = in_data;
@ -240,7 +249,46 @@ int tlsv1_server_decrypt(struct tlsv1_server *conn,
out_end = out_data + out_len;
while (pos < in_end) {
if (pos[0] != TLS_CONTENT_TYPE_APPLICATION_DATA) {
ct = pos[0];
olen = out_end - out_pos;
used = tlsv1_record_receive(&conn->rl, pos, in_end - pos,
out_pos, &olen, &alert);
if (used < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Record layer processing "
"failed");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
return -1;
}
if (used == 0) {
/* need more data */
wpa_printf(MSG_DEBUG, "TLSv1: Partial processing not "
"yet supported");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
return -1;
}
if (ct == TLS_CONTENT_TYPE_ALERT) {
if (olen < 2) {
wpa_printf(MSG_DEBUG, "TLSv1: Alert "
"underflow");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_DECODE_ERROR);
return -1;
}
wpa_printf(MSG_DEBUG, "TLSv1: Received alert %d:%d",
out_pos[0], out_pos[1]);
if (out_pos[0] == TLS_ALERT_LEVEL_WARNING) {
/* Continue processing */
pos += used;
continue;
}
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
out_pos[1]);
return -1;
}
if (ct != TLS_CONTENT_TYPE_APPLICATION_DATA) {
wpa_printf(MSG_DEBUG, "TLSv1: Unexpected content type "
"0x%x", pos[0]);
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
@ -248,15 +296,6 @@ int tlsv1_server_decrypt(struct tlsv1_server *conn,
return -1;
}
olen = out_end - out_pos;
res = tlsv1_record_receive(&conn->rl, pos, in_end - pos,
out_pos, &olen, &alert);
if (res < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Record layer processing "
"failed");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
return -1;
}
out_pos += olen;
if (out_pos > out_end) {
wpa_printf(MSG_DEBUG, "TLSv1: Buffer not large enough "
@ -266,7 +305,7 @@ int tlsv1_server_decrypt(struct tlsv1_server *conn,
return -1;
}
pos += TLS_RECORD_HEADER_LEN + WPA_GET_BE16(pos + 3);
pos += used;
}
return out_pos - out_data;