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