Browse Source

tests: TNC testing

This implements minimal IMC and IMV to allow TNC testing with PEAP (SoH)
and TTLS/FAST with EAP-TNC.

Signed-off-by: Jouni Malinen <j@w1.fi>
Jouni Malinen 10 years ago
parent
commit
dbd1e184e3

+ 33 - 0
src/common/tnc.h

@@ -32,10 +32,43 @@ typedef TNC_Result (*TNC_TNCS_BindFunctionPointer)(
 	TNC_IMVID imvID,
 	char *functionName,
 	void **pOutfunctionPointer);
+typedef TNC_Result (*TNC_TNCS_ReportMessageTypesPointer)(
+	TNC_IMVID imvID,
+	TNC_MessageTypeList supportedTypes,
+	TNC_UInt32 typeCount);
+typedef TNC_Result (*TNC_TNCS_SendMessagePointer)(
+	TNC_IMVID imvID,
+	TNC_ConnectionID connectionID,
+	TNC_BufferReference message,
+	TNC_UInt32 messageLength,
+	TNC_MessageType messageType);
+typedef TNC_Result (*TNC_TNCS_RequestHandshakeRetryPointer)(
+	TNC_IMVID imvID,
+	TNC_ConnectionID connectionID,
+	TNC_RetryReason reason);
+typedef TNC_Result (*TNC_TNCS_ProvideRecommendationPointer)(
+	TNC_IMVID imvID,
+	TNC_ConnectionID connectionID,
+	TNC_IMV_Action_Recommendation recommendation,
+	TNC_IMV_Evaluation_Result evaluation);
 typedef TNC_Result (*TNC_TNCC_BindFunctionPointer)(
 	TNC_IMCID imcID,
 	char *functionName,
 	void **pOutfunctionPointer);
+typedef TNC_Result (*TNC_TNCC_SendMessagePointer)(
+	TNC_IMCID imcID,
+	TNC_ConnectionID connectionID,
+	TNC_BufferReference message,
+	TNC_UInt32 messageLength,
+	TNC_MessageType messageType);
+typedef TNC_Result (*TNC_TNCC_ReportMessageTypesPointer)(
+	TNC_IMCID imcID,
+	TNC_MessageTypeList supportedTypes,
+	TNC_UInt32 typeCount);
+typedef TNC_Result (*TNC_TNCC_RequestHandshakeRetryPointer)(
+	TNC_IMCID imcID,
+	TNC_ConnectionID connectionID,
+	TNC_RetryReason reason);
 
 #define TNC_IFIMV_VERSION_1 1
 #define TNC_IFIMC_VERSION_1 1

+ 4 - 0
tests/hwsim/build.sh

@@ -22,3 +22,7 @@ make -j8
 cd ../mac80211_hwsim/tools
 make clean
 make -j8
+cd ../../tests/hwsim/tnc
+make clean
+make -j8
+cd ..

+ 1 - 0
tests/hwsim/example-hostapd.config

@@ -29,6 +29,7 @@ CONFIG_EAP_VENDOR_TEST=y
 CONFIG_EAP_FAST=y
 CONFIG_EAP_IKEV2=y
 CONFIG_EAP_TNC=y
+CFLAGS += -DTNC_CONFIG_FILE=\"tnc/tnc_config\"
 CONFIG_EAP_UNAUTH_TLS=y
 ifeq ($(CONFIG_TLS), openssl)
 CONFIG_EAP_PWD=y

+ 1 - 0
tests/hwsim/example-wpa_supplicant.config

@@ -27,6 +27,7 @@ CONFIG_EAP_GPSK=y
 CONFIG_EAP_GPSK_SHA256=y
 CONFIG_EAP_EKE=y
 CONFIG_EAP_TNC=y
+CFLAGS += -DTNC_CONFIG_FILE=\"tnc/tnc_config\"
 CONFIG_EAP_FAST=y
 CONFIG_EAP_IKEV2=y
 

+ 93 - 0
tests/hwsim/test_tnc.py

