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