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