@@ -0,0 +1,93 @@
+# -*- coding: utf-8 -*-
+# TNC tests
+# Copyright (c) 2014, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import os.path
+
+import hostapd
+from test_ap_eap import int_eap_server_params
+
+def test_tnc_peap_soh(dev, apdev):
+    """TNC PEAP-SoH"""
+    params = int_eap_server_params()
+    params["tnc"] = "1"
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
+                   eap="PEAP", identity="user", password="password",
+                   ca_cert="auth_serv/ca.pem",
+                   phase1="peapver=0 tnc=soh cryptobinding=0",
+                   phase2="auth=MSCHAPV2",
+                   wait_connect=False)
+    ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=10)
+    if ev is None:
+        raise Exception("Connection timed out")
+
+    dev[1].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
+                   eap="PEAP", identity="user", password="password",
+                   ca_cert="auth_serv/ca.pem",
+                   phase1="peapver=0 tnc=soh1 cryptobinding=1",
+                   phase2="auth=MSCHAPV2",
+                   wait_connect=False)
+    ev = dev[1].wait_event(["CTRL-EVENT-CONNECTED"], timeout=10)
+    if ev is None:
+        raise Exception("Connection timed out")
+
+    dev[2].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
+                   eap="PEAP", identity="user", password="password",
+                   ca_cert="auth_serv/ca.pem",
+                   phase1="peapver=0 tnc=soh2 cryptobinding=2",
+                   phase2="auth=MSCHAPV2",
+                   wait_connect=False)
+    ev = dev[2].wait_event(["CTRL-EVENT-CONNECTED"], timeout=10)
+    if ev is None:
+        raise Exception("Connection timed out")
+
+def test_tnc_ttls(dev, apdev):
+    """TNC TTLS"""
+    params = int_eap_server_params()
+    params["tnc"] = "1"
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    if not os.path.exists("tnc/libhostap_imc.so"):
+        logger.info("No IMC installed - skip")
+        return "skip"
+
+    dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
+                   eap="TTLS", identity="DOMAIN\mschapv2 user",
+                   anonymous_identity="ttls", password="password",
+                   phase2="auth=MSCHAPV2",
+                   ca_cert="auth_serv/ca.pem",
+                   wait_connect=False)
+    ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=10)
+    if ev is None:
+        raise Exception("Connection timed out")
+
+def test_tnc_fast(dev, apdev):
+    """TNC FAST"""
+    params = int_eap_server_params()
+    params["tnc"] = "1"
+    params["pac_opaque_encr_key"] ="000102030405060708090a0b0c0d0e00"
+    params["eap_fast_a_id"] = "101112131415161718191a1b1c1d1e00"
+    params["eap_fast_a_id_info"] = "test server2"
+
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    if not os.path.exists("tnc/libhostap_imc.so"):
+        logger.info("No IMC installed - skip")
+        return "skip"
+
+    dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
+                   eap="FAST", identity="user",
+                   anonymous_identity="FAST", password="password",
+                   phase2="auth=GTC",
+                   phase1="fast_provisioning=2",
+                   pac_file="blob://fast_pac_auth_tnc",
+                   ca_cert="auth_serv/ca.pem",
+                   wait_connect=False)
+    ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=10)
+    if ev is None:
+        raise Exception("Connection timed out")

+ 11 - 0
tests/hwsim/tnc/Makefile

@@ -0,0 +1,11 @@
+CFLAGS += -I$(abspath ../../../src)
+CFLAGS += -I$(abspath ../../../src/utils)
+
+ALL=libhostap_imc.so libhostap_imv.so libhostap2_imc.so libhostap2_imv.so
+all: $(ALL)
+
+lib%.so: %.c
+	$(CC) $(LDFLAGS) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $<
+
+clean:
+	rm -f $(ALL)

+ 183 - 0
tests/hwsim/tnc/hostap2_imc.c

