xref: /freebsd/contrib/wpa/src/eap_peer/tncc.c (revision a7623790fb345e6dc986dfd31df0ace115e6f2e4)
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 */
45 enum {
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 
56 struct 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 
96 struct tncc_data {
97 	struct tnc_if_imc *imc;
98 	unsigned int last_batchid;
99 };
100 
101 #define TNC_MAX_IMC_ID 10
102 static struct tnc_if_imc *tnc_imc[TNC_MAX_IMC_ID] = { NULL };
103 
104 
105 /* TNCC functions that IMCs can call */
106 
107 static 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 
139 static 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 
184 static 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 
204 static 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 
214 static 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 
225 static 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 
257 static 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 
275 static 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 
313 static 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 
329 static 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 
346 static 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 
360 static 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 
379 static 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 
393 static 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 
443 static 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 
462 static 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 
486 static 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 
510 void 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 
529 size_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 
540 u8 * 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 
559 char * 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 
570 char * 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 
580 static 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 
606 static 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 
617 static 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 
643 static 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 
686 enum 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
881 static 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 
995 static 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 
1005 static 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 
1069 static 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 
1118 struct 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 
1147 failed:
1148 	tncc_deinit(tncc);
1149 	return NULL;
1150 }
1151 
1152 
1153 void 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 
1170 static 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 
1283 struct 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