xref: /freebsd/contrib/wpa/src/eap_peer/tncc.c (revision d3d381b2b194b4d24853e92eecef55f262688d1a)
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 =
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 
141 static 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 
186 static 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 
206 static 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 
216 static 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 
227 static 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 
259 static 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 
277 static 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 
315 static 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 
331 static 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 
348 static 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 
362 static 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 
381 static 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 
395 static 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 
445 static 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 
464 static 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 
488 static 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 
512 void 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 
531 size_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 
542 u8 * 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 
561 char * 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 
572 char * 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 
582 static 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 
608 static 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 
619 static 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 
645 static 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 
688 enum 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
883 static 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 
997 static 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 
1007 static 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 
1071 static 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 
1120 struct 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 
1149 failed:
1150 	tncc_deinit(tncc);
1151 	return NULL;
1152 }
1153 
1154 
1155 void 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 
1172 static 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 
1285 struct 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