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