hostapd: Update vendor branch to 0.6.10
[dragonfly.git] / contrib / hostapd / src / eap_peer / tncc.c
blobeaaa1689b58ff99f65e7907dbf980c9abfda30e4
1 /*
2 * EAP-TNC - TNCC (IF-IMC and IF-TNCCS)
3 * Copyright (c) 2007, Jouni Malinen <j@w1.fi>
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.
9 * Alternatively, this software may be distributed under the terms of BSD
10 * license.
12 * See README and COPYING for more details.
15 #include "includes.h"
16 #ifndef CONFIG_NATIVE_WINDOWS
17 #include <dlfcn.h>
18 #endif /* CONFIG_NATIVE_WINDOWS */
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"
27 #ifdef UNICODE
28 #define TSTR "%S"
29 #else /* UNICODE */
30 #define TSTR "%s"
31 #endif /* UNICODE */
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>"
45 /* TNC IF-IMC */
47 typedef unsigned long TNC_UInt32;
48 typedef unsigned char *TNC_BufferReference;
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;
61 typedef TNC_Result (*TNC_TNCC_BindFunctionPointer)(
62 TNC_IMCID imcID,
63 char *functionName,
64 void **pOutfunctionPointer);
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
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
85 #define TNC_IFIMC_VERSION_1 1
87 #define TNC_VENDORID_ANY ((TNC_VendorID) 0xffffff)
88 #define TNC_SUBTYPE_ANY ((TNC_MessageSubtype) 0xff)
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
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
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;
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);
149 struct tncc_data {
150 struct tnc_if_imc *imc;
151 unsigned int last_batchid;
154 #define TNC_MAX_IMC_ID 10
155 static struct tnc_if_imc *tnc_imc[TNC_MAX_IMC_ID] = { NULL };
158 /* TNCC functions that IMCs can call */
160 TNC_Result TNC_TNCC_ReportMessageTypes(
161 TNC_IMCID imcID,
162 TNC_MessageTypeList supportedTypes,
163 TNC_UInt32 typeCount)
165 TNC_UInt32 i;
166 struct tnc_if_imc *imc;
168 wpa_printf(MSG_DEBUG, "TNC: TNC_TNCC_ReportMessageTypes(imcID=%lu "
169 "typeCount=%lu)",
170 (unsigned long) imcID, (unsigned long) typeCount);
172 for (i = 0; i < typeCount; i++) {
173 wpa_printf(MSG_DEBUG, "TNC: supportedTypes[%lu] = %lu",
174 i, supportedTypes[i]);
177 if (imcID >= TNC_MAX_IMC_ID || tnc_imc[imcID] == NULL)
178 return TNC_RESULT_INVALID_PARAMETER;
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;
190 return TNC_RESULT_SUCCESS;
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)
201 struct tnc_if_imc *imc;
202 unsigned char *b64;
203 size_t b64len;
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);
211 if (imcID >= TNC_MAX_IMC_ID || tnc_imc[imcID] == NULL)
212 return TNC_RESULT_INVALID_PARAMETER;
214 b64 = base64_encode(message, messageLength, &b64len);
215 if (b64 == NULL)
216 return TNC_RESULT_FATAL;
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;
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);
233 os_free(b64);
235 return TNC_RESULT_SUCCESS;
239 TNC_Result TNC_TNCC_RequestHandshakeRetry(
240 TNC_IMCID imcID,
241 TNC_ConnectionID connectionID,
242 TNC_RetryReason reason)
244 wpa_printf(MSG_DEBUG, "TNC: TNC_TNCC_RequestHandshakeRetry");
246 if (imcID >= TNC_MAX_IMC_ID || tnc_imc[imcID] == NULL)
247 return TNC_RESULT_INVALID_PARAMETER;
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..
255 return TNC_RESULT_SUCCESS;
259 TNC_Result TNC_9048_LogMessage(TNC_IMCID imcID, TNC_UInt32 severity,
260 const char *message)
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;
269 TNC_Result TNC_9048_UserMessage(TNC_IMCID imcID, TNC_ConnectionID connectionID,
270 const char *message)
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;
279 TNC_Result TNC_TNCC_BindFunction(
280 TNC_IMCID imcID,
281 char *functionName,
282 void **pOutfunctionPointer)
284 wpa_printf(MSG_DEBUG, "TNC: TNC_TNCC_BindFunction(imcID=%lu, "
285 "functionName='%s')", (unsigned long) imcID, functionName);
287 if (imcID >= TNC_MAX_IMC_ID || tnc_imc[imcID] == NULL)
288 return TNC_RESULT_INVALID_PARAMETER;
290 if (pOutfunctionPointer == NULL)
291 return TNC_RESULT_INVALID_PARAMETER;
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") ==
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;
307 return TNC_RESULT_SUCCESS;
311 static void * tncc_get_sym(void *handle, char *func)
313 void *fptr;
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 */
325 return fptr;
329 static int tncc_imc_resolve_funcs(struct tnc_if_imc *imc)
331 void *handle = imc->dlhandle;
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;
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;
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;
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");
363 return 0;
367 static int tncc_imc_initialize(struct tnc_if_imc *imc)
369 TNC_Result res;
370 TNC_Version imc_ver;
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);
379 return res == TNC_RESULT_SUCCESS ? 0 : -1;
383 static int tncc_imc_terminate(struct tnc_if_imc *imc)
385 TNC_Result res;
387 if (imc->Terminate == NULL)
388 return 0;
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);
396 return res == TNC_RESULT_SUCCESS ? 0 : -1;
400 static int tncc_imc_provide_bind_function(struct tnc_if_imc *imc)
402 TNC_Result res;
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);
410 return res == TNC_RESULT_SUCCESS ? 0 : -1;
414 static int tncc_imc_notify_connection_change(struct tnc_if_imc *imc,
415 TNC_ConnectionState state)
417 TNC_Result res;
419 if (imc->NotifyConnectionChange == NULL)
420 return 0;
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);
429 return res == TNC_RESULT_SUCCESS ? 0 : -1;
433 static int tncc_imc_begin_handshake(struct tnc_if_imc *imc)
435 TNC_Result res;
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);
443 return res == TNC_RESULT_SUCCESS ? 0 : -1;
447 static int tncc_load_imc(struct tnc_if_imc *imc)
449 if (imc->path == NULL) {
450 wpa_printf(MSG_DEBUG, "TNC: No IMC configured");
451 return -1;
454 wpa_printf(MSG_DEBUG, "TNC: Opening IMC: %s (%s)",
455 imc->name, imc->path);
456 #ifdef CONFIG_NATIVE_WINDOWS
457 #ifdef UNICODE
459 TCHAR *lib = wpa_strdup_tchar(imc->path);
460 if (lib == NULL)
461 return -1;
462 imc->dlhandle = LoadLibrary(lib);
463 os_free(lib);
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;
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;
480 #endif /* CONFIG_NATIVE_WINDOWS */
482 if (tncc_imc_resolve_funcs(imc) < 0) {
483 wpa_printf(MSG_ERROR, "TNC: Failed to resolve IMC functions");
484 return -1;
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;
493 return 0;
497 static void tncc_unload_imc(struct tnc_if_imc *imc)
499 tncc_imc_terminate(imc);
500 tnc_imc[imc->imcID] = NULL;
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 */
509 os_free(imc->name);
510 os_free(imc->path);
511 os_free(imc->supported_types);
512 os_free(imc->imc_send);
516 static int tncc_supported_type(struct tnc_if_imc *imc, unsigned int type)
518 size_t i;
519 unsigned int vendor, subtype;
521 if (imc == NULL || imc->supported_types == NULL)
522 return 0;
524 vendor = type >> 8;
525 subtype = type & 0xff;
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;
536 return 0;
540 static void tncc_send_to_imcs(struct tncc_data *tncc, unsigned int type,
541 const u8 *msg, size_t len)
543 struct tnc_if_imc *imc;
544 TNC_Result res;
546 wpa_hexdump_ascii(MSG_MSGDUMP, "TNC: Message to IMC(s)", msg, len);
548 for (imc = tncc->imc; imc; imc = imc->next) {
549 if (imc->ReceiveMessage == NULL ||
550 !tncc_supported_type(imc, type))
551 continue;
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);
564 void tncc_init_connection(struct tncc_data *tncc)
566 struct tnc_if_imc *imc;
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);
574 os_free(imc->imc_send);
575 imc->imc_send = NULL;
576 imc->imc_send_len = 0;
578 tncc_imc_begin_handshake(imc);
583 size_t tncc_total_send_len(struct tncc_data *tncc)
585 struct tnc_if_imc *imc;
587 size_t len = 0;
588 for (imc = tncc->imc; imc; imc = imc->next)
589 len += imc->imc_send_len;
590 return len;
594 u8 * tncc_copy_send_buf(struct tncc_data *tncc, u8 *pos)
596 struct tnc_if_imc *imc;
598 for (imc = tncc->imc; imc; imc = imc->next) {
599 if (imc->imc_send == NULL)
600 continue;
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;
609 return pos;
613 char * tncc_if_tnccs_start(struct tncc_data *tncc)
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;
624 char * tncc_if_tnccs_end(void)
626 char *buf = os_malloc(100);
627 if (buf == NULL)
628 return NULL;
629 os_snprintf(buf, 100, IF_TNCCS_END);
630 return buf;
634 static void tncc_notify_recommendation(struct tncc_data *tncc,
635 enum tncc_process_res res)
637 TNC_ConnectionState state;
638 struct tnc_if_imc *imc;
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;
655 for (imc = tncc->imc; imc; imc = imc->next)
656 tncc_imc_notify_connection_change(imc, state);
660 static int tncc_get_type(char *start, unsigned int *type)
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;
671 static unsigned char * tncc_get_base64(char *start, size_t *decoded_len)
673 char *pos, *pos2;
674 unsigned char *decoded;
676 pos = os_strstr(start, "<Base64>");
677 if (pos == NULL)
678 return NULL;
680 pos += 8;
681 pos2 = os_strstr(pos, "</Base64>");
682 if (pos2 == NULL)
683 return NULL;
684 *pos2 = '\0';
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");
693 return decoded;
697 static enum tncc_process_res tncc_get_recommendation(char *start)
699 char *pos, *pos2, saved;
700 int recom;
702 pos = os_strstr(start, "<TNCCS-Recommendation ");
703 if (pos == NULL)
704 return TNCCS_RECOMMENDATION_ERROR;
706 pos += 21;
707 pos = os_strstr(pos, " type=");
708 if (pos == NULL)
709 return TNCCS_RECOMMENDATION_ERROR;
710 pos += 6;
712 if (*pos == '"')
713 pos++;
715 pos2 = pos;
716 while (*pos2 != '\0' && *pos2 != '"' && *pos2 != '>')
717 pos2++;
719 if (*pos2 == '\0')
720 return TNCCS_RECOMMENDATION_ERROR;
722 saved = *pos2;
723 *pos2 = '\0';
724 wpa_printf(MSG_DEBUG, "TNC: TNCCS-Recommendation: '%s'", pos);
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;
734 *pos2 = saved;
736 return recom;
740 enum tncc_process_res tncc_process_if_tnccs(struct tncc_data *tncc,
741 const u8 *msg, size_t len)
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;
750 buf = os_malloc(len + 1);
751 if (buf == NULL)
752 return TNCCS_PROCESS_ERROR;
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;
763 start += 13;
764 while (*start == ' ')
765 start++;
766 *end = '\0';
768 pos = os_strstr(start, "BatchId=");
769 if (pos == NULL) {
770 os_free(buf);
771 return TNCCS_PROCESS_ERROR;
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;
787 tncc->last_batchid = batch_id;
789 while (*pos != '\0' && *pos != '>')
790 pos++;
791 if (*pos == '\0') {
792 os_free(buf);
793 return TNCCS_PROCESS_ERROR;
795 pos++;
796 payload = start;
799 * <IMC-IMV-Message>
800 * <Type>01234567</Type>
801 * <Base64>foo==</Base64>
802 * </IMC-IMV-Message>
805 while (*start) {
806 char *endpos;
807 unsigned int type;
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;
820 if (tncc_get_type(start, &type) < 0) {
821 *endpos = '<';
822 start = end;
823 continue;
825 wpa_printf(MSG_DEBUG, "TNC: IMC-IMV-Message Type 0x%x", type);
827 decoded = tncc_get_base64(start, &decoded_len);
828 if (decoded == NULL) {
829 *endpos = '<';
830 start = end;
831 continue;
834 tncc_send_to_imcs(tncc, type, decoded, decoded_len);
836 os_free(decoded);
838 start = end;
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>
849 start = payload;
850 while (*start) {
851 unsigned int type;
852 char *xml, *xmlend, *endpos;
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;
865 if (tncc_get_type(start, &type) < 0) {
866 *endpos = '<';
867 start = end;
868 continue;
870 wpa_printf(MSG_DEBUG, "TNC: TNCC-TNCS-Message Type 0x%x",
871 type);
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;
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;
897 if (decoded) {
898 wpa_hexdump_ascii(MSG_MSGDUMP,
899 "TNC: TNCC-TNCS-Message Base64",
900 decoded, decoded_len);
901 os_free(decoded);
904 if (xml) {
905 wpa_hexdump_ascii(MSG_MSGDUMP,
906 "TNC: TNCC-TNCS-Message XML",
907 (unsigned char *) xml,
908 xmlend - xml);
911 if (type == TNC_TNCCS_RECOMMENDATION && xml) {
913 * <TNCCS-Recommendation type="allow">
914 * </TNCCS-Recommendation>
916 *xmlend = '\0';
917 res = tncc_get_recommendation(xml);
918 *xmlend = '<';
919 recommendation_msg = 1;
922 start = end;
925 os_free(buf);
927 if (recommendation_msg)
928 tncc_notify_recommendation(tncc, res);
930 return res;
934 #ifdef CONFIG_NATIVE_WINDOWS
935 static int tncc_read_config_reg(struct tncc_data *tncc, HKEY hive)
937 HKEY hk, hk2;
938 LONG ret;
939 DWORD i;
940 struct tnc_if_imc *imc, *last;
941 int j;
943 last = tncc->imc;
944 while (last && last->next)
945 last = last->next;
947 ret = RegOpenKeyEx(hive, TNC_WINREG_PATH, 0, KEY_ENUMERATE_SUB_KEYS,
948 &hk);
949 if (ret != ERROR_SUCCESS)
950 return 0;
952 for (i = 0; ; i++) {
953 TCHAR name[255], *val;
954 DWORD namelen, buflen;
956 namelen = 255;
957 ret = RegEnumKeyEx(hk, i, name, &namelen, NULL, NULL, NULL,
958 NULL);
960 if (ret == ERROR_NO_MORE_ITEMS)
961 break;
963 if (ret != ERROR_SUCCESS) {
964 wpa_printf(MSG_DEBUG, "TNC: RegEnumKeyEx failed: 0x%x",
965 (unsigned int) ret);
966 break;
969 if (namelen >= 255)
970 namelen = 255 - 1;
971 name[namelen] = '\0';
973 wpa_printf(MSG_DEBUG, "TNC: IMC '" TSTR "'", name);
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;
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;
991 val = os_malloc(buflen);
992 if (val == NULL) {
993 RegCloseKey(hk2);
994 continue;
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;
1005 RegCloseKey(hk2);
1007 wpa_unicode2ascii_inplace(val);
1008 wpa_printf(MSG_DEBUG, "TNC: IMC Path '%s'", (char *) val);
1010 for (j = 0; j < TNC_MAX_IMC_ID; j++) {
1011 if (tnc_imc[j] == NULL)
1012 break;
1014 if (j >= TNC_MAX_IMC_ID) {
1015 wpa_printf(MSG_DEBUG, "TNC: Too many IMCs");
1016 os_free(val);
1017 continue;
1020 imc = os_zalloc(sizeof(*imc));
1021 if (imc == NULL) {
1022 os_free(val);
1023 break;
1026 imc->imcID = j;
1028 wpa_unicode2ascii_inplace(name);
1029 imc->name = os_strdup((char *) name);
1030 imc->path = os_strdup((char *) val);
1032 os_free(val);
1034 if (last == NULL)
1035 tncc->imc = imc;
1036 else
1037 last->next = imc;
1038 last = imc;
1040 tnc_imc[imc->imcID] = imc;
1043 RegCloseKey(hk);
1045 return 0;
1049 static int tncc_read_config(struct tncc_data *tncc)
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;
1057 #else /* CONFIG_NATIVE_WINDOWS */
1059 static struct tnc_if_imc * tncc_parse_imc(char *start, char *end, int *error)
1061 struct tnc_if_imc *imc;
1062 char *pos, *pos2;
1063 int i;
1065 for (i = 0; i < TNC_MAX_IMC_ID; i++) {
1066 if (tnc_imc[i] == NULL)
1067 break;
1069 if (i >= TNC_MAX_IMC_ID) {
1070 wpa_printf(MSG_DEBUG, "TNC: Too many IMCs");
1071 return NULL;
1074 imc = os_zalloc(sizeof(*imc));
1075 if (imc == NULL) {
1076 *error = 1;
1077 return NULL;
1080 imc->imcID = i;
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;
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;
1101 *pos2 = '\0';
1102 wpa_printf(MSG_DEBUG, "TNC: Name: '%s'", pos);
1103 imc->name = os_strdup(pos);
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;
1114 pos++;
1115 wpa_printf(MSG_DEBUG, "TNC: IMC file: '%s'", pos);
1116 imc->path = os_strdup(pos);
1117 tnc_imc[imc->imcID] = imc;
1119 return imc;
1123 static int tncc_read_config(struct tncc_data *tncc)
1125 char *config, *end, *pos, *line_end;
1126 size_t config_len;
1127 struct tnc_if_imc *imc, *last;
1129 last = NULL;
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;
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';
1146 if (os_strncmp(pos, "IMC ", 4) == 0) {
1147 int error = 0;
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;
1162 os_free(config);
1164 return 0;
1167 #endif /* CONFIG_NATIVE_WINDOWS */
1170 struct tncc_data * tncc_init(void)
1172 struct tncc_data *tncc;
1173 struct tnc_if_imc *imc;
1175 tncc = os_zalloc(sizeof(*tncc));
1176 if (tncc == NULL)
1177 return NULL;
1179 /* TODO:
1180 * move loading and Initialize() to a location that is not
1181 * re-initialized for every EAP-TNC session (?)
1184 if (tncc_read_config(tncc) < 0) {
1185 wpa_printf(MSG_ERROR, "TNC: Failed to read TNC configuration");
1186 goto failed;
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;
1197 return tncc;
1199 failed:
1200 tncc_deinit(tncc);
1201 return NULL;
1205 void tncc_deinit(struct tncc_data *tncc)
1207 struct tnc_if_imc *imc, *prev;
1209 imc = tncc->imc;
1210 while (imc) {
1211 tncc_unload_imc(imc);
1213 prev = imc;
1214 imc = imc->next;
1215 os_free(prev);
1218 os_free(tncc);
1222 static struct wpabuf * tncc_build_soh(int ver)
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";
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));
1235 buf = wpabuf_alloc(200);
1236 if (buf == NULL)
1237 return NULL;
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 */
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);
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 */
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 */
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)
1283 wpabuf_put_u8(buf, 0x11); /* r=request, vers=1 */
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 */
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);
1300 /* MS-CorrelationId */
1301 wpabuf_put_u8(buf, SSOH_MS_CORRELATIONID);
1302 wpabuf_put_data(buf, correlation_id, sizeof(correlation_id));
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 */
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 */
1318 /* Update SSoH Length */
1319 end = wpabuf_put(buf, 0);
1320 WPA_PUT_BE16(ssoh_len, end - ssoh_len - 2);
1322 /* TODO: SoHReportEntry TLV (zero or more) */
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);
1331 return buf;
1335 struct wpabuf * tncc_process_soh_request(int ver, const u8 *data, size_t len)
1337 const u8 *pos;
1339 wpa_hexdump(MSG_DEBUG, "TNC: SoH Request", data, len);
1341 if (len < 12)
1342 return NULL;
1344 /* SoH Request */
1345 pos = data;
1347 /* TLV Type */
1348 if (WPA_GET_BE16(pos) != EAP_TLV_VENDOR_SPECIFIC_TLV)
1349 return NULL;
1350 pos += 2;
1352 /* Length */
1353 if (WPA_GET_BE16(pos) < 8)
1354 return NULL;
1355 pos += 2;
1357 /* Vendor_Id */
1358 if (WPA_GET_BE32(pos) != EAP_VENDOR_MICROSOFT)
1359 return NULL;
1360 pos += 4;
1362 /* TLV Type */
1363 if (WPA_GET_BE16(pos) != 0x02 /* SoH request TLV */)
1364 return NULL;
1366 wpa_printf(MSG_DEBUG, "TNC: SoH Request TLV received");
1368 return tncc_build_soh(2);