blob: f5edfd52c2502d306fbcf80f1e171c8706b992b2 [file] [log] [blame]
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001/*
2 * EAP-TNC - TNCC (IF-IMC and IF-TNCCS)
3 * Copyright (c) 2007, Jouni Malinen <j@w1.fi>
4 *
Dmitry Shmidtc5ec7f52012-03-06 16:33:24 -08005 * This software may be distributed under the terms of the BSD license.
6 * See README for more details.
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07007 */
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
41typedef unsigned long TNC_UInt32;
42typedef unsigned char *TNC_BufferReference;
43
44typedef TNC_UInt32 TNC_IMCID;
45typedef TNC_UInt32 TNC_ConnectionID;
46typedef TNC_UInt32 TNC_ConnectionState;
47typedef TNC_UInt32 TNC_RetryReason;
48typedef TNC_UInt32 TNC_MessageType;
49typedef TNC_MessageType *TNC_MessageTypeList;
50typedef TNC_UInt32 TNC_VendorID;
51typedef TNC_UInt32 TNC_MessageSubtype;
52typedef TNC_UInt32 TNC_Version;
53typedef TNC_UInt32 TNC_Result;
54
55typedef 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 */
92enum {
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
103struct 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
143struct tncc_data {
144 struct tnc_if_imc *imc;
145 unsigned int last_batchid;
146};
147
148#define TNC_MAX_IMC_ID 10
149static struct tnc_if_imc *tnc_imc[TNC_MAX_IMC_ID] = { NULL };
150
151
152/* TNCC functions that IMCs can call */
153
154TNC_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
188TNC_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
233TNC_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
253TNC_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
263TNC_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
273TNC_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
305static 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
323static 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
361static 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
377static 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
394static 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
408static 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
427static 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
441static 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
491static 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
510static 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
534static 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
558void 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
577size_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
588u8 * 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
607char * 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
618char * 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
628static 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
654static 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
665static 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
691static 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
734enum 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
929static 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
1043static 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
1053static 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
1117static 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
1164struct 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
1193failed:
1194 tncc_deinit(tncc);
1195 return NULL;
1196}
1197
1198
1199void 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
1216static 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
1329struct 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}