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