peap: handle fragmented request

This commit is contained in:
Tim Kourt 2018-02-01 16:01:50 -08:00 committed by Denis Kenzior
parent bcf9ec5922
commit 84be0eb8d3
1 changed files with 123 additions and 0 deletions

View File

@ -31,6 +31,8 @@
#include "eap.h"
#define PEAP_PDU_MAX_LEN 65536
enum peap_version {
PEAP_VERSION_0 = 0x00,
PEAP_VERSION_1 = 0x01,
@ -42,6 +44,8 @@ enum peap_version {
enum peap_flag {
/* Reserved = 0x00, */
PEAP_FLAG_S = 0x20,
PEAP_FLAG_M = 0x40,
PEAP_FLAG_L = 0x80,
};
struct eap_peap_state {
@ -51,6 +55,10 @@ struct eap_peap_state {
uint8_t *tx_pdu_buf;
size_t tx_pdu_buf_len;
uint8_t *rx_pdu_buf;
size_t rx_pdu_buf_len;
size_t rx_pdu_buf_offset;
char *ca_cert;
char *client_cert;
char *client_key;
@ -69,6 +77,19 @@ static void eap_peap_free_tx_buffer(struct eap_state *eap)
peap->tx_pdu_buf_len = 0;
}
static void eap_peap_free_rx_buffer(struct eap_state *eap)
{
struct eap_peap_state *peap = eap_get_data(eap);
if (!peap->rx_pdu_buf)
return;
l_free(peap->rx_pdu_buf);
peap->rx_pdu_buf = NULL;
peap->rx_pdu_buf_len = 0;
peap->rx_pdu_buf_offset = 0;
}
static void eap_peap_free(struct eap_state *eap)
{
struct eap_peap_state *peap = eap_get_data(eap);
@ -80,6 +101,8 @@ static void eap_peap_free(struct eap_state *eap)
eap_peap_free_tx_buffer(eap);
eap_peap_free_rx_buffer(eap);
eap_set_data(eap, NULL);
l_free(peap->ca_cert);
@ -95,6 +118,10 @@ static void eap_peap_send_response(struct eap_state *eap,
{
}
static void eap_peap_send_empty_response(struct eap_state *eap)
{
}
static void eap_peap_tunnel_data_send(const uint8_t *data, size_t data_len,
void *user_data)
{
@ -151,6 +178,41 @@ static void eap_peap_handle_payload(struct eap_state *eap,
{
}
static bool eap_peap_init_request_assembly(struct eap_state *eap,
const uint8_t *pkt, size_t len,
uint8_t flags) {
struct eap_peap_state *peap = eap_get_data(eap);
if (peap->rx_pdu_buf || !(flags & PEAP_FLAG_M) || len < 4)
return false;
peap->rx_pdu_buf_len = l_get_be32(pkt);
if (!peap->rx_pdu_buf_len || peap->rx_pdu_buf_len > PEAP_PDU_MAX_LEN) {
l_warn("Fragmented pkt size is outside of alowed boundaries "
"[1, %u]", PEAP_PDU_MAX_LEN);
return false;
}
if (peap->rx_pdu_buf_len < len) {
l_warn("Fragmented pkt size is smaller than the received "
"packet");
return false;
}
peap->rx_pdu_buf = l_malloc(peap->rx_pdu_buf_len);
peap->rx_pdu_buf_offset = 0;
return true;
}
static void eap_peap_send_fragmented_request_ack(struct eap_state *eap)
{
eap_peap_send_empty_response(eap);
}
static bool eap_peap_validate_version(struct eap_state *eap,
uint8_t flags_version)
{
@ -172,6 +234,46 @@ static bool eap_peap_validate_version(struct eap_state *eap,
return true;
}
static int eap_peap_handle_fragmented_request(struct eap_state *eap,
const uint8_t *pkt,
size_t len,
uint8_t flags_version)
{
struct eap_peap_state *peap = eap_get_data(eap);
size_t rx_header_offset = 0;
size_t pdu_len;
if (flags_version & PEAP_FLAG_L) {
if (!eap_peap_init_request_assembly(eap, pkt, len,
flags_version))
return -EINVAL;
rx_header_offset = 4;
}
if (!peap->rx_pdu_buf)
return -EINVAL;
pdu_len = len - rx_header_offset;
if (peap->rx_pdu_buf_len < peap->rx_pdu_buf_offset + pdu_len) {
l_error("Request fragment pkt size mismatch");
return -EINVAL;
}
memcpy(peap->rx_pdu_buf + peap->rx_pdu_buf_offset,
pkt + rx_header_offset, pdu_len);
peap->rx_pdu_buf_offset += pdu_len;
if (flags_version & PEAP_FLAG_M) {
eap_peap_send_fragmented_request_ack(eap);
return -EAGAIN;
}
return 0;
}
static void eap_peap_handle_request(struct eap_state *eap,
const uint8_t *pkt, size_t len)
{
@ -193,6 +295,25 @@ static void eap_peap_handle_request(struct eap_state *eap,
pkt += 1;
len -= 1;
if (flags_version & PEAP_FLAG_L || peap->rx_pdu_buf) {
int r = eap_peap_handle_fragmented_request(eap, pkt, len,
flags_version);
if (r == -EAGAIN)
return;
if (r < 0)
goto error;
if (peap->rx_pdu_buf_len != peap->rx_pdu_buf_offset) {
l_error("Request fragment pkt size mismatch");
goto error;
}
pkt = peap->rx_pdu_buf;
len = peap->rx_pdu_buf_len;
}
/*
* tx_pdu_buf is used for the retransmission and needs to be cleared on
* a new request
@ -218,6 +339,8 @@ static void eap_peap_handle_request(struct eap_state *eap,
eap_peap_handle_payload(eap, pkt, len);
eap_peap_free_rx_buffer(eap);
send_response:
if (!peap->tx_pdu_buf)
return;