1189251Ssam/*
2189251Ssam * EAP-TNC - TNCS (IF-IMV, IF-TNCCS, and IF-TNCCS-SOH)
3189251Ssam * Copyright (c) 2007-2008, 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#include <dlfcn.h>
11189251Ssam
12189251Ssam#include "common.h"
13189251Ssam#include "base64.h"
14189251Ssam#include "tncs.h"
15189251Ssam#include "eap_common/eap_tlv_common.h"
16189251Ssam#include "eap_common/eap_defs.h"
17189251Ssam
18189251Ssam
19189251Ssam/* TODO: TNCS must be thread-safe; review the code and add locking etc. if
20189251Ssam * needed.. */
21189251Ssam
22189251Ssam#define TNC_CONFIG_FILE "/etc/tnc_config"
23189251Ssam#define IF_TNCCS_START \
24189251Ssam"<?xml version=\"1.0\"?>\n" \
25189251Ssam"<TNCCS-Batch BatchId=\"%d\" Recipient=\"TNCS\" " \
26189251Ssam"xmlns=\"http://www.trustedcomputinggroup.org/IWG/TNC/1_0/IF_TNCCS#\" " \
27189251Ssam"xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " \
28189251Ssam"xsi:schemaLocation=\"http://www.trustedcomputinggroup.org/IWG/TNC/1_0/" \
29189251Ssam"IF_TNCCS# https://www.trustedcomputinggroup.org/XML/SCHEMA/TNCCS_1.0.xsd\">\n"
30189251Ssam#define IF_TNCCS_END "\n</TNCCS-Batch>"
31189251Ssam
32189251Ssam/* TNC IF-IMV */
33189251Ssam
34189251Ssamtypedef unsigned long TNC_UInt32;
35189251Ssamtypedef unsigned char *TNC_BufferReference;
36189251Ssam
37189251Ssamtypedef TNC_UInt32 TNC_IMVID;
38189251Ssamtypedef TNC_UInt32 TNC_ConnectionID;
39189251Ssamtypedef TNC_UInt32 TNC_ConnectionState;
40189251Ssamtypedef TNC_UInt32 TNC_RetryReason;
41189251Ssamtypedef TNC_UInt32 TNC_IMV_Action_Recommendation;
42189251Ssamtypedef TNC_UInt32 TNC_IMV_Evaluation_Result;
43189251Ssamtypedef TNC_UInt32 TNC_MessageType;
44189251Ssamtypedef TNC_MessageType *TNC_MessageTypeList;
45189251Ssamtypedef TNC_UInt32 TNC_VendorID;
46189251Ssamtypedef TNC_UInt32 TNC_Subtype;
47189251Ssamtypedef TNC_UInt32 TNC_Version;
48189251Ssamtypedef TNC_UInt32 TNC_Result;
49189251Ssamtypedef TNC_UInt32 TNC_AttributeID;
50189251Ssam
51189251Ssamtypedef TNC_Result (*TNC_TNCS_BindFunctionPointer)(
52189251Ssam	TNC_IMVID imvID,
53189251Ssam	char *functionName,
54189251Ssam	void **pOutfunctionPointer);
55189251Ssam
56189251Ssam#define TNC_RESULT_SUCCESS 0
57189251Ssam#define TNC_RESULT_NOT_INITIALIZED 1
58189251Ssam#define TNC_RESULT_ALREADY_INITIALIZED 2
59189251Ssam#define TNC_RESULT_NO_COMMON_VERSION 3
60189251Ssam#define TNC_RESULT_CANT_RETRY 4
61189251Ssam#define TNC_RESULT_WONT_RETRY 5
62189251Ssam#define TNC_RESULT_INVALID_PARAMETER 6
63189251Ssam#define TNC_RESULT_CANT_RESPOND 7
64189251Ssam#define TNC_RESULT_ILLEGAL_OPERATION 8
65189251Ssam#define TNC_RESULT_OTHER 9
66189251Ssam#define TNC_RESULT_FATAL 10
67189251Ssam
68189251Ssam#define TNC_CONNECTION_STATE_CREATE 0
69189251Ssam#define TNC_CONNECTION_STATE_HANDSHAKE 1
70189251Ssam#define TNC_CONNECTION_STATE_ACCESS_ALLOWED 2
71189251Ssam#define TNC_CONNECTION_STATE_ACCESS_ISOLATED 3
72189251Ssam#define TNC_CONNECTION_STATE_ACCESS_NONE 4
73189251Ssam#define TNC_CONNECTION_STATE_DELETE 5
74189251Ssam
75189251Ssam#define TNC_IFIMV_VERSION_1 1
76189251Ssam
77189251Ssam#define TNC_VENDORID_ANY ((TNC_VendorID) 0xffffff)
78189251Ssam#define TNC_SUBTYPE_ANY ((TNC_Subtype) 0xff)
79189251Ssam
80189251Ssam/* TNCC-TNCS Message Types */
81189251Ssam#define TNC_TNCCS_RECOMMENDATION		0x00000001
82189251Ssam#define TNC_TNCCS_ERROR				0x00000002
83189251Ssam#define TNC_TNCCS_PREFERREDLANGUAGE		0x00000003
84189251Ssam#define TNC_TNCCS_REASONSTRINGS			0x00000004
85189251Ssam
86189251Ssam/* Possible TNC_IMV_Action_Recommendation values: */
87189251Ssamenum IMV_Action_Recommendation {
88189251Ssam	TNC_IMV_ACTION_RECOMMENDATION_ALLOW,
89189251Ssam	TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS,
90189251Ssam	TNC_IMV_ACTION_RECOMMENDATION_ISOLATE,
91189251Ssam	TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION
92189251Ssam};
93189251Ssam
94189251Ssam/* Possible TNC_IMV_Evaluation_Result values: */
95189251Ssamenum IMV_Evaluation_Result {
96189251Ssam	TNC_IMV_EVALUATION_RESULT_COMPLIANT,
97189251Ssam	TNC_IMV_EVALUATION_RESULT_NONCOMPLIANT_MINOR,
98189251Ssam	TNC_IMV_EVALUATION_RESULT_NONCOMPLIANT_MAJOR,
99189251Ssam	TNC_IMV_EVALUATION_RESULT_ERROR,
100189251Ssam	TNC_IMV_EVALUATION_RESULT_DONT_KNOW
101189251Ssam};
102189251Ssam
103189251Ssamstruct tnc_if_imv {
104189251Ssam	struct tnc_if_imv *next;
105189251Ssam	char *name;
106189251Ssam	char *path;
107189251Ssam	void *dlhandle; /* from dlopen() */
108189251Ssam	TNC_IMVID imvID;
109189251Ssam	TNC_MessageTypeList supported_types;
110189251Ssam	size_t num_supported_types;
111189251Ssam
112189251Ssam	/* Functions implemented by IMVs (with TNC_IMV_ prefix) */
113189251Ssam	TNC_Result (*Initialize)(
114189251Ssam		TNC_IMVID imvID,
115189251Ssam		TNC_Version minVersion,
116189251Ssam		TNC_Version maxVersion,
117189251Ssam		TNC_Version *pOutActualVersion);
118189251Ssam	TNC_Result (*NotifyConnectionChange)(
119189251Ssam		TNC_IMVID imvID,
120189251Ssam		TNC_ConnectionID connectionID,
121189251Ssam		TNC_ConnectionState newState);
122189251Ssam	TNC_Result (*ReceiveMessage)(
123189251Ssam		TNC_IMVID imvID,
124189251Ssam		TNC_ConnectionID connectionID,
125189251Ssam		TNC_BufferReference message,
126189251Ssam		TNC_UInt32 messageLength,
127189251Ssam		TNC_MessageType messageType);
128189251Ssam	TNC_Result (*SolicitRecommendation)(
129189251Ssam		TNC_IMVID imvID,
130189251Ssam		TNC_ConnectionID connectionID);
131189251Ssam	TNC_Result (*BatchEnding)(
132189251Ssam		TNC_IMVID imvID,
133189251Ssam		TNC_ConnectionID connectionID);
134189251Ssam	TNC_Result (*Terminate)(TNC_IMVID imvID);
135189251Ssam	TNC_Result (*ProvideBindFunction)(
136189251Ssam		TNC_IMVID imvID,
137189251Ssam		TNC_TNCS_BindFunctionPointer bindFunction);
138189251Ssam};
139189251Ssam
140189251Ssam
141189251Ssam#define TNC_MAX_IMV_ID 10
142189251Ssam
143189251Ssamstruct tncs_data {
144189251Ssam	struct tncs_data *next;
145189251Ssam	struct tnc_if_imv *imv; /* local copy of tncs_global_data->imv */
146189251Ssam	TNC_ConnectionID connectionID;
147189251Ssam	unsigned int last_batchid;
148189251Ssam	enum IMV_Action_Recommendation recommendation;
149189251Ssam	int done;
150189251Ssam
151189251Ssam	struct conn_imv {
152189251Ssam		u8 *imv_send;
153189251Ssam		size_t imv_send_len;
154189251Ssam		enum IMV_Action_Recommendation recommendation;
155189251Ssam		int recommendation_set;
156189251Ssam	} imv_data[TNC_MAX_IMV_ID];
157189251Ssam
158189251Ssam	char *tncs_message;
159189251Ssam};
160189251Ssam
161189251Ssam
162189251Ssamstruct tncs_global {
163189251Ssam	struct tnc_if_imv *imv;
164189251Ssam	TNC_ConnectionID next_conn_id;
165189251Ssam	struct tncs_data *connections;
166189251Ssam};
167189251Ssam
168189251Ssamstatic struct tncs_global *tncs_global_data = NULL;
169189251Ssam
170189251Ssam
171189251Ssamstatic struct tnc_if_imv * tncs_get_imv(TNC_IMVID imvID)
172189251Ssam{
173189251Ssam	struct tnc_if_imv *imv;
174189251Ssam
175189251Ssam	if (imvID >= TNC_MAX_IMV_ID || tncs_global_data == NULL)
176189251Ssam		return NULL;
177189251Ssam	imv = tncs_global_data->imv;
178189251Ssam	while (imv) {
179189251Ssam		if (imv->imvID == imvID)
180189251Ssam			return imv;
181189251Ssam		imv = imv->next;
182189251Ssam	}
183189251Ssam	return NULL;
184189251Ssam}
185189251Ssam
186189251Ssam
187189251Ssamstatic struct tncs_data * tncs_get_conn(TNC_ConnectionID connectionID)
188189251Ssam{
189189251Ssam	struct tncs_data *tncs;
190189251Ssam
191189251Ssam	if (tncs_global_data == NULL)
192189251Ssam		return NULL;
193189251Ssam
194189251Ssam	tncs = tncs_global_data->connections;
195189251Ssam	while (tncs) {
196189251Ssam		if (tncs->connectionID == connectionID)
197189251Ssam			return tncs;
198189251Ssam		tncs = tncs->next;
199189251Ssam	}
200189251Ssam
201189251Ssam	wpa_printf(MSG_DEBUG, "TNC: Connection ID %lu not found",
202189251Ssam		   (unsigned long) connectionID);
203189251Ssam
204189251Ssam	return NULL;
205189251Ssam}
206189251Ssam
207189251Ssam
208189251Ssam/* TNCS functions that IMVs can call */
209189251SsamTNC_Result TNC_TNCS_ReportMessageTypes(
210189251Ssam	TNC_IMVID imvID,
211189251Ssam	TNC_MessageTypeList supportedTypes,
212189251Ssam	TNC_UInt32 typeCount)
213189251Ssam{
214189251Ssam	TNC_UInt32 i;
215189251Ssam	struct tnc_if_imv *imv;
216189251Ssam
217189251Ssam	wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_ReportMessageTypes(imvID=%lu "
218189251Ssam		   "typeCount=%lu)",
219189251Ssam		   (unsigned long) imvID, (unsigned long) typeCount);
220189251Ssam
221189251Ssam	for (i = 0; i < typeCount; i++) {
222189251Ssam		wpa_printf(MSG_DEBUG, "TNC: supportedTypes[%lu] = %lu",
223189251Ssam			   i, supportedTypes[i]);
224189251Ssam	}
225189251Ssam
226189251Ssam	imv = tncs_get_imv(imvID);
227189251Ssam	if (imv == NULL)
228189251Ssam		return TNC_RESULT_INVALID_PARAMETER;
229189251Ssam	os_free(imv->supported_types);
230189251Ssam	imv->supported_types =
231252726Srpaulo		os_malloc(typeCount * sizeof(TNC_MessageType));
232189251Ssam	if (imv->supported_types == NULL)
233189251Ssam		return TNC_RESULT_FATAL;
234189251Ssam	os_memcpy(imv->supported_types, supportedTypes,
235252726Srpaulo		  typeCount * sizeof(TNC_MessageType));
236189251Ssam	imv->num_supported_types = typeCount;
237189251Ssam
238189251Ssam	return TNC_RESULT_SUCCESS;
239189251Ssam}
240189251Ssam
241189251Ssam
242189251SsamTNC_Result TNC_TNCS_SendMessage(
243189251Ssam	TNC_IMVID imvID,
244189251Ssam	TNC_ConnectionID connectionID,
245189251Ssam	TNC_BufferReference message,
246189251Ssam	TNC_UInt32 messageLength,
247189251Ssam	TNC_MessageType messageType)
248189251Ssam{
249189251Ssam	struct tncs_data *tncs;
250189251Ssam	unsigned char *b64;
251189251Ssam	size_t b64len;
252189251Ssam
253189251Ssam	wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_SendMessage(imvID=%lu "
254189251Ssam		   "connectionID=%lu messageType=%lu)",
255189251Ssam		   imvID, connectionID, messageType);
256189251Ssam	wpa_hexdump_ascii(MSG_DEBUG, "TNC: TNC_TNCS_SendMessage",
257189251Ssam			  message, messageLength);
258189251Ssam
259189251Ssam	if (tncs_get_imv(imvID) == NULL)
260189251Ssam		return TNC_RESULT_INVALID_PARAMETER;
261189251Ssam
262189251Ssam	tncs = tncs_get_conn(connectionID);
263189251Ssam	if (tncs == NULL)
264189251Ssam		return TNC_RESULT_INVALID_PARAMETER;
265189251Ssam
266189251Ssam	b64 = base64_encode(message, messageLength, &b64len);
267189251Ssam	if (b64 == NULL)
268189251Ssam		return TNC_RESULT_FATAL;
269189251Ssam
270189251Ssam	os_free(tncs->imv_data[imvID].imv_send);
271189251Ssam	tncs->imv_data[imvID].imv_send_len = 0;
272189251Ssam	tncs->imv_data[imvID].imv_send = os_zalloc(b64len + 100);
273189251Ssam	if (tncs->imv_data[imvID].imv_send == NULL) {
274189251Ssam		os_free(b64);
275189251Ssam		return TNC_RESULT_OTHER;
276189251Ssam	}
277189251Ssam
278189251Ssam	tncs->imv_data[imvID].imv_send_len =
279189251Ssam		os_snprintf((char *) tncs->imv_data[imvID].imv_send,
280189251Ssam			    b64len + 100,
281189251Ssam			    "<IMC-IMV-Message><Type>%08X</Type>"
282189251Ssam			    "<Base64>%s</Base64></IMC-IMV-Message>",
283189251Ssam			    (unsigned int) messageType, b64);
284189251Ssam
285189251Ssam	os_free(b64);
286189251Ssam
287189251Ssam	return TNC_RESULT_SUCCESS;
288189251Ssam}
289189251Ssam
290189251Ssam
291189251SsamTNC_Result TNC_TNCS_RequestHandshakeRetry(
292189251Ssam	TNC_IMVID imvID,
293189251Ssam	TNC_ConnectionID connectionID,
294189251Ssam	TNC_RetryReason reason)
295189251Ssam{
296189251Ssam	wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_RequestHandshakeRetry");
297189251Ssam	/* TODO */
298189251Ssam	return TNC_RESULT_SUCCESS;
299189251Ssam}
300189251Ssam
301189251Ssam
302189251SsamTNC_Result TNC_TNCS_ProvideRecommendation(
303189251Ssam	TNC_IMVID imvID,
304189251Ssam	TNC_ConnectionID connectionID,
305189251Ssam	TNC_IMV_Action_Recommendation recommendation,
306189251Ssam	TNC_IMV_Evaluation_Result evaluation)
307189251Ssam{
308189251Ssam	struct tncs_data *tncs;
309189251Ssam
310189251Ssam	wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_ProvideRecommendation(imvID=%lu "
311189251Ssam		   "connectionID=%lu recommendation=%lu evaluation=%lu)",
312189251Ssam		   (unsigned long) imvID, (unsigned long) connectionID,
313189251Ssam		   (unsigned long) recommendation, (unsigned long) evaluation);
314189251Ssam
315189251Ssam	if (tncs_get_imv(imvID) == NULL)
316189251Ssam		return TNC_RESULT_INVALID_PARAMETER;
317189251Ssam
318189251Ssam	tncs = tncs_get_conn(connectionID);
319189251Ssam	if (tncs == NULL)
320189251Ssam		return TNC_RESULT_INVALID_PARAMETER;
321189251Ssam
322189251Ssam	tncs->imv_data[imvID].recommendation = recommendation;
323189251Ssam	tncs->imv_data[imvID].recommendation_set = 1;
324189251Ssam
325189251Ssam	return TNC_RESULT_SUCCESS;
326189251Ssam}
327189251Ssam
328189251Ssam
329189251SsamTNC_Result TNC_TNCS_GetAttribute(
330189251Ssam	TNC_IMVID imvID,
331189251Ssam	TNC_ConnectionID connectionID,
332189251Ssam	TNC_AttributeID attribureID,
333189251Ssam	TNC_UInt32 bufferLength,
334189251Ssam	TNC_BufferReference buffer,
335189251Ssam	TNC_UInt32 *pOutValueLength)
336189251Ssam{
337189251Ssam	wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_GetAttribute");
338189251Ssam	/* TODO */
339189251Ssam	return TNC_RESULT_SUCCESS;
340189251Ssam}
341189251Ssam
342189251Ssam
343189251SsamTNC_Result TNC_TNCS_SetAttribute(
344189251Ssam	TNC_IMVID imvID,
345189251Ssam	TNC_ConnectionID connectionID,
346189251Ssam	TNC_AttributeID attribureID,
347189251Ssam	TNC_UInt32 bufferLength,
348189251Ssam	TNC_BufferReference buffer)
349189251Ssam{
350189251Ssam	wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_SetAttribute");
351189251Ssam	/* TODO */
352189251Ssam	return TNC_RESULT_SUCCESS;
353189251Ssam}
354189251Ssam
355189251Ssam
356189251SsamTNC_Result TNC_TNCS_BindFunction(
357189251Ssam	TNC_IMVID imvID,
358189251Ssam	char *functionName,
359189251Ssam	void **pOutFunctionPointer)
360189251Ssam{
361189251Ssam	wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_BindFunction(imcID=%lu, "
362189251Ssam		   "functionName='%s')", (unsigned long) imvID, functionName);
363189251Ssam
364189251Ssam	if (tncs_get_imv(imvID) == NULL)
365189251Ssam		return TNC_RESULT_INVALID_PARAMETER;
366189251Ssam
367189251Ssam	if (pOutFunctionPointer == NULL)
368189251Ssam		return TNC_RESULT_INVALID_PARAMETER;
369189251Ssam
370189251Ssam	if (os_strcmp(functionName, "TNC_TNCS_ReportMessageTypes") == 0)
371189251Ssam		*pOutFunctionPointer = TNC_TNCS_ReportMessageTypes;
372189251Ssam	else if (os_strcmp(functionName, "TNC_TNCS_SendMessage") == 0)
373189251Ssam		*pOutFunctionPointer = TNC_TNCS_SendMessage;
374189251Ssam	else if (os_strcmp(functionName, "TNC_TNCS_RequestHandshakeRetry") ==
375189251Ssam		 0)
376189251Ssam		*pOutFunctionPointer = TNC_TNCS_RequestHandshakeRetry;
377189251Ssam	else if (os_strcmp(functionName, "TNC_TNCS_ProvideRecommendation") ==
378189251Ssam		 0)
379189251Ssam		*pOutFunctionPointer = TNC_TNCS_ProvideRecommendation;
380189251Ssam	else if (os_strcmp(functionName, "TNC_TNCS_GetAttribute") == 0)
381189251Ssam		*pOutFunctionPointer = TNC_TNCS_GetAttribute;
382189251Ssam	else if (os_strcmp(functionName, "TNC_TNCS_SetAttribute") == 0)
383189251Ssam		*pOutFunctionPointer = TNC_TNCS_SetAttribute;
384189251Ssam	else
385189251Ssam		*pOutFunctionPointer = NULL;
386189251Ssam
387189251Ssam	return TNC_RESULT_SUCCESS;
388189251Ssam}
389189251Ssam
390189251Ssam
391189251Ssamstatic void * tncs_get_sym(void *handle, char *func)
392189251Ssam{
393189251Ssam	void *fptr;
394189251Ssam
395189251Ssam	fptr = dlsym(handle, func);
396189251Ssam
397189251Ssam	return fptr;
398189251Ssam}
399189251Ssam
400189251Ssam
401189251Ssamstatic int tncs_imv_resolve_funcs(struct tnc_if_imv *imv)
402189251Ssam{
403189251Ssam	void *handle = imv->dlhandle;
404189251Ssam
405189251Ssam	/* Mandatory IMV functions */
406189251Ssam	imv->Initialize = tncs_get_sym(handle, "TNC_IMV_Initialize");
407189251Ssam	if (imv->Initialize == NULL) {
408189251Ssam		wpa_printf(MSG_ERROR, "TNC: IMV does not export "
409189251Ssam			   "TNC_IMV_Initialize");
410189251Ssam		return -1;
411189251Ssam	}
412189251Ssam
413189251Ssam	imv->SolicitRecommendation = tncs_get_sym(
414189251Ssam		handle, "TNC_IMV_SolicitRecommendation");
415189251Ssam	if (imv->SolicitRecommendation == NULL) {
416189251Ssam		wpa_printf(MSG_ERROR, "TNC: IMV does not export "
417189251Ssam			   "TNC_IMV_SolicitRecommendation");
418189251Ssam		return -1;
419189251Ssam	}
420189251Ssam
421189251Ssam	imv->ProvideBindFunction =
422189251Ssam		tncs_get_sym(handle, "TNC_IMV_ProvideBindFunction");
423189251Ssam	if (imv->ProvideBindFunction == NULL) {
424189251Ssam		wpa_printf(MSG_ERROR, "TNC: IMV does not export "
425189251Ssam			   "TNC_IMV_ProvideBindFunction");
426189251Ssam		return -1;
427189251Ssam	}
428189251Ssam
429189251Ssam	/* Optional IMV functions */
430189251Ssam	imv->NotifyConnectionChange =
431189251Ssam		tncs_get_sym(handle, "TNC_IMV_NotifyConnectionChange");
432189251Ssam	imv->ReceiveMessage = tncs_get_sym(handle, "TNC_IMV_ReceiveMessage");
433189251Ssam	imv->BatchEnding = tncs_get_sym(handle, "TNC_IMV_BatchEnding");
434189251Ssam	imv->Terminate = tncs_get_sym(handle, "TNC_IMV_Terminate");
435189251Ssam
436189251Ssam	return 0;
437189251Ssam}
438189251Ssam
439189251Ssam
440189251Ssamstatic int tncs_imv_initialize(struct tnc_if_imv *imv)
441189251Ssam{
442189251Ssam	TNC_Result res;
443189251Ssam	TNC_Version imv_ver;
444189251Ssam
445189251Ssam	wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMV_Initialize for IMV '%s'",
446189251Ssam		   imv->name);
447189251Ssam	res = imv->Initialize(imv->imvID, TNC_IFIMV_VERSION_1,
448189251Ssam			      TNC_IFIMV_VERSION_1, &imv_ver);
449189251Ssam	wpa_printf(MSG_DEBUG, "TNC: TNC_IMV_Initialize: res=%lu imv_ver=%lu",
450189251Ssam		   (unsigned long) res, (unsigned long) imv_ver);
451189251Ssam
452189251Ssam	return res == TNC_RESULT_SUCCESS ? 0 : -1;
453189251Ssam}
454189251Ssam
455189251Ssam
456189251Ssamstatic int tncs_imv_terminate(struct tnc_if_imv *imv)
457189251Ssam{
458189251Ssam	TNC_Result res;
459189251Ssam
460189251Ssam	if (imv->Terminate == NULL)
461189251Ssam		return 0;
462189251Ssam
463189251Ssam	wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMV_Terminate for IMV '%s'",
464189251Ssam		   imv->name);
465189251Ssam	res = imv->Terminate(imv->imvID);
466189251Ssam	wpa_printf(MSG_DEBUG, "TNC: TNC_IMV_Terminate: %lu",
467189251Ssam		   (unsigned long) res);
468189251Ssam
469189251Ssam	return res == TNC_RESULT_SUCCESS ? 0 : -1;
470189251Ssam}
471189251Ssam
472189251Ssam
473189251Ssamstatic int tncs_imv_provide_bind_function(struct tnc_if_imv *imv)
474189251Ssam{
475189251Ssam	TNC_Result res;
476189251Ssam
477189251Ssam	wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMV_ProvideBindFunction for "
478189251Ssam		   "IMV '%s'", imv->name);
479189251Ssam	res = imv->ProvideBindFunction(imv->imvID, TNC_TNCS_BindFunction);
480189251Ssam	wpa_printf(MSG_DEBUG, "TNC: TNC_IMV_ProvideBindFunction: res=%lu",
481189251Ssam		   (unsigned long) res);
482189251Ssam
483189251Ssam	return res == TNC_RESULT_SUCCESS ? 0 : -1;
484189251Ssam}
485189251Ssam
486189251Ssam
487189251Ssamstatic int tncs_imv_notify_connection_change(struct tnc_if_imv *imv,
488189251Ssam					     TNC_ConnectionID conn,
489189251Ssam					     TNC_ConnectionState state)
490189251Ssam{
491189251Ssam	TNC_Result res;
492189251Ssam
493189251Ssam	if (imv->NotifyConnectionChange == NULL)
494189251Ssam		return 0;
495189251Ssam
496189251Ssam	wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMV_NotifyConnectionChange(%d)"
497189251Ssam		   " for IMV '%s'", (int) state, imv->name);
498189251Ssam	res = imv->NotifyConnectionChange(imv->imvID, conn, state);
499189251Ssam	wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_NotifyConnectionChange: %lu",
500189251Ssam		   (unsigned long) res);
501189251Ssam
502189251Ssam	return res == TNC_RESULT_SUCCESS ? 0 : -1;
503189251Ssam}
504189251Ssam
505189251Ssam
506189251Ssamstatic int tncs_load_imv(struct tnc_if_imv *imv)
507189251Ssam{
508189251Ssam	if (imv->path == NULL) {
509189251Ssam		wpa_printf(MSG_DEBUG, "TNC: No IMV configured");
510189251Ssam		return -1;
511189251Ssam	}
512189251Ssam
513189251Ssam	wpa_printf(MSG_DEBUG, "TNC: Opening IMV: %s (%s)",
514189251Ssam		   imv->name, imv->path);
515189251Ssam	imv->dlhandle = dlopen(imv->path, RTLD_LAZY);
516189251Ssam	if (imv->dlhandle == NULL) {
517189251Ssam		wpa_printf(MSG_ERROR, "TNC: Failed to open IMV '%s' (%s): %s",
518189251Ssam			   imv->name, imv->path, dlerror());
519189251Ssam		return -1;
520189251Ssam	}
521189251Ssam
522189251Ssam	if (tncs_imv_resolve_funcs(imv) < 0) {
523189251Ssam		wpa_printf(MSG_ERROR, "TNC: Failed to resolve IMV functions");
524189251Ssam		return -1;
525189251Ssam	}
526189251Ssam
527189251Ssam	if (tncs_imv_initialize(imv) < 0 ||
528189251Ssam	    tncs_imv_provide_bind_function(imv) < 0) {
529189251Ssam		wpa_printf(MSG_ERROR, "TNC: Failed to initialize IMV");
530189251Ssam		return -1;
531189251Ssam	}
532189251Ssam
533189251Ssam	return 0;
534189251Ssam}
535189251Ssam
536189251Ssam
537189251Ssamstatic void tncs_free_imv(struct tnc_if_imv *imv)
538189251Ssam{
539189251Ssam	os_free(imv->name);
540189251Ssam	os_free(imv->path);
541189251Ssam	os_free(imv->supported_types);
542189251Ssam}
543189251Ssam
544189251Ssamstatic void tncs_unload_imv(struct tnc_if_imv *imv)
545189251Ssam{
546189251Ssam	tncs_imv_terminate(imv);
547189251Ssam
548189251Ssam	if (imv->dlhandle)
549189251Ssam		dlclose(imv->dlhandle);
550189251Ssam
551189251Ssam	tncs_free_imv(imv);
552189251Ssam}
553189251Ssam
554189251Ssam
555189251Ssamstatic int tncs_supported_type(struct tnc_if_imv *imv, unsigned int type)
556189251Ssam{
557189251Ssam	size_t i;
558189251Ssam	unsigned int vendor, subtype;
559189251Ssam
560189251Ssam	if (imv == NULL || imv->supported_types == NULL)
561189251Ssam		return 0;
562189251Ssam
563189251Ssam	vendor = type >> 8;
564189251Ssam	subtype = type & 0xff;
565189251Ssam
566189251Ssam	for (i = 0; i < imv->num_supported_types; i++) {
567189251Ssam		unsigned int svendor, ssubtype;
568189251Ssam		svendor = imv->supported_types[i] >> 8;
569189251Ssam		ssubtype = imv->supported_types[i] & 0xff;
570189251Ssam		if ((vendor == svendor || svendor == TNC_VENDORID_ANY) &&
571189251Ssam		    (subtype == ssubtype || ssubtype == TNC_SUBTYPE_ANY))
572189251Ssam			return 1;
573189251Ssam	}
574189251Ssam
575189251Ssam	return 0;
576189251Ssam}
577189251Ssam
578189251Ssam
579189251Ssamstatic void tncs_send_to_imvs(struct tncs_data *tncs, unsigned int type,
580189251Ssam			      const u8 *msg, size_t len)
581189251Ssam{
582189251Ssam	struct tnc_if_imv *imv;
583189251Ssam	TNC_Result res;
584189251Ssam
585189251Ssam	wpa_hexdump_ascii(MSG_MSGDUMP, "TNC: Message to IMV(s)", msg, len);
586189251Ssam
587189251Ssam	for (imv = tncs->imv; imv; imv = imv->next) {
588189251Ssam		if (imv->ReceiveMessage == NULL ||
589189251Ssam		    !tncs_supported_type(imv, type))
590189251Ssam			continue;
591189251Ssam
592189251Ssam		wpa_printf(MSG_DEBUG, "TNC: Call ReceiveMessage for IMV '%s'",
593189251Ssam			   imv->name);
594189251Ssam		res = imv->ReceiveMessage(imv->imvID, tncs->connectionID,
595189251Ssam					  (TNC_BufferReference) msg, len,
596189251Ssam					  type);
597189251Ssam		wpa_printf(MSG_DEBUG, "TNC: ReceiveMessage: %lu",
598189251Ssam			   (unsigned long) res);
599189251Ssam	}
600189251Ssam}
601189251Ssam
602189251Ssam
603189251Ssamstatic void tncs_batch_ending(struct tncs_data *tncs)
604189251Ssam{
605189251Ssam	struct tnc_if_imv *imv;
606189251Ssam	TNC_Result res;
607189251Ssam
608189251Ssam	for (imv = tncs->imv; imv; imv = imv->next) {
609189251Ssam		if (imv->BatchEnding == NULL)
610189251Ssam			continue;
611189251Ssam
612189251Ssam		wpa_printf(MSG_DEBUG, "TNC: Call BatchEnding for IMV '%s'",
613189251Ssam			   imv->name);
614189251Ssam		res = imv->BatchEnding(imv->imvID, tncs->connectionID);
615189251Ssam		wpa_printf(MSG_DEBUG, "TNC: BatchEnding: %lu",
616189251Ssam			   (unsigned long) res);
617189251Ssam	}
618189251Ssam}
619189251Ssam
620189251Ssam
621189251Ssamstatic void tncs_solicit_recommendation(struct tncs_data *tncs)
622189251Ssam{
623189251Ssam	struct tnc_if_imv *imv;
624189251Ssam	TNC_Result res;
625189251Ssam
626189251Ssam	for (imv = tncs->imv; imv; imv = imv->next) {
627189251Ssam		if (tncs->imv_data[imv->imvID].recommendation_set)
628189251Ssam			continue;
629189251Ssam
630189251Ssam		wpa_printf(MSG_DEBUG, "TNC: Call SolicitRecommendation for "
631189251Ssam			   "IMV '%s'", imv->name);
632189251Ssam		res = imv->SolicitRecommendation(imv->imvID,
633189251Ssam						 tncs->connectionID);
634189251Ssam		wpa_printf(MSG_DEBUG, "TNC: SolicitRecommendation: %lu",
635189251Ssam			   (unsigned long) res);
636189251Ssam	}
637189251Ssam}
638189251Ssam
639189251Ssam
640189251Ssamvoid tncs_init_connection(struct tncs_data *tncs)
641189251Ssam{
642189251Ssam	struct tnc_if_imv *imv;
643189251Ssam	int i;
644189251Ssam
645189251Ssam	for (imv = tncs->imv; imv; imv = imv->next) {
646189251Ssam		tncs_imv_notify_connection_change(
647189251Ssam			imv, tncs->connectionID, TNC_CONNECTION_STATE_CREATE);
648189251Ssam		tncs_imv_notify_connection_change(
649189251Ssam			imv, tncs->connectionID,
650189251Ssam			TNC_CONNECTION_STATE_HANDSHAKE);
651189251Ssam	}
652189251Ssam
653189251Ssam	for (i = 0; i < TNC_MAX_IMV_ID; i++) {
654189251Ssam		os_free(tncs->imv_data[i].imv_send);
655189251Ssam		tncs->imv_data[i].imv_send = NULL;
656189251Ssam		tncs->imv_data[i].imv_send_len = 0;
657189251Ssam	}
658189251Ssam}
659189251Ssam
660189251Ssam
661189251Ssamsize_t tncs_total_send_len(struct tncs_data *tncs)
662189251Ssam{
663189251Ssam	int i;
664189251Ssam	size_t len = 0;
665189251Ssam
666189251Ssam	for (i = 0; i < TNC_MAX_IMV_ID; i++)
667189251Ssam		len += tncs->imv_data[i].imv_send_len;
668189251Ssam	if (tncs->tncs_message)
669189251Ssam		len += os_strlen(tncs->tncs_message);
670189251Ssam	return len;
671189251Ssam}
672189251Ssam
673189251Ssam
674189251Ssamu8 * tncs_copy_send_buf(struct tncs_data *tncs, u8 *pos)
675189251Ssam{
676189251Ssam	int i;
677189251Ssam
678189251Ssam	for (i = 0; i < TNC_MAX_IMV_ID; i++) {
679189251Ssam		if (tncs->imv_data[i].imv_send == NULL)
680189251Ssam			continue;
681189251Ssam
682189251Ssam		os_memcpy(pos, tncs->imv_data[i].imv_send,
683189251Ssam			  tncs->imv_data[i].imv_send_len);
684189251Ssam		pos += tncs->imv_data[i].imv_send_len;
685189251Ssam		os_free(tncs->imv_data[i].imv_send);
686189251Ssam		tncs->imv_data[i].imv_send = NULL;
687189251Ssam		tncs->imv_data[i].imv_send_len = 0;
688189251Ssam	}
689189251Ssam
690189251Ssam	if (tncs->tncs_message) {
691189251Ssam		size_t len = os_strlen(tncs->tncs_message);
692189251Ssam		os_memcpy(pos, tncs->tncs_message, len);
693189251Ssam		pos += len;
694189251Ssam		os_free(tncs->tncs_message);
695189251Ssam		tncs->tncs_message = NULL;
696189251Ssam	}
697189251Ssam
698189251Ssam	return pos;
699189251Ssam}
700189251Ssam
701189251Ssam
702189251Ssamchar * tncs_if_tnccs_start(struct tncs_data *tncs)
703189251Ssam{
704189251Ssam	char *buf = os_malloc(1000);
705189251Ssam	if (buf == NULL)
706189251Ssam		return NULL;
707189251Ssam	tncs->last_batchid++;
708189251Ssam	os_snprintf(buf, 1000, IF_TNCCS_START, tncs->last_batchid);
709189251Ssam	return buf;
710189251Ssam}
711189251Ssam
712189251Ssam
713189251Ssamchar * tncs_if_tnccs_end(void)
714189251Ssam{
715189251Ssam	char *buf = os_malloc(100);
716189251Ssam	if (buf == NULL)
717189251Ssam		return NULL;
718189251Ssam	os_snprintf(buf, 100, IF_TNCCS_END);
719189251Ssam	return buf;
720189251Ssam}
721189251Ssam
722189251Ssam
723189251Ssamstatic int tncs_get_type(char *start, unsigned int *type)
724189251Ssam{
725189251Ssam	char *pos = os_strstr(start, "<Type>");
726189251Ssam	if (pos == NULL)
727189251Ssam		return -1;
728189251Ssam	pos += 6;
729189251Ssam	*type = strtoul(pos, NULL, 16);
730189251Ssam	return 0;
731189251Ssam}
732189251Ssam
733189251Ssam
734189251Ssamstatic unsigned char * tncs_get_base64(char *start, size_t *decoded_len)
735189251Ssam{
736189251Ssam	char *pos, *pos2;
737189251Ssam	unsigned char *decoded;
738189251Ssam
739189251Ssam	pos = os_strstr(start, "<Base64>");
740189251Ssam	if (pos == NULL)
741189251Ssam		return NULL;
742189251Ssam
743189251Ssam	pos += 8;
744189251Ssam	pos2 = os_strstr(pos, "</Base64>");
745189251Ssam	if (pos2 == NULL)
746189251Ssam		return NULL;
747189251Ssam	*pos2 = '\0';
748189251Ssam
749189251Ssam	decoded = base64_decode((unsigned char *) pos, os_strlen(pos),
750189251Ssam				decoded_len);
751189251Ssam	*pos2 = '<';
752189251Ssam	if (decoded == NULL) {
753189251Ssam		wpa_printf(MSG_DEBUG, "TNC: Failed to decode Base64 data");
754189251Ssam	}
755189251Ssam
756189251Ssam	return decoded;
757189251Ssam}
758189251Ssam
759189251Ssam
760189251Ssamstatic enum tncs_process_res tncs_derive_recommendation(struct tncs_data *tncs)
761189251Ssam{
762189251Ssam	enum IMV_Action_Recommendation rec;
763189251Ssam	struct tnc_if_imv *imv;
764189251Ssam	TNC_ConnectionState state;
765189251Ssam	char *txt;
766189251Ssam
767189251Ssam	wpa_printf(MSG_DEBUG, "TNC: No more messages from IMVs");
768189251Ssam
769189251Ssam	if (tncs->done)
770189251Ssam		return TNCCS_PROCESS_OK_NO_RECOMMENDATION;
771189251Ssam
772189251Ssam	tncs_solicit_recommendation(tncs);
773189251Ssam
774189251Ssam	/* Select the most restrictive recommendation */
775189251Ssam	rec = TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION;
776189251Ssam	for (imv = tncs->imv; imv; imv = imv->next) {
777189251Ssam		TNC_IMV_Action_Recommendation irec;
778189251Ssam		irec = tncs->imv_data[imv->imvID].recommendation;
779189251Ssam		if (irec == TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS)
780189251Ssam			rec = TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS;
781189251Ssam		if (irec == TNC_IMV_ACTION_RECOMMENDATION_ISOLATE &&
782189251Ssam		    rec != TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS)
783189251Ssam			rec = TNC_IMV_ACTION_RECOMMENDATION_ISOLATE;
784189251Ssam		if (irec == TNC_IMV_ACTION_RECOMMENDATION_ALLOW &&
785189251Ssam		    rec == TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION)
786189251Ssam			rec = TNC_IMV_ACTION_RECOMMENDATION_ALLOW;
787189251Ssam	}
788189251Ssam
789189251Ssam	wpa_printf(MSG_DEBUG, "TNC: Recommendation: %d", rec);
790189251Ssam	tncs->recommendation = rec;
791189251Ssam	tncs->done = 1;
792189251Ssam
793189251Ssam	txt = NULL;
794189251Ssam	switch (rec) {
795189251Ssam	case TNC_IMV_ACTION_RECOMMENDATION_ALLOW:
796189251Ssam	case TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION:
797189251Ssam		txt = "allow";
798189251Ssam		state = TNC_CONNECTION_STATE_ACCESS_ALLOWED;
799189251Ssam		break;
800189251Ssam	case TNC_IMV_ACTION_RECOMMENDATION_ISOLATE:
801189251Ssam		txt = "isolate";
802189251Ssam		state = TNC_CONNECTION_STATE_ACCESS_ISOLATED;
803189251Ssam		break;
804189251Ssam	case TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS:
805189251Ssam		txt = "none";
806189251Ssam		state = TNC_CONNECTION_STATE_ACCESS_NONE;
807189251Ssam		break;
808189251Ssam	default:
809189251Ssam		state = TNC_CONNECTION_STATE_ACCESS_ALLOWED;
810189251Ssam		break;
811189251Ssam	}
812189251Ssam
813189251Ssam	if (txt) {
814189251Ssam		os_free(tncs->tncs_message);
815189251Ssam		tncs->tncs_message = os_zalloc(200);
816189251Ssam		if (tncs->tncs_message) {
817189251Ssam			os_snprintf(tncs->tncs_message, 199,
818189251Ssam				    "<TNCC-TNCS-Message><Type>%08X</Type>"
819189251Ssam				    "<XML><TNCCS-Recommendation type=\"%s\">"
820189251Ssam				    "</TNCCS-Recommendation></XML>"
821189251Ssam				    "</TNCC-TNCS-Message>",
822189251Ssam				    TNC_TNCCS_RECOMMENDATION, txt);
823189251Ssam		}
824189251Ssam	}
825189251Ssam
826189251Ssam	for (imv = tncs->imv; imv; imv = imv->next) {
827189251Ssam		tncs_imv_notify_connection_change(imv, tncs->connectionID,
828189251Ssam						  state);
829189251Ssam	}
830189251Ssam
831189251Ssam	switch (rec) {
832189251Ssam	case TNC_IMV_ACTION_RECOMMENDATION_ALLOW:
833189251Ssam		return TNCCS_RECOMMENDATION_ALLOW;
834189251Ssam	case TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS:
835189251Ssam		return TNCCS_RECOMMENDATION_NO_ACCESS;
836189251Ssam	case TNC_IMV_ACTION_RECOMMENDATION_ISOLATE:
837189251Ssam		return TNCCS_RECOMMENDATION_ISOLATE;
838189251Ssam	case TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION:
839189251Ssam		return TNCCS_RECOMMENDATION_NO_RECOMMENDATION;
840189251Ssam	default:
841189251Ssam		return TNCCS_PROCESS_ERROR;
842189251Ssam	}
843189251Ssam}
844189251Ssam
845189251Ssam
846189251Ssamenum tncs_process_res tncs_process_if_tnccs(struct tncs_data *tncs,
847189251Ssam					    const u8 *msg, size_t len)
848189251Ssam{
849189251Ssam	char *buf, *start, *end, *pos, *pos2, *payload;
850189251Ssam	unsigned int batch_id;
851189251Ssam	unsigned char *decoded;
852189251Ssam	size_t decoded_len;
853189251Ssam
854189251Ssam	buf = os_malloc(len + 1);
855189251Ssam	if (buf == NULL)
856189251Ssam		return TNCCS_PROCESS_ERROR;
857189251Ssam
858189251Ssam	os_memcpy(buf, msg, len);
859189251Ssam	buf[len] = '\0';
860189251Ssam	start = os_strstr(buf, "<TNCCS-Batch ");
861189251Ssam	end = os_strstr(buf, "</TNCCS-Batch>");
862189251Ssam	if (start == NULL || end == NULL || start > end) {
863189251Ssam		os_free(buf);
864189251Ssam		return TNCCS_PROCESS_ERROR;
865189251Ssam	}
866189251Ssam
867189251Ssam	start += 13;
868189251Ssam	while (*start == ' ')
869189251Ssam		start++;
870189251Ssam	*end = '\0';
871189251Ssam
872189251Ssam	pos = os_strstr(start, "BatchId=");
873189251Ssam	if (pos == NULL) {
874189251Ssam		os_free(buf);
875189251Ssam		return TNCCS_PROCESS_ERROR;
876189251Ssam	}
877189251Ssam
878189251Ssam	pos += 8;
879189251Ssam	if (*pos == '"')
880189251Ssam		pos++;
881189251Ssam	batch_id = atoi(pos);
882189251Ssam	wpa_printf(MSG_DEBUG, "TNC: Received IF-TNCCS BatchId=%u",
883189251Ssam		   batch_id);
884189251Ssam	if (batch_id != tncs->last_batchid + 1) {
885189251Ssam		wpa_printf(MSG_DEBUG, "TNC: Unexpected IF-TNCCS BatchId "
886189251Ssam			   "%u (expected %u)",
887189251Ssam			   batch_id, tncs->last_batchid + 1);
888189251Ssam		os_free(buf);
889189251Ssam		return TNCCS_PROCESS_ERROR;
890189251Ssam	}
891189251Ssam	tncs->last_batchid = batch_id;
892189251Ssam
893189251Ssam	while (*pos != '\0' && *pos != '>')
894189251Ssam		pos++;
895189251Ssam	if (*pos == '\0') {
896189251Ssam		os_free(buf);
897189251Ssam		return TNCCS_PROCESS_ERROR;
898189251Ssam	}
899189251Ssam	pos++;
900189251Ssam	payload = start;
901189251Ssam
902189251Ssam	/*
903189251Ssam	 * <IMC-IMV-Message>
904189251Ssam	 * <Type>01234567</Type>
905189251Ssam	 * <Base64>foo==</Base64>
906189251Ssam	 * </IMC-IMV-Message>
907189251Ssam	 */
908189251Ssam
909189251Ssam	while (*start) {
910189251Ssam		char *endpos;
911189251Ssam		unsigned int type;
912189251Ssam
913189251Ssam		pos = os_strstr(start, "<IMC-IMV-Message>");
914189251Ssam		if (pos == NULL)
915189251Ssam			break;
916189251Ssam		start = pos + 17;
917189251Ssam		end = os_strstr(start, "</IMC-IMV-Message>");
918189251Ssam		if (end == NULL)
919189251Ssam			break;
920189251Ssam		*end = '\0';
921189251Ssam		endpos = end;
922189251Ssam		end += 18;
923189251Ssam
924189251Ssam		if (tncs_get_type(start, &type) < 0) {
925189251Ssam			*endpos = '<';
926189251Ssam			start = end;
927189251Ssam			continue;
928189251Ssam		}
929189251Ssam		wpa_printf(MSG_DEBUG, "TNC: IMC-IMV-Message Type 0x%x", type);
930189251Ssam
931189251Ssam		decoded = tncs_get_base64(start, &decoded_len);
932189251Ssam		if (decoded == NULL) {
933189251Ssam			*endpos = '<';
934189251Ssam			start = end;
935189251Ssam			continue;
936189251Ssam		}
937189251Ssam
938189251Ssam		tncs_send_to_imvs(tncs, type, decoded, decoded_len);
939189251Ssam
940189251Ssam		os_free(decoded);
941189251Ssam
942189251Ssam		start = end;
943189251Ssam	}
944189251Ssam
945189251Ssam	/*
946189251Ssam	 * <TNCC-TNCS-Message>
947189251Ssam	 * <Type>01234567</Type>
948189251Ssam	 * <XML><TNCCS-Foo type="foo"></TNCCS-Foo></XML>
949189251Ssam	 * <Base64>foo==</Base64>
950189251Ssam	 * </TNCC-TNCS-Message>
951189251Ssam	 */
952189251Ssam
953189251Ssam	start = payload;
954189251Ssam	while (*start) {
955189251Ssam		unsigned int type;
956189251Ssam		char *xml, *xmlend, *endpos;
957189251Ssam
958189251Ssam		pos = os_strstr(start, "<TNCC-TNCS-Message>");
959189251Ssam		if (pos == NULL)
960189251Ssam			break;
961189251Ssam		start = pos + 19;
962189251Ssam		end = os_strstr(start, "</TNCC-TNCS-Message>");
963189251Ssam		if (end == NULL)
964189251Ssam			break;
965189251Ssam		*end = '\0';
966189251Ssam		endpos = end;
967189251Ssam		end += 20;
968189251Ssam
969189251Ssam		if (tncs_get_type(start, &type) < 0) {
970189251Ssam			*endpos = '<';
971189251Ssam			start = end;
972189251Ssam			continue;
973189251Ssam		}
974189251Ssam		wpa_printf(MSG_DEBUG, "TNC: TNCC-TNCS-Message Type 0x%x",
975189251Ssam			   type);
976189251Ssam
977189251Ssam		/* Base64 OR XML */
978189251Ssam		decoded = NULL;
979189251Ssam		xml = NULL;
980189251Ssam		xmlend = NULL;
981189251Ssam		pos = os_strstr(start, "<XML>");
982189251Ssam		if (pos) {
983189251Ssam			pos += 5;
984189251Ssam			pos2 = os_strstr(pos, "</XML>");
985189251Ssam			if (pos2 == NULL) {
986189251Ssam				*endpos = '<';
987189251Ssam				start = end;
988189251Ssam				continue;
989189251Ssam			}
990189251Ssam			xmlend = pos2;
991189251Ssam			xml = pos;
992189251Ssam		} else {
993189251Ssam			decoded = tncs_get_base64(start, &decoded_len);
994189251Ssam			if (decoded == NULL) {
995189251Ssam				*endpos = '<';
996189251Ssam				start = end;
997189251Ssam				continue;
998189251Ssam			}
999189251Ssam		}
1000189251Ssam
1001189251Ssam		if (decoded) {
1002189251Ssam			wpa_hexdump_ascii(MSG_MSGDUMP,
1003189251Ssam					  "TNC: TNCC-TNCS-Message Base64",
1004189251Ssam					  decoded, decoded_len);
1005189251Ssam			os_free(decoded);
1006189251Ssam		}
1007189251Ssam
1008189251Ssam		if (xml) {
1009189251Ssam			wpa_hexdump_ascii(MSG_MSGDUMP,
1010189251Ssam					  "TNC: TNCC-TNCS-Message XML",
1011189251Ssam					  (unsigned char *) xml,
1012189251Ssam					  xmlend - xml);
1013189251Ssam		}
1014189251Ssam
1015189251Ssam		start = end;
1016189251Ssam	}
1017189251Ssam
1018189251Ssam	os_free(buf);
1019189251Ssam
1020189251Ssam	tncs_batch_ending(tncs);
1021189251Ssam
1022189251Ssam	if (tncs_total_send_len(tncs) == 0)
1023189251Ssam		return tncs_derive_recommendation(tncs);
1024189251Ssam
1025189251Ssam	return TNCCS_PROCESS_OK_NO_RECOMMENDATION;
1026189251Ssam}
1027189251Ssam
1028189251Ssam
1029189251Ssamstatic struct tnc_if_imv * tncs_parse_imv(int id, char *start, char *end,
1030189251Ssam					  int *error)
1031189251Ssam{
1032189251Ssam	struct tnc_if_imv *imv;
1033189251Ssam	char *pos, *pos2;
1034189251Ssam
1035189251Ssam	if (id >= TNC_MAX_IMV_ID) {
1036189251Ssam		wpa_printf(MSG_DEBUG, "TNC: Too many IMVs");
1037189251Ssam		return NULL;
1038189251Ssam	}
1039189251Ssam
1040189251Ssam	imv = os_zalloc(sizeof(*imv));
1041189251Ssam	if (imv == NULL) {
1042189251Ssam		*error = 1;
1043189251Ssam		return NULL;
1044189251Ssam	}
1045189251Ssam
1046189251Ssam	imv->imvID = id;
1047189251Ssam
1048189251Ssam	pos = start;
1049189251Ssam	wpa_printf(MSG_DEBUG, "TNC: Configured IMV: %s", pos);
1050189251Ssam	if (pos + 1 >= end || *pos != '"') {
1051189251Ssam		wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMV line '%s' "
1052189251Ssam			   "(no starting quotation mark)", start);
1053189251Ssam		os_free(imv);
1054189251Ssam		return NULL;
1055189251Ssam	}
1056189251Ssam
1057189251Ssam	pos++;
1058189251Ssam	pos2 = pos;
1059189251Ssam	while (pos2 < end && *pos2 != '"')
1060189251Ssam		pos2++;
1061189251Ssam	if (pos2 >= end) {
1062189251Ssam		wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMV line '%s' "
1063189251Ssam			   "(no ending quotation mark)", start);
1064189251Ssam		os_free(imv);
1065189251Ssam		return NULL;
1066189251Ssam	}
1067189251Ssam	*pos2 = '\0';
1068189251Ssam	wpa_printf(MSG_DEBUG, "TNC: Name: '%s'", pos);
1069189251Ssam	imv->name = os_strdup(pos);
1070189251Ssam
1071189251Ssam	pos = pos2 + 1;
1072189251Ssam	if (pos >= end || *pos != ' ') {
1073189251Ssam		wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMV line '%s' "
1074189251Ssam			   "(no space after name)", start);
1075189251Ssam		os_free(imv);
1076189251Ssam		return NULL;
1077189251Ssam	}
1078189251Ssam
1079189251Ssam	pos++;
1080189251Ssam	wpa_printf(MSG_DEBUG, "TNC: IMV file: '%s'", pos);
1081189251Ssam	imv->path = os_strdup(pos);
1082189251Ssam
1083189251Ssam	return imv;
1084189251Ssam}
1085189251Ssam
1086189251Ssam
1087189251Ssamstatic int tncs_read_config(struct tncs_global *global)
1088189251Ssam{
1089189251Ssam	char *config, *end, *pos, *line_end;
1090189251Ssam	size_t config_len;
1091189251Ssam	struct tnc_if_imv *imv, *last;
1092189251Ssam	int id = 0;
1093189251Ssam
1094189251Ssam	last = NULL;
1095189251Ssam
1096189251Ssam	config = os_readfile(TNC_CONFIG_FILE, &config_len);
1097189251Ssam	if (config == NULL) {
1098189251Ssam		wpa_printf(MSG_ERROR, "TNC: Could not open TNC configuration "
1099189251Ssam			   "file '%s'", TNC_CONFIG_FILE);
1100189251Ssam		return -1;
1101189251Ssam	}
1102189251Ssam
1103189251Ssam	end = config + config_len;
1104189251Ssam	for (pos = config; pos < end; pos = line_end + 1) {
1105189251Ssam		line_end = pos;
1106189251Ssam		while (*line_end != '\n' && *line_end != '\r' &&
1107189251Ssam		       line_end < end)
1108189251Ssam			line_end++;
1109189251Ssam		*line_end = '\0';
1110189251Ssam
1111189251Ssam		if (os_strncmp(pos, "IMV ", 4) == 0) {
1112189251Ssam			int error = 0;
1113189251Ssam
1114189251Ssam			imv = tncs_parse_imv(id++, pos + 4, line_end, &error);
1115189251Ssam			if (error)
1116189251Ssam				return -1;
1117189251Ssam			if (imv) {
1118189251Ssam				if (last == NULL)
1119189251Ssam					global->imv = imv;
1120189251Ssam				else
1121189251Ssam					last->next = imv;
1122189251Ssam				last = imv;
1123189251Ssam			}
1124189251Ssam		}
1125189251Ssam	}
1126189251Ssam
1127189251Ssam	os_free(config);
1128189251Ssam
1129189251Ssam	return 0;
1130189251Ssam}
1131189251Ssam
1132189251Ssam
1133189251Ssamstruct tncs_data * tncs_init(void)
1134189251Ssam{
1135189251Ssam	struct tncs_data *tncs;
1136189251Ssam
1137189251Ssam	if (tncs_global_data == NULL)
1138189251Ssam		return NULL;
1139189251Ssam
1140189251Ssam	tncs = os_zalloc(sizeof(*tncs));
1141189251Ssam	if (tncs == NULL)
1142189251Ssam		return NULL;
1143189251Ssam	tncs->imv = tncs_global_data->imv;
1144189251Ssam	tncs->connectionID = tncs_global_data->next_conn_id++;
1145189251Ssam	tncs->next = tncs_global_data->connections;
1146189251Ssam	tncs_global_data->connections = tncs;
1147189251Ssam
1148189251Ssam	return tncs;
1149189251Ssam}
1150189251Ssam
1151189251Ssam
1152189251Ssamvoid tncs_deinit(struct tncs_data *tncs)
1153189251Ssam{
1154189251Ssam	int i;
1155189251Ssam	struct tncs_data *prev, *conn;
1156189251Ssam
1157189251Ssam	if (tncs == NULL)
1158189251Ssam		return;
1159189251Ssam
1160189251Ssam	for (i = 0; i < TNC_MAX_IMV_ID; i++)
1161189251Ssam		os_free(tncs->imv_data[i].imv_send);
1162189251Ssam
1163189251Ssam	prev = NULL;
1164189251Ssam	conn = tncs_global_data->connections;
1165189251Ssam	while (conn) {
1166189251Ssam		if (conn == tncs) {
1167189251Ssam			if (prev)
1168189251Ssam				prev->next = tncs->next;
1169189251Ssam			else
1170189251Ssam				tncs_global_data->connections = tncs->next;
1171189251Ssam			break;
1172189251Ssam		}
1173189251Ssam		prev = conn;
1174189251Ssam		conn = conn->next;
1175189251Ssam	}
1176189251Ssam
1177189251Ssam	os_free(tncs->tncs_message);
1178189251Ssam	os_free(tncs);
1179189251Ssam}
1180189251Ssam
1181189251Ssam
1182189251Ssamint tncs_global_init(void)
1183189251Ssam{
1184189251Ssam	struct tnc_if_imv *imv;
1185189251Ssam
1186189251Ssam	tncs_global_data = os_zalloc(sizeof(*tncs_global_data));
1187189251Ssam	if (tncs_global_data == NULL)
1188189251Ssam		return -1;
1189189251Ssam
1190189251Ssam	if (tncs_read_config(tncs_global_data) < 0) {
1191189251Ssam		wpa_printf(MSG_ERROR, "TNC: Failed to read TNC configuration");
1192189251Ssam		goto failed;
1193189251Ssam	}
1194189251Ssam
1195189251Ssam	for (imv = tncs_global_data->imv; imv; imv = imv->next) {
1196189251Ssam		if (tncs_load_imv(imv)) {
1197189251Ssam			wpa_printf(MSG_ERROR, "TNC: Failed to load IMV '%s'",
1198189251Ssam				   imv->name);
1199189251Ssam			goto failed;
1200189251Ssam		}
1201189251Ssam	}
1202189251Ssam
1203189251Ssam	return 0;
1204189251Ssam
1205189251Ssamfailed:
1206189251Ssam	tncs_global_deinit();
1207189251Ssam	return -1;
1208189251Ssam}
1209189251Ssam
1210189251Ssam
1211189251Ssamvoid tncs_global_deinit(void)
1212189251Ssam{
1213189251Ssam	struct tnc_if_imv *imv, *prev;
1214189251Ssam
1215189251Ssam	if (tncs_global_data == NULL)
1216189251Ssam		return;
1217189251Ssam
1218189251Ssam	imv = tncs_global_data->imv;
1219189251Ssam	while (imv) {
1220189251Ssam		tncs_unload_imv(imv);
1221189251Ssam
1222189251Ssam		prev = imv;
1223189251Ssam		imv = imv->next;
1224189251Ssam		os_free(prev);
1225189251Ssam	}
1226189251Ssam
1227189251Ssam	os_free(tncs_global_data);
1228214734Srpaulo	tncs_global_data = NULL;
1229189251Ssam}
1230189251Ssam
1231189251Ssam
1232189251Ssamstruct wpabuf * tncs_build_soh_request(void)
1233189251Ssam{
1234189251Ssam	struct wpabuf *buf;
1235189251Ssam
1236189251Ssam	/*
1237189251Ssam	 * Build a SoH Request TLV (to be used inside SoH EAP Extensions
1238189251Ssam	 * Method)
1239189251Ssam	 */
1240189251Ssam
1241189251Ssam	buf = wpabuf_alloc(8 + 4);
1242189251Ssam	if (buf == NULL)
1243189251Ssam		return NULL;
1244189251Ssam
1245189251Ssam	/* Vendor-Specific TLV (Microsoft) - SoH Request */
1246189251Ssam	wpabuf_put_be16(buf, EAP_TLV_VENDOR_SPECIFIC_TLV); /* TLV Type */
1247189251Ssam	wpabuf_put_be16(buf, 8); /* Length */
1248189251Ssam
1249189251Ssam	wpabuf_put_be32(buf, EAP_VENDOR_MICROSOFT); /* Vendor_Id */
1250189251Ssam
1251189251Ssam	wpabuf_put_be16(buf, 0x02); /* TLV Type - SoH Request TLV */
1252189251Ssam	wpabuf_put_be16(buf, 0); /* Length */
1253189251Ssam
1254189251Ssam	return buf;
1255189251Ssam}
1256189251Ssam
1257189251Ssam
1258189251Ssamstruct wpabuf * tncs_process_soh(const u8 *soh_tlv, size_t soh_tlv_len,
1259189251Ssam				 int *failure)
1260189251Ssam{
1261189251Ssam	wpa_hexdump(MSG_DEBUG, "TNC: SoH TLV", soh_tlv, soh_tlv_len);
1262189251Ssam	*failure = 0;
1263189251Ssam
1264189251Ssam	/* TODO: return MS-SoH Response TLV */
1265189251Ssam
1266189251Ssam	return NULL;
1267189251Ssam}
1268