tncc.c revision 8d520ff1dc2da35cdca849e982051b86468016d8
1/*
2 * EAP-TNC - TNCC (IF-IMC and IF-TNCCS)
3 * Copyright (c) 2007, Jouni Malinen <j@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
47typedef unsigned long TNC_UInt32;
48typedef unsigned char *TNC_BufferReference;
49
50typedef TNC_UInt32 TNC_IMCID;
51typedef TNC_UInt32 TNC_ConnectionID;
52typedef TNC_UInt32 TNC_ConnectionState;
53typedef TNC_UInt32 TNC_RetryReason;
54typedef TNC_UInt32 TNC_MessageType;
55typedef TNC_MessageType *TNC_MessageTypeList;
56typedef TNC_UInt32 TNC_VendorID;
57typedef TNC_UInt32 TNC_MessageSubtype;
58typedef TNC_UInt32 TNC_Version;
59typedef TNC_UInt32 TNC_Result;
60
61typedef 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 */
98enum {
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
109struct 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
149struct tncc_data {
150	struct tnc_if_imc *imc;
151	unsigned int last_batchid;
152};
153
154#define TNC_MAX_IMC_ID 10
155static struct tnc_if_imc *tnc_imc[TNC_MAX_IMC_ID] = { NULL };
156
157
158/* TNCC functions that IMCs can call */
159
160TNC_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_MessageType));
184	if (imc->supported_types == NULL)
185		return TNC_RESULT_FATAL;
186	os_memcpy(imc->supported_types, supportedTypes,
187		  typeCount * sizeof(TNC_MessageType));
188	imc->num_supported_types = typeCount;
189
190	return TNC_RESULT_SUCCESS;
191}
192
193
194TNC_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
239TNC_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
259TNC_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
269TNC_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
279TNC_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
311static 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
329static 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
367static 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
383static 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
400static 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
414static 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
433static 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
447static 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
497static 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
516static 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
540static 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
564void 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
583size_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
594u8 * 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
613char * 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
624char * 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
634static 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
660static 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
671static 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
697static 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
740enum 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
935static 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
1049static 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
1059static 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
1123static 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
1170struct 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
1199failed:
1200	tncc_deinit(tncc);
1201	return NULL;
1202}
1203
1204
1205void 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
1222static 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@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
1335struct 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