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