TLS: Add preliminary support for partial message processing
Reassemble partial TLS records to make the internal TLS client implementation more convenient for stream sockets. Signed-hostap: Jouni Malinen <j@w1.fi>
This commit is contained in:
parent
613522a40a
commit
dbdcfa3979
5 changed files with 151 additions and 61 deletions
|
@ -347,6 +347,12 @@ struct wpabuf * tls_connection_handshake(void *tls_ctx,
|
|||
const struct wpabuf *in_data,
|
||||
struct wpabuf **appl_data);
|
||||
|
||||
struct wpabuf * tls_connection_handshake2(void *tls_ctx,
|
||||
struct tls_connection *conn,
|
||||
const struct wpabuf *in_data,
|
||||
struct wpabuf **appl_data,
|
||||
int *more_data_needed);
|
||||
|
||||
/**
|
||||
* tls_connection_server_handshake - Process TLS handshake (server side)
|
||||
* @tls_ctx: TLS context data from tls_init()
|
||||
|
@ -392,6 +398,11 @@ struct wpabuf * tls_connection_decrypt(void *tls_ctx,
|
|||
struct tls_connection *conn,
|
||||
const struct wpabuf *in_data);
|
||||
|
||||
struct wpabuf * tls_connection_decrypt2(void *tls_ctx,
|
||||
struct tls_connection *conn,
|
||||
const struct wpabuf *in_data,
|
||||
int *more_data_needed);
|
||||
|
||||
/**
|
||||
* tls_connection_resumed - Was session resumption used
|
||||
* @tls_ctx: TLS context data from tls_init()
|
||||
|
|
|
@ -331,6 +331,17 @@ struct wpabuf * tls_connection_handshake(void *tls_ctx,
|
|||
struct tls_connection *conn,
|
||||
const struct wpabuf *in_data,
|
||||
struct wpabuf **appl_data)
|
||||
{
|
||||
return tls_connection_handshake2(tls_ctx, conn, in_data, appl_data,
|
||||
NULL);
|
||||
}
|
||||
|
||||
|
||||
struct wpabuf * tls_connection_handshake2(void *tls_ctx,
|
||||
struct tls_connection *conn,
|
||||
const struct wpabuf *in_data,
|
||||
struct wpabuf **appl_data,
|
||||
int *need_more_data)
|
||||
{
|
||||
#ifdef CONFIG_TLS_INTERNAL_CLIENT
|
||||
u8 *res, *ad;
|
||||
|
@ -344,7 +355,7 @@ struct wpabuf * tls_connection_handshake(void *tls_ctx,
|
|||
res = tlsv1_client_handshake(conn->client,
|
||||
in_data ? wpabuf_head(in_data) : NULL,
|
||||
in_data ? wpabuf_len(in_data) : 0,
|
||||
&res_len, &ad, &ad_len);
|
||||
&res_len, &ad, &ad_len, need_more_data);
|
||||
if (res == NULL)
|
||||
return NULL;
|
||||
out = wpabuf_alloc_ext_data(res, res_len);
|
||||
|
@ -455,23 +466,23 @@ struct wpabuf * tls_connection_decrypt(void *tls_ctx,
|
|||
struct tls_connection *conn,
|
||||
const struct wpabuf *in_data)
|
||||
{
|
||||
return tls_connection_decrypt2(tls_ctx, conn, in_data, NULL);
|
||||
}
|
||||
|
||||
|
||||
struct wpabuf * tls_connection_decrypt2(void *tls_ctx,
|
||||
struct tls_connection *conn,
|
||||
const struct wpabuf *in_data,
|
||||
int *need_more_data)
|
||||
{
|
||||
if (need_more_data)
|
||||
*need_more_data = 0;
|
||||
|
||||
#ifdef CONFIG_TLS_INTERNAL_CLIENT
|
||||
if (conn->client) {
|
||||
struct wpabuf *buf;
|
||||
int res;
|
||||
buf = wpabuf_alloc((wpabuf_len(in_data) + 500) * 3);
|
||||
if (buf == NULL)
|
||||
return NULL;
|
||||
res = tlsv1_client_decrypt(conn->client, wpabuf_head(in_data),
|
||||
return tlsv1_client_decrypt(conn->client, wpabuf_head(in_data),
|
||||
wpabuf_len(in_data),
|
||||
wpabuf_mhead(buf),
|
||||
wpabuf_size(buf));
|
||||
if (res < 0) {
|
||||
wpabuf_free(buf);
|
||||
return NULL;
|
||||
}
|
||||
wpabuf_put(buf, res);
|
||||
return buf;
|
||||
need_more_data);
|
||||
}
|
||||
#endif /* CONFIG_TLS_INTERNAL_CLIENT */
|
||||
#ifdef CONFIG_TLS_INTERNAL_SERVER
|
||||
|
|
|
@ -136,25 +136,43 @@ int tls_derive_keys(struct tlsv1_client *conn,
|
|||
* @out_len: Length of the output buffer.
|
||||
* @appl_data: Pointer to application data pointer, or %NULL if dropped
|
||||
* @appl_data_len: Pointer to variable that is set to appl_data length
|
||||
* @need_more_data: Set to 1 if more data would be needed to complete
|
||||
* processing
|
||||
* Returns: Pointer to output data, %NULL on failure
|
||||
*/
|
||||
u8 * tlsv1_client_handshake(struct tlsv1_client *conn,
|
||||
const u8 *in_data, size_t in_len,
|
||||
size_t *out_len, u8 **appl_data,
|
||||
size_t *appl_data_len)
|
||||
size_t *appl_data_len, int *need_more_data)
|
||||
{
|
||||
const u8 *pos, *end;
|
||||
u8 *msg = NULL, *in_msg, *in_pos, *in_end, alert, ct;
|
||||
u8 *msg = NULL, *in_msg = NULL, *in_pos, *in_end, alert, ct;
|
||||
size_t in_msg_len;
|
||||
int no_appl_data;
|
||||
int used;
|
||||
|
||||
if (need_more_data)
|
||||
*need_more_data = 0;
|
||||
|
||||
if (conn->state == CLIENT_HELLO) {
|
||||
if (in_len)
|
||||
return NULL;
|
||||
return tls_send_client_hello(conn, out_len);
|
||||
}
|
||||
|
||||
if (conn->partial_input) {
|
||||
if (wpabuf_resize(&conn->partial_input, in_len) < 0) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate "
|
||||
"memory for pending record");
|
||||
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
|
||||
TLS_ALERT_INTERNAL_ERROR);
|
||||
goto failed;
|
||||
}
|
||||
wpabuf_put_data(conn->partial_input, in_data, in_len);
|
||||
in_data = wpabuf_head(conn->partial_input);
|
||||
in_len = wpabuf_len(conn->partial_input);
|
||||
}
|
||||
|
||||
if (in_data == NULL || in_len == 0)
|
||||
return NULL;
|
||||
|
||||
|
@ -176,12 +194,24 @@ u8 * tlsv1_client_handshake(struct tlsv1_client *conn,
|
|||
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);
|
||||
struct wpabuf *partial;
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Need more data");
|
||||
os_free(in_msg);
|
||||
partial = wpabuf_alloc_copy(pos, end - pos);
|
||||
wpabuf_free(conn->partial_input);
|
||||
conn->partial_input = partial;
|
||||
if (conn->partial_input == NULL) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Failed to "
|
||||
"allocate memory for pending "
|
||||
"record");
|
||||
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
|
||||
TLS_ALERT_INTERNAL_ERROR);
|
||||
goto failed;
|
||||
}
|
||||
if (need_more_data)
|
||||
*need_more_data = 1;
|
||||
return 0;
|
||||
}
|
||||
ct = pos[0];
|
||||
|
||||
in_pos = in_msg;
|
||||
|
@ -211,6 +241,8 @@ u8 * tlsv1_client_handshake(struct tlsv1_client *conn,
|
|||
failed:
|
||||
os_free(in_msg);
|
||||
if (conn->alert_level) {
|
||||
wpabuf_free(conn->partial_input);
|
||||
conn->partial_input = NULL;
|
||||
conn->state = FAILED;
|
||||
os_free(msg);
|
||||
msg = tlsv1_client_send_alert(conn, conn->alert_level,
|
||||
|
@ -221,6 +253,11 @@ failed:
|
|||
*out_len = 0;
|
||||
}
|
||||
|
||||
if (need_more_data == NULL || !(*need_more_data)) {
|
||||
wpabuf_free(conn->partial_input);
|
||||
conn->partial_input = NULL;
|
||||
}
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
|
@ -263,53 +300,80 @@ int tlsv1_client_encrypt(struct tlsv1_client *conn,
|
|||
* @conn: TLSv1 client connection data from tlsv1_client_init()
|
||||
* @in_data: Pointer to input buffer (encrypted TLS data)
|
||||
* @in_len: Input buffer length
|
||||
* @out_data: Pointer to output buffer (decrypted data from TLS tunnel)
|
||||
* @out_len: Maximum out_data length
|
||||
* Returns: Number of bytes written to out_data, -1 on failure
|
||||
* @need_more_data: Set to 1 if more data would be needed to complete
|
||||
* processing
|
||||
* Returns: Decrypted data or %NULL on failure
|
||||
*
|
||||
* This function is used after TLS handshake has been completed successfully to
|
||||
* receive data from the encrypted tunnel.
|
||||
*/
|
||||
int tlsv1_client_decrypt(struct tlsv1_client *conn,
|
||||
struct wpabuf * tlsv1_client_decrypt(struct tlsv1_client *conn,
|
||||
const u8 *in_data, size_t in_len,
|
||||
u8 *out_data, size_t out_len)
|
||||
int *need_more_data)
|
||||
{
|
||||
const u8 *in_end, *pos;
|
||||
int used;
|
||||
u8 alert, *out_end, *out_pos, ct;
|
||||
u8 alert, *out_pos, ct;
|
||||
size_t olen;
|
||||
struct wpabuf *buf = NULL;
|
||||
|
||||
if (need_more_data)
|
||||
*need_more_data = 0;
|
||||
|
||||
if (conn->partial_input) {
|
||||
if (wpabuf_resize(&conn->partial_input, in_len) < 0) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate "
|
||||
"memory for pending record");
|
||||
alert = TLS_ALERT_INTERNAL_ERROR;
|
||||
goto fail;
|
||||
}
|
||||
wpabuf_put_data(conn->partial_input, in_data, in_len);
|
||||
in_data = wpabuf_head(conn->partial_input);
|
||||
in_len = wpabuf_len(conn->partial_input);
|
||||
}
|
||||
|
||||
pos = in_data;
|
||||
in_end = in_data + in_len;
|
||||
out_pos = out_data;
|
||||
out_end = out_data + out_len;
|
||||
|
||||
while (pos < in_end) {
|
||||
ct = pos[0];
|
||||
olen = out_end - out_pos;
|
||||
if (wpabuf_resize(&buf, in_end - pos) < 0) {
|
||||
alert = TLS_ALERT_INTERNAL_ERROR;
|
||||
goto fail;
|
||||
}
|
||||
out_pos = wpabuf_put(buf, 0);
|
||||
olen = wpabuf_tailroom(buf);
|
||||
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;
|
||||
goto fail;
|
||||
}
|
||||
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;
|
||||
struct wpabuf *partial;
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Need more data");
|
||||
partial = wpabuf_alloc_copy(pos, in_end - pos);
|
||||
wpabuf_free(conn->partial_input);
|
||||
conn->partial_input = partial;
|
||||
if (conn->partial_input == NULL) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Failed to "
|
||||
"allocate memory for pending "
|
||||
"record");
|
||||
alert = TLS_ALERT_INTERNAL_ERROR;
|
||||
goto fail;
|
||||
}
|
||||
if (need_more_data)
|
||||
*need_more_data = 1;
|
||||
return buf;
|
||||
}
|
||||
|
||||
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;
|
||||
alert = TLS_ALERT_DECODE_ERROR;
|
||||
goto fail;
|
||||
}
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Received alert %d:%d",
|
||||
out_pos[0], out_pos[1]);
|
||||
|
@ -319,32 +383,33 @@ int tlsv1_client_decrypt(struct tlsv1_client *conn,
|
|||
continue;
|
||||
}
|
||||
|
||||
tls_alert(conn, TLS_ALERT_LEVEL_FATAL, out_pos[1]);
|
||||
return -1;
|
||||
alert = out_pos[1];
|
||||
goto fail;
|
||||
}
|
||||
|
||||
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;
|
||||
alert = TLS_ALERT_UNEXPECTED_MESSAGE;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
out_pos += olen;
|
||||
if (out_pos > out_end) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Buffer not large enough "
|
||||
"for processing the received record");
|
||||
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
|
||||
TLS_ALERT_INTERNAL_ERROR);
|
||||
return -1;
|
||||
}
|
||||
wpabuf_put(buf, olen);
|
||||
|
||||
pos += used;
|
||||
}
|
||||
|
||||
return out_pos - out_data;
|
||||
wpabuf_free(conn->partial_input);
|
||||
conn->partial_input = NULL;
|
||||
return buf;
|
||||
|
||||
fail:
|
||||
wpabuf_free(buf);
|
||||
wpabuf_free(conn->partial_input);
|
||||
conn->partial_input = NULL;
|
||||
tls_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
@ -427,6 +492,7 @@ void tlsv1_client_deinit(struct tlsv1_client *conn)
|
|||
os_free(conn->client_hello_ext);
|
||||
tlsv1_client_free_dh(conn);
|
||||
tlsv1_cred_free(conn->cred);
|
||||
wpabuf_free(conn->partial_input);
|
||||
os_free(conn);
|
||||
}
|
||||
|
||||
|
|
|
@ -29,13 +29,13 @@ int tlsv1_client_prf(struct tlsv1_client *conn, const char *label,
|
|||
u8 * tlsv1_client_handshake(struct tlsv1_client *conn,
|
||||
const u8 *in_data, size_t in_len,
|
||||
size_t *out_len, u8 **appl_data,
|
||||
size_t *appl_data_len);
|
||||
size_t *appl_data_len, int *need_more_data);
|
||||
int tlsv1_client_encrypt(struct tlsv1_client *conn,
|
||||
const u8 *in_data, size_t in_len,
|
||||
u8 *out_data, size_t out_len);
|
||||
int tlsv1_client_decrypt(struct tlsv1_client *conn,
|
||||
struct wpabuf * tlsv1_client_decrypt(struct tlsv1_client *conn,
|
||||
const u8 *in_data, size_t in_len,
|
||||
u8 *out_data, size_t out_len);
|
||||
int *need_more_data);
|
||||
int tlsv1_client_get_cipher(struct tlsv1_client *conn, char *buf,
|
||||
size_t buflen);
|
||||
int tlsv1_client_shutdown(struct tlsv1_client *conn);
|
||||
|
|
|
@ -68,6 +68,8 @@ struct tlsv1_client {
|
|||
|
||||
tlsv1_client_session_ticket_cb session_ticket_cb;
|
||||
void *session_ticket_cb_ctx;
|
||||
|
||||
struct wpabuf *partial_input;
|
||||
};
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue