/* * EAP-TNC - TNCC (IF-IMC and IF-TNCCS) * Copyright (c) 2007, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Alternatively, this software may be distributed under the terms of BSD * license. * * See README and COPYING for more details. */ #include "includes.h" #ifndef CONFIG_NATIVE_WINDOWS #include <dlfcn.h> #endif /* CONFIG_NATIVE_WINDOWS */ #include "common.h" #include "base64.h" #include "tncc.h" #include "eap_common/eap_tlv_common.h" #include "eap_common/eap_defs.h" #ifdef UNICODE #define TSTR "%S" #else /* UNICODE */ #define TSTR "%s" #endif /* UNICODE */ #define TNC_CONFIG_FILE "/etc/tnc_config" #define TNC_WINREG_PATH TEXT("SOFTWARE\\Trusted Computing Group\\TNC\\IMCs") #define IF_TNCCS_START \ "<?xml version=\"1.0\"?>\n" \ "<TNCCS-Batch BatchId=\"%d\" Recipient=\"TNCS\" " \ "xmlns=\"http://www.trustedcomputinggroup.org/IWG/TNC/1_0/IF_TNCCS#\" " \ "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " \ "xsi:schemaLocation=\"http://www.trustedcomputinggroup.org/IWG/TNC/1_0/" \ "IF_TNCCS# https://www.trustedcomputinggroup.org/XML/SCHEMA/TNCCS_1.0.xsd\">\n" #define IF_TNCCS_END "\n</TNCCS-Batch>" /* TNC IF-IMC */ typedef unsigned long TNC_UInt32; typedef unsigned char *TNC_BufferReference; typedef TNC_UInt32 TNC_IMCID; typedef TNC_UInt32 TNC_ConnectionID; typedef TNC_UInt32 TNC_ConnectionState; typedef TNC_UInt32 TNC_RetryReason; typedef TNC_UInt32 TNC_MessageType; typedef TNC_MessageType *TNC_MessageTypeList; typedef TNC_UInt32 TNC_VendorID; typedef TNC_UInt32 TNC_MessageSubtype; typedef TNC_UInt32 TNC_Version; typedef TNC_UInt32 TNC_Result; typedef TNC_Result (*TNC_TNCC_BindFunctionPointer)( TNC_IMCID imcID, char *functionName, void **pOutfunctionPointer); #define TNC_RESULT_SUCCESS 0 #define TNC_RESULT_NOT_INITIALIZED 1 #define TNC_RESULT_ALREADY_INITIALIZED 2 #define TNC_RESULT_NO_COMMON_VERSION 3 #define TNC_RESULT_CANT_RETRY 4 #define TNC_RESULT_WONT_RETRY 5 #define TNC_RESULT_INVALID_PARAMETER 6 #define TNC_RESULT_CANT_RESPOND 7 #define TNC_RESULT_ILLEGAL_OPERATION 8 #define TNC_RESULT_OTHER 9 #define TNC_RESULT_FATAL 10 #define TNC_CONNECTION_STATE_CREATE 0 #define TNC_CONNECTION_STATE_HANDSHAKE 1 #define TNC_CONNECTION_STATE_ACCESS_ALLOWED 2 #define TNC_CONNECTION_STATE_ACCESS_ISOLATED 3 #define TNC_CONNECTION_STATE_ACCESS_NONE 4 #define TNC_CONNECTION_STATE_DELETE 5 #define TNC_IFIMC_VERSION_1 1 #define TNC_VENDORID_ANY ((TNC_VendorID) 0xffffff) #define TNC_SUBTYPE_ANY ((TNC_MessageSubtype) 0xff) /* TNCC-TNCS Message Types */ #define TNC_TNCCS_RECOMMENDATION 0x00000001 #define TNC_TNCCS_ERROR 0x00000002 #define TNC_TNCCS_PREFERREDLANGUAGE 0x00000003 #define TNC_TNCCS_REASONSTRINGS 0x00000004 /* IF-TNCCS-SOH - SSoH and SSoHR Attributes */ enum { SSOH_MS_MACHINE_INVENTORY = 1, SSOH_MS_QUARANTINE_STATE = 2, SSOH_MS_PACKET_INFO = 3, SSOH_MS_SYSTEMGENERATED_IDS = 4, SSOH_MS_MACHINENAME = 5, SSOH_MS_CORRELATIONID = 6, SSOH_MS_INSTALLED_SHVS = 7, SSOH_MS_MACHINE_INVENTORY_EX = 8 }; struct tnc_if_imc { struct tnc_if_imc *next; char *name; char *path; void *dlhandle; /* from dlopen() */ TNC_IMCID imcID; TNC_ConnectionID connectionID; TNC_MessageTypeList supported_types; size_t num_supported_types; u8 *imc_send; size_t imc_send_len; /* Functions implemented by IMCs (with TNC_IMC_ prefix) */ TNC_Result (*Initialize)( TNC_IMCID imcID, TNC_Version minVersion, TNC_Version maxVersion, TNC_Version *pOutActualVersion); TNC_Result (*NotifyConnectionChange)( TNC_IMCID imcID, TNC_ConnectionID connectionID, TNC_ConnectionState newState); TNC_Result (*BeginHandshake)( TNC_IMCID imcID, TNC_ConnectionID connectionID); TNC_Result (*ReceiveMessage)( TNC_IMCID imcID, TNC_ConnectionID connectionID, TNC_BufferReference messageBuffer, TNC_UInt32 messageLength, TNC_MessageType messageType); TNC_Result (*BatchEnding)( TNC_IMCID imcID, TNC_ConnectionID connectionID); TNC_Result (*Terminate)(TNC_IMCID imcID); TNC_Result (*ProvideBindFunction)( TNC_IMCID imcID, TNC_TNCC_BindFunctionPointer bindFunction); }; struct tncc_data { struct tnc_if_imc *imc; unsigned int last_batchid; }; #define TNC_MAX_IMC_ID 10 static struct tnc_if_imc *tnc_imc[TNC_MAX_IMC_ID] = { NULL }; /* TNCC functions that IMCs can call */ TNC_Result TNC_TNCC_ReportMessageTypes( TNC_IMCID imcID, TNC_MessageTypeList supportedTypes, TNC_UInt32 typeCount) { TNC_UInt32 i; struct tnc_if_imc *imc; wpa_printf(MSG_DEBUG, "TNC: TNC_TNCC_ReportMessageTypes(imcID=%lu " "typeCount=%lu)", (unsigned long) imcID, (unsigned long) typeCount); for (i = 0; i < typeCount; i++) { wpa_printf(MSG_DEBUG, "TNC: supportedTypes[%lu] = %lu", i, supportedTypes[i]); } if (imcID >= TNC_MAX_IMC_ID || tnc_imc[imcID] == NULL) return TNC_RESULT_INVALID_PARAMETER; imc = tnc_imc[imcID]; os_free(imc->supported_types); imc->supported_types = os_malloc(typeCount * sizeof(TNC_MessageType)); if (imc->supported_types == NULL) return TNC_RESULT_FATAL; os_memcpy(imc->supported_types, supportedTypes, typeCount * sizeof(TNC_MessageType)); imc->num_supported_types = typeCount; return TNC_RESULT_SUCCESS; } TNC_Result TNC_TNCC_SendMessage( TNC_IMCID imcID, TNC_ConnectionID connectionID, TNC_BufferReference message, TNC_UInt32 messageLength, TNC_MessageType messageType) { struct tnc_if_imc *imc; unsigned char *b64; size_t b64len; wpa_printf(MSG_DEBUG, "TNC: TNC_TNCC_SendMessage(imcID=%lu " "connectionID=%lu messageType=%lu)", imcID, connectionID, messageType); wpa_hexdump_ascii(MSG_DEBUG, "TNC: TNC_TNCC_SendMessage", message, messageLength); if (imcID >= TNC_MAX_IMC_ID || tnc_imc[imcID] == NULL) return TNC_RESULT_INVALID_PARAMETER; b64 = base64_encode(message, messageLength, &b64len); if (b64 == NULL) return TNC_RESULT_FATAL; imc = tnc_imc[imcID]; os_free(imc->imc_send); imc->imc_send_len = 0; imc->imc_send = os_zalloc(b64len + 100); if (imc->imc_send == NULL) { os_free(b64); return TNC_RESULT_OTHER; } imc->imc_send_len = os_snprintf((char *) imc->imc_send, b64len + 100, "<IMC-IMV-Message><Type>%08X</Type>" "<Base64>%s</Base64></IMC-IMV-Message>", (unsigned int) messageType, b64); os_free(b64); return TNC_RESULT_SUCCESS; } TNC_Result TNC_TNCC_RequestHandshakeRetry( TNC_IMCID imcID, TNC_ConnectionID connectionID, TNC_RetryReason reason) { wpa_printf(MSG_DEBUG, "TNC: TNC_TNCC_RequestHandshakeRetry"); if (imcID >= TNC_MAX_IMC_ID || tnc_imc[imcID] == NULL) return TNC_RESULT_INVALID_PARAMETER; /* * TODO: trigger a call to eapol_sm_request_reauth(). This would * require that the IMC continues to be loaded in memory afer * authentication.. */ return TNC_RESULT_SUCCESS; } TNC_Result TNC_9048_LogMessage(TNC_IMCID imcID, TNC_UInt32 severity, const char *message) { wpa_printf(MSG_DEBUG, "TNC: TNC_9048_LogMessage(imcID=%lu " "severity==%lu message='%s')", imcID, severity, message); return TNC_RESULT_SUCCESS; } TNC_Result TNC_9048_UserMessage(TNC_IMCID imcID, TNC_ConnectionID connectionID, const char *message) { wpa_printf(MSG_DEBUG, "TNC: TNC_9048_UserMessage(imcID=%lu " "connectionID==%lu message='%s')", imcID, connectionID, message); return TNC_RESULT_SUCCESS; } TNC_Result TNC_TNCC_BindFunction( TNC_IMCID imcID, char *functionName, void **pOutfunctionPointer) { wpa_printf(MSG_DEBUG, "TNC: TNC_TNCC_BindFunction(imcID=%lu, " "functionName='%s')", (unsigned long) imcID, functionName); if (imcID >= TNC_MAX_IMC_ID || tnc_imc[imcID] == NULL) return TNC_RESULT_INVALID_PARAMETER; if (pOutfunctionPointer == NULL) return TNC_RESULT_INVALID_PARAMETER; if (os_strcmp(functionName, "TNC_TNCC_ReportMessageTypes") == 0) *pOutfunctionPointer = TNC_TNCC_ReportMessageTypes; else if (os_strcmp(functionName, "TNC_TNCC_SendMessage") == 0) *pOutfunctionPointer = TNC_TNCC_SendMessage; else if (os_strcmp(functionName, "TNC_TNCC_RequestHandshakeRetry") == 0) *pOutfunctionPointer = TNC_TNCC_RequestHandshakeRetry; else if (os_strcmp(functionName, "TNC_9048_LogMessage") == 0) *pOutfunctionPointer = TNC_9048_LogMessage; else if (os_strcmp(functionName, "TNC_9048_UserMessage") == 0) *pOutfunctionPointer = TNC_9048_UserMessage; else *pOutfunctionPointer = NULL; return TNC_RESULT_SUCCESS; } static void * tncc_get_sym(void *handle, char *func) { void *fptr; #ifdef CONFIG_NATIVE_WINDOWS #ifdef _WIN32_WCE fptr = GetProcAddressA(handle, func); #else /* _WIN32_WCE */ fptr = GetProcAddress(handle, func); #endif /* _WIN32_WCE */ #else /* CONFIG_NATIVE_WINDOWS */ fptr = dlsym(handle, func); #endif /* CONFIG_NATIVE_WINDOWS */ return fptr; } static int tncc_imc_resolve_funcs(struct tnc_if_imc *imc) { void *handle = imc->dlhandle; /* Mandatory IMC functions */ imc->Initialize = tncc_get_sym(handle, "TNC_IMC_Initialize"); if (imc->Initialize == NULL) { wpa_printf(MSG_ERROR, "TNC: IMC does not export " "TNC_IMC_Initialize"); return -1; } imc->BeginHandshake = tncc_get_sym(handle, "TNC_IMC_BeginHandshake"); if (imc->BeginHandshake == NULL) { wpa_printf(MSG_ERROR, "TNC: IMC does not export " "TNC_IMC_BeginHandshake"); return -1; } imc->ProvideBindFunction = tncc_get_sym(handle, "TNC_IMC_ProvideBindFunction"); if (imc->ProvideBindFunction == NULL) { wpa_printf(MSG_ERROR, "TNC: IMC does not export " "TNC_IMC_ProvideBindFunction"); return -1; } /* Optional IMC functions */ imc->NotifyConnectionChange = tncc_get_sym(handle, "TNC_IMC_NotifyConnectionChange"); imc->ReceiveMessage = tncc_get_sym(handle, "TNC_IMC_ReceiveMessage"); imc->BatchEnding = tncc_get_sym(handle, "TNC_IMC_BatchEnding"); imc->Terminate = tncc_get_sym(handle, "TNC_IMC_Terminate"); return 0; } static int tncc_imc_initialize(struct tnc_if_imc *imc) { TNC_Result res; TNC_Version imc_ver; wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMC_Initialize for IMC '%s'", imc->name); res = imc->Initialize(imc->imcID, TNC_IFIMC_VERSION_1, TNC_IFIMC_VERSION_1, &imc_ver); wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_Initialize: res=%lu imc_ver=%lu", (unsigned long) res, (unsigned long) imc_ver); return res == TNC_RESULT_SUCCESS ? 0 : -1; } static int tncc_imc_terminate(struct tnc_if_imc *imc) { TNC_Result res; if (imc->Terminate == NULL) return 0; wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMC_Terminate for IMC '%s'", imc->name); res = imc->Terminate(imc->imcID); wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_Terminate: %lu", (unsigned long) res); return res == TNC_RESULT_SUCCESS ? 0 : -1; } static int tncc_imc_provide_bind_function(struct tnc_if_imc *imc) { TNC_Result res; wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMC_ProvideBindFunction for " "IMC '%s'", imc->name); res = imc->ProvideBindFunction(imc->imcID, TNC_TNCC_BindFunction); wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_ProvideBindFunction: res=%lu", (unsigned long) res); return res == TNC_RESULT_SUCCESS ? 0 : -1; } static int tncc_imc_notify_connection_change(struct tnc_if_imc *imc, TNC_ConnectionState state) { TNC_Result res; if (imc->NotifyConnectionChange == NULL) return 0; wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMC_NotifyConnectionChange(%d)" " for IMC '%s'", (int) state, imc->name); res = imc->NotifyConnectionChange(imc->imcID, imc->connectionID, state); wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_NotifyConnectionChange: %lu", (unsigned long) res); return res == TNC_RESULT_SUCCESS ? 0 : -1; } static int tncc_imc_begin_handshake(struct tnc_if_imc *imc) { TNC_Result res; wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMC_BeginHandshake for IMC " "'%s'", imc->name); res = imc->BeginHandshake(imc->imcID, imc->connectionID); wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_BeginHandshake: %lu", (unsigned long) res); return res == TNC_RESULT_SUCCESS ? 0 : -1; } static int tncc_load_imc(struct tnc_if_imc *imc) { if (imc->path == NULL) { wpa_printf(MSG_DEBUG, "TNC: No IMC configured"); return -1; } wpa_printf(MSG_DEBUG, "TNC: Opening IMC: %s (%s)", imc->name, imc->path); #ifdef CONFIG_NATIVE_WINDOWS #ifdef UNICODE { TCHAR *lib = wpa_strdup_tchar(imc->path); if (lib == NULL) return -1; imc->dlhandle = LoadLibrary(lib); os_free(lib); } #else /* UNICODE */ imc->dlhandle = LoadLibrary(imc->path); #endif /* UNICODE */ if (imc->dlhandle == NULL) { wpa_printf(MSG_ERROR, "TNC: Failed to open IMC '%s' (%s): %d", imc->name, imc->path, (int) GetLastError()); return -1; } #else /* CONFIG_NATIVE_WINDOWS */ imc->dlhandle = dlopen(imc->path, RTLD_LAZY); if (imc->dlhandle == NULL) { wpa_printf(MSG_ERROR, "TNC: Failed to open IMC '%s' (%s): %s", imc->name, imc->path, dlerror()); return -1; } #endif /* CONFIG_NATIVE_WINDOWS */ if (tncc_imc_resolve_funcs(imc) < 0) { wpa_printf(MSG_ERROR, "TNC: Failed to resolve IMC functions"); return -1; } if (tncc_imc_initialize(imc) < 0 || tncc_imc_provide_bind_function(imc) < 0) { wpa_printf(MSG_ERROR, "TNC: Failed to initialize IMC"); return -1; } return 0; } static void tncc_unload_imc(struct tnc_if_imc *imc) { tncc_imc_terminate(imc); tnc_imc[imc->imcID] = NULL; if (imc->dlhandle) { #ifdef CONFIG_NATIVE_WINDOWS FreeLibrary(imc->dlhandle); #else /* CONFIG_NATIVE_WINDOWS */ dlclose(imc->dlhandle); #endif /* CONFIG_NATIVE_WINDOWS */ } os_free(imc->name); os_free(imc->path); os_free(imc->supported_types); os_free(imc->imc_send); } static int tncc_supported_type(struct tnc_if_imc *imc, unsigned int type) { size_t i; unsigned int vendor, subtype; if (imc == NULL || imc->supported_types == NULL) return 0; vendor = type >> 8; subtype = type & 0xff; for (i = 0; i < imc->num_supported_types; i++) { unsigned int svendor, ssubtype; svendor = imc->supported_types[i] >> 8; ssubtype = imc->supported_types[i] & 0xff; if ((vendor == svendor || svendor == TNC_VENDORID_ANY) && (subtype == ssubtype || ssubtype == TNC_SUBTYPE_ANY)) return 1; } return 0; } static void tncc_send_to_imcs(struct tncc_data *tncc, unsigned int type, const u8 *msg, size_t len) { struct tnc_if_imc *imc; TNC_Result res; wpa_hexdump_ascii(MSG_MSGDUMP, "TNC: Message to IMC(s)", msg, len); for (imc = tncc->imc; imc; imc = imc->next) { if (imc->ReceiveMessage == NULL || !tncc_supported_type(imc, type)) continue; wpa_printf(MSG_DEBUG, "TNC: Call ReceiveMessage for IMC '%s'", imc->name); res = imc->ReceiveMessage(imc->imcID, imc->connectionID, (TNC_BufferReference) msg, len, type); wpa_printf(MSG_DEBUG, "TNC: ReceiveMessage: %lu", (unsigned long) res); } } void tncc_init_connection(struct tncc_data *tncc) { struct tnc_if_imc *imc; for (imc = tncc->imc; imc; imc = imc->next) { tncc_imc_notify_connection_change( imc, TNC_CONNECTION_STATE_CREATE); tncc_imc_notify_connection_change( imc, TNC_CONNECTION_STATE_HANDSHAKE); os_free(imc->imc_send); imc->imc_send = NULL; imc->imc_send_len = 0; tncc_imc_begin_handshake(imc); } } size_t tncc_total_send_len(struct tncc_data *tncc) { struct tnc_if_imc *imc; size_t len = 0; for (imc = tncc->imc; imc; imc = imc->next) len += imc->imc_send_len; return len; } u8 * tncc_copy_send_buf(struct tncc_data *tncc, u8 *pos) { struct tnc_if_imc *imc; for (imc = tncc->imc; imc; imc = imc->next) { if (imc->imc_send == NULL) continue; os_memcpy(pos, imc->imc_send, imc->imc_send_len); pos += imc->imc_send_len; os_free(imc->imc_send); imc->imc_send = NULL; imc->imc_send_len = 0; } return pos; } char * tncc_if_tnccs_start(struct tncc_data *tncc) { char *buf = os_malloc(1000); if (buf == NULL) return NULL; tncc->last_batchid++; os_snprintf(buf, 1000, IF_TNCCS_START, tncc->last_batchid); return buf; } char * tncc_if_tnccs_end(void) { char *buf = os_malloc(100); if (buf == NULL) return NULL; os_snprintf(buf, 100, IF_TNCCS_END); return buf; } static void tncc_notify_recommendation(struct tncc_data *tncc, enum tncc_process_res res) { TNC_ConnectionState state; struct tnc_if_imc *imc; switch (res) { case TNCCS_RECOMMENDATION_ALLOW: state = TNC_CONNECTION_STATE_ACCESS_ALLOWED; break; case TNCCS_RECOMMENDATION_NONE: state = TNC_CONNECTION_STATE_ACCESS_NONE; break; case TNCCS_RECOMMENDATION_ISOLATE: state = TNC_CONNECTION_STATE_ACCESS_ISOLATED; break; default: state = TNC_CONNECTION_STATE_ACCESS_NONE; break; } for (imc = tncc->imc; imc; imc = imc->next) tncc_imc_notify_connection_change(imc, state); } static int tncc_get_type(char *start, unsigned int *type) { char *pos = os_strstr(start, "<Type>"); if (pos == NULL) return -1; pos += 6; *type = strtoul(pos, NULL, 16); return 0; } static unsigned char * tncc_get_base64(char *start, size_t *decoded_len) { char *pos, *pos2; unsigned char *decoded; pos = os_strstr(start, "<Base64>"); if (pos == NULL) return NULL; pos += 8; pos2 = os_strstr(pos, "</Base64>"); if (pos2 == NULL) return NULL; *pos2 = '\0'; decoded = base64_decode((unsigned char *) pos, os_strlen(pos), decoded_len); *pos2 = '<'; if (decoded == NULL) { wpa_printf(MSG_DEBUG, "TNC: Failed to decode Base64 data"); } return decoded; } static enum tncc_process_res tncc_get_recommendation(char *start) { char *pos, *pos2, saved; int recom; pos = os_strstr(start, "<TNCCS-Recommendation "); if (pos == NULL) return TNCCS_RECOMMENDATION_ERROR; pos += 21; pos = os_strstr(pos, " type="); if (pos == NULL) return TNCCS_RECOMMENDATION_ERROR; pos += 6; if (*pos == '"') pos++; pos2 = pos; while (*pos2 != '\0' && *pos2 != '"' && *pos2 != '>') pos2++; if (*pos2 == '\0') return TNCCS_RECOMMENDATION_ERROR; saved = *pos2; *pos2 = '\0'; wpa_printf(MSG_DEBUG, "TNC: TNCCS-Recommendation: '%s'", pos); recom = TNCCS_RECOMMENDATION_ERROR; if (os_strcmp(pos, "allow") == 0) recom = TNCCS_RECOMMENDATION_ALLOW; else if (os_strcmp(pos, "none") == 0) recom = TNCCS_RECOMMENDATION_NONE; else if (os_strcmp(pos, "isolate") == 0) recom = TNCCS_RECOMMENDATION_ISOLATE; *pos2 = saved; return recom; } enum tncc_process_res tncc_process_if_tnccs(struct tncc_data *tncc, const u8 *msg, size_t len) { char *buf, *start, *end, *pos, *pos2, *payload; unsigned int batch_id; unsigned char *decoded; size_t decoded_len; enum tncc_process_res res = TNCCS_PROCESS_OK_NO_RECOMMENDATION; int recommendation_msg = 0; buf = os_malloc(len + 1); if (buf == NULL) return TNCCS_PROCESS_ERROR; os_memcpy(buf, msg, len); buf[len] = '\0'; start = os_strstr(buf, "<TNCCS-Batch "); end = os_strstr(buf, "</TNCCS-Batch>"); if (start == NULL || end == NULL || start > end) { os_free(buf); return TNCCS_PROCESS_ERROR; } start += 13; while (*start == ' ') start++; *end = '\0'; pos = os_strstr(start, "BatchId="); if (pos == NULL) { os_free(buf); return TNCCS_PROCESS_ERROR; } pos += 8; if (*pos == '"') pos++; batch_id = atoi(pos); wpa_printf(MSG_DEBUG, "TNC: Received IF-TNCCS BatchId=%u", batch_id); if (batch_id != tncc->last_batchid + 1) { wpa_printf(MSG_DEBUG, "TNC: Unexpected IF-TNCCS BatchId " "%u (expected %u)", batch_id, tncc->last_batchid + 1); os_free(buf); return TNCCS_PROCESS_ERROR; } tncc->last_batchid = batch_id; while (*pos != '\0' && *pos != '>') pos++; if (*pos == '\0') { os_free(buf); return TNCCS_PROCESS_ERROR; } pos++; payload = start; /* * <IMC-IMV-Message> * <Type>01234567</Type> * <Base64>foo==</Base64> * </IMC-IMV-Message> */ while (*start) { char *endpos; unsigned int type; pos = os_strstr(start, "<IMC-IMV-Message>"); if (pos == NULL) break; start = pos + 17; end = os_strstr(start, "</IMC-IMV-Message>"); if (end == NULL) break; *end = '\0'; endpos = end; end += 18; if (tncc_get_type(start, &type) < 0) { *endpos = '<'; start = end; continue; } wpa_printf(MSG_DEBUG, "TNC: IMC-IMV-Message Type 0x%x", type); decoded = tncc_get_base64(start, &decoded_len); if (decoded == NULL) { *endpos = '<'; start = end; continue; } tncc_send_to_imcs(tncc, type, decoded, decoded_len); os_free(decoded); start = end; } /* * <TNCC-TNCS-Message> * <Type>01234567</Type> * <XML><TNCCS-Foo type="foo"></TNCCS-Foo></XML> * <Base64>foo==</Base64> * </TNCC-TNCS-Message> */ start = payload; while (*start) { unsigned int type; char *xml, *xmlend, *endpos; pos = os_strstr(start, "<TNCC-TNCS-Message>"); if (pos == NULL) break; start = pos + 19; end = os_strstr(start, "</TNCC-TNCS-Message>"); if (end == NULL) break; *end = '\0'; endpos = end; end += 20; if (tncc_get_type(start, &type) < 0) { *endpos = '<'; start = end; continue; } wpa_printf(MSG_DEBUG, "TNC: TNCC-TNCS-Message Type 0x%x", type); /* Base64 OR XML */ decoded = NULL; xml = NULL; xmlend = NULL; pos = os_strstr(start, "<XML>"); if (pos) { pos += 5; pos2 = os_strstr(pos, "</XML>"); if (pos2 == NULL) { *endpos = '<'; start = end; continue; } xmlend = pos2; xml = pos; } else { decoded = tncc_get_base64(start, &decoded_len); if (decoded == NULL) { *endpos = '<'; start = end; continue; } } if (decoded) { wpa_hexdump_ascii(MSG_MSGDUMP, "TNC: TNCC-TNCS-Message Base64", decoded, decoded_len); os_free(decoded); } if (xml) { wpa_hexdump_ascii(MSG_MSGDUMP, "TNC: TNCC-TNCS-Message XML", (unsigned char *) xml, xmlend - xml); } if (type == TNC_TNCCS_RECOMMENDATION && xml) { /* * <TNCCS-Recommendation type="allow"> * </TNCCS-Recommendation> */ *xmlend = '\0'; res = tncc_get_recommendation(xml); *xmlend = '<'; recommendation_msg = 1; } start = end; } os_free(buf); if (recommendation_msg) tncc_notify_recommendation(tncc, res); return res; } #ifdef CONFIG_NATIVE_WINDOWS static int tncc_read_config_reg(struct tncc_data *tncc, HKEY hive) { HKEY hk, hk2; LONG ret; DWORD i; struct tnc_if_imc *imc, *last; int j; last = tncc->imc; while (last && last->next) last = last->next; ret = RegOpenKeyEx(hive, TNC_WINREG_PATH, 0, KEY_ENUMERATE_SUB_KEYS, &hk); if (ret != ERROR_SUCCESS) return 0; for (i = 0; ; i++) { TCHAR name[255], *val; DWORD namelen, buflen; namelen = 255; ret = RegEnumKeyEx(hk, i, name, &namelen, NULL, NULL, NULL, NULL); if (ret == ERROR_NO_MORE_ITEMS) break; if (ret != ERROR_SUCCESS) { wpa_printf(MSG_DEBUG, "TNC: RegEnumKeyEx failed: 0x%x", (unsigned int) ret); break; } if (namelen >= 255) namelen = 255 - 1; name[namelen] = '\0'; wpa_printf(MSG_DEBUG, "TNC: IMC '" TSTR "'", name); ret = RegOpenKeyEx(hk, name, 0, KEY_QUERY_VALUE, &hk2); if (ret != ERROR_SUCCESS) { wpa_printf(MSG_DEBUG, "Could not open IMC key '" TSTR "'", name); continue; } ret = RegQueryValueEx(hk2, TEXT("Path"), NULL, NULL, NULL, &buflen); if (ret != ERROR_SUCCESS) { wpa_printf(MSG_DEBUG, "TNC: Could not read Path from " "IMC key '" TSTR "'", name); RegCloseKey(hk2); continue; } val = os_malloc(buflen); if (val == NULL) { RegCloseKey(hk2); continue; } ret = RegQueryValueEx(hk2, TEXT("Path"), NULL, NULL, (LPBYTE) val, &buflen); if (ret != ERROR_SUCCESS) { os_free(val); RegCloseKey(hk2); continue; } RegCloseKey(hk2); wpa_unicode2ascii_inplace(val); wpa_printf(MSG_DEBUG, "TNC: IMC Path '%s'", (char *) val); for (j = 0; j < TNC_MAX_IMC_ID; j++) { if (tnc_imc[j] == NULL) break; } if (j >= TNC_MAX_IMC_ID) { wpa_printf(MSG_DEBUG, "TNC: Too many IMCs"); os_free(val); continue; } imc = os_zalloc(sizeof(*imc)); if (imc == NULL) { os_free(val); break; } imc->imcID = j; wpa_unicode2ascii_inplace(name); imc->name = os_strdup((char *) name); imc->path = os_strdup((char *) val); os_free(val); if (last == NULL) tncc->imc = imc; else last->next = imc; last = imc; tnc_imc[imc->imcID] = imc; } RegCloseKey(hk); return 0; } static int tncc_read_config(struct tncc_data *tncc) { if (tncc_read_config_reg(tncc, HKEY_LOCAL_MACHINE) < 0 || tncc_read_config_reg(tncc, HKEY_CURRENT_USER) < 0) return -1; return 0; } #else /* CONFIG_NATIVE_WINDOWS */ static struct tnc_if_imc * tncc_parse_imc(char *start, char *end, int *error) { struct tnc_if_imc *imc; char *pos, *pos2; int i; for (i = 0; i < TNC_MAX_IMC_ID; i++) { if (tnc_imc[i] == NULL) break; } if (i >= TNC_MAX_IMC_ID) { wpa_printf(MSG_DEBUG, "TNC: Too many IMCs"); return NULL; } imc = os_zalloc(sizeof(*imc)); if (imc == NULL) { *error = 1; return NULL; } imc->imcID = i; pos = start; wpa_printf(MSG_DEBUG, "TNC: Configured IMC: %s", pos); if (pos + 1 >= end || *pos != '"') { wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMC line '%s' " "(no starting quotation mark)", start); os_free(imc); return NULL; } pos++; pos2 = pos; while (pos2 < end && *pos2 != '"') pos2++; if (pos2 >= end) { wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMC line '%s' " "(no ending quotation mark)", start); os_free(imc); return NULL; } *pos2 = '\0'; wpa_printf(MSG_DEBUG, "TNC: Name: '%s'", pos); imc->name = os_strdup(pos); pos = pos2 + 1; if (pos >= end || *pos != ' ') { wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMC line '%s' " "(no space after name)", start); os_free(imc->name); os_free(imc); return NULL; } pos++; wpa_printf(MSG_DEBUG, "TNC: IMC file: '%s'", pos); imc->path = os_strdup(pos); tnc_imc[imc->imcID] = imc; return imc; } static int tncc_read_config(struct tncc_data *tncc) { char *config, *end, *pos, *line_end; size_t config_len; struct tnc_if_imc *imc, *last; last = NULL; config = os_readfile(TNC_CONFIG_FILE, &config_len); if (config == NULL) { wpa_printf(MSG_ERROR, "TNC: Could not open TNC configuration " "file '%s'", TNC_CONFIG_FILE); return -1; } end = config + config_len; for (pos = config; pos < end; pos = line_end + 1) { line_end = pos; while (*line_end != '\n' && *line_end != '\r' && line_end < end) line_end++; *line_end = '\0'; if (os_strncmp(pos, "IMC ", 4) == 0) { int error = 0; imc = tncc_parse_imc(pos + 4, line_end, &error); if (error) return -1; if (imc) { if (last == NULL) tncc->imc = imc; else last->next = imc; last = imc; } } } os_free(config); return 0; } #endif /* CONFIG_NATIVE_WINDOWS */ struct tncc_data * tncc_init(void) { struct tncc_data *tncc; struct tnc_if_imc *imc; tncc = os_zalloc(sizeof(*tncc)); if (tncc == NULL) return NULL; /* TODO: * move loading and Initialize() to a location that is not * re-initialized for every EAP-TNC session (?) */ if (tncc_read_config(tncc) < 0) { wpa_printf(MSG_ERROR, "TNC: Failed to read TNC configuration"); goto failed; } for (imc = tncc->imc; imc; imc = imc->next) { if (tncc_load_imc(imc)) { wpa_printf(MSG_ERROR, "TNC: Failed to load IMC '%s'", imc->name); goto failed; } } return tncc; failed: tncc_deinit(tncc); return NULL; } void tncc_deinit(struct tncc_data *tncc) { struct tnc_if_imc *imc, *prev; imc = tncc->imc; while (imc) { tncc_unload_imc(imc); prev = imc; imc = imc->next; os_free(prev); } os_free(tncc); } static struct wpabuf * tncc_build_soh(int ver) { struct wpabuf *buf; u8 *tlv_len, *tlv_len2, *outer_len, *inner_len, *ssoh_len, *end; u8 correlation_id[24]; /* TODO: get correct name */ char *machinename = "wpa_supplicant@w1.fi"; if (os_get_random(correlation_id, sizeof(correlation_id))) return NULL; wpa_hexdump(MSG_DEBUG, "TNC: SoH Correlation ID", correlation_id, sizeof(correlation_id)); buf = wpabuf_alloc(200); if (buf == NULL) return NULL; /* Vendor-Specific TLV (Microsoft) - SoH */ wpabuf_put_be16(buf, EAP_TLV_VENDOR_SPECIFIC_TLV); /* TLV Type */ tlv_len = wpabuf_put(buf, 2); /* Length */ wpabuf_put_be32(buf, EAP_VENDOR_MICROSOFT); /* Vendor_Id */ wpabuf_put_be16(buf, 0x01); /* TLV Type - SoH TLV */ tlv_len2 = wpabuf_put(buf, 2); /* Length */ /* SoH Header */ wpabuf_put_be16(buf, EAP_TLV_VENDOR_SPECIFIC_TLV); /* Outer Type */ outer_len = wpabuf_put(buf, 2); wpabuf_put_be32(buf, EAP_VENDOR_MICROSOFT); /* IANA SMI Code */ wpabuf_put_be16(buf, ver); /* Inner Type */ inner_len = wpabuf_put(buf, 2); if (ver == 2) { /* SoH Mode Sub-Header */ /* Outer Type */ wpabuf_put_be16(buf, EAP_TLV_VENDOR_SPECIFIC_TLV); wpabuf_put_be16(buf, 4 + 24 + 1 + 1); /* Length */ wpabuf_put_be32(buf, EAP_VENDOR_MICROSOFT); /* IANA SMI Code */ /* Value: */ wpabuf_put_data(buf, correlation_id, sizeof(correlation_id)); wpabuf_put_u8(buf, 0x01); /* Intent Flag - Request */ wpabuf_put_u8(buf, 0x00); /* Content-Type Flag */ } /* SSoH TLV */ /* System-Health-Id */ wpabuf_put_be16(buf, 0x0002); /* Type */ wpabuf_put_be16(buf, 4); /* Length */ wpabuf_put_be32(buf, 79616); /* Vendor-Specific Attribute */ wpabuf_put_be16(buf, EAP_TLV_VENDOR_SPECIFIC_TLV); ssoh_len = wpabuf_put(buf, 2); wpabuf_put_be32(buf, EAP_VENDOR_MICROSOFT); /* IANA SMI Code */ /* MS-Packet-Info */ wpabuf_put_u8(buf, SSOH_MS_PACKET_INFO); /* Note: IF-TNCCS-SOH v1.0 r8 claims this field to be: * Reserved(4 bits) r(1 bit) Vers(3 bits), but Windows XP * SP3 seems to be sending 0x11 for SSoH, i.e., r(request/response) bit * would not be in the specified location. * [MS-SOH] 4.0.2: Reserved(3 bits) r(1 bit) Vers(4 bits) */ wpabuf_put_u8(buf, 0x11); /* r=request, vers=1 */ /* MS-Machine-Inventory */ /* TODO: get correct values; 0 = not applicable for OS */ wpabuf_put_u8(buf, SSOH_MS_MACHINE_INVENTORY); wpabuf_put_be32(buf, 0); /* osVersionMajor */ wpabuf_put_be32(buf, 0); /* osVersionMinor */ wpabuf_put_be32(buf, 0); /* osVersionBuild */ wpabuf_put_be16(buf, 0); /* spVersionMajor */ wpabuf_put_be16(buf, 0); /* spVersionMinor */ wpabuf_put_be16(buf, 0); /* procArch */ /* MS-MachineName */ wpabuf_put_u8(buf, SSOH_MS_MACHINENAME); wpabuf_put_be16(buf, os_strlen(machinename) + 1); wpabuf_put_data(buf, machinename, os_strlen(machinename) + 1); /* MS-CorrelationId */ wpabuf_put_u8(buf, SSOH_MS_CORRELATIONID); wpabuf_put_data(buf, correlation_id, sizeof(correlation_id)); /* MS-Quarantine-State */ wpabuf_put_u8(buf, SSOH_MS_QUARANTINE_STATE); wpabuf_put_be16(buf, 1); /* Flags: ExtState=0, f=0, qState=1 */ wpabuf_put_be32(buf, 0xffffffff); /* ProbTime (hi) */ wpabuf_put_be32(buf, 0xffffffff); /* ProbTime (lo) */ wpabuf_put_be16(buf, 1); /* urlLenInBytes */ wpabuf_put_u8(buf, 0); /* null termination for the url */ /* MS-Machine-Inventory-Ex */ wpabuf_put_u8(buf, SSOH_MS_MACHINE_INVENTORY_EX); wpabuf_put_be32(buf, 0); /* Reserved * (note: Windows XP SP3 uses 0xdecafbad) */ wpabuf_put_u8(buf, 1); /* ProductType: Client */ /* Update SSoH Length */ end = wpabuf_put(buf, 0); WPA_PUT_BE16(ssoh_len, end - ssoh_len - 2); /* TODO: SoHReportEntry TLV (zero or more) */ /* Update length fields */ end = wpabuf_put(buf, 0); WPA_PUT_BE16(tlv_len, end - tlv_len - 2); WPA_PUT_BE16(tlv_len2, end - tlv_len2 - 2); WPA_PUT_BE16(outer_len, end - outer_len - 2); WPA_PUT_BE16(inner_len, end - inner_len - 2); return buf; } struct wpabuf * tncc_process_soh_request(int ver, const u8 *data, size_t len) { const u8 *pos; wpa_hexdump(MSG_DEBUG, "TNC: SoH Request", data, len); if (len < 12) return NULL; /* SoH Request */ pos = data; /* TLV Type */ if (WPA_GET_BE16(pos) != EAP_TLV_VENDOR_SPECIFIC_TLV) return NULL; pos += 2; /* Length */ if (WPA_GET_BE16(pos) < 8) return NULL; pos += 2; /* Vendor_Id */ if (WPA_GET_BE32(pos) != EAP_VENDOR_MICROSOFT) return NULL; pos += 4; /* TLV Type */ if (WPA_GET_BE16(pos) != 0x02 /* SoH request TLV */) return NULL; wpa_printf(MSG_DEBUG, "TNC: SoH Request TLV received"); return tncc_build_soh(2); }