diff --git a/src/eap_peer/eap_peap.c b/src/eap_peer/eap_peap.c index c72c79a7b..ae8565eba 100644 --- a/src/eap_peer/eap_peap.c +++ b/src/eap_peer/eap_peap.c @@ -15,6 +15,7 @@ #include "includes.h" #include "common.h" +#include "crypto/sha1.h" #include "eap_i.h" #include "eap_tls_common.h" #include "eap_config.h" @@ -60,6 +61,10 @@ struct eap_peap_data { u8 *key_data; struct wpabuf *pending_phase2_req; + enum { NO_BINDING, OPTIONAL_BINDING, REQUIRE_BINDING } crypto_binding; + int crypto_binding_used; + u8 ipmk[40]; + u8 cmk[20]; }; @@ -112,6 +117,7 @@ static void * eap_peap_init(struct eap_sm *sm) data->peap_version = EAP_PEAP_VERSION; data->force_peap_version = -1; data->peap_outer_success = 2; + data->crypto_binding = NO_BINDING; if (config && config->phase1 && eap_peap_parse_phase1(data, config->phase1) < 0) { @@ -182,6 +188,192 @@ static struct wpabuf * eap_tlv_build_nak(int id, u16 nak_type) } +static int eap_peap_get_isk(struct eap_sm *sm, struct eap_peap_data *data, + u8 *isk, size_t isk_len) +{ + u8 *key; + size_t key_len; + + os_memset(isk, 0, isk_len); + if (data->phase2_method == NULL || data->phase2_priv == NULL || + data->phase2_method->isKeyAvailable == NULL || + data->phase2_method->getKey == NULL) + return 0; + + if (!data->phase2_method->isKeyAvailable(sm, data->phase2_priv) || + (key = data->phase2_method->getKey(sm, data->phase2_priv, + &key_len)) == NULL) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Could not get key material " + "from Phase 2"); + return -1; + } + + if (key_len > isk_len) + key_len = isk_len; + os_memcpy(isk, key, key_len); + os_free(key); + + return 0; +} + + +void peap_prfplus(int version, const u8 *key, size_t key_len, + const char *label, const u8 *seed, size_t seed_len, + u8 *buf, size_t buf_len) +{ + unsigned char counter = 0; + size_t pos, plen; + u8 hash[SHA1_MAC_LEN]; + size_t label_len = os_strlen(label); + u8 extra[2]; + const unsigned char *addr[5]; + size_t len[5]; + + addr[0] = hash; + len[0] = 0; + addr[1] = (unsigned char *) label; + len[1] = label_len; + addr[2] = seed; + len[2] = seed_len; + + if (version == 0) { + /* + * PRF+(K, S, LEN) = T1 | T2 | ... | Tn + * T1 = HMAC-SHA1(K, S | 0x01 | 0x00 | 0x00) + * T2 = HMAC-SHA1(K, T1 | S | 0x02 | 0x00 | 0x00) + * ... + * Tn = HMAC-SHA1(K, Tn-1 | S | n | 0x00 | 0x00) + */ + + extra[0] = 0; + extra[1] = 0; + + addr[3] = &counter; + len[3] = 1; + addr[4] = extra; + len[4] = 2; + } else { + /* + * PRF (K,S,LEN) = T1 | T2 | T3 | T4 | ... where: + * T1 = HMAC-SHA1(K, S | LEN | 0x01) + * T2 = HMAC-SHA1 (K, T1 | S | LEN | 0x02) + * T3 = HMAC-SHA1 (K, T2 | S | LEN | 0x03) + * T4 = HMAC-SHA1 (K, T3 | S | LEN | 0x04) + * ... + */ + + extra[0] = buf_len & 0xff; + + addr[3] = extra; + len[3] = 1; + addr[4] = &counter; + len[4] = 1; + } + + pos = 0; + while (pos < buf_len) { + counter++; + plen = buf_len - pos; + hmac_sha1_vector(key, key_len, 5, addr, len, hash); + if (plen >= SHA1_MAC_LEN) { + os_memcpy(&buf[pos], hash, SHA1_MAC_LEN); + pos += SHA1_MAC_LEN; + } else { + os_memcpy(&buf[pos], hash, plen); + break; + } + len[0] = SHA1_MAC_LEN; + } +} + + +static int eap_peap_derive_cmk(struct eap_sm *sm, struct eap_peap_data *data) +{ + u8 *tk; + u8 isk[32], imck[60]; + + /* + * Tunnel key (TK) is the first 60 octets of the key generated by + * phase 1 of PEAP (based on TLS). + */ + tk = data->key_data; + if (tk == NULL) + return -1; + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TK", tk, 60); + + if (eap_peap_get_isk(sm, data, isk, sizeof(isk)) < 0) + return -1; + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: ISK", isk, sizeof(isk)); + + /* + * IPMK Seed = "Inner Methods Compound Keys" | ISK + * TempKey = First 40 octets of TK + * IPMK|CMK = PRF+(TempKey, IPMK Seed, 60) + * (note: draft-josefsson-pppext-eap-tls-eap-10.txt includes a space + * in the end of the label just before ISK; is that just a typo?) + */ + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TempKey", tk, 40); + peap_prfplus(data->peap_version, tk, 40, "Inner Methods Compound Keys", + isk, sizeof(isk), imck, sizeof(imck)); + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IMCK (IPMKj)", + imck, sizeof(imck)); + + /* TODO: fast-connect: IPMK|CMK = TK */ + os_memcpy(data->ipmk, imck, 40); + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IPMK (S-IPMKj)", data->ipmk, 40); + os_memcpy(data->cmk, imck + 40, 20); + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CMK (CMKj)", data->cmk, 20); + + return 0; +} + + +static int eap_tlv_add_cryptobinding(struct eap_sm *sm, + struct eap_peap_data *data, + struct wpabuf *buf) +{ + u8 *mac; + u8 eap_type = EAP_TYPE_PEAP; + const u8 *addr[2]; + size_t len[2]; + u16 tlv_type; + u8 binding_nonce[32]; + + /* FIX: should binding_nonce be copied from request? */ + if (os_get_random(binding_nonce, 32)) + return -1; + + /* Compound_MAC: HMAC-SHA1-160(cryptobinding TLV | EAP type) */ + addr[0] = wpabuf_put(buf, 0); + len[0] = 60; + addr[1] = &eap_type; + len[1] = 1; + + tlv_type = EAP_TLV_CRYPTO_BINDING_TLV; + if (data->peap_version >= 2) + tlv_type |= EAP_TLV_TYPE_MANDATORY; + wpabuf_put_be16(buf, tlv_type); + wpabuf_put_be16(buf, 56); + + wpabuf_put_u8(buf, 0); /* Reserved */ + wpabuf_put_u8(buf, data->peap_version); /* Version */ + wpabuf_put_u8(buf, data->peap_version); /* RecvVersion */ + wpabuf_put_u8(buf, 1); /* SubType: 0 = Request, 1 = Response */ + wpabuf_put_data(buf, binding_nonce, 32); /* Nonce */ + mac = wpabuf_put(buf, 20); /* Compound_MAC */ + wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC CMK", data->cmk, 20); + wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC data 1", + addr[0], len[0]); + wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC data 2", + addr[1], len[1]); + hmac_sha1_vector(data->cmk, 20, 2, addr, len, mac); + wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC", mac, SHA1_MAC_LEN); + data->crypto_binding_used = 1; + + return 0; +} + + /** * eap_tlv_build_result - Build EAP-TLV Result message * @id: EAP identifier for the header @@ -191,11 +383,21 @@ static struct wpabuf * eap_tlv_build_nak(int id, u16 nak_type) * This funtion builds an EAP-TLV Result message. The caller is responsible for * freeing the returned buffer. */ -static struct wpabuf * eap_tlv_build_result(int id, u16 status) +static struct wpabuf * eap_tlv_build_result(struct eap_sm *sm, + struct eap_peap_data *data, + int crypto_tlv_used, + int id, u16 status) { struct wpabuf *msg; + size_t len; - msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLV, 6, + if (data->crypto_binding == NO_BINDING) + crypto_tlv_used = 0; + + len = 6; + if (crypto_tlv_used) + len += 60; /* Cryptobinding TLV */ + msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLV, len, EAP_CODE_RESPONSE, id); if (msg == NULL) return NULL; @@ -205,10 +407,69 @@ static struct wpabuf * eap_tlv_build_result(int id, u16 status) wpabuf_put_be16(msg, 2); /* Length */ wpabuf_put_be16(msg, status); /* Status */ + if (crypto_tlv_used && eap_tlv_add_cryptobinding(sm, data, msg)) { + wpabuf_free(msg); + return NULL; + } + return msg; } +static int eap_tlv_validate_cryptobinding(struct eap_sm *sm, + struct eap_peap_data *data, + const u8 *crypto_tlv, + size_t crypto_tlv_len) +{ + u8 buf[61], mac[SHA1_MAC_LEN]; + const u8 *pos; + + if (eap_peap_derive_cmk(sm, data) < 0) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Could not derive CMK"); + return -1; + } + + if (crypto_tlv_len != 4 + 56) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Invalid cryptobinding TLV " + "length %d", (int) crypto_tlv_len); + return -1; + } + + pos = crypto_tlv; + pos += 4; /* TLV header */ + if (pos[1] != data->peap_version) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Cryptobinding TLV Version " + "mismatch (was %d; expected %d)", + pos[1], data->peap_version); + return -1; + } + + if (pos[3] != 0) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Unexpected Cryptobinding TLV " + "SubType %d", pos[3]); + return -1; + } + pos += 4; + pos += 32; /* Nonce */ + + /* Compound_MAC: HMAC-SHA1-160(cryptobinding TLV | EAP type) */ + os_memcpy(buf, crypto_tlv, 60); + os_memset(buf + 4 + 4 + 32, 0, 20); /* Compound_MAC */ + buf[60] = EAP_TYPE_PEAP; + hmac_sha1(data->cmk, 20, buf, sizeof(buf), mac); + + if (os_memcmp(mac, pos, SHA1_MAC_LEN) != 0) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Invalid Compound_MAC in " + "cryptobinding TLV"); + return -1; + } + + wpa_printf(MSG_DEBUG, "EAP-PEAP: Valid cryptobinding TLV received"); + + return 0; +} + + /** * eap_tlv_process - Process a received EAP-TLV message and generate a response * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() @@ -223,14 +484,15 @@ static struct wpabuf * eap_tlv_build_result(int id, u16 status) * @force_failure: Force negotiation to fail * Returns: 0 on success, -1 on failure */ -static int eap_tlv_process(struct eap_sm *sm, struct eap_method_ret *ret, +static int eap_tlv_process(struct eap_sm *sm, struct eap_peap_data *data, + struct eap_method_ret *ret, const struct wpabuf *req, struct wpabuf **resp, int force_failure) { size_t left, tlv_len; const u8 *pos; - const u8 *result_tlv = NULL; - size_t result_tlv_len = 0; + const u8 *result_tlv = NULL, *crypto_tlv = NULL; + size_t result_tlv_len = 0, crypto_tlv_len = 0; int tlv_type, mandatory; /* Parse TLVs */ @@ -257,6 +519,10 @@ static int eap_tlv_process(struct eap_sm *sm, struct eap_method_ret *ret, result_tlv = pos; result_tlv_len = tlv_len; break; + case EAP_TLV_CRYPTO_BINDING_TLV: + crypto_tlv = pos; + crypto_tlv_len = tlv_len; + break; default: wpa_printf(MSG_DEBUG, "EAP-TLV: Unsupported TLV Type " "%d%s", tlv_type, @@ -282,6 +548,20 @@ static int eap_tlv_process(struct eap_sm *sm, struct eap_method_ret *ret, } /* Process supported TLVs */ + if (crypto_tlv && data->crypto_binding != NO_BINDING) { + wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Cryptobinding TLV", + crypto_tlv, crypto_tlv_len); + if (eap_tlv_validate_cryptobinding(sm, data, crypto_tlv - 4, + crypto_tlv_len + 4) < 0) { + if (result_tlv == NULL) + return -1; + force_failure = 1; + } + } else if (!crypto_tlv && data->crypto_binding == REQUIRE_BINDING) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: No cryptobinding TLV"); + return -1; + } + if (result_tlv) { int status, resp_status; wpa_hexdump(MSG_DEBUG, "EAP-TLV: Result TLV", @@ -317,7 +597,8 @@ static int eap_tlv_process(struct eap_sm *sm, struct eap_method_ret *ret, } ret->methodState = METHOD_DONE; - *resp = eap_tlv_build_result(eap_get_id(req), resp_status); + *resp = eap_tlv_build_result(sm, data, crypto_tlv != NULL, + eap_get_id(req), resp_status); } return 0; @@ -376,7 +657,7 @@ static int eap_peap_phase2_request(struct eap_sm *sm, break; case EAP_TYPE_TLV: os_memset(&iret, 0, sizeof(iret)); - if (eap_tlv_process(sm, &iret, req, resp, + if (eap_tlv_process(sm, data, &iret, req, resp, data->phase2_eap_started && !data->phase2_eap_success)) { ret->methodState = METHOD_DONE; @@ -426,9 +707,11 @@ static int eap_peap_phase2_request(struct eap_sm *sm, data->phase2_type.method); if (data->phase2_method) { sm->init_phase2 = 1; + sm->mschapv2_full_key = 1; data->phase2_priv = data->phase2_method->init(sm); sm->init_phase2 = 0; + sm->mschapv2_full_key = 0; } } if (data->phase2_priv == NULL || data->phase2_method == NULL) { @@ -883,6 +1166,7 @@ static void eap_peap_deinit_for_reauth(struct eap_sm *sm, void *priv) struct eap_peap_data *data = priv; wpabuf_free(data->pending_phase2_req); data->pending_phase2_req = NULL; + data->crypto_binding_used = 0; } @@ -947,7 +1231,18 @@ static u8 * eap_peap_getKey(struct eap_sm *sm, void *priv, size_t *len) return NULL; *len = EAP_TLS_KEY_LEN; - os_memcpy(key, data->key_data, EAP_TLS_KEY_LEN); + + if (data->crypto_binding_used) { + u8 csk[128]; + peap_prfplus(data->peap_version, data->ipmk, 40, + "Session Key Generating Function", + (u8 *) "", 0, csk, sizeof(csk)); + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CSK", csk, sizeof(csk)); + os_memcpy(key, csk, EAP_TLS_KEY_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Derived key", + key, EAP_TLS_KEY_LEN); + } else + os_memcpy(key, data->key_data, EAP_TLS_KEY_LEN); return key; } diff --git a/src/eap_server/eap_peap.c b/src/eap_server/eap_peap.c index 980d8c3f0..b461d1cb7 100644 --- a/src/eap_server/eap_peap.c +++ b/src/eap_server/eap_peap.c @@ -15,6 +15,7 @@ #include "includes.h" #include "common.h" +#include "sha1.h" #include "eap_i.h" #include "eap_tls_common.h" #include "eap_common/eap_tlv_common.h" @@ -41,11 +42,20 @@ struct eap_peap_data { } state; int peap_version; + int recv_version; const struct eap_method *phase2_method; void *phase2_priv; int force_version; struct wpabuf *pending_phase2_resp; enum { TLV_REQ_NONE, TLV_REQ_SUCCESS, TLV_REQ_FAILURE } tlv_request; + int crypto_binding_sent; + int crypto_binding_used; + enum { NO_BINDING, OPTIONAL_BINDING, REQUIRE_BINDING } crypto_binding; + u8 binding_nonce[32]; + u8 ipmk[40]; + u8 cmk[20]; + u8 *phase2_key; + size_t phase2_key_len; }; @@ -167,6 +177,7 @@ static void * eap_peap_init(struct eap_sm *sm) data->peap_version = data->force_version; } data->state = START; + data->crypto_binding = NO_BINDING; if (eap_server_tls_ssl_init(sm, &data->ssl, 0)) { wpa_printf(MSG_INFO, "EAP-PEAP: Failed to initialize SSL."); @@ -187,6 +198,7 @@ static void eap_peap_reset(struct eap_sm *sm, void *priv) data->phase2_method->reset(sm, data->phase2_priv); eap_server_tls_ssl_deinit(sm, &data->ssl); wpabuf_free(data->pending_phase2_resp); + os_free(data->phase2_key); os_free(data); } @@ -303,13 +315,147 @@ static struct wpabuf * eap_peap_build_phase2_req(struct eap_sm *sm, } +static void eap_peap_get_isk(struct eap_peap_data *data, + u8 *isk, size_t isk_len) +{ + size_t key_len; + + os_memset(isk, 0, isk_len); + if (data->phase2_key == NULL) + return; + + key_len = data->phase2_key_len; + if (key_len > isk_len) + key_len = isk_len; + os_memcpy(isk, data->phase2_key, key_len); +} + + +void peap_prfplus(int version, const u8 *key, size_t key_len, + const char *label, const u8 *seed, size_t seed_len, + u8 *buf, size_t buf_len) +{ + unsigned char counter = 0; + size_t pos, plen; + u8 hash[SHA1_MAC_LEN]; + size_t label_len = os_strlen(label); + u8 extra[2]; + const unsigned char *addr[5]; + size_t len[5]; + + addr[0] = hash; + len[0] = 0; + addr[1] = (unsigned char *) label; + len[1] = label_len; + addr[2] = seed; + len[2] = seed_len; + + if (version == 0) { + /* + * PRF+(K, S, LEN) = T1 | T2 | ... | Tn + * T1 = HMAC-SHA1(K, S | 0x01 | 0x00 | 0x00) + * T2 = HMAC-SHA1(K, T1 | S | 0x02 | 0x00 | 0x00) + * ... + * Tn = HMAC-SHA1(K, Tn-1 | S | n | 0x00 | 0x00) + */ + + extra[0] = 0; + extra[1] = 0; + + addr[3] = &counter; + len[3] = 1; + addr[4] = extra; + len[4] = 2; + } else { + /* + * PRF (K,S,LEN) = T1 | T2 | T3 | T4 | ... where: + * T1 = HMAC-SHA1(K, S | LEN | 0x01) + * T2 = HMAC-SHA1 (K, T1 | S | LEN | 0x02) + * T3 = HMAC-SHA1 (K, T2 | S | LEN | 0x03) + * T4 = HMAC-SHA1 (K, T3 | S | LEN | 0x04) + * ... + */ + + extra[0] = buf_len & 0xff; + + addr[3] = extra; + len[3] = 1; + addr[4] = &counter; + len[4] = 1; + } + + pos = 0; + while (pos < buf_len) { + counter++; + plen = buf_len - pos; + hmac_sha1_vector(key, key_len, 5, addr, len, hash); + if (plen >= SHA1_MAC_LEN) { + os_memcpy(&buf[pos], hash, SHA1_MAC_LEN); + pos += SHA1_MAC_LEN; + } else { + os_memcpy(&buf[pos], hash, plen); + break; + } + len[0] = SHA1_MAC_LEN; + } +} + + +static int eap_peap_derive_cmk(struct eap_sm *sm, struct eap_peap_data *data) +{ + u8 *tk; + u8 isk[32], imck[60]; + + /* + * Tunnel key (TK) is the first 60 octets of the key generated by + * phase 1 of PEAP (based on TLS). + */ + tk = eap_server_tls_derive_key(sm, &data->ssl, "client EAP encryption", + EAP_TLS_KEY_LEN); + if (tk == NULL) + return -1; + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TK", tk, 60); + + eap_peap_get_isk(data, isk, sizeof(isk)); + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: ISK", isk, sizeof(isk)); + + /* + * IPMK Seed = "Inner Methods Compound Keys" | ISK + * TempKey = First 40 octets of TK + * IPMK|CMK = PRF+(TempKey, IPMK Seed, 60) + * (note: draft-josefsson-pppext-eap-tls-eap-10.txt includes a space + * in the end of the label just before ISK; is that just a typo?) + */ + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TempKey", tk, 40); + peap_prfplus(data->peap_version, tk, 40, "Inner Methods Compound Keys", + isk, sizeof(isk), imck, sizeof(imck)); + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IMCK (IPMKj)", + imck, sizeof(imck)); + + os_free(tk); + + /* TODO: fast-connect: IPMK|CMK = TK */ + os_memcpy(data->ipmk, imck, 40); + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IPMK (S-IPMKj)", data->ipmk, 40); + os_memcpy(data->cmk, imck + 40, 20); + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CMK (CMKj)", data->cmk, 20); + + return 0; +} + + static struct wpabuf * eap_peap_build_phase2_tlv(struct eap_sm *sm, struct eap_peap_data *data, u8 id) { struct wpabuf *buf, *encr_req; + size_t len; - buf = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLV, 6, + len = 6; /* Result TLV */ + if (data->crypto_binding != NO_BINDING) + len += 60; /* Cryptobinding TLV */ + + buf = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLV, len, EAP_CODE_REQUEST, id); if (buf == NULL) return NULL; @@ -322,6 +468,50 @@ static struct wpabuf * eap_peap_build_phase2_tlv(struct eap_sm *sm, wpabuf_put_be16(buf, data->tlv_request == TLV_REQ_SUCCESS ? EAP_TLV_RESULT_SUCCESS : EAP_TLV_RESULT_FAILURE); + if (data->peap_version == 0 && data->tlv_request == TLV_REQ_SUCCESS && + data->crypto_binding != NO_BINDING) { + u8 *mac; + u8 eap_type = EAP_TYPE_PEAP; + const u8 *addr[2]; + size_t len[2]; + u16 tlv_type; + + if (eap_peap_derive_cmk(sm, data) < 0 || + os_get_random(data->binding_nonce, 32)) { + wpabuf_free(buf); + return NULL; + } + + /* Compound_MAC: HMAC-SHA1-160(cryptobinding TLV | EAP type) */ + addr[0] = wpabuf_put(buf, 0); + len[0] = 60; + addr[1] = &eap_type; + len[1] = 1; + + tlv_type = EAP_TLV_CRYPTO_BINDING_TLV; + if (data->peap_version >= 2) + tlv_type |= EAP_TLV_TYPE_MANDATORY; + wpabuf_put_be16(buf, tlv_type); + wpabuf_put_be16(buf, 56); + + wpabuf_put_u8(buf, 0); /* Reserved */ + wpabuf_put_u8(buf, data->peap_version); /* Version */ + wpabuf_put_u8(buf, data->recv_version); /* RecvVersion */ + wpabuf_put_u8(buf, 0); /* SubType: 0 = Request, 1 = Response */ + wpabuf_put_data(buf, data->binding_nonce, 32); /* Nonce */ + mac = wpabuf_put(buf, 20); /* Compound_MAC */ + wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC CMK", + data->cmk, 20); + wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC data 1", + addr[0], len[0]); + wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC data 2", + addr[1], len[1]); + hmac_sha1_vector(data->cmk, 20, 2, addr, len, mac); + wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC", + mac, SHA1_MAC_LEN); + data->crypto_binding_sent = 1; + } + wpa_hexdump_buf_key(MSG_DEBUG, "EAP-PEAP: Encrypting Phase 2 TLV data", buf); @@ -423,14 +613,66 @@ static int eap_peap_phase2_init(struct eap_sm *sm, struct eap_peap_data *data, } +static int eap_tlv_validate_cryptobinding(struct eap_sm *sm, + struct eap_peap_data *data, + const u8 *crypto_tlv, + size_t crypto_tlv_len) +{ + u8 buf[61], mac[SHA1_MAC_LEN]; + const u8 *pos; + + if (crypto_tlv_len != 4 + 56) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Invalid cryptobinding TLV " + "length %d", (int) crypto_tlv_len); + return -1; + } + + pos = crypto_tlv; + pos += 4; /* TLV header */ + if (pos[1] != data->peap_version) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Cryptobinding TLV Version " + "mismatch (was %d; expected %d)", + pos[1], data->peap_version); + return -1; + } + + if (pos[3] != 1) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Unexpected Cryptobinding TLV " + "SubType %d", pos[3]); + return -1; + } + pos += 4; + pos += 32; /* Nonce */ + + /* Compound_MAC: HMAC-SHA1-160(cryptobinding TLV | EAP type) */ + os_memcpy(buf, crypto_tlv, 60); + os_memset(buf + 4 + 4 + 32, 0, 20); /* Compound_MAC */ + buf[60] = EAP_TYPE_PEAP; + hmac_sha1(data->cmk, 20, buf, sizeof(buf), mac); + + if (os_memcmp(mac, pos, SHA1_MAC_LEN) != 0) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Invalid Compound_MAC in " + "cryptobinding TLV"); + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CMK", data->cmk, 20); + wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Cryptobinding seed data", + buf, 61); + return -1; + } + + wpa_printf(MSG_DEBUG, "EAP-PEAP: Valid cryptobinding TLV received"); + + return 0; +} + + static void eap_peap_process_phase2_tlv(struct eap_sm *sm, struct eap_peap_data *data, struct wpabuf *in_data) { const u8 *pos; size_t left; - const u8 *result_tlv = NULL; - size_t result_tlv_len = 0; + const u8 *result_tlv = NULL, *crypto_tlv = NULL; + size_t result_tlv_len = 0, crypto_tlv_len = 0; int tlv_type, mandatory, tlv_len; pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TLV, in_data, &left); @@ -440,7 +682,7 @@ static void eap_peap_process_phase2_tlv(struct eap_sm *sm, } /* Parse TLVs */ - wpa_hexdump(MSG_DEBUG, "EAP-TLV: Received TLVs", pos, left); + wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Received TLVs", pos, left); while (left >= 4) { mandatory = !!(pos[0] & 0x80); tlv_type = pos[0] & 0x3f; @@ -449,7 +691,7 @@ static void eap_peap_process_phase2_tlv(struct eap_sm *sm, pos += 4; left -= 4; if ((size_t) tlv_len > left) { - wpa_printf(MSG_DEBUG, "EAP-TLV: TLV underrun " + wpa_printf(MSG_DEBUG, "EAP-PEAP: TLV underrun " "(tlv_len=%d left=%lu)", tlv_len, (unsigned long) left); eap_peap_state(data, FAILURE); @@ -460,8 +702,12 @@ static void eap_peap_process_phase2_tlv(struct eap_sm *sm, result_tlv = pos; result_tlv_len = tlv_len; break; + case EAP_TLV_CRYPTO_BINDING_TLV: + crypto_tlv = pos; + crypto_tlv_len = tlv_len; + break; default: - wpa_printf(MSG_DEBUG, "EAP-TLV: Unsupported TLV Type " + wpa_printf(MSG_DEBUG, "EAP-PEAP: Unsupported TLV Type " "%d%s", tlv_type, mandatory ? " (mandatory)" : ""); if (mandatory) { @@ -476,21 +722,37 @@ static void eap_peap_process_phase2_tlv(struct eap_sm *sm, left -= tlv_len; } if (left) { - wpa_printf(MSG_DEBUG, "EAP-TLV: Last TLV too short in " + 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 (crypto_tlv && data->crypto_binding_sent) { + wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Cryptobinding TLV", + crypto_tlv, crypto_tlv_len); + if (eap_tlv_validate_cryptobinding(sm, data, crypto_tlv - 4, + crypto_tlv_len + 4) < 0) { + eap_peap_state(data, FAILURE); + return; + } + data->crypto_binding_used = 1; + } else if (!crypto_tlv && data->crypto_binding_sent && + data->crypto_binding == REQUIRE_BINDING) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: No cryptobinding TLV"); + eap_peap_state(data, FAILURE); + return; + } + if (result_tlv) { int status; const char *requested; - wpa_hexdump(MSG_DEBUG, "EAP-TLV: Result TLV", + wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Result TLV", result_tlv, result_tlv_len); if (result_tlv_len < 2) { - wpa_printf(MSG_INFO, "EAP-TLV: Too short Result TLV " + wpa_printf(MSG_INFO, "EAP-PEAP: Too short Result TLV " "(len=%lu)", (unsigned long) result_tlv_len); eap_peap_state(data, FAILURE); @@ -500,7 +762,7 @@ static void eap_peap_process_phase2_tlv(struct eap_sm *sm, "Failure"; status = WPA_GET_BE16(result_tlv); if (status == EAP_TLV_RESULT_SUCCESS) { - wpa_printf(MSG_INFO, "EAP-TLV: TLV Result - Success " + wpa_printf(MSG_INFO, "EAP-PEAP: TLV Result - Success " "- requested %s", requested); if (data->tlv_request == TLV_REQ_SUCCESS) eap_peap_state(data, SUCCESS); @@ -508,11 +770,11 @@ static void eap_peap_process_phase2_tlv(struct eap_sm *sm, eap_peap_state(data, FAILURE); } else if (status == EAP_TLV_RESULT_FAILURE) { - wpa_printf(MSG_INFO, "EAP-TLV: TLV Result - Failure - " - "requested %s", requested); + wpa_printf(MSG_INFO, "EAP-PEAP: TLV Result - Failure " + "- requested %s", requested); eap_peap_state(data, FAILURE); } else { - wpa_printf(MSG_INFO, "EAP-TLV: Unknown TLV Result " + wpa_printf(MSG_INFO, "EAP-PEAP: Unknown TLV Result " "Status %d", status); eap_peap_state(data, FAILURE); } @@ -589,6 +851,19 @@ static void eap_peap_process_phase2_response(struct eap_sm *sm, return; } + os_free(data->phase2_key); + if (data->phase2_method->getKey) { + data->phase2_key = data->phase2_method->getKey( + sm, data->phase2_priv, &data->phase2_key_len); + if (data->phase2_key == NULL) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase2 getKey " + "failed"); + eap_peap_req_failure(sm, data); + eap_peap_phase2_init(sm, data, EAP_TYPE_NONE); + return; + } + } + switch (data->state) { case PHASE1_ID2: case PHASE2_ID: @@ -881,7 +1156,7 @@ static void eap_peap_process(struct eap_sm *sm, void *priv, wpa_printf(MSG_DEBUG, "EAP-PEAP: Received packet(len=%lu) - " "Flags 0x%02x", (unsigned long) wpabuf_len(respData), flags); - peer_version = flags & EAP_PEAP_VERSION_MASK; + data->recv_version = peer_version = flags & EAP_PEAP_VERSION_MASK; if (data->force_version >= 0 && peer_version != data->force_version) { wpa_printf(MSG_INFO, "EAP-PEAP: peer did not select the forced" " version (forced=%d peer=%d) - reject", @@ -979,6 +1254,26 @@ static u8 * eap_peap_getKey(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; + if (data->crypto_binding_used) { + u8 csk[128]; + peap_prfplus(data->peap_version, data->ipmk, 40, + "Session Key Generating Function", + (u8 *) "", 0, csk, sizeof(csk)); + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CSK", csk, sizeof(csk)); + eapKeyData = os_malloc(EAP_TLS_KEY_LEN); + if (eapKeyData) { + os_memcpy(eapKeyData, csk, EAP_TLS_KEY_LEN); + *len = EAP_TLS_KEY_LEN; + wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Derived key", + eapKeyData, EAP_TLS_KEY_LEN); + } else { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Failed to derive " + "key"); + } + + return eapKeyData; + } + /* TODO: PEAPv1 - different label in some cases */ eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, "client EAP encryption",