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	unsigned 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((unsigned char *) pos, os_strlen(pos),
682				decoded_len);
683	*pos2 = '<';
684	if (decoded == NULL) {
685		wpa_printf(MSG_DEBUG, "TNC: Failed to decode Base64 data");
686	}
687
688	return decoded;
689}
690
691
692static enum tncs_process_res tncs_derive_recommendation(struct tncs_data *tncs)
693{
694	enum IMV_Action_Recommendation rec;
695	struct tnc_if_imv *imv;
696	TNC_ConnectionState state;
697	char *txt;
698
699	wpa_printf(MSG_DEBUG, "TNC: No more messages from IMVs");
700
701	if (tncs->done)
702		return TNCCS_PROCESS_OK_NO_RECOMMENDATION;
703
704	tncs_solicit_recommendation(tncs);
705
706	/* Select the most restrictive recommendation */
707	rec = TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION;
708	for (imv = tncs->imv; imv; imv = imv->next) {
709		TNC_IMV_Action_Recommendation irec;
710		irec = tncs->imv_data[imv->imvID].recommendation;
711		if (irec == TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS)
712			rec = TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS;
713		if (irec == TNC_IMV_ACTION_RECOMMENDATION_ISOLATE &&
714		    rec != TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS)
715			rec = TNC_IMV_ACTION_RECOMMENDATION_ISOLATE;
716		if (irec == TNC_IMV_ACTION_RECOMMENDATION_ALLOW &&
717		    rec == TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION)
718			rec = TNC_IMV_ACTION_RECOMMENDATION_ALLOW;
719	}
720
721	wpa_printf(MSG_DEBUG, "TNC: Recommendation: %d", rec);
722	tncs->recommendation = rec;
723	tncs->done = 1;
724
725	txt = NULL;
726	switch (rec) {
727	case TNC_IMV_ACTION_RECOMMENDATION_ALLOW:
728	case TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION:
729		txt = "allow";
730		state = TNC_CONNECTION_STATE_ACCESS_ALLOWED;
731		break;
732	case TNC_IMV_ACTION_RECOMMENDATION_ISOLATE:
733		txt = "isolate";
734		state = TNC_CONNECTION_STATE_ACCESS_ISOLATED;
735		break;
736	case TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS:
737		txt = "none";
738		state = TNC_CONNECTION_STATE_ACCESS_NONE;
739		break;
740	default:
741		state = TNC_CONNECTION_STATE_ACCESS_ALLOWED;
742		break;
743	}
744
745	if (txt) {
746		os_free(tncs->tncs_message);
747		tncs->tncs_message = os_zalloc(200);
748		if (tncs->tncs_message) {
749			os_snprintf(tncs->tncs_message, 199,
750				    "<TNCC-TNCS-Message><Type>%08X</Type>"
751				    "<XML><TNCCS-Recommendation type=\"%s\">"
752				    "</TNCCS-Recommendation></XML>"
753				    "</TNCC-TNCS-Message>",
754				    TNC_TNCCS_RECOMMENDATION, txt);
755		}
756	}
757
758	for (imv = tncs->imv; imv; imv = imv->next) {
759		tncs_imv_notify_connection_change(imv, tncs->connectionID,
760						  state);
761	}
762
763	switch (rec) {
764	case TNC_IMV_ACTION_RECOMMENDATION_ALLOW:
765		return TNCCS_RECOMMENDATION_ALLOW;
766	case TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS:
767		return TNCCS_RECOMMENDATION_NO_ACCESS;
768	case TNC_IMV_ACTION_RECOMMENDATION_ISOLATE:
769		return TNCCS_RECOMMENDATION_ISOLATE;
770	case TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION:
771		return TNCCS_RECOMMENDATION_NO_RECOMMENDATION;
772	default:
773		return TNCCS_PROCESS_ERROR;
774	}
775}
776
777
778enum tncs_process_res tncs_process_if_tnccs(struct tncs_data *tncs,
779					    const u8 *msg, size_t len)
780{
781	char *buf, *start, *end, *pos, *pos2, *payload;
782	unsigned int batch_id;
783	unsigned char *decoded;
784	size_t decoded_len;
785
786	buf = dup_binstr(msg, len);
787	if (buf == NULL)
788		return TNCCS_PROCESS_ERROR;
789
790	start = os_strstr(buf, "<TNCCS-Batch ");
791	end = os_strstr(buf, "</TNCCS-Batch>");
792	if (start == NULL || end == NULL || start > end) {
793		os_free(buf);
794		return TNCCS_PROCESS_ERROR;
795	}
796
797	start += 13;
798	while (*start == ' ')
799		start++;
800	*end = '\0';
801
802	pos = os_strstr(start, "BatchId=");
803	if (pos == NULL) {
804		os_free(buf);
805		return TNCCS_PROCESS_ERROR;
806	}
807
808	pos += 8;
809	if (*pos == '"')
810		pos++;
811	batch_id = atoi(pos);
812	wpa_printf(MSG_DEBUG, "TNC: Received IF-TNCCS BatchId=%u",
813		   batch_id);
814	if (batch_id != tncs->last_batchid + 1) {
815		wpa_printf(MSG_DEBUG, "TNC: Unexpected IF-TNCCS BatchId "
816			   "%u (expected %u)",
817			   batch_id, tncs->last_batchid + 1);
818		os_free(buf);
819		return TNCCS_PROCESS_ERROR;
820	}
821	tncs->last_batchid = batch_id;
822
823	while (*pos != '\0' && *pos != '>')
824		pos++;
825	if (*pos == '\0') {
826		os_free(buf);
827		return TNCCS_PROCESS_ERROR;
828	}
829	pos++;
830	payload = start;
831
832	/*
833	 * <IMC-IMV-Message>
834	 * <Type>01234567</Type>
835	 * <Base64>foo==</Base64>
836	 * </IMC-IMV-Message>
837	 */
838
839	while (*start) {
840		char *endpos;
841		unsigned int type;
842
843		pos = os_strstr(start, "<IMC-IMV-Message>");
844		if (pos == NULL)
845			break;
846		start = pos + 17;
847		end = os_strstr(start, "</IMC-IMV-Message>");
848		if (end == NULL)
849			break;
850		*end = '\0';
851		endpos = end;
852		end += 18;
853
854		if (tncs_get_type(start, &type) < 0) {
855			*endpos = '<';
856			start = end;
857			continue;
858		}
859		wpa_printf(MSG_DEBUG, "TNC: IMC-IMV-Message Type 0x%x", type);
860
861		decoded = tncs_get_base64(start, &decoded_len);
862		if (decoded == NULL) {
863			*endpos = '<';
864			start = end;
865			continue;
866		}
867
868		tncs_send_to_imvs(tncs, type, decoded, decoded_len);
869
870		os_free(decoded);
871
872		start = end;
873	}
874
875	/*
876	 * <TNCC-TNCS-Message>
877	 * <Type>01234567</Type>
878	 * <XML><TNCCS-Foo type="foo"></TNCCS-Foo></XML>
879	 * <Base64>foo==</Base64>
880	 * </TNCC-TNCS-Message>
881	 */
882
883	start = payload;
884	while (*start) {
885		unsigned int type;
886		char *xml, *xmlend, *endpos;
887
888		pos = os_strstr(start, "<TNCC-TNCS-Message>");
889		if (pos == NULL)
890			break;
891		start = pos + 19;
892		end = os_strstr(start, "</TNCC-TNCS-Message>");
893		if (end == NULL)
894			break;
895		*end = '\0';
896		endpos = end;
897		end += 20;
898
899		if (tncs_get_type(start, &type) < 0) {
900			*endpos = '<';
901			start = end;
902			continue;
903		}
904		wpa_printf(MSG_DEBUG, "TNC: TNCC-TNCS-Message Type 0x%x",
905			   type);
906
907		/* Base64 OR XML */
908		decoded = NULL;
909		xml = NULL;
910		xmlend = NULL;
911		pos = os_strstr(start, "<XML>");
912		if (pos) {
913			pos += 5;
914			pos2 = os_strstr(pos, "</XML>");
915			if (pos2 == NULL) {
916				*endpos = '<';
917				start = end;
918				continue;
919			}
920			xmlend = pos2;
921			xml = pos;
922		} else {
923			decoded = tncs_get_base64(start, &decoded_len);
924			if (decoded == NULL) {
925				*endpos = '<';
926				start = end;
927				continue;
928			}
929		}
930
931		if (decoded) {
932			wpa_hexdump_ascii(MSG_MSGDUMP,
933					  "TNC: TNCC-TNCS-Message Base64",
934					  decoded, decoded_len);
935			os_free(decoded);
936		}
937
938		if (xml) {
939			wpa_hexdump_ascii(MSG_MSGDUMP,
940					  "TNC: TNCC-TNCS-Message XML",
941					  (unsigned char *) xml,
942					  xmlend - xml);
943		}
944
945		start = end;
946	}
947
948	os_free(buf);
949
950	tncs_batch_ending(tncs);
951
952	if (tncs_total_send_len(tncs) == 0)
953		return tncs_derive_recommendation(tncs);
954
955	return TNCCS_PROCESS_OK_NO_RECOMMENDATION;
956}
957
958
959static struct tnc_if_imv * tncs_parse_imv(int id, char *start, char *end,
960					  int *error)
961{
962	struct tnc_if_imv *imv;
963	char *pos, *pos2;
964
965	if (id >= TNC_MAX_IMV_ID) {
966		wpa_printf(MSG_DEBUG, "TNC: Too many IMVs");
967		return NULL;
968	}
969
970	imv = os_zalloc(sizeof(*imv));
971	if (imv == NULL) {
972		*error = 1;
973		return NULL;
974	}
975
976	imv->imvID = id;
977
978	pos = start;
979	wpa_printf(MSG_DEBUG, "TNC: Configured IMV: %s", pos);
980	if (pos + 1 >= end || *pos != '"') {
981		wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMV line '%s' "
982			   "(no starting quotation mark)", start);
983		os_free(imv);
984		return NULL;
985	}
986
987	pos++;
988	pos2 = pos;
989	while (pos2 < end && *pos2 != '"')
990		pos2++;
991	if (pos2 >= end) {
992		wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMV line '%s' "
993			   "(no ending quotation mark)", start);
994		os_free(imv);
995		return NULL;
996	}
997	*pos2 = '\0';
998	wpa_printf(MSG_DEBUG, "TNC: Name: '%s'", pos);
999	imv->name = os_strdup(pos);
1000
1001	pos = pos2 + 1;
1002	if (pos >= end || *pos != ' ') {
1003		wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMV line '%s' "
1004			   "(no space after name)", start);
1005		os_free(imv);
1006		return NULL;
1007	}
1008
1009	pos++;
1010	wpa_printf(MSG_DEBUG, "TNC: IMV file: '%s'", pos);
1011	imv->path = os_strdup(pos);
1012
1013	return imv;
1014}
1015
1016
1017static int tncs_read_config(struct tncs_global *global)
1018{
1019	char *config, *end, *pos, *line_end;
1020	size_t config_len;
1021	struct tnc_if_imv *imv, *last;
1022	int id = 0;
1023
1024	last = NULL;
1025
1026	config = os_readfile(TNC_CONFIG_FILE, &config_len);
1027	if (config == NULL) {
1028		wpa_printf(MSG_ERROR, "TNC: Could not open TNC configuration "
1029			   "file '%s'", TNC_CONFIG_FILE);
1030		return -1;
1031	}
1032
1033	end = config + config_len;
1034	for (pos = config; pos < end; pos = line_end + 1) {
1035		line_end = pos;
1036		while (*line_end != '\n' && *line_end != '\r' &&
1037		       line_end < end)
1038			line_end++;
1039		*line_end = '\0';
1040
1041		if (os_strncmp(pos, "IMV ", 4) == 0) {
1042			int error = 0;
1043
1044			imv = tncs_parse_imv(id++, pos + 4, line_end, &error);
1045			if (error)
1046				return -1;
1047			if (imv) {
1048				if (last == NULL)
1049					global->imv = imv;
1050				else
1051					last->next = imv;
1052				last = imv;
1053			}
1054		}
1055	}
1056
1057	os_free(config);
1058
1059	return 0;
1060}
1061
1062
1063struct tncs_data * tncs_init(void)
1064{
1065	struct tncs_data *tncs;
1066
1067	if (tncs_global_data == NULL)
1068		return NULL;
1069
1070	tncs = os_zalloc(sizeof(*tncs));
1071	if (tncs == NULL)
1072		return NULL;
1073	tncs->imv = tncs_global_data->imv;
1074	tncs->connectionID = tncs_global_data->next_conn_id++;
1075	tncs->next = tncs_global_data->connections;
1076	tncs_global_data->connections = tncs;
1077
1078	return tncs;
1079}
1080
1081
1082void tncs_deinit(struct tncs_data *tncs)
1083{
1084	int i;
1085	struct tncs_data *prev, *conn;
1086
1087	if (tncs == NULL)
1088		return;
1089
1090	for (i = 0; i < TNC_MAX_IMV_ID; i++)
1091		os_free(tncs->imv_data[i].imv_send);
1092
1093	prev = NULL;
1094	conn = tncs_global_data->connections;
1095	while (conn) {
1096		if (conn == tncs) {
1097			if (prev)
1098				prev->next = tncs->next;
1099			else
1100				tncs_global_data->connections = tncs->next;
1101			break;
1102		}
1103		prev = conn;
1104		conn = conn->next;
1105	}
1106
1107	os_free(tncs->tncs_message);
1108	os_free(tncs);
1109}
1110
1111
1112int tncs_global_init(void)
1113{
1114	struct tnc_if_imv *imv;
1115
1116	if (tncs_global_data)
1117		return 0;
1118
1119	tncs_global_data = os_zalloc(sizeof(*tncs_global_data));
1120	if (tncs_global_data == NULL)
1121		return -1;
1122
1123	if (tncs_read_config(tncs_global_data) < 0) {
1124		wpa_printf(MSG_ERROR, "TNC: Failed to read TNC configuration");
1125		goto failed;
1126	}
1127
1128	for (imv = tncs_global_data->imv; imv; imv = imv->next) {
1129		if (tncs_load_imv(imv)) {
1130			wpa_printf(MSG_ERROR, "TNC: Failed to load IMV '%s'",
1131				   imv->name);
1132			goto failed;
1133		}
1134	}
1135
1136	return 0;
1137
1138failed:
1139	tncs_global_deinit();
1140	return -1;
1141}
1142
1143
1144void tncs_global_deinit(void)
1145{
1146	struct tnc_if_imv *imv, *prev;
1147
1148	if (tncs_global_data == NULL)
1149		return;
1150
1151	imv = tncs_global_data->imv;
1152	while (imv) {
1153		tncs_unload_imv(imv);
1154
1155		prev = imv;
1156		imv = imv->next;
1157		os_free(prev);
1158	}
1159
1160	os_free(tncs_global_data);
1161	tncs_global_data = NULL;
1162}
1163
1164
1165struct wpabuf * tncs_build_soh_request(void)
1166{
1167	struct wpabuf *buf;
1168
1169	/*
1170	 * Build a SoH Request TLV (to be used inside SoH EAP Extensions
1171	 * Method)
1172	 */
1173
1174	buf = wpabuf_alloc(8 + 4);
1175	if (buf == NULL)
1176		return NULL;
1177
1178	/* Vendor-Specific TLV (Microsoft) - SoH Request */
1179	wpabuf_put_be16(buf, EAP_TLV_VENDOR_SPECIFIC_TLV); /* TLV Type */
1180	wpabuf_put_be16(buf, 8); /* Length */
1181
1182	wpabuf_put_be32(buf, EAP_VENDOR_MICROSOFT); /* Vendor_Id */
1183
1184	wpabuf_put_be16(buf, 0x02); /* TLV Type - SoH Request TLV */
1185	wpabuf_put_be16(buf, 0); /* Length */
1186
1187	return buf;
1188}
1189
1190
1191struct wpabuf * tncs_process_soh(const u8 *soh_tlv, size_t soh_tlv_len,
1192				 int *failure)
1193{
1194	wpa_hexdump(MSG_DEBUG, "TNC: SoH TLV", soh_tlv, soh_tlv_len);
1195	*failure = 0;
1196
1197	/* TODO: return MS-SoH Response TLV */
1198
1199	return NULL;
1200}
1201