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