499 lines
14 KiB
C
499 lines
14 KiB
C
/*
|
|
* Copyright (C) 2011 Sansar Choinyambuu
|
|
* HSR Hochschule fuer Technik Rapperswil
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by the
|
|
* Free Software Foundation; either version 2 of the License, or (at your
|
|
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* for more details.
|
|
*/
|
|
|
|
#include "imv_attestation_state.h"
|
|
|
|
#include <imv/imv_agent.h>
|
|
#include <pa_tnc/pa_tnc_msg.h>
|
|
#include <ietf/ietf_attr.h>
|
|
#include <ietf/ietf_attr_pa_tnc_error.h>
|
|
#include <tcg/tcg_attr.h>
|
|
|
|
#include <tcg/tcg_pts_attr_req_proto_caps.h>
|
|
#include <tcg/tcg_pts_attr_meas_algo.h>
|
|
#include <tcg/tcg_pts_attr_get_tpm_version_info.h>
|
|
#include <tcg/tcg_pts_attr_get_aik.h>
|
|
#include <tcg/tcg_pts_attr_req_funct_comp_evid.h>
|
|
#include <tcg/tcg_pts_attr_gen_attest_evid.h>
|
|
#include <tcg/tcg_pts_attr_req_file_meas.h>
|
|
|
|
#include <tncif_pa_subtypes.h>
|
|
|
|
#include <pen/pen.h>
|
|
#include <debug.h>
|
|
#include <utils/linked_list.h>
|
|
|
|
#include <trousers/tss.h>
|
|
#include <trousers/trousers.h>
|
|
|
|
/* IMV definitions */
|
|
|
|
static const char imv_name[] = "Attestation";
|
|
|
|
#define IMV_VENDOR_ID PEN_TCG
|
|
#define IMV_SUBTYPE PA_SUBTYPE_TCG_PTS
|
|
|
|
/**
|
|
* UTF-8 encoding of the character used to delimiter the filename
|
|
*/
|
|
#define SOLIDUS_UTF 0x002F
|
|
#define REVERSE_SOLIDUS_UTF 0x005C
|
|
|
|
static imv_agent_t *imv_attestation;
|
|
|
|
/**
|
|
* List of files and directories to measure
|
|
*/
|
|
static linked_list_t *file_list, *directory_list;
|
|
|
|
/**
|
|
* Monotonic increasing number for Request File Measurement attribute
|
|
*/
|
|
static u_int16_t request_id_counter = 0;
|
|
|
|
/**
|
|
* Struct to hold file or directory name with the request ID for Request File Measurement attribute
|
|
*/
|
|
typedef struct measurement_req_entry_t measurement_req_entry_t;
|
|
|
|
struct measurement_req_entry_t {
|
|
char *path;
|
|
u_int16_t request_id;
|
|
};
|
|
|
|
/**
|
|
* see section 3.7.1 of TCG TNC IF-IMV Specification 1.2
|
|
*/
|
|
TNC_Result TNC_IMV_Initialize(TNC_IMVID imv_id,
|
|
TNC_Version min_version,
|
|
TNC_Version max_version,
|
|
TNC_Version *actual_version)
|
|
{
|
|
if (imv_attestation)
|
|
{
|
|
DBG1(DBG_IMV, "IMV \"%s\" has already been initialized", imv_name);
|
|
return TNC_RESULT_ALREADY_INITIALIZED;
|
|
}
|
|
imv_attestation = imv_agent_create(imv_name, IMV_VENDOR_ID, IMV_SUBTYPE,
|
|
imv_id, actual_version);
|
|
if (!imv_attestation)
|
|
{
|
|
return TNC_RESULT_FATAL;
|
|
}
|
|
if (min_version > TNC_IFIMV_VERSION_1 || max_version < TNC_IFIMV_VERSION_1)
|
|
{
|
|
DBG1(DBG_IMV, "no common IF-IMV version");
|
|
return TNC_RESULT_NO_COMMON_VERSION;
|
|
}
|
|
return TNC_RESULT_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* see section 3.7.2 of TCG TNC IF-IMV Specification 1.2
|
|
*/
|
|
TNC_Result TNC_IMV_NotifyConnectionChange(TNC_IMVID imv_id,
|
|
TNC_ConnectionID connection_id,
|
|
TNC_ConnectionState new_state)
|
|
{
|
|
imv_state_t *state;
|
|
imv_attestation_state_t *attestation_state;
|
|
enumerator_t *enumerator;
|
|
char *files;
|
|
char *directories;
|
|
measurement_req_entry_t *entry;
|
|
char *token;
|
|
|
|
if (!imv_attestation)
|
|
{
|
|
DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name);
|
|
return TNC_RESULT_NOT_INITIALIZED;
|
|
}
|
|
switch (new_state)
|
|
{
|
|
case TNC_CONNECTION_STATE_CREATE:
|
|
state = imv_attestation_state_create(connection_id);
|
|
return imv_attestation->create_state(imv_attestation, state);
|
|
case TNC_CONNECTION_STATE_DELETE:
|
|
return imv_attestation->delete_state(imv_attestation, connection_id);
|
|
case TNC_CONNECTION_STATE_HANDSHAKE:
|
|
if (!imv_attestation->get_state(imv_attestation, connection_id, &state))
|
|
{
|
|
return TNC_RESULT_FATAL;
|
|
}
|
|
state->change_state(state, new_state);
|
|
attestation_state = (imv_attestation_state_t*)state;
|
|
|
|
/** Get the files to measure for
|
|
* PTS Request File Measurement attribute
|
|
*/
|
|
|
|
file_list = linked_list_create();
|
|
directory_list = linked_list_create();
|
|
|
|
files = lib->settings->get_str(lib->settings,
|
|
"libimcv.plugins.imc-attestation.files", "none");
|
|
enumerator = enumerator_create_token(files, " ", " ");
|
|
while (enumerator->enumerate(enumerator, &token))
|
|
{
|
|
entry = malloc_thing(measurement_req_entry_t);
|
|
token = strdup(token);
|
|
entry->path = token;
|
|
entry->request_id = request_id_counter;
|
|
file_list->insert_last(file_list, entry);
|
|
free(token);
|
|
request_id_counter ++;
|
|
}
|
|
|
|
/** Get the directories to measure for
|
|
* PTS Request File Measurement attribute
|
|
*/
|
|
|
|
directories = lib->settings->get_str(lib->settings,
|
|
"libimcv.plugins.imc-attestation.directories", "none");
|
|
enumerator = enumerator_create_token(directories, " ", " ");
|
|
while (enumerator->enumerate(enumerator, &token))
|
|
{
|
|
entry = malloc_thing(measurement_req_entry_t);
|
|
token = strdup(token);
|
|
entry->path = token;
|
|
entry->request_id = request_id_counter;
|
|
directory_list->insert_last(directory_list, entry);
|
|
free(token);
|
|
request_id_counter ++;
|
|
}
|
|
enumerator->destroy(enumerator);
|
|
return TNC_RESULT_SUCCESS;
|
|
default:
|
|
return imv_attestation->change_state(imv_attestation, connection_id,
|
|
new_state, NULL);
|
|
}
|
|
}
|
|
|
|
static TNC_Result send_message(TNC_ConnectionID connection_id)
|
|
{
|
|
pa_tnc_msg_t *msg;
|
|
pa_tnc_attr_t *attr;
|
|
TNC_Result result;
|
|
imv_state_t *state;
|
|
imv_attestation_state_t *attestation_state;
|
|
imv_attestation_handshake_state_t handshake_state;
|
|
|
|
if (!imv_attestation->get_state(imv_attestation, connection_id, &state))
|
|
{
|
|
return TNC_RESULT_FATAL;
|
|
}
|
|
|
|
attestation_state = (imv_attestation_state_t*)state;
|
|
handshake_state = attestation_state->get_handshake_state(attestation_state);
|
|
|
|
/* Switch on the attribute type IMV has received */
|
|
switch (handshake_state)
|
|
{
|
|
case IMV_ATTESTATION_STATE_INIT:
|
|
{
|
|
pts_proto_caps_flag_t flags;
|
|
|
|
/* Send Request Protocol Capabilities attribute */
|
|
flags = PTS_PROTO_CAPS_T | PTS_PROTO_CAPS_VER |
|
|
PTS_PROTO_CAPS_CURRENT;
|
|
attr = tcg_pts_attr_req_proto_caps_create(flags);
|
|
break;
|
|
}
|
|
case IMV_ATTESTATION_STATE_PROTO_CAP:
|
|
{
|
|
pts_meas_algorithms_t algorithms;
|
|
|
|
/* Send Measurement Algorithms attribute */
|
|
algorithms = PTS_MEAS_ALGO_SHA1 | PTS_MEAS_ALGO_SHA256 |
|
|
PTS_MEAS_ALGO_SHA384;
|
|
attr = tcg_pts_attr_meas_algo_create(algorithms, FALSE);
|
|
break;
|
|
}
|
|
case IMV_ATTESTATION_STATE_MEAS_ALGO:
|
|
{
|
|
/* Send Get TPM Version Information attribute */
|
|
attr = tcg_pts_attr_get_tpm_version_info_create();
|
|
break;
|
|
}
|
|
case IMV_ATTESTATION_STATE_TPM_INFO:
|
|
{
|
|
/* Send Get AIK attribute */
|
|
/* TODO: Uncomment when the retrieving of AIK on IMC side is implemented */
|
|
//attr = tcg_pts_attr_get_aik_create();
|
|
//break;
|
|
}
|
|
case IMV_ATTESTATION_STATE_AIK:
|
|
{
|
|
/* Send Request File Measurement attribute */
|
|
enumerator_t *enumerator;
|
|
measurement_req_entry_t *entry;
|
|
char *path;
|
|
u_int16_t request_id;
|
|
u_int32_t delimiter = SOLIDUS_UTF;
|
|
|
|
msg = pa_tnc_msg_create();
|
|
|
|
/**
|
|
* Add files to measure to PTS Request File Measurement attribute
|
|
*/
|
|
enumerator = enumerator_create_single(file_list, NULL);
|
|
while (enumerator->enumerate(enumerator, &entry))
|
|
{
|
|
attr = tcg_pts_attr_req_file_meas_create(false,
|
|
entry->request_id, delimiter,
|
|
chunk_create(entry->path, strlen(entry->path)));
|
|
attr->set_noskip_flag(attr, TRUE);
|
|
msg->add_attribute(msg, attr);
|
|
}
|
|
/** Add directories to measure to PTS Request File Measurement attribute
|
|
*/
|
|
enumerator = enumerator_create_single(directory_list, NULL);
|
|
while (enumerator->enumerate(enumerator, &entry))
|
|
{
|
|
attr = tcg_pts_attr_req_file_meas_create(true,
|
|
entry->request_id, delimiter,
|
|
chunk_create(entry->path, strlen(entry->path)));
|
|
attr->set_noskip_flag(attr, TRUE);
|
|
msg->add_attribute(msg, attr);
|
|
}
|
|
enumerator->destroy(enumerator);
|
|
goto end;
|
|
}
|
|
case IMV_ATTESTATION_STATE_SIMPLE_COMP_EVID:
|
|
case IMV_ATTESTATION_STATE_SIMPLE_EVID_FINAL:
|
|
case IMV_ATTESTATION_STATE_FILE_METADATA:
|
|
case IMV_ATTESTATION_STATE_FILE_MEAS:
|
|
case IMV_ATTESTATION_STATE_IML:
|
|
DBG1(DBG_IMV, "Attestation IMV has nothing to send: \"%s\"", handshake_state);
|
|
return TNC_RESULT_FATAL;
|
|
default:
|
|
DBG1(DBG_IMV, "Attestation IMV is in unknown state: \"%s\"", handshake_state);
|
|
return TNC_RESULT_FATAL;
|
|
}
|
|
|
|
attr->set_noskip_flag(attr, TRUE);
|
|
msg = pa_tnc_msg_create();
|
|
msg->add_attribute(msg, attr);
|
|
|
|
end:
|
|
msg->build(msg);
|
|
result = imv_attestation->send_message(imv_attestation, connection_id,
|
|
msg->get_encoding(msg));
|
|
msg->destroy(msg);
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* see section 3.7.3 of TCG TNC IF-IMV Specification 1.2
|
|
*/
|
|
TNC_Result TNC_IMV_ReceiveMessage(TNC_IMVID imv_id,
|
|
TNC_ConnectionID connection_id,
|
|
TNC_BufferReference msg,
|
|
TNC_UInt32 msg_len,
|
|
TNC_MessageType msg_type)
|
|
{
|
|
pa_tnc_msg_t *pa_tnc_msg;
|
|
pa_tnc_attr_t *attr;
|
|
imv_state_t *state;
|
|
imv_attestation_state_t *imv_attestation_state;
|
|
enumerator_t *enumerator;
|
|
TNC_Result result;
|
|
bool fatal_error = FALSE;
|
|
|
|
if (!imv_attestation)
|
|
{
|
|
DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name);
|
|
return TNC_RESULT_NOT_INITIALIZED;
|
|
}
|
|
|
|
/* get current IMV state */
|
|
if (!imv_attestation->get_state(imv_attestation, connection_id, &state))
|
|
{
|
|
return TNC_RESULT_FATAL;
|
|
}
|
|
|
|
/* parse received PA-TNC message and automatically handle any errors */
|
|
result = imv_attestation->receive_message(imv_attestation, connection_id,
|
|
chunk_create(msg, msg_len), msg_type,
|
|
&pa_tnc_msg);
|
|
|
|
/* no parsed PA-TNC attributes available if an error occurred */
|
|
if (!pa_tnc_msg)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
/* analyze PA-TNC attributes */
|
|
enumerator = pa_tnc_msg->create_attribute_enumerator(pa_tnc_msg);
|
|
while (enumerator->enumerate(enumerator, &attr))
|
|
{
|
|
if (attr->get_vendor_id(attr) == PEN_IETF &&
|
|
attr->get_type(attr) == IETF_ATTR_PA_TNC_ERROR)
|
|
{
|
|
ietf_attr_pa_tnc_error_t *error_attr;
|
|
pa_tnc_error_code_t error_code;
|
|
chunk_t msg_info, attr_info;
|
|
u_int32_t offset;
|
|
|
|
error_attr = (ietf_attr_pa_tnc_error_t*)attr;
|
|
error_code = error_attr->get_error_code(error_attr);
|
|
msg_info = error_attr->get_msg_info(error_attr);
|
|
|
|
DBG1(DBG_IMV, "received PA-TNC error '%N' concerning message %#B",
|
|
pa_tnc_error_code_names, error_code, &msg_info);
|
|
switch (error_code)
|
|
{
|
|
case PA_ERROR_INVALID_PARAMETER:
|
|
offset = error_attr->get_offset(error_attr);
|
|
DBG1(DBG_IMV, " occurred at offset of %u bytes", offset);
|
|
break;
|
|
case PA_ERROR_ATTR_TYPE_NOT_SUPPORTED:
|
|
attr_info = error_attr->get_attr_info(error_attr);
|
|
DBG1(DBG_IMV, " unsupported attribute %#B", &attr_info);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
fatal_error = TRUE;
|
|
}
|
|
else if (attr->get_vendor_id(attr) == PEN_TCG)
|
|
{
|
|
/**
|
|
* Handle TCG PTS attributes
|
|
*/
|
|
switch(attr->get_type(attr))
|
|
{
|
|
case TCG_PTS_PROTO_CAPS:
|
|
break;
|
|
case TCG_PTS_MEAS_ALGO_SELECTION:
|
|
break;
|
|
case TCG_PTS_TPM_VERSION_INFO:
|
|
break;
|
|
case TCG_PTS_AIK:
|
|
break;
|
|
|
|
/* PTS-based Attestation Evidence */
|
|
case TCG_PTS_SIMPLE_COMP_EVID:
|
|
break;
|
|
case TCG_PTS_SIMPLE_EVID_FINAL:
|
|
break;
|
|
case TCG_PTS_FILE_MEAS:
|
|
break;
|
|
|
|
/* TODO: Not implemented yet */
|
|
case TCG_PTS_DH_NONCE_PARAMS_RESP:
|
|
case TCG_PTS_UNIX_FILE_META:
|
|
case TCG_PTS_INTEG_MEAS_LOG:
|
|
/* Attributes using XML */
|
|
case TCG_PTS_TEMPL_REF_MANI_SET_META:
|
|
case TCG_PTS_VERIFICATION_RESULT:
|
|
case TCG_PTS_INTEG_REPORT:
|
|
/* On Windows only*/
|
|
case TCG_PTS_WIN_FILE_META:
|
|
case TCG_PTS_REGISTRY_VALUE:
|
|
/* Received on IMC side only*/
|
|
case TCG_PTS_REQ_PROTO_CAPS:
|
|
case TCG_PTS_DH_NONCE_PARAMS_REQ:
|
|
case TCG_PTS_DH_NONCE_FINISH:
|
|
case TCG_PTS_MEAS_ALGO:
|
|
case TCG_PTS_GET_TPM_VERSION_INFO:
|
|
case TCG_PTS_REQ_TEMPL_REF_MANI_SET_META:
|
|
case TCG_PTS_UPDATE_TEMPL_REF_MANI:
|
|
case TCG_PTS_GET_AIK:
|
|
case TCG_PTS_REQ_FUNCT_COMP_EVID:
|
|
case TCG_PTS_GEN_ATTEST_EVID:
|
|
case TCG_PTS_REQ_FILE_META:
|
|
case TCG_PTS_REQ_FILE_MEAS:
|
|
case TCG_PTS_REQ_INTEG_MEAS_LOG:
|
|
default:
|
|
DBG1(DBG_IMV, "received unsupported attribute '%N'",
|
|
tcg_attr_names, attr->get_type(attr));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
enumerator->destroy(enumerator);
|
|
pa_tnc_msg->destroy(pa_tnc_msg);
|
|
|
|
if (fatal_error)
|
|
{
|
|
state->set_recommendation(state,
|
|
TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION,
|
|
TNC_IMV_EVALUATION_RESULT_ERROR);
|
|
return imv_attestation->provide_recommendation(imv_attestation, connection_id);
|
|
}
|
|
|
|
return imv_attestation->provide_recommendation(imv_attestation, connection_id);
|
|
}
|
|
|
|
/**
|
|
* see section 3.7.4 of TCG TNC IF-IMV Specification 1.2
|
|
*/
|
|
TNC_Result TNC_IMV_SolicitRecommendation(TNC_IMVID imv_id,
|
|
TNC_ConnectionID connection_id)
|
|
{
|
|
if (!imv_attestation)
|
|
{
|
|
DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name);
|
|
return TNC_RESULT_NOT_INITIALIZED;
|
|
}
|
|
return imv_attestation->provide_recommendation(imv_attestation, connection_id);
|
|
}
|
|
|
|
/**
|
|
* see section 3.7.5 of TCG TNC IF-IMV Specification 1.2
|
|
*/
|
|
TNC_Result TNC_IMV_BatchEnding(TNC_IMVID imv_id,
|
|
TNC_ConnectionID connection_id)
|
|
{
|
|
if (!imv_attestation)
|
|
{
|
|
DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name);
|
|
return TNC_RESULT_NOT_INITIALIZED;
|
|
}
|
|
return TNC_RESULT_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* see section 3.7.6 of TCG TNC IF-IMV Specification 1.2
|
|
*/
|
|
TNC_Result TNC_IMV_Terminate(TNC_IMVID imv_id)
|
|
{
|
|
if (!imv_attestation)
|
|
{
|
|
DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name);
|
|
return TNC_RESULT_NOT_INITIALIZED;
|
|
}
|
|
imv_attestation->destroy(imv_attestation);
|
|
imv_attestation = NULL;
|
|
|
|
return TNC_RESULT_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* see section 4.2.8.1 of TCG TNC IF-IMV Specification 1.2
|
|
*/
|
|
TNC_Result TNC_IMV_ProvideBindFunction(TNC_IMVID imv_id,
|
|
TNC_TNCS_BindFunctionPointer bind_function)
|
|
{
|
|
if (!imv_attestation)
|
|
{
|
|
DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name);
|
|
return TNC_RESULT_NOT_INITIALIZED;
|
|
}
|
|
return imv_attestation->bind_functions(imv_attestation, bind_function);
|
|
}
|