NetBSD/dist/hostapd/eap_tlv.c

249 lines
5.6 KiB
C

/*
* hostapd / EAP-TLV (draft-josefsson-pppext-eap-tls-eap-07.txt)
* Copyright (c) 2004, Jouni Malinen <jkmaline@cc.hut.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
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <netinet/in.h>
#include "hostapd.h"
#include "common.h"
#include "eap_i.h"
/* EAP-TLV TLVs (draft-josefsson-ppext-eap-tls-eap-07.txt) */
#define EAP_TLV_RESULT_TLV 3 /* Acknowledged Result */
#define EAP_TLV_NAK_TLV 4
#define EAP_TLV_CRYPTO_BINDING_TLV 5
#define EAP_TLV_CONNECTION_BINDING_TLV 6
#define EAP_TLV_VENDOR_SPECIFIC_TLV 7
#define EAP_TLV_URI_TLV 8
#define EAP_TLV_EAP_PAYLOAD_TLV 9
#define EAP_TLV_INTERMEDIATE_RESULT_TLV 10
#define EAP_TLV_RESULT_SUCCESS 1
#define EAP_TLV_RESULT_FAILURE 2
struct eap_tlv_data {
enum { CONTINUE, SUCCESS, FAILURE } state;
};
static void * eap_tlv_init(struct eap_sm *sm)
{
struct eap_tlv_data *data;
data = malloc(sizeof(*data));
if (data == NULL)
return data;
memset(data, 0, sizeof(*data));
data->state = CONTINUE;
return data;
}
static void eap_tlv_reset(struct eap_sm *sm, void *priv)
{
struct eap_tlv_data *data = priv;
free(data);
}
static u8 * eap_tlv_buildReq(struct eap_sm *sm, void *priv, int id,
size_t *reqDataLen)
{
struct eap_hdr *req;
u8 *pos;
u16 status;
if (sm->tlv_request == TLV_REQ_SUCCESS) {
status = EAP_TLV_RESULT_SUCCESS;
} else {
status = EAP_TLV_RESULT_FAILURE;
}
*reqDataLen = sizeof(struct eap_hdr) + 1 + 6;
req = malloc(*reqDataLen);
if (req == NULL)
return NULL;
req->code = EAP_CODE_REQUEST;
req->identifier = id;
req->length = host_to_be16(*reqDataLen);
pos = (u8 *) (req + 1);
*pos++ = EAP_TYPE_TLV;
*pos++ = 0x80; /* Mandatory */
*pos++ = EAP_TLV_RESULT_TLV;
/* Length */
*pos++ = 0;
*pos++ = 2;
/* Status */
*pos++ = status >> 8;
*pos++ = status & 0xff;
return (u8 *) req;
}
static Boolean eap_tlv_check(struct eap_sm *sm, void *priv,
u8 *respData, size_t respDataLen)
{
struct eap_hdr *resp;
u8 *pos;
size_t len;
resp = (struct eap_hdr *) respData;
pos = (u8 *) (resp + 1);
if (respDataLen < sizeof(*resp) + 1 || *pos != EAP_TYPE_TLV ||
(len = ntohs(resp->length)) > respDataLen) {
wpa_printf(MSG_INFO, "EAP-TLV: Invalid frame");
return TRUE;
}
return FALSE;
}
static void eap_tlv_process(struct eap_sm *sm, void *priv,
u8 *respData, size_t respDataLen)
{
struct eap_tlv_data *data = priv;
struct eap_hdr *resp;
u8 *pos;
int len;
size_t left;
u8 *result_tlv = NULL;
size_t result_tlv_len = 0;
int tlv_type, mandatory, tlv_len;
resp = (struct eap_hdr *) respData;
len = ntohs(resp->length);
pos = (u8 *) (resp + 1);
/* Parse TLVs */
left = be_to_host16(resp->length) - sizeof(struct eap_hdr) - 1;
pos = (u8 *) (resp + 1);
pos++;
wpa_hexdump(MSG_DEBUG, "EAP-TLV: Received TLVs", pos, left);
while (left >= 4) {
mandatory = !!(pos[0] & 0x80);
tlv_type = pos[0] & 0x3f;
tlv_type = (tlv_type << 8) | pos[1];
tlv_len = ((int) pos[2] << 8) | pos[3];
pos += 4;
left -= 4;
if (tlv_len > left) {
wpa_printf(MSG_DEBUG, "EAP-TLV: TLV underrun "
"(tlv_len=%d left=%lu)", tlv_len,
(unsigned long) left);
data->state = FAILURE;
return;
}
switch (tlv_type) {
case EAP_TLV_RESULT_TLV:
result_tlv = pos;
result_tlv_len = tlv_len;
break;
default:
wpa_printf(MSG_DEBUG, "EAP-TLV: Unsupported TLV Type "
"%d%s", tlv_type,
mandatory ? " (mandatory)" : "");
if (mandatory) {
data->state = FAILURE;
return;
}
/* Ignore this TLV, but process other TLVs */
break;
}
pos += tlv_len;
left -= tlv_len;
}
if (left) {
wpa_printf(MSG_DEBUG, "EAP-TLV: Last TLV too short in "
"Request (left=%lu)", (unsigned long) left);
data->state = FAILURE;
return;
}
/* Process supported TLVs */
if (result_tlv) {
int status;
const char *requested;
wpa_hexdump(MSG_DEBUG, "EAP-TLV: Result TLV",
result_tlv, result_tlv_len);
if (result_tlv_len < 2) {
wpa_printf(MSG_INFO, "EAP-TLV: Too short Result TLV "
"(len=%lu)",
(unsigned long) result_tlv_len);
data->state = FAILURE;
return;
}
requested = sm->tlv_request == TLV_REQ_SUCCESS ? "Success" :
"Failure";
status = ((int) result_tlv[0] << 8) | result_tlv[1];
if (status == EAP_TLV_RESULT_SUCCESS) {
wpa_printf(MSG_INFO, "EAP-TLV: TLV Result - Success "
"- requested %s", requested);
if (sm->tlv_request == TLV_REQ_SUCCESS)
data->state = SUCCESS;
else
data->state = FAILURE;
} else if (status == EAP_TLV_RESULT_FAILURE) {
wpa_printf(MSG_INFO, "EAP-TLV: TLV Result - Failure - "
"requested %s", requested);
if (sm->tlv_request == TLV_REQ_FAILURE)
data->state = SUCCESS;
else
data->state = FAILURE;
} else {
wpa_printf(MSG_INFO, "EAP-TLV: Unknown TLV Result "
"Status %d", status);
data->state = FAILURE;
}
}
}
static Boolean eap_tlv_isDone(struct eap_sm *sm, void *priv)
{
struct eap_tlv_data *data = priv;
return data->state != CONTINUE;
}
static Boolean eap_tlv_isSuccess(struct eap_sm *sm, void *priv)
{
struct eap_tlv_data *data = priv;
return data->state == SUCCESS;
}
const struct eap_method eap_method_tlv =
{
.method = EAP_TYPE_TLV,
.name = "TLV",
.init = eap_tlv_init,
.reset = eap_tlv_reset,
.buildReq = eap_tlv_buildReq,
.check = eap_tlv_check,
.process = eap_tlv_process,
.isDone = eap_tlv_isDone,
.isSuccess = eap_tlv_isSuccess,
};