1/*
2 * EAP-TNC - TNCC (IF-IMC and IF-TNCCS)
3 * Copyright (c) 2007, 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#ifndef CONFIG_NATIVE_WINDOWS
11#include <dlfcn.h>
12#endif /* CONFIG_NATIVE_WINDOWS */
13
14#include "common.h"
15#include "base64.h"
16#include "common/tnc.h"
17#include "tncc.h"
18#include "eap_common/eap_tlv_common.h"
19#include "eap_common/eap_defs.h"
20
21
22#ifdef UNICODE
23#define TSTR "%S"
24#else /* UNICODE */
25#define TSTR "%s"
26#endif /* UNICODE */
27
28
29#ifndef TNC_CONFIG_FILE
30#define TNC_CONFIG_FILE "/etc/tnc_config"
31#endif /* TNC_CONFIG_FILE */
32#define TNC_WINREG_PATH TEXT("SOFTWARE\\Trusted Computing Group\\TNC\\IMCs")
33#define IF_TNCCS_START \
34"<?xml version=\"1.0\"?>\n" \
35"<TNCCS-Batch BatchId=\"%d\" Recipient=\"TNCS\" " \
36"xmlns=\"http://www.trustedcomputinggroup.org/IWG/TNC/1_0/IF_TNCCS#\" " \
37"xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " \
38"xsi:schemaLocation=\"http://www.trustedcomputinggroup.org/IWG/TNC/1_0/" \
39"IF_TNCCS# https://www.trustedcomputinggroup.org/XML/SCHEMA/TNCCS_1.0.xsd\">\n"
40#define IF_TNCCS_END "\n</TNCCS-Batch>"
41
42/* TNC IF-IMC */
43
44/* IF-TNCCS-SOH - SSoH and SSoHR Attributes */
45enum {
46	SSOH_MS_MACHINE_INVENTORY = 1,
47	SSOH_MS_QUARANTINE_STATE = 2,
48	SSOH_MS_PACKET_INFO = 3,
49	SSOH_MS_SYSTEMGENERATED_IDS = 4,
50	SSOH_MS_MACHINENAME = 5,
51	SSOH_MS_CORRELATIONID = 6,
52	SSOH_MS_INSTALLED_SHVS = 7,
53	SSOH_MS_MACHINE_INVENTORY_EX = 8
54};
55
56struct tnc_if_imc {
57	struct tnc_if_imc *next;
58	char *name;
59	char *path;
60	void *dlhandle; /* from dlopen() */
61	TNC_IMCID imcID;
62	TNC_ConnectionID connectionID;
63	TNC_MessageTypeList supported_types;
64	size_t num_supported_types;
65	u8 *imc_send;
66	size_t imc_send_len;
67
68	/* Functions implemented by IMCs (with TNC_IMC_ prefix) */
69	TNC_Result (*Initialize)(
70		TNC_IMCID imcID,
71		TNC_Version minVersion,
72		TNC_Version maxVersion,
73		TNC_Version *pOutActualVersion);
74	TNC_Result (*NotifyConnectionChange)(
75		TNC_IMCID imcID,
76		TNC_ConnectionID connectionID,
77		TNC_ConnectionState newState);
78	TNC_Result (*BeginHandshake)(
79		TNC_IMCID imcID,
80		TNC_ConnectionID connectionID);
81	TNC_Result (*ReceiveMessage)(
82		TNC_IMCID imcID,
83		TNC_ConnectionID connectionID,
84		TNC_BufferReference messageBuffer,
85		TNC_UInt32 messageLength,
86		TNC_MessageType messageType);
87	TNC_Result (*BatchEnding)(
88		TNC_IMCID imcID,
89		TNC_ConnectionID connectionID);
90	TNC_Result (*Terminate)(TNC_IMCID imcID);
91	TNC_Result (*ProvideBindFunction)(
92		TNC_IMCID imcID,
93		TNC_TNCC_BindFunctionPointer bindFunction);
94};
95
96struct tncc_data {
97	struct tnc_if_imc *imc;
98	unsigned int last_batchid;
99};
100
101#define TNC_MAX_IMC_ID 10
102static struct tnc_if_imc *tnc_imc[TNC_MAX_IMC_ID] = { NULL };
103
104
105/* TNCC functions that IMCs can call */
106
107TNC_Result TNC_TNCC_ReportMessageTypes(
108	TNC_IMCID imcID,
109	TNC_MessageTypeList supportedTypes,
110	TNC_UInt32 typeCount)
111{
112	TNC_UInt32 i;
113	struct tnc_if_imc *imc;
114
115	wpa_printf(MSG_DEBUG, "TNC: TNC_TNCC_ReportMessageTypes(imcID=%lu "
116		   "typeCount=%lu)",
117		   (unsigned long) imcID, (unsigned long) typeCount);
118
119	for (i = 0; i < typeCount; i++) {
120		wpa_printf(MSG_DEBUG, "TNC: supportedTypes[%lu] = %lu",
121			   i, supportedTypes[i]);
122	}
123
124	if (imcID >= TNC_MAX_IMC_ID || tnc_imc[imcID] == NULL)
125		return TNC_RESULT_INVALID_PARAMETER;
126
127	imc = tnc_imc[imcID];
128	os_free(imc->supported_types);
129	imc->supported_types =
130		os_malloc(typeCount * sizeof(TNC_MessageType));
131	if (imc->supported_types == NULL)
132		return TNC_RESULT_FATAL;
133	os_memcpy(imc->supported_types, supportedTypes,
134		  typeCount * sizeof(TNC_MessageType));
135	imc->num_supported_types = typeCount;
136
137	return TNC_RESULT_SUCCESS;
138}
139
140
141TNC_Result TNC_TNCC_SendMessage(
142	TNC_IMCID imcID,
143	TNC_ConnectionID connectionID,
144	TNC_BufferReference message,
145	TNC_UInt32 messageLength,
146	TNC_MessageType messageType)
147{
148	struct tnc_if_imc *imc;
149	unsigned char *b64;
150	size_t b64len;
151
152	wpa_printf(MSG_DEBUG, "TNC: TNC_TNCC_SendMessage(imcID=%lu "
153		   "connectionID=%lu messageType=%lu)",
154		   imcID, connectionID, messageType);
155	wpa_hexdump_ascii(MSG_DEBUG, "TNC: TNC_TNCC_SendMessage",
156			  message, messageLength);
157
158	if (imcID >= TNC_MAX_IMC_ID || tnc_imc[imcID] == NULL)
159		return TNC_RESULT_INVALID_PARAMETER;
160
161	b64 = base64_encode(message, messageLength, &b64len);
162	if (b64 == NULL)
163		return TNC_RESULT_FATAL;
164
165	imc = tnc_imc[imcID];
166	os_free(imc->imc_send);
167	imc->imc_send_len = 0;
168	imc->imc_send = os_zalloc(b64len + 100);
169	if (imc->imc_send == NULL) {
170		os_free(b64);
171		return TNC_RESULT_OTHER;
172	}
173
174	imc->imc_send_len =
175		os_snprintf((char *) imc->imc_send, b64len + 100,
176			    "<IMC-IMV-Message><Type>%08X</Type>"
177			    "<Base64>%s</Base64></IMC-IMV-Message>",
178			    (unsigned int) messageType, b64);
179
180	os_free(b64);
181
182	return TNC_RESULT_SUCCESS;
183}
184
185
186TNC_Result TNC_TNCC_RequestHandshakeRetry(
187	TNC_IMCID imcID,
188	TNC_ConnectionID connectionID,
189	TNC_RetryReason reason)
190{
191	wpa_printf(MSG_DEBUG, "TNC: TNC_TNCC_RequestHandshakeRetry");
192
193	if (imcID >= TNC_MAX_IMC_ID || tnc_imc[imcID] == NULL)
194		return TNC_RESULT_INVALID_PARAMETER;
195
196	/*
197	 * TODO: trigger a call to eapol_sm_request_reauth(). This would
198	 * require that the IMC continues to be loaded in memory afer
199	 * authentication..
200	 */
201
202	return TNC_RESULT_SUCCESS;
203}
204
205
206TNC_Result TNC_9048_LogMessage(TNC_IMCID imcID, TNC_UInt32 severity,
207			       const char *message)
208{
209	wpa_printf(MSG_DEBUG, "TNC: TNC_9048_LogMessage(imcID=%lu "
210		   "severity==%lu message='%s')",
211		   imcID, severity, message);
212	return TNC_RESULT_SUCCESS;
213}
214
215
216TNC_Result TNC_9048_UserMessage(TNC_IMCID imcID, TNC_ConnectionID connectionID,
217				const char *message)
218{
219	wpa_printf(MSG_DEBUG, "TNC: TNC_9048_UserMessage(imcID=%lu "
220		   "connectionID==%lu message='%s')",
221		   imcID, connectionID, message);
222	return TNC_RESULT_SUCCESS;
223}
224
225
226TNC_Result TNC_TNCC_BindFunction(
227	TNC_IMCID imcID,
228	char *functionName,
229	void **pOutfunctionPointer)
230{
231	wpa_printf(MSG_DEBUG, "TNC: TNC_TNCC_BindFunction(imcID=%lu, "
232		   "functionName='%s')", (unsigned long) imcID, functionName);
233
234	if (imcID >= TNC_MAX_IMC_ID || tnc_imc[imcID] == NULL)
235		return TNC_RESULT_INVALID_PARAMETER;
236
237	if (pOutfunctionPointer == NULL)
238		return TNC_RESULT_INVALID_PARAMETER;
239
240	if (os_strcmp(functionName, "TNC_TNCC_ReportMessageTypes") == 0)
241		*pOutfunctionPointer = TNC_TNCC_ReportMessageTypes;
242	else if (os_strcmp(functionName, "TNC_TNCC_SendMessage") == 0)
243		*pOutfunctionPointer = TNC_TNCC_SendMessage;
244	else if (os_strcmp(functionName, "TNC_TNCC_RequestHandshakeRetry") ==
245		 0)
246		*pOutfunctionPointer = TNC_TNCC_RequestHandshakeRetry;
247	else if (os_strcmp(functionName, "TNC_9048_LogMessage") == 0)
248		*pOutfunctionPointer = TNC_9048_LogMessage;
249	else if (os_strcmp(functionName, "TNC_9048_UserMessage") == 0)
250		*pOutfunctionPointer = TNC_9048_UserMessage;
251	else
252		*pOutfunctionPointer = NULL;
253
254	return TNC_RESULT_SUCCESS;
255}
256
257
258static void * tncc_get_sym(void *handle, char *func)
259{
260	void *fptr;
261
262#ifdef CONFIG_NATIVE_WINDOWS
263#ifdef _WIN32_WCE
264	fptr = GetProcAddressA(handle, func);
265#else /* _WIN32_WCE */
266	fptr = GetProcAddress(handle, func);
267#endif /* _WIN32_WCE */
268#else /* CONFIG_NATIVE_WINDOWS */
269	fptr = dlsym(handle, func);
270#endif /* CONFIG_NATIVE_WINDOWS */
271
272	return fptr;
273}
274
275
276static int tncc_imc_resolve_funcs(struct tnc_if_imc *imc)
277{
278	void *handle = imc->dlhandle;
279
280	/* Mandatory IMC functions */
281	imc->Initialize = tncc_get_sym(handle, "TNC_IMC_Initialize");
282	if (imc->Initialize == NULL) {
283		wpa_printf(MSG_ERROR, "TNC: IMC does not export "
284			   "TNC_IMC_Initialize");
285		return -1;
286	}
287
288	imc->BeginHandshake = tncc_get_sym(handle, "TNC_IMC_BeginHandshake");
289	if (imc->BeginHandshake == NULL) {
290		wpa_printf(MSG_ERROR, "TNC: IMC does not export "
291			   "TNC_IMC_BeginHandshake");
292		return -1;
293	}
294
295	imc->ProvideBindFunction =
296		tncc_get_sym(handle, "TNC_IMC_ProvideBindFunction");
297	if (imc->ProvideBindFunction == NULL) {
298		wpa_printf(MSG_ERROR, "TNC: IMC does not export "
299			   "TNC_IMC_ProvideBindFunction");
300		return -1;
301	}
302
303	/* Optional IMC functions */
304	imc->NotifyConnectionChange =
305		tncc_get_sym(handle, "TNC_IMC_NotifyConnectionChange");
306	imc->ReceiveMessage = tncc_get_sym(handle, "TNC_IMC_ReceiveMessage");
307	imc->BatchEnding = tncc_get_sym(handle, "TNC_IMC_BatchEnding");
308	imc->Terminate = tncc_get_sym(handle, "TNC_IMC_Terminate");
309
310	return 0;
311}
312
313
314static int tncc_imc_initialize(struct tnc_if_imc *imc)
315{
316	TNC_Result res;
317	TNC_Version imc_ver;
318
319	wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMC_Initialize for IMC '%s'",
320		   imc->name);
321	res = imc->Initialize(imc->imcID, TNC_IFIMC_VERSION_1,
322			      TNC_IFIMC_VERSION_1, &imc_ver);
323	wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_Initialize: res=%lu imc_ver=%lu",
324		   (unsigned long) res, (unsigned long) imc_ver);
325
326	return res == TNC_RESULT_SUCCESS ? 0 : -1;
327}
328
329
330static int tncc_imc_terminate(struct tnc_if_imc *imc)
331{
332	TNC_Result res;
333
334	if (imc->Terminate == NULL)
335		return 0;
336
337	wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMC_Terminate for IMC '%s'",
338		   imc->name);
339	res = imc->Terminate(imc->imcID);
340	wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_Terminate: %lu",
341		   (unsigned long) res);
342
343	return res == TNC_RESULT_SUCCESS ? 0 : -1;
344}
345
346
347static int tncc_imc_provide_bind_function(struct tnc_if_imc *imc)
348{
349	TNC_Result res;
350
351	wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMC_ProvideBindFunction for "
352		   "IMC '%s'", imc->name);
353	res = imc->ProvideBindFunction(imc->imcID, TNC_TNCC_BindFunction);
354	wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_ProvideBindFunction: res=%lu",
355		   (unsigned long) res);
356
357	return res == TNC_RESULT_SUCCESS ? 0 : -1;
358}
359
360
361static int tncc_imc_notify_connection_change(struct tnc_if_imc *imc,
362					     TNC_ConnectionState state)
363{
364	TNC_Result res;
365
366	if (imc->NotifyConnectionChange == NULL)
367		return 0;
368
369	wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMC_NotifyConnectionChange(%d)"
370		   " for IMC '%s'", (int) state, imc->name);
371	res = imc->NotifyConnectionChange(imc->imcID, imc->connectionID,
372					  state);
373	wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_NotifyConnectionChange: %lu",
374		   (unsigned long) res);
375
376	return res == TNC_RESULT_SUCCESS ? 0 : -1;
377}
378
379
380static int tncc_imc_begin_handshake(struct tnc_if_imc *imc)
381{
382	TNC_Result res;
383
384	wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMC_BeginHandshake for IMC "
385		   "'%s'", imc->name);
386	res = imc->BeginHandshake(imc->imcID, imc->connectionID);
387	wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_BeginHandshake: %lu",
388		   (unsigned long) res);
389
390	return res == TNC_RESULT_SUCCESS ? 0 : -1;
391}
392
393
394static int tncc_load_imc(struct tnc_if_imc *imc)
395{
396	if (imc->path == NULL) {
397		wpa_printf(MSG_DEBUG, "TNC: No IMC configured");
398		return -1;
399	}
400
401	wpa_printf(MSG_DEBUG, "TNC: Opening IMC: %s (%s)",
402		   imc->name, imc->path);
403#ifdef CONFIG_NATIVE_WINDOWS
404#ifdef UNICODE
405	{
406		TCHAR *lib = wpa_strdup_tchar(imc->path);
407		if (lib == NULL)
408			return -1;
409		imc->dlhandle = LoadLibrary(lib);
410		os_free(lib);
411	}
412#else /* UNICODE */
413	imc->dlhandle = LoadLibrary(imc->path);
414#endif /* UNICODE */
415	if (imc->dlhandle == NULL) {
416		wpa_printf(MSG_ERROR, "TNC: Failed to open IMC '%s' (%s): %d",
417			   imc->name, imc->path, (int) GetLastError());
418		return -1;
419	}
420#else /* CONFIG_NATIVE_WINDOWS */
421	imc->dlhandle = dlopen(imc->path, RTLD_LAZY);
422	if (imc->dlhandle == NULL) {
423		wpa_printf(MSG_ERROR, "TNC: Failed to open IMC '%s' (%s): %s",
424			   imc->name, imc->path, dlerror());
425		return -1;
426	}
427#endif /* CONFIG_NATIVE_WINDOWS */
428
429	if (tncc_imc_resolve_funcs(imc) < 0) {
430		wpa_printf(MSG_ERROR, "TNC: Failed to resolve IMC functions");
431		return -1;
432	}
433
434	if (tncc_imc_initialize(imc) < 0 ||
435	    tncc_imc_provide_bind_function(imc) < 0) {
436		wpa_printf(MSG_ERROR, "TNC: Failed to initialize IMC");
437		return -1;
438	}
439
440	return 0;
441}
442
443
444static void tncc_unload_imc(struct tnc_if_imc *imc)
445{
446	tncc_imc_terminate(imc);
447	tnc_imc[imc->imcID] = NULL;
448
449	if (imc->dlhandle) {
450#ifdef CONFIG_NATIVE_WINDOWS
451		FreeLibrary(imc->dlhandle);
452#else /* CONFIG_NATIVE_WINDOWS */
453		dlclose(imc->dlhandle);
454#endif /* CONFIG_NATIVE_WINDOWS */
455	}
456	os_free(imc->name);
457	os_free(imc->path);
458	os_free(imc->supported_types);
459	os_free(imc->imc_send);
460}
461
462
463static int tncc_supported_type(struct tnc_if_imc *imc, unsigned int type)
464{
465	size_t i;
466	unsigned int vendor, subtype;
467
468	if (imc == NULL || imc->supported_types == NULL)
469		return 0;
470
471	vendor = type >> 8;
472	subtype = type & 0xff;
473
474	for (i = 0; i < imc->num_supported_types; i++) {
475		unsigned int svendor, ssubtype;
476		svendor = imc->supported_types[i] >> 8;
477		ssubtype = imc->supported_types[i] & 0xff;
478		if ((vendor == svendor || svendor == TNC_VENDORID_ANY) &&
479		    (subtype == ssubtype || ssubtype == TNC_SUBTYPE_ANY))
480			return 1;
481	}
482
483	return 0;
484}
485
486
487static void tncc_send_to_imcs(struct tncc_data *tncc, unsigned int type,
488			      const u8 *msg, size_t len)
489{
490	struct tnc_if_imc *imc;
491	TNC_Result res;
492
493	wpa_hexdump_ascii(MSG_MSGDUMP, "TNC: Message to IMC(s)", msg, len);
494
495	for (imc = tncc->imc; imc; imc = imc->next) {
496		if (imc->ReceiveMessage == NULL ||
497		    !tncc_supported_type(imc, type))
498			continue;
499
500		wpa_printf(MSG_DEBUG, "TNC: Call ReceiveMessage for IMC '%s'",
501			   imc->name);
502		res = imc->ReceiveMessage(imc->imcID, imc->connectionID,
503					  (TNC_BufferReference) msg, len,
504					  type);
505		wpa_printf(MSG_DEBUG, "TNC: ReceiveMessage: %lu",
506			   (unsigned long) res);
507	}
508}
509
510
511void tncc_init_connection(struct tncc_data *tncc)
512{
513	struct tnc_if_imc *imc;
514
515	for (imc = tncc->imc; imc; imc = imc->next) {
516		tncc_imc_notify_connection_change(
517			imc, TNC_CONNECTION_STATE_CREATE);
518		tncc_imc_notify_connection_change(
519			imc, TNC_CONNECTION_STATE_HANDSHAKE);
520
521		os_free(imc->imc_send);
522		imc->imc_send = NULL;
523		imc->imc_send_len = 0;
524
525		tncc_imc_begin_handshake(imc);
526	}
527}
528
529
530size_t tncc_total_send_len(struct tncc_data *tncc)
531{
532	struct tnc_if_imc *imc;
533
534	size_t len = 0;
535	for (imc = tncc->imc; imc; imc = imc->next)
536		len += imc->imc_send_len;
537	return len;
538}
539
540
541u8 * tncc_copy_send_buf(struct tncc_data *tncc, u8 *pos)
542{
543	struct tnc_if_imc *imc;
544
545	for (imc = tncc->imc; imc; imc = imc->next) {
546		if (imc->imc_send == NULL)
547			continue;
548
549		os_memcpy(pos, imc->imc_send, imc->imc_send_len);
550		pos += imc->imc_send_len;
551		os_free(imc->imc_send);
552		imc->imc_send = NULL;
553		imc->imc_send_len = 0;
554	}
555
556	return pos;
557}
558
559
560char * tncc_if_tnccs_start(struct tncc_data *tncc)
561{
562	char *buf = os_malloc(1000);
563	if (buf == NULL)
564		return NULL;
565	tncc->last_batchid++;
566	os_snprintf(buf, 1000, IF_TNCCS_START, tncc->last_batchid);
567	return buf;
568}
569
570
571char * tncc_if_tnccs_end(void)
572{
573	char *buf = os_malloc(100);
574	if (buf == NULL)
575		return NULL;
576	os_snprintf(buf, 100, IF_TNCCS_END);
577	return buf;
578}
579
580
581static void tncc_notify_recommendation(struct tncc_data *tncc,
582				       enum tncc_process_res res)
583{
584	TNC_ConnectionState state;
585	struct tnc_if_imc *imc;
586
587	switch (res) {
588	case TNCCS_RECOMMENDATION_ALLOW:
589		state = TNC_CONNECTION_STATE_ACCESS_ALLOWED;
590		break;
591	case TNCCS_RECOMMENDATION_NONE:
592		state = TNC_CONNECTION_STATE_ACCESS_NONE;
593		break;
594	case TNCCS_RECOMMENDATION_ISOLATE:
595		state = TNC_CONNECTION_STATE_ACCESS_ISOLATED;
596		break;
597	default:
598		state = TNC_CONNECTION_STATE_ACCESS_NONE;
599		break;
600	}
601
602	for (imc = tncc->imc; imc; imc = imc->next)
603		tncc_imc_notify_connection_change(imc, state);
604}
605
606
607static int tncc_get_type(char *start, unsigned int *type)
608{
609	char *pos = os_strstr(start, "<Type>");
610	if (pos == NULL)
611		return -1;
612	pos += 6;
613	*type = strtoul(pos, NULL, 16);
614	return 0;
615}
616
617
618static unsigned char * tncc_get_base64(char *start, size_t *decoded_len)
619{
620	char *pos, *pos2;
621	unsigned char *decoded;
622
623	pos = os_strstr(start, "<Base64>");
624	if (pos == NULL)
625		return NULL;
626
627	pos += 8;
628	pos2 = os_strstr(pos, "</Base64>");
629	if (pos2 == NULL)
630		return NULL;
631	*pos2 = '\0';
632
633	decoded = base64_decode((unsigned char *) pos, os_strlen(pos),
634				decoded_len);
635	*pos2 = '<';
636	if (decoded == NULL) {
637		wpa_printf(MSG_DEBUG, "TNC: Failed to decode Base64 data");
638	}
639
640	return decoded;
641}
642
643
644static enum tncc_process_res tncc_get_recommendation(char *start)
645{
646	char *pos, *pos2, saved;
647	int recom;
648
649	pos = os_strstr(start, "<TNCCS-Recommendation ");
650	if (pos == NULL)
651		return TNCCS_RECOMMENDATION_ERROR;
652
653	pos += 21;
654	pos = os_strstr(pos, " type=");
655	if (pos == NULL)
656		return TNCCS_RECOMMENDATION_ERROR;
657	pos += 6;
658
659	if (*pos == '"')
660		pos++;
661
662	pos2 = pos;
663	while (*pos2 != '\0' && *pos2 != '"' && *pos2 != '>')
664		pos2++;
665
666	if (*pos2 == '\0')
667		return TNCCS_RECOMMENDATION_ERROR;
668
669	saved = *pos2;
670	*pos2 = '\0';
671	wpa_printf(MSG_DEBUG, "TNC: TNCCS-Recommendation: '%s'", pos);
672
673	recom = TNCCS_RECOMMENDATION_ERROR;
674	if (os_strcmp(pos, "allow") == 0)
675		recom = TNCCS_RECOMMENDATION_ALLOW;
676	else if (os_strcmp(pos, "none") == 0)
677		recom = TNCCS_RECOMMENDATION_NONE;
678	else if (os_strcmp(pos, "isolate") == 0)
679		recom = TNCCS_RECOMMENDATION_ISOLATE;
680
681	*pos2 = saved;
682
683	return recom;
684}
685
686
687enum tncc_process_res tncc_process_if_tnccs(struct tncc_data *tncc,
688					    const u8 *msg, size_t len)
689{
690	char *buf, *start, *end, *pos, *pos2, *payload;
691	unsigned int batch_id;
692	unsigned char *decoded;
693	size_t decoded_len;
694	enum tncc_process_res res = TNCCS_PROCESS_OK_NO_RECOMMENDATION;
695	int recommendation_msg = 0;
696
697	wpa_hexdump_ascii(MSG_MSGDUMP, "TNC: Received IF-TNCCS message",
698			  msg, len);
699	buf = dup_binstr(msg, len);
700	if (buf == NULL)
701		return TNCCS_PROCESS_ERROR;
702
703	start = os_strstr(buf, "<TNCCS-Batch ");
704	end = os_strstr(buf, "</TNCCS-Batch>");
705	if (start == NULL || end == NULL || start > end) {
706		os_free(buf);
707		return TNCCS_PROCESS_ERROR;
708	}
709
710	start += 13;
711	while (*start == ' ')
712		start++;
713	*end = '\0';
714
715	pos = os_strstr(start, "BatchId=");
716	if (pos == NULL) {
717		os_free(buf);
718		return TNCCS_PROCESS_ERROR;
719	}
720
721	pos += 8;
722	if (*pos == '"')
723		pos++;
724	batch_id = atoi(pos);
725	wpa_printf(MSG_DEBUG, "TNC: Received IF-TNCCS BatchId=%u",
726		   batch_id);
727	if (batch_id != tncc->last_batchid + 1) {
728		wpa_printf(MSG_DEBUG, "TNC: Unexpected IF-TNCCS BatchId "
729			   "%u (expected %u)",
730			   batch_id, tncc->last_batchid + 1);
731		os_free(buf);
732		return TNCCS_PROCESS_ERROR;
733	}
734	tncc->last_batchid = batch_id;
735
736	while (*pos != '\0' && *pos != '>')
737		pos++;
738	if (*pos == '\0') {
739		os_free(buf);
740		return TNCCS_PROCESS_ERROR;
741	}
742	pos++;
743	payload = start;
744
745	/*
746	 * <IMC-IMV-Message>
747	 * <Type>01234567</Type>
748	 * <Base64>foo==</Base64>
749	 * </IMC-IMV-Message>
750	 */
751
752	while (*start) {
753		char *endpos;
754		unsigned int type;
755
756		pos = os_strstr(start, "<IMC-IMV-Message>");
757		if (pos == NULL)
758			break;
759		start = pos + 17;
760		end = os_strstr(start, "</IMC-IMV-Message>");
761		if (end == NULL)
762			break;
763		*end = '\0';
764		endpos = end;
765		end += 18;
766
767		if (tncc_get_type(start, &type) < 0) {
768			*endpos = '<';
769			start = end;
770			continue;
771		}
772		wpa_printf(MSG_DEBUG, "TNC: IMC-IMV-Message Type 0x%x", type);
773
774		decoded = tncc_get_base64(start, &decoded_len);
775		if (decoded == NULL) {
776			*endpos = '<';
777			start = end;
778			continue;
779		}
780
781		tncc_send_to_imcs(tncc, type, decoded, decoded_len);
782
783		os_free(decoded);
784
785		start = end;
786	}
787
788	/*
789	 * <TNCC-TNCS-Message>
790	 * <Type>01234567</Type>
791	 * <XML><TNCCS-Foo type="foo"></TNCCS-Foo></XML>
792	 * <Base64>foo==</Base64>
793	 * </TNCC-TNCS-Message>
794	 */
795
796	start = payload;
797	while (*start) {
798		unsigned int type;
799		char *xml, *xmlend, *endpos;
800
801		pos = os_strstr(start, "<TNCC-TNCS-Message>");
802		if (pos == NULL)
803			break;
804		start = pos + 19;
805		end = os_strstr(start, "</TNCC-TNCS-Message>");
806		if (end == NULL)
807			break;
808		*end = '\0';
809		endpos = end;
810		end += 20;
811
812		if (tncc_get_type(start, &type) < 0) {
813			*endpos = '<';
814			start = end;
815			continue;
816		}
817		wpa_printf(MSG_DEBUG, "TNC: TNCC-TNCS-Message Type 0x%x",
818			   type);
819
820		/* Base64 OR XML */
821		decoded = NULL;
822		xml = NULL;
823		xmlend = NULL;
824		pos = os_strstr(start, "<XML>");
825		if (pos) {
826			pos += 5;
827			pos2 = os_strstr(pos, "</XML>");
828			if (pos2 == NULL) {
829				*endpos = '<';
830				start = end;
831				continue;
832			}
833			xmlend = pos2;
834			xml = pos;
835		} else {
836			decoded = tncc_get_base64(start, &decoded_len);
837			if (decoded == NULL) {
838				*endpos = '<';
839				start = end;
840				continue;
841			}
842		}
843
844		if (decoded) {
845			wpa_hexdump_ascii(MSG_MSGDUMP,
846					  "TNC: TNCC-TNCS-Message Base64",
847					  decoded, decoded_len);
848			os_free(decoded);
849		}
850
851		if (xml) {
852			wpa_hexdump_ascii(MSG_MSGDUMP,
853					  "TNC: TNCC-TNCS-Message XML",
854					  (unsigned char *) xml,
855					  xmlend - xml);
856		}
857
858		if (type == TNC_TNCCS_RECOMMENDATION && xml) {
859			/*
860			 * <TNCCS-Recommendation type="allow">
861			 * </TNCCS-Recommendation>
862			 */
863			*xmlend = '\0';
864			res = tncc_get_recommendation(xml);
865			*xmlend = '<';
866			recommendation_msg = 1;
867		}
868
869		start = end;
870	}
871
872	os_free(buf);
873
874	if (recommendation_msg)
875		tncc_notify_recommendation(tncc, res);
876
877	return res;
878}
879
880
881#ifdef CONFIG_NATIVE_WINDOWS
882static int tncc_read_config_reg(struct tncc_data *tncc, HKEY hive)
883{
884	HKEY hk, hk2;
885	LONG ret;
886	DWORD i;
887	struct tnc_if_imc *imc, *last;
888	int j;
889
890	last = tncc->imc;
891	while (last && last->next)
892		last = last->next;
893
894	ret = RegOpenKeyEx(hive, TNC_WINREG_PATH, 0, KEY_ENUMERATE_SUB_KEYS,
895			   &hk);
896	if (ret != ERROR_SUCCESS)
897		return 0;
898
899	for (i = 0; ; i++) {
900		TCHAR name[255], *val;
901		DWORD namelen, buflen;
902
903		namelen = 255;
904		ret = RegEnumKeyEx(hk, i, name, &namelen, NULL, NULL, NULL,
905				   NULL);
906
907		if (ret == ERROR_NO_MORE_ITEMS)
908			break;
909
910		if (ret != ERROR_SUCCESS) {
911			wpa_printf(MSG_DEBUG, "TNC: RegEnumKeyEx failed: 0x%x",
912				   (unsigned int) ret);
913			break;
914		}
915
916		if (namelen >= 255)
917			namelen = 255 - 1;
918		name[namelen] = '\0';
919
920		wpa_printf(MSG_DEBUG, "TNC: IMC '" TSTR "'", name);
921
922		ret = RegOpenKeyEx(hk, name, 0, KEY_QUERY_VALUE, &hk2);
923		if (ret != ERROR_SUCCESS) {
924			wpa_printf(MSG_DEBUG, "Could not open IMC key '" TSTR
925				   "'", name);
926			continue;
927		}
928
929		ret = RegQueryValueEx(hk2, TEXT("Path"), NULL, NULL, NULL,
930				      &buflen);
931		if (ret != ERROR_SUCCESS) {
932			wpa_printf(MSG_DEBUG, "TNC: Could not read Path from "
933				   "IMC key '" TSTR "'", name);
934			RegCloseKey(hk2);
935			continue;
936		}
937
938		val = os_malloc(buflen);
939		if (val == NULL) {
940			RegCloseKey(hk2);
941			continue;
942		}
943
944		ret = RegQueryValueEx(hk2, TEXT("Path"), NULL, NULL,
945				      (LPBYTE) val, &buflen);
946		if (ret != ERROR_SUCCESS) {
947			os_free(val);
948			RegCloseKey(hk2);
949			continue;
950		}
951
952		RegCloseKey(hk2);
953
954		wpa_unicode2ascii_inplace(val);
955		wpa_printf(MSG_DEBUG, "TNC: IMC Path '%s'", (char *) val);
956
957		for (j = 0; j < TNC_MAX_IMC_ID; j++) {
958			if (tnc_imc[j] == NULL)
959				break;
960		}
961		if (j >= TNC_MAX_IMC_ID) {
962			wpa_printf(MSG_DEBUG, "TNC: Too many IMCs");
963			os_free(val);
964			continue;
965		}
966
967		imc = os_zalloc(sizeof(*imc));
968		if (imc == NULL) {
969			os_free(val);
970			break;
971		}
972
973		imc->imcID = j;
974
975		wpa_unicode2ascii_inplace(name);
976		imc->name = os_strdup((char *) name);
977		imc->path = os_strdup((char *) val);
978
979		os_free(val);
980
981		if (last == NULL)
982			tncc->imc = imc;
983		else
984			last->next = imc;
985		last = imc;
986
987		tnc_imc[imc->imcID] = imc;
988	}
989
990	RegCloseKey(hk);
991
992	return 0;
993}
994
995
996static int tncc_read_config(struct tncc_data *tncc)
997{
998	if (tncc_read_config_reg(tncc, HKEY_LOCAL_MACHINE) < 0 ||
999	    tncc_read_config_reg(tncc, HKEY_CURRENT_USER) < 0)
1000		return -1;
1001	return 0;
1002}
1003
1004#else /* CONFIG_NATIVE_WINDOWS */
1005
1006static struct tnc_if_imc * tncc_parse_imc(char *start, char *end, int *error)
1007{
1008	struct tnc_if_imc *imc;
1009	char *pos, *pos2;
1010	int i;
1011
1012	for (i = 0; i < TNC_MAX_IMC_ID; i++) {
1013		if (tnc_imc[i] == NULL)
1014			break;
1015	}
1016	if (i >= TNC_MAX_IMC_ID) {
1017		wpa_printf(MSG_DEBUG, "TNC: Too many IMCs");
1018		return NULL;
1019	}
1020
1021	imc = os_zalloc(sizeof(*imc));
1022	if (imc == NULL) {
1023		*error = 1;
1024		return NULL;
1025	}
1026
1027	imc->imcID = i;
1028
1029	pos = start;
1030	wpa_printf(MSG_DEBUG, "TNC: Configured IMC: %s", pos);
1031	if (pos + 1 >= end || *pos != '"') {
1032		wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMC line '%s' "
1033			   "(no starting quotation mark)", start);
1034		os_free(imc);
1035		return NULL;
1036	}
1037
1038	pos++;
1039	pos2 = pos;
1040	while (pos2 < end && *pos2 != '"')
1041		pos2++;
1042	if (pos2 >= end) {
1043		wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMC line '%s' "
1044			   "(no ending quotation mark)", start);
1045		os_free(imc);
1046		return NULL;
1047	}
1048	*pos2 = '\0';
1049	wpa_printf(MSG_DEBUG, "TNC: Name: '%s'", pos);
1050	imc->name = os_strdup(pos);
1051
1052	pos = pos2 + 1;
1053	if (pos >= end || *pos != ' ') {
1054		wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMC line '%s' "
1055			   "(no space after name)", start);
1056		os_free(imc->name);
1057		os_free(imc);
1058		return NULL;
1059	}
1060
1061	pos++;
1062	wpa_printf(MSG_DEBUG, "TNC: IMC file: '%s'", pos);
1063	imc->path = os_strdup(pos);
1064	tnc_imc[imc->imcID] = imc;
1065
1066	return imc;
1067}
1068
1069
1070static int tncc_read_config(struct tncc_data *tncc)
1071{
1072	char *config, *end, *pos, *line_end;
1073	size_t config_len;
1074	struct tnc_if_imc *imc, *last;
1075
1076	last = NULL;
1077
1078	config = os_readfile(TNC_CONFIG_FILE, &config_len);
1079	if (config == NULL) {
1080		wpa_printf(MSG_ERROR, "TNC: Could not open TNC configuration "
1081			   "file '%s'", TNC_CONFIG_FILE);
1082		return -1;
1083	}
1084
1085	end = config + config_len;
1086	for (pos = config; pos < end; pos = line_end + 1) {
1087		line_end = pos;
1088		while (*line_end != '\n' && *line_end != '\r' &&
1089		       line_end < end)
1090			line_end++;
1091		*line_end = '\0';
1092
1093		if (os_strncmp(pos, "IMC ", 4) == 0) {
1094			int error = 0;
1095
1096			imc = tncc_parse_imc(pos + 4, line_end, &error);
1097			if (error) {
1098				os_free(config);
1099				return -1;
1100			}
1101			if (imc) {
1102				if (last == NULL)
1103					tncc->imc = imc;
1104				else
1105					last->next = imc;
1106				last = imc;
1107			}
1108		}
1109	}
1110
1111	os_free(config);
1112
1113	return 0;
1114}
1115
1116#endif /* CONFIG_NATIVE_WINDOWS */
1117
1118
1119struct tncc_data * tncc_init(void)
1120{
1121	struct tncc_data *tncc;
1122	struct tnc_if_imc *imc;
1123
1124	tncc = os_zalloc(sizeof(*tncc));
1125	if (tncc == NULL)
1126		return NULL;
1127
1128	/* TODO:
1129	 * move loading and Initialize() to a location that is not
1130	 *    re-initialized for every EAP-TNC session (?)
1131	 */
1132
1133	if (tncc_read_config(tncc) < 0) {
1134		wpa_printf(MSG_ERROR, "TNC: Failed to read TNC configuration");
1135		goto failed;
1136	}
1137
1138	for (imc = tncc->imc; imc; imc = imc->next) {
1139		if (tncc_load_imc(imc)) {
1140			wpa_printf(MSG_ERROR, "TNC: Failed to load IMC '%s'",
1141				   imc->name);
1142			goto failed;
1143		}
1144	}
1145
1146	return tncc;
1147
1148failed:
1149	tncc_deinit(tncc);
1150	return NULL;
1151}
1152
1153
1154void tncc_deinit(struct tncc_data *tncc)
1155{
1156	struct tnc_if_imc *imc, *prev;
1157
1158	imc = tncc->imc;
1159	while (imc) {
1160		tncc_unload_imc(imc);
1161
1162		prev = imc;
1163		imc = imc->next;
1164		os_free(prev);
1165	}
1166
1167	os_free(tncc);
1168}
1169
1170
1171static struct wpabuf * tncc_build_soh(int ver)
1172{
1173	struct wpabuf *buf;
1174	u8 *tlv_len, *tlv_len2, *outer_len, *inner_len, *ssoh_len, *end;
1175	u8 correlation_id[24];
1176	/* TODO: get correct name */
1177	char *machinename = "wpa_supplicant@w1.fi";
1178
1179	if (os_get_random(correlation_id, sizeof(correlation_id)))
1180		return NULL;
1181	wpa_hexdump(MSG_DEBUG, "TNC: SoH Correlation ID",
1182		    correlation_id, sizeof(correlation_id));
1183
1184	buf = wpabuf_alloc(200);
1185	if (buf == NULL)
1186		return NULL;
1187
1188	/* Vendor-Specific TLV (Microsoft) - SoH */
1189	wpabuf_put_be16(buf, EAP_TLV_VENDOR_SPECIFIC_TLV); /* TLV Type */
1190	tlv_len = wpabuf_put(buf, 2); /* Length */
1191	wpabuf_put_be32(buf, EAP_VENDOR_MICROSOFT); /* Vendor_Id */
1192	wpabuf_put_be16(buf, 0x01); /* TLV Type - SoH TLV */
1193	tlv_len2 = wpabuf_put(buf, 2); /* Length */
1194
1195	/* SoH Header */
1196	wpabuf_put_be16(buf, EAP_TLV_VENDOR_SPECIFIC_TLV); /* Outer Type */
1197	outer_len = wpabuf_put(buf, 2);
1198	wpabuf_put_be32(buf, EAP_VENDOR_MICROSOFT); /* IANA SMI Code */
1199	wpabuf_put_be16(buf, ver); /* Inner Type */
1200	inner_len = wpabuf_put(buf, 2);
1201
1202	if (ver == 2) {
1203		/* SoH Mode Sub-Header */
1204		/* Outer Type */
1205		wpabuf_put_be16(buf, EAP_TLV_VENDOR_SPECIFIC_TLV);
1206		wpabuf_put_be16(buf, 4 + 24 + 1 + 1); /* Length */
1207		wpabuf_put_be32(buf, EAP_VENDOR_MICROSOFT); /* IANA SMI Code */
1208		/* Value: */
1209		wpabuf_put_data(buf, correlation_id, sizeof(correlation_id));
1210		wpabuf_put_u8(buf, 0x01); /* Intent Flag - Request */
1211		wpabuf_put_u8(buf, 0x00); /* Content-Type Flag */
1212	}
1213
1214	/* SSoH TLV */
1215	/* System-Health-Id */
1216	wpabuf_put_be16(buf, 0x0002); /* Type */
1217	wpabuf_put_be16(buf, 4); /* Length */
1218	wpabuf_put_be32(buf, 79616);
1219	/* Vendor-Specific Attribute */
1220	wpabuf_put_be16(buf, EAP_TLV_VENDOR_SPECIFIC_TLV);
1221	ssoh_len = wpabuf_put(buf, 2);
1222	wpabuf_put_be32(buf, EAP_VENDOR_MICROSOFT); /* IANA SMI Code */
1223
1224	/* MS-Packet-Info */
1225	wpabuf_put_u8(buf, SSOH_MS_PACKET_INFO);
1226	/* Note: IF-TNCCS-SOH v1.0 r8 claims this field to be:
1227	 * Reserved(4 bits) r(1 bit) Vers(3 bits), but Windows XP
1228	 * SP3 seems to be sending 0x11 for SSoH, i.e., r(request/response) bit
1229	 * would not be in the specified location.
1230	 * [MS-SOH] 4.0.2: Reserved(3 bits) r(1 bit) Vers(4 bits)
1231	 */
1232	wpabuf_put_u8(buf, 0x11); /* r=request, vers=1 */
1233
1234	/* MS-Machine-Inventory */
1235	/* TODO: get correct values; 0 = not applicable for OS */
1236	wpabuf_put_u8(buf, SSOH_MS_MACHINE_INVENTORY);
1237	wpabuf_put_be32(buf, 0); /* osVersionMajor */
1238	wpabuf_put_be32(buf, 0); /* osVersionMinor */
1239	wpabuf_put_be32(buf, 0); /* osVersionBuild */
1240	wpabuf_put_be16(buf, 0); /* spVersionMajor */
1241	wpabuf_put_be16(buf, 0); /* spVersionMinor */
1242	wpabuf_put_be16(buf, 0); /* procArch */
1243
1244	/* MS-MachineName */
1245	wpabuf_put_u8(buf, SSOH_MS_MACHINENAME);
1246	wpabuf_put_be16(buf, os_strlen(machinename) + 1);
1247	wpabuf_put_data(buf, machinename, os_strlen(machinename) + 1);
1248
1249	/* MS-CorrelationId */
1250	wpabuf_put_u8(buf, SSOH_MS_CORRELATIONID);
1251	wpabuf_put_data(buf, correlation_id, sizeof(correlation_id));
1252
1253	/* MS-Quarantine-State */
1254	wpabuf_put_u8(buf, SSOH_MS_QUARANTINE_STATE);
1255	wpabuf_put_be16(buf, 1); /* Flags: ExtState=0, f=0, qState=1 */
1256	wpabuf_put_be32(buf, 0xffffffff); /* ProbTime (hi) */
1257	wpabuf_put_be32(buf, 0xffffffff); /* ProbTime (lo) */
1258	wpabuf_put_be16(buf, 1); /* urlLenInBytes */
1259	wpabuf_put_u8(buf, 0); /* null termination for the url */
1260
1261	/* MS-Machine-Inventory-Ex */
1262	wpabuf_put_u8(buf, SSOH_MS_MACHINE_INVENTORY_EX);
1263	wpabuf_put_be32(buf, 0); /* Reserved
1264				  * (note: Windows XP SP3 uses 0xdecafbad) */
1265	wpabuf_put_u8(buf, 1); /* ProductType: Client */
1266
1267	/* Update SSoH Length */
1268	end = wpabuf_put(buf, 0);
1269	WPA_PUT_BE16(ssoh_len, end - ssoh_len - 2);
1270
1271	/* TODO: SoHReportEntry TLV (zero or more) */
1272
1273	/* Update length fields */
1274	end = wpabuf_put(buf, 0);
1275	WPA_PUT_BE16(tlv_len, end - tlv_len - 2);
1276	WPA_PUT_BE16(tlv_len2, end - tlv_len2 - 2);
1277	WPA_PUT_BE16(outer_len, end - outer_len - 2);
1278	WPA_PUT_BE16(inner_len, end - inner_len - 2);
1279
1280	return buf;
1281}
1282
1283
1284struct wpabuf * tncc_process_soh_request(int ver, const u8 *data, size_t len)
1285{
1286	const u8 *pos;
1287
1288	wpa_hexdump(MSG_DEBUG, "TNC: SoH Request", data, len);
1289
1290	if (len < 12)
1291		return NULL;
1292
1293	/* SoH Request */
1294	pos = data;
1295
1296	/* TLV Type */
1297	if (WPA_GET_BE16(pos) != EAP_TLV_VENDOR_SPECIFIC_TLV)
1298		return NULL;
1299	pos += 2;
1300
1301	/* Length */
1302	if (WPA_GET_BE16(pos) < 8)
1303		return NULL;
1304	pos += 2;
1305
1306	/* Vendor_Id */
1307	if (WPA_GET_BE32(pos) != EAP_VENDOR_MICROSOFT)
1308		return NULL;
1309	pos += 4;
1310
1311	/* TLV Type */
1312	if (WPA_GET_BE16(pos) != 0x02 /* SoH request TLV */)
1313		return NULL;
1314
1315	wpa_printf(MSG_DEBUG, "TNC: SoH Request TLV received");
1316
1317	return tncc_build_soh(2);
1318}
1319