Merge branch 'android-byod'
authorTobias Brunner <tobias@strongswan.org>
Mon, 8 Jul 2013 16:50:09 +0000 (18:50 +0200)
committerTobias Brunner <tobias@strongswan.org>
Mon, 8 Jul 2013 16:50:09 +0000 (18:50 +0200)
Adds support for EAP-TNC with a custom Android-specific IMC that
collects data such as installed packages, file hashes or system
settings.

Some parts of the implementation are based on the bachelor semester
project 'strongSwan Android 4 Client with Endpoint Assessment' by
Christoph Bühler and Patrick Lötscher.

88 files changed:
.gitignore
Android.common.mk.in
Android.mk
src/frontends/android/AndroidManifest.xml
src/frontends/android/jni/Android.mk
src/frontends/android/jni/libandroidbridge/Android.mk
src/frontends/android/jni/libandroidbridge/backend/android_service.c
src/frontends/android/jni/libandroidbridge/byod/imc_android.c [new file with mode: 0644]
src/frontends/android/jni/libandroidbridge/byod/imc_android.h [new file with mode: 0644]
src/frontends/android/jni/libandroidbridge/byod/imc_android_state.c [new file with mode: 0644]
src/frontends/android/jni/libandroidbridge/byod/imc_android_state.h [new file with mode: 0644]
src/frontends/android/jni/libandroidbridge/charonservice.c
src/frontends/android/jni/libandroidbridge/charonservice.h
src/frontends/android/res/drawable/remediation_instruction_background_large.xml [new file with mode: 0644]
src/frontends/android/res/drawable/state_background.xml [new file with mode: 0644]
src/frontends/android/res/drawable/vpn_state_background.xml [deleted file]
src/frontends/android/res/layout-large/remediation_instructions.xml [new file with mode: 0644]
src/frontends/android/res/layout/certificate_selector.xml [deleted file]
src/frontends/android/res/layout/imc_state_fragment.xml [new file with mode: 0644]
src/frontends/android/res/layout/main.xml
src/frontends/android/res/layout/profile_detail_view.xml
src/frontends/android/res/layout/remediation_instruction.xml [new file with mode: 0644]
src/frontends/android/res/layout/remediation_instruction_item.xml [new file with mode: 0644]
src/frontends/android/res/layout/remediation_instructions.xml [new file with mode: 0644]
src/frontends/android/res/layout/two_line_button.xml [new file with mode: 0644]
src/frontends/android/res/layout/vpn_state_fragment.xml
src/frontends/android/res/values-de/arrays.xml
src/frontends/android/res/values-de/strings.xml
src/frontends/android/res/values-pl/arrays.xml
src/frontends/android/res/values-pl/strings.xml
src/frontends/android/res/values-ru/arrays.xml
src/frontends/android/res/values-ru/strings.xml
src/frontends/android/res/values-ua/arrays.xml
src/frontends/android/res/values-ua/strings.xml
src/frontends/android/res/values/arrays.xml
src/frontends/android/res/values/colors.xml
src/frontends/android/res/values/strings.xml
src/frontends/android/src/org/strongswan/android/data/VpnType.java
src/frontends/android/src/org/strongswan/android/logic/CharonVpnService.java
src/frontends/android/src/org/strongswan/android/logic/VpnStateService.java
src/frontends/android/src/org/strongswan/android/logic/imc/AndroidImc.java [new file with mode: 0644]
src/frontends/android/src/org/strongswan/android/logic/imc/ImcState.java [new file with mode: 0644]
src/frontends/android/src/org/strongswan/android/logic/imc/RemediationInstruction.java [new file with mode: 0644]
src/frontends/android/src/org/strongswan/android/logic/imc/attributes/Attribute.java [new file with mode: 0644]
src/frontends/android/src/org/strongswan/android/logic/imc/attributes/AttributeType.java [new file with mode: 0644]
src/frontends/android/src/org/strongswan/android/logic/imc/attributes/DeviceIdAttribute.java [new file with mode: 0644]
src/frontends/android/src/org/strongswan/android/logic/imc/attributes/InstalledPackagesAttribute.java [new file with mode: 0644]
src/frontends/android/src/org/strongswan/android/logic/imc/attributes/PortFilterAttribute.java [new file with mode: 0644]
src/frontends/android/src/org/strongswan/android/logic/imc/attributes/PrivateEnterpriseNumber.java [new file with mode: 0644]
src/frontends/android/src/org/strongswan/android/logic/imc/attributes/ProductInformationAttribute.java [new file with mode: 0644]
src/frontends/android/src/org/strongswan/android/logic/imc/attributes/SettingsAttribute.java [new file with mode: 0644]
src/frontends/android/src/org/strongswan/android/logic/imc/attributes/StringVersionAttribute.java [new file with mode: 0644]
src/frontends/android/src/org/strongswan/android/logic/imc/collectors/Collector.java [new file with mode: 0644]
src/frontends/android/src/org/strongswan/android/logic/imc/collectors/DeviceIdCollector.java [new file with mode: 0644]
src/frontends/android/src/org/strongswan/android/logic/imc/collectors/InstalledPackagesCollector.java [new file with mode: 0644]
src/frontends/android/src/org/strongswan/android/logic/imc/collectors/PortFilterCollector.java [new file with mode: 0644]
src/frontends/android/src/org/strongswan/android/logic/imc/collectors/ProductInformationCollector.java [new file with mode: 0644]
src/frontends/android/src/org/strongswan/android/logic/imc/collectors/Protocol.java [new file with mode: 0644]
src/frontends/android/src/org/strongswan/android/logic/imc/collectors/SettingsCollector.java [new file with mode: 0644]
src/frontends/android/src/org/strongswan/android/logic/imc/collectors/StringVersionCollector.java [new file with mode: 0644]
src/frontends/android/src/org/strongswan/android/ui/ImcStateFragment.java [new file with mode: 0644]
src/frontends/android/src/org/strongswan/android/ui/MainActivity.java
src/frontends/android/src/org/strongswan/android/ui/RemediationInstructionFragment.java [new file with mode: 0644]
src/frontends/android/src/org/strongswan/android/ui/RemediationInstructionsActivity.java [new file with mode: 0644]
src/frontends/android/src/org/strongswan/android/ui/RemediationInstructionsFragment.java [new file with mode: 0644]
src/frontends/android/src/org/strongswan/android/ui/VpnProfileDetailActivity.java
src/frontends/android/src/org/strongswan/android/ui/VpnStateFragment.java
src/frontends/android/src/org/strongswan/android/ui/adapter/RemediationInstructionAdapter.java [new file with mode: 0644]
src/frontends/android/src/org/strongswan/android/utils/BufferedByteWriter.java [new file with mode: 0644]
src/libcharon/Android.mk
src/libcharon/plugins/eap_mschapv2/eap_mschapv2.c
src/libcharon/plugins/load_tester/load_tester_config.c
src/libcharon/plugins/stroke/stroke_config.c
src/libcharon/plugins/stroke/stroke_cred.c
src/libhydra/plugins/kernel_klips/kernel_klips_ipsec.c
src/libhydra/plugins/resolve/resolve_handler.c
src/libimcv/Android.mk [new file with mode: 0644]
src/libimcv/imcv.c
src/libimcv/os_info/os_info.c
src/libimcv/plugins/imv_scanner/imv_scanner_state.c
src/libpts/Android.mk [new file with mode: 0644]
src/libpts/pts/pts_file_meas.c
src/libstrongswan/plugins/ldap/ldap_fetcher.c
src/libstrongswan/plugins/mysql/mysql_database.c
src/libstrongswan/plugins/sqlite/sqlite_database.c
src/libstrongswan/plugins/sshkey/sshkey_builder.c
src/libstrongswan/utils/utils.h
src/libtnccs/tnc/tnc.c

