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