2008-03-09 09:37:18 +01:00
|
|
|
/*
|
2008-03-30 16:10:44 +02:00
|
|
|
* EAP-TNC - TNCS (IF-IMV, IF-TNCCS, and IF-TNCCS-SOH)
|
2008-03-09 09:37:18 +01:00
|
|
|
* Copyright (c) 2007-2008, 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"
|
|
|
|
#include <dlfcn.h>
|
|
|
|
|
|
|
|
#include "common.h"
|
|
|
|
#include "base64.h"
|
|
|
|
#include "tncs.h"
|
2008-03-30 16:10:44 +02:00
|
|
|
#include "eap_common/eap_tlv_common.h"
|
|
|
|
#include "eap_common/eap_defs.h"
|
2008-03-09 09:37:18 +01:00
|
|
|
|
|
|
|
|
|
|
|
/* TODO: TNCS must be thread-safe; review the code and add locking etc. if
|
|
|
|
* needed.. */
|
|
|
|
|
|
|
|
#define TNC_CONFIG_FILE "/etc/tnc_config"
|
|
|
|
#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/" \
|
2008-05-06 20:29:14 +02:00
|
|
|
"IF_TNCCS# https://www.trustedcomputinggroup.org/XML/SCHEMA/TNCCS_1.0.xsd\">\n"
|
2008-03-09 09:37:18 +01:00
|
|
|
#define IF_TNCCS_END "\n</TNCCS-Batch>"
|
|
|
|
|
|
|
|
/* TNC IF-IMV */
|
|
|
|
|
|
|
|
typedef unsigned long TNC_UInt32;
|
|
|
|
typedef unsigned char *TNC_BufferReference;
|
|
|
|
|
|
|
|
typedef TNC_UInt32 TNC_IMVID;
|
|
|
|
typedef TNC_UInt32 TNC_ConnectionID;
|
|
|
|
typedef TNC_UInt32 TNC_ConnectionState;
|
|
|
|
typedef TNC_UInt32 TNC_RetryReason;
|
|
|
|
typedef TNC_UInt32 TNC_IMV_Action_Recommendation;
|
|
|
|
typedef TNC_UInt32 TNC_IMV_Evaluation_Result;
|
|
|
|
typedef TNC_UInt32 TNC_MessageType;
|
|
|
|
typedef TNC_MessageType *TNC_MessageTypeList;
|
|
|
|
typedef TNC_UInt32 TNC_VendorID;
|
|
|
|
typedef TNC_UInt32 TNC_Subtype;
|
|
|
|
typedef TNC_UInt32 TNC_Version;
|
|
|
|
typedef TNC_UInt32 TNC_Result;
|
|
|
|
typedef TNC_UInt32 TNC_AttributeID;
|
|
|
|
|
|
|
|
typedef TNC_Result (*TNC_TNCS_BindFunctionPointer)(
|
|
|
|
TNC_IMVID imvID,
|
|
|
|
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_IFIMV_VERSION_1 1
|
|
|
|
|
|
|
|
#define TNC_VENDORID_ANY ((TNC_VendorID) 0xffffff)
|
|
|
|
#define TNC_SUBTYPE_ANY ((TNC_Subtype) 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
|
|
|
|
|
|
|
|
/* Possible TNC_IMV_Action_Recommendation values: */
|
|
|
|
enum IMV_Action_Recommendation {
|
|
|
|
TNC_IMV_ACTION_RECOMMENDATION_ALLOW,
|
|
|
|
TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS,
|
|
|
|
TNC_IMV_ACTION_RECOMMENDATION_ISOLATE,
|
|
|
|
TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Possible TNC_IMV_Evaluation_Result values: */
|
|
|
|
enum IMV_Evaluation_Result {
|
|
|
|
TNC_IMV_EVALUATION_RESULT_COMPLIANT,
|
|
|
|
TNC_IMV_EVALUATION_RESULT_NONCOMPLIANT_MINOR,
|
|
|
|
TNC_IMV_EVALUATION_RESULT_NONCOMPLIANT_MAJOR,
|
|
|
|
TNC_IMV_EVALUATION_RESULT_ERROR,
|
|
|
|
TNC_IMV_EVALUATION_RESULT_DONT_KNOW
|
|
|
|
};
|
|
|
|
|
|
|
|
struct tnc_if_imv {
|
|
|
|
struct tnc_if_imv *next;
|
|
|
|
char *name;
|
|
|
|
char *path;
|
|
|
|
void *dlhandle; /* from dlopen() */
|
|
|
|
TNC_IMVID imvID;
|
|
|
|
TNC_MessageTypeList supported_types;
|
|
|
|
size_t num_supported_types;
|
|
|
|
|
|
|
|
/* Functions implemented by IMVs (with TNC_IMV_ prefix) */
|
|
|
|
TNC_Result (*Initialize)(
|
|
|
|
TNC_IMVID imvID,
|
|
|
|
TNC_Version minVersion,
|
|
|
|
TNC_Version maxVersion,
|
|
|
|
TNC_Version *pOutActualVersion);
|
|
|
|
TNC_Result (*NotifyConnectionChange)(
|
|
|
|
TNC_IMVID imvID,
|
|
|
|
TNC_ConnectionID connectionID,
|
|
|
|
TNC_ConnectionState newState);
|
|
|
|
TNC_Result (*ReceiveMessage)(
|
|
|
|
TNC_IMVID imvID,
|
|
|
|
TNC_ConnectionID connectionID,
|
|
|
|
TNC_BufferReference message,
|
|
|
|
TNC_UInt32 messageLength,
|
|
|
|
TNC_MessageType messageType);
|
|
|
|
TNC_Result (*SolicitRecommendation)(
|
|
|
|
TNC_IMVID imvID,
|
|
|
|
TNC_ConnectionID connectionID);
|
|
|
|
TNC_Result (*BatchEnding)(
|
|
|
|
TNC_IMVID imvID,
|
|
|
|
TNC_ConnectionID connectionID);
|
|
|
|
TNC_Result (*Terminate)(TNC_IMVID imvID);
|
|
|
|
TNC_Result (*ProvideBindFunction)(
|
|
|
|
TNC_IMVID imvID,
|
|
|
|
TNC_TNCS_BindFunctionPointer bindFunction);
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
#define TNC_MAX_IMV_ID 10
|
|
|
|
|
|
|
|
struct tncs_data {
|
|
|
|
struct tncs_data *next;
|
|
|
|
struct tnc_if_imv *imv; /* local copy of tncs_global_data->imv */
|
|
|
|
TNC_ConnectionID connectionID;
|
|
|
|
unsigned int last_batchid;
|
|
|
|
enum IMV_Action_Recommendation recommendation;
|
|
|
|
int done;
|
|
|
|
|
|
|
|
struct conn_imv {
|
|
|
|
u8 *imv_send;
|
|
|
|
size_t imv_send_len;
|
|
|
|
enum IMV_Action_Recommendation recommendation;
|
|
|
|
int recommendation_set;
|
|
|
|
} imv_data[TNC_MAX_IMV_ID];
|
|
|
|
|
|
|
|
char *tncs_message;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
struct tncs_global {
|
|
|
|
struct tnc_if_imv *imv;
|
|
|
|
TNC_ConnectionID next_conn_id;
|
|
|
|
struct tncs_data *connections;
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct tncs_global *tncs_global_data = NULL;
|
|
|
|
|
|
|
|
|
|
|
|
static struct tnc_if_imv * tncs_get_imv(TNC_IMVID imvID)
|
|
|
|
{
|
|
|
|
struct tnc_if_imv *imv;
|
|
|
|
|
|
|
|
if (imvID >= TNC_MAX_IMV_ID || tncs_global_data == NULL)
|
|
|
|
return NULL;
|
|
|
|
imv = tncs_global_data->imv;
|
|
|
|
while (imv) {
|
|
|
|
if (imv->imvID == imvID)
|
|
|
|
return imv;
|
|
|
|
imv = imv->next;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static struct tncs_data * tncs_get_conn(TNC_ConnectionID connectionID)
|
|
|
|
{
|
|
|
|
struct tncs_data *tncs;
|
|
|
|
|
|
|
|
if (tncs_global_data == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
tncs = tncs_global_data->connections;
|
|
|
|
while (tncs) {
|
|
|
|
if (tncs->connectionID == connectionID)
|
|
|
|
return tncs;
|
|
|
|
tncs = tncs->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
wpa_printf(MSG_DEBUG, "TNC: Connection ID %lu not found",
|
|
|
|
(unsigned long) connectionID);
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* TNCS functions that IMVs can call */
|
|
|
|
TNC_Result TNC_TNCS_ReportMessageTypes(
|
|
|
|
TNC_IMVID imvID,
|
|
|
|
TNC_MessageTypeList supportedTypes,
|
|
|
|
TNC_UInt32 typeCount)
|
|
|
|
{
|
|
|
|
TNC_UInt32 i;
|
|
|
|
struct tnc_if_imv *imv;
|
|
|
|
|
|
|
|
wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_ReportMessageTypes(imvID=%lu "
|
|
|
|
"typeCount=%lu)",
|
|
|
|
(unsigned long) imvID, (unsigned long) typeCount);
|
|
|
|
|
|
|
|
for (i = 0; i < typeCount; i++) {
|
|
|
|
wpa_printf(MSG_DEBUG, "TNC: supportedTypes[%lu] = %lu",
|
|
|
|
i, supportedTypes[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
imv = tncs_get_imv(imvID);
|
|
|
|
if (imv == NULL)
|
|
|
|
return TNC_RESULT_INVALID_PARAMETER;
|
|
|
|
os_free(imv->supported_types);
|
|
|
|
imv->supported_types =
|
|
|
|
os_malloc(typeCount * sizeof(TNC_MessageTypeList));
|
|
|
|
if (imv->supported_types == NULL)
|
|
|
|
return TNC_RESULT_FATAL;
|
|
|
|
os_memcpy(imv->supported_types, supportedTypes,
|
|
|
|
typeCount * sizeof(TNC_MessageTypeList));
|
|
|
|
imv->num_supported_types = typeCount;
|
|
|
|
|
|
|
|
return TNC_RESULT_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TNC_Result TNC_TNCS_SendMessage(
|
|
|
|
TNC_IMVID imvID,
|
|
|
|
TNC_ConnectionID connectionID,
|
|
|
|
TNC_BufferReference message,
|
|
|
|
TNC_UInt32 messageLength,
|
|
|
|
TNC_MessageType messageType)
|
|
|
|
{
|
|
|
|
struct tncs_data *tncs;
|
|
|
|
unsigned char *b64;
|
|
|
|
size_t b64len;
|
|
|
|
|
|
|
|
wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_SendMessage(imvID=%lu "
|
|
|
|
"connectionID=%lu messageType=%lu)",
|
|
|
|
imvID, connectionID, messageType);
|
|
|
|
wpa_hexdump_ascii(MSG_DEBUG, "TNC: TNC_TNCS_SendMessage",
|
|
|
|
message, messageLength);
|
|
|
|
|
|
|
|
if (tncs_get_imv(imvID) == NULL)
|
|
|
|
return TNC_RESULT_INVALID_PARAMETER;
|
|
|
|
|
|
|
|
tncs = tncs_get_conn(connectionID);
|
|
|
|
if (tncs == NULL)
|
|
|
|
return TNC_RESULT_INVALID_PARAMETER;
|
|
|
|
|
|
|
|
b64 = base64_encode(message, messageLength, &b64len);
|
|
|
|
if (b64 == NULL)
|
|
|
|
return TNC_RESULT_FATAL;
|
|
|
|
|
|
|
|
os_free(tncs->imv_data[imvID].imv_send);
|
|
|
|
tncs->imv_data[imvID].imv_send_len = 0;
|
|
|
|
tncs->imv_data[imvID].imv_send = os_zalloc(b64len + 100);
|
|
|
|
if (tncs->imv_data[imvID].imv_send == NULL) {
|
|
|
|
os_free(b64);
|
|
|
|
return TNC_RESULT_OTHER;
|
|
|
|
}
|
|
|
|
|
|
|
|
tncs->imv_data[imvID].imv_send_len =
|
|
|
|
os_snprintf((char *) tncs->imv_data[imvID].imv_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_TNCS_RequestHandshakeRetry(
|
|
|
|
TNC_IMVID imvID,
|
|
|
|
TNC_ConnectionID connectionID,
|
|
|
|
TNC_RetryReason reason)
|
|
|
|
{
|
|
|
|
wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_RequestHandshakeRetry");
|
|
|
|
/* TODO */
|
|
|
|
return TNC_RESULT_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TNC_Result TNC_TNCS_ProvideRecommendation(
|
|
|
|
TNC_IMVID imvID,
|
|
|
|
TNC_ConnectionID connectionID,
|
|
|
|
TNC_IMV_Action_Recommendation recommendation,
|
|
|
|
TNC_IMV_Evaluation_Result evaluation)
|
|
|
|
{
|
|
|
|
struct tncs_data *tncs;
|
|
|
|
|
|
|
|
wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_ProvideRecommendation(imvID=%lu "
|
|
|
|
"connectionID=%lu recommendation=%lu evaluation=%lu)",
|
|
|
|
(unsigned long) imvID, (unsigned long) connectionID,
|
|
|
|
(unsigned long) recommendation, (unsigned long) evaluation);
|
|
|
|
|
|
|
|
if (tncs_get_imv(imvID) == NULL)
|
|
|
|
return TNC_RESULT_INVALID_PARAMETER;
|
|
|
|
|
|
|
|
tncs = tncs_get_conn(connectionID);
|
|
|
|
if (tncs == NULL)
|
|
|
|
return TNC_RESULT_INVALID_PARAMETER;
|
|
|
|
|
|
|
|
tncs->imv_data[imvID].recommendation = recommendation;
|
|
|
|
tncs->imv_data[imvID].recommendation_set = 1;
|
|
|
|
|
|
|
|
return TNC_RESULT_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TNC_Result TNC_TNCS_GetAttribute(
|
|
|
|
TNC_IMVID imvID,
|
|
|
|
TNC_ConnectionID connectionID,
|
|
|
|
TNC_AttributeID attribureID,
|
|
|
|
TNC_UInt32 bufferLength,
|
|
|
|
TNC_BufferReference buffer,
|
|
|
|
TNC_UInt32 *pOutValueLength)
|
|
|
|
{
|
|
|
|
wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_GetAttribute");
|
|
|
|
/* TODO */
|
|
|
|
return TNC_RESULT_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TNC_Result TNC_TNCS_SetAttribute(
|
|
|
|
TNC_IMVID imvID,
|
|
|
|
TNC_ConnectionID connectionID,
|
|
|
|
TNC_AttributeID attribureID,
|
|
|
|
TNC_UInt32 bufferLength,
|
|
|
|
TNC_BufferReference buffer)
|
|
|
|
{
|
|
|
|
wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_SetAttribute");
|
|
|
|
/* TODO */
|
|
|
|
return TNC_RESULT_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TNC_Result TNC_TNCS_BindFunction(
|
|
|
|
TNC_IMVID imvID,
|
|
|
|
char *functionName,
|
|
|
|
void **pOutFunctionPointer)
|
|
|
|
{
|
|
|
|
wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_BindFunction(imcID=%lu, "
|
|
|
|
"functionName='%s')", (unsigned long) imvID, functionName);
|
|
|
|
|
|
|
|
if (tncs_get_imv(imvID) == NULL)
|
|
|
|
return TNC_RESULT_INVALID_PARAMETER;
|
|
|
|
|
|
|
|
if (pOutFunctionPointer == NULL)
|
|
|
|
return TNC_RESULT_INVALID_PARAMETER;
|
|
|
|
|
|
|
|
if (os_strcmp(functionName, "TNC_TNCS_ReportMessageTypes") == 0)
|
|
|
|
*pOutFunctionPointer = TNC_TNCS_ReportMessageTypes;
|
|
|
|
else if (os_strcmp(functionName, "TNC_TNCS_SendMessage") == 0)
|
|
|
|
*pOutFunctionPointer = TNC_TNCS_SendMessage;
|
|
|
|
else if (os_strcmp(functionName, "TNC_TNCS_RequestHandshakeRetry") ==
|
|
|
|
0)
|
|
|
|
*pOutFunctionPointer = TNC_TNCS_RequestHandshakeRetry;
|
|
|
|
else if (os_strcmp(functionName, "TNC_TNCS_ProvideRecommendation") ==
|
|
|
|
0)
|
|
|
|
*pOutFunctionPointer = TNC_TNCS_ProvideRecommendation;
|
|
|
|
else if (os_strcmp(functionName, "TNC_TNCS_GetAttribute") == 0)
|
|
|
|
*pOutFunctionPointer = TNC_TNCS_GetAttribute;
|
|
|
|
else if (os_strcmp(functionName, "TNC_TNCS_SetAttribute") == 0)
|
|
|
|
*pOutFunctionPointer = TNC_TNCS_SetAttribute;
|
|
|
|
else
|
|
|
|
*pOutFunctionPointer = NULL;
|
|
|
|
|
|
|
|
return TNC_RESULT_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void * tncs_get_sym(void *handle, char *func)
|
|
|
|
{
|
|
|
|
void *fptr;
|
|
|
|
|
|
|
|
fptr = dlsym(handle, func);
|
|
|
|
|
|
|
|
return fptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int tncs_imv_resolve_funcs(struct tnc_if_imv *imv)
|
|
|
|
{
|
|
|
|
void *handle = imv->dlhandle;
|
|
|
|
|
|
|
|
/* Mandatory IMV functions */
|
|
|
|
imv->Initialize = tncs_get_sym(handle, "TNC_IMV_Initialize");
|
|
|
|
if (imv->Initialize == NULL) {
|
|
|
|
wpa_printf(MSG_ERROR, "TNC: IMV does not export "
|
|
|
|
"TNC_IMV_Initialize");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
imv->SolicitRecommendation = tncs_get_sym(
|
|
|
|
handle, "TNC_IMV_SolicitRecommendation");
|
|
|
|
if (imv->SolicitRecommendation == NULL) {
|
|
|
|
wpa_printf(MSG_ERROR, "TNC: IMV does not export "
|
|
|
|
"TNC_IMV_SolicitRecommendation");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
imv->ProvideBindFunction =
|
|
|
|
tncs_get_sym(handle, "TNC_IMV_ProvideBindFunction");
|
|
|
|
if (imv->ProvideBindFunction == NULL) {
|
|
|
|
wpa_printf(MSG_ERROR, "TNC: IMV does not export "
|
|
|
|
"TNC_IMV_ProvideBindFunction");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Optional IMV functions */
|
|
|
|
imv->NotifyConnectionChange =
|
|
|
|
tncs_get_sym(handle, "TNC_IMV_NotifyConnectionChange");
|
|
|
|
imv->ReceiveMessage = tncs_get_sym(handle, "TNC_IMV_ReceiveMessage");
|
|
|
|
imv->BatchEnding = tncs_get_sym(handle, "TNC_IMV_BatchEnding");
|
|
|
|
imv->Terminate = tncs_get_sym(handle, "TNC_IMV_Terminate");
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int tncs_imv_initialize(struct tnc_if_imv *imv)
|
|
|
|
{
|
|
|
|
TNC_Result res;
|
|
|
|
TNC_Version imv_ver;
|
|
|
|
|
|
|
|
wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMV_Initialize for IMV '%s'",
|
|
|
|
imv->name);
|
|
|
|
res = imv->Initialize(imv->imvID, TNC_IFIMV_VERSION_1,
|
|
|
|
TNC_IFIMV_VERSION_1, &imv_ver);
|
|
|
|
wpa_printf(MSG_DEBUG, "TNC: TNC_IMV_Initialize: res=%lu imv_ver=%lu",
|
|
|
|
(unsigned long) res, (unsigned long) imv_ver);
|
|
|
|
|
|
|
|
return res == TNC_RESULT_SUCCESS ? 0 : -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int tncs_imv_terminate(struct tnc_if_imv *imv)
|
|
|
|
{
|
|
|
|
TNC_Result res;
|
|
|
|
|
|
|
|
if (imv->Terminate == NULL)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMV_Terminate for IMV '%s'",
|
|
|
|
imv->name);
|
|
|
|
res = imv->Terminate(imv->imvID);
|
|
|
|
wpa_printf(MSG_DEBUG, "TNC: TNC_IMV_Terminate: %lu",
|
|
|
|
(unsigned long) res);
|
|
|
|
|
|
|
|
return res == TNC_RESULT_SUCCESS ? 0 : -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int tncs_imv_provide_bind_function(struct tnc_if_imv *imv)
|
|
|
|
{
|
|
|
|
TNC_Result res;
|
|
|
|
|
|
|
|
wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMV_ProvideBindFunction for "
|
|
|
|
"IMV '%s'", imv->name);
|
|
|
|
res = imv->ProvideBindFunction(imv->imvID, TNC_TNCS_BindFunction);
|
|
|
|
wpa_printf(MSG_DEBUG, "TNC: TNC_IMV_ProvideBindFunction: res=%lu",
|
|
|
|
(unsigned long) res);
|
|
|
|
|
|
|
|
return res == TNC_RESULT_SUCCESS ? 0 : -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int tncs_imv_notify_connection_change(struct tnc_if_imv *imv,
|
|
|
|
TNC_ConnectionID conn,
|
|
|
|
TNC_ConnectionState state)
|
|
|
|
{
|
|
|
|
TNC_Result res;
|
|
|
|
|
|
|
|
if (imv->NotifyConnectionChange == NULL)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMV_NotifyConnectionChange(%d)"
|
|
|
|
" for IMV '%s'", (int) state, imv->name);
|
|
|
|
res = imv->NotifyConnectionChange(imv->imvID, conn, state);
|
|
|
|
wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_NotifyConnectionChange: %lu",
|
|
|
|
(unsigned long) res);
|
|
|
|
|
|
|
|
return res == TNC_RESULT_SUCCESS ? 0 : -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int tncs_load_imv(struct tnc_if_imv *imv)
|
|
|
|
{
|
|
|
|
if (imv->path == NULL) {
|
|
|
|
wpa_printf(MSG_DEBUG, "TNC: No IMV configured");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
wpa_printf(MSG_DEBUG, "TNC: Opening IMV: %s (%s)",
|
|
|
|
imv->name, imv->path);
|
|
|
|
imv->dlhandle = dlopen(imv->path, RTLD_LAZY);
|
|
|
|
if (imv->dlhandle == NULL) {
|
|
|
|
wpa_printf(MSG_ERROR, "TNC: Failed to open IMV '%s' (%s): %s",
|
|
|
|
imv->name, imv->path, dlerror());
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tncs_imv_resolve_funcs(imv) < 0) {
|
|
|
|
wpa_printf(MSG_ERROR, "TNC: Failed to resolve IMV functions");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tncs_imv_initialize(imv) < 0 ||
|
|
|
|
tncs_imv_provide_bind_function(imv) < 0) {
|
|
|
|
wpa_printf(MSG_ERROR, "TNC: Failed to initialize IMV");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void tncs_free_imv(struct tnc_if_imv *imv)
|
|
|
|
{
|
|
|
|
os_free(imv->name);
|
|
|
|
os_free(imv->path);
|
|
|
|
os_free(imv->supported_types);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void tncs_unload_imv(struct tnc_if_imv *imv)
|
|
|
|
{
|
|
|
|
tncs_imv_terminate(imv);
|
|
|
|
|
|
|
|
if (imv->dlhandle)
|
|
|
|
dlclose(imv->dlhandle);
|
|
|
|
|
|
|
|
tncs_free_imv(imv);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int tncs_supported_type(struct tnc_if_imv *imv, unsigned int type)
|
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
unsigned int vendor, subtype;
|
|
|
|
|
|
|
|
if (imv == NULL || imv->supported_types == NULL)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
vendor = type >> 8;
|
|
|
|
subtype = type & 0xff;
|
|
|
|
|
|
|
|
for (i = 0; i < imv->num_supported_types; i++) {
|
|
|
|
unsigned int svendor, ssubtype;
|
|
|
|
svendor = imv->supported_types[i] >> 8;
|
|
|
|
ssubtype = imv->supported_types[i] & 0xff;
|
|
|
|
if ((vendor == svendor || svendor == TNC_VENDORID_ANY) &&
|
|
|
|
(subtype == ssubtype || ssubtype == TNC_SUBTYPE_ANY))
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void tncs_send_to_imvs(struct tncs_data *tncs, unsigned int type,
|
|
|
|
const u8 *msg, size_t len)
|
|
|
|
{
|
|
|
|
struct tnc_if_imv *imv;
|
|
|
|
TNC_Result res;
|
|
|
|
|
|
|
|
wpa_hexdump_ascii(MSG_MSGDUMP, "TNC: Message to IMV(s)", msg, len);
|
|
|
|
|
|
|
|
for (imv = tncs->imv; imv; imv = imv->next) {
|
|
|
|
if (imv->ReceiveMessage == NULL ||
|
|
|
|
!tncs_supported_type(imv, type))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
wpa_printf(MSG_DEBUG, "TNC: Call ReceiveMessage for IMV '%s'",
|
|
|
|
imv->name);
|
|
|
|
res = imv->ReceiveMessage(imv->imvID, tncs->connectionID,
|
|
|
|
(TNC_BufferReference) msg, len,
|
|
|
|
type);
|
|
|
|
wpa_printf(MSG_DEBUG, "TNC: ReceiveMessage: %lu",
|
|
|
|
(unsigned long) res);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void tncs_batch_ending(struct tncs_data *tncs)
|
|
|
|
{
|
|
|
|
struct tnc_if_imv *imv;
|
|
|
|
TNC_Result res;
|
|
|
|
|
|
|
|
for (imv = tncs->imv; imv; imv = imv->next) {
|
|
|
|
if (imv->BatchEnding == NULL)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
wpa_printf(MSG_DEBUG, "TNC: Call BatchEnding for IMV '%s'",
|
|
|
|
imv->name);
|
|
|
|
res = imv->BatchEnding(imv->imvID, tncs->connectionID);
|
|
|
|
wpa_printf(MSG_DEBUG, "TNC: BatchEnding: %lu",
|
|
|
|
(unsigned long) res);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void tncs_solicit_recommendation(struct tncs_data *tncs)
|
|
|
|
{
|
|
|
|
struct tnc_if_imv *imv;
|
|
|
|
TNC_Result res;
|
|
|
|
|
|
|
|
for (imv = tncs->imv; imv; imv = imv->next) {
|
|
|
|
if (tncs->imv_data[imv->imvID].recommendation_set)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
wpa_printf(MSG_DEBUG, "TNC: Call SolicitRecommendation for "
|
|
|
|
"IMV '%s'", imv->name);
|
|
|
|
res = imv->SolicitRecommendation(imv->imvID,
|
|
|
|
tncs->connectionID);
|
|
|
|
wpa_printf(MSG_DEBUG, "TNC: SolicitRecommendation: %lu",
|
|
|
|
(unsigned long) res);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void tncs_init_connection(struct tncs_data *tncs)
|
|
|
|
{
|
|
|
|
struct tnc_if_imv *imv;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (imv = tncs->imv; imv; imv = imv->next) {
|
|
|
|
tncs_imv_notify_connection_change(
|
|
|
|
imv, tncs->connectionID, TNC_CONNECTION_STATE_CREATE);
|
|
|
|
tncs_imv_notify_connection_change(
|
|
|
|
imv, tncs->connectionID,
|
|
|
|
TNC_CONNECTION_STATE_HANDSHAKE);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < TNC_MAX_IMV_ID; i++) {
|
|
|
|
os_free(tncs->imv_data[i].imv_send);
|
|
|
|
tncs->imv_data[i].imv_send = NULL;
|
|
|
|
tncs->imv_data[i].imv_send_len = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
size_t tncs_total_send_len(struct tncs_data *tncs)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
size_t len = 0;
|
|
|
|
|
|
|
|
for (i = 0; i < TNC_MAX_IMV_ID; i++)
|
|
|
|
len += tncs->imv_data[i].imv_send_len;
|
|
|
|
if (tncs->tncs_message)
|
|
|
|
len += os_strlen(tncs->tncs_message);
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
u8 * tncs_copy_send_buf(struct tncs_data *tncs, u8 *pos)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < TNC_MAX_IMV_ID; i++) {
|
|
|
|
if (tncs->imv_data[i].imv_send == NULL)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
os_memcpy(pos, tncs->imv_data[i].imv_send,
|
|
|
|
tncs->imv_data[i].imv_send_len);
|
|
|
|
pos += tncs->imv_data[i].imv_send_len;
|
|
|
|
os_free(tncs->imv_data[i].imv_send);
|
|
|
|
tncs->imv_data[i].imv_send = NULL;
|
|
|
|
tncs->imv_data[i].imv_send_len = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tncs->tncs_message) {
|
|
|
|
size_t len = os_strlen(tncs->tncs_message);
|
|
|
|
os_memcpy(pos, tncs->tncs_message, len);
|
|
|
|
pos += len;
|
|
|
|
os_free(tncs->tncs_message);
|
|
|
|
tncs->tncs_message = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return pos;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
char * tncs_if_tnccs_start(struct tncs_data *tncs)
|
|
|
|
{
|
|
|
|
char *buf = os_malloc(1000);
|
|
|
|
if (buf == NULL)
|
|
|
|
return NULL;
|
|
|
|
tncs->last_batchid++;
|
|
|
|
os_snprintf(buf, 1000, IF_TNCCS_START, tncs->last_batchid);
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
char * tncs_if_tnccs_end(void)
|
|
|
|
{
|
|
|
|
char *buf = os_malloc(100);
|
|
|
|
if (buf == NULL)
|
|
|
|
return NULL;
|
|
|
|
os_snprintf(buf, 100, IF_TNCCS_END);
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int tncs_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 * tncs_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 tncs_process_res tncs_derive_recommendation(struct tncs_data *tncs)
|
|
|
|
{
|
|
|
|
enum IMV_Action_Recommendation rec;
|
|
|
|
struct tnc_if_imv *imv;
|
|
|
|
TNC_ConnectionState state;
|
|
|
|
char *txt;
|
|
|
|
|
|
|
|
wpa_printf(MSG_DEBUG, "TNC: No more messages from IMVs");
|
|
|
|
|
|
|
|
if (tncs->done)
|
|
|
|
return TNCCS_PROCESS_OK_NO_RECOMMENDATION;
|
|
|
|
|
|
|
|
tncs_solicit_recommendation(tncs);
|
|
|
|
|
|
|
|
/* Select the most restrictive recommendation */
|
|
|
|
rec = TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION;
|
|
|
|
for (imv = tncs->imv; imv; imv = imv->next) {
|
|
|
|
TNC_IMV_Action_Recommendation irec;
|
|
|
|
irec = tncs->imv_data[imv->imvID].recommendation;
|
|
|
|
if (irec == TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS)
|
|
|
|
rec = TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS;
|
|
|
|
if (irec == TNC_IMV_ACTION_RECOMMENDATION_ISOLATE &&
|
|
|
|
rec != TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS)
|
|
|
|
rec = TNC_IMV_ACTION_RECOMMENDATION_ISOLATE;
|
|
|
|
if (irec == TNC_IMV_ACTION_RECOMMENDATION_ALLOW &&
|
|
|
|
rec == TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION)
|
|
|
|
rec = TNC_IMV_ACTION_RECOMMENDATION_ALLOW;
|
|
|
|
}
|
|
|
|
|
|
|
|
wpa_printf(MSG_DEBUG, "TNC: Recommendation: %d", rec);
|
|
|
|
tncs->recommendation = rec;
|
|
|
|
tncs->done = 1;
|
|
|
|
|
|
|
|
txt = NULL;
|
|
|
|
switch (rec) {
|
|
|
|
case TNC_IMV_ACTION_RECOMMENDATION_ALLOW:
|
|
|
|
case TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION:
|
|
|
|
txt = "allow";
|
|
|
|
state = TNC_CONNECTION_STATE_ACCESS_ALLOWED;
|
|
|
|
break;
|
|
|
|
case TNC_IMV_ACTION_RECOMMENDATION_ISOLATE:
|
|
|
|
txt = "isolate";
|
|
|
|
state = TNC_CONNECTION_STATE_ACCESS_ISOLATED;
|
|
|
|
break;
|
|
|
|
case TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS:
|
|
|
|
txt = "none";
|
|
|
|
state = TNC_CONNECTION_STATE_ACCESS_NONE;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
state = TNC_CONNECTION_STATE_ACCESS_ALLOWED;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (txt) {
|
|
|
|
os_free(tncs->tncs_message);
|
|
|
|
tncs->tncs_message = os_zalloc(200);
|
|
|
|
if (tncs->tncs_message) {
|
|
|
|
os_snprintf(tncs->tncs_message, 199,
|
|
|
|
"<TNCC-TNCS-Message><Type>%08X</Type>"
|
|
|
|
"<XML><TNCCS-Recommendation type=\"%s\">"
|
|
|
|
"</TNCCS-Recommendation></XML>"
|
|
|
|
"</TNCC-TNCS-Message>",
|
|
|
|
TNC_TNCCS_RECOMMENDATION, txt);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (imv = tncs->imv; imv; imv = imv->next) {
|
|
|
|
tncs_imv_notify_connection_change(imv, tncs->connectionID,
|
|
|
|
state);
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (rec) {
|
|
|
|
case TNC_IMV_ACTION_RECOMMENDATION_ALLOW:
|
|
|
|
return TNCCS_RECOMMENDATION_ALLOW;
|
|
|
|
case TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS:
|
|
|
|
return TNCCS_RECOMMENDATION_NO_ACCESS;
|
|
|
|
case TNC_IMV_ACTION_RECOMMENDATION_ISOLATE:
|
|
|
|
return TNCCS_RECOMMENDATION_ISOLATE;
|
|
|
|
case TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION:
|
|
|
|
return TNCCS_RECOMMENDATION_NO_RECOMMENDATION;
|
|
|
|
default:
|
|
|
|
return TNCCS_PROCESS_ERROR;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
enum tncs_process_res tncs_process_if_tnccs(struct tncs_data *tncs,
|
|
|
|
const u8 *msg, size_t len)
|
|
|
|
{
|
|
|
|
char *buf, *start, *end, *pos, *pos2, *payload;
|
|
|
|
unsigned int batch_id;
|
|
|
|
unsigned char *decoded;
|
|
|
|
size_t decoded_len;
|
|
|
|
|
|
|
|
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 != tncs->last_batchid + 1) {
|
|
|
|
wpa_printf(MSG_DEBUG, "TNC: Unexpected IF-TNCCS BatchId "
|
|
|
|
"%u (expected %u)",
|
|
|
|
batch_id, tncs->last_batchid + 1);
|
|
|
|
os_free(buf);
|
|
|
|
return TNCCS_PROCESS_ERROR;
|
|
|
|
}
|
|
|
|
tncs->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 (tncs_get_type(start, &type) < 0) {
|
|
|
|
*endpos = '<';
|
|
|
|
start = end;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
wpa_printf(MSG_DEBUG, "TNC: IMC-IMV-Message Type 0x%x", type);
|
|
|
|
|
|
|
|
decoded = tncs_get_base64(start, &decoded_len);
|
|
|
|
if (decoded == NULL) {
|
|
|
|
*endpos = '<';
|
|
|
|
start = end;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
tncs_send_to_imvs(tncs, 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 (tncs_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 = tncs_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);
|
|
|
|
}
|
|
|
|
|
|
|
|
start = end;
|
|
|
|
}
|
|
|
|
|
|
|
|
os_free(buf);
|
|
|
|
|
|
|
|
tncs_batch_ending(tncs);
|
|
|
|
|
|
|
|
if (tncs_total_send_len(tncs) == 0)
|
|
|
|
return tncs_derive_recommendation(tncs);
|
|
|
|
|
|
|
|
return TNCCS_PROCESS_OK_NO_RECOMMENDATION;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static struct tnc_if_imv * tncs_parse_imv(int id, char *start, char *end,
|
|
|
|
int *error)
|
|
|
|
{
|
|
|
|
struct tnc_if_imv *imv;
|
|
|
|
char *pos, *pos2;
|
|
|
|
|
|
|
|
if (id >= TNC_MAX_IMV_ID) {
|
|
|
|
wpa_printf(MSG_DEBUG, "TNC: Too many IMVs");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
imv = os_zalloc(sizeof(*imv));
|
|
|
|
if (imv == NULL) {
|
|
|
|
*error = 1;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
imv->imvID = id;
|
|
|
|
|
|
|
|
pos = start;
|
|
|
|
wpa_printf(MSG_DEBUG, "TNC: Configured IMV: %s", pos);
|
|
|
|
if (pos + 1 >= end || *pos != '"') {
|
|
|
|
wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMV line '%s' "
|
|
|
|
"(no starting quotation mark)", start);
|
|
|
|
os_free(imv);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
pos++;
|
|
|
|
pos2 = pos;
|
|
|
|
while (pos2 < end && *pos2 != '"')
|
|
|
|
pos2++;
|
|
|
|
if (pos2 >= end) {
|
|
|
|
wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMV line '%s' "
|
|
|
|
"(no ending quotation mark)", start);
|
|
|
|
os_free(imv);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
*pos2 = '\0';
|
|
|
|
wpa_printf(MSG_DEBUG, "TNC: Name: '%s'", pos);
|
|
|
|
imv->name = os_strdup(pos);
|
|
|
|
|
|
|
|
pos = pos2 + 1;
|
|
|
|
if (pos >= end || *pos != ' ') {
|
|
|
|
wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMV line '%s' "
|
|
|
|
"(no space after name)", start);
|
|
|
|
os_free(imv);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
pos++;
|
|
|
|
wpa_printf(MSG_DEBUG, "TNC: IMV file: '%s'", pos);
|
|
|
|
imv->path = os_strdup(pos);
|
|
|
|
|
|
|
|
return imv;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int tncs_read_config(struct tncs_global *global)
|
|
|
|
{
|
|
|
|
char *config, *end, *pos, *line_end;
|
|
|
|
size_t config_len;
|
|
|
|
struct tnc_if_imv *imv, *last;
|
|
|
|
int id = 0;
|
|
|
|
|
|
|
|
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, "IMV ", 4) == 0) {
|
|
|
|
int error = 0;
|
|
|
|
|
|
|
|
imv = tncs_parse_imv(id++, pos + 4, line_end, &error);
|
|
|
|
if (error)
|
|
|
|
return -1;
|
|
|
|
if (imv) {
|
|
|
|
if (last == NULL)
|
|
|
|
global->imv = imv;
|
|
|
|
else
|
|
|
|
last->next = imv;
|
|
|
|
last = imv;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
os_free(config);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
struct tncs_data * tncs_init(void)
|
|
|
|
{
|
|
|
|
struct tncs_data *tncs;
|
|
|
|
|
|
|
|
if (tncs_global_data == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
tncs = os_zalloc(sizeof(*tncs));
|
|
|
|
if (tncs == NULL)
|
|
|
|
return NULL;
|
|
|
|
tncs->imv = tncs_global_data->imv;
|
|
|
|
tncs->connectionID = tncs_global_data->next_conn_id++;
|
|
|
|
tncs->next = tncs_global_data->connections;
|
|
|
|
tncs_global_data->connections = tncs;
|
|
|
|
|
|
|
|
return tncs;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void tncs_deinit(struct tncs_data *tncs)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
struct tncs_data *prev, *conn;
|
|
|
|
|
|
|
|
if (tncs == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (i = 0; i < TNC_MAX_IMV_ID; i++)
|
|
|
|
os_free(tncs->imv_data[i].imv_send);
|
|
|
|
|
|
|
|
prev = NULL;
|
|
|
|
conn = tncs_global_data->connections;
|
|
|
|
while (conn) {
|
|
|
|
if (conn == tncs) {
|
|
|
|
if (prev)
|
|
|
|
prev->next = tncs->next;
|
|
|
|
else
|
|
|
|
tncs_global_data->connections = tncs->next;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
prev = conn;
|
|
|
|
conn = conn->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
os_free(tncs->tncs_message);
|
|
|
|
os_free(tncs);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int tncs_global_init(void)
|
|
|
|
{
|
|
|
|
struct tnc_if_imv *imv;
|
|
|
|
|
|
|
|
tncs_global_data = os_zalloc(sizeof(*tncs_global_data));
|
|
|
|
if (tncs_global_data == NULL)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (tncs_read_config(tncs_global_data) < 0) {
|
|
|
|
wpa_printf(MSG_ERROR, "TNC: Failed to read TNC configuration");
|
|
|
|
goto failed;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (imv = tncs_global_data->imv; imv; imv = imv->next) {
|
|
|
|
if (tncs_load_imv(imv)) {
|
|
|
|
wpa_printf(MSG_ERROR, "TNC: Failed to load IMV '%s'",
|
|
|
|
imv->name);
|
|
|
|
goto failed;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
failed:
|
|
|
|
tncs_global_deinit();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void tncs_global_deinit(void)
|
|
|
|
{
|
|
|
|
struct tnc_if_imv *imv, *prev;
|
|
|
|
|
|
|
|
if (tncs_global_data == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
imv = tncs_global_data->imv;
|
|
|
|
while (imv) {
|
|
|
|
tncs_unload_imv(imv);
|
|
|
|
|
|
|
|
prev = imv;
|
|
|
|
imv = imv->next;
|
|
|
|
os_free(prev);
|
|
|
|
}
|
|
|
|
|
|
|
|
os_free(tncs_global_data);
|
2010-04-18 10:10:46 +02:00
|
|
|
tncs_global_data = NULL;
|
2008-03-09 09:37:18 +01:00
|
|
|
}
|
2008-03-30 16:10:44 +02:00
|
|
|
|
|
|
|
|
|
|
|
struct wpabuf * tncs_build_soh_request(void)
|
|
|
|
{
|
|
|
|
struct wpabuf *buf;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Build a SoH Request TLV (to be used inside SoH EAP Extensions
|
|
|
|
* Method)
|
|
|
|
*/
|
|
|
|
|
|
|
|
buf = wpabuf_alloc(8 + 4);
|
|
|
|
if (buf == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
/* Vendor-Specific TLV (Microsoft) - SoH Request */
|
|
|
|
wpabuf_put_be16(buf, EAP_TLV_VENDOR_SPECIFIC_TLV); /* TLV Type */
|
|
|
|
wpabuf_put_be16(buf, 8); /* Length */
|
|
|
|
|
|
|
|
wpabuf_put_be32(buf, EAP_VENDOR_MICROSOFT); /* Vendor_Id */
|
|
|
|
|
|
|
|
wpabuf_put_be16(buf, 0x02); /* TLV Type - SoH Request TLV */
|
|
|
|
wpabuf_put_be16(buf, 0); /* Length */
|
|
|
|
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
struct wpabuf * tncs_process_soh(const u8 *soh_tlv, size_t soh_tlv_len,
|
|
|
|
int *failure)
|
|
|
|
{
|
|
|
|
wpa_hexdump(MSG_DEBUG, "TNC: SoH TLV", soh_tlv, soh_tlv_len);
|
|
|
|
*failure = 0;
|
|
|
|
|
|
|
|
/* TODO: return MS-SoH Response TLV */
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|