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