TNC: Added preliminary code for IF-TNCCS-SOH server side support
authorJouni Malinen <j@w1.fi>
Sun, 30 Mar 2008 14:10:44 +0000 (17:10 +0300)
committerJouni Malinen <j@w1.fi>
Sun, 30 Mar 2008 14:10:44 +0000 (17:10 +0300)
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.

src/eap_common/eap_defs.h
src/eap_server/eap_peap.c
src/eap_server/tncs.c
src/eap_server/tncs.h

index 6a75139..abf7a9e 100644 (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 */
 };
 
index 081ca34..20e1953 100644 (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;
index ace1a49..7461351 100644 (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;
+}
index f4e2074..18a3a1f 100644 (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 */