TNC: Added preliminary code for IF-TNCCS-SOH server side support

If TNC is enabled, PEAPv0 server is now sending out SoH request to initiate
IF-TNCCS-SOH (Microsoft NAP / Statement of Health) handshake. The results
are currently only shown in debug log and they do not affect authentication
or authorization result.
This commit is contained in:
Jouni Malinen 2008-03-30 17:10:44 +03:00
parent 30680e9332
commit 1c2ff04f3a
4 changed files with 265 additions and 3 deletions

View file

@ -75,6 +75,7 @@ typedef enum {
/* SMI Network Management Private Enterprise Code for vendor specific types */
enum {
EAP_VENDOR_IETF = 0,
EAP_VENDOR_MICROSOFT = 0x000137 /* Microsoft */,
EAP_VENDOR_WFA = 0x00372A /* Wi-Fi Alliance */
};

View file

@ -20,6 +20,7 @@
#include "eap_tls_common.h"
#include "eap_common/eap_tlv_common.h"
#include "tls.h"
#include "tncs.h"
/* Maximum supported PEAP version
@ -37,7 +38,7 @@ struct eap_peap_data {
struct eap_ssl_data ssl;
enum {
START, PHASE1, PHASE1_ID2, PHASE2_START, PHASE2_ID,
PHASE2_METHOD,
PHASE2_METHOD, PHASE2_SOH,
PHASE2_TLV, SUCCESS_REQ, FAILURE_REQ, SUCCESS, FAILURE
} state;
@ -56,6 +57,7 @@ struct eap_peap_data {
u8 cmk[20];
u8 *phase2_key;
size_t phase2_key_len;
struct wpabuf *soh_response;
};
@ -74,6 +76,8 @@ static const char * eap_peap_state_txt(int state)
return "PHASE2_ID";
case PHASE2_METHOD:
return "PHASE2_METHOD";
case PHASE2_SOH:
return "PHASE2_SOH";
case PHASE2_TLV:
return "PHASE2_TLV";
case SUCCESS_REQ:
@ -199,6 +203,7 @@ static void eap_peap_reset(struct eap_sm *sm, void *priv)
eap_server_tls_ssl_deinit(sm, &data->ssl);
wpabuf_free(data->pending_phase2_resp);
os_free(data->phase2_key);
wpabuf_free(data->soh_response);
os_free(data);
}
@ -291,6 +296,10 @@ static struct wpabuf * eap_peap_build_phase2_req(struct eap_sm *sm,
const u8 *req;
size_t req_len;
if (data->phase2_method == NULL || data->phase2_priv == NULL) {
wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 method not ready");
return NULL;
}
buf = data->phase2_method->buildReq(sm, data->phase2_priv, id);
if (data->peap_version >= 2 && buf)
buf = eap_peapv2_tlv_eap_payload(buf);
@ -315,6 +324,45 @@ static struct wpabuf * eap_peap_build_phase2_req(struct eap_sm *sm,
}
#ifdef EAP_TNC
static struct wpabuf * eap_peap_build_phase2_soh(struct eap_sm *sm,
struct eap_peap_data *data,
u8 id)
{
struct wpabuf *buf1, *buf, *encr_req;
const u8 *req;
size_t req_len;
buf1 = tncs_build_soh_request();
if (buf1 == NULL)
return NULL;
buf = eap_msg_alloc(EAP_VENDOR_MICROSOFT, 0x21, wpabuf_len(buf1),
EAP_CODE_REQUEST, id);
if (buf == NULL) {
wpabuf_free(buf1);
return NULL;
}
wpabuf_put_buf(buf, buf1);
wpabuf_free(buf1);
req = wpabuf_head(buf);
req_len = wpabuf_len(buf);
wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: Encrypting Phase 2 SOH data",
req, req_len);
req += sizeof(struct eap_hdr);
req_len -= sizeof(struct eap_hdr);
encr_req = eap_peap_encrypt(sm, data, id, req, req_len);
wpabuf_free(buf);
return encr_req;
}
#endif /* EAP_TNC */
static void eap_peap_get_isk(struct eap_peap_data *data,
u8 *isk, size_t isk_len)
{
@ -454,6 +502,10 @@ static struct wpabuf * eap_peap_build_phase2_tlv(struct eap_sm *sm,
len = 6; /* Result TLV */
if (data->crypto_binding != NO_BINDING)
len += 60; /* Cryptobinding TLV */
#ifdef EAP_TNC
if (data->soh_response)
len += wpabuf_len(data->soh_response);
#endif /* EAP_TNC */
buf = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLV, len,
EAP_CODE_REQUEST, id);
@ -476,6 +528,16 @@ static struct wpabuf * eap_peap_build_phase2_tlv(struct eap_sm *sm,
size_t len[2];
u16 tlv_type;
#ifdef EAP_TNC
if (data->soh_response) {
wpa_printf(MSG_DEBUG, "EAP-PEAP: Adding MS-SOH "
"Response TLV");
wpabuf_put_buf(buf, data->soh_response);
wpabuf_free(data->soh_response);
data->soh_response = NULL;
}
#endif /* EAP_TNC */
if (eap_peap_derive_cmk(sm, data) < 0 ||
os_get_random(data->binding_nonce, 32)) {
wpabuf_free(buf);
@ -563,6 +625,10 @@ static struct wpabuf * eap_peap_buildReq(struct eap_sm *sm, void *priv, u8 id)
case PHASE2_ID:
case PHASE2_METHOD:
return eap_peap_build_phase2_req(sm, data, id);
#ifdef EAP_TNC
case PHASE2_SOH:
return eap_peap_build_phase2_soh(sm, data, id);
#endif /* EAP_TNC */
case PHASE2_TLV:
return eap_peap_build_phase2_tlv(sm, data, id);
case SUCCESS_REQ:
@ -782,6 +848,137 @@ static void eap_peap_process_phase2_tlv(struct eap_sm *sm,
}
#ifdef EAP_TNC
static void eap_peap_process_phase2_soh(struct eap_sm *sm,
struct eap_peap_data *data,
struct wpabuf *in_data)
{
const u8 *pos, *vpos;
size_t left;
const u8 *soh_tlv = NULL;
size_t soh_tlv_len = 0;
int tlv_type, mandatory, tlv_len, vtlv_len;
u8 next_type;
u32 vendor_id;
pos = eap_hdr_validate(EAP_VENDOR_MICROSOFT, 0x21, in_data, &left);
if (pos == NULL) {
wpa_printf(MSG_DEBUG, "EAP-PEAP: Not a valid SoH EAP "
"Extensions Method header - skip TNC");
goto auth_method;
}
/* Parse TLVs */
wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Received TLVs (SoH)", 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 ((size_t) tlv_len > left) {
wpa_printf(MSG_DEBUG, "EAP-PEAP: TLV underrun "
"(tlv_len=%d left=%lu)", tlv_len,
(unsigned long) left);
eap_peap_state(data, FAILURE);
return;
}
switch (tlv_type) {
case EAP_TLV_VENDOR_SPECIFIC_TLV:
if (tlv_len < 4) {
wpa_printf(MSG_DEBUG, "EAP-PEAP: Too short "
"vendor specific TLV (len=%d)",
(int) tlv_len);
eap_peap_state(data, FAILURE);
return;
}
vendor_id = WPA_GET_BE32(pos);
if (vendor_id != EAP_VENDOR_MICROSOFT) {
if (mandatory) {
eap_peap_state(data, FAILURE);
return;
}
break;
}
vpos = pos + 4;
mandatory = !!(vpos[0] & 0x80);
tlv_type = vpos[0] & 0x3f;
tlv_type = (tlv_type << 8) | vpos[1];
vtlv_len = ((int) vpos[2] << 8) | vpos[3];
vpos += 4;
if (vpos + vtlv_len > pos + left) {
wpa_printf(MSG_DEBUG, "EAP-PEAP: Vendor TLV "
"underrun");
eap_peap_state(data, FAILURE);
return;
}
if (tlv_type == 1) {
soh_tlv = vpos;
soh_tlv_len = vtlv_len;
break;
}
wpa_printf(MSG_DEBUG, "EAP-PEAP: Unsupported MS-TLV "
"Type %d%s", tlv_type,
mandatory ? " (mandatory)" : "");
if (mandatory) {
eap_peap_state(data, FAILURE);
return;
}
/* Ignore this TLV, but process other TLVs */
break;
default:
wpa_printf(MSG_DEBUG, "EAP-PEAP: Unsupported TLV Type "
"%d%s", tlv_type,
mandatory ? " (mandatory)" : "");
if (mandatory) {
eap_peap_state(data, FAILURE);
return;
}
/* Ignore this TLV, but process other TLVs */
break;
}
pos += tlv_len;
left -= tlv_len;
}
if (left) {
wpa_printf(MSG_DEBUG, "EAP-PEAP: Last TLV too short in "
"Request (left=%lu)", (unsigned long) left);
eap_peap_state(data, FAILURE);
return;
}
/* Process supported TLVs */
if (soh_tlv) {
int failure = 0;
wpabuf_free(data->soh_response);
data->soh_response = tncs_process_soh(soh_tlv, soh_tlv_len,
&failure);
if (failure) {
eap_peap_state(data, FAILURE);
return;
}
} else {
wpa_printf(MSG_DEBUG, "EAP-PEAP: No SoH TLV received");
eap_peap_state(data, FAILURE);
return;
}
auth_method:
eap_peap_state(data, PHASE2_METHOD);
next_type = sm->user->methods[0].method;
sm->user_eap_method_index = 1;
wpa_printf(MSG_DEBUG, "EAP-PEAP: try EAP type %d", next_type);
eap_peap_phase2_init(sm, data, next_type);
}
#endif /* EAP_TNC */
static void eap_peap_process_phase2_response(struct eap_sm *sm,
struct eap_peap_data *data,
struct wpabuf *in_data)
@ -796,6 +993,13 @@ static void eap_peap_process_phase2_response(struct eap_sm *sm,
return;
}
#ifdef EAP_TNC
if (data->state == PHASE2_SOH) {
eap_peap_process_phase2_soh(sm, data, in_data);
return;
}
#endif /* EAP_TNC */
if (data->phase2_priv == NULL) {
wpa_printf(MSG_DEBUG, "EAP-PEAP: %s - Phase2 not "
"initialized?!", __func__);
@ -882,6 +1086,7 @@ static void eap_peap_process_phase2_response(struct eap_sm *sm,
switch (data->state) {
case PHASE1_ID2:
case PHASE2_ID:
case PHASE2_SOH:
if (eap_user_get(sm, sm->identity, sm->identity_len, 1) != 0) {
wpa_hexdump_ascii(MSG_DEBUG, "EAP_PEAP: Phase2 "
"Identity not found in the user "
@ -892,6 +1097,17 @@ static void eap_peap_process_phase2_response(struct eap_sm *sm,
break;
}
#ifdef EAP_TNC
if (data->state != PHASE2_SOH && sm->tnc &&
data->peap_version == 0) {
eap_peap_state(data, PHASE2_SOH);
wpa_printf(MSG_DEBUG, "EAP-PEAP: Try to initialize "
"TNC (NAP SOH)");
next_type = EAP_TYPE_NONE;
break;
}
#endif /* EAP_TNC */
eap_peap_state(data, PHASE2_METHOD);
next_type = sm->user->methods[0].method;
sm->user_eap_method_index = 1;
@ -1231,6 +1447,7 @@ static void eap_peap_process(struct eap_sm *sm, void *priv,
case PHASE1_ID2:
case PHASE2_ID:
case PHASE2_METHOD:
case PHASE2_SOH:
case PHASE2_TLV:
eap_peap_process_phase2(sm, data, respData, pos, left);
break;

View file

@ -1,5 +1,5 @@
/*
* EAP-TNC - TNCS (IF-IMV and IF-TNCCS)
* EAP-TNC - TNCS (IF-IMV, IF-TNCCS, and IF-TNCCS-SOH)
* Copyright (c) 2007-2008, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
@ -18,6 +18,8 @@
#include "common.h"
#include "base64.h"
#include "tncs.h"
#include "eap_common/eap_tlv_common.h"
#include "eap_common/eap_defs.h"
/* TODO: TNCS must be thread-safe; review the code and add locking etc. if
@ -1230,3 +1232,41 @@ void tncs_global_deinit(void)
os_free(tncs_global_data);
}
struct wpabuf * tncs_build_soh_request(void)
{
struct wpabuf *buf;
/*
* Build a SoH Request TLV (to be used inside SoH EAP Extensions
* Method)
*/
buf = wpabuf_alloc(8 + 4);
if (buf == NULL)
return NULL;
/* Vendor-Specific TLV (Microsoft) - SoH Request */
wpabuf_put_be16(buf, EAP_TLV_VENDOR_SPECIFIC_TLV); /* TLV Type */
wpabuf_put_be16(buf, 8); /* Length */
wpabuf_put_be32(buf, EAP_VENDOR_MICROSOFT); /* Vendor_Id */
wpabuf_put_be16(buf, 0x02); /* TLV Type - SoH Request TLV */
wpabuf_put_be16(buf, 0); /* Length */
return buf;
}
struct wpabuf * tncs_process_soh(const u8 *soh_tlv, size_t soh_tlv_len,
int *failure)
{
wpa_hexdump(MSG_DEBUG, "TNC: SoH TLV", soh_tlv, soh_tlv_len);
*failure = 0;
/* TODO: return MS-SoH Response TLV */
return NULL;
}

View file

@ -1,5 +1,5 @@
/*
* EAP-TNC - TNCS (IF-IMV and IF-TNCCS)
* EAP-TNC - TNCS (IF-IMV, IF-TNCCS, and IF-TNCCS-SOH)
* Copyright (c) 2007-2008, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
@ -42,4 +42,8 @@ enum tncs_process_res tncs_process_if_tnccs(struct tncs_data *tncs,
int tncs_global_init(void);
void tncs_global_deinit(void);
struct wpabuf * tncs_build_soh_request(void);
struct wpabuf * tncs_process_soh(const u8 *soh_tlv, size_t soh_tlv_len,
int *failure);
#endif /* TNCS_H */