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