f534ee0804
Reduce the amount of time keying material (MSK, EMSK, temporary private data) remains in memory in EAP methods. This provides additional protection should there be any issues that could expose process memory to external observers. Signed-off-by: Jouni Malinen <j@w1.fi>
790 lines
19 KiB
C
790 lines
19 KiB
C
/*
|
|
* EAP peer method: EAP-GPSK (RFC 5433)
|
|
* Copyright (c) 2006-2014, Jouni Malinen <j@w1.fi>
|
|
*
|
|
* This software may be distributed under the terms of the BSD license.
|
|
* See README for more details.
|
|
*/
|
|
|
|
#include "includes.h"
|
|
|
|
#include "common.h"
|
|
#include "crypto/random.h"
|
|
#include "eap_peer/eap_i.h"
|
|
#include "eap_common/eap_gpsk_common.h"
|
|
|
|
struct eap_gpsk_data {
|
|
enum { GPSK_1, GPSK_3, SUCCESS, FAILURE } state;
|
|
u8 rand_server[EAP_GPSK_RAND_LEN];
|
|
u8 rand_peer[EAP_GPSK_RAND_LEN];
|
|
u8 msk[EAP_MSK_LEN];
|
|
u8 emsk[EAP_EMSK_LEN];
|
|
u8 sk[EAP_GPSK_MAX_SK_LEN];
|
|
size_t sk_len;
|
|
u8 pk[EAP_GPSK_MAX_PK_LEN];
|
|
size_t pk_len;
|
|
u8 session_id[128];
|
|
size_t id_len;
|
|
u8 *id_peer;
|
|
size_t id_peer_len;
|
|
u8 *id_server;
|
|
size_t id_server_len;
|
|
int vendor; /* CSuite/Specifier */
|
|
int specifier; /* CSuite/Specifier */
|
|
u8 *psk;
|
|
size_t psk_len;
|
|
u16 forced_cipher; /* force cipher or 0 to allow all supported */
|
|
};
|
|
|
|
|
|
static struct wpabuf * eap_gpsk_send_gpsk_2(struct eap_gpsk_data *data,
|
|
u8 identifier,
|
|
const u8 *csuite_list,
|
|
size_t csuite_list_len);
|
|
static struct wpabuf * eap_gpsk_send_gpsk_4(struct eap_gpsk_data *data,
|
|
u8 identifier);
|
|
|
|
|
|
#ifndef CONFIG_NO_STDOUT_DEBUG
|
|
static const char * eap_gpsk_state_txt(int state)
|
|
{
|
|
switch (state) {
|
|
case GPSK_1:
|
|
return "GPSK-1";
|
|
case GPSK_3:
|
|
return "GPSK-3";
|
|
case SUCCESS:
|
|
return "SUCCESS";
|
|
case FAILURE:
|
|
return "FAILURE";
|
|
default:
|
|
return "?";
|
|
}
|
|
}
|
|
#endif /* CONFIG_NO_STDOUT_DEBUG */
|
|
|
|
|
|
static void eap_gpsk_state(struct eap_gpsk_data *data, int state)
|
|
{
|
|
wpa_printf(MSG_DEBUG, "EAP-GPSK: %s -> %s",
|
|
eap_gpsk_state_txt(data->state),
|
|
eap_gpsk_state_txt(state));
|
|
data->state = state;
|
|
}
|
|
|
|
|
|
static void eap_gpsk_deinit(struct eap_sm *sm, void *priv);
|
|
|
|
|
|
static void * eap_gpsk_init(struct eap_sm *sm)
|
|
{
|
|
struct eap_gpsk_data *data;
|
|
const u8 *identity, *password;
|
|
size_t identity_len, password_len;
|
|
const char *phase1;
|
|
|
|
password = eap_get_config_password(sm, &password_len);
|
|
if (password == NULL) {
|
|
wpa_printf(MSG_INFO, "EAP-GPSK: No key (password) configured");
|
|
return NULL;
|
|
}
|
|
|
|
data = os_zalloc(sizeof(*data));
|
|
if (data == NULL)
|
|
return NULL;
|
|
data->state = GPSK_1;
|
|
|
|
identity = eap_get_config_identity(sm, &identity_len);
|
|
if (identity) {
|
|
data->id_peer = os_malloc(identity_len);
|
|
if (data->id_peer == NULL) {
|
|
eap_gpsk_deinit(sm, data);
|
|
return NULL;
|
|
}
|
|
os_memcpy(data->id_peer, identity, identity_len);
|
|
data->id_peer_len = identity_len;
|
|
}
|
|
|
|
phase1 = eap_get_config_phase1(sm);
|
|
if (phase1) {
|
|
const char *pos;
|
|
|
|
pos = os_strstr(phase1, "cipher=");
|
|
if (pos) {
|
|
data->forced_cipher = atoi(pos + 7);
|
|
wpa_printf(MSG_DEBUG, "EAP-GPSK: Forced cipher %u",
|
|
data->forced_cipher);
|
|
}
|
|
}
|
|
|
|
data->psk = os_malloc(password_len);
|
|
if (data->psk == NULL) {
|
|
eap_gpsk_deinit(sm, data);
|
|
return NULL;
|
|
}
|
|
os_memcpy(data->psk, password, password_len);
|
|
data->psk_len = password_len;
|
|
|
|
return data;
|
|
}
|
|
|
|
|
|
static void eap_gpsk_deinit(struct eap_sm *sm, void *priv)
|
|
{
|
|
struct eap_gpsk_data *data = priv;
|
|
os_free(data->id_server);
|
|
os_free(data->id_peer);
|
|
if (data->psk) {
|
|
os_memset(data->psk, 0, data->psk_len);
|
|
os_free(data->psk);
|
|
}
|
|
bin_clear_free(data, sizeof(*data));
|
|
}
|
|
|
|
|
|
static const u8 * eap_gpsk_process_id_server(struct eap_gpsk_data *data,
|
|
const u8 *pos, const u8 *end)
|
|
{
|
|
u16 alen;
|
|
|
|
if (end - pos < 2) {
|
|
wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short GPSK-1 packet");
|
|
return NULL;
|
|
}
|
|
alen = WPA_GET_BE16(pos);
|
|
pos += 2;
|
|
if (end - pos < alen) {
|
|
wpa_printf(MSG_DEBUG, "EAP-GPSK: ID_Server overflow");
|
|
return NULL;
|
|
}
|
|
os_free(data->id_server);
|
|
data->id_server = os_malloc(alen);
|
|
if (data->id_server == NULL) {
|
|
wpa_printf(MSG_DEBUG, "EAP-GPSK: No memory for ID_Server");
|
|
return NULL;
|
|
}
|
|
os_memcpy(data->id_server, pos, alen);
|
|
data->id_server_len = alen;
|
|
wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Server",
|
|
data->id_server, data->id_server_len);
|
|
pos += alen;
|
|
|
|
return pos;
|
|
}
|
|
|
|
|
|
static const u8 * eap_gpsk_process_rand_server(struct eap_gpsk_data *data,
|
|
const u8 *pos, const u8 *end)
|
|
{
|
|
if (pos == NULL)
|
|
return NULL;
|
|
|
|
if (end - pos < EAP_GPSK_RAND_LEN) {
|
|
wpa_printf(MSG_DEBUG, "EAP-GPSK: RAND_Server overflow");
|
|
return NULL;
|
|
}
|
|
os_memcpy(data->rand_server, pos, EAP_GPSK_RAND_LEN);
|
|
wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Server",
|
|
data->rand_server, EAP_GPSK_RAND_LEN);
|
|
pos += EAP_GPSK_RAND_LEN;
|
|
|
|
return pos;
|
|
}
|
|
|
|
|
|
static int eap_gpsk_select_csuite(struct eap_sm *sm,
|
|
struct eap_gpsk_data *data,
|
|
const u8 *csuite_list,
|
|
size_t csuite_list_len)
|
|
{
|
|
struct eap_gpsk_csuite *csuite;
|
|
int i, count;
|
|
|
|
count = csuite_list_len / sizeof(struct eap_gpsk_csuite);
|
|
data->vendor = EAP_GPSK_VENDOR_IETF;
|
|
data->specifier = EAP_GPSK_CIPHER_RESERVED;
|
|
csuite = (struct eap_gpsk_csuite *) csuite_list;
|
|
for (i = 0; i < count; i++) {
|
|
int vendor, specifier;
|
|
vendor = WPA_GET_BE32(csuite->vendor);
|
|
specifier = WPA_GET_BE16(csuite->specifier);
|
|
wpa_printf(MSG_DEBUG, "EAP-GPSK: CSuite[%d]: %d:%d",
|
|
i, vendor, specifier);
|
|
if (data->vendor == EAP_GPSK_VENDOR_IETF &&
|
|
data->specifier == EAP_GPSK_CIPHER_RESERVED &&
|
|
eap_gpsk_supported_ciphersuite(vendor, specifier) &&
|
|
(!data->forced_cipher || data->forced_cipher == specifier))
|
|
{
|
|
data->vendor = vendor;
|
|
data->specifier = specifier;
|
|
}
|
|
csuite++;
|
|
}
|
|
if (data->vendor == EAP_GPSK_VENDOR_IETF &&
|
|
data->specifier == EAP_GPSK_CIPHER_RESERVED) {
|
|
wpa_msg(sm->msg_ctx, MSG_INFO, "EAP-GPSK: No supported "
|
|
"ciphersuite found");
|
|
return -1;
|
|
}
|
|
wpa_printf(MSG_DEBUG, "EAP-GPSK: Selected ciphersuite %d:%d",
|
|
data->vendor, data->specifier);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static const u8 * eap_gpsk_process_csuite_list(struct eap_sm *sm,
|
|
struct eap_gpsk_data *data,
|
|
const u8 **list,
|
|
size_t *list_len,
|
|
const u8 *pos, const u8 *end)
|
|
{
|
|
size_t len;
|
|
|
|
if (pos == NULL)
|
|
return NULL;
|
|
|
|
if (end - pos < 2) {
|
|
wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short GPSK-1 packet");
|
|
return NULL;
|
|
}
|
|
len = WPA_GET_BE16(pos);
|
|
pos += 2;
|
|
if (len > (size_t) (end - pos)) {
|
|
wpa_printf(MSG_DEBUG, "EAP-GPSK: CSuite_List overflow");
|
|
return NULL;
|
|
}
|
|
if (len == 0 || (len % sizeof(struct eap_gpsk_csuite))) {
|
|
wpa_printf(MSG_DEBUG, "EAP-GPSK: Invalid CSuite_List len %lu",
|
|
(unsigned long) len);
|
|
return NULL;
|
|
}
|
|
|
|
if (eap_gpsk_select_csuite(sm, data, pos, len) < 0)
|
|
return NULL;
|
|
|
|
*list = pos;
|
|
*list_len = len;
|
|
pos += len;
|
|
|
|
return pos;
|
|
}
|
|
|
|
|
|
static struct wpabuf * eap_gpsk_process_gpsk_1(struct eap_sm *sm,
|
|
struct eap_gpsk_data *data,
|
|
struct eap_method_ret *ret,
|
|
const struct wpabuf *reqData,
|
|
const u8 *payload,
|
|
size_t payload_len)
|
|
{
|
|
size_t csuite_list_len;
|
|
const u8 *csuite_list, *pos, *end;
|
|
struct wpabuf *resp;
|
|
|
|
if (data->state != GPSK_1) {
|
|
ret->ignore = TRUE;
|
|
return NULL;
|
|
}
|
|
|
|
wpa_printf(MSG_DEBUG, "EAP-GPSK: Received Request/GPSK-1");
|
|
|
|
end = payload + payload_len;
|
|
|
|
pos = eap_gpsk_process_id_server(data, payload, end);
|
|
pos = eap_gpsk_process_rand_server(data, pos, end);
|
|
pos = eap_gpsk_process_csuite_list(sm, data, &csuite_list,
|
|
&csuite_list_len, pos, end);
|
|
if (pos == NULL) {
|
|
ret->methodState = METHOD_DONE;
|
|
eap_gpsk_state(data, FAILURE);
|
|
return NULL;
|
|
}
|
|
|
|
resp = eap_gpsk_send_gpsk_2(data, eap_get_id(reqData),
|
|
csuite_list, csuite_list_len);
|
|
if (resp == NULL)
|
|
return NULL;
|
|
|
|
eap_gpsk_state(data, GPSK_3);
|
|
|
|
return resp;
|
|
}
|
|
|
|
|
|
static struct wpabuf * eap_gpsk_send_gpsk_2(struct eap_gpsk_data *data,
|
|
u8 identifier,
|
|
const u8 *csuite_list,
|
|
size_t csuite_list_len)
|
|
{
|
|
struct wpabuf *resp;
|
|
size_t len, miclen;
|
|
u8 *rpos, *start;
|
|
struct eap_gpsk_csuite *csuite;
|
|
|
|
wpa_printf(MSG_DEBUG, "EAP-GPSK: Sending Response/GPSK-2");
|
|
|
|
miclen = eap_gpsk_mic_len(data->vendor, data->specifier);
|
|
len = 1 + 2 + data->id_peer_len + 2 + data->id_server_len +
|
|
2 * EAP_GPSK_RAND_LEN + 2 + csuite_list_len +
|
|
sizeof(struct eap_gpsk_csuite) + 2 + miclen;
|
|
|
|
resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GPSK, len,
|
|
EAP_CODE_RESPONSE, identifier);
|
|
if (resp == NULL)
|
|
return NULL;
|
|
|
|
wpabuf_put_u8(resp, EAP_GPSK_OPCODE_GPSK_2);
|
|
start = wpabuf_put(resp, 0);
|
|
|
|
wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Peer",
|
|
data->id_peer, data->id_peer_len);
|
|
wpabuf_put_be16(resp, data->id_peer_len);
|
|
wpabuf_put_data(resp, data->id_peer, data->id_peer_len);
|
|
|
|
wpabuf_put_be16(resp, data->id_server_len);
|
|
wpabuf_put_data(resp, data->id_server, data->id_server_len);
|
|
|
|
if (random_get_bytes(data->rand_peer, EAP_GPSK_RAND_LEN)) {
|
|
wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to get random data "
|
|
"for RAND_Peer");
|
|
eap_gpsk_state(data, FAILURE);
|
|
wpabuf_free(resp);
|
|
return NULL;
|
|
}
|
|
wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Peer",
|
|
data->rand_peer, EAP_GPSK_RAND_LEN);
|
|
wpabuf_put_data(resp, data->rand_peer, EAP_GPSK_RAND_LEN);
|
|
wpabuf_put_data(resp, data->rand_server, EAP_GPSK_RAND_LEN);
|
|
|
|
wpabuf_put_be16(resp, csuite_list_len);
|
|
wpabuf_put_data(resp, csuite_list, csuite_list_len);
|
|
|
|
csuite = wpabuf_put(resp, sizeof(*csuite));
|
|
WPA_PUT_BE32(csuite->vendor, data->vendor);
|
|
WPA_PUT_BE16(csuite->specifier, data->specifier);
|
|
|
|
if (eap_gpsk_derive_keys(data->psk, data->psk_len,
|
|
data->vendor, data->specifier,
|
|
data->rand_peer, data->rand_server,
|
|
data->id_peer, data->id_peer_len,
|
|
data->id_server, data->id_server_len,
|
|
data->msk, data->emsk,
|
|
data->sk, &data->sk_len,
|
|
data->pk, &data->pk_len) < 0) {
|
|
wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to derive keys");
|
|
eap_gpsk_state(data, FAILURE);
|
|
wpabuf_free(resp);
|
|
return NULL;
|
|
}
|
|
|
|
if (eap_gpsk_derive_session_id(data->psk, data->psk_len,
|
|
data->vendor, data->specifier,
|
|
data->rand_peer, data->rand_server,
|
|
data->id_peer, data->id_peer_len,
|
|
data->id_server, data->id_server_len,
|
|
EAP_TYPE_GPSK,
|
|
data->session_id, &data->id_len) < 0) {
|
|
wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to derive Session-Id");
|
|
eap_gpsk_state(data, FAILURE);
|
|
wpabuf_free(resp);
|
|
return NULL;
|
|
}
|
|
wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Derived Session-Id",
|
|
data->session_id, data->id_len);
|
|
|
|
/* No PD_Payload_1 */
|
|
wpabuf_put_be16(resp, 0);
|
|
|
|
rpos = wpabuf_put(resp, miclen);
|
|
if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor,
|
|
data->specifier, start, rpos - start, rpos) <
|
|
0) {
|
|
eap_gpsk_state(data, FAILURE);
|
|
wpabuf_free(resp);
|
|
return NULL;
|
|
}
|
|
|
|
return resp;
|
|
}
|
|
|
|
|
|
static const u8 * eap_gpsk_validate_rand(struct eap_gpsk_data *data,
|
|
const u8 *pos, const u8 *end)
|
|
{
|
|
if (end - pos < EAP_GPSK_RAND_LEN) {
|
|
wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for "
|
|
"RAND_Peer");
|
|
return NULL;
|
|
}
|
|
if (os_memcmp(pos, data->rand_peer, EAP_GPSK_RAND_LEN) != 0) {
|
|
wpa_printf(MSG_DEBUG, "EAP-GPSK: RAND_Peer in GPSK-2 and "
|
|
"GPSK-3 did not match");
|
|
wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Peer in GPSK-2",
|
|
data->rand_peer, EAP_GPSK_RAND_LEN);
|
|
wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Peer in GPSK-3",
|
|
pos, EAP_GPSK_RAND_LEN);
|
|
return NULL;
|
|
}
|
|
pos += EAP_GPSK_RAND_LEN;
|
|
|
|
if (end - pos < EAP_GPSK_RAND_LEN) {
|
|
wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for "
|
|
"RAND_Server");
|
|
return NULL;
|
|
}
|
|
if (os_memcmp(pos, data->rand_server, EAP_GPSK_RAND_LEN) != 0) {
|
|
wpa_printf(MSG_DEBUG, "EAP-GPSK: RAND_Server in GPSK-1 and "
|
|
"GPSK-3 did not match");
|
|
wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Server in GPSK-1",
|
|
data->rand_server, EAP_GPSK_RAND_LEN);
|
|
wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Server in GPSK-3",
|
|
pos, EAP_GPSK_RAND_LEN);
|
|
return NULL;
|
|
}
|
|
pos += EAP_GPSK_RAND_LEN;
|
|
|
|
return pos;
|
|
}
|
|
|
|
|
|
static const u8 * eap_gpsk_validate_id_server(struct eap_gpsk_data *data,
|
|
const u8 *pos, const u8 *end)
|
|
{
|
|
size_t len;
|
|
|
|
if (pos == NULL)
|
|
return NULL;
|
|
|
|
if (end - pos < (int) 2) {
|
|
wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for "
|
|
"length(ID_Server)");
|
|
return NULL;
|
|
}
|
|
|
|
len = WPA_GET_BE16(pos);
|
|
pos += 2;
|
|
|
|
if (end - pos < (int) len) {
|
|
wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for "
|
|
"ID_Server");
|
|
return NULL;
|
|
}
|
|
|
|
if (len != data->id_server_len ||
|
|
os_memcmp(pos, data->id_server, len) != 0) {
|
|
wpa_printf(MSG_INFO, "EAP-GPSK: ID_Server did not match with "
|
|
"the one used in GPSK-1");
|
|
wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Server in GPSK-1",
|
|
data->id_server, data->id_server_len);
|
|
wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Server in GPSK-3",
|
|
pos, len);
|
|
return NULL;
|
|
}
|
|
|
|
pos += len;
|
|
|
|
return pos;
|
|
}
|
|
|
|
|
|
static const u8 * eap_gpsk_validate_csuite(struct eap_gpsk_data *data,
|
|
const u8 *pos, const u8 *end)
|
|
{
|
|
int vendor, specifier;
|
|
const struct eap_gpsk_csuite *csuite;
|
|
|
|
if (pos == NULL)
|
|
return NULL;
|
|
|
|
if (end - pos < (int) sizeof(*csuite)) {
|
|
wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for "
|
|
"CSuite_Sel");
|
|
return NULL;
|
|
}
|
|
csuite = (const struct eap_gpsk_csuite *) pos;
|
|
vendor = WPA_GET_BE32(csuite->vendor);
|
|
specifier = WPA_GET_BE16(csuite->specifier);
|
|
pos += sizeof(*csuite);
|
|
if (vendor != data->vendor || specifier != data->specifier) {
|
|
wpa_printf(MSG_DEBUG, "EAP-GPSK: CSuite_Sel (%d:%d) does not "
|
|
"match with the one sent in GPSK-2 (%d:%d)",
|
|
vendor, specifier, data->vendor, data->specifier);
|
|
return NULL;
|
|
}
|
|
|
|
return pos;
|
|
}
|
|
|
|
|
|
static const u8 * eap_gpsk_validate_pd_payload_2(struct eap_gpsk_data *data,
|
|
const u8 *pos, const u8 *end)
|
|
{
|
|
u16 alen;
|
|
|
|
if (pos == NULL)
|
|
return NULL;
|
|
|
|
if (end - pos < 2) {
|
|
wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for "
|
|
"PD_Payload_2 length");
|
|
return NULL;
|
|
}
|
|
alen = WPA_GET_BE16(pos);
|
|
pos += 2;
|
|
if (end - pos < alen) {
|
|
wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for "
|
|
"%d-octet PD_Payload_2", alen);
|
|
return NULL;
|
|
}
|
|
wpa_hexdump(MSG_DEBUG, "EAP-GPSK: PD_Payload_2", pos, alen);
|
|
pos += alen;
|
|
|
|
return pos;
|
|
}
|
|
|
|
|
|
static const u8 * eap_gpsk_validate_gpsk_3_mic(struct eap_gpsk_data *data,
|
|
const u8 *payload,
|
|
const u8 *pos, const u8 *end)
|
|
{
|
|
size_t miclen;
|
|
u8 mic[EAP_GPSK_MAX_MIC_LEN];
|
|
|
|
if (pos == NULL)
|
|
return NULL;
|
|
|
|
miclen = eap_gpsk_mic_len(data->vendor, data->specifier);
|
|
if (end - pos < (int) miclen) {
|
|
wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for MIC "
|
|
"(left=%lu miclen=%lu)",
|
|
(unsigned long) (end - pos),
|
|
(unsigned long) miclen);
|
|
return NULL;
|
|
}
|
|
if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor,
|
|
data->specifier, payload, pos - payload, mic)
|
|
< 0) {
|
|
wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to compute MIC");
|
|
return NULL;
|
|
}
|
|
if (os_memcmp_const(mic, pos, miclen) != 0) {
|
|
wpa_printf(MSG_INFO, "EAP-GPSK: Incorrect MIC in GPSK-3");
|
|
wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Received MIC", pos, miclen);
|
|
wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Computed MIC", mic, miclen);
|
|
return NULL;
|
|
}
|
|
pos += miclen;
|
|
|
|
return pos;
|
|
}
|
|
|
|
|
|
static struct wpabuf * eap_gpsk_process_gpsk_3(struct eap_sm *sm,
|
|
struct eap_gpsk_data *data,
|
|
struct eap_method_ret *ret,
|
|
const struct wpabuf *reqData,
|
|
const u8 *payload,
|
|
size_t payload_len)
|
|
{
|
|
struct wpabuf *resp;
|
|
const u8 *pos, *end;
|
|
|
|
if (data->state != GPSK_3) {
|
|
ret->ignore = TRUE;
|
|
return NULL;
|
|
}
|
|
|
|
wpa_printf(MSG_DEBUG, "EAP-GPSK: Received Request/GPSK-3");
|
|
|
|
end = payload + payload_len;
|
|
|
|
pos = eap_gpsk_validate_rand(data, payload, end);
|
|
pos = eap_gpsk_validate_id_server(data, pos, end);
|
|
pos = eap_gpsk_validate_csuite(data, pos, end);
|
|
pos = eap_gpsk_validate_pd_payload_2(data, pos, end);
|
|
pos = eap_gpsk_validate_gpsk_3_mic(data, payload, pos, end);
|
|
|
|
if (pos == NULL) {
|
|
eap_gpsk_state(data, FAILURE);
|
|
return NULL;
|
|
}
|
|
if (pos != end) {
|
|
wpa_printf(MSG_DEBUG, "EAP-GPSK: Ignored %lu bytes of extra "
|
|
"data in the end of GPSK-2",
|
|
(unsigned long) (end - pos));
|
|
}
|
|
|
|
resp = eap_gpsk_send_gpsk_4(data, eap_get_id(reqData));
|
|
if (resp == NULL)
|
|
return NULL;
|
|
|
|
eap_gpsk_state(data, SUCCESS);
|
|
ret->methodState = METHOD_DONE;
|
|
ret->decision = DECISION_UNCOND_SUCC;
|
|
|
|
return resp;
|
|
}
|
|
|
|
|
|
static struct wpabuf * eap_gpsk_send_gpsk_4(struct eap_gpsk_data *data,
|
|
u8 identifier)
|
|
{
|
|
struct wpabuf *resp;
|
|
u8 *rpos, *start;
|
|
size_t mlen;
|
|
|
|
wpa_printf(MSG_DEBUG, "EAP-GPSK: Sending Response/GPSK-4");
|
|
|
|
mlen = eap_gpsk_mic_len(data->vendor, data->specifier);
|
|
|
|
resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GPSK, 1 + 2 + mlen,
|
|
EAP_CODE_RESPONSE, identifier);
|
|
if (resp == NULL)
|
|
return NULL;
|
|
|
|
wpabuf_put_u8(resp, EAP_GPSK_OPCODE_GPSK_4);
|
|
start = wpabuf_put(resp, 0);
|
|
|
|
/* No PD_Payload_3 */
|
|
wpabuf_put_be16(resp, 0);
|
|
|
|
rpos = wpabuf_put(resp, mlen);
|
|
if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor,
|
|
data->specifier, start, rpos - start, rpos) <
|
|
0) {
|
|
eap_gpsk_state(data, FAILURE);
|
|
wpabuf_free(resp);
|
|
return NULL;
|
|
}
|
|
|
|
return resp;
|
|
}
|
|
|
|
|
|
static struct wpabuf * eap_gpsk_process(struct eap_sm *sm, void *priv,
|
|
struct eap_method_ret *ret,
|
|
const struct wpabuf *reqData)
|
|
{
|
|
struct eap_gpsk_data *data = priv;
|
|
struct wpabuf *resp;
|
|
const u8 *pos;
|
|
size_t len;
|
|
|
|
pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_GPSK, reqData, &len);
|
|
if (pos == NULL || len < 1) {
|
|
ret->ignore = TRUE;
|
|
return NULL;
|
|
}
|
|
|
|
wpa_printf(MSG_DEBUG, "EAP-GPSK: Received frame: opcode %d", *pos);
|
|
|
|
ret->ignore = FALSE;
|
|
ret->methodState = METHOD_MAY_CONT;
|
|
ret->decision = DECISION_FAIL;
|
|
ret->allowNotifications = FALSE;
|
|
|
|
switch (*pos) {
|
|
case EAP_GPSK_OPCODE_GPSK_1:
|
|
resp = eap_gpsk_process_gpsk_1(sm, data, ret, reqData,
|
|
pos + 1, len - 1);
|
|
break;
|
|
case EAP_GPSK_OPCODE_GPSK_3:
|
|
resp = eap_gpsk_process_gpsk_3(sm, data, ret, reqData,
|
|
pos + 1, len - 1);
|
|
break;
|
|
default:
|
|
wpa_printf(MSG_DEBUG, "EAP-GPSK: Ignoring message with "
|
|
"unknown opcode %d", *pos);
|
|
ret->ignore = TRUE;
|
|
return NULL;
|
|
}
|
|
|
|
return resp;
|
|
}
|
|
|
|
|
|
static Boolean eap_gpsk_isKeyAvailable(struct eap_sm *sm, void *priv)
|
|
{
|
|
struct eap_gpsk_data *data = priv;
|
|
return data->state == SUCCESS;
|
|
}
|
|
|
|
|
|
static u8 * eap_gpsk_getKey(struct eap_sm *sm, void *priv, size_t *len)
|
|
{
|
|
struct eap_gpsk_data *data = priv;
|
|
u8 *key;
|
|
|
|
if (data->state != SUCCESS)
|
|
return NULL;
|
|
|
|
key = os_malloc(EAP_MSK_LEN);
|
|
if (key == NULL)
|
|
return NULL;
|
|
os_memcpy(key, data->msk, EAP_MSK_LEN);
|
|
*len = EAP_MSK_LEN;
|
|
|
|
return key;
|
|
}
|
|
|
|
|
|
static u8 * eap_gpsk_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
|
|
{
|
|
struct eap_gpsk_data *data = priv;
|
|
u8 *key;
|
|
|
|
if (data->state != SUCCESS)
|
|
return NULL;
|
|
|
|
key = os_malloc(EAP_EMSK_LEN);
|
|
if (key == NULL)
|
|
return NULL;
|
|
os_memcpy(key, data->emsk, EAP_EMSK_LEN);
|
|
*len = EAP_EMSK_LEN;
|
|
|
|
return key;
|
|
}
|
|
|
|
|
|
static u8 * eap_gpsk_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
|
|
{
|
|
struct eap_gpsk_data *data = priv;
|
|
u8 *sid;
|
|
|
|
if (data->state != SUCCESS)
|
|
return NULL;
|
|
|
|
sid = os_malloc(data->id_len);
|
|
if (sid == NULL)
|
|
return NULL;
|
|
os_memcpy(sid, data->session_id, data->id_len);
|
|
*len = data->id_len;
|
|
|
|
return sid;
|
|
}
|
|
|
|
|
|
int eap_peer_gpsk_register(void)
|
|
{
|
|
struct eap_method *eap;
|
|
int ret;
|
|
|
|
eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
|
|
EAP_VENDOR_IETF, EAP_TYPE_GPSK, "GPSK");
|
|
if (eap == NULL)
|
|
return -1;
|
|
|
|
eap->init = eap_gpsk_init;
|
|
eap->deinit = eap_gpsk_deinit;
|
|
eap->process = eap_gpsk_process;
|
|
eap->isKeyAvailable = eap_gpsk_isKeyAvailable;
|
|
eap->getKey = eap_gpsk_getKey;
|
|
eap->get_emsk = eap_gpsk_get_emsk;
|
|
eap->getSessionId = eap_gpsk_get_session_id;
|
|
|
|
ret = eap_peer_method_register(eap);
|
|
if (ret)
|
|
eap_peer_method_free(eap);
|
|
return ret;
|
|
}
|