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