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