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,
|
const struct wpabuf *in_data,
|
||||||
struct wpabuf **appl_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_connection_server_handshake - Process TLS handshake (server side)
|
||||||
* @tls_ctx: TLS context data from tls_init()
|
* @tls_ctx: TLS context data from tls_init()
|
||||||
|
@ -392,6 +398,11 @@ struct wpabuf * tls_connection_decrypt(void *tls_ctx,
|
||||||
struct tls_connection *conn,
|
struct tls_connection *conn,
|
||||||
const struct wpabuf *in_data);
|
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_connection_resumed - Was session resumption used
|
||||||
* @tls_ctx: TLS context data from tls_init()
|
* @tls_ctx: TLS context data from tls_init()
|
||||||
|
|
|
@ -331,6 +331,17 @@ struct wpabuf * tls_connection_handshake(void *tls_ctx,
|
||||||
struct tls_connection *conn,
|
struct tls_connection *conn,
|
||||||
const struct wpabuf *in_data,
|
const struct wpabuf *in_data,
|
||||||
struct wpabuf **appl_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
|
#ifdef CONFIG_TLS_INTERNAL_CLIENT
|
||||||
u8 *res, *ad;
|
u8 *res, *ad;
|
||||||
|
@ -344,7 +355,7 @@ struct wpabuf * tls_connection_handshake(void *tls_ctx,
|
||||||
res = tlsv1_client_handshake(conn->client,
|
res = tlsv1_client_handshake(conn->client,
|
||||||
in_data ? wpabuf_head(in_data) : NULL,
|
in_data ? wpabuf_head(in_data) : NULL,
|
||||||
in_data ? wpabuf_len(in_data) : 0,
|
in_data ? wpabuf_len(in_data) : 0,
|
||||||
&res_len, &ad, &ad_len);
|
&res_len, &ad, &ad_len, need_more_data);
|
||||||
if (res == NULL)
|
if (res == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
out = wpabuf_alloc_ext_data(res, res_len);
|
out = wpabuf_alloc_ext_data(res, res_len);
|
||||||
|
@ -455,23 +466,23 @@ struct wpabuf * tls_connection_decrypt(void *tls_ctx,
|
||||||
struct tls_connection *conn,
|
struct tls_connection *conn,
|
||||||
const struct wpabuf *in_data)
|
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
|
#ifdef CONFIG_TLS_INTERNAL_CLIENT
|
||||||
if (conn->client) {
|
if (conn->client) {
|
||||||
struct wpabuf *buf;
|
return tlsv1_client_decrypt(conn->client, wpabuf_head(in_data),
|
||||||
int res;
|
wpabuf_len(in_data),
|
||||||
buf = wpabuf_alloc((wpabuf_len(in_data) + 500) * 3);
|
need_more_data);
|
||||||
if (buf == NULL)
|
|
||||||
return NULL;
|
|
||||||
res = 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;
|
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_TLS_INTERNAL_CLIENT */
|
#endif /* CONFIG_TLS_INTERNAL_CLIENT */
|
||||||
#ifdef CONFIG_TLS_INTERNAL_SERVER
|
#ifdef CONFIG_TLS_INTERNAL_SERVER
|
||||||
|
|
|
@ -136,25 +136,43 @@ int tls_derive_keys(struct tlsv1_client *conn,
|
||||||
* @out_len: Length of the output buffer.
|
* @out_len: Length of the output buffer.
|
||||||
* @appl_data: Pointer to application data pointer, or %NULL if dropped
|
* @appl_data: Pointer to application data pointer, or %NULL if dropped
|
||||||
* @appl_data_len: Pointer to variable that is set to appl_data length
|
* @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
|
* Returns: Pointer to output data, %NULL on failure
|
||||||
*/
|
*/
|
||||||
u8 * tlsv1_client_handshake(struct tlsv1_client *conn,
|
u8 * tlsv1_client_handshake(struct tlsv1_client *conn,
|
||||||
const u8 *in_data, size_t in_len,
|
const u8 *in_data, size_t in_len,
|
||||||
size_t *out_len, u8 **appl_data,
|
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;
|
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;
|
size_t in_msg_len;
|
||||||
int no_appl_data;
|
int no_appl_data;
|
||||||
int used;
|
int used;
|
||||||
|
|
||||||
|
if (need_more_data)
|
||||||
|
*need_more_data = 0;
|
||||||
|
|
||||||
if (conn->state == CLIENT_HELLO) {
|
if (conn->state == CLIENT_HELLO) {
|
||||||
if (in_len)
|
if (in_len)
|
||||||
return NULL;
|
return NULL;
|
||||||
return tls_send_client_hello(conn, out_len);
|
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)
|
if (in_data == NULL || in_len == 0)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
@ -176,11 +194,23 @@ u8 * tlsv1_client_handshake(struct tlsv1_client *conn,
|
||||||
goto failed;
|
goto failed;
|
||||||
}
|
}
|
||||||
if (used == 0) {
|
if (used == 0) {
|
||||||
/* need more data */
|
struct wpabuf *partial;
|
||||||
wpa_printf(MSG_DEBUG, "TLSv1: Partial processing not "
|
wpa_printf(MSG_DEBUG, "TLSv1: Need more data");
|
||||||
"yet supported");
|
os_free(in_msg);
|
||||||
tls_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
|
partial = wpabuf_alloc_copy(pos, end - pos);
|
||||||
goto failed;
|
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];
|
ct = pos[0];
|
||||||
|
|
||||||
|
@ -211,6 +241,8 @@ u8 * tlsv1_client_handshake(struct tlsv1_client *conn,
|
||||||
failed:
|
failed:
|
||||||
os_free(in_msg);
|
os_free(in_msg);
|
||||||
if (conn->alert_level) {
|
if (conn->alert_level) {
|
||||||
|
wpabuf_free(conn->partial_input);
|
||||||
|
conn->partial_input = NULL;
|
||||||
conn->state = FAILED;
|
conn->state = FAILED;
|
||||||
os_free(msg);
|
os_free(msg);
|
||||||
msg = tlsv1_client_send_alert(conn, conn->alert_level,
|
msg = tlsv1_client_send_alert(conn, conn->alert_level,
|
||||||
|
@ -221,6 +253,11 @@ failed:
|
||||||
*out_len = 0;
|
*out_len = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (need_more_data == NULL || !(*need_more_data)) {
|
||||||
|
wpabuf_free(conn->partial_input);
|
||||||
|
conn->partial_input = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -263,53 +300,80 @@ int tlsv1_client_encrypt(struct tlsv1_client *conn,
|
||||||
* @conn: TLSv1 client connection data from tlsv1_client_init()
|
* @conn: TLSv1 client connection data from tlsv1_client_init()
|
||||||
* @in_data: Pointer to input buffer (encrypted TLS data)
|
* @in_data: Pointer to input buffer (encrypted TLS data)
|
||||||
* @in_len: Input buffer length
|
* @in_len: Input buffer length
|
||||||
* @out_data: Pointer to output buffer (decrypted data from TLS tunnel)
|
* @need_more_data: Set to 1 if more data would be needed to complete
|
||||||
* @out_len: Maximum out_data length
|
* processing
|
||||||
* Returns: Number of bytes written to out_data, -1 on failure
|
* Returns: Decrypted data or %NULL on failure
|
||||||
*
|
*
|
||||||
* This function is used after TLS handshake has been completed successfully to
|
* This function is used after TLS handshake has been completed successfully to
|
||||||
* receive data from the encrypted tunnel.
|
* 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,
|
const u8 *in_data, size_t in_len,
|
||||||
u8 *out_data, size_t out_len)
|
int *need_more_data)
|
||||||
{
|
{
|
||||||
const u8 *in_end, *pos;
|
const u8 *in_end, *pos;
|
||||||
int used;
|
int used;
|
||||||
u8 alert, *out_end, *out_pos, ct;
|
u8 alert, *out_pos, ct;
|
||||||
size_t olen;
|
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;
|
pos = in_data;
|
||||||
in_end = in_data + in_len;
|
in_end = in_data + in_len;
|
||||||
out_pos = out_data;
|
|
||||||
out_end = out_data + out_len;
|
|
||||||
|
|
||||||
while (pos < in_end) {
|
while (pos < in_end) {
|
||||||
ct = pos[0];
|
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,
|
used = tlsv1_record_receive(&conn->rl, pos, in_end - pos,
|
||||||
out_pos, &olen, &alert);
|
out_pos, &olen, &alert);
|
||||||
if (used < 0) {
|
if (used < 0) {
|
||||||
wpa_printf(MSG_DEBUG, "TLSv1: Record layer processing "
|
wpa_printf(MSG_DEBUG, "TLSv1: Record layer processing "
|
||||||
"failed");
|
"failed");
|
||||||
tls_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
|
goto fail;
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
if (used == 0) {
|
if (used == 0) {
|
||||||
/* need more data */
|
struct wpabuf *partial;
|
||||||
wpa_printf(MSG_DEBUG, "TLSv1: Partial processing not "
|
wpa_printf(MSG_DEBUG, "TLSv1: Need more data");
|
||||||
"yet supported");
|
partial = wpabuf_alloc_copy(pos, in_end - pos);
|
||||||
tls_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
|
wpabuf_free(conn->partial_input);
|
||||||
return -1;
|
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 (ct == TLS_CONTENT_TYPE_ALERT) {
|
||||||
if (olen < 2) {
|
if (olen < 2) {
|
||||||
wpa_printf(MSG_DEBUG, "TLSv1: Alert "
|
wpa_printf(MSG_DEBUG, "TLSv1: Alert "
|
||||||
"underflow");
|
"underflow");
|
||||||
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
|
alert = TLS_ALERT_DECODE_ERROR;
|
||||||
TLS_ALERT_DECODE_ERROR);
|
goto fail;
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
wpa_printf(MSG_DEBUG, "TLSv1: Received alert %d:%d",
|
wpa_printf(MSG_DEBUG, "TLSv1: Received alert %d:%d",
|
||||||
out_pos[0], out_pos[1]);
|
out_pos[0], out_pos[1]);
|
||||||
|
@ -319,32 +383,33 @@ int tlsv1_client_decrypt(struct tlsv1_client *conn,
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
tls_alert(conn, TLS_ALERT_LEVEL_FATAL, out_pos[1]);
|
alert = out_pos[1];
|
||||||
return -1;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ct != TLS_CONTENT_TYPE_APPLICATION_DATA) {
|
if (ct != TLS_CONTENT_TYPE_APPLICATION_DATA) {
|
||||||
wpa_printf(MSG_DEBUG, "TLSv1: Unexpected content type "
|
wpa_printf(MSG_DEBUG, "TLSv1: Unexpected content type "
|
||||||
"0x%x when decrypting application data",
|
"0x%x when decrypting application data",
|
||||||
pos[0]);
|
pos[0]);
|
||||||
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
|
alert = TLS_ALERT_UNEXPECTED_MESSAGE;
|
||||||
TLS_ALERT_UNEXPECTED_MESSAGE);
|
goto fail;
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
out_pos += olen;
|
wpabuf_put(buf, 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
pos += used;
|
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);
|
os_free(conn->client_hello_ext);
|
||||||
tlsv1_client_free_dh(conn);
|
tlsv1_client_free_dh(conn);
|
||||||
tlsv1_cred_free(conn->cred);
|
tlsv1_cred_free(conn->cred);
|
||||||
|
wpabuf_free(conn->partial_input);
|
||||||
os_free(conn);
|
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,
|
u8 * tlsv1_client_handshake(struct tlsv1_client *conn,
|
||||||
const u8 *in_data, size_t in_len,
|
const u8 *in_data, size_t in_len,
|
||||||
size_t *out_len, u8 **appl_data,
|
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,
|
int tlsv1_client_encrypt(struct tlsv1_client *conn,
|
||||||
const u8 *in_data, size_t in_len,
|
const u8 *in_data, size_t in_len,
|
||||||
u8 *out_data, size_t out_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,
|
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,
|
int tlsv1_client_get_cipher(struct tlsv1_client *conn, char *buf,
|
||||||
size_t buflen);
|
size_t buflen);
|
||||||
int tlsv1_client_shutdown(struct tlsv1_client *conn);
|
int tlsv1_client_shutdown(struct tlsv1_client *conn);
|
||||||
|
|
|
@ -68,6 +68,8 @@ struct tlsv1_client {
|
||||||
|
|
||||||
tlsv1_client_session_ticket_cb session_ticket_cb;
|
tlsv1_client_session_ticket_cb session_ticket_cb;
|
||||||
void *session_ticket_cb_ctx;
|
void *session_ticket_cb_ctx;
|
||||||
|
|
||||||
|
struct wpabuf *partial_input;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue