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