Home | History | Annotate | Download | only in eap_peer
      1 /*
      2  * EAP-TNC - TNCC (IF-IMC and IF-TNCCS)
      3  * Copyright (c) 2007, 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 #ifndef CONFIG_NATIVE_WINDOWS
     17 #include <dlfcn.h>
     18 #endif /* CONFIG_NATIVE_WINDOWS */
     19 
     20 #include "common.h"
     21 #include "base64.h"
     22 #include "tncc.h"
     23 #include "eap_common/eap_tlv_common.h"
     24 #include "eap_common/eap_defs.h"
     25 
     26 
     27 #ifdef UNICODE
     28 #define TSTR "%S"
     29 #else /* UNICODE */
     30 #define TSTR "%s"
     31 #endif /* UNICODE */
     32 
     33 
     34 #define TNC_CONFIG_FILE "/etc/tnc_config"
     35 #define TNC_WINREG_PATH TEXT("SOFTWARE\\Trusted Computing Group\\TNC\\IMCs")
     36 #define IF_TNCCS_START \
     37 "<?xml version=\"1.0\"?>\n" \
     38 "<TNCCS-Batch BatchId=\"%d\" Recipient=\"TNCS\" " \
     39 "xmlns=\"http://www.trustedcomputinggroup.org/IWG/TNC/1_0/IF_TNCCS#\" " \
     40 "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " \
     41 "xsi:schemaLocation=\"http://www.trustedcomputinggroup.org/IWG/TNC/1_0/" \
     42 "IF_TNCCS# https://www.trustedcomputinggroup.org/XML/SCHEMA/TNCCS_1.0.xsd\">\n"
     43 #define IF_TNCCS_END "\n</TNCCS-Batch>"
     44 
     45 /* TNC IF-IMC */
     46 
     47 typedef unsigned long TNC_UInt32;
     48 typedef unsigned char *TNC_BufferReference;
     49 
     50 typedef TNC_UInt32 TNC_IMCID;
     51 typedef TNC_UInt32 TNC_ConnectionID;
     52 typedef TNC_UInt32 TNC_ConnectionState;
     53 typedef TNC_UInt32 TNC_RetryReason;
     54 typedef TNC_UInt32 TNC_MessageType;
     55 typedef TNC_MessageType *TNC_MessageTypeList;
     56 typedef TNC_UInt32 TNC_VendorID;
     57 typedef TNC_UInt32 TNC_MessageSubtype;
     58 typedef TNC_UInt32 TNC_Version;
     59 typedef TNC_UInt32 TNC_Result;
     60 
     61 typedef TNC_Result (*TNC_TNCC_BindFunctionPointer)(
     62 	TNC_IMCID imcID,
     63 	char *functionName,
     64 	void **pOutfunctionPointer);
     65 
     66 #define TNC_RESULT_SUCCESS 0
     67 #define TNC_RESULT_NOT_INITIALIZED 1
     68 #define TNC_RESULT_ALREADY_INITIALIZED 2
     69 #define TNC_RESULT_NO_COMMON_VERSION 3
     70 #define TNC_RESULT_CANT_RETRY 4
     71 #define TNC_RESULT_WONT_RETRY 5
     72 #define TNC_RESULT_INVALID_PARAMETER 6
     73 #define TNC_RESULT_CANT_RESPOND 7
     74 #define TNC_RESULT_ILLEGAL_OPERATION 8
     75 #define TNC_RESULT_OTHER 9
     76 #define TNC_RESULT_FATAL 10
     77 
     78 #define TNC_CONNECTION_STATE_CREATE 0
     79 #define TNC_CONNECTION_STATE_HANDSHAKE 1
     80 #define TNC_CONNECTION_STATE_ACCESS_ALLOWED 2
     81 #define TNC_CONNECTION_STATE_ACCESS_ISOLATED 3
     82 #define TNC_CONNECTION_STATE_ACCESS_NONE 4
     83 #define TNC_CONNECTION_STATE_DELETE 5
     84 
     85 #define TNC_IFIMC_VERSION_1 1
     86 
     87 #define TNC_VENDORID_ANY ((TNC_VendorID) 0xffffff)
     88 #define TNC_SUBTYPE_ANY ((TNC_MessageSubtype) 0xff)
     89 
     90 /* TNCC-TNCS Message Types */
     91 #define TNC_TNCCS_RECOMMENDATION		0x00000001
     92 #define TNC_TNCCS_ERROR				0x00000002
     93 #define TNC_TNCCS_PREFERREDLANGUAGE		0x00000003
     94 #define TNC_TNCCS_REASONSTRINGS			0x00000004
     95 
     96 
     97 /* IF-TNCCS-SOH - SSoH and SSoHR Attributes */
     98 enum {
     99 	SSOH_MS_MACHINE_INVENTORY = 1,
    100 	SSOH_MS_QUARANTINE_STATE = 2,
    101 	SSOH_MS_PACKET_INFO = 3,
    102 	SSOH_MS_SYSTEMGENERATED_IDS = 4,
    103 	SSOH_MS_MACHINENAME = 5,
    104 	SSOH_MS_CORRELATIONID = 6,
    105 	SSOH_MS_INSTALLED_SHVS = 7,
    106 	SSOH_MS_MACHINE_INVENTORY_EX = 8
    107 };
    108 
    109 struct tnc_if_imc {
    110 	struct tnc_if_imc *next;
    111 	char *name;
    112 	char *path;
    113 	void *dlhandle; /* from dlopen() */
    114 	TNC_IMCID imcID;
    115 	TNC_ConnectionID connectionID;
    116 	TNC_MessageTypeList supported_types;
    117 	size_t num_supported_types;
    118 	u8 *imc_send;
    119 	size_t imc_send_len;
    120 
    121 	/* Functions implemented by IMCs (with TNC_IMC_ prefix) */
    122 	TNC_Result (*Initialize)(
    123 		TNC_IMCID imcID,
    124 		TNC_Version minVersion,
    125 		TNC_Version maxVersion,
    126 		TNC_Version *pOutActualVersion);
    127 	TNC_Result (*NotifyConnectionChange)(
    128 		TNC_IMCID imcID,
    129 		TNC_ConnectionID connectionID,
    130 		TNC_ConnectionState newState);
    131 	TNC_Result (*BeginHandshake)(
    132 		TNC_IMCID imcID,
    133 		TNC_ConnectionID connectionID);
    134 	TNC_Result (*ReceiveMessage)(
    135 		TNC_IMCID imcID,
    136 		TNC_ConnectionID connectionID,
    137 		TNC_BufferReference messageBuffer,
    138 		TNC_UInt32 messageLength,
    139 		TNC_MessageType messageType);
    140 	TNC_Result (*BatchEnding)(
    141 		TNC_IMCID imcID,
    142 		TNC_ConnectionID connectionID);
    143 	TNC_Result (*Terminate)(TNC_IMCID imcID);
    144 	TNC_Result (*ProvideBindFunction)(
    145 		TNC_IMCID imcID,
    146 		TNC_TNCC_BindFunctionPointer bindFunction);
    147 };
    148 
    149 struct tncc_data {
    150 	struct tnc_if_imc *imc;
    151 	unsigned int last_batchid;
    152 };
    153 
    154 #define TNC_MAX_IMC_ID 10
    155 static struct tnc_if_imc *tnc_imc[TNC_MAX_IMC_ID] = { NULL };
    156 
    157 
    158 /* TNCC functions that IMCs can call */
    159 
    160 TNC_Result TNC_TNCC_ReportMessageTypes(
    161 	TNC_IMCID imcID,
    162 	TNC_MessageTypeList supportedTypes,
    163 	TNC_UInt32 typeCount)
    164 {
    165 	TNC_UInt32 i;
    166 	struct tnc_if_imc *imc;
    167 
    168 	wpa_printf(MSG_DEBUG, "TNC: TNC_TNCC_ReportMessageTypes(imcID=%lu "
    169 		   "typeCount=%lu)",
    170 		   (unsigned long) imcID, (unsigned long) typeCount);
    171 
    172 	for (i = 0; i < typeCount; i++) {
    173 		wpa_printf(MSG_DEBUG, "TNC: supportedTypes[%lu] = %lu",
    174 			   i, supportedTypes[i]);
    175 	}
    176 
    177 	if (imcID >= TNC_MAX_IMC_ID || tnc_imc[imcID] == NULL)
    178 		return TNC_RESULT_INVALID_PARAMETER;
    179 
    180 	imc = tnc_imc[imcID];
    181 	os_free(imc->supported_types);
    182 	imc->supported_types =
    183 		os_malloc(typeCount * sizeof(TNC_MessageTypeList));
    184 	if (imc->supported_types == NULL)
    185 		return TNC_RESULT_FATAL;
    186 	os_memcpy(imc->supported_types, supportedTypes,
    187 		  typeCount * sizeof(TNC_MessageTypeList));
    188 	imc->num_supported_types = typeCount;
    189 
    190 	return TNC_RESULT_SUCCESS;
    191 }
    192 
    193 
    194 TNC_Result TNC_TNCC_SendMessage(
    195 	TNC_IMCID imcID,
    196 	TNC_ConnectionID connectionID,
    197 	TNC_BufferReference message,
    198 	TNC_UInt32 messageLength,
    199 	TNC_MessageType messageType)
    200 {
    201 	struct tnc_if_imc *imc;
    202 	unsigned char *b64;
    203 	size_t b64len;
    204 
    205 	wpa_printf(MSG_DEBUG, "TNC: TNC_TNCC_SendMessage(imcID=%lu "
    206 		   "connectionID=%lu messageType=%lu)",
    207 		   imcID, connectionID, messageType);
    208 	wpa_hexdump_ascii(MSG_DEBUG, "TNC: TNC_TNCC_SendMessage",
    209 			  message, messageLength);
    210 
    211 	if (imcID >= TNC_MAX_IMC_ID || tnc_imc[imcID] == NULL)
    212 		return TNC_RESULT_INVALID_PARAMETER;
    213 
    214 	b64 = base64_encode(message, messageLength, &b64len);
    215 	if (b64 == NULL)
    216 		return TNC_RESULT_FATAL;
    217 
    218 	imc = tnc_imc[imcID];
    219 	os_free(imc->imc_send);
    220 	imc->imc_send_len = 0;
    221 	imc->imc_send = os_zalloc(b64len + 100);
    222 	if (imc->imc_send == NULL) {
    223 		os_free(b64);
    224 		return TNC_RESULT_OTHER;
    225 	}
    226 
    227 	imc->imc_send_len =
    228 		os_snprintf((char *) imc->imc_send, b64len + 100,
    229 			    "<IMC-IMV-Message><Type>%08X</Type>"
    230 			    "<Base64>%s</Base64></IMC-IMV-Message>",
    231 			    (unsigned int) messageType, b64);
    232 
    233 	os_free(b64);
    234 
    235 	return TNC_RESULT_SUCCESS;
    236 }
    237 
    238 
    239 TNC_Result TNC_TNCC_RequestHandshakeRetry(
    240 	TNC_IMCID imcID,
    241 	TNC_ConnectionID connectionID,
    242 	TNC_RetryReason reason)
    243 {
    244 	wpa_printf(MSG_DEBUG, "TNC: TNC_TNCC_RequestHandshakeRetry");
    245 
    246 	if (imcID >= TNC_MAX_IMC_ID || tnc_imc[imcID] == NULL)
    247 		return TNC_RESULT_INVALID_PARAMETER;
    248 
    249 	/*
    250 	 * TODO: trigger a call to eapol_sm_request_reauth(). This would
    251 	 * require that the IMC continues to be loaded in memory afer
    252 	 * authentication..
    253 	 */
    254 
    255 	return TNC_RESULT_SUCCESS;
    256 }
    257 
    258 
    259 TNC_Result TNC_9048_LogMessage(TNC_IMCID imcID, TNC_UInt32 severity,
    260 			       const char *message)
    261 {
    262 	wpa_printf(MSG_DEBUG, "TNC: TNC_9048_LogMessage(imcID=%lu "
    263 		   "severity==%lu message='%s')",
    264 		   imcID, severity, message);
    265 	return TNC_RESULT_SUCCESS;
    266 }
    267 
    268 
    269 TNC_Result TNC_9048_UserMessage(TNC_IMCID imcID, TNC_ConnectionID connectionID,
    270 				const char *message)
    271 {
    272 	wpa_printf(MSG_DEBUG, "TNC: TNC_9048_UserMessage(imcID=%lu "
    273 		   "connectionID==%lu message='%s')",
    274 		   imcID, connectionID, message);
    275 	return TNC_RESULT_SUCCESS;
    276 }
    277 
    278 
    279 TNC_Result TNC_TNCC_BindFunction(
    280 	TNC_IMCID imcID,
    281 	char *functionName,
    282 	void **pOutfunctionPointer)
    283 {
    284 	wpa_printf(MSG_DEBUG, "TNC: TNC_TNCC_BindFunction(imcID=%lu, "
    285 		   "functionName='%s')", (unsigned long) imcID, functionName);
    286 
    287 	if (imcID >= TNC_MAX_IMC_ID || tnc_imc[imcID] == NULL)
    288 		return TNC_RESULT_INVALID_PARAMETER;
    289 
    290 	if (pOutfunctionPointer == NULL)
    291 		return TNC_RESULT_INVALID_PARAMETER;
    292 
    293 	if (os_strcmp(functionName, "TNC_TNCC_ReportMessageTypes") == 0)
    294 		*pOutfunctionPointer = TNC_TNCC_ReportMessageTypes;
    295 	else if (os_strcmp(functionName, "TNC_TNCC_SendMessage") == 0)
    296 		*pOutfunctionPointer = TNC_TNCC_SendMessage;
    297 	else if (os_strcmp(functionName, "TNC_TNCC_RequestHandshakeRetry") ==
    298 		 0)
    299 		*pOutfunctionPointer = TNC_TNCC_RequestHandshakeRetry;
    300 	else if (os_strcmp(functionName, "TNC_9048_LogMessage") == 0)
    301 		*pOutfunctionPointer = TNC_9048_LogMessage;
    302 	else if (os_strcmp(functionName, "TNC_9048_UserMessage") == 0)
    303 		*pOutfunctionPointer = TNC_9048_UserMessage;
    304 	else
    305 		*pOutfunctionPointer = NULL;
    306 
    307 	return TNC_RESULT_SUCCESS;
    308 }
    309 
    310 
    311 static void * tncc_get_sym(void *handle, char *func)
    312 {
    313 	void *fptr;
    314 
    315 #ifdef CONFIG_NATIVE_WINDOWS
    316 #ifdef _WIN32_WCE
    317 	fptr = GetProcAddressA(handle, func);
    318 #else /* _WIN32_WCE */
    319 	fptr = GetProcAddress(handle, func);
    320 #endif /* _WIN32_WCE */
    321 #else /* CONFIG_NATIVE_WINDOWS */
    322 	fptr = dlsym(handle, func);
    323 #endif /* CONFIG_NATIVE_WINDOWS */
    324 
    325 	return fptr;
    326 }
    327 
    328 
    329 static int tncc_imc_resolve_funcs(struct tnc_if_imc *imc)
    330 {
    331 	void *handle = imc->dlhandle;
    332 
    333 	/* Mandatory IMC functions */
    334 	imc->Initialize = tncc_get_sym(handle, "TNC_IMC_Initialize");
    335 	if (imc->Initialize == NULL) {
    336 		wpa_printf(MSG_ERROR, "TNC: IMC does not export "
    337 			   "TNC_IMC_Initialize");
    338 		return -1;
    339 	}
    340 
    341 	imc->BeginHandshake = tncc_get_sym(handle, "TNC_IMC_BeginHandshake");
    342 	if (imc->BeginHandshake == NULL) {
    343 		wpa_printf(MSG_ERROR, "TNC: IMC does not export "
    344 			   "TNC_IMC_BeginHandshake");
    345 		return -1;
    346 	}
    347 
    348 	imc->ProvideBindFunction =
    349 		tncc_get_sym(handle, "TNC_IMC_ProvideBindFunction");
    350 	if (imc->ProvideBindFunction == NULL) {
    351 		wpa_printf(MSG_ERROR, "TNC: IMC does not export "
    352 			   "TNC_IMC_ProvideBindFunction");
    353 		return -1;
    354 	}
    355 
    356 	/* Optional IMC functions */
    357 	imc->NotifyConnectionChange =
    358 		tncc_get_sym(handle, "TNC_IMC_NotifyConnectionChange");
    359 	imc->ReceiveMessage = tncc_get_sym(handle, "TNC_IMC_ReceiveMessage");
    360 	imc->BatchEnding = tncc_get_sym(handle, "TNC_IMC_BatchEnding");
    361 	imc->Terminate = tncc_get_sym(handle, "TNC_IMC_Terminate");
    362 
    363 	return 0;
    364 }
    365 
    366 
    367 static int tncc_imc_initialize(struct tnc_if_imc *imc)
    368 {
    369 	TNC_Result res;
    370 	TNC_Version imc_ver;
    371 
    372 	wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMC_Initialize for IMC '%s'",
    373 		   imc->name);
    374 	res = imc->Initialize(imc->imcID, TNC_IFIMC_VERSION_1,
    375 			      TNC_IFIMC_VERSION_1, &imc_ver);
    376 	wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_Initialize: res=%lu imc_ver=%lu",
    377 		   (unsigned long) res, (unsigned long) imc_ver);
    378 
    379 	return res == TNC_RESULT_SUCCESS ? 0 : -1;
    380 }
    381 
    382 
    383 static int tncc_imc_terminate(struct tnc_if_imc *imc)
    384 {
    385 	TNC_Result res;
    386 
    387 	if (imc->Terminate == NULL)
    388 		return 0;
    389 
    390 	wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMC_Terminate for IMC '%s'",
    391 		   imc->name);
    392 	res = imc->Terminate(imc->imcID);
    393 	wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_Terminate: %lu",
    394 		   (unsigned long) res);
    395 
    396 	return res == TNC_RESULT_SUCCESS ? 0 : -1;
    397 }
    398 
    399 
    400 static int tncc_imc_provide_bind_function(struct tnc_if_imc *imc)
    401 {
    402 	TNC_Result res;
    403 
    404 	wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMC_ProvideBindFunction for "
    405 		   "IMC '%s'", imc->name);
    406 	res = imc->ProvideBindFunction(imc->imcID, TNC_TNCC_BindFunction);
    407 	wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_ProvideBindFunction: res=%lu",
    408 		   (unsigned long) res);
    409 
    410 	return res == TNC_RESULT_SUCCESS ? 0 : -1;
    411 }
    412 
    413 
    414 static int tncc_imc_notify_connection_change(struct tnc_if_imc *imc,
    415 					     TNC_ConnectionState state)
    416 {
    417 	TNC_Result res;
    418 
    419 	if (imc->NotifyConnectionChange == NULL)
    420 		return 0;
    421 
    422 	wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMC_NotifyConnectionChange(%d)"
    423 		   " for IMC '%s'", (int) state, imc->name);
    424 	res = imc->NotifyConnectionChange(imc->imcID, imc->connectionID,
    425 					  state);
    426 	wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_NotifyConnectionChange: %lu",
    427 		   (unsigned long) res);
    428 
    429 	return res == TNC_RESULT_SUCCESS ? 0 : -1;
    430 }
    431 
    432 
    433 static int tncc_imc_begin_handshake(struct tnc_if_imc *imc)
    434 {
    435 	TNC_Result res;
    436 
    437 	wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMC_BeginHandshake for IMC "
    438 		   "'%s'", imc->name);
    439 	res = imc->BeginHandshake(imc->imcID, imc->connectionID);
    440 	wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_BeginHandshake: %lu",
    441 		   (unsigned long) res);
    442 
    443 	return res == TNC_RESULT_SUCCESS ? 0 : -1;
    444 }
    445 
    446 
    447 static int tncc_load_imc(struct tnc_if_imc *imc)
    448 {
    449 	if (imc->path == NULL) {
    450 		wpa_printf(MSG_DEBUG, "TNC: No IMC configured");
    451 		return -1;
    452 	}
    453 
    454 	wpa_printf(MSG_DEBUG, "TNC: Opening IMC: %s (%s)",
    455 		   imc->name, imc->path);
    456 #ifdef CONFIG_NATIVE_WINDOWS
    457 #ifdef UNICODE
    458 	{
    459 		TCHAR *lib = wpa_strdup_tchar(imc->path);
    460 		if (lib == NULL)
    461 			return -1;
    462 		imc->dlhandle = LoadLibrary(lib);
    463 		os_free(lib);
    464 	}
    465 #else /* UNICODE */
    466 	imc->dlhandle = LoadLibrary(imc->path);
    467 #endif /* UNICODE */
    468 	if (imc->dlhandle == NULL) {
    469 		wpa_printf(MSG_ERROR, "TNC: Failed to open IMC '%s' (%s): %d",
    470 			   imc->name, imc->path, (int) GetLastError());
    471 		return -1;
    472 	}
    473 #else /* CONFIG_NATIVE_WINDOWS */
    474 	imc->dlhandle = dlopen(imc->path, RTLD_LAZY);
    475 	if (imc->dlhandle == NULL) {
    476 		wpa_printf(MSG_ERROR, "TNC: Failed to open IMC '%s' (%s): %s",
    477 			   imc->name, imc->path, dlerror());
    478 		return -1;
    479 	}
    480 #endif /* CONFIG_NATIVE_WINDOWS */
    481 
    482 	if (tncc_imc_resolve_funcs(imc) < 0) {
    483 		wpa_printf(MSG_ERROR, "TNC: Failed to resolve IMC functions");
    484 		return -1;
    485 	}
    486 
    487 	if (tncc_imc_initialize(imc) < 0 ||
    488 	    tncc_imc_provide_bind_function(imc) < 0) {
    489 		wpa_printf(MSG_ERROR, "TNC: Failed to initialize IMC");
    490 		return -1;
    491 	}
    492 
    493 	return 0;
    494 }
    495 
    496 
    497 static void tncc_unload_imc(struct tnc_if_imc *imc)
    498 {
    499 	tncc_imc_terminate(imc);
    500 	tnc_imc[imc->imcID] = NULL;
    501 
    502 	if (imc->dlhandle) {
    503 #ifdef CONFIG_NATIVE_WINDOWS
    504 		FreeLibrary(imc->dlhandle);
    505 #else /* CONFIG_NATIVE_WINDOWS */
    506 		dlclose(imc->dlhandle);
    507 #endif /* CONFIG_NATIVE_WINDOWS */
    508 	}
    509 	os_free(imc->name);
    510 	os_free(imc->path);
    511 	os_free(imc->supported_types);
    512 	os_free(imc->imc_send);
    513 }
    514 
    515 
    516 static int tncc_supported_type(struct tnc_if_imc *imc, unsigned int type)
    517 {
    518 	size_t i;
    519 	unsigned int vendor, subtype;
    520 
    521 	if (imc == NULL || imc->supported_types == NULL)
    522 		return 0;
    523 
    524 	vendor = type >> 8;
    525 	subtype = type & 0xff;
    526 
    527 	for (i = 0; i < imc->num_supported_types; i++) {
    528 		unsigned int svendor, ssubtype;
    529 		svendor = imc->supported_types[i] >> 8;
    530 		ssubtype = imc->supported_types[i] & 0xff;
    531 		if ((vendor == svendor || svendor == TNC_VENDORID_ANY) &&
    532 		    (subtype == ssubtype || ssubtype == TNC_SUBTYPE_ANY))
    533 			return 1;
    534 	}
    535 
    536 	return 0;
    537 }
    538 
    539 
    540 static void tncc_send_to_imcs(struct tncc_data *tncc, unsigned int type,
    541 			      const u8 *msg, size_t len)
    542 {
    543 	struct tnc_if_imc *imc;
    544 	TNC_Result res;
    545 
    546 	wpa_hexdump_ascii(MSG_MSGDUMP, "TNC: Message to IMC(s)", msg, len);
    547 
    548 	for (imc = tncc->imc; imc; imc = imc->next) {
    549 		if (imc->ReceiveMessage == NULL ||
    550 		    !tncc_supported_type(imc, type))
    551 			continue;
    552 
    553 		wpa_printf(MSG_DEBUG, "TNC: Call ReceiveMessage for IMC '%s'",
    554 			   imc->name);
    555 		res = imc->ReceiveMessage(imc->imcID, imc->connectionID,
    556 					  (TNC_BufferReference) msg, len,
    557 					  type);
    558 		wpa_printf(MSG_DEBUG, "TNC: ReceiveMessage: %lu",
    559 			   (unsigned long) res);
    560 	}
    561 }
    562 
    563 
    564 void tncc_init_connection(struct tncc_data *tncc)
    565 {
    566 	struct tnc_if_imc *imc;
    567 
    568 	for (imc = tncc->imc; imc; imc = imc->next) {
    569 		tncc_imc_notify_connection_change(
    570 			imc, TNC_CONNECTION_STATE_CREATE);
    571 		tncc_imc_notify_connection_change(
    572 			imc, TNC_CONNECTION_STATE_HANDSHAKE);
    573 
    574 		os_free(imc->imc_send);
    575 		imc->imc_send = NULL;
    576 		imc->imc_send_len = 0;
    577 
    578 		tncc_imc_begin_handshake(imc);
    579 	}
    580 }
    581 
    582 
    583 size_t tncc_total_send_len(struct tncc_data *tncc)
    584 {
    585 	struct tnc_if_imc *imc;
    586 
    587 	size_t len = 0;
    588 	for (imc = tncc->imc; imc; imc = imc->next)
    589 		len += imc->imc_send_len;
    590 	return len;
    591 }
    592 
    593 
    594 u8 * tncc_copy_send_buf(struct tncc_data *tncc, u8 *pos)
    595 {
    596 	struct tnc_if_imc *imc;
    597 
    598 	for (imc = tncc->imc; imc; imc = imc->next) {
    599 		if (imc->imc_send == NULL)
    600 			continue;
    601 
    602 		os_memcpy(pos, imc->imc_send, imc->imc_send_len);
    603 		pos += imc->imc_send_len;
    604 		os_free(imc->imc_send);
    605 		imc->imc_send = NULL;
    606 		imc->imc_send_len = 0;
    607 	}
    608 
    609 	return pos;
    610 }
    611 
    612 
    613 char * tncc_if_tnccs_start(struct tncc_data *tncc)
    614 {
    615 	char *buf = os_malloc(1000);
    616 	if (buf == NULL)
    617 		return NULL;
    618 	tncc->last_batchid++;
    619 	os_snprintf(buf, 1000, IF_TNCCS_START, tncc->last_batchid);
    620 	return buf;
    621 }
    622 
    623 
    624 char * tncc_if_tnccs_end(void)
    625 {
    626 	char *buf = os_malloc(100);
    627 	if (buf == NULL)
    628 		return NULL;
    629 	os_snprintf(buf, 100, IF_TNCCS_END);
    630 	return buf;
    631 }
    632 
    633 
    634 static void tncc_notify_recommendation(struct tncc_data *tncc,
    635 				       enum tncc_process_res res)
    636 {
    637 	TNC_ConnectionState state;
    638 	struct tnc_if_imc *imc;
    639 
    640 	switch (res) {
    641 	case TNCCS_RECOMMENDATION_ALLOW:
    642 		state = TNC_CONNECTION_STATE_ACCESS_ALLOWED;
    643 		break;
    644 	case TNCCS_RECOMMENDATION_NONE:
    645 		state = TNC_CONNECTION_STATE_ACCESS_NONE;
    646 		break;
    647 	case TNCCS_RECOMMENDATION_ISOLATE:
    648 		state = TNC_CONNECTION_STATE_ACCESS_ISOLATED;
    649 		break;
    650 	default:
    651 		state = TNC_CONNECTION_STATE_ACCESS_NONE;
    652 		break;
    653 	}
    654 
    655 	for (imc = tncc->imc; imc; imc = imc->next)
    656 		tncc_imc_notify_connection_change(imc, state);
    657 }
    658 
    659 
    660 static int tncc_get_type(char *start, unsigned int *type)
    661 {
    662 	char *pos = os_strstr(start, "<Type>");
    663 	if (pos == NULL)
    664 		return -1;
    665 	pos += 6;
    666 	*type = strtoul(pos, NULL, 16);
    667 	return 0;
    668 }
    669 
    670 
    671 static unsigned char * tncc_get_base64(char *start, size_t *decoded_len)
    672 {
    673 	char *pos, *pos2;
    674 	unsigned char *decoded;
    675 
    676 	pos = os_strstr(start, "<Base64>");
    677 	if (pos == NULL)
    678 		return NULL;
    679 
    680 	pos += 8;
    681 	pos2 = os_strstr(pos, "</Base64>");
    682 	if (pos2 == NULL)
    683 		return NULL;
    684 	*pos2 = '\0';
    685 
    686 	decoded = base64_decode((unsigned char *) pos, os_strlen(pos),
    687 				decoded_len);
    688 	*pos2 = '<';
    689 	if (decoded == NULL) {
    690 		wpa_printf(MSG_DEBUG, "TNC: Failed to decode Base64 data");
    691 	}
    692 
    693 	return decoded;
    694 }
    695 
    696 
    697 static enum tncc_process_res tncc_get_recommendation(char *start)
    698 {
    699 	char *pos, *pos2, saved;
    700 	int recom;
    701 
    702 	pos = os_strstr(start, "<TNCCS-Recommendation ");
    703 	if (pos == NULL)
    704 		return TNCCS_RECOMMENDATION_ERROR;
    705 
    706 	pos += 21;
    707 	pos = os_strstr(pos, " type=");
    708 	if (pos == NULL)
    709 		return TNCCS_RECOMMENDATION_ERROR;
    710 	pos += 6;
    711 
    712 	if (*pos == '"')
    713 		pos++;
    714 
    715 	pos2 = pos;
    716 	while (*pos2 != '\0' && *pos2 != '"' && *pos2 != '>')
    717 		pos2++;
    718 
    719 	if (*pos2 == '\0')
    720 		return TNCCS_RECOMMENDATION_ERROR;
    721 
    722 	saved = *pos2;
    723 	*pos2 = '\0';
    724 	wpa_printf(MSG_DEBUG, "TNC: TNCCS-Recommendation: '%s'", pos);
    725 
    726 	recom = TNCCS_RECOMMENDATION_ERROR;
    727 	if (os_strcmp(pos, "allow") == 0)
    728 		recom = TNCCS_RECOMMENDATION_ALLOW;
    729 	else if (os_strcmp(pos, "none") == 0)
    730 		recom = TNCCS_RECOMMENDATION_NONE;
    731 	else if (os_strcmp(pos, "isolate") == 0)
    732 		recom = TNCCS_RECOMMENDATION_ISOLATE;
    733 
    734 	*pos2 = saved;
    735 
    736 	return recom;
    737 }
    738 
    739 
    740 enum tncc_process_res tncc_process_if_tnccs(struct tncc_data *tncc,
    741 					    const u8 *msg, size_t len)
    742 {
    743 	char *buf, *start, *end, *pos, *pos2, *payload;
    744 	unsigned int batch_id;
    745 	unsigned char *decoded;
    746 	size_t decoded_len;
    747 	enum tncc_process_res res = TNCCS_PROCESS_OK_NO_RECOMMENDATION;
    748 	int recommendation_msg = 0;
    749 
    750 	buf = os_malloc(len + 1);
    751 	if (buf == NULL)
    752 		return TNCCS_PROCESS_ERROR;
    753 
    754 	os_memcpy(buf, msg, len);
    755 	buf[len] = '\0';
    756 	start = os_strstr(buf, "<TNCCS-Batch ");
    757 	end = os_strstr(buf, "</TNCCS-Batch>");
    758 	if (start == NULL || end == NULL || start > end) {
    759 		os_free(buf);
    760 		return TNCCS_PROCESS_ERROR;
    761 	}
    762 
    763 	start += 13;
    764 	while (*start == ' ')
    765 		start++;
    766 	*end = '\0';
    767 
    768 	pos = os_strstr(start, "BatchId=");
    769 	if (pos == NULL) {
    770 		os_free(buf);
    771 		return TNCCS_PROCESS_ERROR;
    772 	}
    773 
    774 	pos += 8;
    775 	if (*pos == '"')
    776 		pos++;
    777 	batch_id = atoi(pos);
    778 	wpa_printf(MSG_DEBUG, "TNC: Received IF-TNCCS BatchId=%u",
    779 		   batch_id);
    780 	if (batch_id != tncc->last_batchid + 1) {
    781 		wpa_printf(MSG_DEBUG, "TNC: Unexpected IF-TNCCS BatchId "
    782 			   "%u (expected %u)",
    783 			   batch_id, tncc->last_batchid + 1);
    784 		os_free(buf);
    785 		return TNCCS_PROCESS_ERROR;
    786 	}
    787 	tncc->last_batchid = batch_id;
    788 
    789 	while (*pos != '\0' && *pos != '>')
    790 		pos++;
    791 	if (*pos == '\0') {
    792 		os_free(buf);
    793 		return TNCCS_PROCESS_ERROR;
    794 	}
    795 	pos++;
    796 	payload = start;
    797 
    798 	/*
    799 	 * <IMC-IMV-Message>
    800 	 * <Type>01234567</Type>
    801 	 * <Base64>foo==</Base64>
    802 	 * </IMC-IMV-Message>
    803 	 */
    804 
    805 	while (*start) {
    806 		char *endpos;
    807 		unsigned int type;
    808 
    809 		pos = os_strstr(start, "<IMC-IMV-Message>");
    810 		if (pos == NULL)
    811 			break;
    812 		start = pos + 17;
    813 		end = os_strstr(start, "</IMC-IMV-Message>");
    814 		if (end == NULL)
    815 			break;
    816 		*end = '\0';
    817 		endpos = end;
    818 		end += 18;
    819 
    820 		if (tncc_get_type(start, &type) < 0) {
    821 			*endpos = '<';
    822 			start = end;
    823 			continue;
    824 		}
    825 		wpa_printf(MSG_DEBUG, "TNC: IMC-IMV-Message Type 0x%x", type);
    826 
    827 		decoded = tncc_get_base64(start, &decoded_len);
    828 		if (decoded == NULL) {
    829 			*endpos = '<';
    830 			start = end;
    831 			continue;
    832 		}
    833 
    834 		tncc_send_to_imcs(tncc, type, decoded, decoded_len);
    835 
    836 		os_free(decoded);
    837 
    838 		start = end;
    839 	}
    840 
    841 	/*
    842 	 * <TNCC-TNCS-Message>
    843 	 * <Type>01234567</Type>
    844 	 * <XML><TNCCS-Foo type="foo"></TNCCS-Foo></XML>
    845 	 * <Base64>foo==</Base64>
    846 	 * </TNCC-TNCS-Message>
    847 	 */
    848 
    849 	start = payload;
    850 	while (*start) {
    851 		unsigned int type;
    852 		char *xml, *xmlend, *endpos;
    853 
    854 		pos = os_strstr(start, "<TNCC-TNCS-Message>");
    855 		if (pos == NULL)
    856 			break;
    857 		start = pos + 19;
    858 		end = os_strstr(start, "</TNCC-TNCS-Message>");
    859 		if (end == NULL)
    860 			break;
    861 		*end = '\0';
    862 		endpos = end;
    863 		end += 20;
    864 
    865 		if (tncc_get_type(start, &type) < 0) {
    866 			*endpos = '<';
    867 			start = end;
    868 			continue;
    869 		}
    870 		wpa_printf(MSG_DEBUG, "TNC: TNCC-TNCS-Message Type 0x%x",
    871 			   type);
    872 
    873 		/* Base64 OR XML */
    874 		decoded = NULL;
    875 		xml = NULL;
    876 		xmlend = NULL;
    877 		pos = os_strstr(start, "<XML>");
    878 		if (pos) {
    879 			pos += 5;
    880 			pos2 = os_strstr(pos, "</XML>");
    881 			if (pos2 == NULL) {
    882 				*endpos = '<';
    883 				start = end;
    884 				continue;
    885 			}
    886 			xmlend = pos2;
    887 			xml = pos;
    888 		} else {
    889 			decoded = tncc_get_base64(start, &decoded_len);
    890 			if (decoded == NULL) {
    891 				*endpos = '<';
    892 				start = end;
    893 				continue;
    894 			}
    895 		}
    896 
    897 		if (decoded) {
    898 			wpa_hexdump_ascii(MSG_MSGDUMP,
    899 					  "TNC: TNCC-TNCS-Message Base64",
    900 					  decoded, decoded_len);
    901 			os_free(decoded);
    902 		}
    903 
    904 		if (xml) {
    905 			wpa_hexdump_ascii(MSG_MSGDUMP,
    906 					  "TNC: TNCC-TNCS-Message XML",
    907 					  (unsigned char *) xml,
    908 					  xmlend - xml);
    909 		}
    910 
    911 		if (type == TNC_TNCCS_RECOMMENDATION && xml) {
    912 			/*
    913 			 * <TNCCS-Recommendation type="allow">
    914 			 * </TNCCS-Recommendation>
    915 			 */
    916 			*xmlend = '\0';
    917 			res = tncc_get_recommendation(xml);
    918 			*xmlend = '<';
    919 			recommendation_msg = 1;
    920 		}
    921 
    922 		start = end;
    923 	}
    924 
    925 	os_free(buf);
    926 
    927 	if (recommendation_msg)
    928 		tncc_notify_recommendation(tncc, res);
    929 
    930 	return res;
    931 }
    932 
    933 
    934 #ifdef CONFIG_NATIVE_WINDOWS
    935 static int tncc_read_config_reg(struct tncc_data *tncc, HKEY hive)
    936 {
    937 	HKEY hk, hk2;
    938 	LONG ret;
    939 	DWORD i;
    940 	struct tnc_if_imc *imc, *last;
    941 	int j;
    942 
    943 	last = tncc->imc;
    944 	while (last && last->next)
    945 		last = last->next;
    946 
    947 	ret = RegOpenKeyEx(hive, TNC_WINREG_PATH, 0, KEY_ENUMERATE_SUB_KEYS,
    948 			   &hk);
    949 	if (ret != ERROR_SUCCESS)
    950 		return 0;
    951 
    952 	for (i = 0; ; i++) {
    953 		TCHAR name[255], *val;
    954 		DWORD namelen, buflen;
    955 
    956 		namelen = 255;
    957 		ret = RegEnumKeyEx(hk, i, name, &namelen, NULL, NULL, NULL,
    958 				   NULL);
    959 
    960 		if (ret == ERROR_NO_MORE_ITEMS)
    961 			break;
    962 
    963 		if (ret != ERROR_SUCCESS) {
    964 			wpa_printf(MSG_DEBUG, "TNC: RegEnumKeyEx failed: 0x%x",
    965 				   (unsigned int) ret);
    966 			break;
    967 		}
    968 
    969 		if (namelen >= 255)
    970 			namelen = 255 - 1;
    971 		name[namelen] = '\0';
    972 
    973 		wpa_printf(MSG_DEBUG, "TNC: IMC '" TSTR "'", name);
    974 
    975 		ret = RegOpenKeyEx(hk, name, 0, KEY_QUERY_VALUE, &hk2);
    976 		if (ret != ERROR_SUCCESS) {
    977 			wpa_printf(MSG_DEBUG, "Could not open IMC key '" TSTR
    978 				   "'", name);
    979 			continue;
    980 		}
    981 
    982 		ret = RegQueryValueEx(hk2, TEXT("Path"), NULL, NULL, NULL,
    983 				      &buflen);
    984 		if (ret != ERROR_SUCCESS) {
    985 			wpa_printf(MSG_DEBUG, "TNC: Could not read Path from "
    986 				   "IMC key '" TSTR "'", name);
    987 			RegCloseKey(hk2);
    988 			continue;
    989 		}
    990 
    991 		val = os_malloc(buflen);
    992 		if (val == NULL) {
    993 			RegCloseKey(hk2);
    994 			continue;
    995 		}
    996 
    997 		ret = RegQueryValueEx(hk2, TEXT("Path"), NULL, NULL,
    998 				      (LPBYTE) val, &buflen);
    999 		if (ret != ERROR_SUCCESS) {
   1000 			os_free(val);
   1001 			RegCloseKey(hk2);
   1002 			continue;
   1003 		}
   1004 
   1005 		RegCloseKey(hk2);
   1006 
   1007 		wpa_unicode2ascii_inplace(val);
   1008 		wpa_printf(MSG_DEBUG, "TNC: IMC Path '%s'", (char *) val);
   1009 
   1010 		for (j = 0; j < TNC_MAX_IMC_ID; j++) {
   1011 			if (tnc_imc[j] == NULL)
   1012 				break;
   1013 		}
   1014 		if (j >= TNC_MAX_IMC_ID) {
   1015 			wpa_printf(MSG_DEBUG, "TNC: Too many IMCs");
   1016 			os_free(val);
   1017 			continue;
   1018 		}
   1019 
   1020 		imc = os_zalloc(sizeof(*imc));
   1021 		if (imc == NULL) {
   1022 			os_free(val);
   1023 			break;
   1024 		}
   1025 
   1026 		imc->imcID = j;
   1027 
   1028 		wpa_unicode2ascii_inplace(name);
   1029 		imc->name = os_strdup((char *) name);
   1030 		imc->path = os_strdup((char *) val);
   1031 
   1032 		os_free(val);
   1033 
   1034 		if (last == NULL)
   1035 			tncc->imc = imc;
   1036 		else
   1037 			last->next = imc;
   1038 		last = imc;
   1039 
   1040 		tnc_imc[imc->imcID] = imc;
   1041 	}
   1042 
   1043 	RegCloseKey(hk);
   1044 
   1045 	return 0;
   1046 }
   1047 
   1048 
   1049 static int tncc_read_config(struct tncc_data *tncc)
   1050 {
   1051 	if (tncc_read_config_reg(tncc, HKEY_LOCAL_MACHINE) < 0 ||
   1052 	    tncc_read_config_reg(tncc, HKEY_CURRENT_USER) < 0)
   1053 		return -1;
   1054 	return 0;
   1055 }
   1056 
   1057 #else /* CONFIG_NATIVE_WINDOWS */
   1058 
   1059 static struct tnc_if_imc * tncc_parse_imc(char *start, char *end, int *error)
   1060 {
   1061 	struct tnc_if_imc *imc;
   1062 	char *pos, *pos2;
   1063 	int i;
   1064 
   1065 	for (i = 0; i < TNC_MAX_IMC_ID; i++) {
   1066 		if (tnc_imc[i] == NULL)
   1067 			break;
   1068 	}
   1069 	if (i >= TNC_MAX_IMC_ID) {
   1070 		wpa_printf(MSG_DEBUG, "TNC: Too many IMCs");
   1071 		return NULL;
   1072 	}
   1073 
   1074 	imc = os_zalloc(sizeof(*imc));
   1075 	if (imc == NULL) {
   1076 		*error = 1;
   1077 		return NULL;
   1078 	}
   1079 
   1080 	imc->imcID = i;
   1081 
   1082 	pos = start;
   1083 	wpa_printf(MSG_DEBUG, "TNC: Configured IMC: %s", pos);
   1084 	if (pos + 1 >= end || *pos != '"') {
   1085 		wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMC line '%s' "
   1086 			   "(no starting quotation mark)", start);
   1087 		os_free(imc);
   1088 		return NULL;
   1089 	}
   1090 
   1091 	pos++;
   1092 	pos2 = pos;
   1093 	while (pos2 < end && *pos2 != '"')
   1094 		pos2++;
   1095 	if (pos2 >= end) {
   1096 		wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMC line '%s' "
   1097 			   "(no ending quotation mark)", start);
   1098 		os_free(imc);
   1099 		return NULL;
   1100 	}
   1101 	*pos2 = '\0';
   1102 	wpa_printf(MSG_DEBUG, "TNC: Name: '%s'", pos);
   1103 	imc->name = os_strdup(pos);
   1104 
   1105 	pos = pos2 + 1;
   1106 	if (pos >= end || *pos != ' ') {
   1107 		wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMC line '%s' "
   1108 			   "(no space after name)", start);
   1109 		os_free(imc->name);
   1110 		os_free(imc);
   1111 		return NULL;
   1112 	}
   1113 
   1114 	pos++;
   1115 	wpa_printf(MSG_DEBUG, "TNC: IMC file: '%s'", pos);
   1116 	imc->path = os_strdup(pos);
   1117 	tnc_imc[imc->imcID] = imc;
   1118 
   1119 	return imc;
   1120 }
   1121 
   1122 
   1123 static int tncc_read_config(struct tncc_data *tncc)
   1124 {
   1125 	char *config, *end, *pos, *line_end;
   1126 	size_t config_len;
   1127 	struct tnc_if_imc *imc, *last;
   1128 
   1129 	last = NULL;
   1130 
   1131 	config = os_readfile(TNC_CONFIG_FILE, &config_len);
   1132 	if (config == NULL) {
   1133 		wpa_printf(MSG_ERROR, "TNC: Could not open TNC configuration "
   1134 			   "file '%s'", TNC_CONFIG_FILE);
   1135 		return -1;
   1136 	}
   1137 
   1138 	end = config + config_len;
   1139 	for (pos = config; pos < end; pos = line_end + 1) {
   1140 		line_end = pos;
   1141 		while (*line_end != '\n' && *line_end != '\r' &&
   1142 		       line_end < end)
   1143 			line_end++;
   1144 		*line_end = '\0';
   1145 
   1146 		if (os_strncmp(pos, "IMC ", 4) == 0) {
   1147 			int error = 0;
   1148 
   1149 			imc = tncc_parse_imc(pos + 4, line_end, &error);
   1150 			if (error)
   1151 				return -1;
   1152 			if (imc) {
   1153 				if (last == NULL)
   1154 					tncc->imc = imc;
   1155 				else
   1156 					last->next = imc;
   1157 				last = imc;
   1158 			}
   1159 		}
   1160 	}
   1161 
   1162 	os_free(config);
   1163 
   1164 	return 0;
   1165 }
   1166 
   1167 #endif /* CONFIG_NATIVE_WINDOWS */
   1168 
   1169 
   1170 struct tncc_data * tncc_init(void)
   1171 {
   1172 	struct tncc_data *tncc;
   1173 	struct tnc_if_imc *imc;
   1174 
   1175 	tncc = os_zalloc(sizeof(*tncc));
   1176 	if (tncc == NULL)
   1177 		return NULL;
   1178 
   1179 	/* TODO:
   1180 	 * move loading and Initialize() to a location that is not
   1181 	 *    re-initialized for every EAP-TNC session (?)
   1182 	 */
   1183 
   1184 	if (tncc_read_config(tncc) < 0) {
   1185 		wpa_printf(MSG_ERROR, "TNC: Failed to read TNC configuration");
   1186 		goto failed;
   1187 	}
   1188 
   1189 	for (imc = tncc->imc; imc; imc = imc->next) {
   1190 		if (tncc_load_imc(imc)) {
   1191 			wpa_printf(MSG_ERROR, "TNC: Failed to load IMC '%s'",
   1192 				   imc->name);
   1193 			goto failed;
   1194 		}
   1195 	}
   1196 
   1197 	return tncc;
   1198 
   1199 failed:
   1200 	tncc_deinit(tncc);
   1201 	return NULL;
   1202 }
   1203 
   1204 
   1205 void tncc_deinit(struct tncc_data *tncc)
   1206 {
   1207 	struct tnc_if_imc *imc, *prev;
   1208 
   1209 	imc = tncc->imc;
   1210 	while (imc) {
   1211 		tncc_unload_imc(imc);
   1212 
   1213 		prev = imc;
   1214 		imc = imc->next;
   1215 		os_free(prev);
   1216 	}
   1217 
   1218 	os_free(tncc);
   1219 }
   1220 
   1221 
   1222 static struct wpabuf * tncc_build_soh(int ver)
   1223 {
   1224 	struct wpabuf *buf;
   1225 	u8 *tlv_len, *tlv_len2, *outer_len, *inner_len, *ssoh_len, *end;
   1226 	u8 correlation_id[24];
   1227 	/* TODO: get correct name */
   1228 	char *machinename = "wpa_supplicant (at) w1.fi";
   1229 
   1230 	if (os_get_random(correlation_id, sizeof(correlation_id)))
   1231 		return NULL;
   1232 	wpa_hexdump(MSG_DEBUG, "TNC: SoH Correlation ID",
   1233 		    correlation_id, sizeof(correlation_id));
   1234 
   1235 	buf = wpabuf_alloc(200);
   1236 	if (buf == NULL)
   1237 		return NULL;
   1238 
   1239 	/* Vendor-Specific TLV (Microsoft) - SoH */
   1240 	wpabuf_put_be16(buf, EAP_TLV_VENDOR_SPECIFIC_TLV); /* TLV Type */
   1241 	tlv_len = wpabuf_put(buf, 2); /* Length */
   1242 	wpabuf_put_be32(buf, EAP_VENDOR_MICROSOFT); /* Vendor_Id */
   1243 	wpabuf_put_be16(buf, 0x01); /* TLV Type - SoH TLV */
   1244 	tlv_len2 = wpabuf_put(buf, 2); /* Length */
   1245 
   1246 	/* SoH Header */
   1247 	wpabuf_put_be16(buf, EAP_TLV_VENDOR_SPECIFIC_TLV); /* Outer Type */
   1248 	outer_len = wpabuf_put(buf, 2);
   1249 	wpabuf_put_be32(buf, EAP_VENDOR_MICROSOFT); /* IANA SMI Code */
   1250 	wpabuf_put_be16(buf, ver); /* Inner Type */
   1251 	inner_len = wpabuf_put(buf, 2);
   1252 
   1253 	if (ver == 2) {
   1254 		/* SoH Mode Sub-Header */
   1255 		/* Outer Type */
   1256 		wpabuf_put_be16(buf, EAP_TLV_VENDOR_SPECIFIC_TLV);
   1257 		wpabuf_put_be16(buf, 4 + 24 + 1 + 1); /* Length */
   1258 		wpabuf_put_be32(buf, EAP_VENDOR_MICROSOFT); /* IANA SMI Code */
   1259 		/* Value: */
   1260 		wpabuf_put_data(buf, correlation_id, sizeof(correlation_id));
   1261 		wpabuf_put_u8(buf, 0x01); /* Intent Flag - Request */
   1262 		wpabuf_put_u8(buf, 0x00); /* Content-Type Flag */
   1263 	}
   1264 
   1265 	/* SSoH TLV */
   1266 	/* System-Health-Id */
   1267 	wpabuf_put_be16(buf, 0x0002); /* Type */
   1268 	wpabuf_put_be16(buf, 4); /* Length */
   1269 	wpabuf_put_be32(buf, 79616);
   1270 	/* Vendor-Specific Attribute */
   1271 	wpabuf_put_be16(buf, EAP_TLV_VENDOR_SPECIFIC_TLV);
   1272 	ssoh_len = wpabuf_put(buf, 2);
   1273 	wpabuf_put_be32(buf, EAP_VENDOR_MICROSOFT); /* IANA SMI Code */
   1274 
   1275 	/* MS-Packet-Info */
   1276 	wpabuf_put_u8(buf, SSOH_MS_PACKET_INFO);
   1277 	/* Note: IF-TNCCS-SOH v1.0 r8 claims this field to be:
   1278 	 * Reserved(4 bits) r(1 bit) Vers(3 bits), but Windows XP
   1279 	 * SP3 seems to be sending 0x11 for SSoH, i.e., r(request/response) bit
   1280 	 * would not be in the specified location.
   1281 	 * [MS-SOH] 4.0.2: Reserved(3 bits) r(1 bit) Vers(4 bits)
   1282 	 */
   1283 	wpabuf_put_u8(buf, 0x11); /* r=request, vers=1 */
   1284 
   1285 	/* MS-Machine-Inventory */
   1286 	/* TODO: get correct values; 0 = not applicable for OS */
   1287 	wpabuf_put_u8(buf, SSOH_MS_MACHINE_INVENTORY);
   1288 	wpabuf_put_be32(buf, 0); /* osVersionMajor */
   1289 	wpabuf_put_be32(buf, 0); /* osVersionMinor */
   1290 	wpabuf_put_be32(buf, 0); /* osVersionBuild */
   1291 	wpabuf_put_be16(buf, 0); /* spVersionMajor */
   1292 	wpabuf_put_be16(buf, 0); /* spVersionMinor */
   1293 	wpabuf_put_be16(buf, 0); /* procArch */
   1294 
   1295 	/* MS-MachineName */
   1296 	wpabuf_put_u8(buf, SSOH_MS_MACHINENAME);
   1297 	wpabuf_put_be16(buf, os_strlen(machinename) + 1);
   1298 	wpabuf_put_data(buf, machinename, os_strlen(machinename) + 1);
   1299 
   1300 	/* MS-CorrelationId */
   1301 	wpabuf_put_u8(buf, SSOH_MS_CORRELATIONID);
   1302 	wpabuf_put_data(buf, correlation_id, sizeof(correlation_id));
   1303 
   1304 	/* MS-Quarantine-State */
   1305 	wpabuf_put_u8(buf, SSOH_MS_QUARANTINE_STATE);
   1306 	wpabuf_put_be16(buf, 1); /* Flags: ExtState=0, f=0, qState=1 */
   1307 	wpabuf_put_be32(buf, 0xffffffff); /* ProbTime (hi) */
   1308 	wpabuf_put_be32(buf, 0xffffffff); /* ProbTime (lo) */
   1309 	wpabuf_put_be16(buf, 1); /* urlLenInBytes */
   1310 	wpabuf_put_u8(buf, 0); /* null termination for the url */
   1311 
   1312 	/* MS-Machine-Inventory-Ex */
   1313 	wpabuf_put_u8(buf, SSOH_MS_MACHINE_INVENTORY_EX);
   1314 	wpabuf_put_be32(buf, 0); /* Reserved
   1315 				  * (note: Windows XP SP3 uses 0xdecafbad) */
   1316 	wpabuf_put_u8(buf, 1); /* ProductType: Client */
   1317 
   1318 	/* Update SSoH Length */
   1319 	end = wpabuf_put(buf, 0);
   1320 	WPA_PUT_BE16(ssoh_len, end - ssoh_len - 2);
   1321 
   1322 	/* TODO: SoHReportEntry TLV (zero or more) */
   1323 
   1324 	/* Update length fields */
   1325 	end = wpabuf_put(buf, 0);
   1326 	WPA_PUT_BE16(tlv_len, end - tlv_len - 2);
   1327 	WPA_PUT_BE16(tlv_len2, end - tlv_len2 - 2);
   1328 	WPA_PUT_BE16(outer_len, end - outer_len - 2);
   1329 	WPA_PUT_BE16(inner_len, end - inner_len - 2);
   1330 
   1331 	return buf;
   1332 }
   1333 
   1334 
   1335 struct wpabuf * tncc_process_soh_request(int ver, const u8 *data, size_t len)
   1336 {
   1337 	const u8 *pos;
   1338 
   1339 	wpa_hexdump(MSG_DEBUG, "TNC: SoH Request", data, len);
   1340 
   1341 	if (len < 12)
   1342 		return NULL;
   1343 
   1344 	/* SoH Request */
   1345 	pos = data;
   1346 
   1347 	/* TLV Type */
   1348 	if (WPA_GET_BE16(pos) != EAP_TLV_VENDOR_SPECIFIC_TLV)
   1349 		return NULL;
   1350 	pos += 2;
   1351 
   1352 	/* Length */
   1353 	if (WPA_GET_BE16(pos) < 8)
   1354 		return NULL;
   1355 	pos += 2;
   1356 
   1357 	/* Vendor_Id */
   1358 	if (WPA_GET_BE32(pos) != EAP_VENDOR_MICROSOFT)
   1359 		return NULL;
   1360 	pos += 4;
   1361 
   1362 	/* TLV Type */
   1363 	if (WPA_GET_BE16(pos) != 0x02 /* SoH request TLV */)
   1364 		return NULL;
   1365 
   1366 	wpa_printf(MSG_DEBUG, "TNC: SoH Request TLV received");
   1367 
   1368 	return tncc_build_soh(2);
   1369 }
   1370