Home | History | Annotate | Download | only in eap_server
      1 /*
      2  * EAP-TNC - TNCS (IF-IMV, IF-TNCCS, and IF-TNCCS-SOH)
      3  * Copyright (c) 2007-2008, Jouni Malinen <j (at) w1.fi>
      4  *
      5  * This software may be distributed under the terms of the BSD license.
      6  * See README for more details.
      7  */
      8 
      9 #include "includes.h"
     10 #include <dlfcn.h>
     11 
     12 #include "common.h"
     13 #include "base64.h"
     14 #include "tncs.h"
     15 #include "eap_common/eap_tlv_common.h"
     16 #include "eap_common/eap_defs.h"
     17 
     18 
     19 /* TODO: TNCS must be thread-safe; review the code and add locking etc. if
     20  * needed.. */
     21 
     22 #define TNC_CONFIG_FILE "/etc/tnc_config"
     23 #define IF_TNCCS_START \
     24 "<?xml version=\"1.0\"?>\n" \
     25 "<TNCCS-Batch BatchId=\"%d\" Recipient=\"TNCS\" " \
     26 "xmlns=\"http://www.trustedcomputinggroup.org/IWG/TNC/1_0/IF_TNCCS#\" " \
     27 "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " \
     28 "xsi:schemaLocation=\"http://www.trustedcomputinggroup.org/IWG/TNC/1_0/" \
     29 "IF_TNCCS# https://www.trustedcomputinggroup.org/XML/SCHEMA/TNCCS_1.0.xsd\">\n"
     30 #define IF_TNCCS_END "\n</TNCCS-Batch>"
     31 
     32 /* TNC IF-IMV */
     33 
     34 typedef unsigned long TNC_UInt32;
     35 typedef unsigned char *TNC_BufferReference;
     36 
     37 typedef TNC_UInt32 TNC_IMVID;
     38 typedef TNC_UInt32 TNC_ConnectionID;
     39 typedef TNC_UInt32 TNC_ConnectionState;
     40 typedef TNC_UInt32 TNC_RetryReason;
     41 typedef TNC_UInt32 TNC_IMV_Action_Recommendation;
     42 typedef TNC_UInt32 TNC_IMV_Evaluation_Result;
     43 typedef TNC_UInt32 TNC_MessageType;
     44 typedef TNC_MessageType *TNC_MessageTypeList;
     45 typedef TNC_UInt32 TNC_VendorID;
     46 typedef TNC_UInt32 TNC_Subtype;
     47 typedef TNC_UInt32 TNC_Version;
     48 typedef TNC_UInt32 TNC_Result;
     49 typedef TNC_UInt32 TNC_AttributeID;
     50 
     51 typedef TNC_Result (*TNC_TNCS_BindFunctionPointer)(
     52 	TNC_IMVID imvID,
     53 	char *functionName,
     54 	void **pOutfunctionPointer);
     55 
     56 #define TNC_RESULT_SUCCESS 0
     57 #define TNC_RESULT_NOT_INITIALIZED 1
     58 #define TNC_RESULT_ALREADY_INITIALIZED 2
     59 #define TNC_RESULT_NO_COMMON_VERSION 3
     60 #define TNC_RESULT_CANT_RETRY 4
     61 #define TNC_RESULT_WONT_RETRY 5
     62 #define TNC_RESULT_INVALID_PARAMETER 6
     63 #define TNC_RESULT_CANT_RESPOND 7
     64 #define TNC_RESULT_ILLEGAL_OPERATION 8
     65 #define TNC_RESULT_OTHER 9
     66 #define TNC_RESULT_FATAL 10
     67 
     68 #define TNC_CONNECTION_STATE_CREATE 0
     69 #define TNC_CONNECTION_STATE_HANDSHAKE 1
     70 #define TNC_CONNECTION_STATE_ACCESS_ALLOWED 2
     71 #define TNC_CONNECTION_STATE_ACCESS_ISOLATED 3
     72 #define TNC_CONNECTION_STATE_ACCESS_NONE 4
     73 #define TNC_CONNECTION_STATE_DELETE 5
     74 
     75 #define TNC_IFIMV_VERSION_1 1
     76 
     77 #define TNC_VENDORID_ANY ((TNC_VendorID) 0xffffff)
     78 #define TNC_SUBTYPE_ANY ((TNC_Subtype) 0xff)
     79 
     80 /* TNCC-TNCS Message Types */
     81 #define TNC_TNCCS_RECOMMENDATION		0x00000001
     82 #define TNC_TNCCS_ERROR				0x00000002
     83 #define TNC_TNCCS_PREFERREDLANGUAGE		0x00000003
     84 #define TNC_TNCCS_REASONSTRINGS			0x00000004
     85 
     86 /* Possible TNC_IMV_Action_Recommendation values: */
     87 enum IMV_Action_Recommendation {
     88 	TNC_IMV_ACTION_RECOMMENDATION_ALLOW,
     89 	TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS,
     90 	TNC_IMV_ACTION_RECOMMENDATION_ISOLATE,
     91 	TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION
     92 };
     93 
     94 /* Possible TNC_IMV_Evaluation_Result values: */
     95 enum IMV_Evaluation_Result {
     96 	TNC_IMV_EVALUATION_RESULT_COMPLIANT,
     97 	TNC_IMV_EVALUATION_RESULT_NONCOMPLIANT_MINOR,
     98 	TNC_IMV_EVALUATION_RESULT_NONCOMPLIANT_MAJOR,
     99 	TNC_IMV_EVALUATION_RESULT_ERROR,
    100 	TNC_IMV_EVALUATION_RESULT_DONT_KNOW
    101 };
    102 
    103 struct tnc_if_imv {
    104 	struct tnc_if_imv *next;
    105 	char *name;
    106 	char *path;
    107 	void *dlhandle; /* from dlopen() */
    108 	TNC_IMVID imvID;
    109 	TNC_MessageTypeList supported_types;
    110 	size_t num_supported_types;
    111 
    112 	/* Functions implemented by IMVs (with TNC_IMV_ prefix) */
    113 	TNC_Result (*Initialize)(
    114 		TNC_IMVID imvID,
    115 		TNC_Version minVersion,
    116 		TNC_Version maxVersion,
    117 		TNC_Version *pOutActualVersion);
    118 	TNC_Result (*NotifyConnectionChange)(
    119 		TNC_IMVID imvID,
    120 		TNC_ConnectionID connectionID,
    121 		TNC_ConnectionState newState);
    122 	TNC_Result (*ReceiveMessage)(
    123 		TNC_IMVID imvID,
    124 		TNC_ConnectionID connectionID,
    125 		TNC_BufferReference message,
    126 		TNC_UInt32 messageLength,
    127 		TNC_MessageType messageType);
    128 	TNC_Result (*SolicitRecommendation)(
    129 		TNC_IMVID imvID,
    130 		TNC_ConnectionID connectionID);
    131 	TNC_Result (*BatchEnding)(
    132 		TNC_IMVID imvID,
    133 		TNC_ConnectionID connectionID);
    134 	TNC_Result (*Terminate)(TNC_IMVID imvID);
    135 	TNC_Result (*ProvideBindFunction)(
    136 		TNC_IMVID imvID,
    137 		TNC_TNCS_BindFunctionPointer bindFunction);
    138 };
    139 
    140 
    141 #define TNC_MAX_IMV_ID 10
    142 
    143 struct tncs_data {
    144 	struct tncs_data *next;
    145 	struct tnc_if_imv *imv; /* local copy of tncs_global_data->imv */
    146 	TNC_ConnectionID connectionID;
    147 	unsigned int last_batchid;
    148 	enum IMV_Action_Recommendation recommendation;
    149 	int done;
    150 
    151 	struct conn_imv {
    152 		u8 *imv_send;
    153 		size_t imv_send_len;
    154 		enum IMV_Action_Recommendation recommendation;
    155 		int recommendation_set;
    156 	} imv_data[TNC_MAX_IMV_ID];
    157 
    158 	char *tncs_message;
    159 };
    160 
    161 
    162 struct tncs_global {
    163 	struct tnc_if_imv *imv;
    164 	TNC_ConnectionID next_conn_id;
    165 	struct tncs_data *connections;
    166 };
    167 
    168 static struct tncs_global *tncs_global_data = NULL;
    169 
    170 
    171 static struct tnc_if_imv * tncs_get_imv(TNC_IMVID imvID)
    172 {
    173 	struct tnc_if_imv *imv;
    174 
    175 	if (imvID >= TNC_MAX_IMV_ID || tncs_global_data == NULL)
    176 		return NULL;
    177 	imv = tncs_global_data->imv;
    178 	while (imv) {
    179 		if (imv->imvID == imvID)
    180 			return imv;
    181 		imv = imv->next;
    182 	}
    183 	return NULL;
    184 }
    185 
    186 
    187 static struct tncs_data * tncs_get_conn(TNC_ConnectionID connectionID)
    188 {
    189 	struct tncs_data *tncs;
    190 
    191 	if (tncs_global_data == NULL)
    192 		return NULL;
    193 
    194 	tncs = tncs_global_data->connections;
    195 	while (tncs) {
    196 		if (tncs->connectionID == connectionID)
    197 			return tncs;
    198 		tncs = tncs->next;
    199 	}
    200 
    201 	wpa_printf(MSG_DEBUG, "TNC: Connection ID %lu not found",
    202 		   (unsigned long) connectionID);
    203 
    204 	return NULL;
    205 }
    206 
    207 
    208 /* TNCS functions that IMVs can call */
    209 TNC_Result TNC_TNCS_ReportMessageTypes(
    210 	TNC_IMVID imvID,
    211 	TNC_MessageTypeList supportedTypes,
    212 	TNC_UInt32 typeCount)
    213 {
    214 	TNC_UInt32 i;
    215 	struct tnc_if_imv *imv;
    216 
    217 	wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_ReportMessageTypes(imvID=%lu "
    218 		   "typeCount=%lu)",
    219 		   (unsigned long) imvID, (unsigned long) typeCount);
    220 
    221 	for (i = 0; i < typeCount; i++) {
    222 		wpa_printf(MSG_DEBUG, "TNC: supportedTypes[%lu] = %lu",
    223 			   i, supportedTypes[i]);
    224 	}
    225 
    226 	imv = tncs_get_imv(imvID);
    227 	if (imv == NULL)
    228 		return TNC_RESULT_INVALID_PARAMETER;
    229 	os_free(imv->supported_types);
    230 	imv->supported_types =
    231 		os_malloc(typeCount * sizeof(TNC_MessageType));
    232 	if (imv->supported_types == NULL)
    233 		return TNC_RESULT_FATAL;
    234 	os_memcpy(imv->supported_types, supportedTypes,
    235 		  typeCount * sizeof(TNC_MessageType));
    236 	imv->num_supported_types = typeCount;
    237 
    238 	return TNC_RESULT_SUCCESS;
    239 }
    240 
    241 
    242 TNC_Result TNC_TNCS_SendMessage(
    243 	TNC_IMVID imvID,
    244 	TNC_ConnectionID connectionID,
    245 	TNC_BufferReference message,
    246 	TNC_UInt32 messageLength,
    247 	TNC_MessageType messageType)
    248 {
    249 	struct tncs_data *tncs;
    250 	unsigned char *b64;
    251 	size_t b64len;
    252 
    253 	wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_SendMessage(imvID=%lu "
    254 		   "connectionID=%lu messageType=%lu)",
    255 		   imvID, connectionID, messageType);
    256 	wpa_hexdump_ascii(MSG_DEBUG, "TNC: TNC_TNCS_SendMessage",
    257 			  message, messageLength);
    258 
    259 	if (tncs_get_imv(imvID) == NULL)
    260 		return TNC_RESULT_INVALID_PARAMETER;
    261 
    262 	tncs = tncs_get_conn(connectionID);
    263 	if (tncs == NULL)
    264 		return TNC_RESULT_INVALID_PARAMETER;
    265 
    266 	b64 = base64_encode(message, messageLength, &b64len);
    267 	if (b64 == NULL)
    268 		return TNC_RESULT_FATAL;
    269 
    270 	os_free(tncs->imv_data[imvID].imv_send);
    271 	tncs->imv_data[imvID].imv_send_len = 0;
    272 	tncs->imv_data[imvID].imv_send = os_zalloc(b64len + 100);
    273 	if (tncs->imv_data[imvID].imv_send == NULL) {
    274 		os_free(b64);
    275 		return TNC_RESULT_OTHER;
    276 	}
    277 
    278 	tncs->imv_data[imvID].imv_send_len =
    279 		os_snprintf((char *) tncs->imv_data[imvID].imv_send,
    280 			    b64len + 100,
    281 			    "<IMC-IMV-Message><Type>%08X</Type>"
    282 			    "<Base64>%s</Base64></IMC-IMV-Message>",
    283 			    (unsigned int) messageType, b64);
    284 
    285 	os_free(b64);
    286 
    287 	return TNC_RESULT_SUCCESS;
    288 }
    289 
    290 
    291 TNC_Result TNC_TNCS_RequestHandshakeRetry(
    292 	TNC_IMVID imvID,
    293 	TNC_ConnectionID connectionID,
    294 	TNC_RetryReason reason)
    295 {
    296 	wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_RequestHandshakeRetry");
    297 	/* TODO */
    298 	return TNC_RESULT_SUCCESS;
    299 }
    300 
    301 
    302 TNC_Result TNC_TNCS_ProvideRecommendation(
    303 	TNC_IMVID imvID,
    304 	TNC_ConnectionID connectionID,
    305 	TNC_IMV_Action_Recommendation recommendation,
    306 	TNC_IMV_Evaluation_Result evaluation)
    307 {
    308 	struct tncs_data *tncs;
    309 
    310 	wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_ProvideRecommendation(imvID=%lu "
    311 		   "connectionID=%lu recommendation=%lu evaluation=%lu)",
    312 		   (unsigned long) imvID, (unsigned long) connectionID,
    313 		   (unsigned long) recommendation, (unsigned long) evaluation);
    314 
    315 	if (tncs_get_imv(imvID) == NULL)
    316 		return TNC_RESULT_INVALID_PARAMETER;
    317 
    318 	tncs = tncs_get_conn(connectionID);
    319 	if (tncs == NULL)
    320 		return TNC_RESULT_INVALID_PARAMETER;
    321 
    322 	tncs->imv_data[imvID].recommendation = recommendation;
    323 	tncs->imv_data[imvID].recommendation_set = 1;
    324 
    325 	return TNC_RESULT_SUCCESS;
    326 }
    327 
    328 
    329 TNC_Result TNC_TNCS_GetAttribute(
    330 	TNC_IMVID imvID,
    331 	TNC_ConnectionID connectionID,
    332 	TNC_AttributeID attribureID,
    333 	TNC_UInt32 bufferLength,
    334 	TNC_BufferReference buffer,
    335 	TNC_UInt32 *pOutValueLength)
    336 {
    337 	wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_GetAttribute");
    338 	/* TODO */
    339 	return TNC_RESULT_SUCCESS;
    340 }
    341 
    342 
    343 TNC_Result TNC_TNCS_SetAttribute(
    344 	TNC_IMVID imvID,
    345 	TNC_ConnectionID connectionID,
    346 	TNC_AttributeID attribureID,
    347 	TNC_UInt32 bufferLength,
    348 	TNC_BufferReference buffer)
    349 {
    350 	wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_SetAttribute");
    351 	/* TODO */
    352 	return TNC_RESULT_SUCCESS;
    353 }
    354 
    355 
    356 TNC_Result TNC_TNCS_BindFunction(
    357 	TNC_IMVID imvID,
    358 	char *functionName,
    359 	void **pOutFunctionPointer)
    360 {
    361 	wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_BindFunction(imcID=%lu, "
    362 		   "functionName='%s')", (unsigned long) imvID, functionName);
    363 
    364 	if (tncs_get_imv(imvID) == NULL)
    365 		return TNC_RESULT_INVALID_PARAMETER;
    366 
    367 	if (pOutFunctionPointer == NULL)
    368 		return TNC_RESULT_INVALID_PARAMETER;
    369 
    370 	if (os_strcmp(functionName, "TNC_TNCS_ReportMessageTypes") == 0)
    371 		*pOutFunctionPointer = TNC_TNCS_ReportMessageTypes;
    372 	else if (os_strcmp(functionName, "TNC_TNCS_SendMessage") == 0)
    373 		*pOutFunctionPointer = TNC_TNCS_SendMessage;
    374 	else if (os_strcmp(functionName, "TNC_TNCS_RequestHandshakeRetry") ==
    375 		 0)
    376 		*pOutFunctionPointer = TNC_TNCS_RequestHandshakeRetry;
    377 	else if (os_strcmp(functionName, "TNC_TNCS_ProvideRecommendation") ==
    378 		 0)
    379 		*pOutFunctionPointer = TNC_TNCS_ProvideRecommendation;
    380 	else if (os_strcmp(functionName, "TNC_TNCS_GetAttribute") == 0)
    381 		*pOutFunctionPointer = TNC_TNCS_GetAttribute;
    382 	else if (os_strcmp(functionName, "TNC_TNCS_SetAttribute") == 0)
    383 		*pOutFunctionPointer = TNC_TNCS_SetAttribute;
    384 	else
    385 		*pOutFunctionPointer = NULL;
    386 
    387 	return TNC_RESULT_SUCCESS;
    388 }
    389 
    390 
    391 static void * tncs_get_sym(void *handle, char *func)
    392 {
    393 	void *fptr;
    394 
    395 	fptr = dlsym(handle, func);
    396 
    397 	return fptr;
    398 }
    399 
    400 
    401 static int tncs_imv_resolve_funcs(struct tnc_if_imv *imv)
    402 {
    403 	void *handle = imv->dlhandle;
    404 
    405 	/* Mandatory IMV functions */
    406 	imv->Initialize = tncs_get_sym(handle, "TNC_IMV_Initialize");
    407 	if (imv->Initialize == NULL) {
    408 		wpa_printf(MSG_ERROR, "TNC: IMV does not export "
    409 			   "TNC_IMV_Initialize");
    410 		return -1;
    411 	}
    412 
    413 	imv->SolicitRecommendation = tncs_get_sym(
    414 		handle, "TNC_IMV_SolicitRecommendation");
    415 	if (imv->SolicitRecommendation == NULL) {
    416 		wpa_printf(MSG_ERROR, "TNC: IMV does not export "
    417 			   "TNC_IMV_SolicitRecommendation");
    418 		return -1;
    419 	}
    420 
    421 	imv->ProvideBindFunction =
    422 		tncs_get_sym(handle, "TNC_IMV_ProvideBindFunction");
    423 	if (imv->ProvideBindFunction == NULL) {
    424 		wpa_printf(MSG_ERROR, "TNC: IMV does not export "
    425 			   "TNC_IMV_ProvideBindFunction");
    426 		return -1;
    427 	}
    428 
    429 	/* Optional IMV functions */
    430 	imv->NotifyConnectionChange =
    431 		tncs_get_sym(handle, "TNC_IMV_NotifyConnectionChange");
    432 	imv->ReceiveMessage = tncs_get_sym(handle, "TNC_IMV_ReceiveMessage");
    433 	imv->BatchEnding = tncs_get_sym(handle, "TNC_IMV_BatchEnding");
    434 	imv->Terminate = tncs_get_sym(handle, "TNC_IMV_Terminate");
    435 
    436 	return 0;
    437 }
    438 
    439 
    440 static int tncs_imv_initialize(struct tnc_if_imv *imv)
    441 {
    442 	TNC_Result res;
    443 	TNC_Version imv_ver;
    444 
    445 	wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMV_Initialize for IMV '%s'",
    446 		   imv->name);
    447 	res = imv->Initialize(imv->imvID, TNC_IFIMV_VERSION_1,
    448 			      TNC_IFIMV_VERSION_1, &imv_ver);
    449 	wpa_printf(MSG_DEBUG, "TNC: TNC_IMV_Initialize: res=%lu imv_ver=%lu",
    450 		   (unsigned long) res, (unsigned long) imv_ver);
    451 
    452 	return res == TNC_RESULT_SUCCESS ? 0 : -1;
    453 }
    454 
    455 
    456 static int tncs_imv_terminate(struct tnc_if_imv *imv)
    457 {
    458 	TNC_Result res;
    459 
    460 	if (imv->Terminate == NULL)
    461 		return 0;
    462 
    463 	wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMV_Terminate for IMV '%s'",
    464 		   imv->name);
    465 	res = imv->Terminate(imv->imvID);
    466 	wpa_printf(MSG_DEBUG, "TNC: TNC_IMV_Terminate: %lu",
    467 		   (unsigned long) res);
    468 
    469 	return res == TNC_RESULT_SUCCESS ? 0 : -1;
    470 }
    471 
    472 
    473 static int tncs_imv_provide_bind_function(struct tnc_if_imv *imv)
    474 {
    475 	TNC_Result res;
    476 
    477 	wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMV_ProvideBindFunction for "
    478 		   "IMV '%s'", imv->name);
    479 	res = imv->ProvideBindFunction(imv->imvID, TNC_TNCS_BindFunction);
    480 	wpa_printf(MSG_DEBUG, "TNC: TNC_IMV_ProvideBindFunction: res=%lu",
    481 		   (unsigned long) res);
    482 
    483 	return res == TNC_RESULT_SUCCESS ? 0 : -1;
    484 }
    485 
    486 
    487 static int tncs_imv_notify_connection_change(struct tnc_if_imv *imv,
    488 					     TNC_ConnectionID conn,
    489 					     TNC_ConnectionState state)
    490 {
    491 	TNC_Result res;
    492 
    493 	if (imv->NotifyConnectionChange == NULL)
    494 		return 0;
    495 
    496 	wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMV_NotifyConnectionChange(%d)"
    497 		   " for IMV '%s'", (int) state, imv->name);
    498 	res = imv->NotifyConnectionChange(imv->imvID, conn, state);
    499 	wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_NotifyConnectionChange: %lu",
    500 		   (unsigned long) res);
    501 
    502 	return res == TNC_RESULT_SUCCESS ? 0 : -1;
    503 }
    504 
    505 
    506 static int tncs_load_imv(struct tnc_if_imv *imv)
    507 {
    508 	if (imv->path == NULL) {
    509 		wpa_printf(MSG_DEBUG, "TNC: No IMV configured");
    510 		return -1;
    511 	}
    512 
    513 	wpa_printf(MSG_DEBUG, "TNC: Opening IMV: %s (%s)",
    514 		   imv->name, imv->path);
    515 	imv->dlhandle = dlopen(imv->path, RTLD_LAZY);
    516 	if (imv->dlhandle == NULL) {
    517 		wpa_printf(MSG_ERROR, "TNC: Failed to open IMV '%s' (%s): %s",
    518 			   imv->name, imv->path, dlerror());
    519 		return -1;
    520 	}
    521 
    522 	if (tncs_imv_resolve_funcs(imv) < 0) {
    523 		wpa_printf(MSG_ERROR, "TNC: Failed to resolve IMV functions");
    524 		return -1;
    525 	}
    526 
    527 	if (tncs_imv_initialize(imv) < 0 ||
    528 	    tncs_imv_provide_bind_function(imv) < 0) {
    529 		wpa_printf(MSG_ERROR, "TNC: Failed to initialize IMV");
    530 		return -1;
    531 	}
    532 
    533 	return 0;
    534 }
    535 
    536 
    537 static void tncs_free_imv(struct tnc_if_imv *imv)
    538 {
    539 	os_free(imv->name);
    540 	os_free(imv->path);
    541 	os_free(imv->supported_types);
    542 }
    543 
    544 static void tncs_unload_imv(struct tnc_if_imv *imv)
    545 {
    546 	tncs_imv_terminate(imv);
    547 
    548 	if (imv->dlhandle)
    549 		dlclose(imv->dlhandle);
    550 
    551 	tncs_free_imv(imv);
    552 }
    553 
    554 
    555 static int tncs_supported_type(struct tnc_if_imv *imv, unsigned int type)
    556 {
    557 	size_t i;
    558 	unsigned int vendor, subtype;
    559 
    560 	if (imv == NULL || imv->supported_types == NULL)
    561 		return 0;
    562 
    563 	vendor = type >> 8;
    564 	subtype = type & 0xff;
    565 
    566 	for (i = 0; i < imv->num_supported_types; i++) {
    567 		unsigned int svendor, ssubtype;
    568 		svendor = imv->supported_types[i] >> 8;
    569 		ssubtype = imv->supported_types[i] & 0xff;
    570 		if ((vendor == svendor || svendor == TNC_VENDORID_ANY) &&
    571 		    (subtype == ssubtype || ssubtype == TNC_SUBTYPE_ANY))
    572 			return 1;
    573 	}
    574 
    575 	return 0;
    576 }
    577 
    578 
    579 static void tncs_send_to_imvs(struct tncs_data *tncs, unsigned int type,
    580 			      const u8 *msg, size_t len)
    581 {
    582 	struct tnc_if_imv *imv;
    583 	TNC_Result res;
    584 
    585 	wpa_hexdump_ascii(MSG_MSGDUMP, "TNC: Message to IMV(s)", msg, len);
    586 
    587 	for (imv = tncs->imv; imv; imv = imv->next) {
    588 		if (imv->ReceiveMessage == NULL ||
    589 		    !tncs_supported_type(imv, type))
    590 			continue;
    591 
    592 		wpa_printf(MSG_DEBUG, "TNC: Call ReceiveMessage for IMV '%s'",
    593 			   imv->name);
    594 		res = imv->ReceiveMessage(imv->imvID, tncs->connectionID,
    595 					  (TNC_BufferReference) msg, len,
    596 					  type);
    597 		wpa_printf(MSG_DEBUG, "TNC: ReceiveMessage: %lu",
    598 			   (unsigned long) res);
    599 	}
    600 }
    601 
    602 
    603 static void tncs_batch_ending(struct tncs_data *tncs)
    604 {
    605 	struct tnc_if_imv *imv;
    606 	TNC_Result res;
    607 
    608 	for (imv = tncs->imv; imv; imv = imv->next) {
    609 		if (imv->BatchEnding == NULL)
    610 			continue;
    611 
    612 		wpa_printf(MSG_DEBUG, "TNC: Call BatchEnding for IMV '%s'",
    613 			   imv->name);
    614 		res = imv->BatchEnding(imv->imvID, tncs->connectionID);
    615 		wpa_printf(MSG_DEBUG, "TNC: BatchEnding: %lu",
    616 			   (unsigned long) res);
    617 	}
    618 }
    619 
    620 
    621 static void tncs_solicit_recommendation(struct tncs_data *tncs)
    622 {
    623 	struct tnc_if_imv *imv;
    624 	TNC_Result res;
    625 
    626 	for (imv = tncs->imv; imv; imv = imv->next) {
    627 		if (tncs->imv_data[imv->imvID].recommendation_set)
    628 			continue;
    629 
    630 		wpa_printf(MSG_DEBUG, "TNC: Call SolicitRecommendation for "
    631 			   "IMV '%s'", imv->name);
    632 		res = imv->SolicitRecommendation(imv->imvID,
    633 						 tncs->connectionID);
    634 		wpa_printf(MSG_DEBUG, "TNC: SolicitRecommendation: %lu",
    635 			   (unsigned long) res);
    636 	}
    637 }
    638 
    639 
    640 void tncs_init_connection(struct tncs_data *tncs)
    641 {
    642 	struct tnc_if_imv *imv;
    643 	int i;
    644 
    645 	for (imv = tncs->imv; imv; imv = imv->next) {
    646 		tncs_imv_notify_connection_change(
    647 			imv, tncs->connectionID, TNC_CONNECTION_STATE_CREATE);
    648 		tncs_imv_notify_connection_change(
    649 			imv, tncs->connectionID,
    650 			TNC_CONNECTION_STATE_HANDSHAKE);
    651 	}
    652 
    653 	for (i = 0; i < TNC_MAX_IMV_ID; i++) {
    654 		os_free(tncs->imv_data[i].imv_send);
    655 		tncs->imv_data[i].imv_send = NULL;
    656 		tncs->imv_data[i].imv_send_len = 0;
    657 	}
    658 }
    659 
    660 
    661 size_t tncs_total_send_len(struct tncs_data *tncs)
    662 {
    663 	int i;
    664 	size_t len = 0;
    665 
    666 	for (i = 0; i < TNC_MAX_IMV_ID; i++)
    667 		len += tncs->imv_data[i].imv_send_len;
    668 	if (tncs->tncs_message)
    669 		len += os_strlen(tncs->tncs_message);
    670 	return len;
    671 }
    672 
    673 
    674 u8 * tncs_copy_send_buf(struct tncs_data *tncs, u8 *pos)
    675 {
    676 	int i;
    677 
    678 	for (i = 0; i < TNC_MAX_IMV_ID; i++) {
    679 		if (tncs->imv_data[i].imv_send == NULL)
    680 			continue;
    681 
    682 		os_memcpy(pos, tncs->imv_data[i].imv_send,
    683 			  tncs->imv_data[i].imv_send_len);
    684 		pos += tncs->imv_data[i].imv_send_len;
    685 		os_free(tncs->imv_data[i].imv_send);
    686 		tncs->imv_data[i].imv_send = NULL;
    687 		tncs->imv_data[i].imv_send_len = 0;
    688 	}
    689 
    690 	if (tncs->tncs_message) {
    691 		size_t len = os_strlen(tncs->tncs_message);
    692 		os_memcpy(pos, tncs->tncs_message, len);
    693 		pos += len;
    694 		os_free(tncs->tncs_message);
    695 		tncs->tncs_message = NULL;
    696 	}
    697 
    698 	return pos;
    699 }
    700 
    701 
    702 char * tncs_if_tnccs_start(struct tncs_data *tncs)
    703 {
    704 	char *buf = os_malloc(1000);
    705 	if (buf == NULL)
    706 		return NULL;
    707 	tncs->last_batchid++;
    708 	os_snprintf(buf, 1000, IF_TNCCS_START, tncs->last_batchid);
    709 	return buf;
    710 }
    711 
    712 
    713 char * tncs_if_tnccs_end(void)
    714 {
    715 	char *buf = os_malloc(100);
    716 	if (buf == NULL)
    717 		return NULL;
    718 	os_snprintf(buf, 100, IF_TNCCS_END);
    719 	return buf;
    720 }
    721 
    722 
    723 static int tncs_get_type(char *start, unsigned int *type)
    724 {
    725 	char *pos = os_strstr(start, "<Type>");
    726 	if (pos == NULL)
    727 		return -1;
    728 	pos += 6;
    729 	*type = strtoul(pos, NULL, 16);
    730 	return 0;
    731 }
    732 
    733 
    734 static unsigned char * tncs_get_base64(char *start, size_t *decoded_len)
    735 {
    736 	char *pos, *pos2;
    737 	unsigned char *decoded;
    738 
    739 	pos = os_strstr(start, "<Base64>");
    740 	if (pos == NULL)
    741 		return NULL;
    742 
    743 	pos += 8;
    744 	pos2 = os_strstr(pos, "</Base64>");
    745 	if (pos2 == NULL)
    746 		return NULL;
    747 	*pos2 = '\0';
    748 
    749 	decoded = base64_decode((unsigned char *) pos, os_strlen(pos),
    750 				decoded_len);
    751 	*pos2 = '<';
    752 	if (decoded == NULL) {
    753 		wpa_printf(MSG_DEBUG, "TNC: Failed to decode Base64 data");
    754 	}
    755 
    756 	return decoded;
    757 }
    758 
    759 
    760 static enum tncs_process_res tncs_derive_recommendation(struct tncs_data *tncs)
    761 {
    762 	enum IMV_Action_Recommendation rec;
    763 	struct tnc_if_imv *imv;
    764 	TNC_ConnectionState state;
    765 	char *txt;
    766 
    767 	wpa_printf(MSG_DEBUG, "TNC: No more messages from IMVs");
    768 
    769 	if (tncs->done)
    770 		return TNCCS_PROCESS_OK_NO_RECOMMENDATION;
    771 
    772 	tncs_solicit_recommendation(tncs);
    773 
    774 	/* Select the most restrictive recommendation */
    775 	rec = TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION;
    776 	for (imv = tncs->imv; imv; imv = imv->next) {
    777 		TNC_IMV_Action_Recommendation irec;
    778 		irec = tncs->imv_data[imv->imvID].recommendation;
    779 		if (irec == TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS)
    780 			rec = TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS;
    781 		if (irec == TNC_IMV_ACTION_RECOMMENDATION_ISOLATE &&
    782 		    rec != TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS)
    783 			rec = TNC_IMV_ACTION_RECOMMENDATION_ISOLATE;
    784 		if (irec == TNC_IMV_ACTION_RECOMMENDATION_ALLOW &&
    785 		    rec == TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION)
    786 			rec = TNC_IMV_ACTION_RECOMMENDATION_ALLOW;
    787 	}
    788 
    789 	wpa_printf(MSG_DEBUG, "TNC: Recommendation: %d", rec);
    790 	tncs->recommendation = rec;
    791 	tncs->done = 1;
    792 
    793 	txt = NULL;
    794 	switch (rec) {
    795 	case TNC_IMV_ACTION_RECOMMENDATION_ALLOW:
    796 	case TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION:
    797 		txt = "allow";
    798 		state = TNC_CONNECTION_STATE_ACCESS_ALLOWED;
    799 		break;
    800 	case TNC_IMV_ACTION_RECOMMENDATION_ISOLATE:
    801 		txt = "isolate";
    802 		state = TNC_CONNECTION_STATE_ACCESS_ISOLATED;
    803 		break;
    804 	case TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS:
    805 		txt = "none";
    806 		state = TNC_CONNECTION_STATE_ACCESS_NONE;
    807 		break;
    808 	default:
    809 		state = TNC_CONNECTION_STATE_ACCESS_ALLOWED;
    810 		break;
    811 	}
    812 
    813 	if (txt) {
    814 		os_free(tncs->tncs_message);
    815 		tncs->tncs_message = os_zalloc(200);
    816 		if (tncs->tncs_message) {
    817 			os_snprintf(tncs->tncs_message, 199,
    818 				    "<TNCC-TNCS-Message><Type>%08X</Type>"
    819 				    "<XML><TNCCS-Recommendation type=\"%s\">"
    820 				    "</TNCCS-Recommendation></XML>"
    821 				    "</TNCC-TNCS-Message>",
    822 				    TNC_TNCCS_RECOMMENDATION, txt);
    823 		}
    824 	}
    825 
    826 	for (imv = tncs->imv; imv; imv = imv->next) {
    827 		tncs_imv_notify_connection_change(imv, tncs->connectionID,
    828 						  state);
    829 	}
    830 
    831 	switch (rec) {
    832 	case TNC_IMV_ACTION_RECOMMENDATION_ALLOW:
    833 		return TNCCS_RECOMMENDATION_ALLOW;
    834 	case TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS:
    835 		return TNCCS_RECOMMENDATION_NO_ACCESS;
    836 	case TNC_IMV_ACTION_RECOMMENDATION_ISOLATE:
    837 		return TNCCS_RECOMMENDATION_ISOLATE;
    838 	case TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION:
    839 		return TNCCS_RECOMMENDATION_NO_RECOMMENDATION;
    840 	default:
    841 		return TNCCS_PROCESS_ERROR;
    842 	}
    843 }
    844 
    845 
    846 enum tncs_process_res tncs_process_if_tnccs(struct tncs_data *tncs,
    847 					    const u8 *msg, size_t len)
    848 {
    849 	char *buf, *start, *end, *pos, *pos2, *payload;
    850 	unsigned int batch_id;
    851 	unsigned char *decoded;
    852 	size_t decoded_len;
    853 
    854 	buf = os_malloc(len + 1);
    855 	if (buf == NULL)
    856 		return TNCCS_PROCESS_ERROR;
    857 
    858 	os_memcpy(buf, msg, len);
    859 	buf[len] = '\0';
    860 	start = os_strstr(buf, "<TNCCS-Batch ");
    861 	end = os_strstr(buf, "</TNCCS-Batch>");
    862 	if (start == NULL || end == NULL || start > end) {
    863 		os_free(buf);
    864 		return TNCCS_PROCESS_ERROR;
    865 	}
    866 
    867 	start += 13;
    868 	while (*start == ' ')
    869 		start++;
    870 	*end = '\0';
    871 
    872 	pos = os_strstr(start, "BatchId=");
    873 	if (pos == NULL) {
    874 		os_free(buf);
    875 		return TNCCS_PROCESS_ERROR;
    876 	}
    877 
    878 	pos += 8;
    879 	if (*pos == '"')
    880 		pos++;
    881 	batch_id = atoi(pos);
    882 	wpa_printf(MSG_DEBUG, "TNC: Received IF-TNCCS BatchId=%u",
    883 		   batch_id);
    884 	if (batch_id != tncs->last_batchid + 1) {
    885 		wpa_printf(MSG_DEBUG, "TNC: Unexpected IF-TNCCS BatchId "
    886 			   "%u (expected %u)",
    887 			   batch_id, tncs->last_batchid + 1);
    888 		os_free(buf);
    889 		return TNCCS_PROCESS_ERROR;
    890 	}
    891 	tncs->last_batchid = batch_id;
    892 
    893 	while (*pos != '\0' && *pos != '>')
    894 		pos++;
    895 	if (*pos == '\0') {
    896 		os_free(buf);
    897 		return TNCCS_PROCESS_ERROR;
    898 	}
    899 	pos++;
    900 	payload = start;
    901 
    902 	/*
    903 	 * <IMC-IMV-Message>
    904 	 * <Type>01234567</Type>
    905 	 * <Base64>foo==</Base64>
    906 	 * </IMC-IMV-Message>
    907 	 */
    908 
    909 	while (*start) {
    910 		char *endpos;
    911 		unsigned int type;
    912 
    913 		pos = os_strstr(start, "<IMC-IMV-Message>");
    914 		if (pos == NULL)
    915 			break;
    916 		start = pos + 17;
    917 		end = os_strstr(start, "</IMC-IMV-Message>");
    918 		if (end == NULL)
    919 			break;
    920 		*end = '\0';
    921 		endpos = end;
    922 		end += 18;
    923 
    924 		if (tncs_get_type(start, &type) < 0) {
    925 			*endpos = '<';
    926 			start = end;
    927 			continue;
    928 		}
    929 		wpa_printf(MSG_DEBUG, "TNC: IMC-IMV-Message Type 0x%x", type);
    930 
    931 		decoded = tncs_get_base64(start, &decoded_len);
    932 		if (decoded == NULL) {
    933 			*endpos = '<';
    934 			start = end;
    935 			continue;
    936 		}
    937 
    938 		tncs_send_to_imvs(tncs, type, decoded, decoded_len);
    939 
    940 		os_free(decoded);
    941 
    942 		start = end;
    943 	}
    944 
    945 	/*
    946 	 * <TNCC-TNCS-Message>
    947 	 * <Type>01234567</Type>
    948 	 * <XML><TNCCS-Foo type="foo"></TNCCS-Foo></XML>
    949 	 * <Base64>foo==</Base64>
    950 	 * </TNCC-TNCS-Message>
    951 	 */
    952 
    953 	start = payload;
    954 	while (*start) {
    955 		unsigned int type;
    956 		char *xml, *xmlend, *endpos;
    957 
    958 		pos = os_strstr(start, "<TNCC-TNCS-Message>");
    959 		if (pos == NULL)
    960 			break;
    961 		start = pos + 19;
    962 		end = os_strstr(start, "</TNCC-TNCS-Message>");
    963 		if (end == NULL)
    964 			break;
    965 		*end = '\0';
    966 		endpos = end;
    967 		end += 20;
    968 
    969 		if (tncs_get_type(start, &type) < 0) {
    970 			*endpos = '<';
    971 			start = end;
    972 			continue;
    973 		}
    974 		wpa_printf(MSG_DEBUG, "TNC: TNCC-TNCS-Message Type 0x%x",
    975 			   type);
    976 
    977 		/* Base64 OR XML */
    978 		decoded = NULL;
    979 		xml = NULL;
    980 		xmlend = NULL;
    981 		pos = os_strstr(start, "<XML>");
    982 		if (pos) {
    983 			pos += 5;
    984 			pos2 = os_strstr(pos, "</XML>");
    985 			if (pos2 == NULL) {
    986 				*endpos = '<';
    987 				start = end;
    988 				continue;
    989 			}
    990 			xmlend = pos2;
    991 			xml = pos;
    992 		} else {
    993 			decoded = tncs_get_base64(start, &decoded_len);
    994 			if (decoded == NULL) {
    995 				*endpos = '<';
    996 				start = end;
    997 				continue;
    998 			}
    999 		}
   1000 
   1001 		if (decoded) {
   1002 			wpa_hexdump_ascii(MSG_MSGDUMP,
   1003 					  "TNC: TNCC-TNCS-Message Base64",
   1004 					  decoded, decoded_len);
   1005 			os_free(decoded);
   1006 		}
   1007 
   1008 		if (xml) {
   1009 			wpa_hexdump_ascii(MSG_MSGDUMP,
   1010 					  "TNC: TNCC-TNCS-Message XML",
   1011 					  (unsigned char *) xml,
   1012 					  xmlend - xml);
   1013 		}
   1014 
   1015 		start = end;
   1016 	}
   1017 
   1018 	os_free(buf);
   1019 
   1020 	tncs_batch_ending(tncs);
   1021 
   1022 	if (tncs_total_send_len(tncs) == 0)
   1023 		return tncs_derive_recommendation(tncs);
   1024 
   1025 	return TNCCS_PROCESS_OK_NO_RECOMMENDATION;
   1026 }
   1027 
   1028 
   1029 static struct tnc_if_imv * tncs_parse_imv(int id, char *start, char *end,
   1030 					  int *error)
   1031 {
   1032 	struct tnc_if_imv *imv;
   1033 	char *pos, *pos2;
   1034 
   1035 	if (id >= TNC_MAX_IMV_ID) {
   1036 		wpa_printf(MSG_DEBUG, "TNC: Too many IMVs");
   1037 		return NULL;
   1038 	}
   1039 
   1040 	imv = os_zalloc(sizeof(*imv));
   1041 	if (imv == NULL) {
   1042 		*error = 1;
   1043 		return NULL;
   1044 	}
   1045 
   1046 	imv->imvID = id;
   1047 
   1048 	pos = start;
   1049 	wpa_printf(MSG_DEBUG, "TNC: Configured IMV: %s", pos);
   1050 	if (pos + 1 >= end || *pos != '"') {
   1051 		wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMV line '%s' "
   1052 			   "(no starting quotation mark)", start);
   1053 		os_free(imv);
   1054 		return NULL;
   1055 	}
   1056 
   1057 	pos++;
   1058 	pos2 = pos;
   1059 	while (pos2 < end && *pos2 != '"')
   1060 		pos2++;
   1061 	if (pos2 >= end) {
   1062 		wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMV line '%s' "
   1063 			   "(no ending quotation mark)", start);
   1064 		os_free(imv);
   1065 		return NULL;
   1066 	}
   1067 	*pos2 = '\0';
   1068 	wpa_printf(MSG_DEBUG, "TNC: Name: '%s'", pos);
   1069 	imv->name = os_strdup(pos);
   1070 
   1071 	pos = pos2 + 1;
   1072 	if (pos >= end || *pos != ' ') {
   1073 		wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMV line '%s' "
   1074 			   "(no space after name)", start);
   1075 		os_free(imv);
   1076 		return NULL;
   1077 	}
   1078 
   1079 	pos++;
   1080 	wpa_printf(MSG_DEBUG, "TNC: IMV file: '%s'", pos);
   1081 	imv->path = os_strdup(pos);
   1082 
   1083 	return imv;
   1084 }
   1085 
   1086 
   1087 static int tncs_read_config(struct tncs_global *global)
   1088 {
   1089 	char *config, *end, *pos, *line_end;
   1090 	size_t config_len;
   1091 	struct tnc_if_imv *imv, *last;
   1092 	int id = 0;
   1093 
   1094 	last = NULL;
   1095 
   1096 	config = os_readfile(TNC_CONFIG_FILE, &config_len);
   1097 	if (config == NULL) {
   1098 		wpa_printf(MSG_ERROR, "TNC: Could not open TNC configuration "
   1099 			   "file '%s'", TNC_CONFIG_FILE);
   1100 		return -1;
   1101 	}
   1102 
   1103 	end = config + config_len;
   1104 	for (pos = config; pos < end; pos = line_end + 1) {
   1105 		line_end = pos;
   1106 		while (*line_end != '\n' && *line_end != '\r' &&
   1107 		       line_end < end)
   1108 			line_end++;
   1109 		*line_end = '\0';
   1110 
   1111 		if (os_strncmp(pos, "IMV ", 4) == 0) {
   1112 			int error = 0;
   1113 
   1114 			imv = tncs_parse_imv(id++, pos + 4, line_end, &error);
   1115 			if (error)
   1116 				return -1;
   1117 			if (imv) {
   1118 				if (last == NULL)
   1119 					global->imv = imv;
   1120 				else
   1121 					last->next = imv;
   1122 				last = imv;
   1123 			}
   1124 		}
   1125 	}
   1126 
   1127 	os_free(config);
   1128 
   1129 	return 0;
   1130 }
   1131 
   1132 
   1133 struct tncs_data * tncs_init(void)
   1134 {
   1135 	struct tncs_data *tncs;
   1136 
   1137 	if (tncs_global_data == NULL)
   1138 		return NULL;
   1139 
   1140 	tncs = os_zalloc(sizeof(*tncs));
   1141 	if (tncs == NULL)
   1142 		return NULL;
   1143 	tncs->imv = tncs_global_data->imv;
   1144 	tncs->connectionID = tncs_global_data->next_conn_id++;
   1145 	tncs->next = tncs_global_data->connections;
   1146 	tncs_global_data->connections = tncs;
   1147 
   1148 	return tncs;
   1149 }
   1150 
   1151 
   1152 void tncs_deinit(struct tncs_data *tncs)
   1153 {
   1154 	int i;
   1155 	struct tncs_data *prev, *conn;
   1156 
   1157 	if (tncs == NULL)
   1158 		return;
   1159 
   1160 	for (i = 0; i < TNC_MAX_IMV_ID; i++)
   1161 		os_free(tncs->imv_data[i].imv_send);
   1162 
   1163 	prev = NULL;
   1164 	conn = tncs_global_data->connections;
   1165 	while (conn) {
   1166 		if (conn == tncs) {
   1167 			if (prev)
   1168 				prev->next = tncs->next;
   1169 			else
   1170 				tncs_global_data->connections = tncs->next;
   1171 			break;
   1172 		}
   1173 		prev = conn;
   1174 		conn = conn->next;
   1175 	}
   1176 
   1177 	os_free(tncs->tncs_message);
   1178 	os_free(tncs);
   1179 }
   1180 
   1181 
   1182 int tncs_global_init(void)
   1183 {
   1184 	struct tnc_if_imv *imv;
   1185 
   1186 	tncs_global_data = os_zalloc(sizeof(*tncs_global_data));
   1187 	if (tncs_global_data == NULL)
   1188 		return -1;
   1189 
   1190 	if (tncs_read_config(tncs_global_data) < 0) {
   1191 		wpa_printf(MSG_ERROR, "TNC: Failed to read TNC configuration");
   1192 		goto failed;
   1193 	}
   1194 
   1195 	for (imv = tncs_global_data->imv; imv; imv = imv->next) {
   1196 		if (tncs_load_imv(imv)) {
   1197 			wpa_printf(MSG_ERROR, "TNC: Failed to load IMV '%s'",
   1198 				   imv->name);
   1199 			goto failed;
   1200 		}
   1201 	}
   1202 
   1203 	return 0;
   1204 
   1205 failed:
   1206 	tncs_global_deinit();
   1207 	return -1;
   1208 }
   1209 
   1210 
   1211 void tncs_global_deinit(void)
   1212 {
   1213 	struct tnc_if_imv *imv, *prev;
   1214 
   1215 	if (tncs_global_data == NULL)
   1216 		return;
   1217 
   1218 	imv = tncs_global_data->imv;
   1219 	while (imv) {
   1220 		tncs_unload_imv(imv);
   1221 
   1222 		prev = imv;
   1223 		imv = imv->next;
   1224 		os_free(prev);
   1225 	}
   1226 
   1227 	os_free(tncs_global_data);
   1228 	tncs_global_data = NULL;
   1229 }
   1230 
   1231 
   1232 struct wpabuf * tncs_build_soh_request(void)
   1233 {
   1234 	struct wpabuf *buf;
   1235 
   1236 	/*
   1237 	 * Build a SoH Request TLV (to be used inside SoH EAP Extensions
   1238 	 * Method)
   1239 	 */
   1240 
   1241 	buf = wpabuf_alloc(8 + 4);
   1242 	if (buf == NULL)
   1243 		return NULL;
   1244 
   1245 	/* Vendor-Specific TLV (Microsoft) - SoH Request */
   1246 	wpabuf_put_be16(buf, EAP_TLV_VENDOR_SPECIFIC_TLV); /* TLV Type */
   1247 	wpabuf_put_be16(buf, 8); /* Length */
   1248 
   1249 	wpabuf_put_be32(buf, EAP_VENDOR_MICROSOFT); /* Vendor_Id */
   1250 
   1251 	wpabuf_put_be16(buf, 0x02); /* TLV Type - SoH Request TLV */
   1252 	wpabuf_put_be16(buf, 0); /* Length */
   1253 
   1254 	return buf;
   1255 }
   1256 
   1257 
   1258 struct wpabuf * tncs_process_soh(const u8 *soh_tlv, size_t soh_tlv_len,
   1259 				 int *failure)
   1260 {
   1261 	wpa_hexdump(MSG_DEBUG, "TNC: SoH TLV", soh_tlv, soh_tlv_len);
   1262 	*failure = 0;
   1263 
   1264 	/* TODO: return MS-SoH Response TLV */
   1265 
   1266 	return NULL;
   1267 }
   1268