strongswan/src/frontends/android/app/src/main/jni/libandroidbridge/byod/imc_android.c

723 lines
19 KiB
C

/*
* Copyright (C) 2012-2013 Tobias Brunner
* Copyright (C) 2012 Christoph Buehler
* Copyright (C) 2012 Patrick Loetscher
* Copyright (C) 2011-2012 Andreas Steffen
* 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 "imc_android_state.h"
#include "../android_jni.h"
#include "../charonservice.h"
#include <tnc/tnc.h>
#include <imcv.h>
#include <imc/imc_agent.h>
#include <imc/imc_msg.h>
#include <pa_tnc/pa_tnc_msg.h>
#include <ietf/ietf_attr.h>
#include <ietf/ietf_attr_attr_request.h>
#include <ietf/ietf_attr_installed_packages.h>
#include <ietf/ietf_attr_pa_tnc_error.h>
#include <ietf/ietf_attr_product_info.h>
#include <ietf/ietf_attr_remediation_instr.h>
#include <ietf/ietf_attr_string_version.h>
#include <ita/ita_attr.h>
#include <ita/ita_attr_get_settings.h>
#include <tcg/pts/tcg_pts_attr_file_meas.h>
#include <tcg/pts/tcg_pts_attr_meas_algo.h>
#include <tcg/pts/tcg_pts_attr_proto_caps.h>
#include <tcg/pts/tcg_pts_attr_req_file_meas.h>
#include <os_info/os_info.h>
#include <tncif_pa_subtypes.h>
#include <pen/pen.h>
#include <utils/debug.h>
#include <stdio.h>
/* IMC definitions */
static const char imc_name[] = "Android";
static pen_type_t msg_types[] = {
{ PEN_IETF, PA_SUBTYPE_IETF_OPERATING_SYSTEM },
{ PEN_IETF, PA_SUBTYPE_IETF_VPN },
{ PEN_TCG, PA_SUBTYPE_TCG_PTS },
};
static imc_agent_t *imc_android;
/**
* AndroidImc object accessed via JNI
*/
static jobject android_imc;
/**
* AndroidImc class object
*/
static jclass android_imc_cls;
/**
* see section 3.8.1 of TCG TNC IF-IMC Specification 1.3
*/
static TNC_Result tnc_imc_initialize(TNC_IMCID imc_id,
TNC_Version min_version,
TNC_Version max_version,
TNC_Version *actual_version)
{
if (imc_android)
{
DBG1(DBG_IMC, "IMC \"%s\" has already been initialized", imc_name);
return TNC_RESULT_ALREADY_INITIALIZED;
}
imc_android = imc_agent_create(imc_name, msg_types, countof(msg_types),
imc_id, actual_version);
if (!imc_android)
{
return TNC_RESULT_FATAL;
}
if (min_version > TNC_IFIMC_VERSION_1 || max_version < TNC_IFIMC_VERSION_1)
{
DBG1(DBG_IMC, "no common IF-IMC version");
return TNC_RESULT_NO_COMMON_VERSION;
}
return TNC_RESULT_SUCCESS;
}
/**
* Update the state in the GUI.
*/
static void update_imc_state(TNC_ConnectionState state)
{
android_imc_state_t imc_state = ANDROID_IMC_STATE_UNKNOWN;
switch (state)
{ /* map connection states to the values used by the GUI */
case TNC_CONNECTION_STATE_ACCESS_ALLOWED:
imc_state = ANDROID_IMC_STATE_ALLOW;
break;
case TNC_CONNECTION_STATE_ACCESS_ISOLATED:
imc_state = ANDROID_IMC_STATE_ISOLATE;
break;
case TNC_CONNECTION_STATE_ACCESS_NONE:
imc_state = ANDROID_IMC_STATE_BLOCK;
break;
}
charonservice->update_imc_state(charonservice, imc_state);
}
/**
* see section 3.8.2 of TCG TNC IF-IMC Specification 1.3
*/
static TNC_Result tnc_imc_notifyconnectionchange(TNC_IMCID imc_id,
TNC_ConnectionID connection_id,
TNC_ConnectionState new_state)
{
imc_state_t *state;
if (!imc_android)
{
DBG1(DBG_IMC, "IMC \"%s\" has not been initialized", imc_name);
return TNC_RESULT_NOT_INITIALIZED;
}
switch (new_state)
{
case TNC_CONNECTION_STATE_CREATE:
state = imc_android_state_create(connection_id);
return imc_android->create_state(imc_android, state);
case TNC_CONNECTION_STATE_HANDSHAKE:
if (imc_android->change_state(imc_android, connection_id, new_state,
&state) != TNC_RESULT_SUCCESS)
{
return TNC_RESULT_FATAL;
}
state->set_result(state, imc_id,
TNC_IMV_EVALUATION_RESULT_DONT_KNOW);
return TNC_RESULT_SUCCESS;
case TNC_CONNECTION_STATE_DELETE:
return imc_android->delete_state(imc_android, connection_id);
case TNC_CONNECTION_STATE_ACCESS_ALLOWED:
case TNC_CONNECTION_STATE_ACCESS_ISOLATED:
case TNC_CONNECTION_STATE_ACCESS_NONE:
update_imc_state(new_state);
/* fall-through */
default:
return imc_android->change_state(imc_android, connection_id,
new_state, NULL);
}
}
/**
* Convert the native C strings in the enumerator to a Java String array.
* The given enumerator gets destroyed.
*/
static jobjectArray string_array_create(JNIEnv *env, enumerator_t *enumerator)
{
linked_list_t *list;
jobjectArray jarray;
jstring jstring;
char *native;
jclass cls;
int i = 0;
cls = (*env)->FindClass(env, "java/lang/String");
list = linked_list_create_from_enumerator(enumerator);
jarray = (*env)->NewObjectArray(env, list->get_count(list), cls, NULL);
if (!jarray)
{
goto failed;
}
enumerator = list->create_enumerator(list);
while (enumerator->enumerate(enumerator, (void**)&native))
{
jstring = (*env)->NewStringUTF(env, native);
if (!jstring)
{
enumerator->destroy(enumerator);
goto failed;
}
(*env)->SetObjectArrayElement(env, jarray, i++, jstring);
}
enumerator->destroy(enumerator);
list->destroy(list);
return jarray;
failed:
androidjni_exception_occurred(env);
list->destroy(list);
return NULL;
}
/**
* Get a measurement for the given attribute type from the Android IMC.
* NULL is returned if no measurement is available or an error occurred.
*
* The optional args is an enumerator over char* (gets destroyed).
*/
static pa_tnc_attr_t *get_measurement(pen_type_t attr_type, enumerator_t *args)
{
JNIEnv *env;
pa_tnc_attr_t *attr;
jmethodID method_id;
jbyteArray jmeasurement;
jobjectArray jargs = NULL;
chunk_t data;
androidjni_attach_thread(&env);
if (args)
{
jargs = string_array_create(env, args);
if (!jargs)
{
goto failed;
}
method_id = (*env)->GetMethodID(env, android_imc_cls, "getMeasurement",
"(II[Ljava/lang/String;)[B");
}
else
{
method_id = (*env)->GetMethodID(env, android_imc_cls, "getMeasurement",
"(II)[B");
}
if (!method_id)
{
goto failed;
}
jmeasurement = (*env)->CallObjectMethod(env, android_imc, method_id,
attr_type.vendor_id, attr_type.type,
jargs);
if (!jmeasurement || androidjni_exception_occurred(env))
{
goto failed;
}
data = chunk_create((*env)->GetByteArrayElements(env, jmeasurement, NULL),
(*env)->GetArrayLength(env, jmeasurement));
if (!data.ptr)
{
goto failed;
}
attr = imcv_pa_tnc_attributes->construct(imcv_pa_tnc_attributes,
attr_type.vendor_id, attr_type.type, data);
(*env)->ReleaseByteArrayElements(env, jmeasurement, data.ptr, JNI_ABORT);
androidjni_detach_thread();
return attr;
failed:
androidjni_exception_occurred(env);
androidjni_detach_thread();
return NULL;
}
/**
* Add the measurement for the requested attribute type with optional
* arguments (enumerator over char*, gets destroyed).
*/
static void add_measurement(pen_type_t attr_type, imc_msg_t *msg,
enumerator_t *args)
{
pa_tnc_attr_t *attr;
enum_name_t *pa_attr_names;
attr = get_measurement(attr_type, args);
if (attr)
{
msg->add_attribute(msg, attr);
return;
}
pa_attr_names = imcv_pa_tnc_attributes->get_names(imcv_pa_tnc_attributes,
attr_type.vendor_id);
if (pa_attr_names)
{
DBG1(DBG_IMC, "no measurement available for PA-TNC attribute type "
"'%N/%N' 0x%06x/0x%08x", pen_names, attr_type.vendor_id,
pa_attr_names, attr_type.type, attr_type.vendor_id, attr_type.type);
}
else
{
DBG1(DBG_IMC, "no measurement available for PA-TNC attribute type '%N' "
"0x%06x/0x%08x", pen_names, attr_type.vendor_id,
attr_type.vendor_id, attr_type.type);
}
}
/**
* Handle an IETF attribute
*/
static void handle_ietf_attribute(pen_type_t attr_type, pa_tnc_attr_t *attr,
imc_msg_t *out_msg)
{
if (attr_type.type == IETF_ATTR_ATTRIBUTE_REQUEST)
{
ietf_attr_attr_request_t *attr_cast;
pen_type_t *entry;
enumerator_t *enumerator;
attr_cast = (ietf_attr_attr_request_t*)attr;
enumerator = attr_cast->create_enumerator(attr_cast);
while (enumerator->enumerate(enumerator, &entry))
{
add_measurement(*entry, out_msg, NULL);
}
enumerator->destroy(enumerator);
}
else if (attr_type.type == IETF_ATTR_REMEDIATION_INSTRUCTIONS)
{
ietf_attr_remediation_instr_t *attr_cast;
pen_type_t param;
chunk_t str;
char *instr;
attr_cast = (ietf_attr_remediation_instr_t*)attr;
param = attr_cast->get_parameters_type(attr_cast);
if (pen_type_is(param, PEN_IETF, IETF_REMEDIATION_PARAMETERS_STRING))
{
str = attr_cast->get_string(attr_cast, NULL);
instr = strndup(str.ptr, str.len);
charonservice->add_remediation_instr(charonservice, instr);
free (instr);
}
}
}
/**
* Handle an ITA attribute
*/
static void handle_ita_attribute(pen_type_t attr_type, pa_tnc_attr_t *attr,
imc_msg_t *out_msg)
{
if (attr_type.type == ITA_ATTR_GET_SETTINGS)
{
ita_attr_get_settings_t *attr_cast;
attr_cast = (ita_attr_get_settings_t*)attr;
add_measurement((pen_type_t){ PEN_ITA, ITA_ATTR_SETTINGS },
out_msg, attr_cast->create_enumerator(attr_cast));
}
}
/**
* Handle a TCG attribute
*/
static void handle_tcg_attribute(imc_android_state_t *state,
pen_type_t attr_type, pa_tnc_attr_t *attr,
imc_msg_t *out_msg)
{
pts_t *pts;
pts = state->get_pts(state);
switch (attr_type.type)
{
case TCG_PTS_REQ_PROTO_CAPS:
{
tcg_pts_attr_proto_caps_t *attr_cast;
pts_proto_caps_flag_t caps;
attr_cast = (tcg_pts_attr_proto_caps_t*)attr;
caps = attr_cast->get_flags(attr_cast) & pts->get_proto_caps(pts);
pts->set_proto_caps(pts, caps);
attr = tcg_pts_attr_proto_caps_create(caps, FALSE);
out_msg->add_attribute(out_msg, attr);
break;
}
case TCG_PTS_MEAS_ALGO:
{
tcg_pts_attr_meas_algo_t *attr_cast;
pts_meas_algorithms_t supported, algo;
if (!pts_meas_algo_probe(&supported))
{
attr = pts_hash_alg_error_create(PTS_MEAS_ALGO_NONE);
out_msg->add_attribute(out_msg, attr);
break;
}
attr_cast = (tcg_pts_attr_meas_algo_t*)attr;
algo = pts_meas_algo_select(supported,
attr_cast->get_algorithms(attr_cast));
if (algo == PTS_MEAS_ALGO_NONE)
{
attr = pts_hash_alg_error_create(supported);
out_msg->add_attribute(out_msg, attr);
break;
}
pts->set_meas_algorithm(pts, algo);
attr = tcg_pts_attr_meas_algo_create(algo, TRUE);
out_msg->add_attribute(out_msg, attr);
break;
}
case TCG_PTS_REQ_FILE_MEAS:
{
tcg_pts_attr_req_file_meas_t *attr_cast;
pts_file_meas_t *measurements;
pts_error_code_t pts_error;
uint32_t delim;
uint16_t req_id;
bool is_dir;
char *path;
attr_cast = (tcg_pts_attr_req_file_meas_t*)attr;
path = attr_cast->get_pathname(attr_cast);
if (!pts->is_path_valid(pts, path, &pts_error))
{ /* silently ignore internal errors */
break;
}
else if (pts_error)
{
attr = ietf_attr_pa_tnc_error_create(pen_type_create(PEN_TCG,
pts_error), attr->get_value(attr));
out_msg->add_attribute(out_msg, attr);
break;
}
delim = attr_cast->get_delimiter(attr_cast);
if (delim != SOLIDUS_UTF && delim != REVERSE_SOLIDUS_UTF)
{
attr = ietf_attr_pa_tnc_error_create(pen_type_create(PEN_TCG,
TCG_PTS_INVALID_DELIMITER), attr->get_value(attr));
out_msg->add_attribute(out_msg, attr);
break;
}
req_id = attr_cast->get_request_id(attr_cast);
is_dir = attr_cast->get_directory_flag(attr_cast);
DBG1(DBG_IMC, "measurement request %d for %s '%s'", req_id,
is_dir ? "directory" : "file", path);
measurements = pts_file_meas_create_from_path(req_id, path, is_dir,
TRUE, pts->get_meas_algorithm(pts));
if (!measurements)
{
attr = ietf_attr_pa_tnc_error_create(pen_type_create(PEN_TCG,
TCG_PTS_FILE_NOT_FOUND), attr->get_value(attr));
out_msg->add_attribute(out_msg, attr);
break;
}
attr = tcg_pts_attr_file_meas_create(measurements);
attr->set_noskip_flag(attr, TRUE);
out_msg->add_attribute(out_msg, attr);
break;
}
default:
DBG1(DBG_IMC, "received unsupported TCG attribute '%N'",
tcg_attr_names, attr_type.type);
break;
}
}
/**
* see section 3.8.3 of TCG TNC IF-IMC Specification 1.3
*/
static TNC_Result tnc_imc_beginhandshake(TNC_IMCID imc_id,
TNC_ConnectionID connection_id)
{
imc_state_t *state;
imc_msg_t *out_msg;
TNC_Result result = TNC_RESULT_SUCCESS;
if (!imc_android)
{
DBG1(DBG_IMC, "IMC \"%s\" has not been initialized", imc_name);
return TNC_RESULT_NOT_INITIALIZED;
}
if (!imc_android->get_state(imc_android, connection_id, &state))
{
return TNC_RESULT_FATAL;
}
if (lib->settings->get_bool(lib->settings,
"android.imc.send_os_info", TRUE))
{
out_msg = imc_msg_create(imc_android, state, connection_id, imc_id,
TNC_IMVID_ANY, msg_types[0]);
add_measurement((pen_type_t){ PEN_IETF, IETF_ATTR_PRODUCT_INFORMATION },
out_msg, NULL);
add_measurement((pen_type_t){ PEN_IETF, IETF_ATTR_STRING_VERSION },
out_msg, NULL);
add_measurement((pen_type_t){ PEN_ITA, ITA_ATTR_DEVICE_ID },
out_msg, NULL);
/* send PA-TNC message with the excl flag not set */
result = out_msg->send(out_msg, FALSE);
out_msg->destroy(out_msg);
}
return result;
}
static TNC_Result receive_message(imc_android_state_t *state, imc_msg_t *in_msg)
{
imc_msg_t *out_msg;
enumerator_t *enumerator;
pa_tnc_attr_t *attr;
pen_type_t attr_type;
TNC_Result result;
bool fatal_error = FALSE;
out_msg = imc_msg_create_as_reply(in_msg);
/* parse received PA-TNC message and handle local and remote errors */
result = in_msg->receive(in_msg, out_msg, &fatal_error);
if (result != TNC_RESULT_SUCCESS)
{
out_msg->destroy(out_msg);
return result;
}
/* analyze PA-TNC attributes */
enumerator = in_msg->create_attribute_enumerator(in_msg);
while (enumerator->enumerate(enumerator, &attr))
{
attr_type = attr->get_type(attr);
switch (attr_type.vendor_id)
{
case PEN_IETF:
handle_ietf_attribute(attr_type, attr, out_msg);
continue;
case PEN_ITA:
handle_ita_attribute(attr_type, attr, out_msg);
continue;
case PEN_TCG:
handle_tcg_attribute(state, attr_type, attr, out_msg);
continue;
default:
continue;
}
}
enumerator->destroy(enumerator);
if (fatal_error)
{
result = TNC_RESULT_FATAL;
}
else
{
result = out_msg->send(out_msg, TRUE);
}
out_msg->destroy(out_msg);
return result;
}
/**
* see section 3.8.4 of TCG TNC IF-IMC Specification 1.3
*/
static TNC_Result tnc_imc_receivemessage(TNC_IMCID imc_id,
TNC_ConnectionID connection_id,
TNC_BufferReference msg,
TNC_UInt32 msg_len,
TNC_MessageType msg_type)
{
imc_state_t *state;
imc_msg_t *in_msg;
TNC_Result result;
if (!imc_android)
{
DBG1(DBG_IMC, "IMC \"%s\" has not been initialized", imc_name);
return TNC_RESULT_NOT_INITIALIZED;
}
if (!imc_android->get_state(imc_android, connection_id, &state))
{
return TNC_RESULT_FATAL;
}
in_msg = imc_msg_create_from_data(imc_android, state, connection_id,
msg_type, chunk_create(msg, msg_len));
result = receive_message((imc_android_state_t*)state, in_msg);
in_msg->destroy(in_msg);
return result;
}
/**
* see section 3.8.6 of TCG TNC IF-IMV Specification 1.3
*/
static TNC_Result tnc_imc_receivemessagelong(TNC_IMCID imc_id,
TNC_ConnectionID connection_id,
TNC_UInt32 msg_flags,
TNC_BufferReference msg,
TNC_UInt32 msg_len,
TNC_VendorID msg_vid,
TNC_MessageSubtype msg_subtype,
TNC_UInt32 src_imv_id,
TNC_UInt32 dst_imc_id)
{
imc_state_t *state;
imc_msg_t *in_msg;
TNC_Result result;
if (!imc_android)
{
DBG1(DBG_IMC, "IMC \"%s\" has not been initialized", imc_name);
return TNC_RESULT_NOT_INITIALIZED;
}
if (!imc_android->get_state(imc_android, connection_id, &state))
{
return TNC_RESULT_FATAL;
}
in_msg = imc_msg_create_from_long_data(imc_android, state, connection_id,
src_imv_id, dst_imc_id,msg_vid, msg_subtype,
chunk_create(msg, msg_len));
result = receive_message((imc_android_state_t*)state, in_msg);
in_msg->destroy(in_msg);
return result;
}
/**
* see section 3.8.7 of TCG TNC IF-IMC Specification 1.3
*/
static TNC_Result tnc_imc_batchending(TNC_IMCID imc_id,
TNC_ConnectionID connection_id)
{
if (!imc_android)
{
DBG1(DBG_IMC, "IMC \"%s\" has not been initialized", imc_name);
return TNC_RESULT_NOT_INITIALIZED;
}
return TNC_RESULT_SUCCESS;
}
/**
* see section 3.8.8 of TCG TNC IF-IMC Specification 1.3
*/
static TNC_Result tnc_imc_terminate(TNC_IMCID imc_id)
{
if (!imc_android)
{
DBG1(DBG_IMC, "IMC \"%s\" has not been initialized", imc_name);
return TNC_RESULT_NOT_INITIALIZED;
}
imc_android->destroy(imc_android);
imc_android = NULL;
return TNC_RESULT_SUCCESS;
}
/**
* see section 4.2.8.1 of TCG TNC IF-IMC Specification 1.3
*/
static TNC_Result tnc_imc_providebindfunction(TNC_IMCID imc_id,
TNC_TNCC_BindFunctionPointer bind_function)
{
if (!imc_android)
{
DBG1(DBG_IMC, "IMC \"%s\" has not been initialized", imc_name);
return TNC_RESULT_NOT_INITIALIZED;
}
return imc_android->bind_functions(imc_android, bind_function);
}
/*
* Described in header
*/
bool imc_android_register(plugin_t *plugin, plugin_feature_t *feature,
bool reg, void *data)
{
JNIEnv *env;
jmethodID method_id;
jobject obj, context = (jobject)data;
jclass cls;
bool success = TRUE;
androidjni_attach_thread(&env);
if (reg)
{
cls = (*env)->FindClass(env, JNI_PACKAGE_STRING "/imc/AndroidImc");
if (!cls)
{
goto failed;
}
android_imc_cls = (*env)->NewGlobalRef(env, cls);
method_id = (*env)->GetMethodID(env, cls, "<init>",
"(Landroid/content/Context;)V");
if (!method_id)
{
goto failed;
}
obj = (*env)->NewObject(env, cls, method_id, context);
if (!obj)
{
goto failed;
}
android_imc = (*env)->NewGlobalRef(env, obj);
androidjni_detach_thread();
if (tnc->imcs->load_from_functions(tnc->imcs, "Android",
tnc_imc_initialize, tnc_imc_notifyconnectionchange,
tnc_imc_beginhandshake, tnc_imc_receivemessage,
tnc_imc_receivemessagelong, tnc_imc_batchending,
tnc_imc_terminate, tnc_imc_providebindfunction))
{
return TRUE;
}
failed:
DBG1(DBG_IMC, "initialization of Android IMC failed");
androidjni_exception_occurred(env);
success = FALSE;
}
if (android_imc)
{
(*env)->DeleteGlobalRef(env, android_imc);
android_imc = NULL;
}
if (android_imc_cls)
{
(*env)->DeleteGlobalRef(env, android_imc_cls);
android_imc_cls = NULL;
}
androidjni_detach_thread();
return success;
}