xref: /netbsd-src/external/bsd/wpa/dist/src/eap_server/tncs.c (revision bb6183629cf165db498d8e1f4e2de129f7efb21c)
18dbcf02cSchristos /*
28dbcf02cSchristos  * EAP-TNC - TNCS (IF-IMV, IF-TNCCS, and IF-TNCCS-SOH)
38dbcf02cSchristos  * Copyright (c) 2007-2008, Jouni Malinen <j@w1.fi>
48dbcf02cSchristos  *
5e604d861Schristos  * This software may be distributed under the terms of the BSD license.
6e604d861Schristos  * See README for more details.
78dbcf02cSchristos  */
88dbcf02cSchristos 
98dbcf02cSchristos #include "includes.h"
108dbcf02cSchristos #include <dlfcn.h>
118dbcf02cSchristos 
128dbcf02cSchristos #include "common.h"
138dbcf02cSchristos #include "base64.h"
143c260e60Schristos #include "common/tnc.h"
158dbcf02cSchristos #include "tncs.h"
168dbcf02cSchristos #include "eap_common/eap_tlv_common.h"
178dbcf02cSchristos #include "eap_common/eap_defs.h"
188dbcf02cSchristos 
198dbcf02cSchristos 
208dbcf02cSchristos /* TODO: TNCS must be thread-safe; review the code and add locking etc. if
218dbcf02cSchristos  * needed.. */
228dbcf02cSchristos 
233c260e60Schristos #ifndef TNC_CONFIG_FILE
248dbcf02cSchristos #define TNC_CONFIG_FILE "/etc/tnc_config"
253c260e60Schristos #endif /* TNC_CONFIG_FILE */
268dbcf02cSchristos #define IF_TNCCS_START \
278dbcf02cSchristos "<?xml version=\"1.0\"?>\n" \
288dbcf02cSchristos "<TNCCS-Batch BatchId=\"%d\" Recipient=\"TNCS\" " \
298dbcf02cSchristos "xmlns=\"http://www.trustedcomputinggroup.org/IWG/TNC/1_0/IF_TNCCS#\" " \
308dbcf02cSchristos "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " \
318dbcf02cSchristos "xsi:schemaLocation=\"http://www.trustedcomputinggroup.org/IWG/TNC/1_0/" \
328dbcf02cSchristos "IF_TNCCS# https://www.trustedcomputinggroup.org/XML/SCHEMA/TNCCS_1.0.xsd\">\n"
338dbcf02cSchristos #define IF_TNCCS_END "\n</TNCCS-Batch>"
348dbcf02cSchristos 
358dbcf02cSchristos /* TNC IF-IMV */
368dbcf02cSchristos 
378dbcf02cSchristos struct tnc_if_imv {
388dbcf02cSchristos 	struct tnc_if_imv *next;
398dbcf02cSchristos 	char *name;
408dbcf02cSchristos 	char *path;
418dbcf02cSchristos 	void *dlhandle; /* from dlopen() */
428dbcf02cSchristos 	TNC_IMVID imvID;
438dbcf02cSchristos 	TNC_MessageTypeList supported_types;
448dbcf02cSchristos 	size_t num_supported_types;
458dbcf02cSchristos 
468dbcf02cSchristos 	/* Functions implemented by IMVs (with TNC_IMV_ prefix) */
478dbcf02cSchristos 	TNC_Result (*Initialize)(
488dbcf02cSchristos 		TNC_IMVID imvID,
498dbcf02cSchristos 		TNC_Version minVersion,
508dbcf02cSchristos 		TNC_Version maxVersion,
518dbcf02cSchristos 		TNC_Version *pOutActualVersion);
528dbcf02cSchristos 	TNC_Result (*NotifyConnectionChange)(
538dbcf02cSchristos 		TNC_IMVID imvID,
548dbcf02cSchristos 		TNC_ConnectionID connectionID,
558dbcf02cSchristos 		TNC_ConnectionState newState);
568dbcf02cSchristos 	TNC_Result (*ReceiveMessage)(
578dbcf02cSchristos 		TNC_IMVID imvID,
588dbcf02cSchristos 		TNC_ConnectionID connectionID,
598dbcf02cSchristos 		TNC_BufferReference message,
608dbcf02cSchristos 		TNC_UInt32 messageLength,
618dbcf02cSchristos 		TNC_MessageType messageType);
628dbcf02cSchristos 	TNC_Result (*SolicitRecommendation)(
638dbcf02cSchristos 		TNC_IMVID imvID,
648dbcf02cSchristos 		TNC_ConnectionID connectionID);
658dbcf02cSchristos 	TNC_Result (*BatchEnding)(
668dbcf02cSchristos 		TNC_IMVID imvID,
678dbcf02cSchristos 		TNC_ConnectionID connectionID);
688dbcf02cSchristos 	TNC_Result (*Terminate)(TNC_IMVID imvID);
698dbcf02cSchristos 	TNC_Result (*ProvideBindFunction)(
708dbcf02cSchristos 		TNC_IMVID imvID,
718dbcf02cSchristos 		TNC_TNCS_BindFunctionPointer bindFunction);
728dbcf02cSchristos };
738dbcf02cSchristos 
748dbcf02cSchristos 
758dbcf02cSchristos #define TNC_MAX_IMV_ID 10
768dbcf02cSchristos 
778dbcf02cSchristos struct tncs_data {
788dbcf02cSchristos 	struct tncs_data *next;
798dbcf02cSchristos 	struct tnc_if_imv *imv; /* local copy of tncs_global_data->imv */
808dbcf02cSchristos 	TNC_ConnectionID connectionID;
818dbcf02cSchristos 	unsigned int last_batchid;
828dbcf02cSchristos 	enum IMV_Action_Recommendation recommendation;
838dbcf02cSchristos 	int done;
848dbcf02cSchristos 
858dbcf02cSchristos 	struct conn_imv {
868dbcf02cSchristos 		u8 *imv_send;
878dbcf02cSchristos 		size_t imv_send_len;
888dbcf02cSchristos 		enum IMV_Action_Recommendation recommendation;
898dbcf02cSchristos 		int recommendation_set;
908dbcf02cSchristos 	} imv_data[TNC_MAX_IMV_ID];
918dbcf02cSchristos 
928dbcf02cSchristos 	char *tncs_message;
938dbcf02cSchristos };
948dbcf02cSchristos 
958dbcf02cSchristos 
968dbcf02cSchristos struct tncs_global {
978dbcf02cSchristos 	struct tnc_if_imv *imv;
988dbcf02cSchristos 	TNC_ConnectionID next_conn_id;
998dbcf02cSchristos 	struct tncs_data *connections;
1008dbcf02cSchristos };
1018dbcf02cSchristos 
1028dbcf02cSchristos static struct tncs_global *tncs_global_data = NULL;
1038dbcf02cSchristos 
1048dbcf02cSchristos 
1058dbcf02cSchristos static struct tnc_if_imv * tncs_get_imv(TNC_IMVID imvID)
1068dbcf02cSchristos {
1078dbcf02cSchristos 	struct tnc_if_imv *imv;
1088dbcf02cSchristos 
1098dbcf02cSchristos 	if (imvID >= TNC_MAX_IMV_ID || tncs_global_data == NULL)
1108dbcf02cSchristos 		return NULL;
1118dbcf02cSchristos 	imv = tncs_global_data->imv;
1128dbcf02cSchristos 	while (imv) {
1138dbcf02cSchristos 		if (imv->imvID == imvID)
1148dbcf02cSchristos 			return imv;
1158dbcf02cSchristos 		imv = imv->next;
1168dbcf02cSchristos 	}
1178dbcf02cSchristos 	return NULL;
1188dbcf02cSchristos }
1198dbcf02cSchristos 
1208dbcf02cSchristos 
1218dbcf02cSchristos static struct tncs_data * tncs_get_conn(TNC_ConnectionID connectionID)
1228dbcf02cSchristos {
1238dbcf02cSchristos 	struct tncs_data *tncs;
1248dbcf02cSchristos 
1258dbcf02cSchristos 	if (tncs_global_data == NULL)
1268dbcf02cSchristos 		return NULL;
1278dbcf02cSchristos 
1288dbcf02cSchristos 	tncs = tncs_global_data->connections;
1298dbcf02cSchristos 	while (tncs) {
1308dbcf02cSchristos 		if (tncs->connectionID == connectionID)
1318dbcf02cSchristos 			return tncs;
1328dbcf02cSchristos 		tncs = tncs->next;
1338dbcf02cSchristos 	}
1348dbcf02cSchristos 
1358dbcf02cSchristos 	wpa_printf(MSG_DEBUG, "TNC: Connection ID %lu not found",
1368dbcf02cSchristos 		   (unsigned long) connectionID);
1378dbcf02cSchristos 
1388dbcf02cSchristos 	return NULL;
1398dbcf02cSchristos }
1408dbcf02cSchristos 
1418dbcf02cSchristos 
1428dbcf02cSchristos /* TNCS functions that IMVs can call */
14336ebd06eSchristos static TNC_Result TNC_TNCS_ReportMessageTypes(
1448dbcf02cSchristos 	TNC_IMVID imvID,
1458dbcf02cSchristos 	TNC_MessageTypeList supportedTypes,
1468dbcf02cSchristos 	TNC_UInt32 typeCount)
1478dbcf02cSchristos {
1488dbcf02cSchristos 	TNC_UInt32 i;
1498dbcf02cSchristos 	struct tnc_if_imv *imv;
1508dbcf02cSchristos 
1518dbcf02cSchristos 	wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_ReportMessageTypes(imvID=%lu "
1528dbcf02cSchristos 		   "typeCount=%lu)",
1538dbcf02cSchristos 		   (unsigned long) imvID, (unsigned long) typeCount);
1548dbcf02cSchristos 
1558dbcf02cSchristos 	for (i = 0; i < typeCount; i++) {
1568dbcf02cSchristos 		wpa_printf(MSG_DEBUG, "TNC: supportedTypes[%lu] = %lu",
1578dbcf02cSchristos 			   i, supportedTypes[i]);
1588dbcf02cSchristos 	}
1598dbcf02cSchristos 
1608dbcf02cSchristos 	imv = tncs_get_imv(imvID);
1618dbcf02cSchristos 	if (imv == NULL)
1628dbcf02cSchristos 		return TNC_RESULT_INVALID_PARAMETER;
1638dbcf02cSchristos 	os_free(imv->supported_types);
1640a73ee0aSchristos 	imv->supported_types = os_memdup(supportedTypes,
1650a73ee0aSchristos 					 typeCount * sizeof(TNC_MessageType));
1668dbcf02cSchristos 	if (imv->supported_types == NULL)
1678dbcf02cSchristos 		return TNC_RESULT_FATAL;
1688dbcf02cSchristos 	imv->num_supported_types = typeCount;
1698dbcf02cSchristos 
1708dbcf02cSchristos 	return TNC_RESULT_SUCCESS;
1718dbcf02cSchristos }
1728dbcf02cSchristos 
1738dbcf02cSchristos 
17436ebd06eSchristos static TNC_Result TNC_TNCS_SendMessage(
1758dbcf02cSchristos 	TNC_IMVID imvID,
1768dbcf02cSchristos 	TNC_ConnectionID connectionID,
1778dbcf02cSchristos 	TNC_BufferReference message,
1788dbcf02cSchristos 	TNC_UInt32 messageLength,
1798dbcf02cSchristos 	TNC_MessageType messageType)
1808dbcf02cSchristos {
1818dbcf02cSchristos 	struct tncs_data *tncs;
182*bb618362Schristos 	char *b64;
1838dbcf02cSchristos 	size_t b64len;
1848dbcf02cSchristos 
1858dbcf02cSchristos 	wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_SendMessage(imvID=%lu "
1868dbcf02cSchristos 		   "connectionID=%lu messageType=%lu)",
1878dbcf02cSchristos 		   imvID, connectionID, messageType);
1888dbcf02cSchristos 	wpa_hexdump_ascii(MSG_DEBUG, "TNC: TNC_TNCS_SendMessage",
1898dbcf02cSchristos 			  message, messageLength);
1908dbcf02cSchristos 
1918dbcf02cSchristos 	if (tncs_get_imv(imvID) == NULL)
1928dbcf02cSchristos 		return TNC_RESULT_INVALID_PARAMETER;
1938dbcf02cSchristos 
1948dbcf02cSchristos 	tncs = tncs_get_conn(connectionID);
1958dbcf02cSchristos 	if (tncs == NULL)
1968dbcf02cSchristos 		return TNC_RESULT_INVALID_PARAMETER;
1978dbcf02cSchristos 
1988dbcf02cSchristos 	b64 = base64_encode(message, messageLength, &b64len);
1998dbcf02cSchristos 	if (b64 == NULL)
2008dbcf02cSchristos 		return TNC_RESULT_FATAL;
2018dbcf02cSchristos 
2028dbcf02cSchristos 	os_free(tncs->imv_data[imvID].imv_send);
2038dbcf02cSchristos 	tncs->imv_data[imvID].imv_send_len = 0;
2048dbcf02cSchristos 	tncs->imv_data[imvID].imv_send = os_zalloc(b64len + 100);
2058dbcf02cSchristos 	if (tncs->imv_data[imvID].imv_send == NULL) {
2068dbcf02cSchristos 		os_free(b64);
2078dbcf02cSchristos 		return TNC_RESULT_OTHER;
2088dbcf02cSchristos 	}
2098dbcf02cSchristos 
2108dbcf02cSchristos 	tncs->imv_data[imvID].imv_send_len =
2118dbcf02cSchristos 		os_snprintf((char *) tncs->imv_data[imvID].imv_send,
2128dbcf02cSchristos 			    b64len + 100,
2138dbcf02cSchristos 			    "<IMC-IMV-Message><Type>%08X</Type>"
2148dbcf02cSchristos 			    "<Base64>%s</Base64></IMC-IMV-Message>",
2158dbcf02cSchristos 			    (unsigned int) messageType, b64);
2168dbcf02cSchristos 
2178dbcf02cSchristos 	os_free(b64);
2188dbcf02cSchristos 
2198dbcf02cSchristos 	return TNC_RESULT_SUCCESS;
2208dbcf02cSchristos }
2218dbcf02cSchristos 
2228dbcf02cSchristos 
22336ebd06eSchristos static TNC_Result TNC_TNCS_RequestHandshakeRetry(
2248dbcf02cSchristos 	TNC_IMVID imvID,
2258dbcf02cSchristos 	TNC_ConnectionID connectionID,
2268dbcf02cSchristos 	TNC_RetryReason reason)
2278dbcf02cSchristos {
2288dbcf02cSchristos 	wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_RequestHandshakeRetry");
2298dbcf02cSchristos 	/* TODO */
2308dbcf02cSchristos 	return TNC_RESULT_SUCCESS;
2318dbcf02cSchristos }
2328dbcf02cSchristos 
2338dbcf02cSchristos 
23436ebd06eSchristos static TNC_Result TNC_TNCS_ProvideRecommendation(
2358dbcf02cSchristos 	TNC_IMVID imvID,
2368dbcf02cSchristos 	TNC_ConnectionID connectionID,
2378dbcf02cSchristos 	TNC_IMV_Action_Recommendation recommendation,
2388dbcf02cSchristos 	TNC_IMV_Evaluation_Result evaluation)
2398dbcf02cSchristos {
2408dbcf02cSchristos 	struct tncs_data *tncs;
2418dbcf02cSchristos 
2428dbcf02cSchristos 	wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_ProvideRecommendation(imvID=%lu "
2438dbcf02cSchristos 		   "connectionID=%lu recommendation=%lu evaluation=%lu)",
2448dbcf02cSchristos 		   (unsigned long) imvID, (unsigned long) connectionID,
2458dbcf02cSchristos 		   (unsigned long) recommendation, (unsigned long) evaluation);
2468dbcf02cSchristos 
2478dbcf02cSchristos 	if (tncs_get_imv(imvID) == NULL)
2488dbcf02cSchristos 		return TNC_RESULT_INVALID_PARAMETER;
2498dbcf02cSchristos 
2508dbcf02cSchristos 	tncs = tncs_get_conn(connectionID);
2518dbcf02cSchristos 	if (tncs == NULL)
2528dbcf02cSchristos 		return TNC_RESULT_INVALID_PARAMETER;
2538dbcf02cSchristos 
2548dbcf02cSchristos 	tncs->imv_data[imvID].recommendation = recommendation;
2558dbcf02cSchristos 	tncs->imv_data[imvID].recommendation_set = 1;
2568dbcf02cSchristos 
2578dbcf02cSchristos 	return TNC_RESULT_SUCCESS;
2588dbcf02cSchristos }
2598dbcf02cSchristos 
2608dbcf02cSchristos 
26136ebd06eSchristos static TNC_Result TNC_TNCS_GetAttribute(
2628dbcf02cSchristos 	TNC_IMVID imvID,
2638dbcf02cSchristos 	TNC_ConnectionID connectionID,
2648dbcf02cSchristos 	TNC_AttributeID attribureID,
2658dbcf02cSchristos 	TNC_UInt32 bufferLength,
2668dbcf02cSchristos 	TNC_BufferReference buffer,
2678dbcf02cSchristos 	TNC_UInt32 *pOutValueLength)
2688dbcf02cSchristos {
2698dbcf02cSchristos 	wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_GetAttribute");
2708dbcf02cSchristos 	/* TODO */
2718dbcf02cSchristos 	return TNC_RESULT_SUCCESS;
2728dbcf02cSchristos }
2738dbcf02cSchristos 
2748dbcf02cSchristos 
27536ebd06eSchristos static TNC_Result TNC_TNCS_SetAttribute(
2768dbcf02cSchristos 	TNC_IMVID imvID,
2778dbcf02cSchristos 	TNC_ConnectionID connectionID,
2788dbcf02cSchristos 	TNC_AttributeID attribureID,
2798dbcf02cSchristos 	TNC_UInt32 bufferLength,
2808dbcf02cSchristos 	TNC_BufferReference buffer)
2818dbcf02cSchristos {
2828dbcf02cSchristos 	wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_SetAttribute");
2838dbcf02cSchristos 	/* TODO */
2848dbcf02cSchristos 	return TNC_RESULT_SUCCESS;
2858dbcf02cSchristos }
2868dbcf02cSchristos 
2878dbcf02cSchristos 
28836ebd06eSchristos static TNC_Result TNC_TNCS_BindFunction(
2898dbcf02cSchristos 	TNC_IMVID imvID,
2908dbcf02cSchristos 	char *functionName,
2918dbcf02cSchristos 	void **pOutFunctionPointer)
2928dbcf02cSchristos {
2938dbcf02cSchristos 	wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_BindFunction(imcID=%lu, "
2948dbcf02cSchristos 		   "functionName='%s')", (unsigned long) imvID, functionName);
2958dbcf02cSchristos 
2968dbcf02cSchristos 	if (tncs_get_imv(imvID) == NULL)
2978dbcf02cSchristos 		return TNC_RESULT_INVALID_PARAMETER;
2988dbcf02cSchristos 
2998dbcf02cSchristos 	if (pOutFunctionPointer == NULL)
3008dbcf02cSchristos 		return TNC_RESULT_INVALID_PARAMETER;
3018dbcf02cSchristos 
3028dbcf02cSchristos 	if (os_strcmp(functionName, "TNC_TNCS_ReportMessageTypes") == 0)
3038dbcf02cSchristos 		*pOutFunctionPointer = TNC_TNCS_ReportMessageTypes;
3048dbcf02cSchristos 	else if (os_strcmp(functionName, "TNC_TNCS_SendMessage") == 0)
3058dbcf02cSchristos 		*pOutFunctionPointer = TNC_TNCS_SendMessage;
3068dbcf02cSchristos 	else if (os_strcmp(functionName, "TNC_TNCS_RequestHandshakeRetry") ==
3078dbcf02cSchristos 		 0)
3088dbcf02cSchristos 		*pOutFunctionPointer = TNC_TNCS_RequestHandshakeRetry;
3098dbcf02cSchristos 	else if (os_strcmp(functionName, "TNC_TNCS_ProvideRecommendation") ==
3108dbcf02cSchristos 		 0)
3118dbcf02cSchristos 		*pOutFunctionPointer = TNC_TNCS_ProvideRecommendation;
3128dbcf02cSchristos 	else if (os_strcmp(functionName, "TNC_TNCS_GetAttribute") == 0)
3138dbcf02cSchristos 		*pOutFunctionPointer = TNC_TNCS_GetAttribute;
3148dbcf02cSchristos 	else if (os_strcmp(functionName, "TNC_TNCS_SetAttribute") == 0)
3158dbcf02cSchristos 		*pOutFunctionPointer = TNC_TNCS_SetAttribute;
3168dbcf02cSchristos 	else
3178dbcf02cSchristos 		*pOutFunctionPointer = NULL;
3188dbcf02cSchristos 
3198dbcf02cSchristos 	return TNC_RESULT_SUCCESS;
3208dbcf02cSchristos }
3218dbcf02cSchristos 
3228dbcf02cSchristos 
3238dbcf02cSchristos static void * tncs_get_sym(void *handle, char *func)
3248dbcf02cSchristos {
3258dbcf02cSchristos 	void *fptr;
3268dbcf02cSchristos 
3278dbcf02cSchristos 	fptr = dlsym(handle, func);
3288dbcf02cSchristos 
3298dbcf02cSchristos 	return fptr;
3308dbcf02cSchristos }
3318dbcf02cSchristos 
3328dbcf02cSchristos 
3338dbcf02cSchristos static int tncs_imv_resolve_funcs(struct tnc_if_imv *imv)
3348dbcf02cSchristos {
3358dbcf02cSchristos 	void *handle = imv->dlhandle;
3368dbcf02cSchristos 
3378dbcf02cSchristos 	/* Mandatory IMV functions */
3388dbcf02cSchristos 	imv->Initialize = tncs_get_sym(handle, "TNC_IMV_Initialize");
3398dbcf02cSchristos 	if (imv->Initialize == NULL) {
3408dbcf02cSchristos 		wpa_printf(MSG_ERROR, "TNC: IMV does not export "
3418dbcf02cSchristos 			   "TNC_IMV_Initialize");
3428dbcf02cSchristos 		return -1;
3438dbcf02cSchristos 	}
3448dbcf02cSchristos 
3458dbcf02cSchristos 	imv->SolicitRecommendation = tncs_get_sym(
3468dbcf02cSchristos 		handle, "TNC_IMV_SolicitRecommendation");
3478dbcf02cSchristos 	if (imv->SolicitRecommendation == NULL) {
3488dbcf02cSchristos 		wpa_printf(MSG_ERROR, "TNC: IMV does not export "
3498dbcf02cSchristos 			   "TNC_IMV_SolicitRecommendation");
3508dbcf02cSchristos 		return -1;
3518dbcf02cSchristos 	}
3528dbcf02cSchristos 
3538dbcf02cSchristos 	imv->ProvideBindFunction =
3548dbcf02cSchristos 		tncs_get_sym(handle, "TNC_IMV_ProvideBindFunction");
3558dbcf02cSchristos 	if (imv->ProvideBindFunction == NULL) {
3568dbcf02cSchristos 		wpa_printf(MSG_ERROR, "TNC: IMV does not export "
3578dbcf02cSchristos 			   "TNC_IMV_ProvideBindFunction");
3588dbcf02cSchristos 		return -1;
3598dbcf02cSchristos 	}
3608dbcf02cSchristos 
3618dbcf02cSchristos 	/* Optional IMV functions */
3628dbcf02cSchristos 	imv->NotifyConnectionChange =
3638dbcf02cSchristos 		tncs_get_sym(handle, "TNC_IMV_NotifyConnectionChange");
3648dbcf02cSchristos 	imv->ReceiveMessage = tncs_get_sym(handle, "TNC_IMV_ReceiveMessage");
3658dbcf02cSchristos 	imv->BatchEnding = tncs_get_sym(handle, "TNC_IMV_BatchEnding");
3668dbcf02cSchristos 	imv->Terminate = tncs_get_sym(handle, "TNC_IMV_Terminate");
3678dbcf02cSchristos 
3688dbcf02cSchristos 	return 0;
3698dbcf02cSchristos }
3708dbcf02cSchristos 
3718dbcf02cSchristos 
3728dbcf02cSchristos static int tncs_imv_initialize(struct tnc_if_imv *imv)
3738dbcf02cSchristos {
3748dbcf02cSchristos 	TNC_Result res;
3758dbcf02cSchristos 	TNC_Version imv_ver;
3768dbcf02cSchristos 
3778dbcf02cSchristos 	wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMV_Initialize for IMV '%s'",
3788dbcf02cSchristos 		   imv->name);
3798dbcf02cSchristos 	res = imv->Initialize(imv->imvID, TNC_IFIMV_VERSION_1,
3808dbcf02cSchristos 			      TNC_IFIMV_VERSION_1, &imv_ver);
3818dbcf02cSchristos 	wpa_printf(MSG_DEBUG, "TNC: TNC_IMV_Initialize: res=%lu imv_ver=%lu",
3828dbcf02cSchristos 		   (unsigned long) res, (unsigned long) imv_ver);
3838dbcf02cSchristos 
3848dbcf02cSchristos 	return res == TNC_RESULT_SUCCESS ? 0 : -1;
3858dbcf02cSchristos }
3868dbcf02cSchristos 
3878dbcf02cSchristos 
3888dbcf02cSchristos static int tncs_imv_terminate(struct tnc_if_imv *imv)
3898dbcf02cSchristos {
3908dbcf02cSchristos 	TNC_Result res;
3918dbcf02cSchristos 
3928dbcf02cSchristos 	if (imv->Terminate == NULL)
3938dbcf02cSchristos 		return 0;
3948dbcf02cSchristos 
3958dbcf02cSchristos 	wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMV_Terminate for IMV '%s'",
3968dbcf02cSchristos 		   imv->name);
3978dbcf02cSchristos 	res = imv->Terminate(imv->imvID);
3988dbcf02cSchristos 	wpa_printf(MSG_DEBUG, "TNC: TNC_IMV_Terminate: %lu",
3998dbcf02cSchristos 		   (unsigned long) res);
4008dbcf02cSchristos 
4018dbcf02cSchristos 	return res == TNC_RESULT_SUCCESS ? 0 : -1;
4028dbcf02cSchristos }
4038dbcf02cSchristos 
4048dbcf02cSchristos 
4058dbcf02cSchristos static int tncs_imv_provide_bind_function(struct tnc_if_imv *imv)
4068dbcf02cSchristos {
4078dbcf02cSchristos 	TNC_Result res;
4088dbcf02cSchristos 
4098dbcf02cSchristos 	wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMV_ProvideBindFunction for "
4108dbcf02cSchristos 		   "IMV '%s'", imv->name);
4118dbcf02cSchristos 	res = imv->ProvideBindFunction(imv->imvID, TNC_TNCS_BindFunction);
4128dbcf02cSchristos 	wpa_printf(MSG_DEBUG, "TNC: TNC_IMV_ProvideBindFunction: res=%lu",
4138dbcf02cSchristos 		   (unsigned long) res);
4148dbcf02cSchristos 
4158dbcf02cSchristos 	return res == TNC_RESULT_SUCCESS ? 0 : -1;
4168dbcf02cSchristos }
4178dbcf02cSchristos 
4188dbcf02cSchristos 
4198dbcf02cSchristos static int tncs_imv_notify_connection_change(struct tnc_if_imv *imv,
4208dbcf02cSchristos 					     TNC_ConnectionID conn,
4218dbcf02cSchristos 					     TNC_ConnectionState state)
4228dbcf02cSchristos {
4238dbcf02cSchristos 	TNC_Result res;
4248dbcf02cSchristos 
4258dbcf02cSchristos 	if (imv->NotifyConnectionChange == NULL)
4268dbcf02cSchristos 		return 0;
4278dbcf02cSchristos 
4288dbcf02cSchristos 	wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMV_NotifyConnectionChange(%d)"
4298dbcf02cSchristos 		   " for IMV '%s'", (int) state, imv->name);
4308dbcf02cSchristos 	res = imv->NotifyConnectionChange(imv->imvID, conn, state);
4318dbcf02cSchristos 	wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_NotifyConnectionChange: %lu",
4328dbcf02cSchristos 		   (unsigned long) res);
4338dbcf02cSchristos 
4348dbcf02cSchristos 	return res == TNC_RESULT_SUCCESS ? 0 : -1;
4358dbcf02cSchristos }
4368dbcf02cSchristos 
4378dbcf02cSchristos 
4388dbcf02cSchristos static int tncs_load_imv(struct tnc_if_imv *imv)
4398dbcf02cSchristos {
4408dbcf02cSchristos 	if (imv->path == NULL) {
4418dbcf02cSchristos 		wpa_printf(MSG_DEBUG, "TNC: No IMV configured");
4428dbcf02cSchristos 		return -1;
4438dbcf02cSchristos 	}
4448dbcf02cSchristos 
4458dbcf02cSchristos 	wpa_printf(MSG_DEBUG, "TNC: Opening IMV: %s (%s)",
4468dbcf02cSchristos 		   imv->name, imv->path);
4478dbcf02cSchristos 	imv->dlhandle = dlopen(imv->path, RTLD_LAZY);
4488dbcf02cSchristos 	if (imv->dlhandle == NULL) {
4498dbcf02cSchristos 		wpa_printf(MSG_ERROR, "TNC: Failed to open IMV '%s' (%s): %s",
4508dbcf02cSchristos 			   imv->name, imv->path, dlerror());
4518dbcf02cSchristos 		return -1;
4528dbcf02cSchristos 	}
4538dbcf02cSchristos 
4548dbcf02cSchristos 	if (tncs_imv_resolve_funcs(imv) < 0) {
4558dbcf02cSchristos 		wpa_printf(MSG_ERROR, "TNC: Failed to resolve IMV functions");
4568dbcf02cSchristos 		return -1;
4578dbcf02cSchristos 	}
4588dbcf02cSchristos 
4598dbcf02cSchristos 	if (tncs_imv_initialize(imv) < 0 ||
4608dbcf02cSchristos 	    tncs_imv_provide_bind_function(imv) < 0) {
4618dbcf02cSchristos 		wpa_printf(MSG_ERROR, "TNC: Failed to initialize IMV");
4628dbcf02cSchristos 		return -1;
4638dbcf02cSchristos 	}
4648dbcf02cSchristos 
4658dbcf02cSchristos 	return 0;
4668dbcf02cSchristos }
4678dbcf02cSchristos 
4688dbcf02cSchristos 
4698dbcf02cSchristos static void tncs_free_imv(struct tnc_if_imv *imv)
4708dbcf02cSchristos {
4718dbcf02cSchristos 	os_free(imv->name);
4728dbcf02cSchristos 	os_free(imv->path);
4738dbcf02cSchristos 	os_free(imv->supported_types);
4748dbcf02cSchristos }
4758dbcf02cSchristos 
4768dbcf02cSchristos static void tncs_unload_imv(struct tnc_if_imv *imv)
4778dbcf02cSchristos {
4788dbcf02cSchristos 	tncs_imv_terminate(imv);
4798dbcf02cSchristos 
4808dbcf02cSchristos 	if (imv->dlhandle)
4818dbcf02cSchristos 		dlclose(imv->dlhandle);
4828dbcf02cSchristos 
4838dbcf02cSchristos 	tncs_free_imv(imv);
4848dbcf02cSchristos }
4858dbcf02cSchristos 
4868dbcf02cSchristos 
4878dbcf02cSchristos static int tncs_supported_type(struct tnc_if_imv *imv, unsigned int type)
4888dbcf02cSchristos {
4898dbcf02cSchristos 	size_t i;
4908dbcf02cSchristos 	unsigned int vendor, subtype;
4918dbcf02cSchristos 
4928dbcf02cSchristos 	if (imv == NULL || imv->supported_types == NULL)
4938dbcf02cSchristos 		return 0;
4948dbcf02cSchristos 
4958dbcf02cSchristos 	vendor = type >> 8;
4968dbcf02cSchristos 	subtype = type & 0xff;
4978dbcf02cSchristos 
4988dbcf02cSchristos 	for (i = 0; i < imv->num_supported_types; i++) {
4998dbcf02cSchristos 		unsigned int svendor, ssubtype;
5008dbcf02cSchristos 		svendor = imv->supported_types[i] >> 8;
5018dbcf02cSchristos 		ssubtype = imv->supported_types[i] & 0xff;
5028dbcf02cSchristos 		if ((vendor == svendor || svendor == TNC_VENDORID_ANY) &&
5038dbcf02cSchristos 		    (subtype == ssubtype || ssubtype == TNC_SUBTYPE_ANY))
5048dbcf02cSchristos 			return 1;
5058dbcf02cSchristos 	}
5068dbcf02cSchristos 
5078dbcf02cSchristos 	return 0;
5088dbcf02cSchristos }
5098dbcf02cSchristos 
5108dbcf02cSchristos 
5118dbcf02cSchristos static void tncs_send_to_imvs(struct tncs_data *tncs, unsigned int type,
5128dbcf02cSchristos 			      const u8 *msg, size_t len)
5138dbcf02cSchristos {
5148dbcf02cSchristos 	struct tnc_if_imv *imv;
5158dbcf02cSchristos 	TNC_Result res;
5168dbcf02cSchristos 
5178dbcf02cSchristos 	wpa_hexdump_ascii(MSG_MSGDUMP, "TNC: Message to IMV(s)", msg, len);
5188dbcf02cSchristos 
5198dbcf02cSchristos 	for (imv = tncs->imv; imv; imv = imv->next) {
5208dbcf02cSchristos 		if (imv->ReceiveMessage == NULL ||
5218dbcf02cSchristos 		    !tncs_supported_type(imv, type))
5228dbcf02cSchristos 			continue;
5238dbcf02cSchristos 
5248dbcf02cSchristos 		wpa_printf(MSG_DEBUG, "TNC: Call ReceiveMessage for IMV '%s'",
5258dbcf02cSchristos 			   imv->name);
5268dbcf02cSchristos 		res = imv->ReceiveMessage(imv->imvID, tncs->connectionID,
5278dbcf02cSchristos 					  (TNC_BufferReference) msg, len,
5288dbcf02cSchristos 					  type);
5298dbcf02cSchristos 		wpa_printf(MSG_DEBUG, "TNC: ReceiveMessage: %lu",
5308dbcf02cSchristos 			   (unsigned long) res);
5318dbcf02cSchristos 	}
5328dbcf02cSchristos }
5338dbcf02cSchristos 
5348dbcf02cSchristos 
5358dbcf02cSchristos static void tncs_batch_ending(struct tncs_data *tncs)
5368dbcf02cSchristos {
5378dbcf02cSchristos 	struct tnc_if_imv *imv;
5388dbcf02cSchristos 	TNC_Result res;
5398dbcf02cSchristos 
5408dbcf02cSchristos 	for (imv = tncs->imv; imv; imv = imv->next) {
5418dbcf02cSchristos 		if (imv->BatchEnding == NULL)
5428dbcf02cSchristos 			continue;
5438dbcf02cSchristos 
5448dbcf02cSchristos 		wpa_printf(MSG_DEBUG, "TNC: Call BatchEnding for IMV '%s'",
5458dbcf02cSchristos 			   imv->name);
5468dbcf02cSchristos 		res = imv->BatchEnding(imv->imvID, tncs->connectionID);
5478dbcf02cSchristos 		wpa_printf(MSG_DEBUG, "TNC: BatchEnding: %lu",
5488dbcf02cSchristos 			   (unsigned long) res);
5498dbcf02cSchristos 	}
5508dbcf02cSchristos }
5518dbcf02cSchristos 
5528dbcf02cSchristos 
5538dbcf02cSchristos static void tncs_solicit_recommendation(struct tncs_data *tncs)
5548dbcf02cSchristos {
5558dbcf02cSchristos 	struct tnc_if_imv *imv;
5568dbcf02cSchristos 	TNC_Result res;
5578dbcf02cSchristos 
5588dbcf02cSchristos 	for (imv = tncs->imv; imv; imv = imv->next) {
5598dbcf02cSchristos 		if (tncs->imv_data[imv->imvID].recommendation_set)
5608dbcf02cSchristos 			continue;
5618dbcf02cSchristos 
5628dbcf02cSchristos 		wpa_printf(MSG_DEBUG, "TNC: Call SolicitRecommendation for "
5638dbcf02cSchristos 			   "IMV '%s'", imv->name);
5648dbcf02cSchristos 		res = imv->SolicitRecommendation(imv->imvID,
5658dbcf02cSchristos 						 tncs->connectionID);
5668dbcf02cSchristos 		wpa_printf(MSG_DEBUG, "TNC: SolicitRecommendation: %lu",
5678dbcf02cSchristos 			   (unsigned long) res);
5688dbcf02cSchristos 	}
5698dbcf02cSchristos }
5708dbcf02cSchristos 
5718dbcf02cSchristos 
5728dbcf02cSchristos void tncs_init_connection(struct tncs_data *tncs)
5738dbcf02cSchristos {
5748dbcf02cSchristos 	struct tnc_if_imv *imv;
5758dbcf02cSchristos 	int i;
5768dbcf02cSchristos 
5778dbcf02cSchristos 	for (imv = tncs->imv; imv; imv = imv->next) {
5788dbcf02cSchristos 		tncs_imv_notify_connection_change(
5798dbcf02cSchristos 			imv, tncs->connectionID, TNC_CONNECTION_STATE_CREATE);
5808dbcf02cSchristos 		tncs_imv_notify_connection_change(
5818dbcf02cSchristos 			imv, tncs->connectionID,
5828dbcf02cSchristos 			TNC_CONNECTION_STATE_HANDSHAKE);
5838dbcf02cSchristos 	}
5848dbcf02cSchristos 
5858dbcf02cSchristos 	for (i = 0; i < TNC_MAX_IMV_ID; i++) {
5868dbcf02cSchristos 		os_free(tncs->imv_data[i].imv_send);
5878dbcf02cSchristos 		tncs->imv_data[i].imv_send = NULL;
5888dbcf02cSchristos 		tncs->imv_data[i].imv_send_len = 0;
5898dbcf02cSchristos 	}
5908dbcf02cSchristos }
5918dbcf02cSchristos 
5928dbcf02cSchristos 
5938dbcf02cSchristos size_t tncs_total_send_len(struct tncs_data *tncs)
5948dbcf02cSchristos {
5958dbcf02cSchristos 	int i;
5968dbcf02cSchristos 	size_t len = 0;
5978dbcf02cSchristos 
5988dbcf02cSchristos 	for (i = 0; i < TNC_MAX_IMV_ID; i++)
5998dbcf02cSchristos 		len += tncs->imv_data[i].imv_send_len;
6008dbcf02cSchristos 	if (tncs->tncs_message)
6018dbcf02cSchristos 		len += os_strlen(tncs->tncs_message);
6028dbcf02cSchristos 	return len;
6038dbcf02cSchristos }
6048dbcf02cSchristos 
6058dbcf02cSchristos 
6068dbcf02cSchristos u8 * tncs_copy_send_buf(struct tncs_data *tncs, u8 *pos)
6078dbcf02cSchristos {
6088dbcf02cSchristos 	int i;
6098dbcf02cSchristos 
6108dbcf02cSchristos 	for (i = 0; i < TNC_MAX_IMV_ID; i++) {
6118dbcf02cSchristos 		if (tncs->imv_data[i].imv_send == NULL)
6128dbcf02cSchristos 			continue;
6138dbcf02cSchristos 
6148dbcf02cSchristos 		os_memcpy(pos, tncs->imv_data[i].imv_send,
6158dbcf02cSchristos 			  tncs->imv_data[i].imv_send_len);
6168dbcf02cSchristos 		pos += tncs->imv_data[i].imv_send_len;
6178dbcf02cSchristos 		os_free(tncs->imv_data[i].imv_send);
6188dbcf02cSchristos 		tncs->imv_data[i].imv_send = NULL;
6198dbcf02cSchristos 		tncs->imv_data[i].imv_send_len = 0;
6208dbcf02cSchristos 	}
6218dbcf02cSchristos 
6228dbcf02cSchristos 	if (tncs->tncs_message) {
6238dbcf02cSchristos 		size_t len = os_strlen(tncs->tncs_message);
6248dbcf02cSchristos 		os_memcpy(pos, tncs->tncs_message, len);
6258dbcf02cSchristos 		pos += len;
6268dbcf02cSchristos 		os_free(tncs->tncs_message);
6278dbcf02cSchristos 		tncs->tncs_message = NULL;
6288dbcf02cSchristos 	}
6298dbcf02cSchristos 
6308dbcf02cSchristos 	return pos;
6318dbcf02cSchristos }
6328dbcf02cSchristos 
6338dbcf02cSchristos 
6348dbcf02cSchristos char * tncs_if_tnccs_start(struct tncs_data *tncs)
6358dbcf02cSchristos {
6368dbcf02cSchristos 	char *buf = os_malloc(1000);
6378dbcf02cSchristos 	if (buf == NULL)
6388dbcf02cSchristos 		return NULL;
6398dbcf02cSchristos 	tncs->last_batchid++;
6408dbcf02cSchristos 	os_snprintf(buf, 1000, IF_TNCCS_START, tncs->last_batchid);
6418dbcf02cSchristos 	return buf;
6428dbcf02cSchristos }
6438dbcf02cSchristos 
6448dbcf02cSchristos 
6458dbcf02cSchristos char * tncs_if_tnccs_end(void)
6468dbcf02cSchristos {
6478dbcf02cSchristos 	char *buf = os_malloc(100);
6488dbcf02cSchristos 	if (buf == NULL)
6498dbcf02cSchristos 		return NULL;
6508dbcf02cSchristos 	os_snprintf(buf, 100, IF_TNCCS_END);
6518dbcf02cSchristos 	return buf;
6528dbcf02cSchristos }
6538dbcf02cSchristos 
6548dbcf02cSchristos 
6558dbcf02cSchristos static int tncs_get_type(char *start, unsigned int *type)
6568dbcf02cSchristos {
6578dbcf02cSchristos 	char *pos = os_strstr(start, "<Type>");
6588dbcf02cSchristos 	if (pos == NULL)
6598dbcf02cSchristos 		return -1;
6608dbcf02cSchristos 	pos += 6;
6618dbcf02cSchristos 	*type = strtoul(pos, NULL, 16);
6628dbcf02cSchristos 	return 0;
6638dbcf02cSchristos }
6648dbcf02cSchristos 
6658dbcf02cSchristos 
6668dbcf02cSchristos static unsigned char * tncs_get_base64(char *start, size_t *decoded_len)
6678dbcf02cSchristos {
6688dbcf02cSchristos 	char *pos, *pos2;
6698dbcf02cSchristos 	unsigned char *decoded;
6708dbcf02cSchristos 
6718dbcf02cSchristos 	pos = os_strstr(start, "<Base64>");
6728dbcf02cSchristos 	if (pos == NULL)
6738dbcf02cSchristos 		return NULL;
6748dbcf02cSchristos 
6758dbcf02cSchristos 	pos += 8;
6768dbcf02cSchristos 	pos2 = os_strstr(pos, "</Base64>");
6778dbcf02cSchristos 	if (pos2 == NULL)
6788dbcf02cSchristos 		return NULL;
6798dbcf02cSchristos 	*pos2 = '\0';
6808dbcf02cSchristos 
681*bb618362Schristos 	decoded = base64_decode(pos, os_strlen(pos), decoded_len);
6828dbcf02cSchristos 	*pos2 = '<';
6838dbcf02cSchristos 	if (decoded == NULL) {
6848dbcf02cSchristos 		wpa_printf(MSG_DEBUG, "TNC: Failed to decode Base64 data");
6858dbcf02cSchristos 	}
6868dbcf02cSchristos 
6878dbcf02cSchristos 	return decoded;
6888dbcf02cSchristos }
6898dbcf02cSchristos 
6908dbcf02cSchristos 
6918dbcf02cSchristos static enum tncs_process_res tncs_derive_recommendation(struct tncs_data *tncs)
6928dbcf02cSchristos {
6938dbcf02cSchristos 	enum IMV_Action_Recommendation rec;
6948dbcf02cSchristos 	struct tnc_if_imv *imv;
6958dbcf02cSchristos 	TNC_ConnectionState state;
6968dbcf02cSchristos 	char *txt;
6978dbcf02cSchristos 
6988dbcf02cSchristos 	wpa_printf(MSG_DEBUG, "TNC: No more messages from IMVs");
6998dbcf02cSchristos 
7008dbcf02cSchristos 	if (tncs->done)
7018dbcf02cSchristos 		return TNCCS_PROCESS_OK_NO_RECOMMENDATION;
7028dbcf02cSchristos 
7038dbcf02cSchristos 	tncs_solicit_recommendation(tncs);
7048dbcf02cSchristos 
7058dbcf02cSchristos 	/* Select the most restrictive recommendation */
7068dbcf02cSchristos 	rec = TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION;
7078dbcf02cSchristos 	for (imv = tncs->imv; imv; imv = imv->next) {
7088dbcf02cSchristos 		TNC_IMV_Action_Recommendation irec;
7098dbcf02cSchristos 		irec = tncs->imv_data[imv->imvID].recommendation;
7108dbcf02cSchristos 		if (irec == TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS)
7118dbcf02cSchristos 			rec = TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS;
7128dbcf02cSchristos 		if (irec == TNC_IMV_ACTION_RECOMMENDATION_ISOLATE &&
7138dbcf02cSchristos 		    rec != TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS)
7148dbcf02cSchristos 			rec = TNC_IMV_ACTION_RECOMMENDATION_ISOLATE;
7158dbcf02cSchristos 		if (irec == TNC_IMV_ACTION_RECOMMENDATION_ALLOW &&
7168dbcf02cSchristos 		    rec == TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION)
7178dbcf02cSchristos 			rec = TNC_IMV_ACTION_RECOMMENDATION_ALLOW;
7188dbcf02cSchristos 	}
7198dbcf02cSchristos 
7208dbcf02cSchristos 	wpa_printf(MSG_DEBUG, "TNC: Recommendation: %d", rec);
7218dbcf02cSchristos 	tncs->recommendation = rec;
7228dbcf02cSchristos 	tncs->done = 1;
7238dbcf02cSchristos 
7248dbcf02cSchristos 	txt = NULL;
7258dbcf02cSchristos 	switch (rec) {
7268dbcf02cSchristos 	case TNC_IMV_ACTION_RECOMMENDATION_ALLOW:
7278dbcf02cSchristos 	case TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION:
7288dbcf02cSchristos 		txt = "allow";
7298dbcf02cSchristos 		state = TNC_CONNECTION_STATE_ACCESS_ALLOWED;
7308dbcf02cSchristos 		break;
7318dbcf02cSchristos 	case TNC_IMV_ACTION_RECOMMENDATION_ISOLATE:
7328dbcf02cSchristos 		txt = "isolate";
7338dbcf02cSchristos 		state = TNC_CONNECTION_STATE_ACCESS_ISOLATED;
7348dbcf02cSchristos 		break;
7358dbcf02cSchristos 	case TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS:
7368dbcf02cSchristos 		txt = "none";
7378dbcf02cSchristos 		state = TNC_CONNECTION_STATE_ACCESS_NONE;
7388dbcf02cSchristos 		break;
7398dbcf02cSchristos 	default:
7408dbcf02cSchristos 		state = TNC_CONNECTION_STATE_ACCESS_ALLOWED;
7418dbcf02cSchristos 		break;
7428dbcf02cSchristos 	}
7438dbcf02cSchristos 
7448dbcf02cSchristos 	if (txt) {
7458dbcf02cSchristos 		os_free(tncs->tncs_message);
7468dbcf02cSchristos 		tncs->tncs_message = os_zalloc(200);
7478dbcf02cSchristos 		if (tncs->tncs_message) {
7488dbcf02cSchristos 			os_snprintf(tncs->tncs_message, 199,
7498dbcf02cSchristos 				    "<TNCC-TNCS-Message><Type>%08X</Type>"
7508dbcf02cSchristos 				    "<XML><TNCCS-Recommendation type=\"%s\">"
7518dbcf02cSchristos 				    "</TNCCS-Recommendation></XML>"
7528dbcf02cSchristos 				    "</TNCC-TNCS-Message>",
7538dbcf02cSchristos 				    TNC_TNCCS_RECOMMENDATION, txt);
7548dbcf02cSchristos 		}
7558dbcf02cSchristos 	}
7568dbcf02cSchristos 
7578dbcf02cSchristos 	for (imv = tncs->imv; imv; imv = imv->next) {
7588dbcf02cSchristos 		tncs_imv_notify_connection_change(imv, tncs->connectionID,
7598dbcf02cSchristos 						  state);
7608dbcf02cSchristos 	}
7618dbcf02cSchristos 
7628dbcf02cSchristos 	switch (rec) {
7638dbcf02cSchristos 	case TNC_IMV_ACTION_RECOMMENDATION_ALLOW:
7648dbcf02cSchristos 		return TNCCS_RECOMMENDATION_ALLOW;
7658dbcf02cSchristos 	case TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS:
7668dbcf02cSchristos 		return TNCCS_RECOMMENDATION_NO_ACCESS;
7678dbcf02cSchristos 	case TNC_IMV_ACTION_RECOMMENDATION_ISOLATE:
7688dbcf02cSchristos 		return TNCCS_RECOMMENDATION_ISOLATE;
7698dbcf02cSchristos 	case TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION:
7708dbcf02cSchristos 		return TNCCS_RECOMMENDATION_NO_RECOMMENDATION;
7718dbcf02cSchristos 	default:
7728dbcf02cSchristos 		return TNCCS_PROCESS_ERROR;
7738dbcf02cSchristos 	}
7748dbcf02cSchristos }
7758dbcf02cSchristos 
7768dbcf02cSchristos 
7778dbcf02cSchristos enum tncs_process_res tncs_process_if_tnccs(struct tncs_data *tncs,
7788dbcf02cSchristos 					    const u8 *msg, size_t len)
7798dbcf02cSchristos {
7808dbcf02cSchristos 	char *buf, *start, *end, *pos, *pos2, *payload;
7818dbcf02cSchristos 	unsigned int batch_id;
7828dbcf02cSchristos 	unsigned char *decoded;
7838dbcf02cSchristos 	size_t decoded_len;
7848dbcf02cSchristos 
7853c260e60Schristos 	buf = dup_binstr(msg, len);
7868dbcf02cSchristos 	if (buf == NULL)
7878dbcf02cSchristos 		return TNCCS_PROCESS_ERROR;
7888dbcf02cSchristos 
7898dbcf02cSchristos 	start = os_strstr(buf, "<TNCCS-Batch ");
7908dbcf02cSchristos 	end = os_strstr(buf, "</TNCCS-Batch>");
7918dbcf02cSchristos 	if (start == NULL || end == NULL || start > end) {
7928dbcf02cSchristos 		os_free(buf);
7938dbcf02cSchristos 		return TNCCS_PROCESS_ERROR;
7948dbcf02cSchristos 	}
7958dbcf02cSchristos 
7968dbcf02cSchristos 	start += 13;
7978dbcf02cSchristos 	while (*start == ' ')
7988dbcf02cSchristos 		start++;
7998dbcf02cSchristos 	*end = '\0';
8008dbcf02cSchristos 
8018dbcf02cSchristos 	pos = os_strstr(start, "BatchId=");
8028dbcf02cSchristos 	if (pos == NULL) {
8038dbcf02cSchristos 		os_free(buf);
8048dbcf02cSchristos 		return TNCCS_PROCESS_ERROR;
8058dbcf02cSchristos 	}
8068dbcf02cSchristos 
8078dbcf02cSchristos 	pos += 8;
8088dbcf02cSchristos 	if (*pos == '"')
8098dbcf02cSchristos 		pos++;
8108dbcf02cSchristos 	batch_id = atoi(pos);
8118dbcf02cSchristos 	wpa_printf(MSG_DEBUG, "TNC: Received IF-TNCCS BatchId=%u",
8128dbcf02cSchristos 		   batch_id);
8138dbcf02cSchristos 	if (batch_id != tncs->last_batchid + 1) {
8148dbcf02cSchristos 		wpa_printf(MSG_DEBUG, "TNC: Unexpected IF-TNCCS BatchId "
8158dbcf02cSchristos 			   "%u (expected %u)",
8168dbcf02cSchristos 			   batch_id, tncs->last_batchid + 1);
8178dbcf02cSchristos 		os_free(buf);
8188dbcf02cSchristos 		return TNCCS_PROCESS_ERROR;
8198dbcf02cSchristos 	}
8208dbcf02cSchristos 	tncs->last_batchid = batch_id;
8218dbcf02cSchristos 
8228dbcf02cSchristos 	while (*pos != '\0' && *pos != '>')
8238dbcf02cSchristos 		pos++;
8248dbcf02cSchristos 	if (*pos == '\0') {
8258dbcf02cSchristos 		os_free(buf);
8268dbcf02cSchristos 		return TNCCS_PROCESS_ERROR;
8278dbcf02cSchristos 	}
8288dbcf02cSchristos 	pos++;
8298dbcf02cSchristos 	payload = start;
8308dbcf02cSchristos 
8318dbcf02cSchristos 	/*
8328dbcf02cSchristos 	 * <IMC-IMV-Message>
8338dbcf02cSchristos 	 * <Type>01234567</Type>
8348dbcf02cSchristos 	 * <Base64>foo==</Base64>
8358dbcf02cSchristos 	 * </IMC-IMV-Message>
8368dbcf02cSchristos 	 */
8378dbcf02cSchristos 
8388dbcf02cSchristos 	while (*start) {
8398dbcf02cSchristos 		char *endpos;
8408dbcf02cSchristos 		unsigned int type;
8418dbcf02cSchristos 
8428dbcf02cSchristos 		pos = os_strstr(start, "<IMC-IMV-Message>");
8438dbcf02cSchristos 		if (pos == NULL)
8448dbcf02cSchristos 			break;
8458dbcf02cSchristos 		start = pos + 17;
8468dbcf02cSchristos 		end = os_strstr(start, "</IMC-IMV-Message>");
8478dbcf02cSchristos 		if (end == NULL)
8488dbcf02cSchristos 			break;
8498dbcf02cSchristos 		*end = '\0';
8508dbcf02cSchristos 		endpos = end;
8518dbcf02cSchristos 		end += 18;
8528dbcf02cSchristos 
8538dbcf02cSchristos 		if (tncs_get_type(start, &type) < 0) {
8548dbcf02cSchristos 			*endpos = '<';
8558dbcf02cSchristos 			start = end;
8568dbcf02cSchristos 			continue;
8578dbcf02cSchristos 		}
8588dbcf02cSchristos 		wpa_printf(MSG_DEBUG, "TNC: IMC-IMV-Message Type 0x%x", type);
8598dbcf02cSchristos 
8608dbcf02cSchristos 		decoded = tncs_get_base64(start, &decoded_len);
8618dbcf02cSchristos 		if (decoded == NULL) {
8628dbcf02cSchristos 			*endpos = '<';
8638dbcf02cSchristos 			start = end;
8648dbcf02cSchristos 			continue;
8658dbcf02cSchristos 		}
8668dbcf02cSchristos 
8678dbcf02cSchristos 		tncs_send_to_imvs(tncs, type, decoded, decoded_len);
8688dbcf02cSchristos 
8698dbcf02cSchristos 		os_free(decoded);
8708dbcf02cSchristos 
8718dbcf02cSchristos 		start = end;
8728dbcf02cSchristos 	}
8738dbcf02cSchristos 
8748dbcf02cSchristos 	/*
8758dbcf02cSchristos 	 * <TNCC-TNCS-Message>
8768dbcf02cSchristos 	 * <Type>01234567</Type>
8778dbcf02cSchristos 	 * <XML><TNCCS-Foo type="foo"></TNCCS-Foo></XML>
8788dbcf02cSchristos 	 * <Base64>foo==</Base64>
8798dbcf02cSchristos 	 * </TNCC-TNCS-Message>
8808dbcf02cSchristos 	 */
8818dbcf02cSchristos 
8828dbcf02cSchristos 	start = payload;
8838dbcf02cSchristos 	while (*start) {
8848dbcf02cSchristos 		unsigned int type;
8858dbcf02cSchristos 		char *xml, *xmlend, *endpos;
8868dbcf02cSchristos 
8878dbcf02cSchristos 		pos = os_strstr(start, "<TNCC-TNCS-Message>");
8888dbcf02cSchristos 		if (pos == NULL)
8898dbcf02cSchristos 			break;
8908dbcf02cSchristos 		start = pos + 19;
8918dbcf02cSchristos 		end = os_strstr(start, "</TNCC-TNCS-Message>");
8928dbcf02cSchristos 		if (end == NULL)
8938dbcf02cSchristos 			break;
8948dbcf02cSchristos 		*end = '\0';
8958dbcf02cSchristos 		endpos = end;
8968dbcf02cSchristos 		end += 20;
8978dbcf02cSchristos 
8988dbcf02cSchristos 		if (tncs_get_type(start, &type) < 0) {
8998dbcf02cSchristos 			*endpos = '<';
9008dbcf02cSchristos 			start = end;
9018dbcf02cSchristos 			continue;
9028dbcf02cSchristos 		}
9038dbcf02cSchristos 		wpa_printf(MSG_DEBUG, "TNC: TNCC-TNCS-Message Type 0x%x",
9048dbcf02cSchristos 			   type);
9058dbcf02cSchristos 
9068dbcf02cSchristos 		/* Base64 OR XML */
9078dbcf02cSchristos 		decoded = NULL;
9088dbcf02cSchristos 		xml = NULL;
9098dbcf02cSchristos 		xmlend = NULL;
9108dbcf02cSchristos 		pos = os_strstr(start, "<XML>");
9118dbcf02cSchristos 		if (pos) {
9128dbcf02cSchristos 			pos += 5;
9138dbcf02cSchristos 			pos2 = os_strstr(pos, "</XML>");
9148dbcf02cSchristos 			if (pos2 == NULL) {
9158dbcf02cSchristos 				*endpos = '<';
9168dbcf02cSchristos 				start = end;
9178dbcf02cSchristos 				continue;
9188dbcf02cSchristos 			}
9198dbcf02cSchristos 			xmlend = pos2;
9208dbcf02cSchristos 			xml = pos;
9218dbcf02cSchristos 		} else {
9228dbcf02cSchristos 			decoded = tncs_get_base64(start, &decoded_len);
9238dbcf02cSchristos 			if (decoded == NULL) {
9248dbcf02cSchristos 				*endpos = '<';
9258dbcf02cSchristos 				start = end;
9268dbcf02cSchristos 				continue;
9278dbcf02cSchristos 			}
9288dbcf02cSchristos 		}
9298dbcf02cSchristos 
9308dbcf02cSchristos 		if (decoded) {
9318dbcf02cSchristos 			wpa_hexdump_ascii(MSG_MSGDUMP,
9328dbcf02cSchristos 					  "TNC: TNCC-TNCS-Message Base64",
9338dbcf02cSchristos 					  decoded, decoded_len);
9348dbcf02cSchristos 			os_free(decoded);
9358dbcf02cSchristos 		}
9368dbcf02cSchristos 
9378dbcf02cSchristos 		if (xml) {
9388dbcf02cSchristos 			wpa_hexdump_ascii(MSG_MSGDUMP,
9398dbcf02cSchristos 					  "TNC: TNCC-TNCS-Message XML",
9408dbcf02cSchristos 					  (unsigned char *) xml,
9418dbcf02cSchristos 					  xmlend - xml);
9428dbcf02cSchristos 		}
9438dbcf02cSchristos 
9448dbcf02cSchristos 		start = end;
9458dbcf02cSchristos 	}
9468dbcf02cSchristos 
9478dbcf02cSchristos 	os_free(buf);
9488dbcf02cSchristos 
9498dbcf02cSchristos 	tncs_batch_ending(tncs);
9508dbcf02cSchristos 
9518dbcf02cSchristos 	if (tncs_total_send_len(tncs) == 0)
9528dbcf02cSchristos 		return tncs_derive_recommendation(tncs);
9538dbcf02cSchristos 
9548dbcf02cSchristos 	return TNCCS_PROCESS_OK_NO_RECOMMENDATION;
9558dbcf02cSchristos }
9568dbcf02cSchristos 
9578dbcf02cSchristos 
9588dbcf02cSchristos static struct tnc_if_imv * tncs_parse_imv(int id, char *start, char *end,
9598dbcf02cSchristos 					  int *error)
9608dbcf02cSchristos {
9618dbcf02cSchristos 	struct tnc_if_imv *imv;
9628dbcf02cSchristos 	char *pos, *pos2;
9638dbcf02cSchristos 
9648dbcf02cSchristos 	if (id >= TNC_MAX_IMV_ID) {
9658dbcf02cSchristos 		wpa_printf(MSG_DEBUG, "TNC: Too many IMVs");
9668dbcf02cSchristos 		return NULL;
9678dbcf02cSchristos 	}
9688dbcf02cSchristos 
9698dbcf02cSchristos 	imv = os_zalloc(sizeof(*imv));
9708dbcf02cSchristos 	if (imv == NULL) {
9718dbcf02cSchristos 		*error = 1;
9728dbcf02cSchristos 		return NULL;
9738dbcf02cSchristos 	}
9748dbcf02cSchristos 
9758dbcf02cSchristos 	imv->imvID = id;
9768dbcf02cSchristos 
9778dbcf02cSchristos 	pos = start;
9788dbcf02cSchristos 	wpa_printf(MSG_DEBUG, "TNC: Configured IMV: %s", pos);
9798dbcf02cSchristos 	if (pos + 1 >= end || *pos != '"') {
9808dbcf02cSchristos 		wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMV line '%s' "
9818dbcf02cSchristos 			   "(no starting quotation mark)", start);
9828dbcf02cSchristos 		os_free(imv);
9838dbcf02cSchristos 		return NULL;
9848dbcf02cSchristos 	}
9858dbcf02cSchristos 
9868dbcf02cSchristos 	pos++;
9878dbcf02cSchristos 	pos2 = pos;
9888dbcf02cSchristos 	while (pos2 < end && *pos2 != '"')
9898dbcf02cSchristos 		pos2++;
9908dbcf02cSchristos 	if (pos2 >= end) {
9918dbcf02cSchristos 		wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMV line '%s' "
9928dbcf02cSchristos 			   "(no ending quotation mark)", start);
9938dbcf02cSchristos 		os_free(imv);
9948dbcf02cSchristos 		return NULL;
9958dbcf02cSchristos 	}
9968dbcf02cSchristos 	*pos2 = '\0';
9978dbcf02cSchristos 	wpa_printf(MSG_DEBUG, "TNC: Name: '%s'", pos);
9988dbcf02cSchristos 	imv->name = os_strdup(pos);
9998dbcf02cSchristos 
10008dbcf02cSchristos 	pos = pos2 + 1;
10018dbcf02cSchristos 	if (pos >= end || *pos != ' ') {
10028dbcf02cSchristos 		wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMV line '%s' "
10038dbcf02cSchristos 			   "(no space after name)", start);
10048dbcf02cSchristos 		os_free(imv);
10058dbcf02cSchristos 		return NULL;
10068dbcf02cSchristos 	}
10078dbcf02cSchristos 
10088dbcf02cSchristos 	pos++;
10098dbcf02cSchristos 	wpa_printf(MSG_DEBUG, "TNC: IMV file: '%s'", pos);
10108dbcf02cSchristos 	imv->path = os_strdup(pos);
10118dbcf02cSchristos 
10128dbcf02cSchristos 	return imv;
10138dbcf02cSchristos }
10148dbcf02cSchristos 
10158dbcf02cSchristos 
10168dbcf02cSchristos static int tncs_read_config(struct tncs_global *global)
10178dbcf02cSchristos {
10188dbcf02cSchristos 	char *config, *end, *pos, *line_end;
10198dbcf02cSchristos 	size_t config_len;
10208dbcf02cSchristos 	struct tnc_if_imv *imv, *last;
10218dbcf02cSchristos 	int id = 0;
10228dbcf02cSchristos 
10238dbcf02cSchristos 	last = NULL;
10248dbcf02cSchristos 
10258dbcf02cSchristos 	config = os_readfile(TNC_CONFIG_FILE, &config_len);
10268dbcf02cSchristos 	if (config == NULL) {
10278dbcf02cSchristos 		wpa_printf(MSG_ERROR, "TNC: Could not open TNC configuration "
10288dbcf02cSchristos 			   "file '%s'", TNC_CONFIG_FILE);
10298dbcf02cSchristos 		return -1;
10308dbcf02cSchristos 	}
10318dbcf02cSchristos 
10328dbcf02cSchristos 	end = config + config_len;
10338dbcf02cSchristos 	for (pos = config; pos < end; pos = line_end + 1) {
10348dbcf02cSchristos 		line_end = pos;
10358dbcf02cSchristos 		while (*line_end != '\n' && *line_end != '\r' &&
10368dbcf02cSchristos 		       line_end < end)
10378dbcf02cSchristos 			line_end++;
10388dbcf02cSchristos 		*line_end = '\0';
10398dbcf02cSchristos 
10408dbcf02cSchristos 		if (os_strncmp(pos, "IMV ", 4) == 0) {
10418dbcf02cSchristos 			int error = 0;
10428dbcf02cSchristos 
10438dbcf02cSchristos 			imv = tncs_parse_imv(id++, pos + 4, line_end, &error);
10448dbcf02cSchristos 			if (error)
10458dbcf02cSchristos 				return -1;
10468dbcf02cSchristos 			if (imv) {
10478dbcf02cSchristos 				if (last == NULL)
10488dbcf02cSchristos 					global->imv = imv;
10498dbcf02cSchristos 				else
10508dbcf02cSchristos 					last->next = imv;
10518dbcf02cSchristos 				last = imv;
10528dbcf02cSchristos 			}
10538dbcf02cSchristos 		}
10548dbcf02cSchristos 	}
10558dbcf02cSchristos 
10568dbcf02cSchristos 	os_free(config);
10578dbcf02cSchristos 
10588dbcf02cSchristos 	return 0;
10598dbcf02cSchristos }
10608dbcf02cSchristos 
10618dbcf02cSchristos 
10628dbcf02cSchristos struct tncs_data * tncs_init(void)
10638dbcf02cSchristos {
10648dbcf02cSchristos 	struct tncs_data *tncs;
10658dbcf02cSchristos 
10668dbcf02cSchristos 	if (tncs_global_data == NULL)
10678dbcf02cSchristos 		return NULL;
10688dbcf02cSchristos 
10698dbcf02cSchristos 	tncs = os_zalloc(sizeof(*tncs));
10708dbcf02cSchristos 	if (tncs == NULL)
10718dbcf02cSchristos 		return NULL;
10728dbcf02cSchristos 	tncs->imv = tncs_global_data->imv;
10738dbcf02cSchristos 	tncs->connectionID = tncs_global_data->next_conn_id++;
10748dbcf02cSchristos 	tncs->next = tncs_global_data->connections;
10758dbcf02cSchristos 	tncs_global_data->connections = tncs;
10768dbcf02cSchristos 
10778dbcf02cSchristos 	return tncs;
10788dbcf02cSchristos }
10798dbcf02cSchristos 
10808dbcf02cSchristos 
10818dbcf02cSchristos void tncs_deinit(struct tncs_data *tncs)
10828dbcf02cSchristos {
10838dbcf02cSchristos 	int i;
10848dbcf02cSchristos 	struct tncs_data *prev, *conn;
10858dbcf02cSchristos 
10868dbcf02cSchristos 	if (tncs == NULL)
10878dbcf02cSchristos 		return;
10888dbcf02cSchristos 
10898dbcf02cSchristos 	for (i = 0; i < TNC_MAX_IMV_ID; i++)
10908dbcf02cSchristos 		os_free(tncs->imv_data[i].imv_send);
10918dbcf02cSchristos 
10928dbcf02cSchristos 	prev = NULL;
10938dbcf02cSchristos 	conn = tncs_global_data->connections;
10948dbcf02cSchristos 	while (conn) {
10958dbcf02cSchristos 		if (conn == tncs) {
10968dbcf02cSchristos 			if (prev)
10978dbcf02cSchristos 				prev->next = tncs->next;
10988dbcf02cSchristos 			else
10998dbcf02cSchristos 				tncs_global_data->connections = tncs->next;
11008dbcf02cSchristos 			break;
11018dbcf02cSchristos 		}
11028dbcf02cSchristos 		prev = conn;
11038dbcf02cSchristos 		conn = conn->next;
11048dbcf02cSchristos 	}
11058dbcf02cSchristos 
11068dbcf02cSchristos 	os_free(tncs->tncs_message);
11078dbcf02cSchristos 	os_free(tncs);
11088dbcf02cSchristos }
11098dbcf02cSchristos 
11108dbcf02cSchristos 
11118dbcf02cSchristos int tncs_global_init(void)
11128dbcf02cSchristos {
11138dbcf02cSchristos 	struct tnc_if_imv *imv;
11148dbcf02cSchristos 
11153c260e60Schristos 	if (tncs_global_data)
11163c260e60Schristos 		return 0;
11173c260e60Schristos 
11188dbcf02cSchristos 	tncs_global_data = os_zalloc(sizeof(*tncs_global_data));
11198dbcf02cSchristos 	if (tncs_global_data == NULL)
11208dbcf02cSchristos 		return -1;
11218dbcf02cSchristos 
11228dbcf02cSchristos 	if (tncs_read_config(tncs_global_data) < 0) {
11238dbcf02cSchristos 		wpa_printf(MSG_ERROR, "TNC: Failed to read TNC configuration");
11248dbcf02cSchristos 		goto failed;
11258dbcf02cSchristos 	}
11268dbcf02cSchristos 
11278dbcf02cSchristos 	for (imv = tncs_global_data->imv; imv; imv = imv->next) {
11288dbcf02cSchristos 		if (tncs_load_imv(imv)) {
11298dbcf02cSchristos 			wpa_printf(MSG_ERROR, "TNC: Failed to load IMV '%s'",
11308dbcf02cSchristos 				   imv->name);
11318dbcf02cSchristos 			goto failed;
11328dbcf02cSchristos 		}
11338dbcf02cSchristos 	}
11348dbcf02cSchristos 
11358dbcf02cSchristos 	return 0;
11368dbcf02cSchristos 
11378dbcf02cSchristos failed:
11388dbcf02cSchristos 	tncs_global_deinit();
11398dbcf02cSchristos 	return -1;
11408dbcf02cSchristos }
11418dbcf02cSchristos 
11428dbcf02cSchristos 
11438dbcf02cSchristos void tncs_global_deinit(void)
11448dbcf02cSchristos {
11458dbcf02cSchristos 	struct tnc_if_imv *imv, *prev;
11468dbcf02cSchristos 
11478dbcf02cSchristos 	if (tncs_global_data == NULL)
11488dbcf02cSchristos 		return;
11498dbcf02cSchristos 
11508dbcf02cSchristos 	imv = tncs_global_data->imv;
11518dbcf02cSchristos 	while (imv) {
11528dbcf02cSchristos 		tncs_unload_imv(imv);
11538dbcf02cSchristos 
11548dbcf02cSchristos 		prev = imv;
11558dbcf02cSchristos 		imv = imv->next;
11568dbcf02cSchristos 		os_free(prev);
11578dbcf02cSchristos 	}
11588dbcf02cSchristos 
11598dbcf02cSchristos 	os_free(tncs_global_data);
11608dbcf02cSchristos 	tncs_global_data = NULL;
11618dbcf02cSchristos }
11628dbcf02cSchristos 
11638dbcf02cSchristos 
11648dbcf02cSchristos struct wpabuf * tncs_build_soh_request(void)
11658dbcf02cSchristos {
11668dbcf02cSchristos 	struct wpabuf *buf;
11678dbcf02cSchristos 
11688dbcf02cSchristos 	/*
11698dbcf02cSchristos 	 * Build a SoH Request TLV (to be used inside SoH EAP Extensions
11708dbcf02cSchristos 	 * Method)
11718dbcf02cSchristos 	 */
11728dbcf02cSchristos 
11738dbcf02cSchristos 	buf = wpabuf_alloc(8 + 4);
11748dbcf02cSchristos 	if (buf == NULL)
11758dbcf02cSchristos 		return NULL;
11768dbcf02cSchristos 
11778dbcf02cSchristos 	/* Vendor-Specific TLV (Microsoft) - SoH Request */
11788dbcf02cSchristos 	wpabuf_put_be16(buf, EAP_TLV_VENDOR_SPECIFIC_TLV); /* TLV Type */
11798dbcf02cSchristos 	wpabuf_put_be16(buf, 8); /* Length */
11808dbcf02cSchristos 
11818dbcf02cSchristos 	wpabuf_put_be32(buf, EAP_VENDOR_MICROSOFT); /* Vendor_Id */
11828dbcf02cSchristos 
11838dbcf02cSchristos 	wpabuf_put_be16(buf, 0x02); /* TLV Type - SoH Request TLV */
11848dbcf02cSchristos 	wpabuf_put_be16(buf, 0); /* Length */
11858dbcf02cSchristos 
11868dbcf02cSchristos 	return buf;
11878dbcf02cSchristos }
11888dbcf02cSchristos 
11898dbcf02cSchristos 
11908dbcf02cSchristos struct wpabuf * tncs_process_soh(const u8 *soh_tlv, size_t soh_tlv_len,
11918dbcf02cSchristos 				 int *failure)
11928dbcf02cSchristos {
11938dbcf02cSchristos 	wpa_hexdump(MSG_DEBUG, "TNC: SoH TLV", soh_tlv, soh_tlv_len);
11948dbcf02cSchristos 	*failure = 0;
11958dbcf02cSchristos 
11968dbcf02cSchristos 	/* TODO: return MS-SoH Response TLV */
11978dbcf02cSchristos 
11988dbcf02cSchristos 	return NULL;
11998dbcf02cSchristos }
1200