@@ -0,0 +1,183 @@
+/*
+ * Example IMC for TNC testing
+ * Copyright (c) 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 "common/tnc.h"
+
+static int initialized = 0;
+static TNC_IMCID my_id = -1;
+static TNC_TNCC_SendMessagePointer send_message = NULL;
+static TNC_TNCC_ReportMessageTypesPointer report_message_types = NULL;
+static TNC_TNCC_RequestHandshakeRetryPointer request_retry = NULL;
+
+static TNC_MessageType message_types[] =
+{
+	(TNC_VENDORID_ANY << 8) | TNC_SUBTYPE_ANY
+};
+
+
+TNC_Result TNC_IMC_Initialize(
+	/*in*/ TNC_IMCID imcID,
+	/*in*/ TNC_Version minVersion,
+	/*in*/ TNC_Version maxVersion,
+	/*out*/ TNC_Version *pOutActualVersion)
+{
+	wpa_printf(MSG_INFO,
+		   "IMC(hostap2) %s(imcID=%u, minVersion=%u, maxVersion=%u)",
+		   __func__, (unsigned) imcID, (unsigned) minVersion,
+		   (unsigned) maxVersion);
+
+	if (initialized)
+		return TNC_RESULT_ALREADY_INITIALIZED;
+
+	if (minVersion < TNC_IFIMC_VERSION_1 ||
+	    maxVersion > TNC_IFIMC_VERSION_1)
+		return TNC_RESULT_NO_COMMON_VERSION;
+
+	if (!pOutActualVersion)
+		return TNC_RESULT_INVALID_PARAMETER;
+	*pOutActualVersion = TNC_IFIMC_VERSION_1;
+	my_id = imcID;
+
+	initialized = 1;
+
+	return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_IMC_BeginHandshake(
+	/*in*/ TNC_IMCID imcID,
+	/*in*/ TNC_ConnectionID connectionID)
+{
+	char *msg = "hello";
+	TNC_Result res;
+
+	wpa_printf(MSG_INFO, "IMC(hostap2) %s(imcID=%u, connectionID=%u)",
+		   __func__, (unsigned) imcID, (unsigned) connectionID);
+
+	if (!initialized)
+		return TNC_RESULT_NOT_INITIALIZED;
+
+	if (imcID != my_id)
+		return TNC_RESULT_INVALID_PARAMETER;
+
+	if (!send_message)
+		return TNC_RESULT_FATAL;
+
+	res = send_message(imcID, connectionID, msg, os_strlen(msg), 1);
+	if (res != TNC_RESULT_SUCCESS)
+		return res;
+
+	return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_IMC_ProvideBindFunction(
+	/*in*/ TNC_IMCID imcID,
+	/*in*/ TNC_TNCC_BindFunctionPointer bindFunction)
+{
+	TNC_Result res;
+
+	wpa_printf(MSG_INFO, "IMC(hostap2) %s(imcID=%u)",
+		   __func__, (unsigned) imcID);
+
+	if (!initialized)
+		return TNC_RESULT_NOT_INITIALIZED;
+
+	if (imcID != my_id || !bindFunction)
+		return TNC_RESULT_INVALID_PARAMETER;
+
+	if (bindFunction(imcID, "TNC_TNCC_SendMessage",
+			 (void **) &send_message) != TNC_RESULT_SUCCESS ||
+	    !send_message)
+		return TNC_RESULT_FATAL;
+
+	if (bindFunction(imcID, "TNC_TNCC_ReportMessageTypes",
+			 (void **) &report_message_types) !=
+	    TNC_RESULT_SUCCESS ||
+	    !report_message_types)
+		return TNC_RESULT_FATAL;
+
+	if (bindFunction(imcID, "TNC_TNCC_RequestHandshakeRetry",
+			 (void **) &request_retry) != TNC_RESULT_SUCCESS ||
+	    !request_retry)
+		return TNC_RESULT_FATAL;
+
+	res = report_message_types(imcID, message_types,
+				   ARRAY_SIZE(message_types));
+	if (res != TNC_RESULT_SUCCESS)
+		return res;
+
+	return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_IMC_NotifyConnectionChange(
+	/*in*/ TNC_IMCID imcID,
+	/*in*/ TNC_ConnectionID connectionID,
+	/*in*/ TNC_ConnectionState newState)
+{
+	wpa_printf(MSG_INFO,
+		   "IMC(hostap2) %s(imcID=%u, connectionID=%u, newState=%u)",
+		   __func__, (unsigned) imcID, (unsigned) connectionID,
+		   (unsigned) newState);
+
+	return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_IMC_ReceiveMessage(
+	/*in*/ TNC_IMCID imcID,
+	/*in*/ TNC_ConnectionID connectionID,
+	/*in*/ TNC_BufferReference message,
+	/*in*/ TNC_UInt32 messageLength,
+	/*in*/ TNC_MessageType messageType)
+{
+	TNC_Result res;
+
+	wpa_printf(MSG_INFO,
+		   "IMC(hostap2) %s(imcID=%u, connectionID=%u, messageType=%u)",
+		   __func__, (unsigned) imcID, (unsigned) connectionID,
+		   (unsigned) messageType);
+	wpa_hexdump_ascii(MSG_INFO, "IMC(hostap2) message",
+			  message, messageLength);
+
+	if (messageType == 1 && messageLength == 5 &&
+	    os_memcmp(message, "hello", 5) == 0) {
+		char *msg = "i'm fine";
+
+		res = send_message(imcID, connectionID, msg, os_strlen(msg), 1);
+		if (res != TNC_RESULT_SUCCESS)
+			return res;
+	}
+
+	return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_IMC_BatchEnding(
+	/*in*/ TNC_IMCID imcID,
+	/*in*/ TNC_ConnectionID connectionID)
+{
+	wpa_printf(MSG_INFO, "IMC(hostap2) %s(imcID=%u, connectionID=%u)",
+		   __func__, (unsigned) imcID, (unsigned) connectionID);
+
+	return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_IMC_Terminate(
+	/*in*/ TNC_IMCID imcID)
+{
+	wpa_printf(MSG_INFO, "IMC(hostap2) %s(imcID=%u)",
+		   __func__, (unsigned) imcID);
+
+	return TNC_RESULT_SUCCESS;
+}

+ 203 - 0
tests/hwsim/tnc/hostap2_imv.c

@@ -0,0 +1,203 @@
+/*
+ * Example IMV for TNC testing
+ * Copyright (c) 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 "common/tnc.h"
+
+static int initialized = 0;
+static TNC_IMVID my_id = -1;
+static TNC_TNCS_ReportMessageTypesPointer report_message_types = NULL;
+static TNC_TNCS_SendMessagePointer send_message = NULL;
+static TNC_TNCS_RequestHandshakeRetryPointer request_retry = NULL;
+TNC_TNCS_ProvideRecommendationPointer provide_recomm = NULL;
+
+static TNC_MessageType message_types[] =
+{
+	(TNC_VENDORID_ANY << 8) | TNC_SUBTYPE_ANY
+};
+
+
+TNC_Result TNC_IMV_Initialize(
+	/*in*/ TNC_IMVID imvID,
+	/*in*/ TNC_Version minVersion,
+	/*in*/ TNC_Version maxVersion,
+	/*out*/ TNC_Version *pOutActualVersion)
+{
+	wpa_printf(MSG_INFO,
+		   "IMV(hostap2) %s(imvID=%u, minVersion=%u, maxVersion=%u)",
+		   __func__, (unsigned) imvID, (unsigned) minVersion,
+		   (unsigned) maxVersion);
+
+	if (initialized)
+		return TNC_RESULT_ALREADY_INITIALIZED;
+
+	if (minVersion < TNC_IFIMV_VERSION_1 ||
+	    maxVersion > TNC_IFIMV_VERSION_1)
+		return TNC_RESULT_NO_COMMON_VERSION;
+
+	if (!pOutActualVersion)
+		return TNC_RESULT_INVALID_PARAMETER;
+	*pOutActualVersion = TNC_IFIMV_VERSION_1;
+
+	initialized = 1;
+	my_id = imvID;
+
+	return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_IMV_NotifyConnectionChange(
+	/*in*/ TNC_IMVID imvID,
+	/*in*/ TNC_ConnectionID connectionID,
+	/*in*/ TNC_ConnectionState newState)
+{
+	wpa_printf(MSG_INFO,
+		   "IMV(hostap2) %s(imvID=%u, connectionID=%u, newState=%u)",
+		   __func__, (unsigned) imvID, (unsigned) connectionID,
+		   (unsigned) newState);
+
+	if (!initialized)
+		return TNC_RESULT_NOT_INITIALIZED;
+
+	if (imvID != my_id)
+		return TNC_RESULT_INVALID_PARAMETER;
+
+	/* TODO: call TNC_TNCS_ProvideRecommendation */
+
+	return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_IMV_ReceiveMessage(
+	/*in*/ TNC_IMVID imvID,
+	/*in*/ TNC_ConnectionID connectionID,
+	/*in*/ TNC_BufferReference message,
+	/*in*/ TNC_UInt32 messageLength,
+	/*in*/ TNC_MessageType messageType)
+{
+	TNC_Result res;
+
+	wpa_printf(MSG_INFO,
+		   "IMV(hostap2) %s(imvID=%u, connectionID=%u, messageType=%u)",
+		   __func__, (unsigned) imvID, (unsigned) connectionID,
+		   (unsigned) messageType);
+	wpa_hexdump_ascii(MSG_INFO, "IMV(hostap2) message",
+			  message, messageLength);
+
+	if (!send_message)
+		return TNC_RESULT_FATAL;
+
+	if (messageType == 1 && messageLength == 5 &&
+	    os_memcmp(message, "hello", 5) == 0) {
+		char *msg = "hello";
+
+		res = send_message(imvID, connectionID, msg, os_strlen(msg), 1);
+		if (res != TNC_RESULT_SUCCESS)
+			return res;
+	}
+
+	if (messageType == 1 && messageLength == 8 &&
+	    os_memcmp(message, "i'm fine", 8) == 0) {
+		if (!provide_recomm)
+			return TNC_RESULT_FATAL;
+		res = provide_recomm(imvID, connectionID,
+				     TNC_IMV_ACTION_RECOMMENDATION_ALLOW,
+				     TNC_IMV_EVALUATION_RESULT_COMPLIANT);
+		if (res != TNC_RESULT_SUCCESS)
+			return res;
+	}
+
+	return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_IMV_SolicitRecommendation(
+	/*in*/ TNC_IMVID imvID,
+	/*in*/ TNC_ConnectionID connectionID)
+{
+	wpa_printf(MSG_INFO, "IMV(hostap2) %s(imvID=%u, connectionID=%u)",
+		   __func__, (unsigned) imvID, (unsigned) connectionID);
+
+	if (!initialized)
+		return TNC_RESULT_NOT_INITIALIZED;
+
+	if (imvID != my_id)
+		return TNC_RESULT_INVALID_PARAMETER;
+
+	/* TODO: call TNC_TNCS_ProvideRecommendation */
+
+	return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_IMV_BatchEnding(
+	/*in*/ TNC_IMVID imvID,
+	/*in*/ TNC_ConnectionID connectionID)
+{
+	wpa_printf(MSG_INFO, "IMV(hostap2) %s(imvID=%u, connectionID=%u)",
+		   __func__, (unsigned) imvID, (unsigned) connectionID);
+
+	return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_IMV_Terminate(
+	/*in*/ TNC_IMVID imvID)
+{
+	wpa_printf(MSG_INFO, "IMV(hostap2) %s(imvID=%u)",
+		   __func__, (unsigned) imvID);
+
+	return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_IMV_ProvideBindFunction(
+	/*in*/ TNC_IMVID imvID,
+	/*in*/ TNC_TNCS_BindFunctionPointer bindFunction)
+{
+	TNC_Result res;
+
+	wpa_printf(MSG_INFO, "IMV(hostap2) %s(imvID=%u)",
+		   __func__, (unsigned) imvID);
+
+	if (!initialized)
+		return TNC_RESULT_NOT_INITIALIZED;
+
+	if (imvID != my_id || !bindFunction)
+		return TNC_RESULT_INVALID_PARAMETER;
+
+	if (bindFunction(imvID, "TNC_TNCS_ReportMessageTypes",
+			 (void **) &report_message_types) !=
+	    TNC_RESULT_SUCCESS ||
+	    !report_message_types)
+		return TNC_RESULT_FATAL;
+
+	if (bindFunction(imvID, "TNC_TNCS_SendMessage",
+			 (void **) &send_message) != TNC_RESULT_SUCCESS ||
+	    !send_message)
+		return TNC_RESULT_FATAL;
+
+	if (bindFunction(imvID, "TNC_TNCS_RequestHandshakeRetry",
+			 (void **) &request_retry) != TNC_RESULT_SUCCESS ||
+	    !request_retry)
+		return TNC_RESULT_FATAL;
+
+	if (bindFunction(imvID, "TNC_TNCS_ProvideRecommendation",
+			 (void **) &provide_recomm) != TNC_RESULT_SUCCESS ||
+	    !provide_recomm)
+		return TNC_RESULT_FATAL;
+
+	res = report_message_types(imvID, message_types,
+				   ARRAY_SIZE(message_types));
+	if (res != TNC_RESULT_SUCCESS)
+		return res;
+
+	return TNC_RESULT_SUCCESS;
+}

+ 72 - 0
tests/hwsim/tnc/hostap_imc.c

@@ -0,0 +1,72 @@
+/*
+ * Minimal example IMC for TNC testing
+ * Copyright (c) 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 "common/tnc.h"
+
+static int initialized = 0;
+static TNC_IMCID my_id = -1;
+
+TNC_Result TNC_IMC_Initialize(
+	/*in*/ TNC_IMCID imcID,
+	/*in*/ TNC_Version minVersion,
+	/*in*/ TNC_Version maxVersion,
+	/*out*/ TNC_Version *pOutActualVersion)
+{
+	wpa_printf(MSG_INFO, "IMC(hostap) %s", __func__);
+
+	if (initialized)
+		return TNC_RESULT_ALREADY_INITIALIZED;
+
+	if (minVersion < TNC_IFIMC_VERSION_1 ||
+	    maxVersion > TNC_IFIMC_VERSION_1)
+		return TNC_RESULT_NO_COMMON_VERSION;
+
+	if (!pOutActualVersion)
+		return TNC_RESULT_INVALID_PARAMETER;
+	*pOutActualVersion = TNC_IFIMC_VERSION_1;
+	my_id = imcID;
+
+	initialized = 1;
+
+	return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_IMC_BeginHandshake(
+	/*in*/ TNC_IMCID imcID,
+	/*in*/ TNC_ConnectionID connectionID)
+{
+	wpa_printf(MSG_INFO, "IMC(hostap) %s", __func__);
+
+	if (!initialized)
+		return TNC_RESULT_NOT_INITIALIZED;
+
+	if (imcID != my_id)
+		return TNC_RESULT_INVALID_PARAMETER;
+
+	return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_IMC_ProvideBindFunction(
+	/*in*/ TNC_IMCID imcID,
+	/*in*/ TNC_TNCC_BindFunctionPointer bindFunction)
+{
+	wpa_printf(MSG_INFO, "IMC(hostap) %s", __func__);
+
+	if (!initialized)
+		return TNC_RESULT_NOT_INITIALIZED;
+
+	if (imcID != my_id)
+		return TNC_RESULT_INVALID_PARAMETER;
+
+	return TNC_RESULT_SUCCESS;
+}

+ 66 - 0
tests/hwsim/tnc/hostap_imv.c

@@ -0,0 +1,66 @@
+/*
+ * Minimal example IMV for TNC testing
+ * Copyright (c) 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 "common/tnc.h"
+
+static int initialized = 0;
+static TNC_IMVID my_id = -1;
+
+TNC_Result TNC_IMV_Initialize(
+	/*in*/ TNC_IMVID imvID,
+	/*in*/ TNC_Version minVersion,
+	/*in*/ TNC_Version maxVersion,
+	/*out*/ TNC_Version *pOutActualVersion)
+{
+	if (initialized)
+		return TNC_RESULT_ALREADY_INITIALIZED;
+
+	if (minVersion < TNC_IFIMV_VERSION_1 ||
+	    maxVersion > TNC_IFIMV_VERSION_1)
+		return TNC_RESULT_NO_COMMON_VERSION;
+
+	if (!pOutActualVersion)
+		return TNC_RESULT_INVALID_PARAMETER;
+	*pOutActualVersion = TNC_IFIMV_VERSION_1;
+
+	initialized = 1;
+	my_id = imvID;
+
+	return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_IMV_SolicitRecommendation(
+	/*in*/ TNC_IMVID imvID,
+	/*in*/ TNC_ConnectionID connectionID)
+{
+	if (!initialized)
+		return TNC_RESULT_NOT_INITIALIZED;
+
+	if (imvID != my_id)
+		return TNC_RESULT_INVALID_PARAMETER;
+
+	return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_IMV_ProvideBindFunction(
+	/*in*/ TNC_IMVID imvID,
+	/*in*/ TNC_TNCS_BindFunctionPointer bindFunction)
+{
+	if (!initialized)
+		return TNC_RESULT_NOT_INITIALIZED;
+
+	if (imvID != my_id)
+		return TNC_RESULT_INVALID_PARAMETER;
+
+	return TNC_RESULT_SUCCESS;
+}

+ 4 - 0
tests/hwsim/tnc/tnc_config

@@ -0,0 +1,4 @@
+IMC "hostap IMC" tnc/libhostap_imc.so
+IMV "hostap IMV" tnc/libhostap_imv.so
+IMC "hostap2 IMC" tnc/libhostap2_imc.so
+IMV "hostap2 IMV" tnc/libhostap2_imv.so