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