Convert TLS wrapper to use struct wpabuf

This converts tls_connection_handshake(),
tls_connection_server_handshake(), tls_connection_encrypt(), and
tls_connection_decrypt() to use struct wpa_buf to allow higher layer
code to be cleaned up with consistent struct wpabuf use.
This commit is contained in:
Jouni Malinen 2009-12-20 18:17:55 +02:00
parent 94c3e91fc5
commit 81c85c069a
14 changed files with 621 additions and 712 deletions

View file

@ -1,6 +1,6 @@
/*
* EAP peer: EAP-TLS/PEAP/TTLS/FAST common functions
* Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
* Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@ -296,27 +296,29 @@ fail:
* eap_peer_tls_reassemble_fragment - Reassemble a received fragment
* @data: Data for TLS processing
* @in_data: Next incoming TLS segment
* @in_len: Length of in_data
* Returns: 0 on success, 1 if more data is needed for the full message, or
* -1 on error
*/
static int eap_peer_tls_reassemble_fragment(struct eap_ssl_data *data,
const u8 *in_data, size_t in_len)
const struct wpabuf *in_data)
{
u8 *buf;
size_t tls_in_len, in_len;
if (data->tls_in_len + in_len == 0) {
tls_in_len = data->tls_in ? wpabuf_len(data->tls_in) : 0;
in_len = in_data ? wpabuf_len(in_data) : 0;
if (tls_in_len + in_len == 0) {
/* No message data received?! */
wpa_printf(MSG_WARNING, "SSL: Invalid reassembly state: "
"tls_in_left=%lu tls_in_len=%lu in_len=%lu",
(unsigned long) data->tls_in_left,
(unsigned long) data->tls_in_len,
(unsigned long) tls_in_len,
(unsigned long) in_len);
eap_peer_tls_reset_input(data);
return -1;
}
if (data->tls_in_len + in_len > 65536) {
if (tls_in_len + in_len > 65536) {
/*
* Limit length to avoid rogue servers from causing large
* memory allocations.
@ -335,16 +337,13 @@ static int eap_peer_tls_reassemble_fragment(struct eap_ssl_data *data,
return -1;
}
buf = os_realloc(data->tls_in, data->tls_in_len + in_len);
if (buf == NULL) {
if (wpabuf_resize(&data->tls_in, in_len) < 0) {
wpa_printf(MSG_INFO, "SSL: Could not allocate memory for TLS "
"data");
eap_peer_tls_reset_input(data);
return -1;
}
os_memcpy(buf + data->tls_in_len, in_data, in_len);
data->tls_in = buf;
data->tls_in_len += in_len;
wpabuf_put_buf(data->tls_in, in_data);
data->tls_in_left -= in_len;
if (data->tls_in_left > 0) {
@ -361,8 +360,6 @@ static int eap_peer_tls_reassemble_fragment(struct eap_ssl_data *data,
* eap_peer_tls_data_reassemble - Reassemble TLS data
* @data: Data for TLS processing
* @in_data: Next incoming TLS segment
* @in_len: Length of in_data
* @out_len: Variable for returning length of the reassembled message
* @need_more_input: Variable for returning whether more input data is needed
* to reassemble this TLS packet
* Returns: Pointer to output data, %NULL on error or when more data is needed
@ -371,16 +368,15 @@ static int eap_peer_tls_reassemble_fragment(struct eap_ssl_data *data,
* This function reassembles TLS fragments. Caller must not free the returned
* data buffer since an internal pointer to it is maintained.
*/
const u8 * eap_peer_tls_data_reassemble(
struct eap_ssl_data *data, const u8 *in_data, size_t in_len,
size_t *out_len, int *need_more_input)
static const struct wpabuf * eap_peer_tls_data_reassemble(
struct eap_ssl_data *data, const struct wpabuf *in_data,
int *need_more_input)
{
*need_more_input = 0;
if (data->tls_in_left > in_len || data->tls_in) {
if (data->tls_in_left > wpabuf_len(in_data) || data->tls_in) {
/* Message has fragments */
int res = eap_peer_tls_reassemble_fragment(data, in_data,
in_len);
int res = eap_peer_tls_reassemble_fragment(data, in_data);
if (res) {
if (res == 1)
*need_more_input = 1;
@ -391,14 +387,11 @@ const u8 * eap_peer_tls_data_reassemble(
} else {
/* No fragments in this message, so just make a copy of it. */
data->tls_in_left = 0;
data->tls_in = os_malloc(in_len ? in_len : 1);
data->tls_in = wpabuf_dup(in_data);
if (data->tls_in == NULL)
return NULL;
os_memcpy(data->tls_in, in_data, in_len);
data->tls_in_len = in_len;
}
*out_len = data->tls_in_len;
return data->tls_in;
}
@ -417,14 +410,13 @@ static int eap_tls_process_input(struct eap_sm *sm, struct eap_ssl_data *data,
const u8 *in_data, size_t in_len,
struct wpabuf **out_data)
{
const u8 *msg;
size_t msg_len;
const struct wpabuf *msg;
int need_more_input;
u8 *appl_data;
size_t appl_data_len;
struct wpabuf *appl_data;
struct wpabuf buf;
msg = eap_peer_tls_data_reassemble(data, in_data, in_len,
&msg_len, &need_more_input);
wpabuf_set(&buf, in_data, in_len);
msg = eap_peer_tls_data_reassemble(data, &buf, &need_more_input);
if (msg == NULL)
return need_more_input ? 1 : -1;
@ -433,31 +425,25 @@ static int eap_tls_process_input(struct eap_sm *sm, struct eap_ssl_data *data,
/* This should not happen.. */
wpa_printf(MSG_INFO, "SSL: eap_tls_process_input - pending "
"tls_out data even though tls_out_len = 0");
os_free(data->tls_out);
wpabuf_free(data->tls_out);
WPA_ASSERT(data->tls_out == NULL);
}
appl_data = NULL;
data->tls_out = tls_connection_handshake(sm->ssl_ctx, data->conn,
msg, msg_len,
&data->tls_out_len,
&appl_data, &appl_data_len);
msg, &appl_data);
eap_peer_tls_reset_input(data);
if (appl_data &&
tls_connection_established(sm->ssl_ctx, data->conn) &&
!tls_connection_get_failed(sm->ssl_ctx, data->conn)) {
wpa_hexdump_key(MSG_MSGDUMP, "SSL: Application data",
appl_data, appl_data_len);
*out_data = wpabuf_alloc_ext_data(appl_data, appl_data_len);
if (*out_data == NULL) {
os_free(appl_data);
return -1;
}
wpa_hexdump_buf_key(MSG_MSGDUMP, "SSL: Application data",
appl_data);
*out_data = appl_data;
return 2;
}
os_free(appl_data);
wpabuf_free(appl_data);
return 0;
}
@ -480,11 +466,14 @@ static int eap_tls_process_output(struct eap_ssl_data *data, EapType eap_type,
size_t len;
u8 *flags;
int more_fragments, length_included;
len = data->tls_out_len - data->tls_out_pos;
if (data->tls_out == NULL)
return -1;
len = wpabuf_len(data->tls_out) - data->tls_out_pos;
wpa_printf(MSG_DEBUG, "SSL: %lu bytes left to be sent out (of total "
"%lu bytes)",
(unsigned long) len, (unsigned long) data->tls_out_len);
(unsigned long) len,
(unsigned long) wpabuf_len(data->tls_out));
/*
* Limit outgoing message to the configured maximum size. Fragment
@ -499,7 +488,7 @@ static int eap_tls_process_output(struct eap_ssl_data *data, EapType eap_type,
more_fragments = 0;
length_included = data->tls_out_pos == 0 &&
(data->tls_out_len > data->tls_out_limit ||
(wpabuf_len(data->tls_out) > data->tls_out_limit ||
data->include_tls_length);
if (!length_included &&
eap_type == EAP_TYPE_PEAP && peap_version == 0 &&
@ -525,10 +514,12 @@ static int eap_tls_process_output(struct eap_ssl_data *data, EapType eap_type,
*flags |= EAP_TLS_FLAGS_MORE_FRAGMENTS;
if (length_included) {
*flags |= EAP_TLS_FLAGS_LENGTH_INCLUDED;
wpabuf_put_be32(*out_data, data->tls_out_len);
wpabuf_put_be32(*out_data, wpabuf_len(data->tls_out));
}
wpabuf_put_data(*out_data, &data->tls_out[data->tls_out_pos], len);
wpabuf_put_data(*out_data,
wpabuf_head_u8(data->tls_out) + data->tls_out_pos,
len);
data->tls_out_pos += len;
if (!more_fragments)
@ -576,13 +567,13 @@ int eap_peer_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data,
*out_data = NULL;
if (data->tls_out_len > 0 && in_len > 0) {
if (data->tls_out && wpabuf_len(data->tls_out) > 0 && in_len > 0) {
wpa_printf(MSG_DEBUG, "SSL: Received non-ACK when output "
"fragments are waiting to be sent out");
return -1;
}
if (data->tls_out_len == 0) {
if (data->tls_out == NULL || wpabuf_len(data->tls_out) == 0) {
/*
* No more data to send out - expect to receive more data from
* the AS.
@ -621,14 +612,14 @@ int eap_peer_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data,
/* TODO: clean pin if engine used? */
}
if (data->tls_out_len == 0) {
if (data->tls_out == NULL || wpabuf_len(data->tls_out) == 0) {
/*
* TLS negotiation should now be complete since all other cases
* needing more data should have been caught above based on
* the TLS Message Length field.
*/
wpa_printf(MSG_DEBUG, "SSL: No data to be sent out");
os_free(data->tls_out);
wpabuf_free(data->tls_out);
data->tls_out = NULL;
return 1;
}
@ -780,9 +771,8 @@ const u8 * eap_peer_tls_process_init(struct eap_sm *sm,
if (data->tls_in_left == 0) {
data->tls_in_total = tls_msg_len;
data->tls_in_left = tls_msg_len;
os_free(data->tls_in);
wpabuf_free(data->tls_in);
data->tls_in = NULL;
data->tls_in_len = 0;
}
pos += 4;
left -= 4;
@ -807,8 +797,8 @@ const u8 * eap_peer_tls_process_init(struct eap_sm *sm,
*/
void eap_peer_tls_reset_input(struct eap_ssl_data *data)
{
data->tls_in_left = data->tls_in_total = data->tls_in_len = 0;
os_free(data->tls_in);
data->tls_in_left = data->tls_in_total = 0;
wpabuf_free(data->tls_in);
data->tls_in = NULL;
}
@ -822,9 +812,8 @@ void eap_peer_tls_reset_input(struct eap_ssl_data *data)
*/
void eap_peer_tls_reset_output(struct eap_ssl_data *data)
{
data->tls_out_len = 0;
data->tls_out_pos = 0;
os_free(data->tls_out);
wpabuf_free(data->tls_out);
data->tls_out = NULL;
}
@ -841,44 +830,19 @@ int eap_peer_tls_decrypt(struct eap_sm *sm, struct eap_ssl_data *data,
const struct wpabuf *in_data,
struct wpabuf **in_decrypted)
{
int res;
const u8 *msg;
size_t msg_len, buf_len;
const struct wpabuf *msg;
int need_more_input;
msg = eap_peer_tls_data_reassemble(data, wpabuf_head(in_data),
wpabuf_len(in_data), &msg_len,
&need_more_input);
msg = eap_peer_tls_data_reassemble(data, in_data, &need_more_input);
if (msg == NULL)
return need_more_input ? 1 : -1;
buf_len = wpabuf_len(in_data);
if (data->tls_in_total > buf_len)
buf_len = data->tls_in_total;
/*
* Even though we try to disable TLS compression, it is possible that
* this cannot be done with all TLS libraries. Add extra buffer space
* to handle the possibility of the decrypted data being longer than
* input data.
*/
buf_len += 500;
buf_len *= 3;
*in_decrypted = wpabuf_alloc(buf_len ? buf_len : 1);
if (*in_decrypted == NULL) {
eap_peer_tls_reset_input(data);
wpa_printf(MSG_WARNING, "SSL: Failed to allocate memory for "
"decryption");
return -1;
}
res = tls_connection_decrypt(sm->ssl_ctx, data->conn, msg, msg_len,
wpabuf_mhead(*in_decrypted), buf_len);
*in_decrypted = tls_connection_decrypt(sm->ssl_ctx, data->conn, msg);
eap_peer_tls_reset_input(data);
if (res < 0) {
if (*in_decrypted == NULL) {
wpa_printf(MSG_INFO, "SSL: Failed to decrypt Phase 2 data");
return -1;
}
wpabuf_put(*in_decrypted, res);
return 0;
}
@ -899,29 +863,17 @@ int eap_peer_tls_encrypt(struct eap_sm *sm, struct eap_ssl_data *data,
const struct wpabuf *in_data,
struct wpabuf **out_data)
{
int res;
size_t len;
if (in_data) {
eap_peer_tls_reset_output(data);
len = wpabuf_len(in_data) + 300;
data->tls_out = os_malloc(len);
if (data->tls_out == NULL)
return -1;
res = tls_connection_encrypt(sm->ssl_ctx, data->conn,
wpabuf_head(in_data),
wpabuf_len(in_data),
data->tls_out, len);
if (res < 0) {
data->tls_out = tls_connection_encrypt(sm->ssl_ctx, data->conn,
in_data);
if (data->tls_out == NULL) {
wpa_printf(MSG_INFO, "SSL: Failed to encrypt Phase 2 "
"data (in_len=%lu)",
(unsigned long) wpabuf_len(in_data));
eap_peer_tls_reset_output(data);
return -1;
}
data->tls_out_len = res;
}
return eap_tls_process_output(data, eap_type, peap_version, id, 0,