tncc.c revision 4b06059785b935dd1f4f09314e4e12c417d2c6a4
1/*
2 * EAP-TNC - TNCC (IF-IMC and IF-TNCCS)
3 * Copyright (c) 2007, Jouni Malinen <j@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
41typedef unsigned long TNC_UInt32;
42typedef unsigned char *TNC_BufferReference;
43
44typedef TNC_UInt32 TNC_IMCID;
45typedef TNC_UInt32 TNC_ConnectionID;
46typedef TNC_UInt32 TNC_ConnectionState;
47typedef TNC_UInt32 TNC_RetryReason;
48typedef TNC_UInt32 TNC_MessageType;
49typedef TNC_MessageType *TNC_MessageTypeList;
50typedef TNC_UInt32 TNC_VendorID;
51typedef TNC_UInt32 TNC_MessageSubtype;
52typedef TNC_UInt32 TNC_Version;
53typedef TNC_UInt32 TNC_Result;
54
55typedef 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 */
92enum {
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
103struct 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
143struct tncc_data {
144	struct tnc_if_imc *imc;
145	unsigned int last_batchid;
146};
147
148#define TNC_MAX_IMC_ID 10
149static struct tnc_if_imc *tnc_imc[TNC_MAX_IMC_ID] = { NULL };
150
151
152/* TNCC functions that IMCs can call */
153
154TNC_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
188TNC_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
233TNC_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
253TNC_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
263TNC_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
273TNC_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
305static 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
323static 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
361static 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
377static 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
394static 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
408static 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
427static 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
441static 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
491static 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
510static 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
534static 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
558void 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
577size_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
588u8 * 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
607char * 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
618char * 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
628static 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
654static 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
665static 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
691static 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
734enum 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
927static 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
1041static 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
1051static 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
1115static 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
1162struct 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
1191failed:
1192	tncc_deinit(tncc);
1193	return NULL;
1194}
1195
1196
1197void 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
1214static 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@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
1327struct 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