tncs.c revision 214734
1/*
2 * EAP-TNC - TNCS (IF-IMV, IF-TNCCS, and IF-TNCCS-SOH)
3 * Copyright (c) 2007-2008, Jouni Malinen <j@w1.fi>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
8 *
9 * Alternatively, this software may be distributed under the terms of BSD
10 * license.
11 *
12 * See README and COPYING for more details.
13 */
14
15#include "includes.h"
16#include <dlfcn.h>
17
18#include "common.h"
19#include "base64.h"
20#include "tncs.h"
21#include "eap_common/eap_tlv_common.h"
22#include "eap_common/eap_defs.h"
23
24
25/* TODO: TNCS must be thread-safe; review the code and add locking etc. if
26 * needed.. */
27
28#define TNC_CONFIG_FILE "/etc/tnc_config"
29#define IF_TNCCS_START \
30"<?xml version=\"1.0\"?>\n" \
31"<TNCCS-Batch BatchId=\"%d\" Recipient=\"TNCS\" " \
32"xmlns=\"http://www.trustedcomputinggroup.org/IWG/TNC/1_0/IF_TNCCS#\" " \
33"xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " \
34"xsi:schemaLocation=\"http://www.trustedcomputinggroup.org/IWG/TNC/1_0/" \
35"IF_TNCCS# https://www.trustedcomputinggroup.org/XML/SCHEMA/TNCCS_1.0.xsd\">\n"
36#define IF_TNCCS_END "\n</TNCCS-Batch>"
37
38/* TNC IF-IMV */
39
40typedef unsigned long TNC_UInt32;
41typedef unsigned char *TNC_BufferReference;
42
43typedef TNC_UInt32 TNC_IMVID;
44typedef TNC_UInt32 TNC_ConnectionID;
45typedef TNC_UInt32 TNC_ConnectionState;
46typedef TNC_UInt32 TNC_RetryReason;
47typedef TNC_UInt32 TNC_IMV_Action_Recommendation;
48typedef TNC_UInt32 TNC_IMV_Evaluation_Result;
49typedef TNC_UInt32 TNC_MessageType;
50typedef TNC_MessageType *TNC_MessageTypeList;
51typedef TNC_UInt32 TNC_VendorID;
52typedef TNC_UInt32 TNC_Subtype;
53typedef TNC_UInt32 TNC_Version;
54typedef TNC_UInt32 TNC_Result;
55typedef TNC_UInt32 TNC_AttributeID;
56
57typedef TNC_Result (*TNC_TNCS_BindFunctionPointer)(
58	TNC_IMVID imvID,
59	char *functionName,
60	void **pOutfunctionPointer);
61
62#define TNC_RESULT_SUCCESS 0
63#define TNC_RESULT_NOT_INITIALIZED 1
64#define TNC_RESULT_ALREADY_INITIALIZED 2
65#define TNC_RESULT_NO_COMMON_VERSION 3
66#define TNC_RESULT_CANT_RETRY 4
67#define TNC_RESULT_WONT_RETRY 5
68#define TNC_RESULT_INVALID_PARAMETER 6
69#define TNC_RESULT_CANT_RESPOND 7
70#define TNC_RESULT_ILLEGAL_OPERATION 8
71#define TNC_RESULT_OTHER 9
72#define TNC_RESULT_FATAL 10
73
74#define TNC_CONNECTION_STATE_CREATE 0
75#define TNC_CONNECTION_STATE_HANDSHAKE 1
76#define TNC_CONNECTION_STATE_ACCESS_ALLOWED 2
77#define TNC_CONNECTION_STATE_ACCESS_ISOLATED 3
78#define TNC_CONNECTION_STATE_ACCESS_NONE 4
79#define TNC_CONNECTION_STATE_DELETE 5
80
81#define TNC_IFIMV_VERSION_1 1
82
83#define TNC_VENDORID_ANY ((TNC_VendorID) 0xffffff)
84#define TNC_SUBTYPE_ANY ((TNC_Subtype) 0xff)
85
86/* TNCC-TNCS Message Types */
87#define TNC_TNCCS_RECOMMENDATION		0x00000001
88#define TNC_TNCCS_ERROR				0x00000002
89#define TNC_TNCCS_PREFERREDLANGUAGE		0x00000003
90#define TNC_TNCCS_REASONSTRINGS			0x00000004
91
92/* Possible TNC_IMV_Action_Recommendation values: */
93enum IMV_Action_Recommendation {
94	TNC_IMV_ACTION_RECOMMENDATION_ALLOW,
95	TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS,
96	TNC_IMV_ACTION_RECOMMENDATION_ISOLATE,
97	TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION
98};
99
100/* Possible TNC_IMV_Evaluation_Result values: */
101enum IMV_Evaluation_Result {
102	TNC_IMV_EVALUATION_RESULT_COMPLIANT,
103	TNC_IMV_EVALUATION_RESULT_NONCOMPLIANT_MINOR,
104	TNC_IMV_EVALUATION_RESULT_NONCOMPLIANT_MAJOR,
105	TNC_IMV_EVALUATION_RESULT_ERROR,
106	TNC_IMV_EVALUATION_RESULT_DONT_KNOW
107};
108
109struct tnc_if_imv {
110	struct tnc_if_imv *next;
111	char *name;
112	char *path;
113	void *dlhandle; /* from dlopen() */
114	TNC_IMVID imvID;
115	TNC_MessageTypeList supported_types;
116	size_t num_supported_types;
117
118	/* Functions implemented by IMVs (with TNC_IMV_ prefix) */
119	TNC_Result (*Initialize)(
120		TNC_IMVID imvID,
121		TNC_Version minVersion,
122		TNC_Version maxVersion,
123		TNC_Version *pOutActualVersion);
124	TNC_Result (*NotifyConnectionChange)(
125		TNC_IMVID imvID,
126		TNC_ConnectionID connectionID,
127		TNC_ConnectionState newState);
128	TNC_Result (*ReceiveMessage)(
129		TNC_IMVID imvID,
130		TNC_ConnectionID connectionID,
131		TNC_BufferReference message,
132		TNC_UInt32 messageLength,
133		TNC_MessageType messageType);
134	TNC_Result (*SolicitRecommendation)(
135		TNC_IMVID imvID,
136		TNC_ConnectionID connectionID);
137	TNC_Result (*BatchEnding)(
138		TNC_IMVID imvID,
139		TNC_ConnectionID connectionID);
140	TNC_Result (*Terminate)(TNC_IMVID imvID);
141	TNC_Result (*ProvideBindFunction)(
142		TNC_IMVID imvID,
143		TNC_TNCS_BindFunctionPointer bindFunction);
144};
145
146
147#define TNC_MAX_IMV_ID 10
148
149struct tncs_data {
150	struct tncs_data *next;
151	struct tnc_if_imv *imv; /* local copy of tncs_global_data->imv */
152	TNC_ConnectionID connectionID;
153	unsigned int last_batchid;
154	enum IMV_Action_Recommendation recommendation;
155	int done;
156
157	struct conn_imv {
158		u8 *imv_send;
159		size_t imv_send_len;
160		enum IMV_Action_Recommendation recommendation;
161		int recommendation_set;
162	} imv_data[TNC_MAX_IMV_ID];
163
164	char *tncs_message;
165};
166
167
168struct tncs_global {
169	struct tnc_if_imv *imv;
170	TNC_ConnectionID next_conn_id;
171	struct tncs_data *connections;
172};
173
174static struct tncs_global *tncs_global_data = NULL;
175
176
177static struct tnc_if_imv * tncs_get_imv(TNC_IMVID imvID)
178{
179	struct tnc_if_imv *imv;
180
181	if (imvID >= TNC_MAX_IMV_ID || tncs_global_data == NULL)
182		return NULL;
183	imv = tncs_global_data->imv;
184	while (imv) {
185		if (imv->imvID == imvID)
186			return imv;
187		imv = imv->next;
188	}
189	return NULL;
190}
191
192
193static struct tncs_data * tncs_get_conn(TNC_ConnectionID connectionID)
194{
195	struct tncs_data *tncs;
196
197	if (tncs_global_data == NULL)
198		return NULL;
199
200	tncs = tncs_global_data->connections;
201	while (tncs) {
202		if (tncs->connectionID == connectionID)
203			return tncs;
204		tncs = tncs->next;
205	}
206
207	wpa_printf(MSG_DEBUG, "TNC: Connection ID %lu not found",
208		   (unsigned long) connectionID);
209
210	return NULL;
211}
212
213
214/* TNCS functions that IMVs can call */
215TNC_Result TNC_TNCS_ReportMessageTypes(
216	TNC_IMVID imvID,
217	TNC_MessageTypeList supportedTypes,
218	TNC_UInt32 typeCount)
219{
220	TNC_UInt32 i;
221	struct tnc_if_imv *imv;
222
223	wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_ReportMessageTypes(imvID=%lu "
224		   "typeCount=%lu)",
225		   (unsigned long) imvID, (unsigned long) typeCount);
226
227	for (i = 0; i < typeCount; i++) {
228		wpa_printf(MSG_DEBUG, "TNC: supportedTypes[%lu] = %lu",
229			   i, supportedTypes[i]);
230	}
231
232	imv = tncs_get_imv(imvID);
233	if (imv == NULL)
234		return TNC_RESULT_INVALID_PARAMETER;
235	os_free(imv->supported_types);
236	imv->supported_types =
237		os_malloc(typeCount * sizeof(TNC_MessageTypeList));
238	if (imv->supported_types == NULL)
239		return TNC_RESULT_FATAL;
240	os_memcpy(imv->supported_types, supportedTypes,
241		  typeCount * sizeof(TNC_MessageTypeList));
242	imv->num_supported_types = typeCount;
243
244	return TNC_RESULT_SUCCESS;
245}
246
247
248TNC_Result TNC_TNCS_SendMessage(
249	TNC_IMVID imvID,
250	TNC_ConnectionID connectionID,
251	TNC_BufferReference message,
252	TNC_UInt32 messageLength,
253	TNC_MessageType messageType)
254{
255	struct tncs_data *tncs;
256	unsigned char *b64;
257	size_t b64len;
258
259	wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_SendMessage(imvID=%lu "
260		   "connectionID=%lu messageType=%lu)",
261		   imvID, connectionID, messageType);
262	wpa_hexdump_ascii(MSG_DEBUG, "TNC: TNC_TNCS_SendMessage",
263			  message, messageLength);
264
265	if (tncs_get_imv(imvID) == NULL)
266		return TNC_RESULT_INVALID_PARAMETER;
267
268	tncs = tncs_get_conn(connectionID);
269	if (tncs == NULL)
270		return TNC_RESULT_INVALID_PARAMETER;
271
272	b64 = base64_encode(message, messageLength, &b64len);
273	if (b64 == NULL)
274		return TNC_RESULT_FATAL;
275
276	os_free(tncs->imv_data[imvID].imv_send);
277	tncs->imv_data[imvID].imv_send_len = 0;
278	tncs->imv_data[imvID].imv_send = os_zalloc(b64len + 100);
279	if (tncs->imv_data[imvID].imv_send == NULL) {
280		os_free(b64);
281		return TNC_RESULT_OTHER;
282	}
283
284	tncs->imv_data[imvID].imv_send_len =
285		os_snprintf((char *) tncs->imv_data[imvID].imv_send,
286			    b64len + 100,
287			    "<IMC-IMV-Message><Type>%08X</Type>"
288			    "<Base64>%s</Base64></IMC-IMV-Message>",
289			    (unsigned int) messageType, b64);
290
291	os_free(b64);
292
293	return TNC_RESULT_SUCCESS;
294}
295
296
297TNC_Result TNC_TNCS_RequestHandshakeRetry(
298	TNC_IMVID imvID,
299	TNC_ConnectionID connectionID,
300	TNC_RetryReason reason)
301{
302	wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_RequestHandshakeRetry");
303	/* TODO */
304	return TNC_RESULT_SUCCESS;
305}
306
307
308TNC_Result TNC_TNCS_ProvideRecommendation(
309	TNC_IMVID imvID,
310	TNC_ConnectionID connectionID,
311	TNC_IMV_Action_Recommendation recommendation,
312	TNC_IMV_Evaluation_Result evaluation)
313{
314	struct tncs_data *tncs;
315
316	wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_ProvideRecommendation(imvID=%lu "
317		   "connectionID=%lu recommendation=%lu evaluation=%lu)",
318		   (unsigned long) imvID, (unsigned long) connectionID,
319		   (unsigned long) recommendation, (unsigned long) evaluation);
320
321	if (tncs_get_imv(imvID) == NULL)
322		return TNC_RESULT_INVALID_PARAMETER;
323
324	tncs = tncs_get_conn(connectionID);
325	if (tncs == NULL)
326		return TNC_RESULT_INVALID_PARAMETER;
327
328	tncs->imv_data[imvID].recommendation = recommendation;
329	tncs->imv_data[imvID].recommendation_set = 1;
330
331	return TNC_RESULT_SUCCESS;
332}
333
334
335TNC_Result TNC_TNCS_GetAttribute(
336	TNC_IMVID imvID,
337	TNC_ConnectionID connectionID,
338	TNC_AttributeID attribureID,
339	TNC_UInt32 bufferLength,
340	TNC_BufferReference buffer,
341	TNC_UInt32 *pOutValueLength)
342{
343	wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_GetAttribute");
344	/* TODO */
345	return TNC_RESULT_SUCCESS;
346}
347
348
349TNC_Result TNC_TNCS_SetAttribute(
350	TNC_IMVID imvID,
351	TNC_ConnectionID connectionID,
352	TNC_AttributeID attribureID,
353	TNC_UInt32 bufferLength,
354	TNC_BufferReference buffer)
355{
356	wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_SetAttribute");
357	/* TODO */
358	return TNC_RESULT_SUCCESS;
359}
360
361
362TNC_Result TNC_TNCS_BindFunction(
363	TNC_IMVID imvID,
364	char *functionName,
365	void **pOutFunctionPointer)
366{
367	wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_BindFunction(imcID=%lu, "
368		   "functionName='%s')", (unsigned long) imvID, functionName);
369
370	if (tncs_get_imv(imvID) == NULL)
371		return TNC_RESULT_INVALID_PARAMETER;
372
373	if (pOutFunctionPointer == NULL)
374		return TNC_RESULT_INVALID_PARAMETER;
375
376	if (os_strcmp(functionName, "TNC_TNCS_ReportMessageTypes") == 0)
377		*pOutFunctionPointer = TNC_TNCS_ReportMessageTypes;
378	else if (os_strcmp(functionName, "TNC_TNCS_SendMessage") == 0)
379		*pOutFunctionPointer = TNC_TNCS_SendMessage;
380	else if (os_strcmp(functionName, "TNC_TNCS_RequestHandshakeRetry") ==
381		 0)
382		*pOutFunctionPointer = TNC_TNCS_RequestHandshakeRetry;
383	else if (os_strcmp(functionName, "TNC_TNCS_ProvideRecommendation") ==
384		 0)
385		*pOutFunctionPointer = TNC_TNCS_ProvideRecommendation;
386	else if (os_strcmp(functionName, "TNC_TNCS_GetAttribute") == 0)
387		*pOutFunctionPointer = TNC_TNCS_GetAttribute;
388	else if (os_strcmp(functionName, "TNC_TNCS_SetAttribute") == 0)
389		*pOutFunctionPointer = TNC_TNCS_SetAttribute;
390	else
391		*pOutFunctionPointer = NULL;
392
393	return TNC_RESULT_SUCCESS;
394}
395
396
397static void * tncs_get_sym(void *handle, char *func)
398{
399	void *fptr;
400
401	fptr = dlsym(handle, func);
402
403	return fptr;
404}
405
406
407static int tncs_imv_resolve_funcs(struct tnc_if_imv *imv)
408{
409	void *handle = imv->dlhandle;
410
411	/* Mandatory IMV functions */
412	imv->Initialize = tncs_get_sym(handle, "TNC_IMV_Initialize");
413	if (imv->Initialize == NULL) {
414		wpa_printf(MSG_ERROR, "TNC: IMV does not export "
415			   "TNC_IMV_Initialize");
416		return -1;
417	}
418
419	imv->SolicitRecommendation = tncs_get_sym(
420		handle, "TNC_IMV_SolicitRecommendation");
421	if (imv->SolicitRecommendation == NULL) {
422		wpa_printf(MSG_ERROR, "TNC: IMV does not export "
423			   "TNC_IMV_SolicitRecommendation");
424		return -1;
425	}
426
427	imv->ProvideBindFunction =
428		tncs_get_sym(handle, "TNC_IMV_ProvideBindFunction");
429	if (imv->ProvideBindFunction == NULL) {
430		wpa_printf(MSG_ERROR, "TNC: IMV does not export "
431			   "TNC_IMV_ProvideBindFunction");
432		return -1;
433	}
434
435	/* Optional IMV functions */
436	imv->NotifyConnectionChange =
437		tncs_get_sym(handle, "TNC_IMV_NotifyConnectionChange");
438	imv->ReceiveMessage = tncs_get_sym(handle, "TNC_IMV_ReceiveMessage");
439	imv->BatchEnding = tncs_get_sym(handle, "TNC_IMV_BatchEnding");
440	imv->Terminate = tncs_get_sym(handle, "TNC_IMV_Terminate");
441
442	return 0;
443}
444
445
446static int tncs_imv_initialize(struct tnc_if_imv *imv)
447{
448	TNC_Result res;
449	TNC_Version imv_ver;
450
451	wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMV_Initialize for IMV '%s'",
452		   imv->name);
453	res = imv->Initialize(imv->imvID, TNC_IFIMV_VERSION_1,
454			      TNC_IFIMV_VERSION_1, &imv_ver);
455	wpa_printf(MSG_DEBUG, "TNC: TNC_IMV_Initialize: res=%lu imv_ver=%lu",
456		   (unsigned long) res, (unsigned long) imv_ver);
457
458	return res == TNC_RESULT_SUCCESS ? 0 : -1;
459}
460
461
462static int tncs_imv_terminate(struct tnc_if_imv *imv)
463{
464	TNC_Result res;
465
466	if (imv->Terminate == NULL)
467		return 0;
468
469	wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMV_Terminate for IMV '%s'",
470		   imv->name);
471	res = imv->Terminate(imv->imvID);
472	wpa_printf(MSG_DEBUG, "TNC: TNC_IMV_Terminate: %lu",
473		   (unsigned long) res);
474
475	return res == TNC_RESULT_SUCCESS ? 0 : -1;
476}
477
478
479static int tncs_imv_provide_bind_function(struct tnc_if_imv *imv)
480{
481	TNC_Result res;
482
483	wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMV_ProvideBindFunction for "
484		   "IMV '%s'", imv->name);
485	res = imv->ProvideBindFunction(imv->imvID, TNC_TNCS_BindFunction);
486	wpa_printf(MSG_DEBUG, "TNC: TNC_IMV_ProvideBindFunction: res=%lu",
487		   (unsigned long) res);
488
489	return res == TNC_RESULT_SUCCESS ? 0 : -1;
490}
491
492
493static int tncs_imv_notify_connection_change(struct tnc_if_imv *imv,
494					     TNC_ConnectionID conn,
495					     TNC_ConnectionState state)
496{
497	TNC_Result res;
498
499	if (imv->NotifyConnectionChange == NULL)
500		return 0;
501
502	wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMV_NotifyConnectionChange(%d)"
503		   " for IMV '%s'", (int) state, imv->name);
504	res = imv->NotifyConnectionChange(imv->imvID, conn, state);
505	wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_NotifyConnectionChange: %lu",
506		   (unsigned long) res);
507
508	return res == TNC_RESULT_SUCCESS ? 0 : -1;
509}
510
511
512static int tncs_load_imv(struct tnc_if_imv *imv)
513{
514	if (imv->path == NULL) {
515		wpa_printf(MSG_DEBUG, "TNC: No IMV configured");
516		return -1;
517	}
518
519	wpa_printf(MSG_DEBUG, "TNC: Opening IMV: %s (%s)",
520		   imv->name, imv->path);
521	imv->dlhandle = dlopen(imv->path, RTLD_LAZY);
522	if (imv->dlhandle == NULL) {
523		wpa_printf(MSG_ERROR, "TNC: Failed to open IMV '%s' (%s): %s",
524			   imv->name, imv->path, dlerror());
525		return -1;
526	}
527
528	if (tncs_imv_resolve_funcs(imv) < 0) {
529		wpa_printf(MSG_ERROR, "TNC: Failed to resolve IMV functions");
530		return -1;
531	}
532
533	if (tncs_imv_initialize(imv) < 0 ||
534	    tncs_imv_provide_bind_function(imv) < 0) {
535		wpa_printf(MSG_ERROR, "TNC: Failed to initialize IMV");
536		return -1;
537	}
538
539	return 0;
540}
541
542
543static void tncs_free_imv(struct tnc_if_imv *imv)
544{
545	os_free(imv->name);
546	os_free(imv->path);
547	os_free(imv->supported_types);
548}
549
550static void tncs_unload_imv(struct tnc_if_imv *imv)
551{
552	tncs_imv_terminate(imv);
553
554	if (imv->dlhandle)
555		dlclose(imv->dlhandle);
556
557	tncs_free_imv(imv);
558}
559
560
561static int tncs_supported_type(struct tnc_if_imv *imv, unsigned int type)
562{
563	size_t i;
564	unsigned int vendor, subtype;
565
566	if (imv == NULL || imv->supported_types == NULL)
567		return 0;
568
569	vendor = type >> 8;
570	subtype = type & 0xff;
571
572	for (i = 0; i < imv->num_supported_types; i++) {
573		unsigned int svendor, ssubtype;
574		svendor = imv->supported_types[i] >> 8;
575		ssubtype = imv->supported_types[i] & 0xff;
576		if ((vendor == svendor || svendor == TNC_VENDORID_ANY) &&
577		    (subtype == ssubtype || ssubtype == TNC_SUBTYPE_ANY))
578			return 1;
579	}
580
581	return 0;
582}
583
584
585static void tncs_send_to_imvs(struct tncs_data *tncs, unsigned int type,
586			      const u8 *msg, size_t len)
587{
588	struct tnc_if_imv *imv;
589	TNC_Result res;
590
591	wpa_hexdump_ascii(MSG_MSGDUMP, "TNC: Message to IMV(s)", msg, len);
592
593	for (imv = tncs->imv; imv; imv = imv->next) {
594		if (imv->ReceiveMessage == NULL ||
595		    !tncs_supported_type(imv, type))
596			continue;
597
598		wpa_printf(MSG_DEBUG, "TNC: Call ReceiveMessage for IMV '%s'",
599			   imv->name);
600		res = imv->ReceiveMessage(imv->imvID, tncs->connectionID,
601					  (TNC_BufferReference) msg, len,
602					  type);
603		wpa_printf(MSG_DEBUG, "TNC: ReceiveMessage: %lu",
604			   (unsigned long) res);
605	}
606}
607
608
609static void tncs_batch_ending(struct tncs_data *tncs)
610{
611	struct tnc_if_imv *imv;
612	TNC_Result res;
613
614	for (imv = tncs->imv; imv; imv = imv->next) {
615		if (imv->BatchEnding == NULL)
616			continue;
617
618		wpa_printf(MSG_DEBUG, "TNC: Call BatchEnding for IMV '%s'",
619			   imv->name);
620		res = imv->BatchEnding(imv->imvID, tncs->connectionID);
621		wpa_printf(MSG_DEBUG, "TNC: BatchEnding: %lu",
622			   (unsigned long) res);
623	}
624}
625
626
627static void tncs_solicit_recommendation(struct tncs_data *tncs)
628{
629	struct tnc_if_imv *imv;
630	TNC_Result res;
631
632	for (imv = tncs->imv; imv; imv = imv->next) {
633		if (tncs->imv_data[imv->imvID].recommendation_set)
634			continue;
635
636		wpa_printf(MSG_DEBUG, "TNC: Call SolicitRecommendation for "
637			   "IMV '%s'", imv->name);
638		res = imv->SolicitRecommendation(imv->imvID,
639						 tncs->connectionID);
640		wpa_printf(MSG_DEBUG, "TNC: SolicitRecommendation: %lu",
641			   (unsigned long) res);
642	}
643}
644
645
646void tncs_init_connection(struct tncs_data *tncs)
647{
648	struct tnc_if_imv *imv;
649	int i;
650
651	for (imv = tncs->imv; imv; imv = imv->next) {
652		tncs_imv_notify_connection_change(
653			imv, tncs->connectionID, TNC_CONNECTION_STATE_CREATE);
654		tncs_imv_notify_connection_change(
655			imv, tncs->connectionID,
656			TNC_CONNECTION_STATE_HANDSHAKE);
657	}
658
659	for (i = 0; i < TNC_MAX_IMV_ID; i++) {
660		os_free(tncs->imv_data[i].imv_send);
661		tncs->imv_data[i].imv_send = NULL;
662		tncs->imv_data[i].imv_send_len = 0;
663	}
664}
665
666
667size_t tncs_total_send_len(struct tncs_data *tncs)
668{
669	int i;
670	size_t len = 0;
671
672	for (i = 0; i < TNC_MAX_IMV_ID; i++)
673		len += tncs->imv_data[i].imv_send_len;
674	if (tncs->tncs_message)
675		len += os_strlen(tncs->tncs_message);
676	return len;
677}
678
679
680u8 * tncs_copy_send_buf(struct tncs_data *tncs, u8 *pos)
681{
682	int i;
683
684	for (i = 0; i < TNC_MAX_IMV_ID; i++) {
685		if (tncs->imv_data[i].imv_send == NULL)
686			continue;
687
688		os_memcpy(pos, tncs->imv_data[i].imv_send,
689			  tncs->imv_data[i].imv_send_len);
690		pos += tncs->imv_data[i].imv_send_len;
691		os_free(tncs->imv_data[i].imv_send);
692		tncs->imv_data[i].imv_send = NULL;
693		tncs->imv_data[i].imv_send_len = 0;
694	}
695
696	if (tncs->tncs_message) {
697		size_t len = os_strlen(tncs->tncs_message);
698		os_memcpy(pos, tncs->tncs_message, len);
699		pos += len;
700		os_free(tncs->tncs_message);
701		tncs->tncs_message = NULL;
702	}
703
704	return pos;
705}
706
707
708char * tncs_if_tnccs_start(struct tncs_data *tncs)
709{
710	char *buf = os_malloc(1000);
711	if (buf == NULL)
712		return NULL;
713	tncs->last_batchid++;
714	os_snprintf(buf, 1000, IF_TNCCS_START, tncs->last_batchid);
715	return buf;
716}
717
718
719char * tncs_if_tnccs_end(void)
720{
721	char *buf = os_malloc(100);
722	if (buf == NULL)
723		return NULL;
724	os_snprintf(buf, 100, IF_TNCCS_END);
725	return buf;
726}
727
728
729static int tncs_get_type(char *start, unsigned int *type)
730{
731	char *pos = os_strstr(start, "<Type>");
732	if (pos == NULL)
733		return -1;
734	pos += 6;
735	*type = strtoul(pos, NULL, 16);
736	return 0;
737}
738
739
740static unsigned char * tncs_get_base64(char *start, size_t *decoded_len)
741{
742	char *pos, *pos2;
743	unsigned char *decoded;
744
745	pos = os_strstr(start, "<Base64>");
746	if (pos == NULL)
747		return NULL;
748
749	pos += 8;
750	pos2 = os_strstr(pos, "</Base64>");
751	if (pos2 == NULL)
752		return NULL;
753	*pos2 = '\0';
754
755	decoded = base64_decode((unsigned char *) pos, os_strlen(pos),
756				decoded_len);
757	*pos2 = '<';
758	if (decoded == NULL) {
759		wpa_printf(MSG_DEBUG, "TNC: Failed to decode Base64 data");
760	}
761
762	return decoded;
763}
764
765
766static enum tncs_process_res tncs_derive_recommendation(struct tncs_data *tncs)
767{
768	enum IMV_Action_Recommendation rec;
769	struct tnc_if_imv *imv;
770	TNC_ConnectionState state;
771	char *txt;
772
773	wpa_printf(MSG_DEBUG, "TNC: No more messages from IMVs");
774
775	if (tncs->done)
776		return TNCCS_PROCESS_OK_NO_RECOMMENDATION;
777
778	tncs_solicit_recommendation(tncs);
779
780	/* Select the most restrictive recommendation */
781	rec = TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION;
782	for (imv = tncs->imv; imv; imv = imv->next) {
783		TNC_IMV_Action_Recommendation irec;
784		irec = tncs->imv_data[imv->imvID].recommendation;
785		if (irec == TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS)
786			rec = TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS;
787		if (irec == TNC_IMV_ACTION_RECOMMENDATION_ISOLATE &&
788		    rec != TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS)
789			rec = TNC_IMV_ACTION_RECOMMENDATION_ISOLATE;
790		if (irec == TNC_IMV_ACTION_RECOMMENDATION_ALLOW &&
791		    rec == TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION)
792			rec = TNC_IMV_ACTION_RECOMMENDATION_ALLOW;
793	}
794
795	wpa_printf(MSG_DEBUG, "TNC: Recommendation: %d", rec);
796	tncs->recommendation = rec;
797	tncs->done = 1;
798
799	txt = NULL;
800	switch (rec) {
801	case TNC_IMV_ACTION_RECOMMENDATION_ALLOW:
802	case TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION:
803		txt = "allow";
804		state = TNC_CONNECTION_STATE_ACCESS_ALLOWED;
805		break;
806	case TNC_IMV_ACTION_RECOMMENDATION_ISOLATE:
807		txt = "isolate";
808		state = TNC_CONNECTION_STATE_ACCESS_ISOLATED;
809		break;
810	case TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS:
811		txt = "none";
812		state = TNC_CONNECTION_STATE_ACCESS_NONE;
813		break;
814	default:
815		state = TNC_CONNECTION_STATE_ACCESS_ALLOWED;
816		break;
817	}
818
819	if (txt) {
820		os_free(tncs->tncs_message);
821		tncs->tncs_message = os_zalloc(200);
822		if (tncs->tncs_message) {
823			os_snprintf(tncs->tncs_message, 199,
824				    "<TNCC-TNCS-Message><Type>%08X</Type>"
825				    "<XML><TNCCS-Recommendation type=\"%s\">"
826				    "</TNCCS-Recommendation></XML>"
827				    "</TNCC-TNCS-Message>",
828				    TNC_TNCCS_RECOMMENDATION, txt);
829		}
830	}
831
832	for (imv = tncs->imv; imv; imv = imv->next) {
833		tncs_imv_notify_connection_change(imv, tncs->connectionID,
834						  state);
835	}
836
837	switch (rec) {
838	case TNC_IMV_ACTION_RECOMMENDATION_ALLOW:
839		return TNCCS_RECOMMENDATION_ALLOW;
840	case TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS:
841		return TNCCS_RECOMMENDATION_NO_ACCESS;
842	case TNC_IMV_ACTION_RECOMMENDATION_ISOLATE:
843		return TNCCS_RECOMMENDATION_ISOLATE;
844	case TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION:
845		return TNCCS_RECOMMENDATION_NO_RECOMMENDATION;
846	default:
847		return TNCCS_PROCESS_ERROR;
848	}
849}
850
851
852enum tncs_process_res tncs_process_if_tnccs(struct tncs_data *tncs,
853					    const u8 *msg, size_t len)
854{
855	char *buf, *start, *end, *pos, *pos2, *payload;
856	unsigned int batch_id;
857	unsigned char *decoded;
858	size_t decoded_len;
859
860	buf = os_malloc(len + 1);
861	if (buf == NULL)
862		return TNCCS_PROCESS_ERROR;
863
864	os_memcpy(buf, msg, len);
865	buf[len] = '\0';
866	start = os_strstr(buf, "<TNCCS-Batch ");
867	end = os_strstr(buf, "</TNCCS-Batch>");
868	if (start == NULL || end == NULL || start > end) {
869		os_free(buf);
870		return TNCCS_PROCESS_ERROR;
871	}
872
873	start += 13;
874	while (*start == ' ')
875		start++;
876	*end = '\0';
877
878	pos = os_strstr(start, "BatchId=");
879	if (pos == NULL) {
880		os_free(buf);
881		return TNCCS_PROCESS_ERROR;
882	}
883
884	pos += 8;
885	if (*pos == '"')
886		pos++;
887	batch_id = atoi(pos);
888	wpa_printf(MSG_DEBUG, "TNC: Received IF-TNCCS BatchId=%u",
889		   batch_id);
890	if (batch_id != tncs->last_batchid + 1) {
891		wpa_printf(MSG_DEBUG, "TNC: Unexpected IF-TNCCS BatchId "
892			   "%u (expected %u)",
893			   batch_id, tncs->last_batchid + 1);
894		os_free(buf);
895		return TNCCS_PROCESS_ERROR;
896	}
897	tncs->last_batchid = batch_id;
898
899	while (*pos != '\0' && *pos != '>')
900		pos++;
901	if (*pos == '\0') {
902		os_free(buf);
903		return TNCCS_PROCESS_ERROR;
904	}
905	pos++;
906	payload = start;
907
908	/*
909	 * <IMC-IMV-Message>
910	 * <Type>01234567</Type>
911	 * <Base64>foo==</Base64>
912	 * </IMC-IMV-Message>
913	 */
914
915	while (*start) {
916		char *endpos;
917		unsigned int type;
918
919		pos = os_strstr(start, "<IMC-IMV-Message>");
920		if (pos == NULL)
921			break;
922		start = pos + 17;
923		end = os_strstr(start, "</IMC-IMV-Message>");
924		if (end == NULL)
925			break;
926		*end = '\0';
927		endpos = end;
928		end += 18;
929
930		if (tncs_get_type(start, &type) < 0) {
931			*endpos = '<';
932			start = end;
933			continue;
934		}
935		wpa_printf(MSG_DEBUG, "TNC: IMC-IMV-Message Type 0x%x", type);
936
937		decoded = tncs_get_base64(start, &decoded_len);
938		if (decoded == NULL) {
939			*endpos = '<';
940			start = end;
941			continue;
942		}
943
944		tncs_send_to_imvs(tncs, type, decoded, decoded_len);
945
946		os_free(decoded);
947
948		start = end;
949	}
950
951	/*
952	 * <TNCC-TNCS-Message>
953	 * <Type>01234567</Type>
954	 * <XML><TNCCS-Foo type="foo"></TNCCS-Foo></XML>
955	 * <Base64>foo==</Base64>
956	 * </TNCC-TNCS-Message>
957	 */
958
959	start = payload;
960	while (*start) {
961		unsigned int type;
962		char *xml, *xmlend, *endpos;
963
964		pos = os_strstr(start, "<TNCC-TNCS-Message>");
965		if (pos == NULL)
966			break;
967		start = pos + 19;
968		end = os_strstr(start, "</TNCC-TNCS-Message>");
969		if (end == NULL)
970			break;
971		*end = '\0';
972		endpos = end;
973		end += 20;
974
975		if (tncs_get_type(start, &type) < 0) {
976			*endpos = '<';
977			start = end;
978			continue;
979		}
980		wpa_printf(MSG_DEBUG, "TNC: TNCC-TNCS-Message Type 0x%x",
981			   type);
982
983		/* Base64 OR XML */
984		decoded = NULL;
985		xml = NULL;
986		xmlend = NULL;
987		pos = os_strstr(start, "<XML>");
988		if (pos) {
989			pos += 5;
990			pos2 = os_strstr(pos, "</XML>");
991			if (pos2 == NULL) {
992				*endpos = '<';
993				start = end;
994				continue;
995			}
996			xmlend = pos2;
997			xml = pos;
998		} else {
999			decoded = tncs_get_base64(start, &decoded_len);
1000			if (decoded == NULL) {
1001				*endpos = '<';
1002				start = end;
1003				continue;
1004			}
1005		}
1006
1007		if (decoded) {
1008			wpa_hexdump_ascii(MSG_MSGDUMP,
1009					  "TNC: TNCC-TNCS-Message Base64",
1010					  decoded, decoded_len);
1011			os_free(decoded);
1012		}
1013
1014		if (xml) {
1015			wpa_hexdump_ascii(MSG_MSGDUMP,
1016					  "TNC: TNCC-TNCS-Message XML",
1017					  (unsigned char *) xml,
1018					  xmlend - xml);
1019		}
1020
1021		start = end;
1022	}
1023
1024	os_free(buf);
1025
1026	tncs_batch_ending(tncs);
1027
1028	if (tncs_total_send_len(tncs) == 0)
1029		return tncs_derive_recommendation(tncs);
1030
1031	return TNCCS_PROCESS_OK_NO_RECOMMENDATION;
1032}
1033
1034
1035static struct tnc_if_imv * tncs_parse_imv(int id, char *start, char *end,
1036					  int *error)
1037{
1038	struct tnc_if_imv *imv;
1039	char *pos, *pos2;
1040
1041	if (id >= TNC_MAX_IMV_ID) {
1042		wpa_printf(MSG_DEBUG, "TNC: Too many IMVs");
1043		return NULL;
1044	}
1045
1046	imv = os_zalloc(sizeof(*imv));
1047	if (imv == NULL) {
1048		*error = 1;
1049		return NULL;
1050	}
1051
1052	imv->imvID = id;
1053
1054	pos = start;
1055	wpa_printf(MSG_DEBUG, "TNC: Configured IMV: %s", pos);
1056	if (pos + 1 >= end || *pos != '"') {
1057		wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMV line '%s' "
1058			   "(no starting quotation mark)", start);
1059		os_free(imv);
1060		return NULL;
1061	}
1062
1063	pos++;
1064	pos2 = pos;
1065	while (pos2 < end && *pos2 != '"')
1066		pos2++;
1067	if (pos2 >= end) {
1068		wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMV line '%s' "
1069			   "(no ending quotation mark)", start);
1070		os_free(imv);
1071		return NULL;
1072	}
1073	*pos2 = '\0';
1074	wpa_printf(MSG_DEBUG, "TNC: Name: '%s'", pos);
1075	imv->name = os_strdup(pos);
1076
1077	pos = pos2 + 1;
1078	if (pos >= end || *pos != ' ') {
1079		wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMV line '%s' "
1080			   "(no space after name)", start);
1081		os_free(imv);
1082		return NULL;
1083	}
1084
1085	pos++;
1086	wpa_printf(MSG_DEBUG, "TNC: IMV file: '%s'", pos);
1087	imv->path = os_strdup(pos);
1088
1089	return imv;
1090}
1091
1092
1093static int tncs_read_config(struct tncs_global *global)
1094{
1095	char *config, *end, *pos, *line_end;
1096	size_t config_len;
1097	struct tnc_if_imv *imv, *last;
1098	int id = 0;
1099
1100	last = NULL;
1101
1102	config = os_readfile(TNC_CONFIG_FILE, &config_len);
1103	if (config == NULL) {
1104		wpa_printf(MSG_ERROR, "TNC: Could not open TNC configuration "
1105			   "file '%s'", TNC_CONFIG_FILE);
1106		return -1;
1107	}
1108
1109	end = config + config_len;
1110	for (pos = config; pos < end; pos = line_end + 1) {
1111		line_end = pos;
1112		while (*line_end != '\n' && *line_end != '\r' &&
1113		       line_end < end)
1114			line_end++;
1115		*line_end = '\0';
1116
1117		if (os_strncmp(pos, "IMV ", 4) == 0) {
1118			int error = 0;
1119
1120			imv = tncs_parse_imv(id++, pos + 4, line_end, &error);
1121			if (error)
1122				return -1;
1123			if (imv) {
1124				if (last == NULL)
1125					global->imv = imv;
1126				else
1127					last->next = imv;
1128				last = imv;
1129			}
1130		}
1131	}
1132
1133	os_free(config);
1134
1135	return 0;
1136}
1137
1138
1139struct tncs_data * tncs_init(void)
1140{
1141	struct tncs_data *tncs;
1142
1143	if (tncs_global_data == NULL)
1144		return NULL;
1145
1146	tncs = os_zalloc(sizeof(*tncs));
1147	if (tncs == NULL)
1148		return NULL;
1149	tncs->imv = tncs_global_data->imv;
1150	tncs->connectionID = tncs_global_data->next_conn_id++;
1151	tncs->next = tncs_global_data->connections;
1152	tncs_global_data->connections = tncs;
1153
1154	return tncs;
1155}
1156
1157
1158void tncs_deinit(struct tncs_data *tncs)
1159{
1160	int i;
1161	struct tncs_data *prev, *conn;
1162
1163	if (tncs == NULL)
1164		return;
1165
1166	for (i = 0; i < TNC_MAX_IMV_ID; i++)
1167		os_free(tncs->imv_data[i].imv_send);
1168
1169	prev = NULL;
1170	conn = tncs_global_data->connections;
1171	while (conn) {
1172		if (conn == tncs) {
1173			if (prev)
1174				prev->next = tncs->next;
1175			else
1176				tncs_global_data->connections = tncs->next;
1177			break;
1178		}
1179		prev = conn;
1180		conn = conn->next;
1181	}
1182
1183	os_free(tncs->tncs_message);
1184	os_free(tncs);
1185}
1186
1187
1188int tncs_global_init(void)
1189{
1190	struct tnc_if_imv *imv;
1191
1192	tncs_global_data = os_zalloc(sizeof(*tncs_global_data));
1193	if (tncs_global_data == NULL)
1194		return -1;
1195
1196	if (tncs_read_config(tncs_global_data) < 0) {
1197		wpa_printf(MSG_ERROR, "TNC: Failed to read TNC configuration");
1198		goto failed;
1199	}
1200
1201	for (imv = tncs_global_data->imv; imv; imv = imv->next) {
1202		if (tncs_load_imv(imv)) {
1203			wpa_printf(MSG_ERROR, "TNC: Failed to load IMV '%s'",
1204				   imv->name);
1205			goto failed;
1206		}
1207	}
1208
1209	return 0;
1210
1211failed:
1212	tncs_global_deinit();
1213	return -1;
1214}
1215
1216
1217void tncs_global_deinit(void)
1218{
1219	struct tnc_if_imv *imv, *prev;
1220
1221	if (tncs_global_data == NULL)
1222		return;
1223
1224	imv = tncs_global_data->imv;
1225	while (imv) {
1226		tncs_unload_imv(imv);
1227
1228		prev = imv;
1229		imv = imv->next;
1230		os_free(prev);
1231	}
1232
1233	os_free(tncs_global_data);
1234	tncs_global_data = NULL;
1235}
1236
1237
1238struct wpabuf * tncs_build_soh_request(void)
1239{
1240	struct wpabuf *buf;
1241
1242	/*
1243	 * Build a SoH Request TLV (to be used inside SoH EAP Extensions
1244	 * Method)
1245	 */
1246
1247	buf = wpabuf_alloc(8 + 4);
1248	if (buf == NULL)
1249		return NULL;
1250
1251	/* Vendor-Specific TLV (Microsoft) - SoH Request */
1252	wpabuf_put_be16(buf, EAP_TLV_VENDOR_SPECIFIC_TLV); /* TLV Type */
1253	wpabuf_put_be16(buf, 8); /* Length */
1254
1255	wpabuf_put_be32(buf, EAP_VENDOR_MICROSOFT); /* Vendor_Id */
1256
1257	wpabuf_put_be16(buf, 0x02); /* TLV Type - SoH Request TLV */
1258	wpabuf_put_be16(buf, 0); /* Length */
1259
1260	return buf;
1261}
1262
1263
1264struct wpabuf * tncs_process_soh(const u8 *soh_tlv, size_t soh_tlv_len,
1265				 int *failure)
1266{
1267	wpa_hexdump(MSG_DEBUG, "TNC: SoH TLV", soh_tlv, soh_tlv_len);
1268	*failure = 0;
1269
1270	/* TODO: return MS-SoH Response TLV */
1271
1272	return NULL;
1273}
1274