xref: /freebsd/contrib/wpa/src/eap_server/tncs.c (revision 63d1fd5970ec814904aa0f4580b10a0d302d08b2)
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 
37 struct 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 
77 struct 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 
96 struct tncs_global {
97 	struct tnc_if_imv *imv;
98 	TNC_ConnectionID next_conn_id;
99 	struct tncs_data *connections;
100 };
101 
102 static struct tncs_global *tncs_global_data = NULL;
103 
104 
105 static 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 
121 static 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 */
143 TNC_Result TNC_TNCS_ReportMessageTypes(
144 	TNC_IMVID imvID,
145 	TNC_MessageTypeList supportedTypes,
146 	TNC_UInt32 typeCount)
147 {
148 	TNC_UInt32 i;
149 	struct tnc_if_imv *imv;
150 
151 	wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_ReportMessageTypes(imvID=%lu "
152 		   "typeCount=%lu)",
153 		   (unsigned long) imvID, (unsigned long) typeCount);
154 
155 	for (i = 0; i < typeCount; i++) {
156 		wpa_printf(MSG_DEBUG, "TNC: supportedTypes[%lu] = %lu",
157 			   i, supportedTypes[i]);
158 	}
159 
160 	imv = tncs_get_imv(imvID);
161 	if (imv == NULL)
162 		return TNC_RESULT_INVALID_PARAMETER;
163 	os_free(imv->supported_types);
164 	imv->supported_types =
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 
176 TNC_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 
225 TNC_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 
236 TNC_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 
263 TNC_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 
277 TNC_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 
290 TNC_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 
325 static 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 
335 static 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 
374 static 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 
390 static 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 
407 static 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 
421 static 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 
440 static 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 
471 static 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 
478 static 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 
489 static 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 
513 static 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 
537 static 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 
555 static 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 
574 void 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 
595 size_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 
608 u8 * 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 
636 char * 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 
647 char * 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 
657 static 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 
668 static 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 
694 static 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 
780 enum 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 
961 static 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 
1019 static 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 
1065 struct 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 
1084 void 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 
1114 int 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 
1140 failed:
1141 	tncs_global_deinit();
1142 	return -1;
1143 }
1144 
1145 
1146 void 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 
1167 struct 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 
1193 struct 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