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