index 3deb9fd..0135d6a 100644 (file)
@@ -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
index 1bc8a83..9f8849d 100644 (file)
@@ -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@"
index e762fe9..aa61cc0 100644 (file)
@@ -100,7 +100,8 @@ strongswan_BUILD := \
        libhydra \
        libstrongswan \
        libtncif \
-       libtnccs
+       libtnccs \
+       libimcv
 
 ifneq ($(strongswan_BUILD_STARTER),)
 strongswan_BUILD += \
index 4d76ac0..dd03cf2 100644 (file)
             android:label="@string/log_title" >
         </activity>
         <activity
+            android:name=".ui.RemediationInstructionsActivity"
+            android:label="@string/remediation_instructions_title" >
+        </activity>
+        <activity
             android:name=".ui.VpnProfileSelectActivity"
             android:label="@string/strongswan_shortcut" >
             <intent-filter>
index 0ae18fc..de3403f 100644 (file)
@@ -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)))
index fbb8612..7dd8ad6 100644 (file)
@@ -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
 
-include $(BUILD_SHARED_LIBRARY)
-
+ifneq ($(strongswan_USE_BYOD),)
+LOCAL_SHARED_LIBRARIES += libimcv libtncif libtnccs libpts
+endif
 
+include $(BUILD_SHARED_LIBRARY)
index b221865..c35d348 100644 (file)
@@ -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 (file)
index 0000000..74cb520
--- /dev/null
@@ -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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+#include "imc_android_state.h"
+#include "../android_jni.h"
+#include "../charonservice.h"
+
+#include <tnc/tnc.h>
+#include <libpts.h>
+#include <imcv.h>
+#include <imc/imc_agent.h>
+#include <imc/imc_msg.h>
+#include <pa_tnc/pa_tnc_msg.h>
+#include <ietf/ietf_attr.h>
+#include <ietf/ietf_attr_attr_request.h>
+#include <ietf/ietf_attr_installed_packages.h>
+#include <ietf/ietf_attr_pa_tnc_error.h>
+#include <ietf/ietf_attr_product_info.h>
+#include <ietf/ietf_attr_remediation_instr.h>
+#include <ietf/ietf_attr_string_version.h>
+#include <ita/ita_attr.h>
+#include <ita/ita_attr_get_settings.h>
+#include <tcg/tcg_pts_attr_file_meas.h>
+#include <tcg/tcg_pts_attr_meas_algo.h>
+#include <tcg/tcg_pts_attr_proto_caps.h>
+#include <tcg/tcg_pts_attr_req_file_meas.h>
+#include <os_info/os_info.h>
+
+#include <tncif_pa_subtypes.h>
+
+#include <pen/pen.h>
+#include <utils/debug.h>
+
+#include <stdio.h>
+
+/* IMC definitions */
+
+static const char imc_name[] = "Android";
+
+static pen_type_t msg_types[] = {
+       { PEN_IETF, PA_SUBTYPE_IETF_OPERATING_SYSTEM },
+       { PEN_IETF, PA_SUBTYPE_IETF_VPN },
+       { PEN_TCG, PA_SUBTYPE_TCG_PTS },
+};
+
+static imc_agent_t *imc_android;
+
+/**
+ * AndroidImc object accessed via JNI
+ */
+static jobject android_imc;
+
+/**
+ * AndroidImc class object
+ */
+static jclass android_imc_cls;
+
+/**
+ * see section 3.8.1 of TCG TNC IF-IMC Specification 1.3
+ */
+static TNC_Result tnc_imc_initialize(TNC_IMCID imc_id,
+                                                                        TNC_Version min_version,
+                                                                        TNC_Version max_version,
+                                                                        TNC_Version *actual_version)
+{
+       if (imc_android)
+       {
+               DBG1(DBG_IMC, "IMC \"%s\" has already been initialized", imc_name);
+               return TNC_RESULT_ALREADY_INITIALIZED;
+       }
+       imc_android = imc_agent_create(imc_name, msg_types, countof(msg_types),
+                                                                  imc_id, actual_version);
+       if (!imc_android)
+       {
+               return TNC_RESULT_FATAL;
+       }
+
+       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, "<init>",
+                                                                               "(Landroid/content/Context;)V");
+               if (!method_id)
+               {
+                       goto failed;
+               }
+               obj = (*env)->NewObject(env, cls, method_id, context);
+               if (!obj)
+               {
+                       goto failed;
+               }
+               android_imc = (*env)->NewGlobalRef(env, obj);
+               androidjni_detach_thread();
+
+               if (tnc->imcs->load_from_functions(tnc->imcs, "Android",
+                                                       tnc_imc_initialize, tnc_imc_notifyconnectionchange,
+                                                       tnc_imc_beginhandshake, tnc_imc_receivemessage,
+                                                       tnc_imc_receivemessagelong, tnc_imc_batchending,
+                                                       tnc_imc_terminate, tnc_imc_providebindfunction))
+               {
+                       return TRUE;
+               }
+failed:
+               DBG1(DBG_IMC, "initialization of Android IMC failed");
+               androidjni_exception_occurred(env);
+               success = FALSE;
+       }
+
+       if (android_imc)
+       {
+               (*env)->DeleteGlobalRef(env, android_imc);
+               android_imc = NULL;
+       }
+       if (android_imc_cls)
+       {
+               (*env)->DeleteGlobalRef(env, android_imc_cls);
+               android_imc_cls = NULL;
+       }
+       androidjni_detach_thread();
+       return success;
+}
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 (file)
index 0000000..3bfc6de
--- /dev/null
@@ -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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+/**
+ * @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 (file)
index 0000000..d429b90
--- /dev/null
@@ -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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+#include "imc_android_state.h"
+
+#include <tncif_names.h>
+
+#include <utils/debug.h>
+
+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 (file)
index 0000000..68197f3
--- /dev/null
@@ -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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+/**
+ * @defgroup imc_android_state imc_android_state
+ * @{ @ingroup android_byod
+ */
+
+#ifndef IMC_ANDROID_STATE_H_
+#define IMC_ANDROID_STATE_H_
+
+#include <imc/imc_state.h>
+#include <pts/pts.h>
+
+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_ @}*/
index 25fbf89..41fc4d6 100644 (file)
 #include "kernel/android_ipsec.h"
 #include "kernel/android_net.h"
 
