1189251Ssam/*
2189251Ssam * EAP-TNC - TNCC (IF-IMC and IF-TNCCS)
3189251Ssam * Copyright (c) 2007, Jouni Malinen <j@w1.fi>
4189251Ssam *
5252726Srpaulo * This software may be distributed under the terms of the BSD license.
6252726Srpaulo * See README for more details.
7189251Ssam */
8189251Ssam
9189251Ssam#include "includes.h"
10189251Ssam#ifndef CONFIG_NATIVE_WINDOWS
11189251Ssam#include <dlfcn.h>
12189251Ssam#endif /* CONFIG_NATIVE_WINDOWS */
13189251Ssam
14189251Ssam#include "common.h"
15189251Ssam#include "base64.h"
16281806Srpaulo#include "common/tnc.h"
17189251Ssam#include "tncc.h"
18189251Ssam#include "eap_common/eap_tlv_common.h"
19189251Ssam#include "eap_common/eap_defs.h"
20189251Ssam
21189251Ssam
22189251Ssam#ifdef UNICODE
23189251Ssam#define TSTR "%S"
24189251Ssam#else /* UNICODE */
25189251Ssam#define TSTR "%s"
26189251Ssam#endif /* UNICODE */
27189251Ssam
28189251Ssam
29281806Srpaulo#ifndef TNC_CONFIG_FILE
30189251Ssam#define TNC_CONFIG_FILE "/etc/tnc_config"
31281806Srpaulo#endif /* TNC_CONFIG_FILE */
32189251Ssam#define TNC_WINREG_PATH TEXT("SOFTWARE\\Trusted Computing Group\\TNC\\IMCs")
33189251Ssam#define IF_TNCCS_START \
34189251Ssam"<?xml version=\"1.0\"?>\n" \
35189251Ssam"<TNCCS-Batch BatchId=\"%d\" Recipient=\"TNCS\" " \
36189251Ssam"xmlns=\"http://www.trustedcomputinggroup.org/IWG/TNC/1_0/IF_TNCCS#\" " \
37189251Ssam"xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " \
38189251Ssam"xsi:schemaLocation=\"http://www.trustedcomputinggroup.org/IWG/TNC/1_0/" \
39189251Ssam"IF_TNCCS# https://www.trustedcomputinggroup.org/XML/SCHEMA/TNCCS_1.0.xsd\">\n"
40189251Ssam#define IF_TNCCS_END "\n</TNCCS-Batch>"
41189251Ssam
42189251Ssam/* TNC IF-IMC */
43189251Ssam
44189251Ssam/* IF-TNCCS-SOH - SSoH and SSoHR Attributes */
45189251Ssamenum {
46189251Ssam	SSOH_MS_MACHINE_INVENTORY = 1,
47189251Ssam	SSOH_MS_QUARANTINE_STATE = 2,
48189251Ssam	SSOH_MS_PACKET_INFO = 3,
49189251Ssam	SSOH_MS_SYSTEMGENERATED_IDS = 4,
50189251Ssam	SSOH_MS_MACHINENAME = 5,
51189251Ssam	SSOH_MS_CORRELATIONID = 6,
52189251Ssam	SSOH_MS_INSTALLED_SHVS = 7,
53189251Ssam	SSOH_MS_MACHINE_INVENTORY_EX = 8
54189251Ssam};
55189251Ssam
56189251Ssamstruct tnc_if_imc {
57189251Ssam	struct tnc_if_imc *next;
58189251Ssam	char *name;
59189251Ssam	char *path;
60189251Ssam	void *dlhandle; /* from dlopen() */
61189251Ssam	TNC_IMCID imcID;
62189251Ssam	TNC_ConnectionID connectionID;
63189251Ssam	TNC_MessageTypeList supported_types;
64189251Ssam	size_t num_supported_types;
65189251Ssam	u8 *imc_send;
66189251Ssam	size_t imc_send_len;
67189251Ssam
68189251Ssam	/* Functions implemented by IMCs (with TNC_IMC_ prefix) */
69189251Ssam	TNC_Result (*Initialize)(
70189251Ssam		TNC_IMCID imcID,
71189251Ssam		TNC_Version minVersion,
72189251Ssam		TNC_Version maxVersion,
73189251Ssam		TNC_Version *pOutActualVersion);
74189251Ssam	TNC_Result (*NotifyConnectionChange)(
75189251Ssam		TNC_IMCID imcID,
76189251Ssam		TNC_ConnectionID connectionID,
77189251Ssam		TNC_ConnectionState newState);
78189251Ssam	TNC_Result (*BeginHandshake)(
79189251Ssam		TNC_IMCID imcID,
80189251Ssam		TNC_ConnectionID connectionID);
81189251Ssam	TNC_Result (*ReceiveMessage)(
82189251Ssam		TNC_IMCID imcID,
83189251Ssam		TNC_ConnectionID connectionID,
84189251Ssam		TNC_BufferReference messageBuffer,
85189251Ssam		TNC_UInt32 messageLength,
86189251Ssam		TNC_MessageType messageType);
87189251Ssam	TNC_Result (*BatchEnding)(
88189251Ssam		TNC_IMCID imcID,
89189251Ssam		TNC_ConnectionID connectionID);
90189251Ssam	TNC_Result (*Terminate)(TNC_IMCID imcID);
91189251Ssam	TNC_Result (*ProvideBindFunction)(
92189251Ssam		TNC_IMCID imcID,
93189251Ssam		TNC_TNCC_BindFunctionPointer bindFunction);
94189251Ssam};
95189251Ssam
96189251Ssamstruct tncc_data {
97189251Ssam	struct tnc_if_imc *imc;
98189251Ssam	unsigned int last_batchid;
99189251Ssam};
100189251Ssam
101189251Ssam#define TNC_MAX_IMC_ID 10
102189251Ssamstatic struct tnc_if_imc *tnc_imc[TNC_MAX_IMC_ID] = { NULL };
103189251Ssam
104189251Ssam
105189251Ssam/* TNCC functions that IMCs can call */
106189251Ssam
107337817Scystatic TNC_Result TNC_TNCC_ReportMessageTypes(
108189251Ssam	TNC_IMCID imcID,
109189251Ssam	TNC_MessageTypeList supportedTypes,
110189251Ssam	TNC_UInt32 typeCount)
111189251Ssam{
112189251Ssam	TNC_UInt32 i;
113189251Ssam	struct tnc_if_imc *imc;
114189251Ssam
115189251Ssam	wpa_printf(MSG_DEBUG, "TNC: TNC_TNCC_ReportMessageTypes(imcID=%lu "
116189251Ssam		   "typeCount=%lu)",
117189251Ssam		   (unsigned long) imcID, (unsigned long) typeCount);
118189251Ssam
119189251Ssam	for (i = 0; i < typeCount; i++) {
120189251Ssam		wpa_printf(MSG_DEBUG, "TNC: supportedTypes[%lu] = %lu",
121189251Ssam			   i, supportedTypes[i]);
122189251Ssam	}
123189251Ssam
124189251Ssam	if (imcID >= TNC_MAX_IMC_ID || tnc_imc[imcID] == NULL)
125189251Ssam		return TNC_RESULT_INVALID_PARAMETER;
126189251Ssam
127189251Ssam	imc = tnc_imc[imcID];
128189251Ssam	os_free(imc->supported_types);
129346981Scy	imc->supported_types = os_memdup(supportedTypes,
130346981Scy					 typeCount * sizeof(TNC_MessageType));
131189251Ssam	if (imc->supported_types == NULL)
132189251Ssam		return TNC_RESULT_FATAL;
133189251Ssam	imc->num_supported_types = typeCount;
134189251Ssam
135189251Ssam	return TNC_RESULT_SUCCESS;
136189251Ssam}
137189251Ssam
138189251Ssam
139337817Scystatic TNC_Result TNC_TNCC_SendMessage(
140189251Ssam	TNC_IMCID imcID,
141189251Ssam	TNC_ConnectionID connectionID,
142189251Ssam	TNC_BufferReference message,
143189251Ssam	TNC_UInt32 messageLength,
144189251Ssam	TNC_MessageType messageType)
145189251Ssam{
146189251Ssam	struct tnc_if_imc *imc;
147189251Ssam	unsigned char *b64;
148189251Ssam	size_t b64len;
149189251Ssam
150189251Ssam	wpa_printf(MSG_DEBUG, "TNC: TNC_TNCC_SendMessage(imcID=%lu "
151189251Ssam		   "connectionID=%lu messageType=%lu)",
152189251Ssam		   imcID, connectionID, messageType);
153189251Ssam	wpa_hexdump_ascii(MSG_DEBUG, "TNC: TNC_TNCC_SendMessage",
154189251Ssam			  message, messageLength);
155189251Ssam
156189251Ssam	if (imcID >= TNC_MAX_IMC_ID || tnc_imc[imcID] == NULL)
157189251Ssam		return TNC_RESULT_INVALID_PARAMETER;
158189251Ssam
159189251Ssam	b64 = base64_encode(message, messageLength, &b64len);
160189251Ssam	if (b64 == NULL)
161189251Ssam		return TNC_RESULT_FATAL;
162189251Ssam
163189251Ssam	imc = tnc_imc[imcID];
164189251Ssam	os_free(imc->imc_send);
165189251Ssam	imc->imc_send_len = 0;
166189251Ssam	imc->imc_send = os_zalloc(b64len + 100);
167189251Ssam	if (imc->imc_send == NULL) {
168189251Ssam		os_free(b64);
169189251Ssam		return TNC_RESULT_OTHER;
170189251Ssam	}
171189251Ssam
172189251Ssam	imc->imc_send_len =
173189251Ssam		os_snprintf((char *) imc->imc_send, b64len + 100,
174189251Ssam			    "<IMC-IMV-Message><Type>%08X</Type>"
175189251Ssam			    "<Base64>%s</Base64></IMC-IMV-Message>",
176189251Ssam			    (unsigned int) messageType, b64);
177189251Ssam
178189251Ssam	os_free(b64);
179189251Ssam
180189251Ssam	return TNC_RESULT_SUCCESS;
181189251Ssam}
182189251Ssam
183189251Ssam
184337817Scystatic TNC_Result TNC_TNCC_RequestHandshakeRetry(
185189251Ssam	TNC_IMCID imcID,
186189251Ssam	TNC_ConnectionID connectionID,
187189251Ssam	TNC_RetryReason reason)
188189251Ssam{
189189251Ssam	wpa_printf(MSG_DEBUG, "TNC: TNC_TNCC_RequestHandshakeRetry");
190189251Ssam
191189251Ssam	if (imcID >= TNC_MAX_IMC_ID || tnc_imc[imcID] == NULL)
192189251Ssam		return TNC_RESULT_INVALID_PARAMETER;
193189251Ssam
194189251Ssam	/*
195189251Ssam	 * TODO: trigger a call to eapol_sm_request_reauth(). This would
196189251Ssam	 * require that the IMC continues to be loaded in memory afer
197189251Ssam	 * authentication..
198189251Ssam	 */
199189251Ssam
200189251Ssam	return TNC_RESULT_SUCCESS;
201189251Ssam}
202189251Ssam
203189251Ssam
204337817Scystatic TNC_Result TNC_9048_LogMessage(TNC_IMCID imcID, TNC_UInt32 severity,
205337817Scy				      const char *message)
206189251Ssam{
207189251Ssam	wpa_printf(MSG_DEBUG, "TNC: TNC_9048_LogMessage(imcID=%lu "
208189251Ssam		   "severity==%lu message='%s')",
209189251Ssam		   imcID, severity, message);
210189251Ssam	return TNC_RESULT_SUCCESS;
211189251Ssam}
212189251Ssam
213189251Ssam
214337817Scystatic TNC_Result TNC_9048_UserMessage(TNC_IMCID imcID,
215337817Scy				       TNC_ConnectionID connectionID,
216337817Scy				       const char *message)
217189251Ssam{
218189251Ssam	wpa_printf(MSG_DEBUG, "TNC: TNC_9048_UserMessage(imcID=%lu "
219189251Ssam		   "connectionID==%lu message='%s')",
220189251Ssam		   imcID, connectionID, message);
221189251Ssam	return TNC_RESULT_SUCCESS;
222189251Ssam}
223189251Ssam
224189251Ssam
225337817Scystatic TNC_Result TNC_TNCC_BindFunction(
226189251Ssam	TNC_IMCID imcID,
227189251Ssam	char *functionName,
228189251Ssam	void **pOutfunctionPointer)
229189251Ssam{
230189251Ssam	wpa_printf(MSG_DEBUG, "TNC: TNC_TNCC_BindFunction(imcID=%lu, "
231189251Ssam		   "functionName='%s')", (unsigned long) imcID, functionName);
232189251Ssam
233189251Ssam	if (imcID >= TNC_MAX_IMC_ID || tnc_imc[imcID] == NULL)
234189251Ssam		return TNC_RESULT_INVALID_PARAMETER;
235189251Ssam
236189251Ssam	if (pOutfunctionPointer == NULL)
237189251Ssam		return TNC_RESULT_INVALID_PARAMETER;
238189251Ssam
239189251Ssam	if (os_strcmp(functionName, "TNC_TNCC_ReportMessageTypes") == 0)
240189251Ssam		*pOutfunctionPointer = TNC_TNCC_ReportMessageTypes;
241189251Ssam	else if (os_strcmp(functionName, "TNC_TNCC_SendMessage") == 0)
242189251Ssam		*pOutfunctionPointer = TNC_TNCC_SendMessage;
243189251Ssam	else if (os_strcmp(functionName, "TNC_TNCC_RequestHandshakeRetry") ==
244189251Ssam		 0)
245189251Ssam		*pOutfunctionPointer = TNC_TNCC_RequestHandshakeRetry;
246189251Ssam	else if (os_strcmp(functionName, "TNC_9048_LogMessage") == 0)
247189251Ssam		*pOutfunctionPointer = TNC_9048_LogMessage;
248189251Ssam	else if (os_strcmp(functionName, "TNC_9048_UserMessage") == 0)
249189251Ssam		*pOutfunctionPointer = TNC_9048_UserMessage;
250189251Ssam	else
251189251Ssam		*pOutfunctionPointer = NULL;
252189251Ssam
253189251Ssam	return TNC_RESULT_SUCCESS;
254189251Ssam}
255189251Ssam
256189251Ssam
257189251Ssamstatic void * tncc_get_sym(void *handle, char *func)
258189251Ssam{
259189251Ssam	void *fptr;
260189251Ssam
261189251Ssam#ifdef CONFIG_NATIVE_WINDOWS
262189251Ssam#ifdef _WIN32_WCE
263189251Ssam	fptr = GetProcAddressA(handle, func);
264189251Ssam#else /* _WIN32_WCE */
265189251Ssam	fptr = GetProcAddress(handle, func);
266189251Ssam#endif /* _WIN32_WCE */
267189251Ssam#else /* CONFIG_NATIVE_WINDOWS */
268189251Ssam	fptr = dlsym(handle, func);
269189251Ssam#endif /* CONFIG_NATIVE_WINDOWS */
270189251Ssam
271189251Ssam	return fptr;
272189251Ssam}
273189251Ssam
274189251Ssam
275189251Ssamstatic int tncc_imc_resolve_funcs(struct tnc_if_imc *imc)
276189251Ssam{
277189251Ssam	void *handle = imc->dlhandle;
278189251Ssam
279189251Ssam	/* Mandatory IMC functions */
280189251Ssam	imc->Initialize = tncc_get_sym(handle, "TNC_IMC_Initialize");
281189251Ssam	if (imc->Initialize == NULL) {
282189251Ssam		wpa_printf(MSG_ERROR, "TNC: IMC does not export "
283189251Ssam			   "TNC_IMC_Initialize");
284189251Ssam		return -1;
285189251Ssam	}
286189251Ssam
287189251Ssam	imc->BeginHandshake = tncc_get_sym(handle, "TNC_IMC_BeginHandshake");
288189251Ssam	if (imc->BeginHandshake == NULL) {
289189251Ssam		wpa_printf(MSG_ERROR, "TNC: IMC does not export "
290189251Ssam			   "TNC_IMC_BeginHandshake");
291189251Ssam		return -1;
292189251Ssam	}
293189251Ssam
294189251Ssam	imc->ProvideBindFunction =
295189251Ssam		tncc_get_sym(handle, "TNC_IMC_ProvideBindFunction");
296189251Ssam	if (imc->ProvideBindFunction == NULL) {
297189251Ssam		wpa_printf(MSG_ERROR, "TNC: IMC does not export "
298189251Ssam			   "TNC_IMC_ProvideBindFunction");
299189251Ssam		return -1;
300189251Ssam	}
301189251Ssam
302189251Ssam	/* Optional IMC functions */
303189251Ssam	imc->NotifyConnectionChange =
304189251Ssam		tncc_get_sym(handle, "TNC_IMC_NotifyConnectionChange");
305189251Ssam	imc->ReceiveMessage = tncc_get_sym(handle, "TNC_IMC_ReceiveMessage");
306189251Ssam	imc->BatchEnding = tncc_get_sym(handle, "TNC_IMC_BatchEnding");
307189251Ssam	imc->Terminate = tncc_get_sym(handle, "TNC_IMC_Terminate");
308189251Ssam
309189251Ssam	return 0;
310189251Ssam}
311189251Ssam
312189251Ssam
313189251Ssamstatic int tncc_imc_initialize(struct tnc_if_imc *imc)
314189251Ssam{
315189251Ssam	TNC_Result res;
316189251Ssam	TNC_Version imc_ver;
317189251Ssam
318189251Ssam	wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMC_Initialize for IMC '%s'",
319189251Ssam		   imc->name);
320189251Ssam	res = imc->Initialize(imc->imcID, TNC_IFIMC_VERSION_1,
321189251Ssam			      TNC_IFIMC_VERSION_1, &imc_ver);
322189251Ssam	wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_Initialize: res=%lu imc_ver=%lu",
323189251Ssam		   (unsigned long) res, (unsigned long) imc_ver);
324189251Ssam
325189251Ssam	return res == TNC_RESULT_SUCCESS ? 0 : -1;
326189251Ssam}
327189251Ssam
328189251Ssam
329189251Ssamstatic int tncc_imc_terminate(struct tnc_if_imc *imc)
330189251Ssam{
331189251Ssam	TNC_Result res;
332189251Ssam
333189251Ssam	if (imc->Terminate == NULL)
334189251Ssam		return 0;
335189251Ssam
336189251Ssam	wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMC_Terminate for IMC '%s'",
337189251Ssam		   imc->name);
338189251Ssam	res = imc->Terminate(imc->imcID);
339189251Ssam	wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_Terminate: %lu",
340189251Ssam		   (unsigned long) res);
341189251Ssam
342189251Ssam	return res == TNC_RESULT_SUCCESS ? 0 : -1;
343189251Ssam}
344189251Ssam
345189251Ssam
346189251Ssamstatic int tncc_imc_provide_bind_function(struct tnc_if_imc *imc)
347189251Ssam{
348189251Ssam	TNC_Result res;
349189251Ssam
350189251Ssam	wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMC_ProvideBindFunction for "
351189251Ssam		   "IMC '%s'", imc->name);
352189251Ssam	res = imc->ProvideBindFunction(imc->imcID, TNC_TNCC_BindFunction);
353189251Ssam	wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_ProvideBindFunction: res=%lu",
354189251Ssam		   (unsigned long) res);
355189251Ssam
356189251Ssam	return res == TNC_RESULT_SUCCESS ? 0 : -1;
357189251Ssam}
358189251Ssam
359189251Ssam
360189251Ssamstatic int tncc_imc_notify_connection_change(struct tnc_if_imc *imc,
361189251Ssam					     TNC_ConnectionState state)
362189251Ssam{
363189251Ssam	TNC_Result res;
364189251Ssam
365189251Ssam	if (imc->NotifyConnectionChange == NULL)
366189251Ssam		return 0;
367189251Ssam
368189251Ssam	wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMC_NotifyConnectionChange(%d)"
369189251Ssam		   " for IMC '%s'", (int) state, imc->name);
370189251Ssam	res = imc->NotifyConnectionChange(imc->imcID, imc->connectionID,
371189251Ssam					  state);
372189251Ssam	wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_NotifyConnectionChange: %lu",
373189251Ssam		   (unsigned long) res);
374189251Ssam
375189251Ssam	return res == TNC_RESULT_SUCCESS ? 0 : -1;
376189251Ssam}
377189251Ssam
378189251Ssam
379189251Ssamstatic int tncc_imc_begin_handshake(struct tnc_if_imc *imc)
380189251Ssam{
381189251Ssam	TNC_Result res;
382189251Ssam
383189251Ssam	wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMC_BeginHandshake for IMC "
384189251Ssam		   "'%s'", imc->name);
385189251Ssam	res = imc->BeginHandshake(imc->imcID, imc->connectionID);
386189251Ssam	wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_BeginHandshake: %lu",
387189251Ssam		   (unsigned long) res);
388189251Ssam
389189251Ssam	return res == TNC_RESULT_SUCCESS ? 0 : -1;
390189251Ssam}
391189251Ssam
392189251Ssam
393189251Ssamstatic int tncc_load_imc(struct tnc_if_imc *imc)
394189251Ssam{
395189251Ssam	if (imc->path == NULL) {
396189251Ssam		wpa_printf(MSG_DEBUG, "TNC: No IMC configured");
397189251Ssam		return -1;
398189251Ssam	}
399189251Ssam
400189251Ssam	wpa_printf(MSG_DEBUG, "TNC: Opening IMC: %s (%s)",
401189251Ssam		   imc->name, imc->path);
402189251Ssam#ifdef CONFIG_NATIVE_WINDOWS
403189251Ssam#ifdef UNICODE
404189251Ssam	{
405189251Ssam		TCHAR *lib = wpa_strdup_tchar(imc->path);
406189251Ssam		if (lib == NULL)
407189251Ssam			return -1;
408189251Ssam		imc->dlhandle = LoadLibrary(lib);
409189251Ssam		os_free(lib);
410189251Ssam	}
411189251Ssam#else /* UNICODE */
412189251Ssam	imc->dlhandle = LoadLibrary(imc->path);
413189251Ssam#endif /* UNICODE */
414189251Ssam	if (imc->dlhandle == NULL) {
415189251Ssam		wpa_printf(MSG_ERROR, "TNC: Failed to open IMC '%s' (%s): %d",
416189251Ssam			   imc->name, imc->path, (int) GetLastError());
417189251Ssam		return -1;
418189251Ssam	}
419189251Ssam#else /* CONFIG_NATIVE_WINDOWS */
420189251Ssam	imc->dlhandle = dlopen(imc->path, RTLD_LAZY);
421189251Ssam	if (imc->dlhandle == NULL) {
422189251Ssam		wpa_printf(MSG_ERROR, "TNC: Failed to open IMC '%s' (%s): %s",
423189251Ssam			   imc->name, imc->path, dlerror());
424189251Ssam		return -1;
425189251Ssam	}
426189251Ssam#endif /* CONFIG_NATIVE_WINDOWS */
427189251Ssam
428189251Ssam	if (tncc_imc_resolve_funcs(imc) < 0) {
429189251Ssam		wpa_printf(MSG_ERROR, "TNC: Failed to resolve IMC functions");
430189251Ssam		return -1;
431189251Ssam	}
432189251Ssam
433189251Ssam	if (tncc_imc_initialize(imc) < 0 ||
434189251Ssam	    tncc_imc_provide_bind_function(imc) < 0) {
435189251Ssam		wpa_printf(MSG_ERROR, "TNC: Failed to initialize IMC");
436189251Ssam		return -1;
437189251Ssam	}
438189251Ssam
439189251Ssam	return 0;
440189251Ssam}
441189251Ssam
442189251Ssam
443189251Ssamstatic void tncc_unload_imc(struct tnc_if_imc *imc)
444189251Ssam{
445189251Ssam	tncc_imc_terminate(imc);
446189251Ssam	tnc_imc[imc->imcID] = NULL;
447189251Ssam
448189251Ssam	if (imc->dlhandle) {
449189251Ssam#ifdef CONFIG_NATIVE_WINDOWS
450189251Ssam		FreeLibrary(imc->dlhandle);
451189251Ssam#else /* CONFIG_NATIVE_WINDOWS */
452189251Ssam		dlclose(imc->dlhandle);
453189251Ssam#endif /* CONFIG_NATIVE_WINDOWS */
454189251Ssam	}
455189251Ssam	os_free(imc->name);
456189251Ssam	os_free(imc->path);
457189251Ssam	os_free(imc->supported_types);
458189251Ssam	os_free(imc->imc_send);
459189251Ssam}
460189251Ssam
461189251Ssam
462189251Ssamstatic int tncc_supported_type(struct tnc_if_imc *imc, unsigned int type)
463189251Ssam{
464189251Ssam	size_t i;
465189251Ssam	unsigned int vendor, subtype;
466189251Ssam
467189251Ssam	if (imc == NULL || imc->supported_types == NULL)
468189251Ssam		return 0;
469189251Ssam
470189251Ssam	vendor = type >> 8;
471189251Ssam	subtype = type & 0xff;
472189251Ssam
473189251Ssam	for (i = 0; i < imc->num_supported_types; i++) {
474189251Ssam		unsigned int svendor, ssubtype;
475189251Ssam		svendor = imc->supported_types[i] >> 8;
476189251Ssam		ssubtype = imc->supported_types[i] & 0xff;
477189251Ssam		if ((vendor == svendor || svendor == TNC_VENDORID_ANY) &&
478189251Ssam		    (subtype == ssubtype || ssubtype == TNC_SUBTYPE_ANY))
479189251Ssam			return 1;
480189251Ssam	}
481189251Ssam
482189251Ssam	return 0;
483189251Ssam}
484189251Ssam
485189251Ssam
486189251Ssamstatic void tncc_send_to_imcs(struct tncc_data *tncc, unsigned int type,
487189251Ssam			      const u8 *msg, size_t len)
488189251Ssam{
489189251Ssam	struct tnc_if_imc *imc;
490189251Ssam	TNC_Result res;
491189251Ssam
492189251Ssam	wpa_hexdump_ascii(MSG_MSGDUMP, "TNC: Message to IMC(s)", msg, len);
493189251Ssam
494189251Ssam	for (imc = tncc->imc; imc; imc = imc->next) {
495189251Ssam		if (imc->ReceiveMessage == NULL ||
496189251Ssam		    !tncc_supported_type(imc, type))
497189251Ssam			continue;
498189251Ssam
499189251Ssam		wpa_printf(MSG_DEBUG, "TNC: Call ReceiveMessage for IMC '%s'",
500189251Ssam			   imc->name);
501189251Ssam		res = imc->ReceiveMessage(imc->imcID, imc->connectionID,
502189251Ssam					  (TNC_BufferReference) msg, len,
503189251Ssam					  type);
504189251Ssam		wpa_printf(MSG_DEBUG, "TNC: ReceiveMessage: %lu",
505189251Ssam			   (unsigned long) res);
506189251Ssam	}
507189251Ssam}
508189251Ssam
509189251Ssam
510189251Ssamvoid tncc_init_connection(struct tncc_data *tncc)
511189251Ssam{
512189251Ssam	struct tnc_if_imc *imc;
513189251Ssam
514189251Ssam	for (imc = tncc->imc; imc; imc = imc->next) {
515189251Ssam		tncc_imc_notify_connection_change(
516189251Ssam			imc, TNC_CONNECTION_STATE_CREATE);
517189251Ssam		tncc_imc_notify_connection_change(
518189251Ssam			imc, TNC_CONNECTION_STATE_HANDSHAKE);
519189251Ssam
520189251Ssam		os_free(imc->imc_send);
521189251Ssam		imc->imc_send = NULL;
522189251Ssam		imc->imc_send_len = 0;
523189251Ssam
524189251Ssam		tncc_imc_begin_handshake(imc);
525189251Ssam	}
526189251Ssam}
527189251Ssam
528189251Ssam
529189251Ssamsize_t tncc_total_send_len(struct tncc_data *tncc)
530189251Ssam{
531189251Ssam	struct tnc_if_imc *imc;
532189251Ssam
533189251Ssam	size_t len = 0;
534189251Ssam	for (imc = tncc->imc; imc; imc = imc->next)
535189251Ssam		len += imc->imc_send_len;
536189251Ssam	return len;
537189251Ssam}
538189251Ssam
539189251Ssam
540189251Ssamu8 * tncc_copy_send_buf(struct tncc_data *tncc, u8 *pos)
541189251Ssam{
542189251Ssam	struct tnc_if_imc *imc;
543189251Ssam
544189251Ssam	for (imc = tncc->imc; imc; imc = imc->next) {
545189251Ssam		if (imc->imc_send == NULL)
546189251Ssam			continue;
547189251Ssam
548189251Ssam		os_memcpy(pos, imc->imc_send, imc->imc_send_len);
549189251Ssam		pos += imc->imc_send_len;
550189251Ssam		os_free(imc->imc_send);
551189251Ssam		imc->imc_send = NULL;
552189251Ssam		imc->imc_send_len = 0;
553189251Ssam	}
554189251Ssam
555189251Ssam	return pos;
556189251Ssam}
557189251Ssam
558189251Ssam
559189251Ssamchar * tncc_if_tnccs_start(struct tncc_data *tncc)
560189251Ssam{
561189251Ssam	char *buf = os_malloc(1000);
562189251Ssam	if (buf == NULL)
563189251Ssam		return NULL;
564189251Ssam	tncc->last_batchid++;
565189251Ssam	os_snprintf(buf, 1000, IF_TNCCS_START, tncc->last_batchid);
566189251Ssam	return buf;
567189251Ssam}
568189251Ssam
569189251Ssam
570189251Ssamchar * tncc_if_tnccs_end(void)
571189251Ssam{
572189251Ssam	char *buf = os_malloc(100);
573189251Ssam	if (buf == NULL)
574189251Ssam		return NULL;
575189251Ssam	os_snprintf(buf, 100, IF_TNCCS_END);
576189251Ssam	return buf;
577189251Ssam}
578189251Ssam
579189251Ssam
580189251Ssamstatic void tncc_notify_recommendation(struct tncc_data *tncc,
581189251Ssam				       enum tncc_process_res res)
582189251Ssam{
583189251Ssam	TNC_ConnectionState state;
584189251Ssam	struct tnc_if_imc *imc;
585189251Ssam
586189251Ssam	switch (res) {
587189251Ssam	case TNCCS_RECOMMENDATION_ALLOW:
588189251Ssam		state = TNC_CONNECTION_STATE_ACCESS_ALLOWED;
589189251Ssam		break;
590189251Ssam	case TNCCS_RECOMMENDATION_NONE:
591189251Ssam		state = TNC_CONNECTION_STATE_ACCESS_NONE;
592189251Ssam		break;
593189251Ssam	case TNCCS_RECOMMENDATION_ISOLATE:
594189251Ssam		state = TNC_CONNECTION_STATE_ACCESS_ISOLATED;
595189251Ssam		break;
596189251Ssam	default:
597189251Ssam		state = TNC_CONNECTION_STATE_ACCESS_NONE;
598189251Ssam		break;
599189251Ssam	}
600189251Ssam
601189251Ssam	for (imc = tncc->imc; imc; imc = imc->next)
602189251Ssam		tncc_imc_notify_connection_change(imc, state);
603189251Ssam}
604189251Ssam
605189251Ssam
606189251Ssamstatic int tncc_get_type(char *start, unsigned int *type)
607189251Ssam{
608189251Ssam	char *pos = os_strstr(start, "<Type>");
609189251Ssam	if (pos == NULL)
610189251Ssam		return -1;
611189251Ssam	pos += 6;
612189251Ssam	*type = strtoul(pos, NULL, 16);
613189251Ssam	return 0;
614189251Ssam}
615189251Ssam
616189251Ssam
617189251Ssamstatic unsigned char * tncc_get_base64(char *start, size_t *decoded_len)
618189251Ssam{
619189251Ssam	char *pos, *pos2;
620189251Ssam	unsigned char *decoded;
621189251Ssam
622189251Ssam	pos = os_strstr(start, "<Base64>");
623189251Ssam	if (pos == NULL)
624189251Ssam		return NULL;
625189251Ssam
626189251Ssam	pos += 8;
627189251Ssam	pos2 = os_strstr(pos, "</Base64>");
628189251Ssam	if (pos2 == NULL)
629189251Ssam		return NULL;
630189251Ssam	*pos2 = '\0';
631189251Ssam
632189251Ssam	decoded = base64_decode((unsigned char *) pos, os_strlen(pos),
633189251Ssam				decoded_len);
634189251Ssam	*pos2 = '<';
635189251Ssam	if (decoded == NULL) {
636189251Ssam		wpa_printf(MSG_DEBUG, "TNC: Failed to decode Base64 data");
637189251Ssam	}
638189251Ssam
639189251Ssam	return decoded;
640189251Ssam}
641189251Ssam
642189251Ssam
643189251Ssamstatic enum tncc_process_res tncc_get_recommendation(char *start)
644189251Ssam{
645189251Ssam	char *pos, *pos2, saved;
646189251Ssam	int recom;
647189251Ssam
648189251Ssam	pos = os_strstr(start, "<TNCCS-Recommendation ");
649189251Ssam	if (pos == NULL)
650189251Ssam		return TNCCS_RECOMMENDATION_ERROR;
651189251Ssam
652189251Ssam	pos += 21;
653189251Ssam	pos = os_strstr(pos, " type=");
654189251Ssam	if (pos == NULL)
655189251Ssam		return TNCCS_RECOMMENDATION_ERROR;
656189251Ssam	pos += 6;
657189251Ssam
658189251Ssam	if (*pos == '"')
659189251Ssam		pos++;
660189251Ssam
661189251Ssam	pos2 = pos;
662189251Ssam	while (*pos2 != '\0' && *pos2 != '"' && *pos2 != '>')
663189251Ssam		pos2++;
664189251Ssam
665189251Ssam	if (*pos2 == '\0')
666189251Ssam		return TNCCS_RECOMMENDATION_ERROR;
667189251Ssam
668189251Ssam	saved = *pos2;
669189251Ssam	*pos2 = '\0';
670189251Ssam	wpa_printf(MSG_DEBUG, "TNC: TNCCS-Recommendation: '%s'", pos);
671189251Ssam
672189251Ssam	recom = TNCCS_RECOMMENDATION_ERROR;
673189251Ssam	if (os_strcmp(pos, "allow") == 0)
674189251Ssam		recom = TNCCS_RECOMMENDATION_ALLOW;
675189251Ssam	else if (os_strcmp(pos, "none") == 0)
676189251Ssam		recom = TNCCS_RECOMMENDATION_NONE;
677189251Ssam	else if (os_strcmp(pos, "isolate") == 0)
678189251Ssam		recom = TNCCS_RECOMMENDATION_ISOLATE;
679189251Ssam
680189251Ssam	*pos2 = saved;
681189251Ssam
682189251Ssam	return recom;
683189251Ssam}
684189251Ssam
685189251Ssam
686189251Ssamenum tncc_process_res tncc_process_if_tnccs(struct tncc_data *tncc,
687189251Ssam					    const u8 *msg, size_t len)
688189251Ssam{
689189251Ssam	char *buf, *start, *end, *pos, *pos2, *payload;
690189251Ssam	unsigned int batch_id;
691189251Ssam	unsigned char *decoded;
692189251Ssam	size_t decoded_len;
693189251Ssam	enum tncc_process_res res = TNCCS_PROCESS_OK_NO_RECOMMENDATION;
694189251Ssam	int recommendation_msg = 0;
695189251Ssam
696337817Scy	wpa_hexdump_ascii(MSG_MSGDUMP, "TNC: Received IF-TNCCS message",
697337817Scy			  msg, len);
698281806Srpaulo	buf = dup_binstr(msg, len);
699189251Ssam	if (buf == NULL)
700189251Ssam		return TNCCS_PROCESS_ERROR;
701189251Ssam
702189251Ssam	start = os_strstr(buf, "<TNCCS-Batch ");
703189251Ssam	end = os_strstr(buf, "</TNCCS-Batch>");
704189251Ssam	if (start == NULL || end == NULL || start > end) {
705189251Ssam		os_free(buf);
706189251Ssam		return TNCCS_PROCESS_ERROR;
707189251Ssam	}
708189251Ssam
709189251Ssam	start += 13;
710189251Ssam	while (*start == ' ')
711189251Ssam		start++;
712189251Ssam	*end = '\0';
713189251Ssam
714189251Ssam	pos = os_strstr(start, "BatchId=");
715189251Ssam	if (pos == NULL) {
716189251Ssam		os_free(buf);
717189251Ssam		return TNCCS_PROCESS_ERROR;
718189251Ssam	}
719189251Ssam
720189251Ssam	pos += 8;
721189251Ssam	if (*pos == '"')
722189251Ssam		pos++;
723189251Ssam	batch_id = atoi(pos);
724189251Ssam	wpa_printf(MSG_DEBUG, "TNC: Received IF-TNCCS BatchId=%u",
725189251Ssam		   batch_id);
726189251Ssam	if (batch_id != tncc->last_batchid + 1) {
727189251Ssam		wpa_printf(MSG_DEBUG, "TNC: Unexpected IF-TNCCS BatchId "
728189251Ssam			   "%u (expected %u)",
729189251Ssam			   batch_id, tncc->last_batchid + 1);
730189251Ssam		os_free(buf);
731189251Ssam		return TNCCS_PROCESS_ERROR;
732189251Ssam	}
733189251Ssam	tncc->last_batchid = batch_id;
734189251Ssam
735189251Ssam	while (*pos != '\0' && *pos != '>')
736189251Ssam		pos++;
737189251Ssam	if (*pos == '\0') {
738189251Ssam		os_free(buf);
739189251Ssam		return TNCCS_PROCESS_ERROR;
740189251Ssam	}
741189251Ssam	pos++;
742189251Ssam	payload = start;
743189251Ssam
744189251Ssam	/*
745189251Ssam	 * <IMC-IMV-Message>
746189251Ssam	 * <Type>01234567</Type>
747189251Ssam	 * <Base64>foo==</Base64>
748189251Ssam	 * </IMC-IMV-Message>
749189251Ssam	 */
750189251Ssam
751189251Ssam	while (*start) {
752189251Ssam		char *endpos;
753189251Ssam		unsigned int type;
754189251Ssam
755189251Ssam		pos = os_strstr(start, "<IMC-IMV-Message>");
756189251Ssam		if (pos == NULL)
757189251Ssam			break;
758189251Ssam		start = pos + 17;
759189251Ssam		end = os_strstr(start, "</IMC-IMV-Message>");
760189251Ssam		if (end == NULL)
761189251Ssam			break;
762189251Ssam		*end = '\0';
763189251Ssam		endpos = end;
764189251Ssam		end += 18;
765189251Ssam
766189251Ssam		if (tncc_get_type(start, &type) < 0) {
767189251Ssam			*endpos = '<';
768189251Ssam			start = end;
769189251Ssam			continue;
770189251Ssam		}
771189251Ssam		wpa_printf(MSG_DEBUG, "TNC: IMC-IMV-Message Type 0x%x", type);
772189251Ssam
773189251Ssam		decoded = tncc_get_base64(start, &decoded_len);
774189251Ssam		if (decoded == NULL) {
775189251Ssam			*endpos = '<';
776189251Ssam			start = end;
777189251Ssam			continue;
778189251Ssam		}
779189251Ssam
780189251Ssam		tncc_send_to_imcs(tncc, type, decoded, decoded_len);
781189251Ssam
782189251Ssam		os_free(decoded);
783189251Ssam
784189251Ssam		start = end;
785189251Ssam	}
786189251Ssam
787189251Ssam	/*
788189251Ssam	 * <TNCC-TNCS-Message>
789189251Ssam	 * <Type>01234567</Type>
790189251Ssam	 * <XML><TNCCS-Foo type="foo"></TNCCS-Foo></XML>
791189251Ssam	 * <Base64>foo==</Base64>
792189251Ssam	 * </TNCC-TNCS-Message>
793189251Ssam	 */
794189251Ssam
795189251Ssam	start = payload;
796189251Ssam	while (*start) {
797189251Ssam		unsigned int type;
798189251Ssam		char *xml, *xmlend, *endpos;
799189251Ssam
800189251Ssam		pos = os_strstr(start, "<TNCC-TNCS-Message>");
801189251Ssam		if (pos == NULL)
802189251Ssam			break;
803189251Ssam		start = pos + 19;
804189251Ssam		end = os_strstr(start, "</TNCC-TNCS-Message>");
805189251Ssam		if (end == NULL)
806189251Ssam			break;
807189251Ssam		*end = '\0';
808189251Ssam		endpos = end;
809189251Ssam		end += 20;
810189251Ssam
811189251Ssam		if (tncc_get_type(start, &type) < 0) {
812189251Ssam			*endpos = '<';
813189251Ssam			start = end;
814189251Ssam			continue;
815189251Ssam		}
816189251Ssam		wpa_printf(MSG_DEBUG, "TNC: TNCC-TNCS-Message Type 0x%x",
817189251Ssam			   type);
818189251Ssam
819189251Ssam		/* Base64 OR XML */
820189251Ssam		decoded = NULL;
821189251Ssam		xml = NULL;
822189251Ssam		xmlend = NULL;
823189251Ssam		pos = os_strstr(start, "<XML>");
824189251Ssam		if (pos) {
825189251Ssam			pos += 5;
826189251Ssam			pos2 = os_strstr(pos, "</XML>");
827189251Ssam			if (pos2 == NULL) {
828189251Ssam				*endpos = '<';
829189251Ssam				start = end;
830189251Ssam				continue;
831189251Ssam			}
832189251Ssam			xmlend = pos2;
833189251Ssam			xml = pos;
834189251Ssam		} else {
835189251Ssam			decoded = tncc_get_base64(start, &decoded_len);
836189251Ssam			if (decoded == NULL) {
837189251Ssam				*endpos = '<';
838189251Ssam				start = end;
839189251Ssam				continue;
840189251Ssam			}
841189251Ssam		}
842189251Ssam
843189251Ssam		if (decoded) {
844189251Ssam			wpa_hexdump_ascii(MSG_MSGDUMP,
845189251Ssam					  "TNC: TNCC-TNCS-Message Base64",
846189251Ssam					  decoded, decoded_len);
847189251Ssam			os_free(decoded);
848189251Ssam		}
849189251Ssam
850189251Ssam		if (xml) {
851189251Ssam			wpa_hexdump_ascii(MSG_MSGDUMP,
852189251Ssam					  "TNC: TNCC-TNCS-Message XML",
853189251Ssam					  (unsigned char *) xml,
854189251Ssam					  xmlend - xml);
855189251Ssam		}
856189251Ssam
857189251Ssam		if (type == TNC_TNCCS_RECOMMENDATION && xml) {
858189251Ssam			/*
859189251Ssam			 * <TNCCS-Recommendation type="allow">
860189251Ssam			 * </TNCCS-Recommendation>
861189251Ssam			 */
862189251Ssam			*xmlend = '\0';
863189251Ssam			res = tncc_get_recommendation(xml);
864189251Ssam			*xmlend = '<';
865189251Ssam			recommendation_msg = 1;
866189251Ssam		}
867189251Ssam
868189251Ssam		start = end;
869189251Ssam	}
870189251Ssam
871189251Ssam	os_free(buf);
872189251Ssam
873189251Ssam	if (recommendation_msg)
874189251Ssam		tncc_notify_recommendation(tncc, res);
875189251Ssam
876189251Ssam	return res;
877189251Ssam}
878189251Ssam
879189251Ssam
880189251Ssam#ifdef CONFIG_NATIVE_WINDOWS
881189251Ssamstatic int tncc_read_config_reg(struct tncc_data *tncc, HKEY hive)
882189251Ssam{
883189251Ssam	HKEY hk, hk2;
884189251Ssam	LONG ret;
885189251Ssam	DWORD i;
886189251Ssam	struct tnc_if_imc *imc, *last;
887189251Ssam	int j;
888189251Ssam
889189251Ssam	last = tncc->imc;
890189251Ssam	while (last && last->next)
891189251Ssam		last = last->next;
892189251Ssam
893189251Ssam	ret = RegOpenKeyEx(hive, TNC_WINREG_PATH, 0, KEY_ENUMERATE_SUB_KEYS,
894189251Ssam			   &hk);
895189251Ssam	if (ret != ERROR_SUCCESS)
896189251Ssam		return 0;
897189251Ssam
898189251Ssam	for (i = 0; ; i++) {
899189251Ssam		TCHAR name[255], *val;
900189251Ssam		DWORD namelen, buflen;
901189251Ssam
902189251Ssam		namelen = 255;
903189251Ssam		ret = RegEnumKeyEx(hk, i, name, &namelen, NULL, NULL, NULL,
904189251Ssam				   NULL);
905189251Ssam
906189251Ssam		if (ret == ERROR_NO_MORE_ITEMS)
907189251Ssam			break;
908189251Ssam
909189251Ssam		if (ret != ERROR_SUCCESS) {
910189251Ssam			wpa_printf(MSG_DEBUG, "TNC: RegEnumKeyEx failed: 0x%x",
911189251Ssam				   (unsigned int) ret);
912189251Ssam			break;
913189251Ssam		}
914189251Ssam
915189251Ssam		if (namelen >= 255)
916189251Ssam			namelen = 255 - 1;
917189251Ssam		name[namelen] = '\0';
918189251Ssam
919189251Ssam		wpa_printf(MSG_DEBUG, "TNC: IMC '" TSTR "'", name);
920189251Ssam
921189251Ssam		ret = RegOpenKeyEx(hk, name, 0, KEY_QUERY_VALUE, &hk2);
922189251Ssam		if (ret != ERROR_SUCCESS) {
923189251Ssam			wpa_printf(MSG_DEBUG, "Could not open IMC key '" TSTR
924189251Ssam				   "'", name);
925189251Ssam			continue;
926189251Ssam		}
927189251Ssam
928189251Ssam		ret = RegQueryValueEx(hk2, TEXT("Path"), NULL, NULL, NULL,
929189251Ssam				      &buflen);
930189251Ssam		if (ret != ERROR_SUCCESS) {
931189251Ssam			wpa_printf(MSG_DEBUG, "TNC: Could not read Path from "
932189251Ssam				   "IMC key '" TSTR "'", name);
933189251Ssam			RegCloseKey(hk2);
934189251Ssam			continue;
935189251Ssam		}
936189251Ssam
937189251Ssam		val = os_malloc(buflen);
938189251Ssam		if (val == NULL) {
939189251Ssam			RegCloseKey(hk2);
940189251Ssam			continue;
941189251Ssam		}
942189251Ssam
943189251Ssam		ret = RegQueryValueEx(hk2, TEXT("Path"), NULL, NULL,
944189251Ssam				      (LPBYTE) val, &buflen);
945189251Ssam		if (ret != ERROR_SUCCESS) {
946189251Ssam			os_free(val);
947189251Ssam			RegCloseKey(hk2);
948189251Ssam			continue;
949189251Ssam		}
950189251Ssam
951189251Ssam		RegCloseKey(hk2);
952189251Ssam
953189251Ssam		wpa_unicode2ascii_inplace(val);
954189251Ssam		wpa_printf(MSG_DEBUG, "TNC: IMC Path '%s'", (char *) val);
955189251Ssam
956189251Ssam		for (j = 0; j < TNC_MAX_IMC_ID; j++) {
957189251Ssam			if (tnc_imc[j] == NULL)
958189251Ssam				break;
959189251Ssam		}
960189251Ssam		if (j >= TNC_MAX_IMC_ID) {
961189251Ssam			wpa_printf(MSG_DEBUG, "TNC: Too many IMCs");
962189251Ssam			os_free(val);
963189251Ssam			continue;
964189251Ssam		}
965189251Ssam
966189251Ssam		imc = os_zalloc(sizeof(*imc));
967189251Ssam		if (imc == NULL) {
968189251Ssam			os_free(val);
969189251Ssam			break;
970189251Ssam		}
971189251Ssam
972189251Ssam		imc->imcID = j;
973189251Ssam
974189251Ssam		wpa_unicode2ascii_inplace(name);
975189251Ssam		imc->name = os_strdup((char *) name);
976189251Ssam		imc->path = os_strdup((char *) val);
977189251Ssam
978189251Ssam		os_free(val);
979189251Ssam
980189251Ssam		if (last == NULL)
981189251Ssam			tncc->imc = imc;
982189251Ssam		else
983189251Ssam			last->next = imc;
984189251Ssam		last = imc;
985189251Ssam
986189251Ssam		tnc_imc[imc->imcID] = imc;
987189251Ssam	}
988189251Ssam
989189251Ssam	RegCloseKey(hk);
990189251Ssam
991189251Ssam	return 0;
992189251Ssam}
993189251Ssam
994189251Ssam
995189251Ssamstatic int tncc_read_config(struct tncc_data *tncc)
996189251Ssam{
997189251Ssam	if (tncc_read_config_reg(tncc, HKEY_LOCAL_MACHINE) < 0 ||
998189251Ssam	    tncc_read_config_reg(tncc, HKEY_CURRENT_USER) < 0)
999189251Ssam		return -1;
1000189251Ssam	return 0;
1001189251Ssam}
1002189251Ssam
1003189251Ssam#else /* CONFIG_NATIVE_WINDOWS */
1004189251Ssam
1005189251Ssamstatic struct tnc_if_imc * tncc_parse_imc(char *start, char *end, int *error)
1006189251Ssam{
1007189251Ssam	struct tnc_if_imc *imc;
1008189251Ssam	char *pos, *pos2;
1009189251Ssam	int i;
1010189251Ssam
1011189251Ssam	for (i = 0; i < TNC_MAX_IMC_ID; i++) {
1012189251Ssam		if (tnc_imc[i] == NULL)
1013189251Ssam			break;
1014189251Ssam	}
1015189251Ssam	if (i >= TNC_MAX_IMC_ID) {
1016189251Ssam		wpa_printf(MSG_DEBUG, "TNC: Too many IMCs");
1017189251Ssam		return NULL;
1018189251Ssam	}
1019189251Ssam
1020189251Ssam	imc = os_zalloc(sizeof(*imc));
1021189251Ssam	if (imc == NULL) {
1022189251Ssam		*error = 1;
1023189251Ssam		return NULL;
1024189251Ssam	}
1025189251Ssam
1026189251Ssam	imc->imcID = i;
1027189251Ssam
1028189251Ssam	pos = start;
1029189251Ssam	wpa_printf(MSG_DEBUG, "TNC: Configured IMC: %s", pos);
1030189251Ssam	if (pos + 1 >= end || *pos != '"') {
1031189251Ssam		wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMC line '%s' "
1032189251Ssam			   "(no starting quotation mark)", start);
1033189251Ssam		os_free(imc);
1034189251Ssam		return NULL;
1035189251Ssam	}
1036189251Ssam
1037189251Ssam	pos++;
1038189251Ssam	pos2 = pos;
1039189251Ssam	while (pos2 < end && *pos2 != '"')
1040189251Ssam		pos2++;
1041189251Ssam	if (pos2 >= end) {
1042189251Ssam		wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMC line '%s' "
1043189251Ssam			   "(no ending quotation mark)", start);
1044189251Ssam		os_free(imc);
1045189251Ssam		return NULL;
1046189251Ssam	}
1047189251Ssam	*pos2 = '\0';
1048189251Ssam	wpa_printf(MSG_DEBUG, "TNC: Name: '%s'", pos);
1049189251Ssam	imc->name = os_strdup(pos);
1050189251Ssam
1051189251Ssam	pos = pos2 + 1;
1052189251Ssam	if (pos >= end || *pos != ' ') {
1053189251Ssam		wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMC line '%s' "
1054189251Ssam			   "(no space after name)", start);
1055209158Srpaulo		os_free(imc->name);
1056189251Ssam		os_free(imc);
1057189251Ssam		return NULL;
1058189251Ssam	}
1059189251Ssam
1060189251Ssam	pos++;
1061189251Ssam	wpa_printf(MSG_DEBUG, "TNC: IMC file: '%s'", pos);
1062189251Ssam	imc->path = os_strdup(pos);
1063189251Ssam	tnc_imc[imc->imcID] = imc;
1064189251Ssam
1065189251Ssam	return imc;
1066189251Ssam}
1067189251Ssam
1068189251Ssam
1069189251Ssamstatic int tncc_read_config(struct tncc_data *tncc)
1070189251Ssam{
1071189251Ssam	char *config, *end, *pos, *line_end;
1072189251Ssam	size_t config_len;
1073189251Ssam	struct tnc_if_imc *imc, *last;
1074189251Ssam
1075189251Ssam	last = NULL;
1076189251Ssam
1077189251Ssam	config = os_readfile(TNC_CONFIG_FILE, &config_len);
1078189251Ssam	if (config == NULL) {
1079189251Ssam		wpa_printf(MSG_ERROR, "TNC: Could not open TNC configuration "
1080189251Ssam			   "file '%s'", TNC_CONFIG_FILE);
1081189251Ssam		return -1;
1082189251Ssam	}
1083189251Ssam
1084189251Ssam	end = config + config_len;
1085189251Ssam	for (pos = config; pos < end; pos = line_end + 1) {
1086189251Ssam		line_end = pos;
1087189251Ssam		while (*line_end != '\n' && *line_end != '\r' &&
1088189251Ssam		       line_end < end)
1089189251Ssam			line_end++;
1090189251Ssam		*line_end = '\0';
1091189251Ssam
1092189251Ssam		if (os_strncmp(pos, "IMC ", 4) == 0) {
1093189251Ssam			int error = 0;
1094189251Ssam
1095189251Ssam			imc = tncc_parse_imc(pos + 4, line_end, &error);
1096281806Srpaulo			if (error) {
1097281806Srpaulo				os_free(config);
1098189251Ssam				return -1;
1099281806Srpaulo			}
1100189251Ssam			if (imc) {
1101189251Ssam				if (last == NULL)
1102189251Ssam					tncc->imc = imc;
1103189251Ssam				else
1104189251Ssam					last->next = imc;
1105189251Ssam				last = imc;
1106189251Ssam			}
1107189251Ssam		}
1108189251Ssam	}
1109189251Ssam
1110189251Ssam	os_free(config);
1111189251Ssam
1112189251Ssam	return 0;
1113189251Ssam}
1114189251Ssam
1115189251Ssam#endif /* CONFIG_NATIVE_WINDOWS */
1116189251Ssam
1117189251Ssam
1118189251Ssamstruct tncc_data * tncc_init(void)
1119189251Ssam{
1120189251Ssam	struct tncc_data *tncc;
1121189251Ssam	struct tnc_if_imc *imc;
1122189251Ssam
1123189251Ssam	tncc = os_zalloc(sizeof(*tncc));
1124189251Ssam	if (tncc == NULL)
1125189251Ssam		return NULL;
1126189251Ssam
1127189251Ssam	/* TODO:
1128189251Ssam	 * move loading and Initialize() to a location that is not
1129189251Ssam	 *    re-initialized for every EAP-TNC session (?)
1130189251Ssam	 */
1131189251Ssam
1132189251Ssam	if (tncc_read_config(tncc) < 0) {
1133189251Ssam		wpa_printf(MSG_ERROR, "TNC: Failed to read TNC configuration");
1134189251Ssam		goto failed;
1135189251Ssam	}
1136189251Ssam
1137189251Ssam	for (imc = tncc->imc; imc; imc = imc->next) {
1138189251Ssam		if (tncc_load_imc(imc)) {
1139189251Ssam			wpa_printf(MSG_ERROR, "TNC: Failed to load IMC '%s'",
1140189251Ssam				   imc->name);
1141189251Ssam			goto failed;
1142189251Ssam		}
1143189251Ssam	}
1144189251Ssam
1145189251Ssam	return tncc;
1146189251Ssam
1147189251Ssamfailed:
1148189251Ssam	tncc_deinit(tncc);
1149189251Ssam	return NULL;
1150189251Ssam}
1151189251Ssam
1152189251Ssam
1153189251Ssamvoid tncc_deinit(struct tncc_data *tncc)
1154189251Ssam{
1155189251Ssam	struct tnc_if_imc *imc, *prev;
1156189251Ssam
1157189251Ssam	imc = tncc->imc;
1158189251Ssam	while (imc) {
1159189251Ssam		tncc_unload_imc(imc);
1160189251Ssam
1161189251Ssam		prev = imc;
1162189251Ssam		imc = imc->next;
1163189251Ssam		os_free(prev);
1164189251Ssam	}
1165189251Ssam
1166189251Ssam	os_free(tncc);
1167189251Ssam}
1168189251Ssam
1169189251Ssam
1170189251Ssamstatic struct wpabuf * tncc_build_soh(int ver)
1171189251Ssam{
1172189251Ssam	struct wpabuf *buf;
1173189251Ssam	u8 *tlv_len, *tlv_len2, *outer_len, *inner_len, *ssoh_len, *end;
1174189251Ssam	u8 correlation_id[24];
1175189251Ssam	/* TODO: get correct name */
1176189251Ssam	char *machinename = "wpa_supplicant@w1.fi";
1177189251Ssam
1178189251Ssam	if (os_get_random(correlation_id, sizeof(correlation_id)))
1179189251Ssam		return NULL;
1180189251Ssam	wpa_hexdump(MSG_DEBUG, "TNC: SoH Correlation ID",
1181189251Ssam		    correlation_id, sizeof(correlation_id));
1182189251Ssam
1183189251Ssam	buf = wpabuf_alloc(200);
1184189251Ssam	if (buf == NULL)
1185189251Ssam		return NULL;
1186189251Ssam
1187189251Ssam	/* Vendor-Specific TLV (Microsoft) - SoH */
1188189251Ssam	wpabuf_put_be16(buf, EAP_TLV_VENDOR_SPECIFIC_TLV); /* TLV Type */
1189189251Ssam	tlv_len = wpabuf_put(buf, 2); /* Length */
1190189251Ssam	wpabuf_put_be32(buf, EAP_VENDOR_MICROSOFT); /* Vendor_Id */
1191189251Ssam	wpabuf_put_be16(buf, 0x01); /* TLV Type - SoH TLV */
1192189251Ssam	tlv_len2 = wpabuf_put(buf, 2); /* Length */
1193189251Ssam
1194189251Ssam	/* SoH Header */
1195189251Ssam	wpabuf_put_be16(buf, EAP_TLV_VENDOR_SPECIFIC_TLV); /* Outer Type */
1196189251Ssam	outer_len = wpabuf_put(buf, 2);
1197189251Ssam	wpabuf_put_be32(buf, EAP_VENDOR_MICROSOFT); /* IANA SMI Code */
1198189251Ssam	wpabuf_put_be16(buf, ver); /* Inner Type */
1199189251Ssam	inner_len = wpabuf_put(buf, 2);
1200189251Ssam
1201189251Ssam	if (ver == 2) {
1202189251Ssam		/* SoH Mode Sub-Header */
1203189251Ssam		/* Outer Type */
1204189251Ssam		wpabuf_put_be16(buf, EAP_TLV_VENDOR_SPECIFIC_TLV);
1205189251Ssam		wpabuf_put_be16(buf, 4 + 24 + 1 + 1); /* Length */
1206189251Ssam		wpabuf_put_be32(buf, EAP_VENDOR_MICROSOFT); /* IANA SMI Code */
1207189251Ssam		/* Value: */
1208189251Ssam		wpabuf_put_data(buf, correlation_id, sizeof(correlation_id));
1209189251Ssam		wpabuf_put_u8(buf, 0x01); /* Intent Flag - Request */
1210189251Ssam		wpabuf_put_u8(buf, 0x00); /* Content-Type Flag */
1211189251Ssam	}
1212189251Ssam
1213189251Ssam	/* SSoH TLV */
1214189251Ssam	/* System-Health-Id */
1215189251Ssam	wpabuf_put_be16(buf, 0x0002); /* Type */
1216189251Ssam	wpabuf_put_be16(buf, 4); /* Length */
1217189251Ssam	wpabuf_put_be32(buf, 79616);
1218189251Ssam	/* Vendor-Specific Attribute */
1219189251Ssam	wpabuf_put_be16(buf, EAP_TLV_VENDOR_SPECIFIC_TLV);
1220189251Ssam	ssoh_len = wpabuf_put(buf, 2);
1221189251Ssam	wpabuf_put_be32(buf, EAP_VENDOR_MICROSOFT); /* IANA SMI Code */
1222189251Ssam
1223189251Ssam	/* MS-Packet-Info */
1224189251Ssam	wpabuf_put_u8(buf, SSOH_MS_PACKET_INFO);
1225189251Ssam	/* Note: IF-TNCCS-SOH v1.0 r8 claims this field to be:
1226189251Ssam	 * Reserved(4 bits) r(1 bit) Vers(3 bits), but Windows XP
1227189251Ssam	 * SP3 seems to be sending 0x11 for SSoH, i.e., r(request/response) bit
1228189251Ssam	 * would not be in the specified location.
1229189251Ssam	 * [MS-SOH] 4.0.2: Reserved(3 bits) r(1 bit) Vers(4 bits)
1230189251Ssam	 */
1231189251Ssam	wpabuf_put_u8(buf, 0x11); /* r=request, vers=1 */
1232189251Ssam
1233189251Ssam	/* MS-Machine-Inventory */
1234189251Ssam	/* TODO: get correct values; 0 = not applicable for OS */
1235189251Ssam	wpabuf_put_u8(buf, SSOH_MS_MACHINE_INVENTORY);
1236189251Ssam	wpabuf_put_be32(buf, 0); /* osVersionMajor */
1237189251Ssam	wpabuf_put_be32(buf, 0); /* osVersionMinor */
1238189251Ssam	wpabuf_put_be32(buf, 0); /* osVersionBuild */
1239189251Ssam	wpabuf_put_be16(buf, 0); /* spVersionMajor */
1240189251Ssam	wpabuf_put_be16(buf, 0); /* spVersionMinor */
1241189251Ssam	wpabuf_put_be16(buf, 0); /* procArch */
1242189251Ssam
1243189251Ssam	/* MS-MachineName */
1244189251Ssam	wpabuf_put_u8(buf, SSOH_MS_MACHINENAME);
1245189251Ssam	wpabuf_put_be16(buf, os_strlen(machinename) + 1);
1246189251Ssam	wpabuf_put_data(buf, machinename, os_strlen(machinename) + 1);
1247189251Ssam
1248189251Ssam	/* MS-CorrelationId */
1249189251Ssam	wpabuf_put_u8(buf, SSOH_MS_CORRELATIONID);
1250189251Ssam	wpabuf_put_data(buf, correlation_id, sizeof(correlation_id));
1251189251Ssam
1252189251Ssam	/* MS-Quarantine-State */
1253189251Ssam	wpabuf_put_u8(buf, SSOH_MS_QUARANTINE_STATE);
1254189251Ssam	wpabuf_put_be16(buf, 1); /* Flags: ExtState=0, f=0, qState=1 */
1255189251Ssam	wpabuf_put_be32(buf, 0xffffffff); /* ProbTime (hi) */
1256189251Ssam	wpabuf_put_be32(buf, 0xffffffff); /* ProbTime (lo) */
1257189251Ssam	wpabuf_put_be16(buf, 1); /* urlLenInBytes */
1258189251Ssam	wpabuf_put_u8(buf, 0); /* null termination for the url */
1259189251Ssam
1260189251Ssam	/* MS-Machine-Inventory-Ex */
1261189251Ssam	wpabuf_put_u8(buf, SSOH_MS_MACHINE_INVENTORY_EX);
1262189251Ssam	wpabuf_put_be32(buf, 0); /* Reserved
1263189251Ssam				  * (note: Windows XP SP3 uses 0xdecafbad) */
1264189251Ssam	wpabuf_put_u8(buf, 1); /* ProductType: Client */
1265189251Ssam
1266189251Ssam	/* Update SSoH Length */
1267189251Ssam	end = wpabuf_put(buf, 0);
1268189251Ssam	WPA_PUT_BE16(ssoh_len, end - ssoh_len - 2);
1269189251Ssam
1270189251Ssam	/* TODO: SoHReportEntry TLV (zero or more) */
1271189251Ssam
1272189251Ssam	/* Update length fields */
1273189251Ssam	end = wpabuf_put(buf, 0);
1274189251Ssam	WPA_PUT_BE16(tlv_len, end - tlv_len - 2);
1275189251Ssam	WPA_PUT_BE16(tlv_len2, end - tlv_len2 - 2);
1276189251Ssam	WPA_PUT_BE16(outer_len, end - outer_len - 2);
1277189251Ssam	WPA_PUT_BE16(inner_len, end - inner_len - 2);
1278189251Ssam
1279189251Ssam	return buf;
1280189251Ssam}
1281189251Ssam
1282189251Ssam
1283189251Ssamstruct wpabuf * tncc_process_soh_request(int ver, const u8 *data, size_t len)
1284189251Ssam{
1285189251Ssam	const u8 *pos;
1286189251Ssam
1287189251Ssam	wpa_hexdump(MSG_DEBUG, "TNC: SoH Request", data, len);
1288189251Ssam
1289189251Ssam	if (len < 12)
1290189251Ssam		return NULL;
1291189251Ssam
1292189251Ssam	/* SoH Request */
1293189251Ssam	pos = data;
1294189251Ssam
1295189251Ssam	/* TLV Type */
1296189251Ssam	if (WPA_GET_BE16(pos) != EAP_TLV_VENDOR_SPECIFIC_TLV)
1297189251Ssam		return NULL;
1298189251Ssam	pos += 2;
1299189251Ssam
1300189251Ssam	/* Length */
1301189251Ssam	if (WPA_GET_BE16(pos) < 8)
1302189251Ssam		return NULL;
1303189251Ssam	pos += 2;
1304189251Ssam
1305189251Ssam	/* Vendor_Id */
1306189251Ssam	if (WPA_GET_BE32(pos) != EAP_VENDOR_MICROSOFT)
1307189251Ssam		return NULL;
1308189251Ssam	pos += 4;
1309189251Ssam
1310189251Ssam	/* TLV Type */
1311189251Ssam	if (WPA_GET_BE16(pos) != 0x02 /* SoH request TLV */)
1312189251Ssam		return NULL;
1313189251Ssam
1314189251Ssam	wpa_printf(MSG_DEBUG, "TNC: SoH Request TLV received");
1315189251Ssam
1316189251Ssam	return tncc_build_soh(2);
1317189251Ssam}
1318