diff --git a/.gitignore b/.gitignore
index 3deb9fdb5..0135d6a94 100644
--- a/.gitignore
+++ b/.gitignore
@@ -37,4 +37,8 @@ apidoc/
coverage/
*.gcno
*.gcda
-*.gcov
\ No newline at end of file
+*.gcov
+.cproject
+.project
+.metadata/
+.settings/
\ No newline at end of file
diff --git a/Android.common.mk.in b/Android.common.mk.in
index 1bc8a8304..9f8849d7e 100644
--- a/Android.common.mk.in
+++ b/Android.common.mk.in
@@ -13,6 +13,17 @@ add_plugin = $(if $(call plugin_enabled,$(1)), \
) \
) \
)
+add_plugin_subdirs = $(if $(call plugin_enabled,$(1)), \
+ $(patsubst $(LOCAL_PATH)/%,%, \
+ $(wildcard \
+ $(subst %,$(subst -,_,$(strip $(1))), \
+ $(addprefix $(LOCAL_PATH)/plugins/%/,$(addsuffix /*.c, \
+ $(strip $(2)) \
+ )) \
+ ) \
+ ) \
+ ) \
+ )
# strongSwan version, replaced by top Makefile
strongswan_VERSION := "@PACKAGE_VERSION@"
diff --git a/Android.mk b/Android.mk
index e762fe955..aa61cc0e7 100644
--- a/Android.mk
+++ b/Android.mk
@@ -100,7 +100,8 @@ strongswan_BUILD := \
libhydra \
libstrongswan \
libtncif \
- libtnccs
+ libtnccs \
+ libimcv
ifneq ($(strongswan_BUILD_STARTER),)
strongswan_BUILD += \
diff --git a/src/frontends/android/AndroidManifest.xml b/src/frontends/android/AndroidManifest.xml
index 4d76ac09e..dd03cf2a1 100644
--- a/src/frontends/android/AndroidManifest.xml
+++ b/src/frontends/android/AndroidManifest.xml
@@ -54,6 +54,10 @@
android:name=".ui.LogActivity"
android:label="@string/log_title" >
+
+
diff --git a/src/frontends/android/jni/Android.mk b/src/frontends/android/jni/Android.mk
index 0ae18fc4b..de3403f3d 100644
--- a/src/frontends/android/jni/Android.mk
+++ b/src/frontends/android/jni/Android.mk
@@ -1,11 +1,20 @@
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
+# use "bring your own device" (BYOD) features (also see USE_BYOD in
+# MainActivity.java)
+strongswan_USE_BYOD := true
+
strongswan_CHARON_PLUGINS := android-log openssl fips-prf random nonce pubkey \
pkcs1 pkcs8 pem xcbc hmac socket-default kernel-netlink \
eap-identity eap-mschapv2 eap-md5 eap-gtc
-strongswan_PLUGINS := $(strongswan_CHARON_PLUGINS)
+ifneq ($(strongswan_USE_BYOD),)
+strongswan_BYOD_PLUGINS := eap-ttls eap-tnc tnc-imc tnc-tnccs tnccs-20
+endif
+
+strongswan_PLUGINS := $(strongswan_CHARON_PLUGINS) \
+ $(strongswan_BYOD_PLUGINS)
include $(LOCAL_PATH)/strongswan/Android.common.mk
@@ -52,12 +61,26 @@ strongswan_CFLAGS := \
-DDEV_RANDOM=\"/dev/random\" \
-DDEV_URANDOM=\"/dev/urandom\"
+ifneq ($(strongswan_USE_BYOD),)
+strongswan_CFLAGS += -DUSE_BYOD
+endif
+
+strongswan_BUILD := \
+ vstr \
+ openssl \
+ libandroidbridge \
+ strongswan/src/libipsec \
+ strongswan/src/libcharon \
+ strongswan/src/libhydra \
+ strongswan/src/libstrongswan
+
+ifneq ($(strongswan_USE_BYOD),)
+strongswan_BUILD += \
+ strongswan/src/libtnccs \
+ strongswan/src/libtncif \
+ strongswan/src/libimcv \
+ strongswan/src/libpts
+endif
+
include $(addprefix $(LOCAL_PATH)/,$(addsuffix /Android.mk, \
- vstr \
- openssl \
- libandroidbridge \
- strongswan/src/libipsec \
- strongswan/src/libcharon \
- strongswan/src/libhydra \
- strongswan/src/libstrongswan \
-))
+ $(strongswan_BUILD)))
diff --git a/src/frontends/android/jni/libandroidbridge/Android.mk b/src/frontends/android/jni/libandroidbridge/Android.mk
index fbb86126e..7dd8ad677 100644
--- a/src/frontends/android/jni/libandroidbridge/Android.mk
+++ b/src/frontends/android/jni/libandroidbridge/Android.mk
@@ -14,6 +14,12 @@ kernel/android_net.c \
kernel/network_manager.c \
vpnservice_builder.c
+ifneq ($(strongswan_USE_BYOD),)
+LOCAL_SRC_FILES += \
+byod/imc_android_state.c \
+byod/imc_android.c
+endif
+
# build libandroidbridge -------------------------------------------------------
LOCAL_C_INCLUDES += \
@@ -23,9 +29,22 @@ LOCAL_C_INCLUDES += \
$(strongswan_PATH)/src/libcharon \
$(strongswan_PATH)/src/libstrongswan
+ifneq ($(strongswan_USE_BYOD),)
+LOCAL_C_INCLUDES += \
+ $(strongswan_PATH)/src/libimcv \
+ $(strongswan_PATH)/src/libtncif \
+ $(strongswan_PATH)/src/libtnccs \
+ $(strongswan_PATH)/src/libpts \
+ $(strongswan_PATH)/src/libtls
+endif
+
LOCAL_CFLAGS := $(strongswan_CFLAGS) \
-DPLUGINS='"$(strongswan_CHARON_PLUGINS)"'
+ifneq ($(strongswan_USE_BYOD),)
+LOCAL_CFLAGS += -DPLUGINS_BYOD='"$(strongswan_BYOD_PLUGINS)"'
+endif
+
LOCAL_MODULE := libandroidbridge
LOCAL_MODULE_TAGS := optional
@@ -38,6 +57,8 @@ LOCAL_LDLIBS := -llog
LOCAL_SHARED_LIBRARIES := libstrongswan libhydra libipsec libcharon
+ifneq ($(strongswan_USE_BYOD),)
+LOCAL_SHARED_LIBRARIES += libimcv libtncif libtnccs libpts
+endif
+
include $(BUILD_SHARED_LIBRARY)
-
-
diff --git a/src/frontends/android/jni/libandroidbridge/backend/android_service.c b/src/frontends/android/jni/libandroidbridge/backend/android_service.c
index b22186580..c35d348ba 100644
--- a/src/frontends/android/jni/libandroidbridge/backend/android_service.c
+++ b/src/frontends/android/jni/libandroidbridge/backend/android_service.c
@@ -464,13 +464,18 @@ METHOD(listener_t, ike_reestablish, bool,
}
static void add_auth_cfg_eap(private_android_service_t *this,
- peer_cfg_t *peer_cfg)
+ peer_cfg_t *peer_cfg, bool byod)
{
identification_t *user;
auth_cfg_t *auth;
auth = auth_cfg_create();
auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_EAP);
+ if (byod)
+ { /* use EAP-TTLS if BYOD is enabled */
+ auth->add(auth, AUTH_RULE_EAP_TYPE, EAP_TTLS);
+ }
+
user = identification_create_from_string(this->username);
auth->add(auth, AUTH_RULE_IDENTITY, user);
@@ -549,9 +554,10 @@ static job_requeue_t initiate(private_android_service_t *this)
}
}
if (streq("ikev2-eap", this->type) ||
- streq("ikev2-cert-eap", this->type))
+ streq("ikev2-cert-eap", this->type) ||
+ streq("ikev2-byod-eap", this->type))
{
- add_auth_cfg_eap(this, peer_cfg);
+ add_auth_cfg_eap(this, peer_cfg, strpfx(this->type, "ikev2-byod"));
}
/* remote auth config */
diff --git a/src/frontends/android/jni/libandroidbridge/byod/imc_android.c b/src/frontends/android/jni/libandroidbridge/byod/imc_android.c
new file mode 100644
index 000000000..74cb520e3
--- /dev/null
+++ b/src/frontends/android/jni/libandroidbridge/byod/imc_android.c
@@ -0,0 +1,725 @@
+/*
+ * 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 .
+ *
+ * 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
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+#include
+#include
+
+#include
+
+/* 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;
+ }
+
+ libpts_init();
+
+ 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->create(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;
+ u_int32_t delim;
+ u_int16_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;
+
+ /* parse received PA-TNC message and handle local and remote errors */
+ result = in_msg->receive(in_msg, &fatal_error);
+ if (result != TNC_RESULT_SUCCESS)
+ {
+ return result;
+ }
+ out_msg = imc_msg_create_as_reply(in_msg);
+
+ /* 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;
+ libpts_deinit();
+ 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, "",
+ "(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;
+}
diff --git a/src/frontends/android/jni/libandroidbridge/byod/imc_android.h b/src/frontends/android/jni/libandroidbridge/byod/imc_android.h
new file mode 100644
index 000000000..3bfc6de40
--- /dev/null
+++ b/src/frontends/android/jni/libandroidbridge/byod/imc_android.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2013 Tobias Brunner
+ * 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 .
+ *
+ * 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.
+ */
+
+/**
+ * @defgroup android_imc android_imc
+ * @{ @ingroup android_byod
+ */
+
+#ifndef ANDROID_IMC_H_
+#define ANDROID_IMC_H_
+
+/**
+ * Callback for the Android IMC plugin
+ */
+bool imc_android_register(plugin_t *plugin, plugin_feature_t *feature,
+ bool reg, void *data);
+
+#endif /** ANDROID_IMC_H_ @}*/
diff --git a/src/frontends/android/jni/libandroidbridge/byod/imc_android_state.c b/src/frontends/android/jni/libandroidbridge/byod/imc_android_state.c
new file mode 100644
index 000000000..d429b90b7
--- /dev/null
+++ b/src/frontends/android/jni/libandroidbridge/byod/imc_android_state.c
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2012 Tobias Brunner
+ * Copyright (C) 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 .
+ *
+ * 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
+
+#include
+
+typedef struct private_imc_android_state_t private_imc_android_state_t;
+
+/**
+ * Private data of an imc_state_t object.
+ */
+struct private_imc_android_state_t {
+
+ /**
+ * Public interface
+ */
+ imc_android_state_t public;
+
+ /**
+ * TNCCS connection ID
+ */
+ TNC_ConnectionID connection_id;
+
+ /**
+ * TNCCS connection state
+ */
+ TNC_ConnectionState state;
+
+ /**
+ * Assessment/Evaluation Result
+ */
+ TNC_IMV_Evaluation_Result result;
+
+ /**
+ * Does the TNCCS connection support long message types?
+ */
+ bool has_long;
+
+ /**
+ * Does the TNCCS connection support exclusive delivery?
+ */
+ bool has_excl;
+
+ /**
+ * Maximum PA-TNC message size for this TNCCS connection
+ */
+ u_int32_t max_msg_len;
+
+ /**
+ * TCG Platform Trust Service (PTS)
+ */
+ pts_t *pts;
+};
+
+METHOD(imc_state_t, get_connection_id, TNC_ConnectionID,
+ private_imc_android_state_t *this)
+{
+ return this->connection_id;
+}
+
+METHOD(imc_state_t, has_long, bool,
+ private_imc_android_state_t *this)
+{
+ return this->has_long;
+}
+
+METHOD(imc_state_t, has_excl, bool,
+ private_imc_android_state_t *this)
+{
+ return this->has_excl;
+}
+
+METHOD(imc_state_t, set_flags, void,
+ private_imc_android_state_t *this, bool has_long, bool has_excl)
+{
+ this->has_long = has_long;
+ this->has_excl = has_excl;
+}
+
+METHOD(imc_state_t, set_max_msg_len, void,
+ private_imc_android_state_t *this, u_int32_t max_msg_len)
+{
+ this->max_msg_len = max_msg_len;
+}
+
+METHOD(imc_state_t, get_max_msg_len, u_int32_t,
+ private_imc_android_state_t *this)
+{
+ return this->max_msg_len;
+}
+
+METHOD(imc_state_t, change_state, void,
+ private_imc_android_state_t *this, TNC_ConnectionState new_state)
+{
+ this->state = new_state;
+}
+
+METHOD(imc_state_t, set_result, void,
+ private_imc_android_state_t *this, TNC_IMCID id, TNC_IMV_Evaluation_Result result)
+{
+ this->result = result;
+}
+
+METHOD(imc_state_t, get_result, bool,
+ private_imc_android_state_t *this, TNC_IMCID id, TNC_IMV_Evaluation_Result *result)
+{
+ if (result)
+ {
+ *result = this->result;
+ }
+ return this->result != TNC_IMV_EVALUATION_RESULT_DONT_KNOW;
+}
+
+METHOD(imc_state_t, destroy, void,
+ private_imc_android_state_t *this)
+{
+ this->pts->destroy(this->pts);
+ free(this);
+}
+
+METHOD(imc_android_state_t, get_pts, pts_t*,
+ private_imc_android_state_t *this)
+{
+ return this->pts;
+}
+
+/**
+ * Described in header.
+ */
+imc_state_t *imc_android_state_create(TNC_ConnectionID connection_id)
+{
+ private_imc_android_state_t *this;
+
+ INIT(this,
+ .public = {
+ .interface = {
+ .get_connection_id = _get_connection_id,
+ .has_long = _has_long,
+ .has_excl = _has_excl,
+ .set_flags = _set_flags,
+ .set_max_msg_len = _set_max_msg_len,
+ .get_max_msg_len = _get_max_msg_len,
+ .change_state = _change_state,
+ .set_result = _set_result,
+ .get_result = _get_result,
+ .destroy = _destroy,
+ },
+ .get_pts = _get_pts,
+ },
+ .state = TNC_CONNECTION_STATE_CREATE,
+ .result = TNC_IMV_EVALUATION_RESULT_DONT_KNOW,
+ .connection_id = connection_id,
+ .pts = pts_create(TRUE),
+ );
+
+ return &this->public.interface;
+}
diff --git a/src/frontends/android/jni/libandroidbridge/byod/imc_android_state.h b/src/frontends/android/jni/libandroidbridge/byod/imc_android_state.h
new file mode 100644
index 000000000..68197f331
--- /dev/null
+++ b/src/frontends/android/jni/libandroidbridge/byod/imc_android_state.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2012 Tobias Brunner
+ * 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 .
+ *
+ * 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.
+ */
+
+/**
+ * @defgroup imc_android_state imc_android_state
+ * @{ @ingroup android_byod
+ */
+
+#ifndef IMC_ANDROID_STATE_H_
+#define IMC_ANDROID_STATE_H_
+
+#include
+#include
+
+typedef struct imc_android_state_t imc_android_state_t;
+
+/**
+ * Internal state of an imc_android_t connection instance
+ */
+struct imc_android_state_t {
+
+ /**
+ * imc_state_t interface
+ */
+ imc_state_t interface;
+
+ /**
+ * Get TCG Platform Trust Service (PTS) object
+ */
+ pts_t *(*get_pts)(imc_android_state_t *this);
+};
+
+/**
+ * Create an imc_android_state_t instance
+ *
+ * @param id connection ID
+ */
+imc_state_t* imc_android_state_create(TNC_ConnectionID id);
+
+#endif /** IMC_ANDROID_STATE_H_ @}*/
diff --git a/src/frontends/android/jni/libandroidbridge/charonservice.c b/src/frontends/android/jni/libandroidbridge/charonservice.c
index 25fbf89c6..41fc4d617 100644
--- a/src/frontends/android/jni/libandroidbridge/charonservice.c
+++ b/src/frontends/android/jni/libandroidbridge/charonservice.c
@@ -30,6 +30,10 @@
#include "kernel/android_ipsec.h"
#include "kernel/android_net.h"
+#ifdef USE_BYOD
+#include "byod/imc_android.h"
+#endif
+
#include
#include
#include
@@ -158,6 +162,61 @@ failed:
return success;
}
+METHOD(charonservice_t, update_imc_state, bool,
+ private_charonservice_t *this, android_imc_state_t state)
+{
+ JNIEnv *env;
+ jmethodID method_id;
+ bool success = FALSE;
+
+ androidjni_attach_thread(&env);
+
+ method_id = (*env)->GetMethodID(env, android_charonvpnservice_class,
+ "updateImcState", "(I)V");
+ if (!method_id)
+ {
+ goto failed;
+ }
+ (*env)->CallVoidMethod(env, this->vpn_service, method_id, (jint)state);
+ success = !androidjni_exception_occurred(env);
+
+failed:
+ androidjni_exception_occurred(env);
+ androidjni_detach_thread();
+ return success;
+}
+
+METHOD(charonservice_t, add_remediation_instr, bool,
+ private_charonservice_t *this, char *instr)
+{
+ JNIEnv *env;
+ jmethodID method_id;
+ jstring jinstr;
+ bool success = FALSE;
+
+ androidjni_attach_thread(&env);
+
+ method_id = (*env)->GetMethodID(env, android_charonvpnservice_class,
+ "addRemediationInstruction",
+ "(Ljava/lang/String;)V");
+ if (!method_id)
+ {
+ goto failed;
+ }
+ jinstr = (*env)->NewStringUTF(env, instr);
+ if (!jinstr)
+ {
+ goto failed;
+ }
+ (*env)->CallVoidMethod(env, this->vpn_service, method_id, jinstr);
+ success = !androidjni_exception_occurred(env);
+
+failed:
+ androidjni_exception_occurred(env);
+ androidjni_detach_thread();
+ return success;
+}
+
/**
* Bypass a single socket
*/
@@ -357,7 +416,7 @@ static void initiate(char *type, char *gateway, char *username, char *password)
/**
* Initialize/deinitialize Android backend
*/
-static bool charonservice_register(void *plugin, plugin_feature_t *feature,
+static bool charonservice_register(plugin_t *plugin, plugin_feature_t *feature,
bool reg, void *data)
{
private_charonservice_t *this = (private_charonservice_t*)charonservice;
@@ -434,25 +493,37 @@ static void set_options(char *logfile)
lib->settings->set_str(lib->settings,
"charon.interfaces_ignore", "lo, tun0, tun1, tun2, tun3, "
"tun4");
+
+#ifdef USE_BYOD
+ lib->settings->set_str(lib->settings,
+ "charon.plugins.eap-tnc.protocol", "tnccs-2.0");
+ lib->settings->set_bool(lib->settings,
+ "android.imc.send_os_info", TRUE);
+ lib->settings->set_str(lib->settings,
+ "libtnccs.tnc_config", "");
+#endif
}
/**
* Initialize the charonservice object
*/
-static void charonservice_init(JNIEnv *env, jobject service, jobject builder)
+static void charonservice_init(JNIEnv *env, jobject service, jobject builder,
+ jboolean byod)
{
private_charonservice_t *this;
static plugin_feature_t features[] = {
PLUGIN_CALLBACK(kernel_ipsec_register, kernel_android_ipsec_create),
PLUGIN_PROVIDE(CUSTOM, "kernel-ipsec"),
- PLUGIN_CALLBACK((plugin_feature_callback_t)charonservice_register, NULL),
- PLUGIN_PROVIDE(CUSTOM, "Android backend"),
+ PLUGIN_CALLBACK(charonservice_register, NULL),
+ PLUGIN_PROVIDE(CUSTOM, "android-backend"),
PLUGIN_DEPENDS(CUSTOM, "libcharon"),
};
INIT(this,
.public = {
.update_status = _update_status,
+ .update_imc_state = _update_imc_state,
+ .add_remediation_instr = _add_remediation_instr,
.bypass_socket = _bypass_socket,
.get_trusted_certificates = _get_trusted_certificates,
.get_user_certificate = _get_user_certificate,
@@ -471,6 +542,21 @@ static void charonservice_init(JNIEnv *env, jobject service, jobject builder)
lib->plugins->add_static_features(lib->plugins, "androidbridge", features,
countof(features), TRUE);
+
+#ifdef USE_BYOD
+ if (byod)
+ {
+ plugin_feature_t byod_features[] = {
+ PLUGIN_CALLBACK(imc_android_register, this->vpn_service),
+ PLUGIN_PROVIDE(CUSTOM, "android-imc"),
+ PLUGIN_DEPENDS(CUSTOM, "android-backend"),
+ PLUGIN_DEPENDS(CUSTOM, "imc-manager"),
+ };
+
+ lib->plugins->add_static_features(lib->plugins, "android-byod",
+ byod_features, countof(byod_features), TRUE);
+ }
+#endif
}
/**
@@ -504,11 +590,11 @@ static void segv_handler(int signal)
* Initialize charon and the libraries via JNI
*/
JNI_METHOD(CharonVpnService, initializeCharon, void,
- jobject builder, jstring jlogfile)
+ jobject builder, jstring jlogfile, jboolean byod)
{
struct sigaction action;
struct utsname utsname;
- char *logfile;
+ char *logfile, *plugins;
/* logging for library during initialization, as we have no bus yet */
dbg = dbg_android;
@@ -551,7 +637,7 @@ JNI_METHOD(CharonVpnService, initializeCharon, void,
charon->load_loggers(charon, NULL, FALSE);
- charonservice_init(env, this, builder);
+ charonservice_init(env, this, builder, byod);
if (uname(&utsname) != 0)
{
@@ -560,7 +646,18 @@ JNI_METHOD(CharonVpnService, initializeCharon, void,
DBG1(DBG_DMN, "Starting IKE charon daemon (strongSwan "VERSION", %s %s, %s)",
utsname.sysname, utsname.release, utsname.machine);
- if (!charon->initialize(charon, PLUGINS))
+#ifdef PLUGINS_BYOD
+ if (byod)
+ {
+ plugins = PLUGINS " " PLUGINS_BYOD;
+ }
+ else
+#endif
+ {
+ plugins = PLUGINS;
+ }
+
+ if (!charon->initialize(charon, plugins))
{
libcharon_deinit();
charonservice_deinit(env);
diff --git a/src/frontends/android/jni/libandroidbridge/charonservice.h b/src/frontends/android/jni/libandroidbridge/charonservice.h
index 7391ea61d..0c71d876d 100644
--- a/src/frontends/android/jni/libandroidbridge/charonservice.h
+++ b/src/frontends/android/jni/libandroidbridge/charonservice.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 Tobias Brunner
+ * Copyright (C) 2012-2013 Tobias Brunner
* Copyright (C) 2012 Giuliano Grassi
* Copyright (C) 2012 Ralf Sager
* Hochschule fuer Technik Rapperswil
@@ -21,6 +21,9 @@
* @defgroup android_backend backend
* @ingroup libandroidbridge
*
+ * @defgroup android_byod byod
+ * @ingroup libandroidbridge
+ *
* @defgroup android_kernel kernel
* @ingroup libandroidbridge
*
@@ -38,6 +41,7 @@
#include
typedef enum android_vpn_state_t android_vpn_state_t;
+typedef enum android_imc_state_t android_imc_state_t;
typedef struct charonservice_t charonservice_t;
/**
@@ -53,6 +57,16 @@ enum android_vpn_state_t {
CHARONSERVICE_GENERIC_ERROR,
};
+/**
+ * Final IMC state as defined in ImcState.java
+ */
+enum android_imc_state_t {
+ ANDROID_IMC_STATE_UNKNOWN = 0,
+ ANDROID_IMC_STATE_ALLOW = 1,
+ ANDROID_IMC_STATE_BLOCK = 2,
+ ANDROID_IMC_STATE_ISOLATE = 3,
+};
+
/**
* Public interface of charonservice.
*
@@ -68,6 +82,22 @@ struct charonservice_t {
*/
bool (*update_status)(charonservice_t *this, android_vpn_state_t code);
+ /**
+ * Update final IMC state in the Java domain (UI)
+ *
+ * @param state IMC state
+ * @return TRUE on success
+ */
+ bool (*update_imc_state)(charonservice_t *this, android_imc_state_t state);
+
+ /**
+ * Add a remediation instruction via JNI
+ *
+ * @param instr remediation instruction
+ * @return TRUE on success
+ */
+ bool (*add_remediation_instr)(charonservice_t *this, char *instr);
+
/**
* Install a bypass policy for the given socket using the protect() Method
* of the Android VpnService interface.
diff --git a/src/frontends/android/res/drawable/remediation_instruction_background_large.xml b/src/frontends/android/res/drawable/remediation_instruction_background_large.xml
new file mode 100644
index 000000000..470fecb12
--- /dev/null
+++ b/src/frontends/android/res/drawable/remediation_instruction_background_large.xml
@@ -0,0 +1,32 @@
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+
diff --git a/src/frontends/android/res/drawable/state_background.xml b/src/frontends/android/res/drawable/state_background.xml
new file mode 100644
index 000000000..ee36325cc
--- /dev/null
+++ b/src/frontends/android/res/drawable/state_background.xml
@@ -0,0 +1,32 @@
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+
diff --git a/src/frontends/android/res/layout-large/remediation_instructions.xml b/src/frontends/android/res/layout-large/remediation_instructions.xml
new file mode 100644
index 000000000..5a28dd654
--- /dev/null
+++ b/src/frontends/android/res/layout-large/remediation_instructions.xml
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/frontends/android/res/layout/imc_state_fragment.xml b/src/frontends/android/res/layout/imc_state_fragment.xml
new file mode 100644
index 000000000..171c88d2d
--- /dev/null
+++ b/src/frontends/android/res/layout/imc_state_fragment.xml
@@ -0,0 +1,69 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/frontends/android/res/layout/main.xml b/src/frontends/android/res/layout/main.xml
index 1c7973e20..ab03e72bc 100644
--- a/src/frontends/android/res/layout/main.xml
+++ b/src/frontends/android/res/layout/main.xml
@@ -1,6 +1,6 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/frontends/android/res/layout/remediation_instruction_item.xml b/src/frontends/android/res/layout/remediation_instruction_item.xml
new file mode 100644
index 000000000..30dfb2219
--- /dev/null
+++ b/src/frontends/android/res/layout/remediation_instruction_item.xml
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/frontends/android/res/drawable/vpn_state_background.xml b/src/frontends/android/res/layout/remediation_instructions.xml
similarity index 72%
rename from src/frontends/android/res/drawable/vpn_state_background.xml
rename to src/frontends/android/res/layout/remediation_instructions.xml
index 24f469add..84143b575 100644
--- a/src/frontends/android/res/drawable/vpn_state_background.xml
+++ b/src/frontends/android/res/layout/remediation_instructions.xml
@@ -1,6 +1,6 @@
-
+
-
-
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/src/frontends/android/res/layout/certificate_selector.xml b/src/frontends/android/res/layout/two_line_button.xml
similarity index 100%
rename from src/frontends/android/res/layout/certificate_selector.xml
rename to src/frontends/android/res/layout/two_line_button.xml
diff --git a/src/frontends/android/res/layout/vpn_state_fragment.xml b/src/frontends/android/res/layout/vpn_state_fragment.xml
index 6353f3289..e347c4c4b 100644
--- a/src/frontends/android/res/layout/vpn_state_fragment.xml
+++ b/src/frontends/android/res/layout/vpn_state_fragment.xml
@@ -1,6 +1,6 @@
CA-Zertifikate
@@ -86,6 +89,15 @@
Kein aktives Profil
Fehler
+
+ Assessment:
+ Eingeschränkt
+ Fehlgeschlagen
+ Korrekturanweisungen anzeigen
+
+
+ Korrekturanweisungen
+
Passwort eingeben um zu verbinden
Verbinden
@@ -94,6 +106,7 @@
Gateway ist nicht erreichbar.
Authentifizierung des Gateway ist fehlgeschlagen.
Benutzerauthentifizierung ist fehlgeschlagen.
+ Sicherheitsassessment ist fehlgeschlagen.
Unbekannter Fehler während des Verbindens.
Verbinden: %1$s
Verbinde mit \""%1$s\".
diff --git a/src/frontends/android/res/values-pl/arrays.xml b/src/frontends/android/res/values-pl/arrays.xml
index 1b74b2e57..1a04cbf2e 100644
--- a/src/frontends/android/res/values-pl/arrays.xml
+++ b/src/frontends/android/res/values-pl/arrays.xml
@@ -19,5 +19,6 @@
- IKEv2 EAP (użytkownik/hasło)
- IKEv2 certyfikat
- IKEv2 certyfikat + EAP (użytkownik/hasło)
+ - IKEv2 EAP-TNC (użytkownik/hasło)
\ No newline at end of file
diff --git a/src/frontends/android/res/values-pl/strings.xml b/src/frontends/android/res/values-pl/strings.xml
index 3ba5e1669..7aa9c51a7 100644
--- a/src/frontends/android/res/values-pl/strings.xml
+++ b/src/frontends/android/res/values-pl/strings.xml
@@ -1,7 +1,5 @@
Certyfikaty CA
@@ -88,6 +89,15 @@
Brak aktywnego VPN
Błąd
+
+ Assessment:
+ Restricted
+ Failed
+ View remediation instructions
+
+
+ Remediation instructions
+
Wprowadż hasło
Połącz
@@ -96,6 +106,7 @@
Bramka jest nieosiągalna
Błąd przy weryfikacji bramki
Błąd przy autoryzacji użytkownika
+ Security assessment failed.
Nieznany błąd w czasie połączenia
Łączenie: %1$s
Tworzenie tunelu VPN z \""%1$s\".
diff --git a/src/frontends/android/res/values-ru/arrays.xml b/src/frontends/android/res/values-ru/arrays.xml
index 55144f294..713f8e404 100644
--- a/src/frontends/android/res/values-ru/arrays.xml
+++ b/src/frontends/android/res/values-ru/arrays.xml
@@ -18,5 +18,6 @@
- IKEv2 EAP (Логин/Пароль)
- IKEv2 Сертификат
- IKEv2 Сертификат + EAP (Логин/Пароль)
+ - IKEv2 EAP-TNC (Логин/Пароль)
diff --git a/src/frontends/android/res/values-ru/strings.xml b/src/frontends/android/res/values-ru/strings.xml
index afa21361b..3838485af 100644
--- a/src/frontends/android/res/values-ru/strings.xml
+++ b/src/frontends/android/res/values-ru/strings.xml
@@ -66,6 +66,9 @@
Пожалуйста введите имя пользователя
Не выбран сертификат CA
Пожалуйста выберите один Выбрать автоматически
+ EAP-TNC may affect your privacy
+ Device data is sent to the gateway operator
+ <p>Trusted Network Connect (TNC) allows gateway operators to assess the health of a client device.</p><p>For that purpose the gateway operator may request data such as a unique identifier, a list of installed packages, system settings, or cryptographic checksums of files.</p><b>Any data will be sent only after verifying the gateway\'s identity.</b>
Сертификаты CA
@@ -83,6 +86,15 @@
Нет активных VPN
Ошибка
+
+ Assessment:
+ Restricted
+ Failed
+ View remediation instructions
+
+
+ Remediation instructions
+
Введите пароль для соединения
Соединить
@@ -91,6 +103,7 @@
Шлюз недоступен.
Ошибка авторизаци при подключении к шлюзу.
Ошибка авторизации пользователя.
+ Security assessment failed.
Неизвестная ошибка.
Подключение: %1$s
Подключение к VPN с \""%1$s\".
diff --git a/src/frontends/android/res/values-ua/arrays.xml b/src/frontends/android/res/values-ua/arrays.xml
index 490fea5e1..4bd92fe0a 100644
--- a/src/frontends/android/res/values-ua/arrays.xml
+++ b/src/frontends/android/res/values-ua/arrays.xml
@@ -18,5 +18,6 @@
- IKEv2 EAP (Логін/Пароль)
- IKEv2 Сертифікати
- IKEv2 Сертифікати + EAP (Логін/Пароль)
+ - IKEv2 EAP-TNC (Логін/Пароль)
diff --git a/src/frontends/android/res/values-ua/strings.xml b/src/frontends/android/res/values-ua/strings.xml
index 953ba0a54..df016ff8f 100644
--- a/src/frontends/android/res/values-ua/strings.xml
+++ b/src/frontends/android/res/values-ua/strings.xml
@@ -1,7 +1,7 @@
Сертифікати CA
@@ -84,6 +87,15 @@
Немає активних VPN
Помилка
+
+ Assessment:
+ Restricted
+ Failed
+ View remediation instructions
+
+
+ Remediation instructions
+
Введіть пароль для з\'єднання
Підключити
@@ -92,6 +104,7 @@
Немає зв\'язку зі шлюзом.
Помилка перевірки данних аутентифікації шлюза.
Помилка аутентифікації користувача.
+ Security assessment failed.
Невідома помилка під час підключення.
Підключення: %1$s
Підключення VPN з \""%1$s\".
diff --git a/src/frontends/android/res/values/arrays.xml b/src/frontends/android/res/values/arrays.xml
index 1ac4cc21d..29f999d9a 100644
--- a/src/frontends/android/res/values/arrays.xml
+++ b/src/frontends/android/res/values/arrays.xml
@@ -19,5 +19,6 @@
- IKEv2 EAP (Username/Password)
- IKEv2 Certificate
- IKEv2 Certificate + EAP (Username/Password)
+ - IKEv2 EAP-TNC (Username/Password)
\ No newline at end of file
diff --git a/src/frontends/android/res/values/colors.xml b/src/frontends/android/res/values/colors.xml
index be64d5d5a..4af28b4d4 100644
--- a/src/frontends/android/res/values/colors.xml
+++ b/src/frontends/android/res/values/colors.xml
@@ -1,6 +1,6 @@
CA certificates
@@ -86,6 +89,15 @@
No active VPN
Error
+
+ Assessment:
+ Restricted
+ Failed
+ View remediation instructions
+
+
+ Remediation instructions
+
Enter password to connect
Connect
@@ -94,6 +106,7 @@
Gateway is unreachable.
Verifying gateway authentication failed.
User authentication failed.
+ Security assessment failed.
Unspecified failure while connecting.
Connecting: %1$s
Establishing VPN with \""%1$s\".
diff --git a/src/frontends/android/src/org/strongswan/android/data/VpnType.java b/src/frontends/android/src/org/strongswan/android/data/VpnType.java
index 47cc1cb02..f62c96d76 100644
--- a/src/frontends/android/src/org/strongswan/android/data/VpnType.java
+++ b/src/frontends/android/src/org/strongswan/android/data/VpnType.java
@@ -20,11 +20,13 @@ public enum VpnType
/* the order here must match the items in R.array.vpn_types */
IKEV2_EAP("ikev2-eap", true, false),
IKEV2_CERT("ikev2-cert", false, true),
- IKEV2_CERT_EAP("ikev2-cert-eap", true, true);
+ IKEV2_CERT_EAP("ikev2-cert-eap", true, true),
+ IKEV2_BYOD_EAP("ikev2-byod-eap", true, false, true);
private String mIdentifier;
private boolean mCertificate;
private boolean mUsernamePassword;
+ private boolean mBYOD;
/**
* Enum which provides additional information about the supported VPN types.
@@ -34,10 +36,24 @@ public enum VpnType
* @param certificate true if a client certificate is required
*/
VpnType(String id, boolean userpass, boolean certificate)
+ {
+ this(id, userpass, certificate, false);
+ }
+
+ /**
+ * Enum which provides additional information about the supported VPN types.
+ *
+ * @param id identifier used to store and transmit this specific type
+ * @param userpass true if username and password are required
+ * @param certificate true if a client certificate is required
+ * @param byod true to enable BYOD features
+ */
+ VpnType(String id, boolean userpass, boolean certificate, boolean byod)
{
mIdentifier = id;
mUsernamePassword = userpass;
mCertificate = certificate;
+ mBYOD = byod;
}
/**
@@ -69,6 +85,16 @@ public enum VpnType
return mCertificate;
}
+ /**
+ * Whether BYOD features should be enabled.
+ *
+ * @return true if BYOD features are to be enabled
+ */
+ public boolean getEnableBYOD()
+ {
+ return mBYOD;
+ }
+
/**
* Get the enum entry with the given identifier.
*
diff --git a/src/frontends/android/src/org/strongswan/android/logic/CharonVpnService.java b/src/frontends/android/src/org/strongswan/android/logic/CharonVpnService.java
index 02db8c494..a7b8a8cef 100644
--- a/src/frontends/android/src/org/strongswan/android/logic/CharonVpnService.java
+++ b/src/frontends/android/src/org/strongswan/android/logic/CharonVpnService.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 Tobias Brunner
+ * Copyright (C) 2012-2013 Tobias Brunner
* Copyright (C) 2012 Giuliano Grassi
* Copyright (C) 2012 Ralf Sager
* Hochschule fuer Technik Rapperswil
@@ -27,6 +27,8 @@ import org.strongswan.android.data.VpnProfile;
import org.strongswan.android.data.VpnProfileDataSource;
import org.strongswan.android.logic.VpnStateService.ErrorState;
import org.strongswan.android.logic.VpnStateService.State;
+import org.strongswan.android.logic.imc.ImcState;
+import org.strongswan.android.logic.imc.RemediationInstruction;
import org.strongswan.android.ui.MainActivity;
import android.app.PendingIntent;
@@ -208,10 +210,11 @@ public class CharonVpnService extends VpnService implements Runnable
setProfile(mCurrentProfile);
setError(ErrorState.NO_ERROR);
setState(State.CONNECTING);
+ setImcState(ImcState.UNKNOWN);
mIsDisconnecting = false;
BuilderAdapter builder = new BuilderAdapter(mCurrentProfile.getName());
- initializeCharon(builder, mLogFile);
+ initializeCharon(builder, mLogFile, mCurrentProfile.getVpnType().getEnableBYOD());
Log.i(TAG, "charon started");
initiate(mCurrentProfile.getVpnType().getIdentifier(),
@@ -296,6 +299,23 @@ public class CharonVpnService extends VpnService implements Runnable
}
}
+ /**
+ * Set the IMC state on the state service. Called by the handler thread and
+ * any of charon's threads.
+ *
+ * @param state IMC state
+ */
+ private void setImcState(ImcState state)
+ {
+ synchronized (mServiceLock)
+ {
+ if (mService != null)
+ {
+ mService.setImcState(state);
+ }
+ }
+ }
+
/**
* Set an error on the state service and disconnect the current connection.
* This is not done by calling stopCurrentConnection() above, but instead
@@ -355,6 +375,41 @@ public class CharonVpnService extends VpnService implements Runnable
}
}
+ /**
+ * Updates the IMC state of the current connection.
+ * Called via JNI by different threads (but not concurrently).
+ *
+ * @param value new state
+ */
+ public void updateImcState(int value)
+ {
+ ImcState state = ImcState.fromValue(value);
+ if (state != null)
+ {
+ setImcState(state);
+ }
+ }
+
+ /**
+ * Add a remediation instruction to the VPN state service.
+ * Called via JNI by different threads (but not concurrently).
+ *
+ * @param xml XML text
+ */
+ public void addRemediationInstruction(String xml)
+ {
+ for (RemediationInstruction instruction : RemediationInstruction.fromXml(xml))
+ {
+ synchronized (mServiceLock)
+ {
+ if (mService != null)
+ {
+ mService.addRemediationInstruction(instruction);
+ }
+ }
+ }
+ }
+
/**
* Function called via JNI to generate a list of DER encoded CA certificates
* as byte array.
@@ -461,8 +516,9 @@ public class CharonVpnService extends VpnService implements Runnable
*
* @param builder BuilderAdapter for this connection
* @param logfile absolute path to the logfile
+ * @param boyd enable BYOD features
*/
- public native void initializeCharon(BuilderAdapter builder, String logfile);
+ public native void initializeCharon(BuilderAdapter builder, String logfile, boolean byod);
/**
* Deinitialize charon, provided by libandroidbridge.so
@@ -600,6 +656,15 @@ public class CharonVpnService extends VpnService implements Runnable
{
System.loadLibrary("crypto");
System.loadLibrary("strongswan");
+
+ if (MainActivity.USE_BYOD)
+ {
+ System.loadLibrary("tncif");
+ System.loadLibrary("tnccs");
+ System.loadLibrary("imcv");
+ System.loadLibrary("pts");
+ }
+
System.loadLibrary("hydra");
System.loadLibrary("charon");
System.loadLibrary("ipsec");
diff --git a/src/frontends/android/src/org/strongswan/android/logic/VpnStateService.java b/src/frontends/android/src/org/strongswan/android/logic/VpnStateService.java
index 1c14cb601..2c530bad4 100644
--- a/src/frontends/android/src/org/strongswan/android/logic/VpnStateService.java
+++ b/src/frontends/android/src/org/strongswan/android/logic/VpnStateService.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 Tobias Brunner
+ * Copyright (C) 2012-2013 Tobias Brunner
* Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
@@ -16,10 +16,14 @@
package org.strongswan.android.logic;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.Callable;
import org.strongswan.android.data.VpnProfile;
+import org.strongswan.android.logic.imc.ImcState;
+import org.strongswan.android.logic.imc.RemediationInstruction;
import android.app.Service;
import android.content.Context;
@@ -36,6 +40,8 @@ public class VpnStateService extends Service
private VpnProfile mProfile;
private State mState = State.DISABLED;
private ErrorState mError = ErrorState.NO_ERROR;
+ private ImcState mImcState = ImcState.UNKNOWN;
+ private final LinkedList mRemediationInstructions = new LinkedList();
public enum State
{
@@ -146,6 +152,26 @@ public class VpnStateService extends Service
return mError;
}
+ /**
+ * Get the current IMC state, if any.
+ *
+ * @return imc state
+ */
+ public ImcState getImcState()
+ { /* only updated from the main thread so no synchronization needed */
+ return mImcState;
+ }
+
+ /**
+ * Get the remediation instructions, if any.
+ *
+ * @return read-only list of instructions
+ */
+ public List getRemediationInstructions()
+ { /* only updated from the main thread so no synchronization needed */
+ return Collections.unmodifiableList(mRemediationInstructions);
+ }
+
/**
* Disconnect any existing connection and shutdown the daemon, the
* VpnService is not stopped but it is reset so new connections can be
@@ -261,4 +287,54 @@ public class VpnStateService extends Service
}
});
}
+
+ /**
+ * Set the current IMC state and notify all listeners, if changed.
+ *
+ * Setting the state to UNKNOWN clears all remediation instructions.
+ *
+ * May be called from threads other than the main thread.
+ *
+ * @param error error state
+ */
+ public void setImcState(final ImcState state)
+ {
+ notifyListeners(new Callable() {
+ @Override
+ public Boolean call() throws Exception
+ {
+ if (state == ImcState.UNKNOWN)
+ {
+ VpnStateService.this.mRemediationInstructions.clear();
+ }
+ if (VpnStateService.this.mImcState != state)
+ {
+ VpnStateService.this.mImcState = state;
+ return true;
+ }
+ return false;
+ }
+ });
+ }
+
+ /**
+ * Add the given remediation instruction to the internal list. Listeners
+ * are not notified.
+ *
+ * Instructions are cleared if the IMC state is set to UNKNOWN.
+ *
+ * May be called from threads other than the main thread.
+ *
+ * @param instruction remediation instruction
+ */
+ public void addRemediationInstruction(final RemediationInstruction instruction)
+ {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run()
+ {
+ VpnStateService.this.mRemediationInstructions.add(instruction);
+ }
+ });
+ }
}
diff --git a/src/frontends/android/src/org/strongswan/android/logic/imc/AndroidImc.java b/src/frontends/android/src/org/strongswan/android/logic/imc/AndroidImc.java
new file mode 100644
index 000000000..351fab801
--- /dev/null
+++ b/src/frontends/android/src/org/strongswan/android/logic/imc/AndroidImc.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2013 Tobias Brunner
+ * 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 .
+ *
+ * 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.
+ */
+
+package org.strongswan.android.logic.imc;
+
+import org.strongswan.android.logic.imc.attributes.Attribute;
+import org.strongswan.android.logic.imc.attributes.AttributeType;
+import org.strongswan.android.logic.imc.collectors.Collector;
+import org.strongswan.android.logic.imc.collectors.DeviceIdCollector;
+import org.strongswan.android.logic.imc.collectors.InstalledPackagesCollector;
+import org.strongswan.android.logic.imc.collectors.PortFilterCollector;
+import org.strongswan.android.logic.imc.collectors.ProductInformationCollector;
+import org.strongswan.android.logic.imc.collectors.SettingsCollector;
+import org.strongswan.android.logic.imc.collectors.StringVersionCollector;
+
+import android.content.Context;
+
+public class AndroidImc
+{
+ private final Context mContext;
+
+ public AndroidImc(Context context)
+ {
+ mContext = context;
+ }
+
+ /**
+ * Get a measurement (the binary encoding of the requested attribute) for
+ * the given vendor specific attribute type.
+ *
+ * @param vendor vendor ID
+ * @param type vendor specific attribute type
+ * @return encoded attribute, or null if not available or failed
+ */
+ public byte[] getMeasurement(int vendor, int type)
+ {
+ return getMeasurement(vendor, type, null);
+ }
+
+ /**
+ * Get a measurement (the binary encoding of the requested attribute) for
+ * the given vendor specific attribute type.
+ *
+ * @param vendor vendor ID
+ * @param type vendor specific attribute type
+ * @param args optional arguments for a measurement
+ * @return encoded attribute, or null if not available or failed
+ */
+ public byte[] getMeasurement(int vendor, int type, String[] args)
+ {
+ AttributeType attributeType = AttributeType.fromValues(vendor, type);
+ Collector collector = null;
+
+ switch (attributeType)
+ {
+ case IETF_PRODUCT_INFORMATION:
+ collector = new ProductInformationCollector();
+ break;
+ case IETF_STRING_VERSION:
+ collector = new StringVersionCollector();
+ break;
+ case IETF_PORT_FILTER:
+ collector = new PortFilterCollector();
+ break;
+ case IETF_INSTALLED_PACKAGES:
+ collector = new InstalledPackagesCollector(mContext);
+ break;
+ case ITA_SETTINGS:
+ collector = new SettingsCollector(mContext, args);
+ break;
+ case ITA_DEVICE_ID:
+ collector = new DeviceIdCollector(mContext);
+ break;
+ default:
+ break;
+ }
+ if (collector != null)
+ {
+ Attribute attribute = collector.getMeasurement();
+ if (attribute != null)
+ {
+ return attribute.getEncoding();
+ }
+ }
+ return null;
+ }
+}
diff --git a/src/frontends/android/src/org/strongswan/android/logic/imc/ImcState.java b/src/frontends/android/src/org/strongswan/android/logic/imc/ImcState.java
new file mode 100644
index 000000000..4fc3834f9
--- /dev/null
+++ b/src/frontends/android/src/org/strongswan/android/logic/imc/ImcState.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2013 Tobias Brunner
+ * 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 .
+ *
+ * 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.
+ */
+
+package org.strongswan.android.logic.imc;
+
+public enum ImcState
+{
+ UNKNOWN(0),
+ ALLOW(1),
+ BLOCK(2),
+ ISOLATE(3);
+
+ private final int mValue;
+
+ private ImcState(int value)
+ {
+ mValue = value;
+ }
+
+ /**
+ * Get the numeric value of the IMC state.
+ * @return numeric value
+ */
+ public int getValue()
+ {
+ return mValue;
+ }
+
+ /**
+ * Get the enum entry from a numeric value, if defined
+ *
+ * @param value numeric value
+ * @return the enum entry or null
+ */
+ public static ImcState fromValue(int value)
+ {
+ for (ImcState state : ImcState.values())
+ {
+ if (state.mValue == value)
+ {
+ return state;
+ }
+ }
+ return null;
+ }
+}
diff --git a/src/frontends/android/src/org/strongswan/android/logic/imc/RemediationInstruction.java b/src/frontends/android/src/org/strongswan/android/logic/imc/RemediationInstruction.java
new file mode 100644
index 000000000..5435ad88c
--- /dev/null
+++ b/src/frontends/android/src/org/strongswan/android/logic/imc/RemediationInstruction.java
@@ -0,0 +1,273 @@
+/*
+ * Copyright (C) 2013 Tobias Brunner
+ * 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 .
+ *
+ * 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.
+ */
+
+package org.strongswan.android.logic.imc;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Xml;
+
+public class RemediationInstruction implements Parcelable
+{
+ private String mTitle;
+ private String mDescription;
+ private String mHeader;
+ private final List mItems = new LinkedList();
+
+ @Override
+ public int describeContents()
+ {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags)
+ {
+ dest.writeString(mTitle);
+ dest.writeString(mDescription);
+ dest.writeString(mHeader);
+ dest.writeStringList(mItems);
+ }
+
+ public static final Parcelable.Creator CREATOR = new Creator() {
+
+ @Override
+ public RemediationInstruction[] newArray(int size)
+ {
+ return new RemediationInstruction[size];
+ }
+
+ @Override
+ public RemediationInstruction createFromParcel(Parcel source)
+ {
+ return new RemediationInstruction(source);
+ }
+ };
+
+ private RemediationInstruction()
+ {
+ }
+
+ private RemediationInstruction(Parcel source)
+ {
+ mTitle = source.readString();
+ mDescription = source.readString();
+ mHeader = source.readString();
+ source.readStringList(mItems);
+ }
+
+ public String getTitle()
+ {
+ return mTitle;
+ }
+
+ private void setTitle(String title)
+ {
+ mTitle = title;
+ }
+
+ public String getDescription()
+ {
+ return mDescription;
+ }
+
+ private void setDescription(String description)
+ {
+ mDescription = description;
+ }
+
+ public String getHeader()
+ {
+ return mHeader;
+ }
+
+ private void setHeader(String header)
+ {
+ mHeader = header;
+ }
+
+ public List getItems()
+ {
+ return Collections.unmodifiableList(mItems);
+ }
+
+ private void addItem(String item)
+ {
+ mItems.add(item);
+ }
+
+ /**
+ * Create a list of RemediationInstruction objects from the given XML data.
+ *
+ * @param xml XML data
+ * @return list of RemediationInstruction objects
+ */
+ public static List fromXml(String xml)
+ {
+ List instructions = new LinkedList();
+ XmlPullParser parser = Xml.newPullParser();
+ try
+ {
+ parser.setInput(new StringReader(xml));
+ parser.nextTag();
+ readInstructions(parser, instructions);
+ }
+ catch (XmlPullParserException e)
+ {
+ e.printStackTrace();
+ }
+ catch (IOException e)
+ {
+ e.printStackTrace();
+ }
+ return instructions;
+ }
+
+ /**
+ * Read a <remediationinstructions> element and store the extracted
+ * RemediationInstruction objects in the given list.
+ *
+ * @param parser
+ * @param instructions
+ * @throws XmlPullParserException
+ * @throws IOException
+ */
+ private static void readInstructions(XmlPullParser parser, List instructions) throws XmlPullParserException, IOException
+ {
+ parser.require(XmlPullParser.START_TAG, null, "remediationinstructions");
+ while (parser.next() != XmlPullParser.END_TAG)
+ {
+ if (parser.getEventType() != XmlPullParser.START_TAG)
+ {
+ continue;
+ }
+ if (parser.getName().equals("instruction"))
+ {
+ RemediationInstruction instruction = new RemediationInstruction();
+ readInstruction(parser, instruction);
+ instructions.add(instruction);
+ }
+ else
+ {
+ skipTag(parser);
+ }
+ }
+ }
+
+ /**
+ * Read an <instruction> element and store the information in the
+ * given RemediationInstruction object.
+ *
+ * @param parser
+ * @param instruction
+ * @throws XmlPullParserException
+ * @throws IOException
+ */
+ private static void readInstruction(XmlPullParser parser, RemediationInstruction instruction) throws XmlPullParserException, IOException
+ {
+ parser.require(XmlPullParser.START_TAG, null, "instruction");
+ while (parser.next() != XmlPullParser.END_TAG)
+ {
+ if (parser.getEventType() != XmlPullParser.START_TAG)
+ {
+ continue;
+ }
+ String name = parser.getName();
+ if (name.equals("title"))
+ {
+ instruction.setTitle(parser.nextText());
+ }
+ else if (name.equals("description"))
+ {
+ instruction.setDescription(parser.nextText());
+ }
+ else if (name.equals("itemsheader"))
+ {
+ instruction.setHeader(parser.nextText());
+ }
+ else if (name.equals("items"))
+ {
+ readItems(parser, instruction);
+ }
+ else
+ {
+ skipTag(parser);
+ }
+ }
+ }
+
+ /**
+ * Read all items of an <items> node and add them to the given
+ * RemediationInstruction object.
+ *
+ * @param parser
+ * @param instruction
+ * @throws XmlPullParserException
+ * @throws IOException
+ */
+ private static void readItems(XmlPullParser parser, RemediationInstruction instruction) throws XmlPullParserException, IOException
+ {
+ while (parser.next() != XmlPullParser.END_TAG)
+ {
+ if (parser.getEventType() != XmlPullParser.START_TAG)
+ {
+ continue;
+ }
+ if (parser.getName().equals("item"))
+ {
+ instruction.addItem(parser.nextText());
+ }
+ else
+ {
+ skipTag(parser);
+ }
+ }
+ }
+
+ /**
+ * Skip the current tag and all child elements.
+ *
+ * @param parser
+ * @throws XmlPullParserException
+ * @throws IOException
+ */
+ private static void skipTag(XmlPullParser parser) throws XmlPullParserException, IOException
+ {
+ int depth = 1;
+
+ parser.require(XmlPullParser.START_TAG, null, null);
+ while (depth != 0)
+ {
+ switch (parser.next())
+ {
+ case XmlPullParser.END_TAG:
+ depth--;
+ break;
+ case XmlPullParser.START_TAG:
+ depth++;
+ break;
+ }
+ }
+ }
+}
diff --git a/src/frontends/android/src/org/strongswan/android/logic/imc/attributes/Attribute.java b/src/frontends/android/src/org/strongswan/android/logic/imc/attributes/Attribute.java
new file mode 100644
index 000000000..ca759000f
--- /dev/null
+++ b/src/frontends/android/src/org/strongswan/android/logic/imc/attributes/Attribute.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2013 Tobias Brunner
+ * 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 .
+ *
+ * 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.
+ */
+
+package org.strongswan.android.logic.imc.attributes;
+
+/**
+ * Interface to be implemented by attribute classes
+ */
+public interface Attribute
+{
+ /**
+ * Returns the binary encoding of the attribute
+ * @return binary encoding
+ */
+ public byte[] getEncoding();
+}
diff --git a/src/frontends/android/src/org/strongswan/android/logic/imc/attributes/AttributeType.java b/src/frontends/android/src/org/strongswan/android/logic/imc/attributes/AttributeType.java
new file mode 100644
index 000000000..11f1c61da
--- /dev/null
+++ b/src/frontends/android/src/org/strongswan/android/logic/imc/attributes/AttributeType.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2013 Tobias Brunner
+ * Copyright (C) 2012 Christoph Buehler
+ * Copyright (C) 2012 Patrick Loetscher
+ * 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 .
+ *
+ * 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.
+ */
+
+package org.strongswan.android.logic.imc.attributes;
+
+public enum AttributeType
+{
+ /* IETF standard PA-TNC attribute types defined by RFC 5792 */
+ IETF_TESTING(PrivateEnterpriseNumber.IETF, 0),
+ IETF_ATTRIBUTE_REQUEST(PrivateEnterpriseNumber.IETF, 1),
+ IETF_PRODUCT_INFORMATION(PrivateEnterpriseNumber.IETF, 2),
+ IETF_NUMERIC_VERSION(PrivateEnterpriseNumber.IETF, 3),
+ IETF_STRING_VERSION(PrivateEnterpriseNumber.IETF, 4),
+ IETF_OPERATIONAL_STATUS(PrivateEnterpriseNumber.IETF, 5),
+ IETF_PORT_FILTER(PrivateEnterpriseNumber.IETF, 6),
+ IETF_INSTALLED_PACKAGES(PrivateEnterpriseNumber.IETF, 7),
+ IETF_PA_TNC_ERROR(PrivateEnterpriseNumber.IETF, 8),
+ IETF_ASSESSMENT_RESULT(PrivateEnterpriseNumber.IETF, 9),
+ IETF_REMEDIATION_INSTRUCTIONS(PrivateEnterpriseNumber.IETF, 10),
+ IETF_FORWARDING_ENABLED(PrivateEnterpriseNumber.IETF, 11),
+ IETF_FACTORY_DEFAULT_PWD_ENABLED(PrivateEnterpriseNumber.IETF, 12),
+ IETF_RESERVED(PrivateEnterpriseNumber.IETF, 0xffffffff),
+ /* ITA attributes */
+ ITA_SETTINGS(PrivateEnterpriseNumber.ITA, 4),
+ ITA_DEVICE_ID(PrivateEnterpriseNumber.ITA, 8);
+
+ private PrivateEnterpriseNumber mVendor;
+ private int mType;
+
+ /**
+ * Enum type for vendor specific attributes (defined in their namespace)
+ *
+ * @param vendor private enterprise number of vendor
+ * @param type vendor specific attribute type
+ */
+ private AttributeType(PrivateEnterpriseNumber vendor, int type)
+ {
+ mVendor = vendor;
+ mType = type;
+ }
+
+ /**
+ * Get private enterprise number of vendor
+ *
+ * @return PEN
+ */
+ public PrivateEnterpriseNumber getVendor()
+ {
+ return mVendor;
+ }
+
+ /**
+ * Get vendor specific type
+ *
+ * @return type
+ */
+ public int getType()
+ {
+ return mType;
+ }
+
+ /**
+ * Get the enum entry from the given numeric values, if defined
+ *
+ * @param vendor vendor id
+ * @param type vendor specific type
+ * @return enum entry or null
+ */
+ public static AttributeType fromValues(int vendor, int type)
+ {
+ PrivateEnterpriseNumber pen = PrivateEnterpriseNumber.fromValue(vendor);
+
+ if (pen == null)
+ {
+ return null;
+ }
+ for (AttributeType attr : AttributeType.values())
+ {
+ if (attr.mVendor == pen && attr.mType == type)
+ {
+ return attr;
+ }
+ }
+ return null;
+ }
+}
diff --git a/src/frontends/android/src/org/strongswan/android/logic/imc/attributes/DeviceIdAttribute.java b/src/frontends/android/src/org/strongswan/android/logic/imc/attributes/DeviceIdAttribute.java
new file mode 100644
index 000000000..ecab7db24
--- /dev/null
+++ b/src/frontends/android/src/org/strongswan/android/logic/imc/attributes/DeviceIdAttribute.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2013 Tobias Brunner
+ * 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 .
+ *
+ * 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.
+ */
+
+package org.strongswan.android.logic.imc.attributes;
+
+/**
+ * ITA Device ID attribute
+ *
+ * 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Device ID (Variable Length) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+public class DeviceIdAttribute implements Attribute
+{
+ private String mDeviceId;
+
+ /**
+ * Set the device ID
+ * @param version version number
+ */
+ public void setDeviceId(String deviceId)
+ {
+ this.mDeviceId = deviceId;
+ }
+
+ @Override
+ public byte[] getEncoding()
+ {
+ return mDeviceId.getBytes();
+ }
+}
diff --git a/src/frontends/android/src/org/strongswan/android/logic/imc/attributes/InstalledPackagesAttribute.java b/src/frontends/android/src/org/strongswan/android/logic/imc/attributes/InstalledPackagesAttribute.java
new file mode 100644
index 000000000..dd1ad7292
--- /dev/null
+++ b/src/frontends/android/src/org/strongswan/android/logic/imc/attributes/InstalledPackagesAttribute.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2013 Tobias Brunner
+ * Copyright (C) 2012 Christoph Buehler
+ * Copyright (C) 2012 Patrick Loetscher
+ * 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 .
+ *
+ * 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.
+ */
+
+package org.strongswan.android.logic.imc.attributes;
+
+import java.util.LinkedList;
+
+import org.strongswan.android.utils.BufferedByteWriter;
+
+import android.util.Pair;
+
+/**
+ * PA-TNC Installed Packages attribute (see section 4.2.7 of RFC 5792)
+ *
+ * 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Reserved | Package Count |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Pkg Name Len | Package Name (Variable Length) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Version Len | Package Version Number (Variable Length) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+public class InstalledPackagesAttribute implements Attribute
+{
+ private final short RESERVED = 0;
+ private final LinkedList> mPackages = new LinkedList>();
+
+ /**
+ * Add an installed package to this attribute.
+ * @param name name of the package
+ * @param version version number of the package
+ */
+ public void addPackage(String name, String version)
+ {
+ mPackages.add(new Pair(name, version));
+ }
+
+ @Override
+ public byte[] getEncoding()
+ {
+ BufferedByteWriter writer = new BufferedByteWriter();
+ writer.put16(RESERVED);
+ writer.put16((short)mPackages.size());
+ for (Pair pair : mPackages)
+ {
+ writer.putLen8(pair.first.getBytes());
+ writer.putLen8(pair.second.getBytes());
+ }
+ return writer.toByteArray();
+ }
+}
diff --git a/src/frontends/android/src/org/strongswan/android/logic/imc/attributes/PortFilterAttribute.java b/src/frontends/android/src/org/strongswan/android/logic/imc/attributes/PortFilterAttribute.java
new file mode 100644
index 000000000..191690b94
--- /dev/null
+++ b/src/frontends/android/src/org/strongswan/android/logic/imc/attributes/PortFilterAttribute.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2013 Tobias Brunner
+ * Copyright (C) 2012 Christoph Buehler
+ * Copyright (C) 2012 Patrick Loetscher
+ * 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 .
+ *
+ * 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.
+ */
+
+package org.strongswan.android.logic.imc.attributes;
+
+import java.util.LinkedList;
+
+import org.strongswan.android.logic.imc.collectors.Protocol;
+import org.strongswan.android.utils.BufferedByteWriter;
+
+import android.util.Pair;
+
+/**
+ * PA-TNC Port Filter attribute (see section 4.2.6 of RFC 5792)
+ *
+ * 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Reserved |B| Protocol | Port Number |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Reserved |B| Protocol | Port Number |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+public class PortFilterAttribute implements Attribute
+{
+ private final LinkedList> mPorts = new LinkedList>();
+
+ /**
+ * Add an open port with the given protocol and port number
+ * @param protocol transport protocol
+ * @param port port number
+ */
+ public void addPort(Protocol protocol, short port)
+ {
+ mPorts.add(new Pair(protocol, port));
+ }
+
+ @Override
+ public byte[] getEncoding()
+ {
+ BufferedByteWriter writer = new BufferedByteWriter();
+ for (Pair port : mPorts)
+ {
+ /* we report open ports, so the BLOCKED flag is not set */
+ writer.put((byte)0);
+ writer.put(port.first.getValue());
+ writer.put16(port.second);
+ }
+ return writer.toByteArray();
+ }
+}
diff --git a/src/frontends/android/src/org/strongswan/android/logic/imc/attributes/PrivateEnterpriseNumber.java b/src/frontends/android/src/org/strongswan/android/logic/imc/attributes/PrivateEnterpriseNumber.java
new file mode 100644
index 000000000..9db702ec0
--- /dev/null
+++ b/src/frontends/android/src/org/strongswan/android/logic/imc/attributes/PrivateEnterpriseNumber.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2013 Tobias Brunner
+ * 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 .
+ *
+ * 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.
+ */
+
+package org.strongswan.android.logic.imc.attributes;
+
+public enum PrivateEnterpriseNumber
+{
+ IETF(0x000000),
+ GOOGLE(0x002B79),
+ ITA(0x00902a),
+ UNASSIGNED(0xfffffe),
+ RESERVED(0xffffff);
+
+ private int mValue;
+
+ /**
+ * Enum for private enterprise numbers (PEN) as allocated by IANA
+ *
+ * @param value numeric value
+ */
+ private PrivateEnterpriseNumber(int value)
+ {
+ mValue = value;
+ }
+
+ /**
+ * Get the numeric value of a PEN
+ *
+ * @return numeric value
+ */
+ public int getValue()
+ {
+ return mValue;
+ }
+
+ /**
+ * Get the enum entry from a numeric value, if defined
+ *
+ * @param value numeric value
+ * @return the enum entry or null
+ */
+ public static PrivateEnterpriseNumber fromValue(int value)
+ {
+ for (PrivateEnterpriseNumber pen : PrivateEnterpriseNumber.values())
+ {
+ if (pen.mValue == value)
+ {
+ return pen;
+ }
+ }
+ return null;
+ }
+}
diff --git a/src/frontends/android/src/org/strongswan/android/logic/imc/attributes/ProductInformationAttribute.java b/src/frontends/android/src/org/strongswan/android/logic/imc/attributes/ProductInformationAttribute.java
new file mode 100644
index 000000000..cace18d55
--- /dev/null
+++ b/src/frontends/android/src/org/strongswan/android/logic/imc/attributes/ProductInformationAttribute.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2013 Tobias Brunner
+ * Copyright (C) 2012 Christoph Buehler
+ * Copyright (C) 2012 Patrick Loetscher
+ * 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 .
+ *
+ * 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.
+ */
+
+package org.strongswan.android.logic.imc.attributes;
+
+import org.strongswan.android.utils.BufferedByteWriter;
+
+/**
+ * PA-TNC Product Information attribute (see section 4.2.2 of RFC 5792)
+ *
+ * 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Product Vendor ID | Product ID |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Product ID | Product Name (Variable Length) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+public class ProductInformationAttribute implements Attribute
+{
+ private final String PRODUCT_NAME = "Android";
+ private final short PRODUCT_ID = 0;
+
+ @Override
+ public byte[] getEncoding()
+ {
+ BufferedByteWriter writer = new BufferedByteWriter();
+ writer.put24(PrivateEnterpriseNumber.GOOGLE.getValue());
+ writer.put16(PRODUCT_ID);
+ writer.put(PRODUCT_NAME.getBytes());
+ return writer.toByteArray();
+ }
+}
diff --git a/src/frontends/android/src/org/strongswan/android/logic/imc/attributes/SettingsAttribute.java b/src/frontends/android/src/org/strongswan/android/logic/imc/attributes/SettingsAttribute.java
new file mode 100644
index 000000000..37d820168
--- /dev/null
+++ b/src/frontends/android/src/org/strongswan/android/logic/imc/attributes/SettingsAttribute.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2013 Tobias Brunner
+ * Copyright (C) 2012 Christoph Buehler
+ * Copyright (C) 2012 Patrick Loetscher
+ * 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 .
+ *
+ * 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.
+ */
+
+package org.strongswan.android.logic.imc.attributes;
+
+import java.util.LinkedList;
+
+import org.strongswan.android.utils.BufferedByteWriter;
+
+import android.util.Pair;
+
+/**
+ * ITA Settings attribute
+ *
+ * 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Settings Count |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Name Length | Name (Variable Length) ~
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * ~ Name (Variable Length) ~
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Value Length | Value (Variable Length) ~
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * ~ Value (Variable Length) ~
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Name Length | Name (Variable Length) ~
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * ~ Name (Variable Length) ~
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Value Length | Value (Variable Length) ~
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * ~ Value (Variable Length) ~
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * ...........................
+ */
+public class SettingsAttribute implements Attribute
+{
+ private final LinkedList> mSettings = new LinkedList>();
+
+ /**
+ * Add a setting to this attribute.
+ * @param name name of the setting
+ * @param value value of the setting
+ */
+ public void addSetting(String name, String value)
+ {
+ mSettings.add(new Pair(name, value));
+ }
+
+ @Override
+ public byte[] getEncoding()
+ {
+ BufferedByteWriter writer = new BufferedByteWriter();
+ writer.put32(mSettings.size());
+ for (Pair pair : mSettings)
+ {
+ writer.putLen16(pair.first.getBytes());
+ writer.putLen16(pair.second.getBytes());
+ }
+ return writer.toByteArray();
+ }
+}
diff --git a/src/frontends/android/src/org/strongswan/android/logic/imc/attributes/StringVersionAttribute.java b/src/frontends/android/src/org/strongswan/android/logic/imc/attributes/StringVersionAttribute.java
new file mode 100644
index 000000000..4b6f2bc37
--- /dev/null
+++ b/src/frontends/android/src/org/strongswan/android/logic/imc/attributes/StringVersionAttribute.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2013 Tobias Brunner
+ * Copyright (C) 2012 Christoph Buehler
+ * Copyright (C) 2012 Patrick Loetscher
+ * 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 .
+ *
+ * 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.
+ */
+
+package org.strongswan.android.logic.imc.attributes;
+
+import org.strongswan.android.utils.BufferedByteWriter;
+
+/**
+ * PA-TNC String Version attribute (see section 4.2.4 of RFC 5792)
+ *
+ * 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Version Len | Product Version Number (Variable Length) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Build Num Len | Internal Build Number (Variable Length) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Config. Len | Configuration Version Number (Variable Length)|
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+public class StringVersionAttribute implements Attribute
+{
+ private String mVersionNumber;
+ private String mBuildNumber;
+
+ /**
+ * Set the product version number
+ * @param version version number
+ */
+ public void setProductVersionNumber(String version)
+ {
+ this.mVersionNumber = version;
+ }
+
+ /**
+ * Set the internal build number
+ * @param build build number
+ */
+ public void setInternalBuildNumber(String build)
+ {
+ this.mBuildNumber = build;
+ }
+
+ @Override
+ public byte[] getEncoding()
+ {
+ BufferedByteWriter writer = new BufferedByteWriter();
+ writer.putLen8(mVersionNumber.getBytes());
+ writer.putLen8(mBuildNumber.getBytes());
+ /* we don't provide a configuration number */
+ writer.put((byte)0);
+ return writer.toByteArray();
+ }
+}
diff --git a/src/frontends/android/src/org/strongswan/android/logic/imc/collectors/Collector.java b/src/frontends/android/src/org/strongswan/android/logic/imc/collectors/Collector.java
new file mode 100644
index 000000000..a686f13a1
--- /dev/null
+++ b/src/frontends/android/src/org/strongswan/android/logic/imc/collectors/Collector.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2013 Tobias Brunner
+ * 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 .
+ *
+ * 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.
+ */
+
+package org.strongswan.android.logic.imc.collectors;
+
+import org.strongswan.android.logic.imc.attributes.Attribute;
+
+/**
+ * Interface for measurement collectors
+ */
+public interface Collector
+{
+ /**
+ * This method shall return the result of a measurement, if available
+ * @return attribute or null
+ */
+ public abstract Attribute getMeasurement();
+}
diff --git a/src/frontends/android/src/org/strongswan/android/logic/imc/collectors/DeviceIdCollector.java b/src/frontends/android/src/org/strongswan/android/logic/imc/collectors/DeviceIdCollector.java
new file mode 100644
index 000000000..ebe9e10b0
--- /dev/null
+++ b/src/frontends/android/src/org/strongswan/android/logic/imc/collectors/DeviceIdCollector.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2013 Tobias Brunner
+ * 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 .
+ *
+ * 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.
+ */
+
+package org.strongswan.android.logic.imc.collectors;
+
+import org.strongswan.android.logic.imc.attributes.Attribute;
+import org.strongswan.android.logic.imc.attributes.DeviceIdAttribute;
+
+import android.content.ContentResolver;
+import android.content.Context;
+
+public class DeviceIdCollector implements Collector
+{
+ private final ContentResolver mContentResolver;
+
+ public DeviceIdCollector(Context context)
+ {
+ mContentResolver = context.getContentResolver();
+ }
+
+ @Override
+ public Attribute getMeasurement()
+ {
+ String id = android.provider.Settings.Secure.getString(mContentResolver, "android_id");
+ if (id != null)
+ {
+ DeviceIdAttribute attribute = new DeviceIdAttribute();
+ attribute.setDeviceId(id);
+ return attribute;
+ }
+ return null;
+ }
+}
diff --git a/src/frontends/android/src/org/strongswan/android/logic/imc/collectors/InstalledPackagesCollector.java b/src/frontends/android/src/org/strongswan/android/logic/imc/collectors/InstalledPackagesCollector.java
new file mode 100644
index 000000000..caa5170ec
--- /dev/null
+++ b/src/frontends/android/src/org/strongswan/android/logic/imc/collectors/InstalledPackagesCollector.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2013 Tobias Brunner
+ * Copyright (C) 2012 Christoph Buehler
+ * Copyright (C) 2012 Patrick Loetscher
+ * 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 .
+ *
+ * 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.
+ */
+
+package org.strongswan.android.logic.imc.collectors;
+
+import java.util.List;
+
+import org.strongswan.android.logic.imc.attributes.Attribute;
+import org.strongswan.android.logic.imc.attributes.InstalledPackagesAttribute;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+
+public class InstalledPackagesCollector implements Collector
+{
+ private final PackageManager mPackageManager;
+
+ public InstalledPackagesCollector(Context context)
+ {
+ mPackageManager = context.getPackageManager();
+ }
+
+ @Override
+ public Attribute getMeasurement()
+ {
+ InstalledPackagesAttribute attribute = new InstalledPackagesAttribute();
+ List packages = mPackageManager.getInstalledPackages(0);
+ for (PackageInfo info : packages)
+ {
+ if ((info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0 ||
+ info.packageName == null || info.versionName == null)
+ { /* ignore packages installed in the system image */
+ continue;
+ }
+ attribute.addPackage(info.packageName, info.versionName);
+ }
+ return attribute;
+ }
+}
diff --git a/src/frontends/android/src/org/strongswan/android/logic/imc/collectors/PortFilterCollector.java b/src/frontends/android/src/org/strongswan/android/logic/imc/collectors/PortFilterCollector.java
new file mode 100644
index 000000000..ed86686d8
--- /dev/null
+++ b/src/frontends/android/src/org/strongswan/android/logic/imc/collectors/PortFilterCollector.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2013 Tobias Brunner
+ * Copyright (C) 2012 Christoph Buehler
+ * Copyright (C) 2012 Patrick Loetscher
+ * 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 .
+ *
+ * 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.
+ */
+
+package org.strongswan.android.logic.imc.collectors;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.strongswan.android.logic.imc.attributes.Attribute;
+import org.strongswan.android.logic.imc.attributes.PortFilterAttribute;
+
+public class PortFilterCollector implements Collector
+{
+ private static Pattern LISTEN = Pattern.compile("\\bLISTEN\\b");
+ private static Pattern PROTOCOL = Pattern.compile("\\b(tcp|udp)6?\\b");
+ private static Pattern PORT = Pattern.compile("[:]{1,3}(\\d{1,5})\\b(?!\\.)");
+
+ @Override
+ public Attribute getMeasurement()
+ {
+ PortFilterAttribute attribute = null;
+ try
+ {
+ Process netstat = Runtime.getRuntime().exec("netstat -n");
+ try
+ {
+ BufferedReader reader = new BufferedReader(new InputStreamReader(netstat.getInputStream()));
+ String line;
+ attribute = new PortFilterAttribute();
+ while ((line = reader.readLine()) != null)
+ {
+ if (!LISTEN.matcher(line).find())
+ {
+ continue;
+ }
+ Matcher protocolMatcher = PROTOCOL.matcher(line);
+ Matcher portMatcher = PORT.matcher(line);
+ if (protocolMatcher.find() && portMatcher.find())
+ {
+ Protocol protocol = Protocol.fromName(protocolMatcher.group());
+ if (protocol == null)
+ {
+ continue;
+ }
+ int port = Integer.parseInt(portMatcher.group(1));
+ attribute.addPort(protocol, (short)port);
+ }
+ }
+ }
+ finally
+ {
+ netstat.destroy();
+ }
+ }
+ catch (IOException e)
+ {
+ e.printStackTrace();
+ }
+ return attribute;
+ }
+
+}
diff --git a/src/frontends/android/src/org/strongswan/android/logic/imc/collectors/ProductInformationCollector.java b/src/frontends/android/src/org/strongswan/android/logic/imc/collectors/ProductInformationCollector.java
new file mode 100644
index 000000000..c377e9041
--- /dev/null
+++ b/src/frontends/android/src/org/strongswan/android/logic/imc/collectors/ProductInformationCollector.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2013 Tobias Brunner
+ * Copyright (C) 2012 Christoph Buehler
+ * Copyright (C) 2012 Patrick Loetscher
+ * 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 .
+ *
+ * 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.
+ */
+
+package org.strongswan.android.logic.imc.collectors;
+
+import org.strongswan.android.logic.imc.attributes.Attribute;
+import org.strongswan.android.logic.imc.attributes.ProductInformationAttribute;
+
+public class ProductInformationCollector implements Collector
+{
+ @Override
+ public Attribute getMeasurement()
+ { /* this is currently hardcoded in the attribute */
+ return new ProductInformationAttribute();
+ }
+}
diff --git a/src/frontends/android/src/org/strongswan/android/logic/imc/collectors/Protocol.java b/src/frontends/android/src/org/strongswan/android/logic/imc/collectors/Protocol.java
new file mode 100644
index 000000000..7320652a1
--- /dev/null
+++ b/src/frontends/android/src/org/strongswan/android/logic/imc/collectors/Protocol.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2013 Tobias Brunner
+ * 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 .
+ *
+ * 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.
+ */
+
+package org.strongswan.android.logic.imc.collectors;
+
+public enum Protocol
+{
+ TCP((byte)6, "tcp", "tcp6"),
+ UDP((byte)17, "udp", "udp6");
+
+ private final byte mValue;
+ private String[] mNames;
+
+ private Protocol(byte value, String... names)
+ {
+ mValue = value;
+ mNames = names;
+ }
+
+ /**
+ * Get the numeric value of the protocol.
+ * @return numeric value
+ */
+ public byte getValue()
+ {
+ return mValue;
+ }
+
+ /**
+ * Get the protocol from the given protocol name, if found.
+ * @param name protocol name (e.g. "udp" or "tcp")
+ * @return enum entry or null
+ */
+ public static Protocol fromName(String name)
+ {
+ for (Protocol protocol : Protocol.values())
+ {
+ for (String keyword : protocol.mNames)
+ {
+ if (keyword.equalsIgnoreCase(name))
+ {
+ return protocol;
+ }
+ }
+ }
+ return null;
+ }
+}
diff --git a/src/frontends/android/src/org/strongswan/android/logic/imc/collectors/SettingsCollector.java b/src/frontends/android/src/org/strongswan/android/logic/imc/collectors/SettingsCollector.java
new file mode 100644
index 000000000..658c2daea
--- /dev/null
+++ b/src/frontends/android/src/org/strongswan/android/logic/imc/collectors/SettingsCollector.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2013 Tobias Brunner
+ * Copyright (C) 2012 Christoph Buehler
+ * Copyright (C) 2012 Patrick Loetscher
+ * 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 .
+ *
+ * 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.
+ */
+
+package org.strongswan.android.logic.imc.collectors;
+
+import java.util.Locale;
+
+import org.strongswan.android.logic.imc.attributes.Attribute;
+import org.strongswan.android.logic.imc.attributes.SettingsAttribute;
+
+import android.content.ContentResolver;
+import android.content.Context;
+
+public class SettingsCollector implements Collector
+{
+ private final ContentResolver mContentResolver;
+ private final String[] mSettings;
+
+ public SettingsCollector(Context context, String[] args)
+ {
+ mContentResolver = context.getContentResolver();
+ mSettings = args;
+ }
+
+ @Override
+ public Attribute getMeasurement()
+ {
+ if (mSettings == null || mSettings.length == 0)
+ {
+ return null;
+ }
+ SettingsAttribute attribute = new SettingsAttribute();
+ for (String name : mSettings)
+ {
+ String value = android.provider.Settings.Secure.getString(mContentResolver, name.toLowerCase(Locale.US));
+ if (value == null)
+ {
+ value = android.provider.Settings.System.getString(mContentResolver, name.toLowerCase(Locale.US));
+ }
+ if (value != null)
+ {
+ attribute.addSetting(name, value);
+ }
+ }
+ return attribute;
+ }
+}
diff --git a/src/frontends/android/src/org/strongswan/android/logic/imc/collectors/StringVersionCollector.java b/src/frontends/android/src/org/strongswan/android/logic/imc/collectors/StringVersionCollector.java
new file mode 100644
index 000000000..6e0df94a5
--- /dev/null
+++ b/src/frontends/android/src/org/strongswan/android/logic/imc/collectors/StringVersionCollector.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2013 Tobias Brunner
+ * Copyright (C) 2012 Christoph Buehler
+ * Copyright (C) 2012 Patrick Loetscher
+ * 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 .
+ *
+ * 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.
+ */
+
+package org.strongswan.android.logic.imc.collectors;
+
+import org.strongswan.android.logic.imc.attributes.Attribute;
+import org.strongswan.android.logic.imc.attributes.StringVersionAttribute;
+
+public class StringVersionCollector implements Collector
+{
+ @Override
+ public Attribute getMeasurement()
+ {
+ StringVersionAttribute attribute = new StringVersionAttribute();
+ attribute.setProductVersionNumber(android.os.Build.VERSION.RELEASE);
+ attribute.setInternalBuildNumber(android.os.Build.DISPLAY);
+ return attribute;
+ }
+}
diff --git a/src/frontends/android/src/org/strongswan/android/ui/ImcStateFragment.java b/src/frontends/android/src/org/strongswan/android/ui/ImcStateFragment.java
new file mode 100644
index 000000000..ff58849fc
--- /dev/null
+++ b/src/frontends/android/src/org/strongswan/android/ui/ImcStateFragment.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2013 Tobias Brunner
+ * 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 .
+ *
+ * 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.
+ */
+
+package org.strongswan.android.ui;
+
+import java.util.ArrayList;
+
+import org.strongswan.android.R;
+import org.strongswan.android.logic.VpnStateService;
+import org.strongswan.android.logic.VpnStateService.VpnStateListener;
+import org.strongswan.android.logic.imc.ImcState;
+import org.strongswan.android.logic.imc.RemediationInstruction;
+
+import android.app.Fragment;
+import android.app.FragmentTransaction;
+import android.app.Service;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.view.GestureDetector;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.View.OnTouchListener;
+import android.view.ViewConfiguration;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+public class ImcStateFragment extends Fragment implements VpnStateListener
+{
+ private TextView mStateView;
+ private TextView mAction;
+ private LinearLayout mButton;
+ private VpnStateService mService;
+ private final ServiceConnection mServiceConnection = new ServiceConnection() {
+ @Override
+ public void onServiceDisconnected(ComponentName name)
+ {
+ mService = null;
+ }
+
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service)
+ {
+ mService = ((VpnStateService.LocalBinder)service).getService();
+ mService.registerListener(ImcStateFragment.this);
+ updateView();
+ }
+ };
+
+ @Override
+ public void onCreate(Bundle savedInstanceState)
+ {
+ super.onCreate(savedInstanceState);
+
+ /* bind to the service only seems to work from the ApplicationContext */
+ Context context = getActivity().getApplicationContext();
+ context.bindService(new Intent(context, VpnStateService.class),
+ mServiceConnection, Service.BIND_AUTO_CREATE);
+ /* hide it initially */
+ getFragmentManager().beginTransaction().hide(this).commit();
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState)
+ {
+ View view = inflater.inflate(R.layout.imc_state_fragment, null);
+
+ mButton = (LinearLayout)view.findViewById(R.id.imc_state_button);
+ mButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v)
+ {
+ Intent intent;
+ if (mService != null && !mService.getRemediationInstructions().isEmpty())
+ {
+ intent = new Intent(getActivity(), RemediationInstructionsActivity.class);
+ intent.putParcelableArrayListExtra(RemediationInstructionsFragment.EXTRA_REMEDIATION_INSTRUCTIONS,
+ new ArrayList(mService.getRemediationInstructions()));
+ }
+ else
+ {
+ intent = new Intent(getActivity(), LogActivity.class);
+ }
+ startActivity(intent);
+ }
+ });
+ final GestureDetector gestures = new GestureDetector(getActivity(), new GestureDetector.SimpleOnGestureListener() {
+ /* a better value would be getScaledTouchExplorationTapSlop() but that is hidden */
+ private final int mMinDistance = ViewConfiguration.get(getActivity()).getScaledTouchSlop() * 4;
+
+ @Override
+ public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY)
+ {
+ if (Math.abs(e1.getX() - e2.getX()) >= mMinDistance)
+ { /* only if the user swiped a minimum horizontal distance */
+ if (mService != null)
+ {
+ mService.setImcState(ImcState.UNKNOWN);
+ }
+ return true;
+ }
+ return false;
+ }
+ });
+ mButton.setOnTouchListener(new OnTouchListener() {
+ @Override
+ public boolean onTouch(View v, MotionEvent event)
+ {
+ return gestures.onTouchEvent(event);
+ }
+ });
+
+ mStateView = (TextView)view.findViewById(R.id.imc_state);
+ mAction = (TextView)view.findViewById(R.id.action);
+
+ return view;
+ }
+
+ @Override
+ public void onDestroy()
+ {
+ super.onDestroy();
+ if (mService != null)
+ {
+ mService.unregisterListener(this);
+ getActivity().getApplicationContext().unbindService(mServiceConnection);
+ }
+ }
+
+ @Override
+ public void stateChanged()
+ {
+ updateView();
+ }
+
+ public void updateView()
+ {
+ FragmentTransaction ft = getFragmentManager().beginTransaction();
+
+ switch (mService.getImcState())
+ {
+ case UNKNOWN:
+ case ALLOW:
+ ft.hide(this);
+ break;
+ case ISOLATE:
+ mStateView.setText(R.string.imc_state_isolate);
+ mStateView.setTextColor(getResources().getColor(R.color.warning_text));
+ ft.show(this);
+ break;
+ case BLOCK:
+ mStateView.setText(R.string.imc_state_block);
+ mStateView.setTextColor(getResources().getColor(R.color.error_text));
+ ft.show(this);
+ break;
+ }
+ ft.commit();
+
+ mAction.setText(mService.getRemediationInstructions().isEmpty() ? R.string.show_log
+ : R.string.show_remediation_instructions);
+ }
+}
diff --git a/src/frontends/android/src/org/strongswan/android/ui/MainActivity.java b/src/frontends/android/src/org/strongswan/android/ui/MainActivity.java
index d7c1cc4ef..3cf395054 100644
--- a/src/frontends/android/src/org/strongswan/android/ui/MainActivity.java
+++ b/src/frontends/android/src/org/strongswan/android/ui/MainActivity.java
@@ -58,6 +58,8 @@ public class MainActivity extends Activity implements OnVpnProfileSelectedListen
public static final String CONTACT_EMAIL = "android@strongswan.org";
public static final String START_PROFILE = "org.strongswan.android.action.START_PROFILE";
public static final String EXTRA_VPN_PROFILE_ID = "org.strongswan.android.VPN_PROFILE_ID";
+ /** Use "bring your own device" (BYOD) features */
+ public static final boolean USE_BYOD = true;
private static final int PREPARE_VPN_SERVICE = 0;
private static final String PROFILE_NAME = "org.strongswan.android.MainActivity.PROFILE_NAME";
private static final String PROFILE_REQUIRES_PASSWORD = "org.strongswan.android.MainActivity.REQUIRES_PASSWORD";
diff --git a/src/frontends/android/src/org/strongswan/android/ui/RemediationInstructionFragment.java b/src/frontends/android/src/org/strongswan/android/ui/RemediationInstructionFragment.java
new file mode 100644
index 000000000..04c288bcf
--- /dev/null
+++ b/src/frontends/android/src/org/strongswan/android/ui/RemediationInstructionFragment.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2013 Tobias Brunner
+ * 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 .
+ *
+ * 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.
+ */
+
+package org.strongswan.android.ui;
+
+import org.strongswan.android.R;
+import org.strongswan.android.logic.imc.RemediationInstruction;
+
+import android.app.ListFragment;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.TextView;
+
+public class RemediationInstructionFragment extends ListFragment
+{
+ public static final String ARG_REMEDIATION_INSTRUCTION = "instruction";
+ private RemediationInstruction mInstruction = null;
+ private TextView mTitle;
+ private TextView mDescription;
+ private TextView mHeader;
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
+ {
+ return inflater.inflate(R.layout.remediation_instruction, container, false);
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState)
+ {
+ super.onActivityCreated(savedInstanceState);
+
+ if (savedInstanceState != null)
+ {
+ mInstruction = savedInstanceState.getParcelable(ARG_REMEDIATION_INSTRUCTION);
+ }
+ /* show dividers only between list items */
+ getListView().setHeaderDividersEnabled(false);
+ getListView().setFooterDividersEnabled(false);
+ /* don't show loader while adapter is not set */
+ setListShown(true);
+ mTitle = (TextView)getView().findViewById(R.id.title);
+ mDescription = (TextView)getView().findViewById(R.id.description);
+ mHeader = (TextView)getView().findViewById(R.id.list_header);
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState)
+ {
+ super.onSaveInstanceState(outState);
+ outState.putParcelable(ARG_REMEDIATION_INSTRUCTION, mInstruction);
+ }
+
+ @Override
+ public void onStart()
+ {
+ super.onStart();
+
+ Bundle args = getArguments();
+ if (args != null)
+ {
+ mInstruction = args.getParcelable(ARG_REMEDIATION_INSTRUCTION);
+ }
+ updateView(mInstruction);
+ }
+
+ public void updateView(RemediationInstruction instruction)
+ {
+ mInstruction = instruction;
+ if (mInstruction != null)
+ {
+ mTitle.setText(mInstruction.getTitle());
+ mDescription.setText(mInstruction.getDescription());
+ if (mInstruction.getHeader() != null)
+ {
+ mHeader.setText(mInstruction.getHeader());
+ setListAdapter(new ArrayAdapter(getActivity(),
+ android.R.layout.simple_list_item_1, mInstruction.getItems()));
+ }
+ else
+ {
+ mHeader.setText("");
+ setListAdapter(null);
+ }
+ }
+ else
+ {
+ mTitle.setText("");
+ mDescription.setText("");
+ mHeader.setText("");
+ setListAdapter(null);
+ }
+ }
+}
diff --git a/src/frontends/android/src/org/strongswan/android/ui/RemediationInstructionsActivity.java b/src/frontends/android/src/org/strongswan/android/ui/RemediationInstructionsActivity.java
new file mode 100644
index 000000000..66d0de261
--- /dev/null
+++ b/src/frontends/android/src/org/strongswan/android/ui/RemediationInstructionsActivity.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2013 Tobias Brunner
+ * 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 .
+ *
+ * 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.
+ */
+
+package org.strongswan.android.ui;
+
+import java.util.ArrayList;
+
+import org.strongswan.android.R;
+import org.strongswan.android.logic.imc.RemediationInstruction;
+import org.strongswan.android.ui.RemediationInstructionsFragment.OnRemediationInstructionSelectedListener;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.MenuItem;
+
+public class RemediationInstructionsActivity extends Activity implements OnRemediationInstructionSelectedListener
+{
+ @Override
+ protected void onCreate(Bundle savedInstanceState)
+ {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.remediation_instructions);
+ getActionBar().setDisplayHomeAsUpEnabled(true);
+
+ if (savedInstanceState != null)
+ { /* only update if we're not restoring */
+ return;
+ }
+ RemediationInstructionsFragment frag = (RemediationInstructionsFragment)getFragmentManager().findFragmentById(R.id.remediation_instructions_fragment);
+ if (frag != null)
+ { /* two-pane layout, update fragment */
+ Bundle extras = getIntent().getExtras();
+ ArrayList list = extras.getParcelableArrayList(RemediationInstructionsFragment.EXTRA_REMEDIATION_INSTRUCTIONS);
+ frag.updateView(list);
+ }
+ else
+ { /* one-pane layout, create fragment */
+ frag = new RemediationInstructionsFragment();
+ frag.setArguments(getIntent().getExtras());
+ getFragmentManager().beginTransaction().add(R.id.fragment_container, frag).commit();
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item)
+ {
+ switch (item.getItemId())
+ {
+ case android.R.id.home:
+ /* one-pane layout, pop possible fragment from stack, finish otherwise */
+ if (!getFragmentManager().popBackStackImmediate())
+ {
+ finish();
+ }
+ getActionBar().setTitle(getTitle());
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ @Override
+ public void onRemediationInstructionSelected(RemediationInstruction instruction)
+ {
+ RemediationInstructionFragment frag = (RemediationInstructionFragment)getFragmentManager().findFragmentById(R.id.remediation_instruction_fragment);
+
+ if (frag != null)
+ { /* two-pane layout, update directly */
+ frag.updateView(instruction);
+ }
+ else
+ { /* one-pane layout, replace fragment */
+ frag = new RemediationInstructionFragment();
+ Bundle args = new Bundle();
+ args.putParcelable(RemediationInstructionFragment.ARG_REMEDIATION_INSTRUCTION, instruction);
+ frag.setArguments(args);
+
+ getFragmentManager().beginTransaction().replace(R.id.fragment_container, frag).addToBackStack(null).commit();
+ getActionBar().setTitle(instruction.getTitle());
+ }
+ }
+}
diff --git a/src/frontends/android/src/org/strongswan/android/ui/RemediationInstructionsFragment.java b/src/frontends/android/src/org/strongswan/android/ui/RemediationInstructionsFragment.java
new file mode 100644
index 000000000..86467dc35
--- /dev/null
+++ b/src/frontends/android/src/org/strongswan/android/ui/RemediationInstructionsFragment.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2013 Tobias Brunner
+ * 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 .
+ *
+ * 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.
+ */
+
+package org.strongswan.android.ui;
+
+import java.util.ArrayList;
+
+import org.strongswan.android.R;
+import org.strongswan.android.logic.imc.RemediationInstruction;
+import org.strongswan.android.ui.adapter.RemediationInstructionAdapter;
+
+import android.app.Activity;
+import android.app.ListFragment;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.ListView;
+
+public class RemediationInstructionsFragment extends ListFragment
+{
+ public static final String EXTRA_REMEDIATION_INSTRUCTIONS = "instructions";
+ private static final String KEY_POSITION = "position";
+ private ArrayList mInstructions = null;
+ private OnRemediationInstructionSelectedListener mListener;
+ private RemediationInstructionAdapter mAdapter;
+ private int mCurrentPosition = -1;
+
+ /**
+ * The activity containing this fragment should implement this interface
+ */
+ public interface OnRemediationInstructionSelectedListener
+ {
+ public void onRemediationInstructionSelected(RemediationInstruction instruction);
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState)
+ {
+ super.onActivityCreated(savedInstanceState);
+
+ if (savedInstanceState != null)
+ {
+ mInstructions = savedInstanceState.getParcelableArrayList(EXTRA_REMEDIATION_INSTRUCTIONS);
+ mCurrentPosition = savedInstanceState.getInt(KEY_POSITION);
+ }
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState)
+ {
+ super.onSaveInstanceState(outState);
+ outState.putParcelableArrayList(RemediationInstructionsFragment.EXTRA_REMEDIATION_INSTRUCTIONS, mInstructions);
+ outState.putInt(KEY_POSITION, mCurrentPosition);
+ }
+
+ @Override
+ public void onAttach(Activity activity)
+ {
+ super.onAttach(activity);
+
+ if (activity instanceof OnRemediationInstructionSelectedListener)
+ {
+ mListener = (OnRemediationInstructionSelectedListener)activity;
+ }
+ }
+
+ @Override
+ public void onStart()
+ {
+ super.onStart();
+
+ boolean two_pane = getFragmentManager().findFragmentById(R.id.remediation_instruction_fragment) != null;
+ if (two_pane)
+ { /* two-pane layout, make list items selectable */
+ getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);
+ }
+
+ Bundle args = getArguments();
+ if (mInstructions == null && args != null)
+ {
+ mInstructions = args.getParcelableArrayList(EXTRA_REMEDIATION_INSTRUCTIONS);
+ }
+ updateView(mInstructions);
+
+ if (two_pane && mCurrentPosition == -1 && mInstructions.size() > 0)
+ { /* two-pane layout, select first instruction */
+ mCurrentPosition = 0;
+ mListener.onRemediationInstructionSelected(mInstructions.get(0));
+ }
+ getListView().setItemChecked(mCurrentPosition, true);
+ }
+
+ @Override
+ public void onListItemClick(ListView l, View v, int position, long id)
+ {
+ mCurrentPosition = position;
+ mListener.onRemediationInstructionSelected(mInstructions.get(position));
+ getListView().setItemChecked(position, true);
+ }
+
+ public void updateView(ArrayList instructions)
+ {
+ if (mAdapter == null)
+ {
+ mAdapter = new RemediationInstructionAdapter(getActivity());
+ setListAdapter(mAdapter);
+ }
+ mInstructions = instructions;
+ mAdapter.setData(mInstructions);
+ }
+}
diff --git a/src/frontends/android/src/org/strongswan/android/ui/VpnProfileDetailActivity.java b/src/frontends/android/src/org/strongswan/android/ui/VpnProfileDetailActivity.java
index 91e521cf4..baad9611d 100644
--- a/src/frontends/android/src/org/strongswan/android/ui/VpnProfileDetailActivity.java
+++ b/src/frontends/android/src/org/strongswan/android/ui/VpnProfileDetailActivity.java
@@ -28,6 +28,8 @@ import org.strongswan.android.logic.TrustedCertificateManager;
import android.app.Activity;
import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
@@ -36,6 +38,7 @@ import android.os.Bundle;
import android.security.KeyChain;
import android.security.KeyChainAliasCallback;
import android.security.KeyChainException;
+import android.text.Html;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
@@ -73,6 +76,7 @@ public class VpnProfileDetailActivity extends Activity
private TwoLineListItem mSelectUserCert;
private CheckBox mCheckAuto;
private TwoLineListItem mSelectCert;
+ private TwoLineListItem mTncNotice;
@Override
public void onCreate(Bundle savedInstanceState)
@@ -90,6 +94,7 @@ public class VpnProfileDetailActivity extends Activity
mName = (EditText)findViewById(R.id.name);
mGateway = (EditText)findViewById(R.id.gateway);
mSelectVpnType = (Spinner)findViewById(R.id.vpn_type);
+ mTncNotice = (TwoLineListItem)findViewById(R.id.tnc_notice);
mUsernamePassword = (ViewGroup)findViewById(R.id.username_password_group);
mUsername = (EditText)findViewById(R.id.username);
@@ -117,6 +122,16 @@ public class VpnProfileDetailActivity extends Activity
}
});
+ mTncNotice.getText1().setText(R.string.tnc_notice_title);
+ mTncNotice.getText2().setText(R.string.tnc_notice_subtitle);
+ mTncNotice.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v)
+ {
+ new TncNoticeDialog().show(VpnProfileDetailActivity.this.getFragmentManager(), "TncNotice");
+ }
+ });
+
mSelectUserCert.setOnClickListener(new SelectUserCertOnClickListener());
mCheckAuto.setOnCheckedChangeListener(new OnCheckedChangeListener() {
@@ -225,6 +240,7 @@ public class VpnProfileDetailActivity extends Activity
{
mUsernamePassword.setVisibility(mVpnType.getRequiresUsernamePassword() ? View.VISIBLE : View.GONE);
mUserCertificate.setVisibility(mVpnType.getRequiresCertificate() ? View.VISIBLE : View.GONE);
+ mTncNotice.setVisibility(mVpnType.getEnableBYOD() ? View.VISIBLE : View.GONE);
if (mVpnType.getRequiresCertificate())
{
@@ -536,4 +552,25 @@ public class VpnProfileDetailActivity extends Activity
updateCredentialView();
}
}
+
+ /**
+ * Dialog with notification message if EAP-TNC is used.
+ */
+ public static class TncNoticeDialog extends DialogFragment
+ {
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState)
+ {
+ return new AlertDialog.Builder(getActivity())
+ .setTitle(R.string.tnc_notice_title)
+ .setMessage(Html.fromHtml(getString(R.string.tnc_notice_details)))
+ .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int id)
+ {
+ dialog.dismiss();
+ }
+ }).create();
+ }
+ }
}
diff --git a/src/frontends/android/src/org/strongswan/android/ui/VpnStateFragment.java b/src/frontends/android/src/org/strongswan/android/ui/VpnStateFragment.java
index 738ed111f..3219bba7a 100644
--- a/src/frontends/android/src/org/strongswan/android/ui/VpnStateFragment.java
+++ b/src/frontends/android/src/org/strongswan/android/ui/VpnStateFragment.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 Tobias Brunner
+ * Copyright (C) 2012-2013 Tobias Brunner
* Copyright (C) 2012 Giuliano Grassi
* Copyright (C) 2012 Ralf Sager
* Hochschule fuer Technik Rapperswil
@@ -17,12 +17,17 @@
package org.strongswan.android.ui;
+import java.util.ArrayList;
+import java.util.List;
+
import org.strongswan.android.R;
import org.strongswan.android.data.VpnProfile;
import org.strongswan.android.logic.VpnStateService;
import org.strongswan.android.logic.VpnStateService.ErrorState;
import org.strongswan.android.logic.VpnStateService.State;
import org.strongswan.android.logic.VpnStateService.VpnStateListener;
+import org.strongswan.android.logic.imc.ImcState;
+import org.strongswan.android.logic.imc.RemediationInstruction;
import android.app.AlertDialog;
import android.app.Fragment;
@@ -45,6 +50,7 @@ import android.widget.TextView;
public class VpnStateFragment extends Fragment implements VpnStateListener
{
private static final String KEY_ERROR = "error";
+ private static final String KEY_IMC_STATE = "imc_state";
private static final String KEY_NAME = "name";
private TextView mProfileNameView;
@@ -56,6 +62,7 @@ public class VpnStateFragment extends Fragment implements VpnStateListener
private State mState;
private AlertDialog mErrorDialog;
private ErrorState mError;
+ private ImcState mImcState;
private String mErrorProfileName;
private VpnStateService mService;
private final ServiceConnection mServiceConnection = new ServiceConnection() {
@@ -85,9 +92,11 @@ public class VpnStateFragment extends Fragment implements VpnStateListener
mServiceConnection, Service.BIND_AUTO_CREATE);
mError = ErrorState.NO_ERROR;
+ mImcState = ImcState.UNKNOWN;
if (savedInstanceState != null && savedInstanceState.containsKey(KEY_ERROR))
{
mError = (ErrorState)savedInstanceState.getSerializable(KEY_ERROR);
+ mImcState = (ImcState)savedInstanceState.getSerializable(KEY_IMC_STATE);
mErrorProfileName = savedInstanceState.getString(KEY_NAME);
}
}
@@ -98,6 +107,7 @@ public class VpnStateFragment extends Fragment implements VpnStateListener
super.onSaveInstanceState(outState);
outState.putSerializable(KEY_ERROR, mError);
+ outState.putSerializable(KEY_IMC_STATE, mImcState);
outState.putString(KEY_NAME, mErrorProfileName);
}
@@ -167,6 +177,7 @@ public class VpnStateFragment extends Fragment implements VpnStateListener
{
State state = mService.getState();
ErrorState error = ErrorState.NO_ERROR;
+ ImcState imcState = ImcState.UNKNOWN;
String name = "", gateway = "";
if (state != State.DISABLED)
@@ -178,9 +189,10 @@ public class VpnStateFragment extends Fragment implements VpnStateListener
gateway = profile.getGateway();
}
error = mService.getErrorState();
+ imcState = mService.getImcState();
}
- if (reportError(name, state, error))
+ if (reportError(name, state, error, imcState))
{
return;
}
@@ -223,16 +235,18 @@ public class VpnStateFragment extends Fragment implements VpnStateListener
}
}
- private boolean reportError(String name, State state, ErrorState error)
+ private boolean reportError(String name, State state, ErrorState error, ImcState imcState)
{
if (mError != ErrorState.NO_ERROR)
{ /* we are currently reporting an error which was not yet dismissed */
error = mError;
+ imcState = mImcState;
name = mErrorProfileName;
}
else if (error != ErrorState.NO_ERROR && (state == State.CONNECTING || state == State.CONNECTED))
{ /* while initiating we report errors */
mError = error;
+ mImcState = imcState;
mErrorProfileName = name;
}
else
@@ -257,7 +271,14 @@ public class VpnStateFragment extends Fragment implements VpnStateListener
switch (error)
{
case AUTH_FAILED:
- showErrorDialog(R.string.error_auth_failed);
+ if (imcState == ImcState.BLOCK)
+ {
+ showErrorDialog(R.string.error_assessment_failed);
+ }
+ else
+ {
+ showErrorDialog(R.string.error_auth_failed);
+ }
break;
case PEER_AUTH_FAILED:
showErrorDialog(R.string.error_peer_auth_failed);
@@ -305,6 +326,13 @@ public class VpnStateFragment extends Fragment implements VpnStateListener
}
}
+ private void clearError()
+ {
+ mError = ErrorState.NO_ERROR;
+ mImcState = ImcState.UNKNOWN;
+ updateView();
+ }
+
private void showConnectDialog(String profile, String gateway)
{
mProgressDialog = new ProgressDialog(getActivity());
@@ -338,32 +366,46 @@ public class VpnStateFragment extends Fragment implements VpnStateListener
private void showErrorDialog(int textid)
{
+ final List instructions = mService.getRemediationInstructions();
+ final boolean show_instructions = mImcState == ImcState.BLOCK && !instructions.isEmpty();
+ int text = show_instructions ? R.string.show_remediation_instructions : R.string.show_log;
+
mErrorDialog = new AlertDialog.Builder(getActivity())
.setMessage(getString(R.string.error_introduction) + " " + getString(textid))
.setCancelable(false)
- .setNeutralButton(R.string.show_log, new DialogInterface.OnClickListener() {
+ .setNeutralButton(text, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which)
{
+ clearError();
dialog.dismiss();
- Intent logIntent = new Intent(getActivity(), LogActivity.class);
- startActivity(logIntent);
+ Intent intent;
+ if (show_instructions)
+ {
+ intent = new Intent(getActivity(), RemediationInstructionsActivity.class);
+ intent.putParcelableArrayListExtra(RemediationInstructionsFragment.EXTRA_REMEDIATION_INSTRUCTIONS,
+ new ArrayList(instructions));
+ }
+ else
+ {
+ intent = new Intent(getActivity(), LogActivity.class);
+ }
+ startActivity(intent);
}
})
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id)
{
+ clearError();
dialog.dismiss();
}
}).create();
mErrorDialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
@Override
public void onDismiss(DialogInterface dialog)
- { /* clear the error */
- mError = ErrorState.NO_ERROR;
+ {
mErrorDialog = null;
- updateView();
}
});
mErrorDialog.show();
diff --git a/src/frontends/android/src/org/strongswan/android/ui/adapter/RemediationInstructionAdapter.java b/src/frontends/android/src/org/strongswan/android/ui/adapter/RemediationInstructionAdapter.java
new file mode 100644
index 000000000..e9ab52287
--- /dev/null
+++ b/src/frontends/android/src/org/strongswan/android/ui/adapter/RemediationInstructionAdapter.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2013 Tobias Brunner
+ * 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 .
+ *
+ * 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.
+ */
+
+package org.strongswan.android.ui.adapter;
+
+import java.util.List;
+
+import org.strongswan.android.R;
+import org.strongswan.android.logic.imc.RemediationInstruction;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.TextView;
+
+public class RemediationInstructionAdapter extends ArrayAdapter
+{
+ public RemediationInstructionAdapter(Context context)
+ {
+ super(context, 0);
+ }
+
+ /**
+ * Set new data for this adapter.
+ *
+ * @param data the new data (null to clear)
+ */
+ public void setData(List data)
+ {
+ clear();
+ if (data != null)
+ {
+ addAll(data);
+ }
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent)
+ {
+ View view;
+ if (convertView != null)
+ {
+ view = convertView;
+ }
+ else
+ {
+ LayoutInflater inflater = LayoutInflater.from(getContext());
+ view = inflater.inflate(R.layout.remediation_instruction_item, parent, false);
+ }
+ RemediationInstruction item = getItem(position);
+ TextView text = (TextView)view.findViewById(android.R.id.text1);
+ text.setText(item.getTitle());
+ text = (TextView)view.findViewById(android.R.id.text2);
+ text.setText(item.getDescription());
+ return view;
+ }
+}
diff --git a/src/frontends/android/src/org/strongswan/android/utils/BufferedByteWriter.java b/src/frontends/android/src/org/strongswan/android/utils/BufferedByteWriter.java
new file mode 100644
index 000000000..efc728377
--- /dev/null
+++ b/src/frontends/android/src/org/strongswan/android/utils/BufferedByteWriter.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2013 Tobias Brunner
+ * 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 .
+ *
+ * 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.
+ */
+
+package org.strongswan.android.utils;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Very similar to ByteBuffer (although with a stripped interface) but it
+ * automatically resizes the underlying buffer.
+ */
+public class BufferedByteWriter
+{
+ /**
+ * The underlying byte buffer
+ */
+ private byte[] mBuffer;
+
+ /**
+ * ByteBuffer used as wrapper around the buffer to easily convert values
+ */
+ private ByteBuffer mWriter;
+
+ /**
+ * Create a writer with a default initial capacity
+ */
+ public BufferedByteWriter()
+ {
+ this(0);
+ }
+
+ /**
+ * Create a writer with the given initial capacity (helps avoid expensive
+ * resizing if known).
+ * @param capacity initial capacity
+ */
+ public BufferedByteWriter(int capacity)
+ {
+ capacity = capacity > 4 ? capacity : 32;
+ mBuffer = new byte[capacity];
+ mWriter = ByteBuffer.wrap(mBuffer);
+ }
+
+ /**
+ * Ensure that there is enough space available to write the requested
+ * number of bytes. If necessary the internal buffer is resized.
+ * @param required required number of bytes
+ */
+ private void ensureCapacity(int required)
+ {
+ if (mWriter.remaining() >= required)
+ {
+ return;
+ }
+ byte[] buffer = new byte[(mBuffer.length + required) * 2];
+ System.arraycopy(mBuffer, 0, buffer, 0, mWriter.position());
+ mBuffer = buffer;
+ ByteBuffer writer = ByteBuffer.wrap(buffer);
+ writer.position(mWriter.position());
+ mWriter = writer;
+ }
+
+ /**
+ * Write the given byte array to the buffer
+ * @param value
+ * @return the writer
+ */
+ public BufferedByteWriter put(byte[] value)
+ {
+ ensureCapacity(value.length);
+ mWriter.put(value);
+ return this;
+ }
+
+ /**
+ * Write the given byte to the buffer
+ * @param value
+ * @return the writer
+ */
+ public BufferedByteWriter put(byte value)
+ {
+ ensureCapacity(1);
+ mWriter.put(value);
+ return this;
+ }
+
+ /**
+ * Write the 8-bit length of the given data followed by the data itself
+ * @param value
+ * @return the writer
+ */
+ public BufferedByteWriter putLen8(byte[] value)
+ {
+ ensureCapacity(1 + value.length);
+ mWriter.put((byte)value.length);
+ mWriter.put(value);
+ return this;
+ }
+
+ /**
+ * Write the 16-bit length of the given data followed by the data itself
+ * @param value
+ * @return the writer
+ */
+ public BufferedByteWriter putLen16(byte[] value)
+ {
+ ensureCapacity(2 + value.length);
+ mWriter.putShort((short)value.length);
+ mWriter.put(value);
+ return this;
+ }
+
+ /**
+ * Write the given short value (16-bit) in big-endian order to the buffer
+ * @param value
+ * @return the writer
+ */
+ public BufferedByteWriter put16(short value)
+ {
+ ensureCapacity(2);
+ mWriter.putShort(value);
+ return this;
+ }
+
+ /**
+ * Write 24-bit of the given value in big-endian order to the buffer
+ * @param value
+ * @return the writer
+ */
+ public BufferedByteWriter put24(int value)
+ {
+ ensureCapacity(3);
+ mWriter.put((byte)(value >> 16));
+ mWriter.putShort((short)value);
+ return this;
+ }
+
+ /**
+ * Write the given int value (32-bit) in big-endian order to the buffer
+ * @param value
+ * @return the writer
+ */
+ public BufferedByteWriter put32(int value)
+ {
+ ensureCapacity(4);
+ mWriter.putInt(value);
+ return this;
+ }
+
+ /**
+ * Write the given long value (64-bit) in big-endian order to the buffer
+ * @param value
+ * @return the writer
+ */
+ public BufferedByteWriter put64(long value)
+ {
+ ensureCapacity(8);
+ mWriter.putLong(value);
+ return this;
+ }
+
+ /**
+ * Convert the internal buffer to a new byte array.
+ * @return byte array
+ */
+ public byte[] toByteArray()
+ {
+ int length = mWriter.position();
+ byte[] bytes = new byte[length];
+ System.arraycopy(mBuffer, 0, bytes, 0, length);
+ return bytes;
+ }
+}
diff --git a/src/libcharon/Android.mk b/src/libcharon/Android.mk
index 66606f937..75cf74fa4 100644
--- a/src/libcharon/Android.mk
+++ b/src/libcharon/Android.mk
@@ -186,8 +186,11 @@ endif
LOCAL_SRC_FILES += $(call add_plugin, eap-peap)
-# adding libtls if any of the three plugins above is enabled
-ifneq ($(or $(call plugin_enabled, eap-tls), $(call plugin_enabled, eap-ttls), $(call plugin_enabled, eap-peap)),)
+LOCAL_SRC_FILES += $(call add_plugin, eap-tnc)
+
+# adding libtls if any of the four plugins above is enabled
+ifneq ($(or $(call plugin_enabled, eap-tls), $(call plugin_enabled, eap-ttls), \
+ $(call plugin_enabled, eap-peap), $(call plugin_enabled, eap-tnc)),)
LOCAL_C_INCLUDES += $(LOCAL_PATH)/../libtls/
LOCAL_SRC_FILES += $(addprefix ../libtls/, \
tls_protection.c tls_compression.c tls_fragmentation.c tls_alert.c \
@@ -207,6 +210,32 @@ ifneq ($(call plugin_enabled, stroke),)
LOCAL_C_INCLUDES += $(LOCAL_PATH)/../stroke/
endif
+LOCAL_SRC_FILES += $(call add_plugin, tnc-imc)
+ifneq ($(call plugin_enabled, tnc-imc),)
+LOCAL_SHARED_LIBRARIES += libdl
+endif
+
+LOCAL_SRC_FILES += $(call add_plugin, tnc-tnccs)
+
+LOCAL_SRC_FILES += $(call add_plugin, tnccs-20)
+LOCAL_SRC_FILES += $(call add_plugin_subdirs, tnccs-20, batch messages state_machine)
+ifneq ($(call plugin_enabled, tnccs-20),)
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/plugins/tnccs_20/
+# for tls.h
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/../libtls/
+endif
+
+ifneq ($(or $(call plugin_enabled, eap-tnc), $(call plugin_enabled, tnc-imc), \
+ $(call plugin_enabled, tnc-tnccs), $(call plugin_enabled, tnccs-20)),)
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/../libtnccs/
+LOCAL_SHARED_LIBRARIES += libtnccs
+endif
+
+ifneq ($(or $(call plugin_enabled, tnc-imc), $(call plugin_enabled, tnc-tnccs), \
+ $(call plugin_enabled, tnccs-20)),)
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/../libtncif/
+LOCAL_SHARED_LIBRARIES += libtncif
+endif
# build libcharon --------------------------------------------------------------
diff --git a/src/libcharon/plugins/eap_mschapv2/eap_mschapv2.c b/src/libcharon/plugins/eap_mschapv2/eap_mschapv2.c
index 96f437583..49e3dd142 100644
--- a/src/libcharon/plugins/eap_mschapv2/eap_mschapv2.c
+++ b/src/libcharon/plugins/eap_mschapv2/eap_mschapv2.c
@@ -782,7 +782,7 @@ static status_t process_peer_success(private_eap_mschapv2_t *this,
enumerator = enumerator_create_token(message, " ", " ");
while (enumerator->enumerate(enumerator, &token))
{
- if (strneq(token, "S=", 2))
+ if (strpfx(token, "S="))
{
chunk_t hex;
token += 2;
@@ -795,7 +795,7 @@ static status_t process_peer_success(private_eap_mschapv2_t *this,
hex = chunk_create(token, AUTH_RESPONSE_LEN - 2);
auth_string = chunk_from_hex(hex, NULL);
}
- else if (strneq(token, "M=", 2))
+ else if (strpfx(token, "M="))
{
token += 2;
msg = strdup(token);
@@ -864,16 +864,16 @@ static status_t process_peer_failure(private_eap_mschapv2_t *this,
enumerator = enumerator_create_token(message, " ", " ");
while (enumerator->enumerate(enumerator, &token))
{
- if (strneq(token, "E=", 2))
+ if (strpfx(token, "E="))
{
token += 2;
error = atoi(token);
}
- else if (strneq(token, "R=", 2))
+ else if (strpfx(token, "R="))
{
/* ignore retriable */
}
- else if (strneq(token, "C=", 2))
+ else if (strpfx(token, "C="))
{
chunk_t hex;
token += 2;
@@ -886,11 +886,11 @@ static status_t process_peer_failure(private_eap_mschapv2_t *this,
hex = chunk_create(token, 2 * CHALLENGE_LEN);
challenge = chunk_from_hex(hex, NULL);
}
- else if (strneq(token, "V=", 2))
+ else if (strpfx(token, "V="))
{
/* ignore version */
}
- else if (strneq(token, "M=", 2))
+ else if (strpfx(token, "M="))
{
token += 2;
msg = strdup(token);
diff --git a/src/libcharon/plugins/load_tester/load_tester_config.c b/src/libcharon/plugins/load_tester/load_tester_config.c
index a64affde8..ebadf44ca 100644
--- a/src/libcharon/plugins/load_tester/load_tester_config.c
+++ b/src/libcharon/plugins/load_tester/load_tester_config.c
@@ -355,7 +355,7 @@ static void generate_auth_cfg(private_load_tester_config_t *this, char *str,
}
}
}
- else if (strneq(str, "eap", strlen("eap")))
+ else if (strpfx(str, "eap"))
{ /* EAP authentication, use a NAI */
class = AUTH_CLASS_EAP;
if (*(str + strlen("eap")) == '-')
diff --git a/src/libcharon/plugins/stroke/stroke_config.c b/src/libcharon/plugins/stroke/stroke_config.c
index da8d35c40..079e65f11 100644
--- a/src/libcharon/plugins/stroke/stroke_config.c
+++ b/src/libcharon/plugins/stroke/stroke_config.c
@@ -559,9 +559,9 @@ static auth_cfg_t *build_auth_cfg(private_stroke_config_t *this,
}
/* authentication metod (class, actually) */
- if (strneq(auth, "pubkey", strlen("pubkey")) ||
- strneq(auth, "rsa", strlen("rsa")) ||
- strneq(auth, "ecdsa", strlen("ecdsa")))
+ if (strpfx(auth, "pubkey") ||
+ strpfx(auth, "rsa") ||
+ strpfx(auth, "ecdsa"))
{
cfg->add(cfg, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_PUBKEY);
build_crl_policy(cfg, local, msg->add_conn.crl_policy);
@@ -572,7 +572,7 @@ static auth_cfg_t *build_auth_cfg(private_stroke_config_t *this,
{
cfg->add(cfg, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_PSK);
}
- else if (strneq(auth, "xauth", 5))
+ else if (strpfx(auth, "xauth"))
{
char *pos;
@@ -588,7 +588,7 @@ static auth_cfg_t *build_auth_cfg(private_stroke_config_t *this,
identification_create_from_string(msg->add_conn.xauth_identity));
}
}
- else if (strneq(auth, "eap", 3))
+ else if (strpfx(auth, "eap"))
{
eap_vendor_type_t *type;
diff --git a/src/libcharon/plugins/stroke/stroke_cred.c b/src/libcharon/plugins/stroke/stroke_cred.c
index 6b37ac787..474417886 100644
--- a/src/libcharon/plugins/stroke/stroke_cred.c
+++ b/src/libcharon/plugins/stroke/stroke_cred.c
@@ -175,7 +175,7 @@ METHOD(stroke_cred_t, load_ca, certificate_t*,
certificate_t *cert = NULL;
char path[PATH_MAX];
- if (strneq(filename, "%smartcard", strlen("%smartcard")))
+ if (strpfx(filename, "%smartcard"))
{
smartcard_format_t format;
char module[SC_PART_LEN], keyid[SC_PART_LEN];
@@ -239,7 +239,7 @@ METHOD(stroke_cred_t, load_peer, certificate_t*,
certificate_t *cert = NULL;
char path[PATH_MAX];
- if (strneq(filename, "%smartcard", strlen("%smartcard")))
+ if (strpfx(filename, "%smartcard"))
{
smartcard_format_t format;
char module[SC_PART_LEN], keyid[SC_PART_LEN];
@@ -787,7 +787,7 @@ static bool load_pin(mem_cred_t *secrets, chunk_t line, int line_nr,
}
chunk = chunk_from_hex(chunk_create(keyid, strlen(keyid)), NULL);
- if (secret.len == 7 && strneq(secret.ptr, "%prompt", 7))
+ if (secret.len == 7 && strpfx(secret.ptr, "%prompt"))
{
free(secret.ptr);
if (!prompt)
@@ -880,7 +880,7 @@ static bool load_from_file(chunk_t line, int line_nr, FILE *prompt,
return FALSE;
}
}
- if (secret.len == 7 && strneq(secret.ptr, "%prompt", 7))
+ if (secret.len == 7 && strpfx(secret.ptr, "%prompt"))
{
callback_cred_t *cb;
passphrase_cb_data_t pp_data = {
@@ -1142,8 +1142,7 @@ static void load_secrets(private_stroke_cred_t *this, mem_cred_t *secrets,
{
continue;
}
- if (line.len > strlen("include ") &&
- strneq(line.ptr, "include ", strlen("include ")))
+ if (line.len > strlen("include ") && strpfx(line.ptr, "include "))
{
char **expanded, *dir, pattern[PATH_MAX];
u_char *pos;
@@ -1211,7 +1210,7 @@ static void load_secrets(private_stroke_cred_t *this, mem_cred_t *secrets,
continue;
}
- if (line.len > 2 && strneq(": ", line.ptr, 2))
+ if (line.len > 2 && strpfx(line.ptr, ": "))
{
/* no ids, skip the ':' */
ids = chunk_empty;
diff --git a/src/libhydra/plugins/kernel_klips/kernel_klips_ipsec.c b/src/libhydra/plugins/kernel_klips/kernel_klips_ipsec.c
index 5ca5879ff..38c92ce71 100644
--- a/src/libhydra/plugins/kernel_klips/kernel_klips_ipsec.c
+++ b/src/libhydra/plugins/kernel_klips/kernel_klips_ipsec.c
@@ -78,7 +78,7 @@
/** this is the default number of ipsec devices */
#define DEFAULT_IPSEC_DEV_COUNT 4
/** TRUE if the given name matches an ipsec device */
-#define IS_IPSEC_DEV(name) (strneq((name), IPSEC_DEV_PREFIX, sizeof(IPSEC_DEV_PREFIX) - 1))
+#define IS_IPSEC_DEV(name) (strpfx((name), IPSEC_DEV_PREFIX))
/** the following stuff is from ipsec_tunnel.h */
struct ipsectunnelconf
@@ -2332,7 +2332,7 @@ METHOD(kernel_ipsec_t, query_policy, status_t,
while (fgets(line, sizeof(line), file))
{
- if (strneq(line, said, strlen(said)))
+ if (strpfx(line, said))
{
/* fine we found the correct line, now find the idle time */
u_int32_t idle_time;
diff --git a/src/libhydra/plugins/resolve/resolve_handler.c b/src/libhydra/plugins/resolve/resolve_handler.c
index 6b8d6be7f..6c57fa0bf 100644
--- a/src/libhydra/plugins/resolve/resolve_handler.c
+++ b/src/libhydra/plugins/resolve/resolve_handler.c
@@ -126,7 +126,7 @@ static void remove_nameserver(private_resolve_handler_t *this,
/* copy all, but matching line */
while (fgets(line, sizeof(line), in))
{
- if (strneq(line, matcher, strlen(matcher)))
+ if (strpfx(line, matcher))
{
DBG1(DBG_IKE, "removing DNS server %H from %s",
addr, this->file);
diff --git a/src/libimcv/Android.mk b/src/libimcv/Android.mk
new file mode 100644
index 000000000..db953d955
--- /dev/null
+++ b/src/libimcv/Android.mk
@@ -0,0 +1,65 @@
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+# copy-n-paste from Makefile.am
+libimcv_la_SOURCES := \
+ imcv.h imcv.c \
+ imc/imc_agent.h imc/imc_agent.c imc/imc_state.h \
+ imc/imc_msg.h imc/imc_msg.c \
+ imv/imv_agent.h imv/imv_agent.c imv/imv_state.h \
+ imv/imv_agent_if.h imv/imv_if.h \
+ imv/imv_database.h imv/imv_database.c \
+ imv/imv_msg.h imv/imv_msg.c \
+ imv/imv_lang_string.h imv/imv_lang_string.c \
+ imv/imv_reason_string.h imv/imv_reason_string.c \
+ imv/imv_remediation_string.h imv/imv_remediation_string.c \
+ imv/imv_session.h imv/imv_session.c \
+ imv/imv_workitem.h imv/imv_workitem.c \
+ imv/tables.sql imv/data.sql \
+ ietf/ietf_attr.h ietf/ietf_attr.c \
+ ietf/ietf_attr_assess_result.h ietf/ietf_attr_assess_result.c \
+ ietf/ietf_attr_attr_request.h ietf/ietf_attr_attr_request.c \
+ ietf/ietf_attr_fwd_enabled.h ietf/ietf_attr_fwd_enabled.c \
+ ietf/ietf_attr_default_pwd_enabled.h ietf/ietf_attr_default_pwd_enabled.c \
+ ietf/ietf_attr_installed_packages.h ietf/ietf_attr_installed_packages.c \
+ ietf/ietf_attr_numeric_version.h ietf/ietf_attr_numeric_version.c \
+ ietf/ietf_attr_op_status.h ietf/ietf_attr_op_status.c \
+ ietf/ietf_attr_pa_tnc_error.h ietf/ietf_attr_pa_tnc_error.c \
+ ietf/ietf_attr_port_filter.h ietf/ietf_attr_port_filter.c \
+ ietf/ietf_attr_product_info.h ietf/ietf_attr_product_info.c \
+ ietf/ietf_attr_remediation_instr.h ietf/ietf_attr_remediation_instr.c \
+ ietf/ietf_attr_string_version.h ietf/ietf_attr_string_version.c \
+ ita/ita_attr.h ita/ita_attr.c \
+ ita/ita_attr_command.h ita/ita_attr_command.c \
+ ita/ita_attr_dummy.h ita/ita_attr_dummy.c \
+ ita/ita_attr_get_settings.h ita/ita_attr_get_settings.c \
+ ita/ita_attr_settings.h ita/ita_attr_settings.c \
+ ita/ita_attr_angel.h ita/ita_attr_angel.c \
+ ita/ita_attr_device_id.h ita/ita_attr_device_id.c \
+ os_info/os_info.h os_info/os_info.c \
+ pa_tnc/pa_tnc_attr.h \
+ pa_tnc/pa_tnc_msg.h pa_tnc/pa_tnc_msg.c \
+ pa_tnc/pa_tnc_attr_manager.h pa_tnc/pa_tnc_attr_manager.c
+
+LOCAL_SRC_FILES := $(filter %.c,$(libimcv_la_SOURCES))
+
+# build libimcv ----------------------------------------------------------------
+
+LOCAL_C_INCLUDES += \
+ $(libvstr_PATH) \
+ $(strongswan_PATH)/src/libtncif \
+ $(strongswan_PATH)/src/libstrongswan
+
+LOCAL_CFLAGS := $(strongswan_CFLAGS)
+
+LOCAL_MODULE := libimcv
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_ARM_MODE := arm
+
+LOCAL_PRELINK_MODULE := false
+
+LOCAL_SHARED_LIBRARIES += libstrongswan libtncif
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/src/libimcv/imcv.c b/src/libimcv/imcv.c
index b52b81c42..b5862daee 100644
--- a/src/libimcv/imcv.c
+++ b/src/libimcv/imcv.c
@@ -174,6 +174,7 @@ void libimcv_deinit(void)
imcv_pa_tnc_attributes->remove_vendor(imcv_pa_tnc_attributes, PEN_IETF);
imcv_pa_tnc_attributes->remove_vendor(imcv_pa_tnc_attributes, PEN_ITA);
DESTROY_IF(imcv_pa_tnc_attributes);
+ imcv_pa_tnc_attributes = NULL;
DESTROY_IF(imcv_db);
DBG1(DBG_LIB, "libimcv terminated");
}
diff --git a/src/libimcv/os_info/os_info.c b/src/libimcv/os_info/os_info.c
index 2c49cb01d..17000cd27 100644
--- a/src/libimcv/os_info/os_info.c
+++ b/src/libimcv/os_info/os_info.c
@@ -182,8 +182,8 @@ METHOD(os_info_t, get_setting, chunk_t,
size_t i = 0;
chunk_t value;
- if (!strneq(name, "/etc/", 5) && !strneq(name, "/proc/", 6) &&
- !strneq(name, "/sys/", 5) && !strneq(name, "/var/", 5))
+ if (!strpfx(name, "/etc/") && !strpfx(name, "/proc/") &&
+ !strpfx(name, "/sys/") && !strpfx(name, "/var/"))
{
/**
* In order to guarantee privacy, only settings from the
diff --git a/src/libimcv/plugins/imv_scanner/imv_scanner_state.c b/src/libimcv/plugins/imv_scanner/imv_scanner_state.c
index d8bba988e..4c570c46a 100644
--- a/src/libimcv/plugins/imv_scanner/imv_scanner_state.c
+++ b/src/libimcv/plugins/imv_scanner/imv_scanner_state.c
@@ -282,7 +282,10 @@ METHOD(imv_state_t, get_reason_string, bool,
/* Instantiate a TNC Reason String object */
DESTROY_IF(this->reason_string);
this->reason_string = imv_reason_string_create(*reason_language);
- this->reason_string->add_reason(this->reason_string, reasons);
+ if (this->rec != TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION)
+ {
+ this->reason_string->add_reason(this->reason_string, reasons);
+ }
*reason_string = this->reason_string->get_encoding(this->reason_string);
return TRUE;
diff --git a/src/libpts/Android.mk b/src/libpts/Android.mk
new file mode 100644
index 000000000..7c353c418
--- /dev/null
+++ b/src/libpts/Android.mk
@@ -0,0 +1,70 @@
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+# copy-n-paste from Makefile.am
+libpts_la_SOURCES := \
+ libpts.h libpts.c \
+ pts/pts.h pts/pts.c \
+ pts/pts_error.h pts/pts_error.c \
+ pts/pts_pcr.h pts/pts_pcr.c \
+ pts/pts_proto_caps.h \
+ pts/pts_req_func_comp_evid.h \
+ pts/pts_simple_evid_final.h \
+ pts/pts_creds.h pts/pts_creds.c \
+ pts/pts_database.h pts/pts_database.c \
+ pts/pts_dh_group.h pts/pts_dh_group.c \
+ pts/pts_file_meas.h pts/pts_file_meas.c \
+ pts/pts_file_meta.h pts/pts_file_meta.c \
+ pts/pts_file_type.h pts/pts_file_type.c \
+ pts/pts_meas_algo.h pts/pts_meas_algo.c \
+ pts/components/pts_component.h \
+ pts/components/pts_component_manager.h pts/components/pts_component_manager.c \
+ pts/components/pts_comp_evidence.h pts/components/pts_comp_evidence.c \
+ pts/components/pts_comp_func_name.h pts/components/pts_comp_func_name.c \
+ pts/components/ita/ita_comp_func_name.h pts/components/ita/ita_comp_func_name.c \
+ pts/components/ita/ita_comp_ima.h pts/components/ita/ita_comp_ima.c \
+ pts/components/ita/ita_comp_tboot.h pts/components/ita/ita_comp_tboot.c \
+ pts/components/ita/ita_comp_tgrub.h pts/components/ita/ita_comp_tgrub.c \
+ pts/components/tcg/tcg_comp_func_name.h pts/components/tcg/tcg_comp_func_name.c \
+ tcg/tcg_attr.h tcg/tcg_attr.c \
+ tcg/tcg_pts_attr_proto_caps.h tcg/tcg_pts_attr_proto_caps.c \
+ tcg/tcg_pts_attr_dh_nonce_params_req.h tcg/tcg_pts_attr_dh_nonce_params_req.c \
+ tcg/tcg_pts_attr_dh_nonce_params_resp.h tcg/tcg_pts_attr_dh_nonce_params_resp.c \
+ tcg/tcg_pts_attr_dh_nonce_finish.h tcg/tcg_pts_attr_dh_nonce_finish.c \
+ tcg/tcg_pts_attr_meas_algo.h tcg/tcg_pts_attr_meas_algo.c \
+ tcg/tcg_pts_attr_get_tpm_version_info.h tcg/tcg_pts_attr_get_tpm_version_info.c \
+ tcg/tcg_pts_attr_tpm_version_info.h tcg/tcg_pts_attr_tpm_version_info.c \
+ tcg/tcg_pts_attr_get_aik.h tcg/tcg_pts_attr_get_aik.c \
+ tcg/tcg_pts_attr_aik.h tcg/tcg_pts_attr_aik.c \
+ tcg/tcg_pts_attr_req_func_comp_evid.h tcg/tcg_pts_attr_req_func_comp_evid.c \
+ tcg/tcg_pts_attr_gen_attest_evid.h tcg/tcg_pts_attr_gen_attest_evid.c \
+ tcg/tcg_pts_attr_simple_comp_evid.h tcg/tcg_pts_attr_simple_comp_evid.c \
+ tcg/tcg_pts_attr_simple_evid_final.h tcg/tcg_pts_attr_simple_evid_final.c \
+ tcg/tcg_pts_attr_req_file_meas.h tcg/tcg_pts_attr_req_file_meas.c \
+ tcg/tcg_pts_attr_file_meas.h tcg/tcg_pts_attr_file_meas.c \
+ tcg/tcg_pts_attr_req_file_meta.h tcg/tcg_pts_attr_req_file_meta.c \
+ tcg/tcg_pts_attr_unix_file_meta.h tcg/tcg_pts_attr_unix_file_meta.c
+
+LOCAL_SRC_FILES := $(filter %.c,$(libpts_la_SOURCES))
+
+# build libpts -----------------------------------------------------------------
+
+LOCAL_C_INCLUDES += \
+ $(libvstr_PATH) \
+ $(strongswan_PATH)/src/libtncif \
+ $(strongswan_PATH)/src/libimcv \
+ $(strongswan_PATH)/src/libstrongswan
+
+LOCAL_CFLAGS := $(strongswan_CFLAGS)
+
+LOCAL_MODULE := libpts
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_ARM_MODE := arm
+
+LOCAL_PRELINK_MODULE := false
+
+LOCAL_SHARED_LIBRARIES += libstrongswan libimcv
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/src/libpts/pts/pts_file_meas.c b/src/libpts/pts/pts_file_meas.c
index e69c32443..f684087d7 100644
--- a/src/libpts/pts/pts_file_meas.c
+++ b/src/libpts/pts/pts_file_meas.c
@@ -302,20 +302,7 @@ pts_file_meas_t *pts_file_meas_create_from_path(u_int16_t request_id,
return NULL;
}
measurement = chunk_create(hash, hasher->get_hash_size(hasher));
-
- INIT(this,
- .public = {
- .get_request_id = _get_request_id,
- .get_file_count = _get_file_count,
- .add = _add,
- .create_enumerator = _create_enumerator,
- .check = _check,
- .verify = _verify,
- .destroy = _destroy,
- },
- .request_id = request_id,
- .list = linked_list_create(),
- );
+ this = (private_pts_file_meas_t*)pts_file_meas_create(request_id);
if (is_dir)
{
@@ -338,8 +325,7 @@ pts_file_meas_t *pts_file_meas_create_from_path(u_int16_t request_id,
{
if (!hash_file(hasher, abs_name, hash))
{
- success = FALSE;
- break;
+ continue;
}
filename = use_rel_name ? rel_name : abs_name;
DBG2(DBG_PTS, " %#B for '%s'", &measurement, filename);
diff --git a/src/libstrongswan/plugins/ldap/ldap_fetcher.c b/src/libstrongswan/plugins/ldap/ldap_fetcher.c
index 40b6d1f63..fe4c55545 100644
--- a/src/libstrongswan/plugins/ldap/ldap_fetcher.c
+++ b/src/libstrongswan/plugins/ldap/ldap_fetcher.c
@@ -112,7 +112,7 @@ METHOD(fetcher_t, fetch, status_t,
status_t status = FAILED;
chunk_t *result = userdata;
- if (!strneq(url, "ldap", 4))
+ if (!strpfx(url, "ldap"))
{
return NOT_SUPPORTED;
}
diff --git a/src/libstrongswan/plugins/mysql/mysql_database.c b/src/libstrongswan/plugins/mysql/mysql_database.c
index 789f12f20..8bd64692c 100644
--- a/src/libstrongswan/plugins/mysql/mysql_database.c
+++ b/src/libstrongswan/plugins/mysql/mysql_database.c
@@ -666,7 +666,7 @@ mysql_database_t *mysql_database_create(char *uri)
conn_t *conn;
private_mysql_database_t *this;
- if (!strneq(uri, "mysql://", 8))
+ if (!strpfx(uri, "mysql://"))
{
return NULL;
}
diff --git a/src/libstrongswan/plugins/sqlite/sqlite_database.c b/src/libstrongswan/plugins/sqlite/sqlite_database.c
index 1fb306579..41d45dee7 100644
--- a/src/libstrongswan/plugins/sqlite/sqlite_database.c
+++ b/src/libstrongswan/plugins/sqlite/sqlite_database.c
@@ -319,7 +319,7 @@ sqlite_database_t *sqlite_database_create(char *uri)
/**
* parse sqlite:///path/to/file.db uri
*/
- if (!strneq(uri, "sqlite://", 9))
+ if (!strpfx(uri, "sqlite://"))
{
return NULL;
}
diff --git a/src/libstrongswan/plugins/sshkey/sshkey_builder.c b/src/libstrongswan/plugins/sshkey/sshkey_builder.c
index 986e860d9..d6a7c645a 100644
--- a/src/libstrongswan/plugins/sshkey/sshkey_builder.c
+++ b/src/libstrongswan/plugins/sshkey/sshkey_builder.c
@@ -85,7 +85,7 @@ static sshkey_public_key_t *parse_public_key(chunk_t blob)
BUILD_RSA_MODULUS, n, BUILD_RSA_PUB_EXP, e, BUILD_END);
}
else if (format.len > strlen(ECDSA_PREFIX) &&
- strneq(format.ptr, ECDSA_PREFIX, strlen(ECDSA_PREFIX)))
+ strpfx(format.ptr, ECDSA_PREFIX))
{
chunk_t ec_blob, identifier, q, oid, encoded;
sshkey_public_key_t *key;
diff --git a/src/libstrongswan/utils/utils.h b/src/libstrongswan/utils/utils.h
index 06282de55..0e103de9c 100644
--- a/src/libstrongswan/utils/utils.h
+++ b/src/libstrongswan/utils/utils.h
@@ -81,9 +81,20 @@ static inline bool streq(const char *x, const char *y)
}
/**
- * Macro compares two strings for equality, length limited
+ * Helper function that compares two strings for equality, length limited
*/
-#define strneq(x,y,len) (strncmp(x, y, len) == 0)
+static inline bool strneq(const char *x, const char *y, size_t len)
+{
+ return strncmp(x, y, len) == 0;
+}
+
+/**
+ * Helper function that checks if a string starts with a given prefix
+ */
+static inline bool strpfx(const char *x, const char *prefix)
+{
+ return strneq(x, prefix, strlen(prefix));
+}
/**
* Helper function that compares two strings for equality ignoring case
@@ -94,9 +105,12 @@ static inline bool strcaseeq(const char *x, const char *y)
}
/**
- * Macro compares two strings for equality ignoring case, length limited
+ * Helper function that compares two strings for equality ignoring case, length limited
*/
-#define strncaseeq(x,y,len) (strncasecmp(x, y, len) == 0)
+static inline bool strncaseeq(const char *x, const char *y, size_t len)
+{
+ return strncasecmp(x, y, len) == 0;
+}
/**
* NULL-safe strdup variant
@@ -107,9 +121,12 @@ static inline char *strdupnull(const char *s)
}
/**
- * Macro compares two binary blobs for equality
+ * Helper function that compares two binary blobs for equality
*/
-#define memeq(x,y,len) (memcmp(x, y, len) == 0)
+static inline bool memeq(const void *x, const void *y, size_t len)
+{
+ return memcmp(x, y, len) == 0;
+}
/**
* Macro gives back larger of two values.
diff --git a/src/libtnccs/tnc/tnc.c b/src/libtnccs/tnc/tnc.c
index 769b9fa54..3a5b84596 100644
--- a/src/libtnccs/tnc/tnc.c
+++ b/src/libtnccs/tnc/tnc.c
@@ -100,6 +100,11 @@ static bool load_imcvs_from_config(char *filename, bool is_imc)
void *addr;
char *label;
+ if (!filename || !*filename)
+ {
+ return TRUE;
+ }
+
label = is_imc ? "IMC" : "IMV";
DBG1(DBG_TNC, "loading %ss from '%s'", label, filename);