hostapd: Fix internal crypto build without TLS
[wpasupplicant] / src / eap_peer / eap_peap.c
index ae8565e..894fc63 100644 (file)
@@ -21,6 +21,8 @@
 #include "eap_config.h"
 #include "tls.h"
 #include "eap_common/eap_tlv_common.h"
+#include "eap_common/eap_peap_common.h"
+#include "tncc.h"
 
 
 /* Maximum supported PEAP version
@@ -58,13 +60,17 @@ struct eap_peap_data {
                                 * EAP-Success and expect AS to send outer
                                 * (unencrypted) EAP-Success after this */
        int resuming; /* starting a resumed session */
+       int reauth; /* reauthentication */
        u8 *key_data;
 
        struct wpabuf *pending_phase2_req;
        enum { NO_BINDING, OPTIONAL_BINDING, REQUIRE_BINDING } crypto_binding;
        int crypto_binding_used;
+       u8 binding_nonce[32];
        u8 ipmk[40];
        u8 cmk[20];
+       int soh; /* Whether IF-TNCCS-SOH (Statement of Health; Microsoft NAP)
+                 * is enabled. */
 };
 
 
@@ -101,6 +107,30 @@ static int eap_peap_parse_phase1(struct eap_peap_data *data,
                           "receiving tunneled EAP-Success");
        }
 
+       if (os_strstr(phase1, "crypto_binding=0")) {
+               data->crypto_binding = NO_BINDING;
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: Do not use cryptobinding");
+       } else if (os_strstr(phase1, "crypto_binding=1")) {
+               data->crypto_binding = OPTIONAL_BINDING;
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: Optional cryptobinding");
+       } else if (os_strstr(phase1, "crypto_binding=2")) {
+               data->crypto_binding = REQUIRE_BINDING;
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: Require cryptobinding");
+       }
+
+#ifdef EAP_TNC
+       if (os_strstr(phase1, "tnc=soh2")) {
+               data->soh = 2;
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: SoH version 2 enabled");
+       } else if (os_strstr(phase1, "tnc=soh1")) {
+               data->soh = 1;
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: SoH version 1 enabled");
+       } else if (os_strstr(phase1, "tnc=soh")) {
+               data->soh = 2;
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: SoH version 2 enabled");
+       }
+#endif /* EAP_TNC */
+
        return 0;
 }
 
@@ -117,7 +147,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;
+       data->crypto_binding = OPTIONAL_BINDING;
 
        if (config && config->phase1 &&
            eap_peap_parse_phase1(data, config->phase1) < 0) {
@@ -217,76 +247,6 @@ static int eap_peap_get_isk(struct eap_sm *sm, struct eap_peap_data *data,
 }
 
 
-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;
@@ -301,6 +261,18 @@ static int eap_peap_derive_cmk(struct eap_sm *sm, struct eap_peap_data *data)
                return -1;
        wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TK", tk, 60);
 
+       if (data->reauth &&
+           tls_connection_resumed(sm->ssl_ctx, data->ssl.conn)) {
+               /* Fast-connect: IPMK|CMK = TK */
+               os_memcpy(data->ipmk, tk, 40);
+               wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IPMK from TK",
+                               data->ipmk, 40);
+               os_memcpy(data->cmk, tk + 40, 20);
+               wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CMK from TK",
+                               data->cmk, 20);
+               return 0;
+       }
+
        if (eap_peap_get_isk(sm, data, isk, sizeof(isk)) < 0)
                return -1;
        wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: ISK", isk, sizeof(isk));
@@ -318,7 +290,6 @@ static int eap_peap_derive_cmk(struct eap_sm *sm, struct eap_peap_data *data)
        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);
@@ -337,11 +308,6 @@ static int eap_tlv_add_cryptobinding(struct eap_sm *sm,
        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);
@@ -359,7 +325,7 @@ static int eap_tlv_add_cryptobinding(struct eap_sm *sm,
        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 */
+       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",
@@ -450,17 +416,24 @@ static int eap_tlv_validate_cryptobinding(struct eap_sm *sm,
                return -1;
        }
        pos += 4;
+       os_memcpy(data->binding_nonce, pos, 32);
        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;
+       wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Compound_MAC data",
+                   buf, sizeof(buf));
        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(MSG_DEBUG, "EAP-PEAP: Received MAC",
+                           pos, SHA1_MAC_LEN);
+               wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Expected MAC",
+                           mac, SHA1_MAC_LEN);
                return -1;
        }
 
@@ -556,6 +529,9 @@ static int eap_tlv_process(struct eap_sm *sm, struct eap_peap_data *data,
                        if (result_tlv == NULL)
                                return -1;
                        force_failure = 1;
+                       crypto_tlv = NULL; /* do not include Cryptobinding TLV
+                                           * in response, if the received
+                                           * cryptobinding was invalid. */
                }
        } else if (!crypto_tlv && data->crypto_binding == REQUIRE_BINDING) {
                wpa_printf(MSG_DEBUG, "EAP-PEAP: No cryptobinding TLV");
@@ -671,6 +647,39 @@ static int eap_peap_phase2_request(struct eap_sm *sm,
                        data->phase2_success = 1;
                }
                break;
+       case EAP_TYPE_EXPANDED:
+#ifdef EAP_TNC
+               if (data->soh) {
+                       const u8 *epos;
+                       size_t eleft;
+
+                       epos = eap_hdr_validate(EAP_VENDOR_MICROSOFT, 0x21,
+                                               req, &eleft);
+                       if (epos) {
+                               struct wpabuf *buf;
+                               wpa_printf(MSG_DEBUG,
+                                          "EAP-PEAP: SoH EAP Extensions");
+                               buf = tncc_process_soh_request(data->soh,
+                                                              epos, eleft);
+                               if (buf) {
+                                       *resp = eap_msg_alloc(
+                                               EAP_VENDOR_MICROSOFT, 0x21,
+                                               wpabuf_len(buf),
+                                               EAP_CODE_RESPONSE,
+                                               hdr->identifier);
+                                       if (*resp == NULL) {
+                                               ret->methodState = METHOD_DONE;
+                                               ret->decision = DECISION_FAIL;
+                                               return -1;
+                                       }
+                                       wpabuf_put_buf(*resp, buf);
+                                       wpabuf_free(buf);
+                                       break;
+                               }
+                       }
+               }
+#endif /* EAP_TNC */
+               /* fall through */
        default:
                if (data->phase2_type.vendor == EAP_VENDOR_IETF &&
                    data->phase2_type.method == EAP_TYPE_NONE) {
@@ -707,11 +716,9 @@ 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) {
@@ -1186,6 +1193,7 @@ static void * eap_peap_init_for_reauth(struct eap_sm *sm, void *priv)
        data->phase2_eap_success = 0;
        data->phase2_eap_started = 0;
        data->resuming = 1;
+       data->reauth = 1;
        sm->peap_done = FALSE;
        return priv;
 }
@@ -1234,9 +1242,14 @@ static u8 * eap_peap_getKey(struct eap_sm *sm, void *priv, size_t *len)
 
        if (data->crypto_binding_used) {
                u8 csk[128];
+               /*
+                * Note: It looks like Microsoft implementation requires null
+                * termination for this label while the one used for deriving
+                * IPMK|CMK did not use null termination.
+                */
                peap_prfplus(data->peap_version, data->ipmk, 40,
                             "Session Key Generating Function",
-                            (u8 *) "", 0, csk, sizeof(csk));
+                            (u8 *) "\00", 1, 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",