+#ifdef USE_BYOD
+#include "byod/imc_android.h"
+#endif
+
 #include <daemon.h>
 #include <hydra.h>
 #include <ipsec.h>
@@ -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);
index 7391ea6..0c71d87 100644 (file)
@@ -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 <collections/linked_list.h>
 
 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;
 
 /**
@@ -54,6 +58,16 @@ enum android_vpn_state_t {
 };
 
 /**
+ * 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.
  *
  * Used to communicate with CharonVpnService via JNI
@@ -69,6 +83,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 (file)
index 0000000..470fecb
--- /dev/null
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    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 <http://www.fsf.org/copyleft/gpl.txt>.
+
+    This program is distributed in the hope that it will be useful, but
+    WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+    or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+-->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item>
+        <shape>
+            <solid
+                android:color="@color/panel_separator" />
+        </shape>
+    </item>
+
+    <item android:left="2dp">
+        <shape>
+            <solid
+                android:color="@color/panel_background" />
+        </shape>
+    </item>
+
+</layer-list>
diff --git a/src/frontends/android/res/drawable/state_background.xml b/src/frontends/android/res/drawable/state_background.xml
new file mode 100644 (file)
index 0000000..ee36325
--- /dev/null
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2012-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 <http://www.fsf.org/copyleft/gpl.txt>.
+
+    This program is distributed in the hope that it will be useful, but
+    WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+    or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+-->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item>
+        <shape>
+            <solid
+                android:color="@color/panel_separator" />
+        </shape>
+    </item>
+
+    <item android:bottom="2dp">
+        <shape>
+            <solid
+                android:color="@color/panel_background" />
+        </shape>
+    </item>
+
+</layer-list>
diff --git a/src/frontends/android/res/drawable/vpn_state_background.xml b/src/frontends/android/res/drawable/vpn_state_background.xml
deleted file mode 100644 (file)
index 24f469a..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-    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 <http://www.fsf.org/copyleft/gpl.txt>.
-
-    This program is distributed in the hope that it will be useful, but
-    WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
-    or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
-    for more details.
--->
-<shape xmlns:android="http://schemas.android.com/apk/res/android">
-
-    <solid
-        android:color="#333" />
-
-</shape>
\ No newline at end of file
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 (file)
index 0000000..5a28dd6
--- /dev/null
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    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 <http://www.fsf.org/copyleft/gpl.txt>.
+
+    This program is distributed in the hope that it will be useful, but
+    WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+    or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="horizontal"
+    android:baselineAligned="false" >
+
+    <fragment
+        class="org.strongswan.android.ui.RemediationInstructionsFragment"
+        android:id="@+id/remediation_instructions_fragment"
+        android:layout_height="match_parent"
+        android:layout_weight="1"
+        android:layout_width="0dp" />
+
+    <FrameLayout
+        android:layout_height="match_parent"
+        android:layout_weight="2"
+        android:layout_width="0dp"
+        android:background="@drawable/remediation_instruction_background_large"
+        android:padding="5dp" >
+
+        <fragment
+            class="org.strongswan.android.ui.RemediationInstructionFragment"
+            android:id="@+id/remediation_instruction_fragment"
+            android:layout_height="match_parent"
+            android:layout_width="match_parent" />
+
+    </FrameLayout>
+
+</LinearLayout>
diff --git a/src/frontends/android/res/layout/certificate_selector.xml b/src/frontends/android/res/layout/certificate_selector.xml
deleted file mode 100644 (file)
index c8c2581..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-    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 <http://www.fsf.org/copyleft/gpl.txt>.
-
-    This program is distributed in the hope that it will be useful, but
-    WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
-    or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
-    for more details.
--->
-<TwoLineListItem xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:minHeight="?android:attr/listPreferredItemHeight"
-    android:background="?android:attr/selectableItemBackground"
-    android:mode="twoLine"
-    android:padding="10dp" >
-
-    <TextView
-        android:id="@android:id/text1"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:textAppearance="?android:attr/textAppearanceMedium" />
-
-    <TextView
-        android:id="@android:id/text2"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_below="@android:id/text1"
-        android:layout_alignLeft="@android:id/text1"
-        android:textAppearance="?android:attr/textAppearanceSmall"
-        android:textColor="?android:attr/textColorSecondary" />
-
-</TwoLineListItem>
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 (file)
index 0000000..171c88d
--- /dev/null
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    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 <http://www.fsf.org/copyleft/gpl.txt>.
+
+    This program is distributed in the hope that it will be useful, but
+    WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+    or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:paddingBottom="2dp"
+    android:background="@drawable/state_background"
+    android:orientation="vertical" >
+
+    <LinearLayout
+        android:id="@+id/imc_state_button"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:background="?android:attr/selectableItemBackground"
+        android:orientation="vertical" >
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginLeft="20dp"
+            android:layout_marginRight="20dp"
+            android:layout_marginTop="10dp"
+            android:orientation="horizontal" >
+
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginRight="5dp"
+                android:text="@string/imc_state_label"
+                android:textColor="?android:textColorPrimary"
+                android:textSize="20sp" />
+
+            <TextView
+                android:id="@+id/imc_state"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text=""
+                android:textColor="?android:textColorSecondary"
+                android:textSize="20sp" />
+
+        </LinearLayout>
+
+        <TextView
+            android:id="@+id/action"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginBottom="10dp"
+            android:layout_marginLeft="20dp"
+            android:layout_marginRight="20dp"
+            android:text="@string/show_remediation_instructions"
+            android:textAppearance="?android:attr/textAppearanceSmall"
+            android:textColor="?android:attr/textColorSecondary" />
+
+    </LinearLayout>
+
+</LinearLayout>
index 1c7973e..ab03e72 100644 (file)
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-    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
             android:layout_height="wrap_content" />
 
     <fragment
+            class="org.strongswan.android.ui.ImcStateFragment"
+            android:id="@+id/imc_state_frag"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content" />
+
+    <fragment
             class="org.strongswan.android.ui.VpnProfileListFragment"
             android:id="@+id/profile_list_frag"
             android:layout_width="match_parent"
index 39c9434..91cd345 100644 (file)
             android:spinnerMode="dropdown"
             android:entries="@array/vpn_types" />
 
+        <include
+            android:id="@+id/tnc_notice"
+            layout="@layout/two_line_button"
+            android:visibility="gone" />
+
         <LinearLayout
             android:id="@+id/username_password_group"
             android:layout_width="match_parent"
 
             <include
                 android:id="@+id/select_user_certificate"
-                layout="@layout/certificate_selector" />
+                layout="@layout/two_line_button" />
 
         </LinearLayout>
 
 
         <include
             android:id="@+id/select_certificate"
-            layout="@layout/certificate_selector" />
+            layout="@layout/two_line_button" />
 
     </LinearLayout>
 
diff --git a/src/frontends/android/res/layout/remediation_instruction.xml b/src/frontends/android/res/layout/remediation_instruction.xml
new file mode 100644 (file)
index 0000000..09c0d43
--- /dev/null
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    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 <http://www.fsf.org/copyleft/gpl.txt>.
+
+    This program is distributed in the hope that it will be useful, but
+    WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+    or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+    android:padding="5dp" >
+
+    <TextView
+        android:id="@+id/title"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginLeft="10dp"
+        android:textIsSelectable="true"
+        android:textAppearance="?android:attr/textAppearanceLarge" />
+
+    <TextView
+        android:id="@+id/description"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginLeft="10dp"
+        android:textIsSelectable="true"
+        android:textColor="?android:textColorSecondary"
+        android:textAppearance="?android:attr/textAppearanceMedium" />
+
+    <TextView
+        android:id="@+id/list_header"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginLeft="10dp"
+        android:layout_marginTop="20dp"
+        android:textIsSelectable="true"
+        android:textAppearance="?android:attr/textAppearanceMedium" />
+
+    <include
+        layout="@android:layout/list_content"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1"
+        android:layout_margin="5dp" />
+
+</LinearLayout>
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 (file)
index 0000000..30dfb22
--- /dev/null
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    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 <http://www.fsf.org/copyleft/gpl.txt>.
+
+    This program is distributed in the hope that it will be useful, but
+    WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+    or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+-->
+<TwoLineListItem xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:paddingBottom="8dip"
+    android:paddingTop="8dip"
+    android:background="?android:attr/activatedBackgroundIndicator"
+    android:minHeight="?android:attr/listPreferredItemHeight"
+    android:mode="twoLine" >
+
+    <TextView
+        android:id="@android:id/text1"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginLeft="15dp"
+        android:layout_marginRight="15dp"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        android:textIsSelectable="false" />
+
+    <TextView
+        android:id="@android:id/text2"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_below="@android:id/text1"
+        android:layout_alignLeft="@android:id/text1"
+        android:layout_alignRight="@android:id/text1"
+        android:textColor="?android:textColorSecondary"
+        android:textAppearance="?android:attr/textAppearanceSmall"
+        android:singleLine="true"
+        android:ellipsize="end"
+        android:textIsSelectable="false" />
+
+</TwoLineListItem>
diff --git a/src/frontends/android/res/layout/remediation_instructions.xml b/src/frontends/android/res/layout/remediation_instructions.xml
new file mode 100644 (file)
index 0000000..84143b5
--- /dev/null
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    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 <http://www.fsf.org/copyleft/gpl.txt>.
+
+    This program is distributed in the hope that it will be useful, but
+    WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+    or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:id="@+id/fragment_container">
+
+</FrameLayout>
\ No newline at end of file
diff --git a/src/frontends/android/res/layout/two_line_button.xml b/src/frontends/android/res/layout/two_line_button.xml
new file mode 100644 (file)
index 0000000..c8c2581
--- /dev/null
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    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 <http://www.fsf.org/copyleft/gpl.txt>.
+
+    This program is distributed in the hope that it will be useful, but
+    WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+    or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+-->
+<TwoLineListItem xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:minHeight="?android:attr/listPreferredItemHeight"
+    android:background="?android:attr/selectableItemBackground"
+    android:mode="twoLine"
+    android:padding="10dp" >
+
+    <TextView
+        android:id="@android:id/text1"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:textAppearance="?android:attr/textAppearanceMedium" />
+
+    <TextView
+        android:id="@android:id/text2"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_below="@android:id/text1"
+        android:layout_alignLeft="@android:id/text1"
+        android:textAppearance="?android:attr/textAppearanceSmall"
+        android:textColor="?android:attr/textColorSecondary" />
+
+</TwoLineListItem>
index 6353f32..e347c4c 100644 (file)
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-    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
@@ -18,8 +18,8 @@
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:layout_margin="5dp"
-    android:background="@drawable/vpn_state_background"
+    android:paddingBottom="2dp"
+    android:background="@drawable/state_background"
     android:orientation="vertical" >
 
     <GridLayout
@@ -83,9 +83,4 @@
         style="?android:attr/borderlessButtonStyle" >
     </Button>
 
-    <View
-        android:layout_width="match_parent"
-        android:layout_height="2dp"
-        android:background="?android:attr/listDivider" />
-
 </LinearLayout>
index d0117b2..30578c0 100644 (file)
@@ -19,5 +19,6 @@
         <item>IKEv2 EAP (Benutzername/Passwort)</item>
         <item>IKEv2 Zertifikat</item>
         <item>IKEv2 Zertifikat + EAP (Benutzername/Passwort)</item>
+        <item>IKEv2 EAP-TNC (Benutzername/Passwort)</item>
     </string-array>
 </resources>
\ No newline at end of file
index 3181e0a..db76981 100644 (file)
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-    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
@@ -69,6 +69,9 @@
     <string name="alert_text_no_input_username">Bitte geben Sie hier Ihren Benutzernamen ein</string>
     <string name="alert_text_nocertfound_title">Kein CA-Zertifikat ausgewählt</string>
     <string name="alert_text_nocertfound">Bitte wählen Sie eines aus oder aktivieren Sie <i>Automatisch wählen</i></string>
+    <string name="tnc_notice_title">EAP-TNC kann Ihre Privatsphäre beeinträchtigen</string>
+    <string name="tnc_notice_subtitle">Gerätedaten werden an den Gateway-Betreiber gesendet</string>
+    <string name="tnc_notice_details">&lt;p>Trusted Network Connect (TNC) erlaubt Gateway-Betreibern den Gesundheitszustand von Endgeräten zu prüfen.&lt;/p>&lt;p>Dazu kann der Betreiber Daten verlangen, wie etwa eine eindeutige Identifikationsnummer, eine Liste der installierten Pakete, Systemeinstellungen oder kryptografische Prüfsummen von Dateien.&lt;/p>&lt;b>Solche Daten werden nur übermittelt nachdem die Identität des Gateways geprüft wurde.&lt;/b></string>
 
     <!-- Trusted certificate selection -->
     <string name="trusted_certs_title">CA-Zertifikate</string>
     <string name="state_disabled">Kein aktives Profil</string>
     <string name="state_error">Fehler</string>
 
+    <!-- IMC state fragment -->
+    <string name="imc_state_label">Assessment:</string>
+    <string name="imc_state_isolate">Eingeschränkt</string>
+    <string name="imc_state_block">Fehlgeschlagen</string>
+    <string name="show_remediation_instructions">Korrekturanweisungen anzeigen</string>
+
+    <!-- Remediation instructions -->
+    <string name="remediation_instructions_title">Korrekturanweisungen</string>
+
     <!-- Dialogs -->
     <string name="login_title">Passwort eingeben um zu verbinden</string>
     <string name="login_confirm">Verbinden</string>
     <string name="error_unreachable">Gateway ist nicht erreichbar.</string>
     <string name="error_peer_auth_failed">Authentifizierung des Gateway ist fehlgeschlagen.</string>
     <string name="error_auth_failed">Benutzerauthentifizierung ist fehlgeschlagen.</string>
+    <string name="error_assessment_failed">Sicherheitsassessment ist fehlgeschlagen.</string>
     <string name="error_generic">Unbekannter Fehler während des Verbindens.</string>
     <string name="connecting_title">Verbinden: %1$s</string>
     <string name="connecting_message">Verbinde mit \""%1$s\".</string>
index 1b74b2e..1a04cbf 100644 (file)
@@ -19,5 +19,6 @@
         <item>IKEv2 EAP (użytkownik/hasło)</item>
         <item>IKEv2 certyfikat</item>
         <item>IKEv2 certyfikat + EAP (użytkownik/hasło)</item>
+        <item>IKEv2 EAP-TNC (użytkownik/hasło)</item>
     </string-array>
 </resources>
\ No newline at end of file
index 3ba5e16..7aa9c51 100644 (file)
@@ -1,7 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-    Copyright (C) 2012 Giuliano Grassi
-    Copyright (C) 2012 Ralf Sager
     Copyright (C) 2012 Andreas Steffen
     HSR Hochschule fuer Technik Rapperswil
 
@@ -71,6 +69,9 @@
     <string name="alert_text_no_input_username">Wprowadź swoją nazwę użytkownika</string>
     <string name="alert_text_nocertfound_title">Nie wybrano żadnego certyfikatu CA</string>
     <string name="alert_text_nocertfound">Wybierz lub uaktywnij jeden <i>Wybierz automatycznie</i></string>
+    <string name="tnc_notice_title">EAP-TNC may affect your privacy</string>
+    <string name="tnc_notice_subtitle">Device data is sent to the gateway operator</string>
+    <string name="tnc_notice_details">&lt;p>Trusted Network Connect (TNC) allows gateway operators to assess the health of a client device.&lt;/p>&lt;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.&lt;/p>&lt;b>Any data will be sent only after verifying the gateway\'s identity.&lt;/b></string>
 
     <!-- Trusted certificate selection -->
     <string name="trusted_certs_title">Certyfikaty CA</string>
     <string name="state_disabled">Brak aktywnego VPN</string>
     <string name="state_error">Błąd</string>
 
+    <!-- IMC state fragment -->
+    <string name="imc_state_label">Assessment:</string>
+    <string name="imc_state_isolate">Restricted</string>
+    <string name="imc_state_block">Failed</string>
+    <string name="show_remediation_instructions">View remediation instructions</string>
+
+    <!-- Remediation instructions -->
+    <string name="remediation_instructions_title">Remediation instructions</string>
+
     <!-- Dialogs -->
     <string name="login_title">Wprowadż hasło</string>
     <string name="login_confirm">Połącz</string>
     <string name="error_unreachable">Bramka jest nieosiągalna</string>
     <string name="error_peer_auth_failed">Błąd przy weryfikacji bramki</string>
     <string name="error_auth_failed">Błąd przy autoryzacji użytkownika</string>
+    <string name="error_assessment_failed">Security assessment failed.</string>
     <string name="error_generic">Nieznany błąd w czasie połączenia</string>
     <string name="connecting_title">Łączenie: %1$s</string>
     <string name="connecting_message">Tworzenie tunelu VPN z \""%1$s\".</string>
index 55144f2..713f8e4 100644 (file)
@@ -18,5 +18,6 @@
         <item>IKEv2 EAP (Логин/Пароль)</item>
         <item>IKEv2 Сертификат</item>
         <item>IKEv2 Сертификат + EAP (Логин/Пароль)</item>
+        <item>IKEv2 EAP-TNC (Логин/Пароль)</item>
     </string-array>
 </resources>
index afa2136..3838485 100644 (file)
@@ -66,6 +66,9 @@
     <string name="alert_text_no_input_username">Пожалуйста введите имя пользователя</string>
     <string name="alert_text_nocertfound_title">Не выбран сертификат CA</string>
     <string name="alert_text_nocertfound">Пожалуйста выберите один <i>Выбрать автоматически</i></string>
+    <string name="tnc_notice_title">EAP-TNC may affect your privacy</string>
+    <string name="tnc_notice_subtitle">Device data is sent to the gateway operator</string>
+    <string name="tnc_notice_details">&lt;p>Trusted Network Connect (TNC) allows gateway operators to assess the health of a client device.&lt;/p>&lt;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.&lt;/p>&lt;b>Any data will be sent only after verifying the gateway\'s identity.&lt;/b></string>
 
     <!-- Trusted certificate selection -->
     <string name="trusted_certs_title">Сертификаты CA</string>
     <string name="state_disabled">Нет активных VPN</string>
     <string name="state_error">Ошибка</string>
 
+    <!-- IMC state fragment -->
+    <string name="imc_state_label">Assessment:</string>
+    <string name="imc_state_isolate">Restricted</string>
+    <string name="imc_state_block">Failed</string>
+    <string name="show_remediation_instructions">View remediation instructions</string>
+
+    <!-- Remediation instructions -->
+    <string name="remediation_instructions_title">Remediation instructions</string>
+
     <!-- Dialogs -->
     <string name="login_title">Введите пароль для соединения</string>
     <string name="login_confirm">Соединить</string>
     <string name="error_unreachable">Шлюз недоступен.</string>
     <string name="error_peer_auth_failed">Ошибка авторизаци при подключении к шлюзу.</string>
     <string name="error_auth_failed">Ошибка авторизации пользователя.</string>
+    <string name="error_assessment_failed">Security assessment failed.</string>
     <string name="error_generic">Неизвестная ошибка.</string>
     <string name="connecting_title">Подключение: %1$s</string>
     <string name="connecting_message">Подключение к VPN с \""%1$s\".</string>
index 490fea5..4bd92fe 100644 (file)
@@ -18,5 +18,6 @@
         <item>IKEv2 EAP (Логін/Пароль)</item>
         <item>IKEv2 Сертифікати</item>
         <item>IKEv2 Сертифікати + EAP (Логін/Пароль)</item>
+        <item>IKEv2 EAP-TNC (Логін/Пароль)</item>
     </string-array>
 </resources>
index 953ba0a..df016ff 100644 (file)
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-    Copyright (C) 2012 Dmitry Korzhevin
     Copyright (C) 2013 Pavel Kopchyk
+    Copyright (C) 2012 Dmitry Korzhevin
 
     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
@@ -67,6 +67,9 @@
     <string name="alert_text_no_input_username">Введіть ім\'я користувача тут</string>
     <string name="alert_text_nocertfound_title">Не вибрано сертифікат CA</string>
     <string name="alert_text_nocertfound">Будь ласка виберіть один <i>Вибрати автоматично</i></string>
+    <string name="tnc_notice_title">EAP-TNC may affect your privacy</string>
+    <string name="tnc_notice_subtitle">Device data is sent to the gateway operator</string>
+    <string name="tnc_notice_details">&lt;p>Trusted Network Connect (TNC) allows gateway operators to assess the health of a client device.&lt;/p>&lt;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.&lt;/p>&lt;b>Any data will be sent only after verifying the gateway\'s identity.&lt;/b></string>
 
     <!-- Trusted certificate selection -->
     <string name="trusted_certs_title">Сертифікати CA</string>
     <string name="state_disabled">Немає активних VPN</string>
     <string name="state_error">Помилка</string>
 
+    <!-- IMC state fragment -->
+    <string name="imc_state_label">Assessment:</string>
+    <string name="imc_state_isolate">Restricted</string>
+    <string name="imc_state_block">Failed</string>
+    <string name="show_remediation_instructions">View remediation instructions</string>
+
+    <!-- Remediation instructions -->
+    <string name="remediation_instructions_title">Remediation instructions</string>
+
     <!-- Dialogs -->
     <string name="login_title">Введіть пароль для з\'єднання</string>
     <string name="login_confirm">Підключити</string>
     <string name="error_unreachable">Немає зв\'язку зі шлюзом.</string>
     <string name="error_peer_auth_failed">Помилка перевірки данних аутентифікації шлюза.</string>
     <string name="error_auth_failed">Помилка аутентифікації користувача.</string>
+    <string name="error_assessment_failed">Security assessment failed.</string>
     <string name="error_generic">Невідома помилка під час підключення.</string>
     <string name="connecting_title">Підключення: %1$s</string>
     <string name="connecting_message">Підключення VPN з \""%1$s\".</string>
index 1ac4cc2..29f999d 100644 (file)
@@ -19,5 +19,6 @@
         <item>IKEv2 EAP (Username/Password)</item>
         <item>IKEv2 Certificate</item>
         <item>IKEv2 Certificate + EAP (Username/Password)</item>
+        <item>IKEv2 EAP-TNC (Username/Password)</item>
     </string-array>
 </resources>
\ No newline at end of file
index be64d5d..4af28b4 100644 (file)
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-    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
         name="error_text">#D9192C</color>
 
     <color
+        name="warning_text">#FF9909</color>
+
+    <color
         name="success_text">#99CC00</color>
 
+    <color
+        name="panel_background">#333333</color>
+
+    <color
+        name="panel_separator">#5a5a5a</color>
+
 </resources>
index c91bce4..1809489 100644 (file)
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-    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
@@ -69,6 +69,9 @@
     <string name="alert_text_no_input_username">Please enter your username here</string>
     <string name="alert_text_nocertfound_title">No CA certificate selected</string>
     <string name="alert_text_nocertfound">Please select one or activate <i>Select automatically</i></string>
+    <string name="tnc_notice_title">EAP-TNC may affect your privacy</string>
+    <string name="tnc_notice_subtitle">Device data is sent to the gateway operator</string>
+    <string name="tnc_notice_details">&lt;p>Trusted Network Connect (TNC) allows gateway operators to assess the health of a client device.&lt;/p>&lt;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.&lt;/p>&lt;b>Any data will be sent only after verifying the gateway\'s identity.&lt;/b></string>
 
     <!-- Trusted certificate selection -->
     <string name="trusted_certs_title">CA certificates</string>
     <string name="state_disabled">No active VPN</string>
     <string name="state_error">Error</string>
 
+    <!-- IMC state fragment -->
+    <string name="imc_state_label">Assessment:</string>
+    <string name="imc_state_isolate">Restricted</string>
+    <string name="imc_state_block">Failed</string>
+    <string name="show_remediation_instructions">View remediation instructions</string>
+
+    <!-- Remediation instructions -->
+    <string name="remediation_instructions_title">Remediation instructions</string>
+
     <!-- Dialogs -->
     <string name="login_title">Enter password to connect</string>
     <string name="login_confirm">Connect</string>
     <string name="error_unreachable">Gateway is unreachable.</string>
     <string name="error_peer_auth_failed">Verifying gateway authentication failed.</string>
     <string name="error_auth_failed">User authentication failed.</string>
+    <string name="error_assessment_failed">Security assessment failed.</string>
     <string name="error_generic">Unspecified failure while connecting.</string>
     <string name="connecting_title">Connecting: %1$s</string>
     <string name="connecting_message">Establishing VPN with \""%1$s\".</string>
index 47cc1cb..f62c96d 100644 (file)
@@ -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.
@@ -35,9 +37,23 @@ public enum VpnType
         */
        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;
        }
 
        /**
@@ -70,6 +86,16 @@ public enum VpnType
        }
 
        /**
+        * 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.
         *
         * @param identifier get the enum entry with this identifier
index 02db8c4..a7b8a8c 100644 (file)
@@ -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(),
@@ -297,6 +300,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
         * is done asynchronously via state service.
@@ -356,6 +376,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");
index 1c14cb6..2c530ba 100644 (file)
@@ -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
 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<RemediationInstruction> mRemediationInstructions = new LinkedList<RemediationInstruction>();
 
        public enum State
        {
@@ -147,6 +153,26 @@ public class VpnStateService extends Service
        }
 
        /**
+        * 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<RemediationInstruction> 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
         * started.
@@ -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<Boolean>() {
+                       @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 (file)
index 0000000..351fab8
--- /dev/null
@@ -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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+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 (file)
index 0000000..4fc3834
--- /dev/null
@@ -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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+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 (file)
index 0000000..5435ad8
--- /dev/null
@@ -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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+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<String> mItems = new LinkedList<String>();
+
+       @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<RemediationInstruction> CREATOR = new Creator<RemediationInstruction>() {
+
+               @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<String> 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<RemediationInstruction> fromXml(String xml)
+       {
+               List<RemediationInstruction> instructions = new LinkedList<RemediationInstruction>();
+               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 &lt;remediationinstructions&gt; 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<RemediationInstruction> 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 &lt;instruction&gt; 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 &lt;items&gt; 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 (file)
index 0000000..ca75900
--- /dev/null
@@ -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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+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 (file)
index 0000000..11f1c61
--- /dev/null
@@ -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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+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 (file)
index 0000000..ecab7db
--- /dev/null
@@ -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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+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 (file)
index 0000000..dd1ad72
--- /dev/null
@@ -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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+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<Pair<String, String>> mPackages = new LinkedList<Pair<String, String>>();
+
+       /**
+        * 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<String, String>(name, version));
+       }
+
+       @Override
+       public byte[] getEncoding()
+       {
+               BufferedByteWriter writer = new BufferedByteWriter();
+               writer.put16(RESERVED);
+               writer.put16((short)mPackages.size());
+               for (Pair<String, String> 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 (file)
index 0000000..191690b
--- /dev/null
@@ -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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+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<Pair<Protocol, Short>> mPorts = new LinkedList<Pair<Protocol, Short>>();
+
+       /**
+        * 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, Short>(protocol, port));
+       }
+
+       @Override
+       public byte[] getEncoding()
+       {
+               BufferedByteWriter writer = new BufferedByteWriter();
+               for (Pair<Protocol, Short> 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 (file)
index 0000000..9db702e
--- /dev/null
@@ -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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+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 (file)
index 0000000..cace18d
--- /dev/null
@@ -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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+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 (file)
index 0000000..37d8201
--- /dev/null
@@ -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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+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<Pair<String, String>> mSettings = new LinkedList<Pair<String, String>>();
+
+       /**
+        * 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<String, String>(name, value));
+       }
+
+       @Override
+       public byte[] getEncoding()
+       {
+               BufferedByteWriter writer = new BufferedByteWriter();
+               writer.put32(mSettings.size());
+               for (Pair<String, String> 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 (file)
index 0000000..4b6f2bc
--- /dev/null
@@ -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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+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 (file)
index 0000000..a686f13
--- /dev/null
@@ -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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+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 (file)
index 0000000..ebe9e10
--- /dev/null
@@ -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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+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 (file)
index 0000000..caa5170
--- /dev/null
@@ -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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+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<PackageInfo> 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 (file)
index 0000000..ed86686
--- /dev/null
@@ -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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+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 (file)
index 0000000..c377e90
--- /dev/null
@@ -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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+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 (file)
index 0000000..7320652
--- /dev/null
@@ -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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+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 (file)
index 0000000..658c2da
--- /dev/null
@@ -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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+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 (file)
index 0000000..6e0df94
--- /dev/null
@@ -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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+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 (file)
index 0000000..ff58849
--- /dev/null
@@ -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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+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<RemediationInstruction>(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);
+       }
+}
index d7c1cc4..3cf3950 100644 (file)
@@ -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 (file)
index 0000000..04c288b
--- /dev/null
@@ -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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+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<String>(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 (file)
index 0000000..66d0de2
--- /dev/null
@@ -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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+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<RemediationInstruction> 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 (file)
index 0000000..86467dc
--- /dev/null
@@ -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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+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<RemediationInstruction> 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<RemediationInstruction> instructions)
+       {
+               if (mAdapter == null)
+               {
+                       mAdapter = new RemediationInstructionAdapter(getActivity());
+                       setListAdapter(mAdapter);
+               }
+               mInstructions = instructions;
+               mAdapter.setData(mInstructions);
+       }
+}
index 91e521c..baad961 100644 (file)
@@ -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();
+               }
+       }
 }
index 738ed11..3219bba 100644 (file)
@@ -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
 
 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<RemediationInstruction> 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<RemediationInstruction>(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 (file)
index 0000000..e9ab522
--- /dev/null
@@ -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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+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<RemediationInstruction>
+{
+       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<RemediationInstruction> 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 (file)
index 0000000..efc7283
--- /dev/null
@@ -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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+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;
+       }
+}
index 66606f9..75cf74f 100644 (file)
@@ -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 --------------------------------------------------------------
 
index 96f4375..49e3dd1 100644 (file)
@@ -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);
index a64affd..ebadf44 100644 (file)
@@ -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")) == '-')
index da8d35c..079e65f 100644 (file)
@@ -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;
 
index 6b37ac7..4744178 100644 (file)
@@ -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;
index 5ca5879..38c92ce 100644 (file)
@@ -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;
index 6b8d6be..6c57fa0 100644 (file)
@@ -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 (file)
index 0000000..db953d9
--- /dev/null
@@ -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)
index b52b81c..b5862da 100644 (file)
@@ -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");
        }
index 2c49cb0..17000cd 100644 (file)
@@ -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
index d8bba98..4c570c4 100644 (file)
@@ -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 (file)
index 0000000..7c353c4
--- /dev/null
@@ -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)
index e69c324..f684087 100644 (file)
@@ -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);
index 40b6d1f..fe4c555 100644 (file)
@@ -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;
        }
index 789f12f..8bd6469 100644 (file)
@@ -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;
        }
index 1fb3065..41d45de 100644 (file)
@@ -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;
        }
index 986e860..d6a7c64 100644 (file)
@@ -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;
index 06282de..0e103de 100644 (file)
@@ -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.
index 769b9fa..3a5b845 100644 (file)
@@ -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);