Expand PMF_FN_* macros.
[netbsd-mini2440.git] / dist / wpa / src / eap_peer / tncc.c
blobd1b750878c1d8d0f37d22d42551fddb721963238
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 struct tnc_if_imc {
98 struct tnc_if_imc *next;
99 char *name;
100 char *path;
101 void *dlhandle; /* from dlopen() */
102 TNC_IMCID imcID;
103 TNC_ConnectionID connectionID;
104 TNC_MessageTypeList supported_types;
105 size_t num_supported_types;
106 u8 *imc_send;
107 size_t imc_send_len;
109 /* Functions implemented by IMCs (with TNC_IMC_ prefix) */
110 TNC_Result (*Initialize)(
111 TNC_IMCID imcID,
112 TNC_Version minVersion,
113 TNC_Version maxVersion,
114 TNC_Version *pOutActualVersion);
115 TNC_Result (*NotifyConnectionChange)(
116 TNC_IMCID imcID,
117 TNC_ConnectionID connectionID,
118 TNC_ConnectionState newState);
119 TNC_Result (*BeginHandshake)(
120 TNC_IMCID imcID,
121 TNC_ConnectionID connectionID);
122 TNC_Result (*ReceiveMessage)(
123 TNC_IMCID imcID,
124 TNC_ConnectionID connectionID,
125 TNC_BufferReference messageBuffer,
126 TNC_UInt32 messageLength,
127 TNC_MessageType messageType);
128 TNC_Result (*BatchEnding)(
129 TNC_IMCID imcID,
130 TNC_ConnectionID connectionID);
131 TNC_Result (*Terminate)(TNC_IMCID imcID);
132 TNC_Result (*ProvideBindFunction)(
133 TNC_IMCID imcID,
134 TNC_TNCC_BindFunctionPointer bindFunction);
137 struct tncc_data {
138 struct tnc_if_imc *imc;
139 unsigned int last_batchid;
142 #define TNC_MAX_IMC_ID 10
143 static struct tnc_if_imc *tnc_imc[TNC_MAX_IMC_ID] = { NULL };
146 /* TNCC functions that IMCs can call */
148 TNC_Result TNC_TNCC_ReportMessageTypes(
149 TNC_IMCID imcID,
150 TNC_MessageTypeList supportedTypes,
151 TNC_UInt32 typeCount)
153 TNC_UInt32 i;
154 struct tnc_if_imc *imc;
156 wpa_printf(MSG_DEBUG, "TNC: TNC_TNCC_ReportMessageTypes(imcID=%lu "
157 "typeCount=%lu)",
158 (unsigned long) imcID, (unsigned long) typeCount);
160 for (i = 0; i < typeCount; i++) {
161 wpa_printf(MSG_DEBUG, "TNC: supportedTypes[%lu] = %lu",
162 i, supportedTypes[i]);
165 if (imcID >= TNC_MAX_IMC_ID || tnc_imc[imcID] == NULL)
166 return TNC_RESULT_INVALID_PARAMETER;
168 imc = tnc_imc[imcID];
169 os_free(imc->supported_types);
170 imc->supported_types =
171 os_malloc(typeCount * sizeof(TNC_MessageTypeList));
172 if (imc->supported_types == NULL)
173 return TNC_RESULT_FATAL;
174 os_memcpy(imc->supported_types, supportedTypes,
175 typeCount * sizeof(TNC_MessageTypeList));
176 imc->num_supported_types = typeCount;
178 return TNC_RESULT_SUCCESS;
182 TNC_Result TNC_TNCC_SendMessage(
183 TNC_IMCID imcID,
184 TNC_ConnectionID connectionID,
185 TNC_BufferReference message,
186 TNC_UInt32 messageLength,
187 TNC_MessageType messageType)
189 struct tnc_if_imc *imc;
190 unsigned char *b64;
191 size_t b64len;
193 wpa_printf(MSG_DEBUG, "TNC: TNC_TNCC_SendMessage(imcID=%lu "
194 "connectionID=%lu messageType=%lu)",
195 imcID, connectionID, messageType);
196 wpa_hexdump_ascii(MSG_DEBUG, "TNC: TNC_TNCC_SendMessage",
197 message, messageLength);
199 if (imcID >= TNC_MAX_IMC_ID || tnc_imc[imcID] == NULL)
200 return TNC_RESULT_INVALID_PARAMETER;
202 b64 = base64_encode(message, messageLength, &b64len);
203 if (b64 == NULL)
204 return TNC_RESULT_FATAL;
206 imc = tnc_imc[imcID];
207 os_free(imc->imc_send);
208 imc->imc_send_len = 0;
209 imc->imc_send = os_zalloc(b64len + 100);
210 if (imc->imc_send == NULL) {
211 os_free(b64);
212 return TNC_RESULT_OTHER;
215 imc->imc_send_len =
216 os_snprintf((char *) imc->imc_send, b64len + 100,
217 "<IMC-IMV-Message><Type>%08X</Type>"
218 "<Base64>%s</Base64></IMC-IMV-Message>",
219 (unsigned int) messageType, b64);
221 os_free(b64);
223 return TNC_RESULT_SUCCESS;
227 TNC_Result TNC_TNCC_RequestHandshakeRetry(
228 TNC_IMCID imcID,
229 TNC_ConnectionID connectionID,
230 TNC_RetryReason reason)
232 wpa_printf(MSG_DEBUG, "TNC: TNC_TNCC_RequestHandshakeRetry");
234 if (imcID >= TNC_MAX_IMC_ID || tnc_imc[imcID] == NULL)
235 return TNC_RESULT_INVALID_PARAMETER;
238 * TODO: trigger a call to eapol_sm_request_reauth(). This would
239 * require that the IMC continues to be loaded in memory afer
240 * authentication..
243 return TNC_RESULT_SUCCESS;
247 TNC_Result TNC_9048_LogMessage(TNC_IMCID imcID, TNC_UInt32 severity,
248 const char *message)
250 wpa_printf(MSG_DEBUG, "TNC: TNC_9048_LogMessage(imcID=%lu "
251 "severity==%lu message='%s')",
252 imcID, severity, message);
253 return TNC_RESULT_SUCCESS;
257 TNC_Result TNC_9048_UserMessage(TNC_IMCID imcID, TNC_ConnectionID connectionID,
258 const char *message)
260 wpa_printf(MSG_DEBUG, "TNC: TNC_9048_UserMessage(imcID=%lu "
261 "connectionID==%lu message='%s')",
262 imcID, connectionID, message);
263 return TNC_RESULT_SUCCESS;
267 TNC_Result TNC_TNCC_BindFunction(
268 TNC_IMCID imcID,
269 char *functionName,
270 void **pOutfunctionPointer)
272 wpa_printf(MSG_DEBUG, "TNC: TNC_TNCC_BindFunction(imcID=%lu, "
273 "functionName='%s')", (unsigned long) imcID, functionName);
275 if (imcID >= TNC_MAX_IMC_ID || tnc_imc[imcID] == NULL)
276 return TNC_RESULT_INVALID_PARAMETER;
278 if (pOutfunctionPointer == NULL)
279 return TNC_RESULT_INVALID_PARAMETER;
281 if (os_strcmp(functionName, "TNC_TNCC_ReportMessageTypes") == 0)
282 *pOutfunctionPointer = TNC_TNCC_ReportMessageTypes;
283 else if (os_strcmp(functionName, "TNC_TNCC_SendMessage") == 0)
284 *pOutfunctionPointer = TNC_TNCC_SendMessage;
285 else if (os_strcmp(functionName, "TNC_TNCC_RequestHandshakeRetry") ==
287 *pOutfunctionPointer = TNC_TNCC_RequestHandshakeRetry;
288 else if (os_strcmp(functionName, "TNC_9048_LogMessage") == 0)
289 *pOutfunctionPointer = TNC_9048_LogMessage;
290 else if (os_strcmp(functionName, "TNC_9048_UserMessage") == 0)
291 *pOutfunctionPointer = TNC_9048_UserMessage;
292 else
293 *pOutfunctionPointer = NULL;
295 return TNC_RESULT_SUCCESS;
299 static void * tncc_get_sym(void *handle, char *func)
301 void *fptr;
303 #ifdef CONFIG_NATIVE_WINDOWS
304 #ifdef _WIN32_WCE
305 fptr = GetProcAddressA(handle, func);
306 #else /* _WIN32_WCE */
307 fptr = GetProcAddress(handle, func);
308 #endif /* _WIN32_WCE */
309 #else /* CONFIG_NATIVE_WINDOWS */
310 fptr = dlsym(handle, func);
311 #endif /* CONFIG_NATIVE_WINDOWS */
313 return fptr;
317 static int tncc_imc_resolve_funcs(struct tnc_if_imc *imc)
319 void *handle = imc->dlhandle;
321 /* Mandatory IMC functions */
322 imc->Initialize = tncc_get_sym(handle, "TNC_IMC_Initialize");
323 if (imc->Initialize == NULL) {
324 wpa_printf(MSG_ERROR, "TNC: IMC does not export "
325 "TNC_IMC_Initialize");
326 return -1;
329 imc->BeginHandshake = tncc_get_sym(handle, "TNC_IMC_BeginHandshake");
330 if (imc->BeginHandshake == NULL) {
331 wpa_printf(MSG_ERROR, "TNC: IMC does not export "
332 "TNC_IMC_BeginHandshake");
333 return -1;
336 imc->ProvideBindFunction =
337 tncc_get_sym(handle, "TNC_IMC_ProvideBindFunction");
338 if (imc->ProvideBindFunction == NULL) {
339 wpa_printf(MSG_ERROR, "TNC: IMC does not export "
340 "TNC_IMC_ProvideBindFunction");
341 return -1;
344 /* Optional IMC functions */
345 imc->NotifyConnectionChange =
346 tncc_get_sym(handle, "TNC_IMC_NotifyConnectionChange");
347 imc->ReceiveMessage = tncc_get_sym(handle, "TNC_IMC_ReceiveMessage");
348 imc->BatchEnding = tncc_get_sym(handle, "TNC_IMC_BatchEnding");
349 imc->Terminate = tncc_get_sym(handle, "TNC_IMC_Terminate");
351 return 0;
355 static int tncc_imc_initialize(struct tnc_if_imc *imc)
357 TNC_Result res;
358 TNC_Version imc_ver;
360 wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMC_Initialize for IMC '%s'",
361 imc->name);
362 res = imc->Initialize(imc->imcID, TNC_IFIMC_VERSION_1,
363 TNC_IFIMC_VERSION_1, &imc_ver);
364 wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_Initialize: res=%lu imc_ver=%lu",
365 (unsigned long) res, (unsigned long) imc_ver);
367 return res == TNC_RESULT_SUCCESS ? 0 : -1;
371 static int tncc_imc_terminate(struct tnc_if_imc *imc)
373 TNC_Result res;
375 if (imc->Terminate == NULL)
376 return 0;
378 wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMC_Terminate for IMC '%s'",
379 imc->name);
380 res = imc->Terminate(imc->imcID);
381 wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_Terminate: %lu",
382 (unsigned long) res);
384 return res == TNC_RESULT_SUCCESS ? 0 : -1;
388 static int tncc_imc_provide_bind_function(struct tnc_if_imc *imc)
390 TNC_Result res;
392 wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMC_ProvideBindFunction for "
393 "IMC '%s'", imc->name);
394 res = imc->ProvideBindFunction(imc->imcID, TNC_TNCC_BindFunction);
395 wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_ProvideBindFunction: res=%lu",
396 (unsigned long) res);
398 return res == TNC_RESULT_SUCCESS ? 0 : -1;
402 static int tncc_imc_notify_connection_change(struct tnc_if_imc *imc,
403 TNC_ConnectionState state)
405 TNC_Result res;
407 if (imc->NotifyConnectionChange == NULL)
408 return 0;
410 wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMC_NotifyConnectionChange(%d)"
411 " for IMC '%s'", (int) state, imc->name);
412 res = imc->NotifyConnectionChange(imc->imcID, imc->connectionID,
413 state);
414 wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_NotifyConnectionChange: %lu",
415 (unsigned long) res);
417 return res == TNC_RESULT_SUCCESS ? 0 : -1;
421 static int tncc_imc_begin_handshake(struct tnc_if_imc *imc)
423 TNC_Result res;
425 wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMC_BeginHandshake for IMC "
426 "'%s'", imc->name);
427 res = imc->BeginHandshake(imc->imcID, imc->connectionID);
428 wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_BeginHandshake: %lu",
429 (unsigned long) res);
431 return res == TNC_RESULT_SUCCESS ? 0 : -1;
435 static int tncc_load_imc(struct tnc_if_imc *imc)
437 if (imc->path == NULL) {
438 wpa_printf(MSG_DEBUG, "TNC: No IMC configured");
439 return -1;
442 wpa_printf(MSG_DEBUG, "TNC: Opening IMC: %s (%s)",
443 imc->name, imc->path);
444 #ifdef CONFIG_NATIVE_WINDOWS
445 #ifdef UNICODE
447 TCHAR *lib = wpa_strdup_tchar(imc->path);
448 if (lib == NULL)
449 return -1;
450 imc->dlhandle = LoadLibrary(lib);
451 os_free(lib);
453 #else /* UNICODE */
454 imc->dlhandle = LoadLibrary(imc->path);
455 #endif /* UNICODE */
456 if (imc->dlhandle == NULL) {
457 wpa_printf(MSG_ERROR, "TNC: Failed to open IMC '%s' (%s): %d",
458 imc->name, imc->path, (int) GetLastError());
459 return -1;
461 #else /* CONFIG_NATIVE_WINDOWS */
462 imc->dlhandle = dlopen(imc->path, RTLD_LAZY);
463 if (imc->dlhandle == NULL) {
464 wpa_printf(MSG_ERROR, "TNC: Failed to open IMC '%s' (%s): %s",
465 imc->name, imc->path, dlerror());
466 return -1;
468 #endif /* CONFIG_NATIVE_WINDOWS */
470 if (tncc_imc_resolve_funcs(imc) < 0) {
471 wpa_printf(MSG_ERROR, "TNC: Failed to resolve IMC functions");
472 return -1;
475 if (tncc_imc_initialize(imc) < 0 ||
476 tncc_imc_provide_bind_function(imc) < 0) {
477 wpa_printf(MSG_ERROR, "TNC: Failed to initialize IMC");
478 return -1;
481 return 0;
485 static void tncc_unload_imc(struct tnc_if_imc *imc)
487 tncc_imc_terminate(imc);
488 tnc_imc[imc->imcID] = NULL;
490 if (imc->dlhandle) {
491 #ifdef CONFIG_NATIVE_WINDOWS
492 FreeLibrary(imc->dlhandle);
493 #else /* CONFIG_NATIVE_WINDOWS */
494 dlclose(imc->dlhandle);
495 #endif /* CONFIG_NATIVE_WINDOWS */
497 os_free(imc->name);
498 os_free(imc->path);
499 os_free(imc->supported_types);
500 os_free(imc->imc_send);
504 static int tncc_supported_type(struct tnc_if_imc *imc, unsigned int type)
506 size_t i;
507 unsigned int vendor, subtype;
509 if (imc == NULL || imc->supported_types == NULL)
510 return 0;
512 vendor = type >> 8;
513 subtype = type & 0xff;
515 for (i = 0; i < imc->num_supported_types; i++) {
516 unsigned int svendor, ssubtype;
517 svendor = imc->supported_types[i] >> 8;
518 ssubtype = imc->supported_types[i] & 0xff;
519 if ((vendor == svendor || svendor == TNC_VENDORID_ANY) &&
520 (subtype == ssubtype || ssubtype == TNC_SUBTYPE_ANY))
521 return 1;
524 return 0;
528 static void tncc_send_to_imcs(struct tncc_data *tncc, unsigned int type,
529 const u8 *msg, size_t len)
531 struct tnc_if_imc *imc;
532 TNC_Result res;
534 wpa_hexdump_ascii(MSG_MSGDUMP, "TNC: Message to IMC(s)", msg, len);
536 for (imc = tncc->imc; imc; imc = imc->next) {
537 if (imc->ReceiveMessage == NULL ||
538 !tncc_supported_type(imc, type))
539 continue;
541 wpa_printf(MSG_DEBUG, "TNC: Call ReceiveMessage for IMC '%s'",
542 imc->name);
543 res = imc->ReceiveMessage(imc->imcID, imc->connectionID,
544 (TNC_BufferReference) msg, len,
545 type);
546 wpa_printf(MSG_DEBUG, "TNC: ReceiveMessage: %lu",
547 (unsigned long) res);
552 void tncc_init_connection(struct tncc_data *tncc)
554 struct tnc_if_imc *imc;
556 for (imc = tncc->imc; imc; imc = imc->next) {
557 tncc_imc_notify_connection_change(
558 imc, TNC_CONNECTION_STATE_CREATE);
559 tncc_imc_notify_connection_change(
560 imc, TNC_CONNECTION_STATE_HANDSHAKE);
562 os_free(imc->imc_send);
563 imc->imc_send = NULL;
564 imc->imc_send_len = 0;
566 tncc_imc_begin_handshake(imc);
571 size_t tncc_total_send_len(struct tncc_data *tncc)
573 struct tnc_if_imc *imc;
575 size_t len = 0;
576 for (imc = tncc->imc; imc; imc = imc->next)
577 len += imc->imc_send_len;
578 return len;
582 u8 * tncc_copy_send_buf(struct tncc_data *tncc, u8 *pos)
584 struct tnc_if_imc *imc;
586 for (imc = tncc->imc; imc; imc = imc->next) {
587 if (imc->imc_send == NULL)
588 continue;
590 os_memcpy(pos, imc->imc_send, imc->imc_send_len);
591 pos += imc->imc_send_len;
592 os_free(imc->imc_send);
593 imc->imc_send = NULL;
594 imc->imc_send_len = 0;
597 return pos;
601 char * tncc_if_tnccs_start(struct tncc_data *tncc)
603 char *buf = os_malloc(1000);
604 if (buf == NULL)
605 return NULL;
606 tncc->last_batchid++;
607 os_snprintf(buf, 1000, IF_TNCCS_START, tncc->last_batchid);
608 return buf;
612 char * tncc_if_tnccs_end(void)
614 char *buf = os_malloc(100);
615 if (buf == NULL)
616 return NULL;
617 os_snprintf(buf, 100, IF_TNCCS_END);
618 return buf;
622 static void tncc_notify_recommendation(struct tncc_data *tncc,
623 enum tncc_process_res res)
625 TNC_ConnectionState state;
626 struct tnc_if_imc *imc;
628 switch (res) {
629 case TNCCS_RECOMMENDATION_ALLOW:
630 state = TNC_CONNECTION_STATE_ACCESS_ALLOWED;
631 break;
632 case TNCCS_RECOMMENDATION_NONE:
633 state = TNC_CONNECTION_STATE_ACCESS_NONE;
634 break;
635 case TNCCS_RECOMMENDATION_ISOLATE:
636 state = TNC_CONNECTION_STATE_ACCESS_ISOLATED;
637 break;
638 default:
639 state = TNC_CONNECTION_STATE_ACCESS_NONE;
640 break;
643 for (imc = tncc->imc; imc; imc = imc->next)
644 tncc_imc_notify_connection_change(imc, state);
648 static int tncc_get_type(char *start, unsigned int *type)
650 char *pos = os_strstr(start, "<Type>");
651 if (pos == NULL)
652 return -1;
653 pos += 6;
654 *type = strtoul(pos, NULL, 16);
655 return 0;
659 static unsigned char * tncc_get_base64(char *start, size_t *decoded_len)
661 char *pos, *pos2;
662 unsigned char *decoded;
664 pos = os_strstr(start, "<Base64>");
665 if (pos == NULL)
666 return NULL;
668 pos += 8;
669 pos2 = os_strstr(pos, "</Base64>");
670 if (pos2 == NULL)
671 return NULL;
672 *pos2 = '\0';
674 decoded = base64_decode((unsigned char *) pos, os_strlen(pos),
675 decoded_len);
676 *pos2 = '<';
677 if (decoded == NULL) {
678 wpa_printf(MSG_DEBUG, "TNC: Failed to decode Base64 data");
681 return decoded;
685 static enum tncc_process_res tncc_get_recommendation(char *start)
687 char *pos, *pos2, saved;
688 int recom;
690 pos = os_strstr(start, "<TNCCS-Recommendation ");
691 if (pos == NULL)
692 return TNCCS_RECOMMENDATION_ERROR;
694 pos += 21;
695 pos = os_strstr(pos, " type=");
696 if (pos == NULL)
697 return TNCCS_RECOMMENDATION_ERROR;
698 pos += 6;
700 if (*pos == '"')
701 pos++;
703 pos2 = pos;
704 while (*pos2 != '\0' && *pos2 != '"' && *pos2 != '>')
705 pos2++;
707 if (*pos2 == '\0')
708 return TNCCS_RECOMMENDATION_ERROR;
710 saved = *pos2;
711 *pos2 = '\0';
712 wpa_printf(MSG_DEBUG, "TNC: TNCCS-Recommendation: '%s'", pos);
714 recom = TNCCS_RECOMMENDATION_ERROR;
715 if (os_strcmp(pos, "allow") == 0)
716 recom = TNCCS_RECOMMENDATION_ALLOW;
717 else if (os_strcmp(pos, "none") == 0)
718 recom = TNCCS_RECOMMENDATION_NONE;
719 else if (os_strcmp(pos, "isolate") == 0)
720 recom = TNCCS_RECOMMENDATION_ISOLATE;
722 *pos2 = saved;
724 return recom;
728 enum tncc_process_res tncc_process_if_tnccs(struct tncc_data *tncc,
729 const u8 *msg, size_t len)
731 char *buf, *start, *end, *pos, *pos2, *payload;
732 unsigned int batch_id;
733 unsigned char *decoded;
734 size_t decoded_len;
735 enum tncc_process_res res = TNCCS_PROCESS_OK_NO_RECOMMENDATION;
736 int recommendation_msg = 0;
738 buf = os_malloc(len + 1);
739 if (buf == NULL)
740 return TNCCS_PROCESS_ERROR;
742 os_memcpy(buf, msg, len);
743 buf[len] = '\0';
744 start = os_strstr(buf, "<TNCCS-Batch ");
745 end = os_strstr(buf, "</TNCCS-Batch>");
746 if (start == NULL || end == NULL || start > end) {
747 os_free(buf);
748 return TNCCS_PROCESS_ERROR;
751 start += 13;
752 while (*start == ' ')
753 start++;
754 *end = '\0';
756 pos = os_strstr(start, "BatchId=");
757 if (pos == NULL) {
758 os_free(buf);
759 return TNCCS_PROCESS_ERROR;
762 pos += 8;
763 if (*pos == '"')
764 pos++;
765 batch_id = atoi(pos);
766 wpa_printf(MSG_DEBUG, "TNC: Received IF-TNCCS BatchId=%u",
767 batch_id);
768 if (batch_id != tncc->last_batchid + 1) {
769 wpa_printf(MSG_DEBUG, "TNC: Unexpected IF-TNCCS BatchId "
770 "%u (expected %u)",
771 batch_id, tncc->last_batchid + 1);
772 os_free(buf);
773 return TNCCS_PROCESS_ERROR;
775 tncc->last_batchid = batch_id;
777 while (*pos != '\0' && *pos != '>')
778 pos++;
779 if (*pos == '\0') {
780 os_free(buf);
781 return TNCCS_PROCESS_ERROR;
783 pos++;
784 payload = start;
787 * <IMC-IMV-Message>
788 * <Type>01234567</Type>
789 * <Base64>foo==</Base64>
790 * </IMC-IMV-Message>
793 while (*start) {
794 char *endpos;
795 unsigned int type;
797 pos = os_strstr(start, "<IMC-IMV-Message>");
798 if (pos == NULL)
799 break;
800 start = pos + 17;
801 end = os_strstr(start, "</IMC-IMV-Message>");
802 if (end == NULL)
803 break;
804 *end = '\0';
805 endpos = end;
806 end += 18;
808 if (tncc_get_type(start, &type) < 0) {
809 *endpos = '<';
810 start = end;
811 continue;
813 wpa_printf(MSG_DEBUG, "TNC: IMC-IMV-Message Type 0x%x", type);
815 decoded = tncc_get_base64(start, &decoded_len);
816 if (decoded == NULL) {
817 *endpos = '<';
818 start = end;
819 continue;
822 tncc_send_to_imcs(tncc, type, decoded, decoded_len);
824 os_free(decoded);
826 start = end;
830 * <TNCC-TNCS-Message>
831 * <Type>01234567</Type>
832 * <XML><TNCCS-Foo type="foo"></TNCCS-Foo></XML>
833 * <Base64>foo==</Base64>
834 * </TNCC-TNCS-Message>
837 start = payload;
838 while (*start) {
839 unsigned int type;
840 char *xml, *xmlend, *endpos;
842 pos = os_strstr(start, "<TNCC-TNCS-Message>");
843 if (pos == NULL)
844 break;
845 start = pos + 19;
846 end = os_strstr(start, "</TNCC-TNCS-Message>");
847 if (end == NULL)
848 break;
849 *end = '\0';
850 endpos = end;
851 end += 20;
853 if (tncc_get_type(start, &type) < 0) {
854 *endpos = '<';
855 start = end;
856 continue;
858 wpa_printf(MSG_DEBUG, "TNC: TNCC-TNCS-Message Type 0x%x",
859 type);
861 /* Base64 OR XML */
862 decoded = NULL;
863 xml = NULL;
864 xmlend = NULL;
865 pos = os_strstr(start, "<XML>");
866 if (pos) {
867 pos += 5;
868 pos2 = os_strstr(pos, "</XML>");
869 if (pos2 == NULL) {
870 *endpos = '<';
871 start = end;
872 continue;
874 xmlend = pos2;
875 xml = pos;
876 } else {
877 decoded = tncc_get_base64(start, &decoded_len);
878 if (decoded == NULL) {
879 *endpos = '<';
880 start = end;
881 continue;
885 if (decoded) {
886 wpa_hexdump_ascii(MSG_MSGDUMP,
887 "TNC: TNCC-TNCS-Message Base64",
888 decoded, decoded_len);
889 os_free(decoded);
892 if (xml) {
893 wpa_hexdump_ascii(MSG_MSGDUMP,
894 "TNC: TNCC-TNCS-Message XML",
895 (unsigned char *) xml,
896 xmlend - xml);
899 if (type == TNC_TNCCS_RECOMMENDATION && xml) {
901 * <TNCCS-Recommendation type="allow">
902 * </TNCCS-Recommendation>
904 *xmlend = '\0';
905 res = tncc_get_recommendation(xml);
906 *xmlend = '<';
907 recommendation_msg = 1;
910 start = end;
913 os_free(buf);
915 if (recommendation_msg)
916 tncc_notify_recommendation(tncc, res);
918 return res;
922 #ifdef CONFIG_NATIVE_WINDOWS
923 static int tncc_read_config_reg(struct tncc_data *tncc, HKEY hive)
925 HKEY hk, hk2;
926 LONG ret;
927 DWORD i;
928 struct tnc_if_imc *imc, *last;
929 int j;
931 last = tncc->imc;
932 while (last && last->next)
933 last = last->next;
935 ret = RegOpenKeyEx(hive, TNC_WINREG_PATH, 0, KEY_ENUMERATE_SUB_KEYS,
936 &hk);
937 if (ret != ERROR_SUCCESS)
938 return 0;
940 for (i = 0; ; i++) {
941 TCHAR name[255], *val;
942 DWORD namelen, buflen;
944 namelen = 255;
945 ret = RegEnumKeyEx(hk, i, name, &namelen, NULL, NULL, NULL,
946 NULL);
948 if (ret == ERROR_NO_MORE_ITEMS)
949 break;
951 if (ret != ERROR_SUCCESS) {
952 wpa_printf(MSG_DEBUG, "TNC: RegEnumKeyEx failed: 0x%x",
953 (unsigned int) ret);
954 break;
957 if (namelen >= 255)
958 namelen = 255 - 1;
959 name[namelen] = '\0';
961 wpa_printf(MSG_DEBUG, "TNC: IMC '" TSTR "'", name);
963 ret = RegOpenKeyEx(hk, name, 0, KEY_QUERY_VALUE, &hk2);
964 if (ret != ERROR_SUCCESS) {
965 wpa_printf(MSG_DEBUG, "Could not open IMC key '" TSTR
966 "'", name);
967 continue;
970 ret = RegQueryValueEx(hk2, TEXT("Path"), NULL, NULL, NULL,
971 &buflen);
972 if (ret != ERROR_SUCCESS) {
973 wpa_printf(MSG_DEBUG, "TNC: Could not read Path from "
974 "IMC key '" TSTR "'", name);
975 RegCloseKey(hk2);
976 continue;
979 val = os_malloc(buflen);
980 if (val == NULL) {
981 RegCloseKey(hk2);
982 continue;
985 ret = RegQueryValueEx(hk2, TEXT("Path"), NULL, NULL,
986 (LPBYTE) val, &buflen);
987 if (ret != ERROR_SUCCESS) {
988 os_free(val);
989 RegCloseKey(hk2);
990 continue;
993 RegCloseKey(hk2);
995 wpa_unicode2ascii_inplace(val);
996 wpa_printf(MSG_DEBUG, "TNC: IMC Path '%s'", (char *) val);
998 for (j = 0; j < TNC_MAX_IMC_ID; j++) {
999 if (tnc_imc[j] == NULL)
1000 break;
1002 if (j >= TNC_MAX_IMC_ID) {
1003 wpa_printf(MSG_DEBUG, "TNC: Too many IMCs");
1004 os_free(val);
1005 continue;
1008 imc = os_zalloc(sizeof(*imc));
1009 if (imc == NULL) {
1010 os_free(val);
1011 break;
1014 imc->imcID = j;
1016 wpa_unicode2ascii_inplace(name);
1017 imc->name = os_strdup((char *) name);
1018 imc->path = os_strdup((char *) val);
1020 os_free(val);
1022 if (last == NULL)
1023 tncc->imc = imc;
1024 else
1025 last->next = imc;
1026 last = imc;
1028 tnc_imc[imc->imcID] = imc;
1031 RegCloseKey(hk);
1033 return 0;
1037 static int tncc_read_config(struct tncc_data *tncc)
1039 if (tncc_read_config_reg(tncc, HKEY_LOCAL_MACHINE) < 0 ||
1040 tncc_read_config_reg(tncc, HKEY_CURRENT_USER) < 0)
1041 return -1;
1042 return 0;
1045 #else /* CONFIG_NATIVE_WINDOWS */
1047 static struct tnc_if_imc * tncc_parse_imc(char *start, char *end, int *error)
1049 struct tnc_if_imc *imc;
1050 char *pos, *pos2;
1051 int i;
1053 for (i = 0; i < TNC_MAX_IMC_ID; i++) {
1054 if (tnc_imc[i] == NULL)
1055 break;
1057 if (i >= TNC_MAX_IMC_ID) {
1058 wpa_printf(MSG_DEBUG, "TNC: Too many IMCs");
1059 return NULL;
1062 imc = os_zalloc(sizeof(*imc));
1063 if (imc == NULL) {
1064 *error = 1;
1065 return NULL;
1068 imc->imcID = i;
1070 pos = start;
1071 wpa_printf(MSG_DEBUG, "TNC: Configured IMC: %s", pos);
1072 if (pos + 1 >= end || *pos != '"') {
1073 wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMC line '%s' "
1074 "(no starting quotation mark)", start);
1075 os_free(imc);
1076 return NULL;
1079 pos++;
1080 pos2 = pos;
1081 while (pos2 < end && *pos2 != '"')
1082 pos2++;
1083 if (pos2 >= end) {
1084 wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMC line '%s' "
1085 "(no ending quotation mark)", start);
1086 os_free(imc);
1087 return NULL;
1089 *pos2 = '\0';
1090 wpa_printf(MSG_DEBUG, "TNC: Name: '%s'", pos);
1091 imc->name = os_strdup(pos);
1093 pos = pos2 + 1;
1094 if (pos >= end || *pos != ' ') {
1095 wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMC line '%s' "
1096 "(no space after name)", start);
1097 os_free(imc);
1098 return NULL;
1101 pos++;
1102 wpa_printf(MSG_DEBUG, "TNC: IMC file: '%s'", pos);
1103 imc->path = os_strdup(pos);
1104 tnc_imc[imc->imcID] = imc;
1106 return imc;
1110 static int tncc_read_config(struct tncc_data *tncc)
1112 char *config, *end, *pos, *line_end;
1113 size_t config_len;
1114 struct tnc_if_imc *imc, *last;
1116 last = NULL;
1118 config = os_readfile(TNC_CONFIG_FILE, &config_len);
1119 if (config == NULL) {
1120 wpa_printf(MSG_ERROR, "TNC: Could not open TNC configuration "
1121 "file '%s'", TNC_CONFIG_FILE);
1122 return -1;
1125 end = config + config_len;
1126 for (pos = config; pos < end; pos = line_end + 1) {
1127 line_end = pos;
1128 while (*line_end != '\n' && *line_end != '\r' &&
1129 line_end < end)
1130 line_end++;
1131 *line_end = '\0';
1133 if (os_strncmp(pos, "IMC ", 4) == 0) {
1134 int error = 0;
1136 imc = tncc_parse_imc(pos + 4, line_end, &error);
1137 if (error)
1138 return -1;
1139 if (imc) {
1140 if (last == NULL)
1141 tncc->imc = imc;
1142 else
1143 last->next = imc;
1144 last = imc;
1149 os_free(config);
1151 return 0;
1154 #endif /* CONFIG_NATIVE_WINDOWS */
1157 struct tncc_data * tncc_init(void)
1159 struct tncc_data *tncc;
1160 struct tnc_if_imc *imc;
1162 tncc = os_zalloc(sizeof(*tncc));
1163 if (tncc == NULL)
1164 return NULL;
1166 /* TODO:
1167 * move loading and Initialize() to a location that is not
1168 * re-initialized for every EAP-TNC session (?)
1171 if (tncc_read_config(tncc) < 0) {
1172 wpa_printf(MSG_ERROR, "TNC: Failed to read TNC configuration");
1173 goto failed;
1176 for (imc = tncc->imc; imc; imc = imc->next) {
1177 if (tncc_load_imc(imc)) {
1178 wpa_printf(MSG_ERROR, "TNC: Failed to load IMC '%s'",
1179 imc->name);
1180 goto failed;
1184 return tncc;
1186 failed:
1187 tncc_deinit(tncc);
1188 return NULL;
1192 void tncc_deinit(struct tncc_data *tncc)
1194 struct tnc_if_imc *imc, *prev;
1196 imc = tncc->imc;
1197 while (imc) {
1198 tncc_unload_imc(imc);
1200 prev = imc;
1201 imc = imc->next;
1202 os_free(prev);
1205 os_free(tncc);
1209 static struct wpabuf * tncc_build_soh(void)
1211 struct wpabuf *buf;
1212 u8 *tlv_len, *tlv_len2, *outer_len, *inner_len, *ssoh_len, *end;
1213 u8 correlation_id[24];
1214 int ver = 2;
1216 if (os_get_random(correlation_id, sizeof(correlation_id)))
1217 return NULL;
1218 wpa_hexdump(MSG_DEBUG, "TNC: SoH Correlation ID",
1219 correlation_id, sizeof(correlation_id));
1221 buf = wpabuf_alloc(200);
1222 if (buf == NULL)
1223 return NULL;
1225 /* Vendor-Specific TLV (Microsoft) - SoH */
1226 wpabuf_put_be16(buf, EAP_TLV_VENDOR_SPECIFIC_TLV); /* TLV Type */
1227 tlv_len = wpabuf_put(buf, 2); /* Length */
1228 wpabuf_put_be32(buf, EAP_VENDOR_MICROSOFT); /* Vendor_Id */
1229 wpabuf_put_be16(buf, 0x01); /* TLV Type - SoH TLV */
1230 tlv_len2 = wpabuf_put(buf, 2); /* Length */
1232 /* SoH Header */
1233 wpabuf_put_be16(buf, EAP_TLV_VENDOR_SPECIFIC_TLV); /* Outer Type */
1234 outer_len = wpabuf_put(buf, 2);
1235 wpabuf_put_be32(buf, EAP_VENDOR_MICROSOFT); /* IANA SMI Code */
1236 wpabuf_put_be16(buf, ver); /* Inner Type */
1237 inner_len = wpabuf_put(buf, 2);
1239 if (ver == 2) {
1240 /* SoH Mode Sub-Header */
1241 /* Outer Type */
1242 wpabuf_put_be16(buf, EAP_TLV_VENDOR_SPECIFIC_TLV);
1243 wpabuf_put_be16(buf, 4 + 24 + 1 + 1); /* Length */
1244 wpabuf_put_be32(buf, EAP_VENDOR_MICROSOFT); /* IANA SMI Code */
1245 /* Value: */
1246 wpabuf_put_data(buf, correlation_id, sizeof(correlation_id));
1247 wpabuf_put_u8(buf, 0x01); /* Intent Flag - Request */
1248 wpabuf_put_u8(buf, 0x00); /* Content-Type Flag */
1251 /* SSoH TLV */
1252 /* System-Health-Id */
1253 wpabuf_put_be16(buf, 0x0002); /* Type */
1254 wpabuf_put_be16(buf, 4); /* Length */
1255 wpabuf_put_be32(buf, 79616);
1256 /* Vendor-Specific Attribute */
1257 wpabuf_put_be16(buf, EAP_TLV_VENDOR_SPECIFIC_TLV);
1258 ssoh_len = wpabuf_put(buf, 2);
1259 wpabuf_put_be32(buf, EAP_VENDOR_MICROSOFT); /* IANA SMI Code */
1260 /* TODO: MS-Machine-Inventory */
1261 /* TODO: MS-Quarantine-State */
1262 /* MS-Packet-Info */
1263 wpabuf_put_u8(buf, 0x03);
1264 wpabuf_put_u8(buf, 0x11); /* r=request, vers=1 */
1265 /* TODO: MS-MachineName */
1266 /* MS-CorrelationId */
1267 wpabuf_put_u8(buf, 0x06);
1268 wpabuf_put_data(buf, correlation_id, sizeof(correlation_id));
1269 end = wpabuf_put(buf, 0);
1270 WPA_PUT_BE16(ssoh_len, end - ssoh_len - 2);
1272 /* TODO: SoHReportEntry TLV (zero or more) */
1274 /* Update length fields */
1275 end = wpabuf_put(buf, 0);
1276 WPA_PUT_BE16(tlv_len, end - tlv_len - 2);
1277 WPA_PUT_BE16(tlv_len2, end - tlv_len2 - 2);
1278 WPA_PUT_BE16(outer_len, end - outer_len - 2);
1279 WPA_PUT_BE16(inner_len, end - inner_len - 2);
1281 return buf;
1285 struct wpabuf * tncc_process_soh_request(const u8 *data, size_t len)
1287 const u8 *pos;
1289 wpa_hexdump(MSG_DEBUG, "TNC: SoH Request", data, len);
1291 if (len < 12)
1292 return NULL;
1294 /* SoH Request */
1295 pos = data;
1297 /* TLV Type */
1298 if (WPA_GET_BE16(pos) != EAP_TLV_VENDOR_SPECIFIC_TLV)
1299 return NULL;
1300 pos += 2;
1302 /* Length */
1303 if (WPA_GET_BE16(pos) < 8)
1304 return NULL;
1305 pos += 2;
1307 /* Vendor_Id */
1308 if (WPA_GET_BE32(pos) != EAP_VENDOR_MICROSOFT)
1309 return NULL;
1310 pos += 4;
1312 /* TLV Type */
1313 if (WPA_GET_BE16(pos) != 0x02 /* SoH request TLV */)
1314 return NULL;
1316 wpa_printf(MSG_DEBUG, "TNC: SoH Request TLV received");
1318 return tncc_build_soh();