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